Localization

i18n stands for internationalization (i, 18 letters, n). Shoutem’s localization is based on i18n-js by fnando. Each extension has it’s own set of translations located in the app segment in the translations directory. These extension-level translations are fallbacks for when a new extension is made and an existing language file doesn’t have it’s strings.

Language files are uploaded by the app owner using the shoutem.i18n settings page.

Translating your App

To use your translation, you can download and unzip this file and replace all the English strings with anything you like. Either a different language or alternative English strings.

Once you’ve extracted the en.json file, rename it to the language you want to translate your app to, e.g. de.json, for German.

Then translate all strings from English to German. For example:

#file: en.json
"navBarMapViewButton": "Map"

Is translated to German:

#file: de.json
"navBarMapViewButton": "Karte"

We have short examples of how to translate more complex strings with pluralization and variables.

After translating the strings to your chosen language, you can validate the JSON for any syntax errors using a free and easy to use tool like JSONLint.

Implementing i18n in your Custom Extension

Think back to Getting Started. We made a simple extension with a line of text in one screen:

#file: tom.restaurants/app/screens/List.js
export default class List extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.text}>Let's eat!</Text>
      </View>
    );
  }
}

To implement translations to that we’d have to import I18n:

#file: tom.restaurants/app/screens/List.js
import React, {
  Component
} from 'react';

import {
  StyleSheet,
  Text,
  View
} from 'react-native';

import { I18n } from 'shoutem.i18n';

Then use the imported I18n:

#file: tom.restaurants/app/screens/List.js
<View style={styles.container}>
  <Text style={styles.text}>I18n.t(ext('letsEatMessage'))</Text>
</View>

Now we need to implement the fallback by creating a translations directory in the app segment:

$ mkdir app/translations && touch app/translations/en.json

And adding our extension’s string to it:

#file: tom.restaurants/app/translations/en.json
{
  "shoutem": {
    "restaurants": {
      "letsEatMessage": "Let's eat!"
    }
  }
}

This string has to be exported in app/index.js:

#file: tom.restaurants/app/index.js
import enTranslations from './translations/en.json';

// Constants `screens` (from extension.js) and `reducer` (from index.js)
// are exported via named export
// It is important to use those exact names

// export everything from extension.js
export * from './extension';

export const shoutem = {
  i18n: {
    translations: {
      en: enTranslations,
    },
  },
};

And that’s it!

Pluralized Strings

When editing strings that contain pluralization (e.g. 1 point vs. 2 points) we utilize the following format for the language file:

#file: en.json
{
  "shoutem": {
    "loyalty": {
      "pointsInStore": {
        "one": "{{count}} point collected.",
        "other": "{{count}} points collected.",
        "zero": "No points collected."
      }
    }
  }
}

And the following method inside the actual React Native component:

#file: shoutem.loyalty/app/components/PlaceIconView.js
const { place, points, onPress } = this.props;

return (
  <Caption>
    {I18n.t(ext('pointsInStore'), { count: points })}
  </Caption>
);

It’s important to use the count variable name specifically as described in i18n-js docs.

Variables in Strings

When editing strings that contain variables we utilize the following format for the language file:

#file: en.json
{
  "shoutem": {
    "auth": {
      "loggedInUserInfo": "Username: {{username}}"
    }
  }
}

And the following method inside the actual React Native component:

#file: shoutem.auth/app/screens/EditProfileScreen.js
const { user } = this.props;
const { name, profile_image_url: image } = user;

return (
  <Caption>
    {I18n.t(ext('loggedInUserInfo'), { username: name })}
  </Caption>
);

Implementing Fallback Strings in Custom Extensions

Fallback strings are made to make sure that each string has a translation if the language file (e.g. en.json) is missing a translation. They are implemented in each extension which has strings specific to itself. One such example is the BUY THIS BOOK string, which is specific to the shoutem.books extension. The fallback for this translation looks like this:

#file: shoutem.books/app/translations/en.json
{
  "shoutem": {
    "books": {
      "buyButtonText": "BUY THIS BOOK"
    }
  }
}

As you can see, it’s identical to the full language file, however, it only contains the strings for that specific extension.

You’ll also have to export these translations from app/index.js.

#file: shoutem.books/app/index.js
import enTranslations from './translations/en.json';

export const shoutem = {
  i18n: {
    translations: {
      en: enTranslations,
    },
  },
};