Prepare for selecting languages

UI language is stored in user settings for authenticated users;
otherwise is only stored in the current model (not persisted).
This commit is contained in:
Eike Kettner 2021-03-27 22:00:50 +01:00
parent 9fcab84267
commit 322a3e837c
10 changed files with 335 additions and 1 deletions

View File

@ -2,6 +2,7 @@ module App.Data exposing
( Model ( Model
, Msg(..) , Msg(..)
, defaultPage , defaultPage
, getUiLanguage
, init , init
) )
@ -24,6 +25,7 @@ import Page.Queue.Data
import Page.Register.Data import Page.Register.Data
import Page.Upload.Data import Page.Upload.Data
import Page.UserSettings.Data import Page.UserSettings.Data
import UiLanguage exposing (UiLanguage)
import Url exposing (Url) import Url exposing (Url)
@ -48,6 +50,8 @@ type alias Model =
, uiSettings : UiSettings , uiSettings : UiSettings
, sidebarVisible : Bool , sidebarVisible : Bool
, anonymousTheme : UiTheme , anonymousTheme : UiTheme
, anonymousUiLang : UiLanguage
, langMenuOpen : Bool
} }
@ -97,6 +101,8 @@ init key url flags_ settings =
, uiSettings = settings , uiSettings = settings
, sidebarVisible = settings.sideMenuVisible , sidebarVisible = settings.sideMenuVisible
, anonymousTheme = Data.UiTheme.Light , anonymousTheme = Data.UiTheme.Light
, anonymousUiLang = UiLanguage.English
, langMenuOpen = False
} }
, Cmd.batch , Cmd.batch
[ Cmd.map UserSettingsMsg uc [ Cmd.map UserSettingsMsg uc
@ -152,8 +158,20 @@ type Msg
| GetUiSettings UiSettings | GetUiSettings UiSettings
| ToggleSidebar | ToggleSidebar
| ToggleDarkMode | ToggleDarkMode
| ToggleLangMenu
| SetLanguage UiLanguage
defaultPage : Flags -> Page defaultPage : Flags -> Page
defaultPage flags = defaultPage flags =
HomePage HomePage
getUiLanguage : Model -> UiLanguage
getUiLanguage model =
case model.flags.account of
Just _ ->
model.uiSettings.uiLang
Nothing ->
model.anonymousUiLang

View File

@ -84,6 +84,15 @@ updateWithSub msg model =
, Sub.none , Sub.none
) )
ToggleLangMenu ->
( { model | langMenuOpen = not model.langMenuOpen }
, Cmd.none
, Sub.none
)
SetLanguage lang ->
( { model | anonymousUiLang = lang, langMenuOpen = False }, Cmd.none, Sub.none )
HomeMsg lm -> HomeMsg lm ->
updateHome lm model updateHome lm model

View File

