Breaking news! I just launched Coparrot, a no-install and no-configuration mock server for mobile and web developers, on Product Hunt!
Please check it out and give it some love ❤️!
December 02, 2018 in articles
Switzerland has four national languages: German, French, Italian, and Romansh. The first three languages are the languages which are used by the majority of the people. For that reason most web apps I made here need to support at least those three and English in some.
When developing our first web app, I explored the popular React library for internationalization, React Intl. But I decided not to use it since it’s overkill for our simple requirement: we just need to display different strings based on the selected language.
Our solution was just to use a simple Javascript object which contains the strings for each of the languages.
// @flow
// locales.js file
// you can separate the translations object into different files if you want
export const de = {
title: 'iTheorie Online-Lernen',
welcomeTo: 'Willkommen bei $title',
// and so on ...
}
export type StringsType = typeof de
export const fr: StringsType = {
title: 'Apprentissage en ligne iTheorie',
welcomeTo: 'Bienvenue chez $title',
}
To use the translation, we just need to pass the object to the components that need them.
export const TitleComponent = ({
strings = require('./locales').default,
}: {
strings: StringsType,
}) => <p>{strings.siteTitle}</p>
We use Flow to add type annotation for our translation object. By using it, not only we can prevent missing translation strings, we can also get autocompletion in editor, like Code, when using it in our React components.
To handle language switching, we can use Redux or React’s built-in Context. When using Redux, we can create a reducer that will return the translations object based on the selection language.
export const defaultState = {
selected: 'de',
messages: require('./locales').de,
}
export default (
state: StringsType = defaultState,
action: {
type: 'SET_LANGUAGE_ACTION',
payload: {
selected: string,
messages: StringsType,
},
}
): LanguageState => {
switch (action.type) {
case 'SET_LANGUAGE_ACTION': {
return {
...state,
...action.payload,
}
}
default:
}
return state
}
To support formatted message, we created a small function to replace certain placeholder strings with the actual string.
const escapeRegex = (value: string) =>
value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, '\\$&')
export const formatted = (text: string, replacement: Object): string => {
var newText = text
Object.keys(replacement).forEach(key => {
let regex = new RegExp(escapeRegex(key), 'g')
newText = newText.replace(regex, replacement[key])
})
return newText
}
For example, we have a translation string with key welcomeTo
and value Willkommen bei $title
in the translation object for German. We use the formatted
function to replace $title
with iTheorie Online-Lernen
export const Greeting = ({ strings }: { strings: StringsType }) => {
const stringToUse = formatted(strings.welcomeTo, {
$title: 'iTheorie Online-Lernen',
})
return <p>{stringToUse}</p>
}
This solution is satisfying for several reasons:
Articles, drawings, and codes by Nico Prananta, a software developer (iOS and web) and digital artist (for fun!) in Zürich, Switzerland. I'm on Twitter.
For Indonesian iOS Developers! Gabung yuk tiap hari Sabtu jam 16:00 WIB di Belajar Bareng Swift Mingguan bareng temen-temen iOS developer di Swift Study Group Indonesia.