# Svelte.i18next

![License](https://img.shields.io/github/license/maximux13/svelte-i18next)
![Downloads](https://img.shields.io/npm/dm/@maximux13/svelte-i18next)
![Version](https://img.shields.io/npm/v/@maximux13/svelte-i18next)

## Table of Contents

- [Introduction](#introduction)
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [Advanced Features](#advanced-features)
  - [Managing Namespaces](#managing-namespaces)
  - [getFixedT](#getfixedt)
  - [Trans Component](#trans-component)
- [Acknowledgements](#acknowledgements)
- [Troubleshooting](#troubleshooting)
- [License](#license)

## Introduction

[Svelte.i18next](https://github.com/maximux13/svelte-i18next) is a library that makes it easy to add internationalization (i18n) support to your [SvelteKit](https://kit.svelte.dev/). It provides a simple interface for configuring [i18next](https://www.i18next.com/) and managing translations.

## Installation

To install Svelte.i18next, simply run:

```bash
pnpm install @maximux13/svelte-i18next i18next i18next-browser-languagedetector i18next-http-backend
```

## Configuration

To set up Svelte.i18next in your project, you'll need to go through the following configuration steps:

### i18n Config File

Step 1: Create an i18n Configuration File

Create a file named src/i18n.ts and populate it with the i18n configuration. Here is an example:

```ts
export default {
  supportedLngs: ['en', 'es'], // Supported languages
  fallbackLng: 'en', // Fallback language
  defaultNS: 'common' // Default namespace
};
```

### Locale Files

Step 2: Create Locale Files

Create locale JSON files inside the static/locales/{lng}/{ns}.json directory. Replace {lng} with the language code (e.g., en, es) and {ns} with the namespace (e.g., common).

Example content for `static/locales/en/common.json`:

```json
{
  "title": "Svelte i18next - Hello {{name}}!",
  "world": "World"
}
```

### Server Initialization

Step 3: Initialize SvelteI18next Instance

In your src/i18n.server.ts, initialize a new SvelteI18next instance as shown below:

```ts
// src/i18n.server.ts
import Backend from 'i18next-http-backend';
import { SvelteI18next } from '@maximux13/svelte-i18next';
import i18n from './i18n';

const i18next = new SvelteI18next({
  i18next: {
    ...i18n,
    backend: { loadPath: '/locales/{{lng}}/{{ns}}.json' }
  },
  backend: Backend
});

export default i18next;
```

### Add Server Hook

Step 4: Add Server Hook

Create a server hook to initialize i18next in src/hook.server.ts:

```ts
import type { Handle } from '@sveltejs/kit';
import { createInstance } from 'i18next';
import Backend from 'i18next-http-backend';

import i18n from './i18n';
import i18next from './i18n.server';

import { createFetchRequest } from '@maximux13/svelte-i18next';

export const handle: Handle = async (props) => {
  const { event, resolve } = props;

  const instance = createInstance();
  const lng = await i18next.getLocale(event);
  const ns = await i18next.getNamespaces(event);
  const request = createFetchRequest(event.fetch);

  await instance
    .use(Backend)
    .init({ ...i18n, backend: { loadPath: '/locales/{{lng}}/{{ns}}.json', request }, lng, ns });

  const initOptions = i18next.getInitOptions(instance);

  event.locals.i18n = Object.assign(instance, { initOptions });

  return resolve(event, {
    transformPageChunk: ({ html }) => html.replace('<html lang="en">', `<html lang="${lng}">`)
  });
};
```

### Client Initialization

Step 5: Client Instance Setup

Finally, set up a client instance to expose i18next functionalities in your code. For example, in src/routes/+layout.server.ts and src/routes/+layout.ts:

`src/routes/+layout.server.ts`

```ts
import type { LayoutServerLoad } from './$types';

export const load: LayoutServerLoad = async ({ locals, depends }) => {
  depends('i18n:lng');

  return { i18n: locals.i18n.initOptions };
};
```

`src/routes/+layout.ts`

```ts
import type { LayoutLoad } from './$types';

import i18next from 'i18next';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';

import { createStore } from '@maximux13/svelte-i18next';

export const load: LayoutLoad = async ({ data }) => {
  i18next
    .use(Backend)
    .use(LanguageDetector)
    .init({
      ...data.i18n,
      detection: { caches: ['cookie'], order: ['htmlTag'] }
    });

  const store = createStore(i18next);

  return { i18n: store };
};
```

## Usage

Once you've completed the [Configuration](#configuration) steps, you can start using Svelte.i18next in your Svelte components and server-side code.

### In Svelte Components

You can use the i18n store in your Svelte components to access translations. Here's an example:

```svelte
<script lang="ts">
  export let data;

  $: ({ i18n } = data);
</script>

<h1>{$i18n.t('title', { name: $i18n.t('world') })}</h1>
```

### In Svelte Server Code

You can also use i18n functionalities in your server-side code. For instance:

```ts
// Example in a server-side Svelte file
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ locals, depends }) => {
  depends('i18n:lng');

  return { world: locals.i18n.t('world') };
};
```

**Note**: We use `depends('i18n:lng');` to invalidate the data when the language changes. This invalidation is triggered by the i18n store when calling `$i18n.changeLanguage` method.

## Advanced Features

### Managing Namespaces

To manage namespaces, specify which namespaces should be loaded on each page by setting the `ns` property in your page or layout configuration. For example:

```ts
// src/routes/page.(server).ts
export const config = {
  ns: ['page']
};
```

This will load the `page` namespace for the corresponding page. You can also use an array to load multiple namespaces:

```ts
export const config = {
  ns: ['page', 'otherNamespace']
};
```

### getFixedT

The `getFixedT` function allows you to use a fixed translation function in your server-side code. Example:

```ts
// Inside a server-side Svelte file
export function GET(event) {
  const t = i18next.getFixedT(event, { locale: 'en', namespaces: 'test', options: {} });
  return new Response(t('title'));
}
```

### Trans Component

The `Trans` component provides a convenient way to include complex translations with HTML tags and Svelte components.

**Props**

| Prop         | Type         | Description                                               |
| ------------ | ------------ | --------------------------------------------------------- |
| i18n         | i18n         | i18n store instance (required if not wrapped in provider) |
| t            | i18n.t       | Custom translation function                               |
| tOptions     | object       | Options to pass to the translation function               |
| key          | string       | Translation key                                           |
| values       | object       | Values for interpolation                                  |
| count        | number       | Count for pluralization                                   |
| context      | string       | Context for pluralization                                 |
| ns           | string       | Namespace                                                 |
| defaultValue | string       | Default value                                             |
| components   | array/object | Components to be used for interpolation                   |

For detailed usage of the `Trans` component, please refer to [this section](#trans-component-usage).

### Trans Component Usage

The `Trans` component is designed to handle more complex translations that may include HTML tags, variables, and even Svelte components. Below are some examples and use-cases where you might find the `Trans` component useful.

#### Basic Usage

At its simplest, the `Trans` component can be used to translate static text.

```svelte
<Trans key="hello_world" />
```

#### With Variables

You can also pass variables for interpolation.

```svelte
<Trans key="greeting" values={{ name: 'John' }} />
```

#### With HTML Tags

HTML tags can be included in the translation string and mapped to actual HTML tags using the `components` prop.

**Example translation string**: `Click <link>here</link>`

```svelte
<Trans key="click_here" components={{ link: 'a' }} />
```

#### Different Ways to Declare Components

##### As a String

You can declare the component as a string, representing the HTML tag name.

```svelte
<Trans key="click_here" components={{ link: 'a' }} />
```

##### As an Object

You can also declare the component as an object, providing more options including props.

```svelte
<Trans
  key="click_here"
  components={{
    link: { component: 'a', props: { href: '/page' } }
  }}
/>
```

##### As a Svelte Component

You can use a Svelte component directly and pass props to it.

```svelte
<Trans
  key="click_here"
  components={{
    link: { component: CustomLink, props: { href: '/page' } }
  }}
/>
```

#### `components` as an Array or Object

The `components` prop can either be an array or an object, depending on your needs.

##### As an Array

When declared as an array, the components will replace the tags in the order they appear in the translation string.

**Translation string**: `Hello <0>World</0> and <1>universe</1>`

```svelte
<Trans key="key" components={['strong', 'i']} />
```

##### As an Object

When declared as an object, you can use meaningful keys to represent your tags, making your code more readable.

**Translation string**: `Hello <bold>World</bold> and <italic>universe</italic>`

```svelte
<Trans key="key" components={{ bold: 'strong', italic: 'i' }} />
```

#### Passing Props to Components

You can pass additional props to the components by using the object notation.

```svelte
<Trans
  key="click_here"
  components={{
    bold: { component: 'strong', props: { class: 'font-semibold' } },
    link: { component: CustomLink, props: { href: '#/' } }
  }}
/>
```

#### Advanced Props

The `Trans` component also accepts a number of advanced props like `count`, `context`, `defaultValue`, and so on.

```svelte
<Trans key="itemCount" count={5} />
```

This could translate to "5 items" depending on your translation string and pluralization rules.

## Acknowledgements

This library was inspired by the excellent work of SergioDXA on the [remix-i18next](https://github.com/sergiodxa/remix-i18next) library. Special thanks to all contributors and users for their support and feedback.

## Troubleshooting

If you encounter any issues while using Svelte.i18next, please refer to the following resources for guidance:

- **Documentation**: Make sure to read the documentation carefully for any configuration or usage details you might have missed.
- **GitHub Issues**: Feel free to open an issue on our [GitHub repository](https://github.com/maximux13/svelte-i18next/issues) if you encounter bugs or have feature requests.
- **Community Support**: For general questions and discussions, you can join the [Svelte community](https://svelte.dev/chat) or other relevant forums.

## License

This project is licensed under the MIT License. For more details, see the [LICENSE](LICENSE) file in the repository.