@ -7,6 +7,7 @@ import Data.Flags
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (onClick) import Html.Events exposing (onClick)
import Messages
import Page exposing (Page(..)) import Page exposing (Page(..))
import Page.CollectiveSettings.View2 as CollectiveSettings import Page.CollectiveSettings.View2 as CollectiveSettings
import Page.Home.Data import Page.Home.Data
@ -20,6 +21,7 @@ import Page.Register.View2 as Register
import Page.Upload.View2 as Upload import Page.Upload.View2 as Upload
import Page.UserSettings.View2 as UserSettings import Page.UserSettings.View2 as UserSettings
import Styles as S import Styles as S
import UiLanguage
view : Model -> List (Html Msg) view : Model -> List (Html Msg)
@ -64,13 +66,18 @@ topNavUser auth model =
topNavAnon : Model -> Html Msg topNavAnon : Model -> Html Msg
topNavAnon model = topNavAnon model =
let
texts =
Messages.get <| App.Data.getUiLanguage model
in
nav nav
[ id "top-nav" [ id "top-nav"
, class styleTopNav , class styleTopNav
] ]
[ headerNavItem model [ headerNavItem model
, div [ class "flex flex-grow justify-end" ] , div [ class "flex flex-grow justify-end" ]
[ a [ langMenu model
, a
[ href "#" [ href "#"
, onClick ToggleDarkMode , onClick ToggleDarkMode
, class dropdownLink , class dropdownLink
@ -100,6 +107,10 @@ headerNavItem model =
mainContent : Model -> Html Msg mainContent : Model -> Html Msg
mainContent model = mainContent model =
let
texts =
Messages.get <| App.Data.getUiLanguage model
in
div div
[ id "main" [ id "main"
, class styleMain , class styleMain
@ -151,6 +162,45 @@ styleMain =
"mt-12 flex md:flex-row flex-col w-full h-screen-12 overflow-y-hidden bg-white dark:bg-bluegray-800 text-gray-800 dark:text-bluegray-300 antialiased" "mt-12 flex md:flex-row flex-col w-full h-screen-12 overflow-y-hidden bg-white dark:bg-bluegray-800 text-gray-800 dark:text-bluegray-300 antialiased"
langMenu : Model -> Html Msg
langMenu model =
let
texts =
Messages.get <| App.Data.getUiLanguage model
langItem lang =
let
langMsg =
Messages.get lang
in
a
[ classList
[ ( dropdownItem, True )
, ( "bg-gray-200 dark:bg-bluegray-700", lang == texts.lang )
]
, onClick (SetLanguage lang)
, href "#"
]
[ i [ langMsg |> .flagIcon |> class ] []
, span [ class "ml-2" ] [ text langMsg.label ]
]
in
div [ class "relative" ]
[ a
[ class dropdownLink
, onClick ToggleLangMenu
, href "#"
]
[ i [ class texts.flagIcon ] []
]
, div
[ class dropdownMenu
, classList [ ( "hidden", not model.langMenuOpen ) ]
]
(List.map langItem UiLanguage.all)
]
dataMenu : AuthResult -> Model -> Html Msg dataMenu : AuthResult -> Model -> Html Msg
dataMenu _ model = dataMenu _ model =
div [ class "relative" ] div [ class "relative" ]

View File

@ -11,11 +11,13 @@ import Api.Model.TagList exposing (TagList)
import Comp.BasicSizeField import Comp.BasicSizeField
import Comp.ColorTagger import Comp.ColorTagger
import Comp.FieldListSelect import Comp.FieldListSelect
import Comp.FixedDropdown
import Comp.IntField import Comp.IntField
import Comp.MenuBar as MB import Comp.MenuBar as MB
import Comp.Tabs import Comp.Tabs
import Data.BasicSize exposing (BasicSize) import Data.BasicSize exposing (BasicSize)
import Data.Color exposing (Color) import Data.Color exposing (Color)
import Data.DropdownStyle as DS
import Data.Fields exposing (Field) import Data.Fields exposing (Field)
import Data.Flags exposing (Flags) import Data.Flags exposing (Flags)
import Data.ItemTemplate as IT exposing (ItemTemplate) import Data.ItemTemplate as IT exposing (ItemTemplate)
@ -26,8 +28,10 @@ import Html.Attributes exposing (..)
import Html.Events exposing (onClick, onInput) import Html.Events exposing (onClick, onInput)
import Http import Http
import Markdown import Markdown
import Messages
import Set exposing (Set) import Set exposing (Set)
import Styles as S import Styles as S
import UiLanguage exposing (UiLanguage)
import Util.Maybe import Util.Maybe
import Util.Tag import Util.Tag
@ -56,6 +60,8 @@ type alias Model =
, searchStatsVisible : Bool , searchStatsVisible : Bool
, sideMenuVisible : Bool , sideMenuVisible : Bool
, powerSearchEnabled : Bool , powerSearchEnabled : Bool
, uiLangModel : Comp.FixedDropdown.Model UiLanguage
, uiLang : UiLanguage
, openTabs : Set String , openTabs : Set String
} }
@ -148,6 +154,10 @@ init flags settings =
, searchStatsVisible = settings.searchStatsVisible , searchStatsVisible = settings.searchStatsVisible
, sideMenuVisible = settings.sideMenuVisible , sideMenuVisible = settings.sideMenuVisible
, powerSearchEnabled = settings.powerSearchEnabled , powerSearchEnabled = settings.powerSearchEnabled
, uiLang = settings.uiLang
, uiLangModel =
List.map langItem UiLanguage.all
|> Comp.FixedDropdown.init
, openTabs = Set.empty , openTabs = Set.empty
} }
, Api.getTags flags "" GetTagsResp , Api.getTags flags "" GetTagsResp
@ -174,6 +184,15 @@ type Msg
| ToggleAkkordionTab String | ToggleAkkordionTab String
| ToggleSideMenuVisible | ToggleSideMenuVisible
| TogglePowerSearch | TogglePowerSearch
| UiLangMsg (Comp.FixedDropdown.Msg UiLanguage)
langItem : UiLanguage -> Comp.FixedDropdown.Item UiLanguage
langItem lang =
{ id = lang
, display = Messages.get lang |> .label
, icon = Just (Messages.get lang |> .flagIcon)
}
@ -445,6 +464,22 @@ update sett msg model =
, Just { sett | powerSearchEnabled = next } , Just { sett | powerSearchEnabled = next }
) )
UiLangMsg lm ->
let
( m, sel ) =
Comp.FixedDropdown.update lm model.uiLangModel
newLang =
Maybe.withDefault model.uiLang sel
in
( { model | uiLangModel = m, uiLang = newLang }
, if newLang == model.uiLang then
Nothing
else
Just { sett | uiLang = newLang }
)
--- View2 --- View2
@ -494,6 +529,15 @@ settingFormTabs flags _ model =
, value = model.sideMenuVisible , value = model.sideMenuVisible
} }
] ]
, div [ class "mb-4" ]
[ label [ class S.inputLabel ] [ text "UI Language" ]
, Html.map UiLangMsg
(Comp.FixedDropdown.viewStyled2 DS.mainStyle
False
(Just <| langItem model.uiLang)
model.uiLangModel
)
]
] ]
} }
, { title = "Item Search" , { title = "Item Search"

View File

@ -32,6 +32,8 @@ import Data.UiTheme exposing (UiTheme)
import Dict exposing (Dict) import Dict exposing (Dict)
import Html exposing (Attribute) import Html exposing (Attribute)
import Html.Attributes as HA import Html.Attributes as HA
import Messages
import UiLanguage exposing (UiLanguage)
{-| Settings for the web ui. All fields should be optional, since it {-| Settings for the web ui. All fields should be optional, since it
@ -63,6 +65,7 @@ type alias StoredUiSettings =
, uiTheme : Maybe String , uiTheme : Maybe String
, sideMenuVisible : Bool , sideMenuVisible : Bool
, powerSearchEnabled : Bool , powerSearchEnabled : Bool
, uiLang : Maybe String
} }
@ -94,6 +97,7 @@ type alias UiSettings =
, uiTheme : UiTheme , uiTheme : UiTheme
, sideMenuVisible : Bool , sideMenuVisible : Bool
, powerSearchEnabled : Bool , powerSearchEnabled : Bool
, uiLang : UiLanguage
} }
@ -165,6 +169,7 @@ defaults =
, uiTheme = Data.UiTheme.Light , uiTheme = Data.UiTheme.Light
, sideMenuVisible = True , sideMenuVisible = True
, powerSearchEnabled = False , powerSearchEnabled = False
, uiLang = UiLanguage.English
} }
@ -217,6 +222,9 @@ merge given fallback =
|> Maybe.withDefault fallback.uiTheme |> Maybe.withDefault fallback.uiTheme
, sideMenuVisible = given.sideMenuVisible , sideMenuVisible = given.sideMenuVisible
, powerSearchEnabled = given.powerSearchEnabled , powerSearchEnabled = given.powerSearchEnabled
, uiLang =
Maybe.map Messages.fromIso2 given.uiLang
|> Maybe.withDefault UiLanguage.English
} }
@ -254,6 +262,7 @@ toStoredUiSettings settings =
, uiTheme = Just (Data.UiTheme.toString settings.uiTheme) , uiTheme = Just (Data.UiTheme.toString settings.uiTheme)
, sideMenuVisible = settings.sideMenuVisible , sideMenuVisible = settings.sideMenuVisible
, powerSearchEnabled = settings.powerSearchEnabled , powerSearchEnabled = settings.powerSearchEnabled
, uiLang = Just <| Messages.toIso2 settings.uiLang
} }

