1、前言
一天下午,脚踩凉拖鞋,手拿橙c美式,耳戴AirPros Pro(只戴一边,懂得都懂),主屏开着vscode,副屏开着《五亿探长雷洛传:雷老虎》小屏模式,用上高中考试时训练出的斜眼法,快乐摸鱼的时候。
微信突然被“轰炸”,腚睛一看,好家伙,原来是我那活爹PM又来需求了。
PM: 现在公司业务要开拓市场,进军港澳台,所以要给项目加一个可以选用简繁字体的功能,三天之后的发版日就要上线,非常急!
我虽然心里骂骂咧咧,但还是不争气的说:好的,我办事你放心(玫瑰)
2、思考
这不就是多语言配置嘛,一个i18n就搞定啦,洒洒水啦。
3、实操
第一个方案:i18n
当我开始用i18n写多语言的时候,越写越发现不对劲。
一个页面很多文字是写死的,也就是说我需要给这些文字都翻译并保存在繁体的json文件里,同时还要维护 简/繁 体的json文件,最重要的是,这种页面有十几个。。。
每次页面有新增字段,都需要在json文件加上对应的繁体文字,还要重新发版,甚至有些从谷歌翻译过来的繁体和实际使用的繁体不一样,所以抛弃了这个方案。
第二个方案: 更换font-family
通过更换font-family,可以实现将简体自动翻译成繁体,不需要手动去维护json文件!
繁体font-family链接:点击这里
由于项目是跑在electron上的,所以需要监听BrowserWindow的 'did-finish-load' 事件,往webContents 注入css 。
webContents.on('did-finish-load', () => {
webContents.insertCSS(`
@font-face {
font-family: 'FanWunHak-R';
src: url('app://./assets/fonts/FanWunHak/FanWunHak-R.ttf');
}
.zh-tw *:not(input, textarea, .fa) {
font-family: FanWunHak-R,Arial,sans-serif !important;
}
`)
const localConfig = getConfig().local
if (localConfig.language === 'zh-tw') {
webContents.executeJavaScript(`document.body.classList.add('zh-tw')`)
}
}
15行代码,轻松拿捏。非常自信的把包丢在机器上给测试大佬测试,继续愉快的摸鱼。
可能是我想的过于简单,不一会,测试大佬又发消息过来,说:有些字体还是跟实际使用的字体不一样啊。比如:“爱”的繁体是“愛” ,但实际显示“爱”,存在个别字体不对的现象。
不应该啊,难道说FanWunHak这个字体库是不完整的?腚睛一看,好家伙,原来三年都没有更新过了。那这个方案又得放弃。
第三个方案:动态转换
这个方案是在另一个大佬那里看到的,我也是参考了这个方案。
大佬方案链接:点击这里
实际上,动态转换的核心两个函数,这两个函数,其中一个 charPYStr 返回简体的文字,另一个 ftPYStr 返回繁体的文字,通过下标进行文字的替换。
因为需要在页面渲染出来再进行动态转换,其中也存在接口修改了文字的情况,所以要监听页面dom更新,因此可以通过 MutationObserver 监听dom节点更新,再进行动态转换。
let observer: any
const targetNode = window.document.body;
const config = {
attributes: false,
childList: true,
subtree: true,
characterData: true,
};
const cb = (mutations: MutationRecord[]) => {
mutations.forEach(function (mutation) {
//加上characterData属性,允许观察文本节点的变化
if (mutation.type === "characterData") {
observer.disconnect();
replaceText(mutation.target, 'zh-tw');
observer.observe(targetNode, config);
} else {
replaceText(mutation.target, 'zh-tw');
}
});
};
observer = new MutationObserver(cb);
observer.observe(targetNode, config);
其中动态转换的函数是:replaceText,它的具体实现是这样
// 简体字符转为繁体字符;
function simple2traditional(cc: string) {
let str = "";
const simplifiedChinese = charPYStr();
const traditionalChinese = ftPYStr();
for (let i = 0; i < cc.length; i++) {
const c = cc.charAt(i);
if (c.charCodeAt(0) > 10000 && simplifiedChinese.indexOf(c) != -1)
str += traditionalChinese.charAt(simplifiedChinese.indexOf(c));
else str += c;
}
return str;
}
// 繁体字符转为简体字符;
export function traditional2simple(cc: string) {
let str = "";
const simplifiedChinese = charPYStr();
const traditionalChinese = ftPYStr();
for (let i = 0; i < cc.length; i++) {
const c = cc.charAt(i);
if (c.charCodeAt(0) > 10000 && traditionalChinese.indexOf(c) != -1)
str += simplifiedChinese.charAt(traditionalChinese.indexOf(c));
else str += c;
}
return str;
}
function replaceText(rootNode: Node, language: string) {
const converter = language === "zh-cn" ? traditional2simple : simple2traditional;
function inner(currentNode: HTMLElement, language: string) {
if (currentNode.tagName === "SCRIPT") return;
if (currentNode.tagName === "STYLE") return;
if (currentNode.tagName === "IMG") {
const altAtt = currentNode.getAttribute("alt");
if (altAtt) {
currentNode.setAttribute("alt", converter(altAtt));
return;
}
} else if (currentNode.tagName === "INPUT") {
const placeholderAtt = currentNode.getAttribute("placeholder");
if (placeholderAtt) {
currentNode.setAttribute("placeholder", converter(placeholderAtt));
return;
}
}
for (const node of currentNode.childNodes) {
if (node.nodeType === Node.TEXT_NODE) {
node.textContent = converter(node.textContent);
} else {
inner(node as HTMLElement, language);
}
}
}
if (rootNode.nodeType === Node.TEXT_NODE) {
rootNode.textContent = converter(rootNode.textContent);
} else {
inner(rootNode as HTMLElement, language);
}
}
由于只需要转换文字,因此把 script、style 等这种标签去掉,不需要转换。
只需要转换 input、img、p、span 等标签,通过判断标签的 nodeType === Node.TEXT_NODE ,就可以知道是不是文字标签或者含有文字。
这样就可以实现一个比较精准的简繁体转换了。
4、总结
| 方案 | 优势 | 劣势 |
|---|---|---|
| i18n | 国际化,支持外文场景 | 工作量大,需要逐个调整 |
| FanWunHak | 快速、省事 | 只适合简繁体场景,简繁体字体不一致会影响字体图标 |
| 动态转换 | 快速、省事,简繁体字体一致 | 只适合用简繁体场景 |
总之,上面三个简繁体转换方案可以覆盖大部分的场景,如果有其他更合适的方法,可以在评论区交流哦! 我继续去看雷洛传了,真的很好看!