how to get string from different locales in Android?
Asked Answered
F

9

103

So I want to get the value of a String in several locales regardless of the current locale setting of the device/app. How should I do that?

Basically what I need is a function getString(int id, String locale) rather than getString(int id)

How could I do that?

Thanks

Franek answered 28/2, 2012 at 2:30 Comment(3)
how did you actually implement this ?? Please do reply :)Gylys
are ResourceBundle supposed to refer to assets ?? or can they access our res folder ?Gylys
You need to specify another language than the usual ? If you work with different languages resources, when you do getString(R.strings.text) you will get the string in the users language (or the default if the users language is don't have a folder)Iridium
K
176

NOTE If your minimum API is 17+, go straight to the bottom of this answer. Otherwise, read on...

NOTE If you are using App Bundles, you need to make sure you either disable language splitting or install the different language dynamically. See https://stackoverflow.com/a/51054393 for this. If you don't do this, it will always use the fallback.

If you have various res folders for different locales, you can do something like this:

Configuration conf = getResources().getConfiguration();
conf.locale = new Locale("pl");
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
Resources resources = new Resources(getAssets(), metrics, conf);
String str = resources.getString(id);

Alternatively, you can just restart your activity using the method pointed to by @jyotiprakash.

NOTE Calling the Resources constructor like this changes something internally in Android. You will have to invoke the constructor with your original locale to get things back the way they were.

EDIT A slightly different (and somewhat cleaner) recipe for retrieving resources from a specific locale is:

Resources res = getResources();
Configuration conf = res.getConfiguration();
Locale savedLocale = conf.locale;
conf.locale = desiredLocale; // whatever you want here
res.updateConfiguration(conf, null); // second arg null means don't change

// retrieve resources from desired locale
String str = res.getString(id);

// restore original locale
conf.locale = savedLocale;
res.updateConfiguration(conf, null);

As of API level 17, you should use conf.setLocale() instead of directly setting conf.locale. This will correctly update the configuration's layout direction if you happen to be switching between right-to-left and left-to-right locales. (Layout direction was introduced in 17.)

There's no point in creating a new Configuration object (as @Nulano suggests in a comment) because calling updateConfiguration is going to change the original configuration obtained by calling res.getConfiguration().

I would hesitate to bundle this up into a getString(int id, String locale) method if you're going to be loading several string resources for a locale. Changing locales (using either recipe) calls for the framework to do a lot of work rebinding all the resources. It's much better to update locales once, retrieve everything you need, and then set the locale back.

EDIT (Thanks to @Mygod):

If your minimum API level is 17+, there's a much better approach, as shown in this answer on another thread. For instance, you can create multiple Resource objects, one for each locale you need, with:

@NonNull Resources getLocalizedResources(Context context, Locale desiredLocale) {
    Configuration conf = context.getResources().getConfiguration();
    conf = new Configuration(conf);
    conf.setLocale(desiredLocale);
    Context localizedContext = context.createConfigurationContext(conf);
    return localizedContext.getResources();
}

Then just retrieve the resources you like from the localized Resource object returned by this method. There's no need to reset anything once you've retrieved the resources.

Kindly answered 28/2, 2012 at 2:38 Comment(5)
Shouldn't you make a new configuration from the one returned by getResources().getConfiguration()? According to GrepCode, it returns a local instance stored within the Resources. Changing the locale field afterwards is probably the true reason Android is confused. You should probably do conf = new Configuration(conf); (to clone the returned Configuration) between the first and second line.Highwayman
@Highwayman - It's worth trying, but I don't think that's the source of why Android is "confused" (as you put it). Look at the source for the Resources constructor and you'll see that creating a new Resources object like this changes things deep in the process's assets (apparently a singleton). Once I realized that things would need to be reset, I didn't bother tracking down exactly why. An alternative (perhaps cleaner) approach to constructing a new Resources object is to call resources.updateConfiguration(conf, null) after changing locales.Kindly
There's a better API in API level 17+: https://mcmap.net/q/211817/-can-i-access-to-resources-from-different-locale-androidIndreetloire
@Indreetloire - Yes, that does seem like the right way to go. I'll update my answer to indicate that. Thanks!Kindly
Edited the answer with a note about app bundles breaking this functionality per default, as with bundles the user will only receive one language. See stackoverflow.com/a/51054393 on how to fix this.Niddering
B
38

Here is a combined version of the approaches described by Ted Hopp. This way, the code works for any Android version:

public static String getLocaleStringResource(Locale requestedLocale, int resourceId, Context context) {
    String result;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { // use latest api
        Configuration config = new Configuration(context.getResources().getConfiguration());
        config.setLocale(requestedLocale);
        result = context.createConfigurationContext(config).getText(resourceId).toString();
    }
    else { // support older android versions
        Resources resources = context.getResources();
        Configuration conf = resources.getConfiguration();
        Locale savedLocale = conf.locale;
        conf.locale = requestedLocale;
        resources.updateConfiguration(conf, null);

        // retrieve resources from desired locale
        result = resources.getString(resourceId);

        // restore original locale
        conf.locale = savedLocale;
        resources.updateConfiguration(conf, null);
    }

    return result;
}

Usage example:

String englishName = getLocaleStringResource(new Locale("en"), R.string.name, context);

Note

As already stated in the original answer, it might be more efficient to replace multiple call of the above code with a single configuration change and multiple resources.getString() calls.

Bartram answered 23/5, 2017 at 22:6 Comment(0)
D
5

Based on the above answers which awesome but a little complex. try this simple function:

public String getResStringLanguage(int id, String lang){
    //Get default locale to back it
    Resources res = getResources();
    Configuration conf = res.getConfiguration();
    Locale savedLocale = conf.locale;
    //Retrieve resources from desired locale
    Configuration confAr = getResources().getConfiguration();
    confAr.locale = new Locale(lang);
    DisplayMetrics metrics = new DisplayMetrics();
    Resources resources = new Resources(getAssets(), metrics, confAr);
    //Get string which you want
    String string = resources.getString(id);
    //Restore default locale
    conf.locale = savedLocale;
    res.updateConfiguration(conf, null);
    //return the string that you want
    return string;
}

Then simply call it: String str = getResStringLanguage(R.string.any_string, "en");

Happy code :)

