Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Runtime I18n in Elm

Runtime I18n in Elm

A presentation outlining a sample app implementation of runtime internationalisation (i18n), and a little bit of type-safe i18n, in Elm.

Presented at the Sydney Elm meetup on 21 June 2018.

Presentation slide deck markdown and speaker notes (useable in Deckset 2):
https://github.com/paulfioravanti/presentations/blob/master/runtime_i18n_in_elm

Paul Fioravanti

June 21, 2018
Tweet

More Decks by Paul Fioravanti

Other Decks in Programming

Transcript

  1. Back End 4 Provide translations in JSON 4 Store selected

    language in localStorage @paulfioravanti 10
  2. Back End 4 Provide translations in JSON 4 Store selected

    language in localStorage 4 Explore type safety options @paulfioravanti 11
  3. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 13
  4. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 14
  5. module Styles exposing (..) main_ : String main_ = [

    "bg-dark-pink" , "overflow-container" , "pt3" , "sans-serif" , "vh-100" , "white" ] |> String.join " " @paulfioravanti 15
  6. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 16
  7. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 17
  8. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 18
  9. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ content ] content : Html Msg content = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading ] ] heading : Html Msg heading = h1 [ class Styles.heading ] [ text "Vertically centering things in css is easy!" ] @paulfioravanti 19
  10. Language Dropdown 4 Show current language on menu 4 Reveal

    available languages @paulfioravanti 22
  11. Language Dropdown 4 Show current language on menu 4 Reveal

    available languages 4 Change language @paulfioravanti 23
  12. Language Dropdown 4 Show current language on menu 4 Reveal

    available languages 4 Change language 4 Close on "blur" @paulfioravanti 24
  13. module LanguageDropdown exposing (view) view : Html msg view =

    div [ class Styles.dropdownContainer ] [ currentSelection ] currentSelection : Html msg currentSelection = p [ class Styles.currentSelection ] [ span [] [ text "English" ] , span [ Styles.caret ] [ text "▾" ] ] @paulfioravanti 25
  14. module LanguageDropdown exposing (view) view : Html msg view =

    div [ class Styles.dropdownContainer ] [ currentSelection ] currentSelection : Html msg currentSelection = p [ class Styles.currentSelection ] [ span [] [ text "English" ] , span [ Styles.caret ] [ text "▾" ] ] @paulfioravanti 26
  15. module LanguageDropdown exposing (view) view : Html msg view =

    div [ class Styles.dropdownContainer ] [ currentSelection ] currentSelection : Html msg currentSelection = p [ class Styles.currentSelection ] [ span [] [ text "English" ] , span [ Styles.caret ] [ text "▾" ] ] @paulfioravanti 27
  16. module LanguageDropdown exposing (view) view : Html msg view =

    div [ class Styles.dropdownContainer ] [ currentSelection ] currentSelection : Html msg currentSelection = p [ class Styles.currentSelection ] [ span [] [ text "English" ] , span [ Styles.caret ] [ text "▾" ] ] @paulfioravanti 28
  17. module Main exposing (main) import LanguageDropdown view : Model ->

    Html Msg view model = main_ [ class Styles.main_ ] [ LanguageDropdown.view , content ] @paulfioravanti 29
  18. module Main exposing (main) import LanguageDropdown view : Model ->

    Html Msg view model = main_ [ class Styles.main_ ] [ LanguageDropdown.view , content ] @paulfioravanti 30
  19. view : Html msg view = div [ class Styles.dropdownContainer

    ] [ currentSelection , dropdownList ] dropdownList : Html msg dropdownList = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class Styles.dropdownList ] (List.map dropdownListItem selectableLanguages) dropdownListItem : String -> Html msg dropdownListItem language = li [ class Styles.dropdownListItem ] [ span [] [ text language ] ] @paulfioravanti 33
  20. view : Html msg view = div [ class Styles.dropdownContainer

    ] [ currentSelection , dropdownList ] dropdownList : Html msg dropdownList = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class Styles.dropdownList ] (List.map dropdownListItem selectableLanguages) dropdownListItem : String -> Html msg dropdownListItem language = li [ class Styles.dropdownListItem ] [ span [] [ text language ] ] @paulfioravanti 34
  21. view : Html msg view = div [ class Styles.dropdownContainer

    ] [ currentSelection , dropdownList ] dropdownList : Html msg dropdownList = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class Styles.dropdownList ] (List.map dropdownListItem selectableLanguages) dropdownListItem : String -> Html msg dropdownListItem language = li [ class Styles.dropdownListItem ] [ span [] [ text language ] ] @paulfioravanti 35
  22. view : Html msg view = div [ class Styles.dropdownContainer

    ] [ currentSelection , dropdownList ] dropdownList : Html msg dropdownList = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class Styles.dropdownList ] (List.map dropdownListItem selectableLanguages) dropdownListItem : String -> Html msg dropdownListItem language = li [ class Styles.dropdownListItem ] [ span [] [ text language ] ] @paulfioravanti 36
  23. view : Html msg view = div [ class Styles.dropdownContainer

    ] [ currentSelection , dropdownList ] dropdownList : Html msg dropdownList = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class Styles.dropdownList ] (List.map dropdownListItem selectableLanguages) dropdownListItem : String -> Html msg dropdownListItem language = li [ class Styles.dropdownListItem ] [ span [] [ text language ] ] @paulfioravanti 37
  24. Show/Hide Available Languages 4 Flag to showAvailableLanguages in model 4

    Toggle dropdown list visibility (ShowAvailableLanguages) @paulfioravanti 41
  25. Show/Hide Available Languages 4 Flag to showAvailableLanguages in model 4

    Toggle dropdown list visibility (ShowAvailableLanguages) 4 Hide dropdown list on "blur" (CloseAvailableLanguages) @paulfioravanti 42
  26. type alias Model = { showAvailableLanguages : Bool } init

    : ( Model, Cmd Msg ) init = ( { showAvailableLanguages = False }, Cmd.none ) @paulfioravanti 43
  27. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of CloseAvailableLanguages -> ( { model | showAvailableLanguages = False } , Cmd.none ) ShowAvailableLanguages -> ( { model | showAvailableLanguages = not model.showAvailableLanguages } , Cmd.none ) @paulfioravanti 45
  28. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of CloseAvailableLanguages -> ( { model | showAvailableLanguages = False } , Cmd.none ) ShowAvailableLanguages -> ( { model | showAvailableLanguages = not model.showAvailableLanguages } , Cmd.none ) @paulfioravanti 46
  29. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of CloseAvailableLanguages -> ( { model | showAvailableLanguages = False } , Cmd.none ) ShowAvailableLanguages -> ( { model | showAvailableLanguages = not model.showAvailableLanguages } , Cmd.none ) @paulfioravanti 47
  30. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of CloseAvailableLanguages -> ( { model | showAvailableLanguages = False } , Cmd.none ) ShowAvailableLanguages -> ( { model | showAvailableLanguages = not model.showAvailableLanguages } , Cmd.none ) @paulfioravanti 48
  31. view : Model -> Html msg view { showAvailableLanguages }

    = div [ class Styles.dropdownContainer ] [ currentSelection showAvailableLanguages , dropdownList showAvailableLanguages ] currentSelection : Bool -> Html Msg currentSelection showAvailableLanguages = p [ class (Styles.currentSelection showAvailableLanguages) , onClick ShowAvailableLanguages ] [ span [] [ text "English" ] , span [ class Styles.caret ] [ text "▾" ] ] @paulfioravanti 49
  32. view : Model -> Html msg view { showAvailableLanguages }

    = div [ class Styles.dropdownContainer ] [ currentSelection showAvailableLanguages , dropdownList showAvailableLanguages ] currentSelection : Bool -> Html Msg currentSelection showAvailableLanguages = p [ class (Styles.currentSelection showAvailableLanguages) , onClick ShowAvailableLanguages ] [ span [] [ text "English" ] , span [ class Styles.caret ] [ text "▾" ] ] @paulfioravanti 50
  33. view : Model -> Html msg view { showAvailableLanguages }

    = div [ class Styles.dropdownContainer ] [ currentSelection showAvailableLanguages , dropdownList showAvailableLanguages ] currentSelection : Bool -> Html Msg currentSelection showAvailableLanguages = p [ class (Styles.currentSelection showAvailableLanguages) , onClick ShowAvailableLanguages ] [ span [] [ text "English" ] , span [ class Styles.caret ] [ text "▾" ] ] @paulfioravanti 51
  34. view : Model -> Html msg view { showAvailableLanguages }

    = div [ class Styles.dropdownContainer ] [ currentSelection showAvailableLanguages , dropdownList showAvailableLanguages ] dropdownList : Bool -> Html msg dropdownList showAvailableLanguages = let selectableLanguages = [ "Italiano", "೔ຊޠ" ] in ul [ class (Styles.dropdownList showAvailableLanguages) ] (List.map dropdownListItem selectableLanguages) @paulfioravanti 52
  35. dropdownList : Bool -> String dropdownList showAvailableLanguages = let displayClasses

    = if showAvailableLanguages then [ "flex", "flex-column" ] else [ "dn" ] in [ "absolute" , "b--white" , -- ... ] ++ displayClasses |> String.join " " @paulfioravanti 53
  36. dropdownList : Bool -> String dropdownList showAvailableLanguages = let displayClasses

    = if showAvailableLanguages then [ "flex", "flex-column" ] else [ "dn" ] in [ "absolute" , "b--white" , -- ... ] ++ displayClasses |> String.join " " @paulfioravanti 54
  37. dropdownList : Bool -> String dropdownList showAvailableLanguages = let displayClasses

    = if showAvailableLanguages then [ "flex", "flex-column" ] else [ "dn" ] in [ "absolute" , "b--white" , -- ... ] ++ displayClasses |> String.join " " @paulfioravanti 55
  38. dropdownList : Bool -> String dropdownList showAvailableLanguages = let displayClasses

    = if showAvailableLanguages then [ "flex", "flex-column" ] else [ "dn" ] in [ "absolute" , "b--white" , -- ... ] ++ displayClasses |> String.join " " @paulfioravanti 56
  39. import Mouse subscriptions : Model -> Sub Msg subscriptions model

    = if model.showAvailableLanguages then Mouse.clicks (\_ -> CloseAvailableLanguages) else Sub.none @paulfioravanti 59
  40. { "verticallyCenteringInCssIsEasy": "Vertically centering things in css is easy!" }

    { "verticallyCenteringInCssIsEasy": "Centrare verticalmente con css è facile!" } { "verticallyCenteringInCssIsEasy": "CSSͰਨ௚ηϯλϦϯά͸؆୯ͩΑʂ" } @paulfioravanti 64
  41. module Translations exposing (Lang(..), getLnFromCode) type Lang = En |

    It | Ja getLnFromCode : String -> Lang getLnFromCode code = case code of "en" -> En "it" -> It "ja" -> Ja _ -> En @paulfioravanti 65
  42. getLnFromCode : String -> Lang getLnFromCode code = case code

    of "en" -> En "it" -> It "ja" -> Ja _ -> En @paulfioravanti 67
  43. import Http exposing (Error) import I18Next exposing (Translations) import Translations

    exposing (Lang) type Msg = ChangeLanguage Lang | CloseAvailableLanguages | FetchTranslations (Result Error Translations) | ShowAvailableLanguages @paulfioravanti 68
  44. import Http exposing (Error) import I18Next exposing (Translations) import Translations

    exposing (Lang) type Msg = ChangeLanguage Lang | CloseAvailableLanguages | FetchTranslations (Result Error Translations) | ShowAvailableLanguages @paulfioravanti 69
  45. import Http exposing (Error) import I18Next exposing (Translations) import Translations

    exposing (Lang) type Msg = ChangeLanguage Lang | CloseAvailableLanguages | FetchTranslations (Result Error Translations) | ShowAvailableLanguages @paulfioravanti 70
  46. fetchTranslations : Lang -> Cmd Msg fetchTranslations language = language

    |> toTranslationsUrl |> I18Next.fetchTranslations FetchTranslations toTranslationsUrl : Lang -> String toTranslationsUrl language = let translationLanguage = language |> toString |> String.toLower in "/locale/translations." ++ translationLanguage ++ ".json" @paulfioravanti 71
  47. fetchTranslations : Lang -> Cmd Msg fetchTranslations language = language

    |> toTranslationsUrl |> I18Next.fetchTranslations FetchTranslations toTranslationsUrl : Lang -> String toTranslationsUrl language = let translationLanguage = language |> toString |> String.toLower in "/locale/translations." ++ translationLanguage ++ ".json" @paulfioravanti 72
  48. fetchTranslations : Lang -> Cmd Msg fetchTranslations language = language

    |> toTranslationsUrl |> I18Next.fetchTranslations FetchTranslations toTranslationsUrl : Lang -> String toTranslationsUrl language = let translationLanguage = language |> toString |> String.toLower in "/locale/translations." ++ translationLanguage ++ ".json" @paulfioravanti 73
  49. fetchTranslations : Lang -> Cmd Msg fetchTranslations language = language

    |> toTranslationsUrl |> I18Next.fetchTranslations FetchTranslations toTranslationsUrl : Lang -> String toTranslationsUrl language = let translationLanguage = language |> toString |> String.toLower in "/locale/translations." ++ translationLanguage ++ ".json" @paulfioravanti 74
  50. import I18Next exposing (Translations) type alias Model = { currentLanguage

    : Lang , showAvailableLanguages : Bool , translations : Translations } init : ( Model, Cmd Msg ) init = ( { currentLanguage = En , showAvailableLanguages = False , translations = I18Next.initialTranslations } , fetchTranslations En ) @paulfioravanti 75
  51. import I18Next exposing (Translations) type alias Model = { currentLanguage

    : Lang , showAvailableLanguages : Bool , translations : Translations } init : ( Model, Cmd Msg ) init = ( { currentLanguage = En , showAvailableLanguages = False , translations = I18Next.initialTranslations } , fetchTranslations En ) @paulfioravanti 76
  52. import I18Next exposing (Translations) type alias Model = { currentLanguage

    : Lang , showAvailableLanguages : Bool , translations : Translations } init : ( Model, Cmd Msg ) init = ( { currentLanguage = En , showAvailableLanguages = False , translations = I18Next.initialTranslations } , fetchTranslations En ) @paulfioravanti 77
  53. import I18Next exposing (Translations) type alias Model = { currentLanguage

    : Lang , showAvailableLanguages : Bool , translations : Translations } init : ( Model, Cmd Msg ) init = ( { currentLanguage = En , showAvailableLanguages = False , translations = I18Next.initialTranslations } , fetchTranslations En ) @paulfioravanti 78
  54. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of -- ... ChangeLanguage language -> ( { model | currentLanguage = language } , fetchTranslations language ) FetchTranslations (Ok translations) -> ( { model | translations = translations }, Cmd.none ) FetchTranslations (Err msg) -> ( model, Cmd.none ) @paulfioravanti 79
  55. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of -- ... ChangeLanguage language -> ( { model | currentLanguage = language } , fetchTranslations language ) FetchTranslations (Ok translations) -> ( { model | translations = translations }, Cmd.none ) FetchTranslations (Err msg) -> ( model, Cmd.none ) @paulfioravanti 80
  56. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of -- ... ChangeLanguage language -> ( { model | currentLanguage = language } , fetchTranslations language ) FetchTranslations (Ok translations) -> ( { model | translations = translations }, Cmd.none ) FetchTranslations (Err msg) -> ( model, Cmd.none ) @paulfioravanti 81
  57. module Language exposing (availableLanguages, langToString) import Translations exposing (Lang(En, It,

    Ja)) availableLanguages : List Lang availableLanguages = [ En, It, Ja ] langToString : Lang -> String langToString language = case language of En -> "English" It -> "Italiano" Ja -> "೔ຊޠ" @paulfioravanti 83
  58. module Language exposing (availableLanguages, langToString) import Translations exposing (Lang(En, It,

    Ja)) availableLanguages : List Lang availableLanguages = [ En, It, Ja ] langToString : Lang -> String langToString language = case language of En -> "English" It -> "Italiano" Ja -> "೔ຊޠ" @paulfioravanti 84
  59. module Language exposing (availableLanguages, langToString) import Translations exposing (Lang(En, It,

    Ja)) availableLanguages : List Lang availableLanguages = [ En, It, Ja ] langToString : Lang -> String langToString language = case language of En -> "English" It -> "Italiano" Ja -> "೔ຊޠ" @paulfioravanti 85
  60. module Language exposing (availableLanguages, langToString) import Translations exposing (Lang(En, It,

    Ja)) availableLanguages : List Lang availableLanguages = [ En, It, Ja ] langToString : Lang -> String langToString language = case language of En -> "English" It -> "Italiano" Ja -> "೔ຊޠ" @paulfioravanti 86
  61. module LanguageDropdown exposing (view) view : Model -> Html Msg

    view { currentLanguage, showAvailableLanguages } = let selectableLanguages = Language.availableLanguages |> List.filter (\language -> language /= currentLanguage) in div [ class Styles.dropdownContainer ] [ currentSelection currentLanguage showAvailableLanguages , dropdownList showAvailableLanguages selectableLanguages ] @paulfioravanti 87
  62. module LanguageDropdown exposing (view) view : Model -> Html Msg

    view { currentLanguage, showAvailableLanguages } = let selectableLanguages = Language.availableLanguages |> List.filter (\language -> language /= currentLanguage) in div [ class Styles.dropdownContainer ] [ currentSelection currentLanguage showAvailableLanguages , dropdownList showAvailableLanguages selectableLanguages ] @paulfioravanti 88
  63. module LanguageDropdown exposing (view) view : Model -> Html Msg

    view { currentLanguage, showAvailableLanguages } = let selectableLanguages = Language.availableLanguages |> List.filter (\language -> language /= currentLanguage) in div [ class Styles.dropdownContainer ] [ currentSelection currentLanguage showAvailableLanguages , dropdownList showAvailableLanguages selectableLanguages ] @paulfioravanti 89
  64. currentSelection : Lang -> Bool -> Html Msg currentSelection currentLanguage

    showAvailableLanguages = p [ class (Styles.currentSelection showAvailableLanguages) , onClick ShowAvailableLanguages ] [ span [] [ text (Language.langToString currentLanguage) ] , span [ class Styles.caret ] [ text "▾" ] ] @paulfioravanti 90
  65. currentSelection : Lang -> Bool -> Html Msg currentSelection currentLanguage

    showAvailableLanguages = p [ class (Styles.currentSelection showAvailableLanguages) , onClick ShowAvailableLanguages ] [ span [] [ text (Language.langToString currentLanguage) ] , span [ class Styles.caret ] [ text "▾" ] ] @paulfioravanti 91
  66. dropdownList : Bool -> List Lang -> Html Msg dropdownList

    showAvailableLanguages selectableLanguages = ul [ class (Styles.dropdownList showAvailableLanguages) ] (List.map dropdownListItem selectableLanguages) dropdownListItem : Lang -> Html Msg dropdownListItem language = li [ class Styles.dropdownListItem , onClick (ChangeLanguage language) ] [ span [] [ text (Language.langToString language) ] ] @paulfioravanti 92
  67. dropdownList : Bool -> List Lang -> Html Msg dropdownList

    showAvailableLanguages selectableLanguages = ul [ class (Styles.dropdownList showAvailableLanguages) ] (List.map dropdownListItem selectableLanguages) dropdownListItem : Lang -> Html Msg dropdownListItem language = li [ class Styles.dropdownListItem , onClick (ChangeLanguage language) ] [ span [] [ text (Language.langToString language) ] ] @paulfioravanti 93
  68. dropdownList : Bool -> List Lang -> Html Msg dropdownList

    showAvailableLanguages selectableLanguages = ul [ class (Styles.dropdownList showAvailableLanguages) ] (List.map dropdownListItem selectableLanguages) dropdownListItem : Lang -> Html Msg dropdownListItem language = li [ class Styles.dropdownListItem , onClick (ChangeLanguage language) ] [ span [] [ text (Language.langToString language) ] ] @paulfioravanti 94
  69. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ LanguageDropdown.view , content model.translations ] content : Translations -> Html Msg content translations = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading translations ] ] heading : Translations -> Html Msg heading translations = h1 [ class Styles.heading ] [ text (I18Next.t translations "verticallyCenteringInCssIsEasy")] @paulfioravanti 96
  70. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ LanguageDropdown.view , content model.translations ] content : Translations -> Html Msg content translations = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading translations ] ] heading : Translations -> Html Msg heading translations = h1 [ class Styles.heading ] [ text (I18Next.t translations "verticallyCenteringInCssIsEasy")] @paulfioravanti 97
  71. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ LanguageDropdown.view , content model.translations ] content : Translations -> Html Msg content translations = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading translations ] ] heading : Translations -> Html Msg heading translations = h1 [ class Styles.heading ] [ text (I18Next.t translations "verticallyCenteringInCssIsEasy")] @paulfioravanti 98
  72. view : Model -> Html Msg view model = main_

    [ class Styles.main_ ] [ LanguageDropdown.view , content model.translations ] content : Translations -> Html Msg content translations = article [ class Styles.article ] [ div [ class Styles.articleContainer ] [ heading translations ] ] heading : Translations -> Html Msg heading translations = h1 [ class Styles.heading ] [ text (I18Next.t translations "verticallyCenteringInCssIsEasy")] @paulfioravanti 99
  73. import "tachyons" import { Main } from "./Main.elm" const appContainer

    = document.getElementById("root") if (appContainer) { Main.embed(appContainer, { language: getLanguage() }) } function getLanguage() { return navigator.language || navigator.userLanguage } @paulfioravanti 103
  74. import "tachyons" import { Main } from "./Main.elm" const appContainer

    = document.getElementById("root") if (appContainer) { Main.embed(appContainer, { language: getLanguage() }) } function getLanguage() { return navigator.language || navigator.userLanguage } @paulfioravanti 104
  75. import "tachyons" import { Main } from "./Main.elm" const appContainer

    = document.getElementById("root") if (appContainer) { Main.embed(appContainer, { language: getLanguage() }) } function getLanguage() { return navigator.language || navigator.userLanguage } @paulfioravanti 105
  76. import "tachyons" import { Main } from "./Main.elm" const appContainer

    = document.getElementById("root") if (appContainer) { Main.embed(appContainer, { language: getLanguage() }) } function getLanguage() { return navigator.language || navigator.userLanguage } @paulfioravanti 106
  77. import Json.Decode as Decode exposing (Value) type alias Flags =

    { language : Value } @paulfioravanti 107
  78. init : Flags -> ( Model, Cmd Msg ) init

    flags = let language = flags.language |> Decode.decodeValue Decode.string |> Language.langFromFlag in ( { currentLanguage = language , showAvailableLanguages = False , translations = I18Next.initialTranslations } , Cmd.fetchTranslations language ) @paulfioravanti 108
  79. init : Flags -> ( Model, Cmd Msg ) init

    flags = let language = flags.language |> Decode.decodeValue Decode.string |> Language.langFromFlag in ( { currentLanguage = language , showAvailableLanguages = False , translations = I18Next.initialTranslations } , Cmd.fetchTranslations language ) @paulfioravanti 109
  80. module Language exposing (..) langFromFlag : Result String String ->

    Lang langFromFlag language = case language of Ok language -> Translations.getLnFromCode language Err _ -> En @paulfioravanti 110
  81. port module Main exposing (..) port storeLanguageInLocalStorage : String ->

    Cmd msg storeLanguage : Lang -> Cmd msg storeLanguage language = language |> toString |> String.toLower |> storeLanguageInLocalStorage @paulfioravanti 112
  82. port module Main exposing (..) port storeLanguageInLocalStorage : String ->

    Cmd msg storeLanguage : Lang -> Cmd msg storeLanguage language = language |> toString |> String.toLower |> storeLanguageInLocalStorage @paulfioravanti 113
  83. port module Main exposing (..) port storeLanguageInLocalStorage : String ->

    Cmd msg storeLanguage : Lang -> Cmd msg storeLanguage language = language |> toString |> String.toLower |> storeLanguageInLocalStorage @paulfioravanti 114
  84. if (appContainer) { const app = Main.embed(appContainer, { language: getLanguage()

    }) app.ports.storeLanguageInLocalStorage.subscribe((language) => { localStorage.setItem("elm-i18n-example-language", language) }) } function getLanguage() { return localStorage.getItem("elm-i18n-example-language") || navigator.language || navigator.userLanguage } @paulfioravanti 115
  85. if (appContainer) { const app = Main.embed(appContainer, { language: getLanguage()

    }) app.ports.storeLanguageInLocalStorage.subscribe((language) => { localStorage.setItem("elm-i18n-example-language", language) }) } function getLanguage() { return localStorage.getItem("elm-i18n-example-language") || navigator.language || navigator.userLanguage } @paulfioravanti 116
  86. if (appContainer) { const app = Main.embed(appContainer, { language: getLanguage()

    }) app.ports.storeLanguageInLocalStorage.subscribe((language) => { localStorage.setItem("elm-i18n-example-language", language) }) } function getLanguage() { return localStorage.getItem("elm-i18n-example-language") || navigator.language || navigator.userLanguage } @paulfioravanti 117
  87. if (appContainer) { const app = Main.embed(appContainer, { language: getLanguage()

    }) app.ports.storeLanguageInLocalStorage.subscribe((language) => { localStorage.setItem("elm-i18n-example-language", language) }) } function getLanguage() { return localStorage.getItem("elm-i18n-example-language") || navigator.language || navigator.userLanguage } @paulfioravanti 118
  88. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of -- ... ChangeLanguage language -> ( { model | currentLanguage = language } , Cmd.batch [ fetchTranslations language , storeLanguage language ] ) @paulfioravanti 119
  89. update : Msg -> Model -> ( Model, Cmd Msg

    ) update msg model = case msg of -- ... ChangeLanguage language -> ( { model | currentLanguage = language } , Cmd.batch [ fetchTranslations language , storeLanguage language ] ) @paulfioravanti 120
  90. Lingering Issues 4 Translation key visible on refresh 4 I18Next.t

    translations "thisKeyDoesNotExist" @paulfioravanti 126
  91. Lingering Issues 4 Translation key visible on refresh 4 I18Next.t

    translations "thisKeyDoesNotExist" 4 Missed translations and typos ignored @paulfioravanti 127
  92. module Translations exposing (..) -- ... verticallyCenteringInCssIsEasy : Lang ->

    String verticallyCenteringInCssIsEasy lang = case lang of En -> "Vertically centering things in css is easy!" It -> "Centrare verticalmente con css è facile!" Ja -> "CSSͰਨ௚ηϯλϦϯά͸؆୯ͩΑʂ" @paulfioravanti 131
  93. module Translations exposing (..) type Lang = En | It

    | Ja getLnFromCode : String -> Lang getLnFromCode code = -- ... verticallyCenteringInCssIsEasy : Lang -> String verticallyCenteringInCssIsEasy lang = case lang of En -> "Vertically centering things in css is easy!" It -> "Centrare verticalmente con css è facile!" Ja -> "CSSͰਨ௚ηϯλϦϯά͸؆୯ͩΑʂ" @paulfioravanti 132
  94. module Translations exposing (..) -- ... verticallyCenteringInCssIsEasy : Lang ->

    String verticallyCenteringInCssIsEasy lang = case lang of En -> "Vertically centering things in css is easy!" It -> "Centrare verticalmente con css è facile!" Ja -> "CSSͰਨ௚ηϯλϦϯά͸؆୯ͩΑʂ" @paulfioravanti 133
  95. view : Model -> Html Msg view model = --

    ... content : Translations -> Html Msg content translations = -- ... heading : Translations -> Html Msg heading translations = h1 [ class Styles.heading ] [ text (I18Next.t translations "verticallyCenteringInCssIsEasy")] @paulfioravanti 134
  96. view : Model -> Html Msg view model = --

    ... content : Lang -> Html Msg content language = -- ... heading : Lang -> Html Msg heading language = h1 [ class Styles.heading ] [ text (Translations.verticallyCenteringInCssIsEasy language) ] @paulfioravanti 135
  97. Type-Safe Translations 4 No need to fetch translations 4 No

    translation key visible @paulfioravanti 137
  98. Type-Safe Translations 4 No need to fetch translations 4 No

    translation key visible 4 Compiler errors if translation not provided @paulfioravanti 138