Android 切换应用语言

在 Android 开发中,切换应用语言(即本地化/国际化) 有多种实现方式。以下是几种主流、可靠的方法,适用于不同 Android 版本和项目需求:


✅ 一、系统级语言切换(推荐用于 Android 7.0+)

Android 7.0(API 24) 开始,Google 推荐使用 Configuration + Context.createConfigurationContext() 的方式,并结合 AppCompatDelegate 实现应用内语言切换。

步骤:

  1. ApplicationBaseActivity 中设置语言
  2. 重启 Activity 或重新创建 Context

示例(Kotlin):

kotlin 复制代码
// 工具类:LanguageManager.kt
object LanguageManager {
    fun setAppLanguage(context: Context, languageCode: String) {
        val locale = Locale(languageCode)
        Locale.setDefault(locale)

        val config = Configuration()
        config.setLocale(locale)

        // 保存到 SharedPreferences(可选)
        context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
            .edit().putString("language", languageCode).apply()

        // 更新上下文
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            context.createConfigurationContext(config)
        } else {
            context.resources.updateConfiguration(config, context.resources.displayMetrics)
        }
    }

    fun getSavedLanguage(context: Context): String? {
        return context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
            .getString("language", null)
    }
}

在 Application 中初始化:

kotlin 复制代码
class MyApplication : Application() {
    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(updateBaseContextLocale(base))
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        updateBaseContextLocale(this)
    }

    private fun updateBaseContextLocale(context: Context): Context {
        val language = LanguageManager.getSavedLanguage(context) ?: "en"
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            updateResourcesLocale(context, Locale(language))
        } else {
            updateResourcesLocaleLegacy(context, Locale(language))
        }
    }

    @TargetApi(Build.VERSION_CODES.N)
    private fun updateResourcesLocale(context: Context, locale: Locale): Context {
        val configuration = context.resources.configuration
        configuration.setLocale(locale)
        return context.createConfigurationContext(configuration)
    }

    private fun updateResourcesLocaleLegacy(context: Context, locale: Locale): Context {
        val resources = context.resources
        val configuration = resources.configuration
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.displayMetrics)
        return context
    }
}

✅ 优点:兼容新旧版本,支持后台切换后生效

❗ 注意:切换后需重启当前 Activity (如 recreate())才能立即看到效果


✅ 二、使用 AppCompatDelegate.setApplicationLocales()(Android 13+ 官方推荐)

Android 13(API 33) 起,Google 提供了更标准的 API:AppCompatDelegate.setApplicationLocales()

示例(Kotlin):

kotlin 复制代码
val localeList = LocaleListCompat.forLanguageTags("zh-Hans") // 简体中文
AppCompatDelegate.setApplicationLocales(localeList)
  • 无需手动处理 Configuration
  • 自动持久化,下次启动自动生效
  • 仅适用于 targetSdkVersion ≥ 33 且使用 AppCompat 1.6.0+

📌 Gradle 依赖:

gradle 复制代码
implementation 'androidx.appcompat:appcompat:1.6.1'

✅ 最简单、最官方的方式(未来趋势)

⚠️ 低版本 Android 不支持,需配合降级方案


✅ 三、强制重启整个应用(不推荐,但简单)

适用于快速原型或简单场景:

kotlin 复制代码
fun restartApp(context: Context) {
    val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
    intent?.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
    context.startActivity(intent)
    (context as? Activity)?.finishAffinity()
    Runtime.getRuntime().exit(0) // 强制退出(谨慎使用)
}

❌ 缺点:用户体验差,可能丢失状态


✅ 四、动态切换字符串资源(不改系统语言)

如果你只是想临时显示另一种语言的文案(比如多语言客服界面),可以:

  • 手动加载对应 strings.xml 的资源(通过 Context.createConfigurationContext
  • 或将多语言文本存入数据库/JSON,不依赖 Android 资源系统

适用于轻量级多语言,但维护成本高


🔧 补充:如何组织多语言资源?

res/ 目录下创建对应语言的资源文件夹:

复制代码
res/
├── values/          → 默认(如英文)
│   └── strings.xml
├── values-zh/       → 简体中文
│   └── strings.xml
├── values-zh-rTW/   → 繁体中文(台湾)
├── values-es/       → 西班牙语
└── ...

✅ 最佳实践建议

场景 推荐方案
目标 Android 13+ AppCompatDelegate.setApplicationLocales()
兼容 Android 5.0 ~ 12 自定义 Application + attachBaseContext + recreate()
需要即时生效 切换后调用 activity.recreate()
避免重启 使用 ContextWrapper 动态创建带语言的 Context(复杂)

📱 用户体验提示

  • 切换语言后,应刷新当前界面recreate()finish() + startActivity()
  • 提供语言选择列表(如 "en", "zh", "es" 对应用户可读名称)
  • 默认语言建议跟随系统:Locale.getDefault().language

提供一个示例

我们将创建以下文件:

  1. MainActivity.java - 主界面,包含语言选择的按钮。
  2. LanguageHelper.java - 工具类,用于保存和设置语言偏好。
  3. MyApplication.java - 应用程序类,用于初始化语言设置。

代码示例

1. MainActivity.java
java 复制代码
package com.example.languagechanger;

import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(LanguageHelper.onAttach(newBase));
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button btnChangeToEnglish = findViewById(R.id.btn_change_to_english);
        Button btnChangeToChinese = findViewById(R.id.btn_change_to_chinese);

        btnChangeToEnglish.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LanguageHelper.setLocale(MainActivity.this, "en");
                restartActivity();
            }
        });

        btnChangeToChinese.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                LanguageHelper.setLocale(MainActivity.this, "zh");
                restartActivity();
            }
        });
    }

    private void restartActivity() {
        Intent intent = getIntent();
        finish();
        startActivity(intent);
    }
}
2. LanguageHelper.java
java 复制代码
package com.example.languagechanger;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import java.util.Locale;

