Pusat Bantuan

Angular i18next

Internasionalisasi (i18n) sangat penting untuk membangun aplikasi Angular global. Panduan ini menunjukkan cara mengimplementasikan i18next di Angular dengan pemuatan sumber daya, inisialisasi, dan pola penggunaan yang tepat.

Mengapa i18next untuk Angular?

i18next adalah salah satu framework i18n paling populer, menawarkan fitur canggih dan fleksibilitas:

  • Framework agnostic - bekerja dengan mulus di Angular, React, Vue, dan lainnya.
  • Interpolasi, pluralisasi, dan dukungan konteks yang kuat.
  • Dukungan lazy loading dan pemisahan kode (code splitting).
  • Dukungan TypeScript dengan keamanan tipe.
  • Ekosistem besar dengan plugin dan ekstensi.

Instalasi

Instal i18next menggunakan npm atau yarn:

npm install i18next

Selesai! i18next adalah pustaka mandiri tanpa dependensi khusus framework. Anda tidak memerlukan angular-i18next atau wrapper lainnya - cukup gunakan i18next secara langsung di aplikasi Angular Anda.

Memuat Sumber Daya Terjemahan

Ada dua pendekatan utama untuk memuat sumber daya terjemahan di Angular dengan i18next:

Impor Dinamis (Direkomendasikan)

Menggunakan impor dinamis memanfaatkan pemisahan kode Webpack untuk memuat terjemahan sesuai permintaan. Pendekatan ini ideal untuk aplikasi dengan banyak bahasa atau file terjemahan yang besar.

Keunggulan:

  • Optimalisasi ukuran bundle - hanya memuat terjemahan yang diperlukan.
  • Performa lebih baik - waktu muat awal lebih cepat.
  • Bekerja dengan sempurna dengan SSR dan prerendering.
  • Tidak memerlukan permintaan HTTP tambahan.

Contoh: Memuat dengan Impor Dinamis

private async loadLanguageResources(
  lang: string,
): Promise<Record<string, Record<string, unknown>>> {
  const languageResources: Record<string, Record<string, unknown>> = {};

  // Load all namespaces for the given language in parallel
  await Promise.all(
    NAMESPACES.map(async (namespace) => {
      const translationModule = await this.loadJson(lang, namespace);
      languageResources[namespace] = translationModule.default;
    }),
  );
  return languageResources;
}

private async loadJson(
  language: string,
  namespace: string,
): Promise<{ default: Record<string, unknown> }> {
  // Webpack automatically code-splits this into separate chunks
  return import(`../../i18n/${language}/${namespace}.json`);
}

Pendekatan ini mengimpor file terjemahan sebagai modul ES, memungkinkan Webpack untuk secara otomatis membagi kode menjadi bagian-bagian terpisah yang dimuat sesuai permintaan.

i18next-http-backend

Plugin backend HTTP memuat terjemahan melalui permintaan HTTP dari server atau CDN. Ini berguna saat Anda ingin memperbarui terjemahan tanpa membangun ulang aplikasi Anda.

Keunggulan:

  • Memperbarui terjemahan tanpa redeployment.
  • Memuat terjemahan dari CDN eksternal.
  • Berguna untuk manajemen terjemahan runtime.

Kekurangan:

  • Permintaan HTTP tambahan memperlambat waktu muat awal.
  • Memerlukan konektivitas jaringan.
  • Pengaturan SSR yang lebih kompleks.

Contoh: Memuat dengan Backend HTTP

import HttpBackend from 'i18next-http-backend';

await init({
  lng: initialLanguage,
  fallbackLng: DEFAULT_LANGUAGE,
  backend: {
    loadPath: '/assets/i18n/{{lng}}/{{ns}}.json',
  },
  use: [HttpBackend],
  ns: NAMESPACES,
  defaultNS: DEFAULT_NAMESPACE,
});

Pendekatan ini mengambil file terjemahan dari server saat runtime. Perhatikan bahwa ini memerlukan konfigurasi tambahan untuk SSR.

Pendekatan Mana yang Harus Dipilih?

Gunakan Impor Dinamis saat:

  • Anda menginginkan ukuran bundle dan performa yang optimal.
  • Terjemahan adalah bagian dari proses build Anda.
  • Anda memerlukan dukungan SSR/prerendering.

