Android 仿微信多语言切换

image

一、简介
二、效果预览
三、实现步骤
1、功能实现
2、配置多语言文件
四、MultiLanguageUtils分析
五、Demo地址
六、内容推荐


一、简介

我想搜到这篇文章的朋友应该不需要我多介绍,也明白多语言是用来干嘛。。。

当一个应用越做越大之后,用的人越来越多,要满足不同的群体所以都会有这一功能。

不过你会发现越到后面添加这功能会比较麻烦。所以前期做好准备为佳。

不扯这乱七八糟的东西了。。。。亮剑吧!!!

image

二、效果预览

是不是要先看看大招是咋样的,万一是花拳绣腿。不满意,就浪费大家时间了。

image

大家可可能会奇怪,不是切换成英文了吗? 怎么还有中文字体。

其实我只是对首页的 标题底部导航栏 做了处理 其他的没有变

所以大家看效果的话 只需观察 那两处地方即可。

如果对这个功能有兴趣可以继续往下观察。。没兴趣的也请往下看 ,看完可能会更没兴趣。哈哈 开个玩笑

image

三、实现步骤

1、功能实现

布局我就不解释了,就随便搭搭。源码地址就贴在最下面,想copy的同学自己去下载哈。对了,如果不错。别忘了 Star

主要的功能就在于当我选中某个语言时,点击保存(上图是“打钩”的图片)的时候去实现语言的切换。 那么,是怎么实现的呢?预知后事如何且看下文分解!!

上菜:点击保存执行事假

     //区分是选中哪种语言
    switch (datas.get(checkPos)) {
        case "跟随系统"://切换到 跟随系统
            //获取手机系统语言
            Locale locale = MultiLanguageUtils.getSystemLanguage().get(0);
            String language = locale.getLanguage();
            String country = locale.getCountry();
            //切换成手机系统语言  例:手机系统是中文则换成中文
            MultiLanguageUtils.changeLanguage(activity,language, country);
            //清空SP数据 ,用于当系统切换语言时 应用可以同步保持切换 例:系统转换成英文 则应用语言也会变成英文
            MultiLanguageUtils.changeLanguage(activity,null,null);
             break;
        case "简体中文":// 切换到 中文
            MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");
            break;
        case "English"://切换到 英文
            MultiLanguageUtils.changeLanguage(activity, "en", "US");
            break;
        default://默认切换成中文
            MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");
            break;
    }
    //关闭应用所有Activity
    //AppManager.getAppManager().finishAllActivity();
    //启动 MainActivity
    //IntentUtils.toActivity(activity, MainActivity.class,true);
image.gif

上面的切换语言功能都已经封装成一个工具类,当需要执行切换语言的时候 只需调用一行代码即可。是不是很兴奋 ,其实更麻烦的在后面,先安慰一下大家。

要让切换语言起效果,还需要重启APP。

例:上面代码是先调用了个人封装的一个Activity管理类来关闭所有的页面,然后再重启MainActivity。大家可以根据情况自己写,也可以到源码地址找到相关工具类拿去用。

大餐来了 MultiLanguageUtils.class 先感谢下大神提供的这个类:https://blog.csdn.net/m0_38074457/article/details/84993366

个人做了调整,改的有点面目全非,现在分享给大家。如果还行。。。你们懂得 点个赞意思一下就行 ,,关注,分享,打赏 我是不敢想的。

import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.LocaleList;
import android.support.v4.os.ConfigurationCompat;
import android.support.v4.os.LocaleListCompat;
import android.text.TextUtils;
import android.util.DisplayMetrics;

import java.util.Locale;

import blcs.lwb.lwbtool.Constants;

/**
 * Todo 多语言设置
 * 来自:https://blog.csdn.net/m0_38074457/article/details/84993366
 * 使用步骤:
 * 1、Application中onCreate添加registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);
         @Override
         protected void attachBaseContext(Context base) {
         //系统语言等设置发生改变时会调用此方法,需要要重置app语言
         super.attachBaseContext(MultiLanguageUtils.attachBaseContext(base));
         }
 * 2、改变应用语言调用MultiLanguageUtils.changeLanguage(activity,type,type);
 */
