i18n-0.3: Text/I18n.hs
{-# LANGUAGE FlexibleInstances, TypeSynonymInstances #-}
-----------------------------------------------------------------------------
-- |
-- Module : Text.I18n
-- Copyright : (c) Eugene Grigoriev, 2008
-- License : BSD3
--
-- Maintainer : eugene.grigoriev@gmail.com
-- Stability : experimental
-- Portability : portable
--
-- Internationalization support for Haskell.
-- Use Text.I18n.Po module.
--
-- Plural forms are not yet implemented.
--
-----------------------------------------------------------------------------
module Text.I18n (
-- * Type Declarations
Msgid(..), Msgstr, Locale(..), Context, I18n, L10n,
-- * Internationalization Monad Functions
gettext, localize, withContext, withLocale
) where
import Control.Monad.Reader
import Control.Monad.Trans
import Control.Monad.Identity
import Data.Maybe
import Text.I18n.Printf
import qualified Data.Map as Map
-------------------------------------------------------------------------------
-- Type declarations
-------------------------------------------------------------------------------
newtype Msgid = Msgid String deriving (Show,Eq,Ord)
type Msgstr = String
newtype Locale = Locale String deriving (Show,Eq,Ord)
type Context = String
-- | The Internationalization monad built using monad transformers.
type I18n a = ReaderT (Locale, L10n, Maybe Context) Identity a
-- | The Localization structure.
type L10n = Map.Map Locale
(Map.Map (Maybe Context)
(Map.Map Msgid
[Msgstr]))
instance PrintfType (I18n String) where
spr fmts args = return (uprintf fmts (reverse args))
-------------------------------------------------------------------------------
-- I18N Monad functions
-------------------------------------------------------------------------------
{-|
The top level localization function.
> import Text.I18n.Po
> import Prelude hiding (putStr,putStrLn)
>
> main = do
> (l10n,errors) <- getL10n "dir/to/po" -- directory containing PO files
> putStrLn $ localize l10n (Locale "en") (example "Joe")
-}
localize :: L10n -- ^ Structure containing localization data
-> Locale -- ^ Locale to use
-> I18n a -- ^ Inernationalized expression
-> a -- ^ Localized expression
localize l10n loc expression = runIdentity $ runReaderT expression (loc,l10n,Nothing)
{-|
The heart of I18n monad. Based on 'Text.Printf.printf'.
> example :: String -> I18n String
> example name = do
> hello <- gettext "Hello, %s!"
> return (hello name)
-}
gettext :: PrintfType a => String -> I18n a
gettext msgid = do
(loc, l10n, ctxt) <- ask
case localizeMsgid l10n loc ctxt (Msgid msgid) of
Just msgstr -> return (printf msgstr)
Nothing -> case ctxt of
Just _ -> withContext Nothing (gettext msgid)
Nothing -> return (printf msgid)
{-|
Sets a local 'Context' for an internationalized expression.
If there is no translation, then no context version is tried.
> example2 :: String -> I18n String
> example2 = withContext (Just "test") . example
-}
withContext :: Maybe Context -- ^ Context to use
-> I18n a -- ^ Internationalized expression
-> I18n a -- ^ New internationalized expression
withContext ctxt expression = do
(lang, l10n, _) <- ask
local (const (lang, l10n, ctxt))
expression
{-|
Sets a local 'Locale' for an internationalized expression.
> example3 :: String -> I18n String
> example3 = withLocale (Locale "ru") . example2
-}
withLocale :: Locale -- ^ Locale to use
-> I18n a -- ^ Internationalized expression
-> I18n a -- ^ New internationalized expression.
-- Note: while this expression is localy localized already, it is to be a
-- part of another internationalized expression.
-- Therefore the final type is internationalized.
withLocale loc expression = do
(_, l10n, ctxt) <- ask
local (const (loc, l10n, ctxt))
expression
localizeMsgid :: L10n -> Locale -> Maybe Context -> Msgid -> Maybe String
localizeMsgid l10n loc ctxt msgid = do
local <- Map.lookup loc l10n
contextual <- Map.lookup ctxt local
msgstrs <- Map.lookup msgid contextual
listToMaybe msgstrs