Gunakan Backend HTTP saat:

  • Anda perlu memperbarui terjemahan tanpa redeployment.
  • Terjemahan dikelola secara eksternal.
  • Anda memiliki CDN untuk menyajikan file terjemahan.

Inisialisasi i18next yang Tepat

Menginisialisasi i18next dengan benar sangat penting untuk menghindari kesalahan runtime dan memastikan terjemahan tersedia saat aplikasi Anda dirender.

Menggunakan provideAppInitializer

Fungsi provideAppInitializer Angular (diperkenalkan di Angular 18) memastikan i18next sepenuhnya diinisialisasi sebelum aplikasi mulai dirender. Fungsi yang disediakan dijalankan selama bootstrap aplikasi, dan inisialisasi tidak selesai sampai Promise diselesaikan. Ini mencegah kunci terjemahan muncul alih-alih teks yang diterjemahkan.

Contoh: Konfigurasi App Initializer

export const appConfig: ApplicationConfig = {
  providers: [
    // ... other providers
    provideAppInitializer(initializeI18n),
    // ... more providers
  ],
};

Mengapa Ini Penting:

  • Fungsi berjalan dalam konteks injeksi selama startup aplikasi.
  • Mencegah flickering atau menampilkan kunci terjemahan pada muat awal.
  • Memastikan terjemahan tersedia di semua komponen sejak awal.
  • Bekerja dengan benar dengan SSR, prerendering, dan rendering sisi klien.

Fungsi Inisialisasi i18next

Fungsi inisialisasi menangani pemuatan sumber daya dan konfigurasi pengaturan i18next.

Contoh: Inisialisasi I18next

public async initializeI18n(): Promise<void> {
  const initialLanguage = this.getInitialLanguage();
  const resources: Record<string, Record<string, Record<string, unknown>>> = {};

  if (isPlatformBrowser(this.platformId)) {
    // Browser: Only load the specific language needed
    const languageResources = await this.loadLanguageResources(initialLanguage);
    resources[initialLanguage] = languageResources;
  } else {
    // SSR: Load all languages for prerendering
    for (const lang of SUPPORTED_LANGUAGES) {
      const languageResources = await this.loadLanguageResources(lang);
      resources[lang] = languageResources;
    }
  }

  await init({
    lng: initialLanguage,
    fallbackLng: DEFAULT_LANGUAGE,
    resources,
    ns: NAMESPACES,
    defaultNS: DEFAULT_NAMESPACE,
    interpolation: {
      escapeValue: false,
    },
  });
}

Poin Utama:

  • Muat hanya bahasa saat ini di browser untuk performa optimal.
  • Muat semua bahasa selama SSR untuk dukungan prerendering.
  • Gunakan fallbackLng untuk menangani terjemahan yang hilang dengan baik.
  • Konfigurasikan namespace untuk organisasi yang lebih baik.

Pipe Terjemahan

Pipe terjemahan Angular menyediakan cara yang bersih untuk menggunakan terjemahan dalam template.

Implementasi Pipe

Contoh: Pipe Terjemahan

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: string, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

Pipe ditandai sebagai pure: false untuk memastikan pipe diperbarui saat bahasa berubah. Ini penting karena perubahan bahasa tidak mengubah kunci terjemahan itu sendiri.

Menggunakan Pipe Terjemahan

Dalam Template:

<!-- With namespace -->
<p>{{ "common:welcomeMessage" | t }}</p>

<!-- With nested keys -->
<p>{{ "dashboard.title" | t }}</p>

<!-- Simple interpolation -->
<p>{{ "greeting" | t: { name: userName } }}</p>
<!-- Result: "Hello, John!" from greeting: "Hello, {{name}}!" -->

<!-- With Angular pipes -->
<p>{{ "price" | t: { amount: 29.99 | currency } }}</p>
<!-- Result: "Price: $29.99" from: "Price: {{amount}}" -->

<!-- Multiple variables -->
<p>{{ "updated" | t: { date: lastModified | date, user: currentUser } }}</p>
<!-- Result: "Updated on Jan 15, 2024 by Alice" from: "Updated on {{date}} by {{user}}." -->

Interpolasi memungkinkan Anda menyisipkan nilai dinamis ke dalam terjemahan Anda. Anda dapat meneruskan variabel, menggunakan pipe Angular untuk pemformatan, dan menggabungkan beberapa nilai dalam satu string terjemahan.

