Some time ago, I had a crack in internationalization, and I came up with the following setting:
- defines a language in a global
model
- have a very simple function that will be used in modules and
view
functions - the function has a signature
localString : Language -> String -> String
localString
basically searches the global dictionary to find the translation from the word you provide in the language you provide.- it will always return a
String
, the default is the original word if it cannot find the word you provide, or if it cannot find a translation in the language you provide. - save the global dictionary (and auxiliary) of the NOT function in the model, but in a separate file (this is pretty static data that will not change at run time).
- The
Language
type is the Union Type so that we have only "approved" languages. - the actual dictionary uses string conversions. The
Dict
type does not allow the use of strong types as a key.
Thus, the use of internationalization has minimal impact on the rest of the code:
- You need to add
Language
to your model
(which you can get through the JS port) You can use short and readable code in your views for translation, for example
p [] [ text <| localString model.language "car" ]
All hardcoded strings in your own code remain one simple default language to preserve the rest of your code.
This is the essence of what I worked on, you can copy / paste to elm-lang.org/try (not fully tested functionally or quickly, wise with a lot of lines and translations)
import Html exposing (div, p, text) import Dict exposing (Dict) -- Manage your languages below type Language = English | Spanish | French defaultLanguage : Language defaultLanguage = English languageToKey : Language -> LanguageKey languageToKey language = case language of English -> "English" Spanish -> "Spanish" French -> "French" keyToLanguage : LanguageKey -> Language keyToLanguage key = case key of "English" -> English "Spanish"-> Spanish "French" -> French _ -> defaultLanguage english : LocalWord -> (Language, LocalWord) english word = (English, word) spanish : LocalWord -> (Language, LocalWord) spanish word = (Spanish, word) french : LocalWord -> (Language, LocalWord) french word = (French, word) -- Internal stuff type alias Word = String type alias LocalWord = String type alias LanguageKey = String type alias Dictionary = Dict Word WordDict type alias WordDict = Dict LanguageKey LocalWord init : Dictionary init = Dict.fromList [] newLocalWord : Word -> (Language, LocalWord) -> Maybe WordDict -> Maybe WordDict newLocalWord word (localLanguage, localWord) wordDict = wordDict |> Maybe.withDefault (Dict.fromList []) |> Dict.insert (languageToKey defaultLanguage) word |> Dict.insert (languageToKey localLanguage) localWord |> Just addTranslation : Word -> (Language, LocalWord) -> Dictionary -> Dictionary addTranslation word newTranslation dictionary = dictionary |> Dict.update word (newLocalWord word newTranslation) localString : Language -> Word -> LocalWord localString language word = let wordEntry = Dict.get word globalDictionary localLanguage = languageToKey language in case wordEntry of Just wordDict -> Dict.get localLanguage wordDict |> Maybe.withDefault word Nothing -> word add : Word -> List (Language, LocalWord) -> Dictionary -> Dictionary add word translationList dictionary = List.foldl (addTranslation word) dictionary translationList -- BUILD DICTIONARY BELOW globalDictionary : Dictionary globalDictionary = init |> add "Hello" [ spanish "Hola", french "Bonjour" ] |> add "Man" [ spanish "Hombre", french "Homme" ] |> add "Child" [ french "Enfant" ] -- For Elm-lang Try only localModel = { language = Spanish } main = div [] [ p [] [ text <| "Hello in Spanish: " ++ localString localModel.language "Hello" ] , p [] [ text <| "In dictionary, but not in Spanish: " ++ localString localModel.language "Child" ] , p [] [ text <| "Is not in dictionary: " ++ localString localModel.language "Car" ] ]
source share