在 Android 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。此行为适用于 Android SDK 4.0.1 及更高版本——在 4.0.0 中,省略语言代码会渲染流程的默认本地化。

Warning

请严格按照看板中配置的格式传递语言区域代码——使用连字符分隔的小写子标签。不要直接传递系统语言区域标识符:Locale.getDefault().toLanguageTag() 返回 pt-BRLocale.getDefault().toString() 返回 pt_BR,两者都会回退到默认本地化。请在传递前在应用中对该值进行转换。

用户引导

用户引导在服务端完成本地化,服务端规则对格式有一定的兼容性。当你将 locale 传入 getOnboarding 时:

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

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

实现本地化

在 SDK v4 中,获取流程时无需传入语言区域代码——getFlow 会返回包含所有本地化内容的流程。

  • 在编辑工具中构建的流程:SDK 不会读取设备语言,因此请在您的应用中解析语言设置,并将其作为 locale 参数传入 AdaptyUI.getFlowConfiguration。该参数为可选项——省略时,流程将以 en 渲染;若流程没有 en 本地化,则使用其默认语言
  • 自定义(远程配置)付费墙getFlow 会在 flow.remoteConfigs 中返回所有已配置的本地化内容。每个条目包含 locale 代码和配置内容(jsonString 或已解析的 dataMap)。请自行实现回退逻辑,选取与用户匹配的条目:
Adapty.getFlow("YOUR_PLACEMENT_ID") { result ->
    when (result) {
        is AdaptyResult.Success -> {
            val flow = result.value
            val config = flow.remoteConfigs.firstOrNull { it.locale == "en" }
                ?: flow.remoteConfigs.firstOrNull()
            // read your values from config?.dataMap
        }
        is AdaptyResult.Error -> {
            // handle the error
        }
    }
}

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 strings.xml files

/*
strings.xml - Spanish
*/
<string name="adapty_paywalls_locale">es</string>

/*
strings.xml - Portuguese (Brazil)
*/
<string name="adapty_paywalls_locale">pt-br</string>

// 2. Extract and use the locale code

val localeCode = context.getString(R.string.adapty_paywalls_locale)
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method

这样,您可以完全掌控应用中每位用户所获取的本地化内容。

实现本地化的另一种方式

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

val locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
    context.resources.configuration.locales[0]
else
    context.resources.configuration.locale

val localeCode = locale.toLanguageTag()
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall method

请注意,我们不推荐使用这种方式,因为很难预测 Adapty 服务器究竟会收到什么数据。

如果你仍然决定使用这种方式,请确保已覆盖所有相关的使用场景。