[Vue] 跟著 Vue 闖蕩前端世界 - 14 使用 C# 同步多國語系註解

  • 1516
  • 0
  • Vue
  • 2018-09-02

筆者在套用語系時,習慣將該 key 值對應的中文語系文字當作註解一併插入,但語系文字會經常被調來調去,此時註解在畫面的文字就會有不同步的情況;本文透過 C# 實作一個同步語系文字的工具,讓「語系檔」與「語系套用註解」永遠保持一致的狀態。

前言


筆者在套用語系時,習慣將該 key 值對應的中文語系文字當作註解一併插入,這樣的好處是可以透過畫面上的文字快速搜尋到該頁面位置,並且在檢視頁面原始碼時也會比較清楚各欄位標題,不用再從 key 值去猜想了。

但語系文字經常會被調來調去,此時註解在畫面的文字就會有不同步的情況,這對有點強迫症的開發人員就會很不舒服 (握拳);本文透過 C# 實作一個同步語系文字的工具,讓語系檔與語系註解永遠保持一致的狀態。

 

 

實作說明


首先將語系檔以 StreamReader 開啟後,透過 Regex 找出各 key-value pair 值存放在 Dictionary 中。

public Dictionary<string,string> GetLangDictionary(string langPath) 
{
  var langs = new Dictionary<string, string>();
  var code = string.Empty;

  using (StreamReader sr = new StreamReader(langPath))
  {
    code = sr.ReadToEnd();
    string pattern = @"(__.*): ['|`](.*)['|`]";
    MatchCollection matches = Regex.Matches(code, pattern);
    foreach (Match match in matches)
    {
      var key = match.Groups[1].Value; 
      var value = match.Groups[2].Value; 
      langs[key] = value;
    }
  }

  return langs;
}

 

專案中對於語系的取用都具有一致性,因此可以透過固定 pattern 將這些文字給篩選出來。

$t'__key' /* 中文語系顯示文字 */ )

 

使用以下 Regular Expression 來進行篩選


 

找出程式碼中有套用語系的地方後,依據取出語系的 key 值來尋找先前存入 Dictionary 的語系檔文字,接著直接取代程式碼中的語系註解文字即可,這樣就可以保持註解與語系檔的一致性。

public void ReplaceLangComment(string filePath, Dictionary<string, string> langs)
{
  var code = string.Empty;
  var isModified = false;
  using (StreamReader sr = new StreamReader(filePath))
  {
    code = sr.ReadToEnd();

    string pattern = @"\$t\(*.[`|']([\s\S]*?)[`|'](\/\*.*?\*\/)?.*?\)";
    MatchCollection matches = Regex.Matches(code, pattern);
    foreach (Match match in matches)
    {
      string langAllText = "";
      string langKey = "";
      string langOther = "";

      langAllText = match.Value;         // ex. $t('__resetPCodeTitle'/*重設使用者名稱及固定密碼*/)
      langKey = match.Groups[1].Value; 	 // ex. __resetPCodeTitle
      langOther = match.Groups[2].Value; // ex. /*重設使用者名稱及固定密碼*/


      // 本來就沒有註解就略過
      if (!langOther.Contains(@"/*"))
      { continue; }

      // 找不到語系檔有對應的 key 值 (印出備查)
      if (!langs.ContainsKey(langKey.Trim()))
      {
        langKey.Dump();
        continue;
      }

      // 取出需要取代的字串
      var startIndex = langOther.IndexOf("/*");
      var endIndex = langOther.LastIndexOf("*/");
      string replacedTarget = langOther.Substring(startIndex, endIndex - startIndex + 2);
      
      // 取出正確的語系文字
      var hasSpace = replacedTarget.Contains("/* ");
      string correctLangValue = hasSpace ? $"/* {langs[langKey.Trim()]} */" : $"/*{langs[langKey.Trim()]}*/";

      // 執行取代
      string newLangAllText = langAllText.Replace(replacedTarget, correctLangValue);
      code = code.Replace(langAllText, newLangAllText);
      isModified = true;

    }
  }

  if (isModified)
  {
    File.WriteAllText(filePath, code);
  }
}
Dump 語法為 LINQPad 使用的特殊方法,目的僅在於印出資料至畫面上顯示而已。

 

 

完整代碼


完整代碼如下,只要設定中文語系檔路徑位置後,再透過 Directory.GetFiles 方法過濾出需要同步語系註解的所有檔案清單,逐一執行 ReplaceLangComment 進行同步即可。以下代碼貼到 LINQPad 直接執行即可。


void Main()
{
  // 中文語系檔位置
  var langPath = @"D:\xxx\src\i18n\tw\lang.js";
  var langs = GetLangDictionary(langPath);

  // 掃描程式碼目錄 (取出所有 vue 檔)
  string[] filePaths = Directory.GetFiles(@"D:\xxx\src", "*.vue", SearchOption.AllDirectories);
  foreach (var filePath in filePaths)
  {
    // 進行同步
    ReplaceLangComment(filePath, langs);
  }
}

public Dictionary<string, string> GetLangDictionary(string langPath)
{
  var langs = new Dictionary<string, string>();
  var code = string.Empty;

  using (StreamReader sr = new StreamReader(langPath))
  {
    code = sr.ReadToEnd();
    string pattern = @"(__.*): ['|`](.*)['|`]";
    MatchCollection matches = Regex.Matches(code, pattern);
    foreach (Match match in matches)
    {
      var key = match.Groups[1].Value;
      var value = match.Groups[2].Value;
      langs[key] = value;
    }
  }

  return langs;
}


public void ReplaceLangComment(string filePath, Dictionary<string, string> langs)
{
  var code = string.Empty;
  var isModified = false;
  using (StreamReader sr = new StreamReader(filePath))
  {
    code = sr.ReadToEnd();

    string pattern = @"\$t\(*.[`|']([\s\S]*?)[`|'](\/\*.*?\*\/)?.*?\)";
    MatchCollection matches = Regex.Matches(code, pattern);
    foreach (Match match in matches)
    {
      string langAllText = "";
      string langKey = "";
      string langOther = "";

      langAllText = match.Value;         // ex. $t('__resetPCodeTitle'/*重設使用者名稱及固定密碼*/)
      langKey = match.Groups[1].Value; 	 // ex. __resetPCodeTitle
      langOther = match.Groups[2].Value; // ex. /*重設使用者名稱及固定密碼*/


      // 本來就沒有註解就略過
      if (!langOther.Contains(@"/*"))
      { continue; }

      // 找不到語系檔有對應的 key 值 (印出備查)
      if (!langs.ContainsKey(langKey.Trim()))
      {
        langKey.Dump();
        continue;
      }

      // 取出需要取代的字串
      var startIndex = langOther.IndexOf("/*");
      var endIndex = langOther.LastIndexOf("*/");
      string replacedTarget = langOther.Substring(startIndex, endIndex - startIndex + 2);
      
      // 取出正確的語系文字
      var hasSpace = replacedTarget.Contains("/* ");
      string correctLangValue = hasSpace ? $"/* {langs[langKey.Trim()]} */" : $"/*{langs[langKey.Trim()]}*/";

      // 執行取代
      string newLangAllText = langAllText.Replace(replacedTarget, correctLangValue);
      code = code.Replace(langAllText, newLangAllText);
      isModified = true;

    }
  }

  if (isModified)
  {
    File.WriteAllText(filePath, code);
  }
}

 

 


希望此篇文章可以幫助到需要的人

若內容有誤或有其他建議請不吝留言給筆者喔 !