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.

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:

  • For this project, we are using mostly smaller cloud providers. They are usually easier to use than the "big guys", with minimum setup required and similar prices.

  • We also don't want the whole system coming down if a service goes down in France. Having different clusters allows us to handle everything from the deployment to the maintenance, very easily.

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:

  • Consider from the beginning if your app will have an international audience, and build it with that in mind. You never know if you'll have to rewrite a whole app just to make it work in another language.
  • This case didn't require us to think about time zone management, but that's definitely something you should also keep in mind.
  • Think about the infra architecture for your app. You might not need to have a different cluster for every country, or you might be using AWS for all your deployments. That's fine as well, as long as you have a clear picture of the whole process and architecture.
  • Know your tools. Our knowledge of Rancher and GitLab have simplified our CI/CD processes, and allow us to work smoothly having a unified work centre.
  • Don’t be afraid to try new things. ;)

If you have any questions or comments, you can shoot me an email at 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, / +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.

Posts authored by alex

Contact us

We are here to help your company's technology bloom.
So do not hesitate to contact us in any matters!

Read more insight in our blog