using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.IO;
using System.Linq;
using System.Text;
using XPlugin.Data.Json;
using XPlugin.Localization;
class CodeScanner {
public static string[] IgnoreFileByKeyWord = new string[]
{
"/Editor/",//忽略Editor目录下的代码
"GMConsole",
"ModAnalytics",
"GUI"
};
[MenuItem("XPlugin/本地化C#/提取 选中目录中 所有cs文件中 String 生成字典")]
public static void AutoFindCode() {
string path = AssetDatabase.GetAssetPath(Selection.activeObject);
// 重置字典数据
_dictionaryStr = "";
_txtKey.Clear();
_txtValue.Clear();
_valueRepeatCount = 0;
_keyRepeatCount = 0;
_txtPath = path.Replace("/", "_") + ".txt";
FindAllCode(path);
EditorUtility.DisplayProgressBar("本地化", "保存字典到Txt", 1);
for (var i = 0; i < _txtKey.Count; i++) {
var key = _txtKey[i];
var value = _txtValue[i];
value = LocalizedUtil.ReplaceNewLine(value);
_dictionaryStr += (i + 1) + "\t" + key + "\t" + value + "\r\n";
}
EditorUtility.ClearProgressBar();
File.WriteAllText(_txtPath, _dictionaryStr);
// 输出结果
Debug.Log("Find " + _txtKey.Count + " Keys.");
if (_keyRepeatCount > 0 || _valueRepeatCount > 0) {
Debug.Log("Value repeat : " + _valueRepeatCount + " , Key repeat ; " + _keyRepeatCount);
}
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
///
/// 字典数据
///
private static string _dictionaryStr;
private static readonly List _txtKey = new List();
private static readonly List _txtValue = new List();
private static readonly List _excelKey = new List();
private static readonly List _excelValue = new List();
private static string _txtPath;
/// 转义引号占位符
///
private static string _quotationMark = Guid.NewGuid().ToString();
private static int _valueRepeatCount;
private static int _keyRepeatCount;
#region Regex
///
/// 本地化代码
///
private static string localizeCode = "(LString.{0}).T()";
///
/// 中文正则
///
//private static Regex regexChineseStr = new Regex("\"[^\"]*[\u4e00-\u9fa5]+[^\"]*\"");
///
/// 英文正则
///
private static Regex regexChineseStr = new Regex("\"[^\"]*[a-zA-Z]+[^\"]*\"");
///
/// 命名空间正则
///
private static Regex regexNameSpace = new Regex(".*namespace.*");
///
/// 类名正则
///
private static Regex regexClass = new Regex(".*class.*");
///
/// 函数正则
///
private static Regex regexFunction = new Regex("[^\\.\\s=]+\\s+[^\\.\\s=]+\\([^!=]*\\)\\s*\\{?[^\\;]*\\s*$");
#endregion
///
/// 查找所有代码
///
static void FindAllCode(string path) {
var count = 0;
LoadScriptText();
// 获取所有object的路径
var prefabFile = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories);
// 循环遍历每一个路径,单独加载
foreach (var filePath in prefabFile) {
// 显示进度
var fileName = Path.GetFileNameWithoutExtension(filePath);
EditorUtility.DisplayProgressBar("本地化Code " + (count + 1) + "/" + prefabFile.Length, "当前:" + fileName, (float)(count + 1) / prefabFile.Length);
// 替换路径中的反斜杠为正斜杠
var strTempPath = filePath.Replace(@"\", "/");
// 截取我们需要的路径
if (!CheckFileNeedLocalize(strTempPath)) {
continue;
}
// 本地化处理
LocalizeCode(strTempPath);
count++;
}
}
#region Code Localize
///
/// 本地化代码文件
///
/// 文件路径
private static void LocalizeCode(string codePath) {
var lines = new List(File.ReadAllLines(codePath));
// 初始化
var spaceName = "";
var className = "";
var funcName = "";
var funcIndex = 0;
var classIndex = 0;
// 逐行处理代码
var gmCode = false;
bool needResave = false;
for (var i = 0; i < lines.Count; i++) {
var line = lines[i];
// 结束和忽略检查
if (CheckGMStart(line)) {
gmCode = true;
continue;
}
if (gmCode && CheckGMEnd(line)) {
gmCode = false;
}
if (gmCode) {
continue;
}
if (CheckIgnore(line)) continue;
// 查找命名空间、类名、函数名
if (string.IsNullOrEmpty(spaceName)) {
spaceName = FindNameSpace(line);
}
if (string.IsNullOrEmpty(className)) {
className = FindClass(line);
}
var func = FindFunction(line);
if (func != funcName && !string.IsNullOrEmpty(func)) {
funcName = func;
funcIndex = 0;
}
// 暂时替换转义引号以配合正则表达式
line = line.Replace("\\\"", _quotationMark);
// 匹配需要替换的字符串
var match = regexChineseStr.Match(line);
line = line.Replace(_quotationMark, "\\\"");
while (match.Length > 0) {
needResave = true;
// 匹配到后构造Key和Value
var key = CreateKey(spaceName, className, funcName, classIndex, funcIndex);
var valueOrginal = match.Value.Replace(_quotationMark, "\\\"");
// 截取双引号
var value = valueOrginal.Substring(1, valueOrginal.Length - 2);
// TXT Value 重复检查
int index;
var valueRepeat = false;
if ((index = _txtValue.IndexOf(value)) > -1) {
key = _txtKey[index];
_valueRepeatCount++;
valueRepeat = true;
}
// Excel Value 重复检查
if ((index = _excelValue.IndexOf(value)) > -1) {
key = _excelKey[index];
_valueRepeatCount++;
valueRepeat = true;
}
// TXT Key 重复检查
if (_txtKey.IndexOf(key) > -1 && !valueRepeat) {
_keyRepeatCount++;
// 添加重复标记
var keyTemp = key + "_" + _keyRepeatCount;
while (_excelKey.IndexOf(keyTemp) > -1) {
_keyRepeatCount++;
keyTemp = key + "_" + _keyRepeatCount;
}
key = keyTemp;
}
// Excel Key 重复检查
if (_excelKey.IndexOf(key) > -1 && !valueRepeat) {
var num = 1;
// 添加重复标记
var keyTemp = key + "_" + num;
while (_excelKey.IndexOf(keyTemp) > -1) {
num++;
keyTemp = key + "_" + num;
}
key = keyTemp;
}
// 替换代码
var codeReplace = CreateCode(key);
if (Dict.ContainsKey(key))
{
// line = line.Replace(valueOrginal, codeReplace);
}
// Debug.Log("Key : " + key + "\t Value : " + value + "\t Code : " + codeReplace);
// Debug.Log(valueOrginal);
if (!valueRepeat) {
// 添加到字典
_txtKey.Add(key);
_txtValue.Add(value);
if (!string.IsNullOrEmpty(funcName)) funcIndex++;
else classIndex++;
}
// 匹配下一个
match = match.NextMatch();
}
lines[i] = line;
}
// 保存代码
if (needResave) {
File.WriteAllLines(codePath, lines.ToArray());
}
}
#endregion
#region File Check
///
/// 检查文件是否需要做本地化处理
///
/// ASEET下的路径
private static bool CheckFileNeedLocalize(string assetPath) {
foreach (var s in IgnoreFileByKeyWord) {
if (assetPath.Contains(s)) {
return false;
}
}
return true;
}
#endregion
#region Code & Key
///
/// 创建键值
///
/// 命名空间
/// 类
/// 函数
/// 类索引
/// 函数索引
///
private static string CreateKey(string spaceName, string className, string funcName, int classIndex, int funcIndex) {
var result = "";
if (!string.IsNullOrEmpty(spaceName)) {
result += spaceName;
}
if (!string.IsNullOrEmpty(className)) {
result += "_" + className;
}
if (!string.IsNullOrEmpty(funcName)) {
result += "_" + funcName;
if (funcIndex > 0) {
result += "_" + funcIndex;
}
} else {
if (classIndex > 0) {
result += "_" + classIndex;
}
}
if (result.Substring(0, 1) == "_") {
result = result.Substring(1, result.Length - 1);
}
return result.ToUpper();
}
///
/// 根据键创建用于替换的代码
///
/// 键
///
private static string CreateCode(string key) {
var result = "";
if (!string.IsNullOrEmpty(key)) {
result = string.Format(localizeCode, key);
}
return result;
}
#endregion
#region Code Check
///
/// 检测该文件是否可以结束
///
/// 代码
///
private static bool CheckGMStart(string code) {
var str = code.Trim();
// 遇到GM代码时,文件结束
if (str.Length >= 13 && str.Substring(0, 13) == "#if CLIENT_GM") {
return true;
}
return false;
}
///
/// 检测该文件是否可以结束
///
/// 代码
///
private static bool CheckGMEnd(string code) {
var str = code.Trim();
// 遇到GM代码时,文件结束
if (str.Length >= 6 && str.Substring(0, 6) == "#endif") {
return true;
}
return false;
}
///
/// 检查代码是否需要忽略
///
///
///
private static bool CheckIgnore(string code) {
var str = code.Trim().ToLower();
// 忽略空行
if (str.Length < 1) {
return true;
}
// 忽略宏定义
if (str.Length >= 1 && str.Substring(0, 1) == "#") {
return true;
}
// 忽略注释
if (str.Length >= 2 && str.Substring(0, 2) == "//") {
return true;
}
// 忽略引用
if (str.Length >= 5 && str.Substring(0, 5) == "using") {
return true;
}
// 忽略结尾
if (str.Length >= 1 && str.Substring(0, 1) == "}") {
return true;
}
string[] filter = new[] {"GUILayout","statevent", "debug.", ".log","l10nIgnore","i18nIgnore"};
foreach (var f in filter) {
// 忽略带有关键词的行
if (str.IndexOf(f, StringComparison.OrdinalIgnoreCase) > -1) {
return true;
}
}
// 忽略属性
str = str.Trim().Replace(" ", "");
var index1 = str.IndexOf("[", StringComparison.Ordinal);
var index2 = str.IndexOf("(", StringComparison.Ordinal);
var index3 = str.IndexOf(")]", StringComparison.Ordinal);
if (index1 > -1 && index2 > index1 && index3 > index2 && index3 == str.Length - 2) {
return true;
}
return false;
}
#endregion
#region Code Match
///
/// 查找命名空间
///
/// 代码
/// 结果
private static string FindNameSpace(string code) {
var result = "";
// 匹配命名空间
var match = regexNameSpace.Match(code);
if (match.Length == 0) {
return result;
}
// 提取命名空间
var str = code.Split(' ');
if (str.Length > 1) {
if (str[0].ToLower() == "namespace") {
result = str[1];
}
}
result = result.Replace("{", "").Replace (".", "_");
return result;
}
///
/// 查找类名
///
/// 代码
/// 结果
private static string FindClass(string code) {
var result = "";
// 匹配类名
var match = regexClass.Match(code);
if (match.Length == 0) {
return result;
}
// 提取类名
var classIndex = code.IndexOf("class", StringComparison.Ordinal);
if (classIndex > -1) {
var temp = code.Substring(classIndex, code.Length - classIndex - 1);
var str = temp.Split(' ');
result = str[1];
}
result = result.Replace("{", "");
return result;
}
///
/// 查找函数名
///
/// 代码
/// 结果
private static string FindFunction(string code) {
var result = "";
// 匹配函数
var match = regexFunction.Match(code);
if (match.Length == 0) {
return result;
} else
// 忽略定义
if (code.IndexOf("new", StringComparison.Ordinal) > -1) {
return result;
}
// 忽略()=>{}
if (code.IndexOf("=>", StringComparison.Ordinal) > -1) {
return result;
}
// 提取函数名
var index = -1;
index = code.IndexOf('(');
if (index > -1) {
var temp = code.Substring(0, index);
var str = temp.Split(' ');
result = str.Last();
}
// 忽略<>
index = result.IndexOf('<');
if (index > -1) {
result = result.Substring(0, index);
}
return result;
}
#endregion
#region 根据ScriptText内容,生成ScriptTest脚本
[MenuItem("XPlugin/本地化C#/根据英文ScriptText内容,生成ScriptText脚本")]
public static void GenerateScriptText()
{
scritpText = new StringBuilder();
alreadyText = new List();
LoadScriptText();
ParseScriptText();
}
private static Dictionary Dict;
private static List alreadyText;
private static StringBuilder scritpText;
private static void LoadScriptText()
{
Dict = new Dictionary();
var text = LResources.Load("ScriptText");
if (text == null) {
Debug.LogError("没有找到当前语言对应的资源ScriptText");
return;
}
//从JSON解析字典
var jDic = JObject.Parse(text.text);
foreach (var kv in jDic) {
var key = kv.Name;
if (Dict.ContainsKey(key)) {
Debug.LogError($"发现重复的key{key},将替换该key对应的值");
Dict[key] = kv.AsString();
} else {
Dict.Add(key, kv.AsString());
}
}
}
private static void ParseScriptText()
{
var tmpPath =
"E:/production/footballmanager_client/Assets/_Localized/Genearate/ScriptText.cs";
var tmpFileAllLines = File.ReadAllLines(tmpPath,Encoding.UTF8).ToList();
for (var i = 0; i < tmpFileAllLines.Count; i++)
{
if (tmpFileAllLines[i].Trim() == "}")
{
//最后一行
if (i == tmpFileAllLines.Count - 1)
{
//TODO 根据字典的内容开始添加字段
AddInfoToScriptText();
scritpText.Append("}");
}
else
{
var j = i + 1;
for (; j < tmpFileAllLines.Count; j++)
{
//后面的行只要还有内容,则继续向下
if (tmpFileAllLines[j].Trim() != "")
{
break;
}
}
if (j < tmpFileAllLines.Count)
{
scritpText.Append("\t}\n");
continue;
}
AddInfoToScriptText();
scritpText.Append("}");
}
}
else
{
if (tmpFileAllLines[i].Contains("string"))
{
var tmpInfo = tmpFileAllLines[i].Split(' ');
alreadyText.Add(tmpInfo[3]);
}
scritpText.Append(tmpFileAllLines[i]+"\n");
}
}
// File.WriteAllText("E:\\Test.txt",scritpText.ToString());
File.WriteAllText(tmpPath,scritpText.ToString());
}
private static void AddInfoToScriptText()
{
foreach (var item in Dict)
{
if (!alreadyText.Contains(item.Key.ToUpper().Trim()))
{
scritpText.Append(
$"\n\tpublic const string {item.Key.ToUpper()} = \"{item.Value}\";\n");
}
}
}
#endregion
}