在 Flutter SDK 中使用本地化和语言区域代码

为什么这很重要

当 Adapty 为流程或用户引导选择本地化版本,以及当你读取自定义付费墙的远程配置时,都会用到语言区域代码。

语言区域代码较为复杂,且因平台而异,因此 Adapty 在其支持的所有平台上统一采用一套内部标准。了解这套标准,有助于你准确预测用户最终会收到哪个本地化版本。

Adapty 的语言代码标准

Adapty 的语言代码采用略经修改的 BCP 47 标准:每个代码由小写子标签组成,各子标签之间用连字符分隔。示例:en(英语)、pt-br(葡萄牙语(巴西))、zh(简体中文)、zh-hant(繁体中文)。

语言区域代码匹配

在 SDK v4 中,流程和用户引导的语言区域代码匹配方式有所不同:流程由设备上的 SDK 进行本地化,而用户引导则由 Adapty 服务器处理本地化。

流程与付费墙编辑工具付费墙

在付费墙编辑工具中构建的付费墙,在 SDK v4 中以流程的形式交付,因此以下规则同时适用于两者。

匹配规则为精确匹配。SDK 会逐字符比较你传入的代码与流程的本地化代码:不会转换大小写,不会将下划线(_)替换为连字符(-),也不会回退到语言子标签。对于具有 pt-br 本地化的流程,只有 pt-br 能匹配:pt-BRpt_BRpt-PT 均无法匹配。

当代码未匹配到任何本地化时,流程会静默地以其默认语言环境渲染——SDK 不会返回错误,也不会记录警告。

当代码匹配成功时,Adapty 会将该本地化与默认本地化合并:已匹配本地化中未定义的字符串和资源,将从默认本地化中获取。

省略语言代码并不等同于请求流程的默认本地化:SDK 会固定替换为 en。如果一个流程的默认语言设置为 de,但同时存在 en 本地化版本,则仍会以 en 渲染;只有在没有 en 本地化版本时,才会回退到 de

请严格按照看板中配置的格式传入语言区域代码——小写子标签之间用连字符分隔。不要直接传入系统语言区域标识符:Platform.localeName 返回 pt_BRPlatformDispatcher.instance.locale.toLanguageTag() 返回 pt-BR,这两种格式都会回退到默认本地化。请在应用中转换该值后再传入。

用户引导

用户引导的本地化在服务端完成,服务端规则兼容多种格式。当你向 getOnboarding 传入 locale 时:

  1. locale 字符串会被转换为小写,并将所有下划线(_)替换为连字符(-
  2. Adapty 查找与完整 locale 代码完全匹配的本地化内容
  3. 如果未找到匹配项,Adapty 会截取第一个连字符之前的子字符串(例如 pt-brpt),再次查找匹配的本地化内容
  4. 如果仍未找到匹配项,Adapty 将返回该用户引导默认语言的内容

这样,pt_BRpt-BRpt-br 都会解析到同一个用户引导本地化。

实现本地化

在 SDK v4 中,获取流程时无需传入语言区域代码——getFlow 会返回包含所有本地化内容的流程,Adapty 会在构建流程视图时应用其中一个。getFlowgetFlowForDefaultAudience 中的 locale 参数对流程无效,该参数已废弃,调用时会输出警告日志。

  • 在编辑工具中构建的流程:SDK 不读取设备语言,因此需要在应用中自行解析并将其作为 locale 参数传入 createFlowViewAdaptyUIFlowPlatformView。该参数为可选项——省略时流程将以 en 渲染,或在流程没有 en 本地化时使用其默认语言

AdaptyUIFlowView.locale 报告视图构建时使用的本地化语言。该功能需要 Flutter SDK 4.0.3 配合 iOS 原生 4.0.2 和 Android 4.0.1 版本,使用旧版原生 SDK 时返回 null

  • 自定义(远程配置)付费墙getFlowflow.remoteConfigs 中返回所有已配置的本地化内容。每个条目包含 locale 语言代码和配置内容(data 字符串,或已解析的 dictionary)。请自行选择匹配用户的条目并设置回退逻辑:

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

Adapty 以 Adapty 语言区域代码标准 中描述的格式存储这些 locale 代码。SDK 不会将远程配置与语言区域进行匹配,因此具体使用哪个条目由你的应用自行决定。

为什么这很重要

有几种情况下需要用到语言区域代码——例如,当你尝试为当前应用的本地化版本获取正确的付费墙时。

由于语言区域代码比较复杂,且在不同平台之间可能存在差异,我们为所有支持的平台制定了一套内部标准。正因为这些代码较为复杂,理解你究竟向我们的服务器发送了什么内容以获取正确的本地化版本,以及后续会发生什么,就变得尤为重要——这样你才能始终获得预期的结果。

Adapty 语言代码标准

在语言代码方面,Adapty 采用略经修改的 BCP 47 标准:每个代码由小写子标签组成,以连字符分隔。例如:en(英语)、pt-br(葡萄牙语(巴西))、zh(简体中文)、zh-hant(繁体中文)。

语言区域代码匹配

当 Adapty 收到客户端 SDK 传入的语言区域代码并开始查找付费墙对应的本地化内容时,会按以下步骤处理:

  1. 将传入的语言区域字符串转换为小写,并将所有下划线(_)替换为连字符(-
  2. 查找与完整语言区域代码完全匹配的本地化内容
  3. 若未找到匹配项,则截取第一个连字符前的子字符串(例如 pt-brpt),再次查找匹配的本地化内容
  4. 若仍未找到匹配项,则返回该付费墙默认语言区域的内容

这样,发送 'pt_BR' 的 iOS 设备、发送 pt-BR 的 Android 设备,以及发送 pt-br 的其他设备,都会得到相同的结果。

如果你正在考虑本地化问题,很可能已经在项目中使用了本地化字符串文件。在这种情况下,我们建议在每个本地化文件中添加一个键值对,用于存储对应语言的 Adapty 语言代码。然后在调用 SDK 时提取该键的值,示例如下:

// 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

这样,你就能完全掌控每位用户获取到的本地化内容。

实现本地化的另一种方式

你也可以不为每个本地化显式定义语言区域代码,同样能得到类似(但不完全相同)的结果。这种方式是从平台提供的其他对象中提取语言区域代码,如下所示:

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

不过,出于以下几点原因,我们不推荐这种方式:

  1. 在 iOS 上,首选语言和当前语言区域并不相同。如果希望正确选取本地化内容,你需要依赖 Apple 的内置逻辑(使用推荐的本地化字符串文件方案时开箱即用),或者自行重新实现该逻辑。
  2. 很难预测 Adapty 服务器实际收到的内容。例如,在 iOS 上,设备可能会返回类似 ar_OM@numbers='latn' 这样的语言区域标识,并将其发送至我们的服务器。对于这个请求,你得到的不会是预期的 ar-om 本地化内容,而是 ar,这很可能不符合预期。 Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.