Android多语言国际化适配(兼容7.0)

软件一般都会有多语言的适配,安卓中也有多语言的配置方案,主要是通过Configuration类中的Locale进行多语言的适配。

1.什么是Locale

Locale是JavaSE中一个类,用以表示本地语言的类型,可用于日历、数字和字符串的本地化。

可点击此处查看Java中的Locale类说明。

Locale由下面五个部分组成。

字段 含义 格式 示例
language 国际现有的语言表示 2或3个字母,皆小写 zh-中文(拼音缩写),en-english
script 区分语言或其方言书写形式的脚本 4个字母,首字母大写其余小写 Hans-简体中文,Hant-繁体中文,Latn-拉丁文
country(region) 国家或地区 国家2个字母(大写),区域3数字 CN-中国,US-美国,030-Eastern Asia(东亚)
variant 其他可用子标签未涵盖的语言或其方言的语言变体 字母开头至少5位,数字开头至少4位 pinyin-须有前缀zh-Latn
extensions 从单个字符键到字符串值的映射扩展 2-8字母或数字 ca-japanese(Japanese Calendar)

点击此网站可以查看所有的language,region(country)等的所有列表,需要查询对应类型。

创建Locale的两种方式:

  • 通过构造函数
//传入语言生成Locale,country与variant为空
Locale(String language)
//语言+国家,variant为空
Locale(String language, String country)
//语言+国家+variant
Locale(String language, String country, String variant)
  • 通过Builder构建
//通过设置各个字段来构建Locale,这种方式比构造函数要精确,并且会判断传入的值是否符合Locale类定义的语法要求
Locale aLocale = new Builder().setLanguage("zh").setScript("Hans").setRegion("CN").build();

可用下面代码遍历系统中存在的所有Locale

  Locale[] locales = Locale.getAvailableLocales();
        for (Locale locale : locales) {
            System.out.println("语言:"+locale.getDisplayLanguage()+",国家:"+locale.getDisplayCountry()+","+locale);
        }

注:Locale.getScript()方法是在Android API21中才新增的。

2.Android中的多语言

Android Studio在创建项目时会自动生成一个res目录,res目录下放置了应用所需要运用到的所有资源,包括布局、颜色、图片等等。字符串资源则放在values这个子目录中,用strings.xml放置字符串,官方参考文档

一般默认生成的values文件夹是不带后缀的,如果想添加其它语言,则可以通过Android Studio自带的工具便捷的生成另一个values文件夹,并且带有语言代码后缀。

如下图,添加字符串对应的各语言翻译,在res目录下生成了values-en,values-zh-rCN等文件夹。
这里写图片描述

values文件夹的命名规则如下:

  • 语言通过由两个字母组成的 ISO 639-1 语言代码定义,可以选择后跟两个字母组成的 ISO 3166-1-alpha-2 区域码(前带小写字母“r”)。
  • 这些代码不区分大小写;r 前缀用于区分区域码。 不能单独指定区域。
  • 示例:values-zh,values-zh-rCN,values-zh-rTW,values-en-rUS

当应用启动的时候,系统会根据当前的语言环境自动去匹配对应的values文件夹,匹配规则如下:

  • 7.0之前,先匹配与当前应用Configuration语言一致的资源(language,country相同),如没有再匹配language一致的资源(命名中只有language,如values-en),如无则使用默认资源。
  • 7.0之后,系统语言设置中可添加多个语言,优先匹配规则与上述一样,不过添加了可匹配同一语言不同国家的资源,即language与country都没匹配上,也可匹配同一个language但不同country的资源,即是同一父项下的不同子项。
    如果第一语言没有对应资源匹配,可继续查找匹配第二位的语言,这就是语言列表的作用。如果列表中的语言都没匹配上,则使用默认资源。
  • 特别注意点:简体中文与繁体中文不是同一体系的.
    • 示例1:语言设置为简体中文,没有values-zh-rCN的资源,即使有values-zh-rTW或者values-zh-rHK的资源也不会使用,而会使用默认的资源。
    • 示例2:语言设置为繁体中文,没有values-zh-rTW的资源,即使有values-zh或values-zh-rCN的资源也不会使用,而会使用默认资源。
  • 官方文档说明
  • 关于匹配规则的具体示例,可参照这篇博客,里面详细介绍了7.0之后资源匹配的示范。

3.应用中切换语言

有时应用切换语言并不想通过切换系统语言的方式实现,只是想单独地改变某个应用的语言。

这时就需要通过编码的方式进行实现,我们可以参考微信的语言切换功能。

