packages feed

snaplet-i18n 0.0.3 → 0.0.3.1

raw patch · 11 files changed

+236/−217 lines, 11 filesdep +snap-loader-dynamicdep +snap-loader-staticdep ~heistdep ~snapdep ~snap-core

Dependencies added: snap-loader-dynamic, snap-loader-static

Dependency ranges changed: heist, snap, snap-core, xmlhtml

Files

README.md view
@@ -1,22 +1,25 @@- [![Build Status](https://secure.travis-ci.org/HaskellCNOrg/snaplet-i18n.png?branch=master)](http://travis-ci.org/HaskellCNOrg/snaplet-i18n)  ## snaplet-i18n -1. create config below into `data/message_en.cfg`+  1. create config below into `data/message_en.cfg`  ~~~ hello = "Hello" shanghai = "ShangHai" ~~~ -2. use tag in heist template+  2. use tag in heist template  ~~~ <i18n name="shanghai"></i18n>+<i18nSpan name="shanghai"></i18n>+<i18n name="shanghai">+  <a><i18nValue /></a>+</i18n> ~~~ -**see test at test/snap.hs***+**see example at example dir**  ## Snaplet @@ -27,6 +30,5 @@ ## TODO  - maybe multiple locale support at run time.-- build failed at Hakcage-    - because heist does not specify version of `transformers`, which will conflick with `transformer` of `mtl`-    - Upgrade heist would be fine.+- use getDataDir to retrieve locale msg. [snaplet tutorial](http://snapframework.com/docs/tutorials/snaplets-tutorial)+  
+ example/snap.hs view
@@ -0,0 +1,120 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE CPP               #-}+{-# LANGUAGE TemplateHaskell   #-}+{-# LANGUAGE RankNTypes        #-}+++module Main where++------------------------------------------------------------------------------+import           Control.Category+import           Control.Monad+import           Control.Exception (SomeException, try)+import           Data.ByteString (ByteString)+import           Data.Maybe+import           Prelude hiding ((.))+import           Snap+import qualified Data.Configurator as CF+import qualified Data.Configurator.Types as CF+import           Snap.Core+import           Snap.Http.Server+import           Snap.Snaplet.Heist+import           Snap.Snaplet.Config+import           Snap.Util.FileServe+import           System.IO+import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Text as T+import qualified Data.Text.Encoding as T+import qualified Text.XmlHtml as X+import           Text.Templating.Heist+import           Text.XmlHtml hiding (render)++#ifdef DEVELOPMENT+import           Snap.Loader.Dynamic+#else+import           Snap.Loader.Static+#endif++import Snap.Snaplet.I18N++------------------------------------------------------------------------------++data App = App+    { _heist   :: Snaplet (Heist App)+    , _i18n   :: Snaplet I18NSnaplet+    }++makeLens ''App++instance HasHeist App where+    heistLens = subSnaplet heist++instance HasI18N App where+   i18nLens = i18n++type AppHandler = Handler App App++------------------------------------------------------------------------------++decodedParam :: MonadSnap m => ByteString -> m ByteString+decodedParam p = fromMaybe "" <$> getParam p++------------------------------------------------------------------------------+++testSplice :: Splice AppHandler+testSplice = do+    locale <- liftIO $ getDefaultLocale+    --liftIO $ print locale+    textSplice $ T.pack "test"++index :: AppHandler ()+index = do+     heistLocal (bindSplice "testSplice" testSplice) $ render "index"++-- | wrap to element span+--       ++------------------------------------------------------------------------------++-- | The application's routes.+routes :: [(ByteString, Handler App App ())]+routes  = [ ("/", index)+          , ("", with heist heistServe)+          ]++-- | The application initializer.+app :: SnapletInit App App+app = makeSnaplet "app" "An snaplet example application." Nothing $ do+    h <- nestSnaplet "heist" heist $ heistInit "templates"+    locale <- liftIO $ getDefaultLocale+    i <- nestSnaplet "i18n" i18n $ initI18NSnaplet (Just "zh_CN")+    addRoutes routes+    return $ App h i++getDefaultLocale :: IO (Maybe String)+--getDefaultLocale = return $ Just "zh_CN"+getDefaultLocale = liftM getLocale getConf++------------------------------------------------------------------------------++main :: IO ()+main = do+    (conf, site, cleanup) <- $(loadSnapTH [| getConf |]+                                          'getActions+                                          ["heist/templates"])++    _ <- try $ httpServe conf $ site :: IO (Either SomeException ())+    cleanup++getConf :: IO (Config Snap AppConfig)+getConf = commandLineAppConfig defaultConfig++getActions :: Config Snap AppConfig -> IO (Snap (), IO ())+getActions conf = do+    (msgs, site, cleanup) <- runSnaplet+        (appEnvironment =<< getOther conf) app+    hPutStrLn stderr $ T.unpack msgs+    return (site, cleanup)+
+ example/snaplets/heist/templates/index.tpl view
@@ -0,0 +1,30 @@+<!DOCTYPE html>    +<html>+<head>++<style>+  p > span { display: block; }+</style>++</head>++<body>++<h2>Test I18N</h2>++<testSplice />++<p>+<i18n name="shanghai"></i18n>+<i18nSpan name="hello" class="test"></i18nSpan>+<i18n name="invalidekey"></i18n>+</p>+ + <p>+    <i18n name="shanghai">+        <input type="submit" value="${i18nValue}" />+    </i18n>+</p>++</body>+</html>
+ example/snaplets/i18n/message-en_US.cfg view
@@ -0,0 +1,2 @@+hello = "Hello"+shanghai = "ShangHai"
+ example/snaplets/i18n/message-zh_CN.cfg view
@@ -0,0 +1,2 @@+hello = "你好"+shanghai = "上海"
snaplet-i18n.cabal view
@@ -3,7 +3,7 @@ -- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr. -- The name of the package. Name:                snaplet-i18n-Version:             0.0.3+Version:             0.0.3.1 Description:         A light weight i18n snaplet. Synopsis:            snaplet-i18n Homepage:            https://github.com/HaskellCNOrg/snaplet-i18n@@ -19,13 +19,13 @@  Extra-source-files:     README.md-  test/snap.hs-  test/snaplets/heist/templates/*.tpl-  test/data/*.cfg+  example/snap.hs+  example/snaplets/heist/templates/*.tpl+  example/snaplets/i18n/*.cfg  Source-Repository head   Type:     git-  Location: https://github.com/HaskellCNOrg/snaplet-i18n+  Location: git://github.com/HaskellCNOrg/snaplet-i18n.git   Library@@ -33,12 +33,16 @@   Exposed-modules:            Snap.Snaplet.I18N +  Other-modules: Paths_snaplet_i18n+   Build-Depends:           base               >= 4     && < 5,-      snap               >= 0.9.1 && < 0.10,-      snap-core          == 0.9.0,-      xmlhtml            == 0.2.0,-      heist              == 0.8.1,+      snap               == 0.9.*,+      snap-core          == 0.9.*,+      snap-loader-dynamic == 0.9.*,+      snap-loader-static == 0.9.*,+      xmlhtml            >= 0.2,+      heist              >= 0.8.1 && < 0.9,       bytestring         >= 0.9   && < 1.0,       data-lens-template >= 2.1   && < 2.2,       data-lens          >= 2.0   && < 2.1,
src/Snap/Snaplet/I18N.hs view
@@ -1,8 +1,7 @@-{-# LANGUAGE OverloadedStrings, FlexibleInstances, MultiParamTypeClasses #-}-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE OverloadedStrings #-}  -module Snap.Snaplet.I18N +module Snap.Snaplet.I18N   ( I18NSnaplet   , HasI18N (..)   , I18NMessage (..)@@ -12,22 +11,24 @@   ) where  import           Control.Monad+import qualified Data.Configurator       as Config+import qualified Data.Configurator.Types as Config import           Data.Lens.Common import           Data.Maybe-import           System.Directory+import qualified Data.Text               as T import           System.FilePath.Posix import           Text.Templating.Heist-import           Text.XmlHtml hiding (render)-import qualified Data.Configurator as Config-import qualified Data.Configurator.Types as Config-import qualified Data.Text as T-import qualified Text.XmlHtml as X+import           Text.XmlHtml            hiding (render)+import qualified Text.XmlHtml            as X -import Snap-import Snap.Snaplet.Heist+import Paths_snaplet_i18n+import           Snap+import           Snap.Snaplet.Heist  --------------------------------------------------------+----------------------------------------------------------------------+--    Types+----------------------------------------------------------------------  type Locale      = String type MessageFile = String@@ -36,87 +37,95 @@ defaultLocale = "en_US"  -- | ?? could be multiple message files--- +-- defaultMessageFilePrefix :: MessageFile-defaultMessageFilePrefix = "data/message"+defaultMessageFilePrefix = "message" -data I18NConfig  = I18NConfig { _getLocale      :: Locale       -- ^ locale, default "en"-                              , _getMessageFile :: MessageFile  -- ^ message file name, default to "message"+data I18NConfig  = I18NConfig { _getLocale      :: Locale+                                -- ^ locale, default "en"+                              , _getMessageFile :: MessageFile+                                -- ^ message file name, default to "message"                               } deriving (Show) --- | A simple mapping to hold i18n messages--- -data I18NMessage = I18NMessage Config.Config+-- | Message content.+--+newtype I18NMessage = I18NMessage Config.Config  -- | data type--- +-- data I18NSnaplet = I18NSnaplet                     { _getI18NConfig  :: I18NConfig                     , _getI18NMessage :: I18NMessage-                    } +                    }   -- | Compose App with a I18N Snaplet.--- +-- class HasI18N b where   i18nLens :: Lens b (Snaplet I18NSnaplet)-  + -- | Util functions--- +-- getI18NSnaplet :: HasI18N b => Handler b b I18NSnaplet getI18NSnaplet = with i18nLens Snap.get  -- | Get the @I18NMessage@--- +-- getI18NMessages :: HasI18N b => Handler b b I18NMessage getI18NMessages = liftM _getI18NMessage getI18NSnaplet  -- | Look up a value in, usuallly Handler Monad--- +-- lookupI18NValue :: HasI18N b => T.Text -> Handler b b T.Text lookupI18NValue key = do                       (I18NMessage msg) <- getI18NMessages                       liftIO $ Config.lookupDefault "Error: no value found." msg key --------------------------------------------------------+----------------------------------------------------------------------+--    Init Snaplet+----------------------------------------------------------------------  -- | Init this I18NSnaplet snaplet.--- +-- initI18NSnaplet :: (HasHeist b, HasI18N b)                 => Maybe Locale              -- ^ Locale, default to @defaultLocale@                 -> SnapletInit b I18NSnaplet-initI18NSnaplet l = makeSnaplet "I18NSnaplet" "" Nothing $ do+initI18NSnaplet l = makeSnaplet "i18n" description datadir $ do     let i18nConfig = I18NConfig (fromMaybe defaultLocale l) defaultMessageFilePrefix-    config <- liftIO $ readMessageFile i18nConfig+    fp <- getSnapletFilePath+    msg <- liftIO $ readMessageFile fp i18nConfig     addDefaultSplices-    return $ I18NSnaplet i18nConfig $ I18NMessage config+    return $ I18NSnaplet i18nConfig msg   where addDefaultSplices = addSplices [ ("i18n", liftHeist i18nSplice)                                        , ("i18nSpan", liftHeist i18nSpanSplice)]+        -- config dir for snaplet+        datadir = Just $ liftM (++ "/resources") getDataDir+        description = "light weight i18n snaplet"  ---------------------------------------------------------- +--  -- | Load file --   server will not be able to start up if dir doesnt exists. --   Thus, no additional validation check so far.--- -readMessageFile :: I18NConfig -> IO Config.Config-readMessageFile config = do-    base     <- getCurrentDirectory+--+readMessageFile :: FilePath -> I18NConfig -> IO I18NMessage+readMessageFile base config = do     let fullname = base </> file config-    Config.load [Config.Required fullname]-  where +    fmap I18NMessage (Config.load [Config.Required fullname])+  where     -- file fullname will be like message-en_US.cfg-    -- FIXME: Maybe replace "-" with "_" in locale for typo+    -- FIXME: Maybe replace "-" with "_" in locale in case typo     file c = _getMessageFile c ++ "-" ++ _getLocale c ++ ".cfg"  ---------------------------------------------------------+----------------------------------------------------------------------+--    Splices+----------------------------------------------------------------------  -- | element attribute used for looking up i18n value. --   e.g. <i18n name="hello" />--- +-- i18nSpliceAttr :: T.Text i18nSpliceAttr = "name" @@ -124,8 +133,11 @@ -- | Splices just wrap value fonud at l10n message. --   When it is used for wrap around other elements, a.k.a children is not empty, --   binding `i18nValue`.+--   e.g.+--       <i18n name="hello" />+--       <i18n name="hello"><p><i18nValue/></p></i18n> ----- FIXME: Turns out that it is not possible to fail at compilation if value is Nothing but runtime. +-- FIXME: Turns out that it is not possible to fail at compilation if value is Nothing but runtime. i18nSplice :: HasI18N b => Splice (Handler b b) i18nSplice = do     input <- getParamNode@@ -135,7 +147,7 @@       _  -> runChildrenWithText [("i18nValue", value)]  -- | Splices. use 'span' html element wrap result.--- +-- i18nSpanSplice :: HasI18N b => Splice (Handler b b) i18nSpanSplice = do     input <- getParamNode@@ -143,8 +155,10 @@     return [X.Element "span" (elementAttrs input) [X.TextNode value]]  -- | Look up 'name' attribute value.--- +-- getNameAttr :: Node -> T.Text getNameAttr n = case getAttribute i18nSpliceAttr n of                   Just x -> x-                  _      -> "" +                  _      -> ""++----------------------------------------------------------------------
− test/data/message-en_US.cfg
@@ -1,2 +0,0 @@-hello = "Hello"-shanghai = "ShangHai"
− test/data/message-zh_CN.cfg
@@ -1,2 +0,0 @@-hello = "你好"-shanghai = "上海"
− test/snap.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE CPP               #-}-{-# LANGUAGE TemplateHaskell   #-}-{-# LANGUAGE RankNTypes        #-}---module Main where---------------------------------------------------------------------------------import           Control.Category-import           Control.Monad-import           Control.Exception (SomeException, try)-import           Data.ByteString (ByteString)-import           Data.Maybe-import           Network.HTTP.Conduit (responseBody)-import           Network.HTTP.Types (renderSimpleQuery)-import           Prelude hiding ((.))-import           Snap-import qualified Data.Configurator as CF-import qualified Data.Configurator.Types as CF-import           Snap.Core-import           Snap.Http.Server-import           Snap.Snaplet.Heist-import           Snap.Snaplet.OAuth-import           Snap.Util.FileServe-import           System.IO-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Text.XmlHtml as X-import           Text.Templating.Heist-import           Text.XmlHtml hiding (render)--#ifdef DEVELOPMENT-import           Snap.Loader.Devel-#else-import           Snap.Loader.Prod-#endif--import Snap.Snaplet.I18N----------------------------------------------------------------------------------data App = App-    { _heist   :: Snaplet (Heist App)-    , _i18n   :: Snaplet I18NSnaplet-    }--makeLens ''App--instance HasHeist App where-    heistLens = subSnaplet heist--instance HasI18N App where-   i18nLens = i18n--type AppHandler = Handler App App----------------------------------------------------------------------------------decodedParam :: MonadSnap m => ByteString -> m ByteString-decodedParam p = fromMaybe "" <$> getParam p-----------------------------------------------------------------------------------testSplice :: Splice AppHandler-testSplice = do-    locale <- liftIO $ getDefaultLocale-    liftIO $ print locale-    textSplice $ T.pack "test"--index :: AppHandler ()-index = do-     heistLocal (bindSplice "testSplice" testSplice) $ render "index"---- | wrap to element span---       ------------------------------------------------------------------------------------ | The application's routes.-routes :: [(ByteString, Handler App App ())]-routes  = [ ("/", index)-          , ("", with heist heistServe)-          ]---- | The application initializer.-app :: SnapletInit App App-app = makeSnaplet "app" "An snaplet example application." Nothing $ do-    h <- nestSnaplet "heist" heist $ heistInit "templates"-    locale <- liftIO $ getDefaultLocale-    i <- nestSnaplet "i18n" i18n $ initI18NSnaplet (Just "zh_CN")-    addRoutes routes-    return $ App h i--getDefaultLocale :: IO (Maybe String)---getDefaultLocale = return $ Just "zh_CN"-getDefaultLocale = liftM getLocale getConf----------------------------------------------------------------------------------main :: IO ()-main = do-    (conf, site, cleanup) <- $(loadSnapTH [| getConf |]-                                          'getActions-                                          ["heist/templates"])--    _ <- try $ httpServe conf $ site :: IO (Either SomeException ())-    cleanup--getConf :: IO (Config Snap ())-getConf = commandLineConfig defaultConfig--getActions :: Config Snap () -> IO (Snap (), IO ())-getActions _ = do-    (msgs, site, cleanup) <- runSnaplet app-    hPutStrLn stderr $ T.unpack msgs-    return (site, cleanup)-
− test/snaplets/heist/templates/index.tpl
@@ -1,30 +0,0 @@-<!DOCTYPE html>    -<html>-<head>--<style>-  p > span { display: block; }-</style>--</head>--<body>--<h2>Test I18N</h2>--<testSplice />--<p>-<i18n name="shanghai"></i18n>-<i18nSpan name="hello" class="test"></i18nSpan>-<i18n name="invalidekey"></i18n>-</p>- - <p>-    <i18n name="shanghai">-        <input type="submit" value="${i18nValue}" />-    </i18n>-</p>--</body>-</html>