Use translation for default language, if exists
authorEileen McNaughton <emcnaughton@wikimedia.org>
Tue, 16 May 2023 04:07:14 +0000 (16:07 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Wed, 5 Jul 2023 20:24:56 +0000 (08:24 +1200)
There is an assumption in the code that for the default language a translation is not used.

However, the features around draft versions are all set up around
translations, so it becomes necessary to create translations
even for the default language. This change ensures they are
used if they exist.

Although we have a lot of complexity in the translation framework
on the rendering side I think assuming that where stuff is configured
people configured it to be used is a good underlying principle.

CRM/Core/BAO/Translation.php
tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php

index c81cc46eeef89840083ab422dfdd0ccc2994480e..1a23382e6aa84b1f4aaacb24e32926ec7464dca4 100644 (file)
@@ -178,7 +178,7 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements HookI
     }
 
     $communicationLanguage = \Civi\Core\Locale::detect()->nominal;
-    if ($communicationLanguage === Civi::settings()->get('lcMessages')) {
+    if (!$communicationLanguage || !self::isTranslate($communicationLanguage)) {
       return;
     }
 
@@ -207,6 +207,34 @@ class CRM_Core_BAO_Translation extends CRM_Core_DAO_Translation implements HookI
     }
   }
 
+  /**
+   * Should the translation process be followed.
+   *
+   * It can be short-circuited if there we are in the site default language and
+   * it is not translated.
+   *
+   * @param string $communicationLanguage
+   *
+   * @return bool
+   */
+  protected static function isTranslate(string $communicationLanguage): bool {
+    if ($communicationLanguage !== Civi::settings()->get('lcMessages')) {
+      return TRUE;
+    }
+    if (!isset(\Civi::$statics[__CLASS__]['translate_main'][$communicationLanguage])) {
+      // The code had an assumption that you would not translate the primary language.
+      // However, the UI is such that the features (approval flow) so it makes sense
+      // to translation the default site language as well. If we can see sites are
+      // doing this then let's treat the main locale like any other locale
+      \Civi::$statics[__CLASS__]['translate_main'] = (bool) CRM_Core_DAO::singleValueQuery(
+        'SELECT COUNT(*) FROM civicrm_translation WHERE language = %1 LIMIT 1', [
+          1 => [$communicationLanguage, 'String'],
+        ]
+      );
+    }
+    return \Civi::$statics[__CLASS__]['translate_main'];
+  }
+
   /**
    * @param \Civi\Api4\Generic\AbstractAction $apiRequest
    * @return array translated fields.
index e9efcb9aaea3e2df507f6f308885fc01791368f6..c69c1b663dce1794de7fa32b3b06645316146ac7 100644 (file)
@@ -70,6 +70,10 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
     $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}'];
+    // en_US is the default language. We add it to ensure the translation is loaded rather than falling back to
+    // the message template. While it might seem you don't need a translation for the site default language
+    // all the draft & approval logic is attached to the translation, not the template.
+    $allTemplates['en_US'] = ['subject' => 'site default', 'html' => 'site default language', 'text' => '{contribution.total_amount}'];
 
     $rendered = [];
     // $rendered['*'] = ['subject' => 'Hello', 'html' => 'Look there!', 'text' => '$ 100.00'];
@@ -78,6 +82,7 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
     $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'];
+    $rendered['en_US'] = ['subject' => 'site default', 'html' => 'site default language', 'text' => '$100.00'];
 
     $result = [/* settings, templates, preferredLanguage, expectMessage */];
 
@@ -92,11 +97,16 @@ class CRM_Core_BAO_MessageTemplateTest extends CiviUnitTestCase {
     $result['fr_CA falls back to fr_FR (ltd-tpls; no-partials)'] = [$noPartials, $this->getLocaleTemplates($allTemplates, ['*', 'fr_FR']), 'fr_CA', $rendered['fr_FR']];
 
     $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['*']];
+    $result['th_TH falls back to site default translation (all-tpls; no-partials)'] = [$noPartials, $allTemplates, 'th_TH', $rendered['en_US']];
+    // Absent a translation for the site default language it should fall back to the message template.
+    $result['th_TH falls back to system default (all-tpls; no-partials)'] = [$noPartials, array_diff_key($allTemplates, ['en_US' => TRUE]), '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, $this->getLocaleTemplates($allTemplates, ['*']), 'th_TH', $rendered['*']];
     $result['th_TH falls back to system default (ltd-tpls; no-partials)'] = [$noPartials, $this->getLocaleTemplates($allTemplates, ['*']), 'th_TH', $rendered['*']];
 
+    // 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']];
+
     return $result;
   }