多语言处理
注:该文档demo是在electron+react环境中编写,但是不受环境影响
前言:在react使用i18next库进行处理多语言的时候,感觉在使用当中会较为麻烦,结构也不够清晰,于是去了解有无其他处理方案,了解到一种处理方案如下,无需借助第三方库
超前预览:

可以看到这个欢迎页面也是成功实现了多语言的功能
1、首先建立locales文件夹用于存储我们的多语言和主要入口
目录结构预计如下,比较重要一点就是,各个语言文件的命名需要规范,以地区或者语言缩写命名

2、语言文件中需要做的事(cn.ts...)
在这些语言文件中,只需要编写一个对应程序模块的语言对象就可,如果程序不会太复杂,可以全部写在单文件,这样编写其他语言的时候,可以直接复制结构快速处理翻译
cn.ts 代码示例:
const cn = {
switchLang: '切换语言',
welcome: {
creator: '由 electron-vite 提供支持',
text: {
build: '使用以下技术构建 Electron 应用:',
react: 'React',
and: '和',
ts: 'TypeScript'
},
tip: {
openDevTool: '请尝试按下',
devTool: 'F12',
toOpen: '以打开开发工具'
},
actions: {
documentation: '文档',
sendIPC: '发送 IPC'
}
}
}
export default cn我们只需要根据页面的相关模块进行结构化编写语言代码即可,无需单独编写key文件
3、多语言主要入口文件中需要做的事(index)
index.ts 导入所有语言文件
import cn from './cn'
import en from './en'
import hk from './hk'index.ts 常量的设置,如全部语言对象、用于localStorage存储的key、默认语言
const ALL_LANGS = {
cn,
en,
hk
}
const LANG_KEY = 'lang'
const DEFAULT_LANG = 'en'index.ts 然后就是将需要用到的类型进行推导出来,再导出语言数组和其选项对象
export type LocaleType = typeof cn
export type Lang = keyof typeof ALL_LANGS
export const localeList = Object.keys(ALL_LANGS) as Lang[]
// 使用Record来作为类型限制
export const localeOptions: Record<Lang, string> = {
cn: '简体中文',
en: 'English',
hk: '繁體中文'
}index.ts 主要的获取语言和设置语言函数
export const getLang = (): Lang => {
const lang = localStorage.getItem(LANG_KEY) as Lang
// 如果保存过配置,直接使用配置的语言
if (localeList.includes(lang)) {
return lang
}
// 当没有配置语言,获取系统语言
const sysLang = new Intl.Locale(navigator.language)
const region = sysLang.region?.toLowerCase()
// 如果地区缩写被包含,返回对应地区的语言
if (localeList.includes(region as Lang)) {
return region as Lang
}
// 如果系统语言被包含,返回对应语言
if (localeList.includes(sysLang.language as Lang)) {
return sysLang.language as Lang
}
// 否则返回默认语言
return DEFAULT_LANG
}
// 设置语言,设置后重新加载页面
export const setLang = (lang: Lang) => {
localStorage.setItem(LANG_KEY, lang)
location.reload()
}
// 合并对象函数
export const merge = <T extends object, U extends object>(target: T, source: U): T & U => {
const result = { ...target } as T & U
Object.keys(source).forEach(function (key) {
if (
(source.hasOwnProperty(key) && source[key] && typeof source[key] === 'object') ||
key === '__proto__' ||
key === 'constructor'
) {
result[key] = merge(result[key] || {}, source[key])
return
}
result[key] = source[key]
})
return result
}index.ts 通过设置fallback语言和获取目标语言,通过合并后最后导出语言,合并是为了防止当目标语言不属于主要维护语言,出现存在遗漏字段的情况,遗落的字段由主要维护语言补上
const fallbackLang = en
const targetLang = ALL_LANGS[getLang()] as LocaleType
// 合并后,如果目标语言缺失了字段,会使用备用语言的值
const localeLang = merge(fallbackLang, targetLang)
export default localeLang4、组件中如何使用
在组件中不需要将cn.ts之类的导入,只需要将多语言入口文件导入即可
App.tsx
import Locale, {
getLang,
setLang,
localeList,
localeOptions,
} from "@renderer/locales"
// ...other import
export const App = (): JSX.Element => {
return (
<>
{/* 语言选择组件 */}
<div className="absolute top-4 right-4">
<span className="mr-2">{Locale.switchLang}</span>
<Select
value={getLang()}
style={{ width: 120 }}
onChange={(value) => setLang(value)}
options={
localeList.map((lang) => ({
label: localeOptions[lang],
value: lang,
}))
}
/>
</div>
<img alt="logo" className="logo" src={electronLogo} />
<div className="creator">{Locale.welcome.creator}</div>
<div className="text">
{Locale.welcome.text.build} <span className="react">{Locale.welcome.text.react}</span>
{Locale.welcome.text.and} <span className="ts">{Locale.welcome.text.ts}</span>
</div>
<p className="tip">
{Locale.welcome.tip.openDevTool} <code>{Locale.welcome.tip.devTool}</code> {Locale.welcome.tip.toOpen}
</p>
<div className="actions">
<div className="action">
<a href="https://electron-vite.org/" target="_blank" rel="noreferrer">
{Locale.welcome.actions.documentation}
</a>
</div>
<div className="action">
<a target="_blank" rel="noreferrer" onClick={ipcHandle}>
{Locale.welcome.actions.sendIPC}
</a>
</div>
</div>
<Versions></Versions>
</>
)
}通过语言选择组件,就可以得知所有的使用示例了,如组件标题语言的使用 {Locale.switchLang},就只需要将导入的语言当作一个对象进行使用即可,在 Select 组件中,value为 getLang()方法返回的值,当改变的时候,使用 setLang() 函数进行设置,多语言选项则使用 localeList 来帮助生成,其中label为在 localeOptions 对象中对应的值
{/* 语言选择组件 */ }
<div className="absolute top-4 right-4">
{/* cn显示”切换语言“ */ }
<span className="mr-2">{Locale.switchLang}</span>
<Select
value={getLang()}
style={{ width: 120 }}
onChange={(value) => setLang(value)}
options={
localeList.map((lang) => ({
label: localeOptions[lang],
value: lang,
}))
}
/>
</div>这样就算是给我的electron应用加上了多语言的功能啦
个人目前认为这样实现,各个语言文件进行分开编写,使用简单,后续维护也较为简单