Setup + Numeral

Summary: Setup for Hebrew + Numeral dimension

Reviewed By: niteria

Differential Revision: D4930041

fbshipit-source-id: 965132b
This commit is contained in:
Julien Odent 2017-04-24 06:26:59 -07:00 committed by Facebook Github Bot
parent b26aa7d84d
commit bd96d3dd95
13 changed files with 599 additions and 2 deletions

View File

@ -13,9 +13,10 @@ module Duckling.Dimensions
( allDimensions
, explicitDimensions
) where
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import Prelude
import qualified Data.HashSet as HashSet
import Duckling.Dimensions.Types
import qualified Duckling.Dimensions.Common as CommonDimensions
@ -27,6 +28,7 @@ import qualified Duckling.Dimensions.ES as ESDimensions
import qualified Duckling.Dimensions.ET as ETDimensions
import qualified Duckling.Dimensions.FR as FRDimensions
import qualified Duckling.Dimensions.GA as GADimensions
import qualified Duckling.Dimensions.HE as HEDimensions
import qualified Duckling.Dimensions.ID as IDDimensions
import qualified Duckling.Dimensions.IT as ITDimensions
import qualified Duckling.Dimensions.JA as JADimensions
@ -81,6 +83,7 @@ langDimensions ES = ESDimensions.allDimensions
langDimensions ET = ETDimensions.allDimensions
langDimensions FR = FRDimensions.allDimensions
langDimensions GA = GADimensions.allDimensions
langDimensions HE = HEDimensions.allDimensions
langDimensions ID = IDDimensions.allDimensions
langDimensions IT = ITDimensions.allDimensions
langDimensions JA = JADimensions.allDimensions

18
Duckling/Dimensions/HE.hs Normal file
View File

@ -0,0 +1,18 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
module Duckling.Dimensions.HE
( allDimensions
) where
import Duckling.Dimensions.Types
allDimensions :: [Some Dimension]
allDimensions =
[ This Numeral
]

View File

@ -30,6 +30,7 @@ data Lang
| ET
| FR
| GA
| HE
| ID
| IT
| JA

View File

@ -0,0 +1,94 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.HE.Corpus
( corpus ) where
import Prelude
import Data.String
import Duckling.Lang
import Duckling.Numeral.Types
import Duckling.Resolve
import Duckling.Testing.Types
corpus :: Corpus
corpus = (testContext {lang = HE}, allExamples)
allExamples :: [Example]
allExamples = concat
[ examples (NumeralValue 0)
[ "0"
, "אפס"
]
, examples (NumeralValue 1)
[ "1"
, "אחד"
, "אחת"
, "יחיד"
]
, examples (NumeralValue 2)
[ "2"
, "שתיים"
, "שניים"
, "זוג"
]
, examples (NumeralValue 33)
[ "33"
, "שלושים ושלוש"
, "שלושים ושלושה"
, "0033"
]
, examples (NumeralValue 14)
[ "14"
, "ארבעה עשר"
, "ארבע עשרה"
]
, examples (NumeralValue 16)
[ "16"
, "ששה עשר"
, "שש עשרה"
]
, examples (NumeralValue 17)
[ "17"
, "שבעה עשר"
, "שבע עשרה"
]
, examples (NumeralValue 18)
[ "18"
, "שמונה עשר"
, "שמונה עשרה"
]
, examples (NumeralValue 1.1)
[ "1.1"
, "1.10"
, "01.10"
]
, examples (NumeralValue 0.77)
[ "0.77"
, ".77"
]
, examples (NumeralValue 100000)
[ "100,000"
, "100000"
]
, examples (NumeralValue 3000000)
[ "3000000"
, "3,000,000"
]
, examples (NumeralValue 1200000)
[ "1,200,000"
, "1200000"
]
, examples (NumeralValue (-1200000))
[ "- 1,200,000"
, "-1200000"
, "מינוס 1,200,000"
]
]

View File

