您现在的位置是:网站首页 > 国际化与本地化实现文章详情
国际化与本地化实现
陈川
【
Node.js
】
44324人已围观
6103字
国际化与本地化是现代Web应用开发中不可或缺的一部分,尤其是在Express框架中,通过合理的架构设计和工具支持,可以轻松实现多语言支持。无论是面向全球用户还是特定地区,良好的国际化方案能提升用户体验,而本地化则确保内容符合当地文化习惯。
国际化基础概念
国际化(i18n)是指设计软件时使其能够适应不同语言和地区的过程。在Express中,通常通过中间件和语言资源文件来实现。核心要素包括:
- 语言资源管理:将不同语言的文本存储在JSON或YAML文件中
- 动态语言切换:根据用户请求自动切换语言
- 日期/数字格式化:根据不同地区习惯显示格式
一个典型的项目结构可能如下:
locales/
├── en/
│ ├── common.json
│ └── errors.json
├── zh/
│ ├── common.json
│ └── errors.json
└── ja/
├── common.json
└── errors.json
Express中的i18n实现
使用i18next
和i18next-http-middleware
可以快速搭建国际化系统。首先安装依赖:
npm install i18next i18next-http-middleware
然后配置中间件:
const i18next = require('i18next');
const middleware = require('i18next-http-middleware');
i18next.use(middleware.LanguageDetector).init({
preload: ['en', 'zh'],
ns: ['common', 'errors'],
defaultNS: 'common',
backend: {
loadPath: __dirname + '/locales/{{lng}}/{{ns}}.json'
}
});
app.use(middleware.handle(i18next));
语言资源文件示例(locales/en/common.json):
{
"welcome": "Welcome to our service",
"login": {
"title": "Login to your account",
"button": "Sign in"
}
}
动态内容本地化
在路由处理中使用翻译功能:
app.get('/', (req, res) => {
const greeting = req.t('welcome');
res.send(`
<h1>${greeting}</h1>
<p>${req.t('login.title')}</p>
`);
});
对于复数形式和插值:
{
"itemCount": "You have {{count}} item",
"itemCount_plural": "You have {{count}} items"
}
const message = req.t('itemCount', { count: 5 });
日期与数字本地化
使用luxon
处理日期本地化:
const { DateTime } = require('luxon');
app.get('/events', (req, res) => {
const userLocale = req.language;
const eventDate = DateTime.now()
.setLocale(userLocale)
.toLocaleString(DateTime.DATE_FULL);
res.send(`Next event: ${eventDate}`);
});
数字格式化示例:
const number = 123456.789;
new Intl.NumberFormat(req.language).format(number);
// 英语: "123,456.789"
// 德语: "123.456,789"
高级本地化策略
按域名区分语言
app.use((req, res, next) => {
if(req.hostname.endsWith('.cn')) {
req.language = 'zh';
} else if(req.hostname.endsWith('.jp')) {
req.language = 'ja';
}
next();
});
用户偏好存储
结合数据库存储用户语言偏好:
app.post('/preferences', async (req, res) => {
await User.updateOne(
{ _id: req.user.id },
{ $set: { language: req.body.language } }
);
res.cookie('i18next', req.body.language);
res.sendStatus(200);
});
测试与验证
编写中间件测试语言切换:
const request = require('supertest');
const app = require('../app');
describe('i18n Middleware', () => {
it('should respond in English by default', async () => {
const res = await request(app)
.get('/')
.set('Accept-Language', 'en');
expect(res.text).toContain('Welcome');
});
it('should switch to Chinese with cookie', async () => {
const res = await request(app)
.get('/')
.set('Cookie', ['i18next=zh']);
expect(res.text).toContain('欢迎');
});
});
性能优化
使用内存缓存提升翻译加载速度:
const NodeCache = require('node-cache');
const translationCache = new NodeCache({ stdTTL: 3600 });
app.use(async (req, res, next) => {
const cacheKey = `${req.language}-${req.i18n.options.defaultNS}`;
let resources = translationCache.get(cacheKey);
if(!resources) {
resources = await req.i18n.services.backendConnector.load([req.language], [req.i18n.options.defaultNS]);
translationCache.set(cacheKey, resources);
}
next();
});
错误处理与回退
实现健壮的语言回退机制:
i18next.init({
fallbackLng: {
'zh-CN': ['zh', 'en'],
'zh-TW': ['zh', 'en'],
default: ['en']
},
// 其他配置...
});
// 自定义缺失键处理
i18next.on('missingKey', (lngs, namespace, key) => {
logger.warn(`Missing translation: ${key} for ${lngs}`);
});
前端集成方案
通过API提供翻译资源给前端:
app.get('/locales/:lng/:ns', (req, res) => {
const { lng, ns } = req.params;
const resources = i18next.getResourceBundle(lng, ns);
res.json(resources);
});
前端动态加载示例:
async function loadTranslations(lang) {
const response = await fetch(`/locales/${lang}/common`);
return response.json();
}
// 使用示例
const translations = await loadTranslations('zh');
document.getElementById('title').textContent = translations.welcome;
内容方向处理
针对RTL语言(如阿拉伯语)的特殊处理:
app.use((req, res, next) => {
const rtlLangs = ['ar', 'he', 'fa'];
res.locals.textDirection = rtlLangs.includes(req.language) ? 'rtl' : 'ltr';
next();
});
// 在模板中使用
<html dir="{{textDirection}}">
自动化工作流
集成CI/CD实现翻译同步:
# .github/workflows/translations.yml
name: Sync Translations
on:
push:
paths:
- 'locales/**'
jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm run extract-translations
- run: npm run upload-translations
- run: npm run download-translations
实际应用场景
电商网站的多语言产品描述:
// 产品模型
const productSchema = new Schema({
name: {
en: String,
zh: String,
ja: String
},
description: {
en: String,
zh: String,
ja: String
}
});
// 查询时根据语言返回对应字段
app.get('/products/:id', async (req, res) => {
const product = await Product.findById(req.params.id)
.select(`name.${req.language} description.${req.language}`);
res.json({
name: product.name[req.language],
description: product.description[req.language]
});
});
持续维护策略
建立翻译记忆系统:
// 翻译记忆模型
const translationMemorySchema = new Schema({
sourceText: { type: String, index: true },
sourceLang: String,
translations: [{
lang: String,
text: String,
approved: Boolean
}]
});
// 自动建议已有翻译
app.post('/translate', async (req, res) => {
const { text, from, to } = req.body;
const existing = await TranslationMemory.findOne({
sourceText: text,
sourceLang: from,
'translations.lang': to
});
if(existing) {
const translation = existing.translations.find(t => t.lang === to);
return res.json({ translation: translation.text });
}
// 调用翻译API...
});