在手机应用的实现中经常会遇到需要语言切换用于满足用户环境的多样性。可以根据所处地理位置信息进行经纬度及国家/城市/地区的获取,可以根据此内容进行多语言情况的推荐及切换。
完成上述的想法需要进行几个功能的开发:
- 需要通过手机进行地理位置信息获取
- 需要根据地理位置信息进行多语言内容的推荐(涉及地理位置监听)
- 需要将应用内的多语言进行切换
多语言切换Android分为两个部分:
第一点是本地静态文案或服务器进行多语言设置
建立对应语言国际编码string文件夹,以编码结尾。详情可以查看Android开发者中的多语言设置内容。静态语言文案用于Android工程内部不需要和外部进行交互的文案内容。
在其中踩到了一个坑,文案在引用时有时会变成数字的形式,原因在于使用“R.string.for_you”的形式引用时如果后面再加上string类型的内容时此时会将该文字转换为数字。此时引用resources.getString(R.string.for_you)进行代替,便可以正确引用文字。
第二点进行应用语言的设置
进行应用语言设置,可以调用如下方法。输入语言类型,获取应用使用语言,进行语言替换,最后进行应用重启,从而使应用可以全方位适用新语言。
private fun changeLanguage(language: String) {
if (TextUtils.isEmpty(language)) {
//如果语言和地区都是空,那么跟随系统
saveString(ConstantGlobal.LOCALE_LANGUAGE, "")
saveString(ConstantGlobal.LOCALE_COUNTRY, "")
} else {
//不为空,那么修改app语言,并true是把语言信息保存到sp中,false是不保存到sp中
val newLocale = Locale(language)
MultiLanguageUtils.changeAppLanguage(this, newLocale, true)
App().getContext()?.let { MultiLanguageUtils.changeAppLanguage(it, newLocale, true) }
}
//重启app,这一步一定要加上,如果不重启app,可能打开新的页面显示的语言会不正确
val intent = Intent(this, MainActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
this.startActivity(intent)
}
object SpUtil {
private const val name = "sp_config.cfg"
private var sp: SharedPreferences? = null
fun saveInt(key: String?, value: Int) {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putInt(key, value).commit()
}
fun saveInt(ctx: Context, key: String?, value: Int) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putInt(key, value).commit()
}
fun getInt(key: String?): Int {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getInt(key, -1)
}
fun getInt(ctx: Context, key: String?): Int {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getInt(key, -1)
}
fun getInt(ctx: Context, key: String?, defValue: Int): Int {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getInt(key, defValue)
}
fun saveBoolean(key: String?, value: Boolean) {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putBoolean(key, value).commit()
}
fun saveBoolean(ctx: Context, key: String?, value: Boolean) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putBoolean(key, value).commit()
}
fun getBoolean(key: String?): Boolean {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getBoolean(key, false)
}
fun getBoolean(key: String?, isTrue: Boolean): Boolean {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getBoolean(key, isTrue)
}
fun getBoolean(ctx: Context, key: String?): Boolean {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getBoolean(key, false)
}
fun getBoolean(ctx: Context, key: String?, defValue: Boolean): Boolean {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getBoolean(key, defValue)
}
fun saveLong(ctx: Context, key: String?, value: Long) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putLong(key, value).commit()
}
fun saveLong(key: String?, value: Long) {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putLong(key, value).commit()
}
fun getLong(ctx: Context, key: String?, defValue: Long): Long {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getLong(key, defValue)
}
fun getLong(key: String?, defValue: Long): Long {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getLong(key, defValue)
}
fun getLong(key: String?): Long {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getLong(key, -1)
}
fun saveFloat(ctx: Context?, key: String?, value: Float) {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putFloat(key, value).commit()
}
fun getFloat(key: String?): Float {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getFloat(key, -1f)
}
fun getFloat(ctx: Context, key: String?): Float {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getFloat(key, -1f)
}
fun getFloat(ctx: Context, key: String?, defValue: Float): Float {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getFloat(key, defValue)
}
@Synchronized
fun saveString(key: String?, value: String?) {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putString(key, value).commit()
}
@Synchronized
fun saveString(ctx: Context, key: String?, value: String?) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().putString(key, value).commit()
}
fun getString(key: String?): String? {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getString(key, "")
}
fun getString(key: String?, defValue: String?): String? {
if (sp == null) {
sp = App().getContext()?.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getString(key, defValue)
}
fun getString(ctx: Context, key: String?): String? {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getString(key, "")
}
fun getString(ctx: Context, key: String?, defValue: String?): String? {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
return sp!!.getString(key, defValue)
}
fun remove(ctx: Context, key: String?) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
sp!!.edit().remove(key).commit()
}
/**
* 保存序列化过的对象
*
* @param key
* @param obj
*/
fun saveObj(ctx: Context, key: String?, obj: Any?) {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
if (obj == null) return
require(obj is Serializable) { "The object should implements Serializable!" }
//1.write obj to bytes
val baos = ByteArrayOutputStream()
try {
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
//2.convert obj to string via Base64
val bytes = Base64.encode(baos.toByteArray(), Base64.DEFAULT)
//3.save string
saveString(ctx, key, String(bytes))
} catch (e: IOException) {
e.printStackTrace()
}
}
/**
* 读取序列化过的对象
*
* @param key
* @return
*/
fun getObj(ctx: Context, key: String?): Any? {
if (sp == null) {
sp = ctx.getSharedPreferences(name, Context.MODE_PRIVATE)
}
//1.get string
val string = sp!!.getString(key, null)
if (TextUtils.isEmpty(string)) return null
//2.decode string
val bytes = Base64.decode(string, Base64.DEFAULT)
//3.read bytes to Object
val bais = ByteArrayInputStream(bytes)
try {
val ois = ObjectInputStream(bais)
return ois.readObject()
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
}
object MultiLanguageUtils {
fun attachBaseContext(context: Context): Context {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
createConfigurationResources(context)
} else {
setConfiguration(context)
context
}
}
/**
* 设置语言
*
* @param context
*/
fun setConfiguration(context: Context) {
val appLocale = getAppLocale(context)
//如果本地有语言信息,以本地为主,如果本地没有使用默认Locale
var locale: Locale? = null
val spLanguage = SpUtil.getString(context, ConstantGlobal.LOCALE_LANGUAGE)
val spCountry = SpUtil.getString(context, ConstantGlobal.LOCALE_COUNTRY)
locale = if (!TextUtils.isEmpty(spLanguage) && !TextUtils.isEmpty(spCountry)) {
if (isSameLocal(appLocale, spLanguage, spCountry)) {
appLocale
} else {
Locale(spLanguage, spCountry)
}
} else {
appLocale
}
val configuration = context.resources.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale)
} else {
configuration.locale = locale
}
val resources = context.resources
val dm: DisplayMetrics = resources.displayMetrics
resources.updateConfiguration(configuration, dm) //语言更换生效的代码!
}
@TargetApi(Build.VERSION_CODES.N)
private fun createConfigurationResources(context: Context): Context {
val resources = context.resources
val configuration = resources.configuration
val appLocale = getAppLocale(context)
//如果本地有语言信息,以本地为主,如果本地没有使用默认Locale
var locale: Locale? = null
val spLanguage = SpUtil.getString(context, ConstantGlobal.LOCALE_LANGUAGE)
val spCountry = SpUtil.getString(context, ConstantGlobal.LOCALE_COUNTRY)
locale = if (!TextUtils.isEmpty(spLanguage) && !TextUtils.isEmpty(spCountry)) {
if (isSameLocal(appLocale, spLanguage, spCountry)) {
appLocale
} else {
Locale(spLanguage, spCountry)
}
} else {
appLocale
}
configuration.setLocale(locale)
configuration.setLocales(LocaleList(locale))
return context.createConfigurationContext(configuration)
}
/**
* 更改应用语言
*
* @param
* @param locale 语言地区
* @param persistence 是否持久化
*/
fun changeAppLanguage(context: Context, locale: Locale, persistence: Boolean) {
val resources = context.resources
val metrics: DisplayMetrics = resources.displayMetrics
val configuration = resources.configuration
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(locale)
configuration.setLocales(LocaleList(locale))
context.createConfigurationContext(configuration)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(locale)
} else {
configuration.locale = locale
}
resources.updateConfiguration(configuration, metrics)
if (persistence) {
saveLanguageSetting(context, locale)
}
}
//保存多语言信息到sp中
fun saveLanguageSetting(context: Context?, locale: Locale) {
SpUtil.saveString(context!!, ConstantGlobal.LOCALE_LANGUAGE, locale.language)
SpUtil.saveString(context, ConstantGlobal.LOCALE_COUNTRY, locale.country)
}
//获取本地应用的实际的多语言信息
fun getAppLocale(context: Context): Locale? {
//获取应用语言
val resources = context.resources
val configuration = resources.configuration
var locale: Locale? = null
locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.locales[0]
} else {
configuration.locale
}
return locale
}
//判断sp中和app中的多语言信息是否相同
fun isSameWithSetting(context: Context): Boolean {
val locale = getAppLocale(context)
val language = locale!!.language
val country = locale.country
val sp_language = SpUtil.getString(ConstantGlobal.LOCALE_LANGUAGE)
val sp_country = SpUtil.getString(ConstantGlobal.LOCALE_COUNTRY)
return language == sp_language && country == sp_country
}
fun isSameLocal(appLocale: Locale?, sp_language: String?, sp_country: String?): Boolean {
val appLanguage = appLocale!!.language
val appCountry = appLocale.country
return appLanguage == sp_language && appCountry == sp_country
}
interface ConstantGlobal {
companion object {
const val LOCALE_LANGUAGE = "locale_language"
const val LOCALE_COUNTRY = "locale_country"
}
}
}