Skip to main content
Jet uses Transloco for internationalization (i18n), providing support for multiple languages with runtime translation switching.

Translation Architecture

Translations are stored in JSON files:
public/i18n/
├── en.json    # English translations
└── ar.json    # Arabic translations

Translation File Structure

Translation files use nested objects:
{
  "alerts": {
    "avatar-updated": "Avatar updated.",
    "password-updated": "Password updated.",
    "welcome": "Welcome!"
  },
  "constants": {
    "arabic": "العربية",
    "english": "English",
    "home": "Home",
    "profile": "Profile"
  },
  "jet-home-page": {
    "seo": {
      "title": "Jet - Angular starter-kit",
      "description": "Build quality web apps fast",
      "keywords": "angular, starter, kit"
    },
    "toolbar-title": "Jet"
  }
}

Adding a Language

1

Create translation file

Create a new JSON file in public/i18n/:
touch public/i18n/es.json
2

Copy translation structure

Copy the structure from en.json and translate the values:
{
  "alerts": {
    "welcome": "¡Bienvenido!",
    "ok": "OK"
  },
  "constants": {
    "spanish": "Español",
    "english": "English",
    "home": "Inicio"
  }
}
3

Add language option

Add the language to src/app/constants/language-options.constant.ts:
export const LANGUAGE_OPTIONS: LanguageOption[] = [
  {
    directionality: 'ltr',
    fontPair: 'ns-bg',
    fontPairUrl: 'https://fonts.googleapis.com/css2?display=swap&family=Bricolage+Grotesque:opsz,[email protected],200..800&family=Noto+Sans:[email protected]',
    icon: 'translate',
    nameKey: marker('constants.english'),
    value: 'en',
  },
  {
    directionality: 'rtl',
    fontPair: 'nsa-nsa',
    fontPairUrl: 'https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans+Arabic:[email protected]',
    icon: 'translate',
    nameKey: marker('constants.arabic'),
    value: 'ar',
  },
  {
    directionality: 'ltr',
    fontPair: 'ns-bg',  // Reuse fonts or add Spanish-specific fonts
    fontPairUrl: 'https://fonts.googleapis.com/css2?display=swap&family=Bricolage+Grotesque:opsz,[email protected],200..800&family=Noto+Sans:[email protected]',
    icon: 'translate',
    nameKey: marker('constants.spanish'),
    value: 'es',
  },
];
4

Add font support (if needed)

If using language-specific fonts, update styles.scss:
$es-font-family: ('Your Spanish Font', sans-serif);

body.jet-font-pair-spanish {
  @include mat.theme(
    (
      typography: (
        brand-family: $es-font-family,
        plain-family: $es-font-family,
      ),
    )
  );
}
5

Test the new language

Run the app and switch to the new language in Settings to verify all translations display correctly.

Using Translations in Components

In Templates

Wrap your template in a Transloco container:
<ng-container *transloco="let t">
  <h1>{{ t('jet-home-page.title') }}</h1>
  <p>{{ t('jet-home-page.description') }}</p>
  
  <!-- With interpolation -->
  <p>{{ t('alerts.please-select-an-avatar-lte-x-mb', { x: 5 }) }}</p>
</ng-container>

In Component Code

Inject TranslocoService:
import { inject } from '@angular/core';
import { TranslocoService } from '@jsverse/transloco';

export class MyComponent {
  readonly #translocoService = inject(TranslocoService);

  showAlert() {
    const message = this.#translocoService.translate('alerts.welcome');
    alert(message);
  }

  // With parameters
  showSizeAlert(size: number) {
    const message = this.#translocoService.translate(
      'alerts.please-select-an-avatar-lte-x-mb',
      { x: size }
    );
    alert(message);
  }
}

Structural Directive

For components with TranslocoModule imported:
<ng-container *transloco="let t">
  <mat-card>
    <mat-card-header>
      <mat-card-title>{{ t('jet-profile-page.title') }}</mat-card-title>
    </mat-card-header>
    <mat-card-content>
      {{ t('jet-profile-page.content') }}
    </mat-card-content>
  </mat-card>
</ng-container>

