From 2ec434cc4a9afacfd419dc0febf9ec35d678e405 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Tue, 16 May 2023 16:07:14 +1200 Subject: [PATCH] Use translation for default language, if exists There is an assumption in the code that for the default language a translation is not used. However, the features around draft versions are all set up around translations, so it becomes necessary to create translations even for the default language. This change ensures they are used if they exist. Although we have a lot of complexity in the translation framework on the rendering side I think assuming that where stuff is configured people configured it to be used is a good underlying principle. --- CRM/Core/BAO/Translation.php | 30 ++++++++++++++++++- .../CRM/Core/BAO/MessageTemplateTest.php | 12 +++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/CRM/Core/BAO/Translation.php b/CRM/Core/BAO/Translation.php index c81cc46eee..1a23382e6a 100644 --- a/CRM/Core/BAO/Translation.php +++ b/CRM/Core/BAO/Translation.php @@ -178,7 +178,7 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements HookI } $communicationLanguage = \Civi\Core\Locale::detect()->nominal; - if ($communicationLanguage === Civi::settings()->get('lcMessages')) { + if (!$communicationLanguage || !self::isTranslate($communicationLanguage)) { return; } @@ -207,6 +207,34 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements HookI } } + /** + * Should the translation process be followed. + * + * It can be short-circuited if there we are in the site default language and + * it is not translated. + * + * @param string $communicationLanguage + * + * @return bool + */ + protected static function isTranslate(string $communicationLanguage): bool { + if ($communicationLanguage !== Civi::settings()->get('lcMessages')) { + return TRUE; + } + if (!isset(\Civi::$statics[__CLASS__]['translate_main'][$communicationLanguage])) { + // The code had an assumption that you would not translate the primary language. + // However, the UI is such that the features (approval flow) so it makes sense + // to translation the default site language as well. If we can see sites are + // doing this then let's treat the main locale like any other locale + \Civi::$statics[__CLASS__]['translate_main'] = (bool) CRM_Core_DAO::singleValueQuery( + 'SELECT COUNT(*) FROM civicrm_translation WHERE language = %1 LIMIT 1', [ + 1 => [$communicationLanguage, 'String'], + ] + ); + } + return \Civi::$statics[__CLASS__]['translate_main']; + } + /** * @param \Civi\Api4\Generic\AbstractAction $apiRequest * @return array translated fields. diff --git a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php index e9efcb9aae..c69c1b663d 100644 --- a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php +++ b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php @@ -70,6 +70,10 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { $allTemplates['fr_CA'] = ['subject' => 'Bonjour Canada', 'html' => 'Voila! Canada', 'text' => '{contribution.total_amount}']; $allTemplates['es_PR'] = ['subject' => 'Buenos dias', 'html' => 'Listo', 'text' => '{contribution.total_amount}']; $allTemplates['th_TH'] = ['subject' => 'สวัสดี', 'html' => 'ดังนั้น', 'text' => '{contribution.total_amount}']; + // en_US is the default language. We add it to ensure the translation is loaded rather than falling back to + // the message template. While it might seem you don't need a translation for the site default language + // all the draft & approval logic is attached to the translation, not the template. + $allTemplates['en_US'] = ['subject' => 'site default', 'html' => 'site default language', 'text' => '{contribution.total_amount}']; $rendered = []; // $rendered['*'] = ['subject' => 'Hello', 'html' => 'Look there!', 'text' => '$ 100.00']; @@ -78,6 +82,7 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { $rendered['fr_CA'] = ['subject' => 'Bonjour Canada', 'html' => 'Voila! Canada', 'text' => '100,00 $ US']; $rendered['es_PR'] = ['subject' => 'Buenos dias', 'html' => 'Listo', 'text' => '100.00 $US']; $rendered['th_TH'] = ['subject' => 'สวัสดี', 'html' => 'ดังนั้น', 'text' => 'US$100.00']; + $rendered['en_US'] = ['subject' => 'site default', 'html' => 'site default language', 'text' => '$100.00']; $result = [/* settings, templates, preferredLanguage, expectMessage */]; @@ -92,11 +97,16 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { $result['fr_CA falls back to fr_FR (ltd-tpls; no-partials)'] = [$noPartials, $this->getLocaleTemplates($allTemplates, ['*', 'fr_FR']), 'fr_CA', $rendered['fr_FR']]; $result['th_TH matches th_TH (all-tpls; yes-partials)'] = [$yesPartials, $allTemplates, 'th_TH', $rendered['th_TH']]; - $result['th_TH falls back to system default (all-tpls; no-partials)'] = [$noPartials, $allTemplates, 'th_TH', $rendered['*']]; + $result['th_TH falls back to site default translation (all-tpls; no-partials)'] = [$noPartials, $allTemplates, 'th_TH', $rendered['en_US']]; + // Absent a translation for the site default language it should fall back to the message template. + $result['th_TH falls back to system default (all-tpls; no-partials)'] = [$noPartials, array_diff_key($allTemplates, ['en_US' => TRUE]), 'th_TH', $rendered['*']]; // ^^ The essence of the `partial_locales` setting -- whether partially-supported locales (th_TH) use mixed-mode or fallback to completely diff locale. $result['th_TH falls back to system default (ltd-tpls; yes-partials)'] = [$yesPartials, $this->getLocaleTemplates($allTemplates, ['*']), 'th_TH', $rendered['*']]; $result['th_TH falls back to system default (ltd-tpls; no-partials)'] = [$noPartials, $this->getLocaleTemplates($allTemplates, ['*']), 'th_TH', $rendered['*']]; + // Check that the en_US template is loaded, if exists. + $result['en_US matches en_US (all-tpls; yes-partials)'] = [$yesPartials, $allTemplates, 'en_US', $rendered['en_US']]; + return $result; } -- 2.25.1