Angular + 原生i18n

Angular + 原生i18n

安裝多語系套件
ng add @angular/localize

要多語系翻譯的元素加上i18n的html attribute

加上i18n attribute的html element等下會被轉換成語言檔。我們可以在i18n attribute後面加上@@來設定id。到時候產出的範本檔的id就不會是很長的一串亂數了。

<div class="card-header" i18n="@@testid_1">
	ConfigShowHide
</div>

<div class="col-6">
	<label for="isShowRefdesText" i18n>
		<input type="checkbox" id="isShowRefdesText" formControlName="isShowRefdesText" (change)="onCheckBoxChange($event)" />
		ShowRefdesText
	</label>
</div>

產生語系範本

新增範本檔

# --out-path 用來指定編譯完的檔案要放在哪裡
ng extract-i18n --output-path src/locale

在src下產生一份範本檔(src/locale/messages.xlf),內容大概長這樣。可以看到ShowRefdesText多了很多不相關的東西,把checkbox的onChange()事件都給包進去了,但ChatGPT後,是告訴我不會影響到翻譯。

略...
      <trans-unit id="testid_1" datatype="html">
        <source> ConfigShowHide </source>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">9,10</context>
        </context-group>
      </trans-unit>
      
      <trans-unit id="8165548348396053863" datatype="html">
        <source><x id="TAG_INPUT" ctype="x-input" equiv-text="&lt;input type=&quot;checkbox&quot; id=&quot;isShowRefdesText&quot; formControlName=&quot;isShowRefdesText&quot; (change)=&quot;onCheckBoxChange($event)&quot; /&gt;"/> ShowRefdesText</source>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">16,17</context>
        </context-group>
      </trans-unit>

新增其他語系的語言檔

這邊新增三個語言檔來作測試:

  1. 英文語言檔[messages.en.hant.xlf]
  2. 繁體中文語言檔[messages.zh.hant.xlf]
  3. 簡體中文語言檔[messages.zh.hans.xlf]

英文語言檔由於範本檔裡的標籤已經是英文了,所以不用再編輯內容,改檔名即可。繁體. 簡體中文語言檔用剛剛產生的範本檔[messages.xlf]複製修改即可。在source標籤下新增一個target標籤,並將翻譯的結果用target標籤包起來。

      <!--messages.zh.hant.xlf 繁體中文-->
      <trans-unit id="testid_1" datatype="html">
        <source>ConfigShowHide</source>
        <target>顯示/隱藏</target>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">10</context>
        </context-group>
      </trans-unit>
      
      <trans-unit id="8165548348396053863" datatype="html">
        <source><x id="TAG_INPUT" ctype="x-input" equiv-text="&lt;input type=&quot;checkbox&quot; id=&quot;isShowRefdesText&quot; formControlName=&quot;isShowRefdesText&quot; (change)=&quot;onCheckBoxChange($event)&quot; /&gt;"/> ShowRefdesText</source>
        <target><x id="TAG_INPUT" ctype="x-input" equiv-text="&lt;input type=&quot;checkbox&quot; id=&quot;isShowRefdesText&quot; formControlName=&quot;isShowRefdesText&quot; (change)=&quot;onCheckBoxChange($event)&quot; /&gt;"/> 顯示Refdes Text</target>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">16,17</context>
        </context-group>
      </trans-unit>
      <!--messages.zh.hans.xlf 簡體中文-->
      <trans-unit id="testid_1" datatype="html">
        <source>ConfigShowHide</source>
        <target>显示/隐藏</target>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">10</context>
        </context-group>
      </trans-unit>
      <trans-unit id="8165548348396053863" datatype="html">
        <source><x id="TAG_INPUT" ctype="x-input" equiv-text="&lt;input type=&quot;checkbox&quot; id=&quot;isShowRefdesText&quot; formControlName=&quot;isShowRefdesText&quot; (change)=&quot;onCheckBoxChange($event)&quot; /&gt;"/> ShowRefdesText</source>
        <target><x id="TAG_INPUT" ctype="x-input" equiv-text="&lt;input type=&quot;checkbox&quot; id=&quot;isShowRefdesText&quot; formControlName=&quot;isShowRefdesText&quot; (change)=&quot;onCheckBoxChange($event)&quot; /&gt;"/> 显示Refdes Text</target>
        <context-group purpose="location">
          <context context-type="sourcefile">src/app/features/control-panel-features/config/config.component.html</context>
          <context context-type="linenumber">16,17</context>
        </context-group>
      </trans-unit>

