Skip to content

ninenox/KotlinLocalization

Repository files navigation

KotlinLocalization

Kotlin Version CI License Maven Central

Android Kotlin library for changing the UI language at runtime. Supports Views, Jetpack Compose, and Android 13+ per-app language settings.

Quick Start

1. Add dependency

implementation("io.github.ninenox:kotlin-locale-manager:1.3.0")

2. Create Application class

class App : ApplicationLocale()

Register in AndroidManifest.xml:

<application android:name=".App" ... />

3. Add locale-specific string resources

res/
  values/strings.xml        ← default (English)
  values-th/strings.xml     ← Thai
  values-ja/strings.xml     ← Japanese

4. Extend your Activity

class MainActivity : AppCompatActivityBase() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

5. Switch language

setNewLocale(LocaleManager.LANGUAGE_THAI)   // from AppCompatActivityBase
setNewLocale("fr")                           // any BCP 47 tag works

That's it. The UI refreshes automatically.


Installation

Add mavenCentral() to your repositories and include the dependency in your module build file:

// build.gradle.kts
repositories {
    mavenCentral()
}

dependencies {
    implementation("io.github.ninenox:kotlin-locale-manager:1.3.0")
}

Usage

1. Read the current language code

ApplicationLocale.localeManager?.language // e.g. "en", "th"

2. Get the current Locale instance

val locale = LocaleManager.getLocale(resources)

if (locale.country == "TH") { /* ... */ }

val dateFormat = java.text.DateFormat.getDateInstance(java.text.DateFormat.SHORT, locale)
val formatted = dateFormat.format(java.util.Date())

3. Jetpack Compose

Use rememberLocaleManager() to get the manager and observe changes:

@Composable
fun MyScreen() {
    val localeManager = rememberLocaleManager() ?: return
    val context = LocalContext.current
    val language by localeManager.localeAsState()

    Text("Current language: $language")
    Button(onClick = { localeManager.setNewLocale(context, LocaleManager.LANGUAGE_THAI) }) {
        Text("Switch to Thai")
    }
}

Or observe the full Locale object:

val locale by localeManager.currentLocaleAsState()

Inject via CompositionLocal:

CompositionLocalProvider(LocalLocaleManager provides ApplicationLocale.localeManager) {
    val manager = LocalLocaleManager.current
    Button(onClick = { manager?.setNewLocale(context, LocaleManager.LANGUAGE_THAI) }) {
        Text("Switch to Thai")
    }
}

4. Without extending AppCompatActivityBase

If you already have a custom base Activity and cannot change it, use LocaleHelper instead:

class MainActivity : MyCustomBaseActivity() {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(LocaleHelper.wrap(newBase))
    }

    override fun applyOverrideConfiguration(config: Configuration?) {
        LocaleHelper.applyOverrideConfiguration(baseContext, config)
        super.applyOverrideConfiguration(config)
    }

    fun changeLanguage() {
        LocaleHelper.setNewLocale(this, LocaleManager.LANGUAGE_THAI)
    }
}

5. Observe in a ViewModel

LocaleManager exposes StateFlow properties you can collect in a ViewModel or convert to LiveData:

// StateFlow<String> — language code
val languageFlow: StateFlow<String> = localeManager.localeFlow

// StateFlow<Locale> — typed Locale
val localeFlow: StateFlow<Locale> = localeManager.currentLocaleFlow

// LiveData (requires lifecycle-livedata-ktx)
val localeLiveData: LiveData<Locale> = localeManager.currentLocaleFlow.asLiveData()

Supported Languages

Built-in constants in LocaleManager:

Constant Tag Language
LANGUAGE_ENGLISH en English
LANGUAGE_THAI th Thai
LANGUAGE_JAPANESE ja Japanese
LANGUAGE_KOREAN ko Korean
LANGUAGE_CHINESE_SIMPLIFIED zh-CN Chinese (Simplified)
LANGUAGE_CHINESE_TRADITIONAL zh-TW Chinese (Traditional)
LANGUAGE_ARABIC ar Arabic
LANGUAGE_SPANISH es Spanish
LANGUAGE_FRENCH fr French
LANGUAGE_GERMAN de German
LANGUAGE_PORTUGUESE pt Portuguese
LANGUAGE_PORTUGUESE_BRAZIL pt-BR Portuguese (Brazil)
LANGUAGE_RUSSIAN ru Russian
LANGUAGE_ITALIAN it Italian
LANGUAGE_HINDI hi Hindi
LANGUAGE_INDONESIAN id Indonesian
LANGUAGE_VIETNAMESE vi Vietnamese
LANGUAGE_MALAY ms Malay
LANGUAGE_TURKISH tr Turkish
LANGUAGE_DUTCH nl Dutch
LANGUAGE_POLISH pl Polish
LANGUAGE_UKRAINIAN uk Ukrainian
LANGUAGE_BENGALI bn Bengali
LANGUAGE_FARSI fa Farsi / Persian

Any valid BCP 47 language tag also works — pass it directly as a string.

vs. Android's Built-in Per-App Language (API 33+)

Android 13 introduced AppCompatDelegate.setApplicationLocales() as a system-level solution. Here's how this library relates to it:

KotlinLocalization Android built-in only
Min API 21 21 (AppCompat backport)
Appears in system Settings → App language ✓ (API 33+)
Works without android:localeConfig requires it for Settings integration
StateFlow / Compose support manual wiring needed
FragmentBase helper manual
Single-line language switch setNewLocale("th") multi-step setup

This library wraps AppCompatDelegate on API 33+ so you get system-level integration automatically while keeping a simple, unified API across all API levels.

Limitations

  • RTL languages (Arabic, Farsi, Hebrew) require android:supportsRtl="true" in AndroidManifest.xml for layout mirroring.
  • The language setting is stored in SharedPreferences on API < 33 and in system storage on API 33+. Clearing app data resets the language to system default.
  • If your Activity uses a custom AppCompatDelegate or theme engine (e.g. Aesthetic), you may need to call applyOverrideConfiguration manually.

Testing

Run unit tests with Gradle:

./gradlew test

License

Licensed under the Apache License 2.0. See the LICENSE file for details.

About

Android Kotlin library for changing UI language at runtime. Supports Views, Jetpack Compose, and Android 13+ per-app language settings.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages