Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ jobs:
container: tests-cli
plugin-name: handbook
phpunit-args: "--no-configuration --bootstrap phpunit/bootstrap.php phpunit/tests/"
- name: Post Translation
working-directory: environments
wp-env-args: "--config post-translation/.wp-env.json"
container: cli
plugin-name: wporg-post-translation
phpunit-bin: /wordpress-phpunit/vendor/bin/phpunit
- name: Plugin Directory
working-directory: environments
wp-env-args: "--config plugin-directory/.wp-env.json"
Expand All @@ -90,6 +96,11 @@ jobs:
working-directory: ${{ matrix.working-directory }}
run: wp-env ${{ matrix.wp-env-args || '' }} run ${{ matrix.container }} composer require --dev yoast/phpunit-polyfills:^4.0 --working-dir=/wordpress-phpunit

- name: Downgrade PHPUnit for WP test suite compat
if: matrix.phpunit-bin
working-directory: ${{ matrix.working-directory }}
run: wp-env ${{ matrix.wp-env-args || '' }} run ${{ matrix.container }} composer require --dev phpunit/phpunit:^9.0 -W --working-dir=/wordpress-phpunit

- name: Run PHPUnit
working-directory: ${{ matrix.working-directory }}
run: wp-env ${{ matrix.wp-env-args || '' }} run ${{ matrix.container }} --env-cwd=wp-content/plugins/${{ matrix.plugin-name }} phpunit ${{ matrix.phpunit-args || '' }}
run: wp-env ${{ matrix.wp-env-args || '' }} run ${{ matrix.container }} --env-cwd=wp-content/plugins/${{ matrix.plugin-name }} ${{ matrix.phpunit-bin || 'phpunit' }} ${{ matrix.phpunit-args || '' }}
18 changes: 18 additions & 0 deletions environments/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,24 @@ npm run jobs:env start
npm run jobs:env -- run cli -- wp <command>
```

### Post Translation

A local instance for the Post Translation plugin with the GlotPress Translate Bridge dependency.

**Start:**

```bash
npm run post-translation:env start
```

**Run tests:**

```bash
npm run post-translation:env -- run cli --env-cwd=wp-content/plugins/wporg-post-translation phpunit
```

**Access:** `http://localhost:8888`

### Handbook (in-plugin)

The Handbook plugin has its own `.wp-env.json` in `wordpress.org/public_html/wp-content/plugins/handbook/`.
Expand Down
3 changes: 2 additions & 1 deletion environments/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"plugins:env": "wp-env --config plugin-directory/.wp-env.json",
"plugins:import": "npm run plugins:env -- run cli -- php wp-content/plugins/plugin-directory/bin/import-plugin.php --create --plugin",
"plugins:refresh": "npm run plugins:env -- run cli -- wp option delete wporg_env_imported && npm run plugins:env -- run cli wp eval-file wp-content/env-bin/import-plugins.php",
"jobs:env": "wp-env --config jobs/.wp-env.json"
"jobs:env": "wp-env --config jobs/.wp-env.json",
"post-translation:env": "wp-env --config post-translation/.wp-env.json"
},
"devDependencies": {
"@wordpress/env": "^11"
Expand Down
19 changes: 19 additions & 0 deletions environments/post-translation/.wp-env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"core": "WordPress/WordPress#master",
"testsEnvironment": false,
"plugins": [
"../wordpress.org/public_html/wp-content/plugins/wporg-post-translation",
"../wordpress.org/public_html/wp-content/plugins/glotpress-translate-bridge",
"GlotPress/GlotPress"
],
"mappings": {
"wp-content/mu-plugins/wporg-dev-login.php": "./mocks/mu-plugins/wporg-dev-login.php"
},
"config": {
"GP_URL_BASE": "/glotpress/",
"GLOTPRESS_TABLE_PREFIX": "wp_gp_"
},
"lifecycleScripts": {
"afterStart": "bash post-translation/bin/after-start.sh"
}
}
94 changes: 94 additions & 0 deletions environments/post-translation/bin/after-start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
#!/bin/bash
#
# Runs after wp-env start. Sets up permalinks, GlotPress, and sample content.
#

CONFIG="--config post-translation/.wp-env.json"
WP="npx wp-env $CONFIG run cli --"

