# Internationalization (i18n)

## Setup

```html
<script>
  NoJS.i18n({
    defaultLocale: 'en',
    fallbackLocale: 'en',
    locales: {
      en: {
        greeting: 'Hello, {name}!',
        items: '{count} item | {count} items',  // Pluralization
        nav: {
          home: 'Home',
          about: 'About'
        }
      },
      'pt-BR': {
        greeting: 'Olá, {name}!',
        items: '{count} item | {count} itens',
        nav: {
          home: 'Início',
          about: 'Sobre'
        }
      }
    }
  });
</script>
```

---

## External Locale Files

Instead of inlining all translations in JavaScript, load them from external JSON files.

### Flat Mode (one file per locale)

```
/locales/en.json
/locales/es.json
```

```html
<script>
  NoJS.i18n({
    defaultLocale: 'en',
    loadPath: '/locales/{locale}.json'
  });
</script>
```

### Namespace Mode (split by feature)

```
/locales/en/common.json
/locales/en/dashboard.json
```

```html
<script>
  NoJS.i18n({
    defaultLocale: 'en',
    loadPath: '/locales/{locale}/{ns}.json',
    ns: ['common']   // Loaded at init
  });
</script>
```

### Namespace per Route

```html
<template route="/dashboard" src="./pages/dashboard.tpl" i18n-ns="dashboard"></template>
```

### Namespace on Any Element

```html
<div i18n-ns="settings">
  <h2 t="settings.title"></h2>
  <p t="settings.desc"></p>
</div>
```

### Caching

Fetched JSON files are cached in memory by default. Disable in development:

```html
<script>
  NoJS.i18n({ loadPath: '/locales/{locale}.json', cache: false });
</script>
```

### Fallback Behavior

When a translation key is missing from the current locale, No.JS falls back to the `fallbackLocale`:

```html
<script>
  NoJS.i18n({
    defaultLocale: 'pt-BR',
    fallbackLocale: 'en',
    locales: {
      en: { greeting: 'Hello', farewell: 'Goodbye' },
      'pt-BR': { greeting: 'Olá' }
      // farewell missing → falls back to English "Goodbye"
    }
  });
</script>
```

If the key is missing from both the current locale and the fallback, the raw key string is displayed.

### Browser Locale Detection

Enable automatic locale detection from the browser's `navigator.language`:

```html
<script>
  NoJS.i18n({
    detectBrowser: true,
    defaultLocale: 'en',
    fallbackLocale: 'en'
  });
</script>
```

When `detectBrowser` is `true`, No.JS checks `navigator.language` and uses the matching locale if available. Falls back to `defaultLocale` if no match is found.

---

## Usage

```html
<!-- Simple translation -->
<h1 t="greeting" t-name="user.name"></h1>
<!-- Output: "Hello, John!" or "Olá, John!" -->

<!-- Nested keys -->
<a route="/" t="nav.home"></a>

<!-- Pluralization -->
<span t="items" t-count="cart.items.length"></span>
<!-- Output: "1 item" or "5 items" -->

<!-- Switch locale -->
<button on:click="$i18n.locale = 'pt-BR'">Português</button>
<button on:click="$i18n.locale = 'en'">English</button>

<!-- Current locale -->
<span bind="$i18n.locale"></span>
```

---

## HTML Translations (`t-html`)

Add the `t-html` attribute to render a translation value as sanitized HTML instead of plain text.

```html
<!-- Translation: "Read our <a href='/terms'>terms</a>" -->
<div t="legal.notice" t-html></div>
```

The output is passed through `_sanitizeHtml()` to prevent XSS.

---

## Number & Date Formatting

```html
<!-- Currency -->
<span bind="price | currency"></span>           <!-- $1,234.56 -->
<span bind="price | currency:'BRL'"></span>     <!-- R$ 1.234,56 -->

<!-- Date -->
<span bind="createdAt | date"></span>            <!-- 02/25/2026 -->
<span bind="createdAt | date:'long'"></span>     <!-- February 25, 2026 -->
<span bind="createdAt | datetime"></span>        <!-- 02/25/2026 3:45 PM -->
<span bind="createdAt | relative"></span>        <!-- 2 hours ago -->

<!-- Number -->
<span bind="value | number"></span>              <!-- 1,234.56 -->
<span bind="value | number:0"></span>            <!-- 1,235 -->
<span bind="value | percent"></span>             <!-- 45% -->
```

---

---

## See Also

- [Filters & Pipes](filters.md) — `currency`, `date`, `number`, `percent` filters
- [Routing](routing.md) — `i18n-ns` for per-route namespace loading
- [Configuration](configuration.md) — i18n config options

**Previous:** [Routing ←](routing.md) | **Next:** [Drag & Drop →](drag-and-drop.md)
