# Localization

Which locale your game gets on each host, what the hub translates for you, and what to translate in the game.

filudi.com runs in several languages; every site on the catalog has its own. The hub passes the site's
locale to your game, so a player who reads the page in French gets your game in French when you have it.
The reference is [i18n](/docs/sdk/i18n/).

## What the hub translates

The game's page: your titles, descriptions and categories, written per locale in the console's Settings
screen. Each site writes its own texts; the catalog never shares a description between sites. The hub also
formats the leaderboard, the dates and the `time` meta. None of this is in your bundle.

## What the game translates

Everything drawn inside the frame: menus, tutorial, messages, the `_summary.line` of your save. A game with
no translation runs in its own language on every locale; nothing breaks.

<CodePanel title="Strings per locale">
  <Fragment slot="javascript">
    ```js
    const strings = {
      en: { play: 'Play', best: 'Best', again: 'Tap to run again' },
      fr: { play: 'Jouer', best: 'Record', again: 'Touchez pour rejouer' },
    };
    const language = (tag) => tag.split('-')[0];
    let t = strings[language(sdk.i18n.locale())] ?? strings.en;
    sdk.i18n.on('change', (locale) => {
      t = strings[language(locale)] ?? strings.en;
      redrawMenus();
    });
    ```

  </Fragment>
  <Fragment slot="typescript">
    ```ts
    type Strings = { play: string; best: string; again: string };
    const strings: Record<string, Strings> = {
      en: { play: 'Play', best: 'Best', again: 'Tap to run again' },
      fr: { play: 'Jouer', best: 'Record', again: 'Touchez pour rejouer' },
    };
    const language = (tag: string): string => tag.split('-')[0] ?? 'en';
    let t: Strings = strings[language(sdk.i18n.locale())] ?? strings.en;
    sdk.i18n.on('change', (locale: string) => {
      t = strings[language(locale)] ?? strings.en;
      redrawMenus();
    });
    ```

  </Fragment>
</CodePanel>

## The locale per host

| Host       | Source                                                                       | Changes while running |
| ---------- | ---------------------------------------------------------------------------- | --------------------- |
| filudi.com | The site's locale in `filudi:init`; `filudi:locale` when the player switches | Yes                   |
| CrazyGames | `user.systemInfo.locale`                                                     | No                    |
| Playgama   | `bridge.platform.language`                                                   | No                    |
| Poki       | None: `navigator.language`                                                   | No                    |
| mock       | `mock.locale`, else `navigator.language`                                     | No                    |

`init({ locale })` overrides every source; use it for a language picker inside the game and keep the choice
in your save.

## Fonts and text

Fonts load from the bundle only. A font with the glyphs of every language you ship is one more file in the
zip; a Google Fonts link is blocked by the CSP. Text drawn on a canvas does not reflow: leave room for
French and German, which run a third longer than English.

## Events

`sdk.track()` carries the locale with every event, and the console splits statistics by locale. Keep
event names in English and `snake_case` whatever the player's language.
