Generates Elm types and functions from i18n key/value JSON files.
The tool is meant as an aid if you are using a centralized service to handle the translation of your i18n resources but then need to import the i18n keys/values into your Elm application in a type-safe way.
Note: If you like code that generates code, you might like my other project which turns JSON-schema specs into Elm types+decoders+encoders.
This project requires that you already have elixir
and its build tool mix
installed. This can either be done with
asdf, using the included .tool-versions
file, or
however you and your operating system prefers to install applications.
- Clone this repository:
git clone [email protected]:dragonwasrobot/i18n-to-elm.git
- Build an executable:
MIX_ENV=prod mix build
- An executable,
i18n2elm
, has now been created in your current working directory.
Run ./i18n2elm
for usage instructions.
The executable expects one or more json files with the naming scheme: <language code>_<COUNTRY CODE>.json
, e.g. en_US.json
or da_DK.json
, and that each
json file is a simple dictionary of i18n keys and values, for example:
{
"Hello": "Hej, {0}. Leder du efter {1}?",
"Next" : "Næste",
"No": "Nej",
"Previous": "Forrige",
"Yes": "Ja"
}
A complete example of input and output code can be found in the examples
folder.
If we supply i18n2elm
with the folder examples/input-i18n-json
, containing
the two JSON files, da_DK.json
:
{
"Hello": "Hej, {0}. Leder du efter {1}?",
"Next" : "Næste",
"No": "Nej",
"Previous": "Forrige",
"Yes": "Ja"
}
and en_US.json
:
{
"Hello": "Hello, {0}. Is it {1} you are looking for?",
"Next" : "Next",
"No": "No",
"Previous": "Previous",
"Yes": "Yes"
}
it produces the following Elm files, Translations/DaDk.elm
:
module Translations.DaDk exposing (daDkTranslations)
import Translations.Ids exposing (TranslationId(..))
daDkTranslations : TranslationId -> String
daDkTranslations tid =
case tid of
TidHello hole0 hole1 ->
"Hej, " ++ hole0 ++ ". Leder du efter " ++ hole1 ++ "?"
TidNext ->
"Næste"
TidNo ->
"Nej"
TidPrevious ->
"Forrige"
TidYes ->
"Ja"
and Translations/EnUs.elm
:
module Translations.EnUs exposing (enUsTranslations)
import Translations.Ids exposing (TranslationId(..))
enUsTranslations : TranslationId -> String
enUsTranslations tid =
case tid of
TidHello hole0 hole1 ->
"Hello, " ++ hole0 ++ ". Is it " ++ hole1 ++ " you are looking for?"
TidNext ->
"Next"
TidNo ->
"No"
TidPrevious ->
"Previous"
TidYes ->
"Yes"
and Translations/Ids.elm
:
module Translations.Ids exposing (TranslationId(..))
type TranslationId
= TidHello String String
| TidNext
| TidNo
| TidPrevious
| TidYes
and finally Translations/Util.elm
:
module Translations.Util exposing (parseLanguage, translate, Language(..))
import Translations.Ids exposing (TranslationId)
import Translations.DaDk exposing (daDkTranslations)
import Translations.EnUs exposing (enUsTranslations)
type Language
= DA_DK
| EN_US
parseLanguage : String -> Result String Language
parseLanguage candidate =
case candidate of
"da_DK" ->
Ok DA_DK
"en_US" ->
Ok EN_US
_ ->
Err <| "Unknown language: '" ++ candidate ++ "'"
translate : Language -> TranslationId -> String
translate language translationId =
let
translateFun =
case language of
DA_DK ->
daDkTranslations
EN_US ->
enUsTranslations
in
translateFun translationId
which contains:
- The Danish translations as a function,
daDkTranslations
, - the English translations as a function,
enUsTranslations
, - all the translation IDs as a union type,
TranslationId
, - a union type capturing all available languages,
Language
, - a function to parse a string into a
Language
,parseLanguage
, and - a function to translate a
TranslationId
for a givenLanguage
into a concreteString
value.