Runtime language switch to Elm (2023)

Internationali[s|z]ation (I18n) seems to be a deceptively complex problemfiction.

I have never had to consider the option of generating application translations as a pre-build phase of an application (for example,science fiction-i18n) or dynamically loaded (e.g.elm-i18 next) when working with i18n onrailsoPhoenix. Honestly, I still don't know what is "better" in the context of Elm. But I know I want to have runtime switchable languages ​​via a dropdown, so creating a sample page with dynamically loaded translations will be the main focus of this blog post.

I have usedtachyonsa lot lately about the style and how he plays with Elm, so here's what we're going to do:

  • recreate tachyonsDocumentation page for the Frame-Centered Title component.in elms
  • Add a custom language switch dropdown to the page
  • Provide some translations for the page onJSONFormat and allow dropdown to change language
  • Save the selected language inmemoria localso that any selected language is persisted across page refreshes and across sessions.
  • Explore generating Elm modules from JSON translation files to give translations some type safety

(If you want to go ahead and see the end result, feel free to clone mineexample elm-i18nRepo)

Let's start!

Update (December 21, 2018)

The version of Elm used in this post is 0.18. I still stand by the general points of the post, but some of the code is now deprecated. HeMaestroBranch ofexample elm-i18nRepohas been updated to Elm 0.19. So if you go ahead and run into issues, try matching them there with the updated codebase.

The complete original 0.18 codebase used in this post can be found at0,18 ramarest

Bootstrap a new Elm application

Create Elm Appwill help us start our app, so install it with:

npminstall - GRAMScrear-elm-app

Then generate a new application and initialize it.npm(using the default fields provided for the generatedpackage.jsonfile is fine) and install tachyons:

create-elm-app elm-i18n-exampleCDelm-i18n-examplenpm initnpminstalltachyons

Next, to import tachyons into the project, modify the generatedindex.jsfile looks like this:

src/index.js

matter "tachyons"matter "./principal.css"matter { First of all } outside "./Main.ulm"until application container = document.getElementById("root")And(application container) { First of all.embed(application container)}

Well if you runLaunch elm app,http://localhost:3000/It should open automatically and you should see a familiar splash screen letting you know that your Elm app is working.

Before we really get started, let's clean up some of the generated code given to us by the Create Elm app:

  • We do not needsrc/principal.csssince the tachyons take care of the hairstyle
  • So this app will not use service workerssrc/registerServiceWorker.jscan be removed
  • We won't be using the Elm logo from the splash screen, so it can be removed, along with any references to it.src/Main.elm

So run the following command and make the following changes:

rmsrc/main.css src/registerServiceWorker.js public/logo.svg

src/index.js

matter "tachyons"matter { First of all } outside "./Main.ulm"// ...

src/Main.elm

-- ...vista : Model -> HTML Newsvista Model = division [] [ h1 [] [ Text "Your Elm app is working!" ] ]

You should now have a very basic looking (but buildable) page, so let's lighten it up a bit!

Rebuild the Tachyon documentation page

Using the sample code in theFull screen centered title pageAs a guide (but with some minor adjustments), change theMajor. Science fictionModule definitions, import statements, andvistaFunction to rebuild the page:

src/Main.elm

Module First of all expose (first of all)matter HTML expose (HTML, Article, division, h1, first of all_, Text)matter HTML.Attribute expose (Class)-- ...vista : Model -> HTML Newsvista Model = permission classes = [ "bg-dunkelrosa" , "overflow tank" , "sans serif" , "Weiss" ] |> line.to connect "" |> Class In first of all_ [ classes ] [ Content ]Content : HTML NewsContent = permission ArticleClasses = [ "dt" , "vh-100" , "w-100" ] |> line.to connect "" |> Class divclases = [ "dtc" , "ph3 ph4-l" , "CT" , "v-means" ] |> line.to connect "" |> Class In Article [ ArticleClasses ] [ division [ divclases ] [ titular ] ]titular : HTML Newstitular = permission classes = [ "f6 f2m" , "f-subtitle-l" , "fw6" , "CT" ] |> line.to connect "" |> Class In h1 [ classes ] [ Text "Vertically centering things in CSS is easy!" ]

