之前介紹過 ASP.NET Core 教學 - T4 Template 產生強型別多國語言,JavaScript 的多國語言也可以透過 T4 Template 建立出來。
本篇將介紹用 T4 Template 製作 JavaScript 的多國語言檔,同時產生 Angular 需要的 TypeScript 定義檔。
1. 建立多國語言檔
在網站目錄中建立 Resources 的資料夾,在裡面新增資源檔 *.resx
。如下:
語系檔名稱就可以依照類型自訂,跟 ASP.NET MVC 的命名方式相同如:
- Text.en-GB.resx
- Text.zh-TW.resx
- Message.en-GB.resx
- Message.zh-TW.resx
*.resx
檔案必須要帶語系在後綴。如:*.en-GB.resx
。
我的範例產生了四個資源檔,內容如下:
2. T4 Template
在 Resources 資料夾建立副檔名為 *.tt
的檔案,如:JsLocalizationGenerator.tt。
新增項目可能找不到 *.tt
的檔案,可以隨便建一個 *.txt
的檔案再把副檔名改為 *.tt
。
T4 Template 簡單的說,就是透過程式碼產生程式碼。
我建立了一個 JsLocalizationGenerator.tt 讀取 *.resx
的內容,然後產生出 lang.d.ts
的定義檔及 {culture}.js
的語系檔。
Resources\JsLocalizationGenerator.tt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| <#@ template language="C#" hostspecific="true" #> <#@ output extension=".js" #> <#@ assembly name="EnvDTE" #> <#@ assembly name="System.Core.dll" #> <#@ assembly name="System.Xml.dll" #> <#@ assembly name="System.Xml.Linq.dll" #> <#@ import namespace="EnvDTE" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Text.RegularExpressions" #> <#@ import namespace="System.Xml.Linq" #> <# var defaultCulture = "en-gb"; var resourceFolder = this.Host.ResolvePath(""); var definitionPath = "../wwwroot/app/definitions"; var jsLangPath = "../wwwroot/js/lang"; var resources = GetResourcesByCulture(defaultCulture, resourceFolder);
CreateFolderIfNotExists(Path.Combine(resourceFolder, definitionPath)); CreateDefinition(Path.Combine(resourceFolder, definitionPath, "lang.d.ts"), resources);
CreateFolderIfNotExists(Path.Combine(resourceFolder, jsLangPath)); foreach (var culture in GetCultures(resourceFolder)){ resources = GetResourcesByCulture(culture, resourceFolder); CreateJsLocalization(Path.Combine(resourceFolder, jsLangPath, culture+".js"), resources); } #> <#+ void CreateFolderIfNotExists(string path){ bool folderExists = Directory.Exists(path); if (!folderExists){ Directory.CreateDirectory(path); } }
List<string> GetCultures(string resourceFolder) { var files = Directory.GetFiles(resourceFolder, "*.resx"); var cultures = files.Select(file=>Path.GetFileNameWithoutExtension(file).Split('.').Last().ToLower()).Distinct(); return cultures.ToList(); }
Dictionary<string, Dictionary<string, string>> GetResourcesByCulture(string culture, string resourceFolder) { var files = Directory.GetFiles(resourceFolder, "*.resx"); var resources = files.GroupBy(file => { var fileName = Path.GetFileNameWithoutExtension(file).Split('.'); return fileName.First(); }).ToDictionary(g => g.Key, g => { var defaultFile = g.Single(s => s.IndexOf(culture, StringComparison.CurrentCultureIgnoreCase) != -1); var xdoc = XDocument.Load(defaultFile); var dictionary = xdoc.Root.Elements("data").ToDictionary(e => e.Attribute("name").Value, e => e.Element("value").Value); return dictionary; }); return resources; }
void CreateDefinition(string path, Dictionary<string, Dictionary<string, string>> resources){ var stringBuilder = new StringBuilder(); stringBuilder.Append(@"interface R {"); foreach (var category in resources) { stringBuilder.Append(string.Format("{0}: {{", category.Key)); foreach (var resource in category.Value) { stringBuilder.Append(string.Format("{0}: string;",resource.Key)); } stringBuilder.Append(@"};"); } stringBuilder.Append(@"}"); stringBuilder.Append(@"declare var R:R;"); File.WriteAllText(path, stringBuilder.ToString()); }
void CreateJsLocalization(string path, Dictionary<string, Dictionary<string, string>> resources){ var stringBuilder = new StringBuilder(); stringBuilder.Append(@"var R = {"); foreach (var category in resources) { stringBuilder.Append(string.Format("{0}: {{", category.Key)); foreach (var resource in category.Value) { stringBuilder.Append(string.Format("{0}: \"{1}\",",resource.Key,resource.Value)); } stringBuilder.Append(@"},"); } stringBuilder.Append(@"}"); File.WriteAllText(path, stringBuilder.ToString()); } #>
|
3. Output
只要 JsLocalizationGenerator.tt 有異動,或者是點右鍵執行自訂工具,就會觸發自動產生 lang.d.ts
定義檔及 {culture}.js
語系檔。
我在 JsLocalizationGenerator.tt 中指定了 lang.d.ts
定義檔及 {culture}.js
語系檔的輸出位置,如上述範例輸出的檔案內容如下:
wwwroot/app/definitions/lang.d.ts
1 2 3 4 5 6 7 8 9 10 11 12
| interface R { Message: { InternalServerError: string; }; Text: { Login: string; Password: string; Send: string; UserName: string; }; } declare var R: R;
|
wwwroot/js/lang/en-gb.js
1 2 3 4 5 6 7 8 9 10 11
| var R = { Message: { InternalServerError: "Internal Server Error", }, Text: { Login: "Login", Password: "Password", Send: "Send", UserName: "User Name", }, }
|
wwwroot/js/lang/zh-tw.js
1 2 3 4 5 6 7 8 9 10 11
| var R = { Message: { InternalServerError: "內部伺服器發生錯誤", }, Text: { Login: "登入", Password: "密碼", Send: "送出", UserName: "帳號", }, }
|
實際產出不會排版,會連成同一行
4. 載入語系
取得 culture 的資訊,載入對應的語系檔。
Views\Home\index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| @using System.Text.RegularExpressions @using System.Globalization
@{ var culture = Context.Request.Path.Value.Split('/')[1]; var hasCultureFromUrl = Regex.IsMatch(culture, @"^[A-Za-z]{2}-[A-Za-z]{2}$"); culture = hasCultureFromUrl ? culture : CultureInfo.CurrentCulture.Name; }
<!DOCTYPE html> <html> <head> <title>MyAngular4</title> <base href="/"> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <script src="/node_modules/core-js/client/shim.min.js"></script> <script src="/node_modules/zone.js/dist/zone.js"></script> <script src="/node_modules/systemjs/dist/system.src.js"></script> <script src="/systemjs.config.js"></script> <script> System.import("/app/main.js").catch(function (err) { console.error(err); }); </script> </head> <body> <my-app>Loading...</my-app> <script src="/js/lang/@(culture).js"></script> </body> </html>
|
wwwroot\app\app.component.ts
1 2 3 4 5 6 7 8 9 10 11 12
| import { Component } from "@angular/core"; @Component({ selector: "my-app", templateUrl: "/app/app.component.html" }) export class AppComponent { login = R.Text.Login; password = R.Text.Password; username = R.Text.UserName; send = R.Text.Send; internalServerError = R.Message.InternalServerError; }
|
wwwroot\app\app.component.html
1 2 3 4 5 6 7
| <a href="/en-gb">en-gb</a> | <a href="/zh-tw">zh-tw</a>
<p>Login = {{login}}</p> <p>Password = {{password}}</p> <p>UserName = {{username}}</p> <p>Send = {{send}}</p> <p>InternalServerError = {{internalServerError}}</p>
|
程式碼下載
asp-net-core-angular-localization-t4
相關文章
ASP.NET Core 教學 - T4 Template 產生強型別多國語言
Angular 4 教學 - 多國語言
ASP.NET Core 教學 - 多國語言