使用Angular-CLI发布一个i18n(国际化)应用(译)

3,568 阅读3分钟

第一节:初识Angular-CLI
第二节:登录组件的构建
第三节:建立一个待办事项应用
第四节:进化!模块化你的应用
第五节:多用户版本的待办事项应用
第六节:使用第三方样式库及模块优化用
第七节:给组件带来活力
Rx--隐藏在Angular 2.x中利剑
Redux你的Angular 2应用
第八节:查缺补漏大合集(上)
第九节:查缺补漏大合集(下)

使用Angular-CLI发布一个i18n(国际化)应用

原文作者:Philippe Martin
译者:王芃

原文地址:medium.com/@feloy/depl…


在这篇文章中我将会解释如何使用angular-cli从头创建一个国际化的Angular2应用以及如何把这个应用部署到Apache服务器。

文中涉及到的软件依赖版本如下:

  • angular-cli:1.0.0-beta.24
  • angular: 2.4.1
  • Apache 2.4

文中描述的样例App已经放在github上了:github.com/feloy/angul…

新鲜出炉的i18n App

我们首先使用angular-cli创建一个Angular App:

$ ng new angular-cli-i18n-sample

app.component.html 中,我们做一点小改动,加入一些用于i18n的文本:

<h1 i18n>Hello world!</h1>

接下来,我们需要创建一个 xlf 文件。但在创建之前,我们需要对 src/tsconfig.json 做一些小改动:

{
  "compilerOptions": {
    [...]
  },
  "exclude": [ "test.ts" ],
  "angularCompilerOptions": { "genDir": "i18n" }
}

package.json 中我们也需要加入一个脚本定义,使用 ng-xi18n 这个工具来生成一个可翻译的文本文件:

{
  [...]
  "scripts": {
    [...]
    "extract-i18n": "cd src && ng-xi18n"
  }
  [...]
}

现在,我们可以使用下面的命令来生成 src/i18n/messages.xlf 了:

$ npm run extract-i18n

我们现在要创建不同的语言翻译文件了,首先是英文翻译,拷贝 src/i18n/messages.xlfsrc/i18n/messages.en.xlf

[...]
      <trans-unit id="[...]" datatype="html">
        <source>Hello World!</source>
        <target>Hello World!</target>
      </trans-unit>
[...]

同样创建法语的 src/i18n/messages.fr.xlf :

[...]
      <trans-unit id="[...]" datatype="html">
        <source>Hello World!</source>
        <target>Salut la foule !</target>
      </trans-unit>
[...]

再来创建一个西班牙版本 src/i18n/messages.es.xlf

[...]
      <trans-unit id="[...]" datatype="html">
        <source>Hello World!</source>
        <target>¿hola, qué tal?</target>
      </trans-unit>
[...]

现在我们可以使用 angular-cli 在启动应用时使用你的语言偏好,以西班牙语为例:

$ ng serve --aot \
           --i18n-file=src/i18n/messages.es.xlf \
           --locale=es \
           --i18n-format=xlf

现在你可以打开浏览器访问 http://localhost:4200 ,然后就应该可以看到西班牙语版本的应用了。

发布到生产环境的准备工作

在生产环境,我们希望依照不同语言,以不同的子目录的形式呈现不同语言版本的app。例如西班牙语版本可以通过 http://myapp.com/es/ 来访问;而法语版本通过 http://myapp.com/fr/ 来访问。当然我们还希望根URL http://myapp.com/ 可以重定向到我们选择的语言选项。

要这么做的话,我猜我们需要根据不同的语言来改变 base href,比如改成 es, enfr。幸运的是,angular-cli 有一个特殊的命令行开关:--bh,这个开关允许我们在编译时声明 base href

下面给出一个命令行脚本(译者注:*nix版本可用,windows版本需要改写一下),这个脚本可以帮我们为不同语言创建不同的bundle

$ for lang in es en fr; do \
    ng build -o dist/$lang \
             --aot \
             -prod \
             --bh /$lang/ \
             --i18n-file=src/i18n/messages.$lang.xlf \
             --i18n-format=xlf \
             --locale=$lang; \
  done

当然我们可以在 package.json 中给出这个命令的脚本定义,这样我们就可以使用npm脚本来执行了:npm run build-i18n

{
  [...]
  "scripts": {
    [...]
    "build-i18n": "for lang in en es fr; do ng build -o dist/$lang --aot -prod --bh /$lang/ --i18n-file=src/i18n/messages.$lang.xlf --i18n-format=xlf --locale=$lang; done"
  }
  [...]
}

现在,我们在dist目录下有了3个子目录: es/fr/en/ ,里面有对应不同语言的bundle。

Apache配置

下面是一个虚拟主机配置,这个配置在 /var/www 目录来启动我们的不同版本的bundle文件,所以我们得把刚刚生成的3个目录 es/fr/en/拷贝到这里。

有这样一个配置后,访问 http://www.myapp.com 会根据浏览器优选的语言首选项来重定向到不同的子目录(如果没有匹配的语言时 en作为fallback)。当然你可以通过改动url来访问其他语言版本。

<VirtualHost *:80>
  ServerName www.myapp.com
  DocumentRoot /var/www
  <Directory "/var/www">
    RewriteEngine on
    RewriteBase /
    RewriteRule ^../index\.html$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule (..) $1/index.html [L]
    RewriteCond %{HTTP:Accept-Language} ^fr [NC]
    RewriteRule ^$ /fr/ [R]
    RewriteCond %{HTTP:Accept-Language} ^es [NC]
    RewriteRule ^$ /es/ [R]
    RewriteCond %{HTTP:Accept-Language} !^es [NC]
    RewriteCond %{HTTP:Accept-Language} !^fr [NC]
    RewriteRule ^$ /en/ [R]
  </Directory>
</VirtualHost>

加餐:为不同语言增加链接

如果在应用中有不同语言的选项链接,用户可以点击这些链接来访问不同语言版本,那就太好了(译者注:汗,这不是标配需求吗?)。

在Angular2中,需要知道一个小技巧:当前语言是可以通过 LOCALE_ID 取得的。

下面我们演示一下如何取得 LOCALE_ID、显示不同语言的列表:

// app.component.ts
import { Component, LOCALE_ID, Inject } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  languages = [
    { code: 'en', label: 'English'},
    { code: 'es', label: 'Español'},
    { code: 'fr', label: 'Français'}
  ];
  constructor(@Inject(LOCALE_ID) protected localeId) {}
}
<!-- app.component.html -->
<h1 i18n>Hello World!</h1>
<template ngFor let-lang [ngForOf]="languages">
  <span *ngIf="lang.code !== localeId">
    <a href="/{{lang.code}}/">{{lang.label}}</a> </span>
  <span *ngIf="lang.code === localeId">{{lang.label}} </span>
</template>