add i18next-cli commands for extraction and status

also add false positives removal for several keys
This commit is contained in:
Josh Hawkins 2026-03-23 08:15:23 -05:00
parent 5371eada03
commit 0137569186
3 changed files with 1176 additions and 125 deletions

50
web/i18next.config.ts Normal file
View File

@ -0,0 +1,50 @@
import { defineConfig, type Plugin } from "i18next-cli";
/**
* Plugin to remove false positive keys generated by dynamic namespace patterns
* like useTranslation([i18nLibrary]) and t("key", { ns: configNamespace }).
* These keys already exist in their correct runtime namespaces.
*/
function ignoreDynamicNamespaceKeys(): Plugin {
// Keys that the extractor misattributes to the wrong namespace
// because it can't resolve dynamic ns values at build time.
const falsePositiveKeys = new Set([
// From useTranslation([i18nLibrary]) in ClassificationCard.tsx
// Already in views/classificationModel and views/faceLibrary
"details.unknown",
"details.none",
// From t("key", { ns: configNamespace }) in DetectorHardwareField.tsx
// Already in config/global
"detectors.type.label",
// From t(`${prefix}`) template literals producing empty/partial keys
"",
"_one",
"_other",
]);
return {
name: "ignore-dynamic-namespace-keys",
onEnd: async (keys) => {
for (const key of keys.keys()) {
// Each map key is "ns:actualKey" format
const separatorIndex = key.indexOf(":");
const actualKey = separatorIndex >= 0 ? key.slice(separatorIndex + 1) : key;
if (falsePositiveKeys.has(actualKey)) {
keys.delete(key);
}
}
},
};
}
export default defineConfig({
locales: ["en"],
extract: {
input: ["src/**/*.{ts,tsx}"],
output: "public/locales/{{language}}/{{namespace}}.json",
defaultNS: "common",
removeUnusedKeys: false,
sort: false,
},
plugins: [ignoreDynamicNamespaceKeys()],
});

1245
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,10 @@
"preview": "vite preview",
"prettier:write": "prettier -u -w --ignore-path .gitignore \"*.{ts,tsx,js,jsx,css,html}\"",
"test": "vitest",
"coverage": "vitest run --coverage"
"coverage": "vitest run --coverage",
"i18n:extract": "i18next-cli extract",
"i18n:extract:ci": "i18next-cli extract --ci",
"i18n:status": "i18next-cli status"
},
"dependencies": {
"@cycjimmy/jsmpeg-player": "^6.1.2",
@ -114,6 +117,7 @@
"eslint-plugin-react-refresh": "^0.4.8",
"eslint-plugin-vitest-globals": "^1.5.0",
"fake-indexeddb": "^6.0.0",
"i18next-cli": "^1.5.11",
"jest-websocket-mock": "^2.5.0",
"jsdom": "^24.1.1",
"monaco-editor": "^0.52.2",