Angular $localize using dynamic translation ID
Asked Answered
H

3

11

With new Angular 9 @angular/localize is now possible to translate code directly from typescript. As its usage is not officially well documented, I found some tips on this post.

$localize`:@@my-trans-unit-id:` // IT WORKS

That works correctly when ID is directly passed to the function, but if I want ID to be dynamic (and pass a variable), it does not work, rendering ID without parsing nor translating.

I tried it by passing the variable this way:

const id = "my-trans-unit-id";

$localize`:@@${id}:`; // NOT WORKING
$localize`:@@`+id+`:`; // NOT WORKING
Handel answered 31/3, 2020 at 15:6 Comment(1)
I'm in the same situation.Letter
H
10

Angular does not provide any mechanism to generate dynamic translations as they are generated at compile time.

I ended up creating pipes and calling them every time I need a translation. Instead of using 1 unique instruction to translate the string, I use multiple $localize calls inside a switch to return proper translation by ID.

This is an example for translating order status that can be called on runtime:

import { Pipe, PipeTransform } from '@angular/core';
import { OrderStatusEnum } from 'installation-status.enum';

@Pipe({
    name: 'orderStatusRenderer'
})
export class OrderStatusRendererPipe implements PipeTransform {
    constructor() {}

    transform(value: number, ...args: any[]): any {
        switch (value) {
            case OrderStatusEnum.PREPARING:
                return $localize`:@@order.status.preparing:`;
            case OrderStatusEnum.SHIPPED:
                return $localize`:@@order.status.shipped:`;
            case OrderStatusEnum.COMPLETED:
                return $localize`:@@order.status.completed:`;
        }
    }
}
Handel answered 8/5, 2020 at 13:15 Comment(0)
A
9

This works.

Don't ask me to explain, I got this by trial and error:

const localize = $localize;
const id = "my-trans-unit-id";
const translation = localize(<any>{ '0': `:@@${id}:${id}`, 'raw': [':'] });
Avril answered 20/8, 2020 at 16:10 Comment(2)
Interesting solution, but unfortunately I've got the error: "TypeError: Cannot create property 'message' on string [...]: Unexpected messageParts for $localize (expected an array of strings).", using ng extract-i18n with Angular 12.2.8Patina
Looking at this issue => github.com/angular/angular/issues/35376. I think It may be caused by some optimization routine from angular cli. Can you try with the edited solution and check if the generated outputs are correct? (assigning $localize to a constant first and then using it)Avril
A
1

You can create your own tagged templates function to handle transformation of the trans-unit id:

const transUnitId = '@@Messages.Greeting';
const name = 'Joe';
const message = $localizeId`${transUnitId}:TRANSUNITID:Hi ${name}:NAME:, translated with run-time created trans-unit id.`;
// Original
// message = Hi Joe, translated with run-time created trans-unit id.
// German
// message = Hallo Joe, übersetzt mit einer zur Laufzeit erstellten Trans-Unit-Id.
export function $localizeId(messageParts: TemplateStringsArray, ...expressions: any[]): string {
  // Create writeable copies
  const messagePartsCopy: any = [...messageParts];
  const messagePartsRawCopy = [...messageParts.raw];

  // Strip trans-unit-id element
  const prefix = messagePartsCopy.shift();
  const prefixRaw = messagePartsRawCopy.shift();
  const transUnitId = expressions.shift();

  // Re-add prefix and replace :TRANSUNITID: with transUnitId.
  messagePartsCopy[0] = prefix + messagePartsCopy[0].replace(':TRANSUNITID:', `:@@${transUnitId}:`);
  messagePartsRawCopy[0] = prefixRaw + messagePartsRawCopy[0].replace(':TRANSUNITID:', `:${transUnitId}:`);

  // Create messageParts object
  Object.defineProperty(messagePartsCopy, 'raw', {value: messagePartsRawCopy});

  // Call original localize function
  return $localize(messagePartsCopy as TemplateStringsArray, ...expressions);
}
Antibody answered 25/1, 2021 at 7:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.