//public final static String SP_LANGUAGE="SP_LANGUAGE";
//public final static String SP_COUNTRY="SP_COUNTRY";
public class MultiLanguageUtils {
    /**
     * TODO 1、 修改应用内语言设置
     * @param language    语言  zh/en
     * @param area      地区
     */
    public static void changeLanguage(Context context,String language, String area) {
        if (TextUtils.isEmpty(language) && TextUtils.isEmpty(area)) {
            //如果语言和地区都是空,那么跟随系统s
            SPUtils.put(context, Constants.SP_LANGUAGE,"");
            SPUtils.put(context, Constants.SP_COUNTRY,"");
        } else {
            //不为空,那么修改app语言,并true是把语言信息保存到sp中,false是不保存到sp中
            Locale newLocale = new Locale(language, area);
            setAppLanguage(context,newLocale);
            saveLanguageSetting(context, newLocale);
        }
    }

    /**
     * Todo 更新应用语言
     * @param context
     * @param locale
     */
    private static void setAppLanguage(Context context, Locale locale) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        Configuration configuration = resources.getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            context.createConfigurationContext(configuration);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration,metrics);
        } else {
            configuration.locale = locale;
            resources.updateConfiguration(configuration,metrics);
        }
    }

    /**
     * TODO 3、 跟随系统语言
     */
    public static Context attachBaseContext(Context context) {
        String spLanguage = (String) SPUtils.get(context, Constants.SP_LANGUAGE,"");
        String spCountry = (String) SPUtils.get(context, Constants.SP_COUNTRY,"");
        if(!TextUtils.isEmpty(spLanguage)&&!TextUtils.isEmpty(spCountry)){
            Locale  locale = new Locale(spLanguage, spCountry);
            setAppLanguage(context, locale);
        }
        return context;
    }

    /**
     * 判断sp中和app中的多语言信息是否相同
     */
    public static boolean isSameWithSetting(Context context) {
        Locale locale = getAppLocale(context);
        String language = locale.getLanguage();
        String country = locale.getCountry();
        String sp_language = (String) SPUtils.get(context, Constants.SP_LANGUAGE,"");
        String sp_country = (String) SPUtils.get(context, Constants.SP_COUNTRY,"");
        if (language.equals(sp_language) && country.equals(sp_country)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 保存多语言信息到sp中
     */
    public static void saveLanguageSetting(Context context, Locale locale) {
        SPUtils.put(context, Constants.SP_LANGUAGE,locale.getLanguage());
        SPUtils.put(context, Constants.SP_COUNTRY,locale.getCountry());
    }

    /**
     * 获取应用语言
     */
    public static Locale getAppLocale(Context context){
        Locale local;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            local =context.getResources().getConfiguration().getLocales().get(0);
        } else {
            local =context.getResources().getConfiguration().locale;
        }
      return local;
    }

    /**
     * 获取系统语言
     */
    public static LocaleListCompat getSystemLanguage(){
        Configuration configuration = Resources.getSystem().getConfiguration();
        LocaleListCompat locales = ConfigurationCompat.getLocales(configuration);
      return locales;
    }

    //注册Activity生命周期监听回调,此部分一定加上,因为有些版本不加的话多语言切换不回来
    //registerActivityLifecycleCallbacks(callbacks);
    public static  Application.ActivityLifecycleCallbacks callbacks = new Application.ActivityLifecycleCallbacks() {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setAppLanguage(activity,locale);
                }
            }
        }

        @Override
        public void onActivityStarted(Activity activity) {

        }

        @Override
        public void onActivityResumed(Activity activity) {

        }

        @Override
        public void onActivityPaused(Activity activity) {

        }

        @Override
        public void onActivityStopped(Activity activity) {

        }

        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

        }

        @Override
        public void onActivityDestroyed(Activity activity) {

        }
    };

}

这里可以能还需要一个封装的SP工具

import android.content.Context;
import android.content.SharedPreferences;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
/**
 * SharedPreferences的封装
 */
public class SPUtils
{
    public SPUtils()
    {
        /* cannot be instantiated */
        throw new UnsupportedOperationException("cannot be instantiated");
    }

