Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
*.swp
examples/gmotest
examples/gmodump
*.mo
log
hgettext
messages.po
Expand Down
17 changes: 0 additions & 17 deletions Text/Localize/Locale.hs

This file was deleted.

1 change: 0 additions & 1 deletion examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,3 @@ clean:
find . -name \*.hi -delete
find . -name \*.mo -delete
rm -f gmotest gmodump

3 changes: 1 addition & 2 deletions examples/gmotest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import Data.Text.Format.Heavy (Single (..))
import Text.Localize

data LocState = LocState {
lsTranslations :: Translations,
lsTranslations :: Translations,
lsLanguage :: LanguageId }
deriving (Show)

Expand Down Expand Up @@ -49,4 +49,3 @@ main = runLoc $ do
hello name
setLang "fr"
hello name

3 changes: 1 addition & 2 deletions examples/iotest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ main = do
TLIO.putStr =<< __ "Your name: "
hFlush stdout
name <- liftIO $ TLIO.getLine

withLanguage "en" $ hello name
withLanguage "ru" $ hello name
withLanguage "fr" $ hello name

66 changes: 40 additions & 26 deletions localize.cabal
Original file line number Diff line number Diff line change
@@ -1,44 +1,58 @@
cabal-version: 2.2
name: localize
version: 0.2.0.1
synopsis: GNU Gettext-based messages localization library
description: More or less fully functional translation framework,
based on @haskell-gettext@ and @text-format-heavy@
packages.
license: BSD3
description: Translation framework based on @haskell-gettext@ and
@text-format-heavy@ packages.
license: BSD-3-Clause
license-file: LICENSE
author: IlyaPortnov
maintainer: portnov84@rambler.ru
-- copyright:
copyright: © 2013–2022
category: Text
build-type: Simple
extra-doc-files: README.md
cabal-version: 1.18
extra-source-files: test/mock-program/test.hs,
test/mock-program/en_GB.mo

common shared
build-depends: base == 4.*,
ghc-options: -Wall
default-language: Haskell2010

library
import: shared
exposed-modules: Text.Localize,
Text.Localize.Types,
Text.Localize.IO
other-modules: Text.Localize.Types,
Text.Localize.Load,
Text.Localize.State,
Text.Localize.IO,
Text.Localize.Locale
build-depends: base > 4 && < 5,
mtl >= 2.2.1,
binary >=0.7,
Text.Localize.State
build-depends: binary >=0.7 && < 0.9,
containers >=0.5 && < 0.9,
bytestring >= 0.10 && < 0.13,
text >= 1.2,
data-default >= 0.7,
containers >=0.5,
data-default >= 0.7 && < 0.9,
directory >= 1.3 && < 1.4,
filepath >= 1.4 && < 1.6,
Glob >= 0.7.14 && < 0.11,
haskell-gettext >= 0.1.1.0 && < 0.2,
mtl >= 2.2.1 && < 2.4,
text >= 1.2 && < 2.2,
text-format-heavy >= 0.1.4.0 && < 1.6,
haskell-gettext >= 0.1.1.0,
time >=1.4,
transformers >=0.3,
filepath >= 1.4,
directory >= 1.3,
Glob >= 0.7.14,
setlocale >= 1.0
default-language: Haskell2010
time >=1.4 && < 1.15,
transformers >=0.3 && < 0.7,
setlocale >= 1.0 && < 1.1
hs-source-dirs: src

test-suite test
import: shared
main-is: Test.hs
other-modules: Unit.LocalizeSpec
build-depends: localize,
hspec >= 2.11 && < 2.12
build-tool-depends: hspec-discover:hspec-discover
hs-source-dirs: test
type: exitcode-stdio-1.0

Source-repository head
source-repository head
type: git
location: https://github.com/portnov/localize.git

77 changes: 48 additions & 29 deletions Text/Localize.hs → src/Text/Localize.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,54 @@ module Text.Localize
-- * Most used functions
__, __n, __f,
-- * Basic functions
translate, translateN, translateNFormat,
translate, translateN, translateNFormat,
lookup, withTranslation,

-- * Reexports
module Text.Localize.Types,
module Text.Localize.Load,
module Text.Localize.Locale

-- ** Locale
languageFromLocale,

-- ** Load
Facet,
LocatePolicy(..),
loadTranslations,
locateTranslations,
linuxLocation,
localLocation,

-- ** Types
LanguageId,
Context,
TranslationSource,
Translations(..),
availableLanguages,
Localized(..),
toText,

-- ** Monad
LocState(..),
Localize,
runLocalize,
LocalizeT,
runLocalizeT, -- TODO add execLocalizeT and execLocalize
setLanguage,
withLanguage,
setContext,
withContext

) where

import Prelude hiding (lookup)
import Control.Applicative
import Control.Monad
import qualified Data.Map as M
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.Text.Lazy as T
import qualified Data.Text.Lazy.Encoding as TLE
import qualified Data.Text.Encoding as TE
import Data.String
import Data.List hiding (lookup)
import Data.Monoid
import Data.Typeable
import qualified Data.Gettext as Gettext
import qualified Data.Text.Format.Heavy as F
import Data.Text.Format.Heavy.Parse (parseFormat)

import Text.Localize.Types
import Text.Localize.Load
import Text.Localize.Locale
import Text.Localize.State

