Further work on localization

This commit is contained in:
Tomas Bures 2018-12-15 15:15:48 +01:00
parent fa451fc8da
commit cb1fc5b28d
35 changed files with 430 additions and 2796 deletions

View file

@ -67,7 +67,7 @@ async function _sendTransactionalMail(transport, mail, template) {
}
mail.headers['X-Sending-Zone'] = 'transactional';
const htmlRenderer = await tools.getTemplate(template.html);
const htmlRenderer = await tools.getTemplate(template.html, template.locale);
if (htmlRenderer) {
mail.html = htmlRenderer(template.data || {});
@ -79,7 +79,7 @@ async function _sendTransactionalMail(transport, mail, template) {
mail.html = preparedHtml;
}
const textRenderer = await tools.getTemplate(template.text);
const textRenderer = await tools.getTemplate(template.text, template.locale);
if (textRenderer) {
mail.text = textRenderer(template.data || {});

View file

@ -20,49 +20,49 @@ module.exports = {
sendUnsubscriptionConfirmed
};
async function sendSubscriptionConfirmed(lang, list, email, subscription) {
async function sendSubscriptionConfirmed(locale, list, email, subscription) {
const relativeUrls = {
preferencesUrl: '/subscription/' + list.cid + '/manage/' + subscription.cid,
unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid
};
await _sendMail(list, email, 'subscription_confirmed', lang, tMark('subscriptionconfirmed'), relativeUrls, subscription);
await _sendMail(list, email, 'subscription_confirmed', locale, tMark('subscriptionconfirmed'), relativeUrls, subscription);
}
async function sendAlreadySubscribed(lang, list, email, subscription) {
async function sendAlreadySubscribed(locale, list, email, subscription) {
const relativeUrls = {
preferencesUrl: '/subscription/' + list.cid + '/manage/' + subscription.cid,
unsubscribeUrl: '/subscription/' + list.cid + '/unsubscribe/' + subscription.cid
};
await _sendMail(list, email, 'already_subscribed', lang, tMark('listEmailAddressAlreadyRegistered'), relativeUrls, subscription);
await _sendMail(list, email, 'already_subscribed', locale, tMark('listEmailAddressAlreadyRegistered'), relativeUrls, subscription);
}
async function sendConfirmAddressChange(lang, list, email, cid, subscription) {
async function sendConfirmAddressChange(locale, list, email, cid, subscription) {
const relativeUrls = {
confirmUrl: '/subscription/confirm/change-address/' + cid
};
await _sendMail(list, email, 'confirm_address_change', lang, tMark('listPleaseConfirmEmailChangeIn'), relativeUrls, subscription);
await _sendMail(list, email, 'confirm_address_change', locale, tMark('listPleaseConfirmEmailChangeIn'), relativeUrls, subscription);
}
async function sendConfirmSubscription(lang, list, email, cid, subscription) {
async function sendConfirmSubscription(locale, list, email, cid, subscription) {
const relativeUrls = {
confirmUrl: '/subscription/confirm/subscribe/' + cid
};
await _sendMail(list, email, 'confirm_subscription', lang, tMark('pleaseConfirmSubscription'), relativeUrls, subscription);
await _sendMail(list, email, 'confirm_subscription', locale, tMark('pleaseConfirmSubscription'), relativeUrls, subscription);
}
async function sendConfirmUnsubscription(lang, list, email, cid, subscription) {
async function sendConfirmUnsubscription(locale, list, email, cid, subscription) {
const relativeUrls = {
confirmUrl: '/subscription/confirm/unsubscribe/' + cid
};
await _sendMail(list, email, 'confirm_unsubscription', lang, tMark('listPleaseConfirmUnsubscription'), relativeUrls, subscription);
await _sendMail(list, email, 'confirm_unsubscription', locale, tMark('listPleaseConfirmUnsubscription'), relativeUrls, subscription);
}
async function sendUnsubscriptionConfirmed(lang, list, email, subscription) {
async function sendUnsubscriptionConfirmed(locale, list, email, subscription) {
const relativeUrls = {
subscribeUrl: '/subscription/' + list.cid + '?cid=' + subscription.cid
};
await _sendMail(list, email, 'unsubscription_confirmed', lang, tMark('listUnsubscriptionConfirmed'), relativeUrls, subscription);
await _sendMail(list, email, 'unsubscription_confirmed', locale, tMark('listUnsubscriptionConfirmed'), relativeUrls, subscription);
}
function getDisplayName(flds, subscription) {
@ -95,7 +95,7 @@ function getDisplayName(flds, subscription) {
}
}
async function _sendMail(list, email, template, language, subjectKey, relativeUrls, subscription) {
async function _sendMail(list, email, template, locale, subjectKey, relativeUrls, subscription) {
const flds = await fields.list(contextHelpers.getAdminContext(), list.id);
const encryptionKeys = [];
@ -114,7 +114,7 @@ async function _sendMail(list, email, template, language, subjectKey, relativeUr
};
for (let relativeUrlKey in relativeUrls) {
data[relativeUrlKey] = getPublicUrl(relativeUrls[relativeUrlKey], {language});
data[relativeUrlKey] = getPublicUrl(relativeUrls[relativeUrlKey], {locale});
}
const fsTemplate = template.replace(/_/g, '-');
@ -148,11 +148,12 @@ async function _sendMail(list, email, template, language, subjectKey, relativeUr
name: getDisplayName(flds, subscription),
address: email
},
subject: tUI(subjectKey, language, { list: list.name }),
subject: tUI(subjectKey, locale, { list: list.name }),
encryptionKeys
}, {
html,
text,
locale,
data
});
} else {

View file

@ -19,17 +19,34 @@ const he = require('he');
const fs = require('fs-extra');
const { JSDOM } = require('jsdom');
const { tUI, tLog } = require('./translate');
const { tUI, tLog, getLangCodeFromExpressLocale } = require('./translate');
const templates = new Map();
async function getTemplate(template) {
async function getLocalizedFile(basePath, fileName, language) {
try {
const locFn = path.join(basePath, language, fileName);
const stats = await fs.stat(locFn);
if (stats.isFile()) {
return locFn;
}
} catch (err) {
if (err.code !== 'ENOENT') {
throw err;
}
}
return path.join(basePath, fileName)
}
async function getTemplate(template, locale) {
if (!template) {
return false;
}
const key = (typeof template === 'object') ? hasher.hash(template) : template;
const key = getLangCodeFromExpressLocale(locale) + ':' + ((typeof template === 'object') ? hasher.hash(template) : template);
if (templates.has(key)) {
return templates.get(key);
@ -37,9 +54,9 @@ async function getTemplate(template) {
let source;
if (typeof template === 'object') {
source = await mergeTemplateIntoLayout(template.template, template.layout);
source = await mergeTemplateIntoLayout(template.template, template.layout, locale);
} else {
source = await fs.readFile(path.join(__dirname, '..', 'views', template), 'utf-8');
source = await fs.readFile(await getLocalizedFile(path.join(__dirname, '..', 'views'), template, getLangCodeFromExpressLocale(locale)), 'utf-8');
}
if (template.type === 'mjml') {
@ -53,17 +70,35 @@ async function getTemplate(template) {
}
const renderer = hbs.handlebars.compile(source);
templates.set(key, renderer);
return renderer;
const localizedRenderer = (data, options) => {
if (!options) {
options = {};
}
if (!options.helpers) {
options.helpers = {};
}
options.helpers.translate = function (opts) { // eslint-disable-line prefer-arrow-callback
const result = tUI(opts.fn(this), locale, opts.hash); // eslint-disable-line no-invalid-this
return new hbs.handlebars.SafeString(result);
};
return renderer(data, options);
};
templates.set(key, localizedRenderer);
return localizedRenderer;
}
async function mergeTemplateIntoLayout(template, layout) {
async function mergeTemplateIntoLayout(template, layout, locale) {
layout = layout || '{{{body}}}';
async function readFile(relPath) {
return await fs.readFile(path.join(__dirname, '..', 'views', relPath), 'utf-8');
return await fs.readFile(await getLocalizedFile(path.join(__dirname, '..', 'views'), relPath, getLangCodeFromExpressLocale(locale)), 'utf-8');
}
// Please dont end your custom messages with .hbs ...
@ -184,7 +219,6 @@ function getMessageLinks(campaign, list, subscription) {
module.exports = {
validateEmail,
validateEmailGetMessage,
mergeTemplateIntoLayout,
getTemplate,
prepareHtml,
getMessageLinks,

View file

@ -4,25 +4,24 @@ const config = require('config');
const i18n = require("i18next");
const fs = require('fs');
const path = require('path');
const {convertToFake, langCodes} = require('../../shared/langs');
const {convertToFake, getLang} = require('../../shared/langs');
const resourcesCommon = {};
function loadLanguage(shortCode) {
resourcesCommon[shortCode] = {
common: JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'locales', shortCode, 'common.json')))
function loadLanguage(longCode) {
resourcesCommon[longCode] = {
common: JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'locales', longCode, 'common.json')))
};
}
loadLanguage('en');
loadLanguage('es');
resourcesCommon.fake = convertToFake(resourcesCommon.en);
loadLanguage('en-US');
resourcesCommon['fk-FK'] = convertToFake(resourcesCommon['en-US']);
const resources = {};
for (const lng of config.enabledLanguages) {
const shortCode = langCodes[lng].shortCode;
resources[shortCode] = {
common: resourcesCommon[shortCode]
const langDesc = getLang(lng);
resources[langDesc.longCode] = {
common: resourcesCommon[langDesc.longCode]
};
}
@ -34,6 +33,9 @@ i18n
fallbackLng: config.defaultLanguage,
defaultNS: 'common',
whitelist: config.enabledLanguages,
load: 'currentOnly',
debug: false
})
@ -47,18 +49,29 @@ function tLog(key, args) {
return JSON.stringify([key, args]);
}
function tUI(key, lang, args) {
function tUI(key, locale, args) {
if (!args) {
args = {};
}
return i18n.t(key, { ...args, lng: lang });
return i18n.t(key, { ...args, lng: getLangCodeFromExpressLocale(locale) });
}
function tMark(key) {
return key;
}
function getLangCodeFromExpressLocale(locale) {
const longCode = locale.toString().replace('_', '-');
if (longCode in resources) {
return longCode;
} else {
return config.defaultLanguage
}
}
module.exports.tLog = tLog;
module.exports.tUI = tUI;
module.exports.tMark = tMark;
module.exports.tMark = tMark;
module.exports.getLangCodeFromExpressLocale = getLangCodeFromExpressLocale;

View file

@ -3,6 +3,7 @@
const config = require('config');
const urllib = require('url');
const {anonymousRestrictedAccessToken} = require('../../shared/urls');
const {getLangCodeFromExpressLocale} = require('./translate');
function getTrustedUrlBase() {
return urllib.resolve(config.www.trustedUrlBase, '');
@ -19,8 +20,8 @@ function getPublicUrlBase() {
function _getUrl(urlBase, path, opts) {
const url = new URL(path || '', urlBase);
if (opts && opts.language) {
url.searchParams.append('lang', opts.language)
if (opts && opts.locale) {
url.searchParams.append('locale', getLangCodeFromExpressLocale(opts.locale));
}
return url.toString();