1. 12:09 14/10/2026
## Giới thiệu
Khi phát triển ứng dụng chạy cùng lúc trên web và mobile trong một monorepo, việc quản lý đa ngôn ngữ (i18n) trở thành thách thức lớn. Cách tiếp cận thống nhất giúp giảm thiểu trùng lặp mã, đảm bảo trải nghiệm người dùng nhất quán và dễ dàng mở rộng ngôn ngữ mới.
## Lợi ích của giải pháp chung
- **Chia sẻ tệp dịch thuật**: Một kho ngôn ngữ duy nhất phục vụ cả Next.js và Expo.
- **Cấu hình tập trung**: Thay đổi fallback, định dạng ngày/giờ chỉ cần sửa ở một nơi.
- **Tối ưu bundle**: Chỉ nạp ngôn ngữ cần thiết cho từng nền tảng.
## Kiến trúc đề xuất
1. **Thư viện core i18n** – Tạo package `packages/i18n` chứa:
- `locales/` : JSON cho từng ngôn ngữ (en, vi, ja…).
- `index.ts` : Xuất hook `useTranslation` wrapper cho `react-i18next` (web) và `expo-localization` + `i18n-js` (mobile).
- `detector.ts` : Phát hiện ngôn ngữ thiết bị / trình duyệt.
2. **Cấu hình Next.js** – Trong `next.config.js` thêm `i18n: { locales: ['en','vi'], defaultLocale: 'en' }` và import hook từ `@monorepo/i18n`.
3. **Cấu hình Expo** – Cài `expo-localization`, `i18n-js`; trong `App.tsx` khởi tạo i18n với tài nguyên từ `@monorepo/i18n/locales`.
4. **Script đồng bộ** – Thêm script `sync:locales` dùng `node scripts/sync-locales.js` để copy JSON từ `packages/i18n/locales` sang `apps/web/public/locales` và `apps/mobile/assets/locales` trước khi build.
## Các bước triển khai chi tiết
- **Bước 1**: Khởi tạo package `i18n` với `pnpm create package i18n`.
- **Bước 2**: Viết `useTranslation` wrapper:
```ts
export const useTranslation = (ns?: string) => {
if (Platform.OS === 'web') return useTranslationWeb(ns);
return useTranslationNative(ns);
};
```
- **Bước 3**: Cấu hình detector dùng `navigator.language` trên web và `Localization.locale` trên mobile.
- **Bước 4**: Thêm namespace cho từng feature (auth, settings, errors) để lazy‑load qua `i18next-http-backend` (web) và `react-native-fs` (mobile).
- **Bước 5**: Viết test đơn vị cho hook wrapper, kiểm tra fallback và pluralization.
## Mẹo thực chiến
- Sử dụng **TypeScript** để sinh type an toàn cho key dịch thuật (`declare module 'react-i18next' { interface Resources { auth: AuthLocales; } }`).
- Bật **ICU MessageFormat** cho plural, gender, date – tránh hard‑code logic trong component.
- Tận dụng **GitHub Actions** chạy `pnpm sync:locales` và `pnpm test:i18n` mỗi PR.
- Theo dõi **bundle analyzer** để đảm bảo chỉ nạp ngôn ngữ được chọn.
## Kết luận
Với cấu trúc package chia sẻ và wrapper thống nhất, team có thể thêm ngôn ngữ mới chỉ trong vài phút, giảm thiểu bug liên quan đến locale và giữ cho codebase sạch, dễ bảo trì.