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
-
Pros:
- Small bundle size and optimal performance
- Full tree-shaking support, unused translations are removed
- Official Angular solution, built into the Angular CLI
-
Cons:
- Requires separate builds per locale
- Increases build time significantly with multiple locales
- Switching language at runtime requires additional setup
- Development server can only serve one locale at a time
- More complex deployment
Runtime libraries (e.g., Transloco, @ngx-translate/core
)
-
Pros:
- Switch languages at runtime without a page reload
- Lazy-load translation files on demand
- Single build for all locales
- Easier development and testing workflow
- Better user experience with instant language switching
- Simpler deployment (one application bundle)
-
Cons:
- Slight runtime overhead for translation processing
- Additional HTTP requests for loading translation files
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:
transloco.config.ts
: This file is used by Transloco tools and plugins. We will explore these in more detail later.src/app/transloco-loader.ts
: This is a default HTTP loader implementation that loads the JSON files during runtime.
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
}),
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>
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>
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.",
The structural directive also works with attributes.
<input
*transloco="let t"
[placeholder]="t('searchPlaceholder')"
[attr.aria-label]="t('searchAriaLabel')" />
If there are placeholders in your translations, you need to pass as a second argument an object with the values.
"greeting": "Hello, {{name}}",
<p *transloco="let t">
{{ t('greeting', { name }) }}
</p>
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>
"learn": "Hi {name}. Learn more in the <a href=\"https://jsverse.gitbook.io/transloco\" target=\"_blank\" rel=\"noopener\">Transloco documentation</a>."
Transloco allows you to group translations with nested objects in the JSON file.
"fruits": {
"apples": "Apples",
"bananas": "Bananas",
"cherries": "Cherries"
},
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>
You can use the pipe with parameters for placeholders as well.
<p>
{{ 'greeting' | transloco: { name } }}
</p>
Attribute Directive ¶
The third way to configure translations is by using the attribute directive transloco
.
<p transloco="introText"></p>
You can also pass an object for replacing placeholders.
<p transloco="greeting" [translocoParams]="{ name }"></p>
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']);
});
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);
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
];
});
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);
}
});
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>
The method getActiveLang
of the TranslocoService
returns the currently active language.
activeLang = this.translocoService.getActiveLang() || 'en';
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);
}
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>'),
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}}.",
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(),
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}}",
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>
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,
},
}),
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'])
],
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",
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"
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
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:
- Flattens AOT translation files
- Removes translator comments
- Minifies JSON files
npx transloco-optimize <path-to-i18n-folder>
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.