Sử dụng bản địa hóa và mã ngôn ngữ trong Capacitor 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 hoặc onboarding, cũng như 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 tùy nền tảng, vì vậy Adapty sử dụng một chuẩn nội bộ thống nhất trên tất cả các nền tảng được hỗ trợ. Hiểu rõ chuẩn đó sẽ giúp bạn dự đoán được bản dịch nào mà người dùng sẽ nhận được.

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

Để mã hóa ngôn ngữ, Adapty sử dụng phiên bản chuẩn BCP 47 được điều chỉnh nhẹ: mỗi mã 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ữ

Trong SDK v4, flow và onboarding khớp mã ngôn ngữ theo cách khác nhau: flow được bản địa hóa bởi SDK trên thiết bị, còn onboarding được bản địa hóa bởi máy chủ Adapty.

Flow và paywall xây dựng bằng Paywall Builder

Paywall được xây dựng trong Paywall Builder sẽ được phân phối dưới dạng flow trong SDK v4, vì vậy quy tắc dưới đây áp dụng cho cả hai.

Việc khớp là chính xác tuyệt đối. SDK so sánh mã bạn truyền vào với mã ngôn ngữ của flow theo từng ký tự một: không thay đổi chữ hoa/thường, không thay thế dấu gạch dưới (_) bằng dấu gạch ngang (-), và không tự động dùng thẻ ngôn ngữ gốc. Với một flow có bản địa hóa pt-br, chỉ pt-br mới khớp: pt-BR, pt_BR, và pt-PT đều không khớp.

Khi mã không khớp với bất kỳ bản địa hóa nào, flow sẽ tự động hiển thị theo ngôn ngữ mặc định — SDK không trả về lỗi và không ghi cảnh báo.

Khi mã khớp, Adapty sẽ kết hợp bản địa hóa đó với bản mặc định: những chuỗi và tài nguyên không được định nghĩa trong bản địa hóa khớp sẽ được lấy từ bản địa hóa mặc định.

Bỏ qua mã locale không giống với việc yêu cầu bản địa hóa mặc định của flow: SDK sẽ thay thế bằng en cố định. Một flow có locale mặc định là de vẫn hiển thị bằng en khi nó có bản địa hóa en, và chỉ fallback về de khi không có.

Warning

Truyền mã locale chính xác như đã cấu hình trên dashboard — các subtag viết thường được phân cách bằng dấu gạch ngang. Đừng truyền trực tiếp một system locale identifier: navigator.language trả về pt-BR, và sẽ fallback về bản địa hóa mặc định. Hãy chuyển đổi giá trị trong ứng dụng trước khi truyền vào.

Onboardings

Onboarding được bản địa hóa trên server và các quy tắc của server chấp nhận nhiều định dạng khác nhau. Khi bạn truyền locale vào getOnboarding:

  1. Chuỗi locale đượ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ã locale 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 khớp với chuỗi đó
  4. Nếu vẫn không tìm thấy, Adapty trả về nội dung theo locale mặc định của onboarding

This way pt_BR, pt-BR, and pt-br all resolve to the same onboarding localization.

Triển khai bản địa hóa

Trong SDK v4, bạn không cần truyền mã ngôn ngữ khi lấy một flow — getFlow trả về flow cùng toàn bộ các bản địa hóa của nó, và Adapty sẽ áp dụng một bản khi flow view được tạo.

  • Flow xây dựng trong builder: SDK không đọc ngôn ngữ thiết bị, vì vậy hãy xác định ngôn ngữ trong ứng dụng của bạn và truyền vào dưới dạng tùy chọn locale của createFlowView. Đây là tùy chọn không bắt buộc — nếu bỏ qua, flow sẽ hiển thị bằng en, hoặc bằng ngôn ngữ mặc định nếu flow không có bản địa hóa en.

    import { createFlowView } from '@adapty/capacitor';
    
    const view = await createFlowView(flow, { locale: 'es' });

