Android应用语言国际化方案

最近项目中需要实现国际化,需求如下:

1.支持简体、繁体、英语、法语四国语言

2.App第一次打开时使用系统默认语言,之后App语言不随系统语言改变

3.在App设置中可以手动更改语言

 

一,添加多语言资源

打开strings.xml,可以看到默认的字符串资源,点击Open editor即可打开翻译编辑器:

Android应用语言国际化方案_第1张图片

在这里就选择想要的语言,就会自动生成对应的资源文件: 

Android应用语言国际化方案_第2张图片

可以看到,成功地添加了四国语言:

Android应用语言国际化方案_第3张图片

之后添加字符串时,直接在翻译编辑器里,点击加号添加key,再编辑对应不同翻译即可。

二,准备工具类

首先我们明确方案,Android中的语言和地区等信息由Locale类来管理,设置不同的语言需要更改Locale。而设置Locale需要获取Context中的Resources资源对象,再在资源中的Configuration配置信息中设置Locale. 需要以下工具类:

public class LanguageContextWrapper extends ContextWrapper {

    public LanguageContextWrapper(Context base) {
        super(base);
    }

    public static ContextWrapper wrap(Context context, Locale newLocale) {
        Resources res = context.getResources();
        Configuration configuration = res.getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(newLocale);
            LocaleList localeList = new LocaleList(newLocale);
            LocaleList.setDefault(localeList);
            configuration.setLocales(localeList);
            context = context.createConfigurationContext(configuration);
        } else {
            configuration.setLocale(newLocale);
            context = context.createConfigurationContext(configuration);
        }
        return new ContextWrapper(context);
    }
}

可以看到,我们需要判断Android版本,并对应了不同的设置方法。因为在Android7.0以上,系统语言规则发生了变化,不再是一种语言,而是提供一个列表。用户可以选择支持的语言,这样在App打开时,系统会对App支持的语言和这个列表中的语言依次进行对比,自动选择最匹配的。

然后,我们需要准备一个获取语言的工具类:

public class LanguageUtils {

    public static int getCurrentLanguage() {
        if (UserUtils.isLogin()) {
            return SPUtils.getInstance().getInt(SPConstants.SP_LANGUAGE, 3);
        }
        return LanguageUtils.getInstance().getDefaultLanguage();
    }

    public int getDefaultLanguage() {
        if (SPUtils.getInstance().getInt(SPConstants.SP_LANGUAGE) > 0) {
            return SPUtils.getInstance().getInt(SPConstants.SP_LANGUAGE);
        }
        //默认为英文
        int languageCode = Constants.NUMS.LANGUAGE_EN;
        Locale locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            locale = Utils.getApp().getResources().getConfiguration().getLocales().get(0);
            //个别机型,比如魅族
            if (locale == null) {
                locale = Locale.getDefault();
            }
        } else {
            locale = Locale.getDefault();
        }
        String language = locale.getLanguage();
        if (!TextUtils.isEmpty(language)) {
            /*1 中文 CH 小米机会有:和空格
            2 繁体 TW
            3 英语 EN
            4 法语 FR*/
            if (language.contains(Text.REGION_FR)) {
                languageCode = Constants.NUMS.LANGUAGE_FR;
            } else if (language.contains(Text.REGION_ZH)) {
                if (locale.getCountry().contains(Text.REGION_ZH_CN)) {
                    languageCode = Constants.NUMS.LANGUAGE_CN;
                } else {
                    languageCode = Constants.NUMS.LANGUAGE_TW;
                }
            }
        }
        SPUtils.getInstance().put(SPConstants.SP_LANGUAGE, languageCode);
        return languageCode;
    }

    public Locale getLanguageLocale() {
        Locale locale;
        switch (LanguageUtils.getCurrentLanguage()) {
            case 1:
                locale = Locale.SIMPLIFIED_CHINESE;
                break;
            case 2:
                locale = Locale.TRADITIONAL_CHINESE;
                break;
            case 3:
                locale = Locale.ENGLISH;
                break;
            case 4:
                locale = Locale.FRENCH;
                break;
            default:
                locale = Locale.ENGLISH;
                break;
        }
        return locale;
    }
}

可以看到,我们在调用getCurrentLanguage()方法获取当前App的语言时,会先判断用户登录状态,若已登录就会获取保存在本地的语言类型(判断登录状态是项目需要的逻辑),否则获取系统默认语言,并保存到本地。

三,在正确的位置调用方法

然后,我们需要在BaseActivity中去设置Context:

    @Override
    protected void attachBaseContext(Context newBase) {
        Context context = LanguageContextWrapper.wrap(newBase, LanguageUtils.getInstance().getLanguageLocale());
        super.attachBaseContext(context);
    }

重写BaseActivity中的attachBaseContext()方法,用我们准备好的工具类去更改Context中的Locale。之所以要在这个方法中更改,是因为此方法是用来设置基本上下文的,此时BaseActivity的onCreate()方法还没调用,在这里设置才能生效。

在App的设置中更改语言时,把设置的语言类型再保存到本地:

public void setLanguage(int language) {
        SPUtils.getInstance().put(SPConstants.SP_LANGUAGE, language);
    }

最后,我们需要重启Activity,可以采用如下操作:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(MainActivity.class, intent);
finish();

当然,项目中还需要访问更改语言的接口,才能更改返回数据的语言类型。

但是,以上方案存在一个缺点,那就是只能更改BaseActivity子Activity的语言,对第三方SDK或其他Module中的Activity无效。由于我项目中没有用到SDK中的Activity,对于其他Module,我在公共依赖的Module中也加入了一个工具类ThirdPartContextWrapper,和上面的LanguageContextWrapper类基本一样。然后在需要更改语言的Module中同样在相应的BaseActivity中去设置。

你可能感兴趣的:(Android)