Locale (Localization)

SkillDev tools

The KMP Starter Template localization system — StarterLocales, StarterLocale, LocaleProvider, and the locale selector UI in features/locale.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Locale (Localization) skill

What this skill tells your AI

The instructions your AI receives, as published by devatrii/kmp-starter-template in .agents/skills/kmp-starter/starter-features/locale/SKILL.md and read by ahel’s review.

In-app language switching with RTL/LTR support. Module: features/locale.

Where things live

PiecePath
StarterLocales (registry) / StarterLocalefeatures/locale/src/commonMain/.../StarterLocale.kt
LocaleProviderfeatures/locale/src/commonMain/.../LocaleProvider.kt
LocalAppLocale (expect/actual).../LocalAppLocale.kt (+ .android.kt, .ios.kt)
rememberStarterActiveLocale / rememberStarterLocaleDataStore.../LocaleProvider.kt
LocaleSelectorDropdown / LocaleSelectorContainer.../ui/LocaleSelectorDropdown.kt, LocaleSelectorContainer.kt

Data model

data class StarterLocale(
    val emoji: String,                 // e.g. "🇵🇰"
    val langCode: String,              // "en", "ur", "es", "es_AR"
    val displayName: StringResource,   // localized label (Res.string.lang_*)
    val layoutDirection: LayoutDirection = LayoutDirection.Ltr,
)

StarterLocales is a registry:

object StarterLocales {
    val DEFAULT = StarterLocale("🇺🇸", "en", Res.string.lang_en)
    val locales get() = _locales.toSet()
    fun add(locale: StarterLocale)
    fun add(locales: Iterable<StarterLocale>)
    fun findBy(langCode: String): StarterLocale?
}

Register locales + provide them

In App.kt (composeApp), define supported locales and wrap the app with LocaleProvider:

val supportedLocales = setOf(
    StarterLocale("🇵🇰", "ur", Res.string.lang_ur, LayoutDirection.Rtl),
    StarterLocale("🇮🇳", "hi", Res.string.lang_hi),
    StarterLocale("🇪🇸", "es", Res.string.lang_es),
)

LocaleProvider(
    locales = supportedLocales,
    overrideDefault = StarterLocales.findBy("en"),
) {
    // app content — provides LocalAppLocale + LocalLayoutDirection
}

LocaleProvider resolves the active locale with priority: User preference → overrideDefault → System locale → DEFAULT. It provides both LocalAppLocale (lang code) and LocalLayoutDirection (RTL/LTR) to the tree. The selection persists to DataStore.

Locale selector UI

Drop in LocaleSelectorDropdown (Cupertino-style) where users pick a language:

LocaleSelectorDropdown(
    label = Res.string.choose_language,
    isLast = true,
)

LocaleSelectorContainer wraps the stateful selection; LocaleSelectorDropdown lists StarterLocales.locales and writes the choice back to the DataStore.

Rules

  • Add languages as Res.string.lang_* resources + StarterLocale entries (see resources-theme skill).
  • Use LocaleProvider for resolution — don't manage locale state manually.
  • Respect layoutDirection for RTL languages.
  • No parallel localization system.

Reference

  • Docs: https://starter.atherio.dev/fundamentals/07-multiple-languages/
  • Source: features/locale/*

Signals

GitHub stars
162
Forks
28
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
kmp-starter-feature-locale
Source
github.com/devatrii/kmp-starter-template