From 70d514f922aeaf3c83a665d8d1176a1ba35a9248 Mon Sep 17 00:00:00 2001 From: Pascal Birchler Date: Mon, 12 Aug 2024 09:51:10 +0200 Subject: [PATCH] Leverage `lang_dir_for_domain` filter (#1067) --- .github/workflows/lint-and-test.yml | 14 +- ...-preferred-languages-noop-translations.php | 18 -- inc/functions.php | 219 +++++------------- phpstan.neon.dist | 1 - preferred-languages.php | 7 +- readme.txt | 18 +- tests/phpunit/tests/plugin.php | 109 ++------- 7 files changed, 87 insertions(+), 299 deletions(-) delete mode 100644 inc/class-preferred-languages-noop-translations.php diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 6f223544..e3e525e9 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -146,25 +146,17 @@ jobs: continue-on-error: ${{ matrix.experimental }} strategy: matrix: - php: ['8.1', '8.0', '7.2', '7.4'] + php: ['8.2', '8.1', '8.0', '7.2', '7.4'] wp: ['latest'] coverage: [false] experimental: [false] include: - - php: '8.2' + - php: '8.3' wp: 'latest' coverage: true experimental: false - - php: '8.2' - wp: 'trunk' - experimental: true - - - php: '7.4' - wp: '6.2' - experimental: false - - - php: '8.3' + - php: '8.4' wp: 'trunk' experimental: true diff --git a/inc/class-preferred-languages-noop-translations.php b/inc/class-preferred-languages-noop-translations.php deleted file mode 100644 index 6cc9b91f..00000000 --- a/inc/class-preferred-languages-noop-translations.php +++ /dev/null @@ -1,18 +0,0 @@ -get( $domain, $locale ); - - if ( ! $path ) { - continue; - } - - $mofile = "{$path}/{$domain}-{$locale}.mo"; - - if ( load_textdomain( $domain, $mofile ) ) { - $translations = get_translations_for_domain( $domain ); + $preferred_locales = array_slice( + $preferred_locales, + $offset + ); + } - if ( null !== $plural && null !== $number ) { - return $translations->translate_plural( $single, $plural, $number, $context ); - } + foreach ( $preferred_locales as $preferred_locale ) { + remove_filter( 'lang_dir_for_domain', 'preferred_languages_filter_lang_dir_for_domain' ); + $new_path = $wp_textdomain_registry->get( $domain, $preferred_locale ); + add_filter( 'lang_dir_for_domain', 'preferred_languages_filter_lang_dir_for_domain', 10, 3 ); - return $translations->translate( $single, $context ); - } + if ( $new_path ) { + return $new_path; } - - $l10n[ $domain ] = &$noop_translations; } - return $translation; -} - -/** - * Filters gettext calls to work around limitations in just-in-time loading of translations. - * - * @since 1.1.0 - * - * @param string $translation Translated text. - * @param string $text Text to translate. - * @param string $domain Text domain. Unique identifier for retrieving translated strings. - * - * @return string Translated text. - */ -function preferred_languages_filter_gettext( $translation, $text, $domain ) { - return preferred_languages_load_just_in_time( $translation, $text, null, null, null, $domain ); -} - -/** - * Filters gettext calls to work around limitations in just-in-time loading of translations. - * - * @since 2.1.2 - * - * @param string $translation Translated text. - * @param string $text Text to translate. - * @param string $context Context information for the translators. - * @param string $domain Text domain. Unique identifier for retrieving translated strings. - * - * @return string Translated text. - */ -function preferred_languages_filter_gettext_with_context( $translation, $text, $context, $domain ) { - return preferred_languages_load_just_in_time( $translation, $text, null, null, $context, $domain ); -} - -/** - * Filters gettext calls to work around limitations in just-in-time loading of translations. - * - * @since 2.1.2 - * - * @param string $translation Translated text. - * @param string $single The text to be used if the number is singular. - * @param string $plural The text to be used if the number is plural. - * @param int $number The number to compare against to use either the singular or plural form. - * @param string $domain Text domain. Unique identifier for retrieving translated strings. - * - * @return string Translated text. - */ -function preferred_languages_filter_ngettext( $translation, $single, $plural, $number, $domain ) { - return preferred_languages_load_just_in_time( $translation, $single, $plural, $number, null, $domain ); -} -/** - * Filters gettext calls to work around limitations in just-in-time loading of translations. - * - * @since 2.1.2 - * - * @param string $translation Translated text. - * @param string $single The text to be used if the number is singular. - * @param string $plural The text to be used if the number is plural. - * @param int $number The number to compare against to use either the singular or plural form. - * @param string $context Context information for the translators. - * @param string $domain Text domain. Unique identifier for retrieving translated strings. - * - * @return string Translated text. - */ -function preferred_languages_filter_ngettext_with_context( $translation, $single, $plural, $number, $context, $domain ) { - return preferred_languages_load_just_in_time( $translation, $single, $plural, $number, $context, $domain ); + return $path; } /** diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 9779f682..835b743a 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -6,7 +6,6 @@ parameters: - inc/ - tests/phpunit ignoreErrors: - - message: '/^Call to private\/internal function preferred_languages_load_just_in_time\(\)\.$/' - message: '/has no return type specified\.$/' - message: '/^Call to method PHPUnit\\Framework\\Assert::assertFalse\(\) with false will always evaluate to true\.$/' - message: '/^Cannot access offset 1 on mixed\.$/' diff --git a/preferred-languages.php b/preferred-languages.php index 9b9ddb24..512f3cc6 100644 --- a/preferred-languages.php +++ b/preferred-languages.php @@ -3,14 +3,14 @@ * Plugin Name: Preferred Languages * Plugin URI: https://github.com/swissspidy/preferred-languages/ * Description: Enables you to choose languages for displaying WordPress in, in order of preference. - * Version: 2.3.0 + * Version: 2.4.0 * Author: Pascal Birchler * Author URI: https://pascalbirchler.com * License: GPL-2.0+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt * Text Domain: preferred-languages - * Requires at least: 6.2.1 - * Requires PHP: 7.2 + * Requires at least: 6.6 + * Requires PHP: 7.2.24 * * Copyright (c) 2017 Pascal Birchler (email: swissspidy@chat.wordpress.org) * @@ -34,7 +34,6 @@ /** * Plugin functions. */ -require_once __DIR__ . '/inc/class-preferred-languages-noop-translations.php'; require_once __DIR__ . '/inc/functions.php'; // We need to load before plugins_loaded, see https://core.trac.wordpress.org/ticket/58546. diff --git a/readme.txt b/readme.txt index aa3b0a97..116052e5 100644 --- a/readme.txt +++ b/readme.txt @@ -2,7 +2,7 @@ Contributors: swissspidy Tags: internationalization, i18n, localization, language, translation Tested up to: 6.6 -Stable tag: 2.3.0 +Stable tag: 2.4.0 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -32,21 +32,19 @@ Please help us test this plugin and let us know if something is not working as y **Merging Translations** -By default, only the first available translation for a given locale and domain will be loaded. +Previously, only the first available translation for a given locale and domain will be loaded. However, when translations are incomplete, some strings might still be displayed in English. That's a poor user experience as well. -To prevent this, the `preferred_languages_merge_translations` filter can be used to opt into merging incomplete translations. +To prevent this, Preferred Languages now automatically merges all incomplete translations in the list. + + the `preferred_languages_merge_translations` filter can be used to opt out of this behavior. It provides three parameters: -1. `$merge` - Whether translations should be merged. +1. `$merge` - Whether translations should be merged. Defaults to `true`. 2. `$domain` - The text domain 3. `$current_locale` - The current locale. -**Note:** On WordPress 6.5 and newer, this functionality is enabled by default. -On older versions of WordPress, this is a potentially slow/expensive process, -so it's recommended to use it sparingly and only for specific domains. - = Get Involved = Active development is taking place on [GitHub](https://github.com/swissspidy/preferred-languages). @@ -64,6 +62,6 @@ For the plugin's changelog, please head over to [the GitHub repository](https:// == Upgrade Notice == -= 2.3.0 = += 2.4.0 = -This release adds security hardening for an issue in core and bumps minimum version requirements. +This release improves WordPress compatibility and bumps minimum version requirements. diff --git a/tests/phpunit/tests/plugin.php b/tests/phpunit/tests/plugin.php index d25388f1..a6b4d2d4 100644 --- a/tests/phpunit/tests/plugin.php +++ b/tests/phpunit/tests/plugin.php @@ -1439,57 +1439,9 @@ public function test_update_network_settings_post_data_sanitized() { } /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_default() { - $actual = preferred_languages_filter_gettext( 'Hello World', 'Hello World', 'default' ); - $this->assertSame( 'Hello World', $actual ); - } - - /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time - */ - public function test_filter_gettext_plugin_no_preferred_languages() { - $actual = preferred_languages_filter_gettext( 'This is a dummy plugin', 'This is a dummy plugin', 'internationalized-plugin' ); - $this->assertSame( 'This is a dummy plugin', $actual ); - } - - /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time - */ - public function test_filter_gettext_plugin_already_filtered() { - update_option( 'preferred_languages', 'de_DE,fr_FR' ); - - $filter = static function () { - return 'es_ES'; - }; - - add_filter( 'determine_locale', $filter ); - - $actual = preferred_languages_filter_gettext( 'This is a dummy plugin', 'This is a dummy plugin', 'internationalized-plugin' ); - - $this->assertSame( 'This is a dummy plugin', $actual ); - } - - /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time - */ - public function test_filter_gettext_plugin_already_translated() { - update_option( 'preferred_languages', 'de_DE,fr_FR' ); - - $actual = preferred_languages_filter_gettext( 'Das ist ein Dummy Plugin', 'This is a dummy plugin', 'internationalized-plugin' ); - - $this->assertSame( 'Das ist ein Dummy Plugin', $actual ); - } - - /** - * @covers ::preferred_languages_filter_gettext - */ - public function test_filter_gettext_plugin_filter() { + public function test_filter_lang_dir_plugin_filter() { update_option( 'preferred_languages', 'de_DE,fr_FR' ); $this->assertSame( 'Das ist ein Dummy Plugin', __( 'This is a dummy plugin', 'internationalized-plugin' ) ); @@ -1498,20 +1450,18 @@ public function test_filter_gettext_plugin_filter() { /** * @link https://github.com/swissspidy/preferred-languages/issues/881 * - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_plugin_filter_registry_lookup() { + public function test_filter_lang_dir_plugin_filter_registry_lookup() { update_option( 'preferred_languages', 'it_IT,de_DE,es_ES' ); $this->assertSame( 'Das ist ein Dummy Plugin', __( 'This is a dummy plugin', 'internationalized-plugin' ) ); } /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_plugin_custom_path() { + public function test_filter_lang_dir_plugin_custom_path() { update_option( 'preferred_languages', 'fr_FR,de_DE' ); require_once WP_PLUGIN_DIR . '/custom-internationalized-plugin/custom-internationalized-plugin.php'; @@ -1522,10 +1472,9 @@ public function test_filter_gettext_plugin_custom_path() { } /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_plugin_custom_path_locale_switching() { + public function test_filter_lang_dir_plugin_custom_path_locale_switching() { add_filter( 'preferred_languages_merge_translations', '__return_false' ); update_option( 'preferred_languages', 'fr_FR,de_DE,es_ES' ); @@ -1542,10 +1491,9 @@ public function test_filter_gettext_plugin_custom_path_locale_switching() { } /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_plugin_custom_path_locale_switching_merge_translations() { + public function test_filter_lang_dir_plugin_custom_path_locale_switching_merge_translations() { add_filter( 'preferred_languages_merge_translations', '__return_true' ); update_option( 'preferred_languages', 'fr_FR,de_DE,es_ES' ); @@ -1562,10 +1510,9 @@ public function test_filter_gettext_plugin_custom_path_locale_switching_merge_tr } /** - * @covers ::preferred_languages_filter_gettext_with_context - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_context_plugin_custom_path() { + public function test_filter_lang_dir_context_plugin_custom_path() { update_option( 'preferred_languages', 'fr_FR,de_DE' ); require_once WP_PLUGIN_DIR . '/custom-internationalized-plugin/custom-internationalized-plugin.php'; @@ -1576,37 +1523,9 @@ public function test_filter_gettext_context_plugin_custom_path() { } /** - * @covers ::preferred_languages_filter_ngettext - * @covers ::preferred_languages_load_just_in_time - */ - public function test_filter_gettext_plural_plugin_custom_path() { - update_option( 'preferred_languages', 'fr_FR,de_DE' ); - - require_once WP_PLUGIN_DIR . '/custom-internationalized-plugin/custom-internationalized-plugin.php'; - - $this->assertSame( '%s Dummy Plugin', custom_i18n_plugin_test_plural( 1 ) ); - $this->assertSame( '%s Dummy Plugins', custom_i18n_plugin_test_plural( 2 ) ); - } - - /** - * @covers ::preferred_languages_filter_ngettext_with_context - * @covers ::preferred_languages_load_just_in_time - */ - public function test_filter_gettext_plural_context_plugin_custom_path() { - update_option( 'preferred_languages', 'fr_FR,de_DE' ); - - require_once WP_PLUGIN_DIR . '/custom-internationalized-plugin/custom-internationalized-plugin.php'; - - $this->assertSame( '%s Dummy Plugin mit Kontext', custom_i18n_plugin_test_plural_context( 1 ) ); - $this->assertSame( '%s Dummy Plugins mit Kontext', custom_i18n_plugin_test_plural_context( 2 ) ); - } - - - /** - * @covers ::preferred_languages_filter_gettext - * @covers ::preferred_languages_load_just_in_time + * @covers ::preferred_languages_filter_lang_dir_for_domain */ - public function test_filter_gettext_plugin_cache_list_retrieval() { + public function test_filter_lang_dir_plugin_cache_list_retrieval() { update_option( 'preferred_languages', 'fr_FR,de_DE,es_ES' ); $filter = new MockAction();