# Internalization (/start/translate)





In visual novels and similar games, it is common to allow players to select their native language.

For your Pixi’VN project, you should use an external library to handle translations. This gives you the flexibility to choose the implementation that best fits your needs.

<Callout title="Templates" type="info">
  In all templates, i18next is already included and configured. You can start translating your game right away.
</Callout>

The most popular and widely compatible library is **i18next**.

<img alt="i18next-h2" src="__img0" />

i18next [#i18next]

**What is i18next?** i18next is an internationalization framework for JavaScript. To use it, you need to install and initialize it. Learn more on the [i18next website](https://www.i18next.com/).

Translations are stored in multiple JSON files (one per language), using key-value pairs. The key is a unique identifier for the text, and the value is the translated string.

<CodeBlockTabs defaultValue="i18n.ts">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="i18n.ts">
      i18n.ts
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="main.ts">
      main.ts
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="i18n.ts">
    ```ts
    import i18n from "i18next";
    import LanguageDetector from "i18next-browser-languagedetector";
    import { initReactI18next } from "react-i18next";
    import strings_en from "../src/values/translations/strings_en.json";
    import strings_es from "../src/values/translations/strings_es.json";

    const getUserLang = (): string => {
        let userLang: string = navigator.language || "en";
        return userLang?.toLocaleLowerCase()?.split("-")[0];
    };

    export const useI18n = () => {
        if (!i18n.isInitialized) {
            i18n.use(LanguageDetector)
                .use(initReactI18next)
                .init({
                    debug: false,
                    fallbackLng: "en",
                    lng: getUserLang(),
                    interpolation: {
                        escapeValue: false,
                    },
                    resources: {
                        en: strings_en,
                        es: strings_es,
                        // Add more languages here
                    },
                });
        }
    };
    ```
  </CodeBlockTab>

  <CodeBlockTab value="main.ts">
    ```ts
    import { useI18n } from "./i18n";

    useI18n();
    ```
  </CodeBlockTab>
</CodeBlockTabs>

<Accordions>
  <Accordion title="translate_ui" id="translate-ui">
    To translate the UI, use the `t` function provided by i18next. Pass the translation key to `t` to get the corresponding text in the player's language.

    For example:

    <CodeBlockTabs defaultValue="React">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="React">
          React
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Vue">
          Vue
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="Svelte">
          Svelte
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="React">
        ```tsx
        import { useTranslation } from "react-i18next";

        export default function MyComponent() {
            const { t } = useTranslation("ui");

            return (
                <div>
                    <Button>{t("text_speed")}</Button>
                </div>
            );
        }
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Vue">
        ```vue
        <script setup>
        import { useTranslation } from "i18next-vue";
        const { t } = useTranslation("ui");
        </script>

        <template>
            <div>
                <button>{{ t("text_speed") }}</button>
            </div>
        </template>
        ```
      </CodeBlockTab>

      <CodeBlockTab value="Svelte">
        ```svelte
        <script>
            import { useTranslation } from 'svelte-i18n';
            const { t } = useTranslation("ui");
        </script>

        <div>
            <button>{t('text_speed')}</button>
        </div>
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>

  <Accordion title="translate_narration" id="translate-narration">
    <Callout title="Templates" type="info">
      In all templates, the `StepLabelProps` interface already includes a `t` function. See `pixi-vn.d.ts`.
    </Callout>

    <Callout type="info">
      It is recommended to use `t` inside the <DynamicLink href="/start/labels">label</DynamicLink> rather than directly in the UI, so you can take advantage of [i18n Interpolation](https://i18next.com/translation-function/interpolation).
    </Callout>

    In Pixi’VN projects, you often need to translate narration text in TypeScript/JavaScript <DynamicLink href="/start/labels">`labels`</DynamicLink>. To enable this, the translation function must be in the <DynamicLink href="/start/labels#steplabelprops">`StepLabelProps` interface</DynamicLink>.

    It is recommended to use the original string as the translation key.

    <CodeBlockTabs defaultValue="content/labels/start.label.ts">
      <CodeBlockTabsList>
        <CodeBlockTabsTrigger value="content/labels/start.label.ts">
          content/labels/start.label.ts
        </CodeBlockTabsTrigger>

        <CodeBlockTabsTrigger value="pixi-vn.d.ts">
          pixi-vn.d.ts
        </CodeBlockTabsTrigger>
      </CodeBlockTabsList>

      <CodeBlockTab value="content/labels/start.label.ts">
        ```ts
        export const startLabel = newLabel("start", [
            ({ t }) => {
                // [!code focus]
                narration.dialogue = {
                    character: liam,
                    text: t("Hello, my name is {{name}}", { name: "Liam" }), // [!code focus]
                };
            },
        ]);
        ```
      </CodeBlockTab>

      <CodeBlockTab value="pixi-vn.d.ts">
        ```ts
        declare module "@drincs/pixi-vn" {
            interface StepLabelProps {
                /**
                 * Translate a key to a string.
                 * @param key The key to translate.
                 * @returns The translated string.
                 */
                t: TFunction<[string], undefined>;
                // ...
            }
        }
        ```
      </CodeBlockTab>
    </CodeBlockTabs>
  </Accordion>
</Accordions>

Create [#create]

<Callout title="ink" type="info">
  If you use `ink`, you can automatically extract strings from `ink` text for translation. See more <DynamicLink href="/ink/translate">here</DynamicLink>.
</Callout>

Translation JSON files must be created manually. It is recommended to split translations into two sections (see `strings_es.json`):

* <DynamicLink href="/start/interface">UI</DynamicLink> texts: for screens,
  settings, quick buttons, etc.—everything not part of the narration.
* <DynamicLink href="/start/narration">Narration</DynamicLink> texts: for
  dialogues, choice menus, etc.

<CodeBlockTabs defaultValue="locales/strings_es.json">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="locales/strings_es.json">
      locales/strings_es.json
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="locales/strings_es.json">
    ```json
    {
        "ui": {
            "text_speed": "Velocidad del texto"
            // ...
        },
        "narration": {
            "Hello, my name is {{name}}": "Hola, mi nombre es {{name}}"
            // ...
        }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>
