├── .gitignore ├── App ├── Model.elm ├── Update.elm └── View.elm ├── LICENSE ├── Main.elm ├── README.md ├── TestRunner.elm ├── Translation ├── Test.elm └── Utils.elm └── elm-package.json /.gitignore: -------------------------------------------------------------------------------- 1 | elm-stuff 2 | index.html 3 | elm.js 4 | -------------------------------------------------------------------------------- /App/Model.elm: -------------------------------------------------------------------------------- 1 | module App.Model where 2 | 3 | import Translation.Utils exposing (..) 4 | 5 | type alias Model = 6 | { currentLanguage : Language 7 | , name : String 8 | } 9 | 10 | 11 | initialModel : Model 12 | initialModel = 13 | { currentLanguage = English 14 | , name = "Elm lover" 15 | } 16 | -------------------------------------------------------------------------------- /App/Update.elm: -------------------------------------------------------------------------------- 1 | module App.Update 2 | (Action (..) 3 | , init 4 | , update 5 | ) 6 | where 7 | 8 | import App.Model as App exposing (initialModel, Model) 9 | import Effects exposing (none, Effects) 10 | import Translation.Utils exposing (..) 11 | 12 | init : (Model, Effects Action) 13 | init = 14 | ( App.initialModel 15 | , Effects.none 16 | ) 17 | 18 | type Action 19 | = SetLanguage Language 20 | | SetName String 21 | 22 | update : Action -> Model -> (Model, Effects Action) 23 | update action model = 24 | case action of 25 | SetLanguage lang -> 26 | ( {model | currentLanguage = lang} 27 | , Effects.none 28 | ) 29 | 30 | SetName str -> 31 | ( {model | name = str} 32 | , Effects.none 33 | ) 34 | -------------------------------------------------------------------------------- /App/View.elm: -------------------------------------------------------------------------------- 1 | module App.View where 2 | 3 | import App.Model as App exposing (Model) 4 | import App.Update exposing (Action(..)) 5 | 6 | import Html exposing (button, div, input, text, Attribute, Html) 7 | import Html.Attributes exposing (action, class, disabled, id, hidden, href, placeholder, required, size, style, type', value) 8 | import Html.Events exposing (on, onClick, targetValue) 9 | import Translation.Utils exposing (..) 10 | 11 | view : Signal.Address Action -> Model -> Html 12 | view address model = 13 | div 14 | [ containerStyle ] 15 | [ viewLanguageSwitcher address model.currentLanguage 16 | , viewWelcomeMessage address model 17 | ] 18 | 19 | 20 | viewLanguageSwitcher : Signal.Address Action -> Language -> Html 21 | viewLanguageSwitcher address lang = 22 | let 23 | -- Check if a language is the current language 24 | isCurrent lang' = 25 | lang == lang' 26 | 27 | button' lang' name = 28 | button 29 | [ disabled (isCurrent lang') 30 | , onClick address <| SetLanguage lang' 31 | , elementStyle 32 | ] 33 | [ text name ] 34 | 35 | in 36 | div 37 | [] 38 | [ button' English "English" 39 | , button' Spanish "Spanish" 40 | ] 41 | 42 | viewWelcomeMessage : Signal.Address Action -> Model -> Html 43 | viewWelcomeMessage address model = 44 | div 45 | [] 46 | [ div [ elementStyle ] [ text <| translate model.currentLanguage Login ] 47 | , div [ elementStyle ] [ text <| translate model.currentLanguage <| WelcomeBack { name = model.name } ] 48 | , input 49 | [ type' "text" 50 | , placeholder "Name" 51 | , value model.name 52 | , on "input" targetValue (Signal.message address << SetName) 53 | ] 54 | [] 55 | ] 56 | 57 | containerStyle : Attribute 58 | containerStyle = 59 | style 60 | [ ("width", "100%") 61 | , ("height", "100%") 62 | , ("background-color", "#1275b2") 63 | , ("text-align", "center") 64 | , ("padding", "100px 0") 65 | , ("color", "white") 66 | , ("font-size", "20px") 67 | ] 68 | 69 | elementStyle : Attribute 70 | elementStyle = 71 | style 72 | [ ("margin-bottom", "20px") ] 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Main.elm: -------------------------------------------------------------------------------- 1 | import App.Model as App exposing (Model) 2 | import App.Update exposing (init, update) 3 | import App.View exposing (view) 4 | import StartApp as StartApp 5 | import Task exposing (Task) 6 | 7 | 8 | app = 9 | StartApp.start 10 | { init = App.Update.init 11 | , update = App.Update.update 12 | , view = App.View.view 13 | , inputs = [] 14 | } 15 | 16 | main = 17 | app.html 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elm i18n example 2 | 3 | > A dummy webapp showing a language switcher and translated text, with type safety. 4 | 5 | Read the [blog post](http://gizra.com/content/elm-i18n-type-safety) 6 | 7 | ## Execute 8 | 9 | Run `elm-reactor` 10 | 11 | App: http://localhost:8000/Main.elm 12 | Tests: http://localhost:8000/TestRunner.elm 13 | 14 | ## Resources 15 | 16 | - Script for generating Elm translations from JSON files: 17 | [i18n-to-elm](https://github.com/dragonwasrobot/i18n-to-elm) 18 | -------------------------------------------------------------------------------- /TestRunner.elm: -------------------------------------------------------------------------------- 1 | module Main where 2 | 3 | import Graphics.Element exposing (Element) 4 | 5 | import ElmTest exposing (..) 6 | 7 | import Translation.Test as Translation 8 | 9 | 10 | allTests : Test 11 | allTests = 12 | suite "All tests" 13 | [ Translation.all 14 | ] 15 | 16 | main : Element 17 | main = 18 | elementRunner allTests 19 | -------------------------------------------------------------------------------- /Translation/Test.elm: -------------------------------------------------------------------------------- 1 | module Translation.Test where 2 | 3 | import ElmTest exposing (..) 4 | 5 | import Translation.Utils exposing (..) 6 | 7 | all : Test 8 | all = 9 | suite "Translation tests" 10 | [ test "English no arguments" (assertEqual "Please login" (translate English Login)) 11 | , test "Spanish no arguments" (assertEqual "Por favor haga login" (translate Spanish Login)) 12 | , test "English with arguments" (assertEqual ("Welcome back elm") (translate English <| WelcomeBack { name = "elm" })) 13 | ] 14 | -------------------------------------------------------------------------------- /Translation/Utils.elm: -------------------------------------------------------------------------------- 1 | module Translation.Utils 2 | ( Language (..) 3 | , TranslationId (..) 4 | , translate 5 | ) 6 | where 7 | 8 | type alias TranslationSet = 9 | { english : String 10 | , spanish : String 11 | } 12 | 13 | type TranslationId 14 | = Login 15 | | WelcomeBack {name : String} 16 | 17 | type Language 18 | = English 19 | | Spanish 20 | 21 | translate : Language -> TranslationId -> String 22 | translate lang trans = 23 | let 24 | translationSet = 25 | case trans of 26 | Login -> 27 | TranslationSet "Please login" "Por favor haga login" 28 | 29 | WelcomeBack val -> 30 | TranslationSet ("Welcome back " ++ val.name) ("Bienvenido " ++ val.name) 31 | in 32 | case lang of 33 | English -> 34 | .english translationSet 35 | Spanish -> 36 | .spanish translationSet 37 | -------------------------------------------------------------------------------- /elm-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "summary": "i18n example", 4 | "repository": "https://github.com/gizra/elm-i18n-example.git", 5 | "license": "MIT", 6 | "source-directories": [ 7 | "." 8 | ], 9 | "exposed-modules": [], 10 | "dependencies": { 11 | "deadfoxygrandpa/elm-test": "3.1.1 <= v < 4.0.0", 12 | "elm-lang/core": "3.0.0 <= v < 4.0.0", 13 | "evancz/elm-effects": "2.0.1 <= v < 3.0.0", 14 | "evancz/elm-html": "4.0.2 <= v < 5.0.0", 15 | "evancz/start-app": "2.0.2 <= v < 3.0.0" 16 | }, 17 | "elm-version": "0.16.0 <= v < 0.17.0" 18 | } --------------------------------------------------------------------------------