Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit 5df99abce73c30c4b85d97b07a6810b203b547b8 @ozataman committed Dec 23, 2010
Showing with 255 additions and 0 deletions.
  1. +6 −0 .ghci
  2. +3 −0 .gitignore
  3. +30 −0 LICENSE
  4. +9 −0 README.md
  5. +2 −0 Setup.hs
  6. +40 −0 si18n.cabal
  7. +44 −0 src/Text/Si18n.hs
  8. +38 −0 src/Text/Si18n/ByteString.hs
  9. +42 −0 src/Text/Si18n/Loader.hs
  10. +27 −0 src/Text/Si18n/Types.hs
  11. +4 −0 test/en.csv
  12. +10 −0 test/test.hs
6 .ghci
@@ -0,0 +1,6 @@
+:set -XOverloadedStrings
+:set -XTemplateHaskell
+:set -Wall
+:set -isrc
+:set -itest
+
3 .gitignore
@@ -0,0 +1,3 @@
+dist
+*.hi
+*.o
30 LICENSE
@@ -0,0 +1,30 @@
+Copyright (c)2010, Ozgun Ataman
+
+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.
+
+ * Neither the name of Ozgun Ataman nor the names of other
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"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
+OWNER OR CONTRIBUTORS 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.
9 README.md
@@ -0,0 +1,9 @@
+# Text.Si18n I18n Library
+
+This library is meant to be a simple, effective, fast and reliable translation
+/ localization library for Haskell. The common use case would be web
+applications.
+
+The philosophy is to only use Haskell instead of an FFI into another i18n lib.
+
+Any feedback is welcome.
2 Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
40 si18n.cabal
@@ -0,0 +1,40 @@
+Name: si18n
+Version: 0.0.2
+Synopsis: A simple i18n library for Haskell
+License: BSD3
+License-file: LICENSE
+Author: Ozgun Ataman
+Maintainer: ozataman@gmail.com
+Category: Data
+Build-type: Simple
+Cabal-version: >=1.2
+
+
+Library
+ Exposed-modules:
+ Text.Si18n
+ , Text.Si18n.ByteString
+ , Text.Si18n.Loader
+ , Text.Si18n.Types
+
+ -- Packages needed in order to build this package.
+ Build-depends:
+ base >= 4 && < 5
+ , bytestring >= 0.9
+ , containers
+ , csv-iteratee >= 0.6
+ , safe
+ , text >= 0.11
+
+ Extensions:
+ OverloadedStrings
+
+ hs-source-dirs: src, test
+
+ -- Modules not exported by this package.
+ -- Other-modules:
+
+ -- Extra tools (e.g. alex, hsc2hs, ...) needed to build the source.
+ -- Build-tools:
+
+
44 src/Text/Si18n.hs
@@ -0,0 +1,44 @@
+module Text.Si18n
+
+( module Text.Si18n.Types
+, t
+, t'
+, loadI18nFile
+) where
+
+import Data.List (foldl')
+import qualified Data.Map as Map
+import qualified Data.Text as T
+import Data.Text (Text)
+
+import Text.Si18n.Loader
+import Text.Si18n.Types
+
+
+tToken :: [(Text, Text)] -> I18nToken -> Text
+tToken m ts = T.concat $ foldl' render [] ts
+ where
+ render a (TransToken x) = val x : a
+ render a (Piece x) = x : a
+ val x = maybe (errtext x) id $ lookup x m
+ errtext x = T.concat ["[i18n token not supplied: ", x, " ]"]
+
+
+-----------------------------------------------------------------------------
+-- | Translate a given key with the given token mapping.
+t :: I18nData -- ^ Translation data
+ -> Text -- ^ Locale
+ -> Text -- ^ Key
+ -> TokenMap -- ^ Token values
+ -> Text
+t i18n l s m = maybe (errtext s) (tToken m) token
+ where
+ errtext x = T.concat ["[i18n translation missing: ", x, " ]"]
+ token = Map.lookup k i18n
+ k = T.concat [l, ".", s]
+
+
+-----------------------------------------------------------------------------
+-- | Same as 't' but you don't need to supply a token mapping.
+t' :: I18nData -> Text -> Text -> Text
+t' i18n l s = t i18n l s []
38 src/Text/Si18n/ByteString.hs
@@ -0,0 +1,38 @@
+module Text.Si18n.ByteString
+
+( module Text.Si18n.ByteString
+, t
+, t'
+, S.loadI18nFile
+) where
+
+import qualified Data.Map as Map
+import qualified Data.Text as T
+import qualified Data.ByteString.Char8 as B
+import Data.ByteString (ByteString)
+import Data.Text (Text)
+import Data.Text.Encoding
+
+import Text.Si18n.Loader
+import Text.Si18n.Types
+import qualified Text.Si18n as S
+
+
+-----------------------------------------------------------------------------
+-- | Translate a given key with the given token mapping.
+t :: I18nData -- ^ Translation data
+ -> ByteString -- ^ Locale
+ -> ByteString -- ^ Key
+ -> [(ByteString, ByteString)] -- ^ Token values
+ -> ByteString
+t i18n l s m = encodeUtf8 $ S.t i18n l' s' m'
+ where
+ (l':s':_) = map decodeUtf8 [l, s]
+ m' = map (\(k,v) -> (decodeUtf8 k, decodeUtf8 v)) m
+
+
+
+-----------------------------------------------------------------------------
+-- | Same as 't' but you don't need to supply a token mapping.
+t' :: I18nData -> ByteString -> ByteString -> ByteString
+t' i18n l s = t i18n l s []
42 src/Text/Si18n/Loader.hs
@@ -0,0 +1,42 @@
+module Text.Si18n.Loader
+(loadI18nFile) where
+
+
+import Control.Exception
+import qualified Data.ByteString.Char8 as B
+import Data.ByteString (ByteString)
+import Data.List (foldl')
+import qualified Data.Map as Map
+import Data.Map (Map)
+import qualified Data.Text as T
+import Data.Text (Text)
+import qualified Data.Text.Encoding as E
+
+import Data.CSV.Iteratee
+
+import Text.Si18n.Types
+
+
+loadI18nFile :: FilePath -> IO I18nData
+loadI18nFile f = do
+ ds <- foldCSVFile f defCSVSettings (funToIter i18nLoader) Map.empty
+ return $ either (\e -> error $ "Can't load locale file. Caught error: "
+ ++ show e) id ds
+
+
+i18nLoader :: I18nData -> ParsedRow Row -> I18nData
+i18nLoader a (ParsedRow (Just r)) = Map.insert k' v' a
+ where (k:v:l:_) = map E.decodeUtf8 r
+ k' = T.concat [l, ".", k]
+ v' = tokenize v
+i18nLoader a _ = a
+
+
+tokenize :: Text -> I18nToken
+tokenize t = foldl' collect [] rsplits
+ where
+ lsplits = T.splitOn "{{" t
+ rsplits = concat $ map (T.splitOn "}}") lsplits
+
+ collect a@((Piece _):_) x = TransToken x : a
+ collect a x = Piece x : a
27 src/Text/Si18n/Types.hs
@@ -0,0 +1,27 @@
+module Text.Si18n.Types where
+
+import Data.Map (Map)
+import Data.Text (Text)
+
+
+-----------------------------------------------------------------------------
+-- | Key-Value pairs that the user will supply for when there are token
+-- substitutions in the translation text.
+type TokenMap = [(Text,Text)]
+
+
+-----------------------------------------------------------------------------
+-- | In-memory representation of I18n translation data
+type I18nData = Map Text I18nToken
+
+
+-----------------------------------------------------------------------------
+-- | Translation data for a single key
+type I18nToken = [Segment]
+
+
+-----------------------------------------------------------------------------
+-- | Part of a single translation
+data Segment = TransToken Text
+ | Piece Text
+ deriving (Show, Eq)
4 test/en.csv
@@ -0,0 +1,4 @@
+Key,Value,Locale
+k1,This is a test key,en
+k2,This is the second test key,en
+k3,Key with {{name}} some embedded tokens {{lastname}},en
10 test/test.hs
@@ -0,0 +1,10 @@
+module Test where
+
+import Text.Si18n
+import Text.Si18n.Loader
+import Text.Si18n.Types
+
+
+
+i18n :: IO I18nData
+i18n = loadI18nFile "test/en.csv"

0 comments on commit 5df99ab

Please sign in to comment.
Something went wrong with that request. Please try again.