如何在Express Node.js控制器中实现同步翻译助手?
嘿,这个需求我太懂了——在Express控制器里搞同步翻译,避开烦人的异步嵌套,还要给AJAX请求返回结构化的JSON错误信息,对吧?我给你几个实用的方案,都是我实际项目里用过的:
方案1:预加载翻译文件到内存(同步读取)
直接在启动时用Node.js的fs.readFileSync把所有翻译JSON文件同步读到内存里,之后控制器里就能直接同步获取翻译值,完全没异步问题。
先整个翻译模块translations.js:
const fs = require('fs'); const path = require('path'); // 启动时一次性同步加载所有语言的翻译文件 const translations = { en: JSON.parse(fs.readFileSync(path.join(__dirname, 'locales/en.json'), 'utf8')), es: JSON.parse(fs.readFileSync(path.join(__dirname, 'locales/es.json'), 'utf8')) }; // 同步翻译函数,支持嵌套key(比如'errors.email.invalid') function translate(key, lang = 'en') { return key.split('.').reduce((current, keyPart) => current?.[keyPart], translations[lang]) || key; } module.exports = translate;
然后在控制器里直接用:
const translate = require('./translations'); exports.handleAjaxError = (req, res) => { // 从请求里拿当前语言(比如session、headers或者query参数) const userLang = req.session.lang || req.headers['accept-language']?.split(',')[0] || 'en'; // 一次性获取多个翻译值,完全同步 const errorMessages = { emailInvalid: translate('errors.email.invalid', userLang), passwordShort: translate('errors.password.short', userLang), authFailed: translate('errors.auth.failed', userLang) }; res.status(400).json({ errors: errorMessages }); };
方案2:利用Node.js的require同步加载翻译模块
Node.js的require本身就是同步的,还会自动缓存模块,所以可以把每个翻译文件做成JS模块,直接require进来,比手动读JSON更简洁。
比如locales/en.js:
module.exports = { errors: { email: { invalid: 'Invalid email address' }, password: { short: 'Password must be at least 8 characters' }, auth: { failed: 'Authentication failed' } } };
然后翻译模块translations.js:
// 同步加载各个语言的翻译模块 const translations = { en: require('./locales/en'), es: require('./locales/es') }; function translate(key, lang = 'en') { return key.split('.').reduce((current, keyPart) => current?.[keyPart], translations[lang]) || key; } module.exports = translate;
控制器里的用法和方案1完全一样,这个方案更适合翻译内容有复杂逻辑的场景(比如翻译值是函数?不过一般很少用),而且不用手动解析JSON,Node.js会帮你处理。
方案3:用成熟的i18n库实现同步翻译
如果项目已经在用i18n相关的库,比如i18n这个npm包,它本身就支持同步翻译,只要配置时开启同步加载就行。
先安装并配置:
const i18n = require('i18n'); const express = require('express'); const app = express(); i18n.configure({ locales: ['en', 'es'], directory: __dirname + '/locales', // 翻译文件放在这里,格式是en.json、es.json syncFiles: true, // 关键:同步加载翻译文件 defaultLocale: 'en', register: global // 可选,把翻译方法挂到全局,方便使用 }); // 把i18n挂载到Express请求对象上 app.use(i18n.init);
然后控制器里直接用同步方法:
exports.handleAjaxError = (req, res) => { // 设置当前语言(从session、headers等地方取) req.setLocale(req.session.lang || 'en'); // 用res.__()或者i18n.__()同步获取翻译值 const errorMessages = { emailInvalid: res.__('errors.email.invalid'), passwordShort: res.__('errors.password.short'), authFailed: i18n.__('errors.auth.failed') }; res.status(400).json({ errors: errorMessages }); };
几个关键注意点
- 预加载/同步加载的好处是启动时加载一次,之后全是内存读取,速度极快,完全没有异步嵌套的问题,非常适合AJAX错误返回这种场景。
- 如果翻译文件特别大,启动时加载可能会稍微慢一点,但对于绝大多数Web应用来说,这个代价完全可以接受。
- 嵌套key的处理:用
split('.')加reduce的方法可以轻松解析像errors.email.invalid这种点分隔的嵌套键,不用写一堆translations.lang.errors.email.invalid的链式调用。 - 如果需要动态更新翻译(比如不用重启服务器),可以加个定时任务,每隔一段时间用
fs.readFileSync重新加载翻译文件,但如果没有这个需求,预加载就足够了。
内容的提问来源于stack exchange,提问作者Darko Mitic




