Skip to content

🌍 Locale#

A simple, serializable and modern Kotlin data class for handling locales.
This library provides a type-safe alternative to java.util.Locale, designed for Kotlin Multiplatform projects.

🎯 Supported Targets#

The following targets are supported:

Platform Targets
JVM & Android jvm, android
Apple ios, macos, tvos, watchos
Web js, wasmJs
Native & Other androidNative, linux, mingw, wasmWasi

✨ Features#

  • Serializable: Annotated with @Serializable, ready for kotlinx.serialization (JSON, Protobuf, etc.)
  • Rich Data Models: Provides type-safe Country and Continent classes packed with useful data like ISO codes (Alpha-2, Alpha-3, Numeric), telephone codes, domains, continent codes and even emoji flags.
  • Flexible Parsing: Can parse locales from common language tags (e.g. en-US or fr_CA) with customizable delimiters.
  • Java Interop: Seamless integration with java.util.Locale for easy migration.
  • Apple Interop: Seamless integration with Foundation.Locale for easy migration.

🚀 Installation#

Integration using Version Catalog is highly recommended for aligned version usage.

First declare the library in your Version Catalog:

[libraries]
kommons-locale = { group = "dev.datlag.kommons", name = "locale", version.ref = "kommons" }

Then add the dependency to your module:

dependencies {
    implementation(libs.kommons.locale)
}

Simply add the dependency like this:

dependencies {
    implementation("dev.datlag.kommons:locale:<version>")
}

🛠️ Usage#

Creating a Locale object:

// From language and country strings
val byStrings = Locale("en", "US")

// By parsing a BCP 47 language tag (handles mixed delimiters)
val byTag = Locale.forLanguageTag("sr_Latn-RS")

// Get the system's default locale
val systemDefault = Locale()

// Using the full constructor for scripts and variants
val fullLocale = Locale(
    language = "zh",
    country = "CN",
    script = "Hans",
    variant = "PINYIN"
)

Getting Properties

Accessing Locale properties is easy:

val locale = Locale.forLanguageTag("fr-CA")

println(locale.language) // "fr"
println(locale.country) // Country object for Canada

Converting to a Language Tag

The toString() method is implemented to return a BCP 47 compliant language tag.

val locale = Locale("zh", "CN", script = "Hans")
println(locale.toString()) 
// Output: "zh-Hans-CN"

Receiving a Country object:

// Look up by Alpha-2, Alpha-3
// Also tries to parse numeric code
val byString = Country.forCodeOrNull("DE")

// Look up by ISO 3166-1 numeric code
val byNumber = Country.forCodeOrNull(8) // Albania

Getting Properties

Accessing the rich Country object:

val country = locale.country
if (country != null) {
    println(country.emoji)        // "🇨🇦"
    println(country.codeAlpha3)   // e.g., Code.Alpha3("CAN")
    println(country.codeNumeric)  // e.g., Code.Numeric("124")
    println(country.telephoneCodes) // e.g., [Code.Telephone("+1")]
    println(country.continent)    // Continent object for Country
}

Code Types

All code types (Code.Alpha2, Code.Alpha3, Code.Numeric, etc...) implement either a CharSequence or extend Number.
This decision was made to be as flexible as possible instead of simply providing a String, while providing and keeping easy usage.

Receiving a Continent object:

// Look up by GeoName or STANAG code
// Also tries to parse UN M49 code
val byString = Continent.forCodeOrNull("EU") // Europe

// Look up by UN M49 code
val byNumber = Continent.forCodeOrNull(142) // Asia

Getting Properties

Access nested Continent data:

val continent = country.continent
println(continent.geoName) // e.g., Code.GeoName("NA")

Code Types

All code types (Code.GeoName, Code.STANAG, Code.Numeric) implement either a CharSequence or extend Number.
This decision was made to be as flexible as possible instead of simply providing a String, while providing and keeping easy usage.