How to translate a Symfony and React project.

Florent Destremau
Feb 24, 2020 · 5 min read
Image for post
Image for post
Translating in an international world — Photo by Leonardo Toshiro Okubo on Unsplash

When starting a new web app, I tend to think that internationalization is not a first-version concern. However, as soon as your side project starts becoming a real world app, your user base grows, and when you don’t live in in an English-speaking country (such as France), you might want your application to be available in English.

Although it’s often pretty straightforward to handle internationalization in Symfony or in React on their own, handling it in both at the same time appeared to be quite the challenge for us at Windoo.

In this post I will talk about how we maintain a single translation file for both Symfony & React. I will not address the user-side of handling locale setting as it is not the challenge here.

Translating in a Symfony project

composer require symfony/translation

If you are using twig for your templates (pages, emails…), you will need to have your translations in a file such as messages.fr.yml and messages.en.yml and use the following syntax in your templates:

<a href="{{ path('booking_list') }}">
{{ 'common.see_all'|trans }}
</a>

If you want to translate a message in a controller or in service, you need to inject the TranslatorInterface service:

public function greet(TranslatorInterface $translator): Response
{
return new Response($translator->trans('common.greet'));
}

You end up with a big yaml file such as:

common:
post: Post
greet: Hello !
see_all: See all
hide: Hide
home: Home
post:
title: Post title

This way, you can use the Yaml keys in your translation. You can handle your local with Symfony, have two files, one in English, one in French, and there you go!

Before some people comment I should be using xliff format instead, I can already address the matter: I find Yaml translations files a whole lot easier to maintain and visualize on a single-dev project. The xliff format is the official recommended by Symfony’s best practices, but the conversion is pretty easy if needed. More on that later in the article.

Translating in a React project

import { useTranslation } from 'react-i18next';
const { t, i18n } = useTranslation();
export default () => <h1>{t('common.greet')}</h1>

And your translations’ initialization would look like this:

import i18n from "i18next";
import { initReactI18next } from "react-i18next";
i18next
.use(initReactI18next)
.init({
resources: {
en: {
common: {
greet: 'hello!'
},
post: {
title: 'Post title'
}
},
fr: {
common: {
greet: 'Bonjour !'
},
post: {
title: 'Titre d\'article'
}
}
}
});

So you basically need to have a JSON file that can be parsed in a Javascript object.

Using React and Symfony translations together

For starters, the Symfony and React-i18n format are not exactly compatible. React-i18n doesn’t read xliff format, neither yaml…actually it only wants a javascript object in JSON-style as an input file. So this way, that’s also one of the reason we didn’t use the xliff format for storing our translations.

In order to use the same source file for both projects at Windoo, we decided to script an on-the-fly conversion of the Symfony format to be injected into the React project. To do this we used a yaml loader to convert the yaml files into a javascript object.

// utils/i18n.jsimport i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import frYaml from 'js-yaml-loader!./translations/messages.fr.yml';
import enYaml from 'js-yaml-loader!./translations/messages.en.yml';
i18n
.use(initReactI18next)
.init({
resources: {
fr: {
translation: frYaml,
},
en: {
translation: enYaml,
},
},
lng: (window && window.locale) || 'fr',
fallbackLng: 'fr',
});

export default i18n;

This way , we only need to update one type of file (and I prefer the Yaml format, it’s prettier to look at and easier to search), and both projects can use the shared translations. All good to go, right ?

Beware of pluralization and parameters !

Here is the react-i18n syntax for pluralization in the keys dictionary:

{
"key": "item",
"key_plural": "items",
"keyWithCount": "{{count}} item",
"keyWithCount_plural": "{{count}} items"
}

And here is the Symfony syntax for the same thing:

key: "item|items",
keyWithCount: "%count% item|%count% items"

And I’m not even talking about the full format for each language. We only translate French and English (as of today at least), so we kept using the simple syntax.

So the number of keys is not the same in React and this makes it way more difficult to transpose from Symfony-Yaml to i18n-JS. Ideally we would like to use the ICU Format for both, but I could find sufficient documentation for this. We might come back to it later in due time.

Parameter format

// replace all %param% with {{param}} 
elem = String(elem).replace(/%([^%]+(?=%))%/gi, '{{$1}}');

Then we had to split the plural keys in Symfony

// newObject = {}; -- the new translation dictionary//while looping on the keys
if (elem.includes('|')) {
const plural = elem.split('|');
newObject[key] = plural[0];
newObject[`${key}_plural`] = plural[1];
}

And this way, we made a full transposition of our translations on-the-fly from Symfony to React. Here a complete example in one gist:

a full i18n.js service you could import in your project

After that, you simply import your new i18n.js service into your React components and voilà !

I will write a follow-up on how we improved this workflow with a translation service in the middle, using Localize.

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Florent Destremau

Written by

CTO & Founder @ Windoo, a platform for happiness at work ! https://windoo.fr

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Florent Destremau

Written by

CTO & Founder @ Windoo, a platform for happiness at work ! https://windoo.fr

The Startup

Medium's largest active publication, followed by +773K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store