首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之多语言用中文做KEY(七十五)
2014
07-23

Unity3D研究院之多语言用中文做KEY(七十五)

做多语言的时候用中文做KEY绝对是有100%的好处,如果用英文表示那么代码里面给文字赋值的地方全都是英文,写的代码多了以后维护起来就没有人能看懂了,或者看起来很费劲。

说说用中文做KEY的原理:Unity中给文字赋值的地方就两处, 一个是提前预制在UI Prefab上的文字,还有一个是写在代码里面的文字。那么在开发阶段我们在Prefab和代码里面直接就写中文,等项目后期通过工具把所有中文的地方全部提取出来。然后把提取出来的中文交给策划,让策划他们去翻译去,这样我们之前写的中文就是多语言的KEY,最终显示的界面上的文字是用这个中文KEY读表读出来的。

NGUI里所有的文字都是在UILabel中,可是我们要做图文混排,一般都是在UILabel上在拓展一个自己的脚本,用这个脚本在生成对应的UILabel和UISprite。这篇文章我就先以UILabel来说明原理。

1.遍历所有UIPrefab把包含UILabe(或者是你自己写的)组件找出来,并且把文字提取出来。

2.遍历所有的CS代码,把所有 StrUtil.GetText(雨松MOMOn我要换行); 双引号中间的中文以及字符全部提取出来。

直接上思路代码。

using UnityEngine;
using System.Collections;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;

public class TestStart :Editor 
{
	//UIPrefab文件夹目录
	private static string UIPrefabPath = Application.dataPath + "/UI";
	//脚本的文件夹目录
	private static string ScriptPath = Application.dataPath + "/Scripts";
    //导出的中文KEY路径
	private static string OutPath = Application.dataPath +"/out.txt";

	private static List<string>Localization = null;
	private static string staticWriteText = "";
	[MenuItem("Tools/导出多语言")]
	static void ExportChinese()
	{
		Localization = new List<string>();
		staticWriteText ="";

		//提取Prefab上的中文
		staticWriteText +="----------------Prefab----------------------n";
		LoadDiectoryPrefab(new DirectoryInfo(UIPrefabPath));
		
		//提取CS中的中文
		staticWriteText +="----------------Script----------------------n";
		LoadDiectoryCS(new DirectoryInfo(ScriptPath));


		//最终把提取的中文生成出来
		string textPath = OutPath;
		if (System.IO.File.Exists (textPath)) 
		{
			File.Delete (textPath);
		}
		using(StreamWriter writer = new StreamWriter(textPath, false, Encoding.UTF8))
		{
			writer.Write(staticWriteText);
		}
		AssetDatabase.Refresh();
	}

	//递归所有UI Prefab
	static public  void  LoadDiectoryPrefab(DirectoryInfo dictoryInfo)
	{
		if(!dictoryInfo.Exists)   return;
		FileInfo[] fileInfos = dictoryInfo.GetFiles("*.prefab", SearchOption.AllDirectories);
		foreach (FileInfo files in fileInfos)
		{
			string path = files.FullName;
			string assetPath =  path.Substring(path.IndexOf("Assets/"));
			GameObject prefab = AssetDatabase.LoadAssetAtPath(assetPath, typeof(GameObject)) as GameObject;
			GameObject instance = GameObject.Instantiate(prefab) as GameObject;
			SearchPrefabString(instance.transform);
			GameObject.DestroyImmediate(instance);
		}
	}

     //递归所有C#代码
	static public  void  LoadDiectoryCS(DirectoryInfo dictoryInfo)
	{
		
		if(!dictoryInfo.Exists)   return;
		FileInfo[] fileInfos = dictoryInfo.GetFiles("*.cs", SearchOption.AllDirectories);
		foreach (FileInfo files in fileInfos)
		{
			string path = files.FullName;
			string assetPath =  path.Substring(path.IndexOf("Assets/"));
			TextAsset textAsset = AssetDatabase.LoadAssetAtPath(assetPath, typeof(TextAsset)) as TextAsset;
			string text = textAsset.text;
			//用正则表达式把代码里面两种字符串中间的字符串提取出来。
			Regex reg = new Regex("(?<=StrUtil.GetText\s*\(\s*"\s*)[\s\S]*?(?=\s*")");
			MatchCollection mc = reg.Matches(text);
			foreach(Match m in mc)
			{
				string format = m.Value;
				if(!Localization.Contains(format) && !string.IsNullOrEmpty(format)){
					Localization.Add(format);
					staticWriteText+=format+"n";
				}
			}
		}
	}

