讓現有vue前端項目快速支持多語言 - 用.net core程序快速替換中文為資源Key,咱不干體力活


前言

這是我第一次發博客,2020年立個flag以后要經常發。

最近應公司上層要求,需要將現有項目盡快支持多語言,而中文內容可以找專業人員翻譯。那么咱們說干就干,首先我們項目的前端是用vue寫的spa程序且組件方面用的element ui,那么自然而然想到用vue官方推薦的vue i18n,我快速過了下i18n整個Guide官方文檔,看來使用很簡單,主要步驟就是:

這里有一個最簡單的實例

相信大家都看就懂,但大家有沒想過,我前面說了公司要求是把現有項目盡快支持多語言,那說明我們的項目已經存在大量的代碼。duang,尼瑪,那不是做“定義多語言資源字典對象”是一個體力活? 要知道,我們這個前端項目至少有成百上千個頁面,如果讓我一個一個的翻譯里面的中文跟換成vue i18n要求的$t('key'),那估計我跟團隊得累死呀!!!所以,為了不干體力活,就有了這篇文章。

先隨便拿一個前端文件看看最終的效果圖:

自動替換前:

自動替換后:

感覺如何?最終的代碼點這里

下面我們來詳細介紹下實現的各個步驟

要想替換前端代碼為vue i18n的語法,就涉及到自己編寫一套程序來實現准確替換,在替換之前,我們需要考慮:

  • 可以將<template><script>里面的中文代碼准確找出來(需要排除注釋等特殊情況,至少能替換98%以上的代碼,少量代碼可以自己手動替換)
  • 可以將中文導出excel文件,好讓專業的人可以進行翻譯
  • 可以將翻譯后的excel文件生成我們代碼要求的“定義多語言資源字典對象”

一,將所有中文Key找出來

由於vue i18n的資源key是可以包含中文英文跟特殊字符的,所以我們可以直接將文字直接當成key,這樣代碼中的中文信息就算換成多語言函數后也照樣能很容易讀懂,那么這里直接上這塊核心的.net core代碼將所有key找出來(自然想到正則表達式去匹配),並保持到一個.txt文件


        /// <summary>
        /// 抽離代碼文件中的中文到文件
        /// </summary>
        /// <param name="filePath">代碼文件路徑</param>
        /// <param name="keyFilePath">需要保持的包含所有中文字典的文本文件路徑</param>
        static void CreateKeyTxt(string filePath, string keyFilePath)
        {
            var regex = new Regex(@"((?<key>\w+) *= *['""](?<str>([^'""]*[\u4e00-\u9fa5]+[^'""]*))['""])|((?<start>[`""'>}]*)(?<str>[^\s`""'>}]*[\u4e00-\u9fa5]+[^\s`""'<{]*)(?<end>[`""'<{]*))");
            string fileContent = File.ReadAllText(filePath);

            var chineseMatchs = regex.Matches(fileContent);

            // 有中文需要處理
            if (chineseMatchs.Count > 0)
            {
                Dictionary<string, string> lines = new Dictionary<string, string>();
                foreach (Match htmlMatch in chineseMatchs)
                {
                    var str = htmlMatch.Groups["str"].Value.TrimEnd(':');

                    if (str.Contains("//") || str.Contains("/*") || str.Contains("*/") || str.Contains("<!--") || str.Contains("微軟雅黑"))
                    {
                        continue;
                    }

                    lines[str] = "";
                }

                using (StreamWriter fs = new StreamWriter(keyFilePath, true, Encoding.UTF8))
                {
                    foreach (var line in lines)
                    {
                        fs.WriteLine(line.Key);
                    }
                }
            }
        }

然后,你就可以拿這這個包含所有需要翻譯的內容,高高興興拿給翻譯人員讓他們辛苦勞作了!

二,根據包含所有中文Key跟翻譯內容的excel生成vue i18n要求的“定義多語言資源字典對象”文件

這個步驟其實就是生成兩個js文件,一個是zh-cn.js中文資源文件,一個是比如en.js的英文資源文件,而文件的內容就是簡單的K-V文件,比如:

export default {
    "取 消": "CANCEL",
    "確 定": "OK",
    "取消": "CANCEL",
    "確定": "OK",
    "確認": "OK",
    "@表示RR,- 表示AA": "@ is RR, - is AA",
}