    /**
     * 保存在手机里面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法
     *
     * @param context
     * @param key
     * @param object
     */
    public static void put(Context context, String key, Object object)
    {

        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();

        if (object instanceof String)
        {
            editor.putString(key, (String) object);
        }
        else if (object instanceof Integer)
        {
            editor.putInt(key, (Integer) object);
        }
        else if (object instanceof Boolean)
        {
            editor.putBoolean(key, (Boolean) object);
        }
        else if (object instanceof Float)
        {
            editor.putFloat(key, (Float) object);
        }
        else if (object instanceof Long)
        {
            editor.putLong(key, (Long) object);
        }
        else
        {
            editor.putString(key, object.toString());
        }

        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值
     *
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static Object get(Context context, String key, Object defaultObject)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);

        if (defaultObject instanceof String)
        {
            return sp.getString(key, (String) defaultObject);
        }
        else if (defaultObject instanceof Integer)
        {
            return sp.getInt(key, (Integer) defaultObject);
        }
        else if (defaultObject instanceof Boolean)
        {
            return sp.getBoolean(key, (Boolean) defaultObject);
        }
        else if (defaultObject instanceof Float)
        {
            return sp.getFloat(key, (Float) defaultObject);
        }
        else if (defaultObject instanceof Long)
        {
            return sp.getLong(key, (Long) defaultObject);
        }

