mirror of
https://github.com/wekan/wekan.git
synced 2026-11-08 11:25:19 -05:00
On the server only the default (English) translation bundle is loaded at startup. Translating a notification to a user's language called TAPi18n.__(key, params, lang) with lng=lang, but that resource bundle was never added on the server, so i18next fell back to English — every notification email went out in English regardless of the user's profile language. (The client works because switching language calls setLanguage, which loads the bundle.) Add TAPi18n.ensureLanguageLoaded(language), which loads the bundle on demand if i18next does not already have it, and await it before translating in the email notification, EmailLocalization.sendEmail and outgoing webhook paths. Closes #5875 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
84 lines
3.0 KiB
JavaScript
84 lines
3.0 KiB
JavaScript
import { ReactiveCache } from '/imports/reactiveCache';
|
|
import { TAPi18n } from '/imports/i18n';
|
|
//var nodemailer = require('nodemailer');
|
|
|
|
import EmailLocalization from '../lib/emailLocalization';
|
|
import { Notifications } from '/server/notifications/notifications';
|
|
|
|
// buffer each user's email text in a queue, then flush them in single email
|
|
Meteor.startup(() => {
|
|
Notifications.subscribe('email', async (user, title, description, params) => {
|
|
try {
|
|
if (
|
|
!user ||
|
|
typeof user.getLanguage !== 'function' ||
|
|
typeof user.getEmailBuffer !== 'function' ||
|
|
typeof user.addEmailBuffer !== 'function'
|
|
) {
|
|
console.error('Invalid user helper surface for email notification:', user?._id);
|
|
return;
|
|
}
|
|
|
|
// #5875: the server only loads the English bundle at startup, so make sure
|
|
// the recipient's language is loaded before translating the subject/body —
|
|
// otherwise notification emails silently fall back to English.
|
|
await TAPi18n.ensureLanguageLoaded(user.getLanguage());
|
|
|
|
// add quote to make titles easier to read in email text
|
|
const quoteParams = { ...params };
|
|
['card', 'list', 'oldList', 'board', 'comment'].forEach(key => {
|
|
if (quoteParams[key]) quoteParams[key] = `"${params[key]}"`;
|
|
});
|
|
['timeValue', 'timeOldValue'].forEach(key => {
|
|
quoteParams[key] = quoteParams[key] ? `${params[key]}` : '';
|
|
});
|
|
|
|
const lan = user.getLanguage();
|
|
const subject = TAPi18n.__(title, params, lan);
|
|
const existing = user.getEmailBuffer().length > 0;
|
|
const htmlEnabled =
|
|
Meteor.settings.public &&
|
|
Meteor.settings.public.RICHER_CARD_COMMENT_EDITOR !== false;
|
|
const actorName = params.user || '';
|
|
const text = `${existing ? `\n${subject}\n` : ''}${
|
|
actorName
|
|
} ${TAPi18n.__(description, quoteParams, lan)}\n${params.url}`;
|
|
|
|
user.addEmailBuffer(htmlEnabled ? text.replace(/\n/g, '<br/>') : text);
|
|
|
|
const userId = user._id;
|
|
Meteor.setTimeout(async () => {
|
|
const user = await ReactiveCache.getUser(userId);
|
|
if (
|
|
!user ||
|
|
typeof user.getEmailBuffer !== 'function' ||
|
|
typeof user.clearEmailBuffer !== 'function' ||
|
|
typeof user.getLanguage !== 'function'
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const texts = user.getEmailBuffer();
|
|
if (texts.length === 0) return;
|
|
|
|
const html = texts.join('<br/>\n\n');
|
|
user.clearEmailBuffer();
|
|
try {
|
|
await EmailLocalization.sendEmail({
|
|
to: user.emails[0].address.toLowerCase(),
|
|
from: Accounts.emailTemplates.from,
|
|
subject,
|
|
html,
|
|
language: user.getLanguage(),
|
|
userId: user._id
|
|
});
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
}, process.env.EMAIL_NOTIFICATION_TIMEOUT || 30000);
|
|
} catch (error) {
|
|
console.error('Error preparing email notification:', error);
|
|
}
|
|
});
|
|
});
|