view.locale báo cáo ngôn ngữ mà view được xây dựng cùng. Cả tùy chọn localeview.locale đều yêu cầu Capacitor SDK 4.0.1-beta.1, và view.localeundefined trên các phiên bản cũ hơn. Handler onAppeared báo cáo cùng giá trị đó.

  • Paywall tùy chỉnh (remote config): getFlow trả về mọi cấu hình ngôn ngữ đã được thiết lập trong flow.remoteConfigs. Mỗi entry có mã lang và một object data. Chọn entry phù hợp với người dùng, với fallback do bạn tự xử lý:

const flow = await adapty.getFlow({ placementId: 'placement_id' });
const config = flow.remoteConfigs?.find((c) => c.lang === 'en') ?? flow.remoteConfigs?.[0];
// read your values from config?.data

Adapty lưu trữ các mã lang đó theo định dạng được mô tả trong Tiêu chuẩn mã ngôn ngữ tại Adapty. SDK không tự động khớp Remote Config với ngôn ngữ, vì vậy việc áp dụng mục nào là tùy thuộc vào ứng dụng của bạn.

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

Có một số trường hợp mà mã ngôn ngữ (locale code) đóng vai trò quan trọng — ví dụ, khi bạn cần lấy đúng paywall cho 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 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 đó, điều quan trọng là bạn cần hiểu rõ mình đang gửi gì lên server để nhận đúng bản ngôn ngữ, và quá trình xử lý tiếp theo diễn ra như thế nào — để luôn nhận được kết quả như mong đợi.

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

Đối với mã ngôn ngữ, Adapty sử dụng phiên bản có chỉnh sửa nhẹ của tiêu chuẩn BCP 47: mỗi mã bao gồm các subtag viết thường, phân tá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 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 dịch tương ứng cho một 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 dịch 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 từ pt-br) và tìm kiếm bản dịch 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ề nội dung theo ngôn ngữ mặc định của paywall

Theo 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 sẽ đều nhận được kết quả giống nhau.

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. Nếu vậy, chúng tôi khuyến nghị bạn thêm một cặp key-value chứa mã locale Adapty tương ứng vào mỗi file bản địa hóa. Sau đó, trích xuất giá trị của key đó khi gọi SDK của chúng tôi, như sau:

// 1. Modify your localization files (e.g., using react-i18next)

/*
en.json
*/
{
  "adapty_paywalls_locale": "en"
}

/*
es.json
*/
{
  "adapty_paywalls_locale": "es"
}

/*
pt-BR.json
*/
{
  "adapty_paywalls_locale": "pt-br"
}

// 2. Extract and use the locale code

const MyComponent = () => {
  const { t } = useTranslation();
  
  const fetchPaywall = async () => {
    const locale = t('adapty_paywalls_locale');
    // pass locale code to adapty.getPaywall or adapty.getPaywallForDefaultAudience method
    const paywall = await adapty.getPaywallForDefaultAudience('placement_id', locale);
  };
};

That way you can ensure you’re in full control of what localization will be retrieved for every user of your app.

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 nhau) mà không cần phải khai báo rõ mã ngôn ngữ cho từng bản địa hóa. Thay vào đó, bạn có thể lấy 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:


const getLocaleCode = () => {
  if (Capacitor.getPlatform() === 'ios') {
    return navigator.language || 'en';
  } else {
    return navigator.language || 'en';
  }
};

const fetchPaywall = async () => {
  const locale = getLocaleCode();
  // pass locale code to adapty.getPaywall or adapty.getPaywallForDefaultAudience method
  const paywall = await adapty.getPaywallForDefaultAudience('placement_id', locale);
};

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 bạn muốn localization đượ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 được khuyến nghị với các file chuỗi đã được bản địa hóa — hoặc tự tái tạo logic đó.
  2. Rất khó đoán chính xác server của Adapty sẽ nhận được gì. Ví dụ, trên iOS, thiết bị có thể trả về một locale như ar_OM@numbers='latn' và gửi nó lên server của chúng tôi. Với lệnh gọi này, bạn sẽ không nhận được bản địa hóa ar-om như mong đợi, mà thay vào đó là ar — điều này nhiều khả năng không phải kết quả bạn muốn. Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.