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 Integration
// load from Crowdin using i18next-http-backend
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'
}
}
});
Using 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'));
});
Using Babel Plugin
// .babelrc
{
"plugins": [
["i18next-extract", {
"locales": ["en", "zh"],
"outputPath": "locales/{{locale}}/{{ns}}.json",
"keyAsDefaultValue": true
}]
]
}
Translation Workflow
Development Process
- Extract Translation Keys: Use scanning tools to extract translation keys from code
- Add Translations: Add or update translations in translation files
- Commit Code: Commit translation files to version control
- Send for Translation: Send content needing translation to translation team
- Merge Translations: Merge translated content back into codebase
Automated Workflow
// CI/CD integration example
const { execSync } = require('child_process');
// extract translations
execSync('npm run extract:translations');
// check for new translation keys
const newKeys = checkNewTranslationKeys();
if (newKeys.length > 0) {
console.log('New translation keys found:', newKeys);
// send notification or create issue
notifyTranslationTeam(newKeys);
}
// run tests
execSync('npm test');
Translation Quality Assurance
Translation Validation
function validateTranslations(translations) {
const errors = [];
Object.keys(translations).forEach(lang => {
const langTranslations = translations[lang];
Object.keys(langTranslations).forEach(key => {
const translation = langTranslations[key];
// check for unclosed interpolation placeholders
if (translation.includes('{{') && !translation.includes('}}')) {
errors.push(`${lang}.${key}: Unclosed interpolation placeholder`);
}
// check for HTML tags
if (/<[^>]*>/g.test(translation)) {
errors.push(`${lang}.${key}: Contains HTML tags`);
}
// check length
if (translation.length > 500) {
errors.push(`${lang}.${key}: Translation too long`);
}
});
});
return errors;
}
Translation Coverage Check
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;
}
Translation Update Strategy
Incremental Updates
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);
}
Version Control
// track translation changes using 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'
}
});
Best Practices
- Automated Extraction: Use tools to automatically extract translation keys
- Version Control: Include translation files in version control
- Continuous Integration: Validate translation quality in CI/CD
- Translation Management: Use professional translation management platforms
- Quality Assurance: Implement translation validation and coverage checks
- Team Collaboration: Establish clear translation workflows
- Documentation Maintenance: Keep translation documentation updated