# Outcome Recipes

## 1. Locale switcher that persists the user’s preference

**Goal:** Let the user pick a language, apply it immediately, and remember the choice across sessions.

### Steps

1. Register the i18n plugin with multiple locales (as shown in the main guide).
2. Create a locale service that wraps `I18N.setLocale` and writes to `localStorage`:

   ```typescript
   import { I18N } from '@aurelia/i18n';
   import { resolve } from '@aurelia/kernel';

   export class LocaleService {
     private readonly i18n = resolve(I18N);
     private readonly storageKey = 'preferred-locale';

     constructor() {
       const saved = localStorage.getItem(this.storageKey);
       if (saved) {
         void this.i18n.setLocale(saved);
       }
     }

     async changeLocale(locale: string) {
       await this.i18n.setLocale(locale);
       localStorage.setItem(this.storageKey, locale);
     }
   }
   ```
3. Bind a `<select>` to `localeService.changeLocale(locale)` so choosing a language updates translations immediately.

### Checklist

* Reloading the page restores the last selected locale.
* Changing the locale triggers translations everywhere without manual refresh (thanks to the `i18n:locale:changed` event).
* The UI defaults to the browser locale when no preference is stored.

## 2. Number and date formatting per locale

**Goal:** Display currency and dates using the active locale without writing custom formatting logic in components.

### Steps

1. Inject `I18N` or use the `df`/`nf` value converters provided by the plugin:

   ```html
   <p>Balance: ${user.balance | nf:{ style: 'currency', currency: 'EUR' }}</p>
   <p>Joined: ${user.joined | df:{ dateStyle: 'long' }}</p>
   ```
2. When the locale changes, converters re-run automatically.

### Checklist

* Switching between `en` and `de` changes decimal separators and currency symbols as expected.
* Dates use the correct ordering (month/day vs day/month).
* No manual `Intl.NumberFormat` calls are needed in view-models.

## 3. Validation messages that respect the current locale

**Goal:** Integrate `@aurelia/i18n` with the validation plugin so error messages translate automatically.

### Steps

1. Configure the validation message provider to delegate to i18n using `ValidationI18nConfiguration`:

   ```typescript
   import { ValidationI18nConfiguration } from '@aurelia/validation-i18n';

   Aurelia.register(
     ValidationI18nConfiguration.customize(options => {
       options.DefaultNamespace = 'validation';
       options.DefaultKeyPrefix = 'errors';
     })
   );
   ```
2. Provide translations for the built-in validation keys (for example, `validation.required`).
3. When the locale service changes languages, validation errors re-render with the translated message.

### Checklist

* Required-field errors display in English by default and in German after switching locales.
* Custom rule messages use translated strings from your resource files.
* No manual string concatenation is needed in validators or components.

## 4. Lazy-load namespaces per route

**Goal:** Keep initial bundles small by loading translation namespaces on demand when a route activates.

### Steps

1. Configure `@aurelia/i18n` with a backend (for example `i18next-fetch-backend`) so it can fetch JSON files as needed.
2. In the route view-model, call `i18n.i18next.loadNamespaces` before rendering (accessing the underlying i18next instance):

   ```typescript
   import { I18N } from '@aurelia/i18n';
   import { resolve } from '@aurelia/kernel';

   export class ReportsRoute {
     private readonly i18n = resolve(I18N);

     async canLoad() {
       await this.i18n.i18next.loadNamespaces('reports');
       return true;
     }
   }
   ```
3. Use keys like `reports:title` inside the route template. Once the namespace is loaded, translations render immediately.

### Checklist

* The network tab shows `/locales/{locale}/reports.json` only when the route loads.
* Subsequent navigations reuse the cached namespace (unless you opt in to reloading).
* Users do not see missing key warnings because namespaces load before the view activates.

## 5. Relative time formatting

**Goal:** Show human-friendly “time ago” strings that follow the active locale’s grammar.

### Steps

1. Enable the relative-time formatter in the i18n options (it is on by default when using `@aurelia/i18n`).
2. In templates, use the `rt` value converter:

   ```html
   <!-- Past dates show "X ago" -->
   <p>Updated ${lastUpdated | rt}</p>
   <!-- Future dates automatically show "in X" -->
   <p>Expires ${expiresAt | rt}</p>
   <!-- Optional: customize style -->
   <p>${someDate | rt:{ style: 'narrow' }}</p>
   ```
3. Customize thresholds and language by providing translations for `relativeTime` in each locale file if the defaults are insufficient.

### Checklist

* Switching locale changes phrases like "just now" or "5 minutes ago" automatically.
* Past dates render as "X ago" and future dates render as "in X" automatically based on the date value.
* Dates in the past or future render correctly without manual math.

## Reference material

* [Internationalization guide](https://docs.aurelia.io/aurelia-packages/internationalization)
* [Validation i18n integration](https://docs.aurelia.io/aurelia-packages/validation/i18n-internationalization)
