1. 程式人生 > >多語言切換的思路

多語言切換的思路

1. 實現的效果

和微信類似,在設定介面開啟切換語言的介面,選擇語言後重啟 HomeActivity,語言切換完成,下次重新開啟 App ,也是使用者設定的語言。

2. 實現步驟

1. 新增多語言檔案

在不同的 value 資料夾下(例如 value 、value-en、values-zh-rTW 資料夾)新增不同語言的 string.xml 檔案,我們的專案添加了英文、簡體中文、繁體中文三種語言,如下圖所示:

其中英文需要翻譯,繁體如果沒有專門翻譯的話,可以找個簡繁轉換網站,直接將簡體中文轉成繁體中文,我用的這個網站:線上中文簡體轉繁體

2. 更新 Configuration 中的 locale 屬性

參照 Android 開發者官網 上的描述,Configuration 包含了裝置的所有的配置資訊,這些配置資訊會影響應用獲取的資源。例如 string 資源,就是根據 Configuration 的 locale 屬性來判斷該取哪種語言的 string 資源,預設是 value 資料夾下的。

主要程式碼如下:

Resources resources = getContext().getResources();
DisplayMetrics dm = resources.getDisplayMetrics();
Configuration config = resources.getConfiguration
(); // 應用使用者選擇語言 config.locale = Locale.ENGLISH; resources.updateConfiguration(config, dm);

我們用了 Locale 中的預設值 Locale.ENGLISHLocale.TRADITIONAL_CHINESE和 Locale.SIMPLIFIED_CHINESE,如果你需要設定的語言沒有預設值,你可以自己新建一個 Locale 物件,具體自行 Google 吧。

注:跟隨系統設定是 Locale.getDefault()

3. 重啟 HomeActivity

我們的 App 有個啟動頁 WelcomeActivity,類似微信那個小人啟動頁,如果從歡迎頁重啟,並不是一個好的體驗,應該和微信的語言設定一樣,直接回到 HomeActivity ,而不是從 WelcomeActivity 重新開啟。實現其實也很簡單,程式碼如下:

Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
getActivity().startActivity(intent);

正常來說這段程式碼應該是沒問題的,但是假如你的 App 存在某個 activity 和當前設定頁 activity 不在一個 task 棧內的話(比如你從某個通知頁用 FLAG_ACTIVITY_NEW_TASK 啟動的一個 activity),就不會應用語言設定。因此可以直接殺掉當前 App 的程序,保證是“整個”重啟了:

Intent intent = new Intent(this, HomeActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
// 殺掉程序
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);

按道理殺掉程序的兩行程式碼任意一行即可,但是查閱相關資料,還是兩個都加上吧,如果有詳細瞭解歡迎溝通。此段程式碼其實參考自 CustomActivityOnCrash 開源專案,有興趣的可以研究下這個開源庫捕捉崩潰資訊,重啟應用部分的程式碼。

4. 持久化儲存語言設定

按照上述三個步驟,你應該已經可以看到了改變語言的效果了,但是當你殺掉應用,重新開啟,發現設定又失效了。這是因為應用重啟後會讀取裝置預設的 Configuration 資訊,其中和語言相關的 locale 屬性也會變成預設值,也就是你在系統設定中選擇的語言。

當你的應用需要由使用者單獨設定語言,而不是僅僅跟隨系統語言,你就需要持久化儲存使用者的設定資訊了。你可以選擇資料庫、或 SharedPreferences 來儲存設定資訊。

在應用啟動時需要讀取儲存的設定,並應用該配置,簡要程式碼如下:

public class App extends MultiDexApplication {

    @Override
    public void onCreate() {
        super.onCreate();
        ...
        
        Resources resources = App.getContext().getResources();
        DisplayMetrics dm = resources.getDisplayMetrics();
        Configuration config = resources.getConfiguration();
        config.locale = getSetLocale();
        resources.updateConfiguration(config, dm);
        
    }
    
    // 得到設定的語言資訊
    private static Locale getSetLocale() {
        // 讀取儲存的語言設定資訊
        ...
    
    }
}
5. 改變系統設定的時候需要注意的問題

做完以上的步驟,我覺得應該是沒問題的了,但是事實證明我還是圖樣。

在測試中我又發現了一個問題:當從應用中切出去,改變了系統語言的設定,當再切應用的時候,我發現語言也會變成系統語言(而我並沒在應用內設定跟隨系統)。

然後打斷點除錯,發現在裝置的配置資訊(也就是 Configuration )發生變化時,會立即影響應用中的 Configuration 資訊。

簡單來說,上一步中,我們在 App 啟動時,讀取了使用者的設定資訊,並應用到 Configuration 的 locale 屬性上,然後通過 resources.updateConfiguration(config, dm) 改變了應用的配置資訊( Configuration )並生效,保證我們的應用讀取的 string 資源都是使用者設定語言對應的資源。在我們改變系統的語言之後,再回到我們的應用中,此時的 Configuration 的 locale 屬性就會發生變化了,不再是我們剛才自己的在應用啟動時設定的了,而是變成了系統的設定了。

解決辦法很簡單粗暴,在切回我們的應用時,在顯示的 activity 的生命週期中做一些處理就好了,因為該 activity 可能是應用中任一個,因此我們在 BaseActivity 的 onCreate 中處理下(如評論中提到的,在改變了系統設定之後,回到應用會重走 activity 的 onCreate ,這個需要說明下)就好了:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (!LanguageUtil.isSetValue()) {
            LanguageUtil.resetDefaultLanguage();
        }
        ...
    }
public class LanguageUtil {
    
    ...
    
    /**
     * 是否是設定值
     *
     * @return 是否是設定值
     */
    public static boolean isSetValue() {
        Locale currentLocale = App.getContext().getResources().getConfiguration().locale;
        return currentLocale.equals(getSetLocale());
    }
}

這裡我就簡單說下思路,具體的程式碼實現自行完成。建議將語言設定相關的程式碼都封裝在一個 LanguageUtil 中,便於後期的維護。