Hello guys. App is in ComposeMultiPlatform. I am...
# multiplatform
j
Hello guys. App is in ComposeMultiPlatform. I am using Compose Resources Like this
Copy code
stringResource(Res.string.current_plan)
How can I change locale of app in runtime. Currently locale is system default. It is taking all strings based on system default language correctly. But I want to get strings in any language I want Thanks beforehand
a
I think you need to create the language qualifier, like you do if you were in Android
Here's a live

demonstration

of that
v
Doesn't seem like the demonstration answers on how to change locale at runtime.
r
I just went ahead with a Lyricist-type solution. But I did not use the Lyricist lib, I just defined my own data classes which contain strings (nicely organized inside), and a composition local that depends on a locale (a custom enum), which can be changed at runtime and is persisted. then in my code I just do LocalStrings.current.titles.login to refer to it
m
@Vlad any solution finded?
v
No, but didn't try just yet. I did not migrate to Compose Res from Moko-res just yet. That is one of the blocker and need to do POC before migration.
m
ok
v
That actually surprises me very much if compose-res actually doesn't support runtime change for locale in any way
m
yes i also search it
currently i create own logic
Copy code
data class MyString(
    val english: String,
    val spanish: String
)

val appName = MyString(
    english = "My App",
    spanish = "Mi aplicación"
)
v
I have
enum StringToken(mokoRes: StringResource, appSpecificTranslationKey: String) { Helllo(mokoRes = MR.stringResource.HELLO, appSpecificTranslationKey = "hello")}
I pass only that enum in the app, eg from ViewModel into state. I have my own
@Composable stringResource(token: StringToken)
which will provide actual value from the token. Under the hood it access singletone to resolve the value. The singletone is subscribed to DataStore field with "currentLanguage" and whenever it changes I update Mokos Locale + My custom translator. Since the custom translator is mutableState - all stringResource() recomposed. Because of all these abstractions we don't really care too much what Res lib we are currently use (because in fact there are only 2 classes actually depend on it) and can extend it with custom translations (loaded from API at runtime) So when you design
val appName = MyString(
english = "My App",
spanish = "Mi aplicación"
)
first thing you need to be aware - how you gonna force it to automatically pick app.name.spanish when you change the locale to Spanish
m
can you send me repo or code
little bit i understand
v
To make it simpler I would recommend to have the hardcoded strings to be in the Maps<yourCustomAppNameKey, value> per language. your get string will be as simple as map["myKey"]. But again, since compose-res in prod already, I would not go into that path, unless needed
m
ok Thanks
this is my friend tell me:
val nsUserDefaults = NSUserDefaults.standardUserDefaults()
nsUserDefaults.setObject(listOf(language), forKey = "AppleLanguages")
nsUserDefaults.synchronize()
for ios
v
Well that's iOS. I would do that fully in KMP
Copy code
data class Translator(

    val languageCode: String,

    val translations: Map<String, String>
)

class AppResourcesProvider(
    private val getTranslator: GetTranslator
) {

    private var translator: Translator? by mutableStateOf(
        null
    )

    init {
        loadTranslator()
    }

    @Composable
    override fun getString(stringToken: StringTokens): String {
        return translator?.translate(stringToken.translationKey)
    }
}

// [START Local composition] Adds Translator into local composition, put it somewhere on top of the tree
// This is basically connction between functional and OOP
internal val LocalAppResourcesProvider = staticCompositionLocalOf<ResourcesProvider> {
    error("App resources provider is not provided for local")
}

object AppRes {

    val provider: ResourcesProvider
        @Composable
        get() = LocalAppResourcesProvider.current
}
// [END Local composition]

// [USAGE from UI] aka Text(text = helloToken).
@Composable
internal fun stringResource(stringToken: StringTokens): String {
    return AppRes.provider.getString(stringToken = stringToken)
}
This is our idea. In production it is more complicated because it resolves both Hardcoded resource and optional translation
m
ok thanks