Files
SeaGateway-App/LOCALIZATION.md
2025-11-15 16:58:07 +07:00

6.6 KiB

Localization Setup Guide

Overview

Ứng dụng đã được cấu hình hỗ trợ 2 ngôn ngữ: English (en)Tiếng Việt (vi) sử dụng expo-localizationi18n-js.

Cấu trúc thư mục

/config
  /localization
    - i18n.ts           # Cấu hình i18n (khởi tạo locale, enable fallback, etc.)
  - localization.ts     # Export main exports

/locales
  - en.json             # Các string tiếng Anh
  - vi.json             # Các string tiếng Việt

/hooks
  - use-i18n.ts         # Hook để sử dụng i18n trong components

/state
  - use-locale-store.ts # Zustand store cho global locale state management

Cách sử dụng

1. Import i18n hook trong component

import { useI18n } from "@/hooks/use-i18n";

export default function MyComponent() {
  const { t, locale, setLocale } = useI18n();

  return (
    <View>
      <Text>{t("common.ok")}</Text>
      <Text>{t("navigation.home")}</Text>
      <Text>Current locale: {locale}</Text>
    </View>
  );
}

2. Thêm translation keys

Mở file /locales/en.json và thêm:

{
  "myFeature": {
    "title": "My Feature Title",
    "description": "My Feature Description"
  }
}

Mở file /locales/vi.json và thêm translation tương ứng:

{
  "myFeature": {
    "title": "Tiêu đề Tính năng Của Tôi",
    "description": "Mô tả Tính năng Của Tôi"
  }
}

3. Sử dụng trong component

const { t } = useI18n();

return <Text>{t("myFeature.title")}</Text>;

Cách thay đổi ngôn ngữ

const { setLocale } = useI18n();

// Thay đổi sang Tiếng Việt
<Button onPress={() => setLocale('vi')} title="Tiếng Việt" />

// Thay đổi sang English
<Button onPress={() => setLocale('en')} title="English" />

Lưu ý: Ngôn ngữ được chọn sẽ được lưu tự động vào storage. Khi người dùng tắt app và mở lại, app sẽ sử dụng ngôn ngữ được chọn trước đó.

Persistence (Lưu trữ tùy chọn ngôn ngữ) - Zustand Global State

Localization sử dụng Zustand cho global state management. Ngôn ngữ được chọn tự động được lưu vào AsyncStorage với key app_locale_preference.

Quy trình:

  1. Khi app khởi động, useLocaleStore.getState().initLocale() được gọi trong app/_layout.tsx
  2. Store sẽ load locale từ storage nếu có
  3. Nếu không có, store sẽ detect ngôn ngữ thiết bị (getLocales())
  4. Khi người dùng gọi setLocale('vi'), nó sẽ:
    • Cập nhật Zustand store ngay lập tức
    • Tự động lưu vào storage để dùng lần tiếp theo
    • Tất cả components lắng nghe sẽ re-render ngay lập tức

Kết quả: Khi bạn toggle switch language trong settings:

  • Tab labels cập nhật ngay lập tức
  • UI labels cập nhật ngay lập tức
  • Không cần click vào tab hoặc navigate
  • Locale được persist vào storage

Zustand Store Structure

// /state/use-locale-store.ts
const { locale, setLocale, isLoaded } = useLocaleStore((state) => ({
  locale: state.locale, // Locale hiện tại
  setLocale: state.setLocale, // Async function để thay đổi locale
  isLoaded: state.isLoaded, // Flag để biết khi locale đã load từ storage
}));

Fallback Mechanism

Nếu một key không tồn tại trong ngôn ngữ hiện tại, ứng dụng sẽ tự động sử dụng giá trị từ ngôn ngữ English (default locale).

Ví dụ:

  • Nếu key auth.newFeature chỉ tồn tại trong en.json mà không có trong vi.json
  • Khi ngôn ngữ được set là Vietnamese (vi), nó sẽ hiển thị giá trị từ English

Persistence (Lưu trữ tùy chọn ngôn ngữ)

Ngôn ngữ được chọn tự động được lưu vào AsyncStorage với key app_locale_preference.

Quy trình:

  1. Khi app khởi động, hook useI18n sẽ load giá trị từ storage
  2. Nếu có giá trị lưu trữ, app sẽ sử dụng ngôn ngữ đó
  3. Nếu không có, app sẽ detect ngôn ngữ thiết bị (getLocales())
  4. Khi người dùng gọi setLocale('vi'), nó sẽ:
    • Thay đổi ngôn ngữ hiện tại
    • Tự động lưu vào storage để dùng lần tiếp theo

Kết quả: Người dùng có thể tắt app, mở lại, và app sẽ vẫn sử dụng ngôn ngữ đã chọn trước đó.

Supported Locales

Hiện tại app hỗ trợ:

  • en - English
  • vi - Tiếng Việt

Nếu muốn thêm ngôn ngữ khác:

  1. Tạo file locales/[language-code].json (ví dụ: locales/ja.json)

  2. Thêm translations

  3. Import trong config/localization/i18n.ts:

    import ja from "@/locales/ja.json";
    
    const translations = {
      en,
      vi,
      ja,
    };
    
  4. Cập nhật app.json để thêm locale mới:

    "supportedLocales": {
      "ios": ["en", "vi", "ja"],
      "android": ["en", "vi", "ja"]
    }
    

Translation Keys Hiện Có

Xem file locales/en.jsonlocales/vi.json để xem danh sách tất cả keys có sẵn.

Best Practices

  1. Luôn add key vào cả 2 file en.jsonvi.json cùng lúc
  2. Giữ cấu trúc JSON nhất quán giữa các language files
  3. Sử dụng snake_case cho keys (không sử dụng camelCase)
  4. Nhóm liên quan keys vào categories (ví dụ: common, navigation, auth, etc.)
  5. Để fallback enable để tránh lỗi nếu key bị thiếu

Device Language Detection

Ngôn ngữ của thiết bị sẽ được tự động detect khi app khởi động. Nếu thiết bị set language là Tiếng Việt, app sẽ tự động sử dụng vi locale.

Device language được lấy từ:

import { getLocales } from "expo-localization";

const deviceLanguage = getLocales()[0].languageCode; // 'en', 'vi', etc.

Troubleshooting

App không detect đúng ngôn ngữ thiết bị

  • Kiểm tra console log trong config/localization/i18n.ts
  • Đảm bảo language code của thiết bị khớp với các supported locales

Translation key không hiện

  • Kiểm tra xem key có tồn tại trong cả 2 files en.jsonvi.json không
  • Kiểm tra spelling của key (case-sensitive)
  • Kiểm tra syntax JSON có hợp lệ không

Fallback không hoạt động

  • Đảm bảo i18n.enableFallback = true trong config/localization/i18n.ts
  • Kiểm tra key có tồn tại trong en.json không (default fallback language)

References