Sử dụng localizations và locale codes trong Flutter SDK

Tại sao điều này quan trọng

Mã ngôn ngữ (locale code) được sử dụng khi Adapty chọn bản dịch cho một flow, và khi bạn đọc Remote Config cho một paywall tùy chỉnh.

Mã ngôn ngữ khá phức tạp và có thể khác nhau giữa các nền tảng, vì vậy Adapty sử dụng một tiêu chuẩn nội bộ thống nhất trên mọi nền tảng được hỗ trợ. Hiểu được tiêu chuẩn đó sẽ giúp bạn dự đoán bản dịch nào người dùng sẽ nhận được.

Tiêu chuẩn mã ngôn ngữ tại Adapty

Đối với mã ngôn ngữ, Adapty sử dụng một phiên bản điều chỉnh nhỏ của tiêu chuẩn BCP 47: mỗi mã bao gồm các subtag viết thường, phân cách bằng dấu gạch ngang. Một số ví dụ: en (tiếng Anh), pt-br (tiếng Bồ Đào Nha (Brazil)), zh (tiếng Trung Giản thể), zh-hant (tiếng Trung Phồn thể).

Khớp mã ngôn ngữ

Khi Adapty tìm kiếm bản địa hóa phù hợp với ngôn ngữ của người dùng, quá trình diễn ra như sau:

  1. Chuỗi ngôn ngữ được chuyển thành chữ thường và tất cả dấu gạch dưới (_) được thay thế bằng dấu gạch ngang (-)
  2. Adapty tìm kiếm bản địa hóa có mã ngôn ngữ khớp hoàn toàn
  3. Nếu không tìm thấy, Adapty lấy chuỗi con trước dấu gạch ngang đầu tiên (pt từ pt-br) và tìm kiếm bản địa hóa phù hợp
  4. Nếu vẫn không tìm thấy, Adapty trả về bản địa hóa mặc định en

Nhờ cách này, 'pt_BR', pt-BRpt-br đều được phân giải về cùng một bản địa hóa.

Triển khai localization

Trong SDK v4, bạn không cần truyền mã locale khi lấy một flow.

  • Paywall dùng Flow Builder và Paywall Builder: Adapty tự động xác định localization dựa trên thiết bị và các localization bạn đã cấu hình trong builder. Render flow bằng createFlowView — không cần mã locale.
  • Paywall tùy chỉnh (remote config): getFlow trả về tất cả các localization đã cấu hình trong flow.remoteConfigs. Mỗi mục có mã locale và nội dung config (chuỗi data, hoặc dictionary đã được parse). Hãy chọn mục phù hợp với người dùng, kèm theo logic fallback của riêng bạn:

final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
final config = flow.remoteConfigs.firstWhereOrNull((c) => c.locale == 'en') ??
    flow.remoteConfig; // the first remote config, if present
// read your values from config?.dictionary

Các quy tắc khớp mã locale ở trên mô tả cách Adapty chuẩn hóa các mã locale được lưu trữ trên mỗi Remote Config.

Tại sao điều này quan trọng

Có một vài tình huống mà mã ngôn ngữ (locale code) trở nên quan trọng — ví dụ, khi bạn cần lấy đúng paywall phù hợp với ngôn ngữ hiện tại của ứng dụng.

Vì mã ngôn ngữ khá phức tạp và có thể khác nhau tùy từng nền tảng, chúng tôi sử dụng một tiêu chuẩn nội bộ thống nhất cho tất cả các nền tảng được hỗ trợ. Tuy nhiên, chính vì sự phức tạp đó, bạn cần hiểu rõ mình đang gửi gì lên server để nhận đúng bản ngôn ngữ mong muốn — và điều gì xảy ra tiếp theo — để luôn nhận được kết quả như kỳ vọng.

Tiêu chuẩn mã ngôn ngữ tại Adapty

