(Update) Translations - if a message_template has been translated then get/render...
authorTim Otten <totten@civicrm.org>
Mon, 8 Aug 2022 09:05:12 +0000 (02:05 -0700)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 1 Sep 2022 06:51:02 +0000 (18:51 +1200)
* 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
Civi/Api4/Action/WorkflowMessage/Render.php
tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php

index 10d357e86b2c9728bb63884d03b2390e4a9f2b88..cd260a29c9dd089772d50879376e2d80e6e46e28 100644 (file)
@@ -344,15 +344,7 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate implemen
     $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 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate implemen
     }
     $nullSet = ['subject' => NULL, 'text' => NULL, 'html' => NULL];
     $mailContent = array_merge($nullSet, $mailContent, $rendered);
-    $moneyFormatLocale = $originalValue;
     return [$mailContent, $params];
   }
 
@@ -483,6 +474,7 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate implemen
 
     $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 @@ class CRM_Core_BAO_MessageTemplate extends CRM_Core_DAO_MessageTemplate implemen
       $mailContent['subject'] = $subjectOverride;
     }
 
-    return [$mailContent, $apiCall->getTranslationLanguage()];
+    return [$mailContent, $messageTemplate['actual_language'] ?? NULL];
   }
 
   /**
index c645374ba81f9fbc1ad917e2b4bfd633084f3653..6a5658cb6b8b3b02aec0c38b93a09f96ccea8a0f 100644 (file)
@@ -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']);
   }
 
index 4172795d990cdc5fc696a10dc399bcaf0974297d..4d6a1c3b95638f72cf853117e5d654e2593e7117 100644 (file)
@@ -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 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
   /**
    * 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 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
       ->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 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
     $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