packages feed

shakespeare-i18n (empty) → 0.0.0

raw patch · 4 files changed

+325/−0 lines, 4 filesdep +basedep +bytestringdep +directorysetup-changed

Dependencies added: base, bytestring, directory, parsec, shakespeare, template-haskell, text

Files

+ LICENSE view
@@ -0,0 +1,25 @@+The following license covers this documentation, and the source code, except+where otherwise indicated.++Copyright 2009, Michael Snoyman. All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++* Redistributions of source code must retain the above copyright notice, this+  list of conditions and the following disclaimer.++* Redistributions in binary form must reproduce the above copyright notice,+  this list of conditions and the following disclaimer in the documentation+  and/or other materials provided with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,8 @@+#!/usr/bin/env runhaskell++> module Main where+> import Distribution.Simple+> import System.Cmd (system)++> main :: IO ()+> main = defaultMain
+ Text/Shakespeare/I18N.hs view
@@ -0,0 +1,259 @@+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ExistentialQuantification #-}+module Text.Shakespeare.I18N+    ( mkMessage+    , RenderMessage (..)+    , ToMessage (..)+    , SomeMessage (..)+    ) where++import Language.Haskell.TH.Syntax+import Data.Text (Text, pack, unpack)+import System.Directory+import Data.Maybe (catMaybes)+import Data.List (isSuffixOf, sortBy, foldl')+import qualified Data.ByteString as S+import Data.Text.Encoding (decodeUtf8)+import Data.Char (isSpace, toLower, toUpper)+import Data.Ord (comparing)+import Text.Shakespeare.Base (Deref (..), Ident (..), parseHash, derefToExp)+import Text.ParserCombinators.Parsec (parse, many, eof, many1, noneOf, (<|>))+import Control.Arrow ((***))+import Data.Monoid (mempty, mappend)+import qualified Data.Text as T+import Data.String (IsString (fromString))++class ToMessage a where+    toMessage :: a -> Text+instance ToMessage Text where+    toMessage = id+instance ToMessage String where+    toMessage = Data.Text.pack++class RenderMessage master message where+    renderMessage :: master+                  -> [Text] -- ^ languages+                  -> message+                  -> Text++instance RenderMessage master Text where+    renderMessage _ _ = id++type Lang = Text++mkMessage :: String+          -> FilePath+          -> Lang+          -> Q [Dec]+mkMessage dt folder lang = do+    files <- qRunIO $ getDirectoryContents folder+    contents <- qRunIO $ fmap catMaybes $ mapM (loadLang folder) files+    sdef <-+        case lookup lang contents of+            Nothing -> error $ "Did not find main language file: " ++ unpack lang+            Just def -> toSDefs def+    mapM_ (checkDef sdef) $ map snd contents+    let dt' = ConT $ mkName dt+    let mname = mkName $ dt ++ "Message"+    c1 <- fmap concat $ mapM (toClauses dt) contents+    c2 <- mapM (sToClause dt) sdef+    c3 <- defClause+    return+        [ DataD [] mname [] (map (toCon dt) sdef) []+        , InstanceD+            []+            (ConT ''RenderMessage `AppT` dt' `AppT` ConT mname)+            [ FunD (mkName "renderMessage") $ c1 ++ c2 ++ [c3]+            ]+        ]++toClauses :: String -> (Lang, [Def]) -> Q [Clause]+toClauses dt (lang, defs) =+    mapM go defs+  where+    go def = do+        a <- newName "lang"+        (pat, bod) <- mkBody dt (constr def) (map fst $ vars def) (content def)+        guard <- fmap NormalG [|$(return $ VarE a) == pack $(lift $ unpack lang)|]+        return $ Clause+            [WildP, ConP (mkName ":") [VarP a, WildP], pat]+            (GuardedB [(guard, bod)])+            []++mkBody :: String -- ^ datatype+       -> String -- ^ constructor+       -> [String] -- ^ variable names+       -> [Content]+       -> Q (Pat, Exp)+mkBody dt cs vs ct = do+    vp <- mapM go vs+    let pat = RecP (mkName $ "Msg" ++ cs) (map (varName dt *** VarP) vp)+    let ct' = map (fixVars vp) ct+    pack' <- [|Data.Text.pack|]+    tomsg <- [|toMessage|]+    let ct'' = map (toH pack' tomsg) ct'+    mapp <- [|mappend|]+    let app a b = InfixE (Just a) mapp (Just b)+    e <-+        case ct'' of+            [] -> [|mempty|]+            [x] -> return x+            (x:xs) -> return $ foldl' app x xs+    return (pat, e)+  where+    toH pack' _ (Raw s) = pack' `AppE` SigE (LitE (StringL s)) (ConT ''String)+    toH _ tomsg (Var d) = tomsg `AppE` derefToExp [] d+    go x = do+        let y = mkName $ '_' : x+        return (x, y)+    fixVars vp (Var d) = Var $ fixDeref vp d+    fixVars _ (Raw s) = Raw s+    fixDeref vp (DerefIdent (Ident i)) = DerefIdent $ Ident $ fixIdent vp i+    fixDeref vp (DerefBranch a b) = DerefBranch (fixDeref vp a) (fixDeref vp b)+    fixDeref _ d = d+    fixIdent vp i =+        case lookup i vp of+            Nothing -> i+            Just y -> nameBase y++sToClause :: String -> SDef -> Q Clause+sToClause dt sdef = do+    (pat, bod) <- mkBody dt (sconstr sdef) (map fst $ svars sdef) (scontent sdef)+    return $ Clause+        [WildP, ConP (mkName "[]") [], pat]+        (NormalB bod)+        []++defClause :: Q Clause+defClause = do+    a <- newName "sub"+    c <- newName "langs"+    d <- newName "msg"+    rm <- [|renderMessage|]+    return $ Clause+        [VarP a, ConP (mkName ":") [WildP, VarP c], VarP d]+        (NormalB $ rm `AppE` VarE a `AppE` VarE c `AppE` VarE d)+        []++toCon :: String -> SDef -> Con+toCon dt (SDef c vs _) =+    RecC (mkName $ "Msg" ++ c) $ map go vs+  where+    go (n, t) = (varName dt n, NotStrict, ConT $ mkName t)++varName :: String -> String -> Name+varName a y =+    mkName $ concat [lower a, "Message", upper y]+  where+    lower (x:xs) = toLower x : xs+    lower [] = []+    upper (x:xs) = toUpper x : xs+    upper [] = []++checkDef :: [SDef] -> [Def] -> Q ()+checkDef x y =+    go (sortBy (comparing sconstr) x) (sortBy (comparing constr) y)+  where+    go _ [] = return ()+    go [] (b:_) = error $ "Extra message constructor: " ++ constr b+    go (a:as) (b:bs)+        | sconstr a < constr b = go as (b:bs)+        | sconstr a > constr b = error $ "Extra message constructor: " ++ constr b+        | otherwise = do+            go' (svars a) (vars b)+            go as bs+    go' ((an, at):as) ((bn, mbt):bs)+        | an /= bn = error "Mismatched variable names"+        | otherwise =+            case mbt of+                Nothing -> go' as bs+                Just bt+                    | at == bt -> go' as bs+                    | otherwise -> error "Mismatched variable types"+    go' [] [] = return ()+    go' _ _ = error "Mistmached variable count"++toSDefs :: [Def] -> Q [SDef]+toSDefs = mapM toSDef++toSDef :: Def -> Q SDef+toSDef d = do+    vars' <- mapM go $ vars d+    return $ SDef (constr d) vars' (content d)+  where+    go (a, Just b) = return (a, b)+    go (a, Nothing) = error $ "Main language missing type for " ++ show (constr d, a)++data SDef = SDef+    { sconstr :: String+    , svars :: [(String, String)]+    , scontent :: [Content]+    }++data Def = Def+    { constr :: String+    , vars :: [(String, Maybe String)]+    , content :: [Content]+    }++loadLang :: FilePath -> FilePath -> IO (Maybe (Lang, [Def]))+loadLang folder file = do+    let file' = folder ++ '/' : file+    e <- doesFileExist file'+    if e && ".msg" `isSuffixOf` file+        then do+            let lang = pack $ reverse $ drop 4 $ reverse file+            bs <- S.readFile file'+            let s = unpack $ decodeUtf8 bs+            defs <- fmap catMaybes $ mapM parseDef $ lines s+            return $ Just (lang, defs)+        else return Nothing++parseDef :: String -> IO (Maybe Def)+parseDef "" = return Nothing+parseDef ('#':_) = return Nothing+parseDef s =+    case end of+        ':':end' -> do+            content' <- fmap compress $ parseContent $ dropWhile isSpace end'+            case words begin of+                [] -> error $ "Missing constructor: " ++ s+                (w:ws) -> return $ Just Def+                            { constr = w+                            , vars = map parseVar ws+                            , content = content'+                            }+        _ -> error $ "Missing colon: " ++ s+  where+    (begin, end) = break (== ':') s++data Content = Var Deref | Raw String++compress :: [Content] -> [Content]+compress [] = []+compress (Raw a:Raw b:rest) = compress $ Raw (a ++ b) : rest+compress (x:y) = x : compress y++parseContent :: String -> IO [Content]+parseContent s =+    either (error . show) return $ parse go s s+  where+    go = do+        x <- many go'+        eof+        return x+    go' = (Raw `fmap` many1 (noneOf "#")) <|> (fmap (either Raw Var) parseHash)++parseVar :: String -> (String, Maybe String)+parseVar s =+    case break (== '@') s of+        (x, '@':y) -> (x, Just y)+        _ -> (s, Nothing)++data SomeMessage master = forall msg. RenderMessage master msg => SomeMessage msg++instance IsString (SomeMessage master) where+    fromString = SomeMessage . T.pack
+ shakespeare-i18n.cabal view
@@ -0,0 +1,33 @@+name:            shakespeare-i18n+version:         0.0.0+license:         BSD3+license-file:    LICENSE+author:          Michael Snoyman <michael@snoyman.com>+maintainer:      Michael Snoyman <michael@snoyman.com>+synopsis:        A type-based approach to internationalization.+description:+    This package uses the same approach of type-safe URLs to create translated content. It has a simple syntax for translators, while allowing the ful power of Haskell for applying complex grammar rules.+    .+    This package was spun off from yesod-core, and therefore the Yesod documentation is a good place to start in understanding this package. Please see <http://www.yesodweb.com/book/i18n> for more information.++category:        Web, Yesod+stability:       Stable+cabal-version:   >= 1.8+build-type:      Simple+homepage:        http://www.yesodweb.com/book/i18n++library+    build-depends:   base             >= 4       && < 5+                   , shakespeare      >= 0.10    && < 0.11+                   , template-haskell+                   , text             >= 0.7     && < 0.12+                   , parsec+                   , bytestring+                   , directory++    exposed-modules: Text.Shakespeare.I18N+    ghc-options:     -Wall++source-repository head+  type:     git+  location: git://github.com/yesodweb/hamlet.git