版本: v3.4.0
环境: Mac
简介
i18n是国际化的简称, 全名:internationalization;取首尾字符i和n,18代表单词中间的字符数目。
该插件不需要产品做太多的改变,通过语言的设置,实现不同地区之间页面的切换。
官方提供的i18n
插件,支持Label
和Sprite
的多语言国际化。
更多内容可参考: i18n 多语言支持
安装
从cocos Dashboard -> 商城 -> 搜索i18n免费插件 下载成功后,如果为zip
压缩包,可打开编译器的扩展管理器将i18n
导入进来。
然后通过终端命令进入到项目的extensions/i18n目录下, 执行安装命令:
npm install
如果安装很慢,可能是因为安装镜像为国外,导致网络卡顿,可以运行更换镜源的命令:
npm config set registry https://registry.npm.taobao.org
然后再重新运行npm install
,稍等片刻,等命令执行结束后,再执行构建命令:
npm run build
安装完成后,建议重启下Creator编译器。如果发现控制台有错误,可以忽略掉,在配置完成后,错误会自动消失。
多语言配置
打开编译器,选择主菜单上的扩展 -> i18 Settings, 打开本地化控制面板:
选择**+**号新增配置,比如输入zh代表中文,输入en代表英文,最后按回车键确认。
比如新增语言zh后,会在你的assets/resources
目录下生成i18n\zh
的配置文件。打开该文件内容:
const win = window as any;
export const languages = {
// Data
};
if (!win.languages) {
win.languages = {};
}
win.languages.zh = languages;
在export const languages
下输入内容:
export const languages = {
"test": {
"main": "测试",
"hello": "你好",
}
};
然后回到编译器,创建一个Label
文本, 将资源管理器的i18n/LocalizedLabel
组件拖到文本节点中。
并输入key内容test.main
,如下所示:
文本内容完成替换。
然后我们通过扩展 -> i18 Settings, 打开本地化控制面板,新增en
的内容相关。
// assets/resources/i18n/en.ts
const win = window as any;
export const languages = {
"test": {
"main": "Debug",
"hello": "Hello",
}
};
if (!win.languages) {
win.languages = {};
}
win.languages.en = languages;
通过本地化控制面板的那个眼睛( 表示当前语言, 表示不显示)进行切换语言:
如此就可以实现文本多语言功能了。
图片的配置
在编译器中创建一个Sprite
,然后将资源管理器/i18n/LocalizedSprite的组件拖曳到该节点下。
SpriteList 有几种语言就输入数字几,大概如图所示:
通过本地化控制面板的那个眼睛( 表示当前语言, 表示不显示)进行切换即可。
动态切换
项目运行时,需要支持语言的动态切换, 我们创建一个按钮,并增加点击事件,如下代码:
// import * as i18n from '../extensions/i18n/assets/LanguageData'
import * as i18n from 'db://i18n/LanguageData';
@ccclass('loginScene')
export class loginScene extends Component {
// 点击多语言切换
public clickChangeLanguage() {
// 通过init设置当前语言类型
if (i18n._language === "en") {
i18n.init("zh");
}
else if (i18n._language === "zh") {
i18n.init("en");
}
// 用于更新场景中的对应的LocalizedLabel和LocalizedSprite的内容
// 以实现多语言的替换相关
i18n.updateSceneRenderers();
}
}
大概的实现原理:
- 通过
i18n.init
初始化语言类型 - 通过LocalizedLabel下的
Key
声明语言标记; 通过LocalizedSprite下的spriteList
设置图片SpriteFrame - 除图片外,通过
i18n.t
根据当前语言类型和key想过获取文本内容 - 最后通过
i18n.updateSceneRenderers
对所有符合条件组件进行强制刷新。
简单看下代码的实现相关:
// from '../extensions/i18n/assets/LocalizedLabel'
@ccclass('LocalizedLabel')
@executeInEditMode
export class LocalizedLabel extends Component {
label: Label | null = null;
@property({ visible: false })
key: string = '';
// 声明property属性Key
@property({ displayName: 'Key', visible: true })
get _key() {
return this.key;
}
set _key(str: string) {
this.key = str;
this.updateLabel();
}
onLoad() {
// 初始化语言类型为zh
if (!i18n.ready) {
i18n.init('zh');
}
this.fetchRender();
}
fetchRender () {
// 获取文本Label组件
let label = this.getComponent('cc.Label') as Label;
if (label) {
this.label = label;
this.updateLabel();
return;
}
}
updateLabel () {
// 通过LanguageData的方法接口,根据语言类型和key获取指定的内容
// 然后改变内容的显示
this.label && (this.label.string = i18n.t(this.key));
}
}
里面的i18n指的就是文件LanguageData
,它的主要实现:
// from '../extensions/i18n/assets/LanguageData'
import { director } from 'cc';
// 默认的语言类型
export let _language = 'zh';
export let ready: boolean = false;
// 初始化语言类型,比如zh,en等
export function init(language: string) {
ready = true;
_language = language;
}
// 获取内容
export function t(key: string) {
const win: any = window;
if (!win.languages) {
return key;
}
const searcher = key.split('.');
// 根据语言类型获取不同的内容数据
let data = win.languages[_language];
// 遍历获取到对应key的内容
for (let i = 0; i < searcher.length; i++) {
data = data[searcher[i]];
if (!data) {
return '';
}
}
return data || '';
}
// 更新场景渲染器
export function updateSceneRenderers() {
const rootNodes = director.getScene()!.children;
// walk all nodes with localize label and update
const allLocalizedLabels: any[] = [];
// 遍历所有节点获取LocalizedLabel组件
for (let i = 0; i < rootNodes.length; ++i) {
let labels = rootNodes[i].getComponentsInChildren('LocalizedLabel');
Array.prototype.push.apply(allLocalizedLabels, labels);
}
// 更新内容显示
for (let i = 0; i < allLocalizedLabels.length; ++i) {
let label = allLocalizedLabels[i];
if(!label.node.active)continue;
label.updateLabel();
}
// 遍历所有节点获取LocalizedSprite组件,对图片进行更新
const allLocalizedSprites: any[] = [];
for (let i = 0; i < rootNodes.length; ++i) {
let sprites = rootNodes[i].getComponentsInChildren('LocalizedSprite');
Array.prototype.push.apply(allLocalizedSprites, sprites);
}
for (let i = 0; i < allLocalizedSprites.length; ++i) {
let sprite = allLocalizedSprites[i];
if(!sprite.node.active)continue;
sprite.updateSprite();
}
}
图片的更新方法:updateSprite
// from '../extensions/i18n/assets/LocalizedSprite'
updateSprite () {
for (let i = 0; i < this.spriteList.length; i++) {
const item = this.spriteList[i];
if (item.language === i18n._language) {
this.sprite.spriteFrame = item.spriteFrame;
break;
}
}
}