web-to-app 构建器 UI 语言切换机制:10 语言即时切换、DataStore 持久化与阿拉伯语 RTL 适配
2026/9/17 23:14:12 网站建设 项目流程

web-to-app 构建器 UI 语言切换机制:10 语言即时切换、DataStore 持久化与阿拉伯语 RTL 适配

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

web-to-app(一个完全在手机上运行的 Web 转 APK 工坊)的宿主界面完整本地化为 10 种语言。本文基于仓库文档 language.md 与对应源码实现,讲清楚构建器 UI 语言选择器的入口位置、即时生效与确认提示的工作方式、语言偏好的持久化与系统语言回退策略、阿拉伯语完整 RTL 布局的实现,以及"构建器界面语言"与"生成应用内容语言"之间的作用范围边界。读完本文,你可以准确描述该语言功能的完整调用链,并知道新增一条本地化字符串时应遵循的源码约定。

入口在哪里:顶栏语言按钮与首次启动语言选择页

文档中的原始描述是:"我的应用 顶栏的语言按钮打开构建器 UI 的语言选择器"。结合源码,构建器实际上暴露了三个语言选择入口,它们都调用同一个LanguageManager单例:

  1. 顶栏语言按钮:主界面(我的应用列表)顶栏的语言图标按钮,由 HomeScreen.kt 中挂载的 LanguageSelectorButton 渲染。点击后弹出LanguageSelectionDialog对话框,列出全部 10 种语言的卡片式选项,当前语言以高亮边框加对勾标记展示。
  2. 首次启动语言选择页:MainActivity.kt 通过languageManager.hasSelectedLanguageFlow判断用户是否已选择过语言;若尚未选择(language_selected标记不存在),则在进入主导航之前先展示FirstLaunchLanguageScreen(定义在 LanguageSelector.kt)。该页面在用户做出选择之前,用"中英阿三语并行"的方式显示欢迎语(如Welcome / 欢迎 / مرحبا),并针对 Android TV 环境自动放大图标与卡片间距。
  3. 设置页语言卡片LanguageSettingsCard(同文件)以只读文本框 +ExposedDropdownMenu下拉菜单的形式提供语言切换,作为主界面的补充入口。

三个入口最终都收敛到languageManager.setLanguage(language)这一条路径,因此行为完全一致。

支持的语言:10 种语言的完整定义

宿主界面完整本地化为10 种语言:中文、英文、阿拉伯文(完整 RTL)、葡萄牙文、西班牙文、法文、德文、俄文、日文、韩文。

这不是文档的口头承诺,而是直接由枚举 AppLanguage 决定的。每个语言条目携带 6 个字段,是理解整个语言机制的钥匙:

枚举值code原生名称是否 RTL备注
CHINESEzh中文Locale.CHINESE
ENGLISHenEnglishLocale.ENGLISH,同时是回退默认语言
ARABICarالعربية唯一的isRtl = true条目
PORTUGUESEptPortuguês
SPANISHesEspañol
FRENCHfrFrançais
GERMANdeDeutsch
RUSSIANruРусский
JAPANESEja日本語
KOREANko한국어

两个值得注意的设计细节:

  • fromCode的回退策略:AppLanguage.fromCode 在code匹配不到任何条目时返回CHINESE,而不是抛异常——这与该应用以中文为默认宿主语言的定位一致。
  • translationInProgress标记:枚举支持"翻译进行中"徽章字段,LanguageSelector.kt 中的LanguageDisplayNameWithBadge会据此在语言名后追加徽章。当前 10 种语言均为完整本地化,因此该徽章默认不出现,但它为未来新增语言预留了状态位。

选择对话框中的每个选项显示nativeName(原生名称,如 العربية)为主标题、displayName(英文显示名,如 Arabic)为副标题,这一点从 LanguageOption 的排版代码可以直接确认。

工作方式:即时应用、确认提示与持久化

文档给出的用户视角行为有三条:

  1. 点击语言按钮并选择一种语言;
  2. 更改立即应用到整个构建器 UI,并显示一条确认提示;
  3. 阿拉伯文会把布局切换为完整的从右到左。

下面逐条对应到源码实现。

即时应用的调用链

LanguageSelectorButton内部的选中回调非常短(LanguageSelector.kt):

onLanguageSelected = { language -> scope.launch { languageManager.setLanguage(language) onLanguageChanged() } showDialog = false }

setLanguage是一个 DataStore 写操作(LanguageManager.kt):

suspend fun setLanguage(language: AppLanguage) { context.languageDataStore.edit { prefs -> prefs[LANGUAGE_KEY] = language.code // 键 "app_language" prefs[LANGUAGE_SELECTED_KEY] = "true" // 键 "language_selected" } }

之所以"立即"生效而不需要重启应用,是因为读取侧是一个响应式Flow(LanguageManager.kt):

val currentLanguageFlow: Flow<AppLanguage> = context.languageDataStore.data.map { prefs -> val code = prefs[LANGUAGE_KEY] ?: getSystemLanguageCode() AppLanguage.fromCode(code) }

UI 侧用collectAsState订阅该 Flow,DataStore 一写入,Compose 状态即更新,所有订阅了currentLanguage的组件(语言按钮对话框、设置卡片、以及基于Strings.lang的文案)随之重新组合。这就是"更改立即应用到整个构建器 UI"的底层机制:单一数据源 + Flow 响应式分发

确认提示的来源

文档说"会显示一条确认提示"。在 HomeScreen.kt 中可以看到该提示的具体实现:

LanguageSelectorButton( onLanguageChanged = { scope.launch { snackbarHostState.showSnackbar(Strings.msgLanguageChanged) } } )

即切换成功后通过 Snackbar 弹出本地化文案Strings.msgLanguageChanged。注意这条文案本身也经过Strings本地化体系,所以确认提示同样会跟随新语言显示。

持久化键与系统语言回退

语言偏好存储在名为language_settings的 Preferences DataStore 中(LanguageManager.kt),共两个键:

键名作用取值
app_language用户选择的语言代码zh/en/ar/pt/es/fr/de/ru/ja/ko
language_selected是否已完成过首次语言选择(控制首启语言页是否再出现)"true"

当用户从未显式选择语言时,getSystemLanguageCode 会读取设备系统语言并做白名单映射:zh/ar/pt/es/fr/de/ru/ja/ko各归各,其余任何语言(如泰语、印尼语)一律回退到en。这解释了为什么界面不会出现"半支持"的语言——要么命中 10 种之一,要么显示英文。

阿拉伯语完整 RTL:isRtl与布局方向切换

阿拉伯文是 10 种语言中唯一在枚举上标记isRtl = true的条目。真正的切换发生在 applyLanguage:

fun applyLanguage(context: Context, language: AppLanguage): Context { val locale = language.locale Locale.setDefault(locale) val config = Configuration(context.resources.configuration) config.setLocale(locale) config.setLayoutDirection(locale) // 关键:由 Locale 推导布局方向 return context.createConfigurationContext(config) }

config.setLayoutDirection(locale)ar会返回LAYOUT_DIRECTION_RTL,配合createConfigurationContext派生出整个新的资源上下文。这就是文档所说"阿拉伯文会把布局切换为完整的从右到左"的实现位置——RTL 不是 UI 层手写镜像,而是交给 Android 配置系统的标准能力。开发规范文档 i18n.md 也明确把这一点列为硬性规则:"阿拉伯文必须完整 RTL——验证布局正确镜像。"

从源码结构看,applyLanguage派生的本地化Context由 Strings.attachContext 消费:Strings对象同时持有fallbackContextlocalizedContext两个上下文,切换语言时通过contextVersionmutableIntStateOf)自增版本号来触发 Compose 失效重组,使所有依赖Strings.*的文本节点重新求值。

作用范围边界:构建器语言 ≠ 应用内容语言

这是文档中最容易被忽略、但最实用的一条区分:

这是构建器的界面语言。你生成的应用的语言则按应用单独配置(生成的应用还可提供页内翻译叠加层)。

用仓库内的代码佐证:

  • 构建器语言LanguageManager+language_settingsDataStore 管理,只影响 web-to-app 自身界面(按钮、菜单、对话框、Snackbar 等),全部文案收敛在 Strings.kt 这一个文件中。
  • 生成应用的语言是每个 App 项目独立的配置项,随项目一起打包进导出的 APK;与构建器当前界面语言无关。你可以在英文构建器里生成一个阿拉伯语应用,反之亦然。
  • 此外,生成的 Web 类应用还可以叠加页内翻译能力(即 appearance.md 描述的翻译叠加层),它作用于网页内容本身,同样与构建器 UI 语言相互独立。

三层语言(构建器界面语言 / 生成应用的语言配置 / 生成应用的页内翻译)彼此解耦,这是理解该功能的完整心智模型。

源码级补充:新增一条本地化字符串的约定

如果你要阅读或扩展这套本地化体系,developer/i18n.md 给出了官方约定,可与源码相互印证:

  1. 字符串全部集中在 Strings.kt。该文件约 6.7 万行,Strings对象按字母区间拆分为StringsAStringsE等内部拆分对象,Strings自身只是委托层(例如val languageSettings: String get() = StringsA.languageSettings)。

  2. 必须覆盖全部 10 种语言,且禁止使用else分支

    // 示意——匹配 Strings.kt 中实际的拆分对象风格 val myNewLabel: String get() = when (Strings.lang) { AppLanguage.CHINESE -> "我的标签" AppLanguage.ENGLISH -> "My label" AppLanguage.ARABIC -> "..." // ... 全部 10 种,绝不用 else }

    用穷尽的when代替else,意味着编译器能强制检查每种语言是否都有文案,漏译会在编译期暴露而不是运行时静默回退。

  3. 不要在 Compose 界面中硬编码用户可见文本,始终经由Strings引用——否则该文本将无法跟随语言切换,也不会出现在本地化覆盖检查中。

需要注意的是,首次启动页 FirstLaunchLanguageScreen 中的欢迎语采用了内联的when (selectedLanguage)写法而非走Strings,因为该页面在"选择之前"就需要同时展示多语言文案,属于有意的例外场景。

小结与关键文件索引

web-to-app 的构建器语言功能是一个小而完整的国际化样本:AppLanguage枚举定义 10 种语言及其 RTL 属性;LanguageManager以 DataStore 为单一数据源,配合Flow实现切换即时生效;applyLanguage借助Configuration.setLayoutDirection完成阿拉伯语全量 RTL;UI 侧由顶栏按钮、首启选择页、设置卡片三个入口共用同一调用链;文案则全部收口在Strings.kt中按穷尽式when分发。

文件职责
LanguageManager.ktAppLanguage枚举、DataStore 持久化、系统语言回退、applyLanguage的 Locale/RTL 切换
LanguageSelector.kt顶栏按钮、选择对话框、首启语言页、设置页下拉卡片
HomeScreen.kt顶栏挂载语言按钮,切换成功后弹出 Snackbar 确认提示
MainActivity.kt依据hasSelectedLanguageFlow决定首启是否展示语言选择页
Strings.kt全部宿主界面文案,按Strings.lang穷尽分发
i18n.md开发者侧的本地化规则(10 语全覆盖、禁用else、RTL 验证)

【免费下载链接】web-to-appThe most full featured web-to-app toolkit on Android, a complete APK workshop that runs entirely on your phone项目地址: https://gitcode.com/GitHub_Trending/web/web-to-app

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询