Локализация
Язык интерфейса один на сессию, и выбирает его пользователь в настройках рутового приложения.
Рутовое приложение хранит выбор, переводит собственный интерфейс и меню и сообщает о смене языка
веб-компонентам. Веб-компонент переводит свой интерфейс сам — на Transloco (opens in a new tab)
(пакет @jsverse/transloco), а подключает переводы через provideQLocalization и withTransloco
из @diasoft/qpalette-visual: они берут язык из рутового приложения, настраивают Transloco, локаль
Angular и PrimeNG и заголовок Accept-Language в запросах.
Собственный модуль transloco-root.module.ts, который раньше создавался в каждом проекте, больше
не нужен — см. Устаревшее.
С чего начать
Положите переводы в src/assets/i18n/ своей сборки — по файлу на язык, ru.json и en.json.
Ключи — фразы на русском, поэтому ru.json может оставаться пустым; если ключи уникальные
("I18N_HOME_PAGE"), заполняются оба файла.
{
"Поиск": "Search",
"Конфигурирование поддерживаемых языков": "Configuring supported languages"
}В main.ts добавьте provideQLocalization с withTransloco, указав каталог переводов:
// main.ts
import { createApplication, provideQLocalization, withTransloco } from '@diasoft/qpalette-visual';
import { AppComponent } from './app/app.component';
(async () => {
const app = createApplication();
await app.bootstrap(AppComponent, [
// ...
provideQLocalization(
withTransloco({
translationsUrl: 'i18n' // каталог в ассетах вашей сборки
})
)
]);
})();В компонентах переводы используются штатными средствами Transloco; в standalone-компоненте директиву
*transloco и pipe transloco подключает импорт TranslocoModule:
import { Component } from '@angular/core';
import { TranslocoModule } from '@jsverse/transloco';
@Component({
selector: 'app-home',
imports: [TranslocoModule],
template: `
<ng-container *transloco="let t">
<h1>{{ t('Домашняя страница') }}</h1>
</ng-container>
`
})
export class HomeComponent {}При старте веб-компонент получит текущий язык рутового приложения и загрузит
/api/<сервис>/<компонент>/assets/i18n/<lang>.json — путь без ведущего assets/ ведёт в ассеты
вашей сборки, см. Адрес переводов: строка или функция.
Как это устроено
Список языков и язык по умолчанию задаёт рутовое приложение в assets/data/i18n/languages.json,
выбор пользователя оно хранит в настройках браузера. Веб-компонент при старте читает и то и другое
из общего состояния страницы — отдельного запроса за языком не нужно.
provideQLocalization на основании этих данных:
- регистрирует конфигурацию Transloco: список доступных языков, язык по умолчанию и текущий язык;
- устанавливает локаль Angular (
LOCALE_ID,registerLocaleData) и переводы PrimeNG; - подключает HTTP-перехватчик
LanguageInterceptor: к каждому запросу добавляется заголовокAccept-Languageс текущим языком; - регистрирует провайдеры
TranslocoModule.
withTransloco добавляет загрузчик переводов: для активного языка он запрашивает файл по адресу
из translationsUrl и передаёт его Transloco.
Смена языка в настройках рутового приложения страницу не перезагружает: рутовое приложение
переключает собственные переводы, заново запрашивает меню (assets/data/menu.<lang>.json, а если
его нет — assets/data/menu.json) и заново создаёт открытые вкладки. Веб-компонент во вкладке
стартует ещё раз уже с новым языком — подписываться на смену языка ему не нужно. Веб-компоненты,
которые вкладками не являются и не пересоздаются (например, плагины),
получают событие onLanguageChange и переключают переводы сами —
см. Переключать язык вслед за рутовым приложением.
Локализованные компоненты @diasoft/qpalette-visual (например, q-search-box) подгружают свои
переводы отдельно — из файлов assets/data/i18n/<lang>.json, которые отдаёт рутовое приложение, —
и объединяют их с переводами продукта. Настраивать для них ничего не нужно.
Настройка
Языки рутового приложения: languages.json
Файл rootapp/assets/data/i18n/languages.json задаёт язык по умолчанию и список языков, доступных
в настройках рутового приложения:
{
"default" : "ru",
"list" : [
{
"code" : "ru",
"name" : "Русский"
},
{
"code" : "en",
"name" : "English"
}
]
}Поддерживаются языки ru и en.
Меню на нескольких языках
При переключении языка рутовое приложение запрашивает меню assets/data/menu.<lang>.json; если файла
по этому адресу нет, загружается assets/data/menu.json. Чтобы меню переводилось, положите рядом
с menu.json его переводы: menu.en.json для английского.
Адрес переводов: строка или функция
translationsUrl принимает либо строку — каталог переводов, к которому загрузчик добавляет
/<lang>.json, либо функцию (language: string) => string, которая возвращает полный адрес файла
переводов для языка. Функция нужна, когда адрес зависит не только от языка — например, переводы
отдаёт сервер по своей схеме:
withTransloco({
translationsUrl: language => `/api/<service>/<component>/translations?lang=${ language }`
})Функция обязана вернуть строку, иначе загрузчик завершится ошибкой.
Строка разбирается по общему правилу адресов ассетов (Адреса собственных
ассетов): путь без ведущего
assets/ — каталог в ассетах вашей сборки, у веб-компонента это
/api/<сервис>/<компонент>/assets/…. Так переводы не зависят от имени пакета в адресе:
withTransloco({
translationsUrl: 'i18n' // → /api/<сервис>/<компонент>/assets/i18n/<lang>.json
})assets/…, путь от корня и полный URL используются как есть — assets/… браузер отсчитает от
корня сайта, то есть от ассетов рутового приложения. Если файл не нашёлся, ошибка загрузки
называет адрес, по которому его искали. Правило действует с 8.2.0.
Заголовок Accept-Language
LanguageInterceptor из @diasoft/qpalette-core подключается автоматически — регистрировать его
в HTTP_INTERCEPTORS не нужно. Единственное условие: HTTP-клиент должен принимать перехватчики из DI —
provideHttpClient(withInterceptorsFromDi()).
Как решать типовые задачи
Переключать язык вслед за рутовым приложением
Веб-компоненту во вкладке это не нужно: при смене языка рутовое приложение создаёт вкладки заново,
и он стартует с новым языком. Подписка нужна веб-компоненту, который рутовое приложение
не пересоздаёт, — например, плагину шапки или боковой панели. Подпишитесь на onLanguageChange
и передайте язык в Transloco:
// main.ts
import { inject, provideAppInitializer } from '@angular/core';
import { QEventsService } from '@diasoft/qpalette-visual';
import { TranslocoService } from '@jsverse/transloco';
await app.bootstrap(AppComponent, [
// ...
provideAppInitializer(() => {
const events = inject(QEventsService);
const transloco = inject(TranslocoService);
events.onLanguageChange().subscribe(data => {
transloco.setActiveLang(data.payload.language);
});
})
]);Так переключаются переводы Transloco. Локаль Angular (форматы дат и чисел) и переводы PrimeNG задаются при старте и по событию не меняются — они обновятся при следующем создании веб-компонента.
Менять язык из веб-компонента (QEventsService.changeLanguage) можно только в портальном режиме —
когда рутовое приложение скрыто и веб-компонент занимает всё окно. Во встроенном режиме языком
управляет рутовое приложение.
Получать адрес переводов извне: библиотечные проекты
Библиотечный проект не знает, где его разместит продукт-потребитель: его
файлы копируются в assets продукта, а собственного микросервиса у библиотеки нет. Жёстко заданный
адрес вида /api/<library-service>/<library-component>/assets/... ведёт на несуществующий микросервис,
и переводы не загружаются. Адрес переводов библиотека должна получать от потребителя — через
properties своего endpoint.
Конфигурация задаётся провайдером QTranslocoConfig с зависимостью QWebComponentProperties, в которой
лежат параметры, переданные библиотеке извне. Провайдер регистрируется после provideQLocalization
и переопределяет конфигурацию из withTransloco:
// main.ts библиотечного проекта
import {
createApplication,
provideQLocalization,
withTransloco,
QTranslocoConfig,
QWebComponentProperties
} from '@diasoft/qpalette-visual';
const DEFAULT_TRANSLATIONS_URL = 'i18n'; // ассеты своей сборки, когда библиотека запущена сама
const app = createApplication();
await app.bootstrap(AppComponent, [
// ...
provideQLocalization(withTransloco({ translationsUrl: DEFAULT_TRANSLATIONS_URL })),
{
provide: QTranslocoConfig,
useFactory: (properties: QWebComponentProperties): QTranslocoConfig => ({
translationsUrl: properties?.translationsUrl || DEFAULT_TRANSLATIONS_URL
}),
deps: [QWebComponentProperties]
}
]);Потребитель передаёт адрес в properties при монтировании библиотеки:
const endpoint: QEndpoint = {
service: '<service>',
component: '<component>',
route: '',
properties: {
// каталог переводов внутри скопированного в assets пакета библиотеки
translationsUrl: '/api/<service>/<component>/assets/<library>/<путь до i18n внутри пакета>'
}
};Имя свойства (translationsUrl в примере) — часть контракта библиотеки: опишите его в README.md
пакета вместе с остальными properties.
Брать переводы компонентов Q.Palette из другого каталога
По умолчанию компоненты @diasoft/qpalette-visual читают переводы из assets/data/i18n/<lang>.json
рутового приложения. Если они должны браться из другого каталога, добавьте в провайдеры
...provideVisualLocalization('<каталог>') — файл <каталог>/<lang>.json объединится с переводами
продукта.
Ограничения и типичные ошибки
- Вместо перевода на экране ключ с приставкой языка —
en.Отмена. Активный язык не входит в список доступных, и Transloco считает его не языком, а scope'ом. Начиная с 8.1.0provideQLocalizationдобавляет текущий язык в список сам; в старых версиях — проверьтеavailableLangsв конфигурации Transloco. - В консоли
Не удалось получить файл с переводами по URL: <адрес>. Файл по итоговому адресу недоступен; по адресу видно, из чего он собрался. Для библиотечного проекта, если адрес ведёт в микросервис самой библиотеки, — см. Получать адрес переводов извне. - Ошибка
Неизвестный язык. Вlanguages.jsonуказан язык, кромеruиen; другие языки не поддерживаются. - Плагин не переключается вслед за рутовым приложением. Плагин, в отличие от вкладки, при смене
языка не пересоздаётся — ему нужна подписка на
onLanguageChange, см. Переключать язык вслед за рутовым приложением. - После смены языка даты и числа в прежнем формате. Локаль Angular задаётся при старте веб-компонента и по событию не меняется; обновится при следующем его создании.
changeLanguageне действует. Из веб-компонента язык меняется только в портальном режиме.- Переводы грузятся не по тому адресу или не на том языке. В проекте одновременно
provideQLocalizationи старыйtransloco-root.module.tsс собственными загрузчиком и конфигурацией; какой из них окажется активным, зависит от порядка провайдеров. Удалите модуль — см. Устаревшее.
Справочник
Всё из @diasoft/qpalette-visual, если не указано иное.
provideQLocalization(...features): EnvironmentProviders
Настраивает локализацию веб-компонента по языку рутового приложения. Принимает одну или несколько
фич; пока единственная — withTransloco.
withTransloco(config: QTranslocoConfig)
| Поле | Тип | Описание |
|---|---|---|
translationsUrl | string | ((language: string) => string) | Строка — каталог переводов, файл <каталог>/<lang>.json; функция — полный адрес файла для языка |
QTranslocoConfig можно переопределить провайдером после provideQLocalization — см.
библиотечные проекты.
provideVisualLocalization(translationsUrl = 'assets/data/i18n'): Provider[]
Переводы компонентов @diasoft/qpalette-visual: загружает <translationsUrl>/<lang>.json и объединяет
с переводами активного языка. По умолчанию уже подключено компонентами.
QEventsService
| Метод | Что делает |
|---|---|
onLanguageChange(): Observable<QLanguageChangeContract> | Событие смены языка рутового приложения; язык — в payload.language |
changeLanguage(language: string): void | Просит рутовое приложение сменить язык; только в портальном режиме |
LanguageInterceptor (@diasoft/qpalette-core)
HTTP-перехватчик, добавляющий заголовок Accept-Language с текущим языком. Подключается
provideQLocalization, требует provideHttpClient(withInterceptorsFromDi()).
languages.json
| Поле | Тип | Описание |
|---|---|---|
default | 'ru' | 'en' | Язык по умолчанию |
list | { code: string; name: string }[] | Языки, доступные в настройках рутового приложения; name — подпись в списке |
Совместимость версий
| Версия | Что изменилось |
|---|---|
| 8.1.0 | Текущий язык и язык по умолчанию всегда входят в список доступных — ключи вида en.Отмена больше не появляются; Transloco в производственной сборке работает в производственном режиме |
| 7.5.1 | provideQLocalization и withTransloco; собственный transloco-root.module.ts больше не нужен |
Устаревшее
transloco-root.module.ts — собственный модуль локализации веб-компонента: загрузчик переводов
с методом getTranslation, конфигурация translocoConfig со списком языков, подписка на
onLanguageChange и ручная регистрация LanguageInterceptor. Всё это делает provideQLocalization,
а модуль остался в проектах как наследие.
Держать оба способа одновременно не стоит: модуль регистрирует собственные конфигурацию и загрузчик Transloco, и какой из них окажется активным, зависит от порядка провайдеров.
Удаление вручную:
- Подключите локализацию, как описано в разделе С чего начать:
provideQLocalization(withTransloco({ translationsUrl }))с адресом каталога переводов, который раньше был указан вgetTranslation. - Подписку модуля на
onLanguageChangeвеб-компоненту во вкладке переносить не нужно — рутовое приложение пересоздаёт вкладки при смене языка. Плагину перенесите её вprovideAppInitializer— см. Переключать язык вслед за рутовым приложением. - Уберите
TranslocoRootModuleизimportsкорневого модуля или изimportProvidersFrom(...), а также ручную регистрациюLanguageInterceptorвHTTP_INTERCEPTORS,LOCALE_IDи вызовыregisterLocaleData— их выполняетprovideQLocalization. - Удалите файл
transloco-root.module.ts.
Мигратор qpalette migrate при переходе на 8.x (см. Миграция на 8.x)
переводит такой модуль на пакет @jsverse/transloco и регистрирует в нём provideTransloco
с языками ru и en, чтобы старый способ продолжил работать после обновления. Сам модуль он
не удаляет — переход на provideQLocalization и удаление файла выполняются вручную по шагам выше.