From c941fa2e11a1a99c151881137b28b3fa50b8a162 Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 8 Aug 2022 02:05:12 -0700 Subject: [PATCH] (Update) Translations - if a message_template has been translated then get/render the translated version * Updates for APIv4 calls * Set `$language` and `#ranslationMode()` instead of `$preferredLanguage` * Read 'actual_language' instead of `getTranslationLanguage()` * Updates for tracking global locale properties * Use `$loacleObj->moneyFormat` instead of `$GLOBALS['moneyFormatLocale']` and `IGNORE_SEPARATOR_CONFIG` * Use `$tokenContext['locale']` instead of `$GLOBALS['moneyFormatLocale']` and `IGNORE_SEPARATOR_CONFIG` * Split `testRenderTranslatedTemplate()` in two (for different configurations) --- CRM/Core/BAO/MessageTemplate.php | 14 +--- Civi/Api4/Action/WorkflowMessage/Render.php | 14 +--- .../CRM/Core/BAO/MessageTemplateTest.php | 66 ++++++++++++++++++- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/CRM/Core/BAO/MessageTemplate.php b/CRM/Core/BAO/MessageTemplate.php index 10d357e86b2c..cd260a29c9dd 100644 --- a/CRM/Core/BAO/MessageTemplate.php +++ b/CRM/Core/BAO/MessageTemplate.php @@ -344,15 +344,7 @@ protected static function renderTemplateRaw($params) { $language = $params['language'] ?? (!empty($params['contactId']) ? Civi\Api4\Contact::get(FALSE)->addWhere('id', '=', $params['contactId'])->addSelect('preferred_language')->execute()->first()['preferred_language'] : NULL); CRM_Utils_Hook::alterMailParams($params, 'messageTemplate'); [$mailContent, $translatedLanguage] = self::loadTemplate((string) $params['workflow'], $params['isTest'], $params['messageTemplateID'] ?? NULL, $params['groupName'] ?? '', $params['messageTemplate'], $params['subject'] ?? NULL, $language); - global $moneyFormatLocale; - $originalValue = $moneyFormatLocale; - if ($translatedLanguage) { - // If the template has been translated then set the moneyFormatLocale to match the translation. - // Note that in future if we do the same for dates we are likely to want to set it to match - // the preferred_language rather than the translation language - a long discussion is on the - // property in AbstractAction - $moneyFormatLocale = $translatedLanguage; - } + $params['tokenContext']['locale'] = $translatedLanguage ?? $params['language'] ?? NULL; self::synchronizeLegacyParameters($params); $rendered = CRM_Core_TokenSmarty::render(CRM_Utils_Array::subset($mailContent, ['text', 'html', 'subject']), $params['tokenContext'], $params['tplParams']); @@ -361,7 +353,6 @@ protected static function renderTemplateRaw($params) { } $nullSet = ['subject' => NULL, 'text' => NULL, 'html' => NULL]; $mailContent = array_merge($nullSet, $mailContent, $rendered); - $moneyFormatLocale = $originalValue; return [$mailContent, $params]; } @@ -483,6 +474,7 @@ protected static function loadTemplate(string $workflowName, bool $isTest, int $ $apiCall = MessageTemplate::get(FALSE) ->setLanguage($language) + ->setTranslationMode('fuzzy') ->addSelect('msg_subject', 'msg_text', 'msg_html', 'pdf_format_id', 'id') ->addWhere('is_default', '=', 1); @@ -540,7 +532,7 @@ protected static function loadTemplate(string $workflowName, bool $isTest, int $ $mailContent['subject'] = $subjectOverride; } - return [$mailContent, $apiCall->getTranslationLanguage()]; + return [$mailContent, $messageTemplate['actual_language'] ?? NULL]; } /** diff --git a/Civi/Api4/Action/WorkflowMessage/Render.php b/Civi/Api4/Action/WorkflowMessage/Render.php index c645374ba81f..6a5658cb6b8b 100644 --- a/Civi/Api4/Action/WorkflowMessage/Render.php +++ b/Civi/Api4/Action/WorkflowMessage/Render.php @@ -75,24 +75,12 @@ class Render extends \Civi\Api4\Generic\AbstractAction { public function _run(\Civi\Api4\Generic\Result $result) { $this->validateValues(); - global $moneyFormatLocale; - $separatorConfig = \CRM_Utils_Constant::value('IGNORE_SEPARATOR_CONFIG'); - $originalValue = $moneyFormatLocale; - - if ($this->getTranslationLanguage()) { - // Passing in translation language forces money formatting, useful when the - // template is previewed before being saved. - $moneyFormatLocale = $this->getTranslationLanguage(); - putenv('IGNORE_SEPARATOR_CONFIG=' . 1); - } $r = \CRM_Core_BAO_MessageTemplate::renderTemplate([ 'model' => $this->_model, 'messageTemplate' => $this->getMessageTemplate(), 'messageTemplateId' => $this->getMessageTemplateId(), - 'language' => $this->getPreferredLanguage(), + 'language' => $this->getLanguage(), ]); - $moneyFormatLocale = $originalValue; - putenv('IGNORE_SEPARATOR_CONFIG=' . $separatorConfig); $result[] = \CRM_Utils_Array::subset($r, ['subject', 'html', 'text']); } diff --git a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php index 4172795d990c..4d6a1c3b9563 100644 --- a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php +++ b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php @@ -3,6 +3,7 @@ use Civi\Api4\Address; use Civi\Api4\Contact; use Civi\Api4\MessageTemplate; +use Civi\Api4\Translation; use Civi\Token\TokenProcessor; /** @@ -49,9 +50,16 @@ public function testRenderTemplate(): void { /** * Test that translated strings are rendered for templates where they exist. * + * This system has a relatively open localization policy where any translation can be used, + * even if the system doesn't allow it in the web UI. Ex: The sysadmin has configured 'fr_FR' + * strings. The user has requested 'fr_CA', and we'll fallback to 'fr_CA'. + * * @throws \API_Exception|\CRM_Core_Exception + * @group locale */ - public function testRenderTranslatedTemplate(): void { + public function testRenderTranslatedTemplate_AllowPartialLocales(): void { + $cleanup = \CRM_Utils_AutoClean::swapSettings(['partial_locales' => TRUE, 'uiLanguages' => ['en_US']]); + $this->individualCreate(['preferred_language' => 'fr_FR']); $contributionID = $this->contributionCreate(['contact_id' => $this->ids['Contact']['individual_0']]); $messageTemplateID = MessageTemplate::get() @@ -76,6 +84,7 @@ public function testRenderTranslatedTemplate(): void { ->addWhere('workflow_name', '=', 'contribution_online_receipt') ->addSelect('id', 'msg_subject', 'msg_html') ->setLanguage('fr_FR') + ->setTranslationMode('fuzzy') ->execute()->first(); $this->assertEquals('Bonjour', $messageTemplateFrench['msg_subject']); @@ -134,6 +143,61 @@ public function testRenderTranslatedTemplate(): void { $this->assertEquals('100,00 $ US', $rendered['text']); } + /** + * Test that translated strings are rendered for templates where they exist. + * + * This system has a relatively closed localization policy where translations will only be + * used if the locale is fully supported by the app. Ex: Even though there are some strings + * for 'fr_FR', the language in "Admin=>Localizaton", so we don't use it. + * + * @throws \API_Exception|\CRM_Core_Exception + * @group locale + */ + public function testRenderTranslatedTemplate_OnlyFullLocales(): void { + $cleanup = \CRM_Utils_AutoClean::swapSettings(['partial_locales' => FALSE, 'uiLanguages' => ['en_US']]); + + $this->individualCreate(['preferred_language' => 'fr_FR']); + $contributionID = $this->contributionCreate(['contact_id' => $this->ids['Contact']['individual_0']]); + $messageTemplateID = MessageTemplate::get() + ->addWhere('is_default', '=', 1) + ->addWhere('workflow_name', '=', 'contribution_online_receipt') + ->addSelect('id') + ->execute()->first()['id']; + + Translation::save()->setRecords([ + ['entity_field' => 'msg_subject', 'string' => 'Bonjour'], + ['entity_field' => 'msg_html', 'string' => 'Voila!'], + ['entity_field' => 'msg_text', 'string' => '{contribution.total_amount}'], + ])->setDefaults([ + 'entity_table' => 'civicrm_msg_template', + 'entity_id' => $messageTemplateID, + 'status_id:name' => 'active', + 'language' => 'fr_FR', + ])->execute(); + + $messageTemplateFrench = MessageTemplate::get() + ->addWhere('is_default', '=', 1) + ->addWhere('workflow_name', '=', 'contribution_online_receipt') + ->addSelect('id', 'msg_subject', 'msg_html') + ->setLanguage('fr_FR') + ->setTranslationMode('fuzzy') + ->execute()->first(); + + $this->assertStringNotContainsString('Bonjour', $messageTemplateFrench['msg_subject']); + $this->assertStringNotContainsString('Voila!', $messageTemplateFrench['msg_html']); + + $rendered = CRM_Core_BAO_MessageTemplate::renderTemplate([ + 'workflow' => 'contribution_online_receipt', + 'tokenContext' => [ + 'contactId' => $this->ids['Contact']['individual_0'], + 'contributionId' => $contributionID, + ], + ]); + $this->assertStringNotContainsString('Bonjour', $rendered['subject']); + $this->assertStringNotContainsString('Voila!', $rendered['html']); + $this->assertStringNotContainsString('100,00', $rendered['text']); + } + /** * @throws \API_Exception * @throws \CRM_Core_Exception