How to implement translations in React using React hooks

How to implement translations in React using React hooks

There are lot of packages offering translation capabilities for React, however I found them a bit of a overkill so as part of leveling up my react skills I have decided to come up witch my own simple solution.

It consists of 3 parts

  1. Redux
  2. Custom hook
  3. Translation files

Let’s take closer look with some example code.

Redux will store the current language and the language strings in it’s state and when the change language action is triggered it will update all components that use the useTranslation hook.

Let’s start with the action creators and the custom hook.

I like to store the action types as constant object so they can be imported if needed and if I need to change the key I just change it in one place. So as the first step I define the action type for the language switcher afterwards I create the action creator. You should be familiar with these steps.

When the action type and the action creator is ready let’s take a closer look on the custom hook. As you can see it’s pretty strait forward, first I connect to the redux store using the useSelector which will ensure refreshing data inside the useTranslation hook when the store changes and it will also provide us the data needed to translate things using the translate function. The translate function simple searches for existing translation in the given language, if it’s not found it falls back to default language which I hardcoded ‘sk’ (feel free to upgrade it to some system wide setting) and as a last resort if translation is not found, well just return the translation string key.

Last part of the useTranslation hook is to return array of data containing the function and the current language (it comes handy if you don’t want to connect to store from some component directly).

The reason I went with the array is simple, when I decide to use the hook when destructing the return value, I always know that the first param is the translation function and the second is the language.

import {useSelector} from "react-redux";
import {useEffect, useState} from "react";

export const actionType = {
	changeLanguage: 'TRANSLATION_CHANGE_LANGUAGE',
};

export const changeLanguage = function (lang) {
	return {
		type: actionType.changeLanguage,
		payload: lang
	}
};

export const useTranslation = () => {
	const store = useSelector(state => state.translationState);

	const translate = (key) => {

		if (store.translations[store.language] && store.translations[store.language][key]) {
			return store.translations[store.language][key];
		} else if (store.translations['sk'] && store.translations['sk'][key]) {
			console.log(key, 'is not translated, falling back to default language.');
			return store.translations['sk'][key];
		}

		console.log(key, 'was not found in any language.');
		return key;
	};

	return [translate, store.language];
};

For the redux store, it’s just a basic redux store like the action creator was.

I just simply import the language strings from JS files and pass them to the initial state. I plan in the future to connect it to the database and load them over and API call, but that’s for you to decide if you need it. For now let’s go with the JS files.

import {actionType} from "../actions/translation";

import sk from '@translations/sk';
import en from '@translations/en';

const initialState = {
    language: 'sk',
    translations: {
        sk: sk,
        en: en,
    },
};

export default function TranslationReducer(state = initialState, action) {
    switch (action.type) {
        case actionType.changeLanguage:
            return {
                ...state,
                language: action.payload
            };
        default:
            return state;
    }
}

And now for the translation JS file (en.js)

/*
 Structure: 'domain.keyword': 'translation string'
 */

export default {
	// General
	'mycomponent.current_language_is': 'Current language is %s',
        .... add more strings
}

Simple right? And the best thing is that it just works.

So let’s see some usage example

const MyComponent = () => {
    const [__, language] = useTranslation();

    return <>{__('mycomponent.current_language_is').replace('%s', language)}</>
}

Now if you create JS file with translations for another language and add it to the redux store state and you trigger the change language action, MyComponent should instantly update the text to that language you changed to.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

*

*

Categories
You may also like
Search