# Set up permalinks (required by GlotPress).
echo "Setting up permalinks..."
$WP wp rewrite structure '/%postname%/' --hard

# Create GlotPress database tables.
echo "Creating GlotPress tables..."
$WP wp eval "require_once ABSPATH . 'wp-admin/includes/upgrade.php'; require_once WP_PLUGIN_DIR . '/GlotPress/gp-includes/schema.php'; dbDelta( gp_schema_get() );"

# Grant the admin user GlotPress admin permissions.
echo "Setting up GlotPress admin..."
$WP wp eval "
if ( class_exists( 'GP' ) && class_exists( 'GP_Permission' ) ) {
GP::\$permission->create( array(
'user_id' => 1,
'action' => 'admin',
'object_type' => '',
'object_id' => '',
) );
}
"

# Create the parent 'Post Content' project with a Spanish translation set.
echo "Creating GlotPress projects..."
$WP wp eval "
if ( ! class_exists( 'GP' ) ) return;

\$parent = GP::\$project->by_path( 'post-content' );
if ( ! \$parent ) {
\$parent = GP::\$project->create_and_select( array(
'name' => 'Post Content',
'slug' => 'post-content',
'parent_project_id' => null,
'description' => 'Translations for WordPress post and page content.',
'active' => 1,
) );
GP::\$translation_set->create( array(
'project_id' => \$parent->id,
'name' => 'Spanish',
'locale' => 'es',
'slug' => 'default',
) );
echo 'Created Post Content project with Spanish translation set.';
} else {
echo 'Post Content project already exists.';
}
"

# Set site locale to Spanish so translations are applied on the frontend.
echo "Setting locale to Spanish..."
$WP wp language core install es_ES
$WP wp site switch-language es_ES

# Create sample content with translation enabled.
echo "Creating sample content..."
$WP wp eval "
\$existing = get_page_by_path( 'sample-translated-page' );
if ( \$existing ) {
echo 'Sample content already exists.';
return;
}

