ordinal-0.6.0.0: src/Text/Numerals/Languages/French.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
-- |
-- Module : Text.Numerals.Languages.French
-- Description : A module to convert numbers to words in the /French/ language.
-- Maintainer : hapytexeu+gh@gmail.com
-- Stability : experimental
-- Portability : POSIX
--
-- This module contains logic to convert numbers to words in the /French/ language.
module Text.Numerals.Languages.French
( -- * Num to word algorithm
french,
-- * Convert a cardinal number to text
toCardinal',
-- * Convert to ordinal
ordinize',
-- * Constant words
negativeWord',
zeroWord',
oneWord',
-- * Names for numbers
lowWords',
midWords',
highWords',
-- * Merge function
merge',
)
where
#if __GLASGOW_HASKELL__ < 803
import Data.Semigroup((<>))
#endif
import Data.Text (Text, isSuffixOf, pack, snoc)
import Data.Vector (Vector)
import Text.Numerals.Algorithm (HighNumberAlgorithm (LongScale), NumeralsAlgorithm, numeralsAlgorithm)
import Text.Numerals.Algorithm.Template (ordinizeFromDict)
import Text.Numerals.Class (ClockSegment (Half, OClock, QuarterPast, QuarterTo), ClockText, DayPart (Afternoon, Evening, Morning, Night), DaySegment (dayHour, dayPart), FreeMergerFunction, toCardinal, valueSplit)
import Text.Numerals.Internal (_divisable100, _mergeWith, _mergeWithHyphen, _mergeWithSpace, _million, _pluralize', _showIntegral, _stripLastIf, _thousand)
$( pure
( ordinizeFromDict
"_ordinize'"
[ ("cinq", "cinqu"),
("neuf", "neuv")
]
'id
)
)
-- | A 'NumeralsAlgorithm' to convert numbers to words in the /French/ language.
french ::
-- | A 'NumeralsAlgorithm' that can be used to convert numbers to different formats.
NumeralsAlgorithm
french = numeralsAlgorithm negativeWord' zeroWord' oneWord' lowWords' midWords' (valueSplit highWords') merge' ordinize' shortOrdinal' clockText'
-- | Convert numers to their cardinal counterpart in /French/.
toCardinal' ::
Integral i =>
-- | The number to convert to text.
i ->
-- | The cardinal counterpart in /French/.
Text
toCardinal' = toCardinal french
-- | The words used to mark a negative number in the /French/ language.
negativeWord' :: Text
negativeWord' = "moins"
-- | The word used for the number /zero/ in the /French/ language.
zeroWord' :: Text
zeroWord' = "zéro"
-- | The word used for the number /one/ in the /French/ language.
oneWord' :: Text
oneWord' = "un"
-- | A 'Vector' that contains the word used for the numbers /two/ to /twenty/ in the /French/ language.
lowWords' :: Vector Text
lowWords' =
[ "deux",
"trois",
"quatre",
"cinq",
"six",
"sept",
"huit",
"neuf",
"dix",
"onze",
"douze",
"treize",
"quatorze",
"quinze",
"seize",
"dix-sept",
"dix-huit",
"dix-neuf",
"vingt"
]
-- | A list of 2-tuples that contains the names of values between /thirty/ and
-- /thousand/ in the /French/ language.
midWords' :: [(Integer, Text)]
midWords' =
[ (1000, "mille"),
(100, "cent"),
(80, "quatre-vingts"),
(60, "soixante"),
(50, "cinquante"),
(40, "quarante"),
(30, "trente")
]
-- | A merge function that is used to combine the names of words together to
-- larger words, according to the /French/ grammar rules.
merge' :: FreeMergerFunction
merge' 1 r
| r < _million = const id
| otherwise = _merge' 1 r
merge' l r = \ta tb -> _merge' l r (_firstWithoutS l r ta) (_secondWithS l r tb)
_firstWithoutS :: Integral i => i -> i -> Text -> Text
_firstWithoutS l r t
| (_divisable100 (l + 20) || (_divisable100 l && l < _thousand)) && r < _million = _stripLastIf 's' t
| otherwise = t
_secondWithS :: Integral i => i -> i -> Text -> Text
_secondWithS l r t
| l < _thousand && r /= _thousand && _divisable100 r && not ("s" `isSuffixOf` t) = snoc t 's'
| otherwise = t
_merge' :: Integral i => i -> i -> Text -> Text -> Text
_merge' l r
| r >= l || l >= 100 = _mergeWithSpace
| r `mod` 10 == 1 && l /= 80 = _mergeWith " et "
| otherwise = _mergeWithHyphen
-- | A function that converts a number in words in /cardinal/ form to /ordinal/
-- form according to the /French/ language rules.
ordinize' :: Text -> Text
ordinize' "un" = "premier"
ordinize' t = _stripLastIf 'e' (_ordinize' t) <> "ième"
-- | An algorithm to obtain the names of /large/ numbers (one million or larger)
-- in /French/. French uses a /long scale/ with the @illion@ and @illiard@
-- suffixes.
highWords' :: HighNumberAlgorithm
highWords' = LongScale "illion" "illiard"
-- | A function to convert a number to its /short ordinal/ form in /French/.
shortOrdinal' ::
Integral i =>
-- | The number to convert to /short ordinal/ form.
i ->
-- | The equivalent 'Text' specifying the number in /short ordinal/ form.
Text
shortOrdinal' = pack . (`_showIntegral` "e")
_dayPartText :: DayPart -> Text
_dayPartText Night = " de la nuit"
_dayPartText Morning = " du matin"
_dayPartText Afternoon = " de l'après-midi"
_dayPartText Evening = " du soir"
_heures :: Text -> Int -> DaySegment -> Text
_heures sep dh ds = toCardinal' h <> _pluralize' "e heure" " heures" h <> sep <> _dayPartText (dayPart ds)
where
h = dayHour ds + dh
-- | Converting the time to a text that describes that time in /French/.
clockText' :: ClockText
clockText' _ _ 0 0 = "minuit"
clockText' _ _ 12 0 = "midi"
clockText' OClock ds _ _ = _heures "" 0 ds
clockText' QuarterPast ds _ _ = _heures " et quart" 0 ds
clockText' Half ds _ _ = _heures " et demie" 0 ds
clockText' QuarterTo ds _ _ = _heures " moins le quart" 1 ds
clockText' _ ds _ m
| m <= 30 = _heures (" " <> toCardinal' m) 0 ds
| otherwise = _heures (" moins " <> toCardinal' (60 - m)) 1 ds