Search code examples
angulartypescripttranslateangular11ngx-translate

Translate two strings in different languages at the same time using @ngx-translate/core?


I am working on an Angular@11 application and facing an issue. In my app there are two panels, left and right. I want to translate the left panel in Spanish and the right one in German.

Using @ngx-translate/core, it will translate both panels in any one language at a time, but I want to translate both panels in different languages.

Any kind of help is appreciable.


Solution

  • You can achieve that by creating your own translate pipe, to use it instead of the default one provided by ngx-translte.

    The new custom translate pipe should depend on the same translations provided by TranslateService, and handle the most logic handled by the default translate pipe, but without handling the onLangChange, onDefaultLangChange, or onTranslationChange events.

    You can try something like the following:

    import {
      ChangeDetectorRef,
      Injectable,
      Pipe,
      PipeTransform,
    } from '@angular/core';
    import { isObservable } from 'rxjs';
    import { TranslateService } from '@ngx-translate/core';
    
    @Injectable()
    @Pipe({
      name: 'translateBy',
      pure: false, // required to update the value when the promise is resolved
    })
    export class TranslatePipe implements PipeTransform {
      value: string = '';
      lastKey: string;
      lastParams: any[];
    
      constructor(
        private translate: TranslateService,
        private _ref: ChangeDetectorRef
      ) {}
    
      updateValue(
        key: string,
        interpolateParams?: Object,
        translations?: any
      ): void {
        let onTranslation = (res: string) => {
          this.value = res !== undefined ? res : key;
          this.lastKey = key;
          this._ref.markForCheck();
        };
        if (translations) {
          let res = this.translate.getParsedResult(
            translations,
            key,
            interpolateParams
          );
          if (isObservable(res.subscribe)) {
            res.subscribe(onTranslation);
          } else {
            onTranslation(res);
          }
        } else this.translate.get(key, interpolateParams).subscribe(onTranslation);
      }
    
      transform(query: string, lang: string, ...args: any[]): any {
        if (!query || !query.length) {
          return query;
        }
    
        // if we ask another time for the same key, return the last value
        if (equals(query, this.lastKey) && equals(args, this.lastParams)) {
          return this.value;
        }
    
        let interpolateParams: Object;
        if (!!args[0] && args.length) {
          if (typeof args[0] === 'string' && args[0].length) {
            // we accept objects written in the template such as {n:1}, {'n':1}, {n:'v'}
            // which is why we might need to change it to real JSON objects such as {"n":1} or {"n":"v"}
            let validArgs: string = args[0]
              .replace(/(\')?([a-zA-Z0-9_]+)(\')?(\s)?:/g, '"$2":')
              .replace(/:(\s)?(\')(.*?)(\')/g, ':"$3"');
            try {
              interpolateParams = JSON.parse(validArgs);
            } catch (e) {
              throw new SyntaxError(
                `Wrong parameter in TranslatePipe. Expected a valid Object, received: ${args[0]}`
              );
            }
          } else if (typeof args[0] === 'object' && !Array.isArray(args[0])) {
            interpolateParams = args[0];
          }
        }
    
        // store the query, in case it changes
        this.lastKey = query;
    
        // store the params, in case they change
        this.lastParams = args;
    
        // set the value
        this.updateValue(
          query,
          interpolateParams,
          this.translate.translations[lang] // <<<<< This is the main requried change to return the translation for the provided lang.
        );
    
        return this.value;
      }
    }
    

    And in the component, you can use it like the following:

    <span>ES Value: {{ "MAIN_TITLE" | translateBy: "es" }}</span>
    <span>DE Value: {{ "MAIN_TITLE" | translateBy: "de" }}</span>
    

    P.S. The above custom pipe (and the original pipe also) uses the equals function to check if the args and lastParams are equal or not. You can copy the ngx-translate eqauls function and use it in your code, or you can use any other deeply equals functions (e.g. lodash isEqual)