2020-02-02
關鍵字:自動切換語言、高版本下應用內多語言切換
在 Android 應用開發中,最簡單的多語言實現就是直接在 res 目錄下將你需要的不同語言的資源以 values-xx 子目錄的形式存放。
例如,res 目錄下默認只有一個 values 目錄,這個目錄下存放的資源就是應用默認使用的資源,包括文字、色彩值、尺寸、樣式等等等等。
如果你默認目錄下使用的是中文,現在想要增加一個英文,則只需要自行創建多一個目錄,以名稱 values-en 命名即可。然后將 values 目錄下的資源文件翻譯好后拷貝過去即可。
這種情況下應用選擇的語言環境是跟着 Android 系統走的,即系統的顯示語言是什么它就讀取哪個目錄下的資源。當我們的應用中沒有與系統對應的資源時,就使用默認的 values 目錄下的資源。
想象一下,這種可以將不同語言分目錄保管,並自動加載的機制是不是可以讓我們的應用更容易做到“全球化”?
舉個例子來說就是,我們不說文字,這個太膚淺。假如你正在開發的一款應用,它的其中一個頁面的基本色調是天藍色。而偏偏有一個民族的人非常忌諱天藍色,他們更喜歡大紅色。那我們有這套機制解決起這個問題來豈不易哉。代碼完全不用改,只需要准備兩套基本色調配置表即可。這種機制真是太妙了。
言歸正傳。世界上不同語言種族這么多,我們很難記得住每個國家地區的對應縮寫是什么。那我們要如何來創建這個 values-xx 目錄呢?
如果你是使用 Android Studio 來開發應用的,那這個就不難。只需要按照如下步驟操作即可由 Android Studio 自動幫你創建對應目錄:
當然,其實也完全可以手動創建目錄與資源文件,如果你知道它的對應縮寫的話。
而 Android Studio 中也有很方便的多語言資源編輯工具,可以讓開發者很方便地以對照式來編輯不同語言的資源。其操作步驟如下圖所示:
以上是跟隨 Android 系統來切換顯示語言的情況。但有些應用可能還需要能夠擺脫操作系統的設置,自行選擇顯示語言。這該怎么辦呢?
其實也不難。只需要在上述步驟的基礎之上,再在應用啟動時自行設置一下顯示語言就可以了。
為了全局統一,一般是在自定義的 Application 類的初始化中來設置。
這里簡單說一下筆者的一個比較簡單的解決方案。
因為是應用自行控制語言選項,因為應用需要自行保存當前選擇的顯示語言。筆者采用的是 SharedPreferences 來保存。
從 SharedPreferences 中讀取出要顯示的語言以后,再通過如下代碼設置即可:
Configuration conf = context.getResources().getConfiguration(); conf.locale = isThai ? new Locale(LOCALE_THAI) : Locale.SIMPLIFIED_CHINESE; context.getResources().updateConfiguration(conf, context.getResources().getDisplayMetrics());
上述代碼已將關鍵的一行以加粗標紅表示出來了。需要注意的是,Locale 類默認提供的語言並不全面。如果恰巧你需要的語言各類它沒有默認提供,正如筆者上面所需要的泰語言一樣,那就需要你自行實例化一個 Locale 對象。實例化時直接將對應語言的縮寫作為參數傳入即可。如筆者上面的實例化泰語言時,它的寫法就是:new Locale("th");。
如此即可。
對了,這種方式似乎是針對較低版本的 Android 系統的,這段代碼筆者最高在 Android7.0 上運行通過過。其它更高版本的系統似乎還有另外一種寫法,那種寫法筆者沒有去研究過,就不貼出來了,僅在此提一下。
最后,筆者不得不提一下應用多語言顯示與 AppCompact 主題之間的沖突。
這個沖突當初可是困擾了筆者一整天。
具體現象就是在正確設置好 values-xx 資源目錄以后,發現應用怎么都不能切換至對應語言風格去顯示。無論是用跟隨系統顯示語言的方式還是手動指定顯示語言的方式。
當時筆者真是各種加打印,各種百度谷歌都找不到原因。
在幾近絕望的時候偶然發現在另外一個 Activity 中能夠將語言給切換過來。(注:筆者開發的是一款需要登錄的應用,且這個應用有免密登錄功能。筆者先前保存過登錄信息,因此應用每次打開都只顯示一個固定的界面。)
唯獨那個主 Activity 的語言死活不能切換。
有了這個對照,至少證明了筆者的資源配置沒有錯,甚至也可以證明手動切換顯示語言的代碼也沒有錯。
那就只能在這兩個 Activity 中找差異了。
經過對比,筆者最終發現是因為這兩個 Activity 所使用的主題樣式,就是 style 不同而導致另一個 Activity 不能切換顯示語言的。
這個不能讓應用切換顯示語言的主題就是 AppCompact 主題:
<style name="App_Compact_Theme" parent="Theme.AppCompat.Light.NoActionBar"/>
筆者的主 Activity 使用到了 Fragment,因此最開始繼承了 AppCompactActivity 類,而這個 AppCompactActivity 類又要求其主題必須是 AppCompact 的主題。至於為什么這個主題會不能切換顯示語言,筆者倒是沒有去深究,也覺得沒有必要。
最終筆者將主 Activity 的父類換成 FragmentActivity,再將 style 更換成 <style name="Normal_theme" parent="android:Theme.Light.NoTitleBar"/> 以后成功地解決了問題。
以上就是筆者關於Android應用多語言開發的心得與筆記。
關於Android8.0及以上版本系統下的應用內多語言切換
在Android8.0及以后的版本系統下,上述的語言切換功能將失效。原因是Android自那以后改變了系統語言控制架構。
因此,我們的軟件中關於顯示語言功能需要針對高低版本做兩套兼容代碼。
因絕大多數情況下僅Activity需要展示UI,所以這里僅針對Activity來講解。
首先,我們要將應用內所有的Activity的Context實例做個自定義,即在每個Activity實例化之初給它一個已經指定了顯示語言種類的Context實例。
怎么做呢,給我們的Activity重寫 attachBaseContext() 方法:
@Override protected void attachBaseContext(Context newBase) { if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1){ super.attachBaseContext(getSelfContext()); }else { super.attachBaseContext(newBase); } }
這里有個小坑要注意的,在這個方法剛執行時,其參數中的 newBase 將是 null。因為Activity生命周期中調用這個方法就是為了獲得Context實例的,此時它當然還沒有任何可以使用的Context對象的了。
其次,根據自己的業務邏輯,准備相應的 Context 實例給到上面的方法,即上面 getSelfContext() 方法的實現:
public Context getSelfContext(Context ctx){ if(Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1) { Configuration conf = getApplicationContext().getResources().getConfiguration(); LocaleList ll = new LocaleList(isThai ? new Locale(LOCALE_THAI) : Locale.SIMPLIFIED_CHINESE); conf.setLocales(ll); return new ContextWrapper(getApplicationContext().createConfigurationContext(conf)); } return ctx; }
如此,便可。
參考博客: https://www.jianshu.com/p/32ff13db1f0d