How to Add Internationalization (i18n) to your Preact application
Tags: preact, web, javascript, internationalization
Published: 2019-06-18 14:45:26

---
title: How to Add Internationalization (i18n) to your Preact application
published: true
description: Learn how you can add Internationalization (i18n) to your Preact Application with preact-i18n.
tags: preact, web, javascript, internationalization
cover_image: https://thepracticaldev.s3.amazonaws.com/i/qkkfmwp6bfrpqzinu40f.jpg
canonical_url: https://medium.com/@limhenry/how-to-add-i18n-to-preact-dff1bf19917
---

🇹🇼 中文版 (Chinese Version): https://dev.to/henrylim96/i18n-preact-3pie


---

## What is Internationalization (i18n)?

> **Internationalization** is the design and development of a product, application or document content that enables easy localization for target audiences that vary in culture, region, or language.

In this article, you are going to use the `preact-i18n` library to add internationalization to your Preact application.

---

## Step 1: Setup Preact CLI & Create new project

> Side Note: If you are already familiar with Preact, you may skip to the next step.

If you haven't installed the Preact CLI on your machine, use the following command to install the CLI. Make sure you have Node.js 6.x or above installed.

`$ npm install -g preact-cli`

Once the Preact CLI is installed, let's create a new project using the `default` template, and call it `my-project`.

`$ preact create default my-project`

Start the development server with the command below:

`$ cd my-project && npm run start`

Now, open your browser and go to `http://localhost:8080`, and you should see something like this on your screen:

