{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, ExistentialQuantification, DeriveDataTypeable, OverloadedStrings, RecordWildCards #-} -- | This module contains definitions for loading translation catalogs. module Text.Localize.Load ( -- * Data types LocatePolicy (..), Facet, -- * Main functions loadTranslations, locateTranslations, -- * Commonly used location policies linuxLocation, localLocation ) where import Control.Monad import Control.Monad.Trans import Data.Default import qualified Data.Map as M import qualified Data.Text.Lazy as T import qualified Data.Gettext as Gettext import Data.Text.Format.Heavy import System.Directory import System.FilePath import System.FilePath.Glob import Text.Localize.Types -- | Load translations when path to each translation file is known. loadTranslations :: [(LanguageId, FilePath)] -> IO Translations loadTranslations pairs = do res <- forM pairs $ \(lang, path) -> do gmo <- Gettext.loadCatalog path return (lang, gmo) return $ Translations $ M.fromList res -- | Locale facet (@LC_MESSAGES@ and siblings). type Facet = String -- | This data type defines where to search for catalog files (@.mo@ or @.gmo@) in the file system. data LocatePolicy = LocatePolicy { lcBasePaths :: [FilePath] -- ^ Paths to directory with translations, e.g. @"\/usr\/share\/locale"@. Defaults to @"locale"@. , lcName :: String -- ^ Catalog file name (in gettext this is also known as text domain). Defaults to @"messages"@. , lcFacet :: Facet -- ^ Locale facet. Defaults to @LC_MESSAGES@. , lcFormat :: Format -- ^ File path format. The following variables can be used: -- -- * @{base}@ - path to directory with translations; -- * @{language}@ - language code; -- * @{facet}@ - locale facet; -- * @{name}@ - file name (text domain), without extension. -- -- Please note: assumption is made that the @{language}@ variable is used only once. -- -- Defaults to @"{base}\/{language}\/{facet}\/{name}.mo"@. } deriving (Show) instance Default LocatePolicy where def = LocatePolicy { lcBasePaths = ["locale"], lcName = "messages", lcFacet = "LC_MESSAGES", lcFormat = "{base}/{language}/{facet}/{name}.mo" } -- | Usual Linux translations location policy. -- Catalog files are found under @\/usr\/[local\/]share\/locale\/{language}\/LC_MESSAGES\/{name}.mo@. linuxLocation :: String -- ^ Catalog file name (text domain) -> LocatePolicy linuxLocation name = def {lcBasePaths = ["/usr/share/locale", "/usr/local/share/locale"], lcName = name} -- | Simple translations location polciy, assuming all catalog files located at -- @{base}\/{language}.mo@. localLocation :: FilePath -- ^ Path to directory with translations -> LocatePolicy localLocation base = def {lcBasePaths = [base], lcFormat = "{base}/{language}.mo"} -- | Locate and load translations according to specified policy. locateTranslations :: MonadIO m => LocatePolicy -> m Translations locateTranslations (LocatePolicy {..}) = liftIO $ do basePaths <- mapM makeAbsolute lcBasePaths pairs <- forM basePaths $ \basePath -> do let vars = M.fromList $ [("base", basePath), ("language", "*"), ("facet", lcFacet), ("name", lcName)] :: M.Map T.Text String Format fmtItems = lcFormat (fmtBase, fmtTail) = breakFormat fmtItems pathGlob = T.unpack (format lcFormat vars) pathBaseLen = fromIntegral $ T.length (format (Format fmtBase) vars) pathTailLen = fromIntegral $ T.length (format (Format fmtTail) vars) paths <- glob pathGlob forM paths $ \path -> do let pathWithoutBase = drop pathBaseLen path languageLen = length pathWithoutBase - pathTailLen language = take languageLen pathWithoutBase return (language, path) loadTranslations $ concat pairs where breakFormat items = let (hd, tl) = break isLanguage items in case tl of [] -> (hd, []) _ -> (hd, tail tl) isLanguage (FVariable name _) = name == "language" isLanguage _ = False