Skip to content

Commit d7ff14e

Browse files
nb: Fix and enhance Norwegian fraction pronunciation
Extend the range of denominators recognized as ordinal fractions up to 100. Implement correct singular and plural forms for fractions, including specific handling for a numerator of one with denominators greater than 19. Simplify ordinal fractional word forms (e.g., 'tredjedel' to 'tredel') and ensure proper hyphenation when combining fractions with units.
1 parent ec90375 commit d7ff14e

11 files changed

Lines changed: 213 additions & 106 deletions

File tree

‎Rules/Languages/nb/ClearSpeak_Rules.yaml‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,11 @@
140140
tag: fraction
141141
match:
142142
- "($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and"
143-
- "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and" # NORWEGIAN: Changed decimal point to $DecimalSeparators
144-
- "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and (2<= text() and text()<=10)]" # NORWEGIAN: Removed $ClearSpeak_Fractions='Ordinal', cause the pattern for ordinal fractions is not right.
143+
- "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<100)] and" # NORWEGIAN: Changed decimal point to $DecimalSeparators
144+
- "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and (2<= text() and text()<=100)]" # NORWEGIAN: Removed $ClearSpeak_Fractions='Ordinal', cause the pattern for ordinal fractions is not right.
145145
variables:
146146
- IsPlural: "not(*[1]=1)"
147+
- PluralDenominator: "ToOrdinal(*[2], true(), true())"
147148
# set right level for the location of the common fraction/mixed fraction
148149
- IsMixedFraction: "preceding-sibling::*[1][.='\u2064']"
149150
- UnitSibling: "IfThenElse($IsMixedFraction, .., .)"
@@ -169,7 +170,13 @@
169170
- if: "not($UnitNode)"
170171
then:
171172
- x: "*[1]"
172-
- x: "ToOrdinal(*[2], true(), $IsPlural)" # extra args specify fractional ordinal and whether it is plural
173+
- test:
174+
- if: "*[1]=1 and *[2] > 19"
175+
then:
176+
- x: "translate(substring($PluralDenominator, 1, string-length($PluralDenominator) - 2), ' ', '')"
177+
- else:
178+
- x: "translate(ToOrdinal(*[2], true(), $IsPlural), ' ', '')"
179+
173180
# unit follows the fraction -- special cases
174181
- else:
175182
- test:
@@ -184,7 +191,7 @@
184191
else: [T: "halv"]
185192
- else_if: "*[2] <= 10"
186193
then: [x: "DefinitionValue(*[2], 'Speech', 'OrdinalFractionalDenominatorsForUnits')"]
187-
else: [x: "*[2]", CT: "dels"] # this is wrong but shouldn't occur normally -- need to write a function to compute cardinal numbers as words
194+
else: [x: "*[2]", CT: "-dels"] # this is wrong but shouldn't occur normally -- need to write a function to compute cardinal numbers as words
188195

189196
# NORWEGIAN: Changed to English version, but changed decimal point to $DecimalSeparators
190197
- name: fraction-over-simple # audit-ignore

‎Rules/Languages/nb/SharedRules/general.yaml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,8 @@
316316
IfThenElse($SpeechStyle='ClearSpeak',
317317
$ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac',
318318
$SpeechStyle='SimpleSpeak') and
319-
*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and
320-
*[2][self::m:mn][not(contains(., $DecimalSeparators)) and (2<= text() and text()<=10)]
319+
*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<100)] and
320+
*[2][self::m:mn][not(contains(., $DecimalSeparators)) and (2<= text() and text()<=100)]
321321
) or
322322
(self::m:mrow and *[2][self::m:mo and .='\u2064'])
323323
]

