Kotlin Multiplatform SDKでローカライゼーションとロケールコードを使用する
これが重要な理由
ロケールコードは、Adapty がフローやオンボーディングのローカライズを選択するときと、カスタムペイウォールのリモートコンフィグを読み取るときに使用されます。
ロケールコードはプラットフォームによって異なる場合があり複雑なため、Adapty はサポートするすべてのプラットフォームで共通の内部標準に依存しています。この標準を理解することで、ユーザーがどのローカライズを受け取るかを予測できます。
Adapty のロケールコード標準
ロケールコードには、Adapty は BCP 47 標準 を若干修正したものを使用しています。各コードは小文字のサブタグをハイフンで区切って構成されます。例: en(英語)、pt-br(ポルトガル語(ブラジル))、zh(簡体字中国語)、zh-hant(繁体字中国語)。
ロケールコードのマッチング
SDK v4 では、フローとオンボーディングのロケールコードのマッチング方法が異なります。フローはデバイス上でSDKによってローカライズされ、オンボーディングはAdaptyサーバーによってローカライズされます。
フローとペイウォールビルダーのペイウォール
ペイウォールビルダーで作成されたペイウォールは、SDK v4 ではフローとして配信されます。そのため、以下のルールは両方に適用されます。
マッチングは完全一致です。SDK は渡されたコードとフローのローカライゼーションコードを1文字ずつ比較します。大文字・小文字の変換は行われず、アンダースコア(_)をハイフン(-)に置換することもなく、言語サブタグへのフォールバックも行いません。pt-br のローカライゼーションを持つフローに対してマッチするのは pt-br のみです。pt-BR、pt_BR、pt-PT はいずれもマッチしません。
コードがどのローカリゼーションにも一致しない場合、フローはそのデフォルトロケールで表示されます。SDKはエラーを返さず、警告もログに出力しません。
コードが一致した場合、Adaptyはそのローカリゼーションをデフォルトのものとマージします。一致したローカリゼーションで定義されていない文字列やアセットは、デフォルトのローカリゼーションから補完されます。
ロケールコードを省略することは、フローのデフォルトのローカライゼーションを要求することとは異なります。SDKは固定値として en を代入します。デフォルトロケールが de のフローは、en のローカライゼーションが存在する場合には en で表示され、存在しない場合にのみ de にフォールバックします。
ロケールコードは、ダッシュボードで設定されているとおり、小文字のサブタグをハイフンで区切って渡してください。プラットフォームのロケール識別子をそのまま渡さないでください。Android では Locale.getDefault().toLanguageTag() が pt-BR を返し、iOS では NSLocale.currentLocale.localeIdentifier が pt_BR を返します。どちらもデフォルトのローカライズにフォールバックします。アプリ内で値を変換してから渡してください。
オンボーディング
オンボーディングはサーバー側でローカライズされており、サーバーのルールは他のフォーマットにも対応しています。getOnboarding に locale を渡すと:
- ロケール文字列が小文字に変換され、アンダースコア(
_)がすべてハイフン(-)に置き換えられます - Adapty は完全に一致するロケールコードのローカライゼーションを検索します
- 一致するものが見つからない場合、Adapty は最初のハイフンより前の部分文字列(
pt-brの場合はpt)を取り出し、一致するローカライゼーションを検索します - それでも一致するものが見つからない場合、Adapty はオンボーディングのデフォルトロケールのコンテンツを返します
このようにして、pt_BR、pt-BR、pt-br はすべて同じオンボーディングのローカライゼーションに解決されます。
ローカライズの実装
SDK v4 では、フローを取得する際にロケールコードを渡す必要はありません。getFlow はすべてのローカライズを含むフローを返し、フロービューが構築される際に Adapty が適切なものを適用します。
-
ビルダーで作成されたフロー: SDK はデバイスのロケールを読み取らないため、アプリ側でロケールを解決し、
createFlowViewのlocaleパラメータとして渡してください。このパラメータは省略可能です。省略した場合、フローはenでレンダリングされます。ただし、フローにenのローカライズが存在しない場合は、デフォルトのロケールが使用されます。import com.adapty.kmp.AdaptyUI AdaptyUI.createFlowView(flow = flow, locale = "es") .onSuccess { view -> view.present() } .onError { error -> // handle the error }
createNativeFlowView と AdaptyUIFlowPlatformView コンポーザブルは、どちらも同じオプションの locale パラメータを受け取ります。view.locale は、ビューの構築に使用されたローカライズを返します。locale パラメータと view.locale はどちらも Kotlin Multiplatform SDK 4.0.1-beta.1 以降が必要です。
- カスタム(リモートコンフィグ)ペイウォール:
getFlowは設定されたすべてのローカライズをflow.remoteConfigsに返します。各エントリはlocaleコードとdataMapを持つAdaptyRemoteConfigです。任意のフォールバックロジックを使って、ユーザーに合ったエントリを選択してください。
Adapty.getFlow("YOUR_PLACEMENT_ID")
.onSuccess { flow ->
val config = flow.remoteConfigs.firstOrNull { it.locale == "en" }
?: flow.remoteConfigs.firstOrNull()
// read your values from config?.dataMap
}
.onError { error ->
// handle the error
}Adapty は locale コードを Adapty のロケールコード標準 で説明されている形式で保存します。SDK はリモートコンフィグをロケールと照合しないため、どのエントリを適用するかはアプリ側で決定します。
これが重要な理由
ロケールコードが関係するシナリオはいくつかあります。たとえば、アプリの現在のローカライゼーションに対応する正しいペイウォールを取得しようとする場合などです。
ロケールコードはプラットフォームによって異なる複雑なものであるため、Adapty ではサポートするすべてのプラットフォームに共通の内部標準を使用しています。ただし、このコードが複雑である以上、サーバーに何を送信すると正しいローカライゼーションが返ってくるのか、そしてその後に何が起きるのかをしっかり理解しておくことが非常に重要です。そうすることで、常に期待どおりの結果を得られるようになります。
Adapty でのロケールコード標準
ロケールコードには、Adapty は少し改変した BCP 47 標準 を使用しています。コードはすべて小文字のサブタグをハイフンで区切った形式です。例: en(英語)、pt-br(ポルトガル語(ブラジル))、zh(簡体字中国語)、zh-hant(繁体字中国語)。
ロケールコードのマッチング
Adapty がクライアントサイド SDK からロケールコードを受け取ると、対応するペイウォールのローカライゼーションを次の手順で検索します。
- 受け取ったロケール文字列を小文字に変換し、アンダースコア(
_)をすべてハイフン(-)に置換します - ロケールコードが完全に一致するローカライゼーションを検索します
- 一致するものが見つからない場合は、最初のハイフンより前の部分文字列(
pt-brの場合はpt)を取り出し、一致するローカライゼーションを検索します - それでも一致するものが見つからない場合は、ペイウォールのデフォルトロケールのコンテンツを返します
このようにすることで、'pt_BR'を送信したiOSデバイス、pt-BRを送信したAndroidデバイス、pt-brを送信した別のデバイスが、すべて同じ結果を得られます。
ローカライズの実装:推奨方法
ローカライズについて気になっているなら、すでにプロジェクト内でローカライズされた文字列リソースを扱っているでしょう。その場合は、各ローカライズに対応するリソースファイルに、Adapty のロケールコードをキーとするキーと値のペアを追加することをおすすめします。そして、SDK を呼び出す際に、そのキーの値を取り出して使用します。例:
// 1. Add the Adapty locale code to your Compose Multiplatform resources
/*
composeResources/values/strings.xml (default — English)
*/
<string name="adapty_paywalls_locale">en</string>
/*
composeResources/values-es/strings.xml (Spanish)
*/
<string name="adapty_paywalls_locale">es</string>
/*
composeResources/values-pt-rBR/strings.xml (Portuguese — Brazil)
*/
<string name="adapty_paywalls_locale">pt-br</string>
// 2. Extract and use the locale code
suspend fun fetchPaywall() {
val locale = getString(Res.string.adapty_paywalls_locale)
Adapty.getPaywall(
placementId = "YOUR_PLACEMENT_ID",
locale = locale
).onSuccess { paywall ->
// the requested paywall
}.onError { error ->
// handle the error
}
}このようにすることで、アプリの全ユーザーに対してどのローカライズが取得されるかを完全にコントロールできます。
Compose Multiplatform のリソースを使用していない場合でも、同じ考え方は他のどのローカライズライブラリにも適用できます(例:moko-resources)。各ロケールのリソースバンドルに Adapty のロケールコードを文字列として保存し、SDK を呼び出す前にそれを読み込んでください。
ローカリゼーションの実装:別の方法
すべてのローカリゼーションにロケールコードを明示的に定義しなくても、同様の(ただし完全に同一ではない)結果を得ることができます。その場合、デバイスから直接ロケールコードを取得することになりますが、commonMain にはロケールの共通 API がないため、expect/actual 宣言が必要です。
// commonMain
expect fun currentLocaleTag(): String
// androidMain
actual fun currentLocaleTag(): String = Locale.getDefault().toLanguageTag()
// iosMain
actual fun currentLocaleTag(): String = NSLocale.currentLocale.localeIdentifier
// commonMain — pass the locale code to Adapty
suspend fun fetchPaywall() {
Adapty.getPaywall(
placementId = "YOUR_PLACEMENT_ID",
locale = currentLocaleTag()
).onSuccess { paywall ->
// the requested paywall
}.onError { error ->
// handle the error
}
}この方法はいくつかの理由からお勧めしません:
- iOSでは、ユーザーの優先言語とデバイスの地域ロケールは同一ではありません。
NSLocale.currentLocale.localeIdentifierは地域ロケールを返しますが、これはユーザーが実際にアプリで読む言語と異なる場合があります。ローカライズされた文字列ファイルを使用するiOSアプリは、Appleのresolveロジックによってこれらをうまく組み合わせるため、上記の推奨アプローチではそのまま動作します。 - デバイスが何を返すか、そしてそれがAdaptyのローカライズと一致するかどうかを正確に予測するのは困難です。デバイスのロケールには、Adaptyで設定していない拡張子や地域コードが含まれる場合があり、その場合SDKは最初のサブタグへのフォールバック、最終的には
enへのフォールバックを行います。
Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.