Интернационализация (i18n) в Angular 2

habralogo.jpg
Для создания мультиязычных интерфейсов Ангулар предлагает использовать механизм разметки HTML шаблонов специальным маркером i18n который после компиляции удаляется из финального кода. Для этого достаточно указать этот маркер как атрибут тега окружающего текст.

Hello baby!

Для маркера можно указывать дополнительные параметры которые отображаются в специализированных редакторах использующихся для перевода и дополняют переводимый текст служебной информацией призванной помочь переводчику. Это параметры передаются в формате «Значение|Описание» или только «Описание».

Hello baby!

Hello baby!

Возможно указать маркер и для текста который не окружен тегом. Для этого используется либо специальный пустой тег который не рендерится в финальный код.

I don't output any element

или специальный комментарий

I don't output any element either

Возможно так же делать перевод для аттрибутов тегов. Таких как alt, title, …


Plural


Окончание слов для множественного числа основано на правилах http://unisource.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules и http://cldr.unisource.org/index/cldr-spec/plural-rules в формате:
{wolves, plural, =0 {no wolves} =1 {one wolf} =2 {two wolves} other {a wolf pack}}

wolves  — название переменной содержащей число
plural  — название типа преобразования
=0 — возможное значение переменной, {no wolves} — отображаемый текст для значения данного значения
=1 — возможное значение переменной, {one wolf} — отображаемый текст для данного значения
=2 — возможное значение переменной, {two wolves} — отображаемый текст для данного значения
other — если значение переменной не задано, {a wolf pack} — отображаемый текст для данного значения

Возможные следующие варианты:

  • =0
  • =1
  • =2
  • few (…3–10 или 5–10, зависит от правил локалищации)
  • other (дефолтное значение для всех неуказанных вариантов)

Select


Дает возможность выбирать значение на базе литеральной переменной:
The hero is {gender, select, m {male} f {female}}

gender — название переменной содержащей значение
select — название типа преобразования
m — возможное значение переменной, {male} — отображаемый текст для данного значения
f — возможное значение переменной, {female} — отображаемый текст для данного значения

ng-xi18n


Для извлечения строк из аппликации используется специальная утилита ng-xi18n входящая в состав angular-cli:
./node_modules/.bin/ng-xi18n -p src/tsconfig.json

Данная утилита ищет маркер i18n в шаблонах и копирует соответствующий текст формируя из него XML файл XLIFF формата (версии 1.2).

Так же поддерживается формат XML Message Bundle (XMB). Для этого нужно задать соответствующий ключ:

./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p src/tsconfig.json

Далее XML файл может быть отправлен переводчику который сможет редактировать его одним из специализированных редакторов. Полученный в итоге файл должен быть сохранен в папке i18n и содержать в своем названии язык локализации. Например в случае испанского языка, из исходного messages.xlf получится messages.es.xlf. Для каждого языка интерфейса должен быть создан отдельный файл.

Для того что бы файл с переводом мог быть использован Ангуларом необходимо добавить следующий код в main.ts:

// Указать нужные зависимости
import {TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID} from '@angular/core';

// Задать ссылку на XML файл содержащий перевод
let current_language = 'ru';
import {RU_TRANS} from './i18n/messages.' + current_language;

Тут есть одно замечание. Если мы используем angular-cli, то в настоящее время мы не можем самостоятельно (официально) менять конфигурацию webpack и соответственно не можем добавить правило raw для файлов с расширением xlf позволяющее импортировать их напрямую. Поэтому пока приходится вставлять их в файл обертку (messages.ru.ts):

export const RU_TRANS = ` //Обратные кавычки в файле позволяют многострочный контент.
// сюда нужно скопировать содержимое XML файла messages.ru.xlf
`;

Конфигурируем в main.ts

platformBrowserDynamic().bootstrapModule(AppModule, {
  providers: [
    {provide: TRANSLATIONS, useValue: RU_TRANS},
    {provide: TRANSLATIONS_FORMAT, useValue: 'xlf'},
    {provide: LOCALE_ID, useValue: current_language}
  ]
});

К сожалению описанная выше интернализация имеет ряд подводных камней и ограничений из-за которых она может быть использована только в проектах с самым простым интерфейсом:

  • при смене содержимого тега даже не относящегося к переводимому тексту при последующей экстракции может поменяться ID, что в свою очередь приводит к ошибкам компиляции приложения.
  • экстракция происходит только из HTML файлов и невозможна для динамического контента (например каких либо списков или дропбоксов)

Это ставит крест на данном подходе для использования в большинстве проектов насыщенных динамическим контентом и использующие взаимосвязанные дропбоксы.

Работа по решению данных вопросов происходит и возможно в версиях Ангулар 4 или 5 интернационализация будет доведена до ума.

Поэтому пока единственным рабочим решением, по прежнему, остается ng2-translate.

ng2-translate


Является непрямым наследником хорошо известного модуля angular-translate.
Так же использует translate фильтр. Кроме того тексты с переводом хранятся в файлах JSON в том же формате как и в angular-translate.

Все это позволяет очень просто перенести шаблоны из ng1 в ng2. По сути все что требуется это сделать следующее:

Добавить зависимость в модуль аппликации в файл src/app/app.module.ts

import { HttpModule, Http } from '@angular/http';
import { TranslateModule, TranslateLoader, TranslateStaticLoader } from 'ng2-translate/ng2-translate';

@NgModule({
...
  imports: [
    ...
    HttpModule,
    TranslateModule.forRoot({
      provide: TranslateLoader,
      // можно указать свой путь к папке i18n где находятся файлы с переводом
      useFactory: (http: Http) => new TranslateStaticLoader(http, '/assets/i18n', '.json'),
      deps: [Http]
    })
  ],
  providers: [],
  bootstrap: [AppComponent]
})

Модуль сам подключает JSON файлы находящиеся в папке i18n. Поэтому импортировать вручную их не требуется.

Так же необходимо в корневом компоненте указать используемый язык

import {TranslateService} from 'ng2-translate';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works!';

  constructor(translate: TranslateService) {
    // this language will be used as a fallback when a translation isn't found in the current language
    translate.setDefaultLang('ru');

    // the lang to use, if the lang isn't available, it will use the current loader to get them
    translate.use('ru');
  }
}

Собственно это все что необходимо. Модуль готов к работе.

Формат JSON файла очень простой (ключ: перевод):

{
  "HELLO": "Привет"
}

далее в HTML можно соответственно использовать translate в следующих формах:

{{'HELLO' | translate}}
HELLO

Так же можно использовать TranslateService в компонентах. Полное API https://github.com/ngx-translate/core

Комментарии (0)

© Habrahabr.ru