Yesod i18n,defaultLayout和ihamlet

时间:2013-06-02 17:49:19

标签: haskell internationalization yesod

我正在尝试将即将出版的“Play for Scala”一书的第2章中的教程代码从Scala转换为Haskell(使用Yesod)。我试图将defaultLayout“国际化”时遇到错误。我(故意)不使用脚手架的Yesod网站,因为我试图理解内部。这是我的代码:

{-# LANGUAGE FlexibleInstances
           , MultiParamTypeClasses
           , OverloadedStrings
           , QuasiQuotes
           , TemplateHaskell
           , TypeFamilies #-}

module Main where

import Text.Hamlet (ihamlet)
import Yesod
import Yesod.Static

staticFiles "static"

data PlayTutorial = PlayTutorial
  { getStatic :: Static
  }

mkMessage "PlayTutorial" "messages" "en"

mkYesod "PlayTutorial" [parseRoutes|
  / RootR GET
  /static StaticR Static getStatic
|]

instance Yesod PlayTutorial where
  defaultLayout contents = do
    PageContent title headTags bodyTags <- widgetToPageContent $ do
      addStylesheet $ StaticR stylesheets_bootstrap_css
      addStylesheet $ StaticR stylesheets_main_css
      contents

    ihamletToRepHtml [ihamlet|
      $doctype 5

      <html>
        <head>
          <title>#{title}
          ^{headTags}
        <body>
          <div ."screenshot">
            <div ."navbar navbar-fixed-top">
              <div ."container">
                <a ."brand" href=@{RootR}>
                  _{MsgApplicationName}
            <div ."container">
              ^{bodyTags}
    |]

getRootR :: Handler RepHtml
getRootR = defaultLayout [whamlet|Hello, World!|]

main :: IO ()
main = do
  static@(Static settings) <- static "static"
  warp 8080 $ PlayTutorial static

我尝试使用runhaskell构建或运行它的错误是

src/Main.hs:34:31:
    Couldn't match type `Text.Blaze.Internal.MarkupM ()'
                  with `[(Data.Text.Internal.Text, Data.Text.Internal.Text)]
                        -> Data.Text.Internal.Text'
    Expected type: Text.Hamlet.Render (Route PlayTutorial)
      Actual type: Text.Hamlet.Translate (Route PlayTutorial)
    In the first argument of `headTags', namely `_mrender_a7j2'
    In a stmt of a 'do' block: headTags _mrender_a7j2 _urender_a7j1
    In the expression:
      do { id
             ((Text.Blaze.Internal.preEscapedText . Data.Text.pack)
                "<!DOCTYPE html>\
                \<html><head><title>");
           id (toHtml title);
           id
             ((Text.Blaze.Internal.preEscapedText . Data.Text.pack) "</title>");
           headTags _mrender_a7j2 _urender_a7j1;
           .... }

ihamlet代码发生错误。

我认为headTagsHtmlUrl。我还认为我需要将其转换为HtmlUrlI18n,但无法弄清楚如何。

我可以做同样的事情(定义{{​​1}})作为一个小部件(使用defaultLayout)然后使用whamlet将其转换为PageContent,然后转换为{{{ 1}}(不确定如何),而不是使用widgetToPageContent?这会解决i18n问题吗?

我已经尝试了谷歌搜索几个小时,但找不到任何广泛的例子,用i18n创建一个新的RepHtml

1 个答案:

答案 0 :(得分:1)

Google Groups与Michael Snoyman(Yesod框架的创始人和首席开发人员)进行简短讨论后,我相信我现在理解了这个问题,更重要的是解决了这个问题。

headTags

type HtmlUrl url = Render url -> Html

^{}中的ihamlet插值需要

type HtmlUrlI18n msg url = Translate msg -> Render url -> Html

所以我们必须从一个转换为另一个。

Prelude中的函数const定义为

const :: a -> b -> a
const x _ =  x

因此,如果我们在const headTags插值中嵌入^{},那就可以了。

表达式const headTags是一个 currying 来自一个函数,该函数需要两个参数,这个参数需要一个参数。然后忽略第二个参数。插值调用单参数函数并传入Translate msg,忽略该函数,留下类型为Render url -> Html的函数,这正是我们传递给headTags的函数。

同样的逻辑适用于bodyTags

谢谢,迈克尔。