游戏中可以动态切换语言,比如中文、英文、西班牙文,这要求项目中要并存多语言的配置,比如i18n_zh.json、i18n_en.json、i18n_es.json,游戏根据语言设置动态加载对应的i18文件。
把游戏中的中文翻译成外文,覆盖原来的中文文字,项目中只存一份i18.json
方案 | 优点 | 缺点 |
---|---|---|
多语言 | 灵活,方便横向扩展 | 1 逻辑上更复杂,需要在项目早期的时候就做好框架,如果后期改多语言,修改量比较大;2 如果有使用美术表现的文字,也需要支持动态切换图集,包内需要保护多种语言的美术字体 |
翻译 | 代码改动少,资源简单 | 不够灵活,不易横向扩展 |
一般项目中需要有一个i18配置,配置项目中程序用到的中文文字,结构类似如下
{
"1": "登录成功",
"2": "账号或密码错误",
"3": "注册"
}
翻译的话,直接把上面的i18翻译成对应的语言文字即可,如
{
"1": "Login successful",
"2": "Wrong account or password",
"3": "Regist"
}
除了i18,其他配置表中还有很多中文配置,比如背包配置表,道具名、描述等等字段都是中文的,大量的excel配置,这个时候,可以用python来批量分析这些excel配置表,把中文过滤出来
把下面的phthon脚本放在excel配置文件目录的根目录,然后执行
运行环境python2
# coding=utf-8
import xlrd
import re
import os
import io
txt_t=[]
txt = ""
def filter_zh_from_excel(f):
xl = xlrd.open_workbook(f)
sheets = xl.sheets()
for sheet in sheets:
nrows = sheet.nrows
for i in range(nrows):
#表头2行不检测
if i >= 2:
row_data = sheet.row(i)
j = 0
for data in row_data:
j = j + 1
if 1 == data.ctype:
zhmodel = re.compile(u'[\u4e00-\u9fa5]')
if zhmodel.search(data.value):
#print('t: %s, s: %s, r: %d, c: %d, v: %s' %(f, sheet.name, i+1,j, data.value))
txt = '%s|%s|%d|%d|%s' %(f, sheet.name.encode('gbk'), i+1,j, data.value.encode('gbk'))
txt_t.append(txt.decode('gbk').encode('utf-8'))
def get_all_excels():
for root, dirs, fs in os.walk('.'):
for f in fs:
if f.endswith('.xlsx') or f.endswith('.xls'):
print(os.path.join(root, f))
filter_zh_from_excel(os.path.join(root, f))
def write_output():
f = open('output.csv', 'w')
f.write('文件|sheet|行|列|内容\n ')
for txt in txt_t:
f.write(txt+'\n')
f.close()
get_all_excels()
write_output()
print('done')
运行后会输出如下的表格
接下来,就可以把这个中文列表给外包进行翻译
当外包翻译好之后,可以得到如下的表格
加载到内存
Dictionary<string, string> languageMap;
修改UILabel的text的set属性
public string text
{
set
{
if(languageMap.Contains(value))
{
mText = languageMap[value];
}
}
}
还有预设里也有中文,写个工具扫码一下
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
using System.Text;
using UnityEditor;
using System.Text.RegularExpressions;
using System;
public class PrefabLblTextZnFilter
{
[MenuItem("Tools/扫描预设Label中文")]
public static void StartPrefabLblTextZnFilter()
{
var fs = Directory.GetFiles(Application.dataPath + "\\GameRes\\", "*.prefab", SearchOption.AllDirectories);
List<string> outputs = new List<string>();
RunProgress(fs, "扫描预设Label中文", (f) =>
{
var assetPath = f.Replace(Application.dataPath, "Assets");
var obj = AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
var lbls = obj.GetComponentsInChildren<UILabel>();
foreach (var lbl in lbls)
{
if (IfContainChinse(lbl.text))
{
Debug.Log(lbl.text);
outputs.Add(assetPath + "|" + lbl.gameObject + "|" + lbl.text);
}
}
}, () =>
{
var outputPath = Application.dataPath + "\\..\\prefabl_lbl_zn.csv";
using (StreamWriter sw = new StreamWriter(outputPath))
{
foreach (var lin in outputs)
{
sw.WriteLine(lin);
}
}
Debug.Log("svae output done: " + outputPath);
});
}
private static void RunProgress(string[] fs, string title, Action<string> itemCb, Action finishCb = null)
{
RunProgress<string>(fs, title, itemCb, finishCb);
}
private static void RunProgress<T>(T[] fs, string title, Action<T> itemCb, Action finishCb = null)
{
int startIndex = 0;
Debug.Log(title + " start <<<<<");
EditorApplication.update = delegate ()
{
var f = fs[startIndex];
bool isCancel = EditorUtility.DisplayCancelableProgressBar(title, f.ToString(), (float)startIndex / fs.Length);
try
{
if (null != itemCb) itemCb((T)f);
}
catch (Exception ex)
{
Debug.LogError(ex);
isCancel = true;
}
startIndex++;
if (isCancel || startIndex >= fs.Length)
{
EditorUtility.ClearProgressBar();
EditorApplication.update = null;
startIndex = 0;
Debug.Log(title + " done >>>>>");
if (null != finishCb)
finishCb();
}
};
}
private static bool IfContainChinse(string str)
{
return Regex.IsMatch(str, @"[\u4e00-\u9fa5]");
}
}
点击菜单 Tools/扫描预设Label中文,输出如下的表格
同上给外包翻译后得到一个语言配置表,加载到内存
Dictionary<string, string> languageMap;
实例化界面预设的时候,修改UILabel的text为翻译语言
var lbls = GetComponentsInChildren<UILabel>(panelObj);
for(int i = 0, len = lbls.Lenght; i < len; ++i)
{
var text = lbls[i].text;
// 如果语言容器中存在
if(languageMap.Contains(text))
{
lbls[i].text = languageMap[text];
}
}