Jetpack Compose 中更新应用语言

news2025/5/28 1:38:41

在 Jetpack Compose 应用中更新语言需要结合传统的 Android 语言配置方法和 Compose 的重组机制。以下是完整的实现方案:

1. 创建语言管理类

object LocaleManager {
    private var currentLocale: Locale = Locale.getDefault()

    fun setLocale(context: Context, locale: Locale) {
        currentLocale = locale
        updateResources(context, locale)
        persistLocale(context, locale)
    }

    fun getCurrentLocale(): Locale = currentLocale

    private fun updateResources(context: Context, locale: Locale) {
        Locale.setDefault(locale)
        
        val resources = context.resources
        val configuration = Configuration(resources.configuration)
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale)
            context.createConfigurationContext(configuration)
        } else {
            @Suppress("DEPRECATION")
            configuration.locale = locale
            resources.updateConfiguration(configuration, resources.displayMetrics)
        }
    }

    private fun persistLocale(context: Context, locale: Locale) {
        context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
            .edit()
            .putString("app_language", locale.language)
            .apply()
    }

    fun getPersistedLocale(context: Context): Locale {
        val prefs = context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
        val lang = prefs.getString("app_language", Locale.getDefault().language) ?: Locale.getDefault().language
        return Locale(lang)
    }
}

2. 创建可组合函数管理语言状态

@Composable
fun rememberLocaleState(context: Context): State<Locale> {
    val localeState = remember { mutableStateOf(LocaleManager.getPersistedLocale(context)) }
    
    DisposableEffect(Unit) {
        val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
            if (key == "app_language") {
                localeState.value = LocaleManager.getPersistedLocale(context)
            }
        }
        
        val prefs = context.getSharedPreferences("AppPreferences", Context.MODE_PRIVATE)
        prefs.registerOnSharedPreferenceChangeListener(listener)
        
        onDispose {
            prefs.unregisterOnSharedPreferenceChangeListener(listener)
        }
    }
    
    return localeState
}

3. 创建自定义 Activity

abstract class LocaleAwareComponentActivity : ComponentActivity() {
    override fun attachBaseContext(newBase: Context) {
        super.attachBaseContext(
            newBase.wrapWithLocale(LocaleManager.getPersistedLocale(newBase))
        )
    }
    
    private fun Context.wrapWithLocale(locale: Locale): Context {
        val configuration = Configuration(resources.configuration)
        
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            configuration.setLocale(locale)
            createConfigurationContext(configuration)
        } else {
            @Suppress("DEPRECATION")
            configuration.locale = locale
            resources.updateConfiguration(configuration, resources.displayMetrics)
            this
        }
    }
}

4. 在应用中使用

MainActivity.kt

class MainActivity : LocaleAwareComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        setContent {
            val localeState = rememberLocaleState(this)
            
            // 使用DisposableEffect确保语言变更时更新Activity
            DisposableEffect(localeState.value) {
                onDispose { }
            }
            
            YourAppTheme(locale = localeState.value) {
                AppContent(localeState)
            }
        }
    }
}

主题设置

@Composable
fun YourAppTheme(
    locale: Locale = LocaleManager.getCurrentLocale(),
    content: @Composable () -> Unit
) {
    val context = LocalContext.current
    CompositionLocalProvider(
        LocalContext provides context.wrapWithLocale(locale),
        content = content
    )
}

private fun Context.wrapWithLocale(locale: Locale): Context {
    val configuration = Configuration(resources.configuration)
    
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        configuration.setLocale(locale)
        createConfigurationContext(configuration)
    } else {
        @Suppress("DEPRECATION")
        configuration.locale = locale
        resources.updateConfiguration(configuration, resources.displayMetrics)
        this
    }
}

语言切换界面

@Composable
fun LanguageSwitcher(localeState: State<Locale>) {
    val context = LocalContext.current
    
    Column {
        val currentLocale = localeState.value
        
        Text("Current language: ${currentLocale.displayLanguage}")
        
        Button(onClick = { 
            LocaleManager.setLocale(context, Locale("en"))
            (context as? Activity)?.recreate()
        }) {
            Text("English")
        }
        
        Button(onClick = { 
            LocaleManager.setLocale(context, Locale("es"))
            (context as? Activity)?.recreate()
        }) {
            Text("Español")
        }
        
        Button(onClick = { 
            LocaleManager.setLocale(context, Locale("fr"))
            (context as? Activity)?.recreate()
        }) {
            Text("Français")
        }
    }
}

