是否有可能在使用Android资源的同时以编程方式改变应用程序的语言?

如果不是,是否可以用特定的语言请求资源?

我想让用户改变应用程序的语言从应用程序。


当前回答

只是多添了一块,把我绊倒了。

而其他的答案,比如“de”,都没问题

String lang = "de";
Locale locale = new Locale(lang); 
Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, 
    getBaseContext().getResources().getDisplayMetrics());

上面不会工作,例如“fr_BE”地区,所以它将使用values-fr-rBE文件夹或类似的。

需要以下轻微更改工作与“fr_BE”

String lang = "fr";

//create a string for country
String country = "BE";
//use constructor with country
Locale locale = new Locale(lang, country);

Locale.setDefault(locale);
Configuration config = new Configuration();
config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, 
    getBaseContext().getResources().getDisplayMetrics());

其他回答

我也面临着同样的问题。在GitHub上,我找到了Android-LocalizationActivity库。

这个库使得在运行时更改应用程序的语言变得非常简单,如下面的代码示例所示。包含下面示例代码的示例项目和更多信息可以在github页面上找到。

LocalizationActivity扩展了AppCompatActivity,所以你也可以在使用Fragments时使用它。

public class MainActivity extends LocalizationActivity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple);

        findViewById(R.id.btn_th).setOnClickListener(this);
        findViewById(R.id.btn_en).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.btn_en) {
            setLanguage("en");
        } else if (id == R.id.btn_th) {
            setLanguage("th");
        }
    }
}

如果你正在使用片段,并且你想在Android中以编程方式更改应用程序语言,请使用此方法

  public void setLocale(Activity activity, String languageCode) {
    Locale locale = new Locale(languageCode);
    Locale.setDefault(locale);
    Resources resources = activity.getResources();
    Configuration config = resources.getConfiguration();
    config.setLocale(locale);
    resources.updateConfiguration(config, resources.getDisplayMetrics());
     SettingsFragment settingsFragment = new SettingsFragment();
     FragmentManager fragmentManager = requireActivity().getSupportFragmentManager();
     FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    activity.overridePendingTransition(0,0);
     fragmentTransaction.replace(R.id.main, settingsFragment).commit();
}

这样叫它

setLocale(requireActivity(),"fr");

只需要用你的片段名改变SettingsFragment,在你调用方法的地方,这基本上是刷新布局来获得视图上的新字符串 如果你想在另一个类上设置这个方法只需像这样添加context

public void setLocale(Activity activity, String languageCode,Context context) {
    Locale locale = new Locale(languageCode);
    Locale.setDefault(locale);
    Resources resources = activity.getResources();
    Configuration config = resources.getConfiguration();
    config.setLocale(locale);
    resources.updateConfiguration(config, resources.getDisplayMetrics());
    SettingsFragment settingsFragment = new SettingsFragment();
    FragmentManager fragmentManager = ((AppCompatActivity) context).getSupportFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    activity.overridePendingTransition(0,0);
    fragmentTransaction.replace(R.id.main, settingsFragment).commit();
}

Resources.updateConfiguration()已经弃用,我已经解决了这个问题,没有创建任何自定义的ContextWrapper。

首先我创建了一个扩展函数

fun Context.setAppLocale(language: String): Context {
    val locale = Locale(language)
    Locale.setDefault(locale)
    val config = resources.configuration
    config.setLocale(locale)
    config.setLayoutDirection(locale)
    return createConfigurationContext(config)
}

然后在活动的attachBaseContext方法中,简单地用新的上下文替换上下文。

override fun attachBaseContext(newBase: Context) {
  super.attachBaseContext(ContextWrapper(newBase.setAppLocale("bn")))
}

是时候更新了。

首先,弃用列表和它被弃用的API:

配置。locale (API 17) updateConfiguration(配置,displaymetrics) (API 17)

最近没有问题的答案是正确的是新方法的使用。

createconfigationcontext是updateConfiguration的新方法。

有些人是这样独立使用的:

Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
createConfigurationContext(overrideConfiguration);

... 但这行不通。为什么?该方法返回一个上下文,然后用于处理Strings.xml翻译和其他本地化资源(图像、布局等)。

正确用法是这样的:

Configuration overrideConfiguration = ctx.getResources().getConfiguration();
Locale locale = new Locale("en_US");
overrideConfiguration.setLocale(locale);
//the configuration can be used for other stuff as well
Context context  = createConfigurationContext(overrideConfiguration);
Resources resources = context.getResources();

