From c9f32a075b7f1a3567d537f91061f084d893c027 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Wed, 24 Aug 2022 21:55:20 -0700 Subject: [PATCH] (dev/translation#78) Locale::negotiate() - Implement true support for partial locales --- Civi/Core/Locale.php | 42 ++++++++++++++------- tests/phpunit/CRM/Core/I18n/LocaleTest.php | 43 ++++++++++++++++++++++ 2 files changed, 71 insertions(+), 14 deletions(-) diff --git a/Civi/Core/Locale.php b/Civi/Core/Locale.php index cd5d711821..9a02cd130d 100644 --- a/Civi/Core/Locale.php +++ b/Civi/Core/Locale.php @@ -182,21 +182,26 @@ class Locale { \CRM_Core_OptionValue::getValues(['name' => 'languages'], $optionValues, 'weight', TRUE); $validNominalLocales = array_column($optionValues, 'label', 'name'); $validTsLocales = \CRM_Core_I18n::languages(FALSE); /* Active OV _and_ available MO */ - $validFormatLocales = $validNominalLocales; /* FIXME Where do we get this? */ } else { $validNominalLocales = $validTsLocales = $validFormatLocales = \CRM_Core_I18n::languages(FALSE); // Or stricter? array_fill_keys(\CRM_Core_I18n::uiLanguages(TRUE), TRUE); } - $validDbLocales = \CRM_Core_I18n::isMultiLingual() ? \Civi::settings()->get('languageLimit') : NULL; - // TODO This always falls back to the system locale. Maybe use getLocalePrecedence() instead... $locale = new static(); - $locale->nominal = isset($validNominalLocales[$preferred]) ? $preferred : $systemDefault; - $locale->ts = isset($validTsLocales[$preferred]) ? $preferred : $systemDefault; - $locale->moneyFormat = isset($validFormatLocales[$locale->nominal]) ? $locale->nominal : $systemDefault; - $locale->db = \CRM_Core_I18n::isMultiLingual() && isset($validDbLocales[$locale->nominal]) ? $locale->nominal : NULL; + $locale->nominal = static::pickFirstLocale(array_keys($validNominalLocales), static::getAllFallbacks($preferred)) ?: $systemDefault; + $fallbacks = static::getAllFallbacks($locale->nominal); + + $locale->ts = static::pickFirstLocale(array_keys($validTsLocales), $fallbacks) ?: $systemDefault; + $locale->moneyFormat = $locale->nominal; + if (!\CRM_Core_I18n::isMultiLingual()) { + $locale->db = NULL; + } + else { + $validDbLocales = \Civi::settings()->get('languageLimit'); + $locale->db = static::pickFirstLocale(array_keys($validDbLocales), $fallbacks) ?: $systemDefault; + } return $locale; } @@ -243,13 +248,7 @@ class Locale { * If no good locales could be chosen, then NULL. */ public function renegotiate(array $availableLocales): ?Locale { - $fallbacks = array_merge( - // We'd like to stay in the active locale (or something closely related) - ($this->nominal ? static::getLocalePrecedence($this->nominal) : []), - // If we can't, then try the system locale (or something closely related) - static::getLocalePrecedence(\Civi::settings()->get('lcMessages')) - ); - $picked = static::pickFirstLocale($availableLocales, $fallbacks); + $picked = static::pickFirstLocale($availableLocales, static::getAllFallbacks($this->nominal)); return $picked ? static::negotiate($picked) : NULL; } @@ -273,6 +272,21 @@ class Locale { return NULL; } + /** + * @param string|null $preferred + * ex: 'es_PR' + * @return array + * Ex: ['es_PR', 'es_419', 'es_MX', 'es_ES', 'en_US', 'en_GB] + */ + private static function getAllFallbacks(?string $preferred): array { + return array_merge( + // We'd like to stay in the active locale (or something closely related) + ($preferred ? static::getLocalePrecedence($preferred) : []), + // If we can't, then try the system locale (or something closely related) + static::getLocalePrecedence(\Civi::settings()->get('lcMessages')) + ); + } + /** * (Internal helper) Given a $preferred locale, determine a prioritized list of alternate locales. * diff --git a/tests/phpunit/CRM/Core/I18n/LocaleTest.php b/tests/phpunit/CRM/Core/I18n/LocaleTest.php index 180322d86a..e6234aaaff 100644 --- a/tests/phpunit/CRM/Core/I18n/LocaleTest.php +++ b/tests/phpunit/CRM/Core/I18n/LocaleTest.php @@ -86,6 +86,49 @@ class CRM_Core_I18n_LocaleTest extends CiviUnitTestCase { } } + public function getPartialLocaleExamples(): array { + $results = [/* array $settings, string $preferredLocale, array $expectLocale, string $expectYes */]; + $results['es_PR mixed mode'] = [['partial_locales' => TRUE], 'es_PR', ['nominal' => 'es_PR', 'ts' => 'es_MX', 'moneyFormat' => 'es_PR'], 'Sí']; + $results['th_TH mixed mode'] = [['partial_locales' => TRUE], 'th_TH', ['nominal' => 'th_TH', 'ts' => 'en_US', 'moneyFormat' => 'th_TH'], 'Yes']; + $results['es_PR switched to es_MX'] = [['partial_locales' => FALSE], 'es_PR', ['nominal' => 'es_MX', 'ts' => 'es_MX', 'moneyFormat' => 'es_MX'], 'Sí']; + $results['th_TH switched to en_US'] = [['partial_locales' => FALSE], 'th_TH', ['nominal' => 'en_US', 'ts' => 'en_US', 'moneyFormat' => 'en_US'], 'Yes']; + return $results; + } + + /** + * @param array $settings + * List of settings to apply during the test + * @param string $preferred + * The locale that we should try to use. + * Ex : 'es_PR' + * @param array $expectLocale + * The locale options that we expect to use. + * Ex: ['nominal' => 'es_PR', 'ts' => 'es_MX', 'moneyFormat' => 'es_PR'] + * @param string $expectYes + * The translation for "Yes" in our expected language. + * @dataProvider getPartialLocaleExamples + */ + public function testPartialLocale(array $settings, string $preferred, array $expectLocale, string $expectYes) { + if (count(\CRM_Core_I18n::languages(FALSE)) <= 1) { + $this->markTestIncomplete('Full testing of localization requires l10n data.'); + } + $cleanup = CRM_Utils_AutoClean::swapSettings($settings); + \Civi\Api4\OptionValue::update() + ->addWhere('option_group_id:name', '=', 'languages') + ->addWhere('name', '=', 'es_PR') + ->setValues(['is_active' => 1]) + ->execute(); + + CRM_Core_I18n::singleton()->setLocale($preferred); + global $civicrmLocale; + $this->assertEquals($expectYes, ts('Yes')); + $this->assertEquals($expectLocale['ts'], $civicrmLocale->ts); + $this->assertEquals($expectLocale['moneyFormat'], $civicrmLocale->moneyFormat); + $this->assertEquals($expectLocale['nominal'], $civicrmLocale->nominal); + // Should getLocale() return nominal or ts? + // $this->assertEquals($expectLocale['nominal'], CRM_Core_I18n::getLocale()); + } + /** * Quirk in strtolower does not handle "I" as expected, compared to mb_strtolower. */ -- 2.25.1