Merge pull request #5769 from Adamsss001/feature/email-i18n

Add email notifications language localization feature
This commit is contained in:
Lauri Ojansivu 2025-05-13 21:50:53 +03:00 committed by GitHub
commit d65c422e3f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 142 additions and 118 deletions

View file

@ -275,22 +275,18 @@ if (Meteor.isServer) {
url: FlowRouter.url('sign-up'),
};
const lang = author.getLanguage();
/*
if (process.env.MAIL_SERVICE !== '') {
let transporter = nodemailer.createTransport({
service: process.env.MAIL_SERVICE,
auth: {
user: process.env.MAIL_SERVICE_USER,
pass: process.env.MAIL_SERVICE_PASSWORD
},
})
let info = transporter.sendMail({
// Use EmailLocalization utility to handle email in the proper language
if (typeof EmailLocalization !== 'undefined') {
EmailLocalization.sendEmail({
to: icode.email,
from: Accounts.emailTemplates.from,
subject: TAPi18n.__('email-invite-register-subject', params, lang),
text: TAPi18n.__('email-invite-register-text', params, lang),
})
subject: 'email-invite-register-subject',
text: 'email-invite-register-text',
params: params,
language: lang
});
} else {
// Fallback if EmailLocalization is not available
Email.send({
to: icode.email,
from: Accounts.emailTemplates.from,
@ -298,13 +294,6 @@ if (Meteor.isServer) {
text: TAPi18n.__('email-invite-register-text', params, lang),
});
}
*/
Email.send({
to: icode.email,
from: Accounts.emailTemplates.from,
subject: TAPi18n.__('email-invite-register-subject', params, lang),
text: TAPi18n.__('email-invite-register-text', params, lang),
});
} catch (e) {
InvitationCodes.remove(_id);
throw new Meteor.Error('email-fail', e.message);

View file

@ -1618,9 +1618,7 @@ if (Meteor.isServer) {
subBoard.addMember(user._id);
user.addInvite(subBoard._id);
}
}
try {
} try {
const fullName =
inviter.profile !== undefined &&
inviter.profile.fullname !== undefined
@ -1642,38 +1640,29 @@ if (Meteor.isServer) {
board: board.title,
url: board.absoluteUrl(),
};
// Get the recipient user's language preference for the email
const lang = user.getLanguage();
/*
if (process.env.MAIL_SERVICE !== '') {
let transporter = nodemailer.createTransport({
service: process.env.MAIL_SERVICE,
auth: {
user: process.env.MAIL_SERVICE_USER,
pass: process.env.MAIL_SERVICE_PASSWORD
},
})
let info = transporter.sendMail({
to: user.emails[0].address.toLowerCase(),
// Add code to send invitation with EmailLocalization
if (typeof EmailLocalization !== 'undefined') {
EmailLocalization.sendEmail({
to: user.emails[0].address,
from: Accounts.emailTemplates.from,
subject: TAPi18n.__('email-invite-subject', params, lang),
text: TAPi18n.__('email-invite-text', params, lang),
})
subject: 'email-invite-subject',
text: 'email-invite-text',
params: params,
language: lang,
userId: user._id
});
} else {
// Fallback if EmailLocalization is not available
Email.send({
to: user.emails[0].address.toLowerCase(),
to: user.emails[0].address,
from: Accounts.emailTemplates.from,
subject: TAPi18n.__('email-invite-subject', params, lang),
text: TAPi18n.__('email-invite-text', params, lang),
});
}
*/
Email.send({
to: user.emails[0].address.toLowerCase(),
from: Accounts.emailTemplates.from,
subject: TAPi18n.__('email-invite-subject', params, lang),
text: TAPi18n.__('email-invite-text', params, lang),
});
} catch (e) {
throw new Meteor.Error('email-fail', e.message);
}

View file

@ -0,0 +1,58 @@
// 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
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)
*/
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 = 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';
}
// 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 Email.send({
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
module.exports = EmailLocalization;

4
server/lib/importer.js Normal file
View file

@ -0,0 +1,4 @@
// This file ensures the EmailLocalization utility is imported
// and available throughout the application
import './emailLocalization';

View file

@ -2,6 +2,8 @@ import { ReactiveCache } from '/imports/reactiveCache';
import { TAPi18n } from '/imports/i18n';
//var nodemailer = require('nodemailer');
import EmailLocalization from '../lib/emailLocalization';
// buffer each user's email text in a queue, then flush them in single email
Meteor.startup(() => {
Notifications.subscribe('email', (user, title, description, params) => {
@ -14,6 +16,7 @@ Meteor.startup(() => {
quoteParams[key] = quoteParams[key] ? `${params[key]}` : '';
});
// Get user's preferred language
const lan = user.getLanguage();
const subject = TAPi18n.__(title, params, lan); // the original function has a fault, i believe the title should be used according to original author
const existing = user.getEmailBuffer().length > 0;
@ -42,35 +45,14 @@ Meteor.startup(() => {
const html = texts.join('<br/>\n\n');
user.clearEmailBuffer();
try {
/*
if (process.env.MAIL_SERVICE !== '') {
let transporter = nodemailer.createTransport({
service: process.env.MAIL_SERVICE,
auth: {
user: process.env.MAIL_SERVICE_USER,
pass: process.env.MAIL_SERVICE_PASSWORD
},
})
let info = transporter.sendMail({
to: user.emails[0].address.toLowerCase(),
from: Accounts.emailTemplates.from,
subject,
html,
})
} else {
Email.send({
to: user.emails[0].address.toLowerCase(),
from: Accounts.emailTemplates.from,
subject,
html,
});
}
*/
Email.send({
// Use EmailLocalization utility to ensure the correct language is used
EmailLocalization.sendEmail({
to: user.emails[0].address.toLowerCase(),
from: Accounts.emailTemplates.from,
subject,
html,
language: user.getLanguage(),
userId: user._id
});
} catch (e) {
return;

View file

@ -125,22 +125,31 @@ RulesHelper = {
const text = action.emailMsg || '';
const subject = action.emailSubject || '';
try {
/*
if (process.env.MAIL_SERVICE !== '') {
let transporter = nodemailer.createTransport({
service: process.env.MAIL_SERVICE,
auth: {
user: process.env.MAIL_SERVICE_USER,
pass: process.env.MAIL_SERVICE_PASSWORD
},
})
let info = transporter.sendMail({
// Try to detect the recipient's language preference if it's a Wekan user
// Otherwise, use the default language for the rule-triggered emails
let recipientUser = null;
let recipientLang = TAPi18n.getLanguage() || 'en';
// Check if recipient is a Wekan user to get their language
if (to && to.includes('@')) {
recipientUser = ReactiveCache.getUser({ 'emails.address': to.toLowerCase() });
if (recipientUser && typeof recipientUser.getLanguage === 'function') {
recipientLang = recipientUser.getLanguage();
}
}
// Use EmailLocalization if available
if (typeof EmailLocalization !== 'undefined') {
EmailLocalization.sendEmail({
to,
from: Accounts.emailTemplates.from,
subject,
text,
})
language: recipientLang,
userId: recipientUser ? recipientUser._id : null
});
} else {
// Fallback to standard Email.send
Email.send({
to,
from: Accounts.emailTemplates.from,
@ -148,13 +157,6 @@ RulesHelper = {
text,
});
}
*/
Email.send({
to,
from: Accounts.emailTemplates.from,
subject,
text,
});
} catch (e) {
// eslint-disable-next-line no-console
console.error(e);