Unity中,使用ClosedXml把Excel中的翻译提取到Xml

在项目中,需要从Excel表中提取翻译,使用工具的话相比手动复制粘贴快太多了,工作量从小时计算缩小到以秒计算,简直不要太方便。

在使用ClosedXml前需要先下载ClosedXml的库。在VS中从里"工具"--->“NuGet包管理器”--->“管理解决方案的NuGet程序包”,然后在浏览标签下输入ClosedXml,然后下载。下载好后,相应的文件会出现在工程的Packages下,从Packages文件夹下找到“ClosedXML.dll”,“DocumentFormat.OpenXml.dll”,“ExcelNumberFormat.dll”这三个程序集,然后放到Plugins目录下(Packages下相应的文件夹可以删除了)。下载后库后下面开始从Excel提取内容写入Xml中。

1、从Excel表中把内容提取出来

using (XLWorkbook wb = new XLWorkbook(excelPath))
        {
            //Excel表中的所有工作表
            IXLWorksheets worksheets = wb.Worksheets;
            int index = 0;
            foreach (var worksheet in worksheets)
            {
                //给工作表规定一个顺序,以免混乱
                if (index >= workSheetNames.Length || !worksheet.Name.Equals(workSheetNames[index]))
                {
                    Debug.LogError("Excel表顺序不对!!");
                    break;
                }
                index++;
                if (!EmptyRowCheck(worksheet, 1)) break;
                col1StrList = new List();
                col2StrList = new List();
                //第一列中文
                col1StrList = ReadColStrList(worksheet, 1);
                col1StrLists.Add(col1StrList);
                //第二列英文
                col2StrList = ReadColStrList(worksheet, 2);
                col2StrLists.Add(col2StrList);
            }
        }

2、这里是判断有没有空行的问题,第一列中文是基准,不能有空行,不然造成混乱

//第一列中文从起始行到结束行是否有空行
    private static bool EmptyRowCheck(IXLWorksheet worksheet, int column)
    {
        if(worksheet == null)
        {
            Debug.LogError("工作表为空!!");
            return false;
        }

        for(int i = startIndex; i <= endIndex; i++)
        {
            var cell = worksheet.Cell(i, column);
            if (string.IsNullOrEmpty(cell.Value.ToString()))
            {
                Debug.LogError(worksheet.Name + "第" + column + "列" + "第" + i + "行为空值!!");
                return false;
            }
        }
        return true;
    }

3、从工作表中按行、列读取内容

private static List ReadColStrList(IXLWorksheet worksheet, int column)
    {
        if (worksheet == null)
        {
            Debug.LogError("工作表为空!!");
            return null;
        }
        List strs = new List();
        for(int i = startIndex; i <= endIndex; i++)
        {
            //取某一列从起始行到结束行的内容,如果是用worksheet.CellsUsed();
            //或者worksheet.Cells();就会跳过空行
            var cell = worksheet.Cell(i, column);
            strs.Add(cell.Value.ToString());
        }
        return strs;
    }

4、通过上面的步骤就把文档中所有工作表的相关内容内容取出来了,然后开始写入XML中。这里用System.Xml.Linq库来读写XML。

private static void WriteXml()
    {
        for (int i = 0; i < col1StrLists.Count; i++)
        {
            if (i >= xmlNames.Length) break;
            XElement xmlDoc = ReadXML(i);
            //写一下模块的注释
            WriteComment(xmlDoc, modelTitle);
            for (int j = 0; j < Enum.GetNames(typeof(Keys)).Length; j++)
            {
                if (j >= col1StrLists[i].Count) break;
                WriteString(xmlDoc, Enum.GetNames(typeof(Keys))[j], col2StrLists[i][j], col1StrLists[i][j], xmlNames[i]);
            }
            //结束时再写一下模块的注释
            WriteComment(xmlDoc, modelTitle);
        }
    }

5、写注释的方法,就是这个格式的“

private static void WriteComment(XElement xmlDoc, string value)
    {
        XComment xComment = new XComment(value);
        xmlDoc.Add(xComment);
        xmlDoc.Save(xmlPath);
    }

6、写内容的方法

private static void WriteString(XElement xmlDoc, string key, string value, string comment, string xmlName)
    {
        //先判断一下有没有已经存在的Key
        if (localizaleData.ContainsKey(key))
        {
            Debug.LogError(xmlName + " contains this key:" + key);
            return;
        }
        WriteComment(xmlDoc, comment);
        XElement xe = new XElement("string");
        xe.SetAttributeValue("name", key);
        //这里把翻译中的占位符换成{0}的形式
        for(int i = 0; i < replaceFlags.Length; i++)
        {
            if(value.IndexOf(replaceFlags[i]) >= 0)
                value = value.Replace(replaceFlags[i], "{" + i + "}");
        }
        xe.SetValue(value);
        xmlDoc.Add(xe);
        xmlDoc.Save(xmlPath);
    }

7、下面是全部的代码了,这里做测试的只建了一个工作表,实际是支持N个工作表的。另外这个脚本是放在Editor文件夹下的。

public class ExcelToXml
{
    //模块名称
    static string modelTitle = "模块名称";
    enum Keys
    {
        key1,
        key2,
        key3
    }
    static int startIndex = 2;//起始行序号
    static int endIndex = 4;//结束行序号
    static string excelPath = @"E:\Test.xlsx";//Excel表路径及文件名+后缀(只支持.xlsx)

    static string[] xmlNames = new string[] { "English"};
    static string[] workSheetNames = new string[] {"英语" };
    static string[] replaceFlags = new string[] { "AA", "BB", "CC", "DD", "EE"};
    static Dictionary localizaleData = new Dictionary();
    static string xmlPath;
    static List col1StrList;
    static List col2StrList;
    static List> col1StrLists = new List>();
    static List> col2StrLists = new List>();