Translation Keys Manager

Jet includes Transloco Keys Manager for maintaining translations.

Extract Translation Keys

Automatically extract keys from templates and TypeScript:
npm run i18n:extract
This scans your code for:
  • t('key') in templates
  • marker('key') in TypeScript
  • translate('key') in TypeScript

Find Missing Keys

Find missing or unused translation keys:
npm run i18n:find
This reports:
  • Keys used in code but missing from translation files
  • Keys in translation files but not used in code

Using the Marker Function

For keys defined in TypeScript (like constants):
import { marker } from '@jsverse/transloco-keys-manager/marker';

export const LANGUAGE_OPTIONS: LanguageOption[] = [
  {
    nameKey: marker('constants.english'),  // Marks for extraction
    value: 'en',
  },
];
The marker function doesn’t translate at definition time—it just marks the key for extraction.

Changing the Default Language

1

Update the constant

Edit src/app/constants/default-language-option.constant.ts:
import { LanguageOption } from '@jet/interfaces/language-option.interface';
import { LANGUAGE_OPTIONS } from './language-options.constant';

// Change index to select different default language
// 0 = English, 1 = Arabic, etc.
export const DEFAULT_LANGUAGE_OPTION: LanguageOption = LANGUAGE_OPTIONS[0]!;
2

Test the change

Clear browser storage and reload to see the new default language:
// In browser console
localStorage.clear();
sessionStorage.clear();
location.reload();

Removing a Language

1

Remove from language options

Remove the language entry from src/app/constants/language-options.constant.ts.
2

Delete translation file

rm public/i18n/ar.json
3

Remove font styles (if applicable)

If the language had custom fonts, remove them from styles.scss.
4

Update default if needed

If you removed the default language, update default-language-option.constant.ts.

RTL Language Support

Configuring RTL

For RTL languages (like Arabic), set directionality: 'rtl':
{
  directionality: 'rtl',
  fontPair: 'nsa-nsa',
  fontPairUrl: 'https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans+Arabic:[email protected]',
  icon: 'translate',
  nameKey: marker('constants.arabic'),
  value: 'ar',
}
The app automatically:
  • Sets dir="rtl" on the document
  • Flips layouts using CSS logical properties
  • Adjusts safe areas for notched devices
  • Mirrors animations

Testing RTL

  1. Switch to Arabic in Settings
  2. Verify layout flips correctly
  3. Check text alignment
  4. Test navigation and animations

Translation Best Practices

Organize translations by component or feature:
{
  "jet-profile-page": {
    "title": "Profile",
    "form": {
      "name": "Name",
      "email": "Email",
      "submit": "Save Changes"
    }
  }
}
Name translation keys after component selectors:
{
  "jet-user-card": { ... },
  "jet-dashboard-page": { ... }
}
Make keys descriptive:
{
  "buttons": {
    "save": "Save",
    "cancel": "Cancel",
    "delete-with-confirm": "Delete (cannot be undone)"
  }
}
Use interpolation for dynamic values:
{
  "alerts": {
    "items-selected": "{{ count }} items selected",
    "welcome-user": "Welcome, {{ name }}!"
  }
}
Usage:
{{ t('alerts.items-selected', { count: 5 }) }}
Avoid deep nesting unless necessary:
// Good
{
  "buttons": {
    "save": "Save",
    "cancel": "Cancel"
  }
}

// Avoid excessive nesting
{
  "ui": {
    "components": {
      "buttons": {
        "actions": {
          "save": "Save"
        }
      }
    }
  }
}
Regularly check for missing or unused keys:
npm run i18n:find
Clean up unused keys to keep translation files maintainable.

Translation Optimization

Jet optimizes translations at build time using @jsverse/transloco-optimize.

Post-build Optimization

The build process runs:
node ./scripts/optimize-translations.mjs
This:
  • Removes unused translation keys
  • Minifies translation files
  • Reduces bundle size

Manual Optimization

Optimization runs automatically during npm run build, but you can also:
  1. Build the project
  2. Run the optimization script manually
  3. Verify optimized files in dist/jet/browser/i18n/

Build docs developers (and LLMs) love