##前言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