先来分析一下微信的多语言切换功能:

  • 跟随系统,默认选项,如果系统语言改变则微信语言跟着改变
  • 选择其它语言,系统语言的更改并不会影响到微信语言的改变。比如微信选择了繁体中文,这时切换系统语言为英文,微信的显示语言依然是繁体中文。
  • 点击保存后,所有打开的界面都被关闭,微信回到主界面中,语言也随之更改生效。原因是界面中的语言切换如果要生效,必须得重新加载该界面。

Android 7.0之前语言切换方式

Resources resources = context.getResources();
        Configuration configuration = resources.getConfiguration();
        //当前设置的locale
        Locale locale = getCurrentLang(lang);
        configuration.setLocale(locale);
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

//然后重新启动Actvity,加载新配置的语言资源
 Intent intent = new Intent(this, LocaleDemoActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);

Android 7.0之后切换方式

在Activity中的复写attachBaseContext()方法,该方法的具体作用可参考这篇博客。这个方法是初始化context的,比onCreate()方法要先执行,注意:在application中重写该方法并不会生效语言更改设置。

最佳实践方式是所有的activity都继承自基类activity,然后在基类activity复写此方法。

@Override
protected void attachBaseContext(Context newBase) {
    Resources resources = context.getResources();
    Locale locale = getCurrentLang(lang);
    Configuration configuration = resources.getConfiguration();
    configuration.setLocale(locale);
    super.attachBaseContext(context.createConfigurationContext(configuration));
}

4.关于不同语言适配的最佳实践

只需要在attachBaseContext这个方法里面进行版本的判断,如果是7.0之前则使用 resources.updateConfiguration()进行字符资源的更新,如果是7.0之后则使用context.createConfigurationContext()

这样的做法虽然在每一次打开activity都会进行一次Configuration的设置,但却能达到跟微信一样预期的切换语言功能,当应用选择了一个具体的语言(非跟随系统)然后改变了手机的系统语言,再次打开应用时依然是上次设置的语言,而不会让应该变成系统语言。

以下是具体的代码实现

package cn.pigdreams.blogdemo;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.Log;

import java.util.Locale;

/**
 * Create by pigdreams on 2018-07-26
 * website:pigdreams.cn
 * 语言自适应工具
 * 数组资源中设定语言,现为4个类型
 * 1.跟随系统
 * 2.简体中文
 * 3.繁体中文
 * 4.English
 * 然后每次选择的语言都会存入SP中,根据SP中保存的语言类型进行资源语言设置
 * 根据语言的整数值来匹配对应的Locale对象,却省值为简体中文Locale.SIMPLIFIED_CHINESE
 */
public class LangUtils {
    public static final int FOLLOW_SYSTEM = 0;
    private static final int SIMPLE_CHINESE = 1;
    private static final int TRADITION_CHINESE = 2;
    private static final int ENGLISH = 3;

    private static Locale getCurrentLang(int userLang) {
        switch (userLang) {
            case FOLLOW_SYSTEM:
                return Locale.getDefault();
            case SIMPLE_CHINESE:
                return Locale.SIMPLIFIED_CHINESE;
            case TRADITION_CHINESE:
                return Locale.TRADITIONAL_CHINESE;
            case ENGLISH:
                return Locale.ENGLISH;
            default:
                return Locale.SIMPLIFIED_CHINESE;
        }
    }

    public static Context getAttachBaseContext(Context context, int lang) {
        //Android 7.0之后需要用另一种方式更改res语言,即配置进context中
            Log.d("pigdreams", "配置语言...默认locale=" + Locale.getDefault() + ";用户设置的为=" + getCurrentLang(lang));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                return updateResources(context, lang);
            } else {
                //7.0之前的更新语言资源方式
                changeResLanguage(context, lang);
                return context;
            }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, int lang) {
        Resources resources = context.getResources();
        Locale locale = getCurrentLang(lang);
        Configuration configuration = resources.getConfiguration();
        configuration.setLocale(locale);
        return context.createConfigurationContext(configuration);
    }

    private static void changeResLanguage(Context context, int lang) {
        Resources resources = context.getResources();
        Configuration configuration = resources.getConfiguration();
        Locale locale = getCurrentLang(lang);
        configuration.setLocale(locale);
        resources.updateConfiguration(configuration, resources.getDisplayMetrics());
    }

}


//在基类Activity中实现如下方法
 @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(LangUtils.getAttachBaseContext(newBase, SPUtils.getInstance(Constant.SP_NAME).getInt(Constant.SP_USER_LANG)));
    }

5.项目Demo示例及动图演示

demo地址:传送门
这里写图片描述

发布了92 篇原创文章 · 获赞 68 · 访问量 25万+

猜你喜欢

转载自blog.csdn.net/pigdreams/article/details/81277110