Android通过代码实现多语言切换

前言

最近接手一个Android项目,需要实现对维吾尔族语的支持。虽然做了这么久的android开发,只做过多语言支持,但做应用内部多语言支持还是第一次,而且还是对维吾尔语的支持。所以,又是一次面向搜索引擎编程。

面向搜索编程

如果我们搜索“android 多语言切换”,我相信得到大部分的答案是千篇一律的,连文章都长的一样:

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

以上代码应该说没什么大问题,毕竟确实可以工作,可以实现需求。但是,作为一个强迫症患者,我实际受不了有2处划线的地方,也就是说上面代码中有2处被废弃了(没错,API 25被废弃的):

 config.locale = locale;
 getBaseContext().getResources().updateConfiguration(config, 
 getBaseContext().getResources().getDisplayMetrics());

看到有2处被废弃了,必须进去看看啊。

/**
     * Current user preference for the locale, corresponding to
     * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a>
     * resource qualifier.
     *
     * @deprecated Do not set or read this directly. Use {@link #getLocales()} and
     * {@link #setLocales(LocaleList)}. If only the primary locale is needed,
     * <code>getLocales().get(0)</code> is now the preferred accessor.
     */
    @Deprecated public Locale locale;

很显然我们找到了替代方法:

config.setLocales(LocaleList);
config.setLocale(locale);

很好,没问题。继续查看下一个:

    /**
     * Store the newly updated configuration.
     *
     * @deprecated See {@link android.content.Context#createConfigurationContext(Configuration)}.
     */
    @Deprecated
    public void updateConfiguration(Configuration config, DisplayMetrics metrics) {
        updateConfiguration(config, metrics, null);
    }

很简单嘛,代替方法也找到了哦。于是就这么写了下去:

    Locale locale = new Locale("ug");
    final Resources res = getResources();
    final Configuration config = res.getConfiguration();
    config.setLocale(locale); // getLocale() should return a Locale
    createConfigurationContext(config);

完全不好用!怎么写就是不好用。再仔细一看,文档上说createConfigurationContext(config)会返回一个新的Context,然而对新Context如何处理只字未提。又是一番面向搜索编程,终于找到了正确姿势:

    @Override
    protected void attachBaseContext(Context newBase) {
        Locale locale = new Locale("ug");
        final Resources res = newBase.getResources();
        final Configuration config = res.getConfiguration();
        config.setLocale(locale); // getLocale() should return a Locale
        final Context newContext = newBase.createConfigurationContext(config);
        super.attachBaseContext(newContext);
    }

我们的Activity应该重写这个方法。然后当Activity被创建的时候,新的Context将被应用。需要注意的,如果你想让当前Activity生效,你需要调用recreate()

当然啦,我们也可以写个工具类:

public class ConfigurationWrapper {
    private ConfigurationWrapper() {
    }

    public static Context wrapConfiguration(@NonNull final Context context, @NonNull final Configuration config) {
        return context.createConfigurationContext(config);
    }
 
    
    public static Context wrapLocale(@NonNull final Context context,@NonNull final Locale locale) {
        final Resources res = context.getResources();
        final Configuration config = res.getConfiguration();
        config.setLocale(locale);
        return wrapConfiguration(context, config);
    }
}

然后你就可以这样使用。如果你想更改configuration,就可以用wrapConfiguration

@Override
protected void attachBaseContext(Context newBase) {
    super.attachBaseContext(ConfigurationWrapper.wrapLocale(newContext, getLocale()));
}

关于Locale

实现多语言切换用到了Locale。Locale里很多常见国家和地区以及语言,如果我们做常见的语言,可以直接调用系统的,比如Locale.CHINESE。但是这次做的维吾尔语的适配,略我坑一下。 首先,维吾尔语的英文名字叫Uyghur。Locale里并没有,也可能是我找的不对。 其次,Uyghur是阿拉伯系的。嗯,没错,有个词叫RTL,会出现神奇的东西。 最后,AndroidStudio的values里面是有Uyghur选项的。并且有相对应选项,其中就包括中国,我猜这就是维吾尔语。所以就建了一个,全名是:values-ug-rCN。 问题是,我最开始直接这样写的:

 Locale locale = new Locale("ug-rCN");

好吧,是我太天真,不好用。机智的我看了看Locale的构造方法。后来猜了一个结论

ug代表语种,rCN代表国家和地区。比如说同样都是中文zh,但分了很多国家和地区,如中国大陆,中国台湾,中国香港以及新加坡等

所以下面的写法才是正确姿势:

Locale locale = new Locale("ug", Locale.CHINA.getCountry());

结束

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C#

免费开源的DotNet二维码操作组件ThoughtWorks.QRCode(.NET组件介绍之四)

    在生活中有一种东西几乎已经快要成为我们的另一个电子”身份证“,那就是二维码。无论是在软件开发的过程中,还是在普通用户的日常中,几乎都离不开二维码。...

1.6K90
来自专栏智能计算时代

Microservices Ecosystem Transit Map

…we assembled a map of the ecosystem to help guide practitioners, vendors, inves...

40440
来自专栏浅探ARKit

ARKit中控制.dae动画的播放

4.用时间控制动画--CAAnimation 里的 timeOffset 控制开始时间 duration控制播放时间

78770
来自专栏Jerry的SAP技术分享

使用SAPGUI画图

国内80后上的编程课应该都学过Logo(https://en.wikipedia.org/wiki/Logo_(programming_language)这门编...

15240
来自专栏LeoXu的博客

让 Android 的 WebView 支持 type 为 file 的 input,同时支持拍照

Android 的 WebView 组件默认是不启用 type 为 file 的 input 的,需要在代码中做一些类似 hack 的编码(因为解决问题的目标对...

12220
来自专栏zaking's

RFC2616-HTTP1.1-Header Field Definitions(头字段规定部分—单词注释版)

16020
来自专栏hrscy

使用 Unity 来实现 iOS 原生弹框

如果你有这些疑虑,那么现在你来对地方了。在这篇博客中,我将使用 Unity 创建 iOS 原生弹框。

34430
来自专栏岑玉海

Hbase 学习(二)补充 自定义filter

本来这个内容是不单独讲的,但是因为上一个页面太大,导致Live Writer死机了,不能继续编辑了,所以就放弃了 这里要讲的是自定义filter,从Filt...

37450
来自专栏一个默默无闻的工程师的日常

获取豆瓣电影TOP 250并存入文件

15130
来自专栏zaking's

RFC2616-HTTP1.1-Methods(方法规定部分—单词注释版)

12650

扫码关注云+社区

领取腾讯云代金券