Hi all. I'm really struggling trying to work with...
# multiplatform
m
Hi all. I'm really struggling trying to work with locales and strings in multiplatform. I'm basically trying to implement a function which gets the system locale, and asks my string table for that value in that language, and if not, falls back to using a particular bundle:
Copy code
fun localizableString(
    stringTable: StringTable,
    name: String,
    bundle: NSBundle
)
But i have yet to figure out how to determine the current app's locale, or even get the locale in the bundle instance. There's supposedly functions in NSLocale for that (
NSLocale.currentLocale
) but this function seems to not be showing up in kotlin. Also, bundle.preferredLocalizations returns an Any type, and i can't really figure out what the real type should be.
p
Hey, I ended up doing all of that in the Kotlin way:
Copy code
enum class StringKey {
    APP_NAME,
    APP_DESCRIPTION,
    ACTION_RETRY,
    ACTION_ALLOW,
    ACTION_DENY,
    ACTION_CANCEL,
    ACTION_CONFIRM,
    ACTION_GO_BACK,
    ...
    ...
}

enum class SupportedLocale { EN, TR, PT_BR }

object Strings {
    private var defaultLocale = SupportedLocale.EN
    private var currentLocale = mutableStateOf(SupportedLocale.EN)

    private val strings: Map<SupportedLocale, Map<StringKey, String>> = mapOf(
        SupportedLocale.EN to stringsEN,
        <http://SupportedLocale.TR|SupportedLocale.TR> to stringsTR,
        SupportedLocale.PT_BR to stringsPT_BR,
    )

    fun getLocale() = currentLocale.value

    fun setLocale(locale: String, region: String? = null) {
        currentLocale.value = if (region != null) {
            val localeAndRegion = locale + "_" + region
            SupportedLocale.entries.find { it.name == localeAndRegion.uppercase() } ?: defaultLocale
        } else {
            SupportedLocale.entries.find { it.name == locale.uppercase() } ?: defaultLocale
        }
    }

    fun get(key: StringKey): String {
        return strings[currentLocale.value]?.get(key)
            ?: strings[defaultLocale]?.get(key)
            ?: key.name
    }
}
Copy code
val stringsEN = mapOf(
    StringKey.APP_NAME to "Tracker - Manager for Bluesky",
    StringKey.APP_DESCRIPTION to "Track your Bluesky followers, unfollowers, blocks, and posts in real-time — with post analytics and advanced filters to find inactive or stat-matching accounts.",
    StringKey.ACTION_RETRY to "Retry",
    StringKey.ACTION_ALLOW to "Allow",
    StringKey.ACTION_DENY to "Deny",
    ...
    ...
)
And I init the language with the following (I also have an in-app settings property):
Copy code
private fun initLanguage() {
    val savedLanguage = cache.getLanguage()
    if (savedLanguage.isEmpty()) {
        val region = if (Locale.current.region == "BR") {
            Locale.current.region
        } else {
            null
        }
        Strings.setLocale(Locale.current.language, region)
    } else {
        Strings.setLocale(savedLanguage)
    }
}
So I don't have to deal with any Android-specific or iOS-specific codebase, everything on Kotlin.
m
Unfortunately, that's not my requirements. I have a specific requirement to be able to load strings at startup, then persist them. If stirngs fail to load, or a particular key doesn't exist in the loaded strings, i have to fallback to the compiled in strings. I'm using DataStore<Preferences> to store the strings and the StringTable implementation i'm using is backed by that.
And this is a generic library. Our app is WAY too big to be creating an enum for every single string key, plus it's a mono repo with 100+ modules.
g
> Our app is WAY too big to be creating an enum for every single string key Just generate them Also I think having a function which knows how to resolve each string is better than enum
fun Strings.retry() = stringTable["retry"] ?: fallback()
d
I used moko-resources to do this.
g
I thought that you wanted to use dynamic strings from somehwere