Skip to content

{{t-key}}

In JavaScript files (*.{gjs,gts,js,ts}), you can use tKey() to mark strings that are actually translation keys. This will help programs (e.g. linters like @ember-intl/lint, codemods) check how you use translations.

gts
import Component from '@glimmer/component';
import { t, tKey } from 'ember-intl';

type Option = {
  labelKey: string;
  value: string;
};

interface SelectLocaleSignature {
  Args: {};
}

export default class SelectLocale extends Component<SelectLocaleSignature> {
  get options(): Option[] {
    return [
      {
        labelKey: tKey('components.select-locale.option.de-de'),
        value: 'de-de',
      },
      {
        labelKey: tKey('components.select-locale.option.en-us'),
        value: 'en-us',
      },
    ];
  }

  <template>
    {{#each this.options as |opt|}}
      <option value={{opt.value}}>
        {{t opt.labelKey}}
      </option>
    {{/each}}
  </template>
}
gts
import { type Registry as Services, service } from '@ember/service';
import Component from '@glimmer/component';
import {
  PAYMENT_METHOD,
  PAYMENT_METHOD_TRANSLATION_KEYS,
} from 'my-app/utils/payment';

type Option = {
  label: string;
  value: string;
};

interface PaymentMethodSignature {
  Args: {};
}

export default class PaymentMethod extends Component<PaymentMethodSignature> {
  @service declare intl: Services['intl'];

  get options(): Option[] {
    return Object.values(PAYMENT_METHOD).map((paymentMethod) => {
      const key = PAYMENT_METHOD_TRANSLATION_KEYS[paymentMethod]!;

      return {
        label: this.intl.t(key),
        value: paymentMethod,
      };
    });
  }

  <template>
    {{#each this.options as |opt|}}
      <option value={{opt.value}}>
        {{opt.label}}
      </option>
    {{/each}}
  </template>
}
ts
import { tKey } from 'ember-intl';

// Values set by backend
export const PAYMENT_METHOD = {
  BANK_TRANSFER: 'bank_transfer',
  CREDIT_CARD: 'credit_card',
  DIRECT_DEBIT: 'direct_debit',
};

export const PAYMENT_METHOD_TRANSLATION_KEYS = {
  [PAYMENT_METHOD.BANK_TRANSFER]: tKey('payment-method.bank-transfer'),
  [PAYMENT_METHOD.CREDIT_CARD]: tKey('payment-method.credit-card'),
  [PAYMENT_METHOD.DIRECT_DEBIT]: tKey('payment-method.direct-debit'),
} as const;

NOTE

tKey() is an identity function. The input string and its type (string or a string literal) are unchanged.

What not to do

Don't pass a dynamic expression to tKey(). It's difficult and costly for programs to accurately find all possible values.

Refactor code so that you always pass strings or string literals to tKey().

gts
import { concat } from '@ember/helper';
import { t, tKey } from 'ember-intl';

const locales = ['de-de', 'en-us'] as const;

<template>
  {{#each locales as |locale|}}
    <option value={{locale}}>
      {{! ❌ Helpers like `concat` may be ignored }}
      {{t (tKey (concat "select-language." locale))}}
    </option>
  {{/each}}
</template>
gts
import { get } from '@ember/helper';
import { t, tKey } from 'ember-intl';

const locales = ['de-de', 'en-us'] as const;

const localeToKey = {
  'de-de': tKey('select-language.de-de'),
  'en-us': tKey('select-language.en-us'),
} as const;

<template>
  {{#each locales as |locale|}}
    <option value={{locale}}>
      {{t (get localeToKey locale)}}
    </option>
  {{/each}}
</template>
gts
import { array, hash } from '@ember/helper';
import { t, tKey } from 'ember-intl';

<template>
  {{#let
    (array
      (hash labelKey="select-language.de-de" value="de-de")
      (hash labelKey="select-language.en-us" value="en-us")
    )
    as |options|
  }}
    {{#each options as |opt|}}
      <option value={{opt.value}}>
        {{! ❌ Path expressions like `opt.labelKey` may be ignored }}
        {{t (tKey opt.labelKey)}}
      </option>
    {{/each}}
  {{/let}}
</template>
gts
import { array, hash } from '@ember/helper';
import { t, tKey } from 'ember-intl';

<template>
  {{#let
    (array
      (hash labelKey=(tKey "select-language.de-de") value="de-de")
      (hash labelKey=(tKey "select-language.en-us") value="en-us")
    )
    as |options|
  }}
    {{#each options as |opt|}}
      <option value={{opt.value}}>
        {{t opt.labelKey}}
      </option>
    {{/each}}
  {{/let}}
</template>