npm package: @nicholasperetti/rollup-plugin-i18n
Description: i18n rollup plugin
Author: Nicholas Peretti
Latest version: 0.0.5

# rollup-plugin-i18n

Use i18n in your app with rollup!

- [rollup-plugin-i18n](#rollup-plugin-i18n)
  - [Install](#install)
  - [Usage](#usage)
  - [Globals](#globals)
    - [i18n:languages](#i18nlanguages)
    - [i18n:active-language](#i18nactive-language)
    - [i18n:is-root-bundle](#i18nis-root-bundle)
  - [How it works](#how-it-works)
    - [Where does the translated code will be put?](#where-does-the-translated-code-will-be-put)
    - [So, are my assets beign duplicated for every language?](#so-are-my-assets-beign-duplicated-for-every-language)
    - [What happens to the original files?](#what-happens-to-the-original-files)
    - [Dynamic translation keys](#dynamic-translation-keys)
  - [The translations.js file](#the-translationsjs-file)
    - [The common approach (and its problems)](#the-common-approach-and-its-problems)
    - [The solution adopted](#the-solution-adopted)
      - [Why?](#why)
    - [Structure of the file](#structure-of-the-file)
      - [languages](#languages)
      - [default](#default)
      - [defaultLanguage](#defaultlanguage)
  - [Options](#options)
    - [translationsPath](#translationspath)
    - [disableDynamicTranslations](#disabledynamictranslations)
  - [Typescript support](#typescript-support)

## Install

```
yarn add -D @nicholasperetti/rollup-plugin-i18n
```

or

```
npm i -D @nicholasperetti/rollup-plugin-i18n
```

## Usage

In your `rollup.config.js` file:

```js
import i18n from '@nicholasperetti/rollup-plugin-i18n'

export default {
  plugins: [
    i18n({
      /* options */
    }),
  ],
}
```

Your `translations.js` file

```js
export const languages = {
  EN: 'en'
  IT: 'it'
  FR: 'fr'
}

export default {
  "appName": {
    [languages.EN]: 'My App',
    [languages.FR]: 'Mon appli',
    [languages.IT]: 'La mia App',
  }
}
```

In your code

```js
console.log(__('appName'), availableLanguages)
```

## Globals
It's very likely that you'll need to know which languages are available, which
of them is active and more.

In order to provide to you this runtime info, this plugin will allow you to import
this informations via the following modules:

### i18n:languages
Returns an array of strings. Every element of the array represent one of
the languages that you've set in the `translations.js` file.
You can use these values to redirect the user to the bundle you want.

This is useful if you want to show a dropdown with the available languages.

### i18n:active-language
Returns a string that represent the active language that has been rendered in
the running code.

This is useful if you want to show to the user which language is active

### i18n:is-root-bundle
Returns a boolean. This flag will be `true` if the user is running the root
bundle instead of the translated one.

For example, the user is surfing on `yoursite.com/` instead of `yoursite.com/en/`.

## How it works

After rollup wrote the files, the plugin reads them, analyzes all the chunks and
replace all the `__()` occurences with the respecitve value.

### Where does the translated code will be put?

The plugin creates a directory for each language that you specify in the
`translations.js` file. The whole build is being kind of copy-pasted in the
language directory and then transformed

### So, are my assets beign duplicated for every language?

Nope!

This plugins copies only the chunks to be sure everything has been translated.
All the assets and other static files will be symlinked.

This means that you'll have your `assets` directory in the root of your
`output.dir`, and every build will have a symlink to that.

This solution saves a lot of space on disk when you build your code.

### What happens to the original files?

The original files will be translated to your default language.
If you don't specify one, the plugin will get the first language available and
use it as default language.

You can set your default langauge with the [defaultLanguage](#defaultLanguage) option

### Dynamic translation keys

The plugin will try to replace every `__()` occurency with the right translation.
Anyhow, there are cases where you don't know what the translation will be.
Maybe, the translation key is given to you by an API base on a user action.

For those cases the plugin defines the `__()` globally and inject in your code
the dictionary of the translations for a specific language.

**Note**: If you're sure you won't need the dynamic translations feature, make
sure to use the `disableDynamicTranslations` option.

## The translations.js file

You can store all your translations in the `translations.js` file (you can override
this with the `translationsPath` option).
This file behaves in a different way compared to similar i18n plugins.

### The common approach (and its problems)

It's a common practice to group the translations under a certain languange, like this:

```json
{
  "en": {
    "Title": "Title"
  },
  "it": {
    "Title": "Titolo"
  },
  "fr": {
    "Title": "Titre"
  }
}
```

Anyhow, this approach make it easy to miss information along the way.
Since we need to repeat every key in the config object, we might mispell that
key for some language and generate a bug.

We might even forget to translate some keys for some languages and no tool will
aler us of the bug.

### The solution adopted

In order to solve those issue and ensure consistency within your translations,
this plugins organises data by content instead of language, like this:

```json
{
  "Title": {
    "en": "Title",
    "it": "Titolo",
    "fr": "Titre"
  }
}
```

#### Why?

It's more common to change the content of the app instead of add/remove languages.
This approach make the maintance of translations way more easy.

Also, since the keys are being written only once, it's impossible to mistype a
certain key in your configuration.

This is really easy to use because the human brain oraganise the information
by content, not by language. So this schema reflects way more the way humans
thinks about translations.

### Structure of the file

This file **_must_** exports the following things:

#### languages

This must be an object containing all the languages supported by your app.
If you forget to translate some key in yor config, this plugin will make the build
fail, warning you about what translation is missing

The keys of the object will be used by you in order to reference the language
in your translations object, like this:

```js
export const languages = { EN: 'en' }

//  Optional
export const defaultLanguage = languages.EN

export default {
  Title: {
    [languages.EN]: 'Title',
  },
}
```

#### default

You **_must_** export as default your translations object, like this:

```js
export default {
  Title: {
    [languages.EN]: 'Title',
  },
}
```

#### defaultLanguage

Specify wich language will be the default language.

This is useful to set a default langauge when your users visit `yourapp.domain/` instead of `yourapp.domain/en/`.

## Options

### translationsPath

Tell the plugin where it can find your translations file. By default, it
will look for `translations.js`.

```js
import i18n from '@nicholasperetti/rollup-plugin-i18n'

export default {
  plugins: [
    i18n({
      translationsPath: './i18n/translations.js',
    }),
  ],
}
```

### disableDynamicTranslations

By default the plugin injects the translations of a specific language in your
code in order to be able to dynamically translate all the keys you want to.

The downside of this approach is that, as your translations grows, your bundle
grows too.

So, if you're sure you don't need to dynamically resolve your translations, you
can set this option to `true` and the plugin won't inject neither the translations
dictionary nor the fallback function.

## Typescript support

In order to use this within your Typescript codebase you need to add the definition
of the `__()` function. If you already have a global `.d.ts` file add it in there.
Otherwise, create a `global.d.ts` and include it in your `tsconfig.json`.

Copy paste this definition in there:

```ts
declare function __(translationKey: string): string;

declare module 'i18n:languages' {
  const content: Array<string>
  export default content
}
declare module 'i18n:active-language' {
  const content: string
  export default content
}
declare module 'i18n:is-root-bundle' {
  const content: boolean
  export default content
}
```

More information about typescript global declarations can be found [here](https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-d-ts.html)