Home | Send Feedback | Share on Bluesky |

Angular i18n with Transloco

Published: 11. August 2025  •  angular

In the previous blog post we have seen how to implement i18n in Angular applications using the built-in i18n support. In this blog post, we will explore the Transloco library for internationalization.

The main difference between these two approaches is that Angular's built-in i18n is a compile-time solution, while Transloco provides a runtime solution. This means that with Angular's i18n support, you compile your application with the translations included, whereas with Transloco, you can load and switch translations on the fly.

Here a comparison of the two approaches:

Built‑in compile‑time i18n with @angular/localize


Runtime libraries (e.g., Transloco, @ngx-translate/core)

Setup

The recommended way to install Transloco is adding the following schematics

ng add @jsverse/transloco

The command asks you for a list of locales to be added to your project. You can enter multiple locales comma-separated.

The command then installs the @jsverse/transloco runtime dependency and for each locale, it creates a separate translation file in src/assets/i18n. For example, if you enter en,de, it will create src/assets/i18n/en.json and src/assets/i18n/de.json.

Additionally, the add command creates the following files:

Lastly, the command adds the following code to the appConfig. In a default Angular application, this is typically found in src/app/app.config.ts.

    provideTransloco({
      config: {
        availableLangs: ['en', 'de'],
        defaultLang: 'en',
        // Remove this option if your application doesn't support changing language in runtime.
        reRenderOnLangChange: true,
        prodMode: !isDevMode()
      },
      loader: TranslocoHttpLoader
    }),

app.config.ts

The default language (defaultLang) is the primary language of your application. When the application first loads, and there is no other language specified (stored preference), this language will be used. The default language also serves as the fallback language if other languages can't be loaded.

availableLangs: This is an array of all the languages that your application supports. The default language must be included in this list.

reRenderOnLangChange: If your application does not support changing the language dynamically (dropdown or similar), set this option to false. Transloco will then render the view once and unsubscribe from the language change event, which can save a bit of memory.

prodMode: If set to false, Transloco will print warning messages to the browser console. true disables these messages.

There are more config options available. Check out the Transloco documentation for a complete list.

For more information about the installation process check out this documentation page

Usage in template

Transloco provides three ways how to configure translation in templates.

Structural Directive

The structural directive is the recommended approach as it is efficient. It creates a single subscription per template, and it is memoized, meaning it caches translation results for better performance.

The *transloco directive can be placed on any element. The structural directive exposes a function that needs be assigned to a variable, in this example t. You can then call this function with the translation key as an argument.

<p *transloco="let t">
  {{ t('introText') }}
</p>

home.html

You can also use the ng-container to wrap multiple elements, without changing the structure of your HTML:

<ng-container *transloco="let t">
  <h1>{{ t('homeTitle') }}</h1>
</ng-container>

home.html

The keys must correspond to the keys defined in your JSON translation files.

  "homeTitle": "Welcome to the i18n Showcase",
  "introText": "This page demonstrates the built-in internationalization features of Angular. Explore text, attributes, ICUs (plural/select), and date/number formatting.",

en.json


The structural directive also works with attributes.

<input
  *transloco="let t"
  [placeholder]="t('searchPlaceholder')"
  [attr.aria-label]="t('searchAriaLabel')" />

home.html


If there are placeholders in your translations, you need to pass as a second argument an object with the values.

  "greeting": "Hello, {{name}}",

en.json

<p *transloco="let t">
  {{ t('greeting', { name }) }}
</p>

home.html

Ensure that the names match. In this example the component instance variable has the same name (name) as the placeholder name used in the translation JSON file, therefore, the example uses the shorthand property format.


The translation can also contain HTML code. In this case, you need to use the innerHtml binding.

<p *transloco="let t" [innerHtml]="t('learn', { name })"></p>

home.html

  "learn": "Hi {name}. Learn more in the <a href=\"https://jsverse.gitbook.io/transloco\" target=\"_blank\" rel=\"noopener\">Transloco documentation</a>."

en.json


