##前言javascript
這是我第一次發博客,2020年立個flag之後要常常發。html
最近應公司上層要求,須要將現有項目儘快支持多語言,而中文內容能夠找專業人員翻譯。那麼我們說幹就幹,首先咱們項目的前端是用vue寫的spa程序且組件方面用的element ui,那麼天然而然想到用vue官方推薦的vue i18n,我快速過了下i18n整個Guide官方文檔,看來使用很簡單,主要步驟就是:前端
這裏有一個最簡單的實例vue
相信你們都看就懂,但你們有沒想過,我前面說了公司要求是把現有項目儘快支持多語言,那說明咱們的項目已經存在大量的代碼。duang,尼瑪,那不是作「定義多語言資源字典對象」是一個體力活? 要知道,咱們這個前端項目至少有成百上千個頁面,若是讓我一個一個的翻譯裏面的中文跟換成vue i18n要求的$t('key'),那估計我跟團隊得累死呀!!!因此,爲了避免幹體力活,就有了這篇文章。java
##先隨便拿一個前端文件看看最終的效果圖:git
自動替換前: github
自動替換後: 正則表達式
感受如何?最終的代碼點這裏npm
#下面咱們來詳細介紹下實現的各個步驟c#
要想替換前端代碼爲vue i18n的語法,就涉及到本身編寫一套程序來實現準確替換,在替換以前,咱們須要考慮:
<template>
跟<script>
裏面的中文代碼準確找出來(須要排除註釋等特殊狀況,至少能替換98%以上的代碼,少許代碼能夠本身手動替換)##一,將全部中文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年身體健康,過得順心!!!
原文出處:https://www.cnblogs.com/sutong/p/12221577.html