    //提取Prefab上的中文
	static public void SearchPrefabString(Transform root)
	{
		foreach(Transform chind in root)
		{
			//因为这里是写例子,所以我用的是UILabel 
			//这里应该是写你用于图文混排的脚本。
			UILabel label = chind.GetComponent<UILabel>();
			if(label != null)
			{
				string text = label.text;
				if(!Localization.Contains(text) && !string.IsNullOrEmpty(text)){
					Localization.Add(text);
					text = text.Replace("n",@"n");
					staticWriteText+=text+"n";
				}
			}
			if(chind.childCount >0)
				SearchPrefabString(chind);
		}
	}
}

比如这个是个简单界面上赋值的代码。用StrUtil.GetText()去取中文,StrUtiL类是我们自己写的。

using UnityEngine;
using System.Collections;
public class UIMain : MonoBehaviour 
{
	private UILabel mName = null;
	void Awake () 
	{
		mName = transform.Find("name").GetComponent<UILabel>();
		mName.text = StrUtil.GetText("雨松MOMOn我要换行");
		mName.text = StrUtil.GetText("我是{0}我的网名{1}","宣雨松","雨松MOMO");
	}
}

StrUtiL类里面去处理Key从本地数据表里中替换对应多语言显示的文字。

using UnityEngine;
using System.Collections;

public class StrUtil 
{
	static public string GetText( string text)
	{
		//通过传进来的中文KEY 去数据表里面读对应替换的多语言文字
		return text;
	}

	static public string GetText(string text,params object[] args)
	{
		//通过传进来的中文KEY 去数据表里面读对应替换的多语言文字
		return string.Format(text,args);
	}
}

使用工具代码提取,最终将所有多语言中文的地方提取在txt里面。

Foxmail20140723_1 最后就是让策划拿着生成出来的中文KEY在Excel表里,给出对应的翻译文字。

还有一个重要的知识点就是换行问题,可能你在Prefab上进行的换行的操作,但是n并不是字符串,所以我们要把n转成”n”字符串写进去。

text.Replace(“n”,@”n”);

反过来在读取表的时候还是需要再把”n”字符串转成n换行符

text.Replace(@”n”,”n”);

这样就没问题了。策划也可以直接在数据表里填写n来进行换行了。

最后的思考

1.开发的过程中可能要修改代码或者要加新功能删功能,所以我们要把差异性的中文Key提取出来,也就是把新增加的KEY 或者 新删除的KEY列举出来。因为没有变化的就不需要策划重新翻译了。

2.最好能直接帮策划生成Excel文件,Windows上很容易,但是MAC就不行。我知道怎么在Mac上读取excel文件,但是我不知道在mac上怎么生成Excel有哪位大神知道还请告知一下我。要能生成.xlsx的那种,谢谢啦。

3.因为要做图文混排,所以UILabel我已经不直接使用了,而是又写了一个类去管理UILable和UISprite, 其实就是根据XML或者JSON 一类的描述符去动态生成UILable和UISprite在帮它的动态的算坐标,算间距 一类的。因为你的中文KEY需要传参数  比如 “我叫{0}我今年{1}大了” 一类的字符串,所以还是在写一个方法。

最后是本文的下载地址,其实本文主要还是提供一个思路。 如果你有对多语言更好的建议,或者是办法,欢迎在下面给我留言,谢谢。

http://pan.baidu.com/s/1pJmGbzl

 

补充, 之前的正则表达式有点问题。因为在代码编写的时候允许中间存在 空格或者换行。 比如下面这样,编译器是不会报错的。。
StrUtil.GetText(“我是222中文”);
StrUtil.GetText ( “22 2222雨松MOMO” );
StrUtil.GetText
(
“222雨松520”
);
所以改一下即可。

	string expr = "(?<=StrUtil.GetText\s*\(\s*"\s*)[\s\S]*?(?=\s*")";
		MatchCollection mc = Regex.Matches(text, expr);
        foreach (Match m in mc)
        {
           Debug.Log(m);
         }

 

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之多语言用中文做KEY(七十五)》有 21 条评论

  1. 这种应该是项目中期挽救时的方案,要做国际化在立项时就读property文件就行

  2. 我叫陈小二说:

    为什么我的会报错呢?
    ArgumentOutOfRangeException: Argument is out of range.
    Parameter name: startIndex
    System.String.Substring (Int32 startIndex) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/String.cs:333)
    TestStart.LoadDiectoryPrefab (System.IO.DirectoryInfo dictoryInfo) (at Assets/Scripts/Editor/TestStart.cs:61)
    TestStart.ExportChinese () (at Assets/Scripts/Editor/TestStart.cs:28)

  3. uiprefab上的字符串找出来了。然后怎么换成多语言的strUitl方法呢?
    虽然之前有回答过:“自动生成”,但是还是不知道怎么用,能否具体一点?
    现在是想在已有项目上做这个多语言的替换。

  4. 没有明白。是否是取UILabel的text值,然后以这个text为key去配置表取得相应的翻译?如果这个text就是中文原文,那么当中文原文变动的时候,如何定位到相应的prefab呢?

  5. 用了中文Key,如果要修改中文,那么不仅配置表要改一遍,还要找到相应的prefab或者代码引用,再改一遍。这样会很麻烦吧?

  6. dlqingxi说:

    uiprefab上的字符串找出来了。然后怎么换成多语言的strUitl方法呢?一个个手写?

  7. 毆神说:

    MOMO大神 看到你这篇文章里面多次提到的图文混排,我也在做,就想在UIlabel上加个脚本,然后创建UIlabel跟UISprite,但是位置计算老是出问题,求教你的是怎么做的。

  8. 小史说:

    雨松,我现在需要倒翻译,这种方法,感觉挺好的,但是我有了翻译之后,怎么把从预设里面提取的文本替换成翻译?你对这个了解吗?谢谢。(项目是已经写好的了,直接写死的文本(UILabel、代码设置))

  9. 老蛙说:

    配置表里面的中文呢?

  10. 史志远说:

    使用过,组件显示不了 ,显示Multi-object editing not supported

  11. 好办法。 我们在早期的时候所有文字全在代码或者Prefab上写的中文,后来意识到多语言版本,就对每一句话作了一个id,然后把这个id和对应的文字意思给策划配表。现在代码里全是数字id,排错还得看数据表。 用中文做key的思路,真敢尝试,也不怕编码格式不能识别啊 Unity3D研究院之多语言用中文做KEY(七十五) - 雨松MOMO程序研究院 - 1

  12. shim说:

    假如我有5个cube在同一起跑线上,点击一个按钮,让cube一个接着一个向前移动?在前一个cube前进1秒以后,下一个cube才向前移动;或者前一个cube已经移动了一定距离,下一个cube开始向前移动。

  13. 关于将Unity中使用C#读取获得到的一些文本如何在Mac平台上写入到xlsx中,之前在项目中有遇到过。我们的解决思路是这样的,因为文本内容比较简单,直接将读取到的文本以特定的格式(例如使用Tab制表符分隔Key和Value)写入到纯文本文件中(中间文件),然后在通过C#调用外部的Python脚本来解析文本文件,再按照需要写入到xlsx文件中,Python写xlsx文件可以使用『xlsxwriter』这个类库,地址在这儿『https://pypi.python.org/pypi/XlsxWriter』

  14. 关于将Unity中使用C#读取获得到的一些文本如何在Mac平台上写入到xlsx中,之前在项目中有遇到过。我们的解决思路是这样的,因为文本内容比较简单,直接将读取到的文本以特定的格式(例如使用Tab制表符分隔Key和Value)写入到纯文本文件中(中间文件),然后在通过C#调用外部的Python脚本来解析文本文件,再按照需要写入到xlsx文件中,Python写xlsx文件可以使用『xlsxwriter』这个类库,地址在这儿『https://pypi.python.org/pypi/XlsxWriter』

  15. shim说:

    博主,我想做流体,想泥石流那种效果,改怎么做?求指导

  16. 天真汉说:

    刚加班回来 坐在沙发上 休息会~ Unity3D研究院之多语言用中文做KEY(七十五) - 雨松MOMO程序研究院 - 1Unity3D研究院之多语言用中文做KEY(七十五) - 雨松MOMO程序研究院 - 2

留下一个回复

你的email不会被公开。 必填项已用 * 标注。