State of Localization (l10n) and v0.19

Thank you for taking the time to answer!

I like the whole having placeholders as fixed arguments, so the compiler checks them. Is there a reason why the Locale has to be supplied as an argument. I think, the number printing function could be baked into the message. So having something like

aMessageWithANumber : { a | count : Float } -> String
aMessageWithANumber args =
  String.concat
    [ "This will take "
    , En.defaultNumberPrinter args.count
    , " seconds"
    ]

Or, does it have to be possible to change the formatting of the numbers within the program? If I remember correctly, in FTL you can specify how a number can be formatted within the message, so maybe that should not be changable in the code.

But then on the other hand, I could imagine that for example, if you render a money amount, you may want to have the possibility to change the language and the way the amount is displayed (100 $ or $100 for example) independently from each other. So maybe having some message function aMoneyAmount : Region -> { a | amount : Float } -> String then.

Incorporating HTML is really a tricky question. Is there actually any HTML allowed within FTL messages or is it only things like <b>, <strong>, … without arguments? I think it would be also important to make it possible to use other Html-like packages like style-elements, or accessible-html.

I’m not sure how important it is, to be able to write the translations within Elm and then export them to FTL. Technically it is possible without having to parse the Elm code, if the translations are modelled as serializable data instead of functions. So one would introduce a type Translation args = Translation String (args -> String), where the first string is the serialization and the function is the printer of the actual message. The simplest constructor would just be s text = Translation text (\_ -> text). So, one has

aMessageId : Translation args
aMessageId =
  s "This is a message"

And printing it would need some helper print : Translation args -> args -> String, which can be called in the dispatcher functions, so the actual functions, the user uses would be something like aMessage : Locale -> { ... } -> String again.

One then can define other helpers to get placeholders, …, so the other message would look sth like

aMessageWithAnArg : Translation { args | name : String }
aMessageWithAnArg =
  concat
    [ s "Hello "
    , string .name "name"
    , ", welcome to our website"
    ]

Where concat and string are defined such that they print the string correctly, but also take care of serializing the translation to the FTL syntax. One downside of this approach can be seen here, as every accessor has to be given a string name so serialization can be done.

What I really like about this approach is that, it makes it possible to gradually introduce these messages into your codebase:

  • You maybe start with something like
view email =
  Html.div []
    [ Html.text "Is "
    , Html.b [] [ Html.text email ]
    , Html.text " your email address?"
    ]
  • Then you can refactor it into
view email =
  Translation.asHtml <|
    concat
      [ s "Is "
      , b [] <|
          string .email "email"
      , s " your email address?"
      ]
  • Later you move this into a separate module Translations.En. So, the code looks like
view email =
  Translation.asHtml Translation.En.emailInfo
  • Then, if you need translations to other languages, you can run some tool on this package and generate the FTL files out of them, translate them (or let other people translate them) and generate Elm code for the other languages.

it would also make it possible to use the number formatting functions, without having to introduce another tool into your build chain.

What do you think about this? :slight_smile:

1 Like