Search code examples
angularngx-translate

How to translate a key inside another translation in ngx-translate


I have a translation JSON file and I want to translate another translation inside the value of a different translation.

{
    "COMPANY_NAME": "Apple",
    "WELCOME_TEXT": "{{ COMPANY_NAME }} welcomes you to California!"
}

I can't see how to do this using ngx-translate in Angular 9, can anyone give me a pointer?


Solution

  • I managed to achieve this by implementing a custom TranslateCompiler as follows:

    My app.module.ts:

    // ...
    imports: [
      TranslateModule.forRoot({
        compiler: { provide: TranslateCompiler, useClass: CustomTranslationCompiler }
      })
    ]
    /// ...
    

    My CustomTranslationCompiler.ts:

    import { TranslateCompiler } from '@ngx-translate/core';
    
    export class CustomTranslationCompiler implements TranslateCompiler {
      /**
       * This function is needed to implement the interface, but doesn't
       * actually seem to be used anywhere
       *
       * @param value The translation value
       * @param lang The current language
       */
      public compile(value: string, lang: string): string | Function {
        return value;
      }
    
      /**
       * Look at every translation and pre-translate any nested translation keys within them
       *
       * @param translations All of the translations for the app to be compiled
       * @param lang The current language
       */
      public compileTranslations(translations: any, lang: string): any {
        for (const key in translations) {
          if (translations.hasOwnProperty(key)) {
            translations[key] = this.translateNestedTranslation(translations[key], translations);
          }
        }
    
        return translations;
      }
    
      /**
       * Use a regex to search for and replace translations inside translations
       * with their translated value
       *
       * @param value The translation value
       * @param translations All of the translations for the app
       */
      private translateNestedTranslation(value: string, translations: Object): string {
        const searchRegex  = /{{\s([A-Z_:]*)\s?}}/g;
        const replaceRegex = /({{\s?[A-Z_:]*\s?}})/g;
    
        const matches = searchRegex.exec(value);
        if (matches && matches.length > 0) {
          const searchKey = matches[1];
    
          if (translations.hasOwnProperty(searchKey)) {
            // Replace the full translate syntax with the translated value
            value = value.replace(replaceRegex, translations[searchKey]);
          } else {
            // If we can't find the value, display only the missing key instead of the full translate syntax
            value = value.replace(replaceRegex, searchKey);
            console.error(`Error: Unable to find translation '${searchKey}'!`)
          }
        }
    
        return value;
      }
    }
    
    

    Some notes:

    • With this approach any translation parameters defined in the translation values must be in lowercase to not be matched by the search regex
    • The search and replacement regexes are different
    • I'm not sure why the compile() method is never called. My translations are arriving as an object so maybe that's why...