Как локализовать данные, поступающие из серверной части, с помощью ngx-translate

#angular #localization #internationalization #ngx-translate

#angular #локализация #интернационализация #ngx-translate

Вопрос:

Я создаю приложение angular с помощью ngx-translate. Это работает хорошо. Теперь я начинаю получать данные, поступающие из моего серверной части (firebase), для которых у меня будут переводы.

Каков правильный способ отображения локализованных данных из модели? Я еще не определился с форматом данных, поэтому либо это будет что-то вроде:

 {
  'title_fr': 'mon titre',
  'title_en': 'my title',
  'title_de': 'mein Titel',
  'description_fr' : '....'
  //...
}
  

либо что-то вроде:

 {
   title: {'fr':'mon titre', 'en': 'my title', 'de': 'mein Titel'}
   description: { 'fr': 'ma description', /*...*/}
}
  

и я не уверен, как их отобразить:

  1. У меня будет много ngFor итераций по наблюдаемым из этих элементов, если бы я мог избежать необходимости сопоставлять все данные, это было бы здорово
  2. при изменении языка мне также необходимо отобразить правильный перевод

Что бы вы мне порекомендовали?

Ответ №1:

Библиотека @ngx-translate/ http-loader позволяет загружать переводы с использованием http.

Ниже приведен пример того, как я использую ngx-translate along with http-loader в приложениях, которые я разрабатываю.

Я сделал рабочий блок-блок кода, приведенный ниже (доступен здесь):

Определение структуры данных для строк i18n

В этом примере для каждого языка есть URL-адрес, по которому данные перевода ( json ) могут быть получены в следующем формате:

 {
  welcome: "Welcome",
  subscription_msg: "Subscribe now"
}
  

Создание одного веб-сервиса для каждого поддерживаемого языка

Рассмотрим одно приложение, которое поддерживает 3 языка: en , pt и fr .

Обычно у меня есть один URL-адрес для каждого языка. Каждый URL-адрес возвращает json с key value pairs, где ключи — это метки, которые я использую во внешнем интерфейсе, а значения — это сообщения, которые я хочу показать пользователям на этом языке.

В этом примере я создал 3 макетных URL-адреса с переводами для каждого языка следующим образом:

Русский — Макет URL: https://run.mocky.io/v3/33f736b0-e73a-499e-8a50-01e66041d634

 {
  welcome: "Welcome",
  subscription_msg: "Subscribe now"
}
  

Португальский — Макет url: https://run.mocky.io/v3/db1e37da-342e-4918-8ce2-bd30aa12fe79

 {
  welcome: "Bem-vindo",
  subscription_msg: "Inscreva-se gratuitamente"
}
  

Французский — Макет url: https://run.mocky.io/v3/6960c960-ea66-42a0-87f1-f34568ecb740

 {
  welcome: "Bienvenue",
  subscription_msg: "Abbonez-vous"
}
  

Для вывода subscription_msg во внешнем интерфейсе я использую такой ngx-translate синтаксис:

 {{ 'subscription_msg' | translate}}
  

Установка @ngx-translate/http-loader

Предполагая ngx-translate , что он уже установлен, это команда для установки http-loader :

 npm install @ngx-translate/http-loader --save
  

Создание собственного загрузчика переводов

Нам нужно создать реализацию TranslationLoader интерфейса. У него есть только один метод ( getLanguage() ), который отвечает за загрузку карты сообщений i18n для текущего языка.

Ниже приведена наша простая реализация TranslationHttpLoader . Он вызывается при изменении языка:

 export class TranslationHttpLoader implements TranslateLoader {
  constructor(private httpClient: HttpClient) {}

  public getTranslation(lang: string): Observable<Object> {
    if (lang == null) {
      lang == "en";
    }

    let urls = {
      en: "https://run.mocky.io/v3/33f736b0-e73a-499e-8a50-01e66041d634",
      pt: "https://run.mocky.io/v3/db1e37da-342e-4918-8ce2-bd30aa12fe79",
      fr: "https://run.mocky.io/v3/6960c960-ea66-42a0-87f1-f34568ecb740"
    };

    let observer = new Observable(observer => {
      this.httpClient.get(urls[lang]).subscribe(
        data => {
          observer.next(data);
          observer.complete();
        }
      );
    });
    return observer;
  }
}
  

