Skip to content

Commit

Permalink
extended Locale to support "script" and "extensions"
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcelHeckel committed Dec 21, 2023
1 parent d2371e0 commit b0dae4d
Show file tree
Hide file tree
Showing 7 changed files with 749 additions and 239 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package de.comahe.i18n4k

import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf

/** Default implementation for [Locale] for non JVM targets */
@Suppress("MemberVisibilityCanBePrivate", "unused")
class DefaultLocaleImpl(
private val language: String,
private val script: String,
private val country: String,
private val variant: String,
private val extensions: ImmutableMap<Char, String>,
) {
constructor(
language: String
) : this(language, "", "")

constructor(
language: String,
country: String,
) : this(language, country, "")


constructor(
language: String,
country: String,
variant: String,
) : this(language, "", country, variant, persistentMapOf())

fun getLanguage(): String = language
fun getScript(): String = script
fun getCountry(): String = country
fun getVariant(): String = variant


fun hasExtensions(): Boolean = extensions.isNotEmpty()

fun stripExtensions(): DefaultLocaleImpl {
if (hasExtensions())
return DefaultLocaleImpl(language, script, country, variant, persistentMapOf())
return this
}

fun getExtension(key: Char): String? = extensions[key]

fun getExtensionKeys(): Set<Char> = extensions.keys

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is DefaultLocaleImpl) return false

if (language != other.language) return false
if (script != other.script) return false
if (country != other.country) return false
if (variant != other.variant) return false
if (extensions != other.extensions) return false

return true
}

override fun hashCode(): Int {
var result = language.hashCode()
result = 31 * result + script.hashCode()
result = 31 * result + country.hashCode()
result = 31 * result + variant.hashCode()
result = 31 * result + extensions.hashCode()
return result
}

override fun toString(): String {
return toLocaleTag(language, script, country, variant, extensions)
}
}

/** Default implementation for [createLocale] for non JVM targets */
fun createDefaultLocaleImpl(
language: String,
script: String?,
country: String?,
variant: String?,
extensions: Map<Char, String>?
): DefaultLocaleImpl {
val extensionsBuilder = persistentMapOf<Char, String>().builder()
extensions?.forEach { (key, value) ->
extensionsBuilder[key.lowercaseChar()] = value.lowercase()
}

return DefaultLocaleImpl(
language.trim().lowercase(),
script?.trim()?.capitalize() ?: "",
country?.trim()?.uppercase() ?: "",
variant?.trim()?.lowercase() ?: "",
extensionsBuilder.build()
)
}
108 changes: 97 additions & 11 deletions i18n4k-core/src/commonMain/kotlin/de/comahe/i18n4k/Locale.kt
Original file line number Diff line number Diff line change
@@ -1,38 +1,124 @@
package de.comahe.i18n4k

/** Class representing a locale */
expect class Locale(
/** See [Locale.getLanguage] */
language: String,
/** See [Locale.getCountry] */
country: String,
/** See [Locale.getVariant] */
variant: String
) {

/** Class representing a locale */
expect class Locale {
@Deprecated(
message = "Use `createLocale`",
replaceWith = ReplaceWith("createLocale(language)"),
level = DeprecationLevel.WARNING
)
constructor(
/** See [Locale.getLanguage] */
language: String
)

@Deprecated(
message = "Use `createLocale`",
replaceWith = ReplaceWith("createLocale(language, null, country)"),
level = DeprecationLevel.WARNING
)
constructor(
/** See [Locale.getLanguage] */
language: String,
/** See [Locale.getCountry] */
country: String
)

@Deprecated(
message = "Use `createLocale`",
replaceWith = ReplaceWith("createLocale(language, null, country, variant)"),
level = DeprecationLevel.WARNING
)
constructor (
/** See [Locale.getLanguage] */
language: String,
/** See [Locale.getCountry] */
country: String,
/** See [Locale.getVariant] */
variant: String
)


/** Language code. Should be a [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) */
/**
* Language code.
*
* Should be a
* [ISO 639-1 code](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes).
*
* Will be lowercase.
*/
fun getLanguage(): String

/**country/region code for this locale, which should either be the empty string, an uppercase ISO 3166 2-letter code, or a UN M.49 3-digit code. */
/**
* Returns the script for this locale, which should either be the empty
* string or an "ISO 15924 4"-letter script code.
*
* The first letter is uppercase and the rest are lowercase, for example,
* 'Latn', 'Cyrl'.
*/
fun getScript(): String

/**
* country/region code for this locale, which should either be the empty
* string, an uppercase ISO 3166 2-letter code, or a UN M.49 3-digit code.
*/
fun getCountry(): String

/** the variant code for this locale. Can be an empty string */
fun getVariant(): String

/** Returns `true` if this `Locale` has any extensions. */
fun hasExtensions(): Boolean

/**
* Returns a copy of this (or this itself) `Locale` with no extensions.
*
* If this `Locale` has no extensions, this `Locale` is returned.
*/
fun stripExtensions(): Locale

/**
* Returns the extension (or private use) value associated with the
* specified key, or null if there is no extension associated with the key.
*
* To be well-formed, the key must be one of `[0-9A-Za-z]`.
*
* Keys are case-insensitive, so for example, 'z' and 'Z' represent the
* same extension.
*
* @param key the extension key
* @return The extension, or null if this locale defines no extension for
* the specified key.
* @throws IllegalArgumentException if key is not well-formed
*/
fun getExtension(key: Char): String?

/**
* Returns the set of extension keys associated with this locale, or the
* empty set if it has no extensions.
*
* The returned set is unmodifiable.
*
* The keys will all be lower-case.
*/
fun getExtensionKeys(): Set<Char>
}

/**
* Create a local with extensions.
*
* Regarding extensions see:
* * [language-tags](https://www.w3.org/International/articles/language-tags/#extension)
* * [rfc5646](https://www.rfc-editor.org/rfc/rfc5646.html#section-2.2.6)
*/
expect fun createLocale(
language: String,
script: String? = null,
country: String? = null,
variant: String? = null,
extensions: Map<Char, String>? = null,
): Locale

/** The current locale of the system or user */
expect val systemLocale: Locale
Loading

0 comments on commit b0dae4d

Please sign in to comment.