diff --git a/Rules/Languages/ja/ClearSpeak_Rules.yaml b/Rules/Languages/ja/ClearSpeak_Rules.yaml
index e35ed066..c6132440 100644
--- a/Rules/Languages/ja/ClearSpeak_Rules.yaml
+++ b/Rules/Languages/ja/ClearSpeak_Rules.yaml
@@ -139,19 +139,21 @@
- " BaseNode(*[2])[contains(@data-intent-property, ':unit')] )"
replace:
- x: "*[1]"
- - t: "パーカー" # phrase('5 meters 'per' second)
+ - t: "毎" # phrase('5 meters 'per' second)
- x: "*[2]"
- name: common-fraction
tag: fraction
match:
- "($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and"
- - "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and"
- - "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))]"
- variables: [IsPlural: "*[1]!=1"]
+ - "*[1][self::m:mn][not(contains(., $DecimalSeparators))] and"
+ - "*[2][self::m:mn][not(contains(., $DecimalSeparators))]"
replace:
+ # Japanese says the denominator first: 3/4 is "4 分の 3". There is no separate ordinal
+ # form to build (unlike "three fourths"), so the pattern is uniform for any two numbers.
+ - x: "*[2]"
+ - t: "分の" # phrase(the fraction 3 'over' 4)
- x: "*[1]"
- - x: "ToOrdinal(*[2], true(), $IsPlural)" # extra args specify fractional ordinal and whether it is plural
- name: fraction-over-simple
tag: fraction
@@ -168,14 +170,14 @@
then: [ot: ""]
- t: "分数" # phrase(the 'fraction' with 3 over 4)
- x: "*[1]"
- - t: "分の" # phrase(the fraction 3 'over' 4)
+ - t: "オーバー" # phrase(the fraction 3 'over' 4)
- x: "*[2]"
- test:
# very ugly!!! -- replicate nested ordinal fraction as they are an exception
- if: "$ClearSpeak_Fractions='OverEndFrac' or ($ClearSpeak_Fractions='EndFrac' and not( ($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and *[1][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] and *[2][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] ) )"
+ if: "$ClearSpeak_Fractions='OverEndFrac' or ($ClearSpeak_Fractions='EndFrac' and not( ($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and *[1][*[1][self::m:mn][not(contains(., '.'))] and *[2][self::m:mn][not(contains(., '.'))] ] and *[2][*[1][self::m:mn][not(contains(., '.'))] and *[2][self::m:mn][not(contains(., '.'))] ] ) )"
then:
- pause: short
- - t: "分数終わり" # phrase(7 over 8 'end fraction')
+ - t: "分数終了" # phrase(7 over 8 'end fraction')
- pause: short
- # fraction with text or numbers followed by text in both numerator and denominator
@@ -197,13 +199,13 @@
- ")"
replace:
- x: "*[1]"
- - t: "分の" # phrase(the fraction 3 'over' 4)
+ - t: "オーバー" # phrase(the fraction 3 'over' 4)
- x: "*[2]"
- test:
if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='OverEndFrac'"
then:
- pause: short
- - t: "分数終わり" # phrase(7 over 8 'end fraction')
+ - t: "分数終了" # phrase(7 over 8 'end fraction')
- pause: short
- name: default
@@ -211,20 +213,20 @@
match: "."
replace:
- ot: "" # phrase(5 is 'the' square toot of 25)
- - t: "分子 との 分数" # phrase(the 'fraction with numerator' 6)
+ - t: "分数" # phrase(the 'fraction with numerator' 6)
- test:
if: "not(IsNode(*[1], 'simple'))"
then: [pause: medium]
- x: "*[1]"
- pause: medium
- - t: "デノミネーター" # phrase(the fraction with numerator 5 'and denominator' 8)
+ - t: "オーバー" # phrase(the fraction with numerator 5 'and denominator' 8)
- x: "*[2]"
- pause: long
- test:
if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='GeneralEndFrac'"
then:
- pause: short
- - t: "分数終わり" # phrase(the fraction with 3 over 4 'end fraction')
+ - t: "分数終了" # phrase(the fraction with 3 over 4 'end fraction')
- pause: short
# rules for functions raised to a power
@@ -367,8 +369,8 @@
- " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3
# base is mn, mi, common fraction ([xxx] case)
- " *[1][self::m:mn or self::m:mi or "
- - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
- - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]"
+ - " self::m:fraction[*[1][self::m:mn][not(contains(., '.'))] and"
+ - " *[2][self::m:mn][not(contains(., '.'))]]"
- " ]"
- " ]"
replace:
@@ -387,8 +389,8 @@
- " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3"
# base is mn, mi, common fraction ([xxx] case)
- " *[1][self::m:mn or self::m:mi or "
- - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
- - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]"
+ - " self::m:fraction[*[1][self::m:mn][not(contains(., '.'))] and"
+ - " *[2][self::m:mn][not(contains(., '.'))]]"
- " ]"
- " ]"
- " ]"
@@ -411,8 +413,8 @@
- " *[2][self::m:mo][.=''] and " # invisible times
# base is mn, or common fraction ([xxx] case)
- " *[1][self::m:mn or "
- - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
- - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]"
+ - " self::m:fraction[*[1][self::m:mn][not(contains(., '.'))] and"
+ - " *[2][self::m:mn][not(contains(., '.'))]]"
- " ]"
- " ]"
replace:
@@ -435,8 +437,8 @@
- " *[1][self::m:minus and count(*)=1 and "
# base is mn, or common fraction ([xxx] case)
- " *[1][self::m:mn or "
- - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
- - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]"
+ - " self::m:fraction[*[1][self::m:mn][not(contains(., '.'))] and"
+ - " *[2][self::m:mn][not(contains(., '.'))]]"
- " ]"
- " ]"
- " ]"
diff --git a/Rules/Languages/ja/SimpleSpeak_Rules.yaml b/Rules/Languages/ja/SimpleSpeak_Rules.yaml
index 235bc266..53301276 100644
--- a/Rules/Languages/ja/SimpleSpeak_Rules.yaml
+++ b/Rules/Languages/ja/SimpleSpeak_Rules.yaml
@@ -83,12 +83,13 @@
- name: common-fraction
tag: fraction
match:
- - "*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
- - "*[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]"
- variables: [IsPlural: "*[1]!=1"]
+ - "*[1][self::m:mn][not(contains(., '.'))] and"
+ - "*[2][self::m:mn][not(contains(., '.'))]"
replace:
+ # Japanese says the denominator first: 3/4 is "4 分の 3".
+ - x: "*[2]"
+ - t: "分の" # phrase(the fraction 3 'over' 4)
- x: "*[1]"
- - x: "ToOrdinal(*[2], true(), $IsPlural)" # extra args specify fractional ordinal and whether it is plural
- name: common-fraction-mixed-number
tag: fraction
@@ -96,10 +97,10 @@
- "preceding-sibling::*[1][self::m:mo][.=''] and" # preceding element is invisible plus
- "*[1][self::m:mn][not(contains(., '.'))] and"
- "*[2][self::m:mn][not(contains(., '.'))]"
- variables: [IsPlural: "*[1]!=1"]
replace:
+ - x: "*[2]"
+ - t: "分の" # phrase(the fraction 3 'over' 4)
- x: "*[1]"
- - x: "ToOrdinal(*[2], true(), $IsPlural)" # extra args specify fractional ordinal and whether it is plural
# Units (e.g., meters per second, m^2/s^2, (3m^2)/s)
@@ -112,7 +113,7 @@
- "BaseNode(*[2])[contains(@data-intent-property, ':unit')] "
replace:
- x: "*[1]"
- - t: "パーカー" # phrase('5 meters 'per' second)
+ - t: "毎" # phrase('5 meters 'per' second)
- x: "*[2]"
- name: simple
@@ -125,7 +126,7 @@
- "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)" # FIX: can't test for mrow -- what should be used???
replace:
- x: "*[1]"
- - t: "分の" # phrase(the fraction 3 'over' 4)
+ - t: "オーバー" # phrase(the fraction 3 'over' 4)
- x: "*[2]"
- pause: short
@@ -141,7 +142,7 @@
- test:
if: "not(IsNode(*[1],'leaf'))"
then: [pause: short]
- - t: "分の" # phrase(the fraction 3 'over' 4)
+ - t: "オーバー" # phrase(the fraction 3 'over' 4)
- test:
if: "not(IsNode(*[2],'leaf'))"
then: [pause: short]
@@ -149,7 +150,7 @@
- pause: short
- test:
if: "$Impairment = 'Blindness'"
- then: [t: "分数終わり"] # phrase(start 7 over 8 'end of fraction')
+ then: [t: "分数終了"] # phrase(start 7 over 8 'end of fraction')
- pause: medium
# rules for functions raised to a power
diff --git a/tests/Languages/ja/ja.rs b/tests/Languages/ja/ja.rs
index 44f4c2f2..31373da4 100644
--- a/tests/Languages/ja/ja.rs
+++ b/tests/Languages/ja/ja.rs
@@ -9,11 +9,33 @@ fn arithmetic_operators() -> Result<()> {
return Ok(());
}
-/// Verifies that a simple fraction is spoken with Japanese fraction wording.
+/// A fraction of two numbers is read denominator-first in Japanese:
+/// 21/22 is "22 分の 21", literally "of 22, 21". Reading it the other way round
+/// says 22/21.
#[test]
fn simple_fraction() -> Result<()> {
let expr = "";
- test("ja", "ClearSpeak", expr, "21 分の 22")?;
+ test("ja", "ClearSpeak", expr, "22 分の 21")?;
+ test("ja", "SimpleSpeak", expr, "22 分の 21")?;
+ return Ok(());
+}
+
+/// The denominator-first pattern is not limited to the small numbers that English
+/// has ordinals for ("three fourths"); it is how any two numbers are read.
+#[test]
+fn numeric_fraction_large_denominator() -> Result<()> {
+ let expr = "";
+ test("ja", "ClearSpeak", expr, "128 分の 3")?;
+ return Ok(());
+}
+
+/// When the parts are not plain numbers, Japanese keeps the written order and
+/// borrows the English preposition as "オーバー" instead (Yamaguchi et al. 1996).
+#[test]
+fn fraction_of_variables() -> Result<()> {
+ let expr = "";
+ test("ja", "ClearSpeak", expr, "x オーバー y")?;
+ test("ja", "SimpleSpeak", expr, "x オーバー y")?;
return Ok(());
}