diff --git a/Rules/Braille/Russian/Russian_Rules.yaml b/Rules/Braille/Russian/Russian_Rules.yaml index ee9004aed..1bf1603c9 100644 --- a/Rules/Braille/Russian/Russian_Rules.yaml +++ b/Rules/Braille/Russian/Russian_Rules.yaml @@ -1555,6 +1555,32 @@ replace: - x: "DefinitionValue(., 'Braille', 'RussianFunctionBraille')" +- + name: contextual-square-bracket-recognition-point + tag: mo + match: + - "parent::*[IsBracketed(., '[', ']')] and" + - "((.='[' and not(preceding-sibling::*) and following-sibling::*[1][self::m:mi or self::m:mtext]) or" + - " (.=']' and not(following-sibling::*) and preceding-sibling::*[1][self::m:mi or self::m:mtext]))" + replace: + - T: "⠠" + - x: "text()" + +- + name: contextual-angle-bracket-recognition-point + tag: mo + match: + - "parent::*[IsBracketed(., '⟨', '⟩')] and" + - "((.='⟨' and not(preceding-sibling::*)) or (.='⟩' and not(following-sibling::*))) and" + - "(parent::*/*[2][self::m:mi or self::m:mtext] or" + - " parent::*/*[last()-1][self::m:mi or self::m:mtext])" + replace: + - T: "⠈" + - test: + - if: ".='⟨'" + then: [T: "⠪"] + else: [T: "⠕"] + - name: div-operator-function tag: mo diff --git a/Rules/Braille/Russian/unicode.yaml b/Rules/Braille/Russian/unicode.yaml index 6b08895b9..4fc43ea9d 100644 --- a/Rules/Braille/Russian/unicode.yaml +++ b/Rules/Braille/Russian/unicode.yaml @@ -214,6 +214,8 @@ - "≢": [t: "⠀⠰⠾"] - "≤": [t: "⠀⠪⠶"] - "≥": [t: "⠀⠕⠶"] +- "≶": [T: "⠀⠪⠕⠀"] +- "≷": [T: "⠀⠕⠪⠀"] - "≪": [t: "⠀⠪⠪⠀"] - "≫": [t: "⠀⠕⠕⠀"] - "≺": [t: "⠀⠒⠪⠀"] @@ -238,6 +240,10 @@ - "⋰": [t: "⠠⠲⠲⠲"] - "⋱": [t: "⠠⠲⠲⠲"] - "△": [t: "⠸⠙"] +- "⟨": [T: "⠪"] +- "⟩": [T: "⠕"] +- "↑": [T: "⠰⠌"] +- "↓": [T: "⠘⠡"] - "→": [t: "⠀⠒⠕"] - "⟶": [t: "⠀⠒⠕"] - "←": [t: "⠀⠦⠶"] diff --git a/Rules/Languages/ru/ClearSpeak_Rules.yaml b/Rules/Languages/ru/ClearSpeak_Rules.yaml index c0955204c..0815c96c5 100644 --- a/Rules/Languages/ru/ClearSpeak_Rules.yaml +++ b/Rules/Languages/ru/ClearSpeak_Rules.yaml @@ -760,6 +760,24 @@ - pause: short - x: "*[3]" +# Handle log and ln when they remain individual mi elements rather than the +# canonical three-child function-application mrow handled by the next rule. +- name: ClearSpeak-log + tag: mi + match: ".='log' or .='ln'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity!='Terse' and following-sibling::*[2] and + not(IsNode(following-sibling::*[2],'simple'))" + then: [T: "от"] + - test: + - if: ".='log'" + then: [T: "логарифм"] + - else_if: "$ClearSpeak_Log = 'LnAsNaturalLog'" + then: [T: "натуральный логарифм"] + else: [T: "эл эн"] + - name: ClearSpeak-log tag: mrow variables: [{log_is_simple: "IsNode(*[3],'simple')"}] diff --git a/Rules/Languages/ru/navigate.yaml b/Rules/Languages/ru/navigate.yaml index 527dcce60..01bc51a76 100644 --- a/Rules/Languages/ru/navigate.yaml +++ b/Rules/Languages/ru/navigate.yaml @@ -91,6 +91,15 @@ # Rules for speaking what happens when moving into or out of a notation +# Prefix/silent intents with no NavigationParts entry (for example, unary +# minus, a vector, or a modified variable) should not announce "in"/"out of". +- name: into-or-out-of-prefix-or-silent-without-parts + tag: "*" + match: "$Move2D != '' and + (contains(@data-intent-property, ':prefix:') or contains(@data-intent-property, ':silent:')) and + GetNavigationPartName(name(.), count($Child2D/preceding-sibling::*)) = ''" + replace: [] + - name: into-or-out-of-mfrac tag: "*" match: "$Move2D != '' and (@data-from-mathml='mfrac' or self::m:mfrac)" diff --git a/tests/Languages/ru.rs b/tests/Languages/ru.rs index 397e29a08..97edff4ee 100644 --- a/tests/Languages/ru.rs +++ b/tests/Languages/ru.rs @@ -31,4 +31,5 @@ mod units; mod chemistry; mod alphabets; mod intent; -mod mtable; \ No newline at end of file +mod mtable; +mod navigate; diff --git a/tests/Languages/ru/ClearSpeak/functions.rs b/tests/Languages/ru/ClearSpeak/functions.rs index 941e694d8..5171150f6 100644 --- a/tests/Languages/ru/ClearSpeak/functions.rs +++ b/tests/Languages/ru/ClearSpeak/functions.rs @@ -144,6 +144,22 @@ fn natural_log() -> Result<()> { return Ok(()); } +#[test] +fn individual_log_mi() -> Result<()> { + let expr = "log"; + test("ru", "ClearSpeak", expr, "логарифм")?; + return Ok(()); +} + +#[test] +fn individual_ln_mi() -> Result<()> { + let expr = "ln"; + test("ru", "ClearSpeak", expr, "эл эн")?; + test_ClearSpeak("ru", "ClearSpeak_Log", "LnAsNaturalLog", expr, + "натуральный логарифм")?; + return Ok(()); +} + #[test] fn explicit_function_call_with_parens() -> Result<()> { let expr = "t(x)"; diff --git a/tests/Languages/ru/navigate.rs b/tests/Languages/ru/navigate.rs new file mode 100644 index 000000000..690df9eff --- /dev/null +++ b/tests/Languages/ru/navigate.rs @@ -0,0 +1,56 @@ +//! Russian navigation speech for prefix/silent intents without NavigationParts. + +use crate::common::*; +use anyhow::Result; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +fn init_nav(mathml: &str) -> Result<()> { + set_rules_dir(abs_rules_dir_path())?; + set_preference("Language", "ru")?; + set_preference("SpeechStyle", "SimpleSpeak")?; + set_preference("Verbosity", "Medium")?; + set_preference("NavMode", "Enhanced")?; + set_preference("NavVerbosity", "Verbose")?; + set_preference("AutoZoomOut", "False")?; + set_preference("Overview", "False")?; + set_mathml(mathml)?; + Ok(()) +} + +fn assert_zoom_in(mathml: &str, expected: &str) -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + init_nav(mathml)?; + let speech = do_navigate_command("ZoomIn")?; + let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); + assert_eq!(expected, trimmed_speech); + Ok(()) + })); + report_any_panic(result) +} + +#[test] +fn no_parts_prefix_vector_suppresses_base_announcement() -> Result<()> { + let expr = r#" + + + v + + + + "#; + assert_zoom_in(expr, "переход внутрь; вэ") +} + +#[test] +fn no_parts_silent_modified_variable_suppresses_base_announcement() -> Result<()> { + let expr = r#" + + + x + ^ + + + "#; + assert_zoom_in(expr, "переход внутрь; икс") +} diff --git a/tests/braille/Russian/russian.rs b/tests/braille/Russian/russian.rs index 5138fb374..1b40f13b7 100644 --- a/tests/braille/Russian/russian.rs +++ b/tests/braille/Russian/russian.rs @@ -1387,6 +1387,35 @@ fn source_less_common_math_symbols_11() -> Result<()> { return test_russian_braille(r#"ab"#, "⠠⠁⠀⠼⠀⠃"); } +#[test] +fn source_gost_order_relations_greater_or_less() -> Result<()> { + return test_russian_braille(r#"xy"#, "⠠⠭⠀⠕⠪⠀⠽"); +} + +#[test] +fn source_gost_order_relations_less_or_greater() -> Result<()> { + return test_russian_braille(r#"xy"#, "⠠⠭⠀⠪⠕⠀⠽"); +} + +#[test] +fn source_gost_large_vertical_arrows() -> Result<()> { + test_russian_braille(r#""#, "⠰⠌")?; + return test_russian_braille(r#""#, "⠘⠡"); +} + +#[test] +fn source_gost_contextual_square_bracket_recognition_points() -> Result<()> { + test_russian_braille(r#"[x]"#, "⠠⠷⠠⠭⠠⠾")?; + return test_russian_braille(r#"[1]"#, "⠷⠼⠁⠾"); +} + +#[test] +fn source_gost_contextual_angle_bracket_recognition_points() -> Result<()> { + test_russian_braille(r#"x"#, "⠈⠪⠠⠭⠈⠕")?; + test_russian_braille(r#"1"#, "⠪⠼⠁⠕")?; + return test_russian_braille(r#"x<y"#, "⠠⠭⠀⠪⠀⠽"); +} + #[test] fn source_typeform_and_mathvariant_indicators() -> Result<()> { let expr = r#"x"#;