1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DataKinds #-}
4 {-# LANGUAGE ExistentialQuantification #-}
5 {-# LANGUAGE FlexibleContexts #-}
6 {-# LANGUAGE FlexibleInstances #-}
8 {-# LANGUAGE MultiParamTypeClasses #-}
9 {-# LANGUAGE OverloadedStrings #-}
10 {-# LANGUAGE ScopedTypeVariables #-}
11 {-# LANGUAGE TypeApplications #-}
12 {-# LANGUAGE TypeFamilies #-}
13 {-# LANGUAGE TypeOperators #-}
14 module Language.Locale where
16 import Data.Eq (Eq(..))
17 import Data.Function (($), (.))
18 import Data.Functor ((<$>))
19 import Data.Map.Strict (Map)
20 import Data.Maybe (Maybe(..), isJust)
21 import Data.Ord (Ord(..), Ordering(..))
22 import Data.Semigroup (Semigroup(..))
23 import Data.Text (Text)
24 import Data.Tuple (swap)
25 import Data.Type.Equality ((:~:)(..))
26 import Prelude (error, max)
27 import Text.Show (Show(..))
28 import qualified Data.Char as Char
29 import qualified Data.Map.Strict as Map
30 import qualified Data.Text as Text
37 type family Index xs x where
38 Index (x ': xs) x = Zero
39 Index (not_x ': xs) x = Succ (Index xs x)
42 data Locale (ls::[*]) (l:: *) where
43 LocaleZ :: FullLocale l -> Locale (l ': ls) l
44 LocaleS :: Locale ls l -> Locale (not_l ': ls) l
47 instance ( Show (FullLocale l)
49 ) => Show (Locale (l ': ls) l) where
52 s0:s1:s@('_':_) -> Char.toLower s0:Char.toLower s1:s
54 show (LocaleS l) = show l
62 eqLocale (LocaleZ x) (LocaleZ y) =
63 if x == y then Just Refl else Nothing
64 eqLocale (LocaleS x) (LocaleS y) = eqLocale x y
65 eqLocale _x _y = Nothing
73 compareLocale (LocaleZ x) (LocaleZ y) = compare x y
74 compareLocale (LocaleS x) (LocaleS y) = compareLocale x y
75 compareLocale LocaleZ{} LocaleS{} = LT
76 compareLocale LocaleS{} LocaleZ{} = GT
78 -- ** Type 'FullLocale'
79 data family FullLocale (l:: *) :: *
86 ) => LocaleIn (Locale ls l)
88 instance Eq (LocaleIn ls) where
89 LocaleIn x == LocaleIn y = isJust (eqLocale x y)
90 instance Ord (LocaleIn ls) where
91 compare (LocaleIn x) (LocaleIn y) = compareLocale x y
92 instance Locales ls => Show (LocaleIn ls) where
93 show = Text.unpack . (textLocales @ls Map.!)
95 -- ** Class 'LocaleInj'
97 = LocaleInjP (Index ls l) ls l
98 localeInj :: forall l ls. LocaleInj ls l => FullLocale l -> Locale ls l
99 localeInj = localeInjP @(Index ls l)
101 -- *** Class 'LocaleInjP'
102 class LocaleInjP p ls l where
103 localeInjP :: FullLocale l -> Locale ls l
104 instance LocaleInjP Zero (l ': ls) l where
106 instance LocaleInjP p ls l =>
107 LocaleInjP (Succ p) (not_t ': ls) l where
108 localeInjP = LocaleS . localeInjP @p
110 -- * Class 'Localize'
111 class Localize ls msg a where
112 localize :: LocaleIn ls -> a -> msg
113 instance LocalizeIn l msg a => Localize '[l] msg a where
114 localize (LocaleIn (LocaleZ l)) = localizeIn @l l
115 instance ( LocalizeIn l msg a
116 , Localize (l1 ': ls) msg a
117 ) => Localize (l ': l1 ': ls) msg a where
118 localize (LocaleIn (LocaleZ l)) = localizeIn @l l
119 localize (LocaleIn (LocaleS l)) = localize (LocaleIn l)
121 -- ** Class 'LocalizeIn'
122 class LocalizeIn l msg a where
123 localizeIn :: FullLocale l -> a -> msg
126 class Locales ls where
127 locales :: Map Text (LocaleIn ls)
129 textLocales :: Locales ls => Map (LocaleIn ls) Text
130 textLocales = Map.fromListWith max $ swap <$> Map.toList locales
132 instance Locales '[] where
134 instance (LocalesFor l, Locales ls, Ord (FullLocale l)) => Locales (l ': ls) where
137 (\k _n _o -> error $ "locales: duplicate locale: "<>Text.unpack k)
138 (LocaleIn . localeInj <$> localesFor @l)
139 ((\(LocaleIn l) -> LocaleIn $ LocaleS l) <$> locales @ls)
141 -- ** Class 'LocalesFor'
142 class LocalesFor l where
143 localesFor :: Map Text (FullLocale l)
147 data instance FullLocale FR
153 deriving (Eq,Ord,Show)
154 instance LocalesFor FR where
155 localesFor = Map.fromList
163 fr_FR :: LocaleInj ls FR => Locale ls FR
164 fr_FR = localeInj FR_FR
168 data instance FullLocale EN
187 deriving (Eq,Ord,Show)
188 instance LocalesFor EN where
189 localesFor = Map.fromList
210 en_US :: LocaleInj ls EN => Locale ls EN
211 en_US = localeInj EN_US