From 719c57ac144bfd6de436bbb6a566bb444338dfe7 Mon Sep 17 00:00:00 2001 From: Eileen McNaughton Date: Tue, 11 Jul 2023 12:16:28 +1200 Subject: [PATCH] Fix failure to fall back to site default language, if configured This addresses scenario 6 here https://github.com/civicrm/civicrm-core/pull/26232#issuecomment-1624455432 --- CRM/Core/BAO/Translation.php | 41 +++++++++++++++++-- .../Traits/LocalizationTrait.php | 4 +- .../CRM/Core/BAO/MessageTemplateTest.php | 34 ++++++++++++--- 3 files changed, 69 insertions(+), 10 deletions(-) diff --git a/CRM/Core/BAO/Translation.php b/CRM/Core/BAO/Translation.php index 1a23382e6a..f6d969a8b7 100644 --- a/CRM/Core/BAO/Translation.php +++ b/CRM/Core/BAO/Translation.php @@ -278,13 +278,48 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements HookI foreach ($languages as $language => $languageFields) { if ($language !== $bizLocale->nominal) { // Merge in any missing entities. Ie we might have a translation for one template in es_MX but - // need to fall back to es_ES for another. - $fields = array_merge($languageFields, $fields); + // need to fall back to es_ES for another. If there is a translation for the site default + // language we should fall back to that rather than the messageTemplate + // see https://github.com/civicrm/civicrm-core/pull/26232 + $fields = array_merge(self::getSiteDefaultLanguageTranslations($apiRequest['entity'])['fields'] ?? [], $languageFields, $fields); } } return ['fields' => $fields, 'language' => $bizLocale->nominal]; } - return []; + + // Finally fall back to the translation of the site language, if exists. + // ie if the site language is en_US and there is a translation for that, then use it. + // see https://github.com/civicrm/civicrm-core/pull/26232 + return self::getSiteDefaultLanguageTranslations($apiRequest['entity']); + } + + /** + * Get any translations configured for the site-default language. + * + * @param string $entity + * + * @throws \CRM_Core_Exception + */ + protected static function getSiteDefaultLanguageTranslations(string $entity): array { + if (!isset(\Civi::$statics[__CLASS__]) || !array_key_exists('site_language_translation', \Civi::$statics[__CLASS__])) { + \Civi::$statics[__CLASS__]['site_language_translation'] = []; + $translations = Translation::get(FALSE) + ->addWhere('entity_table', '=', CRM_Core_DAO_AllCoreTables::getTableForEntityName($entity)) + ->setCheckPermissions(FALSE) + ->setSelect(['entity_field', 'entity_id', 'string', 'language']) + ->addWhere('language', '=', \Civi::settings()->get('lcMessages')) + ->execute(); + if ($translations !== NULL) { + \Civi::$statics[__CLASS__]['site_language_translation'] = [ + 'fields' => [], + 'language' => \Civi::settings()->get('lcMessages'), + ]; + foreach ($translations as $translatedField) { + \Civi::$statics[__CLASS__]['site_language_translation']['fields'][$translatedField['entity_id'] . $translatedField['entity_field']] = $translatedField; + } + } + } + return \Civi::$statics[__CLASS__]['site_language_translation']; } } diff --git a/Civi/WorkflowMessage/Traits/LocalizationTrait.php b/Civi/WorkflowMessage/Traits/LocalizationTrait.php index 619256ff7d..d5974630b9 100644 --- a/Civi/WorkflowMessage/Traits/LocalizationTrait.php +++ b/Civi/WorkflowMessage/Traits/LocalizationTrait.php @@ -31,7 +31,9 @@ trait LocalizationTrait { * 1. Your organization serves many similar locales (eg es_MX+es_CR+es_HN or en_GB+en_US+en_NZ). * 2. You want to write one message (es_MX) for several locales (es_MX+es_CR+es_HN) * 3. You want to include some conditional content that adapts the recipient's requested locale - * (es_CR) -- _even though_ the template was stored as es_MX. + * (es_CR) -- _even though_ the template was stored as es_MX. For example your front end + * website has more nuanced translations than your workflow messages and you wish + * to redirect the user to a page on your website. * * @var string|null * @scope tokenContext diff --git a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php index c69c1b663d..8b51e6f0eb 100644 --- a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php +++ b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php @@ -106,6 +106,9 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { // 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']]; + // We have no translation for Danish but there IS an en_US one & en_US is the site-default language. We should + // fall back to it rather than the message template. Ref https://github.com/civicrm/civicrm-core/pull/26232 + $result['da_DK matches en_US (all-tpls; yes-partials)'] = [$yesPartials, $allTemplates, 'da_DK', $rendered['en_US']]; return $result; } @@ -218,16 +221,22 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { ->addWhere('is_default', '=', 1) ->addWhere('workflow_name', 'LIKE', 'contribution_%') ->addSelect('id', 'workflow_name') - ->setLimit(2) + ->setLimit(3) ->execute()->indexBy('workflow_name'); - $firstTemplate = array_key_first($messageTemplates); - $secondTemplate = array_key_last($messageTemplates); + [$firstTemplate, $secondTemplate, $thirdTemplate] = array_keys($messageTemplates); foreach ($messageTemplates as $workflowName => $messageTemplate) { + // The translation for the site default language is only used when neither of the other 2 exist. $records = [ - ['entity_field' => 'msg_subject', 'string' => 'subject - Spanish', 'language' => 'es_ES'], - ['entity_field' => 'msg_html', 'string' => 'html -Spanish', 'language' => 'es_ES'], + ['entity_field' => 'msg_subject', 'string' => 'subject - site default translation', 'language' => 'en_US'], + ['entity_field' => 'msg_html', 'string' => 'html - site default translation', 'language' => 'en_US'], ]; + if ($workflowName !== $thirdTemplate) { + // All except one template gets a Spanish translation. This should be used where there is no Mexican translation. + $records[] = ['entity_field' => 'msg_subject', 'string' => 'subject - Spanish', 'language' => 'es_ES']; + $records[] = ['entity_field' => 'msg_html', 'string' => 'html -Spanish', 'language' => 'es_ES']; + } if ($secondTemplate === $workflowName) { + // One gets a Mexican translation. This should be used. $records[] = ['entity_field' => 'msg_subject', 'string' => 'subject - Mexican', 'language' => 'es_MX']; $records[] = ['entity_field' => 'msg_html', 'string' => 'html - Mexican', 'language' => 'es_MX']; } @@ -246,8 +255,9 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { ->setTranslationMode('fuzzy') ->execute()->first(); $this->assertEquals('subject - Mexican', $translatedTemplate['msg_subject']); + $this->assertEquals('html - Mexican', $translatedTemplate['msg_html']); - // This SHOULD fall back to Spanish... + // This should fall back to Spanish... $translatedTemplate = MessageTemplate::get() ->addWhere('is_default', '=', 1) ->addWhere('workflow_name', '=', $firstTemplate) @@ -256,6 +266,18 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase { ->setTranslationMode('fuzzy') ->execute()->first(); $this->assertEquals('subject - Spanish', $translatedTemplate['msg_subject']); + $this->assertEquals('html -Spanish', $translatedTemplate['msg_html']); + + // This should fall back to en_US translation... + $translatedTemplate = MessageTemplate::get() + ->addWhere('is_default', '=', 1) + ->addWhere('workflow_name', '=', $thirdTemplate) + ->addSelect('id', 'msg_subject', 'msg_html', 'msg_text') + ->setLanguage('es_MX') + ->setTranslationMode('fuzzy') + ->execute()->first(); + $this->assertEquals('subject - site default translation', $translatedTemplate['msg_subject']); + $this->assertEquals('html - site default translation', $translatedTemplate['msg_html']); } /** -- 2.25.1