‎Rules/Languages/nb/SimpleSpeak_Rules.yaml‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,11 @@
7272
- name: common-fraction # audit-ignore
7373
tag: fraction
7474
match:
75-
- "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and text()<20] and" # NORWEGIAN: Changed decimal point to $DecimalSeparators
76-
- "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and 2<= text() and text()<=10]"
75+
- "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and text()<100] and" # NORWEGIAN: Changed decimal point to $DecimalSeparators
76+
- "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and 2<= text() and text()<=100]"
7777
variables:
7878
- IsPlural: "not(*[1]=1)"
79+
- PluralDenominator: "ToOrdinal(*[2], true(), true())"
7980
- UnitSibling: "IfThenElse(preceding-sibling::*[1][.='\u2064'], .., .)"
8081
# set the potential unit node -- otherwise set it to a empty node set (use a made up name)
8182
- UnitNodeLocation: "IfThenElse($UnitSibling/..[self::m:mrow and count(*)=3 and *[2][.='\u2062']],
@@ -99,7 +100,12 @@
99100
- if: "not($UnitNode)"
100101
then:
101102
- x: "*[1]"
102-
- x: "ToOrdinal(*[2], true(), $IsPlural)" # extra args specify fractional ordinal and whether it is plural
103+
- test:
104+
- if: "*[1]=1 and *[2] > 19"
105+
then:
106+
- x: "translate(substring($PluralDenominator, 1, string-length($PluralDenominator) - 2), ' ', '')"
107+
- else:
108+
- x: "translate(ToOrdinal(*[2], true(), $IsPlural), ' ', '')"
103109
# unit follows the fraction -- special cases
104110
- else:
105111
- test:
@@ -114,7 +120,7 @@
114120
else: [T: "halv"]
115121
- else_if: "*[2] <= 10"
116122
then: [x: "DefinitionValue(*[2], 'Speech', 'OrdinalFractionalDenominatorsForUnits')"]
117-
else: [x: "*[2]", CT: "dels"] # this is wrong but shouldn't occur normally -- need to write a function to compute cardinal numbers as words
123+
else: [x: "*[2]", CT: "-dels"] # this is wrong but shouldn't occur normally -- need to write a function to compute cardinal numbers as words
118124

119125
# Units (e.g., meters per second, m^2/s^2, (3m^2)/s)
120126
# NORWEGIAN: Added rule for t="time"

‎Rules/Languages/nb/definitions.yaml‎

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,8 @@
420420

421421
# stop when regularity begins
422422
- OrdinalFractionalDenominatorsForUnits: {
423-
"0": "", "1": "", "2": "todels", "3": "tredjedels", "4": "fjerdedels", "5": "femtedels",
424-
"6": "sjettedels", "7": "sjudels", "8": "åttedels", "9": "nidels", "10": "tidels",
423+
"0": "", "1": "", "2": "todels", "3": "tredels", "4": "firedels", "5": "femdels",
424+
"6": "seksdels", "7": "sjudels", "8": "åttedels", "9": "nidels", "10": "tidels",
425425
}
426426

427427
# ---------------- Cardinal and Ordinal Numbers --------------------------
@@ -442,15 +442,8 @@
442442
# The definitions for the "ones" should extend until a regular pattern begins
443443
# The minimum length is 10.
444444

445-
# NORWEGIAN: The pattern for fraction is not correct.
446-
# 1/20 = "1 tjuende". Needs a number set for NumbersOrdinalFractionalTens. ("tidel", "tjuedel"...)
447-
# 2/24 = "2 tjue fjerde". The set NumbersOrdinalPluralOnes should be changed to ("", "endeler", "todeler", "tredeler"...)
448-
# 1/100 = "1 hundrede". Needs a number set for NumbersOrdinalFractionalHundreds". ("hundredel", "to hundredel"...)
449-
# 1/1000 = "1 én tusende". Needs number set for NumbersOrdinalFractionalLargeNumbers, and "én" should be removed. ("tusendel", "milliondel"...)
450-
# 2/1000 = "2 én tusendeler". Needs to remove "én".
451-
# 2/1008 = "2 én tusen åttende"
452-
453445
# For English, a regular pattern starts at twenty
446+
454447
# NORWEGIAN: Added in front of the numbers in "NumbersOrdinalLarge" and "NumbersLarge". Easiest to just remove "én", but you will probably never see these big ordinal numbers.
455448
- NumbersOnes: [
456449
"null", "én", "to", "tre", "fire", "fem", "seks", "sju", "åtte", "ni",
@@ -464,22 +457,24 @@
464457
"syttende", "attende", "nittende"
465458
]
466459

467-
# NORWEGIAN: Should be changed to "", "endeler", "todeler"...Then, for example, the fraction 2/24 will be "2 tjue firedeler"
460+
# Norwegian: Used for the last part in words like "femtiseksdeler"
468461
- NumbersOrdinalPluralOnes: [
469-
"nullte", "første", "andre", "tredje", "fjerde", "femte", "sjette", "sjuende", "åttende", "niende",
470-
"tiende", "ellevte", "tolvte", "trettende", "fjortende", "femtende", "sekstende",
471-
"syttende", "attende", "nittende"
462+
"nulldeler", "endeler", "todeler", "tredeler", "firedeler", "femdeler", "seksdeler", "sjudeler", "åttedeler", "nideler",
472463
]
473464

