翻译管理平台集成
i18next-locize-backend
import Locize from 'i18next-locize-backend';
i18next
.use(Locize)
.init({
backend: {
projectId: 'your-project-id',
apiKey: 'your-api-key',
referenceLng: 'en',
version: 'latest'
}
});
Crowdin 集成
// 使用 i18next-http-backend 从 Crowdin 加载
i18next
.use(HttpBackend)
.init({
backend: {
loadPath: 'https://cdn.crowdin.com/api/v2/projects/{{projectId}}/translations/{{lng}}/{{ns}}?apiKey={{apiKey}}',
queryStringParams: {
projectId: 'your-project-id',
apiKey: 'your-api-key'
}
}
});
自动翻译提取
使用 i18next-scanner
npm install --save-dev i18next-scanner
// gulpfile.js
const gulp = require('gulp');
const scanner = require('i18next-scanner');
gulp.task('i18next', function() {
return gulp.src(['src/**/*.{js,jsx,ts,tsx}'])
.pipe(scanner({
lngs: ['en', 'zh', 'fr'],
resource: {
loadPath: 'locales/{{lng}}/{{ns}}.json',
savePath: 'locales/{{lng}}/{{ns}}.json'
},
keySeparator: '.',
nsSeparator: ':',
defaultValue: '__TRANSLATION__'
}))
.pipe(gulp.dest('locales'));
});
使用 Babel 插件
// .babelrc
{
"plugins": [
["i18next-extract", {
"locales": ["en", "zh"],
"outputPath": "locales/{{locale}}/{{ns}}.json",
"keyAsDefaultValue": true
}]
]
}
翻译工作流
开发流程
- 提取翻译键: 使用扫描工具从代码中提取翻译键
- 添加翻译: 在翻译文件中添加或更新翻译
- 提交代码: 将翻译文件提交到版本控制
- 发送翻译: 将需要翻译的内容发送给翻译团队
- 合并翻译: 将翻译好的内容合并回代码库
自动化工作流
// CI/CD 集成示例
const { execSync } = require('child_process');
// 提取翻译
execSync('npm run extract:translations');
// 检查是否有新增的翻译键
const newKeys = checkNewTranslationKeys();
if (newKeys.length > 0) {
console.log('发现新的翻译键:', newKeys);
// 发送通知或创建 issue
notifyTranslationTeam(newKeys);
}
// 运行测试
execSync('npm test');
翻译质量保证
翻译验证
function validateTranslations(translations) {
const errors = [];
Object.keys(translations).forEach(lang => {
const langTranslations = translations[lang];
Object.keys(langTranslations).forEach(key => {
const translation = langTranslations[key];
// 检查是否包含插值占位符
if (translation.includes('{{') && !translation.includes('}}')) {
errors.push(`${lang}.${key}: 未闭合的插值占位符`);
}
// 检查是否包含 HTML 标签
if (/<[^>]*>/g.test(translation)) {
errors.push(`${lang}.${key}: 包含 HTML 标签`);
}
// 检查长度
if (translation.length > 500) {
errors.push(`${lang}.${key}: 翻译过长`);
}
});
});
return errors;
}
翻译覆盖率检查
function checkTranslationCoverage(translations) {
const referenceLang = 'en';
const referenceKeys = Object.keys(translations[referenceLang]);
const coverage = {};
Object.keys(translations).forEach(lang => {
if (lang === referenceLang) return;
const langKeys = Object.keys(translations[lang]);
const missingKeys = referenceKeys.filter(key => !langKeys.includes(key));
coverage[lang] = {
total: referenceKeys.length,
translated: langKeys.length,
missing: missingKeys,
percentage: (langKeys.length / referenceKeys.length) * 100
};
});
return coverage;
}
翻译更新策略
增量更新
async function updateTranslations(newTranslations) {
const currentTranslations = await loadCurrentTranslations();
Object.keys(newTranslations).forEach(lang => {
if (!currentTranslations[lang]) {
currentTranslations[lang] = {};
}
Object.assign(currentTranslations[lang], newTranslations[lang]);
});
await saveTranslations(currentTranslations);
}
版本控制
// 使用 Git 追踪翻译变更
const { execSync } = require('child_process');
function commitTranslations(message) {
execSync('git add locales/');
execSync(`git commit -m "${message}"`);
execSync('git tag translations-v' + Date.now());
}
翻译平台集成
Locize
import locize from 'locize';
import locizeBackend from 'i18next-locize-backend';
const locizeOptions = {
projectId: 'your-project-id',
apiKey: 'your-api-key',
referenceLng: 'en',
version: 'latest'
};
i18next
.use(locizeBackend)
.use(locize.plugin())
.init({
backend: locizeOptions,
locizeLastUsed: locizeOptions,
saveMissing: true,
debug: true
});
PhraseApp
import PhraseBackend from 'i18next-phraseapp-backend';
i18next
.use(PhraseBackend)
.init({
backend: {
projectId: 'your-project-id',
apiKey: 'your-api-key',
version: 'latest'
}
});
最佳实践
- 自动化提取: 使用工具自动提取翻译键
- 版本控制: 将翻译文件纳入版本控制
- 持续集成: 在 CI/CD 中验证翻译质量
- 翻译管理: 使用专业的翻译管理平台
- 质量保证: 实施翻译验证和覆盖率检查
- 团队协作: 建立清晰的翻译工作流程
- 文档维护: 保持翻译文档的更新