From 5b1e57aec35147a00fc4b642020e3d091c04e4ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Fri, 12 Feb 2021 14:32:27 +0100 Subject: [PATCH 1/3] Prototype direction --- lib/class-wp-theme-json-resolver.php | 6 ++---- lib/class-wp-theme-json.php | 15 ++++++++++++++- phpunit/class-wp-theme-json-test.php | 18 ++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php index e67492896223c..29c256ef6d6db 100644 --- a/lib/class-wp-theme-json-resolver.php +++ b/lib/class-wp-theme-json-resolver.php @@ -208,7 +208,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( @@ -288,8 +287,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 ) ) { @@ -300,7 +298,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; diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index c602e28172b4c..65bfb73e40707 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -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 @@ -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; @@ -406,6 +409,16 @@ 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 ) = sscanf( $item, '_x( %s %s )' ); + error_log( "item is $item - name is $name / context is $context" ); + $item = translate_with_gettext_context( $name, $context, self::$domain ); + } } /** diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 857b46d57baf5..2b29e72b806c3 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -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( SomeTitle SomeContext )', + ), + ), + ) + ); + + $expected = 'SomeTitle'; + $actual = $theme_json->get_raw_data()['customTemplates']['page-home']['title']; + + $this->assertEquals( $expected, $actual ); + } + } From 0ba281bcc65f33f9dbe9a4b9cbd440cdb642660a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Fri, 12 Feb 2021 14:37:49 +0100 Subject: [PATCH 2/3] Remove no longer necessary bits --- lib/class-wp-theme-json-resolver.php | 118 --------------------------- lib/experimental-i18n-theme.json | 18 ---- 2 files changed, 136 deletions(-) delete mode 100644 lib/experimental-i18n-theme.json diff --git a/lib/class-wp-theme-json-resolver.php b/lib/class-wp-theme-json-resolver.php index 29c256ef6d6db..8dfef1e52426c 100644 --- a/lib/class-wp-theme-json-resolver.php +++ b/lib/class-wp-theme-json-resolver.php @@ -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. * diff --git a/lib/experimental-i18n-theme.json b/lib/experimental-i18n-theme.json deleted file mode 100644 index 80c7011208d53..0000000000000 --- a/lib/experimental-i18n-theme.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "settings": { - "*": { - "typography": { - "fontSizes": [ { "name": "Font size name" } ], - "fontStyles": [ { "name": "Font style name" } ], - "fontWeights": [ { "name": "Font weight name" } ], - "fontFamilies": [ { "name": "Font family name" } ], - "textTransforms": [ { "name": "Text transform name" } ], - "textDecorations": [ { "name": "Text decoration name" } ] - }, - "color": { - "palette": [ { "name": "Color name" } ], - "gradients": [ { "name": "Gradient name" } ] - } - } - } -} From 6b96d3006057b490b9b6f2bc1c169bb54c5f0f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9?= Date: Fri, 12 Feb 2021 14:45:16 +0100 Subject: [PATCH 3/3] Cover real use cases: names of strings to translate and contexts can contain spaces --- lib/class-wp-theme-json.php | 5 ++--- phpunit/class-wp-theme-json-test.php | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 65bfb73e40707..e51e4dc31b93c 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -415,9 +415,8 @@ public function __construct( $theme_json = array(), $domain = 'default' ) { private static function translate( &$item, $key ) { if ( is_string( $item ) && ( '_x(' === substr( $item, 0, 3 ) ) ) { - list( $name, $context ) = sscanf( $item, '_x( %s %s )' ); - error_log( "item is $item - name is $name / context is $context" ); - $item = translate_with_gettext_context( $name, $context, self::$domain ); + list( $name, $context ) = explode( ',', str_replace( ')', '', str_replace( '_x(', '', $item ) ) ); + $item = translate_with_gettext_context( trim( $name ), trim( $context ), self::$domain ); } } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index 2b29e72b806c3..9cba4288132e4 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -789,13 +789,13 @@ function test_translation_mechanism() { array( 'customTemplates' => array( 'page-home' => array( - 'title' => '_x( SomeTitle SomeContext )', + 'title' => '_x( Some title, Some context )', ), ), ) ); - $expected = 'SomeTitle'; + $expected = 'Some title'; $actual = $theme_json->get_raw_data()['customTemplates']['page-home']['title']; $this->assertEquals( $expected, $actual );