Skip to content

Instantly share code, notes, and snippets.

@sebleblanc
Last active July 26, 2020 06:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebleblanc/6bee5a64b6618c49a336b5f682caad51 to your computer and use it in GitHub Desktop.
Save sebleblanc/6bee5a64b6618c49a336b5f682caad51 to your computer and use it in GitHub Desktop.
Bilingual (tri-, quadrilingual…) strings
I had to deal with bilingual strings from an XML API (Canadian weather
website) in a short script that I wrote. I wanted a way to handle
those that was lightweight and simple.
First we define a sum type for languages that we support. Thanks to
this type, we can ensure through exhaustive pattern matching that we
have always included all supported languages in a string. It is so
simple to implement that it remains simple while still making use of
the safeties of the type system.
> data Lang = English | French -- | Spanish | Japanese …
The following type synonym will make type signatures clearer:
> type Translated a = Lang -> a
Next is a helper function. Its definition depends on the `Lang`
type. With several languages, it might get repetitive. Also, it needs
all possible translations to be included, but keep in mind that this
is a quick and dirty solution meant for two or three languages at
once. In a pinch, you may always pass `undefined` as one of the
translated strings, but you would have to know elsewhere if the
string is missing, or else you will get a `Prelude.undefined
exception:
> biling :: String -> String -> Translated String
> biling e _ English = e
> biling _ f French = f
It is invoked like so:
> let greeting = biling "Hello" "Bonjour"
To retrieve a translated string, one only has to pass the language to
the translated string, as it is after all just a function awaiting a
parameter. In the following snippet, the first line yields "Hello",
while the second yields "Bonjour":
> greeting English
> greeting French
If you want an alternative where you can expand to more languages
later, the following will demonstrate a method of doing it.
First we declare a default language code. We chose English.
> instance Default Lang where def = English
Once this is set, we add a catch all case that will defer to the default
language.
> greeting English = "Hello"
> greeting French = "Bonjour"
> greeting _ = greeting def
The compiler will warn you of redundant pattern matching, but it is
better than a non-exhaustive pattern error.
When you are ready to support an additional language, you only have to
add it to the sum type. Your program will still compile, deferring to
the default language when a translation is not provided.
> data Lang = English | French | Italian
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment