Translation File Management
Seiring aplikasi tumbuh, mengelola file terjemahan bisa jadi rumit. Struktur yang baik sejak awal mencegah chaos di kemudian hari.
Struktur File JSON
locales/
├── en/
│ ├── common.json # Shared: buttons, labels
│ ├── auth.json # Login, register, forgot password
│ ├── dashboard.json # Dashboard-specific
│ ├── errors.json # Error messages
│ └── validation.json # Form validation
├── id/
│ ├── common.json
│ ├── auth.json
│ ├── dashboard.json
│ ├── errors.json
│ └── validation.json
└── ja/
└── ...
Konvensi Penamaan Key
// ✅ GOOD: Hierarchical, descriptive
{
"auth": {
"login": {
"title": "Masuk ke Akun",
"email_label": "Alamat Email",
"password_label": "Kata Sandi",
"submit": "Masuk",
"forgot_password": "Lupa kata sandi?"
},
"register": {
"title": "Buat Akun Baru",
"submit": "Daftar"
}
}
}
// ❌ BAD: Flat, ambiguous
{
"title": "Masuk ke Akun",
"title2": "Buat Akun Baru",
"btn1": "Masuk",
"btn2": "Daftar"
}
Namespace Strategy
// Per-feature namespaces — load hanya yang dibutuhkan
i18n.init({
ns: ["common", "auth", "dashboard"],
defaultNS: "common",
fallbackNS: "common",
});
// Usage
t("save") // common.json (default ns)
t("login.title", { ns: "auth" }) // auth.json
Key Extraction Otomatis
# i18next-parser: scan source code, extract keys
npm install -D i18next-parser
# i18next-parser.config.js
module.exports = {
locales: ["en", "id"],
output: "public/locales/$LOCALE/$NAMESPACE.json",
input: ["src/**/*.{ts,tsx}"],
sort: true,
createOldCatalogs: false,
keySeparator: ".",
namespaceSeparator: ":",
};
# Jalankan
npx i18next-parser
Missing Key Detection
// i18n.js — log missing keys di development
i18n.init({
saveMissing: true,
missingKeyHandler: (lngs, ns, key) => {
console.warn(`🌐 Missing translation: [${lngs}] ${ns}:${key}`);
},
});
// Atau pakai i18next-missing-key-handler plugin
// untuk otomatis mengirim missing keys ke backend/service
Menjaga Konsistensi
// eslint-plugin-i18next — enforce i18n usage
// .eslintrc
{
"plugins": ["i18next"],
"rules": {
"i18next/no-literal-string": "warn" // Warn jika ada hardcoded string
}
}
// Script CI: cek apakah semua locale lengkap
// check-translations.js
const en = require("./locales/en/common.json");
const id = require("./locales/id/common.json");
function findMissing(source, target, path = "") {
for (const key of Object.keys(source)) {
const fullPath = path ? `${path}.${key}` : key;
if (!(key in target)) {
console.error(`Missing in ID: ${fullPath}`);
}
}
}