![](https://thepracticaldev.s3.amazonaws.com/i/kk6nzdsfzs4nzrlpahvx.jpg)

## Step 2: Add preact-i18n library

Install the `preact-i18n` library to your project using the command below:

`$ npm install --save preact-i18n`

`preact-i18n` is very easy to use, and most importantly, it's extremely small, around 1.3kb after gzipped. You can learn more about the library here: [https://github.com/synacor/preact-i18n](https://github.com/synacor/preact-i18n)

## Step 3: Create a definition file

Once you have the library installed, you will need to create a definition file, which you will store all the translate strings in a JSON file. 

In this case, you will need to save this file in `src/i18n/zh-tw.json`:

```json
{ 
  "home": {
    "title": "主頁", 
    "text": "這是個Home組件。"
  }
}
```

## Step 4: Import IntlProvider and definition file

Next, open the `app.js` file, which is located in the `src/components` folder. Then, import the `IntlProvider` and your `definition` file to the `app.js` file:

```jsx
import { IntlProvider } from 'preact-i18n';
import definition from '../i18n/zh-tw.json';
```

## Step 5: Expose the definition via IntlProvider

After that, you will need to expose the definition file to the whole app via `<IntlProvider>`. By doing this, you will be able to read the definition file everywhere in the app.

```jsx
render() {
  return(
    <IntlProvider definition={definition}>
      <div id="app" />
    </IntlProvider>
  );
}
```

At this moment, here's how your `app.js` file should looks like:

```jsx
import { h, Component } from 'preact';
import { Router } from 'preact-router';
import Header from './header';
import Home from '../routes/home';
import Profile from '../routes/profile';
// Import IntlProvider and the definition file.
import { IntlProvider } from 'preact-i18n';
import definition from '../i18n/zh-tw.json';
export default class App extends Component {
 
 handleRoute = e => {
  this.currentUrl = e.url;
 };
render() {
  return (
   // Expose the definition to your whole app via <IntlProvider>
   <IntlProvider definition={definition}>
    <div id="app">
     <Header />
     <Router onChange={this.handleRoute}>
      <Home path="/" />
      <Profile path="/profile/" user="me" />
      <Profile path="/profile/:user" />
     </Router>
    </div>
   </IntlProvider>
  );
 }
}
```

## Step 6: Use Text to translate string literals

You are almost done, now you just need to replace the text in the page with `<Text>`. In this case, you will need to update the content of the home page (`src/routes/home/index.js`) by adding the `<Text>` inside the `<h1>` and `<p>` tags.

```jsx
import { Text } from 'preact-i18n';
const Home = () => ( 
  <div> 
    <h1> 
      <Text id="home.title">Home</Text> 
    </h1> 
    <p> 
      <Text id="home.text">This is the Home component.</Text> 
    </p> 
  </div> 
); 
export default Home;
```

![](https://thepracticaldev.s3.amazonaws.com/i/vsgnzsjx6bdg25inkuvj.jpg)

## Fallback Text

In order to prevent blank text being rendered in the page, you should set a fallback text to the `<Text>`. If you didn't include the definition for `unknown.definition`, the library will render any text contained within `<Text>…</Text>` as fallback text: 

```html
<Text id="unknown.definition">This is a fallback text.</Text>
// It will render this text: "This is a fallback text."
```

## Localizer and MarkupText

If you want to translate the text of the HTML attribute's value (ie: `placeholder=""`, `title=""`, etc …), then you will need to use `<Localizer>` instead of `<Text>`.

However, if you want to include HTML markup in your rendered string, then you will need to use `<MarkupText>`. With this component, your text will be rendered in a `<span>` tag.

In the example below, you are going to add few more lines of code to your definition file. `first_name` and `last_name` will be used for the `<Localizer>`'s example, and `link` for the example for `<MarkupText>`.

```json
{ 
  "first_name": "名",
  "last_name": "姓",
  "link": "這是個<a href='https://www.google.com'>連結</a>"
}
```

With this, you will able to use `<Localizer>` and `<MarkupText>` in the page. Please take note that you need to import `Localizer` and `MarkupText` to the `src/routes/home/index.js` file.

```jsx
import { Text, Localizer, MarkupText } from 'preact-i18n';
const Home = () => ( 
  <div> 
    <Localizer> 
      <input placeholder={<Text id="first_name" />} /> 
    </Localizer> 
    <Localizer> 
      <input placeholder={<Text id="last_name" />} /> 
    </Localizer> 
    <MarkupText id="link"> 
      This is a <a href="https://www.google.com">link</a>
    </MarkupText>
  </div>
);
export default Home;
```

![](https://thepracticaldev.s3.amazonaws.com/i/opgth3dzvj3mqknojxha.jpg)

## Templating

If you want to inject a custom string or value into the definition, you could do it with the `fields` props.

First, you will need to update the definition file with the `{{field}}` placeholder. The placeholder will get replaced with the matched keys in an object you passed in the `fields` props.

```json
{
  "page": "{{count}} / {{total}} 頁"
}
```

Next, you will need to add the `fields` attribute together with the value into the `<Text />`. As a result, your code should looks like this:

```jsx
import { Text } from 'preact-i18n'; 
const Home = () => ( 
  <div>
    <h2> 
      <Text id="page" fields={{ count: 5, total: 10 }}>
         5 / 10 Pages
      </Text> 
    </h2> 
  </div> 
); 
export default Home;
```

![](https://thepracticaldev.s3.amazonaws.com/i/heuay1pm410dxt5e7jbk.jpg)

## Pluralization

With `preact-i18n`, you have 3 ways to specific the pluralization values:

* `"key": { "singular":"apple", "plural":"apples" }`
* `"key": { "none":"no apples", "one":"apple", "many":"apples" }`
* `"key": ["apples", "apple"]`

For the next example, you will combine both pluralization and templating. First, you will need to update the definition file with the code below:

```json
{
  "apple": { 
    "singular": "Henry has {{count}} apple.", 
    "plural":"Henry has {{count}} apples." 
  }
}
```

Next, you will update the home page (`src/routes/home/index.js`) with the following code:

```jsx
import { Text } from 'preact-i18n'; 
const Home = () => ( 
  <div> 
    <p> 
      <Text id="apple" plural={1} fields={{ count: 1 }} /> 
    </p> 
    <p> 
      <Text id="apple" plural={100} fields={{ count: 100 }} /> 
    </p> 
  </div> 
); 
export default Home;
```
![](https://thepracticaldev.s3.amazonaws.com/i/rqvabjs585uxp2ywggpm.jpg)

With the method above, you will able to add pluralization and templating to your Preact application.

---

## Dynamically import language definition file

In a real-world scenario, you would like to set the language site based on the user's choice, which is either based on the `navigator.language` or the user can change the site language on their own.

However, in order to prevent you from importing all the unnecessary definition files to the project, you can import the language definition file dynamically by using `import()`. By doing this, you can import the language definition file based on the user's choice.

```jsx
import { Component } from 'preact'; 
import { IntlProvider } from 'preact-i18n'; 
import defaultDefinition from '../i18n/zh-tw.json'; 
export default class App extends Component { 
  state = { 
    definition: defaultDefinition 
  } 
  changeLanguage = (lang) => { 
    // Call this function to change language 
    import(`../i18n/${lang}.json`) 
      .then(definition => this.setState({ definition })); 
  }; 
  render({ }, { definition }) { 
    return ( 
      <IntlProvider definition={definition}> 
        <div id="app" /> 
      </IntlProvider> 
    ); 
  } 
}
```

In this case, you can call the `this.changeLanguage('zh-TW')` function to change the site language.

---

## Who's using preact-i18n?

I am using `preact-i18n` for my side project: Remote for Slides.

Remote for Slides is a Progressive Web App + Chrome Extension that allows the user to control their Google Slides on any device, remotely, without the need of any extra hardware.

Remote for Slides Progressive Web App supports more than 8 languages, which includes: Català, English, Español, Euskera, Français, Polski, Traditional Chinese, and Simplified Chinese.

In this side project, I am using the "dynamically import language definition file" method I mentioned earlier. This could prevent the web app from loading some unnecessary definition language files, thus this will improve the page performance.

Furthermore, the Remote for Slides Progressive Web App will set the language based on the browser's language (`navigator.language`), or based on the URL parameter (ie: [s.limhenry.xyz/?hl=zh-tw](https://slides.limhenry.xyz/?hl=zh-tw)), or the user can change it from the [Settings page](https://slides.limhenry.xyz/settings/language).

![](https://thepracticaldev.s3.amazonaws.com/i/wxgg59lyyisatdza3pgl.jpg)

You can learn more about Remote for Slides here:

{% link henrylim96/meet-remote-for-slides-a-new-way-to-control-your-presentation-slides-1o7n %}

---

## Resources

* [preact-i18n](https://github.com/synacor/preact-i18n)
* [Preact CLI](https://github.com/preactjs/preact-cli)
* [Preact](https://preactjs.com/)