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 = "2122"; - 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 = "3128"; + 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 = "xy"; + test("ja", "ClearSpeak", expr, "x オーバー y")?; + test("ja", "SimpleSpeak", expr, "x オーバー y")?; return Ok(()); }