Pusat Bantuan

Angular i18next

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

Mengapa i18next untuk Angular?

i18next adalah salah satu kerangka kerja i18n yang paling populer, menawarkan fitur kuat dan fleksibilitas:

  • Agnostik kerangka kerja - bekerja tanpa masalah dengan Angular, React, Vue, dan lainnya.
  • Dukungan interpolasi, pluralisasi, dan konteks yang kuat.
  • Dukungan pemuatan malas dan pemisahan kode.
  • Dukungan TypeScript dengan keamanan tipe.
  • Ekosistem besar dengan plugin dan ekstensi.

Instalasi

Instal i18next menggunakan npm atau yarn:

npm install i18next

Itu saja! i18next adalah perpustakaan mandiri tanpa ketergantungan spesifik kerangka kerja. Anda tidak perlu angular-i18next atau pembungkus lainnya - cukup gunakan i18next 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 besar.

Keuntungan:

  • Optimasi ukuran bundel - hanya memuat terjemahan yang dibutuhkan.
  • Kinerja lebih baik - waktu muat awal lebih cepat.
  • Bekerja dengan sempurna dengan SSR dan prerendering.
  • Tidak diperlukan 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>> = {};

  for (const namespace of NAMESPACES) {
    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 memisahkan kode menjadi bagian terpisah yang dimuat sesuai permintaan.

i18next-http-backend

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

Keuntungan:

  • Perbarui terjemahan tanpa penyebaran ulang.
  • Muat terjemahan dari CDN eksternal.
  • Berguna untuk manajemen terjemahan waktu nyata.

Kerugian:

  • Permintaan HTTP tambahan memperlambat 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 ketika:

  • Anda ingin ukuran bundel dan kinerja yang optimal.
  • Terjemahan adalah bagian dari proses pembangunan Anda.
  • Anda memerlukan dukungan SSR/prerendering.

Gunakan Backend HTTP ketika:

  • Anda perlu memperbarui terjemahan tanpa penyebaran ulang.
  • 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 merender. Fungsi yang disediakan dieksekusi selama bootstrap aplikasi, dan inisialisasi tidak selesai sampai Promise terpecahkan. 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 ini dijalankan dalam konteks injeksi selama startup aplikasi.
  • Mencegah kedipan atau menampilkan kunci terjemahan saat 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 mengonfigurasi 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 Penting:

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

Pipa Terjemahan

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

Implementasi Pipa

Contoh: Pipa 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);
  }
}

Pipa ditandai sebagai murni: false untuk memastikan ia diperbarui saat bahasa berubah. Ini penting karena perubahan bahasa tidak memodifikasi kunci terjemahan itu sendiri.

Menggunakan Pipa 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 untuk menyisipkan nilai dinamis ke dalam terjemahan Anda. Anda dapat meneruskan variabel, menggunakan pipa 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 pipa bersih dan mudah dibaca. Untuk akses programatik, panggil t() 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

Pisahkan 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 untuk 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 secara manual memelihara definisi tipe, manfaatkan typeof dan impor dinamis TypeScript untuk secara otomatis menghasilkan tipe dari file terjemahan Anda yang sebenarnya.

Tentukan tipe yang mengekstrak semua kunci yang mungkin dari file terjemahan JSON Anda menggunakan tipe pembantu NestedKeys yang 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 transformasi pipa 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 autocompletion IntelliSense penuh dan kesalahan waktu kompilasi untuk kunci yang tidak valid baik di 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:

  • Autocomplete 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 - ganti nama kunci dengan aman di seluruh basis kode Anda.
  • Tidak ada overhead runtime - tipe sepenuhnya dihapus selama kompilasi.
  • Dihasilkan secara otomatis dari file JSON Anda yang sebenarnya - tidak ada pemeliharaan tipe manual yang diperlukan.

Deteksi dan Pergantian Bahasa

Terapkan deteksi bahasa yang cerdas dan izinkan pengguna untuk mengganti 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.

Ganti bahasa saat runtime sambil secara otomatis memuat sumber daya terjemahan 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');

    // Check if any namespace is missing for this language
    for (const namespace of NAMESPACES) {
      const currentResources = i18next.default.getResourceBundle(lang, namespace);

      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 tidak ada, ia mengimpor secara dinamis dan menambahkannya menggunakan addResourceBundle(). Ini mencegah permintaan jaringan yang berlebihan sambil memastikan semua terjemahan yang diperlukan tersedia.

Detail Implementasi Kunci:

  • Periksa localStorage terlebih dahulu untuk mengembalikan bahasa preferensi pengguna.
  • Gunakan navigator.language sebagai cadangan 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 terhadap kesalahan. Di sinilah 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 terjemahan yang siap digunakan di aplikasi Angular Anda.

Hemat berjam-jam kerja terjemahan manual dan hindari kesalahan umum seperti placeholder yang rusak atau bentuk jamak yang tidak benar.

Siap untuk menyederhanakan alur kerja i18n Angular Anda?