Goals:
- Elm's typechecker enforces that
- each localization has all the same messages.
- the messages you are referring to in your view code exist.
- Only the localized strings for the current locale are sent across the wire
- Messages which have interpolations are typechecked as well
It's not even a library; just a pattern...
type alias I18n =
{ header :
{ logIn : String
, signUp : String
}
, checkoutFlow :
{ placeOrder : String
, youHaveNItems : Int -> String
}
}
-- could break into smaller named aliases
module Localizations.EnUs where
english : I18n
english =
{ header =
{ logIn : "Log In"
, signUp : "Sign Up"
}
, checkoutFlow =
{ placeOrder = "Place Order"
, youHaveNItems = \n -> "You have " ++ toString n ++ " items" -- I want string iterpolation!
}
}
Depending on the approach, you may or may not need to pass the messages for the current language... (See below)
viewCheckout : Signal.Address Action -> I18n -> Model -> Html
viewCheckout addr i18n model =
p [] [text <| i18n.checkoutFlow.youHaveNItems model.numItems]
button [...] [text i18n.checkoutFlow.placeOrder]
Both achieve goals 1 & 2; only the second achieves goal #3.
Generate JSON values for each localization; use a port to supply one for the current locale (a value of type I18n
). So you have port messages : I18n
in your main module, and have to pass that down through all of your view functions. The server is responsible for putting the localization for the current locale onto the page as a JSON blob and passing it in when the module is initialized.
Evan had the same idea in this previous thread.
- Pros:
- No compile-time funny business compared w/ approach #2 below
- Easy to generate localization JSON files from other sources, e.g. Rails YML localization files — sharing translations between Rails and Elm could be nice.
- Cons:
- You can't serialize template functions (e.g.
youHaveNItems
in the example) to JSON or pass them through ports. - Have to add a parameter to each view function. If you're using a port to set the current language it seems hard to get away from this (as discussed in this previous thread); e.g. it seems elm-i18n would need it as well.
- You can't serialize template functions (e.g.
Put each localization in its own Elm module (e.g. Localizations.EN
), compile a version of the app for each locale, using some kind of compile-time template for each view module which has i18n text: import Localizations.{{ currentLocale }} as I18n
, and serve the version corresponding to the user's locale.
Precedent: Google Web Toolkit compiled a version for each (browser, locale)
combo. Never having used GWT, I can't say how well that worked out, but it seems reasonable...
- Pros
- Template functions are just normal Elm functions; no hard-to-typecheck templating library necessary
- Defaulting to another language is easy — if you haven't translated a message, just import another localization and use its version of that message.
- Don't have to add an
I18n
argument to view functions, since it's an import. Just refer toI18n.myMessage
.
- Cons
- Harder to share w/ Rails — you would have to generate one's format from the other's...
- Complicates your Elm build; have to munge your imports and build one for each locale
- Complicates your serving & caching; have to serve & cache multiple versions