Android localization
Skill almasumdev/awesome-kotlin-android-agent-skills/.github/skills/ui/android-localization
Curated agent skills, conventions, and workflows for building Kotlin Android apps with AI coding agents.
npx -y skills add almasumdev/awesome-kotlin-android-agent-skills --skill android-localizationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Expert guidance on Android localization, res/values-*, string plurals, ICU MessageFormat, RTL layouts, and per-app locale (AppCompat 1.7 / Android 13+). Use this for any i18n / l10n task.
SKILL.md
4.7 KB, as published. Nobody here has run it
Android Localization & Internationalization
Instructions
1. Resource Directory Strategy
app/src/main/res/
├── values/ strings.xml (default, English)
├── values-es/ Spanish
├── values-pt-rBR/ Brazilian Portuguese (region-qualified)
├── values-ar/ Arabic (auto-RTL)
├── values-night/ dark theme overrides
└── values-w600dp/ tablet overrides
Rules:
- Default
values/strings.xmlis English. Never leave English strings in region-specific folders. - Region qualifier format is
-rXX(BCP-47 lowercase-then-r-then-uppercase region). - Never concatenate strings in code. Use placeholders and ICU.
2. Plurals with ICU MessageFormat
<!-- res/values/strings.xml -->
<string name="unread_count">{count, plural,
=0 {No unread messages}
one {# unread message}
other {# unread messages}
}</string>
Load with getQuantityString replacement:
val formatted = MessageFormat.format(
getString(R.string.unread_count),
mapOf("count" to count),
)
Use android.icu.text.MessageFormat (API 24+, matches our minSdk = 24). It handles plural, select, and gender correctly in every locale. Do not use the legacy <plurals> XML tag for new strings — ICU is more flexible.
3. Interpolation in Compose
<string name="greeting">Hello, %1$s!</string>
Text(stringResource(R.string.greeting, user.name))
For ICU patterns pass through MessageFormat.format and wrap in a helper:
@Composable
fun icuStringResource(@StringRes id: Int, vararg args: Pair<String, Any>): String {
val template = stringResource(id)
return remember(id, args) { MessageFormat.format(template, args.toMap()) }
}
4. Right-to-Left (RTL) Support
In AndroidManifest.xml:
<application android:supportsRtl="true" ... >
In Compose:
- Use
start/endalignment (already default in Compose;Arrangement.Startflips automatically). - Mirror directional icons:
Icon(Icons.AutoMirrored.Filled.ArrowBack, null)— note theAutoMirroredpackage. - Test with Developer Options → Force RTL layout direction and with Arabic (
values-ar) locale.
5. Per-App Locale (Android 13+, AppCompat 1.7+)
// Set from Settings screen
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags("pt-BR"))
// Read
val current = AppCompatDelegate.getApplicationLocales()[0]
Declare the supported set so the system can expose a per-app language picker:
<!-- res/xml/locales_config.xml -->
<locale-config xmlns:android="http://schemas.android.com/apk/res/android">
<locale android:name="en"/>
<locale android:name="es"/>
<locale android:name="pt-BR"/>
<locale android:name="ar"/>
</locale-config>
<application android:localeConfig="@xml/locales_config" ... >
For Android 12 and below, AppCompat stores the choice in a backing store and re-applies it at each Activity create.
6. Dates, Numbers, Currency
Never format these manually. Use java.text.NumberFormat, java.text.DateFormat, or kotlinx.datetime combined with the user's locale:
val money = NumberFormat.getCurrencyInstance(Locale.getDefault()).format(19.99)
val day = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM).withLocale(Locale.getDefault()).format(LocalDate.now())
7. Pseudolocales for Testing
Enable Developer Options → "Force pseudolocale", pick en-XA (accented) or ar-XB (RTL-bidi). This catches:
- Hard-coded English.
- Strings that overflow at +40% length.
- Layouts that don't flip for RTL.
Also add to your debug build:
android.defaultConfig { /* ... */ }
android.buildTypes.debug { pseudoLocalesEnabled = true }
8. Translation Workflow
- Keep
strings.xmlsorted alphabetically by key; usetranslatable="false"for non-translatable identifiers. - Use
tools:ignore="MissingTranslation"only for intentionally untranslated keys (e.g., brand names). - Integrate with a TMS (Crowdin, Lokalise, Phrase) via their Gradle plugin so translators pull and push directly.
Checklist
-
supportsRtl="true"and alocales_configare declared in the manifest. - All strings come from resources; no hard-coded English in Kotlin or XML.
- Plurals and selects use ICU
MessageFormat. - Directional icons use
Icons.AutoMirrored.*. - Dates/numbers/currency format with the current
Locale. -
pseudoLocalesEnabled = truein debug builds; screens tested aten-XAandar-XB. - Per-app locale API is wired for Android 13+.