修改Angular.json

如果預設的網頁內容是使用英文,那我們就可以把sourceLocale設定為:en。這邊有一點要注意,sourceLocale使用:en-US後,locales的翻譯目標裡面就不能有en-US這個翻譯目標。不然建置時會回報錯誤。

Angular.json => {{專案名稱}} => i18n

  "projects": {
    "{{angular專案名稱}}": {
      "i18n": {
		"sourceLocale": "en-US",
        "locales": {
          // "en-US": {
          //   "translation": "src/locale/messages.en.xlf",
          //   "baseHref": "/en/"
          // },      
          "zh-hant": {            
            "translation": "src/locale/messages.zh.hant.xlf",
            "baseHref": "/zh-hant/"
          },
          "zh-hans": {
            "translation": "src/locale/messages.zh.hans.xlf",
            "baseHref": "/zh-hans/"
          }
        }
      }
	}
	略...
}	

 

Angular.json => {{專案名稱}} => architect => build => configurations

localize的值對應的是i18n設定的語系。

"en": {
	"localize": ["en-US"]
},
"tw": {
	"localize": ["zh-hant"]
},
"cn": {
	"localize": ["zh-hans"]
}  

 

Angular.json => {{專案名稱}} => architect => serve => configuration (本機測試)

browserTarget的值對應的是buil.configuration的語系屬性。

"en": {
	"browserTarget": "navigator-viewer:build:en"
},
"tw": {
	"browserTarget": "navigator-viewer:build:tw"
},
"cn": {
	"browserTarget": "navigator-viewer:build:cn"
}   

 

建置

設定好angular.json後,如果要發佈到Server上,就要先執行建置。加上localize參數後,Angular會依照不同的語系打包成不同的資料夾。這三個資料夾會分別對應到不同的翻譯檔,其中en-US比較特別,會對應到預設的message.xlf範本檔。

  • en-US => messages.xlf
  • zh-hans => messages.zh.hans
  • zh-hant => message.zh.hant
ng b --configuration=vt01 --localize
      "i18n": {
        "sourceLocale": "en-US",
        "locales": {
          "zh-hant": {
            "translation": "src/locale/messages.zh.hant.xlf",
            "baseHref": "/tw/"
          },
          "zh-hans": {
            "translation": "src/locale/messages.zh.hans.xlf",
            "baseHref": "/cn/"
          }
        }
      }, 

測試

語系為繁體中文

ng s --configuration=tw

 

語系為簡體中文

ng s --configuration=cn

 

不給configuration參數的話,預設為英文。

ng s

發佈

前面可以看到,編譯後,會依照不同語系產出不同的資料夾。那前端要如何依照不同的語系來載入不同的資料夾裡面的檔案呢? 這邊以我目前的MVC + Angular專案來當範例。

 

Controller:

我這邊是把當前語系先存到cookie裡,後端Controller取得後,放在ViewModel裡面,再透過ViewModel傳給前端的View(Index.cshtml)

//Controller

public ActionResult Index()
{
	string myLang = Request.Cookies["MyLang"] != null ? Request.Cookies["MyLang"].Value : "en";
	viewModel.LanguageInfo = new LanguageInfo()
	{
		CurrentLanguage = myL	
	}
}            
               

View:

依照ViewModel.CurrentLanguage來判斷要載入哪個語系的程式包的前端js & css

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>@ViewBag.Title</title>
    
    @{
        @*載入多語系css*@
        switch (Model.LanguageInfo.CurrentLanguage)
        {

            case "en":
                @Styles.Render("~/Areas/Viewer/Content/en_wwwrootCss")
                break;
            case "zh-Hans":
                @Styles.Render("~/Areas/Viewer/Content/zh-hans_wwwrootCss")
                break;
            case "zh-Hant":
                @Styles.Render("~/Areas/Viewer/Content/zh-hant_wwwrootCss")
                break;
        }
    }
</head>

	省略...

<body>
	省略...
    @{
        @*載入多語系js*@
        switch (Model.LanguageInfo.CurrentLanguage)
        {
            case "en":
                @Scripts.Render("~/Areas/Viewer/Scripts/en_wwwrootJs")
                break;
            case "zh-Hans":
                @Scripts.Render("~/Areas/Viewer/Scripts/zh-hans_wwwrootJs")
                break;
            case "zh-Hant":
                @Scripts.Render("~/Areas/Viewer/Scripts/zh-hant_wwwrootJs")
                break;
        }
    }
</body>	

ref: