Using translations in a component
Once the languages are configured (see Localization) and the scope translation files are in place, this guide shows how to display translated text in a component — and how to avoid hardcoded strings.
Mint uses Transloco. Translations live under
src/assets/i18n/:
- root / global scope:
src/assets/i18n/<lang>.json— generic keys with no prefix (back,cancel,close,clear,filters,history,loadMore,more, …). - named scopes: one folder per scope,
src/assets/i18n/<scope>/<lang>.json(e.g.filters/,sort-selector/, and theagt-*scopes coming from@sinequa/agent). Library scope files are copied in by the extraction step (npm run transloco:extract-scoped-libs, run onpostinstall).
Import the pipe
In a standalone component, add TranslocoPipe to the imports:
import { TranslocoPipe } from "@jsverse/transloco";
@Component({
selector: "my-widget",
imports: [TranslocoPipe /* … */],
templateUrl: "./my-widget.html"
})
export class MyWidgetComponent {}
Root (global) keys
Generic keys defined in src/assets/i18n/<lang>.json are used without a prefix and need no
scope:
<span>{{ "filters" | transloco }}</span>
Before adding a new string, check whether a suitable global key already exists (e.g. filters,
close, clear, more) and reuse it.
Scoped keys
A scope groups translations under a folder and is requested per component via
provideTranslocoScope. The key is prefixed with the scope name in camelCase (the kebab-case
folder saved-searches becomes the prefix savedSearches):
@Component({
// …
providers: [provideTranslocoScope("filters")]
})
<span>{{ "filters.clearAllFilters" | transloco }}</span>
A global key and a scope can share the same name without conflict: the root filters key (a plain
string) and the filters/ scope (keys like filters.toggle) coexist. A component without the scope
reads the root key; with the scope it can address filters.xxx. Global keys remain reachable by
their plain name even inside a scoped component.
Translating attributes (title, aria-label, …)
Use a binding, never a static attribute, so the pipe is evaluated:
<button [title]="'filters' | transloco" [attr.aria-label]="'close' | transloco">
<filter-icon />
</button>
Parameters and ICU
Pass parameters as the pipe argument. The MessageFormat plugin
(provideTranslocoMessageformat) is enabled, so ICU syntax (plurals / select) is supported:
{ "resultsCount": "{count, plural, =0 {No results} one {# result} other {# results}}" }
<span>{{ "resultsCount" | transloco: { count: total() } }}</span>
Reusing a key across a block (directive)
To translate several keys in one block, the structural directive avoids repeating the pipe:
<ng-container *transloco="let t">
<h2>{{ t("filters") }}</h2>
<button>{{ t("close") }}</button>
</ng-container>
Programmatic translation
In TypeScript, inject TranslocoService:
private readonly transloco = inject(TranslocoService);
const label = this.transloco.translate("filters");
Adding a new key
- Global key → add it to every language file:
en.json,fr.jsonandde.json. A missing key falls back to English and logs a warning. - Scope key from a library → the source of truth is the library; do not edit
src/assets/i18n/<scope>/<lang>.jsonby hand (it is overwritten by the extraction step). To change a library string locally, use the override system — see i18n Translation Overrides.
Best practice: no hardcoded text
All user-facing text must go through Transloco. When introducing UI strings, prefer reusing an existing global key over adding a new one. Example from the left filters drawer on the search page:
<!-- panel header -->
<span class="font-semibold">{{ "filters" | transloco }}</span>
<!-- close button -->
<button [attr.aria-label]="'close' | transloco"><xmark-icon /></button>