{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Hcompta.CLI.Lang where import Control.Monad (liftM) import qualified Data.List import Data.Maybe (catMaybes, fromMaybe) import qualified Data.Text import Data.Text (Text) import qualified Data.Text.Lazy as TL import Prelude hiding (error) import System.Environment (getEnvironment) import System.IO.Memoize (once) import qualified Text.Parsec as Parsec import qualified Text.Parsec.Error as Parsec.Error import qualified Hcompta.Account as Account import Hcompta.Amount (Amount) import Hcompta.Amount.Unit (Unit) import qualified Hcompta.Amount.Write as Amount.Write import Hcompta.Date (Date) import qualified Hcompta.Date.Read as Date.Read import qualified Hcompta.Date.Write as Date.Write import qualified Hcompta.Filter.Read as Filter.Read import Hcompta.Lib.Leijen (ToDoc(..), (<>)) import qualified Hcompta.Lib.Leijen as W import qualified Hcompta.Lib.Parsec as Lib.Parsec -- * Type 'Lang' data Lang = EN | FR deriving (Show) -- * Class 'Translate' class Translate from to where translate :: Lang -> from -> to instance Translate e e where translate _lang = id -- TODO: check that this is expected behavior -- and portability issues from_Env :: IO Lang from_Env = do once getEnvironment >>= liftM (\env -> fromMaybe EN $ from_Strings $ Data.List.concatMap ((\lang -> let short = takeWhile (/= '_') lang in if short == lang then [lang] else [lang, short]) . Data.List.takeWhile (/= '.') ) $ catMaybes [ Data.List.lookup "LC_ALL" env , Data.List.lookup "LC_CTYPE" env , Data.List.lookup "LANG" env ]) from_Strings :: [String] -> Maybe Lang from_Strings s = case s of ("fr" :_) -> Just FR ("fr_FR":_) -> Just FR ("en" :_) -> Just EN ("en_US":_) -> Just EN (_:xs) -> from_Strings xs [] -> Nothing (#) :: ToDoc () a => a -> W.Doc (#) = toDoc () instance ToDoc () Text where toDoc _ = W.strict_text instance ToDoc () String where toDoc _ = W.strict_text . Data.Text.pack instance ToDoc () Int where toDoc _ = W.int instance ToDoc () Integer where toDoc _ = W.integer instance ToDoc () Unit where toDoc _ = Amount.Write.unit instance ToDoc () Amount where toDoc _ = Amount.Write.amount instance ToDoc Lang Date where toDoc _ = Date.Write.date instance ToDoc Lang Date.Read.Error where toDoc FR e = case e of Date.Read.Error_year_or_day_is_missing -> "l’année ou le jour est manquant·e" Date.Read.Error_invalid_date (year, month, day) -> "date incorrecte (année " <> (#)year <> ", mois " <> (#)month <> ", jour " <> (#)day <> ")" Date.Read.Error_invalid_time_of_day (hour, minute, second) -> "heure incorrecte (heure " <> (#)hour <> ", minute " <> (#)minute <> ", seconde " <> (#)second <> ")" toDoc EN e = case e of Date.Read.Error_year_or_day_is_missing -> "year or day is missing" Date.Read.Error_invalid_date (year, month, day) -> "invalid date (year " <> (#)year <> ", month " <> (#)month <> ", day " <> (#)day <> ")" Date.Read.Error_invalid_time_of_day (hour, minute, second) -> "invalid time of day (hour " <> (#)hour <> ", minute " <> (#)minute <> ", second " <> (#)second <> ")" instance Translate Filter.Read.Error W.Doc where translate lang@FR err = case err of Filter.Read.Error_Unknown -> "erreur" Filter.Read.Error_Filter_Date d -> toDoc lang d Filter.Read.Error_Filter_Date_Interval (l, h) -> "mauvais intervalle: (" <> toDoc () l <> ", " <> toDoc () h <> ")" translate lang@EN err = case err of Filter.Read.Error_Unknown -> "error" Filter.Read.Error_Filter_Date d -> toDoc lang d Filter.Read.Error_Filter_Date_Interval (l, h) -> "wrong interval: (" <> toDoc () l <> ", " <> toDoc () h <> ")" -- * Type 'Account' data Account = Account_Equilibrium instance Translate Account Account.Account where translate EN t = case t of Account_Equilibrium -> Account.account "Equilibrium" [] translate FR t = case t of Account_Equilibrium -> Account.account "Équilibre" [] -- * Type 'Comment' data Comment = Comment_Equilibrium instance Translate Comment Text where translate EN t = case t of Comment_Equilibrium -> "Equilibrium posting" translate FR t = case t of Comment_Equilibrium -> "Mouvement d’équilibre" -- * Type 'Description' data Description = Description_Exercise Exercise_OC data Exercise_OC = Exercise_Opening | Exercise_Closing deriving (Eq, Show) instance Translate Description Text where translate EN t = case t of Description_Exercise oc -> case oc of Exercise_Opening -> "Opening balance" Exercise_Closing -> "Closing balance" translate FR t = case t of Description_Exercise oc -> case oc of Exercise_Opening -> "Solde d’ouverture" Exercise_Closing -> "Solde de fermeture" -- * Type 'Error' data Error = Error_Failed_to_include_file FilePath | Error_Failed_to_read_file FilePath | Error_No_input_file_given | Error_One_command_is_required | Error_Option_Balance_Format | Error_Option_Balance_Heritage | Error_Option_Balance_Redundant | Error_Option_Balance_Total | Error_Option_Color | Error_Option_Equilibrium | Error_Option_Equilibrium_Credit | Error_Option_Equilibrium_Debit | Error_Option_Tags_Tree | Error_Option_Verbosity | Error_Transaction_Invalid_date Integer Int Int | Error_Transaction_Invalid_time_of_day Int Int Integer | Error_Transaction_The_following_transaction_is_not_equilibrated_because | Error_Transaction_The_following_virtual_transaction_is_not_equilibrated_because | Error_Transaction_Unit_sums_up_to_the_non_null_amount Unit Amount | Error_Transaction_Year_or_day_is_missing | Error_Unkown_command String instance Translate Error W.Doc where translate EN t = case t of Error_Failed_to_read_file path -> "failed to read file: " <> (#)path Error_Failed_to_include_file path -> "failed to include file: " <> (#)path Error_No_input_file_given -> W.vsep [ "no input file given, please use:" , "- either -i FILE_OF_JOURNAL parameter" , "- or LEDGER_FILE environment variable." ] Error_One_command_is_required -> "a COMMAND is required" Error_Option_Balance_Format -> "--format option expects \"close\", \"open\", or \"table\" as argument" Error_Option_Balance_Heritage -> "--heritage option expects \"yes\" or \"no\" as argument" Error_Option_Balance_Redundant -> "--redundant option expects \"yes\" or \"no\" as argument" Error_Option_Balance_Total -> "--total option expects \"yes\" or \"no\" as argument" Error_Option_Color -> "--color option expects \"auto\" (default), \"yes\" or \"no\" as argument" Error_Option_Equilibrium -> "--eq option expects an ACCOUNT" Error_Option_Equilibrium_Credit -> "--eq-credit option expects an ACCOUNT" Error_Option_Equilibrium_Debit -> "--eq-debit option expects an ACCOUNT" Error_Option_Tags_Tree -> "--tree option expects \"yes\" or \"no\" as value" Error_Option_Verbosity -> "--verbosity option expects \"error\", \"warn\", \"info\" or \"debug\" as argument" Error_Transaction_Invalid_date year month dom -> "invalid date (year " <> (#)year <> ", month " <> (#)month <> ", day " <> (#)dom <> ")" Error_Transaction_Invalid_time_of_day hour minute second -> "invalid time of day (hour " <> (#)hour <> ", minute " <> (#)minute <> ", second " <> (#)second <> ")" Error_Transaction_The_following_transaction_is_not_equilibrated_because -> "the following transaction is not equilibrated, because:" Error_Transaction_The_following_virtual_transaction_is_not_equilibrated_because -> "the following virtual transaction is not equilibrated, because:" Error_Transaction_Unit_sums_up_to_the_non_null_amount unit amount -> " - unit " <> (#)unit <> " sums up to the non-null amount: " <> (#)amount Error_Transaction_Year_or_day_is_missing -> "year or day is missing" Error_Unkown_command cmd -> "unkown command: " <> (#)cmd translate FR t = case t of Error_Failed_to_read_file path -> "échec de la lecture du fichier : " <> (#)path Error_Failed_to_include_file path -> "échec à l’inclusion du fichier : " <> (#)path Error_No_input_file_given -> W.vsep [ "aucun fichier d’entrée indiqué, veuillez utiliser :" , " - soit le paramètre -i FICHIER_DE_JOURNAL," , " - soit la variable d’environnement LEDGER_FILE." ] Error_One_command_is_required -> "une COMMANDE est requise" Error_Option_Balance_Format -> "le paramètre --format s’attend à \"close\", \"open\" ou \"table\" comme argument" Error_Option_Balance_Heritage -> "le paramètre --heritage s’attend à \"yes\" ou \"no\" comme argument" Error_Option_Balance_Redundant -> "le paramètre --redundant s’attend à \"yes\" ou \"no\" comme argument" Error_Option_Balance_Total -> "le paramètre --total s’attend à \"yes\" ou \"no\" comme argument" Error_Option_Color -> "le paramètre --color s’attend à \"auto\" (défaut), \"yes\" ou \"no\" comme argument" Error_Option_Equilibrium -> "le paramètre --eq s’attend à un COMPTE" Error_Option_Equilibrium_Credit -> "le paramètre --eq-credit s’attend à un COMPTE" Error_Option_Equilibrium_Debit -> "le paramètre --eq-debit s’attend à un COMPTE" Error_Option_Tags_Tree -> "le paramètre --total s’attend à \"yes\" ou \"no\" comme argument" Error_Option_Verbosity -> "le paramètre --verbosity s’attend à \"error\", \"warn\", \"info\", or \"debug\" comme argument" Error_Transaction_Invalid_date year month dom -> "date incorrecte (année " <> (#)year <> ", mois " <> (#)month <> ", jour " <> (#)dom <> ")" Error_Transaction_Invalid_time_of_day hour minute second -> "heure incorrecte (heure " <> (#)hour <> ", minute " <> (#)minute <> ", seconde " <> (#)second <> ")" Error_Transaction_The_following_transaction_is_not_equilibrated_because -> "la transaction suivante n’est pas équilibrée, car :" Error_Transaction_The_following_virtual_transaction_is_not_equilibrated_because -> "la transaction virtuelle suivante n’est pas équilibrée, car :" Error_Transaction_Unit_sums_up_to_the_non_null_amount unit amount -> " - l’unité " <> (#)unit <> " a le solde non-nul : " <> (#)amount Error_Transaction_Year_or_day_is_missing -> "l’année ou le jour est manquant-e" Error_Unkown_command cmd -> "commande inconnue : " <> (#)cmd -- * Type 'Error_Parsec' data Error_Parsec = Error_Parsec_Expect W.Doc | Error_Parsec_Message String | Error_Parsec_Or | Error_Parsec_Sysunexpect String | Error_Parsec_Sysunexpect_EOI | Error_Parsec_Unexpect W.Doc | Error_Parsec_Unknown instance ToDoc Lang Error_Parsec where toDoc EN t = case t of Error_Parsec_Expect doc -> "but expect : " <> (#)doc Error_Parsec_Message doc -> (#)doc Error_Parsec_Or -> "or" Error_Parsec_Sysunexpect doc -> "is written : " <> (#)doc Error_Parsec_Sysunexpect_EOI -> "end of file unexpected" Error_Parsec_Unexpect doc -> "found : " <> (#)doc Error_Parsec_Unknown -> "unkown" toDoc FR t = case t of Error_Parsec_Expect doc -> "mais s’attend à : " <> (#)doc Error_Parsec_Message doc -> (#)doc Error_Parsec_Or -> "ou" Error_Parsec_Sysunexpect doc -> "est écrit : " <> (#)doc Error_Parsec_Sysunexpect_EOI -> "fin de fichier inattendue" Error_Parsec_Unexpect doc -> "trouve : " <> (#)doc Error_Parsec_Unknown -> "inconnu" instance Translate Parsec.SourcePos W.Doc where translate EN pos = do let line = Parsec.sourceLine pos let col = Parsec.sourceColumn pos case Parsec.sourceName pos of "" -> "(line " <> (#)line <> ", column " <> (#)col <> ")" path -> "(line " <> (#)line <> ", column " <> (#)col <> ") in: " <> (#)path translate FR pos = do let line = Parsec.sourceLine pos let col = Parsec.sourceColumn pos case Parsec.sourceName pos of "" -> "(ligne " <> (#)line <> ", colonne " <> (#)col <> ")" path -> "(ligne " <> (#)line <> ", colonne " <> (#)col <> ") dans : " <> (#)path instance Translate e W.Doc => Translate [Lib.Parsec.Error e] W.Doc where translate lang errors = W.vsep $ do (flip map) errors $ (\error -> case error of Lib.Parsec.Error_At pos errs -> W.vsep $ [ translate lang pos , translate lang errs ] Lib.Parsec.Error_Parser err -> W.vsep $ [ translate lang (Parsec.errorPos err) , showErrorMessages (Parsec.Error.errorMessages err) ] Lib.Parsec.Error_Custom pos err -> W.vsep $ [ translate lang pos , translate lang err ] ) where showErrorMessages :: [Parsec.Error.Message] -> W.Doc showErrorMessages msgs | null msgs = toDoc lang $ Error_Parsec_Unknown | otherwise = W.vsep $ -- clean $ [showSysUnExpect, showUnExpect, showExpect, showMessages] where (sysUnExpect,msgs1) = span ((Parsec.Error.SysUnExpect "") ==) msgs (unExpect,msgs2) = span ((Parsec.Error.UnExpect "") ==) msgs1 (expect,messages) = span ((Parsec.Error.Expect "") ==) msgs2 showExpect = showMany (Just (toDoc lang . Error_Parsec_Expect)) expect showUnExpect = showMany (Just (toDoc lang . Error_Parsec_Unexpect)) unExpect showSysUnExpect | not (null unExpect) || null sysUnExpect = W.empty | null firstMsg = toDoc lang $ Error_Parsec_Sysunexpect_EOI | otherwise = toDoc lang $ Error_Parsec_Sysunexpect firstMsg where firstMsg = Parsec.Error.messageString (head sysUnExpect) showMessages = showMany Nothing messages -- helpers showMany :: (Maybe (W.Doc -> W.Doc)) -> [Parsec.Error.Message] -> W.Doc showMany pre msgs_ = case clean (map Parsec.Error.messageString msgs_) of [] -> W.empty ms -> case pre of Nothing -> commasOr ms Just p -> p $ commasOr ms commasOr :: [String] -> W.Doc commasOr [] = W.empty commasOr [m] = W.bold $ W.dullblack $ W.text $ TL.pack m commasOr ms = commaSep (init ms) <> (W.space <> toDoc lang Error_Parsec_Or <> W.space) <> (W.bold $ W.dullblack $ W.text $ TL.pack $ last ms) commaSep = W.intercalate (W.comma <> W.space) (W.bold . W.dullblack . W.text . TL.pack) . clean clean = Data.List.nub . filter (not . null) -- * Type 'Header' data Header = Header_Accounts | Header_Accounts_Depth | Header_Journals | Header_Tags | Header_Tags_Distinct | Header_Transactions | Header_Transactions_Date | Header_Units instance Translate Header [Text] where translate EN t = case t of Header_Accounts -> ["Accounts"] Header_Accounts_Depth -> ["Accounts","Depth"] Header_Journals -> ["Journals"] Header_Tags -> ["Tags"] Header_Tags_Distinct -> ["Tags", "Distinct"] Header_Transactions -> ["Transactions"] Header_Transactions_Date -> ["Transactions", "Date"] Header_Units -> ["Unit"] translate FR t = case t of Header_Accounts -> ["Comptes"] Header_Accounts_Depth -> ["Comptes","Profondeur"] Header_Journals -> ["Journaux"] Header_Tags -> ["Tags"] Header_Tags_Distinct -> ["Tags", "Distincts"] Header_Transactions -> ["Écritures"] Header_Transactions_Date -> ["Écritures", "Date"] Header_Units -> ["Unités"] -- * Type 'Help' data Help = Help_Command_Balance | Help_Command_General_Ledger | Help_Command_Journal | Help_Command_Journals | Help_Command_Stats | Help_Command_Tags | Help_Option_Balance_Format | Help_Option_Balance_Heritage | Help_Option_Balance_Redundant | Help_Option_Balance_Total | Help_Option_Color | Help_Option_Equilibrium | Help_Option_Equilibrium_Credit | Help_Option_Equilibrium_Debit | Help_Option_Filter_Balance | Help_Option_Filter_General_Ledger | Help_Option_Filter_Posting | Help_Option_Filter_Tag | Help_Option_Filter_Transaction | Help_Option_Help | Help_Option_Input | Help_Option_Lang | Help_Option_Output | Help_Option_Overwrite | Help_Option_Tags_Tree | Help_Option_Verbosity | Help_Synopsis instance Translate Help String where translate EN t = case t of Help_Command_Balance -> "List final DEBITs, CREDITs and BALANCEs of ACCOUNTs" Help_Command_General_Ledger -> "List DEBITs, CREDITs and BALANCEs of ACCOUNTs after each TRANSACTION" Help_Command_Journal -> "List TRANSACTIONs" Help_Command_Journals -> "List JOURNAL FILEs" Help_Command_Stats -> "Show some statistics" Help_Command_Tags -> "List TAGs" Help_Option_Balance_Format -> "Select BALANCE output format" Help_Option_Balance_Heritage -> "Propagate AMOUNTs to ascending ACCOUNTs" Help_Option_Balance_Redundant -> "Also show ACCOUNTs with null AMOUNT or the same AMOUNTs than its descending ACCOUNT" Help_Option_Balance_Total -> "Show transversal DEBIT, CREDIT, and BALANCE by UNIT" Help_Option_Color -> "Colorize output" Help_Option_Equilibrium -> "Specify the ACCOUNT equilibrating an opening or closing BALANCE" Help_Option_Equilibrium_Credit -> "Like --eq but only when the AMOUNT is a CREDIT" Help_Option_Equilibrium_Debit -> "Like --eq but only when the AMOUNT is a DEBIT" Help_Option_Filter_Balance -> "Apply given FILTER_OF_BALANCE, multiple uses are joined with a logical AND" Help_Option_Filter_General_Ledger -> "Apply given FILTER_OF_GENERAL_LEDGER, multiple uses are joined with a logical AND" Help_Option_Filter_Posting -> "Apply given FILTER_OF_POSTING, multiple uses are joined with a logical AND" Help_Option_Filter_Tag -> "Apply given FILTER_OF_TAG, multiple uses are joined with a logical AND" Help_Option_Filter_Transaction -> "Apply given FILTER_OF_TRANSACTION, multiple uses are joined with a logical AND" Help_Option_Help -> "Show this help" Help_Option_Input -> "Read a JOURNAL from given FILE, multiple uses merge the data" Help_Option_Lang -> "RFC1766 / ISO 639-1 language code (fr, en-GB, etc.)" Help_Option_Output -> "Append output data to given FILE, multiple uses output to multiple FILEs" Help_Option_Overwrite -> "Overwrite given FILE with output data, multiple uses overwrite to multiple FILEs" Help_Option_Tags_Tree -> "Show TAGs as a tree" Help_Option_Verbosity -> "Set verbosity level, or increment it when used multiple times" Help_Synopsis -> "[OPTIONS] COMMAND [COMMAND_OPTIONS]" translate FR t = case t of Help_Command_Balance -> "Liste les DÉBITs, CRÉDITs et SOLDEs finaux des COMPTEs" Help_Command_General_Ledger -> "Liste les DÉBITs, CRÉDITs et SOLDEs des COMPTEs après chaque ÉCRITURE" Help_Command_Journal -> "Liste les ÉCRITUREs" Help_Command_Journals -> "Liste les FICHIERs des JOURNAUX" Help_Command_Stats -> "Affiche quelques statistiques" Help_Command_Tags -> "Liste les TAGs" Help_Option_Balance_Format -> "Sélectionne le format de BALANCE en sortie" Help_Option_Balance_Heritage -> "Propage les MONTANTs aux COMPTEs ascendants" Help_Option_Balance_Redundant -> "Affiche également les COMPTEs dont le MONTANT est nul ou qui ont les mêmes MONTANTs que leur COMPTE descendant" Help_Option_Balance_Total -> "Affiche les SOLDEs transversaux par UNITÉ" Help_Option_Color -> "Colore la sortie" Help_Option_Equilibrium -> "Indique le COMPTE d’équilibre pour une BALANCE d’ouverture ou de fermeture" Help_Option_Equilibrium_Credit -> "Comme --eq mais seulement lorsque le MONTANT est un CRÉDIT" Help_Option_Equilibrium_Debit -> "Comme --eq mais seulement lorsque le MONTANT est un DÉBIT" Help_Option_Filter_Balance -> "Applique le FILTRE_DE_BALANCE donné, un usage multiple agit comme un ET logique" Help_Option_Filter_General_Ledger -> "Applique le FILTRE_DE_GRAND_LIVRE donné, un usage multiple agit comme un ET logique" Help_Option_Filter_Posting -> "Applique le FILTRE_DE_MOUVEMENT donné, un usage multiple agit comme un ET logique" Help_Option_Filter_Tag -> "Applique le FILTRE_DE_TAG donné, un usage multiple agit comme un ET logique" Help_Option_Filter_Transaction -> "Applique le FILTRE_D’ÉCRITURE donné, un usage multiple agit comme un ET logique" Help_Option_Help -> "Affiche cette aide" Help_Option_Input -> "Lit un JOURNAL dans le FICHIER donné, un usage multiple fusionne les données" Help_Option_Lang -> "Code de langue RFC1766 / ISO 639-1 language code (fr, en-GB, etc.)" Help_Option_Output -> "Ajoute la sortie au FICHIER donné, un usage multiple écrit dans plusieurs FICHIERs" Help_Option_Overwrite -> "Écrase le FICHIER donné avec la sortie, un usage multiple écrase plusieurs FICHIERs" Help_Option_Tags_Tree -> "Affiche les TAGs en arborescence" Help_Option_Verbosity -> "Indique le niveau de verbosité, ou l’incrémente lorsque utilisé plusieurs fois" Help_Synopsis -> "[PARAMÈTRES] COMMANDE [PARAMÈTRES_DE_COMMANDE]" -- * Type 'Section' data Section = Section_Commands | Section_Description | Section_Syntax | Section_Options deriving (Eq, Show) instance Translate Section String where translate EN t = case t of Section_Commands -> "COMMANDS (use COMMAND --help for help on COMMAND)" Section_Description -> "DESCRIPTION" Section_Syntax -> "SYNTAX" Section_Options -> "OPTIONS" translate FR t = case t of Section_Commands -> "COMMANDES (utilisez COMMANDE --help pour une aide sur COMMANDE)" Section_Description -> "DESCRIPTION" Section_Syntax -> "SYNTAXE" Section_Options -> "PARAMÈTRES" -- * Type 'Title' data Title = Title_Account | Title_Balance | Title_Credit | Title_Date | Title_Debit | Title_Description | Title_Running_balance | Title_Running_credit | Title_Running_debit instance Translate Title Text where translate EN t = case t of Title_Account -> "Account" Title_Balance -> "Balance" Title_Credit -> "Credit" Title_Date -> "Date" Title_Debit -> "Debit" Title_Description -> "Wording" Title_Running_balance -> "Running balance" Title_Running_credit -> "Running credit" Title_Running_debit -> "Running debit" translate FR t = case t of Title_Account -> "Compte" Title_Balance -> "Solde" Title_Credit -> "Crédit" Title_Date -> "Date" Title_Debit -> "Débit" Title_Description -> "Libellé" Title_Running_balance -> "Solde cumulé" Title_Running_credit -> "Crédit cumulé" Title_Running_debit -> "Débit cumulé" -- * Type 'Type' data Type = Type_Account | Type_File | Type_File_Journal | Type_Filter_Balance | Type_Filter_General_Ledger | Type_Filter_Posting | Type_Filter_Tag | Type_Filter_Transaction | Type_Option deriving (Eq, Show) instance Translate Type String where translate EN t = case t of Type_Account -> "ACCOUNT" Type_File -> "FILE" Type_File_Journal -> "FILE_OF_JOURNAL" Type_Filter_Balance -> "FILTER_OF_BALANCE" Type_Filter_General_Ledger -> "FILTER_OF_GENERAL_LEDGER" Type_Filter_Posting -> "FILTER_OF_POSTING" Type_Filter_Tag -> "FILTER_OF_TAG" Type_Filter_Transaction -> "FILTER_OF_TRANSACTION" Type_Option -> "OPTION" translate FR t = case t of Type_Account -> "COMPTE" Type_File -> "FICHIER" Type_File_Journal -> "FICHIER_DE_JOURNAL" Type_Filter_Balance -> "FILTRE_DE_BALANCE" Type_Filter_General_Ledger -> "FILTRE_DE_GRAND_LIVRE" Type_Filter_Posting -> "FILTRE_DE_MOUVEMENT" Type_Filter_Tag -> "FILTRE_DE_TAG" Type_Filter_Transaction -> "FILTRE_D’ÉCRITURE" Type_Option -> "PARAMÈTRE" -- * Type 'Write' data Write = Write_Debug | Write_Error instance Translate Write W.Doc where translate EN t = case t of Write_Error -> "ERROR" Write_Debug -> "DEBUG" translate FR t = case t of Write_Error -> "ERREUR" Write_Debug -> "DÉBUG"