Transloco allows you to group translations with nested objects in the JSON file.

  "fruits": {
    "apples": "Apples",
    "bananas": "Bananas",
    "cherries": "Cherries"
  },

en.json

The key for the translations is fruits.apples, fruits.bananas, and fruits.cherries. To eliminate repetition, you can use the prefix option to get translations from nested properties.

<ng-container *transloco="let t; prefix: 'fruits'">
  <p>{{ t('apples') }}</p>
  <p>{{ t('bananas') }}</p>
  <p>{{ t('cherries') }}</p>
</ng-container>

Pipe

The second way how to use Transloco is by using the pipe.

<button [title]="'detailsButtonTitle' | transloco">
  {{ 'detailsButton' | transloco }}
</button>

home.html

You can use the pipe with parameters for placeholders as well.

<p>
  {{ 'greeting' | transloco: { name } }}
</p>

home.html


Attribute Directive

The third way to configure translations is by using the attribute directive transloco.

<p transloco="introText"></p>

home.html

You can also pass an object for replacing placeholders.

<p transloco="greeting" [translocoParams]="{ name }"></p>

home.html

Usage in code

Because Transloco loads the translation JSON files during runtime, you can't access translations synchronously in your component code.

One way to access translations in the component code is with the translateSignal and translateObjectSignal functions. These functions return a signal that will emit the translation whenever the JSON file has been loaded or the user changes the language. The method translateObjectSignal can be used to load a whole nested object from the translation JSON file. Each key in the nested object is then accessible.

    const home = translateSignal('homeTitle');
    const greeting = translateSignal('greeting', { name: this.name });
    const fruits = translateObjectSignal('fruits');

    effect(() => {
      console.log('Home title:', home());
      console.log('Greeting:', greeting());
      console.log('Fruits:', fruits()?.['apples']);
    });

home.ts


Another way to access translations in the component code is with the TranslocoService.

First, inject this service like any other service.

  private readonly translocoService = inject(TranslocoService);

home.ts

The application can then call the selectTranslate or selectTranslateObject method to get a translation for a specific key. These methods return an observable.

    this.translocoService
      .selectTranslate('homeTitle')
      .subscribe(translation => console.log('Home title:', translation));

    this.translocoService
      .selectTranslate('greeting', { name: this.name })
      .subscribe(translation => console.log('Greeting:', translation));

    this.translocoService
      .selectTranslateObject('fruits')
      .subscribe(translations => {
        this.items = [
          translations.apples,
          translations.bananas,
          translations.cherries
        ];
      });

home.ts

Like the signal variants, these two methods will emit the translation whenever a new language is selected or loaded.


I said at the beginning that you can only access the translation asynchronously. That's not exactly true because the TranslocoService also provides methods to get translations synchronously: translate and translateObject.

const homeTitle = this.translocoService.translate('homeTitle');
const greeting = this.translocoService.translate('greeting', { name: this.name });
const fruits = this.translocoService.translateObject('fruits');

The problem with these methods is that you need to be sure that the translation JSON file has been loaded before you can call these methods. The default behavior of translate is to return the key itself, when Transloco can't find the key in the JSON file or the file is not loaded yet. translateObject returns undefined in this case.

One way to make sure that the JSON file has been loaded is by subscribing to the events$ observable of the TranslocoService. This observable emits events whenever the translation state changes, such as when a new language is loaded or when a translation fails to load.

    this.translocoService.events$.subscribe((event: TranslocoEvents) => {
      console.log('Event received:', event);
      switch (event.type) {
        case 'langChanged':
          console.log('Language changed to:', event.payload);
          break;
        case 'translationLoadFailure':
          console.log('Translation changed for:', event.payload);
          break;
        case 'translationLoadSuccess':
          console.log('Translation loaded for:', event.payload);
          const ht2 = this.translocoService.translate('homeTitle');
          console.log('Sync Home title after load:', ht2);
          break;
        default:
          console.warn('Unhandled event:', event);
      }
    });

home.ts

Another way is to call the load method that loads the translation JSON file for the specified language. Inside the subscription you can call the synchronous translate and translateObject methods.

 translocoService.load('en').subscribe(() => {
	 const homeTitle = this.translocoService.translate('homeTitle');
	 console.log('Home title:', homeTitle);
   });

Besides the methods to fetch translations the TranslocoService also provides methods to fetch the default and active language. The service also provides a method to fetch a list of all available languages, for displaying a language switcher, for example. Check out the documentation for an overview of all available methods.

Language Switching

Implementing a language switcher is straightforward. In this example application I use two buttons that switch the language between English and German.

<div class="lang-switch" role="group" aria-label="Language switch">
  <button
    type="button"
    (click)="setLang('en')"
    [disabled]="activeLang === 'en'">
    EN
  </button>
  <button
    type="button"
    (click)="setLang('de')"
    [disabled]="activeLang === 'de'">
    DE
  </button>
  <span class="sr-only">Current: {{ activeLang | uppercase }}</span>
</div>

home.html

The method getActiveLang of the TranslocoService returns the currently active language.

  activeLang = this.translocoService.getActiveLang() || 'en';

home.ts

To switch to another language, an application has to call the setter method setActiveLang and Transloco will automatically load the corresponding translation file (if not already cached or loaded) and then refresh the application with the new language.

  setLang(lang: 'en' | 'de') {
    if (lang === this.activeLang) {
      return;
    }
    this.translocoService.setActiveLang(lang);
  }

home.ts

Another useful method is getAvailableLangs, which returns a list of all available languages. This method can be useful when you need to support a lot of languages, and you want to show a combo box for language selection.

	const availableLangs = this.translocoService.getAvailableLangs();

Loading

Because Transloco loads the translation files asynchronously over HTTP, it can take some time for the translations to be available. Problems could arise if the user already sees the application before the translation is loaded. To prevent any issues, you can configure a loading template that Transloco displays until the translation is ready.

You can use the provideTranslocoLoadingTpl function to define a loading template. If you put this into the app.config.ts file it will be applied to the whole application.

    provideTranslocoLoadingTpl('<p>Loading...</p>'),

app.config.ts

The structural directive *transloco also provides a way to show a loading template while the translations are being fetched. This gives you more control over the loading experience.

<ng-container *transloco="let t; loadingTpl: loading">
  <h1>{{ t('title') }}</h1>
</ng-container>

<ng-template #loading>
  <h1>Loading...</h1>
</ng-template>

Key Referencing

Transloco allows you to reference keys within other keys in the same JSON file. This feature uses the same syntax as variable interpolation: {{other_key}}.

  "greeting": "Hello, {{name}}",
  "greetingWithTime": "{{greeting}}. The current time is {{time}}.",

en.json

This feature also works with placeholder values. When code references the greetingWithTime key it needs to pass the time and the name parameters. Transloco will automatically resolve the greeting translation and pass the name parameter.

<p transloco="greetingWithTime" [translocoParams]="{ name: 'John', time: currentTime }"></p>

or in code

    this.translocoService.selectTranslate('greetingWithTime', { name: this.name, time: this.currentTime })
      .subscribe(translation => console.log('Greeting with Time:', translation));

Plugins

The Transloco library provides several plugins that extend its functionality. These plugins introduce support for ICU expressions, save translations and languages in the browser's local storage, and help with preloading translation JSON files. All these plugins need to be installed separately, they are not part of the core library.


Message Format

This plugin introduces ICU expressions for advanced message formatting.

Install the plugin with:

npm install @jsverse/transloco-messageformat

To enable the plugin, add this line to the providers array. Either globally in the app.config.ts file or in a specific component where you want to use it.

    provideTranslocoMessageformat(),

app.config.ts

After installing and enabling the plugin, you can use expressions like this in your translation files

  "itemsInfoRule": "You have {itemCount, plural, =0 {no items} =1 {one item} other {more than one item}} in your cart.",
  "genderSelectRule": "{gender, select, male {He} female {She} other {They}} added a new comment.",
  "nestedIcu": "{unreadCount, plural, =0 { {{genderMessageRule}} no unread messages} =1 { {{genderMessageRule}} one unread message} other { {{genderMessageRule}} {unreadCount} unread messages}}",
  "genderMessageRule": "{gender, select, male {He has} female {She has} other {They have}}",

