Android:如何在特定区域设置中获取字符串而不更改当前区域设置

sch*_*rer 34 android locale localization android-resources

使用案例:记录显示给用户的错误消息.

但是,您不希望日志中的消息依赖于用户设备的区域设置.另一方面,您不希望仅为您的(技术)日志记录更改用户设备的区域设置.这可以实现吗?我在stackoverflow上找到了几个可能的解决方案:

但是,它们都会导致更改设备的区域设置(直到下一次配置更改).

无论如何,我目前的解决方法是这样的:

public String getStringInDefaultLocale(int resId) {
    Resources currentResources = getResources();
    AssetManager assets = currentResources.getAssets();
    DisplayMetrics metrics = currentResources.getDisplayMetrics();
    Configuration config = new Configuration(
            currentResources.getConfiguration());
    config.locale = DEFAULT_LOCALE;
    /*
     * Note: This (temporiarily) 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;
}
Run Code Online (Sandbox Code Playgroud)

说实话,我根本不喜欢这种方法.它并不高效 - 考虑并发性和所有这些 - 它可能导致"错误"区域设置中的某些视图.

所以 - 任何想法?也许这可以使用ResourceBundle实现,就像在标准Java中一样?

Kha*_*ela 24

你可以使用它 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);
}
Run Code Online (Sandbox Code Playgroud)

更新(1):如何支持旧版本.

@NonNull
public 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;
}
Run Code Online (Sandbox Code Playgroud)

更新(2): 查看这篇文章


Zba*_*ian 6

感谢 @Khaled Lela 的回答,我做了一个 Kotlin 扩展:

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)
}
Run Code Online (Sandbox Code Playgroud)

我直接在 或相关类中使用ActivityFragment只需Context简单地调用:

getStringByLocale(R.string.my_text, Locale.UK)或带参数:

getStringByLocale(R.string.my_other_text, Locale.RU, arg1, arg2, arg3)