什么是使Elm应用程序国际化的好方法?

时间:2016-10-27 00:22:50

标签: elm

我需要将ELM HTML应用程序中的UI字符串国际化为3种不同的语言。

我正在考虑这样做:

1)我将从Javascript获取currentLanguage,并在ProgramWithFlags中传递它。我将保持模型中的语言

2)我将在我的代码中设置一些类型

type alias Languages = English | French | Spanish
-- One of these for each string I want to internationalize
type alias InternationalizedStrings = StringHello | StringFoo | StringBar

3)我将创建一个函数,用于返回在我的视图中使用的每个翻译短语。

getPhrase: InternationalizationString Languages -> string
getPhrase stringId lang = 
   case lang of
      English ->
           case stringId of
              StringHello -> "Hello"
              StringFoo -> "Foo"
              StringBar -> "Bar"
      French ->
           case stringId of
              StringHello -> "Bonjour"
              StringFoo -> "Oui"
              StringBar -> "Non"
      ...

有更好的方法吗?我有很多字符串。

3 个答案:

答案 0 :(得分:4)

如果您在没有为字符串提供翻译时想要编译错误,那么您的解决方案就在正确的轨道上。

如果您想要允许未翻译的字符串,或者发现每个可翻译字符串都有一个类型,那么您可能需要切换到基于Dict的解决方案。修补它,把它扔进http://elm-lang.org/try

import Dict exposing (Dict)
import Html exposing (text)


type Language
    = English
    | French
    | Spanish


type alias Key =
    String


main =
    text <| translate French "Hello"


translate : Language -> Key -> String
translate lang key =
    let
        dict =
            case lang of
                English ->
                    Dict.fromList
                        [ ( "Hello", "in english" )
                        ]

                French ->
                    Dict.fromList
                        [ ( "Hello", "salut" )
                        ]

                Spanish ->
                    Dict.fromList
                        [ ( "Hello", "hola" )
                        , ( "someKeyThatOnlyExistsInSpanish", "42" )
                        ]
    in
        Dict.get key dict |> Maybe.withDefault ("can not find translation for " ++ key)

答案 1 :(得分:2)

不久前,我在国际化方面遇到了麻烦,并提出了以下设置:

  • 在全球model
  • 中定义语言
  • 有一个非常简单的功能,可用于view模块和功能
  • 该函数的签名为localString : Language -> String -> String
  • localString基本上在全球字典中查找,以便从您提供的字词到您提供的语言中找到翻译。
  • 如果无法找到您提供的字词,或者无法找到您提供的语言的翻译,它将始终返回String,默认为原始字词。
  • 保持全局字典(和帮助器)函数不在模型中,但在单独的文件中(它是非常静态的数据,在运行时不会改变)。
  • Language类型是联盟类型,以确保我们只有“已批准”的语言。
  • 实际字典使用转换为字符串。 Dict类型不允许使用强类型作为密钥。

这样,使用国际化对其余代码的影响最小:

  • 您需要向Language添加Model(您可以通过JS端口获取)
  • 您仍然可以在视图中使用简短易读的代码进行翻译,例如

    p [] [ text <| localString model.language "car" ]

  • 您自己代码中的所有硬编码字符串都保留在一种简单的默认语言中,以保持代码的其余部分可读。

以下是我正在处理的内容,您可以复制/粘贴到elm-lang.org/try(未通过大量字符串和翻译在功能或性能方面进行全面测试)

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" 
      ]
    ]

答案 2 :(得分:2)

几个月前我写了blog post about this。如果你有能力,试着优先使用Dict以上的ADT,因为Dict不能在类型级别给你相同的保证(这就是Dict.get返回{{1}的原因}})。 ADT还可以将您正在执行类型检查的数据类型以及Maybe a模式匹配,并使用您想要的任何MyPhrase Int String方法(例如toString)。话虽如此,现有的系统/翻译服务可能会在不编写从MyPhrase foo bar -> "My phrase contains " ++ toString foo ++ " & " ++ bar ++ ".".elm.json的解析器的情况下难以使用此方法。