Files
wekan/server/lib/emailLocalization.js
T
Lauri OjansivuandClaude Opus 4.8 22f996903b fix: notification emails ignored the user's language and were sent in English
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>
2026-06-23 15:37:56 +03:00

63 lines
2.2 KiB
JavaScript

// emailLocalization.js
// Utility functions to handle email localization in Wekan
import { TAPi18n } from '/imports/i18n';
import { ReactiveCache } from '/imports/reactiveCache';
// Main object for email localization utilities
const EmailLocalization = {
/**
* Send an email using the recipient's preferred language
* @param {Object} options - Standard email sending options plus language options
* @param {String} options.to - Recipient email address
* @param {String} options.from - Sender email address
* @param {String} options.subject - Email subject i18n key
* @param {String} options.text - Email text i18n key
* @param {Object} options.params - Parameters for i18n translation
* @param {String} options.language - Language code to use (if not provided, will try to detect)
* @param {String} options.userId - User ID to determine language (if not provided with language)
*/
async sendEmail(options) {
// Determine the language to use
let lang = options.language;
// If no language is specified but we have a userId, try to get the user's language
if (!lang && options.userId) {
const user = await ReactiveCache.getUser(options.userId);
if (user) {
lang = user.getLanguage();
}
}
// If no language could be determined, use the site default
if (!lang) {
lang = TAPi18n.getLanguage() || 'en';
}
// #5875: ensure the chosen language's bundle is loaded on the server before
// translating, otherwise __() falls back to English.
await TAPi18n.ensureLanguageLoaded(lang);
// Translate subject and text using the determined language
const subject = TAPi18n.__(options.subject, options.params || {}, lang);
let text = options.text;
// If text is an i18n key, translate it
if (typeof text === 'string' && text.startsWith('email-')) {
text = TAPi18n.__(text, options.params || {}, lang);
}
// Send the email with translated content
return await Email.sendAsync({
to: options.to,
from: options.from || Accounts.emailTemplates.from,
subject: subject,
text: text,
html: options.html
});
}
};
// Add module.exports to make it accessible from other files
export default EmailLocalization;