Asynchronously loading translations with ember-intl

1k Views Asked by At

I'm trying to achieve asynchronous fetching of translations. I set up the publicOnly to true as docs say:

// config/ember-intl.js
module.exports = function() {
  return {
    publicOnly: true
  }
};

Skipped the step of setting up the locales key, because translations are stored in /translations folder.

Next, I should modify beforeModel hook to asynchronously fetch the translations, and that's where docs are pretty vague:

// app/routes/application.js
export default Ember.Route.extend({
  intl: Ember.inject.service(),
  async beforeModel() {
    let translations = await fetch('/api/translations.json');
    this.get('intl').addTranslations('en-us', translations);
    this.get('intl').setLocale('en-us');
  }
});

How these lines supposed to work:

let translations = await fetch('/api/translations.json');
this.get('intl').addTranslations('en-us', translations);

In runtime I have no translations.json file anywhere in the dist folder. I've got dist/translations/en-us.json only for my one and only translation and no clue how to make it work.

Service API misses documentation of addTranslations method.

Would appreciate any help.

2

There are 2 best solutions below

3
NullVoxPopuli On

This is what I was using back when I was doing async translations (and I may bring this back as my application grows)

This is in services/intl.ts

import IntlService from 'ember-intl/services/intl';
import fetch from 'fetch';

import config from 'emberclear/config/environment';

export default class EmberClearIntl extends IntlService {
  async loadTranslations(locale: string) {
    let response = await fetch(`${config.hostUrl}/translations/${locale}.json`);
    let translations = await response.json();

    this.addTranslations(locale, translations);
  }

  lookup(key: string, localeName: string, options: any = {}) {
    const localeNames = this.localeWithDefault(localeName);
    const translation = this._adapter.lookup(localeNames, key);

    if (!options.resilient && translation === undefined) {
      const missingMessage = this._owner.resolveRegistration('util:intl/missing-message');

      return missingMessage.call(this, key, [localeNames]);
    }

    return translation;
  }
};

I believe it's inspired by one of the github issues on the ember-intl repository, and I modified it to work with my setup. (like, the config.hostUrl stuff -- this is just set to my domain at the moment, but it may be handy if your site isn't deployed at the root of your domain).

Usage in my application route:

import Route from '@ember/routing/route';
import { service } from '@ember-decorators/service';

import EmberClearIntl from 'emberclear/services/intl';

export default class ApplicationRoute extends Route {
  @service intl!: EmberClearIntl;

  async beforeModel() {
    const locale = 'en-us';

    await this.intl.loadTranslations(locale);

    this.intl.setLocale(locale);
  }
}

Something I haven't yet figured out is how to best manage async translations with a progressive web app. In the current version of my app, I've removed the async behavior and just bundle the translation file (just one) with my app.

0
NormanHaze On

You're right that the Docs aren't clear - the response object isn't the JSON itself. Change the path for the fetch and add translations.json() instead of just translations:

// app/routes/application.js
export default Ember.Route.extend({
  intl: service(),
  async beforeModel() {
    let translations = await fetch('/translations/en-us.json');
    this.intl.addTranslations('en-us', translations.json());
    this.intl.setLocale('en-us');
  }
});