5. 完整应用结构示例

@Composable
fun AppContent(localeState: State<Locale>) {
    val context = LocalContext.current
    
    Scaffold(
        topBar = { TopAppBar(title = { Text(stringResource(R.string.app_name)) }) },
        content = { padding ->
            Column(modifier = Modifier.padding(padding)) {
                LanguageSwitcher(localeState)
                
                // 示例文本,会随语言变化
                Text(stringResource(R.string.hello_world))
            }
        }
    )
}

注意事项

  1. Activity重启:在Compose中更改语言后,仍然需要调用activity.recreate()来完全刷新界面

  2. 资源文件:确保在res/values-<language code>目录下有对应的翻译资源

  3. 动态文本:对于动态生成的文本(如从API获取的),需要在代码中根据当前语言处理

  4. 测试:特别注意测试不同Android版本的兼容性

  5. 性能优化:频繁的语言切换可能会影响性能,考虑添加适当的防抖机制

这种方法结合了传统的Android语言配置和Compose的响应式特性,能够在保持良好用户体验的同时实现动态语言切换。

​​​​​​​Android应用中设置非系统默认语言(java)-CSDN博客

Android应用中设置非系统默认语言(使用Kotlin)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2386192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java 中的 super 关键字

个人总结&#xff1a; 1.子类构造方法中没有显式使用super&#xff0c;Java 也会默认调用父类的无参构造方法 2.当父类中没有无参构造方法&#xff0c;只有有参构造方法时&#xff0c;子类构造方法就必须显式地使用super来调用父类的有参构造方法。 3.如果父类没有定义任何构造…

PCM音频数据的编解码

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 例如&#xff1a…

Ubuntu 通过指令远程命令行配置WiFi连接

前提设备已经安装了无线网卡。 1、先通过命令行 ssh 登录机器。 2、搜索wifi设备&#xff0c;指令如下&#xff1a; sudo nmcli device wifi 3、输入需要联接的 wifi 名称和对应的wifi密码&#xff0c;指令如下&#xff1a; sudo nmcli device wifi connect wifi名称 passw…

8天Python从入门到精通【itheima】-41~44

目录 41节-while循环的嵌套应用 1.学习目标 2.while循环的伪代码和生活情境中的应用 3.图片应用的代码案例 4.代码实例【Patrick自己亲手写的】&#xff1a; 5.whlie嵌套循环的注意点 6.小节总结 42节-while循环的嵌套案例-九九乘法表 1.补充知识-print的不换行 2.补充…

深度图数据增强方案-随机增加ROI区域的深度

主要思想&#xff1a;随机增加ROI区域的深度&#xff0c;模拟物体处在不同位置的形态。 首先打印一张深度图中的深度信息分布&#xff1a; import cv2 import matplotlib.pyplot as plt import numpy as np import seaborn as sns def plot_grayscale_histogram(image_path)…

两个mysql的maven依赖要用哪个?

背景 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId> </dependency>和 <dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId> &l…

Kafka Consumer工作流程

Kafka Consumer工作流程图 1、启动与加入组 消费者启动后&#xff0c;会向 Kafka 集群中的某个 Broker 发送请求&#xff0c;请求加入特定消费者组。这个 Broker 中的消费者协调器&#xff08;Consumer Coordinator&#xff09;负责管理消费者组相关事宜。 2、组内分区分配&am…

大腾智能 PDM 系统:全生命周期管理重塑制造企业数字化转型路径

在当今激烈的市场竞争中&#xff0c;产品迭代速度与质量已成为企业生存与发展的核心命脉。面对客户需求多元化、供应链协同复杂化、研发成本管控精细化等挑战&#xff0c;企业亟需一套能够贯穿产品全生命周期的数字化解决方案。 大腾智能PDM系统通过构建覆盖设计、研发、生产、…

【短距离通信】【WiFi】WiFi7关键技术之4096-QAM、MRU

目录 3. 4096-QAM 3.1 4096-QAM 3.2 QAM 的阶数越高越好吗&#xff1f; 4. MRU 4.1 OFDMA 和 RU 4.2 MRU 资源分配 3. 4096-QAM 摘要 本章主要介绍了Wi-Fi 7引入的4096-QAM对数据传输速率的提升。 3.1 4096-QAM 对速率的提升 Wi-Fi 标准一直致力于提升数据传输速率&a…

