Internationalization in web monorepo

Bài tóm tắt Mercari 2025 3. Quy mô lớn Độ khó: Trung bình

Mercari · 2025 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc

Một bài trong series Global App (ngoài sheet). Bài trước của cùng tác giả: Mercari 2025.

Bối cảnh

  • Mercari có "golden path" cho web app mới (Terraform, Kubernetes, CI/CD, gói npm cấu hình sẵn) nên số web app tăng nhanh, mỗi app một repo. Hai chỗ đau: chia sẻ code chung (theme, URL, cấu hình thí nghiệm, logging) phải qua gói npm, tốn công và cản cộng tác; cấu hình và bảo trì GitHub workflow cho mỗi repo tốn thời gian và cần chuyên môn.

Thay đổi chính

  • Đổi repo web Nhật thành monorepo thay vì thêm một repo nữa cho Global: pnpm (workspace protocol cho phụ thuộc nội bộ, catalog để ghim một phiên bản React cho cả repo) + Turborepo cho pipeline build.
  • Tầng module giống cách backend chia tầng: App (Next.js, cấu hình toàn cục, nối route) → Feature (logic + UI một tính năng) → Domain (logic riêng của ứng dụng dùng chung giữa feature) → Core (thư viện không chứa nghiệp vụ). Ban đầu có 5 tầng, bỏ tầng Page vì khả năng dùng lại thấp.
  • i18n theo module: i18next không hỗ trợ module hoá. Import tay từng file dịch của từng feature cho từng ngôn ngữ là cấu hình N×M (hơn 10 module, dự kiến 50 vùng). Thay vào đó mỗi module xuất một hàm dùng webpack import context (import(\./translations/${lang}.json`)`), app chỉ liệt kê feature và ngôn ngữ (N+M), chỉ lấy bản dịch cần lúc build và đóng vào image Docker.
  • Để module Core dùng bản dịch của app mà không phụ thuộc vòng, dùng bundler alias: @alias/i18n-config trỏ tới file cấu hình của từng app trong next.config.js. Hai API: client (React Context khởi tạo ở root layout) và server (RSC).
  • Định áp dụng cùng cách cho module thí nghiệm: app khai báo feature flag, Core đọc trực tiếp qua alias, bỏ các module Domain chỉ bọc Core với chút cấu hình.

Vì sao

  • Mỗi app một repo dễ dựng nhờ golden path, nhưng code chung giữa các app lại không có chỗ sống.

Bài học cho SWE

  • Nền tảng làm việc tạo service/app mới rẻ sẽ sinh nhiều repo; chi phí dời sang chia sẻ code và bảo trì CI.
  • Cấu hình theo tích (module × ngôn ngữ) cần đảo thành tổng; tiêm cấu hình của app vào thư viện chung qua alias lúc build thay vì phụ thuộc ngược.

Liên quan