Skip to content

Commit

Permalink
Updates WP_Theme_JSON_Resolver and tests to be equal to core (#36223)
Browse files Browse the repository at this point in the history
  • Loading branch information
oandregal authored Nov 5, 2021
1 parent 6b6c4d8 commit 92c7170
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 222 deletions.
152 changes: 10 additions & 142 deletions lib/class-wp-theme-json-resolver-gutenberg.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@ class WP_Theme_JSON_Resolver_Gutenberg {
private static $user_custom_post_type_id = null;

/**
* Structure to hold i18n metadata.
* Container to keep loaded i18n schema for `theme.json`.
*
* @var Array
*/
private static $theme_json_i18n = null;
private static $i18n_schema = null;

/**
* Processes a file that adheres to the theme.json
Expand All @@ -67,116 +67,22 @@ class WP_Theme_JSON_Resolver_Gutenberg {
private static function read_json_file( $file_path ) {
$config = array();
if ( $file_path ) {
$decoded_file = json_decode(
file_get_contents( $file_path ),
true
);

$json_decoding_error = json_last_error();
if ( JSON_ERROR_NONE !== $json_decoding_error ) {
trigger_error( "Error when decoding a theme.json schema at path $file_path " . json_last_error_msg() );
return $config;
}

$decoded_file = gutenberg_json_file_decode( $file_path, array( 'associative' => true ) );
if ( is_array( $decoded_file ) ) {
$config = $decoded_file;
}
}
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 ) {
$result[] = array(
'path' => $current_path,
'key' => $key,
'context' => $context,
);
}
return $result;
}
$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 fields that are translatable and the keys that are translatable
*/
public static function get_fields_to_translate() {
if ( null === self::$theme_json_i18n ) {
$file_structure = self::read_json_file( __DIR__ . '/theme-i18n.json' );
self::$theme_json_i18n = self::extract_paths_to_translate( $file_structure );
}
return self::$theme_json_i18n;
}

/**
* Translates a chunk of the loaded theme.json structure.
*
* @param array $array_to_translate The chunk of theme.json to translate.
* @param string $key The key of the field that contains the string to translate.
* @param string $context The context to apply in the translation call.
* @param string $domain Text domain. Unique identifier for retrieving translated strings.
*
* @return array Returns the modified $theme_json chunk.
*/
private static function translate_theme_json_chunk( array $array_to_translate, $key, $context, $domain ) {
foreach ( $array_to_translate as $item_key => $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
$array_to_translate[ $item_key ][ $key ] = translate_with_gettext_context( $array_to_translate[ $item_key ][ $key ], $context, $domain );
}

return $array_to_translate;
_deprecated_function( __METHOD__, '5.9.0' );
return array();
}

/**
Expand All @@ -191,50 +97,12 @@ private static function translate_theme_json_chunk( array $array_to_translate, $
* @return array Returns the modified $theme_json_structure.
*/
private static function translate( $theme_json, $domain = 'default' ) {
$fields = self::get_fields_to_translate();
foreach ( $fields as $field ) {
$path = $field['path'];
$key = $field['key'];
$context = $field['context'];

/*
* We need to process the paths that include '*' separately.
* One example of such a path would be:
* [ 'settings', 'blocks', '*', 'color', 'palette' ]
*/
$nodes_to_iterate = array_keys( $path, '*', true );
if ( ! empty( $nodes_to_iterate ) ) {
/*
* At the moment, we only need to support one '*' in the path, so take it directly.
* - base will be [ 'settings', 'blocks' ]
* - data will be [ 'color', 'palette' ]
*/
$base_path = array_slice( $path, 0, $nodes_to_iterate[0] );
$data_path = array_slice( $path, $nodes_to_iterate[0] + 1 );
$base_tree = _wp_array_get( $theme_json, $base_path, array() );
foreach ( $base_tree as $node_name => $node_data ) {
$array_to_translate = _wp_array_get( $node_data, $data_path, null );
if ( is_null( $array_to_translate ) ) {
continue;
}

// Whole path will be [ 'settings', 'blocks', 'core/paragraph', 'color', 'palette' ].
$whole_path = array_merge( $base_path, array( $node_name ), $data_path );
$translated_array = self::translate_theme_json_chunk( $array_to_translate, $key, $context, $domain );
gutenberg_experimental_set( $theme_json, $whole_path, $translated_array );
}
} else {
$array_to_translate = _wp_array_get( $theme_json, $path, null );
if ( is_null( $array_to_translate ) ) {
continue;
}

$translated_array = self::translate_theme_json_chunk( $array_to_translate, $key, $context, $domain );
gutenberg_experimental_set( $theme_json, $path, $translated_array );
}
if ( null === self::$i18n_schema ) {
$i18n_schema = wp_json_file_decode( __DIR__ . '/theme-i18n.json' );
self::$i18n_schema = null === $i18n_schema ? array() : $i18n_schema;
}

return $theme_json;
return translate_settings_using_i18n_schema( self::$i18n_schema, $theme_json, $domain );
}

/**
Expand Down Expand Up @@ -510,7 +378,7 @@ public static function clean_cached_data() {
self::$user = null;
self::$user_custom_post_type_id = null;
self::$theme_has_support = null;
self::$theme_json_i18n = null;
self::$i18n_schema = null;
}

}
Expand Down
52 changes: 52 additions & 0 deletions lib/compat/wordpress-5.9/json-file-decode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php
/**
* Function to read json files.
*
* @package gutenberg
*/

/**
* Reads and decodes a JSON file.
*
* @param string $filename Path to the JSON file.
* @param array $options {
* Optional. Options to be used with `json_decode()`.
*
* @type bool associative Optional. When `true`, JSON objects will be returned as associative arrays.
* When `false`, JSON objects will be returned as objects.
* }
*
* @return mixed Returns the value encoded in JSON in appropriate PHP type.
* `null` is returned if the file is not found, or its content can't be decoded.
*/
function gutenberg_json_file_decode( $filename, $options = array() ) {
$result = null;
$filename = wp_normalize_path( realpath( $filename ) );
if ( ! file_exists( $filename ) ) {
trigger_error(
sprintf(
/* translators: %s: Path to the JSON file. */
__( "File %s doesn't exist!", 'gutenberg' ),
$filename
)
);
return $result;
}

$options = wp_parse_args( $options, array( 'associative' => false ) );
$decoded_file = json_decode( file_get_contents( $filename ), $options['associative'] );

if ( JSON_ERROR_NONE !== json_last_error() ) {
trigger_error(
sprintf(
/* translators: 1: Path to the JSON file, 2: Error message. */
__( 'Error when decoding a JSON file at path %1$s: %2$s', 'gutenberg' ),
$filename,
json_last_error_msg()
)
);
return $result;
}

return $decoded_file;
}
48 changes: 48 additions & 0 deletions lib/compat/wordpress-5.9/translate-settings-using-i18n-schema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php
/**
* Function to translate json files.
*
* @package gutenberg
*/

/**
* Translates the provided settings value using its i18n schema.
*
* @param string|string[]|array[]|object $i18n_schema I18n schema for the setting.
* @param string|string[]|array[] $settings Value for the settings.
* @param string $textdomain Textdomain to use with translations.
*
* @return string|string[]|array[] Translated settings.
*/
function gutenberg_translate_settings_using_i18n_schema( $i18n_schema, $settings, $textdomain ) {
if ( empty( $i18n_schema ) || empty( $settings ) || empty( $textdomain ) ) {
return $settings;
}

if ( is_string( $i18n_schema ) && is_string( $settings ) ) {
//phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText, WordPress.WP.I18n.NonSingularStringLiteralContext, WordPress.WP.I18n.NonSingularStringLiteralDomain, WordPress.WP.I18n.LowLevelTranslationFunction
return translate_with_gettext_context( $settings, $i18n_schema, $textdomain );
}
if ( is_array( $i18n_schema ) && is_array( $settings ) ) {
$translated_settings = array();
foreach ( $settings as $value ) {
$translated_settings[] = translate_settings_using_i18n_schema( $i18n_schema[0], $value, $textdomain );
}
return $translated_settings;
}
if ( is_object( $i18n_schema ) && is_array( $settings ) ) {
$group_key = '*';
$translated_settings = array();
foreach ( $settings as $key => $value ) {
if ( isset( $i18n_schema->$key ) ) {
$translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$key, $value, $textdomain );
} elseif ( isset( $i18n_schema->$group_key ) ) {
$translated_settings[ $key ] = translate_settings_using_i18n_schema( $i18n_schema->$group_key, $value, $textdomain );
} else {
$translated_settings[ $key ] = $value;
}
}
return $translated_settings;
}
return $settings;
}
2 changes: 2 additions & 0 deletions lib/load.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ function gutenberg_is_experiment_enabled( $name ) {
require __DIR__ . '/compat/wordpress-5.8.1/index.php';
require __DIR__ . '/compat/wordpress-5.9/default-editor-styles.php';
require __DIR__ . '/compat/wordpress-5.9/get-global-styles-and-settings.php';
require __DIR__ . '/compat/wordpress-5.9/json-file-decode.php';
require __DIR__ . '/compat/wordpress-5.9/translate-settings-using-i18n-schema.php';
require __DIR__ . '/utils.php';
require __DIR__ . '/editor-settings.php';

Expand Down
Loading

0 comments on commit 92c7170

Please sign in to comment.