I think it's easier to scan and maintain the Tachyon classes in lists like this, but it also has the side effect of making the function definitions very long, so here we've split the content into three different smaller functions.

The use of utility-based CSS frameworks like tachyons andtailwindIt can seem daunting at first, with all the mnemonics you seem to have to memorize, so I always keep themThe tachyon style table.Open it in a browser tab for quick reference, and if this is your first look at tachyons, I recommend doing the same.

In either case, your page should now look like the following screenshot:

Runtime language switch to Elm (1)

If not, check your codeDieRebuild page 1-tachyons-docZweigmy codebase to see if anything is missing.

Add language dropdown

Right now the language dropdown is populated with placeholder values ​​and you can't change the language, but what we want from the menu at the end is:

  • The current language should be displayed in the menu by default
  • If you click on the menu it should open up and display all the other available languagesapart fromthe current language
  • When you hover over a menu item, it should be highlighted somehow
  • Clicking on a menu item should change the current language of the app (more on this later)
  • If you click anywhere else on the page while the menu is open, the menu should close

Most of these requirements sound like they're better served in their own module, so let's come up with a nameLanguageDropdown.elm, and start rendering only the current language selection so that we get the correct positioning of the menu.

current selection

src/LanguageDropdown.elm

Module language dropdown menu expose (vista)matter HTML expose (HTML, division, li, PAG, equipment, Text, ul)matter HTML.Attribute expose (Class)vista : HTML Newsvista = permission classes = [ "Center" , "f3" , "to fold" , "h3" , "Element Center" , "justify-end" , "w-90" ] |> line.to connect "" |> Class In division [ classes ] [ current selection ]current selection : HTML Newscurrent selection = permission classes = [ "b-white" , "licensed in letters" , "br2" , "pa2" , "Pointer" , "CT" , "w4" ] |> line.to connect "" |> Class caretClasses = [ "absolute" , "ml2" ] |> line.to connect "" |> Class In PAG [ classes ] [ equipment [] [ Text "English" ] , equipment [ caretClasses ] [ Text "▾" ] ]

Next, we need to import the language dropdown code into theFirst of allmodule, as well as easily customize the styles invistaFunction, since there is now more to the page than just the message:

src/Main.elm

-- ...matter language dropdown menu-- ...vista : Model -> HTML Newsvista Model = permission classes = [ "bg-dunkelrosa" , "overflow tank" , "pt3" , "sans serif" , "vh-100" , "Weiss" ] |> line.to connect "" |> Class In first of all_ [ classes ] [ language dropdown menu.vista , Content ]Content : HTML NewsContent = permission ArticleClasses = [ "dt" , "vh-75" , "w-100" ] |> line.to connect "" |> Class -- ... In -- ...

Now your page should look like this:

Runtime language switch to Elm (2)

