We Build Websites

Elm i18n and Type Safety
By Amitai Burstein (@amitaibu) // 24 Mar 2016

We’ve reached the point we needed to translate one of our Elm apps to multiple languages.

We looked for existing solutions and stumbled upon the elm-i18n library. This of course made us happy, to see that someone has already solved the problem for us. However, when we looked at the example something felt missing. Type safety.

In Gizra we deal most hours of our working days with PHP, so you can say we are fearless developers. That is, we hope and believe our code is right, and worst case we know we will catch our bugs on run time.

But Elm can make us better developers, and give us some safety!

Type Safety

If you look at show (i18nText "I am {0} years old." ["24"]) you might notice that there is no guarantee that the string is even translated to other languages. Further more, are we sure that we are sending the correct number of arguments (in the above case it’s 24)?

I jumped into the Elm channel and had a very nice chat with @lukewestby, elm-i18n library author, which pointed out some really good points (like the need for type safety not just for the strings, but for the arguments as-well).

This was no doubt yet another case for the great power of open source, collaboration, and friendly community: I came to the chat with a half baked solution, and left with a complete one.

The entire logic of the translation is in this short file. We define our translation as types.

type TranslationId
  = Login
  | WelcomeBack {name : String}

All the strings in our app will be represented in this TranslationId type, along with their required arguments. By having this, we can now do things like:

div [] [text <| translate Spanish Login]
-- Result: "Por favor haga login"

div [] [text <| translate English <| WelcomeBack {name = "Elm lover"}]
-- Result: "Welcome back Elm lover"

This is one of the great powers of Elm! By having those types, we let the compiler help us with preventing silly mistakes.

We can now never try to translate a non existing string. We also can not try to call a string, without passing all the arguments. Or, we can’t ever miss translating an existing string.
It’s all covered by Elm’s types.

Go ahead, and see the View.elm file or the unit tests.

Next steps

Developers don’t necessarily speak all the app’s languages. I mean, we are a smart bunch, but there’s a limit, right? :)

What often happens is that translation is written in different text files (e.g. in PO format). So our next plan is to write a script that will get as input those files, and will auto-generate an Elm file that will contain valid code with all the strings.