diff --git a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php index 4d6a1c3b9563..b8d657498de5 100644 --- a/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php +++ b/tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php @@ -47,116 +47,68 @@ public function testRenderTemplate(): void { $this->assertStringContainsString('
Hello testRenderTemplate Abba Baab!
', $rendered['html']); } - /** - * 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_AllowPartialLocales(): void { - $cleanup = \CRM_Utils_AutoClean::swapSettings(['partial_locales' => TRUE, 'uiLanguages' => ['en_US']]); + public function getLocaleConfigurations(): array { + $yesPartials = ['partial_locales' => TRUE, 'uiLanguages' => ['en_US']]; + $noPartials = ['partial_locales' => FALSE, 'uiLanguages' => ['en_US'], 'format_locale' => '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']; + $allTemplates = []; + $allTemplates['*'] = ['subject' => 'Hello', 'html' => 'Looky there!', 'text' => '{contribution.total_amount}']; + $allTemplates['fr_FR'] = ['subject' => 'Bonjour', 'html' => 'Voila!', 'text' => '{contribution.total_amount}']; + $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}']; - 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(); + $onlyTemplates = function(array $locales) use ($allTemplates) { + return CRM_Utils_Array::subset($allTemplates, $locales); + }; - $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(); + $rendered = []; + // $rendered['*'] = ['subject' => 'Hello', 'html' => 'Looky there!', 'text' => '$ 100.00']; + $rendered['*'] = ['subject' => 'Hello', 'html' => 'Looky there!', 'text' => '$100.00']; + $rendered['fr_FR'] = ['subject' => 'Bonjour', 'html' => 'Voila!', 'text' => '100,00 $US']; + $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']; - $this->assertEquals('Bonjour', $messageTemplateFrench['msg_subject']); - $this->assertEquals('Voila!', $messageTemplateFrench['msg_html']); + $result = [/* settings, templates, preferredLanguage, expectMessage */]; - $rendered = CRM_Core_BAO_MessageTemplate::renderTemplate([ - 'workflow' => 'contribution_online_receipt', - 'tokenContext' => [ - 'contactId' => $this->ids['Contact']['individual_0'], - 'contributionId' => $contributionID, - ], - ]); - $this->assertEquals('Bonjour', $rendered['subject']); - $this->assertEquals('Voila!', $rendered['html']); - $this->assertEquals('100,00 $US', $rendered['text']); + $result['fr_FR matches fr_FR (all-tpls; yes-partials)'] = [$yesPartials, $allTemplates, 'fr_FR', $rendered['fr_FR']]; + $result['fr_FR matches fr_FR (all-tpls; no-partials)'] = [$noPartials, $allTemplates, 'fr_FR', $rendered['fr_FR']]; + $result['fr_FR falls back to fr_CA (ltd-tpls; yes-partials)'] = [$yesPartials, $onlyTemplates(['*', 'fr_CA']), 'fr_FR', $rendered['fr_CA']]; + $result['fr_FR falls back to fr_CA (ltd-tpls; no-partials)'] = [$noPartials, $onlyTemplates(['*', 'fr_CA']), 'fr_FR', $rendered['fr_CA']]; - // French Canadian should ALSO pick up French if there - //is no specific French Canadian. - $rendered = CRM_Core_BAO_MessageTemplate::renderTemplate([ - 'workflow' => 'contribution_online_receipt', - 'tokenContext' => [ - 'contactId' => $this->ids['Contact']['individual_0'], - 'contributionId' => $contributionID, - ], - 'language' => 'fr_CA', - ]); - $this->assertEquals('Bonjour', $rendered['subject']); - $this->assertEquals('Voila!', $rendered['html']); - // Money is formatted per fr_FR locale as that is the found-template-locale. - $this->assertEquals('100,00 $US', $rendered['text']); + $result['fr_CA matches fr_CA (all-tpls; yes-partials)'] = [$yesPartials, $allTemplates, 'fr_CA', $rendered['fr_CA']]; + $result['fr_CA matches fr_CA (all-tpls; no-partials)'] = [$noPartials, $allTemplates, 'fr_CA', $rendered['fr_CA']]; + $result['fr_CA falls back to fr_FR (ltd-tpls; yes-partials)'] = [$yesPartials, $onlyTemplates(['*', 'fr_FR']), 'fr_CA', $rendered['fr_FR']]; + $result['fr_CA falls back to fr_FR (ltd-tpls; no-partials)'] = [$noPartials, $onlyTemplates(['*', 'fr_FR']), 'fr_CA', $rendered['fr_FR']]; - Translation::save()->setRecords([ - ['entity_field' => 'msg_subject', 'string' => 'Bonjour Canada'], - ['entity_field' => 'msg_html', 'string' => 'Voila! Canada'], - ['entity_field' => 'msg_text', 'string' => '{contribution.total_amount}'], - ])->setDefaults([ - 'entity_table' => 'civicrm_msg_template', - 'entity_id' => $messageTemplateID, - 'status_id:name' => 'active', - 'language' => 'fr_CA', - ])->execute(); + $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['*']]; + // ^^ 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, $onlyTemplates(['*']), 'th_TH', $rendered['*']]; + $result['th_TH falls back to system default (ltd-tpls; no-partials)'] = [$noPartials, $onlyTemplates(['*']), 'th_TH', $rendered['*']]; - // But, prefer French Canadian where both exist. - $rendered = CRM_Core_BAO_MessageTemplate::renderTemplate([ - 'workflow' => 'contribution_online_receipt', - 'tokenContext' => [ - 'contactId' => $this->ids['Contact']['individual_0'], - 'contributionId' => $contributionID, - ], - 'language' => 'fr_CA', - ]); - $this->assertEquals('Bonjour Canada', $rendered['subject']); - $this->assertEquals('Voila! Canada', $rendered['html']); - // Note that as there was a native-Canada format the money-formatting is - // also subtly different. - $this->assertEquals('100,00 $ US', $rendered['text']); + return $result; } /** * 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. + * 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 + * @dataProvider getLocaleConfigurations */ - public function testRenderTranslatedTemplate_OnlyFullLocales(): void { - $cleanup = \CRM_Utils_AutoClean::swapSettings(['partial_locales' => FALSE, 'uiLanguages' => ['en_US']]); + public function testRenderTranslatedTemplate($settings, $templates, $preferredLanguage, $expectRendered): void { + if (empty($settings['partial_locales']) && count(\CRM_Core_I18n::languages(FALSE)) <= 1) { + $this->markTestIncomplete('Full testing of localization requires l10n data.'); + } + $cleanup = \CRM_Utils_AutoClean::swapSettings($settings); - $this->individualCreate(['preferred_language' => 'fr_FR']); + $this->individualCreate(['preferred_language' => $preferredLanguage]); $contributionID = $this->contributionCreate(['contact_id' => $this->ids['Contact']['individual_0']]); $messageTemplateID = MessageTemplate::get() ->addWhere('is_default', '=', 1) @@ -164,27 +116,43 @@ public function testRenderTranslatedTemplate_OnlyFullLocales(): void { ->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(); + foreach ($templates as $tplLocale => $tplData) { + if ($tplLocale === '*') { + MessageTemplate::update() + ->addWhere('id', '=', $messageTemplateID) + ->setValues([ + 'msg_subject' => $tplData['subject'], + 'msg_html' => $tplData['html'], + 'msg_text' => $tplData['text'], + ]) + ->execute(); + } + else { + Translation::save()->setRecords([ + ['entity_field' => 'msg_subject', 'string' => $tplData['subject']], + ['entity_field' => 'msg_html', 'string' => $tplData['html']], + ['entity_field' => 'msg_text', 'string' => $tplData['text']], + ])->setDefaults([ + 'entity_table' => 'civicrm_msg_template', + 'entity_id' => $messageTemplateID, + 'status_id:name' => 'active', + 'language' => $tplLocale, + ])->execute(); + } + } - $messageTemplateFrench = MessageTemplate::get() + $myMessageTemplate = MessageTemplate::get() ->addWhere('is_default', '=', 1) ->addWhere('workflow_name', '=', 'contribution_online_receipt') - ->addSelect('id', 'msg_subject', 'msg_html') - ->setLanguage('fr_FR') + ->addSelect('id', 'msg_subject', 'msg_html', 'msg_text') + ->setLanguage($preferredLanguage) ->setTranslationMode('fuzzy') ->execute()->first(); - $this->assertStringNotContainsString('Bonjour', $messageTemplateFrench['msg_subject']); - $this->assertStringNotContainsString('Voila!', $messageTemplateFrench['msg_html']); + // In our examples, subject+html are constant values, but text has tokens. + $this->assertEquals($expectRendered['subject'], $myMessageTemplate['msg_subject']); + $this->assertEquals($expectRendered['html'], $myMessageTemplate['msg_html']); + $this->assertNotEquals($expectRendered['text'], $myMessageTemplate['msg_text']); $rendered = CRM_Core_BAO_MessageTemplate::renderTemplate([ 'workflow' => 'contribution_online_receipt', @@ -193,9 +161,10 @@ public function testRenderTranslatedTemplate_OnlyFullLocales(): void { 'contributionId' => $contributionID, ], ]); - $this->assertStringNotContainsString('Bonjour', $rendered['subject']); - $this->assertStringNotContainsString('Voila!', $rendered['html']); - $this->assertStringNotContainsString('100,00', $rendered['text']); + $this->assertEquals( + CRM_Utils_Array::subset($expectRendered, ['subject', 'html', 'text']), + CRM_Utils_Array::subset($rendered, ['subject', 'html', 'text']) + ); } /**