Darken answered 20/6, 2020 at 18:10 Comment(0)
T
4

place this Kotlin extension function into your code and use it like this:

getLocaleStringResource(Locale.ENGLISH,R.string.app_name)

fun Context.getLocaleStringResource(
requestedLocale: Locale?,
resourceId: Int,
): String {
val result: String
val config =
    Configuration(resources.configuration)
config.setLocale(requestedLocale)
result = createConfigurationContext(config).getText(resourceId).toString()

return result
}
Tilburg answered 30/4, 2021 at 10:36 Comment(1)
Nice clean solution.Tiertza
D
1

SOLUTION 2023:

Slightly modified solution: Usama Saeed US


class LanguageUtil(private val activity: MainActivity) {

    companion object {
        var locale: Locale = Locale.getDefault()
    }

    fun getStringResource(
        resourceId: Int,
        locale: Locale = LanguageUtil.locale,
    ): String = with(activity) { Configuration(resources.configuration).run {
        setLocale(locale)
        createConfigurationContext(this).getString(resourceId)
    } }

}

PS. Vel_daN: Love what You DO 💚.

Danika answered 5/4, 2022 at 16:14 Comment(0)
I
0

Could this be done by for example?

String stringFormat = getString(stringId);
String string = String.format(Locale.ENGLISH, stringFormat, 6);
Izmir answered 8/9, 2021 at 2:50 Comment(1)
The question is not about formatting a string but about getting a string from a different string.xml file without changing the phone localization.Yarak
T
0

You can use this extension function to get your string locale

fun Context.getLocaleStringResource(
localeName: String,
resourceId: Int,
): String {
    val config = Configuration(resources.configuration)
    config.setLocale(Locale(localeName))

    return createConfigurationContext(config).getText(resourceId).toString()
}

Then call it:

getLocaleStringResource("en", R.string.app_name)
Thoracotomy answered 9/3, 2023 at 7:33 Comment(0)
S
0

I created those two extensions functions for kotlin:

fun Context.getString(locale: Locale, @StringRes resId: Int, vararg formatArgs: Any): String {
    var conf: Configuration = resources.configuration
    conf = Configuration(conf)
    conf.setLocale(locale)
    val localizedContext = createConfigurationContext(conf)
    return localizedContext.resources.getString(resId, *formatArgs)
}

fun Context.getString(locale: Locale, @StringRes resId: Int): String {
    var conf: Configuration = resources.configuration
    conf = Configuration(conf)
    conf.setLocale(locale)
    val localizedContext = createConfigurationContext(conf)
    return localizedContext.resources.getString(resId)
}

And you can use it like this:

getString(Locale("PT"), R.string.some_string, foo, bar)
getString(Locale.FRANCE, R.string.some_other_string)
Symptomatic answered 5/11, 2023 at 20:7 Comment(0)
B
0

Another modification of previous answers in Kotlin:

private fun getLocalString(id:Int, langName:String): String{
 var conf = this.resources.configuration
 conf = Configuration(conf)
 conf.setLocale(Locale(langName))
 val localizedContext = this.createConfigurationContext(conf)
 val lr = localizedContext.resources
 return lr.getString(id)
}
    
getLocalString("R.string.Open", "en")
Bromo answered 19/4 at 6:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.