Creator 版本 2.3.1
1. 起因
由于公司的项目需要上线苹果商店,其中有一些文字不能显示,但是安卓端需要显示,老大说用I18n吧,思来想去这个方案挺合适的呀, 于是就采用了多语言的解决方案[手动狗头]
2.过程
既然敲定了方案,那下一步就是找到解决方案,于是屁颠屁颠来到官网上找i18n的插件, 一看官方文档居然有介绍,当时感觉这个妙呀,素不知我正一步一步进入官方的深渊
以上就是在新手教程中找的资源,然后我迫不及待的下载下来想试着跑一下,并幻想着明天就能升职加薪当上CEO迎娶白富美从此走上人生巅峰呢
3.探索
- 插件下载下来了,一看有些年头了,心中的信念瞬间灭了一半,但是万一好使呢[狗头],于是我新开了一个项目,将插件放在packages下面,想着接下来就能见证奇迹了呢
- 然后经过不短的折腾最后证明,你越害怕什么,就越会发生什么。奇迹没有发生,插件跑步起来,甚至在我的编辑器中都显示不出来文字
- 事情到了这一步感觉有点亏呀,作为一个羊毛党不能白干这半天,想着他既然跑不起来,那我学习一下原理也算是有收获了呀,于是开始查看源码,插件的源码就懒得看了,主要看了一下加载及初始化的过程及给label赋值的过程,大致明白了它的原理: 给label加一个组件,在onLoad中给他赋值(过去有点久了,快忘了)。
- 作为一个程序员,你告诉我要给每一个Label添加一个组件不如让我去死呀


,我必须找到一键式的替换方式,毕竟项目是中途要求做I18n,如果一个一个替换,可能我的职业生涯就到此结束了
4.不断尝试
既然想要偷懒,那有一个可以偷懒的方案才可以,怎么办才好呢。。。。
一个危险的想法从我心中冒了出来,既然是改Label显示,那就直接一点,吧Label的实现改掉就好了,作为一个多人合作的项目,项目定制就显得太麻烦,作为一个程序猿最怕的就是麻烦了,定制引擎不仅要建仓库,还要让好几个人替换更改配置,而且之后引擎升级也会有困难。于是js的好处提现出来了,我直接覆盖你的方法就行了。于是好几种方法复现在我眼前

随便找个文件,将代码粘上就行,只要是全局运行一次就会覆盖之前定义好的方法
1.
import { i18n } from './I18n';
const CacheMode = cc.Enum({
NONE: 0,
BITMAP: 1,
CHAR: 2,
});
cc.Label.prototype['onLoad'] = function(){
// For compatibility with v2.0.x temporary reservation.
if (this._batchAsBitmap && this.cacheMode === CacheMode.NONE) {
this.cacheMode = CacheMode.BITMAP;
this._batchAsBitmap = false;
}
if (cc.game.renderType === cc.game.RENDER_TYPE_CANVAS) {
// CacheMode is not supported in Canvas.
this.cacheMode = CacheMode.NONE;
}
this.string = i18n.zh[this._string] === undefined ? this._string : i18n.zh[this._string];
}
怎么说呢, 感觉有点累赘,并且以后扩展也会有问题 于是改版
2.
import { i18n } from './I18n';
cc.Label.prototype['onLoadOld'] = cc.Label.prototype['onLoad']
cc.Label.prototype['onLoad'] = function(){
cc.Label.prototype['onLoadOld']();
this.string = i18n.zh[this._string] === undefined ? this._string : i18n.zh[this._string];
}
嗯, 是比以前好点了, 但是吧,丑于是在改
import { i18n } from './I18n';
Object.defineProperty(cc.Label.prototype, 'string', {
get : function(){
if(this._string.indexOf("/i18n/") == 0){
return i18n.zh[this._string] === undefined ? this._string : i18n.zh[this._string];
}
return this._string;
}
})
嗯,我只改了get方法,官方不会打我吧
注意以上方法都会改变在编辑器中的显示,而且只需要全局执行一次,不需要再每个Label组件中再添加一个组件
5.不断优化
上面提到了,更改了set方法后会再编辑器中直接显示结果而不是I18n中的key的值,我们是一个追求完美的人呀,怎么能这么粗糙了事,我们追求的是在场景中显示i18n中的值在检查器中显示i18n中的key,怎么做呢。。。。
看来要扣引擎的源码了
于是找找找 找到了inspector: ‘packages://inspector/inspectors/comps/label.js’ 这样一句话,看我白嫖党的威力
这个inspector一般手段是找不到的, 不过官方的 开发者 工具给了我一线希望
格式化之后看了一下主要是 target.string 在作怪,而且内容是固定的,所以增加@property(cc.String)等方式扩展属性是不好使的
又想到官方可以通过Vue更定制inspector
于是在危险的边缘疯狂试探终于找打了方案,使用mixin给Vue组件扩展功能

if(CC_EDITOR && CC_DEV){
Vue.mixin({
created(){
if(this.constructor.name == 'CcLabel'){
this.target = new Proxy(this.target,{
get(t, p, r){
if(p == 'string'){
t[p].value = t['_string'].value;
t[p].values = t['_string'].values;
}
return t[p];
}
})
}
}
})
}
6. 总结
-
关于后期提取文本
核心思路是遍历 prefab 因为 prefab 是一维数组,使用 JSON.parse 解析读取到的文件找到 “type” 为 “cc.Label"的对象,将其中的”_N$string"和"_string"都替换掉,然后通过JSON.stringify(prefabArr, null, 2) 覆盖原文件就可以了,最终的文件key的顺序不会乱,看来官方也是这么生成prefab文件的 [狗头护体] -
其实我用到的I18n 就是一个 简单的 Object 可以在一个文件中处理好后再另一个地方导入
贴上代码
// I18n.ts
export let i18n = {
"zh": {
"/i18n/name:931": "九九九"
}
}
// manager.ts 随便一个文件,加载一次就会覆盖引擎的方法
import { i18n } from './I18n';
if(CC_EDITOR && CC_DEV){
Vue.mixin({
created(){
if(this.constructor.name == 'CcLabel'){
this.target = new Proxy(this.target,{
get(t, p, r){
if(p == 'string'){
t[p].value = t['_string'].value;
t[p].values = t['_string'].values;
}
return t[p];
}
})
}
}
})
}
Object.defineProperty(cc.Label.prototype, 'string', {
get : function(){
if(this._string.indexOf("/i18n/") == 0){
return i18n.zh[this._string] === undefined ? this._string : i18n.zh[this._string];
}
return this._string;
}
})
效果图