The "menu" here (yes, currently it's only onePAGtag) currently doesn't do anything, but at least we can confirm that it seems to be in a good place on the page. Now we're actually giving you onedrop down listunder thecurrent selection!

Language dropdown list

src/LanguageDropdown.elm

vista : HTML Newsvista = permission -- ... In division [ classes ] [ current selection , drop down list ]-- ...drop down list : HTML Newsdrop down list = permission classes = [ "absolute" , "b-white" , "Bed and breakfast" , "Degree in Law" , "brother" , "br-down" , "br2" , "Element Center" , "List" , "mt5 , "pl0" , "Pointer" , "pr0" , "pt1" , "CT" , "top-0" , "w4" ] |> line.to connect "" |> Class selectable languages = [ "Italian", "Japanese" ] In ul [ classes ] (List.Map DropdownListItem selectable languages)DropdownListItem : line -> HTML NewsDropdownListItem Language = permission classes = [ "hover-bg-blanco" , "Hover-Dunkelrosa" , "ph1" , "PV2" , "pt0" , "w-100" ] |> line.to connect "" |> Class In li [ classes ] [ equipment [] [ Text Language ] ]

That leads to:

Runtime language switch to Elm (3)

  • For the dropdown list, we insert an HTML unordered list (ul) directly belowPAGlabel that simulates opening a menu.
  • When we hover over a menu item, we can see which item is currently selected.
  • Selectable languages ​​currently have strings as placeholders, but we'll change this later when we introduce the concept of language in the app.

So now we know what the menu looks like when it's open, but we need it to respond to mouse clicks to open and close the dropdown list (read: turn the list on and off), so let's do that now.

Show and hide available languages

The app needs to be able to track whether to show or hide the dropdown and be able to track menu and page clicks, so it sounds like we'll need the following:

  • A boolean flag to tell the application if this is the caseshowAvailable languagesO no
  • An updateNewsthis toggles the visibility of the dropdown list; let's call itShow available languages
  • An updateNewsThis will hide the dropdown menu when it is open but a click is being tracked elsewhere on the page. let's call itCloseAvailable languages

We start by updating theNewsjoin type. BothMajor. Science fictionYLanguageDropdown.elmneed access toNews, so let's extract it into its own module:

src/Msj.elm

Module News expose (News(..))type News = CloseAvailable languages | Show available languages

Next excerptModeland theinsidefunction disabledFirst of allin a new onemodel.olmoModule, make sure the dropdown is hidden by default:

src/model.elm

Module Model expose (Model, inside)matter News expose (News)type alias Model = { showAvailable languages : bool }inside : ( Model, Before News )inside = ( { showAvailable languages = INCORRECT }, Before.none )

Now we have to updateMajor. Science fictionYLanguageDropdown.elmto import these modules and then write handling code for themNewsit's in theUpdateFunction:

src/Main.elm

-- ...matter Model expose (Model)matter News expose (News(CloseAvailable languages, Show available languages))Update : News -> Model -> ( Model, Before News )Update News Model = Fall News Von CloseAvailable languages -> ( { Model | showAvailable languages = INCORRECT }, Before.none ) Show available languages -> ( { Model | showAvailable languages = no Model.showAvailable languages } , Before.none )-- ...first of all : program Never Model Newsfirst of all = HTML.program { vista = vista , inside = Model.inside , Update = Update , subscriptions = always Sub.none }

Soylanguage dropdown menu, since we are going to send type messages nowNews, all feature notes withHTML messageneeds to be updatedHTML message. We will also make use of that.vistafunctionModelParameters on the dropdown to determine what to show and how the dropdown should be styled on opening and closing:

src/LanguageDropdown.elm

-- ...matter HTML.events expose (on click)matter Model expose (Model)matter News expose (News(Show available languages))vista : Model -> HTML Newsvista Model = permission -- ... In division [ classes ] [ current selection Model , drop down list Model ]current selection : Model -> HTML Newscurrent selection Model = permission show classes = And Model.showAvailable languages So [ "br--up" ] Anders [] classes = [ -- ... ] ++ show classes |> line.to connect "" |> Class -- ... In PAG [ classes, on click Show available languages ] [ -- ... ]drop down list : Model -> HTML Newsdrop down list Model = permission show classes = And Model.showAvailable languages So [ "to fold", "columna flexible" ] Anders [ "dn" ] classes = [ -- ... ] ++ show classes |> line.to connect "" |> Class -- ... In -- ...DropdownListItem : line -> HTML News-- ...

Once you've made the above changes, you should be able to click the dropdown menu to open and close it and show and hide the available languages. However, if you open the menu and then click anywhere else, the menu remains open.

To close it (and actually use thatCloseAvailable languagesmessage), we have to use thatelm mouse packand a subscription to mouse clicks.

Subscribe to mouse clicks

Install the Elm Mouse package:

elm bundleinstall -yLong Elm/Mouse

then create onesubscriptionsFunction in Elm that listens for mouse clickssoloIf the dropdown is open and a click is detected, a message will be sent.CloseAvailable languagesNews:

src/Main.elm

-- ...matter Maus-- ...subscriptions : Model -> Sub Newssubscriptions Model = And Model.showAvailable languages So Maus.clicks (\_ -> CloseAvailable languages) Anders Sub.nonefirst of all : program Never Model Newsfirst of all = HTML.ProgrammMitFlags { vista = vista , inside = Model.inside , Update = Update , subscriptions = subscriptions }

Now if you click on the dropdown and then click somewhere else, the menu will close as expected.

If the app doesn't behave as expected so far, check your code againstDieAdd 2 language dropdown menuZweigmy codebase to see if anything is missing.

Now it's time to give your app the concept of a language to switch to and replace those placeholder values ​​with actual data!

change of language

It's time to put some translations in the app and for that we will useelm-i18 next, WithHTTP en ElmPackage, so let's start installing:

elm bundleinstall -yChristophP/elm-i18nextalm-Packetinstall -yelm-lang/http

First, let's provide some translation JSON files for the English, Italian, and Japanese on-screen message. A... createpublic/local/directory and add the following files under it:

public/locale/translations.en.json

{ "CentroVerticalInCssIsSimple": "Centering things vertically in CSS is easy!"}

public/locale/translations.it.json

{ "CentroVerticalInCssIsSimple": "Centering vertically with CSS is easy!"}

public/local/translations.ja.json

{ "CentroVerticalInCssIsSimple": "CSS vertical centering is easy!"}

Next we create onetranslationsModule where we define the type of a language and provide a helper function to convert a string language code to a language:

src/Translations.elm

Module translations expose (Language(..), getLnFromCode)type Language = In | ES | YgetLnFromCode : line -> LanguagegetLnFromCode Code = Fall Code Von "In" -> In "Es" -> ES "Y" -> Y _ -> In

Excellent! Now we have to add some new ones.NewsTypes for:

  • Get the translations of the JSON files
  • change language

src/Msj.elm

Module News expose (News(..))matter Http expose (Error)matter I18Weiter expose (translations)matter translations expose (Language)type News = change language Language | CloseAvailable languages | Get Translations (Result Error translations) | Show available languages

Now we need a way to get the translations of the JSON files and return the result via theGet Translationsmessage, so let's create that in a new module calledBefore:

src/Cmd.elm

Module Before expose (look up translations)matter I18Weitermatter News expose (News(Get Translations))matter translations expose (Language)look up translations : Language -> Before Newslook up translations Language = Language |> aUrlTranslations |> I18Weiter.look up translations Get TranslationsaUrlTranslations : Language -> lineaUrlTranslations Language = permission translationlanguage = Language |> Chain |> line.reduce In "/locale/translations". ++ translationlanguage ++ ".json"

Now himModelshould know whatcurrent languagethe application is to determine whattranslationsshould be loaded, so let's add that information and call thatCmd.fetchTranslations EsCommand to immediately get the translations corresponding to English, which we will also set as the default language:

src/model.elm

Module Model expose (Model, inside)matter Beforematter I18Weiter expose (translations)matter News expose (News)matter translations expose (Language(In))type alias Model = { current language : Language , showAvailable languages : bool , translations : translations }inside : ( Model, Before News )inside = ( { current language = In , showAvailable languages = INCORRECT , translations = I18Weiter.initial translations } , Before.look up translations In )

Next we have to deal with the newchange languageYGet Translationsnews in theUpdateFunction:

  • If the language is changed, as well as the changecurrent language, we need to get the translations for that language the same way we did ininsidefunction
  • If fetching the translations is successful, we display the new translations; otherwise, we just ignore any bugs for now, as we wouldn't expect to get translations for a language we didn't create ourselves.

src/Main.elm

-- ...matter Beforematter News expose ( News ( change language , CloseAvailable languages , Get Translations , Show available languages ) )---...Update : News -> Model -> ( Model, Before News )Update News Model = Fall News Von -- ... change language Language -> ( { Model | current language = Language } , Before.look up translations Language ) Get Translations (OK translations) -> ( { Model | translations = translations }, Before.none ) Get Translations (err News) -> ( Model, Before.none )

At this point, the app should be back to a point where everything is recompiled, but on the surface there's no change as the language display is still made up of static values, so let's change that.

First we create oneLanguageModule that has some helper functions to generate the string value for a language (for example "English" should always be displayed as "English" regardless of the current language) and maintain a static list of available languages ​​for us to display in the Menu dropdown. Unfortunately, there is no way to get a list of type values ​​of a type (for example,[And, that, yes]of theLanguagetype), so it must be a separate definition:

src/language.elm

Module Language expose (Available languages, langToString)matter translations expose (Language(In, ES, Y))Available languages : List LanguageAvailable languages = [ In, ES, Y ]langToString : Language -> linelangToString Language = Fall Language Von In -> "English" ES -> "Italian" Y -> "Japanese"

Now that we have our language data set, let's go back to the view code and move down the page to start viewing. First, let's display the correct information in the dropdown for both the current language and the other languages ​​available in the dropdown:

src/LanguageDropdown.elm

-- ...matter Languagematter News expose (News(change language, Show available languages))matter translations expose (Language)-- ...current selection : Model -> HTML Newscurrent selection Model = permission -- ... In PAG [ classes, on click Show available languages ] [ equipment [] [ Text (Language.langToString Model.current language) ] , equipment [ caretClasses ] [ Text "▾" ] ]drop down list : Model -> HTML Newsdrop down list Model = permission -- ... selectable languages = List.Filter (\Language -> Language /= Model.current language) Language.Available languages In ul [ classes ] (List.Map DropdownListItem selectable languages)DropdownListItem : Language -> HTML NewsDropdownListItem Language = permission -- ... In li [ classes, on click (change language Language) ] [ equipment [] [ Text (Language.langToString Language) ] ]

At this point, if you select a new language from the dropdown, you'll see the current language indicator change in the menu, and if you open the elm debugger, you'll see the application language isStrictly speakingchange, and translations for the languageSonloaded in the application:

Runtime language switch to Elm (4)

Excellent! Now let's make this translated message display on the page by telling the content which translations to display:

src/Main.elm

-- ...matter translations expose (Language)-- ...vista : Model -> HTML Newsvista Model = permission -- ... In first of all_ [ classes ] [ language dropdown menu.vista Model , Content Model.translations ]Content : translations -> HTML NewsContent translations = permission -- ... In Article [ ArticleClasses ] [ division [ divclases ] [ titular translations ] ]titular : translations -> HTML Newstitular translations = permission -- ... In h1 [ classes ] [ Text (I18Weiter.T translations "VerticalCenterInCssIsSimple") ]

And now when you change the language, you should see the message displayed in that language:

Runtime language switch to Elm (5)

Fantastic! That covers the core functionality of language switching, but we can do more. However, before you continue, if you are unable to switch languages, you should re-verify your codeDie3-Add language changeZweigmy base code.

detect user language

Currently, the app's default language is English at launch, but it would be nice if we at least tried to configure the app to initially display in the user's preferred language. To get around the idea of ​​a “preferred language” (becauseThis is not universal across browsers.), we consider it as the language of the browser used. How do we do that? In javascript we can use:

So, let's get this information from Javascript and pass it to Elm as a flag:

src/index.js

matter "tachyons"matter { First of all } outside "./Main.ulm"until application container = document.getElementById("root")And(application container) { First of all.embed(application container, { Language: getLanguage() })}function getLanguage() { go back Browser.Language || Browser.language of the user}

Our application currently cannot accept javascript flags, so let's change our program type toProgrammMitFlagsFor this to happen:

src/Main.elm

-- ...matter Model expose (banderas, Model)-- ...first of all : program banderas Model Newsfirst of all = HTML.ProgrammMitFlags { vista = vista , inside = Model.inside , Update = Update , subscriptions = subscriptions }

Next we define what type of flags we accept in theModelmodule and since we don't trust the information passed by javascript, we decode the flag to make sure we get a string.

src/model.elm

Module Model expose (banderas, Model, inside)-- ...matter json.decode and decode expose (Wert)matter Languagetype alias banderas = { Language : Wert }-- ...inside : banderas -> ( Model, Before News )inside banderas = permission Language = banderas.Language |> decode.decode value decode.line |> Language.longFromFlag In ( { current language = Language , showAvailable languages = INCORRECT , translations = I18Weiter.initial translations } , Before.look up translations Language )

Finally, we create theidioma.langFromFlagFunction that returns a language if the decoding goes well and a default language if it doesn't:

src/language.elm

Module Language expose (Available languages, longFromFlag, langToString)-- ...longFromFlag : Result line line -> LanguagelongFromFlag Language = Fall Language Von OK Language -> translations.getLnFromCode Language err _ -> In

If your browser language is English, you won't notice any changes from these additions, but if you change your browser language to Italian or Japanese and then refresh the page, you'll see the app launch in that language.

For Chrome, you can change the language settings by opening browser settings, opening advanced settings...

Runtime language switch to Elm (6)

...Find the language settings, then select a language to move to the top of the list:

Runtime language switch to Elm (7)

Does the default language not change? Check your code againstDie4-detect the language of the userZweigmy base code.

Now it's nice to have a default language, but if you change the language and then refresh the page, the app reverts to the language set in the browser. Many people want to read content in a different language than their system settings and it would be nice to be able to save your language preference for this app. So, let's use the browser one thenmemoria localto help us do just that.

Save language settings

To send data from elm to javascript we need to use itElm-Ports. All port functions return acommand message, so let's add the function to save a language setting in theBeforemodule and change it to aport-how:

src/Cmd.elm

Puerto Module Before expose (look up translations, StoreSprache)-- ...Puerto storeLanguageInLocalStorage : line -> Before News-- ...StoreSprache : Language -> Before NewsStoreSprache Language = Language |> Chain |> line.reduce |> storeLanguageInLocalStorage

here we create oneStoreSpracheCommand function that a assumesLanguagewrite, type and send it to JavascriptstoreLanguageInLocalStoragePort. There is currently no code on the javascript side that subscribes to messages from this port, so let's do it next:

src/index.js

// ...And(application container) { until application = First of all.embed(application container, { Language: getLanguage() }) application.ports.storeLanguageInLocalStorage.subscribe to((Language) => { memoria local.setItem("elm-i18n sample language", Language) })}// ...

There is no particular reason behind the "elm-i18n-example-language" key; It could have had any name, but it's best to make it as unique as possible, since many different applications are likely to use it.memoria local.

Ok, we have configured the path to javascript, now we need to make sure that the command is executed every time the language is changed (i.e. thechange languagemessage is sent), so let's make this addition to thatUpdateFunction:

src/Main.elm

-- ...Update : News -> Model -> ( Model, Before News )Update News Model = Fall News Von change language Language -> ( { Model | current language = Language } , Before.Carry [ Before.look up translations Language , Before.StoreSprache Language ] ) -- ...

Hechange languageBranch ofUpdateThe feature has gotten a bit busier and needs to be usedcommand batchto send commands to retrieve new language translations and save the user's language preference.

You should now be able to change the language and save it.memoria local. Open the Javascript console in your browser's developer tools to confirm this with the following command:localStorage.getItem("elm-i18n sample language")

Runtime language switch to Elm (8)

Success! However, there is a small problem: when you refresh the browser, the application still reverts to the default language, English. We need to have our javascript code to get the languagememoria local(if applicable) and pass it in as the Elm language flag, so let's do this:

src/index.js

// ...function getLanguage() { go back memoria local.gets the object("elm-i18n sample language") || Browser.Language || Browser.language of the user}

Now when you change the language and refresh the page, the app should still show you the language you originally selected! If not, check your codeDieMemory language preference 5Zweigmy base code.

At this point, our site is almost complete. However, there are still some potential issues that might be worth investigating further:

  • When updating the page, the translation key "verticalCenteringInCssIsEasy" may blink briefly before the translation appears. This is particularly noticeable with the Japanese translation. Perhaps the translations are loading too slowly…?
  • If you accidentally make a typo while requesting a translation by pressing a button in a view (eg.I18Next.t Übersetzungen "this key does not exist"), then no error is thrown - the key is just displayed on the page, which might not be what you want.
  • If you inadvertently fail to provide a translation for a particular key for a known available language, or make a typographical error in the translation file (for example, thetranslations.ja.jsonfile or change its key name), then again no error is thrown and the requested key is displayed unchanged on the page.

Elm programmers are spoiled by the fact that the Elm compiler is always looking over its shoulder, helping us avoid such bugs. If you are sure that you solve the described problems manually or if they are not important to you, then everything is fine and you do not need to continue. But if you want Elm to keep a better eye on your i18n development, what are your options?

Type-safe translations

Since we have our translation files as JSON, we canelm i18n generationto generate onetranslationsModule containing a function for each translation in the JSON files. So let's try it.

Install it with the following command:

npminstall - GRAMSelm-i18n-gen

create a new onetranslationsModule for the application with the following command:

elm-i18n-gen public/locale src/Translations.elm

and when you open ittranslationsmodule you should see:

src/Translations.elm

Module translations expose (..)type Language = In | ES | YgetLnFromCode : line -> LanguagegetLnFromCode Code = Fall Code Von "In" -> In "Es" -> ES "Y" -> Y _ -> InVerticalCenterInCssIsSimple : Language -> lineVerticalCenterInCssIsSimple language = Fall language Von In -> "Vertically centering things in CSS is easy!" ES -> "Vertical centering with CSS is easy!" Y -> "Vertical centering with CSS is easy! "

We only have one translation key in our JSON files, soelm-i18n-gencreated just one feature for us that covers translations for all of our languages. You can also see here that I adoptedelm-i18n-genThe naming conventions specific toLanguage, YgetLnFromCodein advance, and intentionally put this information in thetranslationsmodule knowing that it will be overwritten when the newtranslationsthe file was generated (... I think mineChekhov's gunstuck...).

Anyway, now that we have our function, let's use it in the view:

src/Main.elm

-- ...matter translations expose (Language)-- ...vista : Model -> HTML Newsvista Model = permission -- ... In first of all_ [ classes ] [ language dropdown menu.vista Model , Content Model.current language ]Content : Language -> HTML NewsContent Language = permission -- ... In Article [ ArticleClasses ] [ division [ divclases ] [ titular Language ] ]titular : Language -> HTML Newstitular Language = permission -- ... In h1 [ classes ] [ Text (translations.VerticalCenterInCssIsSimple Language) ]

The implications of this change are as follows:

  • It is no longer necessary to search for the translations and, therefore, neitherGet Translations News, Dielook up translationsfunction in theBeforemodule, whichtranslationsentrance inModel, and any trace of theI18WeiterYHttpPackages can now be safely removed.
  • Consequently, the problem of displaying a translation key before the translation is loaded has disappeared since now we only call one function.
  • Elm generates a compile error if a translation is not provided for all languages.

Those are some pretty cool perks! I'm not sure if there are any downsides to this, other than maybe there is a single module with potentially hundreds of functions for a large JSON translation file. But I guess the maintenance overhead for this module would be the same for the JSON file. Please let me know if I'm wrong about this!

VerDie6 types of safe translationsZweigmy codebase to see the final form of the app with all redundant code removed.

Diploma

Even after all this, I'm still not sure what to think about an ideal solution for I18n on Elm. I plan to use the methods outlined in this blog post for now, but if you have better ways of doing things (I'd love to see a real example of an app that does thescience fiction-i18npackage), please let me know!

Top Articles
Latest Posts
Article information

Author: Francesca Jacobs Ret

Last Updated: 03/16/2023

Views: 5569

Rating: 4.8 / 5 (68 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Francesca Jacobs Ret

Birthday: 1996-12-09

Address: Apt. 141 1406 Mitch Summit, New Teganshire, UT 82655-0699

Phone: +2296092334654

Job: Technology Architect

Hobby: Snowboarding, Scouting, Foreign language learning, Dowsing, Baton twirling, Sculpting, Cabaret

Introduction: My name is Francesca Jacobs Ret, I am a innocent, super, beautiful, charming, lucky, gentle, clever person who loves writing and wants to share my knowledge and understanding with you.