Frontend Wolves

Custom

 Translations

Engine

You may ask what about...

Translations structure

Interpolations

Performance

Loading translations sources

Overall complexity

Motivation

Different sources

Translations @ runtime

Lazy loading

Technical independence

Facade

Configuration

Language

Language Discovery

Helpers

Translation Provider

Storage

Managers

Loader

Pipes

Directives

namespace

translations table

key description

app.
main.
xxx.yyy.zzz

Translations structure

- translations/
  - app/
    - main/
      en.json 
      pl.json
    - admin/
      en.json
      pl.json
<p translate="app.main.xxx.yyy.zzz"> ... </p>
<p translate="app.main.xxx.yyy.zzz || app.main.globals.xxx.yyy.zzz"> ... </p>

fallback notation

App Initializer

export function appInitializerFactory( facadeService: FacadeService) {

   // eager translation loading
   const defaultTranslationTable = require('../../translations/app/main/en.json');

   // bootstrap engine
   return () => facadeService.bootstrap( defaultTranslationTable );
  };

...

@NgModule({
  ...
  providers: [FacadeService,
    { provide: APP_INITIALIZER, useFactory: appInitializerFactory, deps [...]. multi: true}]
  }
})

1

2

Loading Translations Sources

loadTranslationsUsingWebpack(namespace, table, lang) {
  return import(/* webpackMode: "lazy" */
  `../../../../translations/${namespace}/${table}/${lang}.json`);
}

loadTranslationsUsingHttpClient(namespace, table, lang) {
  return this.httpClient.get(`{$apiUrl}/translations/${namespace}/${table}/${lang}`);
}
@Injectable()
export class TranslationService {
  // ...
  translate(
    translationKey: string, contextValues: object = {},
    languageId: string = this.languageService.getCurrentLanguage(),
    fallbackTranslationKey?: string) {

    // key validation
    if (!this.isTranslationKeyValid(translationKey)) {
      return translationKey;
    }

    // caching
    const cachedValue = this.getFromCache(translationKey, languageId, contextValues);
    if (cachedValue) return cachedValue;

    // get namespace, table and key from string
    const destructurizedTranslationKey = this.destructurizeTranslationKey(translationKey);
    
    // identify translationTable - current language or default language
    const translationTableToUse = this.identifyTranslationTableToUse(destructurizedTranslationKey);

    ...

Translation Service

1

2

3

4

    ...

    // do the magic!
    if (translationTableToUse.hasOwnProperty(translationKey)) {
      
      // handle simple interpolations
      result = Mustache.render(translationTableToUse[translationKey], contextValues);

    } else {
      
      // handle fallback
      if (typeof fallbackTranslationKey === 'string') {
        return this.translate(fallbackTranslationKey, contextValues, languageId);
      }
      
      // log missing key
      this.logMissingKeys() && console.warn(`Missing translation for '${translationKey}'`);

      result = translationKey;
    }
    
    // store in cache in proper context
    this.storeInCache(translationKey, languageId, contextValues, result);
   
    return result;
  }
}

5

6

8

7

Wasn't it easy? :-)

linkedin.com/in/lukasz-max-kokoszka

twitter.com/rafal_brzoska

linkedin.com/in/rafalbrzoska

Angular Internationalization

By Rafał Brzoska

Angular Internationalization

  • 575