From 5b46851f7c52c2548630314d456b6e058d32a645 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 1 Feb 2024 11:43:21 +0000 Subject: [PATCH] I18N: Improve singular lookup of pluralized strings. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures that string lookup in MO files only uses the singular string. This matches expected behavior with gettext files and improves compatibility for cases where for example both `__( 'Product' )` and `_n( 'Product', 'Products’, num )` are used in a project, where both will use the same translation for the singular version. Maintains backward compatibility and feature parity with the pomo library and the PHP translation file format. Replaces [57386], which was reverted in [57505], with a more accurate and performant solution. See #59656. git-svn-id: https://develop.svn.wordpress.org/trunk@57513 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/l10n/class-wp-translation-file-mo.php | 10 +++++++++- .../l10n/class-wp-translation-file-php.php | 8 ++------ src/wp-includes/l10n/class-wp-translations.php | 11 +++-------- tests/phpunit/data/l10n/example-simple.php | 10 +++++----- tests/phpunit/tests/l10n/wpTranslations.php | 3 +-- tests/phpunit/tests/l10n/wpTranslationsConvert.php | 4 ++++ 6 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/wp-includes/l10n/class-wp-translation-file-mo.php b/src/wp-includes/l10n/class-wp-translation-file-mo.php index 225b48a836..bf39cc70ec 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-mo.php +++ b/src/wp-includes/l10n/class-wp-translation-file-mo.php @@ -161,7 +161,15 @@ class WP_Translation_File_MO extends WP_Translation_File { $this->headers[ strtolower( $name ) ] = $value; } } else { - $this->entries[ (string) $original ] = $translation; + /* + * In MO files, the key normally contains both singular and plural versions. + * However, this just adds the singular string for lookup, + * which caters for cases where both __( 'Product' ) and _n( 'Product', 'Products' ) + * are used and the translation is expected to be the same for both. + */ + $parts = explode( "\0", (string) $original ); + + $this->entries[ $parts[0] ] = $translation; } } diff --git a/src/wp-includes/l10n/class-wp-translation-file-php.php b/src/wp-includes/l10n/class-wp-translation-file-php.php index 9f5b5abd98..f93dd0163f 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-php.php +++ b/src/wp-includes/l10n/class-wp-translation-file-php.php @@ -28,12 +28,8 @@ class WP_Translation_File_PHP extends WP_Translation_File { } if ( isset( $result['messages'] ) && is_array( $result['messages'] ) ) { - foreach ( $result['messages'] as $singular => $translations ) { - if ( is_array( $translations ) ) { - $this->entries[ $singular ] = implode( "\0", $translations ); - } elseif ( is_string( $translations ) ) { - $this->entries[ $singular ] = $translations; - } + foreach ( $result['messages'] as $original => $translation ) { + $this->entries[ (string) $original ] = $translation; } unset( $result['messages'] ); } diff --git a/src/wp-includes/l10n/class-wp-translations.php b/src/wp-includes/l10n/class-wp-translations.php index 5992c855b6..e177e1d8c5 100644 --- a/src/wp-includes/l10n/class-wp-translations.php +++ b/src/wp-includes/l10n/class-wp-translations.php @@ -95,15 +95,10 @@ class WP_Translations { $entry->context = $parts[0]; } - // Look for plural original. - $parts = explode( "\0", $original ); - $entry->singular = $parts[0]; - if ( isset( $parts[1] ) ) { - $entry->is_plural = true; - $entry->plural = $parts[1]; - } - + $entry->singular = $original; $entry->translations = explode( "\0", $translations ); + $entry->is_plural = count( $entry->translations ) > 1; + return $entry; } diff --git a/tests/phpunit/data/l10n/example-simple.php b/tests/phpunit/data/l10n/example-simple.php index 4eb71d4405..24b3552bfd 100644 --- a/tests/phpunit/data/l10n/example-simple.php +++ b/tests/phpunit/data/l10n/example-simple.php @@ -2,10 +2,10 @@ return [ 'messages' => [ - 'original' => ['translation'], - 'contextoriginal with context' => ['translation with context'], - 'plural0' . "\0" . 'plural1' => ['translation0', 'translation1'], - 'contextplural0 with context' . "\0" . 'plural1 with context' => ['translation0 with context', 'translation1 with context'], - 'Product' . "\0" . 'Products' => 'Produkt' . "\0" . 'Produkte', + 'original' => 'translation', + 'contextoriginal with context' => 'translation with context', + 'plural0' => 'translation0' . "\0" . 'translation1', + 'contextplural0 with context' => 'translation0 with context' . "\0" . 'translation1 with context', + 'Product' => 'Produkt' . "\0" . 'Produkte', ], ]; diff --git a/tests/phpunit/tests/l10n/wpTranslations.php b/tests/phpunit/tests/l10n/wpTranslations.php index f2ef4535a0..3879a570e3 100644 --- a/tests/phpunit/tests/l10n/wpTranslations.php +++ b/tests/phpunit/tests/l10n/wpTranslations.php @@ -71,8 +71,8 @@ class WP_Translations_Tests extends WP_UnitTestCase { array( new Translation_Entry( array( + 'is_plural' => true, 'singular' => 'one dragon', - 'plural' => '%d dragons', 'translations' => array( 'oney dragoney', 'twoey dragoney', @@ -119,7 +119,6 @@ class WP_Translations_Tests extends WP_UnitTestCase { array( 'is_plural' => true, 'singular' => 'one dragon', - 'plural' => '%d dragons', 'context' => 'dragonland', 'translations' => array( 'oney dragoney', diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index 000388aabb..ea318eb329 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -199,6 +199,10 @@ class WP_Translation_Controller_Convert_Tests extends WP_UnitTestCase { $this->assertSame( 'translation1 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); $this->assertSame( 'translation0 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); $this->assertSame( 'translation1 with context', $controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + + $this->assertSame( 'Produkt', $controller->translate( 'Product', '', 'unittest' ) ); + $this->assertSame( 'Produkt', $controller->translate_plural( array( 'Product', 'Products' ), 1, '', 'unittest' ) ); + $this->assertSame( 'Produkte', $controller->translate_plural( array( 'Product', 'Products' ), 2, '', 'unittest' ) ); } /**