Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(dev/core#4409) Fix failure to fall back to site default language, if configured #26785

Merged
merged 3 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions CRM/Core/BAO/Translation.php
Original file line number Diff line number Diff line change
Expand Up @@ -278,13 +278,48 @@ protected static function getTranslatedFieldsForRequest(AbstractAction $apiReque
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'];
}

}
4 changes: 3 additions & 1 deletion Civi/WorkflowMessage/Traits/LocalizationTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 11 additions & 7 deletions ext/message_admin/ang/crmMsgadm/ListCtrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@
});

var $ctrl = this;
$ctrl.records = _.map(
[].concat(prefetch.records, _.map(prefetch.translations || [], simpleKeys)),
function(r) {
r._is_translation = (r.tx_language !== undefined);
return r;
}
);
var allRecords = [].concat(prefetch.records, _.map(prefetch.translations || [], simpleKeys));
$ctrl.records = _.map(allRecords, function(r) {
r._is_translation = (r.tx_language !== undefined);

// If there is a translation in the system-default-locale, then it replaces the "Standard" tpl as the primary/visible item entry.
const defaultLocaleTpl = _.find(allRecords, {workflow_name: r.workflow_name, tx_language: CRM.config.lcMessages});
r._is_primary = defaultLocaleTpl ? (r === defaultLocaleTpl) : (!r._is_translation);
r._is_visible = (r._is_translation || r._is_primary);

return r;
});

function findTranslations(record) {
return _.reduce($ctrl.records, function(existing, rec){
Expand Down
4 changes: 2 additions & 2 deletions ext/message_admin/ang/crmMsgadm/Workflow.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
prefetch: function(crmApi4, crmStatus) {
var q = crmApi4({
records: ['MessageTemplate', 'get', {
select: ["id", "msg_title", "is_default", "is_active"],
select: ["id", "msg_title", "is_default", "is_active", "workflow_name"],
where: [["workflow_name", "IS NOT EMPTY"], ["is_reserved", "=", "0"]]
}],
translations: ['MessageTemplate', 'get', {
select: ["id", "msg_title", "is_default", "is_active", "tx.language:label", "tx.language"],
select: ["id", "msg_title", "is_default", "is_active", "workflow_name", "tx.language:label", "tx.language"],
join: [["Translation AS tx", "INNER", null, ["tx.entity_table", "=", "'civicrm_msg_template'"], ["tx.entity_id", "=", "id"]]],
where: [["workflow_name", "IS NOT EMPTY"], ["is_reserved", "=", "0"]],
groupBy: ["id", "tx.language"],
Expand Down
4 changes: 2 additions & 2 deletions ext/message_admin/ang/crmMsgadm/WorkflowTranslated.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</tr>
</thead>
<tbody>
<tr ng-repeat="record in $ctrl.records | filter:filters.text | orderBy:['msg_title','_is_translation','tx_language_label']">
<tr ng-repeat="record in $ctrl.records | filter:filters.text | filter:{_is_visible: true} | orderBy:['msg_title','!_is_primary','tx_language_label']">
<td>{{record.msg_title}}</td>
<td>{{record.tx_language_label || ts('Standard')}}</td>
<td>
Expand All @@ -39,7 +39,7 @@
</span>
</td>
<td>
<span ng-if="!record.tx_language">
<span ng-if="record._is_primary">
<a href crm-icon="fa-plus" ng-click="$ctrl.addTranslation(record)" title="{{ts('Add translation, &quot;%1&quot;', {1: record.msg_title})}}">{{:: ts('Translate') }}</a>
</span>
</td>
Expand Down
34 changes: 28 additions & 6 deletions tests/phpunit/CRM/Core/BAO/MessageTemplateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ public function getLocaleConfigurations(): array {

// 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;
}
Expand Down Expand Up @@ -218,16 +221,22 @@ public function testGetTemplateTranslationIncompleteTemplateSet(): void {
->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'];
}
Expand All @@ -246,8 +255,9 @@ public function testGetTemplateTranslationIncompleteTemplateSet(): void {
->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)
Expand All @@ -256,6 +266,18 @@ public function testGetTemplateTranslationIncompleteTemplateSet(): void {
->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']);
}

/**
Expand Down