localization-patterns
モバイルアプリの多言語対応に必要な、文字列リソース管理、複数形対応、右から左への表示サポート、Composeローカライズ、KMP共有文字列、ロケール管理などを効率的に行うためのパターンを提供するSkill。
📜 元の英語説明(参考)
Localization patterns for mobile - string resources, plurals, RTL support, Compose localization, KMP shared strings, and locale management.
🇯🇵 日本人クリエイター向け解説
モバイルアプリの多言語対応に必要な、文字列リソース管理、複数形対応、右から左への表示サポート、Composeローカライズ、KMP共有文字列、ロケール管理などを効率的に行うためのパターンを提供するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o localization-patterns.zip https://jpskill.com/download/16424.zip && unzip -o localization-patterns.zip && rm localization-patterns.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/16424.zip -OutFile "$d\localization-patterns.zip"; Expand-Archive "$d\localization-patterns.zip" -DestinationPath $d -Force; ri "$d\localization-patterns.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
localization-patterns.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
localization-patternsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
モバイル向けのローカライゼーションパターン
Android
strings.xml リソース
<!-- res/values/strings.xml (デフォルト - 英語) -->
<resources>
<string name="app_name">MyApp</string>
<string name="welcome_message">Welcome, %1$s!</string>
<string name="price_format">%1$s %2$.2f</string>
<string-array name="categories">
<item>Electronics</item>
<item>Clothing</item>
<item>Books</item>
</string-array>
<plurals name="items_count">
<item quantity="zero">No items</item>
<item quantity="one">%d item</item>
<item quantity="other">%d items</item>
</plurals>
</resources>
<!-- res/values-es/strings.xml (スペイン語) -->
<resources>
<string name="app_name">MiApp</string>
<string name="welcome_message">Bienvenido, %1$s!</string>
<plurals name="items_count">
<item quantity="one">%d elemento</item>
<item quantity="other">%d elementos</item>
</plurals>
</resources>
<!-- res/values-ar/strings.xml (アラビア語 - RTL) -->
<resources>
<string name="welcome_message">%1$s ,مرحبًا</string>
<plurals name="items_count">
<item quantity="zero">لا عناصر</item>
<item quantity="one">عنصر واحد</item>
<item quantity="two">عنصران</item>
<item quantity="few">%d عناصر</item>
<item quantity="many">%d عنصرًا</item>
<item quantity="other">%d عنصر</item>
</plurals>
</resources>
引数を使用した文字列のフォーマット
// 位置引数: %1$s = 最初の文字列、%2$d = 2番目の整数
// strings.xml: <string name="greeting">Hello %1$s, you have %2$d new messages</string>
val formatted = context.getString(R.string.greeting, userName, messageCount)
// 複数形
val itemsText = context.resources.getQuantityString(
R.plurals.items_count,
count, // quantity selector
count // format argument
)
Compose ローカライゼーション
@Composable
fun WelcomeScreen(userName: String, itemCount: Int) {
Column {
Text(text = stringResource(R.string.welcome_message, userName))
Text(text = pluralStringResource(R.plurals.items_count, itemCount, itemCount))
}
}
アプリごとの言語設定 (API 33+)
// AndroidManifest.xml
<application android:localeConfig="@xml/locales_config" ...>
// res/xml/locales_config.xml
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="es" />
<locale android:name="ar" />
<locale android:name="ja" />
</locale-config>
// プログラムによる言語の変更
val localeManager = context.getSystemService(LocaleManager::class.java)
localeManager.applicationLocales = LocaleList(Locale.forLanguageTag("es"))
// API 33 より前の AppCompat の場合
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags("es")
)
RTL レイアウトのサポート
<!-- AndroidManifest.xml -->
<application android:supportsRtl="true" ...>
<!-- left/right の代わりに start/end を使用 -->
<TextView
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:paddingStart="12dp"
android:textAlignment="viewStart" />
// Compose RTL
@Composable
fun AdaptiveLayout() {
val layoutDirection = LocalLayoutDirection.current
val isRtl = layoutDirection == LayoutDirection.Rtl
Row(
modifier = Modifier.padding(start = 16.dp, end = 8.dp)
// start/end は RTL で自動的に反転
) {
Icon(
imageVector = if (isRtl) Icons.Default.ArrowBack else Icons.Default.ArrowForward,
contentDescription = null
)
Text(text = stringResource(R.string.next))
}
}
iOS
Localizable.strings
/* Localizable.strings (英語) */
"app_name" = "MyApp";
"welcome_message" = "Welcome, %@!";
"price_format" = "%@ %.2f";
"settings_title" = "Settings";
/* Localizable.strings (スペイン語) */
"app_name" = "MiApp";
"welcome_message" = "Bienvenido, %@!";
"settings_title" = "Configuración";
String Catalogs (.xcstrings - Xcode 15+)
Xcode 15 では、.strings および .stringsdict を置き換える単一の JSON ファイルとして、String Catalogs (.xcstrings) が導入されました。Xcode はコードからローカライズ可能な文字列を自動的に抽出し、翻訳を一元管理します。
String(localized:) と NSLocalizedString
// モダン (iOS 16+)
let welcome = String(localized: "welcome_message")
// 引数付き
let greeting = String(localized: "Welcome, \(userName)!")
// レガシー
let title = NSLocalizedString("settings_title", comment: "Title for settings screen")
SwiftUI Text と LocalizedStringKey
struct WelcomeView: View {
let userName: String
var body: some View {
VStack {
// 自動的に Localizable.strings を使用
Text("settings_title")
// 補間付き
Text("Welcome, \(userName)!")
// 明示的なローカライズされたキー
Text(LocalizedStringKey("welcome_message"))
}
}
}
.stringsdict を使用した複数形ルール
<!-- Localizable.stringsdict -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ...>
<plist version="1.0">
<dict>
<key>items_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No items</string>
<key>one</key>
<string>%d item</string>
<key>other</key>
<string>%d items</string>
</dict>
</dict>
</dict>
</plist>
let text = String(format: NSLocalizedString("items_count", comment: ""), itemCount)
SwiftUI での RTL サポート
str 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Localization Patterns for Mobile
Android
strings.xml Resources
<!-- res/values/strings.xml (default - English) -->
<resources>
<string name="app_name">MyApp</string>
<string name="welcome_message">Welcome, %1$s!</string>
<string name="price_format">%1$s %2$.2f</string>
<string-array name="categories">
<item>Electronics</item>
<item>Clothing</item>
<item>Books</item>
</string-array>
<plurals name="items_count">
<item quantity="zero">No items</item>
<item quantity="one">%d item</item>
<item quantity="other">%d items</item>
</plurals>
</resources>
<!-- res/values-es/strings.xml (Spanish) -->
<resources>
<string name="app_name">MiApp</string>
<string name="welcome_message">Bienvenido, %1$s!</string>
<plurals name="items_count">
<item quantity="one">%d elemento</item>
<item quantity="other">%d elementos</item>
</plurals>
</resources>
<!-- res/values-ar/strings.xml (Arabic - RTL) -->
<resources>
<string name="welcome_message">%1$s ,مرحبًا</string>
<plurals name="items_count">
<item quantity="zero">لا عناصر</item>
<item quantity="one">عنصر واحد</item>
<item quantity="two">عنصران</item>
<item quantity="few">%d عناصر</item>
<item quantity="many">%d عنصرًا</item>
<item quantity="other">%d عنصر</item>
</plurals>
</resources>
String Formatting with Arguments
// Positional arguments: %1$s = first string, %2$d = second integer
// strings.xml: <string name="greeting">Hello %1$s, you have %2$d new messages</string>
val formatted = context.getString(R.string.greeting, userName, messageCount)
// Plurals
val itemsText = context.resources.getQuantityString(
R.plurals.items_count,
count, // quantity selector
count // format argument
)
Compose Localization
@Composable
fun WelcomeScreen(userName: String, itemCount: Int) {
Column {
Text(text = stringResource(R.string.welcome_message, userName))
Text(text = pluralStringResource(R.plurals.items_count, itemCount, itemCount))
}
}
Per-App Language Preference (API 33+)
// AndroidManifest.xml
<application android:localeConfig="@xml/locales_config" ...>
// res/xml/locales_config.xml
<?xml version="1.0" encoding="utf-8"?>
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en" />
<locale android:name="es" />
<locale android:name="ar" />
<locale android:name="ja" />
</locale-config>
// Programmatic language change
val localeManager = context.getSystemService(LocaleManager::class.java)
localeManager.applicationLocales = LocaleList(Locale.forLanguageTag("es"))
// For pre-API 33 with AppCompat
AppCompatDelegate.setApplicationLocales(
LocaleListCompat.forLanguageTags("es")
)
RTL Layout Support
<!-- AndroidManifest.xml -->
<application android:supportsRtl="true" ...>
<!-- Use start/end instead of left/right -->
<TextView
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:paddingStart="12dp"
android:textAlignment="viewStart" />
// Compose RTL
@Composable
fun AdaptiveLayout() {
val layoutDirection = LocalLayoutDirection.current
val isRtl = layoutDirection == LayoutDirection.Rtl
Row(
modifier = Modifier.padding(start = 16.dp, end = 8.dp)
// start/end automatically flip in RTL
) {
Icon(
imageVector = if (isRtl) Icons.Default.ArrowBack else Icons.Default.ArrowForward,
contentDescription = null
)
Text(text = stringResource(R.string.next))
}
}
iOS
Localizable.strings
/* Localizable.strings (English) */
"app_name" = "MyApp";
"welcome_message" = "Welcome, %@!";
"price_format" = "%@ %.2f";
"settings_title" = "Settings";
/* Localizable.strings (Spanish) */
"app_name" = "MiApp";
"welcome_message" = "Bienvenido, %@!";
"settings_title" = "Configuración";
String Catalogs (.xcstrings - Xcode 15+)
Xcode 15 introduces String Catalogs (.xcstrings) as a single JSON file replacing .strings and .stringsdict. Xcode auto-extracts localizable strings from code and manages translations in one place.
String(localized:) and NSLocalizedString
// Modern (iOS 16+)
let welcome = String(localized: "welcome_message")
// With arguments
let greeting = String(localized: "Welcome, \(userName)!")
// Legacy
let title = NSLocalizedString("settings_title", comment: "Title for settings screen")
SwiftUI Text with LocalizedStringKey
struct WelcomeView: View {
let userName: String
var body: some View {
VStack {
// Automatically uses Localizable.strings
Text("settings_title")
// With interpolation
Text("Welcome, \(userName)!")
// Explicit localized key
Text(LocalizedStringKey("welcome_message"))
}
}
}
Plural Rules with .stringsdict
<!-- Localizable.stringsdict -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ...>
<plist version="1.0">
<dict>
<key>items_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@count@</string>
<key>count</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>d</string>
<key>zero</key>
<string>No items</string>
<key>one</key>
<string>%d item</string>
<key>other</key>
<string>%d items</string>
</dict>
</dict>
</dict>
</plist>
let text = String(format: NSLocalizedString("items_count", comment: ""), itemCount)
RTL Support in SwiftUI
struct AdaptiveRow: View {
@Environment(\.layoutDirection) var layoutDirection
var body: some View {
HStack {
// leading/trailing automatically respect RTL
Text("Label")
Spacer()
Image(systemName: layoutDirection == .rightToLeft
? "chevron.left" : "chevron.right")
}
.padding(.leading, 16)
.padding(.trailing, 8)
}
}
KMP Shared Strings
Common Interface with expect/actual
// commonMain
expect class StringProvider {
fun getString(key: String): String
fun getString(key: String, vararg args: Any): String
fun getPluralString(key: String, quantity: Int, vararg args: Any): String
}
// androidMain
actual class StringProvider(private val context: Context) {
actual fun getString(key: String): String {
val resId = context.resources.getIdentifier(key, "string", context.packageName)
return if (resId != 0) context.getString(resId) else key
}
actual fun getString(key: String, vararg args: Any): String {
val resId = context.resources.getIdentifier(key, "string", context.packageName)
return if (resId != 0) context.getString(resId, *args) else key
}
actual fun getPluralString(key: String, quantity: Int, vararg args: Any): String {
val resId = context.resources.getIdentifier(key, "plurals", context.packageName)
return if (resId != 0) context.resources.getQuantityString(resId, quantity, *args) else key
}
}
// iosMain
actual class StringProvider {
actual fun getString(key: String): String {
return NSBundle.mainBundle.localizedStringForKey(key, value = key, table = null)
}
actual fun getString(key: String, vararg args: Any): String {
val template = getString(key)
return String.format(template, *args)
}
actual fun getPluralString(key: String, quantity: Int, vararg args: Any): String {
val template = getString(key)
return String.format(template, *args)
}
}
moko-resources Library Pattern
// build.gradle.kts
commonMain {
dependencies {
implementation("dev.icerock.moko:resources:0.23.0")
}
}
// commonMain/resources/MR/base/strings.xml
// commonMain/resources/MR/es/strings.xml
// Access: MR.strings.welcome_message.getString()
Testing
Pseudo-Localization
Android: Enable in Developer Options > "Force pseudo-locales" to test with accented characters and RTL wrapping.
# Force locale on Android emulator
adb shell settings put system user_locale "ar-SA"
adb shell am restart
Layout Testing with Long Strings
Create values-en-rXA/strings.xml (pseudo-locale) to test text expansion. German and Finnish text is typically 30-40% longer than English.
// Compose UI test with locale
@Test
fun testLongTextLayout() {
composeTestRule.setContent {
CompositionLocalProvider(
LocalConfiguration provides Configuration().apply {
setLocale(Locale("de"))
}
) {
WelcomeScreen(userName = "Maximilian Schwarzenegger")
}
}
// Verify no text truncation or overflow
}