Fix failure to fall back to site default language, if configured
authorEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 11 Jul 2023 00:16:28 +0000 (12:16 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 11 Jul 2023 01:16:08 +0000 (13:16 +1200)
This addresses scenario 6 here
https://github.com/civicrm/civicrm-core/pull/26232#issuecomment-1624455432

CRM/Core/BAO/Translation.php
Civi/WorkflowMessage/Traits/LocalizationTrait.php
tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php

index 1a23382e6aa84b1f4aaacb24e32926ec7464dca4..f6d969a8b7f93ebf41a7bef484cfcdc7b9d22900 100644 (file)
@@ -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'];
   }
 
 }
index 619256ff7d4fcd7fdbafc81f22b4a7859b29bf65..d5974630b99b88208cccfa9accf0c30a22c6a711 100644 (file)
@@ -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
index c69c1b663dce1794de7fa32b3b06645316146ac7..8b51e6f0eb2c22b4ff8ffec25493000e20e0b306 100644 (file)
@@ -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']);
   }
 
   /**