From 208e4e5ca58daa3aa8c69e647c5ab59ec9f3095d Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 20:43:54 +0100 Subject: [PATCH 01/14] Add `availableLanguages` --- Text/Localize/Types.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Text/Localize/Types.hs b/Text/Localize/Types.hs index e4441f9..4b2bf4f 100644 --- a/Text/Localize/Types.hs +++ b/Text/Localize/Types.hs @@ -22,6 +22,10 @@ type TranslationSource = B.ByteString data Translations = Translations { tMap :: M.Map LanguageId Gettext.Catalog } +-- | List available languages. +availableLanguages :: Translations -> [LanguageId] +availableLanguages ts = M.keys (tMap ts) + instance Show Translations where show t = "" From ed0bec2606057fdb9638383af218a4e36535b43c Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 20:49:54 +0100 Subject: [PATCH 02/14] Copy edit Fix whitespace and missing full stops. --- Text/Localize.hs | 27 +++++++++++++-------------- Text/Localize/IO.hs | 1 - Text/Localize/Load.hs | 11 +++++------ Text/Localize/State.hs | 9 ++++----- Text/Localize/Types.hs | 13 ++++++------- examples/Makefile | 1 - examples/gmotest.hs | 3 +-- examples/iotest.hs | 3 +-- localize.cabal | 1 - 9 files changed, 30 insertions(+), 39 deletions(-) diff --git a/Text/Localize.hs b/Text/Localize.hs index 6d70cd3..0d1480d 100644 --- a/Text/Localize.hs +++ b/Text/Localize.hs @@ -8,7 +8,7 @@ module Text.Localize -- * Most used functions __, __n, __f, -- * Basic functions - translate, translateN, translateNFormat, + translate, translateN, translateNFormat, lookup, withTranslation, -- * Reexports module Text.Localize.Types, @@ -54,16 +54,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: " @@ -122,9 +122,9 @@ __ = 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 @@ -140,8 +140,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 @@ -157,10 +157,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 @@ -171,4 +171,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 - diff --git a/Text/Localize/IO.hs b/Text/Localize/IO.hs index 8bff6a1..2e3fb49 100644 --- a/Text/Localize/IO.hs +++ b/Text/Localize/IO.hs @@ -73,4 +73,3 @@ withContext ctxt actions = do result <- actions setContext oldContext return result - diff --git a/Text/Localize/Load.hs b/Text/Localize/Load.hs index 33195ac..6ec7ea6 100644 --- a/Text/Localize/Load.hs +++ b/Text/Localize/Load.hs @@ -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 @@ -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"} @@ -102,4 +102,3 @@ locateTranslations (LocatePolicy {..}) = liftIO $ do isLanguage (FVariable name _) = name == "language" isLanguage _ = False - diff --git a/Text/Localize/State.hs b/Text/Localize/State.hs index 307590c..b692e8a 100644 --- a/Text/Localize/State.hs +++ b/Text/Localize/State.hs @@ -19,15 +19,15 @@ import Control.Monad.Trans import Text.Localize.Types --- | Localization state +-- | Localization state. data LocState = LocState { - lsTranslations :: Translations, + lsTranslations :: Translations, lsLanguage :: LanguageId, lsContext :: Maybe Context } deriving (Show) --- | Localization monad transformer +-- | Localization monad transformer. newtype LocalizeT m a = LocalizeT { unLocalizeT :: StateT LocState m a } @@ -42,7 +42,7 @@ instance Monad m => Localized (LocalizeT m) where runLocalizeT :: Monad m => LocalizeT m a -> LocState -> m a runLocalizeT actions st = evalStateT (unLocalizeT actions) st --- | Set current language +-- | Set current language. setLanguage :: Monad m => LanguageId -> LocalizeT m () setLanguage lang = modify $ \st -> st {lsLanguage = lang} @@ -67,4 +67,3 @@ withContext ctxt actions = do result <- actions setContext oldContext return result - diff --git a/Text/Localize/Types.hs b/Text/Localize/Types.hs index 4b2bf4f..7599615 100644 --- a/Text/Localize/Types.hs +++ b/Text/Localize/Types.hs @@ -9,16 +9,16 @@ import qualified Data.Text.Lazy as T import qualified Data.Text.Encoding as TE import qualified Data.Gettext as Gettext --- | Language identifier +-- | Language identifier. type LanguageId = String --- | Context name +-- | Context name. type Context = B.ByteString --- | String to be translated +-- | String to be translated. type TranslationSource = B.ByteString --- | Stores translation catalogs for all supported languages +-- | Stores translation catalogs for all supported languages. data Translations = Translations { tMap :: M.Map LanguageId Gettext.Catalog } @@ -31,7 +31,7 @@ instance Show Translations where -- | This is the main type class of the package. -- All functions work with any instance of this type class. --- +-- -- Note that this class only supports **getting** current language -- and translation, not setting them. Though concrete implementation -- can have its own ways to change language or context, these ways are @@ -40,7 +40,7 @@ instance Show Translations where class (Monad m, Applicative m) => Localized m where -- | Obtain currently selected language ID. getLanguage :: m LanguageId - + -- | Obtain currently loaded translations. getTranslations :: m Translations @@ -52,4 +52,3 @@ class (Monad m, Applicative m) => Localized m where -- | This assumes UTF-8 encoding. toText :: TranslationSource -> T.Text toText bstr = T.fromStrict $ TE.decodeUtf8 bstr - diff --git a/examples/Makefile b/examples/Makefile index 0cd9462..e5a7504 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -21,4 +21,3 @@ clean: find . -name \*.hi -delete find . -name \*.mo -delete rm -f gmotest gmodump - diff --git a/examples/gmotest.hs b/examples/gmotest.hs index 74704ae..0697142 100644 --- a/examples/gmotest.hs +++ b/examples/gmotest.hs @@ -14,7 +14,7 @@ import Data.Text.Format.Heavy (Single (..)) import Text.Localize data LocState = LocState { - lsTranslations :: Translations, + lsTranslations :: Translations, lsLanguage :: LanguageId } deriving (Show) @@ -49,4 +49,3 @@ main = runLoc $ do hello name setLang "fr" hello name - diff --git a/examples/iotest.hs b/examples/iotest.hs index 00f0824..bbd5534 100644 --- a/examples/iotest.hs +++ b/examples/iotest.hs @@ -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 - diff --git a/localize.cabal b/localize.cabal index 766a9ec..7e88eee 100644 --- a/localize.cabal +++ b/localize.cabal @@ -41,4 +41,3 @@ library Source-repository head type: git location: https://github.com/portnov/localize.git - From a2f1ecf83e6c5bad19ec34d29a8e948d93fa1b19 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 21:01:19 +0100 Subject: [PATCH 03/14] Rework .cabal file --- localize.cabal | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/localize.cabal b/localize.cabal index 7e88eee..2bd87c7 100644 --- a/localize.cabal +++ b/localize.cabal @@ -1,42 +1,45 @@ +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 + +common shared + build-depends: base == 4.*, + ghc-options: -Wall + default-language: Haskell2010 library + import: shared exposed-modules: Text.Localize, 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, + build-depends: binary >=0.7 && < 0.9, + containers >=0.5 && < 0.8, 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 Source-repository head type: git From a357f267bb6bb808bbcb53c99a004e4df80c8d01 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 21:19:04 +0100 Subject: [PATCH 04/14] Move source to src/ --- localize.cabal | 1 + {Text => src/Text}/Localize.hs | 0 {Text => src/Text}/Localize/IO.hs | 0 {Text => src/Text}/Localize/Load.hs | 0 {Text => src/Text}/Localize/Locale.hs | 0 {Text => src/Text}/Localize/State.hs | 0 {Text => src/Text}/Localize/Types.hs | 0 7 files changed, 1 insertion(+) rename {Text => src/Text}/Localize.hs (100%) rename {Text => src/Text}/Localize/IO.hs (100%) rename {Text => src/Text}/Localize/Load.hs (100%) rename {Text => src/Text}/Localize/Locale.hs (100%) rename {Text => src/Text}/Localize/State.hs (100%) rename {Text => src/Text}/Localize/Types.hs (100%) diff --git a/localize.cabal b/localize.cabal index 2bd87c7..3cdca6c 100644 --- a/localize.cabal +++ b/localize.cabal @@ -40,6 +40,7 @@ library time >=1.4 && < 1.15, transformers >=0.3 && < 0.7, setlocale >= 1.0 && < 1.1 + hs-source-dirs: src Source-repository head type: git diff --git a/Text/Localize.hs b/src/Text/Localize.hs similarity index 100% rename from Text/Localize.hs rename to src/Text/Localize.hs diff --git a/Text/Localize/IO.hs b/src/Text/Localize/IO.hs similarity index 100% rename from Text/Localize/IO.hs rename to src/Text/Localize/IO.hs diff --git a/Text/Localize/Load.hs b/src/Text/Localize/Load.hs similarity index 100% rename from Text/Localize/Load.hs rename to src/Text/Localize/Load.hs diff --git a/Text/Localize/Locale.hs b/src/Text/Localize/Locale.hs similarity index 100% rename from Text/Localize/Locale.hs rename to src/Text/Localize/Locale.hs diff --git a/Text/Localize/State.hs b/src/Text/Localize/State.hs similarity index 100% rename from Text/Localize/State.hs rename to src/Text/Localize/State.hs diff --git a/Text/Localize/Types.hs b/src/Text/Localize/Types.hs similarity index 100% rename from Text/Localize/Types.hs rename to src/Text/Localize/Types.hs From 4a60a26c90bd03cf1ef0af21636110001a908e0a Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Wed, 2 Jul 2025 14:41:02 +0200 Subject: [PATCH 05/14] Add Monoid instance for Translations --- src/Text/Localize/Types.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Text/Localize/Types.hs b/src/Text/Localize/Types.hs index 7599615..c862442 100644 --- a/src/Text/Localize/Types.hs +++ b/src/Text/Localize/Types.hs @@ -22,6 +22,12 @@ type TranslationSource = B.ByteString data Translations = Translations { tMap :: M.Map LanguageId Gettext.Catalog } +instance Semigroup Translations where + (<>) (Translations a) (Translations b) = Translations (a <> b) + +instance Monoid Translations where + mempty = Translations M.empty + -- | List available languages. availableLanguages :: Translations -> [LanguageId] availableLanguages ts = M.keys (tMap ts) From e0e261e6e3801de108dacba26831d6b9992978ad Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 22:24:04 +0100 Subject: [PATCH 06/14] Fix some warnings --- src/Text/Localize.hs | 10 ---------- src/Text/Localize/Load.hs | 1 - src/Text/Localize/State.hs | 2 -- src/Text/Localize/Types.hs | 1 - 4 files changed, 14 deletions(-) diff --git a/src/Text/Localize.hs b/src/Text/Localize.hs index 0d1480d..b03fcd0 100644 --- a/src/Text/Localize.hs +++ b/src/Text/Localize.hs @@ -17,18 +17,8 @@ module Text.Localize ) 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) diff --git a/src/Text/Localize/Load.hs b/src/Text/Localize/Load.hs index 6ec7ea6..b82b172 100644 --- a/src/Text/Localize/Load.hs +++ b/src/Text/Localize/Load.hs @@ -17,7 +17,6 @@ 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 diff --git a/src/Text/Localize/State.hs b/src/Text/Localize/State.hs index b692e8a..fd21037 100644 --- a/src/Text/Localize/State.hs +++ b/src/Text/Localize/State.hs @@ -13,9 +13,7 @@ module Text.Localize.State setContext, withContext ) where -import Control.Applicative import Control.Monad.State -import Control.Monad.Trans import Text.Localize.Types diff --git a/src/Text/Localize/Types.hs b/src/Text/Localize/Types.hs index c862442..d442159 100644 --- a/src/Text/Localize/Types.hs +++ b/src/Text/Localize/Types.hs @@ -2,7 +2,6 @@ -- | This module contains data type definitions for the @localize@ package. module Text.Localize.Types where -import Control.Applicative import qualified Data.Map as M import qualified Data.ByteString as B import qualified Data.Text.Lazy as T From 47f8ad4b5d1a41d6bfc2f8809c969a9323068659 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 22:36:11 +0100 Subject: [PATCH 07/14] Reduce number of exposed modules --- localize.cabal | 4 ++-- src/Text/Localize.hs | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/localize.cabal b/localize.cabal index 3cdca6c..100442a 100644 --- a/localize.cabal +++ b/localize.cabal @@ -21,10 +21,10 @@ common shared 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: binary >=0.7 && < 0.9, containers >=0.5 && < 0.8, diff --git a/src/Text/Localize.hs b/src/Text/Localize.hs index b03fcd0..7841beb 100644 --- a/src/Text/Localize.hs +++ b/src/Text/Localize.hs @@ -10,10 +10,38 @@ module Text.Localize -- * Basic functions 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(..), + LocalizeT, + runLocalizeT, -- TODO add execLocalizeT and execLocalize + setLanguage, + withLanguage, + setContext, + withContext + ) where import Prelude hiding (lookup) @@ -26,6 +54,7 @@ import Data.Text.Format.Heavy.Parse (parseFormat) import Text.Localize.Types import Text.Localize.Load import Text.Localize.Locale +import Text.Localize.State -- $description -- From 455341bd5fbe7a764cee423364b5db885e174f17 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sun, 12 Jan 2025 06:24:25 +0100 Subject: [PATCH 08/14] Add `Localize` and `runLocalize` So we have monads along with monad transformers. --- src/Text/Localize.hs | 4 +++- src/Text/Localize/State.hs | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Text/Localize.hs b/src/Text/Localize.hs index 7841beb..8c3240e 100644 --- a/src/Text/Localize.hs +++ b/src/Text/Localize.hs @@ -30,11 +30,13 @@ module Text.Localize TranslationSource, Translations(..), availableLanguages, - Localized, + Localized(..), toText, -- ** Monad LocState(..), + Localize, + runLocalize, LocalizeT, runLocalizeT, -- TODO add execLocalizeT and execLocalize setLanguage, diff --git a/src/Text/Localize/State.hs b/src/Text/Localize/State.hs index fd21037..01fba94 100644 --- a/src/Text/Localize/State.hs +++ b/src/Text/Localize/State.hs @@ -6,8 +6,10 @@ module Text.Localize.State (-- * Data types LocState (..), + Localize (..), LocalizeT (..), -- * Functions + runLocalize, runLocalizeT, setLanguage, withLanguage, setContext, withContext @@ -17,6 +19,11 @@ import Control.Monad.State import Text.Localize.Types +import qualified Control.Monad.Identity as I + +------------------------------------------------------------------------------- +-- Types + -- | Localization state. data LocState = LocState { lsTranslations :: Translations, @@ -40,6 +47,24 @@ instance Monad m => Localized (LocalizeT m) where runLocalizeT :: Monad m => LocalizeT m a -> LocState -> m a runLocalizeT actions st = evalStateT (unLocalizeT actions) st +-- | Localization monad. +newtype Localize a = Localize { + unLocalize :: StateT LocState I.Identity a + } + deriving (Functor, Applicative, Monad, MonadState LocState) + +instance Localized Localize where + getTranslations = gets lsTranslations + getLanguage = gets lsLanguage + getContext = gets lsContext + +-- | Run a computation inside 'LocalizeT'. +runLocalize :: Localize a -> LocState -> a +runLocalize actions st = evalState (unLocalize actions) st + +------------------------------------------------------------------------------- +-- Operate + -- | Set current language. setLanguage :: Monad m => LanguageId -> LocalizeT m () setLanguage lang = modify $ \st -> st {lsLanguage = lang} From 63086377c978b38e3037ec0eda4c2c1cd189d004 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sun, 23 Feb 2025 21:08:56 +0100 Subject: [PATCH 09/14] Clarify `loadTranslations` documentation --- src/Text/Localize/Load.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Text/Localize/Load.hs b/src/Text/Localize/Load.hs index b82b172..de12027 100644 --- a/src/Text/Localize/Load.hs +++ b/src/Text/Localize/Load.hs @@ -21,7 +21,8 @@ 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 From 7fabfe4733e2fa3048c78c9d78e437cc1f7a24c4 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Wed, 2 Jul 2025 14:27:21 +0200 Subject: [PATCH 10/14] Fix Haddock markup --- src/Text/Localize.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Localize.hs b/src/Text/Localize.hs index 8c3240e..dce4fc6 100644 --- a/src/Text/Localize.hs +++ b/src/Text/Localize.hs @@ -137,7 +137,7 @@ 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 From 7bd7f40d19390635ea6ec9f6f9f247090778babd Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Wed, 2 Jul 2025 14:47:50 +0200 Subject: [PATCH 11/14] Do not use tail Pattern match instead. --- src/Text/Localize/Load.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Text/Localize/Load.hs b/src/Text/Localize/Load.hs index de12027..e5cd52a 100644 --- a/src/Text/Localize/Load.hs +++ b/src/Text/Localize/Load.hs @@ -98,7 +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 From 02b362920580816f00c1f897c503381fc33897e2 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Wed, 2 Jul 2025 15:02:24 +0200 Subject: [PATCH 12/14] Fix orphan instance --- localize.cabal | 3 +-- src/Text/Localize.hs | 1 - src/Text/Localize/IO.hs | 22 ---------------------- src/Text/Localize/Locale.hs | 17 ----------------- src/Text/Localize/Types.hs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 32 insertions(+), 42 deletions(-) delete mode 100644 src/Text/Localize/Locale.hs diff --git a/localize.cabal b/localize.cabal index 100442a..757f5a0 100644 --- a/localize.cabal +++ b/localize.cabal @@ -24,8 +24,7 @@ library Text.Localize.IO other-modules: Text.Localize.Types, Text.Localize.Load, - Text.Localize.State, - Text.Localize.Locale + Text.Localize.State build-depends: binary >=0.7 && < 0.9, containers >=0.5 && < 0.8, bytestring >= 0.10 && < 0.13, diff --git a/src/Text/Localize.hs b/src/Text/Localize.hs index dce4fc6..5e19262 100644 --- a/src/Text/Localize.hs +++ b/src/Text/Localize.hs @@ -55,7 +55,6 @@ import Data.Text.Format.Heavy.Parse (parseFormat) import Text.Localize.Types import Text.Localize.Load -import Text.Localize.Locale import Text.Localize.State -- $description diff --git a/src/Text/Localize/IO.hs b/src/Text/Localize/IO.hs index 2e3fb49..2e85317 100644 --- a/src/Text/Localize/IO.hs +++ b/src/Text/Localize/IO.hs @@ -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. diff --git a/src/Text/Localize/Locale.hs b/src/Text/Localize/Locale.hs deleted file mode 100644 index 0546129..0000000 --- a/src/Text/Localize/Locale.hs +++ /dev/null @@ -1,17 +0,0 @@ --- | This module contains definitions related to --- obtaining current localization settings from --- process's locale. -module Text.Localize.Locale where - -import Data.Maybe -import System.Locale.SetLocale - -import Text.Localize.Types - --- | Obtain language to be used from process's locale. -languageFromLocale :: IO LanguageId -languageFromLocale = do - mbLocale <- setLocale LC_MESSAGES (Just "") - let locale = fromMaybe "C" mbLocale - return $ takeWhile (/= '_') locale - diff --git a/src/Text/Localize/Types.hs b/src/Text/Localize/Types.hs index d442159..f67ee4b 100644 --- a/src/Text/Localize/Types.hs +++ b/src/Text/Localize/Types.hs @@ -2,11 +2,16 @@ -- | This module contains data type definitions for the @localize@ package. module Text.Localize.Types where +import qualified Data.IORef as IR +import qualified Data.Maybe as MB import qualified Data.Map as M import qualified Data.ByteString as B import qualified Data.Text.Lazy as T import qualified Data.Text.Encoding as TE import qualified Data.Gettext as Gettext +import qualified System.IO.Unsafe as UI +import qualified System.Locale.SetLocale as SL + -- | Language identifier. type LanguageId = String @@ -57,3 +62,29 @@ class (Monad m, Applicative m) => Localized m where -- | This assumes UTF-8 encoding. toText :: TranslationSource -> T.Text toText bstr = T.fromStrict $ TE.decodeUtf8 bstr + +instance Localized IO where + getLanguage = IR.readIORef currentLanguage + getTranslations = IR.readIORef currentTranslations + getContext = IR.readIORef currentContext + +currentContext :: IR.IORef (Maybe Context) +currentContext = UI.unsafePerformIO $ IR.newIORef Nothing +{-# NOINLINE currentContext #-} + +currentLanguage :: IR.IORef LanguageId +currentLanguage = UI.unsafePerformIO $ do + language <- languageFromLocale + IR.newIORef language +{-# NOINLINE currentLanguage #-} + +currentTranslations :: IR.IORef Translations +currentTranslations = UI.unsafePerformIO $ IR.newIORef undefined +{-# NOINLINE currentTranslations #-} + +-- | Obtain language to be used from process's locale. +languageFromLocale :: IO LanguageId +languageFromLocale = do + mbLocale <- SL.setLocale SL.LC_MESSAGES (Just "") + let locale = MB.fromMaybe "C" mbLocale + return $ takeWhile (/= '_') locale From 43b146631f91740b4c4bf899d2a893cdc266f791 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Sat, 11 Jan 2025 21:30:02 +0100 Subject: [PATCH 13/14] Add testuite --- .gitignore | 1 - localize.cabal | 14 +++++++++++++- test/Test.hs | 1 + test/Unit/LocalizeSpec.hs | 20 ++++++++++++++++++++ test/mock-program/en_GB.mo | Bin 0 -> 385 bytes test/mock-program/test.hs | 4 ++++ 6 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test/Test.hs create mode 100644 test/Unit/LocalizeSpec.hs create mode 100644 test/mock-program/en_GB.mo create mode 100644 test/mock-program/test.hs diff --git a/.gitignore b/.gitignore index 080b6e1..4e9bcbb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ *.swp examples/gmotest examples/gmodump -*.mo log hgettext messages.po diff --git a/localize.cabal b/localize.cabal index 757f5a0..90a63fa 100644 --- a/localize.cabal +++ b/localize.cabal @@ -12,6 +12,8 @@ copyright: © 2013–2022 category: Text build-type: Simple extra-doc-files: README.md +extra-source-files: test/mock-program/test.hs, + test/mock-program/en_GB.mo common shared build-depends: base == 4.*, @@ -41,6 +43,16 @@ library setlocale >= 1.0 && < 1.1 hs-source-dirs: src -Source-repository head +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 type: git location: https://github.com/portnov/localize.git diff --git a/test/Test.hs b/test/Test.hs new file mode 100644 index 0000000..a824f8c --- /dev/null +++ b/test/Test.hs @@ -0,0 +1 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} diff --git a/test/Unit/LocalizeSpec.hs b/test/Unit/LocalizeSpec.hs new file mode 100644 index 0000000..20e56a5 --- /dev/null +++ b/test/Unit/LocalizeSpec.hs @@ -0,0 +1,20 @@ +{-# Language OverloadedStrings #-} + +module Unit.LocalizeSpec where + +import Text.Localize as L + +import Test.Hspec + +main :: IO () +main = hspec spec + +spec :: Spec +spec = do + + describe "lookup" $ do + it "returns source string if the translation is not found" $ + L.lookup mempty "en" "prova" `shouldBe` "prova" + it "returns a translated string if the translation is present" $ do + ts <- loadTranslations [("en_GB", "test/mock-program/en_GB.mo")] + L.lookup ts "en_GB" "prova" `shouldBe` "test" diff --git a/test/mock-program/en_GB.mo b/test/mock-program/en_GB.mo new file mode 100644 index 0000000000000000000000000000000000000000..3145d6ad1c6ef64fdd40141f6c8e84a6156ba906 GIT binary patch literal 385 zcmYL@&rZTX5XP&)%N{*@7!wZ~UEDSykcArgQ%Ivo#fnF>!dh#}ZnL`(OnePr&u4Lp zM!xJXGhebZ^L-!IzC6@Ea)ewU2S^90lpvMAM^5%U?+I%S`NSUIR@LmkN~71pON@S( zb4TL^ok?SJtvDpz{!Moj!7Q3S#FINRm8CY0j%}7NXm6ESnraRtnb4_R=hX}y2q!rN zEND>Hq$~ix!Tshr3m7A}!aAB7p==?XHXP(eEXzVd$4Tq1|6E<0?23%zakLBK*BR++ z<)p#|A0=LJa^o(`LgcCq^EY9vbm3zd>n+G*WZD$W_Ln^^!YGp|X|8aEDaS kqaI0$l@SFUYO}N)RJpY_4BGJ9hNlXSL-5bq#H(_8KL{#jzyJUM literal 0 HcmV?d00001 diff --git a/test/mock-program/test.hs b/test/mock-program/test.hs new file mode 100644 index 0000000..a7e2e36 --- /dev/null +++ b/test/mock-program/test.hs @@ -0,0 +1,4 @@ +-- Run `hgettext -o messages.pot test.hs` to obtain `messages.pot`. +-- Then `msginit --locale=en_GB` to get `en_GB.po. +-- Finally `msgfmt en_GB.po -o en_GB.mo`. +string = __ "prova" From 1b8103ebcc739dac6368414d90b8970cb51c2657 Mon Sep 17 00:00:00 2001 From: Francesco Ariis Date: Mon, 21 Jul 2025 12:03:00 +0200 Subject: [PATCH 14/14] Bump containers --- localize.cabal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localize.cabal b/localize.cabal index 90a63fa..a1d3f20 100644 --- a/localize.cabal +++ b/localize.cabal @@ -28,7 +28,7 @@ library Text.Localize.Load, Text.Localize.State build-depends: binary >=0.7 && < 0.9, - containers >=0.5 && < 0.8, + containers >=0.5 && < 0.9, bytestring >= 0.10 && < 0.13, data-default >= 0.7 && < 0.9, directory >= 1.3 && < 1.4,