474465
# stop when regularity begins
466+
467+
# Norwegian: Used for common-fractions where the numerator is 1, and the denominator is 1-19.
475468
- NumbersOrdinalFractionalOnes: [
476-
"nulldel", "hel", "halv", "tredjedel", "fjerdedel", "femtedel", "sjettedel", "sjudel", "åttedel", "nidel", "tidel", "ellevedel",
469+
"nulldel", "hel", "halv", "tredel", "firedel", "femdel", "seksdel", "sjudel", "åttedel", "nidel", "tidel", "ellevedel",
477470
"tolvdel", "trettendel", "fjortendel", "femtendel", "sekstendel", "syttendel", "attendel", "nittendel"
478471
]
479472

480473
# stop when regularity begins
474+
475+
# Norwegian: Used for common-fractions where the denominator is 1-19.
481476
- NumbersOrdinalFractionalPluralOnes: [
482-
"nulldeler", "hele", "halve", "tredjedeler", "fjerdedeler", "femtedeler", "sjettedeler", "sjudeler", "åttedeler", "nideler",
477+
"nulldeler", "hele", "halve", "tredeler", "firedeler", "femdeler", "seksdeler", "sjudeler", "åttedeler", "nideler",
483478
"tideler", "ellevedeler", "tolvdeler", "trettendeler", "fjortendeler", "femtendeler", "sekstendeler",
484479
"syttendeler", "attendeler", "nittendeler"
485480
]

‎Rules/Languages/nb/unicode-full.yaml‎

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -499,20 +499,20 @@
499499
- "ⅎ": [T: "rotert f"] # 0x214e (en: 'turned F', google: 'vände f')
500500
#--------------------------------------------------------------------------------------------------------------
501501
#Number formats OK
502-
- "¼": [T: "én fjerdedel"] # 0x00bc
502+
- "¼": [T: "én firedel"] # 0x00bc
503503
- "½": [T: "én halv"] # 0x00bd
504-
- "¾": [T: "tre fjerdedeler"] # 0x00be
504+
- "¾": [T: "tre firedeler"] # 0x00be
505505
- "⅐": [T: "én sjudel"] # 0x2150 (en: 'one seventh', google: 'en sjunde')
506506
- "⅑": [T: "én nidel"] # 0x2151 (en: 'one ninth', google: 'en nionde')
507507
- "⅒": [T: "én tidel"] # 0x2152 (en: 'one tenth')
508-
- "⅓": [T: "én tredjedel"] # 0x2153 (en: 'one third')
509-
- "⅔": [T: "to tredjedeler"] # 0x2154 (en: 'two thirds')
510-
- "⅕": [T: "én femtedel"] # 0x2155 (en: 'one fifth')
511-
- "⅖": [T: "to femtedeler"] # 0x2156 (en: 'two fifths')
512-
- "⅗": [T: "tre femtedeler"] # 0x2157 (en: 'three fifths')
513-
- "⅘": [T: "fire femtedeler"] # 0x2158 (en: 'four fifths')
514-
- "⅙": [T: "én sjettedel"] # 0x2159 (en: 'one sixth')
515-
- "⅚": [T: "fem sjettedeler"] # 0x215a (en: 'five sixths')
508+
- "⅓": [T: "én tredel"] # 0x2153 (en: 'one third')
509+
- "⅔": [T: "to tredeler"] # 0x2154 (en: 'two thirds')
510+
- "⅕": [T: "én femdel"] # 0x2155 (en: 'one fifth')
511+
- "⅖": [T: "to femdeler"] # 0x2156 (en: 'two fifths')
512+
- "⅗": [T: "tre femdeler"] # 0x2157 (en: 'three fifths')
513+
- "⅘": [T: "fire femdeler"] # 0x2158 (en: 'four fifths')
514+
- "⅙": [T: "én seksdel"] # 0x2159 (en: 'one sixth')
515+
- "⅚": [T: "fem seksdeler"] # 0x215a (en: 'five sixths')
516516
- "⅛": [T: "én åttedel"] # 0x215b (en: 'one eighth')
517517
- "⅜": [T: "tre åttedeler"] # 0x215c (en: 'three eighths')
518518
- "⅝": [T: "fem åttedeler"] # 0x215d (en: 'five eighths')
@@ -550,7 +550,7 @@
550550
- "ⅽ": [T: "c"] # 0x217d (en: 'C', google translation)
551551
- "ⅾ": [T: "d"] # 0x217e (en: 'D', google translation)
552552
- "ⅿ": [T: "m"] # 0x217f (en: 'M', google translation)
553-
- "↉": [T: "null tredjedeler"] # 0x2189 (en: 'zero thirds')
553+
- "↉": [T: "null tredeler"] # 0x2189 (en: 'zero thirds')
554554
#-----------------------------------------------------------------------------------------------------
555555
#Arrows OK
556556
- "←": [T: "venstrepil"] # 0x2190 (en: 'leftwards arrow')

