<h1 i18n>Hello baby!</h1>
Для маркера можно указывать дополнительные параметры которые отображаются в специализированных редакторах использующихся для перевода и дополняют переводимый текст служебной информацией призванной помочь переводчику. Это параметры передаются в формате «Значение|Описание» или только «Описание».
<h1 i18n="An introduction header for this sample">Hello baby!</h1>
<h1 i18n="User welcome|An introduction header for this sample">Hello baby!</h1>
Возможно указать маркер и для текста который не окружен тегом. Для этого используется либо специальный пустой тег который не рендерится в финальный код.
<ng-container i18n>I don't output any element</ng-container>
или специальный комментарий
<!--i18n: optional meaning|optional description -->
I don't output any element either<!--/i18n-->
Возможно так же делать перевод для аттрибутов тегов. Таких как alt, title,…
<img [src]="logo" i18n-title title="Angular logo" />
Plural
Окончание слов для множественного числа основано на правилах http://unisource.org/reports/tr35/tr35-numbers.html#Language_Plural_Rules и http://cldr.unisource.org/index/cldr-spec/plural-rules в формате:
<span i18n>{wolves, plural, =0 {no wolves} =1 {one wolf} =2 {two wolves} other {a wolf pack}}</span>
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
Дает возможность выбирать значение на базе литеральной переменной:
<span i18n>The hero is {gender, select, m {male} f {female}}</span>
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
ngx-translate.com
Является непрямым наследником хорошо известного модуля 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]
})
Вышеприведенный пример загрузки зависимости актуален для 5-ой версии ng2-translate.
Модуль сам подключает 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 в следующих формах:
<div>{{'HELLO' | translate}}</div>
<div [translate]="'HELLO'" [translateParams]="{value: 'world'}"></div>
<div translate [translateParams]="{value: 'world'}">HELLO</div>
Так же можно использовать TranslateService в компонентах.
Update (4.3.2017):
Буквально накануне вышла 6-я версия, в которой ng2-translate был переименован в ngx-translate для соответствия официальной конвенции.
Кроме этого принципиальных изменений не произошло. Единственное TranslateStaticLoader был вынесен в самостоятельный модуль и переименован в TranslateHttpLoader. Соответственно синтаксис загрузки модуля в app.module.ts стал следующим:
import { HttpModule, Http } from '@angular/http';
import {TranslateModule, TranslateLoader} from "@ngx-translate/core"; //изменилось
import {TranslateHttpLoader} from "@ngx-translate/http-loader"; //изменилось
@NgModule({
...
imports: [
...
HttpModule,
TranslateModule.forRoot({
provide: TranslateLoader,
// можно указать свой путь к папке i18n где находятся файлы с переводом
useFactory: (http: Http) => new TranslateHttpLoader(http, "i18n/", ".json"), //изменилось
deps: [Http]
})
],
providers: [],
bootstrap: [AppComponent]
})
Комментарии (7)
kemsky
04.03.2017 14:21Ситуация с встроенной локализацией остается печальной с момента релиза (полгода уже?), я бы пока никому не советовал с ней связываться.
Кроме того, идея компилировать приложение для каждой локализации лично мне кажется слишком ограниченной. До сих пор нет возможности переводить из кода, можно только в шаблоне. Поддержка в angular-cli также хромает.
Radli007
04.03.2017 14:351-ый способ мне не понравился, т.к. там xml (размер файла, структура файла предполагает лишнюю информацию), пробывать не стал
ng2-translate способ не понравился тем, что там мешает кеширование браузера. При обновлении словаря, файлы из папки i18n в формате json кешируются и у клиента моут обновиться только спустя какое то время. ( возможно можно настроить, что бы в продакшене так же приписывать к названию файлов guid)
использую другой translate чуть модифицированный (что бы можно было использовать в shared module)
https://github.com/AtnagulovID/Angular-2-CLI-Learn-project/tree/master/src/app/_translate (откуда взял не помню, но кода меньше чем в ng2-translate это во первых и в файлы переводов тоже идут как часть приложения в продакшене на angular cli, так что нет проблем с кешированием браузеров)
atnagulovid.myjino.ru — тут можно посмотреть как работает (приложение само ниочем, создается во время изучени я angular 2 )
rostman_rk
04.03.2017 17:53А как быть если словарь храниться в базе данных? Использовать еще 1 сервис для перевода из базы в файл?
Сам некогда искал способ, но ничего в то время не нашел. В итоге пришлось грузить вюхи из сервера (написаном на .Net), что несет за собой дополнительные запросы, но кэш работает на ура + при смене языка надо перегружать страницу, а сам язык ходил вместе с куки (так сервер знал что вернуть). И еще 1 минус такого подхода, требуется настройка для бандла, а использовал я SystemJs + Gulp
navix
05.03.2017 10:25Для xlf-формата есть много софта, который помогает мержить и обновлять файлы. Я использовал Virtaal и это было удобно, программа достаточно успешно вылавливала измененные фразы и показывала предыдущий вариант перевода.
Но у нативного i18n есть большие проблеми с plural, фраза заменяется на пустой тег, т.е нужно задавать дополнительный контекст или выдумывать что-то еще.
Если нужно получить фразу в коде, то можно использовать вот такой хак: забирать фразу из шаблона во время выполнения кода.
Сторонние либы неплохо справляются с задачами, ну и если нужен какой-то специфический функционал, то всегда можно реализовать его самому. При этом у меня есть уверенность, что нативная система будет доработана и ее использование не будет вызывать столько проблем.
elepner
05.03.2017 12:46Пробовал завести Angular2 i18n, все сыпалось с ужасными ошибками. Видимо, он еще не очень дружит с Angular Universal. Пришлось использовать старый, добрый, знакомый с первого Angular'a ng-translate. Кстати, не знаю у кого как, но вечные замечания от мереджеров/UX типа: «тут надо запятую поставить», «тут с большой буквы», «не хватает перевода, пожалуйста добавь» у меня вызывают нервный тик.
Поэтому я заэкстендил [translate] директиву, чтобы она добавляла маленькую кнопку к тексу, чтобы можно было перевести все и записать изменения прямо на сервер. Единственный недостаток, что при редеплое все изменения могут быть потеряны, но я всем объяснил, чтоб сохраняли *.json c переводами и отсылали его по почте.
Кстати, кому-нибудь интересен данный подход?
PS: естественно этот код даже не подключается в продакшне.
diomas
06.03.2017 17:160
,1
,2
,few
,other
и всё?
Куда-то затерялась еще одна категория:
many
. В арабском, например 5-ю категориями не обойтись.
lega
В Angular Light можно изящней сделать, вместо
например так: (текст без кавычек и «директива» одним символом), пример jsfiddle, при этом нагрузка на dirty-checking не идет в отличие от ангуляра (как минимум первого).