Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try: extract strings for translation directly from the source JSON file #28976

Closed
wants to merge 3 commits into from
Closed
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
124 changes: 2 additions & 122 deletions lib/class-wp-theme-json-resolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,124 +78,6 @@ private static function get_from_file( $file_path ) {
return $config;
}

/**
* Converts a tree as in i18n-theme.json into a linear array
* containing metadata to translate a theme.json file.
*
* For example, given this input:
*
* {
* "settings": {
* "*": {
* "typography": {
* "fontSizes": [ { "name": "Font size name" } ],
* "fontStyles": [ { "name": "Font size name" } ]
* }
* }
* }
* }
*
* will return this output:
*
* [
* 0 => [
* 'path' => [ 'settings', '*', 'typography', 'fontSizes' ],
* 'key' => 'name',
* 'context' => 'Font size name'
* ],
* 1 => [
* 'path' => [ 'settings', '*', 'typography', 'fontStyles' ],
* 'key' => 'name',
* 'context' => 'Font style name'
* ]
* ]
*
* @param array $i18n_partial A tree that follows the format of i18n-theme.json.
* @param array $current_path Keeps track of the path as we walk down the given tree.
*
* @return array A linear array containing the paths to translate.
*/
private static function extract_paths_to_translate( $i18n_partial, $current_path = array() ) {
$result = array();
foreach ( $i18n_partial as $property => $partial_child ) {
if ( is_numeric( $property ) ) {
foreach ( $partial_child as $key => $context ) {
return array(
array(
'path' => $current_path,
'key' => $key,
'context' => $context,
),
);
}
}
$result = array_merge(
$result,
self::extract_paths_to_translate( $partial_child, array_merge( $current_path, array( $property ) ) )
);
}
return $result;
}

/**
* Returns a data structure used in theme.json translation.
*
* @return array An array of theme.json paths that are translatable and the keys that are translatable
*/
public static function get_presets_to_translate() {
static $theme_json_i18n = null;
if ( null === $theme_json_i18n ) {
$file_structure = self::get_from_file( __DIR__ . '/experimental-i18n-theme.json' );
$theme_json_i18n = self::extract_paths_to_translate( $file_structure );
}
return $theme_json_i18n;
}

/**
* Given a theme.json structure modifies it in place
* to update certain values by its translated strings
* according to the language set by the user.
*
* @param array $theme_json The theme.json to translate.
* @param string $domain Optional. Text domain. Unique identifier for retrieving translated strings.
* Default 'default'.
*/
private static function translate( &$theme_json, $domain = 'default' ) {
if ( ! isset( $theme_json['settings'] ) ) {
return;
}

$presets = self::get_presets_to_translate();
foreach ( $theme_json['settings'] as &$settings ) {
if ( empty( $settings ) ) {
continue;
}

foreach ( $presets as $preset ) {
$path = array_slice( $preset['path'], 2 );
$key = $preset['key'];
$context = $preset['context'];

$array_to_translate = gutenberg_experimental_get( $settings, $path, null );
if ( null === $array_to_translate ) {
continue;
}

foreach ( $array_to_translate as &$item_to_translate ) {
if ( empty( $item_to_translate[ $key ] ) ) {
continue;
}

// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralContext,WordPress.WP.I18n.NonSingularStringLiteralDomain
$item_to_translate[ $key ] = translate_with_gettext_context( $item_to_translate[ $key ], $context, $domain );
// phpcs:enable
}

gutenberg_experimental_set( $settings, $path, $array_to_translate );
}
}
}

/**
* Return core's origin config.
*
Expand All @@ -208,7 +90,6 @@ public static function get_core_data() {

$all_blocks = WP_Theme_JSON::ALL_BLOCKS_NAME;
$config = self::get_from_file( __DIR__ . '/experimental-default-theme.json' );
self::translate( $config );

// Start i18n logic to remove when JSON i18 strings are extracted.
$default_colors_i18n = array(
Expand Down Expand Up @@ -288,8 +169,7 @@ public static function get_core_data() {
public static function get_theme_data( $theme_support_data = array() ) {
if ( null === self::$theme ) {
$theme_json_data = self::get_from_file( locate_template( 'experimental-theme.json' ) );
self::translate( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
self::$theme = new WP_Theme_JSON( $theme_json_data );
self::$theme = new WP_Theme_JSON( $theme_json_data, wp_get_theme()->get( 'TextDomain' ) );
}

if ( empty( $theme_support_data ) ) {
Expand All @@ -300,7 +180,7 @@ public static function get_theme_data( $theme_support_data = array() ) {
* We want the presets and settings declared in theme.json
* to override the ones declared via add_theme_support.
*/
$with_theme_supports = new WP_Theme_JSON( $theme_support_data );
$with_theme_supports = new WP_Theme_JSON( $theme_support_data, wp_get_theme()->get( 'TextDomain' ) );
$with_theme_supports->merge( self::$theme );

return $with_theme_supports;
Expand Down
14 changes: 13 additions & 1 deletion lib/class-wp-theme-json.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class WP_Theme_JSON {
*/
private $theme_json = null;

private static $domain = null;

/**
* Holds block metadata extracted from block.json
* to be shared among all instances so we don't
Expand Down Expand Up @@ -329,8 +331,9 @@ class WP_Theme_JSON {
*
* @param array $theme_json A structure that follows the theme.json schema.
*/
public function __construct( $theme_json = array() ) {
public function __construct( $theme_json = array(), $domain = 'default' ) {
$this->theme_json = array();
self::$domain = $domain;

if ( ! is_array( $theme_json ) ) {
return;
Expand Down Expand Up @@ -406,6 +409,15 @@ public function __construct( $theme_json = array() ) {
}
}

// Walk the array and translate in place
array_walk_recursive( $this->theme_json, [ 'WP_Theme_JSON', 'translate' ] );
}

private static function translate( &$item, $key ) {
if ( is_string( $item ) && ( '_x(' === substr( $item, 0, 3 ) ) ) {
list( $name, $context ) = explode( ',', str_replace( ')', '', str_replace( '_x(', '', $item ) ) );
$item = translate_with_gettext_context( trim( $name ), trim( $context ), self::$domain );
}
}

/**
Expand Down
18 changes: 0 additions & 18 deletions lib/experimental-i18n-theme.json

This file was deleted.

18 changes: 18 additions & 0 deletions phpunit/class-wp-theme-json-test.php
Original file line number Diff line number Diff line change
Expand Up @@ -783,4 +783,22 @@ function test_get_template_parts() {
)
);
}

function test_translation_mechanism() {
$theme_json = new WP_Theme_JSON(
array(
'customTemplates' => array(
'page-home' => array(
'title' => '_x( Some title, Some context )',
),
),
)
);

$expected = 'Some title';
$actual = $theme_json->get_raw_data()['customTemplates']['page-home']['title'];

$this->assertEquals( $expected, $actual );
}

}