Android: How to get string in specific locale WITHOUT changing the current locale
You can use this for API +17
@NonNull@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)public static String getStringByLocal(Activity context, int id, String locale) { Configuration configuration = new Configuration(context.getResources().getConfiguration()); configuration.setLocale(new Locale(locale)); return context.createConfigurationContext(configuration).getResources().getString(id);}
Update (1) : How to support old versions.
@NonNullpublic static String getStringByLocal(Activity context, int resId, String locale) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) return getStringByLocalPlus17(context, resId, locale); else return getStringByLocalBefore17(context, resId, locale);}@NonNull@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)private static String getStringByLocalPlus17(Activity context, int resId, String locale) { Configuration configuration = new Configuration(context.getResources().getConfiguration()); configuration.setLocale(new Locale(locale)); return context.createConfigurationContext(configuration).getResources().getString(resId);}private static String getStringByLocalBefore17(Context context,int resId, String language) { Resources currentResources = context.getResources(); AssetManager assets = currentResources.getAssets(); DisplayMetrics metrics = currentResources.getDisplayMetrics(); Configuration config = new Configuration(currentResources.getConfiguration()); Locale locale = new Locale(language); Locale.setDefault(locale); config.locale = locale;/* * Note: This (temporarily) changes the devices locale! TODO find a * better way to get the string in the specific locale */ Resources defaultLocaleResources = new Resources(assets, metrics, config); String string = defaultLocaleResources.getString(resId); // Restore device-specific locale new Resources(assets, metrics, currentResources.getConfiguration()); return string;}
Update (2): Check this article
You can save all the strings of the default locale in a Map
inside a global class, like Application
that is executed when launching the app:
public class DualLocaleApplication extends Application { private static Map<Integer, String> defaultLocaleString; public void onCreate() { super.onCreate(); Resources currentResources = getResources(); AssetManager assets = currentResources.getAssets(); DisplayMetrics metrics = currentResources.getDisplayMetrics(); Configuration config = new Configuration( currentResources.getConfiguration()); config.locale = Locale.ENGLISH; new Resources(assets, metrics, config); defaultLocaleString = new HashMap<Integer, String>(); Class<?> stringResources = R.string.class; for (Field field : stringResources.getFields()) { String packageName = getPackageName(); int resId = getResources().getIdentifier(field.getName(), "string", packageName); defaultLocaleString.put(resId, getString(resId)); } // Restore device-specific locale new Resources(assets, metrics, currentResources.getConfiguration()); } public static String getStringInDefaultLocale(int resId) { return defaultLocaleString.get(resId); }}
This solution is not optimal, but you won't have concurrency problems.
Thanks to @Khaled Lela answer, I've made a Kotlin extension:
fun Context.getStringByLocale(@StringRes stringRes: Int, locale: Locale, vararg formatArgs: Any): String { val configuration = Configuration(resources.configuration) configuration.setLocale(locale) return createConfigurationContext(configuration).resources.getString(stringRes, *formatArgs)}
And I'm using directly within an Activity
, Fragment
or Context
related classes, just simply calling:
getStringByLocale(R.string.my_text, Locale.UK)
or with arguments:
getStringByLocale(R.string.my_other_text, Locale.RU, arg1, arg2, arg3)