‎tests/Languages/nb/ClearSpeak/mfrac.rs‎

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ fn common_fraction_thirds() -> Result<()> {
1919
let expr = "<math>
2020
<mfrac> <mn>2</mn> <mn>3</mn> </mfrac>
2121
</math>";
22-
test("nb", "ClearSpeak", expr, "2 tredjedeler")?;
22+
test("nb", "ClearSpeak", expr, "2 tredeler")?;
2323
return Ok(());
2424

2525
}
@@ -41,7 +41,7 @@ fn not_ClearSpeak_common_fraction_tenths() -> Result<()> {
4141
let expr = "<math>
4242
<mfrac> <mn>89</mn> <mn>10</mn> </mfrac>
4343
</math>";
44-
test_prefs("nb", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "89 over 10")?;
44+
test_prefs("nb", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "89 tideler")?;
4545
test_prefs("nb", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "89 tideler")?;
4646
return Ok(());
4747

@@ -121,7 +121,7 @@ fn mixed_number_big() -> Result<()> {
121121
<mn>3</mn>
122122
<mfrac> <mn>7</mn> <mn>83</mn> </mfrac>
123123
</math>";
124-
test("nb", "ClearSpeak", expr, "3 og 7 over 83")?;
124+
test("nb", "ClearSpeak", expr, "3 og 7 åttitredeler")?;
125125
return Ok(());
126126

127127
}
@@ -172,14 +172,14 @@ fn nested_simple_fractions() -> Result<()> {
172172
</mfrac>
173173
</mrow>
174174
</math>";
175-
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "Auto")], expr, "1 halv over 2 tredjedeler")?;
176-
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "Ordinal")], expr, "1 halv over 2 tredjedeler")?;
175+
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "Auto")], expr, "1 halv over 2 tredeler")?;
176+
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "Ordinal")], expr, "1 halv over 2 tredeler")?;
177177
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "Over")], expr, "1 over 2, over 2 over 3")?;
178178
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "FracOver")], expr,
179179
"brøken brøken 1 over 2, over brøken 2 over 3")?;
180180
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "General")], expr,
181181
"brøken med teller brøken med teller 1; og nevner 2; og nevner brøken med teller 2; og nevner 3")?;
182-
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "EndFrac")], expr, "1 halv over 2 tredjedeler")?;
182+
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "EndFrac")], expr, "1 halv over 2 tredeler")?;
183183
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "GeneralEndFrac")], expr,
184184
"brøken med teller brøken med teller 1; og nevner 2; slutt brøk; og nevner brøken med teller 2; og nevner 3; slutt brøk; slutt brøk")?;
185185
test_prefs("nb", "ClearSpeak", vec![("ClearSpeak_Fractions", "OverEndFrac")], expr,
@@ -205,7 +205,7 @@ fn semi_nested_fraction() -> Result<()> {
205205
</mfrac>
206206
</mrow>
207207
</math>";
208-
test("nb", "ClearSpeak", expr, "2 tredjedeler x over 6")?;
208+
test("nb", "ClearSpeak", expr, "2 tredeler x over 6")?;
209209
return Ok(());
210210

211211
}
@@ -319,7 +319,7 @@ fn mixed_number_one() -> Result<()> {
319319
<mn>1</mn>
320320
<mfrac><mn>2</mn><mn>3</mn></mfrac>
321321
</math>"#;
322-
test("nb", "ClearSpeak", expr, "1 og 2 tredjedeler")?;
322+
test("nb", "ClearSpeak", expr, "1 og 2 tredeler")?;
323323
return Ok(());
324324

325325
}
@@ -331,7 +331,106 @@ fn mixed_number_two() -> Result<()> {
331331
<mn>2</mn>
332332
<mfrac><mn>4</mn><mn>4</mn></mfrac>
333333
</math>"#;
334-
test("nb", "ClearSpeak", expr, "2 og 4 fjerdedeler")?;
334+
test("nb", "ClearSpeak", expr, "2 og 4 firedeler")?;
335+
return Ok(());
336+
337+
}
338+
339+
#[test]
340+
fn common_fraction_plural() -> Result<()> {
341+
let expr = r#"
342+
<math>
343+
<mfrac><mn>3</mn><mn>52</mn></mfrac>
344+
</math>"#;
345+
test("nb", "ClearSpeak", expr, "3 femtitodeler")?;
346+
return Ok(());
347+
348+
}
349+
350+
#[test]
351+
fn common_fraction_singular() -> Result<()> {
352+
let expr = r#"
353+
<math>
354+
<mfrac><mn>1</mn><mn>52</mn></mfrac>
355+
</math>"#;
356+
test("nb", "ClearSpeak", expr, "1 femtitodel")?;
357+
return Ok(());
358+
359+
}
360+
361+
#[test]
362+
fn common_fraction_plural_2() -> Result<()> {
363+
let expr = r#"
364+
<math>
365+
<mfrac><mn>21</mn><mn>52</mn></mfrac>
366+
</math>"#;
367+
test("nb", "ClearSpeak", expr, "21 femtitodeler")?;
368+
return Ok(());
369+
370+
}
371+
372+
#[test]
373+
fn common_fraction_plural_3() -> Result<()> {
374+
let expr = r#"
375+
<math>
376+
<mfrac><mn>99</mn><mn>100</mn></mfrac>
377+
</math>"#;
378+
test("nb", "ClearSpeak", expr, "99 hundredeler")?;
379+
return Ok(());
380+
381+
}
382+
383+
#[test]
384+
fn common_fraction_singular_hundred() -> Result<()> {
385+
let expr = r#"
386+
<math>
387+
<mfrac><mn>1</mn><mn>100</mn></mfrac>
388+
</math>"#;
389+
test("nb", "ClearSpeak", expr, "1 hundredel")?;
390+
return Ok(());
391+
392+
}
393+
394+
#[test]
395+
fn common_fraction_singular_ten() -> Result<()> {
396+
let expr = r#"
397+
<math>
398+
<mfrac><mn>1</mn><mn>20</mn></mfrac>
399+
</math>"#;
400+
test("nb", "ClearSpeak", expr, "1 tjuedel")?;
401+
return Ok(());
402+
403+
}
404+
405+
#[test]
406+
fn fraction_singular_large_denominator() -> Result<()> {
407+
let expr = r#"
408+
<math>
409+
<mfrac><mn>1</mn><mn>103</mn></mfrac>
410+
</math>"#;
411+
test("nb", "ClearSpeak", expr, "1 over 103")?;
412+
return Ok(());
413+
414+
}
415+
416+
#[test]
417+
fn fraction_plural_large_denominator() -> Result<()> {
418+
let expr = r#"
419+
<math>
420+
<mfrac><mn>4</mn><mn>200</mn></mfrac>
421+
</math>"#;
422+
test("nb", "ClearSpeak", expr, "4 over 200")?;
423+
return Ok(());
424+
425+
}
426+
427+
#[test]
428+
fn fraction_plural_large_numerator() -> Result<()> {
429+
let expr = r#"
430+
<math>
431+
<mfrac><mn>103</mn><mn>82</mn></mfrac>
432+
</math>"#;
433+
test("nb", "ClearSpeak", expr, "103 over 82")?;
335434
return Ok(());
336435

337436
}

‎tests/Languages/nb/ClearSpeak/mroot.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ fn simple_fraction_power() -> Result<()> {
157157
<mfrac><mn>1</mn><mn>3</mn></mfrac>
158158
</mroot>
159159
</math>";
160-
test("nb", "ClearSpeak", expr, "roten av grad 1 tredjedel, av x")?;
160+
test("nb", "ClearSpeak", expr, "roten av grad 1 tredel, av x")?;
161161
return Ok(());
162162

163163
}

0 commit comments

Comments
 (0)