public class LanguageHelper {

    private static final String SELECTED_LANGUAGE = "Locale.Helper.Selected.Language";

    public static Context onAttach(Context context) {
        String lang = getPersistedData(context, Locale.getDefault().getLanguage());
        return setLocale(context, lang);
    }

    public static String getLanguage(Context context) {
        return getPersistedData(context, Locale.getDefault().getLanguage());
    }

    public static Context setLocale(Context context, String language) {
        persist(context, language);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return updateResources(context, language);
        }

        return updateResourcesLegacy(context, language);
    }

    private static String getPersistedData(Context context, String defaultLanguage) {
        SharedPreferences preferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
        return preferences.getString(SELECTED_LANGUAGE, defaultLanguage);
    }

    private static void persist(Context context, String language) {
        SharedPreferences.Editor editor = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE).edit();
        editor.putString(SELECTED_LANGUAGE, language);
        editor.apply();
    }

    @TargetApi(Build.VERSION_CODES.N)
    private static Context updateResources(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Configuration configuration = context.getResources().getConfiguration();
        configuration.setLocale(locale);
        configuration.setLayoutDirection(locale);

        return context.createConfigurationContext(configuration);
    }

    @SuppressWarnings("deprecation")
    private static Context updateResourcesLegacy(Context context, String language) {
        Locale locale = new Locale(language);
        Locale.setDefault(locale);

        Resources resources = context.getResources();

        Configuration configuration = resources.getConfiguration();
        configuration.locale = locale;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            configuration.setLayoutDirection(locale);
        }

        resources.updateConfiguration(configuration, resources.getDisplayMetrics());

        return context;
    }
}
3. MyApplication.java
java 复制代码
package com.example.languagechanger;

import android.app.Application;

public class MyApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(LanguageHelper.onAttach(base));
    }
}
4. activity_main.xml

确保你的布局文件中有两个按钮用于切换语言:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_change_to_english"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Change to English" />

    <Button
        android:id="@+id/btn_change_to_chinese"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换到中文" />
</LinearLayout>
5. 配置资源文件

请确保你已经为不同的语言准备了相应的 strings.xml 文件,例如 values/strings.xml(默认英语)和 values-zh/strings.xml(简体中文)。


注意事项

  • 确保你的 AndroidManifest.xml 中声明了 MyApplication 类:
xml 复制代码
<application
    android:name=".MyApplication"
    ... >
    ...
</application>
  • 根据需要调整语言代码(如 "en" 对应英语, "zh" 对应简体中文)。如果需要支持更多语言,请相应地添加更多的语言资源文件夹和字符串资源。
相关推荐
杀死那个蝈坦32 分钟前
Caffeine
java·jvm·spring cloud·tomcat
n***271934 分钟前
JAVA (Springboot) i18n国际化语言配置
java·spring boot·python
汤姆yu35 分钟前
基于springboot的校园家教信息系统
java·spring boot·后端·校园家教
q***062937 分钟前
Spring Boot--@PathVariable、@RequestParam、@RequestBody
java·spring boot·后端
小石头 100861 小时前
【Java】String类(超级详细!!!)
java·开发语言·算法
.柒宇.1 小时前
力扣hot100---42.接雨水(java版)
java·算法·leetcode
D***44141 小时前
Spring Boot 多数据源解决方案:dynamic-datasource-spring-boot-starter 的奥秘(上)
java·spring boot·后端
i***11861 小时前
MySQL-mysql zip安装包配置教程
android·mysql·adb
迈巴赫车主1 小时前
蓝桥杯20534爆破 java
java·数据结构·算法·职场和发展·蓝桥杯