1

我正在尝试将即将出版的“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代码上。

我相信这headTags是一个HtmlUrl. 我也认为我需要将其转换为 a HtmlUrlI18n,但不知道如何。

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

我已经尝试谷歌搜索几个小时,但找不到任何defaultLayout使用 i18n 创建新的广泛示例。

4

1 回答 1

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是从一个需要两个参数的函数到一个需要一个参数的函数的柯里化。然后忽略第二个参数。插值调用该单参数函数并传入 a Translate msg,它被忽略,留下一个类型的函数Render url -> Html,正是我们传入的headTags

同样的逻辑适用于bodyTags.

谢谢,迈克尔。

于 2013-06-08T17:48:40.673 回答