View File

@ -0,0 +1,87 @@
module Messages exposing
( Messages
, fromIso2
, get
, toIso2
)
import Messages.App
import Messages.LoginPage
import UiLanguage exposing (UiLanguage(..))
{-| The messages record contains all strings used in the application.
-}
type alias Messages =
{ lang : UiLanguage
, iso2 : String
, label : String
, flagIcon : String
, app : Messages.App.Texts
, login : Messages.LoginPage.Texts
}
get : UiLanguage -> Messages
get lang =
case lang of
English ->
gb
German ->
de
{-| Get a ISO-3166-1 code of the given lanugage.
-}
toIso2 : UiLanguage -> String
toIso2 lang =
get lang |> .iso2
{-| Return the UiLanguage from given iso2 code. If the iso2 code is not
known, return Nothing.
-}
readIso2 : String -> Maybe UiLanguage
readIso2 iso =
let
isIso lang =
iso == toIso2 lang
in
List.filter isIso UiLanguage.all
|> List.head
{-| return the language from the given iso2 code. if the iso2 code is
not known, return English as a default.
-}
fromIso2 : String -> UiLanguage
fromIso2 iso =
readIso2 iso
|> Maybe.withDefault English
--- Messages Definitions
gb : Messages
gb =
{ lang = English
, iso2 = "gb"
, label = "English"
, flagIcon = "flag-icon flag-icon-gb"
, app = Messages.App.gb
, login = Messages.LoginPage.gb
}
de : Messages
de =
{ lang = German
, iso2 = "de"
, label = "Deutsch"
, flagIcon = "flag-icon flag-icon-de"
, app = Messages.App.de
, login = Messages.LoginPage.de
}