Dalam Kode Komponen:

import { Component } from '@angular/core';
import { t } from 'i18next';

@Component({
  selector: 'app-dashboard',
  template: `
    <h1>{{ title }}</h1>
    <p>{{ welcomeMsg }}</p>
  `,
})
export class DashboardComponent {
  title = t('dashboard.title');
  welcomeMsg = t('greeting', { name: 'John' });
}

Untuk penggunaan template, sintaks pipe bersih dan mudah dibaca. Untuk akses programatik, panggil t() secara langsung. Ini berguna untuk menampilkan notifikasi, mengatur judul dinamis, atau menangani terjemahan dalam logika komponen.

Praktik Terbaik untuk Angular i18next

Gunakan Kunci yang Bermakna

Gunakan notasi titik yang mencerminkan struktur aplikasi Anda: 'auth:login.title' alih-alih 'loginTitle' atau teks mentah.

Atur dengan Namespace

Pecah terjemahan ke dalam namespace logis (misalnya, common.json, auth.json, dashboard.json) untuk pemeliharaan yang lebih baik.

Tangani Pluralisasi

Gunakan dukungan pluralisasi bawaan i18next untuk menangani bentuk tunggal/jamak dengan benar dalam semua bahasa.

Tunggu Inisialisasi

Selalu pastikan i18next diinisialisasi sebelum merender aplikasi Anda menggunakan provideAppInitializer() untuk menjalankan inisialisasi selama bootstrap aplikasi.

Keamanan Tipe untuk Kunci Terjemahan

Gunakan TypeScript untuk mengekstrak kunci terjemahan langsung dari file JSON Anda untuk keamanan tipe yang lengkap. Alih-alih memelihara definisi tipe secara manual, manfaatkan typeof dan impor dinamis TypeScript untuk menghasilkan tipe secara otomatis dari file terjemahan Anda yang sebenarnya.

Definisikan tipe yang mengekstrak semua kemungkinan kunci dari file terjemahan JSON Anda menggunakan tipe pembantu NestedKeys rekursif.

import common from './en/common.json'; // defaultNS: DEFAULT_NAMESPACE,
import auth from './en/auth.json'; // namespaces

type NestedKeys<T, Prefix extends string = ''> = T extends object
  ? {
      [K in keyof T & (string | number)]: T[K] extends object
        ?
            | NestedKeys<T[K], `${Prefix}${K & (string | number)}.`>
            | `${Prefix}${K & (string | number)}`
        : `${Prefix}${K & (string | number)}`;
    }[keyof T & (string | number)]
  : never;

export type TranslationKey =
  | `common:${NestedKeys<typeof common>}`
  | `auth:${NestedKeys<typeof auth>}`;

Ubah metode transform pipe untuk menerima TranslationKey alih-alih string. Ini memberikan keamanan tipe dalam template dan memastikan hanya kunci terjemahan yang valid yang dapat digunakan.

import { Pipe, PipeTransform } from '@angular/core';
import { t } from 'i18next';

import { TranslationKey } from '../../i18n/translation-key';

@Pipe({
  name: 't',
  standalone: true,
  pure: false, // Need to update when language changes
})
export class TranslatePipe implements PipeTransform {
  transform(key: TranslationKey, options?: Record<string, unknown>): string {
    return t(key, options);
  }
}

Sekarang Anda mendapatkan pelengkapan otomatis IntelliSense penuh dan kesalahan waktu kompilasi untuk kunci yang tidak valid baik dalam kode TypeScript maupun template.

t('common:greeting');       // ✅ Autocomplete
t('auth:login.title');      // ✅ Valid
t('invalid.key');           // ❌ IDE shows error

{{ "common:greeting" | t }} // ✅ Valid in templates
{{ "invalid.key" | t }}     // ❌ IDE shows error

Manfaat Kunci Terjemahan yang Aman Tipe:

  • Pelengkapan otomatis di IDE Anda untuk semua kunci terjemahan yang tersedia di semua namespace.
  • Kesalahan waktu kompilasi jika Anda menggunakan kunci yang tidak ada, menangkap kesalahan ketik sebelum runtime.
  • Dukungan refactoring - ubah nama kunci dengan aman di seluruh basis kode Anda.
  • Tanpa overhead runtime - tipe sepenuhnya dihapus selama kompilasi.
  • Dihasilkan secara otomatis dari file JSON Anda yang sebenarnya - tidak perlu pemeliharaan tipe manual.