Настройка поставщика перевода

В app.module.ts мы должны определить HttpLoaderFactory :

 export function HttpLoaderFactory(httpClient: HttpClient) {
  return new TranslationHttpLoader(httpClient);
}
  

и связать его с предоставленным TranslateLoader :

 @NgModule({
  imports: [
    BrowserModule,
    FormsModule,
    HttpClientModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: HttpLoaderFactory,
        deps: [HttpClient]
      }
    })
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}
  

Тестирование — изменение языка и отображение локализованных сообщений

Для тестирования

  • мы создаем простой компонент, который имеет a <select> с языками в качестве опций и обработчик (change) событий.
  • для вывода локализованных строк мы используем ngx-translate синтаксис {{ 'key_in_json_file' | translate }} .

app.component.html

 <div>
    <label>Language</label>
    <select (change)="changeLanguage($event)">
    <option value="en">English</option>
    <option value="pt">Português</option>
    <option value="fr">Français</option>
  </select>

    <p>
        {{'welcome' | translate}}.
    </p>
    <p>
        {{'subscription_msg' | translate}}
    </p>
</div>
  

Когда (change) событие происходит, все, что нам нужно сделать, это вызвать translate.use(lang) . Наш загрузчик извлекет данные для выбранного языка из предоставленного URL-адреса и предоставит их ngx-translate :

app.component.ts

 export class AppComponent {
  defaultLang: string = "en";

  constructor(private translateService: TranslateService) {}
  ngOnInit() {
    this.translateService.use(this.defaultLang);
  }
  changeLanguage(event) {
    let lang = (event.target as HTMLInputElement).value;
    this.translateService.use(lang);
  }
}
  

Предварительный просмотр вывода

Предварительный просмотр выходных данных для 3 языков

Следующие шаги

Некоторые улучшения можно выполнить в приведенном здесь коде, например:

  • Определение языка браузера при первом доступе пользователя с помощью translateService.getBrowserLang()
  • Сохраните текущий язык пользователя в localstorage или другом постоянном хранилище.
  • Адаптируйте TranslationHttpLoader , чтобы он мог работать в автономном режиме с токенами по умолчанию для каждого языка (особенно полезно в мобильных приложениях).

Код, описанный выше, можно увидеть здесь, в этом стекблите: https://stackblitz.com/edit/angular-ivy-pwcrvy?file=src/app/translation-http-loader.ts

Комментарии:

1. Большое вам спасибо за ваш полный ответ, но я не уверен, что это относится ко мне. Я получаю данные из firebase, поэтому мне нужно использовать пакет ngfire для доступа к моим данным (он позволяет автоматически управлять синхронизацией между серверной частью и интерфейсом, позволяет работать в автономном режиме, а затем синхронизировать, …), поэтому я не могу использовать HttpLoader : (. Кроме того, я не уверен, что будет ключом? На самом деле речь идет о наличии локализованных бизнес-данных в моем бэкэнде, например, если бы у tripadvisor было локализованное описание.

Ответ №2:

Если вы можете определить данные, которые вы можете получить из своего серверной части, то, вероятно, вы можете создать структуру данных, как показано ниже,

 {
    "dataLocalization": [{
                            "locale": "fr",
                            "title": "mon titre",
                            "description":"ma description"
                        },
                        {
                            "locale": "en",
                            "title": "my title",
                            "description":"my description"
                        }]
}
  

Затем вы можете найти свой язык с помощью метода findIndex в массиве dataLocalization и использовать его значения title и description

Комментарии:

1. Проверьте этот способ, дайте мне знать, если у вас есть еще какие-либо запросы, если вы нашли это полезным, отметьте его как правильный и, пожалуйста, поддержите

2. Да, но как это изменится, если пользователь переключит язык

3. эти данные, полученные из вашей firebase, будут где-то храниться, верно? всякий раз, когда пользователь меняет язык в подписке на изменение, где вы можете использовать translate.use там, вам нужно снова проверить этот массив и изменить значения в соответствии с этим массивом

4. да, но каков наилучший способ обработки изменений из ngx-translate изменений, поступающих из firebase?