-- $description
--
Expand All @@ -54,16 +74,16 @@ import Text.Localize.Locale
-- import qualified Data.Text as T
-- import qualified Data.Text.Lazy.IO as TLIO
-- import Text.Localize
--
--
-- newtype MyMonad a = MyMonad {unMyMonad :: ... }
-- deriving (Monad)
--
--
-- instance Localized MyMonad where
-- ...
--
-- runMyMonad :: Translations -> MyMonad a -> IO a
-- runMyMonad = ...
--
--
-- hello :: T.Text -> MyMonad ()
-- hello name = do
-- liftIO $ TLIO.putStrLn =<< __ "Your name: "
Expand Down Expand Up @@ -116,15 +136,15 @@ translate orig = do
Nothing -> return $ Gettext.gettext gmo orig
Just ctxt -> return $ Gettext.cgettext gmo ctxt orig

-- | Short alias for @translate@.
-- | Short alias for 'translate'.
__ :: (Localized m) => TranslationSource -> m T.Text
__ = translate

-- | Translate a string, taking plural forms into account.
translateN :: (Localized m)
=> TranslationSource -- ^ Single form in original language
-> TranslationSource -- ^ Plural form in original language
-> Int -- ^ Number
=> TranslationSource -- ^ Single form in original language.
-> TranslationSource -- ^ Plural form in original language.
-> Int -- ^ Number.
-> m T.Text
translateN orig plural n = do
t <- getTranslations
Expand All @@ -140,8 +160,8 @@ translateN orig plural n = do
-- | Translate a string and substitute variables into it.
-- Data.Text.Format.Heavy.format syntax is used.
translateFormat :: (Localized m, MonadFail m, F.VarContainer vars)
=> TranslationSource -- ^ Original formatting string
-> vars -- ^ Substitution variables
=> TranslationSource -- ^ Original formatting string.
-> vars -- ^ Substitution variables.
-> m T.Text
translateFormat orig vars = do
fmtStr <- translate orig
Expand All @@ -157,10 +177,10 @@ __f = translateFormat
-- and substitute variables into it.
-- Data.Text.Format.Heavy.format syntax is used.
translateNFormat :: (Localized m, MonadFail m, F.VarContainer vars)
=> TranslationSource -- ^ Single form of formatting string in original language
-> TranslationSource -- ^ Plural form of formatting string in original language
-> Int -- ^ Number
-> vars -- ^ Substitution variables
=> TranslationSource -- ^ Single form of formatting string in original language.
-> TranslationSource -- ^ Plural form of formatting string in original language.
-> Int -- ^ Number.
-> vars -- ^ Substitution variables.
-> m T.Text
translateNFormat orig plural n vars = do
fmtStr <- translateN orig plural n
Expand All @@ -171,4 +191,3 @@ translateNFormat orig plural n vars = do
-- | Short alias for @translateNFormat@.
__n :: (Localized m, MonadFail m, F.VarContainer c) => TranslationSource -> TranslationSource -> Int -> c -> m T.Text
__n = translateNFormat

23 changes: 0 additions & 23 deletions Text/Localize/IO.hs → src/Text/Localize/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,8 @@ module Text.Localize.IO
) where

import Data.IORef
import System.IO.Unsafe (unsafePerformIO)

import Text.Localize.Types
import Text.Localize.Load
import Text.Localize.Locale

currentContext :: IORef (Maybe Context)
currentContext = unsafePerformIO $ newIORef Nothing
{-# NOINLINE currentContext #-}

currentLanguage :: IORef LanguageId
currentLanguage = unsafePerformIO $ do
language <- languageFromLocale
newIORef language
{-# NOINLINE currentLanguage #-}

currentTranslations :: IORef Translations
currentTranslations = unsafePerformIO $ newIORef undefined
{-# NOINLINE currentTranslations #-}

instance Localized IO where
getLanguage = readIORef currentLanguage
getTranslations = readIORef currentTranslations
getContext = readIORef currentContext

-- | This function must be called before any translation function call,
-- otherwise you will get runtime error.
Expand Down Expand Up @@ -73,4 +51,3 @@ withContext ctxt actions = do
result <- actions
setContext oldContext
return result

17 changes: 8 additions & 9 deletions Text/Localize/Load.hs → src/Text/Localize/Load.hs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, ExistentialQuantification, DeriveDataTypeable, OverloadedStrings, RecordWildCards #-}
-- | This module contains definitions for loading translation catalogs.
module Text.Localize.Load
( -- * Data types
( -- * Data types.
LocatePolicy (..), Facet,
-- * Main functions
-- * Main functions.
loadTranslations, locateTranslations,
-- * Commonly used location policies
-- * Commonly used location policies.
linuxLocation, localLocation
) where

Expand All @@ -17,12 +17,12 @@ 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.
-- | Load compiled translations (@mo/@) when path to each translation file
-- is known.
loadTranslations :: [(LanguageId, FilePath)] -> IO Translations
loadTranslations pairs = do
res <- forM pairs $ \(lang, path) -> do
Expand Down Expand Up @@ -61,13 +61,13 @@ instance Default LocatePolicy where

-- | 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)
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
localLocation :: FilePath -- ^ Path to directory with translations.
-> LocatePolicy
localLocation base = def {lcBasePaths = [base], lcFormat = "{base}/{language}.mo"}

Expand Down Expand Up @@ -98,8 +98,7 @@ locateTranslations (LocatePolicy {..}) = liftIO $ do
let (hd, tl) = break isLanguage items
in case tl of
[] -> (hd, [])
_ -> (hd, tail tl)
(_:tl') -> (hd, tl')

isLanguage (FVariable name _) = name == "language"
isLanguage _ = False

Loading