View File

@ -0,0 +1,22 @@
module Messages.App exposing
( Texts
, de
, gb
)
type alias Texts =
{ login : String
}
gb : Texts
gb =
{ login = "Login"
}
de : Texts
de =
{ login = "Anmelden"
}

View File

@ -0,0 +1,22 @@
module Messages.FixedDropdown exposing
( Texts
, de
, gb
)
type alias Texts =
{ select : String
}
gb : Texts
gb =
{ select = "Select"
}
de : Texts
de =
{ select = "Auswahl"
}

View File

@ -0,0 +1,57 @@
module Messages.LoginPage exposing
( Texts
, de
, fr
, gb
)
type alias Texts =
{ username : String
, password : String
, loginPlaceholder : String
, passwordPlaceholder : String
, loginButton : String
, loginSuccessful : String
, noAccount : String
, signupLink : String
}
gb : Texts
gb =
{ username = "Username"
, password = "Password"
, loginPlaceholder = "Login"
, passwordPlaceholder = "Password"
, loginButton = "Login"
, loginSuccessful = "Login successful"
, noAccount = "No account?"
, signupLink = "Sign up!"
}
de : Texts
de =
{ username = "Benutzer"
, password = "Passwort"
, loginPlaceholder = "Benutzer"
, passwordPlaceholder = "Passwort"
, loginButton = "Anmelden"
, loginSuccessful = "Anmeldung erfolgreich"
, noAccount = "Kein Konto?"
, signupLink = "Hier registrieren!"
}
fr : Texts
fr =
{ username = "Identifiant"
, password = "Mot de passe"
, loginPlaceholder = "Utilisateur"
, passwordPlaceholder = "Mot de passe"
, loginButton = "Connexion"
, loginSuccessful = "Identification réussie"
, noAccount = "Pas de compte ?"
, signupLink = "S'inscrire"
}

View File

@ -0,0 +1,16 @@
module UiLanguage exposing
( UiLanguage(..)
, all
)
type UiLanguage
= English
| German
all : List UiLanguage
all =
[ English
, German
]