Deteksi dan Pengalihan Bahasa

Implementasikan deteksi bahasa yang cerdas dan izinkan pengguna untuk beralih bahasa saat runtime dengan pemuatan sumber daya otomatis.

Tentukan bahasa pengguna yang akan digunakan saat aplikasi Anda dimulai dengan memeriksa beberapa sumber dalam urutan prioritas.

private getInitialLanguage(): string {
  if (isPlatformBrowser(this.platformId)) {
    // Try to get language from localStorage (user preference)
    const savedLang = localStorage.getItem('language');
    if (savedLang) {
      return savedLang;
    }

    // Fall back to browser language
    return navigator.language ?? DEFAULT_LANGUAGE;
  }

  // Default language for SSR
  return DEFAULT_LANGUAGE;
}

Pendekatan ini memeriksa localStorage terlebih dahulu (preferensi pengguna), kemudian kembali ke bahasa browser (navigator.language), dan akhirnya default ke bahasa default aplikasi.

Beralih bahasa saat runtime sambil memuat sumber daya terjemahan secara otomatis sesuai permintaan.

public async switchLanguage(
  language: string
): Promise<void> {
  // In browser, check if we need to load the translation first
  if (isPlatformBrowser(this.platformId)) {
    const i18next = await import('i18next');

    await Promise.all(
      NAMESPACES.map(async (namespace) => {
        const currentResources = i18next.default.getResourceBundle(
          lang,
          namespace,
        );
        // Check if any namespace is missing for this language
        if (!currentResources) {
          // Translation not loaded yet, load it dynamically
          const translationModule = await this.loadJson(lang, namespace);
          i18next.default.addResourceBundle(
            lang,
            namespace,
            translationModule.default,
          );
        }
      }),
    );
  }

  // Switch to the new language
  await changeLanguage(lang);

  // Save preference to localStorage
  if (isPlatformBrowser(this.platformId)) {
    localStorage.setItem('language', lang);
  }
}

Saat menggunakan impor dinamis, terjemahan dimuat sesuai permintaan. Metode switchLanguage() memeriksa apakah sumber daya sudah dimuat menggunakan getResourceBundle(). Jika hilang, metode ini secara dinamis mengimpor dan menambahkannya menggunakan addResourceBundle(). Ini mencegah permintaan jaringan yang berlebihan sambil memastikan semua terjemahan yang diperlukan tersedia.

Detail Implementasi Utama:

  • Periksa localStorage terlebih dahulu untuk memulihkan bahasa pilihan pengguna.
  • Gunakan navigator.language sebagai fallback untuk preferensi bahasa browser.
  • Muat sumber daya yang hilang secara dinamis dengan addResourceBundle().

Otomatisasi Terjemahan dengan AI

Mengelola terjemahan secara manual di berbagai bahasa memakan waktu dan rentan kesalahan. Di situlah lokalisasi berbasis AI berperan.

Mengapa l10n.dev?

l10n.dev adalah layanan terjemahan berbasis AI yang dirancang khusus untuk file JSON i18next:

  • Mempertahankan struktur JSON, kunci, dan objek bersarang.
  • Mempertahankan placeholder seperti {{name}}, {{count}} dengan benar.
  • Secara otomatis menghasilkan bentuk jamak untuk semua bahasa.
  • Menangani konteks dan interpolasi dengan cerdas.
  • Mendukung semua fitur i18next termasuk namespace.

Alur Kerja Sederhana

  1. Ekspor file JSON bahasa dasar Anda (misalnya, en/common.json)
  2. Unggah ke l10n.dev dan pilih bahasa target.
  3. AI menerjemahkan dengan kesadaran konteks dan pemformatan yang tepat.
  4. Unduh file yang diterjemahkan siap digunakan di aplikasi Angular Anda.

Hemat waktu berjam-jam untuk pekerjaan terjemahan manual dan hindari kesalahan umum seperti placeholder yang rusak atau bentuk jamak yang salah.

Siap untuk menyederhanakan alur kerja i18n Angular Anda?