05 Jun 2019 - by Alejandro Ramirez

Working with international apps

For the first time in my life, I had the opportunity to work with an international app. By international app I mean an app that goes to different countries in different languages. How on earth did we manage to get it working?

In this post, I will explain the approach we took to solve this challenge, how we faced the issues that came along the way, and some afterthoughts.

The context

The main app consists of a back-end which also provides some internal services. On top of the APIs provided by the back-end, there’s another web app for public usage, which will be the focus of this post.

Time to get technical

From the beginning we knew that this app was going to be used in several languages, so we built it with that in mind from day one. This might have been a hassle at the beginning, with just a few <h1>s and <p>s, but eventually as the app started to grow, it paid off by not having to rewrite everything once we needed to use the different languages.

Our client application was built using Vue. To handle the translations, the vue-i18n plugin had the best support. With this plugin, we can split every language on a simple JSON file which we can share with our client for translation without touching any code.

vue-i18n works like this: You use the $t object which is injected to the global Vue instance, and pass as parameter the key of the text to use.

<div id="app">
  <p>{{ $t("message.hello") }}</p>

Simple, isn’t it?

This app also uses moment.js to manage the display and localization of the dates. It comes very handy to handle the formatting and translation of all the date related texts. Moment uses the same locale that vue-i18n is using, so we have consistency along the app.

However, Moment does not react to Vue updates, therefore we need to manually refresh the components for the language changes to be applied. To avoid refreshing the whole website when the language is changed, we came up with this approach:

<!--  The `key` binds the whole app and causes a refresh when-->
<!--  switching languages, which is required for localized-->
<!--  moments to work.-->
  <div id="app" :key="locale">
    <router-view id="content"/>

This binds the app component to the language key. This means that when the key (language) changes, the app will be re-rendered without forcing the user to manually refresh the whole website and lose their progress. Works like a charm.

The most important (and probably overlooked) feature is the language switcher. We provide a simple language switcher, where we show the whole language name. With this, we avoid binding a language to a flag and the confusion that could come with it. Also, we use the whole name and avoid abbreviations to make it clearer which language it is.

I know, flags look colorful and nice, but the experience of our users goes first. You can read more about language switch design here.

Last, for this specific case, we have different build configurations, one for each language. So when we’re deploying this to different clusters, it will have the default language according to the country.

However, we prioritize the browser language over the default. We found this to provide better UX. Usually the language configured (either on the phone or the computer) is the one we’re expecting to read, so we try to prioritize that over the default.

For example, I could be using the app from the Finnish website, but I don’t speak any Finnish. If my browser is configured to English by default, then I won’t have to struggle to find my way around to change the language. Now, if a native Finnish speaker has their phone configured in Finnish, the website will be displayed in Finnish and they will be happy. :)

Taking it to production

Once we have the app ready, it’s time to take it to production!

Our development process is based on GitFlow, following the develop-staging master-production model. However, we extended it to fit our needs. We have a single development branch which goes to all the country clusters at the same time, so we can have a consistent staging environment for our stakeholders to test the latest features. When it comes to the production environment, we have a different master branch for every country. This allows us to have more control over what and when to deploy to each country, and have different configurations depending on the country.

Our flavor of GitFlow

As I mentioned above, we have a different cluster per environment and country. Among the reasons we had for using this architecture, these are the most important ones:

During the writing of this post, we went precisely through this situation. Our cloud provider had an outage which affected one of our nodes and took our staging environment down for hours. However, since we have every country and environment on different clusters, the rest of the countries were safe.

To manage the different clusters, we use Rancher, an amazing tool manage Kubernetes clusters. It provides an interface on top of Kubernetes which simplifies the deployment of any app to just some clicks.

Rancher combined with the DevOps automation tools provided by GitLab make it extremely easy to configure different pipelines, which will then deploy the app to various environments in the corresponding country/clusters.

Closing it up

Overall, based on my learnings from this experience, these are some of my key takeaways:

If you have any questions or comments, you can shoot me an email at alex@montel.fi or via LinkedIn.

If you’d like to avoid the common pitfalls when developing globally distributed apps, or other type of quality software, don’t hesitate to contact us. Tuba, tuba@montel.fi / +358 400636636 will be happy to tell more about our intergalactic services.

Alejandro Ramirez

Full-Stack Engineer and Certified Taco Eater

Alex is a full-stack developer imported from the warm lands of Mexico. He enjoys writing neat code, learning new stuff, and palju.

Read more insights in our blog