Further work on localization
This commit is contained in:
parent
fa451fc8da
commit
cb1fc5b28d
35 changed files with 430 additions and 2796 deletions
|
@ -11,7 +11,6 @@ const favicon = require('serve-favicon');
|
|||
const logger = require('morgan');
|
||||
const cookieParser = require('cookie-parser');
|
||||
const session = require('express-session');
|
||||
const RedisStore = require('connect-redis')(session);
|
||||
const flash = require('connect-flash');
|
||||
const hbs = require('hbs');
|
||||
const compression = require('compression');
|
||||
|
@ -157,17 +156,32 @@ function createApp(appType) {
|
|||
}));
|
||||
|
||||
app.use(cookieParser());
|
||||
app.use(session({
|
||||
store: config.redis.enabled ? new RedisStore(config.redis) : false,
|
||||
secret: config.www.secret,
|
||||
saveUninitialized: false,
|
||||
resave: false
|
||||
}));
|
||||
|
||||
if (config.redis.enabled) {
|
||||
const RedisStore = require('connect-redis')(session);
|
||||
|
||||
app.use(session({
|
||||
store: new RedisStore(config.redis),
|
||||
secret: config.www.secret,
|
||||
saveUninitialized: false,
|
||||
resave: false
|
||||
}));
|
||||
} else {
|
||||
app.use(session({
|
||||
store: false,
|
||||
secret: config.www.secret,
|
||||
saveUninitialized: false,
|
||||
resave: false
|
||||
}));
|
||||
}
|
||||
|
||||
app.use(expressLocale({
|
||||
priority: ['query', 'accept-language', 'default'],
|
||||
priority: ['query', 'cookie', 'accept-language', 'default'],
|
||||
query: {
|
||||
name: 'language'
|
||||
name: 'locale'
|
||||
},
|
||||
cookie: {
|
||||
name: 'i18nextLng'
|
||||
},
|
||||
default: config.defaultLanguage
|
||||
}));
|
||||
|
|
|
@ -37,12 +37,12 @@ editors:
|
|||
- codeeditor
|
||||
|
||||
# Default language to use
|
||||
defaultLanguage: en_US
|
||||
defaultLanguage: en-US
|
||||
|
||||
# Enabled languages
|
||||
enabledLanguages:
|
||||
- en_US
|
||||
- es
|
||||
- en-US
|
||||
- fk-FK
|
||||
|
||||
# Inject custom scripts in subscription/layout.mjml.hbs
|
||||
# customSubscriptionScripts: [/custom/hello-world.js]
|
||||
|
|
|
@ -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 || {});
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -29,7 +29,7 @@ function getFilePath(type, subType, entityId, filename) {
|
|||
}
|
||||
|
||||
function getFileUrl(context, type, subType, entityId, filename) {
|
||||
return getPublicUrl(`files/${type}/${subType}/${entityId}/${filename}`, context)
|
||||
return getPublicUrl(`files/${type}/${subType}/${entityId}/${filename}`)
|
||||
}
|
||||
|
||||
function getFilesTable(type, subType) {
|
||||
|
@ -109,7 +109,7 @@ async function getFileByFilename(context, type, subType, entityId, name) {
|
|||
}
|
||||
|
||||
async function getFileByUrl(context, url) {
|
||||
const urlPrefix = getPublicUrl('files/', context);
|
||||
const urlPrefix = getPublicUrl('files/');
|
||||
if (url.startsWith(urlPrefix)) {
|
||||
const path = url.substring(urlPrefix.length);
|
||||
const pathElem = path.split('/');
|
||||
|
|
|
@ -286,7 +286,7 @@ async function resetAccessToken(userId) {
|
|||
return token;
|
||||
}
|
||||
|
||||
async function sendPasswordReset(language, usernameOrEmail) {
|
||||
async function sendPasswordReset(locale, usernameOrEmail) {
|
||||
enforce(passport.isAuthMethodLocal, 'Local user management is required');
|
||||
|
||||
await knex.transaction(async tx => {
|
||||
|
@ -310,12 +310,13 @@ async function sendPasswordReset(language, usernameOrEmail) {
|
|||
to: {
|
||||
address: user.email
|
||||
},
|
||||
subject: tUI('mailerPasswordChangeRequest', language)
|
||||
subject: tUI('mailerPasswordChangeRequest', locale)
|
||||
}, {
|
||||
html: 'emails/password-reset-html.hbs',
|
||||
text: 'emails/password-reset-text.hbs',
|
||||
html: 'users/password-reset-html.hbs',
|
||||
text: 'users/password-reset-text.hbs',
|
||||
locale,
|
||||
data: {
|
||||
title: 'Mailtrain',
|
||||
title: tUI('Mailtrain', locale),
|
||||
username: user.username,
|
||||
name: user.name,
|
||||
confirmUrl: getTrustedUrl(`/account/reset/${encodeURIComponent(user.username)}/${encodeURIComponent(resetToken)}`)
|
||||
|
|
|
@ -62,7 +62,7 @@ router.postAsync('/subscribe/:listCid', passport.loggedIn, async (req, res) => {
|
|||
};
|
||||
|
||||
const confirmCid = await confirmations.addConfirmation(list.id, 'subscribe', req.ip, data);
|
||||
await mailHelpers.sendConfirmSubscription(req.language, list, input.EMAIL, confirmCid, subscription);
|
||||
await mailHelpers.sendConfirmSubscription(req.locale, list, input.EMAIL, confirmCid, subscription);
|
||||
|
||||
res.status(200);
|
||||
res.json({
|
||||
|
|
|
@ -44,7 +44,10 @@ router.post('/login', passport.csrfProtection, passport.restLogin);
|
|||
router.post('/logout', passport.csrfProtection, passport.restLogout);
|
||||
|
||||
router.postAsync('/password-reset-send', passport.csrfProtection, async (req, res) => {
|
||||
await users.sendPasswordReset(req.language, req.body.usernameOrEmail);
|
||||
// FIXME
|
||||
console.log(req.locale);
|
||||
console.log(req.cookies);
|
||||
await users.sendPasswordReset(req.locale, req.body.usernameOrEmail);
|
||||
return res.json();
|
||||
});
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ function getRouter(appType) {
|
|||
const mailtrainConfig = await clientHelpers.getAnonymousConfig(req.context, appType);
|
||||
|
||||
let languageStrings = null;
|
||||
const lang = req.language;
|
||||
const lang = req.locale.language;
|
||||
if (lang && lang !== 'en') {
|
||||
try {
|
||||
const file = path.join(__dirname, '..', '..', 'client', 'static', 'mosaico', 'lang', 'mosaico-' + lang + '.json');
|
||||
|
|
|
@ -126,7 +126,7 @@ router.getAsync('/confirm/subscribe/:cid', async (req, res) => {
|
|||
|
||||
const list = await lists.getById(contextHelpers.getAdminContext(), confirmation.list);
|
||||
subscription.cid = meta.cid;
|
||||
await mailHelpers.sendSubscriptionConfirmed(req.language, list, subscription.email, subscription);
|
||||
await mailHelpers.sendSubscriptionConfirmed(req.locale, list, subscription.email, subscription);
|
||||
|
||||
res.redirect('/subscription/' + encodeURIComponent(list.cid) + '/subscribed-notice');
|
||||
});
|
||||
|
@ -139,9 +139,9 @@ router.getAsync('/confirm/change-address/:cid', async (req, res) => {
|
|||
|
||||
const subscription = await subscriptions.updateAddressAndGet(contextHelpers.getAdminContext(), list.id, data.subscriptionId, data.emailNew);
|
||||
|
||||
await mailHelpers.sendSubscriptionConfirmed(req.language, list, data.emailNew, subscription);
|
||||
await mailHelpers.sendSubscriptionConfirmed(req.locale, list, data.emailNew, subscription);
|
||||
|
||||
req.flash('info', tUI('emailAddressChanged', req.language));
|
||||
req.flash('info', tUI('emailAddressChanged', req.locale));
|
||||
res.redirect('/subscription/' + encodeURIComponent(list.cid) + '/manage/' + subscription.cid);
|
||||
});
|
||||
|
||||
|
@ -153,7 +153,7 @@ router.getAsync('/confirm/unsubscribe/:cid', async (req, res) => {
|
|||
|
||||
const subscription = await subscriptions.unsubscribeByCidAndGet(contextHelpers.getAdminContext(), list.id, data.subscriptionCid, data.campaignCid);
|
||||
|
||||
await mailHelpers.sendUnsubscriptionConfirmed(req.language, list, subscription.email, subscription);
|
||||
await mailHelpers.sendUnsubscriptionConfirmed(req.locale, list, subscription.email, subscription);
|
||||
|
||||
res.redirect('/subscription/' + encodeURIComponent(list.cid) + '/unsubscribed-notice');
|
||||
});
|
||||
|
@ -181,7 +181,7 @@ async function _renderSubscribe(req, res, list, subscription) {
|
|||
|
||||
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-subscribe', data);
|
||||
|
||||
const htmlRenderer = await tools.getTemplate(data.template);
|
||||
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
|
||||
|
||||
data.isWeb = true;
|
||||
data.needsJsWarning = true;
|
||||
|
@ -245,7 +245,7 @@ router.postAsync('/:cid/subscribe', passport.parseForm, corsOrCsrfProtection, as
|
|||
throw new Error('Email address not set');
|
||||
}
|
||||
|
||||
req.flash('danger', tUI('emailAddressNotSet', req.language));
|
||||
req.flash('danger', tUI('emailAddressNotSet', req.locale));
|
||||
return await _renderSubscribe(req, res, list, subscriptionData);
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,7 @@ router.postAsync('/:cid/subscribe', passport.parseForm, corsOrCsrfProtection, as
|
|||
}
|
||||
|
||||
if (existingSubscription && existingSubscription.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
await mailHelpers.sendAlreadySubscribed(req.language, list, email, existingSubscription);
|
||||
await mailHelpers.sendAlreadySubscribed(req.locale, list, email, existingSubscription);
|
||||
res.redirect('/subscription/' + encodeURIComponent(req.params.cid) + '/confirm-subscription-notice');
|
||||
|
||||
} else {
|
||||
|
@ -296,12 +296,12 @@ router.postAsync('/:cid/subscribe', passport.parseForm, corsOrCsrfProtection, as
|
|||
if (!testsPass) {
|
||||
log.info('Subscription', 'Confirmation message for %s marked to be skipped (%s)', email, JSON.stringify(data));
|
||||
} else {
|
||||
await mailHelpers.sendConfirmSubscription(req.language, list, email, confirmCid, subscriptionData);
|
||||
await mailHelpers.sendConfirmSubscription(req.locale, list, email, confirmCid, subscriptionData);
|
||||
}
|
||||
|
||||
if (req.xhr) {
|
||||
return res.status(200).json({
|
||||
msg: tUI('pleaseConfirmSubscription', req.language)
|
||||
msg: tUI('pleaseConfirmSubscription', req.locale)
|
||||
});
|
||||
}
|
||||
res.redirect('/subscription/' + encodeURIComponent(req.params.cid) + '/confirm-subscription-notice');
|
||||
|
@ -385,7 +385,7 @@ router.getAsync('/:lcid/manage/:ucid', passport.csrfProtection, async (req, res)
|
|||
|
||||
await injectCustomFormData(req.query.fid || list.default_form, 'data/web-manage', data);
|
||||
|
||||
const htmlRenderer = await tools.getTemplate(data.template);
|
||||
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
|
||||
|
||||
data.isWeb = true;
|
||||
data.needsJsWarning = true;
|
||||
|
@ -435,7 +435,7 @@ router.getAsync('/:lcid/manage-address/:ucid', passport.csrfProtection, async (r
|
|||
|
||||
await injectCustomFormData(req.query.fid || list.default_form, 'data/web-manage-address', data);
|
||||
|
||||
const htmlRenderer = await tools.getTemplate(data.template);
|
||||
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
|
||||
|
||||
data.isWeb = true;
|
||||
data.needsJsWarning = true;
|
||||
|
@ -458,7 +458,7 @@ router.postAsync('/:lcid/manage-address', passport.parseForm, passport.csrfProte
|
|||
}
|
||||
|
||||
if (subscription.email === emailNew) {
|
||||
req.flash('info', tUI('nothingSeemsToBeChanged', req.language));
|
||||
req.flash('info', tUI('nothingSeemsToBeChanged', req.locale));
|
||||
|
||||
} else {
|
||||
const emailErr = await tools.validateEmail(emailNew);
|
||||
|
@ -479,7 +479,7 @@ router.postAsync('/:lcid/manage-address', passport.parseForm, passport.csrfProte
|
|||
}
|
||||
|
||||
if (newSubscription && newSubscription.status === SubscriptionStatus.SUBSCRIBED) {
|
||||
await mailHelpers.sendAlreadySubscribed(req.language, list, emailNew, subscription);
|
||||
await mailHelpers.sendAlreadySubscribed(req.locale, list, emailNew, subscription);
|
||||
} else {
|
||||
const data = {
|
||||
subscriptionId: subscription.id,
|
||||
|
@ -487,10 +487,10 @@ router.postAsync('/:lcid/manage-address', passport.parseForm, passport.csrfProte
|
|||
};
|
||||
|
||||
const confirmCid = await confirmations.addConfirmation(list.id, 'change-address', req.ip, data);
|
||||
await mailHelpers.sendConfirmAddressChange(req.language, list, emailNew, confirmCid, subscription);
|
||||
await mailHelpers.sendConfirmAddressChange(req.locale, list, emailNew, confirmCid, subscription);
|
||||
}
|
||||
|
||||
req.flash('info', tUI('anEmailWithFurtherInstructionsHasBeen', req.language));
|
||||
req.flash('info', tUI('anEmailWithFurtherInstructionsHasBeen', req.locale));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -535,7 +535,7 @@ router.getAsync('/:lcid/unsubscribe/:ucid', passport.csrfProtection, async (req,
|
|||
|
||||
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-unsubscribe', data);
|
||||
|
||||
const htmlRenderer = await tools.getTemplate(data.template);
|
||||
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
|
||||
|
||||
data.isWeb = true;
|
||||
data.needsJsWarning = true;
|
||||
|
@ -565,7 +565,7 @@ async function handleUnsubscribe(list, subscriptionCid, autoUnsubscribe, campaig
|
|||
try {
|
||||
const subscription = await subscriptions.unsubscribeByCidAndGet(contextHelpers.getAdminContext(), list.id, subscriptionCid, campaignCid);
|
||||
|
||||
await mailHelpers.sendUnsubscriptionConfirmed(req.language, list, subscription.email, subscription);
|
||||
await mailHelpers.sendUnsubscriptionConfirmed(req.locale, list, subscription.email, subscription);
|
||||
|
||||
res.redirect('/subscription/' + encodeURIComponent(list.cid) + '/unsubscribed-notice');
|
||||
|
||||
|
@ -590,7 +590,7 @@ async function handleUnsubscribe(list, subscriptionCid, autoUnsubscribe, campaig
|
|||
};
|
||||
|
||||
const confirmCid = await confirmations.addConfirmation(list.id, 'unsubscribe', ip, data);
|
||||
await mailHelpers.sendConfirmUnsubscription(req.language, list, subscription.email, confirmCid, subscription);
|
||||
await mailHelpers.sendConfirmUnsubscription(req.locale, list, subscription.email, confirmCid, subscription);
|
||||
|
||||
res.redirect('/subscription/' + encodeURIComponent(list.cid) + '/confirm-unsubscription-notice');
|
||||
|
||||
|
@ -684,7 +684,7 @@ async function webNotice(type, req, res) {
|
|||
|
||||
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-' + type + '-notice', data);
|
||||
|
||||
const htmlRenderer = await tools.getTemplate(data.template);
|
||||
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
|
||||
|
||||
data.isWeb = true;
|
||||
data.flashMessages = await captureFlashMessages(res);
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
{{#if typeSubscriptionEmail}}
|
||||
<div class="form-group email">
|
||||
<label for="EMAIL">Email Address</label>
|
||||
<label for="EMAIL">{{#translate}}Email Address{{/translate}}</label>
|
||||
{{#if ../isManagePreferences}}
|
||||
<div class="input-group">
|
||||
<input type="email" name="EMAIL" id="email" placeholder="" value="{{../email}}" readonly>
|
||||
<div class="input-group-addon"><a href="/subscription/{{../lcid}}/manage-address/{{../cid}}">want to change it?</a></div>
|
||||
<div class="input-group-addon"><a href="/subscription/{{../lcid}}/manage-address/{{../cid}}">{{#translate}}want to change it?{{/translate}}</a></div>
|
||||
</div>
|
||||
{{else}}
|
||||
<input type="email" name="EMAIL" id="email" placeholder="" value="{{../email}}" required>
|
||||
|
@ -53,11 +53,11 @@
|
|||
<div class="form-group gpg {{key}}">
|
||||
<label for="{{key}}">{{name}}</label>
|
||||
{{#if ../hasPubkey}}
|
||||
<button class="btn-download-pubkey" type="submit" form="download-pubkey">Download signature verification key</button>
|
||||
<button class="btn-download-pubkey" type="submit" form="download-pubkey">{{#translate}}Download signature verification key{{/translate}}</button>
|
||||
{{/if}}
|
||||
<textarea class="form-control gpg-text" rows="4" name="{{key}}" placeholder="Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'">{{value}}</textarea>
|
||||
<textarea class="form-control gpg-text" rows="4" name="{{key}}" placeholder="{{#translate}}Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'{{/translate}}">{{value}}</textarea>
|
||||
<span class="help-block">
|
||||
Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em>
|
||||
{{#translate}}Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em>{{/translate}}
|
||||
</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
|
@ -95,7 +95,7 @@
|
|||
<label for="{{key}}">{{name}}</label>
|
||||
<select name="{{key}}" class="form-control">
|
||||
<option value="">
|
||||
–– Select ––
|
||||
{{#translate}}–– Select ––{{/translate}}
|
||||
</option>
|
||||
{{#each options}}
|
||||
<option value="{{key}}" {{#if value}} selected {{/if}}>{{name}}</option>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
{{#if needsJsWarning}}
|
||||
<div class="alert alert-danger js-warning" role="alert">
|
||||
<strong>Warning!</strong>
|
||||
JavaScript must be enabled in order for this form to work
|
||||
<strong>{{#translate}}Warning!{{/translate}}</strong>
|
||||
{{#translate}}JavaScript must be enabled in order for this form to work{{/translate}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
|
|
@ -4,19 +4,19 @@
|
|||
<input type="hidden" name="cid" value="{{cid}}">
|
||||
|
||||
<div class="form-group email">
|
||||
<label for="EMAIL">Existing Email Address</label>
|
||||
<label for="EMAIL">{{#translate}}Existing Email Address{{/translate}}</label>
|
||||
<input type="email" name="EMAIL" id="email" placeholder="" value="{{email}}" readonly>
|
||||
</div>
|
||||
|
||||
<div class="form-group email">
|
||||
<label for="EMAIL_NEW">New Email Address</label>
|
||||
<label for="EMAIL_NEW">{{#translate}}New Email Address{{/translate}}</label>
|
||||
<input type="email" name="EMAIL_NEW" id="email-new" placeholder="Your new email address" value="{{email}}">
|
||||
</div>
|
||||
|
||||
<p>
|
||||
You will receive a confirmation request to your new email address that you need to accept before your email is actually changed
|
||||
{{#translate}}You will receive a confirmation request to your new email address that you need to accept before your email is actually changed{{/translate}}
|
||||
</p>
|
||||
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">Update Email Address</button>
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Update Email Address{{/translate}}</button>
|
||||
|
||||
</form>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
{{> subscription_custom_fields}}
|
||||
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">Update Profile</button>
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Update Profile{{/translate}}</button>
|
||||
</form>
|
||||
|
||||
<script src="/moment/moment.min.js"></script>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
{{> subscription_custom_fields}}
|
||||
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">Subscribe to list</button>
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Subscribe to list{{/translate}}</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
<input type="hidden" name="ucid" value="{{ucid}}">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email address</label>
|
||||
<label for="email">{{#translate}}Email address{{/translate}}</label>
|
||||
<input type="email" name="email" id="email" placeholder="" value="{{email}}" readonly>
|
||||
</div>
|
||||
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">Unsubscribe</button>
|
||||
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Unsubscribe{{/translate}}</button>
|
||||
</form>
|
||||
|
||||
|
|
18
server/views/users/password-reset-html.hbs
Normal file
18
server/views/users/password-reset-html.hbs
Normal file
|
@ -0,0 +1,18 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{{title}}</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<h3>Change your password</h3>
|
||||
|
||||
<p>We have received a password change request for your Mailtrain account: <strong>{{username}}</strong>.</p>
|
||||
|
||||
<p><a href="{{confirmUrl}}" role="button">Reset password</a></p>
|
||||
|
||||
<p>If you did not ask to change your password, then you can ignore this email and your password will not be changed.</p>
|
||||
|
||||
</body>
|
||||
</html>
|
9
server/views/users/password-reset-text.hbs
Normal file
9
server/views/users/password-reset-text.hbs
Normal file
|
@ -0,0 +1,9 @@
|
|||
{{{title}}}
|
||||
Change your password
|
||||
====================
|
||||
|
||||
We have received a password change request for your Mailtrain account: ({{{username}}}).
|
||||
|
||||
Reset password: {{{confirmUrl}}}
|
||||
|
||||
If you did not ask to change your password, then you can ignore this email and your password will not be changed.
|
Loading…
Add table
Add a link
Reference in a new issue