@ -0,0 +1,389 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Numeral.HE.Rules
( rules ) where
import Data.Maybe
import Data.String
import Data.Text (Text)
import Prelude
import qualified Data.Text as Text
import Duckling.Dimensions.Types
import Duckling.Numeral.Helpers
import Duckling.Numeral.Types (NumeralData (..))
import Duckling.Regex.Types
import Duckling.Types
import qualified Duckling.Numeral.Types as TNumeral
ruleInteger5 :: Rule
ruleInteger5 = Rule
{ name = "integer 4"
, pattern =
[ regex "(\x05d0\x05e8\x05d1\x05e2(\x05d4)?)"
]
, prod = \_ -> integer 4
}
ruleIntersectNumerals :: Rule
ruleIntersectNumerals = Rule
{ name = "intersect numbers"
, pattern =
[ numberWith (fromMaybe 0 . TNumeral.grain) (>1)
, numberWith TNumeral.multipliable not
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = val1, TNumeral.grain = Just g}):
Token Numeral (NumeralData {TNumeral.value = val2}):
_) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
_ -> Nothing
}
ruleIntersectWithAnd :: Rule
ruleIntersectWithAnd = Rule
{ name = "intersect (with and)"
, pattern =
[ numberWith (fromMaybe 0 . TNumeral.grain) (>1)
, regex "\x05d5"
, numberWith TNumeral.multipliable not
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = val1, TNumeral.grain = Just g}):
_:
Token Numeral (NumeralData {TNumeral.value = val2}):
_) | (10 ** fromIntegral g) > val2 -> double $ val1 + val2
_ -> Nothing
}
ruleCompositeTens :: Rule
ruleCompositeTens = Rule
{ name = "integer 21..99"
, pattern =
[ oneOf [ 20, 30..90 ]
, numberBetween 1 10
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = tens}):
Token Numeral (NumeralData {TNumeral.value = units}):
_) -> double $ tens + units
_ -> Nothing
}
ruleCompositeTensWithAnd :: Rule
ruleCompositeTensWithAnd = Rule
{ name = "integer 21..99 (with and)"
, pattern =
[ oneOf [ 20, 30..90 ]
, regex "\x05d5"
, numberBetween 1 10
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = tens}):
_:
Token Numeral (NumeralData {TNumeral.value = units}):
_) -> double $ tens + units
_ -> Nothing
}
ruleNumeralsPrefixWithNegativeOrMinus :: Rule
ruleNumeralsPrefixWithNegativeOrMinus = Rule
{ name = "numbers prefix with -, negative or minus"
, pattern =
[ regex "-|\x05de\x05d9\x05e0\x05d5\x05e1"
, dimension Numeral
]
, prod = \tokens -> case tokens of
(_:Token Numeral nd:_) -> double (TNumeral.value nd * (-1))
_ -> Nothing
}
ruleIntegerNumeric :: Rule
ruleIntegerNumeric = Rule
{ name = "integer (numeric)"
, pattern =
[ regex "(\\d{1,18})"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> do
v <- toInteger <$> parseInt match
integer v
_ -> Nothing
}
ruleInteger10 :: Rule
ruleInteger10 = Rule
{ name = "integer 9"
, pattern =
[ regex "(\x05ea\x05e9\x05e2(\x05d4)?)"
]
, prod = \_ -> integer 9
}
ruleInteger15 :: Rule
ruleInteger15 = Rule
{ name = "integer (20..90)"
, pattern =
[ regex "(\x05e2\x05e9\x05e8\x05d9\x05dd|\x05e9\x05dc\x05d5\x05e9\x05d9\x05dd|\x05d0\x05e8\x05d1\x05e2\x05d9\x05dd|\x05d7\x05de\x05d9\x05e9\x05d9\x05dd|\x05e9\x05d9\x05e9\x05d9\x05dd|\x05e9\x05d1\x05e2\x05d9\x05dd|\x05e9\x05de\x05d5\x05e0\x05d9\x05dd|\x05ea\x05e9\x05e2\x05d9\x05dd)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> case match of
"\x05e2\x05e9\x05e8\x05d9\x05dd" -> integer 20
"\x05e9\x05dc\x05d5\x05e9\x05d9\x05dd" -> integer 30
"\x05d0\x05e8\x05d1\x05e2\x05d9\x05dd" -> integer 40
"\x05d7\x05de\x05d9\x05e9\x05d9\x05dd" -> integer 50
"\x05e9\x05d9\x05e9\x05d9\x05dd" -> integer 60
"\x05e9\x05d1\x05e2\x05d9\x05dd" -> integer 70
"\x05e9\x05de\x05d5\x05e0\x05d9\x05dd" -> integer 80
"\x05ea\x05e9\x05e2\x05d9\x05dd" -> integer 90
_ -> Nothing
_ -> Nothing
}
ruleDecimalNumeral :: Rule
ruleDecimalNumeral = Rule
{ name = "decimal number"
, pattern =
[ regex "(\\d*\\.\\d+)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> parseDecimal True match
_ -> Nothing
}
ruleInteger3 :: Rule
ruleInteger3 = Rule
{ name = "integer 2"
, pattern =
[ regex "(\x05e9\x05ea\x05d9\x05d9\x05dd|\x05e9\x05e0\x05d9\x05d9\x05dd)"
]
, prod = \_ -> integer 2
}
ruleSingle :: Rule
ruleSingle = Rule
{ name = "single"
, pattern =
[ regex "\x05d9\x05d7\x05d9\x05d3"
]
, prod = \_ -> integer 1
}
ruleInteger13 :: Rule
ruleInteger13 = Rule
{ name = "integer 12"
, pattern =
[ regex "(\x05e9\x05e0\x05d9\x05d9\x05dd \x05e2\x05e9\x05e8|\x05ea\x05e8\x05d9 \x05e2\x05e9\x05e8)"
]
, prod = \_ -> integer 12
}
ruleMultiply :: Rule
ruleMultiply = Rule
{ name = "compose by multiplication"
, pattern =
[ dimension Numeral
, numberWith TNumeral.multipliable id
]
, prod = \tokens -> case tokens of
(token1:token2:_) -> multiply token1 token2
_ -> Nothing
}
ruleInteger6 :: Rule
ruleInteger6 = Rule
{ name = "integer 5"
, pattern =
[ regex "(\x05d7\x05de(\x05e9|\x05d9\x05e9\x05d4))"
]
, prod = \_ -> integer 5
}
rulePowersOfTen :: Rule
rulePowersOfTen = Rule
{ name = "powers of tens"
, pattern =
[ regex "(\x05de\x05d0(\x05d4|\x05d5\x05ea)|\x05d0\x05dc(\x05e3|\x05e4\x05d9\x05dd)|\x05de\x05d9\x05dc\x05d9\x05d5(\x05df|\x05e0\x05d9\x05dd))"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) -> case Text.toLower match of
"\x05de\x05d0\x05d4" ->
double 1e2 >>= withGrain 2 >>= withMultipliable
"\x05de\x05d0\x05d5\x05ea" ->
double 1e2 >>= withGrain 2 >>= withMultipliable
"\x05d0\x05dc\x05e3" ->
double 1e3 >>= withGrain 3 >>= withMultipliable
"\x05d0\x05dc\x05e4\x05d9\x05dd" ->
double 1e3 >>= withGrain 3 >>= withMultipliable
"\x05de\x05d9\x05dc\x05d9\x05d5\x05df" ->
double 1e6 >>= withGrain 6 >>= withMultipliable
"\x05de\x05d9\x05dc\x05d9\x05d5\x05e0\x05d9\x05dd" ->
double 1e6 >>= withGrain 6 >>= withMultipliable
_ -> Nothing
_ -> Nothing
}
ruleInteger7 :: Rule
ruleInteger7 = Rule
{ name = "integer 6"
, pattern =
[ regex "(\x05e9\x05e9(\x05d4)?)"
]
, prod = \_ -> integer 6
}
ruleInteger14 :: Rule
ruleInteger14 = Rule
{ name = "integer 11..19"
, pattern =
[ numberBetween 1 10
, numberWith TNumeral.value (== 10)
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v1}):
Token Numeral (NumeralData {TNumeral.value = v2}):
_) -> double $ v1 + v2
_ -> Nothing
}
ruleInteger8 :: Rule
ruleInteger8 = Rule
{ name = "integer 7"
, pattern =
[ regex "(\x05e9\x05d1\x05e2(\x05d4)?)"
]
, prod = \_ -> integer 7
}
ruleCouple :: Rule
ruleCouple = Rule
{ name = "couple"
, pattern =
[ regex "\x05d6\x05d5\x05d2( \x05e9\x05dc)?"
]
, prod = \_ -> integer 2
}
ruleInteger16 :: Rule
ruleInteger16 = Rule
{ name = "integer 101..999"
, pattern =
[ oneOf [300, 600, 500, 100, 800, 200, 900, 700, 400]
, numberBetween 1 100
]
, prod = \tokens -> case tokens of
(Token Numeral (NumeralData {TNumeral.value = v1}):
Token Numeral (NumeralData {TNumeral.value = v2}):
_) -> double $ v1 + v2
_ -> Nothing
}
ruleInteger9 :: Rule
ruleInteger9 = Rule
{ name = "integer 8"
, pattern =
[ regex "(\x05e9\x05de\x05d5\x05e0\x05d4)"
]
, prod = \_ -> integer 8
}
ruleInteger :: Rule
ruleInteger = Rule
{ name = "integer 0"
, pattern =
[ regex "(\x05d0\x05e4\x05e1|\x05db\x05dc\x05d5\x05dd)"
]
, prod = \_ -> integer 0
}
ruleInteger4 :: Rule
ruleInteger4 = Rule
{ name = "integer 3"
, pattern =
[ regex "(\x05e9\x05dc\x05d5\x05e9(\x05d4)?)"
]
, prod = \_ -> integer 3
}
ruleInteger2 :: Rule
ruleInteger2 = Rule
{ name = "integer 1"
, pattern =
[ regex "(\x05d0\x05d7\x05d3|\x05d0\x05d7\x05ea)"
]
, prod = \_ -> integer 1
}
ruleInteger11 :: Rule
ruleInteger11 = Rule
{ name = "integer 10"
, pattern =
[ regex "(\x05e2\x05e9\x05e8(\x05d4)?)"
]
, prod = \_ -> integer 10
}
ruleNumeralDotNumeral :: Rule
ruleNumeralDotNumeral = Rule
{ name = "number dot number"
, pattern =
[ dimension Numeral
, regex "\x05e0\x05e7\x05d5\x05d3\x05d4"
, numberWith TNumeral.grain isNothing
]
, prod = \tokens -> case tokens of
(Token Numeral nd1:_:Token Numeral nd2:_) ->
double $ TNumeral.value nd1 + decimalsToDouble (TNumeral.value nd2)
_ -> Nothing
}
ruleCommas :: Rule
ruleCommas = Rule
{ name = "comma-separated numbers"
, pattern =
[ regex "(\\d+(,\\d\\d\\d)+(\\.\\d+)?)"
]
, prod = \tokens -> case tokens of
(Token RegexMatch (GroupMatch (match:_)):_) ->
parseDouble (Text.replace (Text.singleton ',') Text.empty match) >>= double
_ -> Nothing
}
rules :: [Rule]
rules =
[ ruleCommas
, ruleCompositeTens
, ruleCompositeTensWithAnd
, ruleCouple
, ruleDecimalNumeral
, ruleInteger
, ruleInteger10
, ruleInteger11
, ruleInteger13
, ruleInteger14
, ruleInteger15
, ruleInteger16
, ruleInteger2
, ruleInteger3
, ruleInteger4
, ruleInteger5
, ruleInteger6
, ruleInteger7
, ruleInteger8
, ruleInteger9
, ruleIntegerNumeric
, ruleIntersectNumerals
, ruleIntersectWithAnd
, ruleMultiply
, ruleNumeralDotNumeral
, ruleNumeralsPrefixWithNegativeOrMinus
, rulePowersOfTen
, ruleSingle
]

View File

@ -19,6 +19,7 @@ import qualified Duckling.Ranking.Classifiers.ES as ESClassifiers
import qualified Duckling.Ranking.Classifiers.ET as ETClassifiers
import qualified Duckling.Ranking.Classifiers.FR as FRClassifiers
import qualified Duckling.Ranking.Classifiers.GA as GAClassifiers
import qualified Duckling.Ranking.Classifiers.HE as HEClassifiers
import qualified Duckling.Ranking.Classifiers.ID as IDClassifiers
import qualified Duckling.Ranking.Classifiers.IT as ITClassifiers
import qualified Duckling.Ranking.Classifiers.JA as JAClassifiers
@ -46,6 +47,7 @@ classifiers ES = ESClassifiers.classifiers
classifiers ET = ETClassifiers.classifiers
classifiers FR = FRClassifiers.classifiers
classifiers GA = GAClassifiers.classifiers
classifiers HE = HEClassifiers.classifiers
classifiers ID = IDClassifiers.classifiers
classifiers IT = ITClassifiers.classifiers
classifiers JA = JAClassifiers.classifiers

View File

@ -0,0 +1,22 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
-----------------------------------------------------------------
-- Auto-generated by regenClassifiers
--
-- DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING
-- @generated
-----------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Ranking.Classifiers.HE (classifiers) where
import Prelude
import Duckling.Ranking.Types
import qualified Data.HashMap.Strict as HashMap
import Data.String
classifiers :: Classifiers
classifiers = HashMap.fromList []

View File

@ -13,9 +13,10 @@ module Duckling.Rules
( allRules
, rulesFor
) where
import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet
import Prelude
import qualified Data.HashSet as HashSet
import Duckling.Dimensions
import Duckling.Dimensions.Types
@ -29,6 +30,7 @@ import qualified Duckling.Rules.ES as ESRules
import qualified Duckling.Rules.ET as ETRules
import qualified Duckling.Rules.FR as FRRules
import qualified Duckling.Rules.GA as GARules
import qualified Duckling.Rules.HE as HERules
import qualified Duckling.Rules.ID as IDRules
import qualified Duckling.Rules.IT as ITRules
import qualified Duckling.Rules.JA as JARules
@ -72,6 +74,7 @@ langRules ES = ESRules.rules
langRules ET = ETRules.rules
langRules FR = FRRules.rules
langRules GA = GARules.rules
langRules HE = HERules.rules
langRules ID = IDRules.rules
langRules IT = ITRules.rules
langRules JA = JARules.rules

34
Duckling/Rules/HE.hs Normal file
View File

@ -0,0 +1,34 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
module Duckling.Rules.HE
( rules
) where
import Duckling.Dimensions.Types
import Duckling.Types
import qualified Duckling.Numeral.HE.Rules as Numeral
rules :: Some Dimension -> [Rule]
rules (This Distance) = []
rules (This Duration) = []
rules (This Numeral) = Numeral.rules
rules (This Email) = []
rules (This AmountOfMoney) = []
rules (This Ordinal) = []
rules (This PhoneNumber) = []
rules (This Quantity) = []
rules (This RegexMatch) = []
rules (This Temperature) = []
rules (This Time) = []
rules (This TimeGrain) = []
rules (This Url) = []
rules (This Volume) = []

View File

@ -44,6 +44,7 @@ library
, Duckling.Rules.ET
, Duckling.Rules.FR
, Duckling.Rules.GA
, Duckling.Rules.HE
, Duckling.Rules.ID
, Duckling.Rules.IT
, Duckling.Rules.JA
@ -73,6 +74,7 @@ library
, Duckling.Ranking.Classifiers.ES
, Duckling.Ranking.Classifiers.FR
, Duckling.Ranking.Classifiers.GA
, Duckling.Ranking.Classifiers.HE
, Duckling.Ranking.Classifiers.IT
, Duckling.Ranking.Classifiers.KO
, Duckling.Ranking.Classifiers.NB
@ -105,6 +107,7 @@ library
, Duckling.Dimensions.ET
, Duckling.Dimensions.FR
, Duckling.Dimensions.GA
, Duckling.Dimensions.HE
, Duckling.Dimensions.ID
, Duckling.Dimensions.IT
, Duckling.Dimensions.JA
@ -225,6 +228,8 @@ library
, Duckling.Numeral.FR.Rules
, Duckling.Numeral.GA.Corpus
, Duckling.Numeral.GA.Rules
, Duckling.Numeral.HE.Corpus
, Duckling.Numeral.HE.Rules
, Duckling.Numeral.ID.Corpus
, Duckling.Numeral.ID.Rules
, Duckling.Numeral.IT.Corpus
@ -540,6 +545,7 @@ test-suite duckling-test
, Duckling.Numeral.ET.Tests
, Duckling.Numeral.FR.Tests
, Duckling.Numeral.GA.Tests
, Duckling.Numeral.HE.Tests
, Duckling.Numeral.ID.Tests
, Duckling.Numeral.IT.Tests
, Duckling.Numeral.JA.Tests

View File

@ -73,6 +73,7 @@ regenClassifiers lang = do
ET -> (testContext, [])
FR -> FRTime.corpus
GA -> GATime.corpus
HE -> (testContext, [])
ID -> (testContext, [])
IT -> ITTime.corpus
JA -> (testContext, [])

View File

@ -0,0 +1,22 @@
-- Copyright (c) 2016-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is licensed under the BSD-style license found in the
-- LICENSE file in the root directory of this source tree. An additional grant
-- of patent rights can be found in the PATENTS file in the same directory.
module Duckling.Numeral.HE.Tests
( tests ) where
import Data.String
import Prelude
import Test.Tasty
import Duckling.Dimensions.Types
import Duckling.Numeral.HE.Corpus
import Duckling.Testing.Asserts
tests :: TestTree
tests = testGroup "HE Tests"
[ makeCorpusTest [This Numeral] corpus
]

View File

@ -20,6 +20,7 @@ import qualified Duckling.Numeral.ES.Tests as ES
import qualified Duckling.Numeral.ET.Tests as ET
import qualified Duckling.Numeral.FR.Tests as FR
import qualified Duckling.Numeral.GA.Tests as GA
import qualified Duckling.Numeral.HE.Tests as HE
import qualified Duckling.Numeral.ID.Tests as ID
import qualified Duckling.Numeral.IT.Tests as IT
import qualified Duckling.Numeral.JA.Tests as JA
@ -47,6 +48,7 @@ tests = testGroup "Numeral Tests"
, ET.tests
, FR.tests
, GA.tests
, HE.tests
, ID.tests
, IT.tests
, JA.tests