From a3f43ba0bfb45232376bb2d38cca8259b5c2cbce Mon Sep 17 00:00:00 2001 From: Pablo Diaz Date: Sat, 18 May 2024 23:38:07 -0500 Subject: [PATCH] working partially fr --- README.md | 7 +- examples/alpha/fr/main.go | 15 +++ itn/base.go | 54 ++++++++- itn/i18n.go | 134 +++++++++++++++++++++- itn/lang_fr_test.go | 229 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 433 insertions(+), 6 deletions(-) create mode 100644 examples/alpha/fr/main.go create mode 100644 itn/lang_fr_test.go diff --git a/README.md b/README.md index a080f18..10b1189 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ Check [folder](/examples/) ## Supported languages - ✅ español +- ✅ inglés +- ✅ portugués - 🌀 francés -- 🌀 italiano -- 🌀 portugués +- 🌀 alemán +- 🌀 ruso +- 🌀 Catalan diff --git a/examples/alpha/fr/main.go b/examples/alpha/fr/main.go new file mode 100644 index 0000000..7c3f095 --- /dev/null +++ b/examples/alpha/fr/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/pablodz/itn/itn" +) + +func main() { + itn.SetDebug(true) + + processor, _ := itn.NewLanguage(itn.French) + new_string := processor.Alpha2Digit("un millième", false, true, 3) + println(new_string) + println("-----------------------------------------------------") + println("un 1000ème") +} diff --git a/itn/base.go b/itn/base.go index 194d4b5..461250f 100644 --- a/itn/base.go +++ b/itn/base.go @@ -28,6 +28,7 @@ type Language struct { RadMap map[string]string // Optional Composites map[string]int // Optional PtOrdinals map[string]string // Only for Portuguese + IrrOrd map[string]RelaxTuple } type RelaxTuple struct { @@ -37,6 +38,41 @@ type RelaxTuple struct { func (lg *Language) Ord2Card(word string) string { switch lg.LangCode { + case French: + logPrintf(">>>> Ord2Card.0 [word] %s", word) + if containsKey(lg.IrrOrd, word) { + logPrintf(">>>> Ord2Card.1 %s", word) + return lg.IrrOrd[word].Zero + } + + plurSuff := strings.HasSuffix(word, "ièmes") + singSuff := strings.HasSuffix(word, "ième") + if !(plurSuff || singSuff) { + logPrintf(">>>> Ord2Card.2 %s", word) + return "" + } + + source := "" + if plurSuff { + source = word[:len(word)-5] + } else { + source = word[:len(word)-4] + } + + if source == "cinqu" { + source = "cinq" + } else if source == "neuv" { + source = "neuf" + } else if !containsKey(lg.Numbers, source) { + source = source + "e" + if !containsKey(lg.Numbers, source) { + logPrintf(">>>> Ord2Card.3 %s", source) + return "" + } + } + logPrintf(">>>> Ord2Card.4 %s", source) + return source + case Portuguese: logPrintf(">>>> Ord2Card.0 [word] %s", word) if len(word) < 1 { @@ -98,8 +134,20 @@ func (lg *Language) Ord2Card(word string) string { func (lg *Language) NumOrd(digits string, originalWord string) string { switch lg.LangCode { - case English: + case French: logPrintf(">>>> NumOrd.0 %s", originalWord) + + if containsKey(lg.IrrOrd, originalWord) { + return lg.IrrOrd[originalWord].One + } + + if strings.HasSuffix(originalWord, "e") { + return fmt.Sprintf("%s%s", digits, "ème") + } + return fmt.Sprintf("%s%s", digits, "èmes") + + case English: + logPrintf(">>>> NumOrd.1 %s", originalWord) sf := "" if strings.HasSuffix(originalWord, "s") { sf = originalWord[len(originalWord)-3:] @@ -110,7 +158,7 @@ func (lg *Language) NumOrd(digits string, originalWord string) string { return fmt.Sprintf("%s%s", digits, sf) case Portuguese, Spanish: - logPrintf(">>>> NumOrd.1 %s", originalWord) + logPrintf(">>>> NumOrd.2 %s", originalWord) if strings.HasSuffix(originalWord, "o") { return fmt.Sprintf("%sº", digits) } @@ -124,6 +172,8 @@ func (lg *Language) NumOrd(digits string, originalWord string) string { func (lg *Language) Normalize(word string) string { switch lg.LangCode { + case French: + return strings.ReplaceAll(word, "vingts", "vingt") default: return word } diff --git a/itn/i18n.go b/itn/i18n.go index 3534add..fc6a599 100644 --- a/itn/i18n.go +++ b/itn/i18n.go @@ -10,12 +10,143 @@ type LanguageCode int const ( Spanish LanguageCode = iota English - French Portuguese + French + Deutsch + Russian + Catalan ) func NewLanguage(LangCode LanguageCode) (*Language, error) { switch LangCode { + case French: + + l := &Language{ + LangCode: LangCode, + Multipliers: map[string]int{ + "mil": 1000, + "mille": 1000, + "milles": 1000, + "million": 1000000, + "millions": 1000000, + "milliard": 1000000000, + "milliards": 1000000000, + }, + Units: map[string]int{ + "un": 1, + "deux": 2, + "trois": 3, + "quatre": 4, + "cinq": 5, + "six": 6, + "sept": 7, + "huit": 8, + "neuf": 9, + "une": 1, // optional + }, + STens: map[string]int{ + "dix": 10, + "onze": 11, + "douze": 12, + "treize": 13, + "quatorze": 14, + "quinze": 15, + "seize": 16, + "dix-sept": 17, + "dix-huit": 18, + "dix-neuf": 19, + }, + MTens: map[string]int{ + "vingt": 20, + "trente": 30, + "quarante": 40, + "cinquante": 50, + "soixante": 60, + "septante": 70, + "huitante": 80, + "nonante": 90, + "quatre-vingt": 80, // with hyphen + "octante": 80, // with typo + }, + MTensWSTens: []string{ + "soixante", + "quatre-vingt", + }, + Hundred: map[string]int{ + "cent": 100, + "cents": 100, + }, + Sign: map[string]string{ + "plus": "+", + "moins": "-", + }, + Zero: []string{ + "zéro", + }, + DecimalSep: "virgule", + DecimalSYM: ",", + AndNums: []string{ + "un", + "une", + "unième", + "onze", + "onzième", + }, + + And: "et", + NeverIfAlone: []string{ + "un", + "une", + }, + Relaxed: map[string]RelaxTuple{ + "quatre": {"vingt", "quatre-vingt"}, + }, + Composites: map[string]int{}, + IrrOrd: map[string]RelaxTuple{ + "premier": {"un", "1er"}, + "première": {"un", "1ère"}, + "second": {"deux", "2nd"}, + "seconde": {"deux", "2nde"}, + }, + } + + for k1, v1 := range l.MTens { + for k2, v2 := range l.Units { + if v2 != 1 { + l.Composites[fmt.Sprintf("%s-%s", k1, k2)] = v1 + v2 + } + } + } + + for k1, v1 := range l.MTens { + for k2, v2 := range map[string]int{"et-un": 1, "et-une": 1} { + if v1 > 10 && v2 <= 90 { + l.Composites[fmt.Sprintf("%s-%s", k1, k2)] = v1 + v2 + } + } + } + + l.Composites["quatre-vingt-un"] = 81 + + for k1, v1 := range map[string]int{"soixante": 60, "quatre-vingt": 80} { + for k2, v2 := range l.STens { + l.Composites[fmt.Sprintf("%s-%s", k1, k2)] = v1 + v2 + } + } + + l.Composites["soixante-et-onze"] = 71 + + // deep copy from l.multipliers + l.Numbers = maps.Clone(l.Multipliers) + maps.Copy(l.Numbers, l.Units) + maps.Copy(l.Numbers, l.STens) + maps.Copy(l.Numbers, l.MTens) + maps.Copy(l.Numbers, l.Hundred) + maps.Copy(l.Numbers, l.Composites) + + l.Numbers["quatre-vingts"] = 80 + + return l, nil case Spanish: l := &Language{ LangCode: LangCode, @@ -135,7 +266,6 @@ func NewLanguage(LangCode LanguageCode) (*Language, error) { maps.Copy(l.Numbers, l.STens) maps.Copy(l.Numbers, l.MTens) maps.Copy(l.Numbers, l.Hundred) - maps.Copy(l.Numbers, l.MTens) return l, nil diff --git a/itn/lang_fr_test.go b/itn/lang_fr_test.go new file mode 100644 index 0000000..a8f3525 --- /dev/null +++ b/itn/lang_fr_test.go @@ -0,0 +1,229 @@ +package itn + +import ( + "testing" +) + +func TestAlpha2DigitFR(t *testing.T) { + type test struct { + input string + output string + } + + tests := []test{ + { + input: "Vingt-cinq vaches, douze poulets et cent vingt-cinq kg de pommes de terre.", + output: "25 vaches, 12 poulets et 125 kg de pommes de terre.", + }, + { + input: "Mille deux cent soixante-six clous.", + output: "1266 clous.", + }, + { + input: "Mille deux cents soixante-six clous.", + output: "1266 clous.", + }, + { + input: "Nonante-cinq = quatre-vingt-quinze", + output: "95 = 95", + }, + { + input: "Nonante cinq = quatre-vingt quinze", + output: "95 = 95", + }, + { + input: "un deux trois quatre vingt quinze", + output: "1 2 3 4 20 15", + }, + { + input: "Vingt et un, trente et un.", + output: "21, 31.", + }, + { + input: "trente-quatre = trente quatre", + output: "34 = 34", + }, + { + input: "plus trente-trois neuf soixante zéro six douze vingt et un", + output: "+33 9 60 06 12 21", + }, + { + input: "zéro neuf soixante zéro six douze vingt et un", + output: "09 60 06 12 21", + }, + { + input: "cinquante soixante trente et onze", + output: "50 60 30 11", + }, + { + input: "treize mille zéro quatre-vingt-dix", + output: "13000 090", + }, + { + input: "treize mille zéro quatre-vingts", + output: "13000 080", + }, + { + input: "zéro", + output: "0", + }, + { + input: "a a un trois sept trois trois sept cinq quatre zéro c c", + output: "a a 1 3 7 3 3 7 5 4 0 c c", + }, + { + input: "sept un zéro", + output: "7 1 0", + }, + { + input: "Cinquième premier second troisième vingt et unième centième mille deux cent trentième.", + output: "5ème premier second troisième 21ème 100ème 1230ème.", + }, + { + input: "un millième", + output: "un 1000ème", + }, + { + input: "un millionième", + output: "un 1000000ème", + }, + { + input: "Douze virgule quatre-vingt dix-neuf, cent vingt virgule zéro cinq, un virgule deux cent trente six.", + output: "12,99, 120,05, 1,236.", + }, + { + input: "la densité moyenne est de zéro virgule cinq.", + output: "la densité moyenne est de 0,5.", + }, + { + input: "Il fait plus vingt degrés à l'intérieur et moins quinze à l'extérieur.", + output: "Il fait +20 degrés à l'intérieur et -15 à l'extérieur.", + }, + { + input: "J'en ai vu au moins trois dans le jardin, et non plus deux.", + output: "J'en ai vu au moins 3 dans le jardin, et non plus 2.", + }, + { + input: "Ne pas confondre un article ou un nom avec un chiffre et inversement : les uns et les autres ; une suite de chiffres : un, deux, trois !", + output: "Ne pas confondre un article ou un nom avec un chiffre et inversement : les uns et les autres ; une suite de chiffres : 1, 2, 3 !", + }, + { + input: "Je n'en veux qu'un. J'annonce: le un", + output: "Je n'en veux qu'un. J'annonce: le un", + }, + { + input: "dix + deux\n= douze", + output: "10 + 2\n= 12", + }, + } + + for _, tt := range tests { + processor, _ := NewLanguage(French) + new_string := processor.Alpha2Digit(tt.input, false, true, 3) + if new_string != tt.output { + t.Errorf("❌ Expected <%s>, got <%s>", tt.output, new_string) + } else { + t.Logf("✅ Expected <%s>, got <%s>", tt.output, new_string) + } + } +} + +func TestAlpha2DigitFRRelaxed(t *testing.T) { + type test struct { + input string + output string + } + + tests := []test{ + { + input: "un deux trois quatre vingt quinze.", + output: "1 2 3 95.", + }, + { + input: "Quatre, vingt, quinze, quatre-vingts.", + output: "4, 20, 15, 80.", + }, + } + + for _, tt := range tests { + processor, _ := NewLanguage(French) + new_string := processor.Alpha2Digit(tt.input, true, true, 3) + if new_string != tt.output { + t.Errorf("❌ Expected <%s>, got <%s>", tt.output, new_string) + } else { + t.Logf("✅ Expected <%s>, got <%s>", tt.output, new_string) + } + } +} + +func TestAlpha2DigitFROrdinal0(t *testing.T) { + type test struct { + input string + output string + } + + tests := []test{ + { + input: "Cinquième premier second troisième vingt et unième centième mille deux cent trentième.", + output: "5ème 1er 2nd 3ème 21ème 100ème 1230ème.", + }, + } + + for _, tt := range tests { + processor, _ := NewLanguage(French) + new_string := processor.Alpha2Digit(tt.input, false, true, 0) + if new_string != tt.output { + t.Errorf("❌ Expected <%s>, got <%s>", tt.output, new_string) + } else { + t.Logf("✅ Expected <%s>, got <%s>", tt.output, new_string) + } + } +} + +func TestAlpha2DigitFRSignedTrue(t *testing.T) { + type test struct { + input string + output string + } + + tests := []test{ + { + input: "J'en ai vu au moins trois dans le jardin, et non plus deux.", + output: "J'en ai vu au moins 3 dans le jardin, et non plus 2.", + }, + } + + for _, tt := range tests { + processor, _ := NewLanguage(French) + new_string := processor.Alpha2Digit(tt.input, false, false, 3) + if new_string != tt.output { + t.Errorf("❌ Expected <%s>, got <%s>", tt.output, new_string) + } else { + t.Logf("✅ Expected <%s>, got <%s>", tt.output, new_string) + } + } +} + +func TestAlpha2DigitFRSignedFalse(t *testing.T) { + type test struct { + input string + output string + } + + tests := []test{ + { + input: "J'en ai vu au moins trois dans le jardin, et non plus deux.", + output: "J'en ai vu au moins 3 dans le jardin, et non plus 2.", + }, + } + + for _, tt := range tests { + processor, _ := NewLanguage(French) + new_string := processor.Alpha2Digit(tt.input, false, true, 3) + if new_string != tt.output { + t.Errorf("❌ Expected <%s>, got <%s>", tt.output, new_string) + } else { + t.Logf("✅ Expected <%s>, got <%s>", tt.output, new_string) + } + } +}