From c1f1b306d61530951257d0cb5a3d5ed094a28844 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 26 Sep 2023 13:50:36 +0200 Subject: [PATCH 01/65] Add Ginger MO --- .../includes/class-language-pack-upgrader.php | 39 ++ src/wp-includes/class-wp-locale-switcher.php | 2 + .../class-ginger-mo-translation-file-json.php | 117 ++++++ .../class-ginger-mo-translation-file-mo.php | 209 ++++++++++ .../class-ginger-mo-translation-file-php.php | 111 ++++++ .../class-ginger-mo-translation-file.php | 289 ++++++++++++++ .../class-ginger-mo-translations.php | 141 +++++++ src/wp-includes/ginger-mo/class-ginger-mo.php | 376 ++++++++++++++++++ src/wp-includes/l10n.php | 111 +++++- src/wp-settings.php | 8 + .../phpunit/data/languages/admin-es_ES.mo.php | 2 + tests/phpunit/data/languages/de_DE.mo.php | 2 + tests/phpunit/data/languages/es_ES.mo.php | 2 + 13 files changed, 1398 insertions(+), 11 deletions(-) create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translations.php create mode 100644 src/wp-includes/ginger-mo/class-ginger-mo.php create mode 100644 tests/phpunit/data/languages/admin-es_ES.mo.php create mode 100644 tests/phpunit/data/languages/de_DE.mo.php create mode 100644 tests/phpunit/data/languages/es_ES.mo.php diff --git a/src/wp-admin/includes/class-language-pack-upgrader.php b/src/wp-admin/includes/class-language-pack-upgrader.php index 3c3d42a56a9c8..34fa706b5e3b5 100644 --- a/src/wp-admin/includes/class-language-pack-upgrader.php +++ b/src/wp-admin/includes/class-language-pack-upgrader.php @@ -288,6 +288,45 @@ public function bulk_upgrade( $language_updates = array(), $args = array() ) { ) ); + foreach ( $language_updates_results as $translation ) { + switch ( $translation['type'] ) { + case 'plugin': + $file = WP_LANG_DIR . '/plugins/' . $translation['slug'] . '-' . $translation['language'] . '.mo'; + break; + case 'theme': + $file = WP_LANG_DIR . '/themes/' . $translation['slug'] . '-' . $translation['language'] . '.mo'; + break; + default: + $file = WP_LANG_DIR . '/' . $translation['language'] . '.mo'; + break; + } + + if ( file_exists( $file ) ) { + /** This filter is documented in wp-includes/l10n.php */ + $preferred_format = apply_filters( 'translation_file_format', 'php' ); + if ( ! in_array( $preferred_format, array( 'php', 'mo', 'json' ), true ) ) { + $preferred_format = 'php'; + } + + $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $file ); + + /** This filter is documented in wp-includes/l10n.php */ + $convert = apply_filters( 'convert_translation_files', true ); + + if ( 'mo' !== $preferred_format && $convert ) { + $contents = Ginger_MO_Translation_File::transform( $file, $preferred_format ); + + if ( false !== $contents ) { + if ( true === $this->fs_connect( array( dirname( $file ) ) ) ) { + $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE ); + } else { + file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents + } + } + } + } + } + // Re-add upgrade hooks. add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 ); diff --git a/src/wp-includes/class-wp-locale-switcher.php b/src/wp-includes/class-wp-locale-switcher.php index d07490f107d1d..8b52fcc4fbec8 100644 --- a/src/wp-includes/class-wp-locale-switcher.php +++ b/src/wp-includes/class-wp-locale-switcher.php @@ -283,6 +283,8 @@ private function change_locale( $locale ) { $wp_locale = new WP_Locale(); + Ginger_MO::instance()->set_locale( $locale ); + /** * Fires when the locale is switched to or restored. * diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php new file mode 100644 index 0000000000000..e1821adafea7f --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php @@ -0,0 +1,117 @@ +parsed = true; + + $data = file_get_contents( $this->file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + + if ( false === $data ) { + $this->error = true; + return; + } + + $data = json_decode( $data, true ); + + if ( false === $data || ! is_array( $data ) ) { + $this->error = json_last_error_msg(); + return; + } + + if ( ! isset( $data['domain'] ) || ! isset( $data['locale_data'][ $data['domain'] ] ) ) { + $this->error = true; + return; + } + + if ( isset( $data['translation-revision-date'] ) ) { + $this->headers['po-revision-date'] = $data['translation-revision-date']; + } + + $entries = $data['locale_data'][ $data['domain'] ]; + + foreach ( $entries as $key => $item ) { + if ( '' === $key ) { + $headers = array_change_key_case( $item ); + if ( isset( $headers['lang'] ) ) { + $this->headers['language'] = $headers['lang']; + unset( $headers['lang'] ); + } + + $this->headers = array_merge( + $this->headers, + $headers + ); + continue; + } + + if ( is_string( $item ) ) { + $this->entries[ (string) $key ] = $item; + } elseif ( is_array( $item ) ) { + $this->entries[ (string) $key ] = implode( "\0", $item ); + } + } + + unset( $this->headers['domain'] ); + } + + /** + * Exports translation contents as a string. + * + * @return string Translation file contents. + */ + public function export(): string { + $headers = array_change_key_case( $this->headers ); + + $domain = $headers['domain'] ?? 'messages'; + + $data = array( + 'domain' => $domain, + 'locale_data' => array( + $domain => $this->entries, + ), + ); + + if ( isset( $headers['po-revision-date'] ) ) { + $data['translation-revision-date'] = $headers['po-revision-date']; + } + + if ( isset( $headers['x-generator'] ) ) { + $data['generator'] = $headers['x-generator']; + } + + $data['locale_data'][ $domain ][''] = array( + 'domain' => $domain, + ); + + if ( isset( $headers['plural-forms'] ) ) { + $data['locale_data'][ $domain ]['']['plural-forms'] = $headers['plural-forms']; + } + + if ( isset( $headers['language'] ) ) { + $data['locale_data'][ $domain ]['']['lang'] = $headers['language']; + } + + $json = json_encode( $data, JSON_PRETTY_PRINT ); + + if ( false === $json ) { + return ''; + } + + return $json; + } +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php new file mode 100644 index 0000000000000..f54ec917bb922 --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php @@ -0,0 +1,209 @@ +error = "Magic Marker doesn't exist"; + return false; + } + + /** + * Parses the file. + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * + * @return bool True on success, false otherwise. + */ + protected function parse_file(): bool { + $this->parsed = true; + + $file_contents = file_get_contents( $this->file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents + + if ( false === $file_contents ) { + return false; + } + + $file_length = strlen( $file_contents ); + + if ( $file_length < 24 ) { + $this->error = 'Invalid Data.'; + return false; + } + + $this->uint32 = $this->detect_endian_and_validate_file( substr( $file_contents, 0, 4 ) ); + + if ( false === $this->uint32 ) { + return false; + } + + $offsets = substr( $file_contents, 4, 24 ); + + if ( false === $offsets ) { + return false; + } + + $offsets = unpack( "{$this->uint32}rev/{$this->uint32}total/{$this->uint32}originals_addr/{$this->uint32}translations_addr/{$this->uint32}hash_length/{$this->uint32}hash_addr", $offsets ); + + if ( false === $offsets ) { + return false; + } + + $offsets['originals_length'] = $offsets['translations_addr'] - $offsets['originals_addr']; + $offsets['translations_length'] = $offsets['hash_addr'] - $offsets['translations_addr']; + + if ( $offsets['rev'] > 0 ) { + $this->error = 'Unsupported Revision.'; + return false; + } + + if ( $offsets['translations_addr'] > $file_length || $offsets['originals_addr'] > $file_length ) { + $this->error = 'Invalid Data.'; + return false; + } + + // Load the Originals. + $original_data = str_split( substr( $file_contents, $offsets['originals_addr'], $offsets['originals_length'] ), 8 ); + $translations_data = str_split( substr( $file_contents, $offsets['translations_addr'], $offsets['translations_length'] ), 8 ); + + foreach ( array_keys( $original_data ) as $i ) { + $o = unpack( "{$this->uint32}length/{$this->uint32}pos", $original_data[ $i ] ); + $t = unpack( "{$this->uint32}length/{$this->uint32}pos", $translations_data[ $i ] ); + + if ( false === $o || false === $t ) { + continue; + } + + $original = substr( $file_contents, $o['pos'], $o['length'] ); + $translation = substr( $file_contents, $t['pos'], $t['length'] ); + // GlotPress bug. + $translation = rtrim( $translation, "\0" ); + + // Metadata about the MO file is stored in the first translation entry. + if ( '' === $original ) { + foreach ( explode( "\n", $translation ) as $meta_line ) { + if ( '' === $meta_line ) { + continue; + } + + list( $name, $value ) = array_map( 'trim', explode( ':', $meta_line, 2 ) ); + + $this->headers[ strtolower( $name ) ] = $value; + } + } else { + $this->entries[ (string) $original ] = $translation; + } + } + + return true; + } + + /** + * Exports translation contents as a string. + * + * @return string Translation file contents. + */ + public function export(): string { + // Prefix the headers as the first key. + $headers_string = ''; + foreach ( $this->headers as $header => $value ) { + $headers_string .= "{$header}: $value\n"; + } + $entries = array_merge( array( '' => $headers_string ), $this->entries ); + $entry_count = count( $entries ); + + if ( false === $this->uint32 ) { + $this->uint32 = 'V'; + } + + $bytes_for_entries = $entry_count * 4 * 2; + // Pair of 32bit ints per entry. + $originals_addr = 28; /* header */ + $translations_addr = $originals_addr + $bytes_for_entries; + $hash_addr = $translations_addr + $bytes_for_entries; + $entry_offsets = $hash_addr; + + $file_header = pack( $this->uint32 . '*', self::MAGIC_MARKER, 0 /* rev */, $entry_count, $originals_addr, $translations_addr, 0 /* hash_length */, $hash_addr ); + + $o_entries = ''; + $t_entries = ''; + $o_addr = ''; + $t_addr = ''; + + foreach ( array_keys( $entries ) as $original ) { + $o_addr .= pack( $this->uint32 . '*', strlen( $original ), $entry_offsets ); + $entry_offsets += strlen( $original ) + 1; + $o_entries .= $original . pack( 'x' ); + } + + foreach ( $entries as $translations ) { + $t_addr .= pack( $this->uint32 . '*', strlen( $translations ), $entry_offsets ); + $entry_offsets += strlen( $translations ) + 1; + $t_entries .= $translations . pack( 'x' ); + } + + return $file_header . $o_addr . $t_addr . $o_entries . $t_entries; + } +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php new file mode 100644 index 0000000000000..ae1d451af445f --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php @@ -0,0 +1,111 @@ +parsed = true; + + $result = include $this->file; + if ( ! $result || ! is_array( $result ) ) { + $this->error = true; + return; + } + + 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; + } + } + unset( $result['messages'] ); + } + + $this->headers = array_change_key_case( $result ); + } + + /** + * Exports translation contents as a string. + * + * @return string Translation file contents. + */ + public function export(): string { + $data = array_merge( $this->headers, array( 'messages' => $this->entries ) ); + + return 'var_export( $data ) . ';' . PHP_EOL; + } + + /** + * Determines if the given array is a list. + * + * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. + * + * Polyfill for array_is_list() in PHP 8.1. + * + * @see https://github.com/symfony/polyfill-php81/tree/main + * + * @SuppressWarnings(PHPMD.UnusedLocalVariable) + * + * @codeCoverageIgnore + * + * @param array $arr The array being evaluated. + * @return bool True if array is a list, false otherwise. + */ + private function array_is_list( array $arr ): bool { + if ( function_exists( 'array_is_list' ) ) { + return array_is_list( $arr ); + } + + if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) { + return true; + } + + $next_key = -1; + + foreach ( $arr as $k => $v ) { + if ( ++$next_key !== $k ) { + return false; + } + } + + return true; + } + + /** + * Outputs or returns a parsable string representation of a variable. + * + * Like {@see var_export()} but "minified", using short array syntax + * and no newlines. + * + * @param mixed $value The variable you want to export. + * @return string The variable representation. + */ + private function var_export( $value ): string { + if ( ! is_array( $value ) ) { + return var_export( $value, true ); + } + + $entries = array(); + + $is_list = $this->array_is_list( $value ); + + foreach ( $value as $key => $val ) { + $entries[] = $is_list ? $this->var_export( $val ) : var_export( $key, true ) . '=>' . $this->var_export( $val ); + } + + return '[' . implode( ',', $entries ) . ']'; + } +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php new file mode 100644 index 0000000000000..7f15b0223e792 --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php @@ -0,0 +1,289 @@ + + */ + protected $headers = array(); + + /** + * Whether file has been parsed. + * + * @var bool + */ + protected $parsed = false; + + /** + * Error information. + * + * @var bool|string + */ + protected $error = false; + + /** + * File name. + * + * @var string + */ + protected $file = ''; + + /** + * Translation entries. + * + * @var array + */ + protected $entries = array(); + + /** + * Plural forms function. + * + * @var callable|null Plural forms. + */ + protected $plural_forms = null; + + /** + * Constructor. + * + * @param string $file File to load. + */ + protected function __construct( string $file ) { + $this->file = $file; + } + + /** + * Creates a new Ginger_MO_Translation_File instance for a given file. + * + * @param string $file File name. + * @param string|null $filetype Optional. File type. Default inferred from file name. + * @return false|Ginger_MO_Translation_File + * + * @phpstan-param 'mo'|'json'|'php'|null $filetype + */ + public static function create( string $file, string $filetype = null ) { + if ( ! is_readable( $file ) ) { + return false; + } + + if ( null === $filetype ) { + $pos = strrpos( $file, '.' ); + if ( false !== $pos ) { + $filetype = substr( $file, $pos + 1 ); + } + } + + switch ( $filetype ) { + case 'mo': + return new Ginger_MO_Translation_File_MO( $file ); + case 'php': + return new Ginger_MO_Translation_File_PHP( $file ); + case 'json': + if ( function_exists( 'json_decode' ) ) { + return new Ginger_MO_Translation_File_JSON( $file ); + } + break; + default: + return false; + } + + return false; + } + + /** + * Returns all headers. + * + * @return array Headers. + */ + public function headers() { + if ( ! $this->parsed ) { + $this->parse_file(); + } + return $this->headers; + } + + /** + * Returns all entries. + * + * @return array Entries. + * @phstan-return array> Entries. + */ + public function entries() { + if ( ! $this->parsed ) { + $this->parse_file(); + } + + return $this->entries; + } + + /** + * Returns the current error information. + * + * @phpstan-impure + * + * @return bool|string Error + */ + public function error() { + return $this->error; + } + + /** + * Returns the file name. + * + * @return string File name. + */ + public function get_file(): string { + return $this->file; + } + + /** + * Translates a given string. + * + * @param string $text String to translate. + * @return false|string Translation(s) on success, false otherwise. + */ + public function translate( string $text ) { + if ( ! $this->parsed ) { + $this->parse_file(); + } + + return $this->entries[ $text ] ?? false; + } + + /** + * Returns the plural form for a count. + * + * @param int $number Count. + * @return int Plural form. + */ + public function get_plural_form( int $number ): int { + if ( ! $this->parsed ) { + $this->parse_file(); + } + + // In case a plural form is specified as a header, but no function included, build one. + if ( null === $this->plural_forms && isset( $this->headers['plural-forms'] ) ) { + $this->plural_forms = $this->make_plural_form_function( $this->headers['plural-forms'] ); + } + + if ( is_callable( $this->plural_forms ) ) { + /** + * Plural form. + * + * @phpstan-var int $result Plural form. + */ + $result = call_user_func( $this->plural_forms, $number ); + return $result; + } + + // Default plural form matches English, only "One" is considered singular. + return ( 1 === $number ? 0 : 1 ); + } + + /** + * Makes a function, which will return the right translation index, according to the + * plural forms header + * + * @param string $expression Plural form expression. + * @return callable(int $num): int Plural forms function. + */ + public function make_plural_form_function( string $expression ) { + try { + $handler = new Plural_Forms( rtrim( $expression, ';' ) ); + return array( $handler, 'get' ); + } catch ( Exception $e ) { + // Fall back to default plural-form function. + return $this->make_plural_form_function( 'n != 1' ); + } + } + + /** + * Creates a new Ginger_MO_Translation_File instance for a given file. + * + * @param string $file Source file name. + * @param string $filetype Desired target file type. + * @return string|false Transformed translation file contents on success, false otherwise. + * + * @phpstan-param 'mo'|'json'|'php' $filetype + */ + public static function transform( string $file, string $filetype ) { + $destination = null; + + $source = self::create( $file ); + + if ( false === $source ) { + return false; + } + + switch ( $filetype ) { + case 'mo': + $destination = new Ginger_MO_Translation_File_MO( '' ); + break; + case 'php': + $destination = new Ginger_MO_Translation_File_PHP( '' ); + break; + case 'json': + if ( function_exists( 'json_decode' ) ) { + $destination = new Ginger_MO_Translation_File_JSON( '' ); + } + break; + default: + return false; + } + + if ( null === $destination ) { + return false; + } + + $success = $destination->import( $source ); + + if ( ! $success ) { + return false; + } + + return $destination->export(); + } + + /** + * Parses the file. + * + * @return void + */ + protected function parse_file() { + // Needs to be implemented in child classes. + // Not abstract because it's used in this class. + } + + /** + * Imports translations from another file. + * + * @param Ginger_MO_Translation_File $source Source file. + * @return bool True on success, false otherwise. + */ + protected function import( Ginger_MO_Translation_File $source ): bool { + if ( false !== $source->error() ) { + return false; + } + + $this->headers = $source->headers(); + $this->entries = $source->entries(); + $this->error = $source->error(); + + return false === $this->error; + } + + /** + * Exports translation contents as a string. + * + * @return string Translation file contents. + */ + abstract public function export(): string; +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php new file mode 100644 index 0000000000000..9aade2f29e8f0 --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php @@ -0,0 +1,141 @@ + $headers + * @property-read array $entries + */ +class Ginger_MO_Translations { + /** + * Text domain. + * + * @var string + */ + protected $textdomain = 'default'; + + /** + * Constructor. + * + * @param string $textdomain Text domain. + */ + public function __construct( string $textdomain = 'default' ) { + $this->textdomain = $textdomain; + } + + /** + * Magic getter for backward compatibility. + * + * @param string $name Property name. + * @return mixed + */ + public function __get( string $name ) { + if ( 'entries' === $name ) { + $entries = Ginger_MO::instance()->get_entries( $this->textdomain ); + + $result = array(); + + foreach ( $entries as $original => $translations ) { + $result[] = $this->make_entry( $original, $translations ); + } + + return $result; + } + + if ( 'headers' === $name ) { + return Ginger_MO::instance()->get_headers( $this->textdomain ); + } + + return null; + } + + /** + * Magic setter. + * + * @param string $name Property name. + * @param mixed $value Property value. + * @return void + */ + public function __set( string $name, $value ) {} + + /** + * Build a Translation_Entry from original string and translation strings. + * + * @see MO::make_entry() + * + * @param string $original Original string to translate from MO file. Might contain + * 0x04 as context separator or 0x00 as singular/plural separator. + * @param string $translations Translation strings from MO file. + * @return Translation_Entry Entry instance. + */ + private function make_entry( $original, $translations ): Translation_Entry { + $entry = new Translation_Entry(); + + // Look for context, separated by \4. + $parts = explode( "\4", $original ); + if ( isset( $parts[1] ) ) { + $original = $parts[1]; + $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->translations = explode( "\0", $translations ); + return $entry; + } + + /** + * Translates a plural string. + * + * @param string|null $singular Singular string. + * @param string|null $plural Plural string. + * @param int|float $count Count. Should be an integer, but some plugins pass floats. + * @param string|null $context Context. + * @return string|null Translation if it exists, or the unchanged singular string. + */ + public function translate_plural( $singular, $plural, $count = 1, $context = '' ) { + if ( null === $singular || null === $plural ) { + return $singular; + } + + $translation = Ginger_MO::instance()->translate_plural( array( $singular, $plural ), (int) $count, (string) $context, $this->textdomain ); + if ( false !== $translation ) { + return $translation; + } + + // Fall back to the original with English grammar rules. + return ( 1 === $count ? $singular : $plural ); + } + + /** + * Translates a singular string. + * + * @param string|null $singular Singular string. + * @param string|null $context Context. + * @return string|null Translation if it exists, or the unchanged singular string + */ + public function translate( $singular, $context = '' ) { + if ( null === $singular ) { + return $singular; + } + + $translation = Ginger_MO::instance()->translate( $singular, (string) $context, $this->textdomain ); + if ( false !== $translation ) { + return $translation; + } + + // Fall back to the original. + return $singular; + } +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo.php new file mode 100644 index 0000000000000..4b7c296647928 --- /dev/null +++ b/src/wp-includes/ginger-mo/class-ginger-mo.php @@ -0,0 +1,376 @@ + [ Textdomain => [ .., .. ] ] ] + * + * @var array> + */ + protected $loaded_translations = array(); + + /** + * List of loaded translation files. + * + * [ Filename => [ Locale => [ Textdomain => Ginger_MO_Translation_File ] ] ] + * + * @var array>> + */ + protected $loaded_files = array(); + + /** + * Returns the Ginger_MO singleton. + * + * @return Ginger_MO + */ + public static function instance(): Ginger_MO { + static $instance; + + if ( ! $instance ) { + $instance = new Ginger_MO(); + } + + return $instance; + } + + /** + * Returns the current locale. + * + * @return string Locale. + */ + public function get_locale(): string { + return $this->current_locale; + } + + /** + * Sets the current locale. + * + * @param string $locale Locale. + * @return void + */ + public function set_locale( string $locale ) { + $this->current_locale = $locale; + } + + /** + * Loads a translation file. + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * + * @param string $translation_file Translation file. + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return bool True on success, false otherwise. + */ + public function load( string $translation_file, string $textdomain = 'default', string $locale = null ): bool { + if ( null === $locale ) { + $locale = $this->current_locale; + } + + $translation_file = realpath( $translation_file ); + + if ( false === $translation_file ) { + return false; + } + + if ( + isset( $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] ) && + false !== $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] + ) { + return false === $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ]->error(); + } + + if ( + isset( $this->loaded_files[ $translation_file ][ $locale ] ) && + array() !== $this->loaded_files[ $translation_file ][ $locale ] + ) { + $moe = reset( $this->loaded_files[ $translation_file ][ $locale ] ); + } else { + $moe = Ginger_MO_Translation_File::create( $translation_file ); + if ( false === $moe || false !== $moe->error() ) { + $moe = false; + } + } + + $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] = $moe; + + if ( ! $moe instanceof Ginger_MO_Translation_File ) { + return false; + } + + if ( ! isset( $this->loaded_translations[ $locale ][ $textdomain ] ) ) { + $this->loaded_translations[ $locale ][ $textdomain ] = array(); + } + + // Ensure that last-loaded translation takes precedence. + array_unshift( $this->loaded_translations[ $locale ][ $textdomain ], $moe ); + + return true; + } + + /** + * Unload all translation files or a specific one for a given text domain. + * + * @SuppressWarnings(PHPMD.NPathComplexity) + * + * @param string $textdomain Text domain. + * @param Ginger_MO_Translation_File|string $mo Translation file instance or file name. + * @param string $locale Optional. Locale. Default all locales. + * @return bool True on success, false otherwise. + */ + public function unload( string $textdomain = 'default', $mo = null, string $locale = null ): bool { + if ( ! $this->is_loaded( $textdomain, $locale ) ) { + return false; + } + + if ( null !== $mo ) { + if ( null !== $locale ) { + foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { + if ( $mo === $moe || $mo === $moe->get_file() ) { + unset( $this->loaded_translations[ $locale ][ $textdomain ][ $i ] ); + unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); + return true; + } + } + + return true; + } + + foreach ( $this->loaded_translations as $l => $domains ) { + foreach ( $domains[ $textdomain ] as $i => $moe ) { + if ( $mo === $moe || $mo === $moe->get_file() ) { + unset( $this->loaded_translations[ $l ][ $textdomain ][ $i ] ); + unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); + return true; + } + } + } + + return true; + } + + if ( null !== $locale ) { + foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $moe ) { + unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); + } + + unset( $this->loaded_translations[ $locale ][ $textdomain ] ); + + return true; + } + + foreach ( $this->loaded_translations as $l => $domains ) { + if ( ! isset( $domains[ $textdomain ] ) ) { + continue; + } + + foreach ( $domains[ $textdomain ] as $moe ) { + unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); + } + + unset( $this->loaded_translations[ $l ][ $textdomain ] ); + } + + return true; + } + + /** + * Determines whether translations are loaded for a given text domain. + * + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return bool True if there are any loaded translations, false otherwise. + */ + public function is_loaded( string $textdomain = 'default', string $locale = null ): bool { + if ( null === $locale ) { + $locale = $this->current_locale; + } + + return isset( $this->loaded_translations[ $locale ][ $textdomain ] ) && + array() !== $this->loaded_translations[ $locale ][ $textdomain ]; + } + + /** + * Translates a singular string. + * + * @param string $text Text to translate. + * @param string $context Optional. Context for the string. + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return string|false Translation on success, false otherwise. + */ + public function translate( string $text, string $context = '', string $textdomain = 'default', string $locale = null ) { + if ( '' !== $context ) { + $context .= "\4"; + } + + $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale ); + + if ( false === $translation ) { + return false; + } + + return $translation['entries'][0]; + } + + /** + * Translates plurals. + * + * Checks both singular+plural combinations as well as just singulars, + * in case the translation file does not store the plural. + * + * @todo Revisit this. + * + * @param array{0: string, 1: string} $plurals Pair of singular and plural translation. + * @param int $number Number of items. + * @param string $context Optional. Context for the string. + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return string|false Translation on success, false otherwise. + */ + public function translate_plural( array $plurals, int $number, string $context = '', string $textdomain = 'default', string $locale = null ) { + if ( '' !== $context ) { + $context .= "\4"; + } + + $text = implode( "\0", $plurals ); + $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale ); + + if ( false === $translation ) { + $text = $plurals[0]; + $translation = $this->locate_translation( "{$context}{$text}", $textdomain, $locale ); + + if ( false === $translation ) { + return false; + } + } + + /* @var Ginger_MO_Translation_File $source */ + $source = $translation['source']; + $num = $source->get_plural_form( $number ); + + // TODO: Use nplurals from Plural-Forms header? + // See \Translations::translate_plural() in core. + + return $translation['entries'][ $num ] ?? $translation['entries'][0]; + } + + /** + * Returns all existing headers for a given text domain. + * + * @param string $textdomain Text domain. + * @return array Headers. + */ + public function get_headers( string $textdomain = 'default' ): array { + if ( array() === $this->loaded_translations ) { + return array(); + } + + $headers = array(); + + foreach ( $this->get_files( $textdomain ) as $moe ) { + foreach ( $moe->headers() as $header => $value ) { + $headers[ $this->normalize_header( $header ) ] = $value; + } + } + + return $headers; + } + + /** + * Normalizes header names to be capitalized. + * + * @param string $header Header name. + * @return string Normalized header name. + */ + protected function normalize_header( string $header ): string { + $parts = explode( '-', $header ); + $parts = array_map( 'ucfirst', $parts ); + return implode( '-', $parts ); + } + + /** + * Returns all entries for a given text domain. + * + * @param string $textdomain Text domain. + * @return array Entries. + */ + public function get_entries( string $textdomain = 'default' ): array { + if ( array() === $this->loaded_translations ) { + return array(); + } + + $entries = array(); + + foreach ( $this->get_files( $textdomain ) as $moe ) { + $entries = array_merge( $entries, $moe->entries() ); + } + + return $entries; + } + + /** + * Locates translation for a given string and text domain. + * + * @param string $singular Singular translation. + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return array{source: Ginger_MO_Translation_File, entries: string[]}|false Translations on success, false otherwise. + */ + protected function locate_translation( string $singular, string $textdomain = 'default', string $locale = null ) { + if ( array() === $this->loaded_translations ) { + return false; + } + + // Find the translation in all loaded files for this text domain. + foreach ( $this->get_files( $textdomain, $locale ) as $moe ) { + $translation = $moe->translate( $singular ); + if ( false !== $translation ) { + return array( + 'entries' => explode( "\0", $translation ), + 'source' => $moe, + ); + } + if ( false !== $moe->error() ) { + // Unload this file, something is wrong. + $this->unload( $textdomain, $moe, $locale ); + } + } + + // Nothing could be found. + return false; + } + + /** + * Returns all translation files for a given text domain. + * + * @param string $textdomain Text domain. + * @param string $locale Optional. Locale. Default current locale. + * @return Ginger_MO_Translation_File[] List of translation files. + */ + protected function get_files( string $textdomain = 'default', string $locale = null ): array { + if ( null === $locale ) { + $locale = $this->current_locale; + } + + return $this->loaded_translations[ $locale ][ $textdomain ] ?? array(); + } +} diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index a085d4483f827..03eac8f41d634 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -707,6 +707,7 @@ function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) * @global MO[] $l10n An array of all currently loaded text domains. * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again. * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. + * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $domain Text domain. Unique identifier for retrieving translated strings. * @param string $mofile Path to the .mo file. @@ -715,7 +716,7 @@ function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) */ function load_textdomain( $domain, $mofile, $locale = null ) { /** @var WP_Textdomain_Registry $wp_textdomain_registry */ - global $l10n, $l10n_unloaded, $wp_textdomain_registry; + global $l10n, $l10n_unloaded, $wp_textdomain_registry, $wp_filesystem; $l10n_unloaded = (array) $l10n_unloaded; @@ -788,22 +789,109 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $locale = determine_locale(); } - $mo = new MO(); - if ( ! $mo->import_from_file( $mofile ) ) { - $wp_textdomain_registry->set( $domain, $locale, false ); + // Ensures the correct locale is set as the current one, in case it was filtered. + Ginger_MO::instance()->set_locale( $locale ); - return false; + /** + * Filters the preferred file format for translation files. + * + * @since 6.5.0 + * + * @param string $convert Preferred file format. Possible values: 'php', 'mo', 'json'. Default: 'php'. + */ + $preferred_format = apply_filters( 'translation_file_format', 'php' ); + if ( ! in_array( $preferred_format, array( 'php', 'mo', 'json' ), true ) ) { + $preferred_format = 'php'; } - if ( isset( $l10n[ $domain ] ) ) { - $mo->merge_with( $l10n[ $domain ] ); + $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $mofile ); + + if ( 'mo' !== $preferred_format ) { + + /** This filter is documented in wp-includes/l10n.php */ + $mofile_preferred = apply_filters( 'load_textdomain_mofile', $mofile_preferred, $domain ); + + /** + * Filters the file path for loading translations for the given text domain. + * + * The file could be an MO, JSON, or PHP file. + * + * @since 6.5.0 + * + * @param string $file Path to the translation file to load. + * @param string $domain The text domain. + */ + $mofile_preferred = apply_filters( 'load_translation_file', $mofile_preferred, $domain ); + + $success = Ginger_MO::instance()->load( $mofile_preferred, $domain, $locale ); + + if ( $success ) { + if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { + Ginger_MO::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + } + + // Unset Noop_Translations reference in get_translations_for_domain. + unset( $l10n[ $domain ] ); + $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); + + $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); + + return true; + } } - unset( $l10n_unloaded[ $domain ] ); + /** This action is documented in wp-includes/l10n.php */ + do_action( 'load_textdomain', $domain, $mofile ); + + /** This filter is documented in wp-includes/l10n.php */ + $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain ); + + /** This filter is documented in wp-includes/l10n.php */ + $mofile = apply_filters( 'load_translation_file', $mofile, $domain ); + + $success = Ginger_MO::instance()->load( $mofile, $domain, $locale ); - $l10n[ $domain ] = &$mo; + if ( $success ) { + if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { + Ginger_MO::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + } - $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); + // Unset Noop_Translations reference in get_translations_for_domain. + unset( $l10n[ $domain ] ); + + $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); + + $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); + + /** + * Filters whether existing MO files should be automatically converted to the preferred format. + * + * Only runs when no corresponding PHP or JSON translation file exists yet. + * + * The preferred format is determined by the {@see 'translation_file_format'} filter + * + * Useful for testing/debugging. + * + * @param bool $convert Whether to convert MO files to PHP or JSON files. Default true. + */ + $convert = apply_filters( 'convert_translation_files', true ); + + if ( 'mo' !== $preferred_format && $convert ) { + $contents = Ginger_MO_Translation_File::transform( $mofile, $preferred_format ); + + if ( false !== $contents ) { + if ( ! function_exists( 'WP_Filesystem' ) ) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + } + + if ( true === WP_Filesystem() ) { + $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE ); + } else { + file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents + } + } + } + } return true; } @@ -867,7 +955,8 @@ function unload_textdomain( $domain, $reloadable = false ) { unset( $l10n[ $domain ] ); if ( ! $reloadable ) { - $l10n_unloaded[ $domain ] = true; + // Since we support multiple locales, we don't actually need to unload reloadable text domains. + return Ginger_MO::instance()->unload( $domain ); } return true; diff --git a/src/wp-settings.php b/src/wp-settings.php index 528f335cb7c2a..845effcfe889a 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -114,6 +114,12 @@ require ABSPATH . WPINC . '/class-wp.php'; require ABSPATH . WPINC . '/class-wp-error.php'; require ABSPATH . WPINC . '/pomo/mo.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translations.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-mo.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-json.php'; +require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-php.php'; /** * @global wpdb $wpdb WordPress database abstraction object. @@ -604,6 +610,8 @@ $GLOBALS['wp_locale_switcher'] = new WP_Locale_Switcher(); $GLOBALS['wp_locale_switcher']->init(); +Ginger_MO::instance()->set_locale( $locale ); + // Load the functions for the active theme, for both parent and child theme if applicable. foreach ( wp_get_active_and_valid_themes() as $theme ) { if ( file_exists( $theme . '/functions.php' ) ) { diff --git a/tests/phpunit/data/languages/admin-es_ES.mo.php b/tests/phpunit/data/languages/admin-es_ES.mo.php new file mode 100644 index 0000000000000..b9b1f126ddf4d --- /dev/null +++ b/tests/phpunit/data/languages/admin-es_ES.mo.php @@ -0,0 +1,2 @@ +'2016-10-25 18:29+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Administration','language'=>'es_ES','messages'=>['Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.'=>'Las consultas de comentarios ahora tiene una caché que mejora el rendimiento. Nuevos argumentos en %s hacen que sea más fácil crear consultas robustas.','Comment query improvements'=>'Mejoras en las consultas de comentarios','New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.'=>'Ahora los objetos %1$s, %2$s y %3$s hacen que interactuar con términos, comentarios y redes sea más predecible y que el código sea más intuitivo.','Term, comment, and network objects'=>'Objetos de término, comentario y red','Thank you for updating! WordPress %s makes your site more connected and responsive.'=>'¡Gracias por actualizar! WordPress %s hace que tu sitio esté más conectado y sea más adaptable.']]; diff --git a/tests/phpunit/data/languages/de_DE.mo.php b/tests/phpunit/data/languages/de_DE.mo.php new file mode 100644 index 0000000000000..7905f5905aec6 --- /dev/null +++ b/tests/phpunit/data/languages/de_DE.mo.php @@ -0,0 +1,2 @@ +'2019-03-28 19:42+0300','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 2.2.1','project-id-version'=>'Development (5.2.x)','language'=>'de_DE','pot-creation-date'=>'','last-translator'=>'','language-team'=>'','messages'=>['Update %s now'=>'Jetzt %s aktualisieren','[%1$s] Confirm Action: %2$s'=>'[%1$s] Aktion bestätigen: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Löschauftrag ausgeführt','[%s] Personal Data Export'=>'[%s] Export personenbezogener Daten','html_lang_attribute'=>'de-DE','number_format_decimal_point'=>',','number_format_thousands_sep'=>'.','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/es_ES.mo.php b/tests/phpunit/data/languages/es_ES.mo.php new file mode 100644 index 0000000000000..76bbb0878bb2a --- /dev/null +++ b/tests/phpunit/data/languages/es_ES.mo.php @@ -0,0 +1,2 @@ +'2020-07-23 21:12+0300','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 2.3','project-id-version'=>'Development (5.2.x)','language'=>'es_ES','last-translator'=>'','language-team'=>'','messages'=>['ERROR: Sorry, that username is not allowed.'=>'ERROR: Lo siento, ese nombre de usuario no está permitido.','Invalid parameter.'=>'Parámetro no válido. ','[%1$s] Confirm Action: %2$s'=>'[%1$s] Confirmar la acción: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Solicitud de borrado completada','[%s] Personal Data Export'=>'[%s] Exportación de datos personales','menu(Currently set to: %s)'=>'(Actualmente fijado en: %s)','menu location(Current: %s)'=>'(Actual: %s)','text directionltr'=>'ltr']]; From 227a4223a2409640cb59e2e98fa037a0ea688498 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 3 Oct 2023 14:42:19 +0200 Subject: [PATCH 02/65] Fix conditionals --- src/wp-includes/l10n.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 03eac8f41d634..2ceaa1cabf760 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -806,7 +806,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $mofile ); - if ( 'mo' !== $preferred_format ) { + if ( 'mo' !== $preferred_format || str_ends_with( $mofile, $preferred_format ) ) { /** This filter is documented in wp-includes/l10n.php */ $mofile_preferred = apply_filters( 'load_textdomain_mofile', $mofile_preferred, $domain ); @@ -876,7 +876,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { */ $convert = apply_filters( 'convert_translation_files', true ); - if ( 'mo' !== $preferred_format && $convert ) { + if ( 'mo' !== $preferred_format && $convert && str_ends_with( $mofile, '.mo' ) ) { $contents = Ginger_MO_Translation_File::transform( $mofile, $preferred_format ); if ( false !== $contents ) { From 17a5c6ee3ec8a65a7467e23af9c3bf5783c08d16 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 3 Oct 2023 14:42:23 +0200 Subject: [PATCH 03/65] Add realpath --- src/wp-includes/ginger-mo/class-ginger-mo.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/wp-includes/ginger-mo/class-ginger-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo.php index 4b7c296647928..2bb26b5bdedaa 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo.php @@ -142,6 +142,8 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca } if ( null !== $mo ) { + $mo = realpath( $mo ); + if ( null !== $locale ) { foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { if ( $mo === $moe || $mo === $moe->get_file() ) { From 4fadb695ef004f067874b818b7a22ed52711fd9f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 3 Oct 2023 15:03:58 +0200 Subject: [PATCH 04/65] Add PHPUnit tests --- src/wp-includes/functions.php | 2 +- src/wp-includes/l10n.php | 4 +- .../data/ginger-mo/example-simple.json | 19 + .../phpunit/data/ginger-mo/example-simple.mo | Bin 0 -> 373 bytes .../phpunit/data/ginger-mo/example-simple.php | 10 + .../phpunit/data/ginger-mo/example-simple.po | 23 + tests/phpunit/data/ginger-mo/fa_IR.mo | Bin 0 -> 541 bytes tests/phpunit/data/ginger-mo/plural.mo | Bin 0 -> 268 bytes tests/phpunit/data/ginger-mo/simple.mo | Bin 0 -> 202 bytes .../phpunit/data/languages/admin-en_GB.mo.php | 2 + tests/phpunit/data/languages/en_GB.mo.php | 2 + tests/phpunit/data/languages/ja_JP.mo.php | 2 + .../internationalized-plugin-de_DE.mo.php | 2 + .../internationalized-plugin-es_ES.mo.php | 2 + .../languages/plugins/notice-pl_PL.mo.php | 2 + .../internationalized-theme-de_DE.mo.php | 2 + ...stom-internationalized-plugin-de_DE.mo.php | 2 + ...stom-internationalized-plugin-es_ES.mo.php | 2 + tests/phpunit/data/pomo/plural.mo.php | 2 + tests/phpunit/data/pomo/simple.mo.php | 3 + .../languages/de_DE.mo.php | 2 + .../languages/es_ES.mo.php | 2 + tests/phpunit/includes/abstract-testcase.php | 15 +- .../class-dummy-language-pack-upgrader.php | 74 ++ .../includes/class-dummy-plugin-upgrader.php | 74 ++ .../includes/class-dummy-upgrader-skin.php | 33 + tests/phpunit/tests/l10n/gingerMo.php | 296 ++++++++ tests/phpunit/tests/l10n/gingerMoConvert.php | 572 ++++++++++++++ .../tests/l10n/gingerMoIntegration.php | 696 ++++++++++++++++++ 29 files changed, 1840 insertions(+), 5 deletions(-) create mode 100644 tests/phpunit/data/ginger-mo/example-simple.json create mode 100644 tests/phpunit/data/ginger-mo/example-simple.mo create mode 100644 tests/phpunit/data/ginger-mo/example-simple.php create mode 100644 tests/phpunit/data/ginger-mo/example-simple.po create mode 100644 tests/phpunit/data/ginger-mo/fa_IR.mo create mode 100644 tests/phpunit/data/ginger-mo/plural.mo create mode 100644 tests/phpunit/data/ginger-mo/simple.mo create mode 100644 tests/phpunit/data/languages/admin-en_GB.mo.php create mode 100644 tests/phpunit/data/languages/en_GB.mo.php create mode 100644 tests/phpunit/data/languages/ja_JP.mo.php create mode 100644 tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php create mode 100644 tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php create mode 100644 tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php create mode 100644 tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php create mode 100644 tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php create mode 100644 tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php create mode 100644 tests/phpunit/data/pomo/plural.mo.php create mode 100644 tests/phpunit/data/pomo/simple.mo.php create mode 100644 tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php create mode 100644 tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php create mode 100644 tests/phpunit/includes/class-dummy-language-pack-upgrader.php create mode 100644 tests/phpunit/includes/class-dummy-plugin-upgrader.php create mode 100644 tests/phpunit/includes/class-dummy-upgrader-skin.php create mode 100644 tests/phpunit/tests/l10n/gingerMo.php create mode 100644 tests/phpunit/tests/l10n/gingerMoConvert.php create mode 100644 tests/phpunit/tests/l10n/gingerMoIntegration.php diff --git a/src/wp-includes/functions.php b/src/wp-includes/functions.php index 4b6bc14acfdc7..32c078c0379bf 100644 --- a/src/wp-includes/functions.php +++ b/src/wp-includes/functions.php @@ -6531,7 +6531,7 @@ function wp_timezone_choice( $selected_zone, $locale = null ) { if ( ! $mo_loaded || $locale !== $locale_loaded ) { $locale_loaded = $locale ? $locale : get_locale(); $mofile = WP_LANG_DIR . '/continents-cities-' . $locale_loaded . '.mo'; - unload_textdomain( 'continents-cities' ); + unload_textdomain( 'continents-cities', true ); load_textdomain( 'continents-cities', $mofile, $locale_loaded ); $mo_loaded = true; } diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 2ceaa1cabf760..896a347f94800 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -954,8 +954,8 @@ function unload_textdomain( $domain, $reloadable = false ) { unset( $l10n[ $domain ] ); + // Since we support multiple locales, we don't actually need to unload reloadable text domains. if ( ! $reloadable ) { - // Since we support multiple locales, we don't actually need to unload reloadable text domains. return Ginger_MO::instance()->unload( $domain ); } @@ -984,7 +984,7 @@ function load_default_textdomain( $locale = null ) { } // Unload previously loaded strings so we can switch translations. - unload_textdomain( 'default' ); + unload_textdomain( 'default', true ); $return = load_textdomain( 'default', WP_LANG_DIR . "/$locale.mo", $locale ); diff --git a/tests/phpunit/data/ginger-mo/example-simple.json b/tests/phpunit/data/ginger-mo/example-simple.json new file mode 100644 index 0000000000000..c775a4302f267 --- /dev/null +++ b/tests/phpunit/data/ginger-mo/example-simple.json @@ -0,0 +1,19 @@ +{ + "domain": "unittest", + "locale_data": { + "unittest": { + "": { + "domain": "unittest", + "plural-forms": "nplurals=2; plural=n != 1;", + "lang": "de" + }, + "original": "translation", + "context\u0004original with context": ["translation with context"], + "plural0\u0000plural1": ["translation0", "translation1"], + "context\u0004plural0 with context\u0000plural1 with context": [ + "translation0 with context", + "translation1 with context" + ] + } + } +} diff --git a/tests/phpunit/data/ginger-mo/example-simple.mo b/tests/phpunit/data/ginger-mo/example-simple.mo new file mode 100644 index 0000000000000000000000000000000000000000..73fb5f21ab3baaece60f8b000a54f947b51bda07 GIT binary patch literal 373 zcmZ9Gy$ZrG6h@;|5FG?3MTP7okhE5$bN7m?Q;?#8*h(9J-oe?&arF@#eHK5pO4yL}LG-?$^j9BGaNYp{E}#u=pa<^21rIO)%?2SpJc7G$3=iQIOx9Uy zlCyELTy0b;H9aY_rDgY@?)4#;npeYbQ&gM3Ii&njT98G|mdQ~SDoa@;O%hU%iy7mB o@qmhXI1NJIa|P$TLrgByLQ8|U)#+AHTgFOi8(E1xnA;aYUSkDiOaK4? literal 0 HcmV?d00001 diff --git a/tests/phpunit/data/ginger-mo/example-simple.php b/tests/phpunit/data/ginger-mo/example-simple.php new file mode 100644 index 0000000000000..e9c2f73ad6705 --- /dev/null +++ b/tests/phpunit/data/ginger-mo/example-simple.php @@ -0,0 +1,10 @@ + + [ + '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'], + ], +]; diff --git a/tests/phpunit/data/ginger-mo/example-simple.po b/tests/phpunit/data/ginger-mo/example-simple.po new file mode 100644 index 0000000000000..b40fcda69bda4 --- /dev/null +++ b/tests/phpunit/data/ginger-mo/example-simple.po @@ -0,0 +1,23 @@ +msgid "" +msgstr "" +"PO-Revision-Date: 2016-01-05 18:45:32+1000\n" + +msgid "original" +msgstr "translation" + +msgctxt "context" +msgid "original with context" +msgstr "translation with context" + +msgid "plural0" +msgid_plural "plural1" +msgstr[0] "translation0" +msgstr[1] "translation1" + +msgctxt "context" +msgid "plural0 with context" +msgid_plural "plural1 with context" +msgstr[0] "translation0 with context" +msgstr[1] "translation1 with context" + + diff --git a/tests/phpunit/data/ginger-mo/fa_IR.mo b/tests/phpunit/data/ginger-mo/fa_IR.mo new file mode 100644 index 0000000000000000000000000000000000000000..19f165658ea5cfe2930d9f923e38811053e93a0c GIT binary patch literal 541 zcmYk2K}#D!6vxLJDJ+FTp`LnpJqSXXbvG#`Y??zcDuQW4iER&F#_UU6+01Nb#;7+1 z(HwH^xksT0ON*C&iJoT_JoJ2{B_>hQ zfK(PxitQ9*pY@XzEAQeM-s3*&Xtj$y%hzN633WLg!Dpr8jz(jE!yCWmACR_igsI9( z53loq|9_gdgvAJ|LAAz%I(cWhE^c^6p_qCKeCuQkIHDHlVj7v_ae4?5^_q{aoqQN6lAcPsNEG zL{Q&OXyJ5Ei?6x$`umRJw88o~vd<`Y)>7pHlablt7hhF)kF2A0*{3>_n x#Tj!yi*a#UjNPUC@%Ly_oVjx-j@@s%Jb~hr+Bu!M&!}xL5H^D+FqKiwKgbtYvEPlMGS?pg+ZRQ#en zS{RD#3SQM-BWBxt&s(RpvV%z*J%SIsLSq#_M1%YcMfMo^2yQYC0+!BVz+A)ty$Hd| OTtc5RH3'2016-10-25 18:29+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Administration','language'=>'en_GB','messages'=>['Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.'=>'Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.','Comment query improvements'=>'Comment query improvements','New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.'=>'New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.','Term, comment, and network objects'=>'Term, comment, and network objects','Thank you for updating! WordPress %s makes your site more connected and responsive.'=>'Thank you for updating! WordPress %s makes your site more connected and responsive.']]; diff --git a/tests/phpunit/data/languages/en_GB.mo.php b/tests/phpunit/data/languages/en_GB.mo.php new file mode 100644 index 0000000000000..444fe6c6609ff --- /dev/null +++ b/tests/phpunit/data/languages/en_GB.mo.php @@ -0,0 +1,2 @@ +'2016-10-26 00:01+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Development (4.4.x)','language'=>'en_GB','messages'=>['ERROR: Sorry, that username is not allowed.'=>'ERROR: Sorry, that username is not allowed.','Invalid parameter.'=>'Invalid parameter.','menu(Currently set to: %s)'=>'(Currently set to: %s)','menu location(Current: %s)'=>'(Current: %s)','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/ja_JP.mo.php b/tests/phpunit/data/languages/ja_JP.mo.php new file mode 100644 index 0000000000000..37751a8594a4d --- /dev/null +++ b/tests/phpunit/data/languages/ja_JP.mo.php @@ -0,0 +1,2 @@ +'2018-04-21 18:27+0900','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'5.0.x','language'=>'ja_JP','messages'=>['Update %s now'=>'今すぐ %s を更新','Word count type. Do not translate!words'=>'characters_including_spaces','comment_excerpt_length20'=>'40','draft_length10'=>'40','excerpt_length55'=>'110','html_lang_attribute'=>'ja','number_format_decimal_point'=>'number_format_decimal_point','number_format_thousands_sep'=>'number_format_thousands_sep','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php b/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php new file mode 100644 index 0000000000000..4d41f632ba13e --- /dev/null +++ b/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2020-10-20 17:11+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','language-team'=>'','last-translator'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Das ist ein Dummy Plugin']]; diff --git a/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php b/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php new file mode 100644 index 0000000000000..0795ed3e99812 --- /dev/null +++ b/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2020-10-20 17:12+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','language-team'=>'','last-translator'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Este es un plugin dummy']]; diff --git a/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php b/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php new file mode 100644 index 0000000000000..b1e6fb74354e4 --- /dev/null +++ b/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2021-08-11 12:25+0200','language'=>'pl_PL','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 3.0','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','last-translator'=>'','language-team'=>'','x-poedit-searchpath-0'=>'.','messages'=>['block descriptionShows warning, error or success notices…'=>'Wyświetla ostrzeżenie, błąd lub powiadomienie o sukcesie…','block keywordalert'=>'ostrzeżenie','block keywordmessage'=>'wiadomość','block style labelDefault'=>'Domyślny','block style labelOther'=>'Inny','block titleNotice'=>'Powiadomienie','block variation descriptionShows error.'=>'Wyświetla błąd.','block variation keywordfailure'=>'niepowodzenie','block variation titleError'=>'Błąd']]; diff --git a/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php b/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php new file mode 100644 index 0000000000000..db405ba9fadaf --- /dev/null +++ b/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2020-10-20 17:11+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','last-translator'=>'','language-team'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Das ist ein Dummy Theme']]; diff --git a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php new file mode 100644 index 0000000000000..c52a82df306ea --- /dev/null +++ b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2022-07-04 18:48+0200','last-translator'=>'Dominik Schilling','language-team'=>'','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Das ist ein Dummy Plugin']]; diff --git a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php new file mode 100644 index 0000000000000..9da781b6d10cc --- /dev/null +++ b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2022-07-04 18:48+0200','last-translator'=>'Dominik Schilling','language-team'=>'','language'=>'es_ES','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Este es un plugin dummy']]; diff --git a/tests/phpunit/data/pomo/plural.mo.php b/tests/phpunit/data/pomo/plural.mo.php new file mode 100644 index 0000000000000..8ad4b285b1638 --- /dev/null +++ b/tests/phpunit/data/pomo/plural.mo.php @@ -0,0 +1,2 @@ +'text/plain; charset=UTF-8','plural-forms'=>'nplurals=5; plural=n != 1;','messages'=>['one dragon' . "\0" . '%d dragons'=>'oney dragoney' . "\0" . 'twoey dragoney' . "\0" . 'manyey dragoney' . "\0" . 'manyeyey dragoney' . "\0" . 'manyeyeyey dragoney']]; diff --git a/tests/phpunit/data/pomo/simple.mo.php b/tests/phpunit/data/pomo/simple.mo.php new file mode 100644 index 0000000000000..078c8a95cb6f8 --- /dev/null +++ b/tests/phpunit/data/pomo/simple.mo.php @@ -0,0 +1,3 @@ +'WordPress 2.6-bleeding','report-msgid-bugs-to'=>'wp-polyglots@lists.automattic.com','messages'=>['baba'=>'dyado','kuku +ruku'=>'yes']]; diff --git a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php new file mode 100644 index 0000000000000..75e90b638cde0 --- /dev/null +++ b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2022-07-04 18:47+0200','last-translator'=>'','language-team'=>'','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Das ist ein Dummy Theme']]; diff --git a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php new file mode 100644 index 0000000000000..abf62de99ac3c --- /dev/null +++ b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php @@ -0,0 +1,2 @@ +'','po-revision-date'=>'2022-07-04 18:47+0200','last-translator'=>'','language-team'=>'','language'=>'es_ES','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Este es un tema dummy']]; diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 3f5519ae41a76..81877a75d229d 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -1345,9 +1345,10 @@ public function prepareTemplate( Text_Template $template ) { * * @since 3.5.0 * + * @param string $contents Optional. File contents. * @return string|bool Path on success, else false. */ - public function temp_filename() { + public function temp_filename( string $contents = null ) { $tmp_dir = ''; $dirs = array( 'TMP', 'TMPDIR', 'TEMP' ); @@ -1364,7 +1365,17 @@ public function temp_filename() { $tmp_dir = realpath( $tmp_dir ); - return tempnam( $tmp_dir, 'wpunit' ); + $file = tempnam( $tmp_dir, 'wpunit' ); + + if ( false === $file ) { + return false; + } + + if ( null !== $contents ) { + file_put_contents( $file, $contents ); + } + + return $file; } /** diff --git a/tests/phpunit/includes/class-dummy-language-pack-upgrader.php b/tests/phpunit/includes/class-dummy-language-pack-upgrader.php new file mode 100644 index 0000000000000..1a6dc5aa84028 --- /dev/null +++ b/tests/phpunit/includes/class-dummy-language-pack-upgrader.php @@ -0,0 +1,74 @@ +} $options + * @phpstan-return array{source: string, source_files: string[], destination: string, destination_name: string, local_destination: string, remote_destination: string, clear_destination: mixed}|false|WP_Error + */ + public function run( $options ) { + $defaults = array( + 'package' => '', + // Please always pass this. + 'destination' => '', + // ...and this. + 'clear_destination' => false, + 'clear_working' => true, + 'abort_if_destination_exists' => true, + // Abort if the destination directory exists. Pass clear_destination as false please. + 'is_multi' => false, + 'hook_extra' => array(), + // Pass any extra $hook_extra args here, this will be passed to any hooked filters. + ); + + $options = wp_parse_args( $options, $defaults ); + + $result = array( + 'source' => '/tmp/source', + 'source_files' => array( + 'foo.txt', + 'bar.txt', + ), + 'destination' => '/tmp/destination', + 'destination_name' => 'destination', + 'local_destination' => '/tmp/destination', + 'remote_destination' => '/tmp/destination', + 'clear_destination' => $options['clear_destination'], + ); + + $this->result = $result; + + return $result; + } +} diff --git a/tests/phpunit/includes/class-dummy-plugin-upgrader.php b/tests/phpunit/includes/class-dummy-plugin-upgrader.php new file mode 100644 index 0000000000000..78a1cc8b6ca04 --- /dev/null +++ b/tests/phpunit/includes/class-dummy-plugin-upgrader.php @@ -0,0 +1,74 @@ +} $options + * @phpstan-return array{source: string, source_files: string[], destination: string, destination_name: string, local_destination: string, remote_destination: string, clear_destination: mixed}|false|WP_Error + */ + public function run( $options ) { + $defaults = array( + 'package' => '', + // Please always pass this. + 'destination' => '', + // ...and this. + 'clear_destination' => false, + 'clear_working' => true, + 'abort_if_destination_exists' => true, + // Abort if the destination directory exists. Pass clear_destination as false please. + 'is_multi' => false, + 'hook_extra' => array(), + // Pass any extra $hook_extra args here, this will be passed to any hooked filters. + ); + + $options = wp_parse_args( $options, $defaults ); + + $result = array( + 'source' => '/tmp/source', + 'source_files' => array( + 'foo.txt', + 'bar.txt', + ), + 'destination' => '/tmp/destination', + 'destination_name' => 'destination', + 'local_destination' => '/tmp/destination', + 'remote_destination' => '/tmp/destination', + 'clear_destination' => $options['clear_destination'], + ); + + $this->result = $result; + + return $result; + } +} diff --git a/tests/phpunit/includes/class-dummy-upgrader-skin.php b/tests/phpunit/includes/class-dummy-upgrader-skin.php new file mode 100644 index 0000000000000..0f3b4b72a59a9 --- /dev/null +++ b/tests/phpunit/includes/class-dummy-upgrader-skin.php @@ -0,0 +1,33 @@ +unlink( DIR_TESTDATA . '/pomo/simple.mo.php' ); + } + + if ( file_exists( DIR_TESTDATA . '/pomo/plural.php' ) ) { + $this->unlink( DIR_TESTDATA . '/pomo/plural.php' ); + } + } + + /** + * @covers ::__construct + * @covers ::__get + * @covers ::make_entry + * + * @return void + */ + public function test_get_entries() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $entries = $compat_instance ? $compat_instance->entries : array(); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertEqualSets( + array( + new Translation_Entry( + array( + 'singular' => 'baba', + 'translations' => array( 'dyado' ), + ) + ), + new Translation_Entry( + array( + 'singular' => "kuku\nruku", + 'translations' => array( 'yes' ), + ) + ), + ), + $entries, + 'Actual translation entries do not match expected ones' + ); + } + + /** + * @covers ::__get + * @covers ::make_entry + * + * @return void + */ + public function test_get_entries_plural() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/plural.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $entries = $compat_instance ? $compat_instance->entries : array(); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertEqualSets( + array( + new Translation_Entry( + array( + 'singular' => 'one dragon', + 'plural' => '%d dragons', + 'translations' => array( + 'oney dragoney', + 'twoey dragoney', + 'manyey dragoney', + 'manyeyey dragoney', + 'manyeyeyey dragoney', + ), + ) + ), + ), + $entries, + 'Actual translation entries do not match expected ones' + ); + } + + + /** + * @covers ::__get + * @covers ::make_entry + * + * @return void + */ + public function test_get_entries_context() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $entries = $compat_instance ? $compat_instance->entries : array(); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertEqualSets( + array( + new Translation_Entry( + array( + 'context' => 'not so dragon', + 'singular' => 'one dragon', + 'translations' => array( 'oney dragoney' ), + ) + ), + new Translation_Entry( + array( + 'is_plural' => true, + 'singular' => 'one dragon', + 'plural' => '%d dragons', + 'context' => 'dragonland', + 'translations' => array( + 'oney dragoney', + 'twoey dragoney', + 'manyey dragoney', + ), + ) + ), + ), + $entries, + 'Actual translation entries do not match expected ones' + ); + } + + /** + * @covers ::__get + * + * @return void + */ + public function test_get_headers() { + global $l10n; + + $load_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $headers = $compat_instance ? $compat_instance->headers : array(); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertEqualSetsWithIndex( + array( + 'Project-Id-Version' => 'WordPress 2.6-bleeding', + 'Report-Msgid-Bugs-To' => 'wp-polyglots@lists.automattic.com', + ), + $headers, + 'Actual translation headers do not match expected ones' + ); + } + + /** + * @covers ::__get + * + * @return void + */ + public function test_getter_unsupported_property() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance ); + + $this->assertNull( $compat_instance->foo ); + } + + /** + * @covers ::translate + * + * @return void + */ + public function test_translate() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $translation = $compat_instance ? $compat_instance->translate( 'baba' ) : false; + $translation_missing = $compat_instance ? $compat_instance->translate( 'does not exist' ) : false; + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertSame( 'dyado', $translation, 'Actual translation does not match expected one' ); + $this->assertSame( 'does not exist', $translation_missing, 'Actual translation fallback does not match expected one' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + } + + /** + * @covers ::translate_plural + * + * @return void + */ + public function test_translate_plural() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/plural.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $translation_1 = $compat_instance ? $compat_instance->translate_plural( 'one dragon', '%d dragons', 1 ) : false; + $translation_2 = $compat_instance ? $compat_instance->translate_plural( 'one dragon', '%d dragons', 2 ) : false; + $translation_minus_8 = $compat_instance ? $compat_instance->translate_plural( 'one dragon', '%d dragons', -8 ) : false; + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertSame( 'oney dragoney', $translation_1, 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $translation_2, 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $translation_minus_8, 'Actual translation does not match expected one' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + } + + /** + * @covers ::translate_plural + * + * @return void + */ + public function test_translate_plural_missing() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/plural.mo' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $translation_1 = $compat_instance ? $compat_instance->translate_plural( '%d house', '%d houses', 1 ) : false; + $translation_2 = $compat_instance ? $compat_instance->translate_plural( '%d car', '%d cars', 2 ) : false; + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertSame( '%d house', $translation_1, 'Actual translation fallback does not match expected one' ); + $this->assertSame( '%d cars', $translation_2, 'Actual plural translation fallback does not match expected one' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + } + + /** + * @covers ::translate + * @covers ::translate_plural + * + * @ticket 41257 + * + * @return void + */ + public function test_translate_invalid_edge_cases() { + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $null_string = __( null, 'wp-tests-domain' ); + $null_singular = _n( null, 'plural', 1, 'wp-tests-domain' ); + $null_plural = _n( 'singular', null, 1, 'wp-tests-domain' ); + $null_both = _n( null, null, 1, 'wp-tests-domain' ); + $null_context = _x( 'foo', null, 'wp-tests-domain' ); + $float_number = _n( '%d house', '%d houses', 7.5, 'wp-tests-domain' ); + + unload_textdomain( 'wp-tests-domain' ); + + $this->assertNull( $null_string ); + $this->assertNull( $null_singular ); + $this->assertSame( 'singular', $null_plural ); + $this->assertNull( $null_both ); + $this->assertSame( 'foo', $null_context ); + $this->assertSame( '%d houses', $float_number ); + } +} diff --git a/tests/phpunit/tests/l10n/gingerMoConvert.php b/tests/phpunit/tests/l10n/gingerMoConvert.php new file mode 100644 index 0000000000000..6d49c359d39bb --- /dev/null +++ b/tests/phpunit/tests/l10n/gingerMoConvert.php @@ -0,0 +1,572 @@ +assertSame( $instance, $instance2 ); + } + + /** + * @return void + */ + public function test_no_files_loaded_returns_false() { + $instance = new Ginger_MO(); + $this->assertFalse( $instance->translate( 'singular' ) ); + $this->assertFalse( $instance->translate_plural( array( 'plural0', 'plural1' ), 1 ) ); + } + + /** + * @covers ::unload + * + * @return void + */ + public function test_unload_not_loaded() { + $instance = new Ginger_MO(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->unload( 'unittest' ) ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::locate_translation + * @covers ::get_files + * + * @return void + */ + public function test_unload_entire_textdomain() { + $instance = new Ginger_MO(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); + + $this->assertTrue( $instance->unload( 'unittest' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); + } + + /** + * @covers ::unload + * @covers Ginger_MO_Translation_File::get_file + * + * @return void + */ + public function test_unload_file_is_not_actually_loaded() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/simple.mo' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); + } + + /** + * @covers ::unload + * @covers ::is_loaded + * + * @return void + */ + public function test_unload_specific_locale() { + $instance = new Ginger_MO(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + + $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); + + $this->assertTrue( $instance->unload( 'unittest', null, $instance->get_locale() ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); + + $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->unload( 'unittest', null, 'es_ES' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); + } + + /** + * @dataProvider data_invalid_files + * + * @param string $type + * @param string $file_contents + * @param string|bool $expected_error + * @return void + * + * @phpstan-param 'mo'|'json'|'php' $type + */ + public function test_invalid_files( string $type, string $file_contents, $expected_error = null ) { + $file = $this->temp_filename( $file_contents ); + + $this->assertNotFalse( $file ); + + $instance = Ginger_MO_Translation_File::create( $file, $type ); + + $this->assertInstanceOf( Ginger_MO_Translation_File::class, $instance ); + + // Not an error condition until it attempts to parse the file. + $this->assertFalse( $instance->error() ); + + // Trigger parsing. + $instance->headers(); + + $this->assertNotFalse( $instance->error() ); + + if ( null !== $expected_error ) { + $this->assertSame( $expected_error, $instance->error() ); + } + } + + /** + * @return array{0: array{0: 'mo'|'json'|'php', 1: string|false, 2?: string}} + */ + public function data_invalid_files(): array { + return array( + array( 'php', '' ), + array( 'php', 'assertFalse( $instance->load( DIR_TESTDATA . '/ginger-mo/file-that-doesnt-exist.mo', 'unittest' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + } + + /** + * @covers Ginger_MO_Translation_File::create + * + * @return void + */ + public function test_create_non_existent_file() { + $this->assertFalse( Ginger_MO_Translation_File::create( 'this-file-does-not-exist' ) ); + } + + /** + * @covers Ginger_MO_Translation_File::create + * + * @return void + */ + public function test_create_invalid_filetype() { + $file = $this->temp_filename( '' ); + $this->assertNotFalse( $file ); + $this->assertFalse( Ginger_MO_Translation_File::create( $file, 'invalid' ) ); + } + + /** + * @covers ::load + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * + * @dataProvider data_simple_example_files + * + * @param string $file + * @return void + */ + public function test_simple_translation_files( string $file ) { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/' . $file, 'unittest' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertFalse( $ginger_mo->is_loaded( 'textdomain not loaded' ) ); + + $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $ginger_mo->translate( 'original', '', 'textdomain not loaded' ) ); + + $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $ginger_mo->translate( 'original with context', 'context', 'unittest' ) ); + + $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + + $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); + $this->assertSame( 'translation0 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); + $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + } + + /** + * @return array + */ + public function data_simple_example_files(): array { + return array( + array( 'example-simple.json' ), + array( 'example-simple.mo' ), + array( 'example-simple.php' ), + ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * @covers Ginger_MO_Translation_File::get_plural_form + * @covers Ginger_MO_Translation_File::make_plural_form_function + * + * @return void + */ + public function test_load_multiple_files() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/plural.mo', 'unittest' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + + $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $ginger_mo->translate( 'original', '', 'textdomain not loaded' ) ); + + // From example-simple.mo + + $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $ginger_mo->translate( 'original with context', 'context', 'unittest' ) ); + + $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + + $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); + $this->assertSame( 'translation0 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); + $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + + // From simple.mo. + + $this->assertSame( 'dyado', $ginger_mo->translate( 'baba', '', 'unittest' ) ); + + // From plural.mo. + + $this->assertSame( 'oney dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); + + $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/simple.mo' ) ); + + $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest' ) ); + } + + /** + * @covers ::set_locale + * @covers ::get_locale + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * + * @return void + */ + public function test_load_multiple_locales() { + $ginger_mo = new Ginger_MO(); + + $this->assertSame( 'en_US', $ginger_mo->get_locale() ); + + $ginger_mo->set_locale( 'de_DE' ); + + $this->assertSame( 'de_DE', $ginger_mo->get_locale() ); + + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'unittest', 'es_ES' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/plural.mo', 'unittest', 'en_US' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + + // From example-simple.mo + + $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ), 'String should be translated in de_DE' ); + $this->assertFalse( $ginger_mo->translate( 'original', '', 'unittest', 'es_ES' ), 'String should not be translated in es_ES' ); + $this->assertFalse( $ginger_mo->translate( 'original', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + + // From simple.mo. + + $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest' ), 'String should not be translated in de_DE' ); + $this->assertSame( 'dyado', $ginger_mo->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); + $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + + $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/plural.mo', 'de_DE' ) ); + + $this->assertSame( 'oney dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); + + $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/plural.mo', 'en_US' ) ); + + $this->assertFalse( $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + } + + /** + * @covers ::load + * @covers ::locate_translation + * + * @return void + */ + public function test_load_with_default_textdomain() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ) ); + $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $ginger_mo->translate( 'original' ) ); + } + + /** + * @covers ::load + * + * @return void + */ + public function test_load_same_file_twice() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + } + + /** + * @covers ::load + * + * @return void + */ + public function test_load_file_is_already_loaded_for_different_textdomain() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'foo' ) ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'bar' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'foo' ) ); + $this->assertTrue( $ginger_mo->is_loaded( 'bar' ) ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * @covers Ginger_MO_Translation_File::get_plural_form + * @covers Ginger_MO_Translation_File::make_plural_form_function + * + * @return void + */ + public function test_load_no_plurals() { + $ginger_mo = new Ginger_MO(); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/fa_IR.mo', 'unittest' ) ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + + $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); + + $this->assertSame( 'رونوشت‌ها فعال نشدند.', $ginger_mo->translate( 'Revisions not enabled.', '', 'unittest' ) ); + $this->assertSame( 'افزودن جدید', $ginger_mo->translate( 'Add New', 'file', 'unittest' ) ); + + $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 0, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 1, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 2, '', 'unittest' ) ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_no_loaded_translations() { + $ginger_mo = new Ginger_MO(); + $headers = $ginger_mo->get_headers(); + $this->assertEmpty( $headers ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_with_default_textdomain() { + $ginger_mo = new Ginger_MO(); + $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ); + $headers = $ginger_mo->get_headers(); + $this->assertSame( + array( + 'Po-Revision-Date' => '2016-01-05 18:45:32+1000', + ), + $headers + ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_no_loaded_translations_for_domain() { + $ginger_mo = new Ginger_MO(); + $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'foo' ); + $headers = $ginger_mo->get_headers( 'bar' ); + $this->assertEmpty( $headers ); + } + + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_no_loaded_translations() { + $ginger_mo = new Ginger_MO(); + $headers = $ginger_mo->get_entries(); + $this->assertEmpty( $headers ); + } + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_with_default_textdomain() { + $ginger_mo = new Ginger_MO(); + $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo' ); + $headers = $ginger_mo->get_entries(); + $this->assertSame( + array( + 'baba' => 'dyado', + "kuku\nruku" => 'yes', + ), + $headers + ); + } + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_no_loaded_translations_for_domain() { + $ginger_mo = new Ginger_MO(); + $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'foo' ); + $headers = $ginger_mo->get_entries( 'bar' ); + $this->assertEmpty( $headers ); + } + + /** + * @dataProvider data_export_matrix + * + * @param string $source_file + * @param string $destination_format + * @return void + * + * @phpstan-param 'mo'|'json'|'php' $destination_format + */ + public function test_convert_format( string $source_file, string $destination_format ) { + $destination_file = $this->temp_filename(); + + $this->assertNotFalse( $destination_file ); + + $source = Ginger_MO_Translation_File::create( $source_file ); + + $this->assertInstanceOf( Ginger_MO_Translation_File::class, $source ); + + $contents = Ginger_MO_Translation_File::transform( $source_file, $destination_format ); + + $this->assertNotFalse( $contents ); + + file_put_contents( $destination_file, $contents ); + + $destination = Ginger_MO_Translation_File::create( $destination_file, $destination_format ); + + $this->assertInstanceOf( Ginger_MO_Translation_File::class, $destination ); + $this->assertFalse( $destination->error() ); + + $this->assertTrue( filesize( $destination_file ) > 0 ); + + $destination_read = Ginger_MO_Translation_File::create( $destination_file, $destination_format ); + + $this->assertInstanceOf( Ginger_MO_Translation_File::class, $destination_read ); + $this->assertFalse( $destination_read->error() ); + + $source_headers = $source->headers(); + $destination_headers = $destination_read->headers(); + + $this->assertEquals( $source_headers, $destination_headers ); + + foreach ( $source->entries() as $original => $translation ) { + // Verify the translation is in the destination file + if ( false !== strpos( $original, "\0" ) ) { + // Plurals: + $new_translation = $destination_read->translate( $original ); + + $this->assertSame( $translation, $new_translation ); + + } else { + // Single + $new_translation = $destination_read->translate( $original ); + + $this->assertSame( $translation, $new_translation ); + } + } + } + + /** + * @return array + */ + public function data_export_matrix(): array { + $formats = array( 'mo', 'json', 'php' ); + + $matrix = array(); + + foreach ( $formats as $input_format ) { + foreach ( $formats as $output_format ) { + $matrix[ "$input_format to $output_format" ] = array( DIR_TESTDATA . '/ginger-mo/example-simple.' . $input_format, $output_format ); + } + } + + return $matrix; + } + + /** + * @covers Ginger_MO_Translation_File::transform + * + * @return void + */ + public function test_convert_format_invalid_source() { + $this->assertFalse( Ginger_MO_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); + $this->assertFalse( Ginger_MO_Translation_File::transform( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'invalid' ) ); + $this->assertNotFalse( Ginger_MO_Translation_File::transform( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'php' ) ); + } +} diff --git a/tests/phpunit/tests/l10n/gingerMoIntegration.php b/tests/phpunit/tests/l10n/gingerMoIntegration.php new file mode 100644 index 0000000000000..78ac7f344d281 --- /dev/null +++ b/tests/phpunit/tests/l10n/gingerMoIntegration.php @@ -0,0 +1,696 @@ +unlink( $file ); + } + } + + remove_all_filters( 'convert_translation_files' ); + remove_all_filters( 'translation_file_format' ); + + remove_all_filters( 'filesystem_method' ); + + unload_textdomain( 'wp-tests-domain' ); + } + + /** + * @covers ::load_textdomain + * @covers Ginger_MO::get_entries + * @covers Ginger_MO::get_headers + * @covers Ginger_MO::normalize_header + * + * @return void + */ + public function test_load_textdomain() { + global $l10n; + + $loaded_before_load = is_textdomain_loaded( 'wp-tests-domain' ); + + $load_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $loaded_after_load = is_textdomain_loaded( 'wp-tests-domain' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + $headers = Ginger_MO::instance()->get_headers( 'wp-tests-domain' ); + $entries = Ginger_MO::instance()->get_entries( 'wp-tests-domain' ); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $loaded_after_unload = is_textdomain_loaded( 'wp-tests-domain' ); + + $this->assertFalse( $loaded_before_load, 'Text domain was already loaded at beginning of the test' ); + $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); + $this->assertTrue( $loaded_after_load, 'Text domain is not considered loaded' ); + $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertFalse( $loaded_after_unload, 'Text domain still considered loaded after unload' ); + $this->assertTrue( $is_loaded, 'Text domain not considered loaded in Ginger-MO' ); + $this->assertEqualSetsWithIndex( + array( + 'Project-Id-Version' => 'WordPress 2.6-bleeding', + 'Report-Msgid-Bugs-To' => 'wp-polyglots@lists.automattic.com', + ), + $headers, + 'Actual translation headers do not match expected ones' + ); + $this->assertEqualSetsWithIndex( + array( + 'baba' => 'dyado', + "kuku\nruku" => 'yes', + ), + $entries, + 'Actual translation entries do not match expected ones' + ); + } + + /** + * @covers ::load_textdomain + * @covers Ginger_MO::get_entries + * @covers Ginger_MO::get_headers + * @covers Ginger_MO::normalize_header + * + * @return void + */ + public function test_load_textdomain_existing_override() { + add_filter( 'override_load_textdomain', '__return_true' ); + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $is_loaded_wp = is_textdomain_loaded( 'wp-tests-domain' ); + + $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + + remove_filter( 'override_load_textdomain', '__return_true' ); + + $this->assertFalse( $is_loaded_wp ); + $this->assertFalse( $is_loaded ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_mo_files() { + add_filter( + 'translation_file_format', + static function () { + return 'mo'; + } + ); + + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php' ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_creates_and_reads_php_files() { + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); + + $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); + $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); + $this->assertTrue( $unload_php_successful ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_creates_and_reads_php_files_if_filtered_format_is_unsupported() { + add_filter( + 'translation_file_format', + static function () { + return 'unknown-format'; + } + ); + + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); + + $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); + $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); + $this->assertTrue( $unload_php_successful ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_creates_and_reads_php_files_no_wp_filesystem() { + add_filter( 'filesystem_method', '__return_empty_string' ); + + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); + + $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); + $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); + $this->assertTrue( $unload_php_successful ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_does_not_create_php_files_if_disabled() { + add_filter( 'convert_translation_files', '__return_false' ); + + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php' ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_creates_and_reads_json_files() { + add_filter( + 'translation_file_format', + static function () { + return 'json'; + } + ); + + $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); + + $load_json_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.json' ); + + $unload_json_successful = unload_textdomain( 'wp-tests-domain' ); + + $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); + $this->assertTrue( $unload_mo_successful ); + $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.json' ); + $this->assertTrue( $load_json_successful, 'JSON file not successfully loaded' ); + $this->assertTrue( $unload_json_successful ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_existing_translation_is_kept() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + + $mo = new MO(); + $mo->import_from_file( DIR_TESTDATA . '/pomo/context.mo' ); + $mo->merge_with( $l10n['wp-tests-domain'] ); + $l10n['wp-tests-domain'] = $mo; + + $simple = __( 'baba', 'wp-tests-domain' ); + $context = _x( 'one dragon', 'not so dragon', 'wp-tests-domain' ); + + $this->assertSame( 'dyado', $simple ); + $this->assertSame( 'oney dragoney', $context ); + $this->assertInstanceOf( Translations::class, $l10n['wp-tests-domain'] ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_loads_existing_translation() { + global $l10n; + + $mo = new MO(); + $mo->import_from_file( DIR_TESTDATA . '/pomo/simple.mo' ); + $l10n['wp-tests-domain'] = $mo; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + + $simple = __( 'baba', 'wp-tests-domain' ); + $context = _x( 'one dragon', 'not so dragon', 'wp-tests-domain' ); + + $this->assertSame( 'dyado', $simple ); + $this->assertSame( 'oney dragoney', $context ); + $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_loads_existing_translation_mo_files() { + global $l10n; + + add_filter( + 'translation_file_format', + static function () { + return 'mo'; + } + ); + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $mo = new MO(); + $mo->import_from_file( DIR_TESTDATA . '/pomo/simple.mo' ); + $l10n['wp-tests-domain'] = $mo; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + + $simple = __( 'baba', 'wp-tests-domain' ); + $context = _x( 'one dragon', 'not so dragon', 'wp-tests-domain' ); + + $this->assertSame( 'dyado', $simple ); + $this->assertSame( 'oney dragoney', $context ); + $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_loads_existing_translation_php_files() { + global $l10n; + + // Just to ensure the PHP files exist. + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + unload_textdomain( 'wp-tests-domain' ); + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $mo = new MO(); + $mo->import_from_file( DIR_TESTDATA . '/pomo/simple.mo' ); + $l10n['wp-tests-domain'] = $mo; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/context.mo' ); + + $simple = __( 'baba', 'wp-tests-domain' ); + $context = _x( 'one dragon', 'not so dragon', 'wp-tests-domain' ); + + $this->assertSame( 'dyado', $simple ); + $this->assertSame( 'oney dragoney', $context ); + $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + } + + /** + * @param string $domain + * @param string $file + * @return void + */ + public function _on_load_textdomain( $domain, $file ) { + remove_action( 'load_textdomain', array( $this, '_on_load_textdomain' ) ); + load_textdomain( $domain, $file ); + } + + /** + * @covers ::load_textdomain + * + * @return void + */ + public function test_load_textdomain_inception_does_not_create_duplicate_files() { + add_action( 'load_textdomain', array( $this, '_on_load_textdomain' ), 10, 2 ); + + // Just to ensure the PHP files exist. + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + unload_textdomain( 'wp-tests-domain' ); + + $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); + $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php.php' ); + } + + /** + * @covers ::unload_textdomain + * @covers Ginger_MO::get_entries + * @covers Ginger_MO::get_headers + * @covers Ginger_MO::normalize_header + * + * @return void + */ + public function test_unload_textdomain() { + global $l10n; + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $loaded_after_unload = is_textdomain_loaded( 'wp-tests-domain' ); + + $compat_instance = $l10n['wp-tests-domain'] ?? null; + + $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + $headers = Ginger_MO::instance()->get_headers( 'wp-tests-domain' ); + $entries = Ginger_MO::instance()->get_entries( 'wp-tests-domain' ); + + $this->assertNull( $compat_instance, 'Compat instance was not removed' ); + $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); + $this->assertFalse( $loaded_after_unload, 'Text domain still considered loaded after unload' ); + $this->assertFalse( $is_loaded, 'Text domain still considered loaded in Ginger-MO' ); + $this->assertEmpty( $headers, 'Actual translation headers are not empty' ); + $this->assertEmpty( $entries, 'Actual translation entries are not empty' ); + } + + /** + * @covers ::unload_textdomain + * + * @return void + */ + public function test_unload_textdomain_existing_override() { + add_filter( 'override_unload_textdomain', '__return_true' ); + + load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + + $unload_successful = unload_textdomain( 'wp-tests-domain' ); + + $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + + remove_filter( 'override_unload_textdomain', '__return_true' ); + + $unload_successful_after = unload_textdomain( 'wp-tests-domain' ); + + $is_loaded_after = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + + $this->assertTrue( $unload_successful ); + $this->assertTrue( $is_loaded ); + $this->assertTrue( $unload_successful_after ); + $this->assertFalse( $is_loaded_after ); + } + + /** + * @covers ::load_textdomain + * @covers ::unload_textdomain + * + * @return void + */ + public function test_switch_to_locale_translations_stay_loaded_default_textdomain() { + switch_to_locale( 'es_ES' ); + + $actual = __( 'Invalid parameter.' ); + + $this->assertTrue( Ginger_MO::instance()->is_loaded() ); + $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); + + restore_previous_locale(); + + $actual_2 = __( 'Invalid parameter.' ); + + $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); + + $this->assertSame( 'Parámetro no válido. ', $actual ); + $this->assertSame( 'Invalid parameter.', $actual_2 ); + } + + /** + * @covers ::load_textdomain + * @covers ::unload_textdomain + * @covers ::change_locale + * + * @return void + */ + public function test_switch_to_locale_translations_stay_loaded_custom_textdomain() { + $this->assertSame( 'en_US', Ginger_MO::instance()->get_locale() ); + + require_once DIR_TESTDATA . '/plugins/internationalized-plugin.php'; + + $before = i18n_plugin_test(); + + switch_to_locale( 'es_ES' ); + + $actual = i18n_plugin_test(); + + $this->assertSame( 'es_ES', Ginger_MO::instance()->get_locale() ); + $this->assertTrue( Ginger_MO::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertFalse( Ginger_MO::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); + + restore_previous_locale(); + + $after = i18n_plugin_test(); + + $this->assertTrue( Ginger_MO::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + + $this->assertSame( 'This is a dummy plugin', $before ); + $this->assertSame( 'Este es un plugin dummy', $actual ); + $this->assertSame( 'This is a dummy plugin', $after ); + } + + /** + * @covers ::upgrader_process_complete + * + * @return void + */ + public function test_create_translation_files_after_translations_update() { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-language-pack-upgrader.php'; + + $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); + + // These translations exist in the core test suite. + // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. + $result = $upgrader->bulk_upgrade( + array( + (object) array( + 'type' => 'plugin', + 'slug' => 'internationalized-plugin', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'theme', + 'slug' => 'internationalized-theme', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'core', + 'slug' => 'default', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + ) + ); + + $this->assertIsNotBool( $result ); + $this->assertNotWPError( $result ); + $this->assertNotEmpty( $result ); + + $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); + } + + /** + * @covers ::upgrader_process_complete + * + * @return void + */ + public function test_create_translation_files_after_translations_update_if_filtered_format_is_unsupported() { + add_filter( + 'translation_file_format', + static function () { + return 'unknown-format'; + } + ); + + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; + + $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); + + // These translations exist in the core test suite. + // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. + $result = $upgrader->bulk_upgrade( + array( + (object) array( + 'type' => 'plugin', + 'slug' => 'internationalized-plugin', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'theme', + 'slug' => 'internationalized-theme', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'core', + 'slug' => 'default', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + ) + ); + + $this->assertIsNotBool( $result ); + $this->assertNotWPError( $result ); + $this->assertNotEmpty( $result ); + + $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); + } + + /** + * @covers ::upgrader_process_complete + * + * @return void + */ + public function test_create_translation_files_after_translations_update_no_wp_filesystem() { + $callback = static function () { + add_filter( 'filesystem_method', '__return_empty_string' ); + }; + + add_action( 'upgrader_process_complete', $callback, 1 ); + + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-language-pack-upgrader.php'; + + $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); + + // These translations exist in the core test suite. + // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. + $result = $upgrader->bulk_upgrade( + array( + (object) array( + 'type' => 'plugin', + 'slug' => 'internationalized-plugin', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'theme', + 'slug' => 'internationalized-theme', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + (object) array( + 'type' => 'core', + 'slug' => 'default', + 'language' => 'de_DE', + 'version' => '99.9.9', + 'package' => '/tmp/notused.zip', + ), + ) + ); + + remove_action( 'upgrader_process_complete', $callback, 1 ); + + $this->assertIsNotBool( $result ); + $this->assertNotWPError( $result ); + $this->assertNotEmpty( $result ); + + $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); + $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); + } + + /** + * @covers ::upgrader_process_complete + * + * @return void + */ + public function test_do_not_create_translations_after_plugin_update() { + require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; + require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; + require_once DIR_TESTROOT . '/includes/class-dummy-plugin-upgrader.php'; + + $upgrader = new Dummy_Plugin_Upgrader( new Dummy_Upgrader_Skin() ); + + set_site_transient( + 'update_plugins', + (object) array( + 'response' => array( + 'custom-internationalized-plugin/custom-internationalized-plugin.php' => (object) array( + 'package' => 'https://urltozipfile.local', + ), + ), + ) + ); + + $result = $upgrader->bulk_upgrade( + array( + 'custom-internationalized-plugin/custom-internationalized-plugin.php', + ) + ); + + $this->assertNotFalse( $result ); + $this->assertFileDoesNotExist( WP_LANG_DIR . '/plugins/custom-internationalized-plugin-de_DE.php' ); + $this->assertFileDoesNotExist( WP_PLUGIN_DIR . '/plugins/custom-internationalized-plugin/custom-internationalized-plugin-de_DE.php' ); + } +} From 7af8b634d09d4ff67c1d71c909e965e16d2f6e6a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 3 Oct 2023 15:08:41 +0200 Subject: [PATCH 05/65] Lint fixes --- tests/phpunit/includes/abstract-testcase.php | 2 +- tests/phpunit/tests/l10n/gingerMo.php | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 81877a75d229d..5849e6b0937a7 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -1348,7 +1348,7 @@ public function prepareTemplate( Text_Template $template ) { * @param string $contents Optional. File contents. * @return string|bool Path on success, else false. */ - public function temp_filename( string $contents = null ) { + public function temp_filename( string $contents = null ) { $tmp_dir = ''; $dirs = array( 'TMP', 'TMPDIR', 'TEMP' ); diff --git a/tests/phpunit/tests/l10n/gingerMo.php b/tests/phpunit/tests/l10n/gingerMo.php index 3c7f062727848..0413fd21b0c1d 100644 --- a/tests/phpunit/tests/l10n/gingerMo.php +++ b/tests/phpunit/tests/l10n/gingerMo.php @@ -277,12 +277,14 @@ public function test_translate_plural_missing() { public function test_translate_invalid_edge_cases() { load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); + // phpcs:disable WordPress.WP.I18n $null_string = __( null, 'wp-tests-domain' ); $null_singular = _n( null, 'plural', 1, 'wp-tests-domain' ); $null_plural = _n( 'singular', null, 1, 'wp-tests-domain' ); $null_both = _n( null, null, 1, 'wp-tests-domain' ); $null_context = _x( 'foo', null, 'wp-tests-domain' ); $float_number = _n( '%d house', '%d houses', 7.5, 'wp-tests-domain' ); + // phpcs:enable WordPress.WP.I18n unload_textdomain( 'wp-tests-domain' ); From 95d32ea87c5dd7be3e29a299948824a7df4f12f8 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 4 Oct 2023 00:11:36 +0200 Subject: [PATCH 06/65] Check if is string --- src/wp-includes/ginger-mo/class-ginger-mo.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/ginger-mo/class-ginger-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo.php index 2bb26b5bdedaa..b4bfbe255eecb 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo.php @@ -142,7 +142,9 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca } if ( null !== $mo ) { - $mo = realpath( $mo ); + if ( is_string( $mo ) ) { + $mo = realpath( $mo ); + } if ( null !== $locale ) { foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { From f3e76b2d932ce1f586fe0499ca68c64daf56fc8b Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 5 Oct 2023 09:55:35 +0200 Subject: [PATCH 07/65] Remove json support and upgrader integration --- .../includes/class-language-pack-upgrader.php | 39 -- .../class-ginger-mo-translation-file-json.php | 117 ------ .../class-ginger-mo-translation-file.php | 40 +- src/wp-includes/l10n.php | 39 +- src/wp-settings.php | 1 - .../data/ginger-mo/example-simple.json | 19 - .../phpunit/data/languages/admin-en_GB.mo.php | 2 - .../phpunit/data/languages/admin-es_ES.mo.php | 2 - tests/phpunit/data/languages/de_DE.mo.php | 2 - tests/phpunit/data/languages/en_GB.mo.php | 2 - tests/phpunit/data/languages/es_ES.mo.php | 2 - tests/phpunit/data/languages/ja_JP.mo.php | 2 - ...stom-internationalized-plugin-de_DE.mo.php | 2 - ...stom-internationalized-plugin-es_ES.mo.php | 2 - .../languages/de_DE.mo.php | 2 - .../languages/es_ES.mo.php | 2 - .../class-dummy-language-pack-upgrader.php | 74 ---- .../includes/class-dummy-plugin-upgrader.php | 74 ---- .../includes/class-dummy-upgrader-skin.php | 33 -- tests/phpunit/tests/l10n/gingerMoConvert.php | 13 +- .../tests/l10n/gingerMoIntegration.php | 346 +----------------- 21 files changed, 22 insertions(+), 793 deletions(-) delete mode 100644 src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php delete mode 100644 tests/phpunit/data/ginger-mo/example-simple.json delete mode 100644 tests/phpunit/data/languages/admin-en_GB.mo.php delete mode 100644 tests/phpunit/data/languages/admin-es_ES.mo.php delete mode 100644 tests/phpunit/data/languages/de_DE.mo.php delete mode 100644 tests/phpunit/data/languages/en_GB.mo.php delete mode 100644 tests/phpunit/data/languages/es_ES.mo.php delete mode 100644 tests/phpunit/data/languages/ja_JP.mo.php delete mode 100644 tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php delete mode 100644 tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php delete mode 100644 tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php delete mode 100644 tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php delete mode 100644 tests/phpunit/includes/class-dummy-language-pack-upgrader.php delete mode 100644 tests/phpunit/includes/class-dummy-plugin-upgrader.php delete mode 100644 tests/phpunit/includes/class-dummy-upgrader-skin.php diff --git a/src/wp-admin/includes/class-language-pack-upgrader.php b/src/wp-admin/includes/class-language-pack-upgrader.php index 34fa706b5e3b5..3c3d42a56a9c8 100644 --- a/src/wp-admin/includes/class-language-pack-upgrader.php +++ b/src/wp-admin/includes/class-language-pack-upgrader.php @@ -288,45 +288,6 @@ public function bulk_upgrade( $language_updates = array(), $args = array() ) { ) ); - foreach ( $language_updates_results as $translation ) { - switch ( $translation['type'] ) { - case 'plugin': - $file = WP_LANG_DIR . '/plugins/' . $translation['slug'] . '-' . $translation['language'] . '.mo'; - break; - case 'theme': - $file = WP_LANG_DIR . '/themes/' . $translation['slug'] . '-' . $translation['language'] . '.mo'; - break; - default: - $file = WP_LANG_DIR . '/' . $translation['language'] . '.mo'; - break; - } - - if ( file_exists( $file ) ) { - /** This filter is documented in wp-includes/l10n.php */ - $preferred_format = apply_filters( 'translation_file_format', 'php' ); - if ( ! in_array( $preferred_format, array( 'php', 'mo', 'json' ), true ) ) { - $preferred_format = 'php'; - } - - $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $file ); - - /** This filter is documented in wp-includes/l10n.php */ - $convert = apply_filters( 'convert_translation_files', true ); - - if ( 'mo' !== $preferred_format && $convert ) { - $contents = Ginger_MO_Translation_File::transform( $file, $preferred_format ); - - if ( false !== $contents ) { - if ( true === $this->fs_connect( array( dirname( $file ) ) ) ) { - $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE ); - } else { - file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents - } - } - } - } - } - // Re-add upgrade hooks. add_action( 'upgrader_process_complete', array( 'Language_Pack_Upgrader', 'async_upgrade' ), 20 ); add_action( 'upgrader_process_complete', 'wp_version_check', 10, 0 ); diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php deleted file mode 100644 index e1821adafea7f..0000000000000 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-json.php +++ /dev/null @@ -1,117 +0,0 @@ -parsed = true; - - $data = file_get_contents( $this->file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents - - if ( false === $data ) { - $this->error = true; - return; - } - - $data = json_decode( $data, true ); - - if ( false === $data || ! is_array( $data ) ) { - $this->error = json_last_error_msg(); - return; - } - - if ( ! isset( $data['domain'] ) || ! isset( $data['locale_data'][ $data['domain'] ] ) ) { - $this->error = true; - return; - } - - if ( isset( $data['translation-revision-date'] ) ) { - $this->headers['po-revision-date'] = $data['translation-revision-date']; - } - - $entries = $data['locale_data'][ $data['domain'] ]; - - foreach ( $entries as $key => $item ) { - if ( '' === $key ) { - $headers = array_change_key_case( $item ); - if ( isset( $headers['lang'] ) ) { - $this->headers['language'] = $headers['lang']; - unset( $headers['lang'] ); - } - - $this->headers = array_merge( - $this->headers, - $headers - ); - continue; - } - - if ( is_string( $item ) ) { - $this->entries[ (string) $key ] = $item; - } elseif ( is_array( $item ) ) { - $this->entries[ (string) $key ] = implode( "\0", $item ); - } - } - - unset( $this->headers['domain'] ); - } - - /** - * Exports translation contents as a string. - * - * @return string Translation file contents. - */ - public function export(): string { - $headers = array_change_key_case( $this->headers ); - - $domain = $headers['domain'] ?? 'messages'; - - $data = array( - 'domain' => $domain, - 'locale_data' => array( - $domain => $this->entries, - ), - ); - - if ( isset( $headers['po-revision-date'] ) ) { - $data['translation-revision-date'] = $headers['po-revision-date']; - } - - if ( isset( $headers['x-generator'] ) ) { - $data['generator'] = $headers['x-generator']; - } - - $data['locale_data'][ $domain ][''] = array( - 'domain' => $domain, - ); - - if ( isset( $headers['plural-forms'] ) ) { - $data['locale_data'][ $domain ]['']['plural-forms'] = $headers['plural-forms']; - } - - if ( isset( $headers['language'] ) ) { - $data['locale_data'][ $domain ]['']['lang'] = $headers['language']; - } - - $json = json_encode( $data, JSON_PRETTY_PRINT ); - - if ( false === $json ) { - return ''; - } - - return $json; - } -} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php index 7f15b0223e792..51546dba41533 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php @@ -67,7 +67,7 @@ protected function __construct( string $file ) { * @param string|null $filetype Optional. File type. Default inferred from file name. * @return false|Ginger_MO_Translation_File * - * @phpstan-param 'mo'|'json'|'php'|null $filetype + * @phpstan-param 'mo'|'php'|null $filetype */ public static function create( string $file, string $filetype = null ) { if ( ! is_readable( $file ) ) { @@ -86,16 +86,9 @@ public static function create( string $file, string $filetype = null ) { return new Ginger_MO_Translation_File_MO( $file ); case 'php': return new Ginger_MO_Translation_File_PHP( $file ); - case 'json': - if ( function_exists( 'json_decode' ) ) { - return new Ginger_MO_Translation_File_JSON( $file ); - } - break; default: return false; } - - return false; } /** @@ -212,11 +205,9 @@ public function make_plural_form_function( string $expression ) { * @param string $filetype Desired target file type. * @return string|false Transformed translation file contents on success, false otherwise. * - * @phpstan-param 'mo'|'json'|'php' $filetype + * @phpstan-param 'mo'|'php' $filetype */ public static function transform( string $file, string $filetype ) { - $destination = null; - $source = self::create( $file ); if ( false === $source ) { @@ -230,19 +221,10 @@ public static function transform( string $file, string $filetype ) { case 'php': $destination = new Ginger_MO_Translation_File_PHP( '' ); break; - case 'json': - if ( function_exists( 'json_decode' ) ) { - $destination = new Ginger_MO_Translation_File_JSON( '' ); - } - break; default: return false; } - if ( null === $destination ) { - return false; - } - $success = $destination->import( $source ); if ( ! $success ) { @@ -252,16 +234,6 @@ public static function transform( string $file, string $filetype ) { return $destination->export(); } - /** - * Parses the file. - * - * @return void - */ - protected function parse_file() { - // Needs to be implemented in child classes. - // Not abstract because it's used in this class. - } - /** * Imports translations from another file. * @@ -280,6 +252,14 @@ protected function import( Ginger_MO_Translation_File $source ): bool { return false === $this->error; } + /** + * Parses the file. + * + * @return void + */ + abstract protected function parse_file(); + + /** * Exports translation contents as a string. * diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 896a347f94800..5bc75171cff78 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -707,7 +707,6 @@ function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) * @global MO[] $l10n An array of all currently loaded text domains. * @global MO[] $l10n_unloaded An array of all text domains that have been unloaded again. * @global WP_Textdomain_Registry $wp_textdomain_registry WordPress Textdomain Registry. - * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param string $domain Text domain. Unique identifier for retrieving translated strings. * @param string $mofile Path to the .mo file. @@ -716,7 +715,7 @@ function translate_nooped_plural( $nooped_plural, $count, $domain = 'default' ) */ function load_textdomain( $domain, $mofile, $locale = null ) { /** @var WP_Textdomain_Registry $wp_textdomain_registry */ - global $l10n, $l10n_unloaded, $wp_textdomain_registry, $wp_filesystem; + global $l10n, $l10n_unloaded, $wp_textdomain_registry; $l10n_unloaded = (array) $l10n_unloaded; @@ -797,10 +796,10 @@ function load_textdomain( $domain, $mofile, $locale = null ) { * * @since 6.5.0 * - * @param string $convert Preferred file format. Possible values: 'php', 'mo', 'json'. Default: 'php'. + * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'. */ $preferred_format = apply_filters( 'translation_file_format', 'php' ); - if ( ! in_array( $preferred_format, array( 'php', 'mo', 'json' ), true ) ) { + if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) { $preferred_format = 'php'; } @@ -814,7 +813,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { /** * Filters the file path for loading translations for the given text domain. * - * The file could be an MO, JSON, or PHP file. + * The file could be an MO or PHP file. * * @since 6.5.0 * @@ -832,6 +831,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { // Unset Noop_Translations reference in get_translations_for_domain. unset( $l10n[ $domain ] ); + $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); @@ -862,35 +862,6 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); - - /** - * Filters whether existing MO files should be automatically converted to the preferred format. - * - * Only runs when no corresponding PHP or JSON translation file exists yet. - * - * The preferred format is determined by the {@see 'translation_file_format'} filter - * - * Useful for testing/debugging. - * - * @param bool $convert Whether to convert MO files to PHP or JSON files. Default true. - */ - $convert = apply_filters( 'convert_translation_files', true ); - - if ( 'mo' !== $preferred_format && $convert && str_ends_with( $mofile, '.mo' ) ) { - $contents = Ginger_MO_Translation_File::transform( $mofile, $preferred_format ); - - if ( false !== $contents ) { - if ( ! function_exists( 'WP_Filesystem' ) ) { - require_once ABSPATH . '/wp-admin/includes/file.php'; - } - - if ( true === WP_Filesystem() ) { - $wp_filesystem->put_contents( $mofile_preferred, $contents, FS_CHMOD_FILE ); - } else { - file_put_contents( $mofile_preferred, $contents, LOCK_EX ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents - } - } - } } return true; diff --git a/src/wp-settings.php b/src/wp-settings.php index 845effcfe889a..7d843c86d4ecf 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -118,7 +118,6 @@ require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translations.php'; require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file.php'; require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-mo.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-json.php'; require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-php.php'; /** diff --git a/tests/phpunit/data/ginger-mo/example-simple.json b/tests/phpunit/data/ginger-mo/example-simple.json deleted file mode 100644 index c775a4302f267..0000000000000 --- a/tests/phpunit/data/ginger-mo/example-simple.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "domain": "unittest", - "locale_data": { - "unittest": { - "": { - "domain": "unittest", - "plural-forms": "nplurals=2; plural=n != 1;", - "lang": "de" - }, - "original": "translation", - "context\u0004original with context": ["translation with context"], - "plural0\u0000plural1": ["translation0", "translation1"], - "context\u0004plural0 with context\u0000plural1 with context": [ - "translation0 with context", - "translation1 with context" - ] - } - } -} diff --git a/tests/phpunit/data/languages/admin-en_GB.mo.php b/tests/phpunit/data/languages/admin-en_GB.mo.php deleted file mode 100644 index 2cc082ec00e29..0000000000000 --- a/tests/phpunit/data/languages/admin-en_GB.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2016-10-25 18:29+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Administration','language'=>'en_GB','messages'=>['Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.'=>'Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.','Comment query improvements'=>'Comment query improvements','New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.'=>'New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.','Term, comment, and network objects'=>'Term, comment, and network objects','Thank you for updating! WordPress %s makes your site more connected and responsive.'=>'Thank you for updating! WordPress %s makes your site more connected and responsive.']]; diff --git a/tests/phpunit/data/languages/admin-es_ES.mo.php b/tests/phpunit/data/languages/admin-es_ES.mo.php deleted file mode 100644 index b9b1f126ddf4d..0000000000000 --- a/tests/phpunit/data/languages/admin-es_ES.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2016-10-25 18:29+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Administration','language'=>'es_ES','messages'=>['Comment queries now have cache handling to improve performance. New arguments in %s make crafting robust comment queries simpler.'=>'Las consultas de comentarios ahora tiene una caché que mejora el rendimiento. Nuevos argumentos en %s hacen que sea más fácil crear consultas robustas.','Comment query improvements'=>'Mejoras en las consultas de comentarios','New %1$s, %2$s, and %3$s objects make interacting with terms, comments, and networks more predictable and intuitive in code.'=>'Ahora los objetos %1$s, %2$s y %3$s hacen que interactuar con términos, comentarios y redes sea más predecible y que el código sea más intuitivo.','Term, comment, and network objects'=>'Objetos de término, comentario y red','Thank you for updating! WordPress %s makes your site more connected and responsive.'=>'¡Gracias por actualizar! WordPress %s hace que tu sitio esté más conectado y sea más adaptable.']]; diff --git a/tests/phpunit/data/languages/de_DE.mo.php b/tests/phpunit/data/languages/de_DE.mo.php deleted file mode 100644 index 7905f5905aec6..0000000000000 --- a/tests/phpunit/data/languages/de_DE.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2019-03-28 19:42+0300','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 2.2.1','project-id-version'=>'Development (5.2.x)','language'=>'de_DE','pot-creation-date'=>'','last-translator'=>'','language-team'=>'','messages'=>['Update %s now'=>'Jetzt %s aktualisieren','[%1$s] Confirm Action: %2$s'=>'[%1$s] Aktion bestätigen: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Löschauftrag ausgeführt','[%s] Personal Data Export'=>'[%s] Export personenbezogener Daten','html_lang_attribute'=>'de-DE','number_format_decimal_point'=>',','number_format_thousands_sep'=>'.','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/en_GB.mo.php b/tests/phpunit/data/languages/en_GB.mo.php deleted file mode 100644 index 444fe6c6609ff..0000000000000 --- a/tests/phpunit/data/languages/en_GB.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2016-10-26 00:01+0200','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'Development (4.4.x)','language'=>'en_GB','messages'=>['ERROR: Sorry, that username is not allowed.'=>'ERROR: Sorry, that username is not allowed.','Invalid parameter.'=>'Invalid parameter.','menu(Currently set to: %s)'=>'(Currently set to: %s)','menu location(Current: %s)'=>'(Current: %s)','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/es_ES.mo.php b/tests/phpunit/data/languages/es_ES.mo.php deleted file mode 100644 index 76bbb0878bb2a..0000000000000 --- a/tests/phpunit/data/languages/es_ES.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2020-07-23 21:12+0300','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 2.3','project-id-version'=>'Development (5.2.x)','language'=>'es_ES','last-translator'=>'','language-team'=>'','messages'=>['ERROR: Sorry, that username is not allowed.'=>'ERROR: Lo siento, ese nombre de usuario no está permitido.','Invalid parameter.'=>'Parámetro no válido. ','[%1$s] Confirm Action: %2$s'=>'[%1$s] Confirmar la acción: %2$s','[%s] Erasure Request Fulfilled'=>'[%s] Solicitud de borrado completada','[%s] Personal Data Export'=>'[%s] Exportación de datos personales','menu(Currently set to: %s)'=>'(Actualmente fijado en: %s)','menu location(Current: %s)'=>'(Actual: %s)','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/languages/ja_JP.mo.php b/tests/phpunit/data/languages/ja_JP.mo.php deleted file mode 100644 index 37751a8594a4d..0000000000000 --- a/tests/phpunit/data/languages/ja_JP.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'2018-04-21 18:27+0900','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=n != 1;','x-generator'=>'Poedit 1.8.10','project-id-version'=>'5.0.x','language'=>'ja_JP','messages'=>['Update %s now'=>'今すぐ %s を更新','Word count type. Do not translate!words'=>'characters_including_spaces','comment_excerpt_length20'=>'40','draft_length10'=>'40','excerpt_length55'=>'110','html_lang_attribute'=>'ja','number_format_decimal_point'=>'number_format_decimal_point','number_format_thousands_sep'=>'number_format_thousands_sep','text directionltr'=>'ltr']]; diff --git a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php deleted file mode 100644 index c52a82df306ea..0000000000000 --- a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-de_DE.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2022-07-04 18:48+0200','last-translator'=>'Dominik Schilling','language-team'=>'','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Das ist ein Dummy Plugin']]; diff --git a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php b/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php deleted file mode 100644 index 9da781b6d10cc..0000000000000 --- a/tests/phpunit/data/plugins/custom-internationalized-plugin/languages/custom-internationalized-plugin-es_ES.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2022-07-04 18:48+0200','last-translator'=>'Dominik Schilling','language-team'=>'','language'=>'es_ES','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Este es un plugin dummy']]; diff --git a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php deleted file mode 100644 index 75e90b638cde0..0000000000000 --- a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/de_DE.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2022-07-04 18:47+0200','last-translator'=>'','language-team'=>'','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Das ist ein Dummy Theme']]; diff --git a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php b/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php deleted file mode 100644 index abf62de99ac3c..0000000000000 --- a/tests/phpunit/data/themedir1/custom-internationalized-theme/languages/es_ES.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2022-07-04 18:47+0200','last-translator'=>'','language-team'=>'','language'=>'es_ES','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','plural-forms'=>'nplurals=2; plural=(n != 1);','x-generator'=>'Poedit 3.1','x-poedit-basepath'=>'.','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Este es un tema dummy']]; diff --git a/tests/phpunit/includes/class-dummy-language-pack-upgrader.php b/tests/phpunit/includes/class-dummy-language-pack-upgrader.php deleted file mode 100644 index 1a6dc5aa84028..0000000000000 --- a/tests/phpunit/includes/class-dummy-language-pack-upgrader.php +++ /dev/null @@ -1,74 +0,0 @@ -} $options - * @phpstan-return array{source: string, source_files: string[], destination: string, destination_name: string, local_destination: string, remote_destination: string, clear_destination: mixed}|false|WP_Error - */ - public function run( $options ) { - $defaults = array( - 'package' => '', - // Please always pass this. - 'destination' => '', - // ...and this. - 'clear_destination' => false, - 'clear_working' => true, - 'abort_if_destination_exists' => true, - // Abort if the destination directory exists. Pass clear_destination as false please. - 'is_multi' => false, - 'hook_extra' => array(), - // Pass any extra $hook_extra args here, this will be passed to any hooked filters. - ); - - $options = wp_parse_args( $options, $defaults ); - - $result = array( - 'source' => '/tmp/source', - 'source_files' => array( - 'foo.txt', - 'bar.txt', - ), - 'destination' => '/tmp/destination', - 'destination_name' => 'destination', - 'local_destination' => '/tmp/destination', - 'remote_destination' => '/tmp/destination', - 'clear_destination' => $options['clear_destination'], - ); - - $this->result = $result; - - return $result; - } -} diff --git a/tests/phpunit/includes/class-dummy-plugin-upgrader.php b/tests/phpunit/includes/class-dummy-plugin-upgrader.php deleted file mode 100644 index 78a1cc8b6ca04..0000000000000 --- a/tests/phpunit/includes/class-dummy-plugin-upgrader.php +++ /dev/null @@ -1,74 +0,0 @@ -} $options - * @phpstan-return array{source: string, source_files: string[], destination: string, destination_name: string, local_destination: string, remote_destination: string, clear_destination: mixed}|false|WP_Error - */ - public function run( $options ) { - $defaults = array( - 'package' => '', - // Please always pass this. - 'destination' => '', - // ...and this. - 'clear_destination' => false, - 'clear_working' => true, - 'abort_if_destination_exists' => true, - // Abort if the destination directory exists. Pass clear_destination as false please. - 'is_multi' => false, - 'hook_extra' => array(), - // Pass any extra $hook_extra args here, this will be passed to any hooked filters. - ); - - $options = wp_parse_args( $options, $defaults ); - - $result = array( - 'source' => '/tmp/source', - 'source_files' => array( - 'foo.txt', - 'bar.txt', - ), - 'destination' => '/tmp/destination', - 'destination_name' => 'destination', - 'local_destination' => '/tmp/destination', - 'remote_destination' => '/tmp/destination', - 'clear_destination' => $options['clear_destination'], - ); - - $this->result = $result; - - return $result; - } -} diff --git a/tests/phpunit/includes/class-dummy-upgrader-skin.php b/tests/phpunit/includes/class-dummy-upgrader-skin.php deleted file mode 100644 index 0f3b4b72a59a9..0000000000000 --- a/tests/phpunit/includes/class-dummy-upgrader-skin.php +++ /dev/null @@ -1,33 +0,0 @@ -temp_filename( $file_contents ); @@ -138,14 +138,12 @@ public function test_invalid_files( string $type, string $file_contents, $expect } /** - * @return array{0: array{0: 'mo'|'json'|'php', 1: string|false, 2?: string}} + * @return array{0: array{0: 'mo'|'php', 1: string|false, 2?: string}} */ public function data_invalid_files(): array { return array( array( 'php', '' ), array( 'php', 'temp_filename(); @@ -543,10 +540,10 @@ public function test_convert_format( string $source_file, string $destination_fo } /** - * @return array + * @return array */ public function data_export_matrix(): array { - $formats = array( 'mo', 'json', 'php' ); + $formats = array( 'mo', 'php' ); $matrix = array(); diff --git a/tests/phpunit/tests/l10n/gingerMoIntegration.php b/tests/phpunit/tests/l10n/gingerMoIntegration.php index 78ac7f344d281..3f7d0df1ac454 100644 --- a/tests/phpunit/tests/l10n/gingerMoIntegration.php +++ b/tests/phpunit/tests/l10n/gingerMoIntegration.php @@ -10,27 +10,7 @@ class Ginger_MO_Integration_Tests extends WP_UnitTestCase { * @return void */ public function tear_down() { - $generated_translation_files = array( - DIR_TESTDATA . '/pomo/simple.mo.php', - DIR_TESTDATA . '/pomo/simple.mo.json', - DIR_TESTDATA . '/pomo/simple.mo.json', - DIR_TESTDATA . '/pomo/context.mo.php', - WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php', - WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php', - WP_LANG_DIR . '/de_DE.mo.php', - ); - - foreach ( $generated_translation_files as $file ) { - if ( file_exists( $file ) ) { - $this->unlink( $file ); - } - } - - remove_all_filters( 'convert_translation_files' ); remove_all_filters( 'translation_file_format' ); - - remove_all_filters( 'filesystem_method' ); - unload_textdomain( 'wp-tests-domain' ); } @@ -114,40 +94,11 @@ public function test_load_textdomain_existing_override() { * * @return void */ - public function test_load_textdomain_mo_files() { - add_filter( - 'translation_file_format', - static function () { - return 'mo'; - } - ); - - $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - - $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - - $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); - $this->assertTrue( $unload_mo_successful ); - $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php' ); - } - - /** - * @covers ::load_textdomain - * - * @return void - */ - public function test_load_textdomain_creates_and_reads_php_files() { - $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - - $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - + public function test_load_textdomain_php_files() { $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); - $this->assertTrue( $unload_mo_successful ); - $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); $this->assertTrue( $unload_php_successful ); } @@ -157,7 +108,7 @@ public function test_load_textdomain_creates_and_reads_php_files() { * * @return void */ - public function test_load_textdomain_creates_and_reads_php_files_if_filtered_format_is_unsupported() { + public function test_load_textdomain_reads_php_files_if_filtered_format_is_unsupported() { add_filter( 'translation_file_format', static function () { @@ -175,79 +126,10 @@ static function () { $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); $this->assertTrue( $unload_mo_successful ); - $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); $this->assertTrue( $unload_php_successful ); } - /** - * @covers ::load_textdomain - * - * @return void - */ - public function test_load_textdomain_creates_and_reads_php_files_no_wp_filesystem() { - add_filter( 'filesystem_method', '__return_empty_string' ); - - $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - - $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - - $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); - - $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); - - $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); - $this->assertTrue( $unload_mo_successful ); - $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); - $this->assertTrue( $load_php_successful, 'PHP file not successfully loaded' ); - $this->assertTrue( $unload_php_successful ); - } - - /** - * @covers ::load_textdomain - * - * @return void - */ - public function test_load_textdomain_does_not_create_php_files_if_disabled() { - add_filter( 'convert_translation_files', '__return_false' ); - - $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - - $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - - $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); - $this->assertTrue( $unload_mo_successful ); - $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php' ); - } - - /** - * @covers ::load_textdomain - * - * @return void - */ - public function test_load_textdomain_creates_and_reads_json_files() { - add_filter( - 'translation_file_format', - static function () { - return 'json'; - } - ); - - $load_mo_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - - $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - - $load_json_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.json' ); - - $unload_json_successful = unload_textdomain( 'wp-tests-domain' ); - - $this->assertTrue( $load_mo_successful, 'MO file not successfully loaded' ); - $this->assertTrue( $unload_mo_successful ); - $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.json' ); - $this->assertTrue( $load_json_successful, 'JSON file not successfully loaded' ); - $this->assertTrue( $unload_json_successful ); - } - /** * @covers ::load_textdomain * @@ -355,32 +237,6 @@ public function test_load_textdomain_loads_existing_translation_php_files() { $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); } - /** - * @param string $domain - * @param string $file - * @return void - */ - public function _on_load_textdomain( $domain, $file ) { - remove_action( 'load_textdomain', array( $this, '_on_load_textdomain' ) ); - load_textdomain( $domain, $file ); - } - - /** - * @covers ::load_textdomain - * - * @return void - */ - public function test_load_textdomain_inception_does_not_create_duplicate_files() { - add_action( 'load_textdomain', array( $this, '_on_load_textdomain' ), 10, 2 ); - - // Just to ensure the PHP files exist. - load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo' ); - unload_textdomain( 'wp-tests-domain' ); - - $this->assertFileExists( DIR_TESTDATA . '/pomo/simple.mo.php' ); - $this->assertFileDoesNotExist( DIR_TESTDATA . '/pomo/simple.mo.php.php' ); - } - /** * @covers ::unload_textdomain * @covers Ginger_MO::get_entries @@ -495,202 +351,4 @@ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain $this->assertSame( 'Este es un plugin dummy', $actual ); $this->assertSame( 'This is a dummy plugin', $after ); } - - /** - * @covers ::upgrader_process_complete - * - * @return void - */ - public function test_create_translation_files_after_translations_update() { - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-language-pack-upgrader.php'; - - $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); - - // These translations exist in the core test suite. - // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. - $result = $upgrader->bulk_upgrade( - array( - (object) array( - 'type' => 'plugin', - 'slug' => 'internationalized-plugin', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'theme', - 'slug' => 'internationalized-theme', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'core', - 'slug' => 'default', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - ) - ); - - $this->assertIsNotBool( $result ); - $this->assertNotWPError( $result ); - $this->assertNotEmpty( $result ); - - $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); - } - - /** - * @covers ::upgrader_process_complete - * - * @return void - */ - public function test_create_translation_files_after_translations_update_if_filtered_format_is_unsupported() { - add_filter( - 'translation_file_format', - static function () { - return 'unknown-format'; - } - ); - - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; - - $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); - - // These translations exist in the core test suite. - // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. - $result = $upgrader->bulk_upgrade( - array( - (object) array( - 'type' => 'plugin', - 'slug' => 'internationalized-plugin', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'theme', - 'slug' => 'internationalized-theme', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'core', - 'slug' => 'default', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - ) - ); - - $this->assertIsNotBool( $result ); - $this->assertNotWPError( $result ); - $this->assertNotEmpty( $result ); - - $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); - } - - /** - * @covers ::upgrader_process_complete - * - * @return void - */ - public function test_create_translation_files_after_translations_update_no_wp_filesystem() { - $callback = static function () { - add_filter( 'filesystem_method', '__return_empty_string' ); - }; - - add_action( 'upgrader_process_complete', $callback, 1 ); - - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-language-pack-upgrader.php'; - - $upgrader = new Dummy_Language_Pack_Upgrader( new Dummy_Upgrader_Skin() ); - - // These translations exist in the core test suite. - // See https://github.com/WordPress/wordpress-develop/tree/e3d345800d3403f3902dc7b18c1ddb07158b0bd3/tests/phpunit/data/languages. - $result = $upgrader->bulk_upgrade( - array( - (object) array( - 'type' => 'plugin', - 'slug' => 'internationalized-plugin', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'theme', - 'slug' => 'internationalized-theme', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - (object) array( - 'type' => 'core', - 'slug' => 'default', - 'language' => 'de_DE', - 'version' => '99.9.9', - 'package' => '/tmp/notused.zip', - ), - ) - ); - - remove_action( 'upgrader_process_complete', $callback, 1 ); - - $this->assertIsNotBool( $result ); - $this->assertNotWPError( $result ); - $this->assertNotEmpty( $result ); - - $this->assertFileExists( WP_LANG_DIR . '/plugins/internationalized-plugin-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/themes/internationalized-theme-de_DE.mo.php' ); - $this->assertFileExists( WP_LANG_DIR . '/de_DE.mo.php' ); - } - - /** - * @covers ::upgrader_process_complete - * - * @return void - */ - public function test_do_not_create_translations_after_plugin_update() { - require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; - require_once ABSPATH . 'wp-admin/includes/class-plugin-upgrader.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-upgrader-skin.php'; - require_once DIR_TESTROOT . '/includes/class-dummy-plugin-upgrader.php'; - - $upgrader = new Dummy_Plugin_Upgrader( new Dummy_Upgrader_Skin() ); - - set_site_transient( - 'update_plugins', - (object) array( - 'response' => array( - 'custom-internationalized-plugin/custom-internationalized-plugin.php' => (object) array( - 'package' => 'https://urltozipfile.local', - ), - ), - ) - ); - - $result = $upgrader->bulk_upgrade( - array( - 'custom-internationalized-plugin/custom-internationalized-plugin.php', - ) - ); - - $this->assertNotFalse( $result ); - $this->assertFileDoesNotExist( WP_LANG_DIR . '/plugins/custom-internationalized-plugin-de_DE.php' ); - $this->assertFileDoesNotExist( WP_PLUGIN_DIR . '/plugins/custom-internationalized-plugin/custom-internationalized-plugin-de_DE.php' ); - } } From b474eb298afcb7630d4bd32d5f68b7594b1c14f9 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 5 Oct 2023 09:56:58 +0200 Subject: [PATCH 08/65] Remove some more files --- .../languages/plugins/internationalized-plugin-de_DE.mo.php | 2 -- .../languages/plugins/internationalized-plugin-es_ES.mo.php | 2 -- tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php | 2 -- .../data/languages/themes/internationalized-theme-de_DE.mo.php | 2 -- tests/phpunit/data/pomo/plural.mo.php | 2 -- tests/phpunit/data/pomo/simple.mo.php | 3 --- 6 files changed, 13 deletions(-) delete mode 100644 tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php delete mode 100644 tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php delete mode 100644 tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php delete mode 100644 tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php delete mode 100644 tests/phpunit/data/pomo/plural.mo.php delete mode 100644 tests/phpunit/data/pomo/simple.mo.php diff --git a/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php b/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php deleted file mode 100644 index 4d41f632ba13e..0000000000000 --- a/tests/phpunit/data/languages/plugins/internationalized-plugin-de_DE.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2020-10-20 17:11+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','language-team'=>'','last-translator'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Das ist ein Dummy Plugin']]; diff --git a/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php b/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php deleted file mode 100644 index 0795ed3e99812..0000000000000 --- a/tests/phpunit/data/languages/plugins/internationalized-plugin-es_ES.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2020-10-20 17:12+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','language-team'=>'','last-translator'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy plugin'=>'Este es un plugin dummy']]; diff --git a/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php b/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php deleted file mode 100644 index b1e6fb74354e4..0000000000000 --- a/tests/phpunit/data/languages/plugins/notice-pl_PL.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2021-08-11 12:25+0200','language'=>'pl_PL','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 3.0','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','last-translator'=>'','language-team'=>'','x-poedit-searchpath-0'=>'.','messages'=>['block descriptionShows warning, error or success notices…'=>'Wyświetla ostrzeżenie, błąd lub powiadomienie o sukcesie…','block keywordalert'=>'ostrzeżenie','block keywordmessage'=>'wiadomość','block style labelDefault'=>'Domyślny','block style labelOther'=>'Inny','block titleNotice'=>'Powiadomienie','block variation descriptionShows error.'=>'Wyświetla błąd.','block variation keywordfailure'=>'niepowodzenie','block variation titleError'=>'Błąd']]; diff --git a/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php b/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php deleted file mode 100644 index db405ba9fadaf..0000000000000 --- a/tests/phpunit/data/languages/themes/internationalized-theme-de_DE.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'','po-revision-date'=>'2020-10-20 17:11+0200','language'=>'de_DE','mime-version'=>'1.0','content-type'=>'text/plain; charset=UTF-8','content-transfer-encoding'=>'8bit','x-generator'=>'Poedit 2.4.1','x-poedit-basepath'=>'.','plural-forms'=>'nplurals=2; plural=(n != 1);','x-poedit-keywordslist'=>'__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c','x-textdomain-support'=>'yes','last-translator'=>'','language-team'=>'','x-poedit-searchpath-0'=>'.','messages'=>['This is a dummy theme'=>'Das ist ein Dummy Theme']]; diff --git a/tests/phpunit/data/pomo/plural.mo.php b/tests/phpunit/data/pomo/plural.mo.php deleted file mode 100644 index 8ad4b285b1638..0000000000000 --- a/tests/phpunit/data/pomo/plural.mo.php +++ /dev/null @@ -1,2 +0,0 @@ -'text/plain; charset=UTF-8','plural-forms'=>'nplurals=5; plural=n != 1;','messages'=>['one dragon' . "\0" . '%d dragons'=>'oney dragoney' . "\0" . 'twoey dragoney' . "\0" . 'manyey dragoney' . "\0" . 'manyeyey dragoney' . "\0" . 'manyeyeyey dragoney']]; diff --git a/tests/phpunit/data/pomo/simple.mo.php b/tests/phpunit/data/pomo/simple.mo.php deleted file mode 100644 index 078c8a95cb6f8..0000000000000 --- a/tests/phpunit/data/pomo/simple.mo.php +++ /dev/null @@ -1,3 +0,0 @@ -'WordPress 2.6-bleeding','report-msgid-bugs-to'=>'wp-polyglots@lists.automattic.com','messages'=>['baba'=>'dyado','kuku -ruku'=>'yes']]; From 3e286240fe8662aa5fca6d8836ac606ee32e444a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 5 Oct 2023 09:59:11 +0200 Subject: [PATCH 09/65] Remove unlink --- tests/phpunit/tests/l10n/gingerMo.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/phpunit/tests/l10n/gingerMo.php b/tests/phpunit/tests/l10n/gingerMo.php index 0413fd21b0c1d..ad293900dbe6e 100644 --- a/tests/phpunit/tests/l10n/gingerMo.php +++ b/tests/phpunit/tests/l10n/gingerMo.php @@ -11,14 +11,6 @@ class Ginger_MO_Translations_Tests extends WP_UnitTestCase { */ public function tear_down() { unload_textdomain( 'wp-tests-domain' ); - - if ( file_exists( DIR_TESTDATA . '/pomo/simple.mo.php' ) ) { - $this->unlink( DIR_TESTDATA . '/pomo/simple.mo.php' ); - } - - if ( file_exists( DIR_TESTDATA . '/pomo/plural.php' ) ) { - $this->unlink( DIR_TESTDATA . '/pomo/plural.php' ); - } } /** From fad84598c72cfcb7330e3676716bf82c3a2ee670 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 5 Oct 2023 10:05:12 +0200 Subject: [PATCH 10/65] Delete PHP translation file on plugin uninstall --- src/wp-admin/includes/plugin.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wp-admin/includes/plugin.php b/src/wp-admin/includes/plugin.php index f55bbd80eb5df..a134696546d78 100644 --- a/src/wp-admin/includes/plugin.php +++ b/src/wp-admin/includes/plugin.php @@ -1009,6 +1009,7 @@ function delete_plugins( $plugins, $deprecated = '' ) { foreach ( $translations as $translation => $data ) { $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' ); $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' ); + $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo.php' ); $json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' ); if ( $json_translation_files ) { From 1d7964afba612f22f4c9b84a7fc01b784d0f9928 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 5 Oct 2023 10:31:12 +0200 Subject: [PATCH 11/65] =?UTF-8?q?Add=20back=20one=20file=20that=E2=80=99s?= =?UTF-8?q?=20actually=20used?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/phpunit/data/pomo/simple.mo.php | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/phpunit/data/pomo/simple.mo.php diff --git a/tests/phpunit/data/pomo/simple.mo.php b/tests/phpunit/data/pomo/simple.mo.php new file mode 100644 index 0000000000000..078c8a95cb6f8 --- /dev/null +++ b/tests/phpunit/data/pomo/simple.mo.php @@ -0,0 +1,3 @@ +'WordPress 2.6-bleeding','report-msgid-bugs-to'=>'wp-polyglots@lists.automattic.com','messages'=>['baba'=>'dyado','kuku +ruku'=>'yes']]; From 3e5e34433767149f6f47de895500972a238695fa Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Oct 2023 10:45:42 +0200 Subject: [PATCH 12/65] docblock updates --- src/wp-includes/compat.php | 32 +++++++++ .../class-ginger-mo-translation-file-mo.php | 20 ++++-- .../class-ginger-mo-translation-file-php.php | 54 ++++----------- .../class-ginger-mo-translation-file.php | 65 ++++++++++++++----- .../class-ginger-mo-translations.php | 3 +- src/wp-includes/ginger-mo/class-ginger-mo.php | 61 ++++++++++++----- 6 files changed, 155 insertions(+), 80 deletions(-) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index 5bfdbc23d6d60..09bbda67de650 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -497,3 +497,35 @@ function str_ends_with( $haystack, $needle ) { if ( ! defined( 'IMG_WEBP' ) ) { define( 'IMG_WEBP', IMAGETYPE_WEBP ); } + +if ( ! function_exists( 'array_is_list' ) ) { + /** + * Determines if the given array is a list. + * + * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. + * + * Polyfill for array_is_list() in PHP 8.1. + * + * @see https://github.com/symfony/polyfill-php81/tree/main + * + * @since 6.5.0 + * + * @param array $arr The array being evaluated. + * @return bool True if array is a list, false otherwise. + */ + function array_is_list( $arr ) { + if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) { + return true; + } + + $next_key = -1; + + foreach ( $arr as $k => $v ) { + if ( ++$next_key !== $k ) { + return false; + } + } + + return true; + } +} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php index f54ec917bb922..676611369271a 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php @@ -1,12 +1,16 @@ parsed = true; $file_contents = file_get_contents( $this->file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents @@ -163,9 +173,11 @@ protected function parse_file(): bool { /** * Exports translation contents as a string. * + * @since 6.5.0 + * * @return string Translation file contents. */ - public function export(): string { + public function export() { // Prefix the headers as the first key. $headers_string = ''; foreach ( $this->headers as $header => $value ) { diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php index ae1d451af445f..b63bc5e737d91 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-php.php @@ -1,18 +1,22 @@ parsed = true; @@ -40,67 +44,35 @@ protected function parse_file() { /** * Exports translation contents as a string. * + * @since 6.5.0 + * * @return string Translation file contents. */ - public function export(): string { + public function export() { $data = array_merge( $this->headers, array( 'messages' => $this->entries ) ); return 'var_export( $data ) . ';' . PHP_EOL; } - /** - * Determines if the given array is a list. - * - * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. - * - * Polyfill for array_is_list() in PHP 8.1. - * - * @see https://github.com/symfony/polyfill-php81/tree/main - * - * @SuppressWarnings(PHPMD.UnusedLocalVariable) - * - * @codeCoverageIgnore - * - * @param array $arr The array being evaluated. - * @return bool True if array is a list, false otherwise. - */ - private function array_is_list( array $arr ): bool { - if ( function_exists( 'array_is_list' ) ) { - return array_is_list( $arr ); - } - - if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) { - return true; - } - - $next_key = -1; - - foreach ( $arr as $k => $v ) { - if ( ++$next_key !== $k ) { - return false; - } - } - - return true; - } - /** * Outputs or returns a parsable string representation of a variable. * * Like {@see var_export()} but "minified", using short array syntax * and no newlines. * + * @since 6.5.0 + * * @param mixed $value The variable you want to export. * @return string The variable representation. */ - private function var_export( $value ): string { + private function var_export( $value ) { if ( ! is_array( $value ) ) { return var_export( $value, true ); } $entries = array(); - $is_list = $this->array_is_list( $value ); + $is_list = array_is_list( $value ); foreach ( $value as $key => $val ) { $entries[] = $is_list ? $this->var_export( $val ) : var_export( $key, true ) . '=>' . $this->var_export( $val ); diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php index 51546dba41533..84005a46f8bee 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translation-file.php @@ -1,17 +1,23 @@ */ protected $headers = array(); @@ -19,6 +25,8 @@ abstract class Ginger_MO_Translation_File { /** * Whether file has been parsed. * + * @since 6.5.0 + * * @var bool */ protected $parsed = false; @@ -26,6 +34,8 @@ abstract class Ginger_MO_Translation_File { /** * Error information. * + * @since 6.5.0 + * * @var bool|string */ protected $error = false; @@ -33,6 +43,8 @@ abstract class Ginger_MO_Translation_File { /** * File name. * + * @since 6.5.0 + * * @var string */ protected $file = ''; @@ -40,6 +52,8 @@ abstract class Ginger_MO_Translation_File { /** * Translation entries. * + * @since 6.5.0 + * * @var array */ protected $entries = array(); @@ -47,6 +61,8 @@ abstract class Ginger_MO_Translation_File { /** * Plural forms function. * + * @since 6.5.0 + * * @var callable|null Plural forms. */ protected $plural_forms = null; @@ -54,7 +70,9 @@ abstract class Ginger_MO_Translation_File { /** * Constructor. * - * @param string $file File to load. + * @since 6.5.0 + * + * @param string $file File to load. */ protected function __construct( string $file ) { $this->file = $file; @@ -63,11 +81,11 @@ protected function __construct( string $file ) { /** * Creates a new Ginger_MO_Translation_File instance for a given file. * + * @since 6.5.0 + * * @param string $file File name. * @param string|null $filetype Optional. File type. Default inferred from file name. * @return false|Ginger_MO_Translation_File - * - * @phpstan-param 'mo'|'php'|null $filetype */ public static function create( string $file, string $filetype = null ) { if ( ! is_readable( $file ) ) { @@ -94,6 +112,8 @@ public static function create( string $file, string $filetype = null ) { /** * Returns all headers. * + * @since 6.5.0 + * * @return array Headers. */ public function headers() { @@ -106,8 +126,9 @@ public function headers() { /** * Returns all entries. * - * @return array Entries. - * @phstan-return array> Entries. + * @since 6.5.0 + * + * @return array Entries. */ public function entries() { if ( ! $this->parsed ) { @@ -120,7 +141,7 @@ public function entries() { /** * Returns the current error information. * - * @phpstan-impure + * @since 6.5.0 * * @return bool|string Error */ @@ -131,15 +152,19 @@ public function error() { /** * Returns the file name. * + * @since 6.5.0 + * * @return string File name. */ - public function get_file(): string { + public function get_file() { return $this->file; } /** * Translates a given string. * + * @since 6.5.0 + * * @param string $text String to translate. * @return false|string Translation(s) on success, false otherwise. */ @@ -154,10 +179,12 @@ public function translate( string $text ) { /** * Returns the plural form for a count. * + * @since 6.5.0 + * * @param int $number Count. * @return int Plural form. */ - public function get_plural_form( int $number ): int { + public function get_plural_form( int $number ) { if ( ! $this->parsed ) { $this->parse_file(); } @@ -171,7 +198,7 @@ public function get_plural_form( int $number ): int { /** * Plural form. * - * @phpstan-var int $result Plural form. + * @var int $result Plural form. */ $result = call_user_func( $this->plural_forms, $number ); return $result; @@ -183,7 +210,9 @@ public function get_plural_form( int $number ): int { /** * Makes a function, which will return the right translation index, according to the - * plural forms header + * plural forms header. + * + * @since 6.5.0 * * @param string $expression Plural form expression. * @return callable(int $num): int Plural forms function. @@ -201,11 +230,11 @@ public function make_plural_form_function( string $expression ) { /** * Creates a new Ginger_MO_Translation_File instance for a given file. * + * @since 6.5.0 + * * @param string $file Source file name. * @param string $filetype Desired target file type. * @return string|false Transformed translation file contents on success, false otherwise. - * - * @phpstan-param 'mo'|'php' $filetype */ public static function transform( string $file, string $filetype ) { $source = self::create( $file ); @@ -237,10 +266,12 @@ public static function transform( string $file, string $filetype ) { /** * Imports translations from another file. * + * @since 6.5.0 + * * @param Ginger_MO_Translation_File $source Source file. * @return bool True on success, false otherwise. */ - protected function import( Ginger_MO_Translation_File $source ): bool { + protected function import( Ginger_MO_Translation_File $source ) { if ( false !== $source->error() ) { return false; } @@ -255,7 +286,7 @@ protected function import( Ginger_MO_Translation_File $source ): bool { /** * Parses the file. * - * @return void + * @since 6.5.0 */ abstract protected function parse_file(); @@ -263,7 +294,9 @@ abstract protected function parse_file(); /** * Exports translation contents as a string. * + * @since 6.5.0 + * * @return string Translation file contents. */ - abstract public function export(): string; + abstract public function export(); } diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php index 9aade2f29e8f0..ae47b38255fac 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php @@ -57,9 +57,10 @@ public function __get( string $name ) { /** * Magic setter. * + * @since 6.5.0 + * * @param string $name Property name. * @param mixed $value Property value. - * @return void */ public function __set( string $name, $value ) {} diff --git a/src/wp-includes/ginger-mo/class-ginger-mo.php b/src/wp-includes/ginger-mo/class-ginger-mo.php index b4bfbe255eecb..f6882a78180f2 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo.php @@ -1,19 +1,23 @@ [ Textdomain => [ .., .. ] ] ] * + * @since 6.5.0 + * * @var array> */ protected $loaded_translations = array(); @@ -32,6 +38,8 @@ class Ginger_MO { * * [ Filename => [ Locale => [ Textdomain => Ginger_MO_Translation_File ] ] ] * + * @since 6.5.0 + * * @var array>> */ protected $loaded_files = array(); @@ -39,9 +47,11 @@ class Ginger_MO { /** * Returns the Ginger_MO singleton. * + * @since 6.5.0 + * * @return Ginger_MO */ - public static function instance(): Ginger_MO { + public static function instance() { static $instance; if ( ! $instance ) { @@ -54,17 +64,20 @@ public static function instance(): Ginger_MO { /** * Returns the current locale. * + * @since 6.5.0 + * * @return string Locale. */ - public function get_locale(): string { + public function get_locale() { return $this->current_locale; } /** * Sets the current locale. * + * @since 6.5.0 + * * @param string $locale Locale. - * @return void */ public function set_locale( string $locale ) { $this->current_locale = $locale; @@ -73,14 +86,14 @@ public function set_locale( string $locale ) { /** * Loads a translation file. * - * @SuppressWarnings(PHPMD.NPathComplexity) + * @since 6.5.0 * * @param string $translation_file Translation file. * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. * @return bool True on success, false otherwise. */ - public function load( string $translation_file, string $textdomain = 'default', string $locale = null ): bool { + public function load( string $translation_file, string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } @@ -129,14 +142,14 @@ public function load( string $translation_file, string $textdomain = 'default', /** * Unload all translation files or a specific one for a given text domain. * - * @SuppressWarnings(PHPMD.NPathComplexity) + * @since 6.5.0 * * @param string $textdomain Text domain. * @param Ginger_MO_Translation_File|string $mo Translation file instance or file name. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ - public function unload( string $textdomain = 'default', $mo = null, string $locale = null ): bool { + public function unload( string $textdomain = 'default', $mo = null, string $locale = null ) { if ( ! $this->is_loaded( $textdomain, $locale ) ) { return false; } @@ -199,11 +212,13 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca /** * Determines whether translations are loaded for a given text domain. * + * @since 6.5.0 + * * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. * @return bool True if there are any loaded translations, false otherwise. */ - public function is_loaded( string $textdomain = 'default', string $locale = null ): bool { + public function is_loaded( string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } @@ -215,6 +230,8 @@ public function is_loaded( string $textdomain = 'default', string $locale = null /** * Translates a singular string. * + * @since 6.5.0 + * * @param string $text Text to translate. * @param string $context Optional. Context for the string. * @param string $textdomain Text domain. @@ -241,7 +258,7 @@ public function translate( string $text, string $context = '', string $textdomai * Checks both singular+plural combinations as well as just singulars, * in case the translation file does not store the plural. * - * @todo Revisit this. + * @since 6.5.0 * * @param array{0: string, 1: string} $plurals Pair of singular and plural translation. * @param int $number Number of items. @@ -271,19 +288,19 @@ public function translate_plural( array $plurals, int $number, string $context = $source = $translation['source']; $num = $source->get_plural_form( $number ); - // TODO: Use nplurals from Plural-Forms header? - // See \Translations::translate_plural() in core. - + // See \Translations::translate_plural(). return $translation['entries'][ $num ] ?? $translation['entries'][0]; } /** * Returns all existing headers for a given text domain. * + * @since 6.5.0 + * * @param string $textdomain Text domain. * @return array Headers. */ - public function get_headers( string $textdomain = 'default' ): array { + public function get_headers( string $textdomain = 'default' ) { if ( array() === $this->loaded_translations ) { return array(); } @@ -302,10 +319,12 @@ public function get_headers( string $textdomain = 'default' ): array { /** * Normalizes header names to be capitalized. * + * @since 6.5.0 + * * @param string $header Header name. * @return string Normalized header name. */ - protected function normalize_header( string $header ): string { + protected function normalize_header( string $header ) { $parts = explode( '-', $header ); $parts = array_map( 'ucfirst', $parts ); return implode( '-', $parts ); @@ -314,10 +333,12 @@ protected function normalize_header( string $header ): string { /** * Returns all entries for a given text domain. * + * @since 6.5.0 + * * @param string $textdomain Text domain. * @return array Entries. */ - public function get_entries( string $textdomain = 'default' ): array { + public function get_entries( string $textdomain = 'default' ) { if ( array() === $this->loaded_translations ) { return array(); } @@ -334,6 +355,8 @@ public function get_entries( string $textdomain = 'default' ): array { /** * Locates translation for a given string and text domain. * + * @since 6.5.0 + * * @param string $singular Singular translation. * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. @@ -366,11 +389,13 @@ protected function locate_translation( string $singular, string $textdomain = 'd /** * Returns all translation files for a given text domain. * + * @since 6.5.0 + * * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. * @return Ginger_MO_Translation_File[] List of translation files. */ - protected function get_files( string $textdomain = 'default', string $locale = null ): array { + protected function get_files( string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } From 8064c47f91bcb4a7c5c5ba8a002696b3df15680a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 18 Oct 2023 10:48:57 +0200 Subject: [PATCH 13/65] More doc updates --- src/wp-includes/compat.php | 4 ++-- .../ginger-mo/class-ginger-mo-translations.php | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index 09bbda67de650..b59ee6d48e1fc 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -500,12 +500,12 @@ function str_ends_with( $haystack, $needle ) { if ( ! function_exists( 'array_is_list' ) ) { /** + * Polyfill for `array_is_list()` function added in PHP 8.1. + * * Determines if the given array is a list. * * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. * - * Polyfill for array_is_list() in PHP 8.1. - * * @see https://github.com/symfony/polyfill-php81/tree/main * * @since 6.5.0 diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php index ae47b38255fac..eb14a3ab01d7f 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php +++ b/src/wp-includes/ginger-mo/class-ginger-mo-translations.php @@ -1,13 +1,17 @@ $headers * @property-read array $entries */ @@ -15,6 +19,8 @@ class Ginger_MO_Translations { /** * Text domain. * + * @since 6.5.0 + * * @var string */ protected $textdomain = 'default'; @@ -22,6 +28,8 @@ class Ginger_MO_Translations { /** * Constructor. * + * @since 6.5.0 + * * @param string $textdomain Text domain. */ public function __construct( string $textdomain = 'default' ) { @@ -31,6 +39,8 @@ public function __construct( string $textdomain = 'default' ) { /** * Magic getter for backward compatibility. * + * @since 6.5.0 + * * @param string $name Property name. * @return mixed */ @@ -69,6 +79,8 @@ public function __set( string $name, $value ) {} * * @see MO::make_entry() * + * @since 6.5.0 + * * @param string $original Original string to translate from MO file. Might contain * 0x04 as context separator or 0x00 as singular/plural separator. * @param string $translations Translation strings from MO file. @@ -99,6 +111,8 @@ private function make_entry( $original, $translations ): Translation_Entry { /** * Translates a plural string. * + * @since 6.5.0 + * * @param string|null $singular Singular string. * @param string|null $plural Plural string. * @param int|float $count Count. Should be an integer, but some plugins pass floats. @@ -122,6 +136,8 @@ public function translate_plural( $singular, $plural, $count = 1, $context = '' /** * Translates a singular string. * + * @since 6.5.0 + * * @param string|null $singular Singular string. * @param string|null $context Context. * @return string|null Translation if it exists, or the unchanged singular string From fb7013604e225b3150f40dda598f3c23e22bf1a1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Oct 2023 12:40:04 +0200 Subject: [PATCH 14/65] Rename classes --- src/wp-includes/class-wp-locale-switcher.php | 2 +- .../class-wp-i18n-translation-controller.php} | 32 +- .../class-wp-i18n-translation-file-mo.php} | 8 +- .../class-wp-i18n-translation-file-php.php} | 8 +- .../class-wp-i18n-translation-file.php} | 26 +- .../class-wp-i18n-translations.php} | 16 +- src/wp-includes/l10n.php | 16 +- src/wp-settings.php | 12 +- .../{ginger-mo => i18n}/example-simple.mo | Bin .../{ginger-mo => i18n}/example-simple.php | 0 .../{ginger-mo => i18n}/example-simple.po | 0 .../phpunit/data/{ginger-mo => i18n}/fa_IR.mo | Bin .../data/{ginger-mo => i18n}/plural.mo | Bin .../data/{ginger-mo => i18n}/simple.mo | Bin tests/phpunit/tests/l10n/gingerMoConvert.php | 569 ------------------ tests/phpunit/tests/l10n/wpI18nConvert.php | 569 ++++++++++++++++++ ...on.php => wpI18nTranslationController.php} | 68 +-- .../{gingerMo.php => wpI18nTranslations.php} | 20 +- 18 files changed, 673 insertions(+), 673 deletions(-) rename src/wp-includes/{ginger-mo/class-ginger-mo.php => i18n/class-wp-i18n-translation-controller.php} (90%) rename src/wp-includes/{ginger-mo/class-ginger-mo-translation-file-mo.php => i18n/class-wp-i18n-translation-file-mo.php} (96%) rename src/wp-includes/{ginger-mo/class-ginger-mo-translation-file-php.php => i18n/class-wp-i18n-translation-file-php.php} (89%) rename src/wp-includes/{ginger-mo/class-ginger-mo-translation-file.php => i18n/class-wp-i18n-translation-file.php} (88%) rename src/wp-includes/{ginger-mo/class-ginger-mo-translations.php => i18n/class-wp-i18n-translations.php} (84%) rename tests/phpunit/data/{ginger-mo => i18n}/example-simple.mo (100%) rename tests/phpunit/data/{ginger-mo => i18n}/example-simple.php (100%) rename tests/phpunit/data/{ginger-mo => i18n}/example-simple.po (100%) rename tests/phpunit/data/{ginger-mo => i18n}/fa_IR.mo (100%) rename tests/phpunit/data/{ginger-mo => i18n}/plural.mo (100%) rename tests/phpunit/data/{ginger-mo => i18n}/simple.mo (100%) delete mode 100644 tests/phpunit/tests/l10n/gingerMoConvert.php create mode 100644 tests/phpunit/tests/l10n/wpI18nConvert.php rename tests/phpunit/tests/l10n/{gingerMoIntegration.php => wpI18nTranslationController.php} (76%) rename tests/phpunit/tests/l10n/{gingerMo.php => wpI18nTranslations.php} (88%) diff --git a/src/wp-includes/class-wp-locale-switcher.php b/src/wp-includes/class-wp-locale-switcher.php index 8b52fcc4fbec8..028b5e4899d5f 100644 --- a/src/wp-includes/class-wp-locale-switcher.php +++ b/src/wp-includes/class-wp-locale-switcher.php @@ -283,7 +283,7 @@ private function change_locale( $locale ) { $wp_locale = new WP_Locale(); - Ginger_MO::instance()->set_locale( $locale ); + WP_I18n_Translation_Controller::instance()->set_locale( $locale ); /** * Fires when the locale is switched to or restored. diff --git a/src/wp-includes/ginger-mo/class-ginger-mo.php b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php similarity index 90% rename from src/wp-includes/ginger-mo/class-ginger-mo.php rename to src/wp-includes/i18n/class-wp-i18n-translation-controller.php index f6882a78180f2..2d7def6acee1f 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php @@ -1,18 +1,18 @@ > + * @var array> */ protected $loaded_translations = array(); /** * List of loaded translation files. * - * [ Filename => [ Locale => [ Textdomain => Ginger_MO_Translation_File ] ] ] + * [ Filename => [ Locale => [ Textdomain => WP_I18n_Translation_File ] ] ] * * @since 6.5.0 * - * @var array>> + * @var array>> */ protected $loaded_files = array(); /** - * Returns the Ginger_MO singleton. + * Returns the WP_I18n_Translation_Controller singleton. * * @since 6.5.0 * - * @return Ginger_MO + * @return WP_I18n_Translation_Controller */ public static function instance() { static $instance; if ( ! $instance ) { - $instance = new Ginger_MO(); + $instance = new WP_I18n_Translation_Controller(); } return $instance; @@ -117,7 +117,7 @@ public function load( string $translation_file, string $textdomain = 'default', ) { $moe = reset( $this->loaded_files[ $translation_file ][ $locale ] ); } else { - $moe = Ginger_MO_Translation_File::create( $translation_file ); + $moe = WP_I18n_Translation_File::create( $translation_file ); if ( false === $moe || false !== $moe->error() ) { $moe = false; } @@ -125,7 +125,7 @@ public function load( string $translation_file, string $textdomain = 'default', $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] = $moe; - if ( ! $moe instanceof Ginger_MO_Translation_File ) { + if ( ! $moe instanceof WP_I18n_Translation_File ) { return false; } @@ -145,7 +145,7 @@ public function load( string $translation_file, string $textdomain = 'default', * @since 6.5.0 * * @param string $textdomain Text domain. - * @param Ginger_MO_Translation_File|string $mo Translation file instance or file name. + * @param WP_I18n_Translation_File|string $mo Translation file instance or file name. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ @@ -284,7 +284,7 @@ public function translate_plural( array $plurals, int $number, string $context = } } - /* @var Ginger_MO_Translation_File $source */ + /* @var WP_I18n_Translation_File $source */ $source = $translation['source']; $num = $source->get_plural_form( $number ); @@ -360,7 +360,7 @@ public function get_entries( string $textdomain = 'default' ) { * @param string $singular Singular translation. * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. - * @return array{source: Ginger_MO_Translation_File, entries: string[]}|false Translations on success, false otherwise. + * @return array{source: WP_I18n_Translation_File, entries: string[]}|false Translations on success, false otherwise. */ protected function locate_translation( string $singular, string $textdomain = 'default', string $locale = null ) { if ( array() === $this->loaded_translations ) { @@ -393,7 +393,7 @@ protected function locate_translation( string $singular, string $textdomain = 'd * * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. - * @return Ginger_MO_Translation_File[] List of translation files. + * @return WP_I18n_Translation_File[] List of translation files. */ protected function get_files( string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php b/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php similarity index 96% rename from src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php rename to src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php index 676611369271a..a2a4f5fe161e9 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translation-file-mo.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php @@ -1,18 +1,18 @@ error() ) { return false; } diff --git a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php b/src/wp-includes/i18n/class-wp-i18n-translations.php similarity index 84% rename from src/wp-includes/ginger-mo/class-ginger-mo-translations.php rename to src/wp-includes/i18n/class-wp-i18n-translations.php index eb14a3ab01d7f..e5a7850fdcd56 100644 --- a/src/wp-includes/ginger-mo/class-ginger-mo-translations.php +++ b/src/wp-includes/i18n/class-wp-i18n-translations.php @@ -1,21 +1,21 @@ $headers * @property-read array $entries */ -class Ginger_MO_Translations { +class WP_I18n_Translations { /** * Text domain. * @@ -46,7 +46,7 @@ public function __construct( string $textdomain = 'default' ) { */ public function __get( string $name ) { if ( 'entries' === $name ) { - $entries = Ginger_MO::instance()->get_entries( $this->textdomain ); + $entries = WP_I18n_Translation_Controller::instance()->get_entries( $this->textdomain ); $result = array(); @@ -58,7 +58,7 @@ public function __get( string $name ) { } if ( 'headers' === $name ) { - return Ginger_MO::instance()->get_headers( $this->textdomain ); + return WP_I18n_Translation_Controller::instance()->get_headers( $this->textdomain ); } return null; @@ -124,7 +124,7 @@ public function translate_plural( $singular, $plural, $count = 1, $context = '' return $singular; } - $translation = Ginger_MO::instance()->translate_plural( array( $singular, $plural ), (int) $count, (string) $context, $this->textdomain ); + $translation = WP_I18n_Translation_Controller::instance()->translate_plural( array( $singular, $plural ), (int) $count, (string) $context, $this->textdomain ); if ( false !== $translation ) { return $translation; } @@ -147,7 +147,7 @@ public function translate( $singular, $context = '' ) { return $singular; } - $translation = Ginger_MO::instance()->translate( $singular, (string) $context, $this->textdomain ); + $translation = WP_I18n_Translation_Controller::instance()->translate( $singular, (string) $context, $this->textdomain ); if ( false !== $translation ) { return $translation; } diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 5bc75171cff78..f2162cd8509d7 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -789,7 +789,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { } // Ensures the correct locale is set as the current one, in case it was filtered. - Ginger_MO::instance()->set_locale( $locale ); + WP_I18n_Translation_Controller::instance()->set_locale( $locale ); /** * Filters the preferred file format for translation files. @@ -822,17 +822,17 @@ function load_textdomain( $domain, $mofile, $locale = null ) { */ $mofile_preferred = apply_filters( 'load_translation_file', $mofile_preferred, $domain ); - $success = Ginger_MO::instance()->load( $mofile_preferred, $domain, $locale ); + $success = WP_I18n_Translation_Controller::instance()->load( $mofile_preferred, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - Ginger_MO::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + WP_I18n_Translation_Controller::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); } // Unset Noop_Translations reference in get_translations_for_domain. unset( $l10n[ $domain ] ); - $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); + $l10n[ $domain ] = new WP_I18n_Translations( $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); @@ -849,17 +849,17 @@ function load_textdomain( $domain, $mofile, $locale = null ) { /** This filter is documented in wp-includes/l10n.php */ $mofile = apply_filters( 'load_translation_file', $mofile, $domain ); - $success = Ginger_MO::instance()->load( $mofile, $domain, $locale ); + $success = WP_I18n_Translation_Controller::instance()->load( $mofile, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - Ginger_MO::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + WP_I18n_Translation_Controller::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); } // Unset Noop_Translations reference in get_translations_for_domain. unset( $l10n[ $domain ] ); - $l10n[ $domain ] = new Ginger_MO_Translations( $domain ); + $l10n[ $domain ] = new WP_I18n_Translations( $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); } @@ -927,7 +927,7 @@ function unload_textdomain( $domain, $reloadable = false ) { // Since we support multiple locales, we don't actually need to unload reloadable text domains. if ( ! $reloadable ) { - return Ginger_MO::instance()->unload( $domain ); + return WP_I18n_Translation_Controller::instance()->unload( $domain ); } return true; diff --git a/src/wp-settings.php b/src/wp-settings.php index a9e5e114af88b..56854c3229e10 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -114,11 +114,11 @@ require ABSPATH . WPINC . '/class-wp.php'; require ABSPATH . WPINC . '/class-wp-error.php'; require ABSPATH . WPINC . '/pomo/mo.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translations.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-mo.php'; -require ABSPATH . WPINC . '/ginger-mo/class-ginger-mo-translation-file-php.php'; +require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-controller.php'; +require ABSPATH . WPINC . '/i18n/class-wp-i18n-translations.php'; +require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file.php'; +require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file-mo.php'; +require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file-php.php'; /** * @global wpdb $wpdb WordPress database abstraction object. @@ -610,7 +610,7 @@ $GLOBALS['wp_locale_switcher'] = new WP_Locale_Switcher(); $GLOBALS['wp_locale_switcher']->init(); -Ginger_MO::instance()->set_locale( $locale ); +WP_I18n_Translation_Controller::instance()->set_locale( $locale ); // Load the functions for the active theme, for both parent and child theme if applicable. foreach ( wp_get_active_and_valid_themes() as $theme ) { diff --git a/tests/phpunit/data/ginger-mo/example-simple.mo b/tests/phpunit/data/i18n/example-simple.mo similarity index 100% rename from tests/phpunit/data/ginger-mo/example-simple.mo rename to tests/phpunit/data/i18n/example-simple.mo diff --git a/tests/phpunit/data/ginger-mo/example-simple.php b/tests/phpunit/data/i18n/example-simple.php similarity index 100% rename from tests/phpunit/data/ginger-mo/example-simple.php rename to tests/phpunit/data/i18n/example-simple.php diff --git a/tests/phpunit/data/ginger-mo/example-simple.po b/tests/phpunit/data/i18n/example-simple.po similarity index 100% rename from tests/phpunit/data/ginger-mo/example-simple.po rename to tests/phpunit/data/i18n/example-simple.po diff --git a/tests/phpunit/data/ginger-mo/fa_IR.mo b/tests/phpunit/data/i18n/fa_IR.mo similarity index 100% rename from tests/phpunit/data/ginger-mo/fa_IR.mo rename to tests/phpunit/data/i18n/fa_IR.mo diff --git a/tests/phpunit/data/ginger-mo/plural.mo b/tests/phpunit/data/i18n/plural.mo similarity index 100% rename from tests/phpunit/data/ginger-mo/plural.mo rename to tests/phpunit/data/i18n/plural.mo diff --git a/tests/phpunit/data/ginger-mo/simple.mo b/tests/phpunit/data/i18n/simple.mo similarity index 100% rename from tests/phpunit/data/ginger-mo/simple.mo rename to tests/phpunit/data/i18n/simple.mo diff --git a/tests/phpunit/tests/l10n/gingerMoConvert.php b/tests/phpunit/tests/l10n/gingerMoConvert.php deleted file mode 100644 index af177ef8a0522..0000000000000 --- a/tests/phpunit/tests/l10n/gingerMoConvert.php +++ /dev/null @@ -1,569 +0,0 @@ -assertSame( $instance, $instance2 ); - } - - /** - * @return void - */ - public function test_no_files_loaded_returns_false() { - $instance = new Ginger_MO(); - $this->assertFalse( $instance->translate( 'singular' ) ); - $this->assertFalse( $instance->translate_plural( array( 'plural0', 'plural1' ), 1 ) ); - } - - /** - * @covers ::unload - * - * @return void - */ - public function test_unload_not_loaded() { - $instance = new Ginger_MO(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertFalse( $instance->unload( 'unittest' ) ); - } - - /** - * @covers ::load - * @covers ::unload - * @covers ::is_loaded - * @covers ::translate - * @covers ::locate_translation - * @covers ::get_files - * - * @return void - */ - public function test_unload_entire_textdomain() { - $instance = new Ginger_MO(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest' ) ); - - $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); - - $this->assertTrue( $instance->unload( 'unittest' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); - } - - /** - * @covers ::unload - * @covers Ginger_MO_Translation_File::get_file - * - * @return void - */ - public function test_unload_file_is_not_actually_loaded() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/simple.mo' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); - } - - /** - * @covers ::unload - * @covers ::is_loaded - * - * @return void - */ - public function test_unload_specific_locale() { - $instance = new Ginger_MO(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest' ) ); - - $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/ginger-mo/example-simple.php', 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); - - $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); - $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); - - $this->assertTrue( $instance->unload( 'unittest', null, $instance->get_locale() ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); - - $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->unload( 'unittest', null, 'es_ES' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertFalse( $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); - } - - /** - * @dataProvider data_invalid_files - * - * @param string $type - * @param string $file_contents - * @param string|bool $expected_error - * @return void - * - * @phpstan-param 'mo'|'php' $type - */ - public function test_invalid_files( string $type, string $file_contents, $expected_error = null ) { - $file = $this->temp_filename( $file_contents ); - - $this->assertNotFalse( $file ); - - $instance = Ginger_MO_Translation_File::create( $file, $type ); - - $this->assertInstanceOf( Ginger_MO_Translation_File::class, $instance ); - - // Not an error condition until it attempts to parse the file. - $this->assertFalse( $instance->error() ); - - // Trigger parsing. - $instance->headers(); - - $this->assertNotFalse( $instance->error() ); - - if ( null !== $expected_error ) { - $this->assertSame( $expected_error, $instance->error() ); - } - } - - /** - * @return array{0: array{0: 'mo'|'php', 1: string|false, 2?: string}} - */ - public function data_invalid_files(): array { - return array( - array( 'php', '' ), - array( 'php', 'assertFalse( $instance->load( DIR_TESTDATA . '/ginger-mo/file-that-doesnt-exist.mo', 'unittest' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - } - - /** - * @covers Ginger_MO_Translation_File::create - * - * @return void - */ - public function test_create_non_existent_file() { - $this->assertFalse( Ginger_MO_Translation_File::create( 'this-file-does-not-exist' ) ); - } - - /** - * @covers Ginger_MO_Translation_File::create - * - * @return void - */ - public function test_create_invalid_filetype() { - $file = $this->temp_filename( '' ); - $this->assertNotFalse( $file ); - $this->assertFalse( Ginger_MO_Translation_File::create( $file, 'invalid' ) ); - } - - /** - * @covers ::load - * @covers ::is_loaded - * @covers ::translate - * @covers ::translate_plural - * @covers ::locate_translation - * @covers ::get_files - * - * @dataProvider data_simple_example_files - * - * @param string $file - * @return void - */ - public function test_simple_translation_files( string $file ) { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/' . $file, 'unittest' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'textdomain not loaded' ) ); - - $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); - $this->assertFalse( $ginger_mo->translate( 'original', '', 'textdomain not loaded' ) ); - - $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); - $this->assertSame( 'translation with context', $ginger_mo->translate( 'original with context', 'context', 'unittest' ) ); - - $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); - $this->assertSame( 'translation0', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); - $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); - - $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); - $this->assertSame( 'translation0 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); - } - - /** - * @return array - */ - public function data_simple_example_files(): array { - return array( - array( 'example-simple.mo' ), - array( 'example-simple.php' ), - ); - } - - /** - * @covers ::load - * @covers ::unload - * @covers ::is_loaded - * @covers ::translate - * @covers ::translate_plural - * @covers ::locate_translation - * @covers ::get_files - * @covers Ginger_MO_Translation_File::get_plural_form - * @covers Ginger_MO_Translation_File::make_plural_form_function - * - * @return void - */ - public function test_load_multiple_files() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'unittest' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/plural.mo', 'unittest' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - - $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); - $this->assertFalse( $ginger_mo->translate( 'original', '', 'textdomain not loaded' ) ); - - // From example-simple.mo - - $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ) ); - $this->assertSame( 'translation with context', $ginger_mo->translate( 'original with context', 'context', 'unittest' ) ); - - $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); - $this->assertSame( 'translation0', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); - $this->assertSame( 'translation1', $ginger_mo->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); - - $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); - $this->assertSame( 'translation0 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $ginger_mo->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); - - // From simple.mo. - - $this->assertSame( 'dyado', $ginger_mo->translate( 'baba', '', 'unittest' ) ); - - // From plural.mo. - - $this->assertSame( 'oney dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertSame( 'twoey dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertSame( 'twoey dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); - - $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/simple.mo' ) ); - - $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest' ) ); - } - - /** - * @covers ::set_locale - * @covers ::get_locale - * @covers ::load - * @covers ::unload - * @covers ::is_loaded - * @covers ::translate - * @covers ::translate_plural - * - * @return void - */ - public function test_load_multiple_locales() { - $ginger_mo = new Ginger_MO(); - - $this->assertSame( 'en_US', $ginger_mo->get_locale() ); - - $ginger_mo->set_locale( 'de_DE' ); - - $this->assertSame( 'de_DE', $ginger_mo->get_locale() ); - - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'unittest', 'es_ES' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/plural.mo', 'unittest', 'en_US' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - - // From example-simple.mo - - $this->assertSame( 'translation', $ginger_mo->translate( 'original', '', 'unittest' ), 'String should be translated in de_DE' ); - $this->assertFalse( $ginger_mo->translate( 'original', '', 'unittest', 'es_ES' ), 'String should not be translated in es_ES' ); - $this->assertFalse( $ginger_mo->translate( 'original', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); - - // From simple.mo. - - $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest' ), 'String should not be translated in de_DE' ); - $this->assertSame( 'dyado', $ginger_mo->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); - $this->assertFalse( $ginger_mo->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); - - $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/plural.mo', 'de_DE' ) ); - - $this->assertSame( 'oney dragoney', $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); - - $this->assertTrue( $ginger_mo->unload( 'unittest', DIR_TESTDATA . '/ginger-mo/plural.mo', 'en_US' ) ); - - $this->assertFalse( $ginger_mo->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); - } - - /** - * @covers ::load - * @covers ::locate_translation - * - * @return void - */ - public function test_load_with_default_textdomain() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); - $this->assertSame( 'translation', $ginger_mo->translate( 'original' ) ); - } - - /** - * @covers ::load - * - * @return void - */ - public function test_load_same_file_twice() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'unittest' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - } - - /** - * @covers ::load - * - * @return void - */ - public function test_load_file_is_already_loaded_for_different_textdomain() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'foo' ) ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'bar' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'foo' ) ); - $this->assertTrue( $ginger_mo->is_loaded( 'bar' ) ); - } - - /** - * @covers ::load - * @covers ::unload - * @covers ::is_loaded - * @covers ::translate - * @covers ::translate_plural - * @covers ::locate_translation - * @covers ::get_files - * @covers Ginger_MO_Translation_File::get_plural_form - * @covers Ginger_MO_Translation_File::make_plural_form_function - * - * @return void - */ - public function test_load_no_plurals() { - $ginger_mo = new Ginger_MO(); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/fa_IR.mo', 'unittest' ) ); - - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); - - $this->assertFalse( $ginger_mo->translate( "string that doesn't exist", '', 'unittest' ) ); - - $this->assertSame( 'رونوشت‌ها فعال نشدند.', $ginger_mo->translate( 'Revisions not enabled.', '', 'unittest' ) ); - $this->assertSame( 'افزودن جدید', $ginger_mo->translate( 'Add New', 'file', 'unittest' ) ); - - $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 0, '', 'unittest' ) ); - $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 1, '', 'unittest' ) ); - $this->assertSame( '%s دیدگاه', $ginger_mo->translate_plural( array( '%s comment', '%s comments' ), 2, '', 'unittest' ) ); - } - - /** - * @covers ::get_headers - * - * @return void - */ - public function test_get_headers_no_loaded_translations() { - $ginger_mo = new Ginger_MO(); - $headers = $ginger_mo->get_headers(); - $this->assertEmpty( $headers ); - } - - /** - * @covers ::get_headers - * - * @return void - */ - public function test_get_headers_with_default_textdomain() { - $ginger_mo = new Ginger_MO(); - $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo' ); - $headers = $ginger_mo->get_headers(); - $this->assertSame( - array( - 'Po-Revision-Date' => '2016-01-05 18:45:32+1000', - ), - $headers - ); - } - - /** - * @covers ::get_headers - * - * @return void - */ - public function test_get_headers_no_loaded_translations_for_domain() { - $ginger_mo = new Ginger_MO(); - $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'foo' ); - $headers = $ginger_mo->get_headers( 'bar' ); - $this->assertEmpty( $headers ); - } - - - /** - * @covers ::get_entries - * - * @return void - */ - public function test_get_entries_no_loaded_translations() { - $ginger_mo = new Ginger_MO(); - $headers = $ginger_mo->get_entries(); - $this->assertEmpty( $headers ); - } - - /** - * @covers ::get_entries - * - * @return void - */ - public function test_get_entries_with_default_textdomain() { - $ginger_mo = new Ginger_MO(); - $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo' ); - $headers = $ginger_mo->get_entries(); - $this->assertSame( - array( - 'baba' => 'dyado', - "kuku\nruku" => 'yes', - ), - $headers - ); - } - - /** - * @covers ::get_entries - * - * @return void - */ - public function test_get_entries_no_loaded_translations_for_domain() { - $ginger_mo = new Ginger_MO(); - $ginger_mo->load( DIR_TESTDATA . '/ginger-mo/simple.mo', 'foo' ); - $headers = $ginger_mo->get_entries( 'bar' ); - $this->assertEmpty( $headers ); - } - - /** - * @dataProvider data_export_matrix - * - * @param string $source_file - * @param string $destination_format - * @return void - * - * @phpstan-param 'mo'|'php' $destination_format - */ - public function test_convert_format( string $source_file, string $destination_format ) { - $destination_file = $this->temp_filename(); - - $this->assertNotFalse( $destination_file ); - - $source = Ginger_MO_Translation_File::create( $source_file ); - - $this->assertInstanceOf( Ginger_MO_Translation_File::class, $source ); - - $contents = Ginger_MO_Translation_File::transform( $source_file, $destination_format ); - - $this->assertNotFalse( $contents ); - - file_put_contents( $destination_file, $contents ); - - $destination = Ginger_MO_Translation_File::create( $destination_file, $destination_format ); - - $this->assertInstanceOf( Ginger_MO_Translation_File::class, $destination ); - $this->assertFalse( $destination->error() ); - - $this->assertTrue( filesize( $destination_file ) > 0 ); - - $destination_read = Ginger_MO_Translation_File::create( $destination_file, $destination_format ); - - $this->assertInstanceOf( Ginger_MO_Translation_File::class, $destination_read ); - $this->assertFalse( $destination_read->error() ); - - $source_headers = $source->headers(); - $destination_headers = $destination_read->headers(); - - $this->assertEquals( $source_headers, $destination_headers ); - - foreach ( $source->entries() as $original => $translation ) { - // Verify the translation is in the destination file - if ( false !== strpos( $original, "\0" ) ) { - // Plurals: - $new_translation = $destination_read->translate( $original ); - - $this->assertSame( $translation, $new_translation ); - - } else { - // Single - $new_translation = $destination_read->translate( $original ); - - $this->assertSame( $translation, $new_translation ); - } - } - } - - /** - * @return array - */ - public function data_export_matrix(): array { - $formats = array( 'mo', 'php' ); - - $matrix = array(); - - foreach ( $formats as $input_format ) { - foreach ( $formats as $output_format ) { - $matrix[ "$input_format to $output_format" ] = array( DIR_TESTDATA . '/ginger-mo/example-simple.' . $input_format, $output_format ); - } - } - - return $matrix; - } - - /** - * @covers Ginger_MO_Translation_File::transform - * - * @return void - */ - public function test_convert_format_invalid_source() { - $this->assertFalse( Ginger_MO_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); - $this->assertFalse( Ginger_MO_Translation_File::transform( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'invalid' ) ); - $this->assertNotFalse( Ginger_MO_Translation_File::transform( DIR_TESTDATA . '/ginger-mo/example-simple.mo', 'php' ) ); - } -} diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpI18nConvert.php new file mode 100644 index 0000000000000..706831e10b52a --- /dev/null +++ b/tests/phpunit/tests/l10n/wpI18nConvert.php @@ -0,0 +1,569 @@ +assertSame( $instance, $instance2 ); + } + + /** + * @return void + */ + public function test_no_files_loaded_returns_false() { + $instance = new WP_I18n_Translation_Controller(); + $this->assertFalse( $instance->translate( 'singular' ) ); + $this->assertFalse( $instance->translate_plural( array( 'plural0', 'plural1' ), 1 ) ); + } + + /** + * @covers ::unload + * + * @return void + */ + public function test_unload_not_loaded() { + $instance = new WP_I18n_Translation_Controller(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->unload( 'unittest' ) ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::locate_translation + * @covers ::get_files + * + * @return void + */ + public function test_unload_entire_textdomain() { + $instance = new WP_I18n_Translation_Controller(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); + + $this->assertTrue( $instance->unload( 'unittest' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); + } + + /** + * @covers ::unload + * @covers WP_I18n_Translation_File::get_file + * + * @return void + */ + public function test_unload_file_is_not_actually_loaded() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); + } + + /** + * @covers ::unload + * @covers ::is_loaded + * + * @return void + */ + public function test_unload_specific_locale() { + $instance = new WP_I18n_Translation_Controller(); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + + $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); + + $this->assertTrue( $instance->unload( 'unittest', null, $instance->get_locale() ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); + + $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->unload( 'unittest', null, 'es_ES' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); + } + + /** + * @dataProvider data_invalid_files + * + * @param string $type + * @param string $file_contents + * @param string|bool $expected_error + * @return void + * + * @phpstan-param 'mo'|'php' $type + */ + public function test_invalid_files( string $type, string $file_contents, $expected_error = null ) { + $file = $this->temp_filename( $file_contents ); + + $this->assertNotFalse( $file ); + + $instance = WP_I18n_Translation_File::create( $file, $type ); + + $this->assertInstanceOf( WP_I18n_Translation_File::class, $instance ); + + // Not an error condition until it attempts to parse the file. + $this->assertFalse( $instance->error() ); + + // Trigger parsing. + $instance->headers(); + + $this->assertNotFalse( $instance->error() ); + + if ( null !== $expected_error ) { + $this->assertSame( $expected_error, $instance->error() ); + } + } + + /** + * @return array{0: array{0: 'mo'|'php', 1: string|false, 2?: string}} + */ + public function data_invalid_files(): array { + return array( + array( 'php', '' ), + array( 'php', 'assertFalse( $instance->load( DIR_TESTDATA . '/i18n/file-that-doesnt-exist.mo', 'unittest' ) ); + $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + } + + /** + * @covers WP_I18n_Translation_File::create + * + * @return void + */ + public function test_create_non_existent_file() { + $this->assertFalse( WP_I18n_Translation_File::create( 'this-file-does-not-exist' ) ); + } + + /** + * @covers WP_I18n_Translation_File::create + * + * @return void + */ + public function test_create_invalid_filetype() { + $file = $this->temp_filename( '' ); + $this->assertNotFalse( $file ); + $this->assertFalse( WP_I18n_Translation_File::create( $file, 'invalid' ) ); + } + + /** + * @covers ::load + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * + * @dataProvider data_simple_example_files + * + * @param string $file + * @return void + */ + public function test_simple_translation_files( string $file ) { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/' . $file, 'unittest' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertFalse( $WP_I18n_Translation_Controller->is_loaded( 'textdomain not loaded' ) ); + + $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'textdomain not loaded' ) ); + + $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $WP_I18n_Translation_Controller->translate( 'original with context', 'context', 'unittest' ) ); + + $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + + $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); + $this->assertSame( 'translation0 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); + $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + } + + /** + * @return array + */ + public function data_simple_example_files(): array { + return array( + array( 'example-simple.mo' ), + array( 'example-simple.php' ), + ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * @covers WP_I18n_Translation_File::get_plural_form + * @covers WP_I18n_Translation_File::make_plural_form_function + * + * @return void + */ + public function test_load_multiple_files() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + + $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'textdomain not loaded' ) ); + + // From example-simple.mo + + $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $WP_I18n_Translation_Controller->translate( 'original with context', 'context', 'unittest' ) ); + + $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + + $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); + $this->assertSame( 'translation0 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); + $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + + // From simple.mo. + + $this->assertSame( 'dyado', $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ) ); + + // From plural.mo. + + $this->assertSame( 'oney dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); + + $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ) ); + } + + /** + * @covers ::set_locale + * @covers ::get_locale + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * + * @return void + */ + public function test_load_multiple_locales() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + + $this->assertSame( 'en_US', $WP_I18n_Translation_Controller->get_locale() ); + + $WP_I18n_Translation_Controller->set_locale( 'de_DE' ); + + $this->assertSame( 'de_DE', $WP_I18n_Translation_Controller->get_locale() ); + + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest', 'es_ES' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest', 'en_US' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + + // From example-simple.mo + + $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ), 'String should be translated in de_DE' ); + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest', 'es_ES' ), 'String should not be translated in es_ES' ); + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + + // From simple.mo. + + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ), 'String should not be translated in de_DE' ); + $this->assertSame( 'dyado', $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); + $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + + $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'de_DE' ) ); + + $this->assertSame( 'oney dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); + + $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'en_US' ) ); + + $this->assertFalse( $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + } + + /** + * @covers ::load + * @covers ::locate_translation + * + * @return void + */ + public function test_load_with_default_textdomain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); + $this->assertFalse( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original' ) ); + } + + /** + * @covers ::load + * + * @return void + */ + public function test_load_same_file_twice() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + } + + /** + * @covers ::load + * + * @return void + */ + public function test_load_file_is_already_loaded_for_different_textdomain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'bar' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'foo' ) ); + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'bar' ) ); + } + + /** + * @covers ::load + * @covers ::unload + * @covers ::is_loaded + * @covers ::translate + * @covers ::translate_plural + * @covers ::locate_translation + * @covers ::get_files + * @covers WP_I18n_Translation_File::get_plural_form + * @covers WP_I18n_Translation_File::make_plural_form_function + * + * @return void + */ + public function test_load_no_plurals() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/fa_IR.mo', 'unittest' ) ); + + $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + + $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); + + $this->assertSame( 'رونوشت‌ها فعال نشدند.', $WP_I18n_Translation_Controller->translate( 'Revisions not enabled.', '', 'unittest' ) ); + $this->assertSame( 'افزودن جدید', $WP_I18n_Translation_Controller->translate( 'Add New', 'file', 'unittest' ) ); + + $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 0, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 1, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 2, '', 'unittest' ) ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_no_loaded_translations() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $headers = $WP_I18n_Translation_Controller->get_headers(); + $this->assertEmpty( $headers ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_with_default_textdomain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ); + $headers = $WP_I18n_Translation_Controller->get_headers(); + $this->assertSame( + array( + 'Po-Revision-Date' => '2016-01-05 18:45:32+1000', + ), + $headers + ); + } + + /** + * @covers ::get_headers + * + * @return void + */ + public function test_get_headers_no_loaded_translations_for_domain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ); + $headers = $WP_I18n_Translation_Controller->get_headers( 'bar' ); + $this->assertEmpty( $headers ); + } + + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_no_loaded_translations() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $headers = $WP_I18n_Translation_Controller->get_entries(); + $this->assertEmpty( $headers ); + } + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_with_default_textdomain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo' ); + $headers = $WP_I18n_Translation_Controller->get_entries(); + $this->assertSame( + array( + 'baba' => 'dyado', + "kuku\nruku" => 'yes', + ), + $headers + ); + } + + /** + * @covers ::get_entries + * + * @return void + */ + public function test_get_entries_no_loaded_translations_for_domain() { + $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'foo' ); + $headers = $WP_I18n_Translation_Controller->get_entries( 'bar' ); + $this->assertEmpty( $headers ); + } + + /** + * @dataProvider data_export_matrix + * + * @param string $source_file + * @param string $destination_format + * @return void + * + * @phpstan-param 'mo'|'php' $destination_format + */ + public function test_convert_format( string $source_file, string $destination_format ) { + $destination_file = $this->temp_filename(); + + $this->assertNotFalse( $destination_file ); + + $source = WP_I18n_Translation_File::create( $source_file ); + + $this->assertInstanceOf( WP_I18n_Translation_File::class, $source ); + + $contents = WP_I18n_Translation_File::transform( $source_file, $destination_format ); + + $this->assertNotFalse( $contents ); + + file_put_contents( $destination_file, $contents ); + + $destination = WP_I18n_Translation_File::create( $destination_file, $destination_format ); + + $this->assertInstanceOf( WP_I18n_Translation_File::class, $destination ); + $this->assertFalse( $destination->error() ); + + $this->assertTrue( filesize( $destination_file ) > 0 ); + + $destination_read = WP_I18n_Translation_File::create( $destination_file, $destination_format ); + + $this->assertInstanceOf( WP_I18n_Translation_File::class, $destination_read ); + $this->assertFalse( $destination_read->error() ); + + $source_headers = $source->headers(); + $destination_headers = $destination_read->headers(); + + $this->assertEquals( $source_headers, $destination_headers ); + + foreach ( $source->entries() as $original => $translation ) { + // Verify the translation is in the destination file + if ( false !== strpos( $original, "\0" ) ) { + // Plurals: + $new_translation = $destination_read->translate( $original ); + + $this->assertSame( $translation, $new_translation ); + + } else { + // Single + $new_translation = $destination_read->translate( $original ); + + $this->assertSame( $translation, $new_translation ); + } + } + } + + /** + * @return array + */ + public function data_export_matrix(): array { + $formats = array( 'mo', 'php' ); + + $matrix = array(); + + foreach ( $formats as $input_format ) { + foreach ( $formats as $output_format ) { + $matrix[ "$input_format to $output_format" ] = array( DIR_TESTDATA . '/i18n/example-simple.' . $input_format, $output_format ); + } + } + + return $matrix; + } + + /** + * @covers WP_I18n_Translation_File::transform + * + * @return void + */ + public function test_convert_format_invalid_source() { + $this->assertFalse( WP_I18n_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); + $this->assertFalse( WP_I18n_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'invalid' ) ); + $this->assertNotFalse( WP_I18n_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'php' ) ); + } +} diff --git a/tests/phpunit/tests/l10n/gingerMoIntegration.php b/tests/phpunit/tests/l10n/wpI18nTranslationController.php similarity index 76% rename from tests/phpunit/tests/l10n/gingerMoIntegration.php rename to tests/phpunit/tests/l10n/wpI18nTranslationController.php index 3f7d0df1ac454..b2f9361241a81 100644 --- a/tests/phpunit/tests/l10n/gingerMoIntegration.php +++ b/tests/phpunit/tests/l10n/wpI18nTranslationController.php @@ -4,7 +4,7 @@ * @group l10n * @group i18n */ -class Ginger_MO_Integration_Tests extends WP_UnitTestCase { +class WP_I18n_Translation_Controller_Integration_Tests extends WP_UnitTestCase { /** * @return void @@ -16,9 +16,9 @@ public function tear_down() { /** * @covers ::load_textdomain - * @covers Ginger_MO::get_entries - * @covers Ginger_MO::get_headers - * @covers Ginger_MO::normalize_header + * @covers WP_I18n_Translation_Controller::get_entries + * @covers WP_I18n_Translation_Controller::get_headers + * @covers WP_I18n_Translation_Controller::normalize_header * * @return void */ @@ -33,9 +33,9 @@ public function test_load_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); - $headers = Ginger_MO::instance()->get_headers( 'wp-tests-domain' ); - $entries = Ginger_MO::instance()->get_entries( 'wp-tests-domain' ); + $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $headers = WP_I18n_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); + $entries = WP_I18n_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); $unload_successful = unload_textdomain( 'wp-tests-domain' ); @@ -44,10 +44,10 @@ public function test_load_textdomain() { $this->assertFalse( $loaded_before_load, 'Text domain was already loaded at beginning of the test' ); $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); $this->assertTrue( $loaded_after_load, 'Text domain is not considered loaded' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertFalse( $loaded_after_unload, 'Text domain still considered loaded after unload' ); - $this->assertTrue( $is_loaded, 'Text domain not considered loaded in Ginger-MO' ); + $this->assertTrue( $is_loaded, 'Text domain not considered loaded' ); $this->assertEqualSetsWithIndex( array( 'Project-Id-Version' => 'WordPress 2.6-bleeding', @@ -68,9 +68,9 @@ public function test_load_textdomain() { /** * @covers ::load_textdomain - * @covers Ginger_MO::get_entries - * @covers Ginger_MO::get_headers - * @covers Ginger_MO::normalize_header + * @covers WP_I18n_Translation_Controller::get_entries + * @covers WP_I18n_Translation_Controller::get_headers + * @covers WP_I18n_Translation_Controller::normalize_header * * @return void */ @@ -81,7 +81,7 @@ public function test_load_textdomain_existing_override() { $is_loaded_wp = is_textdomain_loaded( 'wp-tests-domain' ); - $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); remove_filter( 'override_load_textdomain', '__return_true' ); @@ -174,7 +174,7 @@ public function test_load_textdomain_loads_existing_translation() { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); } /** @@ -205,7 +205,7 @@ static function () { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); } /** @@ -234,14 +234,14 @@ public function test_load_textdomain_loads_existing_translation_php_files() { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); } /** * @covers ::unload_textdomain - * @covers Ginger_MO::get_entries - * @covers Ginger_MO::get_headers - * @covers Ginger_MO::normalize_header + * @covers WP_I18n_Translation_Controller::get_entries + * @covers WP_I18n_Translation_Controller::get_headers + * @covers WP_I18n_Translation_Controller::normalize_header * * @return void */ @@ -256,14 +256,14 @@ public function test_unload_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); - $headers = Ginger_MO::instance()->get_headers( 'wp-tests-domain' ); - $entries = Ginger_MO::instance()->get_entries( 'wp-tests-domain' ); + $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $headers = WP_I18n_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); + $entries = WP_I18n_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); $this->assertNull( $compat_instance, 'Compat instance was not removed' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertFalse( $loaded_after_unload, 'Text domain still considered loaded after unload' ); - $this->assertFalse( $is_loaded, 'Text domain still considered loaded in Ginger-MO' ); + $this->assertFalse( $is_loaded, 'Text domain still considered loaded' ); $this->assertEmpty( $headers, 'Actual translation headers are not empty' ); $this->assertEmpty( $entries, 'Actual translation entries are not empty' ); } @@ -280,13 +280,13 @@ public function test_unload_textdomain_existing_override() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $is_loaded = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); remove_filter( 'override_unload_textdomain', '__return_true' ); $unload_successful_after = unload_textdomain( 'wp-tests-domain' ); - $is_loaded_after = Ginger_MO::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded_after = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); $this->assertTrue( $unload_successful ); $this->assertTrue( $is_loaded ); @@ -305,14 +305,14 @@ public function test_switch_to_locale_translations_stay_loaded_default_textdomai $actual = __( 'Invalid parameter.' ); - $this->assertTrue( Ginger_MO::instance()->is_loaded() ); - $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded() ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); restore_previous_locale(); $actual_2 = __( 'Invalid parameter.' ); - $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); $this->assertSame( 'Parámetro no válido. ', $actual ); $this->assertSame( 'Invalid parameter.', $actual_2 ); @@ -326,7 +326,7 @@ public function test_switch_to_locale_translations_stay_loaded_default_textdomai * @return void */ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain() { - $this->assertSame( 'en_US', Ginger_MO::instance()->get_locale() ); + $this->assertSame( 'en_US', WP_I18n_Translation_Controller::instance()->get_locale() ); require_once DIR_TESTDATA . '/plugins/internationalized-plugin.php'; @@ -336,16 +336,16 @@ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain $actual = i18n_plugin_test(); - $this->assertSame( 'es_ES', Ginger_MO::instance()->get_locale() ); - $this->assertTrue( Ginger_MO::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); - $this->assertTrue( Ginger_MO::instance()->is_loaded( 'default', 'es_ES' ) ); - $this->assertFalse( Ginger_MO::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); + $this->assertSame( 'es_ES', WP_I18n_Translation_Controller::instance()->get_locale() ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertFalse( WP_I18n_Translation_Controller::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); restore_previous_locale(); $after = i18n_plugin_test(); - $this->assertTrue( Ginger_MO::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); $this->assertSame( 'This is a dummy plugin', $before ); $this->assertSame( 'Este es un plugin dummy', $actual ); diff --git a/tests/phpunit/tests/l10n/gingerMo.php b/tests/phpunit/tests/l10n/wpI18nTranslations.php similarity index 88% rename from tests/phpunit/tests/l10n/gingerMo.php rename to tests/phpunit/tests/l10n/wpI18nTranslations.php index ad293900dbe6e..01a89cf77d95c 100644 --- a/tests/phpunit/tests/l10n/gingerMo.php +++ b/tests/phpunit/tests/l10n/wpI18nTranslations.php @@ -1,11 +1,11 @@ assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -70,7 +70,7 @@ public function test_get_entries_plural() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -111,7 +111,7 @@ public function test_get_entries_context() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -158,7 +158,7 @@ public function test_get_headers() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSetsWithIndex( array( @@ -182,7 +182,7 @@ public function test_getter_unsupported_property() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance ); $this->assertNull( $compat_instance->foo ); } @@ -204,7 +204,7 @@ public function test_translate() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( 'dyado', $translation, 'Actual translation does not match expected one' ); $this->assertSame( 'does not exist', $translation_missing, 'Actual translation fallback does not match expected one' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); @@ -228,7 +228,7 @@ public function test_translate_plural() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( 'oney dragoney', $translation_1, 'Actual translation does not match expected one' ); $this->assertSame( 'twoey dragoney', $translation_2, 'Actual translation does not match expected one' ); $this->assertSame( 'twoey dragoney', $translation_minus_8, 'Actual translation does not match expected one' ); @@ -252,7 +252,7 @@ public function test_translate_plural_missing() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( Ginger_MO_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( '%d house', $translation_1, 'Actual translation fallback does not match expected one' ); $this->assertSame( '%d cars', $translation_2, 'Actual plural translation fallback does not match expected one' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); From c658b3f55eb4e33020062fea13b1012cdc3857aa Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Oct 2023 12:53:30 +0200 Subject: [PATCH 15/65] Rename var --- tests/phpunit/tests/l10n/wpI18nConvert.php | 194 ++++++++++----------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpI18nConvert.php index 706831e10b52a..8c5ba6be05e28 100644 --- a/tests/phpunit/tests/l10n/wpI18nConvert.php +++ b/tests/phpunit/tests/l10n/wpI18nConvert.php @@ -68,12 +68,12 @@ public function test_unload_entire_textdomain() { * @return void */ public function test_unload_file_is_not_actually_loaded() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); - $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); } /** @@ -199,25 +199,25 @@ public function test_create_invalid_filetype() { * @return void */ public function test_simple_translation_files( string $file ) { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/' . $file, 'unittest' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/' . $file, 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->is_loaded( 'textdomain not loaded' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertFalse( $controller->is_loaded( 'textdomain not loaded' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'textdomain not loaded' ) ); + $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $controller->translate( 'original', '', 'textdomain not loaded' ) ); - $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); - $this->assertSame( 'translation with context', $WP_I18n_Translation_Controller->translate( 'original with context', 'context', 'unittest' ) ); + $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $controller->translate( 'original with context', 'context', 'unittest' ) ); - $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); - $this->assertSame( 'translation0', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); - $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + $this->assertSame( 'translation1', $controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); - $this->assertSame( 'translation0 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + $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' ) ); } /** @@ -244,42 +244,42 @@ public function data_simple_example_files(): array { * @return void */ public function test_load_multiple_files() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'textdomain not loaded' ) ); + $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $controller->translate( 'original', '', 'textdomain not loaded' ) ); // From example-simple.mo - $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ) ); - $this->assertSame( 'translation with context', $WP_I18n_Translation_Controller->translate( 'original with context', 'context', 'unittest' ) ); + $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); + $this->assertSame( 'translation with context', $controller->translate( 'original with context', 'context', 'unittest' ) ); - $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); - $this->assertSame( 'translation0', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); - $this->assertSame( 'translation1', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); + $this->assertSame( 'translation1', $controller->translate_plural( array( 'plural0', 'plural1' ), 0, '', 'unittest' ) ); + $this->assertSame( 'translation0', $controller->translate_plural( array( 'plural0', 'plural1' ), 1, '', 'unittest' ) ); + $this->assertSame( 'translation1', $controller->translate_plural( array( 'plural0', 'plural1' ), 2, '', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 0, 'context', 'unittest' ) ); - $this->assertSame( 'translation0 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 1, 'context', 'unittest' ) ); - $this->assertSame( 'translation1 with context', $WP_I18n_Translation_Controller->translate_plural( array( 'plural0 with context', 'plural1 with context' ), 2, 'context', 'unittest' ) ); + $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' ) ); // From simple.mo. - $this->assertSame( 'dyado', $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ) ); + $this->assertSame( 'dyado', $controller->translate( 'baba', '', 'unittest' ) ); // From plural.mo. - $this->assertSame( 'oney dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertSame( 'twoey dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertSame( 'twoey dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'oney dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); + $this->assertSame( 'twoey dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ) ); + $this->assertFalse( $controller->translate( 'baba', '', 'unittest' ) ); } /** @@ -294,39 +294,39 @@ public function test_load_multiple_files() { * @return void */ public function test_load_multiple_locales() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); + $controller = new WP_I18n_Translation_Controller(); - $this->assertSame( 'en_US', $WP_I18n_Translation_Controller->get_locale() ); + $this->assertSame( 'en_US', $controller->get_locale() ); - $WP_I18n_Translation_Controller->set_locale( 'de_DE' ); + $controller->set_locale( 'de_DE' ); - $this->assertSame( 'de_DE', $WP_I18n_Translation_Controller->get_locale() ); + $this->assertSame( 'de_DE', $controller->get_locale() ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest', 'es_ES' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest', 'en_US' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest', 'es_ES' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest', 'en_US' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); // From example-simple.mo - $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest' ), 'String should be translated in de_DE' ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest', 'es_ES' ), 'String should not be translated in es_ES' ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'original', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ), 'String should be translated in de_DE' ); + $this->assertFalse( $controller->translate( 'original', '', 'unittest', 'es_ES' ), 'String should not be translated in es_ES' ); + $this->assertFalse( $controller->translate( 'original', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); // From simple.mo. - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest' ), 'String should not be translated in de_DE' ); - $this->assertSame( 'dyado', $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + $this->assertFalse( $controller->translate( 'baba', '', 'unittest' ), 'String should not be translated in de_DE' ); + $this->assertSame( 'dyado', $controller->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); + $this->assertFalse( $controller->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); - $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'de_DE' ) ); + $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'de_DE' ) ); - $this->assertSame( 'oney dragoney', $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); + $this->assertSame( 'oney dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); - $this->assertTrue( $WP_I18n_Translation_Controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'en_US' ) ); + $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'en_US' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); + $this->assertFalse( $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); } /** @@ -336,11 +336,11 @@ public function test_load_multiple_locales() { * @return void */ public function test_load_with_default_textdomain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); - $this->assertSame( 'translation', $WP_I18n_Translation_Controller->translate( 'original' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); + $this->assertFalse( $controller->is_loaded( 'unittest' ) ); + $this->assertSame( 'translation', $controller->translate( 'original' ) ); } /** @@ -349,11 +349,11 @@ public function test_load_with_default_textdomain() { * @return void */ public function test_load_same_file_twice() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); } /** @@ -362,12 +362,12 @@ public function test_load_same_file_twice() { * @return void */ public function test_load_file_is_already_loaded_for_different_textdomain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'bar' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ) ); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'bar' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'foo' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'bar' ) ); + $this->assertTrue( $controller->is_loaded( 'foo' ) ); + $this->assertTrue( $controller->is_loaded( 'bar' ) ); } /** @@ -384,19 +384,19 @@ public function test_load_file_is_already_loaded_for_different_textdomain() { * @return void */ public function test_load_no_plurals() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $this->assertTrue( $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/fa_IR.mo', 'unittest' ) ); + $controller = new WP_I18n_Translation_Controller(); + $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/fa_IR.mo', 'unittest' ) ); - $this->assertTrue( $WP_I18n_Translation_Controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_loaded( 'unittest' ) ); - $this->assertFalse( $WP_I18n_Translation_Controller->translate( "string that doesn't exist", '', 'unittest' ) ); + $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); - $this->assertSame( 'رونوشت‌ها فعال نشدند.', $WP_I18n_Translation_Controller->translate( 'Revisions not enabled.', '', 'unittest' ) ); - $this->assertSame( 'افزودن جدید', $WP_I18n_Translation_Controller->translate( 'Add New', 'file', 'unittest' ) ); + $this->assertSame( 'رونوشت‌ها فعال نشدند.', $controller->translate( 'Revisions not enabled.', '', 'unittest' ) ); + $this->assertSame( 'افزودن جدید', $controller->translate( 'Add New', 'file', 'unittest' ) ); - $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 0, '', 'unittest' ) ); - $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 1, '', 'unittest' ) ); - $this->assertSame( '%s دیدگاه', $WP_I18n_Translation_Controller->translate_plural( array( '%s comment', '%s comments' ), 2, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $controller->translate_plural( array( '%s comment', '%s comments' ), 0, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $controller->translate_plural( array( '%s comment', '%s comments' ), 1, '', 'unittest' ) ); + $this->assertSame( '%s دیدگاه', $controller->translate_plural( array( '%s comment', '%s comments' ), 2, '', 'unittest' ) ); } /** @@ -405,8 +405,8 @@ public function test_load_no_plurals() { * @return void */ public function test_get_headers_no_loaded_translations() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $headers = $WP_I18n_Translation_Controller->get_headers(); + $controller = new WP_I18n_Translation_Controller(); + $headers = $controller->get_headers(); $this->assertEmpty( $headers ); } @@ -416,9 +416,9 @@ public function test_get_headers_no_loaded_translations() { * @return void */ public function test_get_headers_with_default_textdomain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ); - $headers = $WP_I18n_Translation_Controller->get_headers(); + $controller = new WP_I18n_Translation_Controller(); + $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ); + $headers = $controller->get_headers(); $this->assertSame( array( 'Po-Revision-Date' => '2016-01-05 18:45:32+1000', @@ -433,9 +433,9 @@ public function test_get_headers_with_default_textdomain() { * @return void */ public function test_get_headers_no_loaded_translations_for_domain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ); - $headers = $WP_I18n_Translation_Controller->get_headers( 'bar' ); + $controller = new WP_I18n_Translation_Controller(); + $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ); + $headers = $controller->get_headers( 'bar' ); $this->assertEmpty( $headers ); } @@ -446,8 +446,8 @@ public function test_get_headers_no_loaded_translations_for_domain() { * @return void */ public function test_get_entries_no_loaded_translations() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $headers = $WP_I18n_Translation_Controller->get_entries(); + $controller = new WP_I18n_Translation_Controller(); + $headers = $controller->get_entries(); $this->assertEmpty( $headers ); } @@ -457,9 +457,9 @@ public function test_get_entries_no_loaded_translations() { * @return void */ public function test_get_entries_with_default_textdomain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo' ); - $headers = $WP_I18n_Translation_Controller->get_entries(); + $controller = new WP_I18n_Translation_Controller(); + $controller->load( DIR_TESTDATA . '/i18n/simple.mo' ); + $headers = $controller->get_entries(); $this->assertSame( array( 'baba' => 'dyado', @@ -475,9 +475,9 @@ public function test_get_entries_with_default_textdomain() { * @return void */ public function test_get_entries_no_loaded_translations_for_domain() { - $WP_I18n_Translation_Controller = new WP_I18n_Translation_Controller(); - $WP_I18n_Translation_Controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'foo' ); - $headers = $WP_I18n_Translation_Controller->get_entries( 'bar' ); + $controller = new WP_I18n_Translation_Controller(); + $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'foo' ); + $headers = $controller->get_entries( 'bar' ); $this->assertEmpty( $headers ); } From a33ae171c30f07ab46de016ed76f95de5b808b9a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Oct 2023 12:58:22 +0200 Subject: [PATCH 16/65] Fix alignment --- tests/phpunit/tests/l10n/wpI18nConvert.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpI18nConvert.php index 8c5ba6be05e28..7248ab5c7a99b 100644 --- a/tests/phpunit/tests/l10n/wpI18nConvert.php +++ b/tests/phpunit/tests/l10n/wpI18nConvert.php @@ -406,7 +406,7 @@ public function test_load_no_plurals() { */ public function test_get_headers_no_loaded_translations() { $controller = new WP_I18n_Translation_Controller(); - $headers = $controller->get_headers(); + $headers = $controller->get_headers(); $this->assertEmpty( $headers ); } @@ -447,7 +447,7 @@ public function test_get_headers_no_loaded_translations_for_domain() { */ public function test_get_entries_no_loaded_translations() { $controller = new WP_I18n_Translation_Controller(); - $headers = $controller->get_entries(); + $headers = $controller->get_entries(); $this->assertEmpty( $headers ); } From 922966cf17b1380610eddc2c691fb4208f90765e Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 19 Oct 2023 12:58:26 +0200 Subject: [PATCH 17/65] Fix test --- tests/phpunit/tests/l10n/wpI18nConvert.php | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpI18nConvert.php index 7248ab5c7a99b..2fded9fce6a12 100644 --- a/tests/phpunit/tests/l10n/wpI18nConvert.php +++ b/tests/phpunit/tests/l10n/wpI18nConvert.php @@ -524,18 +524,8 @@ public function test_convert_format( string $source_file, string $destination_fo foreach ( $source->entries() as $original => $translation ) { // Verify the translation is in the destination file - if ( false !== strpos( $original, "\0" ) ) { - // Plurals: - $new_translation = $destination_read->translate( $original ); - - $this->assertSame( $translation, $new_translation ); - - } else { - // Single - $new_translation = $destination_read->translate( $original ); - - $this->assertSame( $translation, $new_translation ); - } + $new_translation = $destination_read->translate( $original ); + $this->assertSame( $translation, $new_translation ); } } From 2010f0786a16161e0ee1785469b86ded16c10779 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 8 Nov 2023 17:00:28 +0100 Subject: [PATCH 18/65] Merge latest change from plugin See swissspidy/performant-translations#115 --- src/wp-includes/i18n/class-wp-i18n-translation-controller.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php index 2d7def6acee1f..b94f434498e6d 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php @@ -133,8 +133,7 @@ public function load( string $translation_file, string $textdomain = 'default', $this->loaded_translations[ $locale ][ $textdomain ] = array(); } - // Ensure that last-loaded translation takes precedence. - array_unshift( $this->loaded_translations[ $locale ][ $textdomain ], $moe ); + $this->loaded_translations[ $locale ][ $textdomain ][] = $moe; return true; } From 00e7bcfb683512117c5a26a26e59c5c5f54f9bfe Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 8 Nov 2023 21:49:53 +0100 Subject: [PATCH 19/65] Fix alignment --- src/wp-includes/i18n/class-wp-i18n-translation-controller.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php index b94f434498e6d..f46c84f7c7ac7 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php @@ -143,9 +143,9 @@ public function load( string $translation_file, string $textdomain = 'default', * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Text domain. * @param WP_I18n_Translation_File|string $mo Translation file instance or file name. - * @param string $locale Optional. Locale. Default all locales. + * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ public function unload( string $textdomain = 'default', $mo = null, string $locale = null ) { From 3ed966ebbdb5eaac1336234beeab537b8323b80f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 8 Nov 2023 22:29:18 +0100 Subject: [PATCH 20/65] Apply some suggestions from code review --- .../class-wp-i18n-translation-controller.php | 8 +++--- .../i18n/class-wp-i18n-translations.php | 14 ++-------- src/wp-includes/l10n.php | 26 +++++++------------ 3 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php index f46c84f7c7ac7..1660329b794b0 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php @@ -139,11 +139,11 @@ public function load( string $translation_file, string $textdomain = 'default', } /** - * Unload all translation files or a specific one for a given text domain. + * Unloads all translation files or a specific one for a given text domain. * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param WP_I18n_Translation_File|string $mo Translation file instance or file name. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. @@ -213,7 +213,7 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return bool True if there are any loaded translations, false otherwise. */ @@ -259,7 +259,7 @@ public function translate( string $text, string $context = '', string $textdomai * * @since 6.5.0 * - * @param array{0: string, 1: string} $plurals Pair of singular and plural translation. + * @param array{0: string, 1: string} $plurals Pair of singular and plural translations. * @param int $number Number of items. * @param string $context Optional. Context for the string. * @param string $textdomain Text domain. diff --git a/src/wp-includes/i18n/class-wp-i18n-translations.php b/src/wp-includes/i18n/class-wp-i18n-translations.php index e5a7850fdcd56..80a90e7045bb8 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translations.php +++ b/src/wp-includes/i18n/class-wp-i18n-translations.php @@ -65,17 +65,7 @@ public function __get( string $name ) { } /** - * Magic setter. - * - * @since 6.5.0 - * - * @param string $name Property name. - * @param mixed $value Property value. - */ - public function __set( string $name, $value ) {} - - /** - * Build a Translation_Entry from original string and translation strings. + * Builds a Translation_Entry from original string and translation strings. * * @see MO::make_entry() * @@ -144,7 +134,7 @@ public function translate_plural( $singular, $plural, $count = 1, $context = '' */ public function translate( $singular, $context = '' ) { if ( null === $singular ) { - return $singular; + return null; } $translation = WP_I18n_Translation_Controller::instance()->translate( $singular, (string) $context, $this->textdomain ); diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index f2162cd8509d7..1ad1e21c1b7f9 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -788,8 +788,10 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $locale = determine_locale(); } + $i18n_controller = WP_I18n_Translation_Controller::instance(); + // Ensures the correct locale is set as the current one, in case it was filtered. - WP_I18n_Translation_Controller::instance()->set_locale( $locale ); + $i18n_controller->set_locale( $locale ); /** * Filters the preferred file format for translation files. @@ -803,12 +805,8 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $preferred_format = 'php'; } - $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $mofile ); - - if ( 'mo' !== $preferred_format || str_ends_with( $mofile, $preferred_format ) ) { - - /** This filter is documented in wp-includes/l10n.php */ - $mofile_preferred = apply_filters( 'load_textdomain_mofile', $mofile_preferred, $domain ); + if ( 'mo' !== $preferred_format ) { + $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $mofile ); /** * Filters the file path for loading translations for the given text domain. @@ -822,11 +820,11 @@ function load_textdomain( $domain, $mofile, $locale = null ) { */ $mofile_preferred = apply_filters( 'load_translation_file', $mofile_preferred, $domain ); - $success = WP_I18n_Translation_Controller::instance()->load( $mofile_preferred, $domain, $locale ); + $success = $i18n_controller->load( $mofile_preferred, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - WP_I18n_Translation_Controller::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + $i18n_controller->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); } // Unset Noop_Translations reference in get_translations_for_domain. @@ -840,20 +838,14 @@ function load_textdomain( $domain, $mofile, $locale = null ) { } } - /** This action is documented in wp-includes/l10n.php */ - do_action( 'load_textdomain', $domain, $mofile ); - - /** This filter is documented in wp-includes/l10n.php */ - $mofile = apply_filters( 'load_textdomain_mofile', $mofile, $domain ); - /** This filter is documented in wp-includes/l10n.php */ $mofile = apply_filters( 'load_translation_file', $mofile, $domain ); - $success = WP_I18n_Translation_Controller::instance()->load( $mofile, $domain, $locale ); + $success = $i18n_controller->load( $mofile, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - WP_I18n_Translation_Controller::instance()->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + $i18n_controller->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); } // Unset Noop_Translations reference in get_translations_for_domain. From 841821a5a1716b822a15dbb61d865f9497045813 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Thu, 9 Nov 2023 16:48:53 +0100 Subject: [PATCH 21/65] Fix `@subpackage` annotations --- src/wp-includes/i18n/class-wp-i18n-translation-controller.php | 2 +- src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php | 2 +- src/wp-includes/i18n/class-wp-i18n-translation-file-php.php | 2 +- src/wp-includes/i18n/class-wp-i18n-translation-file.php | 2 +- src/wp-includes/i18n/class-wp-i18n-translations.php | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php index 1660329b794b0..f576fcc3f29e6 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-controller.php @@ -3,7 +3,7 @@ * I18N: WP_I18n_Translation_Controller class. * * @package WordPress - * @subpackage WP_I18n_Translation_Controller + * @subpackage I18N * @since 6.5.0 */ diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php b/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php index a2a4f5fe161e9..b31adf76ad257 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php @@ -3,7 +3,7 @@ * I18N: WP_I18n_Translation_File_MO class. * * @package WordPress - * @subpackage WP_I18n_Translation_Controller + * @subpackage I18N * @since 6.5.0 */ diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-file-php.php b/src/wp-includes/i18n/class-wp-i18n-translation-file-php.php index 25dd664a53a50..7afdabd1c537c 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-file-php.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-file-php.php @@ -3,7 +3,7 @@ * I18N: WP_I18n_Translation_File_PHP class. * * @package WordPress - * @subpackage WP_I18n_Translation_Controller + * @subpackage I18N * @since 6.5.0 */ diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-file.php b/src/wp-includes/i18n/class-wp-i18n-translation-file.php index 3adf92151b170..2384821b57fce 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-file.php +++ b/src/wp-includes/i18n/class-wp-i18n-translation-file.php @@ -3,7 +3,7 @@ * I18N: WP_I18n_Translation_File class. * * @package WordPress - * @subpackage WP_I18n_Translation_Controller + * @subpackage I18N * @since 6.5.0 */ diff --git a/src/wp-includes/i18n/class-wp-i18n-translations.php b/src/wp-includes/i18n/class-wp-i18n-translations.php index 80a90e7045bb8..c35e3e723ad3a 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translations.php +++ b/src/wp-includes/i18n/class-wp-i18n-translations.php @@ -3,7 +3,7 @@ * I18N: WP_I18n_Translations class. * * @package WordPress - * @subpackage WP_I18n_Translation_Controller + * @subpackage I18N * @since 6.5.0 */ From 38e607d358f851269df0c2d8a3c9ea2f2d49ac6e Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 08:36:18 +0100 Subject: [PATCH 22/65] Use loop, update docs --- src/wp-includes/l10n.php | 41 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 1ad1e21c1b7f9..91d541b75732b 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -796,6 +796,8 @@ function load_textdomain( $domain, $mofile, $locale = null ) { /** * Filters the preferred file format for translation files. * + * Can be used to disable the use of PHP files for translations. + * * @since 6.5.0 * * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'. @@ -805,22 +807,29 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $preferred_format = 'php'; } + $translation_files = array( $mofile ); if ( 'mo' !== $preferred_format ) { - $mofile_preferred = str_replace( '.mo', ".mo.$preferred_format", $mofile ); + array_unshift( + $translation_files, + str_replace( '.mo', ".mo.$preferred_format", $mofile ) + ); + } + foreach ( $translation_files as $file ) { /** * Filters the file path for loading translations for the given text domain. * - * The file could be an MO or PHP file. + * Similar to the {@see 'load_textdomain_mofile'} filter with the difference that + * the file path could be for an MO or PHP file. * * @since 6.5.0 * * @param string $file Path to the translation file to load. * @param string $domain The text domain. */ - $mofile_preferred = apply_filters( 'load_translation_file', $mofile_preferred, $domain ); + $file = apply_filters( 'load_translation_file', $file, $domain ); - $success = $i18n_controller->load( $mofile_preferred, $domain, $locale ); + $success = $i18n_controller->load( $file, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { @@ -832,30 +841,12 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $l10n[ $domain ] = new WP_I18n_Translations( $domain ); - $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); + $wp_textdomain_registry->set( $domain, $locale, dirname( $file ) ); return true; } } - /** This filter is documented in wp-includes/l10n.php */ - $mofile = apply_filters( 'load_translation_file', $mofile, $domain ); - - $success = $i18n_controller->load( $mofile, $domain, $locale ); - - if ( $success ) { - if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - $i18n_controller->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); - } - - // Unset Noop_Translations reference in get_translations_for_domain. - unset( $l10n[ $domain ] ); - - $l10n[ $domain ] = new WP_I18n_Translations( $domain ); - - $wp_textdomain_registry->set( $domain, $locale, dirname( $mofile ) ); - } - return true; } @@ -917,8 +908,10 @@ function unload_textdomain( $domain, $reloadable = false ) { unset( $l10n[ $domain ] ); - // Since we support multiple locales, we don't actually need to unload reloadable text domains. + // Since multiple locales are supported,reloadable text domains don't actually need to be unloaded. if ( ! $reloadable ) { + $l10n_unloaded[ $domain ] = true; + return WP_I18n_Translation_Controller::instance()->unload( $domain ); } From d816760c540b164adfe640281baff90d86bea6ba Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 08:59:57 +0100 Subject: [PATCH 23/65] Pass i18n controller instance to `WP_I18N_Translations` --- .../i18n/class-wp-i18n-translations.php | 23 ++++++++++++++----- src/wp-includes/l10n.php | 2 +- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-i18n-translations.php b/src/wp-includes/i18n/class-wp-i18n-translations.php index c35e3e723ad3a..2e1767c8a773b 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translations.php +++ b/src/wp-includes/i18n/class-wp-i18n-translations.php @@ -25,14 +25,25 @@ class WP_I18n_Translations { */ protected $textdomain = 'default'; + /** + * Translation controller instance. + * + * @since 6.5.0 + * + * @var WP_I18n_Translation_Controller + */ + protected $controller; + /** * Constructor. * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param WP_I18n_Translation_Controller $controller I18N controller. + * @param string $textdomain Text domain. */ - public function __construct( string $textdomain = 'default' ) { + public function __construct( WP_I18n_Translation_Controller $controller, string $textdomain = 'default' ) { + $this->controller = $controller; $this->textdomain = $textdomain; } @@ -46,7 +57,7 @@ public function __construct( string $textdomain = 'default' ) { */ public function __get( string $name ) { if ( 'entries' === $name ) { - $entries = WP_I18n_Translation_Controller::instance()->get_entries( $this->textdomain ); + $entries = $this->controller->get_entries( $this->textdomain ); $result = array(); @@ -58,7 +69,7 @@ public function __get( string $name ) { } if ( 'headers' === $name ) { - return WP_I18n_Translation_Controller::instance()->get_headers( $this->textdomain ); + return $this->controller->get_headers( $this->textdomain ); } return null; @@ -114,7 +125,7 @@ public function translate_plural( $singular, $plural, $count = 1, $context = '' return $singular; } - $translation = WP_I18n_Translation_Controller::instance()->translate_plural( array( $singular, $plural ), (int) $count, (string) $context, $this->textdomain ); + $translation = $this->controller->translate_plural( array( $singular, $plural ), (int) $count, (string) $context, $this->textdomain ); if ( false !== $translation ) { return $translation; } @@ -137,7 +148,7 @@ public function translate( $singular, $context = '' ) { return null; } - $translation = WP_I18n_Translation_Controller::instance()->translate( $singular, (string) $context, $this->textdomain ); + $translation = $this->controller->translate( $singular, (string) $context, $this->textdomain ); if ( false !== $translation ) { return $translation; } diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 91d541b75732b..e4588d90d6836 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -839,7 +839,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { // Unset Noop_Translations reference in get_translations_for_domain. unset( $l10n[ $domain ] ); - $l10n[ $domain ] = new WP_I18n_Translations( $domain ); + $l10n[ $domain ] = new WP_I18n_Translations( $i18n_controller, $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $file ) ); From 42e5f442a4e5713987201a74e5ea34feade00475 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 15:18:01 +0100 Subject: [PATCH 24/65] Drop `_i18n_` infix --- src/wp-includes/class-wp-locale-switcher.php | 2 +- ...hp => class-wp-translation-controller.php} | 30 +++--- ...o.php => class-wp-translation-file-mo.php} | 6 +- ....php => class-wp-translation-file-php.php} | 6 +- ...file.php => class-wp-translation-file.php} | 24 ++--- ...slations.php => class-wp-translations.php} | 12 +-- src/wp-includes/l10n.php | 6 +- src/wp-settings.php | 12 +-- tests/phpunit/tests/dependencies/scripts.php | 2 +- tests/phpunit/tests/l10n/wpI18nConvert.php | 94 +++++++++---------- .../l10n/wpI18nTranslationController.php | 64 ++++++------- .../phpunit/tests/l10n/wpI18nTranslations.php | 20 ++-- 12 files changed, 139 insertions(+), 139 deletions(-) rename src/wp-includes/i18n/{class-wp-i18n-translation-controller.php => class-wp-translation-controller.php} (91%) rename src/wp-includes/i18n/{class-wp-i18n-translation-file-mo.php => class-wp-translation-file-mo.php} (97%) rename src/wp-includes/i18n/{class-wp-i18n-translation-file-php.php => class-wp-translation-file-php.php} (92%) rename src/wp-includes/i18n/{class-wp-i18n-translation-file.php => class-wp-translation-file.php} (88%) rename src/wp-includes/i18n/{class-wp-i18n-translations.php => class-wp-translations.php} (91%) diff --git a/src/wp-includes/class-wp-locale-switcher.php b/src/wp-includes/class-wp-locale-switcher.php index 028b5e4899d5f..b3e163014aa90 100644 --- a/src/wp-includes/class-wp-locale-switcher.php +++ b/src/wp-includes/class-wp-locale-switcher.php @@ -283,7 +283,7 @@ private function change_locale( $locale ) { $wp_locale = new WP_Locale(); - WP_I18n_Translation_Controller::instance()->set_locale( $locale ); + WP_Translation_Controller::instance()->set_locale( $locale ); /** * Fires when the locale is switched to or restored. diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php b/src/wp-includes/i18n/class-wp-translation-controller.php similarity index 91% rename from src/wp-includes/i18n/class-wp-i18n-translation-controller.php rename to src/wp-includes/i18n/class-wp-translation-controller.php index f576fcc3f29e6..9cc02651ecd10 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-translation-controller.php @@ -1,6 +1,6 @@ > + * @var array> */ protected $loaded_translations = array(); /** * List of loaded translation files. * - * [ Filename => [ Locale => [ Textdomain => WP_I18n_Translation_File ] ] ] + * [ Filename => [ Locale => [ Textdomain => WP_Translation_File ] ] ] * * @since 6.5.0 * - * @var array>> + * @var array>> */ protected $loaded_files = array(); /** - * Returns the WP_I18n_Translation_Controller singleton. + * Returns the WP_Translation_Controller singleton. * * @since 6.5.0 * - * @return WP_I18n_Translation_Controller + * @return WP_Translation_Controller */ public static function instance() { static $instance; if ( ! $instance ) { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); } return $instance; @@ -117,7 +117,7 @@ public function load( string $translation_file, string $textdomain = 'default', ) { $moe = reset( $this->loaded_files[ $translation_file ][ $locale ] ); } else { - $moe = WP_I18n_Translation_File::create( $translation_file ); + $moe = WP_Translation_File::create( $translation_file ); if ( false === $moe || false !== $moe->error() ) { $moe = false; } @@ -125,7 +125,7 @@ public function load( string $translation_file, string $textdomain = 'default', $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] = $moe; - if ( ! $moe instanceof WP_I18n_Translation_File ) { + if ( ! $moe instanceof WP_Translation_File ) { return false; } @@ -144,7 +144,7 @@ public function load( string $translation_file, string $textdomain = 'default', * @since 6.5.0 * * @param string $textdomain Optional. Text domain. Default 'default'. - * @param WP_I18n_Translation_File|string $mo Translation file instance or file name. + * @param WP_Translation_File|string $mo Translation file instance or file name. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ @@ -283,7 +283,7 @@ public function translate_plural( array $plurals, int $number, string $context = } } - /* @var WP_I18n_Translation_File $source */ + /* @var WP_Translation_File $source */ $source = $translation['source']; $num = $source->get_plural_form( $number ); @@ -359,7 +359,7 @@ public function get_entries( string $textdomain = 'default' ) { * @param string $singular Singular translation. * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. - * @return array{source: WP_I18n_Translation_File, entries: string[]}|false Translations on success, false otherwise. + * @return array{source: WP_Translation_File, entries: string[]}|false Translations on success, false otherwise. */ protected function locate_translation( string $singular, string $textdomain = 'default', string $locale = null ) { if ( array() === $this->loaded_translations ) { @@ -392,7 +392,7 @@ protected function locate_translation( string $singular, string $textdomain = 'd * * @param string $textdomain Text domain. * @param string $locale Optional. Locale. Default current locale. - * @return WP_I18n_Translation_File[] List of translation files. + * @return WP_Translation_File[] List of translation files. */ protected function get_files( string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { diff --git a/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php b/src/wp-includes/i18n/class-wp-translation-file-mo.php similarity index 97% rename from src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php rename to src/wp-includes/i18n/class-wp-translation-file-mo.php index b31adf76ad257..2af3597298cb5 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translation-file-mo.php +++ b/src/wp-includes/i18n/class-wp-translation-file-mo.php @@ -1,6 +1,6 @@ error() ) { return false; } diff --git a/src/wp-includes/i18n/class-wp-i18n-translations.php b/src/wp-includes/i18n/class-wp-translations.php similarity index 91% rename from src/wp-includes/i18n/class-wp-i18n-translations.php rename to src/wp-includes/i18n/class-wp-translations.php index 2e1767c8a773b..58c05d107cc10 100644 --- a/src/wp-includes/i18n/class-wp-i18n-translations.php +++ b/src/wp-includes/i18n/class-wp-translations.php @@ -1,6 +1,6 @@ $headers * @property-read array $entries */ -class WP_I18n_Translations { +class WP_Translations { /** * Text domain. * @@ -30,7 +30,7 @@ class WP_I18n_Translations { * * @since 6.5.0 * - * @var WP_I18n_Translation_Controller + * @var WP_Translation_Controller */ protected $controller; @@ -39,10 +39,10 @@ class WP_I18n_Translations { * * @since 6.5.0 * - * @param WP_I18n_Translation_Controller $controller I18N controller. + * @param WP_Translation_Controller $controller I18N controller. * @param string $textdomain Text domain. */ - public function __construct( WP_I18n_Translation_Controller $controller, string $textdomain = 'default' ) { + public function __construct( WP_Translation_Controller $controller, string $textdomain = 'default' ) { $this->controller = $controller; $this->textdomain = $textdomain; } diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index e4588d90d6836..32050f08f3327 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -788,7 +788,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $locale = determine_locale(); } - $i18n_controller = WP_I18n_Translation_Controller::instance(); + $i18n_controller = WP_Translation_Controller::instance(); // Ensures the correct locale is set as the current one, in case it was filtered. $i18n_controller->set_locale( $locale ); @@ -839,7 +839,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { // Unset Noop_Translations reference in get_translations_for_domain. unset( $l10n[ $domain ] ); - $l10n[ $domain ] = new WP_I18n_Translations( $i18n_controller, $domain ); + $l10n[ $domain ] = new WP_Translations( $i18n_controller, $domain ); $wp_textdomain_registry->set( $domain, $locale, dirname( $file ) ); @@ -912,7 +912,7 @@ function unload_textdomain( $domain, $reloadable = false ) { if ( ! $reloadable ) { $l10n_unloaded[ $domain ] = true; - return WP_I18n_Translation_Controller::instance()->unload( $domain ); + return WP_Translation_Controller::instance()->unload( $domain ); } return true; diff --git a/src/wp-settings.php b/src/wp-settings.php index 56854c3229e10..eb20518c5d38b 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -114,11 +114,11 @@ require ABSPATH . WPINC . '/class-wp.php'; require ABSPATH . WPINC . '/class-wp-error.php'; require ABSPATH . WPINC . '/pomo/mo.php'; -require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-controller.php'; -require ABSPATH . WPINC . '/i18n/class-wp-i18n-translations.php'; -require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file.php'; -require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file-mo.php'; -require ABSPATH . WPINC . '/i18n/class-wp-i18n-translation-file-php.php'; +require ABSPATH . WPINC . '/i18n/class-wp-translation-controller.php'; +require ABSPATH . WPINC . '/i18n/class-wp-translations.php'; +require ABSPATH . WPINC . '/i18n/class-wp-translation-file.php'; +require ABSPATH . WPINC . '/i18n/class-wp-translation-file-mo.php'; +require ABSPATH . WPINC . '/i18n/class-wp-translation-file-php.php'; /** * @global wpdb $wpdb WordPress database abstraction object. @@ -610,7 +610,7 @@ $GLOBALS['wp_locale_switcher'] = new WP_Locale_Switcher(); $GLOBALS['wp_locale_switcher']->init(); -WP_I18n_Translation_Controller::instance()->set_locale( $locale ); +WP_Translation_Controller::instance()->set_locale( $locale ); // Load the functions for the active theme, for both parent and child theme if applicable. foreach ( wp_get_active_and_valid_themes() as $theme ) { diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 95cf958d3ebbd..043d93fec8a57 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -2917,7 +2917,7 @@ public function data_wp_localize_script_data_formats() { * * @covers ::wp_set_script_translations */ - public function test_wp_external_wp_i18n_print_order() { + public function test_wp_external_WP_print_order() { global $wp_scripts, $wp_version; $wp_scripts->do_concat = true; diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpI18nConvert.php index 2fded9fce6a12..ab18a483ce563 100644 --- a/tests/phpunit/tests/l10n/wpI18nConvert.php +++ b/tests/phpunit/tests/l10n/wpI18nConvert.php @@ -1,19 +1,19 @@ assertSame( $instance, $instance2 ); } @@ -22,7 +22,7 @@ public function test_get_instance() { * @return void */ public function test_no_files_loaded_returns_false() { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->translate( 'singular' ) ); $this->assertFalse( $instance->translate_plural( array( 'plural0', 'plural1' ), 1 ) ); } @@ -33,7 +33,7 @@ public function test_no_files_loaded_returns_false() { * @return void */ public function test_unload_not_loaded() { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); $this->assertFalse( $instance->unload( 'unittest' ) ); } @@ -49,7 +49,7 @@ public function test_unload_not_loaded() { * @return void */ public function test_unload_entire_textdomain() { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); $this->assertTrue( $instance->is_loaded( 'unittest' ) ); @@ -63,12 +63,12 @@ public function test_unload_entire_textdomain() { /** * @covers ::unload - * @covers WP_I18n_Translation_File::get_file + * @covers WP_Translation_File::get_file * * @return void */ public function test_unload_file_is_not_actually_loaded() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); @@ -83,7 +83,7 @@ public function test_unload_file_is_not_actually_loaded() { * @return void */ public function test_unload_specific_locale() { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); $this->assertTrue( $instance->is_loaded( 'unittest' ) ); @@ -120,9 +120,9 @@ public function test_invalid_files( string $type, string $file_contents, $expect $this->assertNotFalse( $file ); - $instance = WP_I18n_Translation_File::create( $file, $type ); + $instance = WP_Translation_File::create( $file, $type ); - $this->assertInstanceOf( WP_I18n_Translation_File::class, $instance ); + $this->assertInstanceOf( WP_Translation_File::class, $instance ); // Not an error condition until it attempts to parse the file. $this->assertFalse( $instance->error() ); @@ -153,36 +153,36 @@ public function data_invalid_files(): array { } /** - * @covers WP_I18n_Translation_Controller::load - * @covers WP_I18n_Translation_Controller::is_loaded + * @covers WP_Translation_Controller::load + * @covers WP_Translation_Controller::is_loaded * * @return void */ public function test_load_non_existent_file() { - $instance = new WP_I18n_Translation_Controller(); + $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->load( DIR_TESTDATA . '/i18n/file-that-doesnt-exist.mo', 'unittest' ) ); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); } /** - * @covers WP_I18n_Translation_File::create + * @covers WP_Translation_File::create * * @return void */ public function test_create_non_existent_file() { - $this->assertFalse( WP_I18n_Translation_File::create( 'this-file-does-not-exist' ) ); + $this->assertFalse( WP_Translation_File::create( 'this-file-does-not-exist' ) ); } /** - * @covers WP_I18n_Translation_File::create + * @covers WP_Translation_File::create * * @return void */ public function test_create_invalid_filetype() { $file = $this->temp_filename( '' ); $this->assertNotFalse( $file ); - $this->assertFalse( WP_I18n_Translation_File::create( $file, 'invalid' ) ); + $this->assertFalse( WP_Translation_File::create( $file, 'invalid' ) ); } /** @@ -199,7 +199,7 @@ public function test_create_invalid_filetype() { * @return void */ public function test_simple_translation_files( string $file ) { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/' . $file, 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); @@ -238,13 +238,13 @@ public function data_simple_example_files(): array { * @covers ::translate_plural * @covers ::locate_translation * @covers ::get_files - * @covers WP_I18n_Translation_File::get_plural_form - * @covers WP_I18n_Translation_File::make_plural_form_function + * @covers WP_Translation_File::get_plural_form + * @covers WP_Translation_File::make_plural_form_function * * @return void */ public function test_load_multiple_files() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); @@ -294,7 +294,7 @@ public function test_load_multiple_files() { * @return void */ public function test_load_multiple_locales() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertSame( 'en_US', $controller->get_locale() ); @@ -336,7 +336,7 @@ public function test_load_multiple_locales() { * @return void */ public function test_load_with_default_textdomain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); $this->assertFalse( $controller->is_loaded( 'unittest' ) ); @@ -349,7 +349,7 @@ public function test_load_with_default_textdomain() { * @return void */ public function test_load_same_file_twice() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); @@ -362,7 +362,7 @@ public function test_load_same_file_twice() { * @return void */ public function test_load_file_is_already_loaded_for_different_textdomain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ) ); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'bar' ) ); @@ -378,13 +378,13 @@ public function test_load_file_is_already_loaded_for_different_textdomain() { * @covers ::translate_plural * @covers ::locate_translation * @covers ::get_files - * @covers WP_I18n_Translation_File::get_plural_form - * @covers WP_I18n_Translation_File::make_plural_form_function + * @covers WP_Translation_File::get_plural_form + * @covers WP_Translation_File::make_plural_form_function * * @return void */ public function test_load_no_plurals() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/fa_IR.mo', 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); @@ -405,7 +405,7 @@ public function test_load_no_plurals() { * @return void */ public function test_get_headers_no_loaded_translations() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $headers = $controller->get_headers(); $this->assertEmpty( $headers ); } @@ -416,7 +416,7 @@ public function test_get_headers_no_loaded_translations() { * @return void */ public function test_get_headers_with_default_textdomain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ); $headers = $controller->get_headers(); $this->assertSame( @@ -433,7 +433,7 @@ public function test_get_headers_with_default_textdomain() { * @return void */ public function test_get_headers_no_loaded_translations_for_domain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ); $headers = $controller->get_headers( 'bar' ); $this->assertEmpty( $headers ); @@ -446,7 +446,7 @@ public function test_get_headers_no_loaded_translations_for_domain() { * @return void */ public function test_get_entries_no_loaded_translations() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $headers = $controller->get_entries(); $this->assertEmpty( $headers ); } @@ -457,7 +457,7 @@ public function test_get_entries_no_loaded_translations() { * @return void */ public function test_get_entries_with_default_textdomain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $controller->load( DIR_TESTDATA . '/i18n/simple.mo' ); $headers = $controller->get_entries(); $this->assertSame( @@ -475,7 +475,7 @@ public function test_get_entries_with_default_textdomain() { * @return void */ public function test_get_entries_no_loaded_translations_for_domain() { - $controller = new WP_I18n_Translation_Controller(); + $controller = new WP_Translation_Controller(); $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'foo' ); $headers = $controller->get_entries( 'bar' ); $this->assertEmpty( $headers ); @@ -495,26 +495,26 @@ public function test_convert_format( string $source_file, string $destination_fo $this->assertNotFalse( $destination_file ); - $source = WP_I18n_Translation_File::create( $source_file ); + $source = WP_Translation_File::create( $source_file ); - $this->assertInstanceOf( WP_I18n_Translation_File::class, $source ); + $this->assertInstanceOf( WP_Translation_File::class, $source ); - $contents = WP_I18n_Translation_File::transform( $source_file, $destination_format ); + $contents = WP_Translation_File::transform( $source_file, $destination_format ); $this->assertNotFalse( $contents ); file_put_contents( $destination_file, $contents ); - $destination = WP_I18n_Translation_File::create( $destination_file, $destination_format ); + $destination = WP_Translation_File::create( $destination_file, $destination_format ); - $this->assertInstanceOf( WP_I18n_Translation_File::class, $destination ); + $this->assertInstanceOf( WP_Translation_File::class, $destination ); $this->assertFalse( $destination->error() ); $this->assertTrue( filesize( $destination_file ) > 0 ); - $destination_read = WP_I18n_Translation_File::create( $destination_file, $destination_format ); + $destination_read = WP_Translation_File::create( $destination_file, $destination_format ); - $this->assertInstanceOf( WP_I18n_Translation_File::class, $destination_read ); + $this->assertInstanceOf( WP_Translation_File::class, $destination_read ); $this->assertFalse( $destination_read->error() ); $source_headers = $source->headers(); @@ -547,13 +547,13 @@ public function data_export_matrix(): array { } /** - * @covers WP_I18n_Translation_File::transform + * @covers WP_Translation_File::transform * * @return void */ public function test_convert_format_invalid_source() { - $this->assertFalse( WP_I18n_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); - $this->assertFalse( WP_I18n_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'invalid' ) ); - $this->assertNotFalse( WP_I18n_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'php' ) ); + $this->assertFalse( WP_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); + $this->assertFalse( WP_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'invalid' ) ); + $this->assertNotFalse( WP_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'php' ) ); } } diff --git a/tests/phpunit/tests/l10n/wpI18nTranslationController.php b/tests/phpunit/tests/l10n/wpI18nTranslationController.php index b2f9361241a81..d6dfeeae0e8b1 100644 --- a/tests/phpunit/tests/l10n/wpI18nTranslationController.php +++ b/tests/phpunit/tests/l10n/wpI18nTranslationController.php @@ -4,7 +4,7 @@ * @group l10n * @group i18n */ -class WP_I18n_Translation_Controller_Integration_Tests extends WP_UnitTestCase { +class WP_Translation_Controller_Integration_Tests extends WP_UnitTestCase { /** * @return void @@ -16,9 +16,9 @@ public function tear_down() { /** * @covers ::load_textdomain - * @covers WP_I18n_Translation_Controller::get_entries - * @covers WP_I18n_Translation_Controller::get_headers - * @covers WP_I18n_Translation_Controller::normalize_header + * @covers WP_Translation_Controller::get_entries + * @covers WP_Translation_Controller::get_headers + * @covers WP_Translation_Controller::normalize_header * * @return void */ @@ -33,9 +33,9 @@ public function test_load_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); - $headers = WP_I18n_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); - $entries = WP_I18n_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $headers = WP_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); + $entries = WP_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); $unload_successful = unload_textdomain( 'wp-tests-domain' ); @@ -44,7 +44,7 @@ public function test_load_textdomain() { $this->assertFalse( $loaded_before_load, 'Text domain was already loaded at beginning of the test' ); $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); $this->assertTrue( $loaded_after_load, 'Text domain is not considered loaded' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertFalse( $loaded_after_unload, 'Text domain still considered loaded after unload' ); $this->assertTrue( $is_loaded, 'Text domain not considered loaded' ); @@ -68,9 +68,9 @@ public function test_load_textdomain() { /** * @covers ::load_textdomain - * @covers WP_I18n_Translation_Controller::get_entries - * @covers WP_I18n_Translation_Controller::get_headers - * @covers WP_I18n_Translation_Controller::normalize_header + * @covers WP_Translation_Controller::get_entries + * @covers WP_Translation_Controller::get_headers + * @covers WP_Translation_Controller::normalize_header * * @return void */ @@ -81,7 +81,7 @@ public function test_load_textdomain_existing_override() { $is_loaded_wp = is_textdomain_loaded( 'wp-tests-domain' ); - $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); remove_filter( 'override_load_textdomain', '__return_true' ); @@ -174,7 +174,7 @@ public function test_load_textdomain_loads_existing_translation() { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_Translations::class, $l10n['wp-tests-domain'] ); } /** @@ -205,7 +205,7 @@ static function () { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_Translations::class, $l10n['wp-tests-domain'] ); } /** @@ -234,14 +234,14 @@ public function test_load_textdomain_loads_existing_translation_php_files() { $this->assertSame( 'dyado', $simple ); $this->assertSame( 'oney dragoney', $context ); - $this->assertInstanceOf( WP_I18n_Translations::class, $l10n['wp-tests-domain'] ); + $this->assertInstanceOf( WP_Translations::class, $l10n['wp-tests-domain'] ); } /** * @covers ::unload_textdomain - * @covers WP_I18n_Translation_Controller::get_entries - * @covers WP_I18n_Translation_Controller::get_headers - * @covers WP_I18n_Translation_Controller::normalize_header + * @covers WP_Translation_Controller::get_entries + * @covers WP_Translation_Controller::get_headers + * @covers WP_Translation_Controller::normalize_header * * @return void */ @@ -256,9 +256,9 @@ public function test_unload_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); - $headers = WP_I18n_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); - $entries = WP_I18n_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $headers = WP_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); + $entries = WP_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); $this->assertNull( $compat_instance, 'Compat instance was not removed' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); @@ -280,13 +280,13 @@ public function test_unload_textdomain_existing_override() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $is_loaded = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); remove_filter( 'override_unload_textdomain', '__return_true' ); $unload_successful_after = unload_textdomain( 'wp-tests-domain' ); - $is_loaded_after = WP_I18n_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded_after = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); $this->assertTrue( $unload_successful ); $this->assertTrue( $is_loaded ); @@ -305,14 +305,14 @@ public function test_switch_to_locale_translations_stay_loaded_default_textdomai $actual = __( 'Invalid parameter.' ); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded() ); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded() ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); restore_previous_locale(); $actual_2 = __( 'Invalid parameter.' ); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); $this->assertSame( 'Parámetro no válido. ', $actual ); $this->assertSame( 'Invalid parameter.', $actual_2 ); @@ -326,7 +326,7 @@ public function test_switch_to_locale_translations_stay_loaded_default_textdomai * @return void */ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain() { - $this->assertSame( 'en_US', WP_I18n_Translation_Controller::instance()->get_locale() ); + $this->assertSame( 'en_US', WP_Translation_Controller::instance()->get_locale() ); require_once DIR_TESTDATA . '/plugins/internationalized-plugin.php'; @@ -336,16 +336,16 @@ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain $actual = i18n_plugin_test(); - $this->assertSame( 'es_ES', WP_I18n_Translation_Controller::instance()->get_locale() ); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); - $this->assertFalse( WP_I18n_Translation_Controller::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); + $this->assertSame( 'es_ES', WP_Translation_Controller::instance()->get_locale() ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertFalse( WP_Translation_Controller::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); restore_previous_locale(); $after = i18n_plugin_test(); - $this->assertTrue( WP_I18n_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); $this->assertSame( 'This is a dummy plugin', $before ); $this->assertSame( 'Este es un plugin dummy', $actual ); diff --git a/tests/phpunit/tests/l10n/wpI18nTranslations.php b/tests/phpunit/tests/l10n/wpI18nTranslations.php index 01a89cf77d95c..d9b11732834b7 100644 --- a/tests/phpunit/tests/l10n/wpI18nTranslations.php +++ b/tests/phpunit/tests/l10n/wpI18nTranslations.php @@ -1,11 +1,11 @@ assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -70,7 +70,7 @@ public function test_get_entries_plural() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -111,7 +111,7 @@ public function test_get_entries_context() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSets( array( @@ -158,7 +158,7 @@ public function test_get_headers() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); $this->assertTrue( $load_successful, 'Text domain not successfully loaded' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); $this->assertEqualSetsWithIndex( array( @@ -182,7 +182,7 @@ public function test_getter_unsupported_property() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance ); $this->assertNull( $compat_instance->foo ); } @@ -204,7 +204,7 @@ public function test_translate() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( 'dyado', $translation, 'Actual translation does not match expected one' ); $this->assertSame( 'does not exist', $translation_missing, 'Actual translation fallback does not match expected one' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); @@ -228,7 +228,7 @@ public function test_translate_plural() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( 'oney dragoney', $translation_1, 'Actual translation does not match expected one' ); $this->assertSame( 'twoey dragoney', $translation_2, 'Actual translation does not match expected one' ); $this->assertSame( 'twoey dragoney', $translation_minus_8, 'Actual translation does not match expected one' ); @@ -252,7 +252,7 @@ public function test_translate_plural_missing() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $this->assertInstanceOf( WP_I18n_Translations::class, $compat_instance, 'No compat provider instance used' ); + $this->assertInstanceOf( WP_Translations::class, $compat_instance, 'No compat provider instance used' ); $this->assertSame( '%d house', $translation_1, 'Actual translation fallback does not match expected one' ); $this->assertSame( '%d cars', $translation_2, 'Actual plural translation fallback does not match expected one' ); $this->assertTrue( $unload_successful, 'Text domain not successfully unloaded' ); From f9a03371eb34e4a910b7793cfdb6b68638b5c8f0 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 20:01:31 +0100 Subject: [PATCH 25/65] Docblock improvements Co-authored-by: Felix Arntz --- src/wp-includes/i18n/class-wp-translation-controller.php | 4 ++-- src/wp-includes/i18n/class-wp-translations.php | 2 +- src/wp-includes/l10n.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-translation-controller.php b/src/wp-includes/i18n/class-wp-translation-controller.php index 9cc02651ecd10..309f685a4b4fe 100644 --- a/src/wp-includes/i18n/class-wp-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-translation-controller.php @@ -143,9 +143,9 @@ public function load( string $translation_file, string $textdomain = 'default', * * @since 6.5.0 * - * @param string $textdomain Optional. Text domain. Default 'default'. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param WP_Translation_File|string $mo Translation file instance or file name. - * @param string $locale Optional. Locale. Default all locales. + * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ public function unload( string $textdomain = 'default', $mo = null, string $locale = null ) { diff --git a/src/wp-includes/i18n/class-wp-translations.php b/src/wp-includes/i18n/class-wp-translations.php index 58c05d107cc10..0bf570cec4433 100644 --- a/src/wp-includes/i18n/class-wp-translations.php +++ b/src/wp-includes/i18n/class-wp-translations.php @@ -40,7 +40,7 @@ class WP_Translations { * @since 6.5.0 * * @param WP_Translation_Controller $controller I18N controller. - * @param string $textdomain Text domain. + * @param string $textdomain Text domain. */ public function __construct( WP_Translation_Controller $controller, string $textdomain = 'default' ) { $this->controller = $controller; diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 32050f08f3327..de041f05673c8 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -908,7 +908,7 @@ function unload_textdomain( $domain, $reloadable = false ) { unset( $l10n[ $domain ] ); - // Since multiple locales are supported,reloadable text domains don't actually need to be unloaded. + // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded. if ( ! $reloadable ) { $l10n_unloaded[ $domain ] = true; From b46509f558c488626f5acc9d8506d8c4870bf525 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 23:05:18 +0100 Subject: [PATCH 26/65] Revert accidental change --- tests/phpunit/tests/dependencies/scripts.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/phpunit/tests/dependencies/scripts.php b/tests/phpunit/tests/dependencies/scripts.php index 043d93fec8a57..95cf958d3ebbd 100644 --- a/tests/phpunit/tests/dependencies/scripts.php +++ b/tests/phpunit/tests/dependencies/scripts.php @@ -2917,7 +2917,7 @@ public function data_wp_localize_script_data_formats() { * * @covers ::wp_set_script_translations */ - public function test_wp_external_WP_print_order() { + public function test_wp_external_wp_i18n_print_order() { global $wp_scripts, $wp_version; $wp_scripts->do_concat = true; From b33a83f13735db1b20b579bff2c5945c9560881a Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 23:06:22 +0100 Subject: [PATCH 27/65] Rename test files --- ...18nTranslationController.php => wpTranslationController.php} | 2 +- .../tests/l10n/{wpI18nTranslations.php => wpTranslations.php} | 0 .../tests/l10n/{wpI18nConvert.php => wpTranslationsConvert.php} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename tests/phpunit/tests/l10n/{wpI18nTranslationController.php => wpTranslationController.php} (99%) rename tests/phpunit/tests/l10n/{wpI18nTranslations.php => wpTranslations.php} (100%) rename tests/phpunit/tests/l10n/{wpI18nConvert.php => wpTranslationsConvert.php} (100%) diff --git a/tests/phpunit/tests/l10n/wpI18nTranslationController.php b/tests/phpunit/tests/l10n/wpTranslationController.php similarity index 99% rename from tests/phpunit/tests/l10n/wpI18nTranslationController.php rename to tests/phpunit/tests/l10n/wpTranslationController.php index d6dfeeae0e8b1..e0ee9152054ef 100644 --- a/tests/phpunit/tests/l10n/wpI18nTranslationController.php +++ b/tests/phpunit/tests/l10n/wpTranslationController.php @@ -4,7 +4,7 @@ * @group l10n * @group i18n */ -class WP_Translation_Controller_Integration_Tests extends WP_UnitTestCase { +class WP_Translation_Controller_Tests extends WP_UnitTestCase { /** * @return void diff --git a/tests/phpunit/tests/l10n/wpI18nTranslations.php b/tests/phpunit/tests/l10n/wpTranslations.php similarity index 100% rename from tests/phpunit/tests/l10n/wpI18nTranslations.php rename to tests/phpunit/tests/l10n/wpTranslations.php diff --git a/tests/phpunit/tests/l10n/wpI18nConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php similarity index 100% rename from tests/phpunit/tests/l10n/wpI18nConvert.php rename to tests/phpunit/tests/l10n/wpTranslationsConvert.php From 86a84fcef51da39e1ce1e4ad98b9796973b395b1 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Fri, 10 Nov 2023 23:38:23 +0100 Subject: [PATCH 28/65] Add missing parent call --- tests/phpunit/tests/l10n/wpTranslationController.php | 4 +++- tests/phpunit/tests/l10n/wpTranslations.php | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/phpunit/tests/l10n/wpTranslationController.php b/tests/phpunit/tests/l10n/wpTranslationController.php index e0ee9152054ef..0e24d6b7d59fb 100644 --- a/tests/phpunit/tests/l10n/wpTranslationController.php +++ b/tests/phpunit/tests/l10n/wpTranslationController.php @@ -5,13 +5,15 @@ * @group i18n */ class WP_Translation_Controller_Tests extends WP_UnitTestCase { - /** * @return void */ public function tear_down() { remove_all_filters( 'translation_file_format' ); unload_textdomain( 'wp-tests-domain' ); + unload_textdomain( 'internationalized-plugin' ); + + parent::tear_down(); } /** diff --git a/tests/phpunit/tests/l10n/wpTranslations.php b/tests/phpunit/tests/l10n/wpTranslations.php index d9b11732834b7..36d3716c8e099 100644 --- a/tests/phpunit/tests/l10n/wpTranslations.php +++ b/tests/phpunit/tests/l10n/wpTranslations.php @@ -11,6 +11,8 @@ class WP_Translations_Tests extends WP_UnitTestCase { */ public function tear_down() { unload_textdomain( 'wp-tests-domain' ); + + parent::tear_down(); } /** From e572b0c234d1d5cdf838b00637e0bb945474fb73 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 11 Nov 2023 10:02:23 +0100 Subject: [PATCH 29/65] Simplify unloading --- .../i18n/class-wp-translation-controller.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-translation-controller.php b/src/wp-includes/i18n/class-wp-translation-controller.php index 309f685a4b4fe..a82b1bcc7075f 100644 --- a/src/wp-includes/i18n/class-wp-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-translation-controller.php @@ -149,10 +149,6 @@ public function load( string $translation_file, string $textdomain = 'default', * @return bool True on success, false otherwise. */ public function unload( string $textdomain = 'default', $mo = null, string $locale = null ) { - if ( ! $this->is_loaded( $textdomain, $locale ) ) { - return false; - } - if ( null !== $mo ) { if ( is_string( $mo ) ) { $mo = realpath( $mo ); @@ -193,11 +189,15 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca return true; } + $unloaded = false; + foreach ( $this->loaded_translations as $l => $domains ) { if ( ! isset( $domains[ $textdomain ] ) ) { continue; } + $unloaded = true; + foreach ( $domains[ $textdomain ] as $moe ) { unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); } @@ -205,7 +205,7 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca unset( $this->loaded_translations[ $l ][ $textdomain ] ); } - return true; + return $unloaded; } /** From d1d1ec908d5aed7cf0b1c57e57cfa12f1c13bbf4 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 11 Nov 2023 10:03:26 +0100 Subject: [PATCH 30/65] Rename parameter --- .../i18n/class-wp-translation-controller.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-translation-controller.php b/src/wp-includes/i18n/class-wp-translation-controller.php index a82b1bcc7075f..5f52aed4f0aec 100644 --- a/src/wp-includes/i18n/class-wp-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-translation-controller.php @@ -144,19 +144,19 @@ public function load( string $translation_file, string $textdomain = 'default', * @since 6.5.0 * * @param string $textdomain Optional. Text domain. Default 'default'. - * @param WP_Translation_File|string $mo Translation file instance or file name. + * @param WP_Translation_File|string $file Optional. Translation file instance or file name. Default all files. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ - public function unload( string $textdomain = 'default', $mo = null, string $locale = null ) { - if ( null !== $mo ) { - if ( is_string( $mo ) ) { - $mo = realpath( $mo ); + public function unload( string $textdomain = 'default', $file = null, string $locale = null ) { + if ( null !== $file ) { + if ( is_string( $file ) ) { + $file = realpath( $file ); } if ( null !== $locale ) { foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { - if ( $mo === $moe || $mo === $moe->get_file() ) { + if ( $file === $moe || $file === $moe->get_file() ) { unset( $this->loaded_translations[ $locale ][ $textdomain ][ $i ] ); unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); return true; @@ -168,7 +168,7 @@ public function unload( string $textdomain = 'default', $mo = null, string $loca foreach ( $this->loaded_translations as $l => $domains ) { foreach ( $domains[ $textdomain ] as $i => $moe ) { - if ( $mo === $moe || $mo === $moe->get_file() ) { + if ( $file === $moe || $file === $moe->get_file() ) { unset( $this->loaded_translations[ $l ][ $textdomain ][ $i ] ); unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); return true; From eae66c6617d532870f1709387c7e58acdf3a30a9 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 11 Nov 2023 10:03:50 +0100 Subject: [PATCH 31/65] Improve cleanup in other test --- tests/phpunit/tests/l10n/loadTextdomainJustInTime.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php index 3b93d4a975660..db6a82527455f 100644 --- a/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php +++ b/tests/phpunit/tests/l10n/loadTextdomainJustInTime.php @@ -48,6 +48,9 @@ public function tear_down() { $wp_textdomain_registry = new WP_Textdomain_Registry(); + unload_textdomain( 'internationalized-plugin' ); + unload_textdomain( 'internationalized-theme' ); + parent::tear_down(); } From 12e0a25ee2efaac8362e040cb890db57d56f5702 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 11 Nov 2023 10:04:13 +0100 Subject: [PATCH 32/65] Avoid leaky tests with multiple interfering class instances --- tests/phpunit/tests/l10n/wpLocaleSwitcher.php | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/tests/phpunit/tests/l10n/wpLocaleSwitcher.php b/tests/phpunit/tests/l10n/wpLocaleSwitcher.php index 1b0b8f796d42a..ba12a432e41b5 100644 --- a/tests/phpunit/tests/l10n/wpLocaleSwitcher.php +++ b/tests/phpunit/tests/l10n/wpLocaleSwitcher.php @@ -21,6 +21,11 @@ class Tests_L10n_wpLocaleSwitcher extends WP_UnitTestCase { */ protected static $user_id; + /** + * @var WP_Locale_Switcher + */ + protected $orig_instance; + public static function wpSetUpBeforeClass( WP_UnitTest_Factory $factory ) { self::$user_id = $factory->user->create( array( @@ -42,7 +47,11 @@ public function set_up() { $wp_textdomain_registry = new WP_Textdomain_Registry(); - remove_filter( 'locale', array( $wp_locale_switcher, 'filter_locale' ) ); + $this->orig_instance = $wp_locale_switcher; + + remove_all_filters( 'locale' ); + remove_all_filters( 'determine_locale' ); + $wp_locale_switcher = new WP_Locale_Switcher(); $wp_locale_switcher->init(); } @@ -58,9 +67,13 @@ public function tear_down() { // before resetting $wp_locale_switcher. restore_current_locale(); - remove_filter( 'locale', array( $wp_locale_switcher, 'filter_locale' ) ); - $wp_locale_switcher = new WP_Locale_Switcher(); - $wp_locale_switcher->init(); + remove_all_filters( 'locale' ); + remove_all_filters( 'determine_locale' ); + + $wp_locale_switcher = $this->orig_instance; + + unload_textdomain( 'internationalized-plugin' ); + unload_textdomain( 'custom-internationalized-theme' ); parent::tear_down(); } From c16d995eb189642b758ea05b89a8e83583ebc882 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sat, 11 Nov 2023 10:05:05 +0100 Subject: [PATCH 33/65] move unload higher up --- src/wp-includes/l10n.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index de041f05673c8..8c426a63a2156 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -899,6 +899,11 @@ function unload_textdomain( $domain, $reloadable = false ) { */ do_action( 'unload_textdomain', $domain, $reloadable ); + // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded. + if ( ! $reloadable ) { + WP_Translation_Controller::instance()->unload( $domain ); + } + if ( isset( $l10n[ $domain ] ) ) { if ( $l10n[ $domain ] instanceof NOOP_Translations ) { unset( $l10n[ $domain ] ); @@ -908,11 +913,8 @@ function unload_textdomain( $domain, $reloadable = false ) { unset( $l10n[ $domain ] ); - // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded. if ( ! $reloadable ) { $l10n_unloaded[ $domain ] = true; - - return WP_Translation_Controller::instance()->unload( $domain ); } return true; From 3fd0424735406f058055487e8097972ec124fb8c Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 12 Nov 2023 14:04:22 +0100 Subject: [PATCH 34/65] Add test for e572b0c234d1d5cdf838b00637e0bb945474fb73 --- .../tests/l10n/wpTranslationsConvert.php | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index ab18a483ce563..f2e369a48815e 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -329,6 +329,42 @@ public function test_load_multiple_locales() { $this->assertFalse( $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); } + /** + * @covers ::unload + * + * @return void + */ + public function test_unload_with_multiple_locales() { + $ginger_mo = new WP_Translation_Controller(); + + $ginger_mo->set_locale( 'de_DE' ); + + $this->assertSame( 'de_DE', $ginger_mo->get_locale() ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $ginger_mo->set_locale( 'es_ES' ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); + $ginger_mo->set_locale( 'pl_PL' ); + $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); + $this->assertSame( 'pl_PL', $ginger_mo->get_locale() ); + + $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + + $ginger_mo->set_locale( 'en_US' ); + $this->assertSame( 'en_US', $ginger_mo->get_locale() ); + + $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'pl_PL' ) ); + $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'de_DE' ) ); + + $this->assertTrue( $ginger_mo->unload( 'unittest' ) ); + + $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'pl_PL' ) ); + $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'de_DE' ) ); + } + /** * @covers ::load * @covers ::locate_translation From d6086219f6b5de2ec246b246f13cbd1dd517df39 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Sun, 12 Nov 2023 14:19:31 +0100 Subject: [PATCH 35/65] Rename `.mo.php` to `.l10n.php` --- src/wp-admin/includes/plugin.php | 2 +- src/wp-includes/l10n.php | 2 +- tests/phpunit/data/pomo/{simple.mo.php => simple.l10n.php} | 0 tests/phpunit/tests/l10n/wpTranslationController.php | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename tests/phpunit/data/pomo/{simple.mo.php => simple.l10n.php} (100%) diff --git a/src/wp-admin/includes/plugin.php b/src/wp-admin/includes/plugin.php index a134696546d78..123c9d8f5ff44 100644 --- a/src/wp-admin/includes/plugin.php +++ b/src/wp-admin/includes/plugin.php @@ -1009,7 +1009,7 @@ function delete_plugins( $plugins, $deprecated = '' ) { foreach ( $translations as $translation => $data ) { $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' ); $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' ); - $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo.php' ); + $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.l10n.php' ); $json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' ); if ( $json_translation_files ) { diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 8c426a63a2156..1f28f4a33ab4b 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -811,7 +811,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { if ( 'mo' !== $preferred_format ) { array_unshift( $translation_files, - str_replace( '.mo', ".mo.$preferred_format", $mofile ) + str_replace( '.mo', ".l10n.$preferred_format", $mofile ) ); } diff --git a/tests/phpunit/data/pomo/simple.mo.php b/tests/phpunit/data/pomo/simple.l10n.php similarity index 100% rename from tests/phpunit/data/pomo/simple.mo.php rename to tests/phpunit/data/pomo/simple.l10n.php diff --git a/tests/phpunit/tests/l10n/wpTranslationController.php b/tests/phpunit/tests/l10n/wpTranslationController.php index 0e24d6b7d59fb..6f4fdb99c1e4e 100644 --- a/tests/phpunit/tests/l10n/wpTranslationController.php +++ b/tests/phpunit/tests/l10n/wpTranslationController.php @@ -97,7 +97,7 @@ public function test_load_textdomain_existing_override() { * @return void */ public function test_load_textdomain_php_files() { - $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); + $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.l10n.php' ); $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); @@ -122,7 +122,7 @@ static function () { $unload_mo_successful = unload_textdomain( 'wp-tests-domain' ); - $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.mo.php' ); + $load_php_successful = load_textdomain( 'wp-tests-domain', DIR_TESTDATA . '/pomo/simple.l10n.php' ); $unload_php_successful = unload_textdomain( 'wp-tests-domain' ); From e457732aa919b90d105c252f621b99b0d5d41b29 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 13 Nov 2023 17:12:44 +0100 Subject: [PATCH 36/65] Docblock improvements --- .../i18n/class-wp-translation-controller.php | 18 +++++++++--------- src/wp-includes/i18n/class-wp-translations.php | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/wp-includes/i18n/class-wp-translation-controller.php b/src/wp-includes/i18n/class-wp-translation-controller.php index 5f52aed4f0aec..5bc5e064db43e 100644 --- a/src/wp-includes/i18n/class-wp-translation-controller.php +++ b/src/wp-includes/i18n/class-wp-translation-controller.php @@ -89,7 +89,7 @@ public function set_locale( string $locale ) { * @since 6.5.0 * * @param string $translation_file Translation file. - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return bool True on success, false otherwise. */ @@ -232,8 +232,8 @@ public function is_loaded( string $textdomain = 'default', string $locale = null * @since 6.5.0 * * @param string $text Text to translate. - * @param string $context Optional. Context for the string. - * @param string $textdomain Text domain. + * @param string $context Optional. Context for the string. Default empty string. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return string|false Translation on success, false otherwise. */ @@ -261,8 +261,8 @@ public function translate( string $text, string $context = '', string $textdomai * * @param array{0: string, 1: string} $plurals Pair of singular and plural translations. * @param int $number Number of items. - * @param string $context Optional. Context for the string. - * @param string $textdomain Text domain. + * @param string $context Optional. Context for the string. Default empty string. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return string|false Translation on success, false otherwise. */ @@ -296,7 +296,7 @@ public function translate_plural( array $plurals, int $number, string $context = * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @return array Headers. */ public function get_headers( string $textdomain = 'default' ) { @@ -334,7 +334,7 @@ protected function normalize_header( string $header ) { * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @return array Entries. */ public function get_entries( string $textdomain = 'default' ) { @@ -357,7 +357,7 @@ public function get_entries( string $textdomain = 'default' ) { * @since 6.5.0 * * @param string $singular Singular translation. - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return array{source: WP_Translation_File, entries: string[]}|false Translations on success, false otherwise. */ @@ -390,7 +390,7 @@ protected function locate_translation( string $singular, string $textdomain = 'd * * @since 6.5.0 * - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. * @return WP_Translation_File[] List of translation files. */ diff --git a/src/wp-includes/i18n/class-wp-translations.php b/src/wp-includes/i18n/class-wp-translations.php index 0bf570cec4433..eac787954a4aa 100644 --- a/src/wp-includes/i18n/class-wp-translations.php +++ b/src/wp-includes/i18n/class-wp-translations.php @@ -40,7 +40,7 @@ class WP_Translations { * @since 6.5.0 * * @param WP_Translation_Controller $controller I18N controller. - * @param string $textdomain Text domain. + * @param string $textdomain Optional. Text domain. Default 'default'. */ public function __construct( WP_Translation_Controller $controller, string $textdomain = 'default' ) { $this->controller = $controller; From a0710ceeda97af9893a8cc5f876f14ac17824f33 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 22 Nov 2023 11:34:33 +0100 Subject: [PATCH 37/65] Add text domain to `translation_file_format` filter --- src/wp-includes/l10n.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 8c426a63a2156..0725392d74f09 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -801,8 +801,9 @@ function load_textdomain( $domain, $mofile, $locale = null ) { * @since 6.5.0 * * @param string $preferred_format Preferred file format. Possible values: 'php', 'mo'. Default: 'php'. + * @param string $domain The text domain. */ - $preferred_format = apply_filters( 'translation_file_format', 'php' ); + $preferred_format = apply_filters( 'translation_file_format', 'php', $domain ); if ( ! in_array( $preferred_format, array( 'php', 'mo' ), true ) ) { $preferred_format = 'php'; } From 6309d0982e0b9287016c591c1301a0bcad545173 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 15 Jan 2024 19:43:43 +0100 Subject: [PATCH 38/65] Minor adjustments --- src/wp-includes/l10n.php | 6 +- .../class-wp-translation-controller.php | 74 ++++++++------- .../class-wp-translation-file-mo.php | 1 - .../class-wp-translation-file-php.php | 0 .../class-wp-translation-file.php | 88 ++++++++---------- .../{i18n => l10n}/class-wp-translations.php | 2 - src/wp-settings.php | 10 +- .../data/{i18n => l10n}/example-simple.mo | Bin .../data/{i18n => l10n}/example-simple.php | 0 .../data/{i18n => l10n}/example-simple.po | 0 tests/phpunit/data/{i18n => l10n}/fa_IR.mo | Bin tests/phpunit/data/{i18n => l10n}/plural.mo | Bin tests/phpunit/data/{i18n => l10n}/simple.mo | Bin .../tests/l10n/wpTranslationsConvert.php | 84 ++++++++--------- 14 files changed, 131 insertions(+), 134 deletions(-) rename src/wp-includes/{i18n => l10n}/class-wp-translation-controller.php (85%) rename src/wp-includes/{i18n => l10n}/class-wp-translation-file-mo.php (99%) rename src/wp-includes/{i18n => l10n}/class-wp-translation-file-php.php (100%) rename src/wp-includes/{i18n => l10n}/class-wp-translation-file.php (96%) rename src/wp-includes/{i18n => l10n}/class-wp-translations.php (99%) rename tests/phpunit/data/{i18n => l10n}/example-simple.mo (100%) rename tests/phpunit/data/{i18n => l10n}/example-simple.php (100%) rename tests/phpunit/data/{i18n => l10n}/example-simple.po (100%) rename tests/phpunit/data/{i18n => l10n}/fa_IR.mo (100%) rename tests/phpunit/data/{i18n => l10n}/plural.mo (100%) rename tests/phpunit/data/{i18n => l10n}/simple.mo (100%) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 802ad4c8adb0a..b554f1b841819 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -830,11 +830,11 @@ function load_textdomain( $domain, $mofile, $locale = null ) { */ $file = apply_filters( 'load_translation_file', $file, $domain ); - $success = $i18n_controller->load( $file, $domain, $locale ); + $success = $i18n_controller->load_file( $file, $domain, $locale ); if ( $success ) { if ( isset( $l10n[ $domain ] ) && $l10n[ $domain ] instanceof MO ) { - $i18n_controller->load( $l10n[ $domain ]->get_filename(), $domain, $locale ); + $i18n_controller->load_file( $l10n[ $domain ]->get_filename(), $domain, $locale ); } // Unset Noop_Translations reference in get_translations_for_domain. @@ -902,7 +902,7 @@ function unload_textdomain( $domain, $reloadable = false ) { // Since multiple locales are supported, reloadable text domains don't actually need to be unloaded. if ( ! $reloadable ) { - WP_Translation_Controller::instance()->unload( $domain ); + WP_Translation_Controller::instance()->unload_textdomain( $domain ); } if ( isset( $l10n[ $domain ] ) ) { diff --git a/src/wp-includes/i18n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php similarity index 85% rename from src/wp-includes/i18n/class-wp-translation-controller.php rename to src/wp-includes/l10n/class-wp-translation-controller.php index 5bc5e064db43e..de6effcccc60f 100644 --- a/src/wp-includes/i18n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -17,7 +17,6 @@ class WP_Translation_Controller { * Current locale. * * @since 6.5.0 - * * @var string */ protected $current_locale = 'en_US'; @@ -28,7 +27,6 @@ class WP_Translation_Controller { * [ Locale => [ Textdomain => [ .., .. ] ] ] * * @since 6.5.0 - * * @var array> */ protected $loaded_translations = array(); @@ -39,7 +37,6 @@ class WP_Translation_Controller { * [ Filename => [ Locale => [ Textdomain => WP_Translation_File ] ] ] * * @since 6.5.0 - * * @var array>> */ protected $loaded_files = array(); @@ -84,7 +81,7 @@ public function set_locale( string $locale ) { } /** - * Loads a translation file. + * Loads a translation file for a given text domain. * * @since 6.5.0 * @@ -93,7 +90,7 @@ public function set_locale( string $locale ) { * @param string $locale Optional. Locale. Default current locale. * @return bool True on success, false otherwise. */ - public function load( string $translation_file, string $textdomain = 'default', string $locale = null ) { + public function load_file( string $translation_file, string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } @@ -108,7 +105,7 @@ public function load( string $translation_file, string $textdomain = 'default', isset( $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] ) && false !== $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ] ) { - return false === $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ]->error(); + return null === $this->loaded_files[ $translation_file ][ $locale ][ $textdomain ]->error(); } if ( @@ -118,7 +115,7 @@ public function load( string $translation_file, string $textdomain = 'default', $moe = reset( $this->loaded_files[ $translation_file ][ $locale ] ); } else { $moe = WP_Translation_File::create( $translation_file ); - if ( false === $moe || false !== $moe->error() ) { + if ( false === $moe || null !== $moe->error() ) { $moe = false; } } @@ -139,46 +136,55 @@ public function load( string $translation_file, string $textdomain = 'default', } /** - * Unloads all translation files or a specific one for a given text domain. + * Unloads a translation file for a given text domain. * * @since 6.5.0 * - * @param string $textdomain Optional. Text domain. Default 'default'. * @param WP_Translation_File|string $file Optional. Translation file instance or file name. Default all files. + * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ - public function unload( string $textdomain = 'default', $file = null, string $locale = null ) { - if ( null !== $file ) { - if ( is_string( $file ) ) { - $file = realpath( $file ); - } + public function unload_file( $file, string $textdomain = 'default', string $locale = null ) { + if ( is_string( $file ) ) { + $file = realpath( $file ); + } - if ( null !== $locale ) { - foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { - if ( $file === $moe || $file === $moe->get_file() ) { - unset( $this->loaded_translations[ $locale ][ $textdomain ][ $i ] ); - unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); - return true; - } + if ( null !== $locale ) { + foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $i => $moe ) { + if ( $file === $moe || $file === $moe->get_file() ) { + unset( $this->loaded_translations[ $locale ][ $textdomain ][ $i ] ); + unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); + return true; } - - return true; } - foreach ( $this->loaded_translations as $l => $domains ) { - foreach ( $domains[ $textdomain ] as $i => $moe ) { - if ( $file === $moe || $file === $moe->get_file() ) { - unset( $this->loaded_translations[ $l ][ $textdomain ][ $i ] ); - unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); - return true; - } + return true; + } + + foreach ( $this->loaded_translations as $l => $domains ) { + foreach ( $domains[ $textdomain ] as $i => $moe ) { + if ( $file === $moe || $file === $moe->get_file() ) { + unset( $this->loaded_translations[ $l ][ $textdomain ][ $i ] ); + unset( $this->loaded_files[ $moe->get_file() ][ $l ][ $textdomain ] ); + return true; } } - - return true; } + return true; + } + + /** + * Unloads all translation files for a given text domain. + * + * @since 6.5.0 + * + * @param string $textdomain Optional. Text domain. Default 'default'. + * @param string $locale Optional. Locale. Default all locales. + * @return bool True on success, false otherwise. + */ + public function unload_textdomain( string $textdomain = 'default', string $locale = null ) { if ( null !== $locale ) { foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $moe ) { unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); @@ -375,9 +381,9 @@ protected function locate_translation( string $singular, string $textdomain = 'd 'source' => $moe, ); } - if ( false !== $moe->error() ) { + if ( null !== $moe->error() ) { // Unload this file, something is wrong. - $this->unload( $textdomain, $moe, $locale ); + $this->unload_file( $moe, $textdomain, $locale ); } } diff --git a/src/wp-includes/i18n/class-wp-translation-file-mo.php b/src/wp-includes/l10n/class-wp-translation-file-mo.php similarity index 99% rename from src/wp-includes/i18n/class-wp-translation-file-mo.php rename to src/wp-includes/l10n/class-wp-translation-file-mo.php index 2af3597298cb5..6d81efe6036ac 100644 --- a/src/wp-includes/i18n/class-wp-translation-file-mo.php +++ b/src/wp-includes/l10n/class-wp-translation-file-mo.php @@ -21,7 +21,6 @@ class WP_Translation_File_MO extends WP_Translation_File { * Used for unpack(). * * @since 6.5.0 - * * @var false|'V'|'N' */ protected $uint32 = false; diff --git a/src/wp-includes/i18n/class-wp-translation-file-php.php b/src/wp-includes/l10n/class-wp-translation-file-php.php similarity index 100% rename from src/wp-includes/i18n/class-wp-translation-file-php.php rename to src/wp-includes/l10n/class-wp-translation-file-php.php diff --git a/src/wp-includes/i18n/class-wp-translation-file.php b/src/wp-includes/l10n/class-wp-translation-file.php similarity index 96% rename from src/wp-includes/i18n/class-wp-translation-file.php rename to src/wp-includes/l10n/class-wp-translation-file.php index 9476529286b54..820b6b763876c 100644 --- a/src/wp-includes/i18n/class-wp-translation-file.php +++ b/src/wp-includes/l10n/class-wp-translation-file.php @@ -17,7 +17,6 @@ abstract class WP_Translation_File { * List of headers. * * @since 6.5.0 - * * @var array */ protected $headers = array(); @@ -26,7 +25,6 @@ abstract class WP_Translation_File { * Whether file has been parsed. * * @since 6.5.0 - * * @var bool */ protected $parsed = false; @@ -35,16 +33,14 @@ abstract class WP_Translation_File { * Error information. * * @since 6.5.0 - * - * @var bool|string + * @var string|null Error message or null if no error. */ - protected $error = false; + protected $error; /** * File name. * * @since 6.5.0 - * * @var string */ protected $file = ''; @@ -53,7 +49,6 @@ abstract class WP_Translation_File { * Translation entries. * * @since 6.5.0 - * * @var array */ protected $entries = array(); @@ -62,7 +57,6 @@ abstract class WP_Translation_File { * Plural forms function. * * @since 6.5.0 - * * @var callable|null Plural forms. */ protected $plural_forms = null; @@ -109,6 +103,42 @@ public static function create( string $file, string $filetype = null ) { } } + /** + * Creates a new WP_Translation_File instance for a given file. + * + * @since 6.5.0 + * + * @param string $file Source file name. + * @param string $filetype Desired target file type. + * @return string|false Transformed translation file contents on success, false otherwise. + */ + public static function transform( string $file, string $filetype ) { + $source = self::create( $file ); + + if ( false === $source ) { + return false; + } + + switch ( $filetype ) { + case 'mo': + $destination = new WP_Translation_File_MO( '' ); + break; + case 'php': + $destination = new WP_Translation_File_PHP( '' ); + break; + default: + return false; + } + + $success = $destination->import( $source ); + + if ( ! $success ) { + return false; + } + + return $destination->export(); + } + /** * Returns all headers. * @@ -143,7 +173,7 @@ public function entries() { * * @since 6.5.0 * - * @return bool|string Error + * @return string|null Error message or null if no error. */ public function error() { return $this->error; @@ -227,42 +257,6 @@ public function make_plural_form_function( string $expression ) { } } - /** - * Creates a new WP_Translation_File instance for a given file. - * - * @since 6.5.0 - * - * @param string $file Source file name. - * @param string $filetype Desired target file type. - * @return string|false Transformed translation file contents on success, false otherwise. - */ - public static function transform( string $file, string $filetype ) { - $source = self::create( $file ); - - if ( false === $source ) { - return false; - } - - switch ( $filetype ) { - case 'mo': - $destination = new WP_Translation_File_MO( '' ); - break; - case 'php': - $destination = new WP_Translation_File_PHP( '' ); - break; - default: - return false; - } - - $success = $destination->import( $source ); - - if ( ! $success ) { - return false; - } - - return $destination->export(); - } - /** * Imports translations from another file. * @@ -272,7 +266,7 @@ public static function transform( string $file, string $filetype ) { * @return bool True on success, false otherwise. */ protected function import( WP_Translation_File $source ) { - if ( false !== $source->error() ) { + if ( null !== $source->error() ) { return false; } @@ -280,7 +274,7 @@ protected function import( WP_Translation_File $source ) { $this->entries = $source->entries(); $this->error = $source->error(); - return false === $this->error; + return null === $this->error; } /** diff --git a/src/wp-includes/i18n/class-wp-translations.php b/src/wp-includes/l10n/class-wp-translations.php similarity index 99% rename from src/wp-includes/i18n/class-wp-translations.php rename to src/wp-includes/l10n/class-wp-translations.php index eac787954a4aa..c3f5b16a55ece 100644 --- a/src/wp-includes/i18n/class-wp-translations.php +++ b/src/wp-includes/l10n/class-wp-translations.php @@ -20,7 +20,6 @@ class WP_Translations { * Text domain. * * @since 6.5.0 - * * @var string */ protected $textdomain = 'default'; @@ -29,7 +28,6 @@ class WP_Translations { * Translation controller instance. * * @since 6.5.0 - * * @var WP_Translation_Controller */ protected $controller; diff --git a/src/wp-settings.php b/src/wp-settings.php index 55bfcbcc16989..5679b02241683 100644 --- a/src/wp-settings.php +++ b/src/wp-settings.php @@ -114,11 +114,11 @@ require ABSPATH . WPINC . '/class-wp.php'; require ABSPATH . WPINC . '/class-wp-error.php'; require ABSPATH . WPINC . '/pomo/mo.php'; -require ABSPATH . WPINC . '/i18n/class-wp-translation-controller.php'; -require ABSPATH . WPINC . '/i18n/class-wp-translations.php'; -require ABSPATH . WPINC . '/i18n/class-wp-translation-file.php'; -require ABSPATH . WPINC . '/i18n/class-wp-translation-file-mo.php'; -require ABSPATH . WPINC . '/i18n/class-wp-translation-file-php.php'; +require ABSPATH . WPINC . '/l10n/class-wp-translation-controller.php'; +require ABSPATH . WPINC . '/l10n/class-wp-translations.php'; +require ABSPATH . WPINC . '/l10n/class-wp-translation-file.php'; +require ABSPATH . WPINC . '/l10n/class-wp-translation-file-mo.php'; +require ABSPATH . WPINC . '/l10n/class-wp-translation-file-php.php'; /** * @global wpdb $wpdb WordPress database abstraction object. diff --git a/tests/phpunit/data/i18n/example-simple.mo b/tests/phpunit/data/l10n/example-simple.mo similarity index 100% rename from tests/phpunit/data/i18n/example-simple.mo rename to tests/phpunit/data/l10n/example-simple.mo diff --git a/tests/phpunit/data/i18n/example-simple.php b/tests/phpunit/data/l10n/example-simple.php similarity index 100% rename from tests/phpunit/data/i18n/example-simple.php rename to tests/phpunit/data/l10n/example-simple.php diff --git a/tests/phpunit/data/i18n/example-simple.po b/tests/phpunit/data/l10n/example-simple.po similarity index 100% rename from tests/phpunit/data/i18n/example-simple.po rename to tests/phpunit/data/l10n/example-simple.po diff --git a/tests/phpunit/data/i18n/fa_IR.mo b/tests/phpunit/data/l10n/fa_IR.mo similarity index 100% rename from tests/phpunit/data/i18n/fa_IR.mo rename to tests/phpunit/data/l10n/fa_IR.mo diff --git a/tests/phpunit/data/i18n/plural.mo b/tests/phpunit/data/l10n/plural.mo similarity index 100% rename from tests/phpunit/data/i18n/plural.mo rename to tests/phpunit/data/l10n/plural.mo diff --git a/tests/phpunit/data/i18n/simple.mo b/tests/phpunit/data/l10n/simple.mo similarity index 100% rename from tests/phpunit/data/i18n/simple.mo rename to tests/phpunit/data/l10n/simple.mo diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index f2e369a48815e..c2cef44957b0f 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -35,7 +35,7 @@ public function test_no_files_loaded_returns_false() { public function test_unload_not_loaded() { $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertFalse( $instance->unload( 'unittest' ) ); + $this->assertFalse( $instance->unload_textdomain( 'unittest' ) ); } /** @@ -51,12 +51,12 @@ public function test_unload_not_loaded() { public function test_unload_entire_textdomain() { $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest' ) ); $this->assertTrue( $instance->is_loaded( 'unittest' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); - $this->assertTrue( $instance->unload( 'unittest' ) ); + $this->assertTrue( $instance->unload_textdomain( 'unittest' ) ); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); } @@ -69,8 +69,8 @@ public function test_unload_entire_textdomain() { */ public function test_unload_file_is_not_actually_loaded() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); @@ -85,22 +85,22 @@ public function test_unload_file_is_not_actually_loaded() { public function test_unload_specific_locale() { $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest' ) ); + $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest' ) ); $this->assertTrue( $instance->is_loaded( 'unittest' ) ); $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->load( DIR_TESTDATA . '/i18n/example-simple.php', 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest', 'es_ES' ) ); $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->unload( 'unittest', null, $instance->get_locale() ) ); + $this->assertTrue( $instance->unload_textdomain( 'unittest', $instance->get_locale() ) ); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->unload( 'unittest', null, 'es_ES' ) ); + $this->assertTrue( $instance->unload_textdomain( 'unittest', 'es_ES' ) ); $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); } @@ -125,12 +125,12 @@ public function test_invalid_files( string $type, string $file_contents, $expect $this->assertInstanceOf( WP_Translation_File::class, $instance ); // Not an error condition until it attempts to parse the file. - $this->assertFalse( $instance->error() ); + $this->assertNull( $instance->error() ); // Trigger parsing. $instance->headers(); - $this->assertNotFalse( $instance->error() ); + $this->assertNotNull( $instance->error() ); if ( null !== $expected_error ) { $this->assertSame( $expected_error, $instance->error() ); @@ -161,7 +161,7 @@ public function data_invalid_files(): array { public function test_load_non_existent_file() { $instance = new WP_Translation_Controller(); - $this->assertFalse( $instance->load( DIR_TESTDATA . '/i18n/file-that-doesnt-exist.mo', 'unittest' ) ); + $this->assertFalse( $instance->load_file( DIR_TESTDATA . '/l10n/file-that-doesnt-exist.mo', 'unittest' ) ); $this->assertFalse( $instance->is_loaded( 'unittest' ) ); } @@ -200,7 +200,7 @@ public function test_create_invalid_filetype() { */ public function test_simple_translation_files( string $file ) { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/' . $file, 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/' . $file, 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); $this->assertFalse( $controller->is_loaded( 'textdomain not loaded' ) ); @@ -245,9 +245,9 @@ public function data_simple_example_files(): array { */ public function test_load_multiple_files() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); @@ -277,7 +277,7 @@ public function test_load_multiple_files() { $this->assertSame( 'twoey dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), 2, '', 'unittest' ), 'Actual translation does not match expected one' ); $this->assertSame( 'twoey dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), -8, '', 'unittest' ), 'Actual translation does not match expected one' ); - $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/simple.mo' ) ); + $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); $this->assertFalse( $controller->translate( 'baba', '', 'unittest' ) ); } @@ -302,9 +302,9 @@ public function test_load_multiple_locales() { $this->assertSame( 'de_DE', $controller->get_locale() ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest', 'es_ES' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest', 'en_US' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest', 'es_ES' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest', 'en_US' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); @@ -320,11 +320,11 @@ public function test_load_multiple_locales() { $this->assertSame( 'dyado', $controller->translate( 'baba', '', 'unittest', 'es_ES' ), 'String should be translated in es_ES' ); $this->assertFalse( $controller->translate( 'baba', '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); - $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'de_DE' ) ); + $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest', 'de_DE' ) ); $this->assertSame( 'oney dragoney', $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should be translated in en_US' ); - $this->assertTrue( $controller->unload( 'unittest', DIR_TESTDATA . '/i18n/plural.mo', 'en_US' ) ); + $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest', 'en_US' ) ); $this->assertFalse( $controller->translate_plural( array( 'one dragon', '%d dragons' ), 1, '', 'unittest', 'en_US' ), 'String should not be translated in en_US' ); } @@ -340,11 +340,11 @@ public function test_unload_with_multiple_locales() { $ginger_mo->set_locale( 'de_DE' ); $this->assertSame( 'de_DE', $ginger_mo->get_locale() ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); $ginger_mo->set_locale( 'es_ES' ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/simple.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); $ginger_mo->set_locale( 'pl_PL' ); - $this->assertTrue( $ginger_mo->load( DIR_TESTDATA . '/i18n/plural.mo', 'unittest' ) ); + $this->assertTrue( $ginger_mo->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest' ) ); $this->assertSame( 'pl_PL', $ginger_mo->get_locale() ); $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); @@ -357,7 +357,7 @@ public function test_unload_with_multiple_locales() { $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'es_ES' ) ); $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'de_DE' ) ); - $this->assertTrue( $ginger_mo->unload( 'unittest' ) ); + $this->assertTrue( $ginger_mo->unload_textdomain( 'unittest' ) ); $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'pl_PL' ) ); @@ -373,8 +373,8 @@ public function test_unload_with_multiple_locales() { */ public function test_load_with_default_textdomain() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo' ) ); $this->assertFalse( $controller->is_loaded( 'unittest' ) ); $this->assertSame( 'translation', $controller->translate( 'original' ) ); } @@ -386,8 +386,8 @@ public function test_load_with_default_textdomain() { */ public function test_load_same_file_twice() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); } @@ -399,8 +399,8 @@ public function test_load_same_file_twice() { */ public function test_load_file_is_already_loaded_for_different_textdomain() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ) ); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'bar' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'foo' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'bar' ) ); $this->assertTrue( $controller->is_loaded( 'foo' ) ); $this->assertTrue( $controller->is_loaded( 'bar' ) ); @@ -421,7 +421,7 @@ public function test_load_file_is_already_loaded_for_different_textdomain() { */ public function test_load_no_plurals() { $controller = new WP_Translation_Controller(); - $this->assertTrue( $controller->load( DIR_TESTDATA . '/i18n/fa_IR.mo', 'unittest' ) ); + $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/fa_IR.mo', 'unittest' ) ); $this->assertTrue( $controller->is_loaded( 'unittest' ) ); @@ -453,7 +453,7 @@ public function test_get_headers_no_loaded_translations() { */ public function test_get_headers_with_default_textdomain() { $controller = new WP_Translation_Controller(); - $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo' ); + $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo' ); $headers = $controller->get_headers(); $this->assertSame( array( @@ -470,7 +470,7 @@ public function test_get_headers_with_default_textdomain() { */ public function test_get_headers_no_loaded_translations_for_domain() { $controller = new WP_Translation_Controller(); - $controller->load( DIR_TESTDATA . '/i18n/example-simple.mo', 'foo' ); + $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'foo' ); $headers = $controller->get_headers( 'bar' ); $this->assertEmpty( $headers ); } @@ -494,7 +494,7 @@ public function test_get_entries_no_loaded_translations() { */ public function test_get_entries_with_default_textdomain() { $controller = new WP_Translation_Controller(); - $controller->load( DIR_TESTDATA . '/i18n/simple.mo' ); + $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo' ); $headers = $controller->get_entries(); $this->assertSame( array( @@ -512,7 +512,7 @@ public function test_get_entries_with_default_textdomain() { */ public function test_get_entries_no_loaded_translations_for_domain() { $controller = new WP_Translation_Controller(); - $controller->load( DIR_TESTDATA . '/i18n/simple.mo', 'foo' ); + $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'foo' ); $headers = $controller->get_entries( 'bar' ); $this->assertEmpty( $headers ); } @@ -544,14 +544,14 @@ public function test_convert_format( string $source_file, string $destination_fo $destination = WP_Translation_File::create( $destination_file, $destination_format ); $this->assertInstanceOf( WP_Translation_File::class, $destination ); - $this->assertFalse( $destination->error() ); + $this->assertNull( $destination->error() ); $this->assertTrue( filesize( $destination_file ) > 0 ); $destination_read = WP_Translation_File::create( $destination_file, $destination_format ); $this->assertInstanceOf( WP_Translation_File::class, $destination_read ); - $this->assertFalse( $destination_read->error() ); + $this->assertNull( $destination_read->error() ); $source_headers = $source->headers(); $destination_headers = $destination_read->headers(); @@ -575,7 +575,7 @@ public function data_export_matrix(): array { foreach ( $formats as $input_format ) { foreach ( $formats as $output_format ) { - $matrix[ "$input_format to $output_format" ] = array( DIR_TESTDATA . '/i18n/example-simple.' . $input_format, $output_format ); + $matrix[ "$input_format to $output_format" ] = array( DIR_TESTDATA . '/l10n/example-simple.' . $input_format, $output_format ); } } @@ -589,7 +589,7 @@ public function data_export_matrix(): array { */ public function test_convert_format_invalid_source() { $this->assertFalse( WP_Translation_File::transform( 'this-file-does-not-exist', 'invalid' ) ); - $this->assertFalse( WP_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'invalid' ) ); - $this->assertNotFalse( WP_Translation_File::transform( DIR_TESTDATA . '/i18n/example-simple.mo', 'php' ) ); + $this->assertFalse( WP_Translation_File::transform( DIR_TESTDATA . '/l10n/example-simple.mo', 'invalid' ) ); + $this->assertNotFalse( WP_Translation_File::transform( DIR_TESTDATA . '/l10n/example-simple.mo', 'php' ) ); } } From 8914c25fe6005fd95cfe634cfcd356ef4313a2d8 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 15 Jan 2024 22:30:08 +0100 Subject: [PATCH 39/65] Another minor docblock change --- src/wp-includes/l10n/class-wp-translation-file-mo.php | 1 - 1 file changed, 1 deletion(-) 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 6d81efe6036ac..0f59e98765d49 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-mo.php +++ b/src/wp-includes/l10n/class-wp-translation-file-mo.php @@ -29,7 +29,6 @@ class WP_Translation_File_MO extends WP_Translation_File { * The magic number of the GNU message catalog format. * * @since 6.5.0 - * * @var int */ const MAGIC_MARKER = 0x950412de; From 8cf9742c62f536db0a5e55de685f214a95bb2428 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:13:37 +0100 Subject: [PATCH 40/65] Add back return types Co-authored-by: Weston Ruter --- .../l10n/class-wp-translation-file-mo.php | 8 ++++---- .../l10n/class-wp-translation-file-php.php | 4 ++-- src/wp-includes/l10n/class-wp-translation-file.php | 12 ++++++------ 3 files changed, 12 insertions(+), 12 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 0f59e98765d49..22c2b452b180d 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-mo.php +++ b/src/wp-includes/l10n/class-wp-translation-file-mo.php @@ -85,7 +85,7 @@ protected function detect_endian_and_validate_file( string $header ) { * * @return bool True on success, false otherwise. */ - protected function parse_file() { + protected function parse_file(): bool { $this->parsed = true; $file_contents = file_get_contents( $this->file ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents @@ -175,7 +175,7 @@ protected function parse_file() { * * @return string Translation file contents. */ - public function export() { + public function export(): string { // Prefix the headers as the first key. $headers_string = ''; foreach ( $this->headers as $header => $value ) { @@ -205,13 +205,13 @@ public function export() { foreach ( array_keys( $entries ) as $original ) { $o_addr .= pack( $this->uint32 . '*', strlen( $original ), $entry_offsets ); $entry_offsets += strlen( $original ) + 1; - $o_entries .= $original . pack( 'x' ); + $o_entries .= $original . "\0"; } foreach ( $entries as $translations ) { $t_addr .= pack( $this->uint32 . '*', strlen( $translations ), $entry_offsets ); $entry_offsets += strlen( $translations ) + 1; - $t_entries .= $translations . pack( 'x' ); + $t_entries .= $translations . "\0"; } return $file_header . $o_addr . $t_addr . $o_entries . $t_entries; 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 b34e6519baa0f..63ece6250ba8f 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-php.php +++ b/src/wp-includes/l10n/class-wp-translation-file-php.php @@ -48,7 +48,7 @@ protected function parse_file() { * * @return string Translation file contents. */ - public function export() { + public function export(): string { $data = array_merge( $this->headers, array( 'messages' => $this->entries ) ); return 'var_export( $data ) . ';' . PHP_EOL; @@ -65,7 +65,7 @@ public function export() { * @param mixed $value The variable you want to export. * @return string The variable representation. */ - private function var_export( $value ) { + private function var_export( $value ): string { if ( ! is_array( $value ) ) { return var_export( $value, true ); } diff --git a/src/wp-includes/l10n/class-wp-translation-file.php b/src/wp-includes/l10n/class-wp-translation-file.php index 820b6b763876c..cb358de12bd6b 100644 --- a/src/wp-includes/l10n/class-wp-translation-file.php +++ b/src/wp-includes/l10n/class-wp-translation-file.php @@ -146,7 +146,7 @@ public static function transform( string $file, string $filetype ) { * * @return array Headers. */ - public function headers() { + public function headers(): array { if ( ! $this->parsed ) { $this->parse_file(); } @@ -160,7 +160,7 @@ public function headers() { * * @return array Entries. */ - public function entries() { + public function entries(): array { if ( ! $this->parsed ) { $this->parse_file(); } @@ -186,7 +186,7 @@ public function error() { * * @return string File name. */ - public function get_file() { + public function get_file(): string { return $this->file; } @@ -198,7 +198,7 @@ public function get_file() { * @param string $text String to translate. * @return false|string Translation(s) on success, false otherwise. */ - public function translate( string $text ) { + public function translate( string $text ): bool { if ( ! $this->parsed ) { $this->parse_file(); } @@ -214,7 +214,7 @@ public function translate( string $text ) { * @param int $number Count. * @return int Plural form. */ - public function get_plural_form( int $number ) { + public function get_plural_form( int $number ): int { if ( ! $this->parsed ) { $this->parse_file(); } @@ -265,7 +265,7 @@ public function make_plural_form_function( string $expression ) { * @param WP_Translation_File $source Source file. * @return bool True on success, false otherwise. */ - protected function import( WP_Translation_File $source ) { + protected function import( WP_Translation_File $source ): bool { if ( null !== $source->error() ) { return false; } From bed71871dbf88cd19d5f492549b12312b5a3a89b Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:15:45 +0100 Subject: [PATCH 41/65] Rename method --- .../l10n/class-wp-translation-controller.php | 2 +- .../tests/l10n/wpTranslationController.php | 24 +++--- .../tests/l10n/wpTranslationsConvert.php | 76 +++++++++---------- 3 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index de6effcccc60f..f2c573a298391 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -223,7 +223,7 @@ public function unload_textdomain( string $textdomain = 'default', string $local * @param string $locale Optional. Locale. Default current locale. * @return bool True if there are any loaded translations, false otherwise. */ - public function is_loaded( string $textdomain = 'default', string $locale = null ) { + public function is_textdomain_loaded(string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } diff --git a/tests/phpunit/tests/l10n/wpTranslationController.php b/tests/phpunit/tests/l10n/wpTranslationController.php index 6f4fdb99c1e4e..9e33a1f5430e1 100644 --- a/tests/phpunit/tests/l10n/wpTranslationController.php +++ b/tests/phpunit/tests/l10n/wpTranslationController.php @@ -35,7 +35,7 @@ public function test_load_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_textdomain_loaded( 'wp-tests-domain' ); $headers = WP_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); $entries = WP_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); @@ -83,7 +83,7 @@ public function test_load_textdomain_existing_override() { $is_loaded_wp = is_textdomain_loaded( 'wp-tests-domain' ); - $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_textdomain_loaded( 'wp-tests-domain' ); remove_filter( 'override_load_textdomain', '__return_true' ); @@ -258,7 +258,7 @@ public function test_unload_textdomain() { $compat_instance = $l10n['wp-tests-domain'] ?? null; - $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_textdomain_loaded( 'wp-tests-domain' ); $headers = WP_Translation_Controller::instance()->get_headers( 'wp-tests-domain' ); $entries = WP_Translation_Controller::instance()->get_entries( 'wp-tests-domain' ); @@ -282,13 +282,13 @@ public function test_unload_textdomain_existing_override() { $unload_successful = unload_textdomain( 'wp-tests-domain' ); - $is_loaded = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded = WP_Translation_Controller::instance()->is_textdomain_loaded( 'wp-tests-domain' ); remove_filter( 'override_unload_textdomain', '__return_true' ); $unload_successful_after = unload_textdomain( 'wp-tests-domain' ); - $is_loaded_after = WP_Translation_Controller::instance()->is_loaded( 'wp-tests-domain' ); + $is_loaded_after = WP_Translation_Controller::instance()->is_textdomain_loaded( 'wp-tests-domain' ); $this->assertTrue( $unload_successful ); $this->assertTrue( $is_loaded ); @@ -307,14 +307,14 @@ public function test_switch_to_locale_translations_stay_loaded_default_textdomai $actual = __( 'Invalid parameter.' ); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded() ); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded() ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded( 'default', 'es_ES' ) ); restore_previous_locale(); $actual_2 = __( 'Invalid parameter.' ); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded( 'default', 'es_ES' ) ); $this->assertSame( 'Parámetro no válido. ', $actual ); $this->assertSame( 'Invalid parameter.', $actual_2 ); @@ -339,15 +339,15 @@ public function test_switch_to_locale_translations_stay_loaded_custom_textdomain $actual = i18n_plugin_test(); $this->assertSame( 'es_ES', WP_Translation_Controller::instance()->get_locale() ); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'default', 'es_ES' ) ); - $this->assertFalse( WP_Translation_Controller::instance()->is_loaded( 'foo-bar', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded( 'default', 'es_ES' ) ); + $this->assertFalse( WP_Translation_Controller::instance()->is_textdomain_loaded( 'foo-bar', 'es_ES' ) ); restore_previous_locale(); $after = i18n_plugin_test(); - $this->assertTrue( WP_Translation_Controller::instance()->is_loaded( 'internationalized-plugin', 'es_ES' ) ); + $this->assertTrue( WP_Translation_Controller::instance()->is_textdomain_loaded( 'internationalized-plugin', 'es_ES' ) ); $this->assertSame( 'This is a dummy plugin', $before ); $this->assertSame( 'Este es un plugin dummy', $actual ); diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index c2cef44957b0f..862aef5caeada 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -34,14 +34,14 @@ public function test_no_files_loaded_returns_false() { */ public function test_unload_not_loaded() { $instance = new WP_Translation_Controller(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertFalse( $instance->unload_textdomain( 'unittest' ) ); } /** * @covers ::load * @covers ::unload - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * @covers ::translate * @covers ::locate_translation * @covers ::get_files @@ -50,14 +50,14 @@ public function test_unload_not_loaded() { */ public function test_unload_entire_textdomain() { $instance = new WP_Translation_Controller(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); $this->assertTrue( $instance->unload_textdomain( 'unittest' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); } @@ -72,36 +72,36 @@ public function test_unload_file_is_not_actually_loaded() { $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); } /** * @covers ::unload - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * * @return void */ public function test_unload_specific_locale() { $instance = new WP_Translation_Controller(); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest' ) ); + $this->assertTrue( $instance->is_textdomain_loaded( 'unittest' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest', 'es_ES' ) ); $this->assertTrue( $instance->load_file( DIR_TESTDATA . '/l10n/example-simple.php', 'unittest', 'es_ES' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->is_textdomain_loaded( 'unittest', 'es_ES' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest' ) ); $this->assertSame( 'translation', $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); $this->assertTrue( $instance->unload_textdomain( 'unittest', $instance->get_locale() ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest' ) ); - $this->assertTrue( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $instance->is_textdomain_loaded( 'unittest', 'es_ES' ) ); $this->assertTrue( $instance->unload_textdomain( 'unittest', 'es_ES' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest', 'es_ES' ) ); $this->assertFalse( $instance->translate( 'original', '', 'unittest', 'es_ES' ) ); } @@ -154,7 +154,7 @@ public function data_invalid_files(): array { /** * @covers WP_Translation_Controller::load - * @covers WP_Translation_Controller::is_loaded + * @covers WP_Translation_Controller::is_textdomain_loaded * * @return void */ @@ -162,7 +162,7 @@ public function test_load_non_existent_file() { $instance = new WP_Translation_Controller(); $this->assertFalse( $instance->load_file( DIR_TESTDATA . '/l10n/file-that-doesnt-exist.mo', 'unittest' ) ); - $this->assertFalse( $instance->is_loaded( 'unittest' ) ); + $this->assertFalse( $instance->is_textdomain_loaded( 'unittest' ) ); } /** @@ -187,7 +187,7 @@ public function test_create_invalid_filetype() { /** * @covers ::load - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * @covers ::translate * @covers ::translate_plural * @covers ::locate_translation @@ -202,8 +202,8 @@ public function test_simple_translation_files( string $file ) { $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/' . $file, 'unittest' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); - $this->assertFalse( $controller->is_loaded( 'textdomain not loaded' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); + $this->assertFalse( $controller->is_textdomain_loaded( 'textdomain not loaded' ) ); $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); $this->assertFalse( $controller->translate( 'original', '', 'textdomain not loaded' ) ); @@ -233,7 +233,7 @@ public function data_simple_example_files(): array { /** * @covers ::load * @covers ::unload - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * @covers ::translate * @covers ::translate_plural * @covers ::locate_translation @@ -249,7 +249,7 @@ public function test_load_multiple_files() { $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); $this->assertFalse( $controller->translate( 'original', '', 'textdomain not loaded' ) ); @@ -287,7 +287,7 @@ public function test_load_multiple_files() { * @covers ::get_locale * @covers ::load * @covers ::unload - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * @covers ::translate * @covers ::translate_plural * @@ -306,7 +306,7 @@ public function test_load_multiple_locales() { $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest', 'es_ES' ) ); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest', 'en_US' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); // From example-simple.mo @@ -347,22 +347,22 @@ public function test_unload_with_multiple_locales() { $this->assertTrue( $ginger_mo->load_file( DIR_TESTDATA . '/l10n/plural.mo', 'unittest' ) ); $this->assertSame( 'pl_PL', $ginger_mo->get_locale() ); - $this->assertTrue( $ginger_mo->is_loaded( 'unittest' ) ); + $this->assertTrue( $ginger_mo->is_textdomain_loaded( 'unittest' ) ); $ginger_mo->set_locale( 'en_US' ); $this->assertSame( 'en_US', $ginger_mo->get_locale() ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); - $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'pl_PL' ) ); - $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertTrue( $ginger_mo->is_loaded( 'unittest', 'de_DE' ) ); + $this->assertFalse( $ginger_mo->is_textdomain_loaded( 'unittest' ) ); + $this->assertTrue( $ginger_mo->is_textdomain_loaded( 'unittest', 'pl_PL' ) ); + $this->assertTrue( $ginger_mo->is_textdomain_loaded( 'unittest', 'es_ES' ) ); + $this->assertTrue( $ginger_mo->is_textdomain_loaded( 'unittest', 'de_DE' ) ); $this->assertTrue( $ginger_mo->unload_textdomain( 'unittest' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'pl_PL' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'es_ES' ) ); - $this->assertFalse( $ginger_mo->is_loaded( 'unittest', 'de_DE' ) ); + $this->assertFalse( $ginger_mo->is_textdomain_loaded( 'unittest' ) ); + $this->assertFalse( $ginger_mo->is_textdomain_loaded( 'unittest', 'pl_PL' ) ); + $this->assertFalse( $ginger_mo->is_textdomain_loaded( 'unittest', 'es_ES' ) ); + $this->assertFalse( $ginger_mo->is_textdomain_loaded( 'unittest', 'de_DE' ) ); } /** @@ -375,7 +375,7 @@ public function test_load_with_default_textdomain() { $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo' ) ); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo' ) ); - $this->assertFalse( $controller->is_loaded( 'unittest' ) ); + $this->assertFalse( $controller->is_textdomain_loaded( 'unittest' ) ); $this->assertSame( 'translation', $controller->translate( 'original' ) ); } @@ -389,7 +389,7 @@ public function test_load_same_file_twice() { $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); } /** @@ -402,14 +402,14 @@ public function test_load_file_is_already_loaded_for_different_textdomain() { $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'foo' ) ); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'bar' ) ); - $this->assertTrue( $controller->is_loaded( 'foo' ) ); - $this->assertTrue( $controller->is_loaded( 'bar' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'foo' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'bar' ) ); } /** * @covers ::load * @covers ::unload - * @covers ::is_loaded + * @covers ::is_textdomain_loaded * @covers ::translate * @covers ::translate_plural * @covers ::locate_translation @@ -423,7 +423,7 @@ public function test_load_no_plurals() { $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/fa_IR.mo', 'unittest' ) ); - $this->assertTrue( $controller->is_loaded( 'unittest' ) ); + $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); $this->assertFalse( $controller->translate( "string that doesn't exist", '', 'unittest' ) ); From 2e9382b9fff9a7aeaf15f6b4fb7cfdc6ce596207 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:27:38 +0100 Subject: [PATCH 42/65] Remove errant return type again --- src/wp-includes/l10n/class-wp-translation-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-file.php b/src/wp-includes/l10n/class-wp-translation-file.php index cb358de12bd6b..4b3975c7ebf0a 100644 --- a/src/wp-includes/l10n/class-wp-translation-file.php +++ b/src/wp-includes/l10n/class-wp-translation-file.php @@ -198,7 +198,7 @@ public function get_file(): string { * @param string $text String to translate. * @return false|string Translation(s) on success, false otherwise. */ - public function translate( string $text ): bool { + public function translate( string $text ) { if ( ! $this->parsed ) { $this->parse_file(); } From 2cbda5b1ee64d6b8e910cd4449e3ff46ea4d43c4 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:30:24 +0100 Subject: [PATCH 43/65] Apply suggestions from code review Co-authored-by: Weston Ruter --- src/wp-includes/l10n.php | 2 +- src/wp-includes/l10n/class-wp-translation-file.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 5b7f2ec23199a..39ed4e4ff5365 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -837,7 +837,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { * @param string $file Path to the translation file to load. * @param string $domain The text domain. */ - $file = apply_filters( 'load_translation_file', $file, $domain ); + $file = (string) apply_filters( 'load_translation_file', $file, $domain ); $success = $i18n_controller->load_file( $file, $domain, $locale ); diff --git a/src/wp-includes/l10n/class-wp-translation-file.php b/src/wp-includes/l10n/class-wp-translation-file.php index 4b3975c7ebf0a..9dd05711db1fa 100644 --- a/src/wp-includes/l10n/class-wp-translation-file.php +++ b/src/wp-includes/l10n/class-wp-translation-file.php @@ -49,7 +49,7 @@ abstract class WP_Translation_File { * Translation entries. * * @since 6.5.0 - * @var array + * @var array */ protected $entries = array(); @@ -247,7 +247,7 @@ public function get_plural_form( int $number ): int { * @param string $expression Plural form expression. * @return callable(int $num): int Plural forms function. */ - public function make_plural_form_function( string $expression ) { + public function make_plural_form_function( string $expression ): callable { try { $handler = new Plural_Forms( rtrim( $expression, ';' ) ); return array( $handler, 'get' ); From ba412b0798a9647064ac8224ba51faafc1413332 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:34:19 +0100 Subject: [PATCH 44/65] Apply suggestions from code review Co-authored-by: Weston Ruter --- .../l10n/class-wp-translation-controller.php | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index f2c573a298391..b4fbea2e34d06 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -12,7 +12,7 @@ * * @since 6.5.0 */ -class WP_Translation_Controller { +final class WP_Translation_Controller { /** * Current locale. * @@ -24,7 +24,7 @@ class WP_Translation_Controller { /** * Map of loaded translations per locale and text domain. * - * [ Locale => [ Textdomain => [ .., .. ] ] ] + * [ Locale => [ Textdomain => [ ..., ... ] ] ] * * @since 6.5.0 * @var array> @@ -37,7 +37,7 @@ class WP_Translation_Controller { * [ Filename => [ Locale => [ Textdomain => WP_Translation_File ] ] ] * * @since 6.5.0 - * @var array>> + * @var array>> */ protected $loaded_files = array(); @@ -48,11 +48,11 @@ class WP_Translation_Controller { * * @return WP_Translation_Controller */ - public static function instance() { + public static function instance(): WP_Translation_Controller { static $instance; if ( ! $instance ) { - $instance = new WP_Translation_Controller(); + $instance = new self(); } return $instance; @@ -65,7 +65,7 @@ public static function instance() { * * @return string Locale. */ - public function get_locale() { + public function get_locale(): string { return $this->current_locale; } @@ -90,7 +90,7 @@ public function set_locale( string $locale ) { * @param string $locale Optional. Locale. Default current locale. * @return bool True on success, false otherwise. */ - public function load_file( string $translation_file, string $textdomain = 'default', string $locale = null ) { + public function load_file( string $translation_file, string $textdomain = 'default', string $locale = null ): bool { if ( null === $locale ) { $locale = $this->current_locale; } @@ -145,7 +145,7 @@ public function load_file( string $translation_file, string $textdomain = 'defau * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ - public function unload_file( $file, string $textdomain = 'default', string $locale = null ) { + public function unload_file( $file, string $textdomain = 'default', string $locale = null ): bool { if ( is_string( $file ) ) { $file = realpath( $file ); } @@ -184,7 +184,7 @@ public function unload_file( $file, string $textdomain = 'default', string $loca * @param string $locale Optional. Locale. Default all locales. * @return bool True on success, false otherwise. */ - public function unload_textdomain( string $textdomain = 'default', string $locale = null ) { + public function unload_textdomain( string $textdomain = 'default', string $locale = null ): bool { if ( null !== $locale ) { foreach ( $this->loaded_translations[ $locale ][ $textdomain ] as $moe ) { unset( $this->loaded_files[ $moe->get_file() ][ $locale ][ $textdomain ] ); @@ -289,7 +289,7 @@ public function translate_plural( array $plurals, int $number, string $context = } } - /* @var WP_Translation_File $source */ + /** @var WP_Translation_File $source */ $source = $translation['source']; $num = $source->get_plural_form( $number ); @@ -305,7 +305,7 @@ public function translate_plural( array $plurals, int $number, string $context = * @param string $textdomain Optional. Text domain. Default 'default'. * @return array Headers. */ - public function get_headers( string $textdomain = 'default' ) { + public function get_headers( string $textdomain = 'default' ): array { if ( array() === $this->loaded_translations ) { return array(); } @@ -329,7 +329,7 @@ public function get_headers( string $textdomain = 'default' ) { * @param string $header Header name. * @return string Normalized header name. */ - protected function normalize_header( string $header ) { + protected function normalize_header( string $header ): string { $parts = explode( '-', $header ); $parts = array_map( 'ucfirst', $parts ); return implode( '-', $parts ); @@ -343,7 +343,7 @@ protected function normalize_header( string $header ) { * @param string $textdomain Optional. Text domain. Default 'default'. * @return array Entries. */ - public function get_entries( string $textdomain = 'default' ) { + public function get_entries( string $textdomain = 'default' ): array { if ( array() === $this->loaded_translations ) { return array(); } @@ -400,7 +400,7 @@ protected function locate_translation( string $singular, string $textdomain = 'd * @param string $locale Optional. Locale. Default current locale. * @return WP_Translation_File[] List of translation files. */ - protected function get_files( string $textdomain = 'default', string $locale = null ) { + protected function get_files( string $textdomain = 'default', string $locale = null ): array { if ( null === $locale ) { $locale = $this->current_locale; } From b2b98e9ccb227dc7eeec1ccd4a4b0b407e987a48 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:34:04 +0100 Subject: [PATCH 45/65] Lint fix --- src/wp-includes/l10n/class-wp-translation-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index b4fbea2e34d06..6f7f3d5d03d4a 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -223,7 +223,7 @@ public function unload_textdomain( string $textdomain = 'default', string $local * @param string $locale Optional. Locale. Default current locale. * @return bool True if there are any loaded translations, false otherwise. */ - public function is_textdomain_loaded(string $textdomain = 'default', string $locale = null ) { + public function is_textdomain_loaded( string $textdomain = 'default', string $locale = null ) { if ( null === $locale ) { $locale = $this->current_locale; } From 6df6c513312293bacdc7bef2a9bd36fe30e25eb3 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:34:13 +0100 Subject: [PATCH 46/65] Return false if nothing unloaded --- src/wp-includes/l10n/class-wp-translation-controller.php | 2 +- tests/phpunit/tests/l10n/wpTranslationsConvert.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index 6f7f3d5d03d4a..406d556f1b3ed 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -172,7 +172,7 @@ public function unload_file( $file, string $textdomain = 'default', string $loca } } - return true; + return false; } /** diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index 862aef5caeada..a957f9795e45e 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -70,7 +70,7 @@ public function test_unload_entire_textdomain() { public function test_unload_file_is_not_actually_loaded() { $controller = new WP_Translation_Controller(); $this->assertTrue( $controller->load_file( DIR_TESTDATA . '/l10n/example-simple.mo', 'unittest' ) ); - $this->assertTrue( $controller->unload_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); + $this->assertFalse( $controller->unload_file( DIR_TESTDATA . '/l10n/simple.mo', 'unittest' ) ); $this->assertTrue( $controller->is_textdomain_loaded( 'unittest' ) ); $this->assertSame( 'translation', $controller->translate( 'original', '', 'unittest' ) ); From ad57b5c05ef8f553e007c7fee5cfc5bc71af6219 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:39:32 +0100 Subject: [PATCH 47/65] use `substr_replace` --- src/wp-includes/l10n.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index 39ed4e4ff5365..b33e5aefadc1d 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -821,7 +821,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { if ( 'mo' !== $preferred_format ) { array_unshift( $translation_files, - str_replace( '.mo', ".l10n.$preferred_format", $mofile ) + substr_replace( $mofile, '.l10n.', - strlen( $preferred_format ) ) ); } From 6cc6397dce7d71f244f46f7b735c924935f79eec Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:39:37 +0100 Subject: [PATCH 48/65] Add return type --- src/wp-includes/l10n/class-wp-translation-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index 406d556f1b3ed..1adc53a0e59f6 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -223,7 +223,7 @@ public function unload_textdomain( string $textdomain = 'default', string $local * @param string $locale Optional. Locale. Default current locale. * @return bool True if there are any loaded translations, false otherwise. */ - public function is_textdomain_loaded( string $textdomain = 'default', string $locale = null ) { + public function is_textdomain_loaded( string $textdomain = 'default', string $locale = null ): bool { if ( null === $locale ) { $locale = $this->current_locale; } From aa2f84272136a2420312ad225ca2ef279918c66f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:42:13 +0100 Subject: [PATCH 49/65] fix phpdoc --- src/wp-includes/l10n/class-wp-translation-file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-file.php b/src/wp-includes/l10n/class-wp-translation-file.php index 9dd05711db1fa..61efd98270779 100644 --- a/src/wp-includes/l10n/class-wp-translation-file.php +++ b/src/wp-includes/l10n/class-wp-translation-file.php @@ -49,7 +49,7 @@ abstract class WP_Translation_File { * Translation entries. * * @since 6.5.0 - * @var array + * @var array */ protected $entries = array(); From d13731b84ee148475abb919786eb042e88304944 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:46:50 +0100 Subject: [PATCH 50/65] String errors --- src/wp-includes/l10n/class-wp-translation-file-mo.php | 8 ++++---- src/wp-includes/l10n/class-wp-translation-file-php.php | 2 +- 2 files changed, 5 insertions(+), 5 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 22c2b452b180d..225b48a836342 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-mo.php +++ b/src/wp-includes/l10n/class-wp-translation-file-mo.php @@ -74,7 +74,7 @@ protected function detect_endian_and_validate_file( string $header ) { return 'V'; } - $this->error = "Magic Marker doesn't exist"; + $this->error = 'Magic marker does not exist'; return false; } @@ -97,7 +97,7 @@ protected function parse_file(): bool { $file_length = strlen( $file_contents ); if ( $file_length < 24 ) { - $this->error = 'Invalid Data.'; + $this->error = 'Invalid data'; return false; } @@ -123,12 +123,12 @@ protected function parse_file(): bool { $offsets['translations_length'] = $offsets['hash_addr'] - $offsets['translations_addr']; if ( $offsets['rev'] > 0 ) { - $this->error = 'Unsupported Revision.'; + $this->error = 'Unsupported revision'; return false; } if ( $offsets['translations_addr'] > $file_length || $offsets['originals_addr'] > $file_length ) { - $this->error = 'Invalid Data.'; + $this->error = 'Invalid data'; return false; } 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 63ece6250ba8f..9f5b5abd9889b 100644 --- a/src/wp-includes/l10n/class-wp-translation-file-php.php +++ b/src/wp-includes/l10n/class-wp-translation-file-php.php @@ -23,7 +23,7 @@ protected function parse_file() { $result = include $this->file; if ( ! $result || ! is_array( $result ) ) { - $this->error = true; + $this->error = 'Invalid data'; return; } From bf1a7a90a031abe196263ff04753b0cde891edbe Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:46:53 +0100 Subject: [PATCH 51/65] Array shape --- src/wp-includes/l10n/class-wp-translation-controller.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index 1adc53a0e59f6..825533a5a4b21 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -365,7 +365,12 @@ public function get_entries( string $textdomain = 'default' ): array { * @param string $singular Singular translation. * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Default current locale. - * @return array{source: WP_Translation_File, entries: string[]}|false Translations on success, false otherwise. + * @return array{source: WP_Translation_File, entries: string[]}|false { + * Translations on success, false otherwise. + * + * @type WP_Translation_File $source Translation file instance. + * @type string[] $entries Array of translation entries. + * } */ protected function locate_translation( string $singular, string $textdomain = 'default', string $locale = null ) { if ( array() === $this->loaded_translations ) { From 5702b59818663874c1cf31e5dbe0e5443b642925 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 09:54:20 +0100 Subject: [PATCH 52/65] Update error messages in tests --- tests/phpunit/tests/l10n/wpTranslationsConvert.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index a957f9795e45e..6fa1396a6d0a5 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -144,11 +144,11 @@ public function data_invalid_files(): array { return array( array( 'php', '' ), array( 'php', ' Date: Wed, 17 Jan 2024 22:03:39 +0100 Subject: [PATCH 53/65] Apply suggestion Co-authored-by: Weston Ruter --- src/wp-includes/l10n/class-wp-translation-controller.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index 825533a5a4b21..cca7f026eea9e 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -265,7 +265,12 @@ public function translate( string $text, string $context = '', string $textdomai * * @since 6.5.0 * - * @param array{0: string, 1: string} $plurals Pair of singular and plural translations. + * @param array{0: string, 1: string} { + * Pair of singular and plural translations. + * + * @type string $0 Singular translation. + * @type string $1 Plural translation. + * } * @param int $number Number of items. * @param string $context Optional. Context for the string. Default empty string. * @param string $textdomain Optional. Text domain. Default 'default'. From 0e8f5a1b5e221d05dce97b9a931a5eda61c70840 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Wed, 17 Jan 2024 08:52:00 +0000 Subject: [PATCH 54/65] Bundled Themes: Typo corrections in various themes docblocks. Props shailu25. Fixes #60268. See #59651. git-svn-id: https://develop.svn.wordpress.org/trunk@57300 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-content/themes/twentyfifteen/css/editor-blocks.css | 2 +- src/wp-content/themes/twentynineteen/inc/color-patterns.php | 4 ++-- src/wp-content/themes/twentysixteen/css/blocks.css | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-content/themes/twentyfifteen/css/editor-blocks.css b/src/wp-content/themes/twentyfifteen/css/editor-blocks.css index 71afab38b7a14..1041ff5f14741 100644 --- a/src/wp-content/themes/twentyfifteen/css/editor-blocks.css +++ b/src/wp-content/themes/twentyfifteen/css/editor-blocks.css @@ -803,7 +803,7 @@ p.has-drop-cap:not(:focus)::first-letter { } } -/* Seperator */ +/* Separator */ .wp-block-separator { max-width: 100px; diff --git a/src/wp-content/themes/twentynineteen/inc/color-patterns.php b/src/wp-content/themes/twentynineteen/inc/color-patterns.php index 2295436897ff3..abe3419fa9f14 100644 --- a/src/wp-content/themes/twentynineteen/inc/color-patterns.php +++ b/src/wp-content/themes/twentynineteen/inc/color-patterns.php @@ -72,8 +72,8 @@ function twentynineteen_custom_colors_css() { * Set background for: * - featured image :before * - featured image :before - * - post thumbmail :before - * - post thumbmail :before + * - post thumbnail :before + * - post thumbnail :before * - Submenu * - Sticky Post * - buttons diff --git a/src/wp-content/themes/twentysixteen/css/blocks.css b/src/wp-content/themes/twentysixteen/css/blocks.css index de6f19fd5359d..3b6257acf0e55 100644 --- a/src/wp-content/themes/twentysixteen/css/blocks.css +++ b/src/wp-content/themes/twentysixteen/css/blocks.css @@ -292,7 +292,7 @@ p.has-drop-cap:not(:focus)::first-letter { outline-offset: -4px; } -/* Seperator */ +/* Separator */ hr.wp-block-separator { border: 0; From b45415e80927e624eab48e8f7edb4e2e72cc5091 Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Wed, 17 Jan 2024 08:59:42 +0000 Subject: [PATCH 55/65] Twenty Twenty: Replace `wp_date()` with `date_i18n()`. Since WordPress 5.3 it is recommended to use `wp_date()` instead of `date_i18n()`. This changeset replaces the function in Twenty Twenty, with a fallback to `date_i18n()` for old versions of WordPress. For more info, see https://make.wordpress.org/core/2019/09/23/date-time-improvements-wp-5-3/. Props sachyya-sachet, joyously, sabernhardt, poena. Fixes #48589. git-svn-id: https://develop.svn.wordpress.org/trunk@57301 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-content/themes/twentytwenty/footer.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/wp-content/themes/twentytwenty/footer.php b/src/wp-content/themes/twentytwenty/footer.php index fb3bc2f2c373a..860266e52a367 100644 --- a/src/wp-content/themes/twentytwenty/footer.php +++ b/src/wp-content/themes/twentytwenty/footer.php @@ -20,10 +20,13 @@ From 06939bea69dde5207dcb393c77591a692fbfdbeb Mon Sep 17 00:00:00 2001 From: Jb Audras Date: Wed, 17 Jan 2024 09:07:15 +0000 Subject: [PATCH 56/65] Docs: Fix var types of parameters in `sanitize_option()` and `sanitize_option_{$option}`. The related docblocks were previously defining `$value` and `$original_value` as if they were of type `string`, but they can also be of other types, like `array`. Props gerardreches, crstauf, mukesh27. Fixes #60214. See #59651. git-svn-id: https://develop.svn.wordpress.org/trunk@57302 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/formatting.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php index 83938306b0774..05b103e6f755f 100644 --- a/src/wp-includes/formatting.php +++ b/src/wp-includes/formatting.php @@ -4846,8 +4846,8 @@ function wp_make_link_relative( $link ) { * @global wpdb $wpdb WordPress database abstraction object. * * @param string $option The name of the option. - * @param string $value The unsanitized value. - * @return string Sanitized value. + * @param mixed $value The unsanitized value. + * @return mixed Sanitized value. */ function sanitize_option( $option, $value ) { global $wpdb; @@ -5119,9 +5119,9 @@ function sanitize_option( $option, $value ) { * @since 2.3.0 * @since 4.3.0 Added the `$original_value` parameter. * - * @param string $value The sanitized option value. + * @param mixed $value The sanitized option value. * @param string $option The option name. - * @param string $original_value The original value passed to the function. + * @param mixed $original_value The original value passed to the function. */ return apply_filters( "sanitize_option_{$option}", $value, $option, $original_value ); } From 53125107c80bf761f616c3595b9ae7dc321e5a14 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 14:30:47 +0000 Subject: [PATCH 57/65] I18N: Prevent PHP warning in `WP_Textdomain_Registry`. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevents a warning upon cache invalidation after language pack updates if the arguments don’t have the expected format. Follow-up to [57287], [57290], [57298], [57299]. See #58919. git-svn-id: https://develop.svn.wordpress.org/trunk@57303 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-textdomain-registry.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/wp-includes/class-wp-textdomain-registry.php b/src/wp-includes/class-wp-textdomain-registry.php index 8ff8ad0ae23de..54f5f15c4d069 100644 --- a/src/wp-includes/class-wp-textdomain-registry.php +++ b/src/wp-includes/class-wp-textdomain-registry.php @@ -228,7 +228,11 @@ public function get_language_files_from_path( $path ) { * @return void */ public function invalidate_mo_files_cache( $upgrader, $hook_extra ) { - if ( 'translation' !== $hook_extra['type'] || array() === $hook_extra['translations'] ) { + if ( + ! isset( $hook_extra['type'] ) || + 'translation' !== $hook_extra['type'] || + array() === $hook_extra['translations'] + ) { return; } From 3884032f0650d4ba600913a89d887a4acba0a709 Mon Sep 17 00:00:00 2001 From: Sergey Biryukov Date: Wed, 17 Jan 2024 17:58:09 +0000 Subject: [PATCH 58/65] Twenty Twenty-Four: Remove extra tab character inside the text domain. Follow-up to [57281]. Props sabernhardt. Fixes #60245. git-svn-id: https://develop.svn.wordpress.org/trunk@57304 602fd350-edb4-49c9-b593-d223f7449a82 --- .../themes/twentytwentyfour/patterns/hidden-portfolio-hero.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php b/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php index b0922d2ec27f4..1af3e7dbd17c1 100644 --- a/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php +++ b/src/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php @@ -13,7 +13,7 @@
-

Leia Acosta, a passionate photographer who finds inspiration in capturing the fleeting beauty of life.', 'twentytwentyfour ' ) ); ?>

+

Leia Acosta, a passionate photographer who finds inspiration in capturing the fleeting beauty of life.', 'twentytwentyfour' ) ); ?>

From 54869c70bce65dba6f319048e0b807e2b48c63ae Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 18:54:36 +0000 Subject: [PATCH 59/65] I18N: Fix duplicate `determine_locale()` tests added in [57286]. Props johnbillion. See #58696. git-svn-id: https://develop.svn.wordpress.org/trunk@57305 602fd350-edb4-49c9-b593-d223f7449a82 --- tests/phpunit/tests/l10n/determineLocale.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/phpunit/tests/l10n/determineLocale.php b/tests/phpunit/tests/l10n/determineLocale.php index 3a675dc515098..ca8c0bafeccb7 100644 --- a/tests/phpunit/tests/l10n/determineLocale.php +++ b/tests/phpunit/tests/l10n/determineLocale.php @@ -300,11 +300,11 @@ public function test_language_param_installing_incorrect_string() { } public function test_wp_local_package_global_not_installing() { - $_REQUEST['language'] = 'de_DE'; + $GLOBALS['wp_local_package'] = 'de_DE'; $this->assertSame( 'en_US', determine_locale() ); } public function test_wp_local_package_global_installing() { - $_REQUEST['language'] = 'de_DE'; + $GLOBALS['wp_local_package'] = 'de_DE'; wp_installing( true ); $this->assertSame( 'de_DE', determine_locale() ); } From 569853618bef5a671dd9bc4c0558422dc015989f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Wed, 17 Jan 2024 22:04:27 +0100 Subject: [PATCH 60/65] Fix phpdoc --- src/wp-includes/l10n/class-wp-translation-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index cca7f026eea9e..f8f51b4d962be 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -265,7 +265,7 @@ public function translate( string $text, string $context = '', string $textdomai * * @since 6.5.0 * - * @param array{0: string, 1: string} { + * @param array{0: string, 1: string} $plurals { * Pair of singular and plural translations. * * @type string $0 Singular translation. From 726357f07a7c27f7bf5389c0da7fc8b97f9ce057 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 22 Jan 2024 23:09:14 +0100 Subject: [PATCH 61/65] Move compat function closer to others --- src/wp-includes/compat.php | 64 +++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/wp-includes/compat.php b/src/wp-includes/compat.php index b59ee6d48e1fc..429c5f92e7e5c 100644 --- a/src/wp-includes/compat.php +++ b/src/wp-includes/compat.php @@ -420,6 +420,38 @@ function array_key_last( array $array ) { // phpcs:ignore Universal.NamingConven } } +if ( ! function_exists( 'array_is_list' ) ) { + /** + * Polyfill for `array_is_list()` function added in PHP 8.1. + * + * Determines if the given array is a list. + * + * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. + * + * @see https://github.com/symfony/polyfill-php81/tree/main + * + * @since 6.5.0 + * + * @param array $arr The array being evaluated. + * @return bool True if array is a list, false otherwise. + */ + function array_is_list( $arr ) { + if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) { + return true; + } + + $next_key = -1; + + foreach ( $arr as $k => $v ) { + if ( ++$next_key !== $k ) { + return false; + } + } + + return true; + } +} + if ( ! function_exists( 'str_contains' ) ) { /** * Polyfill for `str_contains()` function added in PHP 8.0. @@ -497,35 +529,3 @@ function str_ends_with( $haystack, $needle ) { if ( ! defined( 'IMG_WEBP' ) ) { define( 'IMG_WEBP', IMAGETYPE_WEBP ); } - -if ( ! function_exists( 'array_is_list' ) ) { - /** - * Polyfill for `array_is_list()` function added in PHP 8.1. - * - * Determines if the given array is a list. - * - * An array is considered a list if its keys consist of consecutive numbers from 0 to count($array)-1. - * - * @see https://github.com/symfony/polyfill-php81/tree/main - * - * @since 6.5.0 - * - * @param array $arr The array being evaluated. - * @return bool True if array is a list, false otherwise. - */ - function array_is_list( $arr ) { - if ( ( array() === $arr ) || ( array_values( $arr ) === $arr ) ) { - return true; - } - - $next_key = -1; - - foreach ( $arr as $k => $v ) { - if ( ++$next_key !== $k ) { - return false; - } - } - - return true; - } -} From f32eb49ce40b76ac61515ed7b5db5ab2d299e49f Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 22 Jan 2024 23:10:03 +0100 Subject: [PATCH 62/65] Correcting the class name capitalization and adding brackets for the function name --- src/wp-includes/l10n.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n.php b/src/wp-includes/l10n.php index b33e5aefadc1d..4f3ec8bfedc02 100644 --- a/src/wp-includes/l10n.php +++ b/src/wp-includes/l10n.php @@ -846,7 +846,7 @@ function load_textdomain( $domain, $mofile, $locale = null ) { $i18n_controller->load_file( $l10n[ $domain ]->get_filename(), $domain, $locale ); } - // Unset Noop_Translations reference in get_translations_for_domain. + // Unset NOOP_Translations reference in get_translations_for_domain(). unset( $l10n[ $domain ] ); $l10n[ $domain ] = new WP_Translations( $i18n_controller, $domain ); From f06a84ab7829e06dd1d38819ea9b4ad51db961dc Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 22 Jan 2024 23:11:15 +0100 Subject: [PATCH 63/65] Update docblocks as per feedback --- src/wp-includes/l10n/class-wp-translation-controller.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index f8f51b4d962be..a8a52d95ef5e8 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -140,9 +140,9 @@ public function load_file( string $translation_file, string $textdomain = 'defau * * @since 6.5.0 * - * @param WP_Translation_File|string $file Optional. Translation file instance or file name. Default all files. + * @param WP_Translation_File|string $file Optional. Translation file instance or file name. Defaults to all files. * @param string $textdomain Optional. Text domain. Default 'default'. - * @param string $locale Optional. Locale. Default all locales. + * @param string $locale Optional. Locale. Defaults to all locales. * @return bool True on success, false otherwise. */ public function unload_file( $file, string $textdomain = 'default', string $locale = null ): bool { @@ -181,7 +181,7 @@ public function unload_file( $file, string $textdomain = 'default', string $loca * @since 6.5.0 * * @param string $textdomain Optional. Text domain. Default 'default'. - * @param string $locale Optional. Locale. Default all locales. + * @param string $locale Optional. Locale. Defaults to all locales. * @return bool True on success, false otherwise. */ public function unload_textdomain( string $textdomain = 'default', string $locale = null ): bool { From d603381ffa96edb02b808a535c744831c39de41c Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 22 Jan 2024 23:27:35 +0100 Subject: [PATCH 64/65] Fix description --- src/wp-includes/l10n/class-wp-translation-controller.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wp-includes/l10n/class-wp-translation-controller.php b/src/wp-includes/l10n/class-wp-translation-controller.php index a8a52d95ef5e8..fbe5fa7d0c746 100644 --- a/src/wp-includes/l10n/class-wp-translation-controller.php +++ b/src/wp-includes/l10n/class-wp-translation-controller.php @@ -140,7 +140,7 @@ public function load_file( string $translation_file, string $textdomain = 'defau * * @since 6.5.0 * - * @param WP_Translation_File|string $file Optional. Translation file instance or file name. Defaults to all files. + * @param WP_Translation_File|string $file Translation file instance or file name. * @param string $textdomain Optional. Text domain. Default 'default'. * @param string $locale Optional. Locale. Defaults to all locales. * @return bool True on success, false otherwise. From 8e9a8a8525125138d3b627cb993603897546dd46 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Tue, 23 Jan 2024 10:49:18 +0100 Subject: [PATCH 65/65] Do not change `temp_filename` signature --- tests/phpunit/includes/abstract-testcase.php | 15 ++------------- .../phpunit/tests/l10n/wpTranslationsConvert.php | 7 +++++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/phpunit/includes/abstract-testcase.php b/tests/phpunit/includes/abstract-testcase.php index 954ea5f6dd9de..3600722f4448d 100644 --- a/tests/phpunit/includes/abstract-testcase.php +++ b/tests/phpunit/includes/abstract-testcase.php @@ -1345,10 +1345,9 @@ public function prepareTemplate( Text_Template $template ) { * * @since 3.5.0 * - * @param string $contents Optional. File contents. * @return string|bool Path on success, else false. */ - public function temp_filename( string $contents = null ) { + public function temp_filename() { $tmp_dir = ''; $dirs = array( 'TMP', 'TMPDIR', 'TEMP' ); @@ -1365,17 +1364,7 @@ public function temp_filename( string $contents = null ) { $tmp_dir = realpath( $tmp_dir ); - $file = tempnam( $tmp_dir, 'wpunit' ); - - if ( false === $file ) { - return false; - } - - if ( null !== $contents ) { - file_put_contents( $file, $contents ); - } - - return $file; + return tempnam( $tmp_dir, 'wpunit' ); } /** diff --git a/tests/phpunit/tests/l10n/wpTranslationsConvert.php b/tests/phpunit/tests/l10n/wpTranslationsConvert.php index 6fa1396a6d0a5..0de159b609218 100644 --- a/tests/phpunit/tests/l10n/wpTranslationsConvert.php +++ b/tests/phpunit/tests/l10n/wpTranslationsConvert.php @@ -116,10 +116,12 @@ public function test_unload_specific_locale() { * @phpstan-param 'mo'|'php' $type */ public function test_invalid_files( string $type, string $file_contents, $expected_error = null ) { - $file = $this->temp_filename( $file_contents ); + $file = $this->temp_filename(); $this->assertNotFalse( $file ); + file_put_contents( $file, $file_contents ); + $instance = WP_Translation_File::create( $file, $type ); $this->assertInstanceOf( WP_Translation_File::class, $instance ); @@ -180,8 +182,9 @@ public function test_create_non_existent_file() { * @return void */ public function test_create_invalid_filetype() { - $file = $this->temp_filename( '' ); + $file = $this->temp_filename(); $this->assertNotFalse( $file ); + file_put_contents( $file, '' ); $this->assertFalse( WP_Translation_File::create( $file, 'invalid' ) ); }