主要代碼是:

        static void SaveI18N()
        {
            // 需要生成或者更新的i18n js資源文件夾地址
            var i18nResourceOutputFolderPath = Configuration["i18nResourceOutputFolderPath"];

            // 需要生成或者更新的i18n js資源文件名
            var i18nResourceFileName = Configuration["i18nResourceFileName"];

            if (string.IsNullOrEmpty(i18nResourceOutputFolderPath))
            {
                throw new ApplicationException("失敗:請先配置需要生成或者更新的i18n js資源文件夾地址");
            }
            if (string.IsNullOrEmpty(i18nResourceFileName))
            {
                throw new ApplicationException("失敗:請先配置需要生成或者更新的i18n js資源文件名");
            }

            // 獲取前端資源字典文件數據
            Dictionary<string, string> chineseDic = new Dictionary<string, string>();
            Dictionary<string, string> tDic = new Dictionary<string, string>();
            for (int i = 1; i < ExcelResourceFileData.Rows.Count; i++)
            {
                var shortName = (ExcelResourceFileData.Rows[i][0].ToString()).Trim();
                var chineseContent = (ExcelResourceFileData.Rows[i][1].ToString()).Trim();
                var tContent = (ExcelResourceFileData.Rows[i][2].ToString()).Trim();

                if (string.IsNullOrEmpty(shortName))
                {
                    throw new ApplicationException($"失敗:在第{i + 1}行存在空白的簡稱");
                }
                if (string.IsNullOrEmpty(chineseContent))
                {
                    throw new ApplicationException($"失敗:在第{i + 1}行存在空白的中文");
                }

                var key = $"\"{shortName}\"";
                chineseDic[key] = $"\"{chineseContent}\"";
                tDic[key] = $"\"{tContent}\"";
            }

            SaveI18NFile(i18nResourceOutputFolderPath, "zh-cn.js", chineseDic);
            SaveI18NFile(i18nResourceOutputFolderPath, i18nResourceFileName, tDic);
        }

        private static void SaveI18NFile(string i18nResourceOutputFolderPath, string fileName, Dictionary<string, string> resourceDic)
        {
            resourceDic = GetNewestResourceDic(i18nResourceOutputFolderPath, fileName, resourceDic);

            // 構建資源文件內容
            StringBuilder newFileContent = new StringBuilder();
            newFileContent.AppendLine("export default {");
            foreach (var chineseKeyValue in resourceDic)
            {
                newFileContent.AppendLine($"    {chineseKeyValue.Key}: {chineseKeyValue.Value},");
            }
            newFileContent.AppendLine("}");

            File.WriteAllText(Path.Combine(i18nResourceOutputFolderPath, fileName), newFileContent.ToString(), Encoding.UTF8);
        }

三,最后當然就是重頭戲,替換中文前端代碼

對於 <template> 里面的代碼,我們需要給property還有innerText分別單獨處理,比如

<el-button @click="onCancel" title="取消此上傳功能">取 消</el-button>

其中的title="取消此上傳功能"這個property是需要替換成:title="$t('取消此上傳功能')" 而innerText 取 消是需要替換成{{$t(取 消)}}的,最終替換為

<el-button @click="onCancel" :title="$t('取消此上傳功能')">{{$t(`取 消`)}}</el-button>

其中對 <template> 的替換核心代碼為:

        /// <summary>
        /// 替換代碼文件template中的中文為資源key
        /// </summary>
        /// <param name="input">代碼內容</param>
        /// <param name="resourceTypeStr">資源類型</param>
        /// <param name="pattern">正則表達式</param>
        /// <param name="isProperty">是否是屬性</param>
        /// <returns></returns>
        static string ReplaceChineseToI18NKeyForTemplate(string input, string resourceTypeStr, string pattern, bool isProperty = false)
        {
            var htmlMatchs = Regex.Matches(input, pattern, RegexOptions.IgnoreCase);
            int changedLength = 0;
            foreach (Match htmlMatch in htmlMatchs)
            {
                var newHtmlMatchIndex = htmlMatch.Index + changedLength;
                var chineseWordsMatch = Regex.Match(htmlMatch.Value, wordPattern, RegexOptions.IgnoreCase);
                var key = GetResourceKey(chineseWordsMatch.Value);

                // key不會空才需要替換
                if (!string.IsNullOrEmpty(key))
                {
                    string newHtml;

                    if (isProperty)
                    {
                        newHtml = ":" + Regex.Replace(htmlMatch.Value, wordPattern, "$t('" + key + "')");
                    }
                    else
                    {
                        newHtml = Regex.Replace(htmlMatch.Value, wordPattern, "{{$t('" + key + "')}}");
                    }

                    input = input.Substring(0, newHtmlMatchIndex) + newHtml + input.Substring(newHtmlMatchIndex + htmlMatch.Length);

                    changedLength += newHtml.Length - htmlMatch.Length;
                }
            }

            return input;
        }

<script> 的替換核心代碼跟<template> 類似,最主要的區別是js代碼里面使用的是this.$t('key')。

到這里我們就將整個前端系統的中文文本代碼全部修改成通過資源key動態顯示對應的語言文本了。

本代碼的一些不足

  • 上面的正則只能提取跟替換大部分中文情況,少數情況還需要手動修改;但即使這樣,也比一個一個替換節省了大量的人力物力。
  • <script> 替換為this.$t('key')中的this有不對的情況,改進方案是在代碼前面import i18n對象,然后將this換成i18n對象,由於時間有限,本文的代碼並沒涉及這塊,但實現起來比較容易。

一些思考

各位,相信你們跟我一樣,認為本文並沒有多少技術亮點,說白了,無非就是運用正則表達式替換一些代碼而已。但我想說的,如果我們一開始就一個頁面一個頁面弄,那得弄多久才能完成老板給我們的任務,所以往往解決問題,需要我們多靜下心來多思考一下,然后運用一些簡單的技術,即可快速實現我們想要的東西。特別是現在是一個快速發展的時代,更需要我們高效的解決問題,這樣才能體現我們的價值。

最終的代碼點這里

最后希望大家多多評論,2020年身體健康,過得順心!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM