Jabberwocky

snicker-snack!

Internationalization With Yesod

| Comments

Internationalization with Yesod

Internationalization can be a pain, but it’s often a necessary one. Especially in Europe, composed of a patchwork language communities which correspond only marginally to national borders, it’s often mandatory.

Fortunately Yesod offers good tools for that, allowing you to work through your app and hand off a friendly YAML file to your translators.

There’s good guides in the Yesod book, but they often explain the why of a feature more than a how (also interesting, but less immediately practical). This is a small “Getting started” guide for internationalisation with Yesod.

We need a language switcher

Yesod lets you change the language with

1
setLanguage :: String -> GHandler sub master ()

Often the first step is to allow users to set the language from the web interface so that you can see immediately if what you’re doing is working. Language switch handler:

1
2
3
4
5
6
postLangR :: Handler ()
postLangR = do
    setUltDestReferer
    lang <- runInputPost $ ireq textField "lang"
    setLanguage lang
    redirectUltDest HomeR

setUltDestReferer and redirectUltDest will make sure that if you decide to switch languages, you’ll still be on the page you were on before.

Where do the translations go

The translations are all stored in a YAML formatted file.

1
messages/<2-letter language code>.msg

OR

1
messages/<2-letter language code>-<2-letter locale code>.msg

Simple vanilla translation

1
Home: Startseite

Use in the template:

1
_{MsgHome}

Or using a variable or more (with a type, of course)

1
Price price@Int: "This will cost you #{show price} "

Use in the template:

1
_{MsgPrice priceVar}

For your own types you can try and define a better show or even define custom functions to display it in the appropriate language. From Yesod docs:

1
2
3
4
5
6
7
8
9
renderEn Hello = "Hello"
renderEn (Greet name) = "Welcome, " <> name <> "!"
renderHe Hello = "שלום"
renderHe (Greet name) = "ברוכים הבאים, " <> name <> "!"
instance RenderMessage MyApp MyMessage where
    renderMessage _ ("en":_) = renderEn
    renderMessage _ ("he":_) = renderHe
    renderMessage master (_:langs) = renderMessage master langs
    renderMessage _ [] = renderEn

How to render templates

Your normal

1
$(widgetFile"templateName")

fitted in a layout: works out of the box.

hamlet quasiquotation:

1
2
3
giveUrlRenderer [hamlet|
  ...
|]

becomes

1
2
3
4
import Text.Hamlet (ihamlet)
ihamletToHtml [ihamlet|
  ...
|]

If you’re using hamletFile (which I’m told is not the approved way)

1
giveUrlRenderer $(hamletFile "templates/patient-details.hamlet")

becomes

1
ihamletToHtml $(ihamletFile templates/patient-details.hamlet")

page title

1
setTitle "Home"

becomes

1
setTitleI MsgHomeTitle

Custom bits

Sometimes, you may want to create custom helpers which don’t fit into the above framework. In that case you need to retrieve the message renderer from the Handler monadic context, and work with that.

1
2
render <- getMessageRender
render MsgModuleManagement  -- -> Text

That should help you do what you need to get your Yesod web app internationalized. Have fun :)

Comments