【高阶用法】uniapp的i18n/修复/增强/App无重启更换语言

news/2024/9/23 22:40:22/

痛点

i18n多语言模块使用过程中,发现下面几个问题,需要解决

1)uni-best框架下,$t功能函数无法实时的切换语言,可能跟使用有关

2)uni-best建议的translate方式在vue块外使用太繁琐,希望不用导入,直接书写$t使用。统一逻辑,减少复杂度

3)uniapp默认的多语言模式在APP下会重启,造成的体验不好

目标

需要完成的目标如下

1)将多语言模块放到公共区域,可能会导致原生标题无法正常切换语音。这个无所谓,因为标题栏已经custom定制并组件化了

2)修复无法正常实时切换语言的$t,这个可能跟使用方式有关,anyway,让它能按原模式正常工作

3)在任何地方都可以使用$t功能,无论是template还是script部分

4)不用重启直接刷新界面

实现

uni-best的translate方法代码实现了一个很好的思路,只是无法支持占位符的功能。让我们改进它

/*** 任意文件使用$t翻译方法,需要在app里全局导入* @param { string } localeKey 多语言的key,eg: "app.name"*/
export const translate = (localeKey: string, opt: Record<string, any> = {}) => {if (!localeKey) {console.error(`[i18n] Function translate(), localeKey param is required`)return ''}const locale = uni.getLocale()const message = messages[locale]if (Object.keys(message).includes(localeKey)) {const template = message[localeKey]// 使用 Object.keys 遍历 params 对象,替换模板中的大括号占位符return Object.keys(opt).reduce((acc, key) => acc.replace(new RegExp(`{${key}}`, 'g'), opt[key]),template)}return localeKey // 转换不了则原样输出
}

然后在main.ts里把它挂载到全局

import { message, alert, confirm, translate } from '@/utils'...export function createApp() {const app = createSSRApp(App)...app.use(i18n)app.config.globalProperties.$t = translate // 覆盖不能正常工作的$t函数// #ifdef MP-WEIXIN// 由于微信小程序的运行机制问题,需声明如下一行,H5和APP非必填app.config.globalProperties._i18n = i18n// #endifreturn {app}
}

至于在任意位置使用,让我们把translate挂载到代码部分的全局

// 安装到全局,覆盖不能正常工作的$t
;(function (global) {console.log('install');(global as any).$t = translate
})(this || window || globalThis)

下面是最终完成的i18n.ts模块,添加了语言切换功能的导出。

API.tools.locale.request是后端的语言切换代码,实现前后端语言统一切换,目前只导入了3种语言,需要其它语言可以自行增加

/*** ccframe i18n模块* 注意:由于某种未知的原因,uni-best的$t()翻译方法有无法切换语音以及安卓出错的问题,因此使用导出的translate方法进行动态翻译* @Jim 24/09/20*/import { createI18n } from 'vue-i18n'import en from '@/datas/en.json'
import zhHans from '@/datas/zh-Hans.json'
import zhHant from '@/datas/zh-Hant.json'const messages = {en,'zh-Hant': zhHant,'zh-Hans': zhHans // key 不能乱写,查看截图 screenshots/i18n.png
}const i18n = createI18n({legacy: false, // 解决空白报错问题locale: uni.getLocale(), // 获取已设置的语言,fallback 语言需要再 manifest.config.ts 中设置messages
})type LocaleType = 'en' | 'zh-Hant' | 'zh-Hans'i18n.global.locale.value = import.meta.env.VITE_FALLBACK_LOCALE/*** 任意文件使用$t翻译方法,需要在app里全局导入* @param { string } localeKey 多语言的key,eg: "app.name"*/
export const translate = (localeKey: string, opt: Record<string, any> = {}) => {if (!localeKey) {console.error(`[i18n] Function translate(), localeKey param is required`)return ''}const locale = uni.getLocale()const message = messages[locale]if (Object.keys(message).includes(localeKey)) {const template = message[localeKey]// 使用 Object.keys 遍历 params 对象,替换模板中的大括号占位符return Object.keys(opt).reduce((acc, key) => acc.replace(new RegExp(`{${key}}`, 'g'), opt[key]),template)}return localeKey // 转换不了则原样输出
}const langMapper: Record<string, string> = {'zh-Hans': 'zh-CN','zh-Hant': 'zh-TW',en: 'en-US'
}export const setLocale = async (locale: LocaleType) => {await API.tools.locale.request({ lang: langMapper[locale] })// #ifdef APP-PLUSsetTimeout(() => {// 如果是APP,需要等待重新启动页面i18n.global.locale.value = localeuni.setLocale(locale)}, 300)// #endif// #ifndef APP-PLUSi18n.global.locale.value = localeuni.setLocale(locale)// #endif// currentLang.value = locale
}// 安装到全局,覆盖不能正常工作的$t
;(function (global) {console.log('install');(global as any).$t = translate
})(this || window || globalThis)export default i18n/** 非vue 文件使用 i18n
export const testI18n = () => {console.log(t('app.name'))// 下面同样生效uni.showModal({title: 'i18n 测试',content: t('app.name')})
} */

然后就可以简单愉快的使用多语言功能了。

页面上$t('login.enterPhone')根据语言显示“输入手机号码”:

          <up-input

            fontSize="32rpx"

            :placeholder="$t('login.enterPhone')"

            border="surround"

            v-model="data.userMobile"

            style="letter-spacing: 2rpx"

            @change="data.mobileErr = ''"

            type="number"

            maxlength="11"

          />
代码片段,这个是form表单验证公共库里的使用:
 

  required(error?: string): Validator {

    this.push({

      required: true,

      validator: (rule: any, val: any, callback: (error?: Error) => void) => {

        // 补充验证模式

        return val !== undefined && val !== null && val !== ''

      },

      message:

        error ??

        (this.labelText

          ? $t('utils.validator.required') + this.labelText

          : $t('utils.validator.notEmpty')),

      trigger: ['change', 'blur']

    })

    return this

  }

$t('utils.validator.required')根据语言输出:请输入
$t('utils.validator.notEmpty')根据语言输出:内容不能为空

完善Typescript类型定义

这样使用起来,还有那么一点不舒服,就是在script中使用$t时,会报错类型找不到红红的一片(实际编译没问题)。对于代码强迫症人会有点一点受不了,那么让这个错误的爆红消失掉:

unibest里原本带了i18n.d.ts文件,把我们挂载到script全局的定义添加进去:
 

/* eslint-disable no-unused-vars */
export {}declare module 'vue' {interface ComponentCustomProperties {$t: (key: string, opt?: Record<string, any>) => string// $tm: (key: string, opt?: Record<string, any>) => [] | { [p: string]: any }}
}declare global {function $t(localeKey: string, opt?: Record<string, any>): string
}

刷新一下vscode,不爆红了,完美~

--------------------------------------- [ 2024/9/21 分界线] ---------------------------------------------

UNIAPP 安卓/IOS模式不用重启界面直接切换语言

经过前面的折腾,发现可以直接绕过uni.getLocale和uni.setLocale来实现语言切换了。这样等于就是解决了uniapp官方框架下必须重启APP才能更换语言的软肋

导出切换语言功能:
setStorageSync是我将语言保存到存储,以便系统启动时按照指定的语言启动

export const getLocale = () => i18n.global.locale.valueexport const setLocale = async (locale: LocaleType) => {await API.tools.locale.request({ lang: langMapper[locale] })uni.setStorageSync(Global.STOAGE_LOCALE, locale)i18n.global.locale.value = locale
}

调用:

const switchLang = async () => {// 切换应用语言到 {langName} 吗?应用将重启以更改语言(新技术不用重启)if (utils.getLocale() !== 'zh-Hans') {await utils.setLocale('zh-Hans')} else {await utils.setLocale('en')}
}

这个方法虽然可以直接切换,但是有个条件,就是官方的组件无法依赖默认的。好在大部分组件都提供了默认占位作为参数可以传入,因此像对话框这类基本组件只需要使用时添加多几个国际化参数即可

效果:


http://www.ppmy.cn/news/1529525.html

相关文章

kubernetes技术详解,带你深入了解k8s

目录 一、Kubernetes简介 1.1 容器编排应用 1.2 Kubernetes简介 1.3 k8s的设计架构 1.3.1 k8s各个组件的用途 1.3.2 k8s各组件之间的调用关系 1.3.3 k8s的常用名词概念 1.3.4 k8s的分层结构 二、k8s集群环境搭建 2.1 k8s中容器的管理方式 2.2 k8s环境部署 2.2.1 禁用…

828华为云征文 | 华为云X实例监控与告警管理详解

前言 随着云计算的飞速发展&#xff0c;越来越多的企业将业务部署在云平台上&#xff0c;云服务器实例的管理变得尤为重要。云实例的稳定性、性能及安全性&#xff0c;直接影响着业务的连续性与用户体验。为了确保这些目标的实现&#xff0c;监控与告警是关键手段。本文将详细…

恶意Bot流量识别分析实践

1、摘要 随着互联网的发展&#xff0c;自动化工具和脚本&#xff08;Bots&#xff09;的使用越来越普遍。虽然一些善意 Bots 对于网站的正常运行和数据采集至关重要&#xff0c;但恶意 Bots 可能会对网站带来负面影响&#xff0c;如爬取敏感信息、恶意注册、刷流量等。因此&am…

2024年及未来:构筑防御通胀的堡垒,保护您的投资

随着全球经济的波动和不确定性&#xff0c;通货膨胀已成为投资者不得不面对的现实问题。通胀会侵蚀货币的购买力&#xff0c;从而影响投资的实际回报。因此&#xff0c;制定有效的策略来保护投资免受通胀影响&#xff0c;对于确保资产的长期增值至关重要。在2024年及未来&#…

Android横竖屏 mdpi hdpi xhdpi xxhdpi xxxhdpi

DPI:每英寸像素数 简单的屏幕分辨率计算方法&#xff1a; DisplayMetrics metrics this.getResources().getDisplayMetrics(); float density metrics.density; int dpi metrics.densityDpi; int heightPixels metrics.heightPixels; int widthPixels metrics.widthPixels…

【C++】 —— string的使用

前言 string类虽然不在STL的容器中&#xff0c;但string类十分重要&#xff0c;string类是对字符串的存储和相关操作。 basic_string std::basic_string类是C的一个模版类&#xff0c;它支持多种字符类型。 char &#xff1a;用于表示一个字节的字符&#xff0c;使用ASCII编码。…

bprc二次封装

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、封装的思想二、封装单个服务的信道管理类1.成员变量2.成员函数 三、封装总体的服务信道管理类1.成员变量2.成员函数 四.etcd和brpc联合测试1.服务注册客户端2.服…

线程同步:消费者模型(非常重要的模型)

一.线程同步的概念 线程同步&#xff1a;是指在互斥的基础上&#xff0c;通过其它机制实现访问者对 资源的有序访问。条件变量&#xff1a;线程库提供的专门针对线程同步的机制线程同步比较典型的应用场合就是 生产者与消费者 二、生产者与消费者模型原理 在这个模型中&…