Skip to main content

i18n (Internationalization)

Overview

Frontend CLM mendukung Bahasa Indonesia dan English menggunakan i18next.

Setup

// src/i18n.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

const resources = {
id: {
translation: {
// Indonesian translations
"app.name": "Centralize Management System",
"nav.dashboard": "Dashboard",
"nav.search": "Pencarian Log",
"nav.alerting": "Alerting",
"nav.security": "Keamanan",
"nav.compliance": "Kepatuhan",
"nav.masterData": "Master Data",
"nav.infrastructure": "Infrastruktur",
"common.loading": "Memuat...",
"common.error": "Terjadi kesalahan",
"common.success": "Berhasil",
"common.save": "Simpan",
"common.cancel": "Batal",
"common.delete": "Hapus",
"common.edit": "Edit",
"common.search": "Cari",
"common.filter": "Filter",
"common.export": "Ekspor",
"auth.login": "Masuk",
"auth.logout": "Keluar",
"auth.username": "Nama Pengguna",
"auth.password": "Kata Sandi",
"dashboard.pipeline": "Status Pipeline",
"dashboard.datacenter": "Status Data Center",
"dashboard.throughput": "Throughput",
"dashboard.logVolume": "Volume Log",
"search.placeholder": "Cari log...",
"search.timeRange": "Rentang Waktu",
"search.results": "Hasil",
"alert.active": "Alert Aktif",
"alert.rules": "Aturan Alert",
"alert.history": "Riwayat Alert",
},
},
en: {
translation: {
// English translations
"app.name": "Centralize Management System",
"nav.dashboard": "Dashboard",
"nav.search": "Log Search",
"nav.alerting": "Alerting",
"nav.security": "Security",
"nav.compliance": "Compliance",
"nav.masterData": "Master Data",
"nav.infrastructure": "Infrastructure",
"common.loading": "Loading...",
"common.error": "An error occurred",
"common.success": "Success",
"common.save": "Save",
"common.cancel": "Cancel",
"common.delete": "Delete",
"common.edit": "Edit",
"common.search": "Search",
"common.filter": "Filter",
"common.export": "Export",
"auth.login": "Login",
"auth.logout": "Logout",
"auth.username": "Username",
"auth.password": "Password",
"dashboard.pipeline": "Pipeline Status",
"dashboard.datacenter": "Data Center Status",
"dashboard.throughput": "Throughput",
"dashboard.logVolume": "Log Volume",
"search.placeholder": "Search logs...",
"search.timeRange": "Time Range",
"search.results": "Results",
"alert.active": "Active Alerts",
"alert.rules": "Alert Rules",
"alert.history": "Alert History",
},
},
};

i18n.use(initReactI18next).init({
resources,
lng: 'id', // Default language
fallbackLng: 'id',
interpolation: {
escapeValue: false, // React already escapes
},
});

export default i18n;

Usage in Components

useTranslation Hook

import { useTranslation } from 'react-i18next';

function Header() {
const { t } = useTranslation();

return (
<header>
<h1>{t('app.name')}</h1>
<nav>
<a>{t('nav.dashboard')}</a>
<a>{t('nav.search')}</a>
<a>{t('nav.alerting')}</a>
</nav>
</header>
);
}

With Parameters

import { useTranslation } from 'react-i18next';

function WelcomeMessage() {
const { t } = useTranslation();

return (
<div>
{/* Simple parameter */}
<p>{t('welcome.message', { name: 'John' })}</p>

{/* Pluralization */}
<p>{t('items.count', { count: 5 })}</p>
</div>
);
}

Language Switcher

import { useTranslation } from 'react-i18next';

function LanguageSwitcher() {
const { i18n } = useTranslation();

return (
<select
value={i18n.language}
onChange={(e) => i18n.changeLanguage(e.target.value)}
>
<option value="id">Bahasa Indonesia</option>
<option value="en">English</option>
</select>
);
}

Translation Structure

src/
├── i18n.ts # i18next config
├── locales/
│ ├── id.json # Indonesian translations
│ └── en.json # English translations
└── components/
└── LanguageSwitcher.tsx # Language switcher component

Supported Languages

CodeLanguageDefault
idBahasa Indonesia
enEnglish

Best Practices

1. Use Translation Keys Consistently

// ✅ Good: Descriptive keys
t('dashboard.pipeline.status')
t('search.timeRange.last24h')
t('alert.active.count')

// ❌ Bad: Unclear keys
t('text1')
t('label2')
t('btn3')

2. Group Translations by Feature

{
"dashboard": {
"pipeline": { "status": "Pipeline Status" },
"datacenter": { "status": "DC Status" }
},
"search": {
"placeholder": "Search logs...",
"timeRange": "Time Range"
},
"alert": {
"active": "Active Alerts",
"rules": "Alert Rules"
}
}

3. Use Interpolation for Dynamic Content

// ✅ Good: Interpolation
t('welcome.user', { username: userName })
t('items.found', { count: results.length })

// ❌ Bad: String concatenation
`Welcome, ${userName}`
`${results.length} items found`

4. Handle Missing Translations

i18n.init({
fallbackLng: 'id',
saveMissing: true,
missingKeyHandler: (lng, ns, key) => {
console.warn(`Missing translation: ${ns}:${key} [${lng}]`);
},
});

Next Steps