Merge pull request #3274 from xinnan-tech/i18n-duplicate-check

test: 增加 i18n 重复 key 检测
This commit is contained in:
hrz
2026-07-10 09:59:33 +08:00
committed by GitHub
2 changed files with 97 additions and 0 deletions
+1
View File
@@ -4,6 +4,7 @@
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"check:i18n": "node scripts/check-i18n.js",
"build": "vue-cli-service build",
"analyze": "cross-env ANALYZE=true vue-cli-service build"
},
+96
View File
@@ -0,0 +1,96 @@
const fs = require('fs');
const path = require('path');
const rootDir = path.resolve(__dirname, '..');
const locales = [
{ name: 'zh_CN', file: 'src/i18n/zh_CN.js' },
{ name: 'zh_TW', file: 'src/i18n/zh_TW.js' },
{ name: 'en', file: 'src/i18n/en.js' },
{ name: 'de', file: 'src/i18n/de.js' },
{ name: 'vi', file: 'src/i18n/vi.js' },
{ name: 'pt_BR', file: 'src/i18n/pt_BR.js' },
];
function readLocale(locale) {
const filePath = path.join(rootDir, locale.file);
const lines = fs.readFileSync(filePath, 'utf8').split(/\r?\n/);
const keys = [];
const seen = new Map();
const duplicates = [];
lines.forEach((line, index) => {
const match = line.match(/^\s*['"]([^'"]+)['"]\s*:/);
if (!match) {
return;
}
const key = match[1];
keys.push(key);
if (!seen.has(key)) {
seen.set(key, []);
}
seen.get(key).push(index + 1);
});
seen.forEach((lineNumbers, key) => {
if (lineNumbers.length > 1) {
duplicates.push({ key, lineNumbers });
}
});
return {
...locale,
filePath,
keys,
keySet: new Set(keys),
duplicates,
};
}
function diffKeys(left, right) {
return [...left].filter((key) => !right.has(key)).sort();
}
function formatKeyList(keys) {
return keys.length ? keys.map((key) => ` - ${key}`).join('\n') : '';
}
function main() {
const results = locales.map(readLocale);
const base = results.find((locale) => locale.name === 'zh_CN');
const errors = [];
results.forEach((locale) => {
locale.duplicates.forEach(({ key, lineNumbers }) => {
errors.push(
`${locale.file} has duplicate key "${key}" on lines ${lineNumbers.join(', ')}`
);
});
});
results
.filter((locale) => locale !== base)
.forEach((locale) => {
const missing = diffKeys(base.keySet, locale.keySet);
const extra = diffKeys(locale.keySet, base.keySet);
if (missing.length) {
errors.push(`${locale.file} is missing keys from ${base.file}:\n${formatKeyList(missing)}`);
}
if (extra.length) {
errors.push(`${locale.file} has keys that do not exist in ${base.file}:\n${formatKeyList(extra)}`);
}
});
if (errors.length) {
console.error(`i18n check failed with ${errors.length} issue(s):`);
console.error(errors.join('\n\n'));
process.exit(1);
}
const keyCount = base.keySet.size;
console.log(`i18n check passed: ${results.length} locale files, ${keyCount} keys each.`);
}
main();