Đối với mã ngôn ngữ, Adapty sử dụng chuẩn BCP 47 được chỉnh sửa nhẹ: mỗi mã bao gồm các subtag viết thường, ngăn cách nhau bằng dấu gạch ngang. Một số ví dụ: en (tiếng Anh), pt-br (tiếng Bồ Đào Nha (Brazil)), zh (tiếng Trung giản thể), zh-hant (tiếng Trung phồn thể).

Đối sánh mã ngôn ngữ

Khi Adapty nhận được lệnh gọi từ SDK phía client kèm theo mã ngôn ngữ và bắt đầu tìm kiếm bản bản địa hóa tương ứng của paywall, quá trình diễn ra như sau:

  1. Chuỗi ngôn ngữ đầu vào được chuyển thành chữ thường và tất cả dấu gạch dưới (_) được thay thế bằng dấu gạch ngang (-)
  2. Chúng tôi tìm kiếm bản bản địa hóa có mã ngôn ngữ khớp hoàn toàn
  3. Nếu không tìm thấy kết quả khớp, chúng tôi lấy chuỗi con trước dấu gạch ngang đầu tiên (pt trong pt-br) và tìm kiếm bản bản địa hóa tương ứng
  4. Nếu vẫn không tìm thấy kết quả khớp, chúng tôi trả về bản bản địa hóa mặc định en Bằng cách này, một thiết bị iOS gửi 'pt_BR', một thiết bị Android gửi pt-BR, và một thiết bị khác gửi pt-br đều sẽ nhận được cùng một kết quả.

Nếu bạn đang tìm hiểu về bản địa hóa, có thể bạn đã làm việc với các file chuỗi đã được bản địa hóa trong dự án của mình. Trong trường hợp đó, chúng tôi khuyến nghị bạn thêm một cặp key-value với mã locale Adapty tương ứng vào mỗi file cho từng bản địa hóa. Sau đó, trích xuất giá trị của key đó khi gọi SDK, như ví dụ dưới đây:

// 1. Modify your app_en.arb, app_es.arb, app_pt_br.arb files

/*
app_en.arb
*/
"adapty_paywalls_locale": "en",

/*
app_es.arb
*/
"adapty_paywalls_locale": "es",

/*
app_pt_br.arb
*/
"adapty_paywalls_locale": "pt-br",

// 2. Extract and use the locale code
final locale = AppLocalizations.of(context)!.adapty_paywalls_locale;
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method

Bằng cách này, bạn có thể đảm bảo kiểm soát hoàn toàn ngôn ngữ nào sẽ được tải về cho từng người dùng trong ứng dụng của bạn.

Triển khai bản địa hóa theo cách khác

Bạn có thể đạt được kết quả tương tự (nhưng không hoàn toàn giống) mà không cần định nghĩa rõ ràng mã ngôn ngữ cho từng bản địa hóa. Điều đó có nghĩa là trích xuất mã ngôn ngữ từ các đối tượng khác mà nền tảng của bạn cung cấp, như sau:

final locale = Localizations.localeOf(context).languageCode;
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method

Lưu ý rằng chúng tôi không khuyến nghị cách tiếp cận này vì một số lý do:

  1. Trên iOS, ngôn ngữ ưu tiên và locale hiện tại không giống nhau. Nếu muốn bản địa hóa được chọn đúng, bạn cần dựa vào logic của Apple — hoạt động tự động nếu bạn dùng cách tiếp cận được khuyến nghị với các file string đã bản địa hóa — hoặc tự tái tạo lại logic đó.
  2. Rất khó dự đoán chính xác những gì server của Adapty sẽ nhận được. Ví dụ: trên iOS, có thể lấy được một locale như ar_OM@numbers='latn' từ thiết bị và gửi lên server. Khi đó, thay vì nhận được bản địa hóa ar-om như mong muốn, bạn sẽ nhận được ar — điều này có thể nằm ngoài kỳ vọng. Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.