线程池实战——数据库连接池

引言 作者在前面写了很多并发编程知识深度探索系列文章&#xff0c;反馈得知友友们收获颇丰&#xff0c;同时我也了解到友友们也有了对知识如何应用感到很模糊的问题。所以作者就打算写一个实战系列文章&#xff0c;让友友们切身感受一下怎么应用知识。话不多说&#xff0c;开…

基于moonshot模型的Dify大语言模型应用开发核心场景

基于moonshot模型的Dify大语言模型应用开发核心场景学习总结 一、Dify环境部署 1.Docker环境部署 这里使用vagrant部署&#xff0c;下载vagrant之后&#xff0c;vagrant up登陆&#xff0c;vagrant ssh&#xff0c;在vagrant 中使用 vagrant centos/7 init 快速创建虚拟机 安装…

华为OD机试真题——字符串序列判定(2025B卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 B卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

工商总局可视化模版-Echarts的纯HTML源码

概述 基于ECharts的工商总局数据可视化HTML模版&#xff0c;帮助开发者快速搭建专业级工商广告数据展示平台。这款模版设计规范&#xff0c;功能完善&#xff0c;适合各类工商监管场景使用。 主要内容 本套模版采用现代化设计风格&#xff0c;主要包含以下核心功能模块&…

Spring AI 和 Elasticsearch 作为你的向量数据库

作者&#xff1a;来自 Elastic Josh Long, Philipp Krenn 及 Laura Trotta 使用 Spring AI 和 Elasticsearch 构建一个完整的 AI 应用程序。 Elasticsearch 原生集成了业界领先的生成式 AI 工具和服务提供商。查看我们关于超越 RAG 基础或使用 Elastic 向量数据库构建生产级应用…

集群聊天服务器学习 配置开发环境(VScode远程连接虚拟机Linux开发)(2)

配置远程开发环境 第一步&#xff1a;Linux系统运行sshd服务 第二步&#xff1a;在vscode上安装Remote Deve I opment插件&#xff0c;其依赖插件会自动安装 第三步&#xff1a;配置远程Linux主机的信息 第四步&#xff1a;在vscode上开发远程连接Linux 第一步&#xff1a;…

rabbitmq的使用介绍

一.队列工作模式介绍 1.WorkQueues模型 生产者直接把消息发送给队列&#xff0c;然后消费者订阅队列 特点: 消息不会重复, 分配给不同的消费者. 代码实现&#xff1a; 消费者代码&#xff1a; Component Slf4j public class SpringRabbitListener {RabbitListener(queues &q…

系统编程day04

一.进程的基本概念 一.定义 进程是一个程序执行的过程&#xff08;也可以说是正在运行的程序&#xff09;&#xff0c;是系统分配资源的基本单位&#xff0c;由cpu对各个进程指挥调度&#xff0c;在单核cpu的情况下,各个进程可以通过一定规则在cpu上并发运行。 二.PCB块 1.PC…

Arduino Uno KY-037声音传感器实验

KY-037声音传感器实验 KY-037声音传感器实验1、 实验内容2、KY-037声音传感器介绍3、实验注意事项4、代码和实验现象 KY-037声音传感器实验 1、 实验内容 通过对KY-037声音传感器吹气&#xff0c;控制LED的打开和关闭&#xff0c;吹一下LED打开&#xff0c;在吹一下LED关闭。…

基于音频Transformer与动作单元的多模态情绪识别算法设计与实现(在RAVDESS数据集上的应用)

摘要&#xff1a;情感识别技术在医学、自动驾驶等多个领域的广泛应用&#xff0c;正吸引着研究界的持续关注。本研究提出了一种融合语音情感识别&#xff08;SER&#xff09;与面部情感识别&#xff08;FER&#xff09;的自动情绪识别系统。在SER方面&#xff0c;我们采用两种迁…

什么是VR实景?有哪些高价值场景?

在数字化浪潮的推动下&#xff0c;虚拟现实技术正以前所未有的速度改变着我们的生活方式和工作模式。 其中&#xff0c;VR实景作为VR技术的一个重要应用场景&#xff0c;独特的沉浸感和交互性&#xff0c;在众多领域展现出应用潜力和高价值场景。什么是VR实景&#xff1f;VR实…