I18n.kt

package com.hexagonkt.core

import java.util.*
import kotlin.reflect.KClass

/** Set of allowed country codes in this JVM. */
val countryCodes: Set<String> by lazy {
    Locale.getISOCountries().toSet()
}

/** Set of allowed language codes in this JVM. */
val languageCodes: Set<String> by lazy {
    Locale.getISOLanguages().toSet()
}

/** Set of allowed currency codes in this JVM. */
val currencyCodes: Set<String> by lazy {
    Currency.getAvailableCurrencies().map { it.currencyCode }.toSet()
}

/**
 * [TODO](https://github.com/hexagontk/hexagon/issues/271).
 *
 * @param locale .
 * @return .
 */
inline fun <reified T : ResourceBundle> resourceBundle(
    locale: Locale = Locale.getDefault()): ResourceBundle =
    resourceBundle(T::class, locale)

/**
 * [TODO](https://github.com/hexagontk/hexagon/issues/271).
 *
 * @param type .
 * @param locale .
 * @return .
 */
fun <T : ResourceBundle> resourceBundle(
    type: KClass<T>, locale: Locale = Locale.getDefault()): ResourceBundle =
    ResourceBundle.getBundle(type.java.name, locale)

/**
 * [TODO](https://github.com/hexagontk/hexagon/issues/271).
 *
 * @param language .
 * @param country .
 * @return .
 */
fun localeOf(language: String = "", country: String = ""): Locale {
    require(language.isNotEmpty() || country.isNotEmpty()) {
        "A non-blank language or country is required"
    }
    require(language.isEmpty() || language in languageCodes) { "Language: '$language' not allowed" }
    require(country.isEmpty() || country in countryCodes) { "Country: '$country' not allowed" }
    return Locale.Builder().setLanguage(language).setRegion(country).build()
}

/**
 * [TODO](https://github.com/hexagontk/hexagon/issues/271).
 *
 * @param language .
 * @param country .
 * @return .
 */
fun localeOfOrNull(language: String = "", country: String = ""): Locale? =
    try {
        localeOf(language, country)
    }
    catch (_: IllegalArgumentException) {
        null
    }

fun languageOf(language: String): Locale =
    localeOf(language = language)

fun languageOfOrNull(language: String): Locale? =
    try {
        languageOf(language)
    }
    catch (_: IllegalArgumentException) {
        null
    }

fun countryOf(country: String): Locale =
    localeOf(country = country)

fun countryOfOrNull(country: String): Locale? =
    try {
        countryOf(country)
    }
    catch (_: IllegalArgumentException) {
        null
    }

fun parseLocale(languageCountry: String): Locale =
    languageCountry
        .split("_")
        .let {
            when {
                it.size == 1 && it[0].all { c -> c.isUpperCase() } -> countryOf(it[0])
                it.size == 1 -> languageOf(it[0])
                it.size == 2 -> localeOf(it[0], it[1])
                else -> throw IllegalArgumentException("Invalid locale format: $languageCountry")
            }
        }

fun parseLocaleOrNull(languageCountry: String): Locale? =
    try {
        parseLocale(languageCountry)
    }
    catch (_: IllegalArgumentException) {
        null
    }