en.json

When you reference these translations, you always need to specify a parameter, and it must match the name in the expression. For example, the itemsInfoRule translation uses a plural ICU expression and the first parameter in this expression itemCount must be provided when referencing the translation.

<p transloco="itemsInfoRule" [translocoParams]="{ itemCount }"></p>

home.html

Like normal translation keys, you can also reference other keys in an ICU expression. In the example above the nestedIcu key references the genderMessageRule key.

Check out the documentation for more in-depth information about this plugin.


Persist Translation

This plugin provides a way to persist translations in the browser's local storage or IndexedDB. With this plugin active users only have to download the translations once, and they will be cached for future visits.

Install it with this command:

npm install @jsverse/transloco-persist-translations

Then configure it

provideTranslocoPersistTranslations({
  loader: TranslocoHttpLoader,
  storage: { useValue: localStorage },
  ttl: 86_400, // Cache duration in seconds (1 day)
  storageKey: 'custom-translations-key',
});

This plugin also provides a service that can be used for clearing the cached translations

  private readonly translationsCache = inject(TranslocoPersistTranslations);

  clearTranslationsCache() {
    this.translationsCache.clearCache();
  }

See documentation for more information.


Persist Language

Similar to the translation persistence, this plugin persists the selected language in a specific storage.

Whenever the user changes the language, this plugin will store the current language in the provided storage. When the user visits the application again, the plugin checks if there is a cached language available. If yes, it will use the cached language as the active language, otherwise it falls back to the default language.

npm install @jsverse/transloco-persist-lang
provideTranslocoPersistLang({
      storage: {
        useValue: localStorage,
      },
    }),

Documentation.


Preload Languages

This plugin preloads the specified languages during browser inactivity. Internally it uses the requestIdleCallback API to schedule the loading of translations when the browser is idle.

This plugin can be helpful if you know that a lot of people will switch the language. When the language JSON file is preloaded the application can switch to the new language without any delay.

npm install @jsverse/transloco-preload-langs
  providers: [
    provideTranslocoPreloadLangs(['de'])
  ],

Documentation

Tools

Transloco provides several tools that help you manage translations. These tools are not part of the core library and need to be installed separately. These tools read the configuration from the transloco.config.ts file, that was generated by the ng add command during the setup process.


Keys Manager

npm i -D @jsverse/transloco-keys-manager

This tool consists of the keys extractor and the keys detective.

The keys extractor is a tool that scans an Angular project and identifies text in your components, templates, and other files that should be translated. This is helpful when you started the project without translation in mind and then got the requirement later on to add support for multiple languages.

Call it with npx

npx transloco-keys-manager extract

or add it to the package.json scripts and call it with npm run:

    "i18n:extract": "transloco-keys-manager extract",

package.json


The keys detective does two things. First, it identifies keys present in one translation file but missing from others. Second, it detects keys that exist in the translation files but are not used in the application.

    "i18n:find": "transloco-keys-manager find"

package.json

For more information about the keys manager, check out the documentation.


Validator

Install with:

npm i -D @jsverse/transloco-validator

The validator verifies that the translation files are valid JSON, and it checks that all keys are unique.

npx transloco-validator <path-to-your-translation-file>.json

Documentation


Optimizer

Install with:

npm i -D @jsverse/transloco-optimize

This tool helps you optimize the translation files for performance and size. It does three things:

npx transloco-optimize <path-to-i18n-folder>

Documentation

Conclusion

This concludes this tour of the Transloco library for Angular. We covered the basics of setting up translations, switching languages, and using various plugins and tools to enhance the internationalization workflow.

Transloco is not the only runtime i18n library for Angular. Another popular library is ngx-translate. If you are interested in a compile-time solution, check out my blog post about Angular's built-in i18n support. I discussed the differences between the two approaches at the beginning of this post.