如果您只是复制粘贴到您的IDE中,您可能会看到一个警告,该API要求您针对API 17或更高版本。这可以通过将它放在一个方法中并添加注释@TargetApi(17)来解决。

但是等待。那么旧的API呢?

您需要在没有TargetApi注释的情况下使用updateConfiguration创建另一个方法。

Resources res = YourApplication.getInstance().getResources();
// Change locale settings in the app.
DisplayMetrics dm = res.getDisplayMetrics();
android.content.res.Configuration conf = res.getConfiguration();
conf.locale = new Locale("th");
res.updateConfiguration(conf, dm);

这里不需要返回上下文。

现在,管理这些可能很困难。在API 17+中,你需要创建上下文(或者创建上下文中的资源)来获得基于本地化的适当资源。你如何处理这个问题?

好吧,我是这么做的:

/**
 * Full locale list: https://stackoverflow.com/questions/7973023/what-is-the-list-of-supported-languages-locales-on-android
 * @param lang language code (e.g. en_US)
 * @return the context
 * PLEASE READ: This method can be changed for usage outside an Activity. Simply add a COntext to the arguments
 */
public Context setLanguage(String lang/*, Context c*/){
    Context c = AndroidLauncher.this;//remove if the context argument is passed. This is a utility line, can be removed totally by replacing calls to c with the activity (if argument Context isn't passed)
    int API = Build.VERSION.SDK_INT;
    if(API >= 17){
        return setLanguage17(lang, c);
    }else{
        return setLanguageLegacy(lang, c);
    }
}

/**
 * Set language for API 17
 * @param lang
 * @param c
 * @return
 */
@TargetApi(17)
public Context setLanguage17(String lang, Context c){
    Configuration overrideConfiguration = c.getResources().getConfiguration();
    Locale locale = new Locale(lang);
    Locale.setDefault(locale);
    overrideConfiguration.setLocale(locale);
    //the configuration can be used for other stuff as well
    Context context  = createConfigurationContext(overrideConfiguration);//"local variable is redundant" if the below line is uncommented, it is needed
    //Resources resources = context.getResources();//If you want to pass the resources instead of a Context, uncomment this line and put it somewhere useful
    return context;
}

public Context setLanguageLegacy(String lang, Context c){
    Resources res = c.getResources();
    // Change locale settings in the app.
    DisplayMetrics dm = res.getDisplayMetrics();//Utility line
    android.content.res.Configuration conf = res.getConfiguration();

    conf.locale = new Locale(lang);//setLocale requires API 17+ - just like createConfigurationContext
    Locale.setDefault(conf.locale);
    res.updateConfiguration(conf, dm);

    //Using this method you don't need to modify the Context itself. Setting it at the start of the app is enough. As you
    //target both API's though, you want to return the context as you have no clue what is called. Now you can use the Context
    //supplied for both things
    return c;
}

This code works by having one method that makes calls to the appropriate method based on what API. This is something I have done with a lot of different deprecated calls (including Html.fromHtml). You have one method that takes in the arguments needed, which then splits it into one of two (or three or more) methods and returns the appropriate result based on API level. It is flexible as you do't have to check multiple times, the "entry" method does it for you. The entry-method here is setLanguage

使用前请仔细阅读

您需要使用获取资源时返回的Context。为什么?我在这里看到了其他使用createconfigationcontext而不使用它返回的上下文的答案。为了让它这样工作,必须调用updateConfiguration。这是不赞成的。使用方法返回的上下文来获取资源。

使用示例:

构造函数或类似的地方:

ctx = getLanguage(lang);//lang is loaded or generated. How you get the String lang is not something this answer handles (nor will handle in the future)

然后,无论你想在哪里获得资源,你都可以:

String fromResources = ctx.getString(R.string.helloworld);

使用任何其他上下文(理论上)都会打破这一点。

AFAIK,你仍然需要使用活动上下文来显示对话框或toast。为此,您可以使用一个活动的实例(如果您在外部)


最后,在活动上使用rebuild()来刷新内容。快捷方式可以不用创建意图刷新。

对每应用语言偏好的支持刚刚添加到API 33 (Android 13,提拉米苏目前在开发者预览版)。

要改变应用程序的语言环境,只需从LocaleManager调用setApplicationLocales:

// Set app locale to pt-BR (Portuguese, Brazil)
getSystemService(LocaleManager::class.java)
    .applicationLocales = LocaleList(Locale.forLanguageTag("pt-BR"))

详见https://developer.android.com/about/versions/13/features/app-languages#api-impl

我已经写了一篇关于这个功能的文章https://proandroiddev.com/exploring-the-new-android-13-per-app-language-preferences-8d99b971b578