    [MenuItem("Assets/ExcelToXml(关闭文档再执行)")]
    static void ExcelToXmlTool()
    {
        ReadWorkSheet();
    }

    private static void ReadWorkSheet()
    {
        if (!ValidCheck()) return;
        using (XLWorkbook wb = new XLWorkbook(excelPath))
        {
            IXLWorksheets worksheets = wb.Worksheets;
            int index = 0;
            foreach (var worksheet in worksheets)
            {
                if (index >= workSheetNames.Length || !worksheet.Name.Equals(workSheetNames[index]))
                {
                    Debug.LogError("Excel表顺序不对!!");
                    break;
                }
                index++;
                if (!EmptyRowCheck(worksheet, 1)) break;
                col1StrList = new List();
                col2StrList = new List();
                //第一列中文
                col1StrList = ReadColStrList(worksheet, 1);
                col1StrLists.Add(col1StrList);
                //第二列英文
                col2StrList = ReadColStrList(worksheet, 2);
                col2StrLists.Add(col2StrList);
            }
        }
        WriteXml();
        Debug.LogError("Excel To Xml Complete!!");
    }

    //第一列中文从起始行到结束行是否有空行
    private static bool EmptyRowCheck(IXLWorksheet worksheet, int column)
    {
        if(worksheet == null)
        {
            Debug.LogError("工作表为空!!");
            return false;
        }

        for(int i = startIndex; i <= endIndex; i++)
        {
            var cell = worksheet.Cell(i, column);
            if (string.IsNullOrEmpty(cell.Value.ToString()))
            {
                Debug.LogError(worksheet.Name + "第" + column + "列" + "第" + i + "行为空值!!");
                return false;
            }
        }
        return true;
    }

    private static bool ValidCheck()
    {
        bool isValid = true;
        if (Enum.GetNames(typeof(Keys)).Length == 0)
        {
            Debug.LogError("请输入Key!!");
            return isValid = false;
        }
        if (string.IsNullOrEmpty(excelPath))
        {
            Debug.LogError("请输入Excel表路径!!");
            return isValid = false;
        }
        if (!File.Exists(excelPath))
        {
            Debug.LogError("Excel表不存在!!");
            return isValid = false;
        }
        if (!Path.GetExtension(excelPath).Equals(".xlsx"))
        {
            Debug.LogError("Excel表类型不是xlsx!!");
            return isValid = false;
        }
        return isValid;
    }

    private static List ReadColStrList(IXLWorksheet worksheet, int column)
    {
        if (worksheet == null)
        {
            Debug.LogError("工作表为空!!");
            return null;
        }
        List strs = new List();
        for(int i = startIndex; i <= endIndex; i++)
        {
            var cell = worksheet.Cell(i, column);
            strs.Add(cell.Value.ToString());
        }
        return strs;
    }

    private static XElement ReadXML(int index)
    {
        localizaleData.Clear();
        XElement xmlDoc;
        string fileName = "strings_" + xmlNames[index] + ".xml";
        xmlPath = Application.dataPath + "/Localization" + "/" + fileName;
        string text = File.ReadAllText(xmlPath);
        xmlDoc = XElement.Parse(text);
        IEnumerable elements =
        from el in xmlDoc.Elements()
        select el;

        foreach (XElement el in elements)
        {
            XAttribute xat = el.FirstAttribute;
            localizaleData.Add(xat.Value, el.Value);
        }
        return xmlDoc;
    }

    private static void WriteXml()
    {
        for (int i = 0; i < col1StrLists.Count; i++)
        {
            if (i >= xmlNames.Length) break;
            XElement xmlDoc = ReadXML(i);
            WriteComment(xmlDoc, modelTitle);
            for (int j = 0; j < Enum.GetNames(typeof(Keys)).Length; j++)
            {
                if (j >= col1StrLists[i].Count) break;
                WriteString(xmlDoc, Enum.GetNames(typeof(Keys))[j], col2StrLists[i][j], col1StrLists[i][j], xmlNames[i]);
            }
            WriteComment(xmlDoc, modelTitle);
        }
    }

    private static void WriteString(XElement xmlDoc, string key, string value, string comment, string xmlName)
    {
        if (localizaleData.ContainsKey(key))
        {
            Debug.LogError(xmlName + " contains this key:" + key);
            return;
        }
        WriteComment(xmlDoc, comment);
        XElement xe = new XElement("string");
        xe.SetAttributeValue("name", key);
        for(int i = 0; i < replaceFlags.Length; i++)
        {
            if(value.IndexOf(replaceFlags[i]) >= 0)
                value = value.Replace(replaceFlags[i], "{" + i + "}");
        }
        xe.SetValue(value);
        xmlDoc.Add(xe);
        xmlDoc.Save(xmlPath);
    }

    private static void WriteComment(XElement xmlDoc, string value)
    {
        XComment xComment = new XComment(value);
        xmlDoc.Add(xComment);
        xmlDoc.Save(xmlPath);
    }
}

8、内容取出来后XML中大概是这个样子的。转换之前要把Excel表关闭,不然后报错“IOException: Sharing violation on path E:\Test.xlsx”

Unity中,使用ClosedXml把Excel中的翻译提取到Xml_第1张图片



  
  
  Test1
  
  Test2
  
  Test3 {0} Test3
  

有个这样的工具太方便了,取翻译几秒钟就搞定了。当然前提是文档是按约定的格式做的,比如工作表的顺序,内容的格式等,如果格式不对的话,取出来的内容就会混乱了。

你可能感兴趣的:(Unity,Unity,Excel转XML,ClosedXml转Excel,从Excel到XML)