        return null;
    }

    /**
     * 移除某个key值已经对应的值
     *
     * @param context
     * @param key
     */
    public static void remove(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 清除所有数据
     *
     * @param context
     */
    public static void clear(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 查询某个key是否已经存在
     *
     * @param context
     * @param key
     * @return
     */
    public static boolean contains(Context context, String key)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的键值对
     *
     * @param context
     * @return
     */
    public static Map<String, ?> getAll(Context context)
    {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.getAll();
    }

    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     * @author zhy
     *
     */
    private static class SharedPreferencesCompat
    {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         * @return
         */
        @SuppressWarnings({ "unchecked", "rawtypes" })
        private static Method findApplyMethod()
        {
            try
            {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e)
            {
            }

            return null;
        }

        /**
         * 如果找到则使用apply执行,否则使用commit
         *
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor)
        {
            try
            {
                if (sApplyMethod != null)
                {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (IllegalArgumentException e)
            {
            } catch (IllegalAccessException e)
            {
            } catch (InvocationTargetException e)
            {
            }
            editor.commit();
        }
    }
}

如果你们以为有了这几个工具就可以出去吹牛逼,那么是你们天真了,而不是我想的太多。还有更麻烦的事情还没开始呢

在继承Application的类中onCreate方法内添加监听回调

 @Override
 public void onCreate() {
    super.onCreate();
    //多语言设置
    registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);
}

跟随系统语言也要在Application中的attachBaseContext配置(感觉可以去掉,本人亲测根本没有调用到。不知道是不是不同机型的原因。先保留着 ,后续若发现无用,可删掉。写出来主要是钱作者有写但描述不够详细)

    @Override
    protected void attachBaseContext(Context base) {
        //系统语言等设置发生改变时会调用此方法,需要要重置app语言
        super.attachBaseContext(MultiLanguageUtils.attachBaseContext(base));
    }

2、配置多语言文件

最麻烦的工作来了,若前期没做好相关工作。那么你可以开始跟我一步步来实现了

1、新增多语言文件

image

可以手动创建,但要符合命名规范。(不推荐)

这里介绍androidStudio 新建多语言文本方法

image

右击res——New——Android resource file

image
image

创建完文件后。。。剩下就是抽出文本进行翻译了。。

2、把项目中的文本都写到strings.xml文件中

image

像我这样:

image

如何使用这些文本?

UI中: android:text="@string/Title"

代码中:context.getString(R.string.Title)

快捷键使用:选中文本使用 Alt + Enter 打开Extract string resource

image
image

给文本命名 ——> 打钩所有的values——>点击OK 就可以自动生成到strings文件里面

接着翻译 values-en 文件中的文本。

image

到这里差不多都介绍完了 ,若有代码遗落可查看源码 。毕竟已经实现过了 ,拿出来分析给大家,可能会有疏忽。

image

MultiLanguageUtils只加了简单注释,若对MultiLanguageUtils有什么不理解的地方 请继续往下看。

四、MultiLanguageUtils分析

我们切换语言只需调用:MultiLanguageUtils.changeLanguage(activity, "zh", "ZH");

1、changeLanguage(Context context,String language, String area):

 public static void changeLanguage(Context context,String language, String area) {
        if (TextUtils.isEmpty(language) && TextUtils.isEmpty(area)) {
            //如果语言和地区都是空,那么跟随系统s
            SPUtils.put(context, Constants.SP_LANGUAGE,"");
            SPUtils.put(context, Constants.SP_COUNTRY,"");
        } else {
            //不为空,那么修改app语言,并true是把语言信息保存到sp中,false是不保存到sp中
            Locale newLocale = new Locale(language, area);
            setAppLanguage(context,newLocale);
            saveLanguageSetting(context, newLocale);
        }
    }

首先判断language,area 是否为空

空:清空app本地存储多语言的sp,为什么要清空呢?

当启动APP时会回调 registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);

该方法内部会绑定生命周期调用如下方法

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setAppLanguage(activity,locale);
                }
            }
        }

若sp有值则根据对应的值设置APP语言 ,若为空 ,则会默认取系统语言。

注:当选中“跟随系统语言”时,若不清空SP。则会造成当系统语言改变,App还是会根据SP的值来设置语言。

有值:根据传入的language,area 值切换成相对应的语言

2、setAppLanguage(Context context, Locale locale)

/**
     * Todo 更新应用语言
     * @param context
     * @param locale
     */
    private static void setAppLanguage(Context context, Locale locale) {
        Resources resources = context.getResources();
        DisplayMetrics metrics = resources.getDisplayMetrics();
        Configuration configuration = resources.getConfiguration();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale);
            configuration.setLocales(new LocaleList(locale));
            context.createConfigurationContext(configuration);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLocale(locale);
            resources.updateConfiguration(configuration,metrics);
        } else {
            configuration.locale = locale;
            resources.updateConfiguration(configuration,metrics);
        }
    }

更改应用的语言主要还是通过更改App配置来实现,主要实现步骤如下:

1、先获取资源配置对象 2、更改资源对象属性 3、更新配置信息

多语言语言适配了Android N (7.0)

7.0以前更新配置信息使用

resources.updateConfiguration(configuration, metrics);

7.0以后更新配置信息使用

context.createConfigurationContext(configuration);

3、跟随系统语言实现思路分析

1、当保存跟随系统语言时,获取系统语言 ,并更新App语言。

2、当更改手机设置语言后,启动App的时候重新更改App语言(保存其它语言信息的时候,启动App是时候也需要根据保存信息重新设置)

这里主要分析第二点。第一点只要调用changeAppLanguage 就行了

当App启动的时候会回调registerActivityLifecycleCallbacks(MultiLanguageUtils.callbacks);

        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
            String language = (String) SPUtils.get(activity, Constants.SP_LANGUAGE,"");
            String country = (String) SPUtils.get(activity, Constants.SP_COUNTRY,"");
            LogUtils.e(language);
            if (!TextUtils.isEmpty(language) && !TextUtils.isEmpty(country)) {
                //强制修改应用语言
                if (!isSameWithSetting(activity)) {
                    Locale locale = new Locale(language, country);
                    setLanguage(activity,locale);
                }
            }
        }

也是通过Sp是否有保存信息来设置系统语言。

终于写完了,看了一下篇幅还是挺长,容我感慨一下。又熬夜了 ,大家要手下留

image

好吧!!! 再见

五、Demo地址

https://github.com/DayorNight/BLCS

六、内容推荐

CSDN:https://blog.csdn.net/cs_lwb/article/details/88051709

《Android 仿微信全局字体大小调整》

《Android JUnit单元测试》

《Android Log日志封装》

《Android Rxjava+Retrofit网络请求框架封装(一)》

如果你觉得我写的不错或者对您有所帮助的话

不妨顶一个【微笑】,别忘了点赞、收藏、加关注哈

看在我花了这么多时间整理写成文章分享给大家的份上,记得手下留情哈

您的每个举动都是对我莫大的支持

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容