\$page_id = wp_insert_post( array(
'post_type' => 'page',
'post_status' => 'publish',
'post_title' => 'About WordPress',
'post_name' => 'sample-translated-page',
'post_content' => '<!-- wp:heading --><h2>Welcome</h2><!-- /wp:heading -->
<!-- wp:paragraph --><p>WordPress is open source software you can use to create a beautiful website, blog, or app.</p><!-- /wp:paragraph -->
<!-- wp:heading {\"level\":3} --><h3>Features</h3><!-- /wp:heading -->
<!-- wp:list --><ul><li>Easy to use</li><li>Flexible and extensible</li><li>Available in many languages</li></ul><!-- /wp:list -->
<!-- wp:quote --><blockquote class=\"wp-block-quote\"><p>Code is poetry.</p><cite>WordPress</cite></blockquote><!-- /wp:quote -->
<!-- wp:paragraph --><p>Get started today and join the millions who trust WordPress.</p><!-- /wp:paragraph -->',
) );

update_post_meta( \$page_id, '_post_translation_enabled', true );

echo 'Created sample page (ID: ' . \$page_id . ') with translation enabled.';
"

# Trigger the import cron to send strings to GlotPress.
echo "Importing strings to GlotPress..."
$WP wp cron event run --all > /dev/null 2>&1 || true

echo "Post Translation environment ready."
echo " Sample page: http://localhost:8888/sample-translated-page/"
echo " GlotPress: http://localhost:8888/glotpress/"
3 changes: 2 additions & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
<arg value="ps" />
<arg name="colors" />

<!-- Exclude 3rd-party files -->
<!-- Exclude 3rd-party and generated files -->
<exclude-pattern>*/vendor/*</exclude-pattern>
<exclude-pattern>*/node_modules/*</exclude-pattern>
<exclude-pattern>*/build/*</exclude-pattern>
<exclude-pattern>*/plugins/plugin-directory/libs/*</exclude-pattern>
<exclude-pattern>*/plugins/theme-directory/lib/*</exclude-pattern>
<exclude-pattern>*/plugins/wpf-stripe/stripe-php/*</exclude-pattern>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,35 +33,41 @@ class GlotPress_Translate_Bridge {
/**
* Translate a single string.
*
* @param $singular The string to translate.
* @param $project_path The GlotPress project path.
* @param $context The strings context. Default: null.
* @param string $singular The string to translate.
* @param string $project_path The GlotPress project path.
* @param string $context The strings context. Default: null.
* @param bool|null $found Optional. Set to true if a translation was found, false otherwise. Default: null.
*
* @return string The translated string if it exists, else, the existing string.
*/
static function translate( $singular, $project_path, $context = null ) {
public static function translate( $singular, $project_path, $context = null, &$found = null ) {
$t = self::instance();

$translation = $t->find_translation( compact( 'singular', 'context' ), $project_path );

$found = (bool) $translation;

return $translation ? $translation[0] : $singular;
}

/**
* Translate a pluralised string. This does not support the `$count` parameter of `_n()`.
*
* @param $singular The singular form of the string.
* @param $plural The plural form of the string.
* @param $project_path The GlotPress project path.
* @param $context The strings context. Default: null
* @param string $singular The singular form of the string.
* @param string $plural The plural form of the string.
* @param string $project_path The GlotPress project path.
* @param string $context The strings context. Default: null.
* @param bool|null $found Optional. Set to true if a translation was found, false otherwise. Default: null.
*
* @return array The translated plural forms of the string.
*/
static function translate_plural( $singular, $plural, $project_path, $context = null ) {
public static function translate_plural( $singular, $plural, $project_path, $context = null, &$found = null ) {
$t = self::instance();

$translation = $t->find_translation( compact( 'singular', 'plural', 'context' ), $project_path );

$found = (bool) $translation;

return $translation ?: array( $singular, $plural );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<?php return array('dependencies' => array('react-jsx-runtime', 'wp-components', 'wp-core-data', 'wp-data', 'wp-editor', 'wp-i18n', 'wp-plugins'), 'version' => '56b462bc2448247e606c');

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?php
namespace WordPressdotorg\Post_Translation;

/**
* Handles scheduling GlotPress imports when posts are saved.
*/
class Admin {
public static function init() {
add_action( 'save_post', [ __CLASS__, 'on_save_post' ], 10, 2 );
add_action( 'post_translation_import', [ __CLASS__, 'handle_import' ], 10, 4 );
}

/**
* When a published post with translation enabled is saved, schedule an import.
*/
public static function on_save_post( $post_id, $post ) {
if ( 'publish' !== $post->post_status ) {
return;
}

$project = get_translation_project( $post );
if ( ! $project ) {
return;
}

$source_blog_id = get_current_blog_id();
$permalink = get_permalink( $post );
$args = [ $post_id, $source_blog_id, $project, $permalink ];

if ( wp_next_scheduled( 'post_translation_import', $args ) ) {
return;
}

// On WordPress.org multisite, schedule the import on translate.w.org.
if ( defined( 'WPORG_TRANSLATE_BLOGID' ) && is_multisite() ) {
switch_to_blog( WPORG_TRANSLATE_BLOGID );
wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'post_translation_import', $args );
restore_current_blog();
} else {
// Local/dev or single-site: run on the same site.
wp_schedule_single_event( time() + MINUTE_IN_SECONDS, 'post_translation_import', $args );
}
}

/**
* Handle the scheduled import cron event.
*
* On WordPress.org, this runs on translate.w.org and switches to the
* source blog to fetch post content. On single-site, it runs directly.
*/
public static function handle_import( $post_id, $source_blog_id, $project, $permalink = '' ) {
require_once __DIR__ . '/class-importer.php';

$importer = new Importer( $project );
$needs_switch = is_multisite() && get_current_blog_id() !== $source_blog_id;

if ( $needs_switch ) {
switch_to_blog( $source_blog_id );
}

$post = get_post( $post_id );
if ( ! $post || 'publish' !== $post->post_status ) {
if ( $needs_switch ) {
restore_current_blog();
}
return;
}

$strings = Post_Parser::post_to_strings( $post );

// Use the permalink captured at save time if available.
if ( ! $permalink ) {
$permalink = get_permalink( $post );
}

if ( $needs_switch ) {
restore_current_blog();
}

if ( empty( $strings ) ) {
return;
}

$importer->import( $strings, $permalink );
}
}
Loading
Loading