Translation File Management — i18n

Translation File Management Seiring aplikasi tumbuh, mengelola file terjemahan bisa jadi rumit. Struktur yang baik sejak awal mencegah chaos di kemudian hari…

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}`);
    }
  }
}

Yang akan kamu pelajari