From b01d5be42b26cd59cd1dc6de3074a816552761fc Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Wed, 30 Sep 2026 20:40:20 +0200 Subject: [PATCH 01/10] Rules/Languages/hu/definitions.yaml: add a new rule, based the english new definitions rule Signed-off-by: Attila Hammer --- Rules/Languages/hu/definitions.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Rules/Languages/hu/definitions.yaml b/Rules/Languages/hu/definitions.yaml index f070f72f..a089571b 100644 --- a/Rules/Languages/hu/definitions.yaml +++ b/Rules/Languages/hu/definitions.yaml @@ -351,6 +351,10 @@ ## Other : Not tested, don't appear in https://w3c.github.io/mathml-docs/intent-core-concepts/ "modified-variable": "silent= " } + # The connector word to say for functional 'IntentMappings' (in English, "of") + # This is likely the word used for U+2061 in unicode.yaml, but for technical reasons needs to be defined here + # vector of length=1 because scalars not supported +- FunctionApplicationWord: ["a(z)"] # Names of functions that in terse mode don't say "of" (or it's equivalent in other languages) - TerseFunctionNames: { From b34775dae38d8161c7f51aab5061445a04726c9d Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sat, 3 Oct 2026 19:33:59 +0200 Subject: [PATCH 02/10] Update tests, add navigation test and table separator test Signed-off-by: Attila Hammer --- tests/Languages/hu.rs | 2 +- tests/Languages/hu/navigate.rs | 274 +++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 tests/Languages/hu/navigate.rs diff --git a/tests/Languages/hu.rs b/tests/Languages/hu.rs index ac24dd22..b15d2705 100644 --- a/tests/Languages/hu.rs +++ b/tests/Languages/hu.rs @@ -32,4 +32,4 @@ mod alphabets; mod definitions; mod intent; mod mtable; - +mod navigate; diff --git a/tests/Languages/hu/navigate.rs b/tests/Languages/hu/navigate.rs new file mode 100644 index 00000000..3596cd28 --- /dev/null +++ b/tests/Languages/hu/navigate.rs @@ -0,0 +1,274 @@ +//! Navigation ZoomIn speech vs NavigationParts and intent fixity. +//! +//! Uses only real intents / MathML from `definitions.yaml` and `Rules/Intent/*.yaml` +//! (same shapes as other en language tests). Fixities covered are those that actually +//! appear there: prefix, infix, postfix, function, silent, nofix. + +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", "hu")?; + 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(command: &str, mathml: &str, expected: &str) -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + init_nav(mathml)?; + let speech = do_navigate_command(command)?; + let trimmed_speech = speech.trim_end_matches([' ', ',', ';']).to_string(); + assert_eq!(expected, trimmed_speech); + Ok(()) + })); + report_any_panic(result) +} + +// --- Intents in NavigationParts (prefix, infix, function, silent; no postfix/nofix) --- + +#[test] +fn parts_prefix_logarithm_with_base() -> Result<()> { + // Intent/general.yaml log-with-base → logarithm-with-base:prefix; parts "base" + let expr = r#" + + + log + b + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; b") +} + +#[test] +fn parts_infix_power() -> Result<()> { + // power:infix; parts "base; exponent" + let expr = r#" + + + x + 2 + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") +} + +#[test] +fn parts_infix_indexed_by() -> Result<()> { + // indexed-by:infix; parts "base; subscript" + let expr = r#" + + + x + 1 + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") +} + +#[test] +fn parts_function_fraction() -> Result<()> { + // fraction (from mfrac); parts "numerator; denominator" + let expr = r#" + + + 1 + 2 + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in számláló; 1") +} + +#[test] +fn parts_function_square_root() -> Result<()> { + // square-root:function; parts "root" + let expr = r#" + + + x + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in gyök; x") +} + +#[test] +fn parts_silent_skip_super() -> Result<()> { + // skip-super:silent (degree); parts "base; superscript" + let expr = r#" + + + x + ° + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") +} + +// --- Intents not in NavigationParts --- + +#[test] +fn no_parts_prefix_unary_minus() -> Result<()> { + // minus:prefix (Intent/general.yaml positive-or-negative) — silent "in" + let expr = r#" + + + - + b + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; b") +} + +#[test] +fn no_parts_prefix_limit() -> Result<()> { + // limit:prefix — silent "in" + let expr = r#" + + + x + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; x") +} + +#[test] +fn no_parts_prefix_vector() -> Result<()> { + // vector:prefix from mover + arrow + let expr = r#" + + + v + → + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; v") +} + +#[test] +fn no_parts_infix_binomial() -> Result<()> { + // binomial:infix (choose); no NavigationParts → "in part 1" + let expr = r#" + + + ( + + 7 + 3 + + ) + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in része 1; 7") +} + +#[test] +fn no_parts_postfix_transpose() -> Result<()> { + // transpose:postfix — not prefix, so still announces "in …" + let expr = r#" + + + M + T + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in transzponált; nagy m") +} + +#[test] +fn no_parts_function_absolute_value() -> Result<()> { + // absolute-value:function + let expr = r#" + + + | + x + | + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; in az abszolút érték; x") +} + +#[test] +fn no_parts_silent_modified_variable() -> Result<()> { + // modified-variable:silent (x-hat) — no NavigationParts → silent "in" + let expr = r#" + + + x + ^ + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; x") +} + +#[test] +fn no_parts_nofix_set_of_reals() -> Result<()> { + // set-of-reals:nofix — leaf + let expr = r#" + + R + + "#; + assert_zoom_in("ZoomIn", expr, "teljesen ráközelített; az összes valós szám halmaza") +} + +// --- No intent= attribute (native MathML only; may still be inferred) --- + +#[test] +fn no_intent_sum_mrow() -> Result<()> { + let expr = r#" + + + x + + + y + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; x") +} + +#[test] +fn no_intent_times_mrow() -> Result<()> { + let expr = r#" + + + 2 + ⁢ + a + + + "#; + assert_zoom_in("ZoomIn", expr, "nagyítás; 2") +} + +#[test] +fn no_intent_mi() -> Result<()> { + let expr = r#" + + x + + "#; + assert_zoom_in("ZoomIn", expr, "teljesen ráközelített; x") +} From b56ea85bd013081c002dce0c2ef7cd101c7baac6 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sat, 3 Oct 2026 20:16:51 +0200 Subject: [PATCH 03/10] Rules/Languages/hu/definitions.yaml: added missign navigation related fixes with not merged to the main branch Signed-off-by: Attila Hammer --- Rules/Languages/hu/definitions.yaml | 34 ++++++++++++++--------------- Rules/Languages/hu/navigate.yaml | 18 +++++++-------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/Rules/Languages/hu/definitions.yaml b/Rules/Languages/hu/definitions.yaml index a089571b..c72e0fac 100644 --- a/Rules/Languages/hu/definitions.yaml +++ b/Rules/Languages/hu/definitions.yaml @@ -363,30 +363,30 @@ - NavigationParts: { # These are the parts of a formula that can be navigated to - "large-op": "base; lower limit; upper limit", - "mfrac": "numerator; denominator", + "large-op": "alap; alsó határ; felső határ", + "mfrac": "számláló; nevező", "fraction": "számláló; nevező", "msqrt": "gyök", "square-root": "gyök", - "mroot": "root; root index", + "mroot": "gyök; gyök index", "root": "gyök; gyök index", - "msub": "base; subscript", - "sub": "base; subscript", - "logarithm-with-base": "base", - "indexed-by": "base; subscript", - "msup": "base; superscript", - "say-super": "base; superscript", - "skip-super": "base; superscript", - "power": "base; exponent", - "msubsup": "base; subscript; superscript", - "munder": "base; under limit", - "mover": "base; upper limit", - "munderover": "base; under limit; upper limit", + "msub": "alap; alsó index", + "sub": "alap; alsó index", + "logarithm-with-base": "alap logaritmus", + "indexed-by": "alap; alsó index", + "msup": "alap; felső index", + "say-super": "alap; felső index", + "skip-super": "alap; felső index", + "power": "alap; kitevő", + "msubsup": "alap; alsó index; felső index", + "munder": "alap; határérték alatt", + "mover": "alap; határérték felett", + "munderover": "alap; határérték alatt; határérték felett", # words for moving into and out of one of the parts (e.g., "move right 'out of' numerator, 'in' denominator") # it's a hack to put them here, but at least they are grouped with the other navigation parts - "in": "in", - "out": "out of", + "in": "ban/ben", + "out": "ki", } - KnownWords: { diff --git a/Rules/Languages/hu/navigate.yaml b/Rules/Languages/hu/navigate.yaml index 475e1f62..e98c0e77 100644 --- a/Rules/Languages/hu/navigate.yaml +++ b/Rules/Languages/hu/navigate.yaml @@ -56,30 +56,30 @@ then: [T: "teljesen ráközelített"] # phrase('zoomed in all of the way') - else_if: "$NavNodeOffset = 0" then: [T: "ráközelített az első karakterre"] # phrase('zoomed in to first character') - else: [T: "karakterre nagyítva"] # phrase('zoomed in to character') + else: [T: "ráközelített a karakterre"] # phrase('zoomed in to character') - pause: "medium" else: - test: # CommandOffset is 1 + length of the English NavCommand stem (Zoom/Move/Read/Describe), # not the spoken Prefix length — so translations can use a different Prefix word. - if: "starts-with($NavCommand, 'Zoom')" - then: [set_variables: [Prefix: "'zoom'", CommandOffset: "5"]] # phrase('zoom' in to see more details) + then: [set_variables: [Prefix: "'nagyítás'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" - then: [set_variables: [Prefix: "'move'", CommandOffset: "5"]] # phrase('move' to next entry in table) + then: [set_variables: [Prefix: "'ugrás'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'read'", CommandOffset: "5"]] # phrase('read' to next entry in table) + then: [set_variables: [Prefix: "'olvasás'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'describe'", CommandOffset: "9"]] # phrase('describe' to next entry in table) + then: [set_variables: [Prefix: "'leírás'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: - x: "$Prefix" - test: - if: "substring($NavCommand, $CommandOffset) = 'In'" - then: [T: "ban"] # phrase(zoom 'in' to see more details) + then: [T: ""] # phrase(zoom 'in' to see more details) - else_if: "substring($NavCommand, $CommandOffset) = 'InAll'" # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom" - then: [T: "Teljesen kinagyítva"] # phrase(zoom 'out all of the way' to see more details) + then: [T: "Teljesen nagyítsa ki"] # phrase(zoom 'out all of the way' to see more details) - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" then: [T: "ki"] # phrase(zoom 'out' to see more details) - else_if: "substring($NavCommand, $CommandOffset) = 'OutAll'" @@ -1322,12 +1322,12 @@ if: "preceding-sibling::* and following-sibling::*[1][name(.)='none']" then: - with: - variables: [Move2D: "'out of'", Child2D: "."] + variables: [Move2D: "'ki'", Child2D: "."] replace: [x: ".."] - x: "following-sibling::*[1]" else: - with: - variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) + variables: [Move2D: "'ban/ben'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) replace: [x: ".."] - with: variables: [NavCommand: "'MoveNextZoom'"] From 2f415a789bc9b7b5bdad4dbf51f2384e7bb0d994 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sat, 3 Oct 2026 20:22:19 +0200 Subject: [PATCH 04/10] tests/Languages/hu/mtable.rs: added a not merged table separator tests related modification Signed-off-by: Attila Hammer --- tests/Languages/hu/mtable.rs | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) diff --git a/tests/Languages/hu/mtable.rs b/tests/Languages/hu/mtable.rs index f02050e4..8f33d176 100644 --- a/tests/Languages/hu/mtable.rs +++ b/tests/Languages/hu/mtable.rs @@ -240,6 +240,105 @@ fn matrix_2x3() -> Result<()> { } +#[test] +fn augmented_matrix_2x3() -> Result<()> { + let expr = " + + + [ + + + + 3 + + + 1 + + + 4 + + + + + 0 + + + 2 + + + 6 + + + + ] + + "; + test("hu", "ClearSpeak", expr, "a(z) 2 x 3 mátrix; 1 . sor; 3, 1, elválasztó, 4; 2 . sor; 0, 2, elválasztó, 6")?; + test("hu", "SimpleSpeak", expr, "a(z) 2 x 3 mátrix; 1 . sor; 3, 1, elválasztó, 4; 2 . sor; 0, 2, elválasztó, 6")?; + Ok(()) +} + +#[test] +fn dashed_augmented_matrix_separator() -> Result<()> { + let expr = " + + [ + + 123 + + ] + "; + test("hu", "ClearSpeak", expr, "az 1-szer 3 -es sor mátrix; 1, elválasztó, 2, elválasztó, 3")?; + test("hu", "SimpleSpeak", expr, "az 1-szer 3 -es sor mátrix; 1, elválasztó, 2, elválasztó, 3")?; + Ok(()) +} + +/// A horizontal line is announced once, after the row it separates from the next row. +#[test] +fn matrix_row_separator() -> Result<()> { + let expr = " + + [ + + + 1 + 2 + + + 3 + 4 + + + ] + "; + test("hu", "ClearSpeak", expr, "a(z) 2 x 2 mátrix; 1 . sor; 1, 2, sorelválasztó; 2 . sor; 3, 4")?; + test("hu", "SimpleSpeak", expr, "a(z) 2 x 2 mátrix; 1 . sor; 1, 2, sorelválasztó; 2 . sor; 3, 4")?; + Ok(()) +} + +/// Horizontal and vertical lines use distinct announcements at their respective boundaries. +#[test] +fn matrix_row_and_column_separators() -> Result<()> { + let expr = " + + [ + + + 1 + 2 + + + 3 + 4 + + + ] + "; + test("hu", "ClearSpeak", expr, "a(z) 2 x 2 mátrix; 1 . sor; 1, elválasztó, 2, sorelválasztó; 2 . sor; 3, elválasztó, 4")?; + test("hu", "SimpleSpeak", expr, "a(z) 2 x 2 mátrix; 1 . sor; 1, elválasztó, 2, sorelválasztó; 2 . sor; 3, elválasztó, 4")?; + Ok(()) +} + // AI generated #[test] fn matrix_2x3_labeled() -> Result<()> { @@ -860,6 +959,40 @@ test_ClearSpeak("hu", "ClearSpeak_Matrix", "EndMatrix", return Ok(()); } +#[test] +fn augmented_matrix_3x4_end_matrix() -> Result<()> { +let expr = " + + [ + + + 1 + 2 + -1 + 3 + + + -3 + 3 + -1 + 2 + + + 2 + 3 + 2 + -1 + + + ] + +"; +test_ClearSpeak("hu", "ClearSpeak_Matrix", "EndMatrix", + expr, "a(z) 3 x 4 mátrix; 1 . sor; oszlop 1; 1, oszlop 2; 2, oszlop 3; negatív 1, elválasztó, oszlop 4; 3; 2 . sor; oszlop 1; negatív 3, oszlop 2; 3, oszlop 3; negatív 1, elválasztó, oszlop 4; 2; 3 . sor; oszlop 1; 2, oszlop 2; 3, oszlop 3; 2, elválasztó, oszlop 4; negatív 1; vége a mátrixnak")?; + test("hu", "SimpleSpeak", + expr, "a(z) 3 x 4 mátrix; 1 . sor; oszlop 1; 1, oszlop 2; 2, oszlop 3; negatív 1, elválasztó, oszlop 4; 3; 2 . sor; oszlop 1; negatív 3, oszlop 2; 3, oszlop 3; negatív 1, elválasztó, oszlop 4; 2; 3 . sor; oszlop 1; 2, oszlop 2; 3, oszlop 3; 2, elválasztó, oszlop 4; negatív 1; vége a mátrixnak")?; + Ok(()) + } // AI generated #[test] From c78f147c4db97c45d88bb7627a441658aede61c0 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sat, 3 Oct 2026 20:25:49 +0200 Subject: [PATCH 05/10] convert tests/Languages/hu/navigate.rs with crlf format Signed-off-by: Attila Hammer --- tests/Languages/hu/navigate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Languages/hu/navigate.rs b/tests/Languages/hu/navigate.rs index 3596cd28..adc92334 100644 --- a/tests/Languages/hu/navigate.rs +++ b/tests/Languages/hu/navigate.rs @@ -46,7 +46,7 @@ fn parts_prefix_logarithm_with_base() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; b") + assert_zoom_in("ZoomIn", expr, "nagyítás; in alap logaritmus; b") } #[test] From c0f9af0368db83d5c4ef48e32dffee766995c608 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sun, 4 Oct 2026 17:50:30 +0200 Subject: [PATCH 06/10] =?UTF-8?q?Rules/Languages/hu/navigate.yaml:=20remov?= =?UTF-8?q?e=20unneed=20in=20suffix=20after=20hungarian=20zoom=20part=20tr?= =?UTF-8?q?anslation=20(the=20nagy=C3=ADt=C3=A1s=20word=20related=20test)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Hammer --- Rules/Languages/hu/navigate.yaml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Rules/Languages/hu/navigate.yaml b/Rules/Languages/hu/navigate.yaml index e98c0e77..2bfc25ae 100644 --- a/Rules/Languages/hu/navigate.yaml +++ b/Rules/Languages/hu/navigate.yaml @@ -133,7 +133,11 @@ variables: - NumPrecedingSiblings: "count($Child2D/preceding-sibling::*)" replace: - - x: "$Move2D" + # "in" is not spoken: Hungarian expresses it with a case suffix on the part name, which cannot be added generically, + # and the command word (e.g. "nagyítás") already conveys the direction + - test: + if: "$Move2D != 'in' and $Move2D != 'ban/ben'" + then: [x: "$Move2D"] - test: - if: "$NumPrecedingSiblings=0" then: [T: "alapja"] # phrase(the 'base' of the power) @@ -162,7 +166,11 @@ - PartNumber: "count($Child2D/preceding-sibling::*)" - PartName: "GetNavigationPartName(name(.), $PartNumber)" replace: - - x: "$Move2D" + # "in" is not spoken: Hungarian expresses it with a case suffix on the part name, which cannot be added generically, + # and the command word (e.g. "nagyítás") already conveys the direction + - test: + if: "$Move2D != 'in' and $Move2D != 'ban/ben'" + then: [x: "$Move2D"] - test: - if: "$PartName != ''" then: [x: "$PartName"] From 5c56ee086cf6cbca43a7031badf067311cb19992 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sun, 4 Oct 2026 17:52:29 +0200 Subject: [PATCH 07/10] =?UTF-8?q?tests/Languages/hu/navigate.rs:=20adapt?= =?UTF-8?q?=20the=20nagy=C3=ADt=C3=A1s=20(zoom=20in)=20related=20changes?= =?UTF-8?q?=20to=20the=20test=20file=20to=20get=20correct=20translation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Attila Hammer --- tests/Languages/hu/navigate.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/Languages/hu/navigate.rs b/tests/Languages/hu/navigate.rs index adc92334..5f43a538 100644 --- a/tests/Languages/hu/navigate.rs +++ b/tests/Languages/hu/navigate.rs @@ -46,7 +46,7 @@ fn parts_prefix_logarithm_with_base() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in alap logaritmus; b") + assert_zoom_in("ZoomIn", expr, "nagyítás; alap logaritmus; b") } #[test] @@ -60,7 +60,7 @@ fn parts_infix_power() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") + assert_zoom_in("ZoomIn", expr, "nagyítás; alap; x") } #[test] @@ -74,7 +74,7 @@ fn parts_infix_indexed_by() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") + assert_zoom_in("ZoomIn", expr, "nagyítás; alap; x") } #[test] @@ -88,7 +88,7 @@ fn parts_function_fraction() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in számláló; 1") + assert_zoom_in("ZoomIn", expr, "nagyítás; számláló; 1") } #[test] @@ -101,7 +101,7 @@ fn parts_function_square_root() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in gyök; x") + assert_zoom_in("ZoomIn", expr, "nagyítás; gyök; x") } #[test] @@ -115,7 +115,7 @@ fn parts_silent_skip_super() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in alap; x") + assert_zoom_in("ZoomIn", expr, "nagyítás; alap; x") } // --- Intents not in NavigationParts --- @@ -163,7 +163,7 @@ fn no_parts_prefix_vector() -> Result<()> { #[test] fn no_parts_infix_binomial() -> Result<()> { - // binomial:infix (choose); no NavigationParts → "in part 1" + // binomial:infix (choose); no NavigationParts → "part 1" let expr = r#" @@ -176,12 +176,12 @@ fn no_parts_infix_binomial() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in része 1; 7") + assert_zoom_in("ZoomIn", expr, "nagyítás; része 1; 7") } #[test] fn no_parts_postfix_transpose() -> Result<()> { - // transpose:postfix — not prefix, so still announces "in …" + // transpose:postfix — not prefix, so still announces its name let expr = r#" @@ -190,7 +190,7 @@ fn no_parts_postfix_transpose() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in transzponált; nagy m") + assert_zoom_in("ZoomIn", expr, "nagyítás; transzponált; nagy m") } #[test] @@ -205,12 +205,12 @@ fn no_parts_function_absolute_value() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; in az abszolút érték; x") + assert_zoom_in("ZoomIn", expr, "nagyítás; az abszolút érték; x") } #[test] fn no_parts_silent_modified_variable() -> Result<()> { - // modified-variable:silent (x-hat) — no NavigationParts → silent "in" + // modified-variable:silent (x-hat) — no NavigationParts → silent let expr = r#" From ed3f34e4b6b3796fc06f23dce70b96e67f5a9155 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sun, 4 Oct 2026 19:11:47 +0200 Subject: [PATCH 08/10] Rules/Languages/hu/navigate.yaml: fixed few translations, based with experiences Signed-off-by: Attila Hammer --- Rules/Languages/hu/navigate.yaml | 96 +++++++++++++++++++------------- 1 file changed, 57 insertions(+), 39 deletions(-) diff --git a/Rules/Languages/hu/navigate.yaml b/Rules/Languages/hu/navigate.yaml index 2bfc25ae..ed81a8eb 100644 --- a/Rules/Languages/hu/navigate.yaml +++ b/Rules/Languages/hu/navigate.yaml @@ -48,7 +48,7 @@ - if: "$MatchCounter = 0 and $SayCommand = 'true'" then_test: - if: "self::m:math and starts-with($NavCommand, 'ZoomOut')" - then: [T: "teljesen kinagyítva", pause: "medium"] + then: [T: "teljesen kicsinyítve", pause: "medium"] # phrase('zoomed out all of the way') - else_if: "IsNode(., 'leaf') and starts-with($NavCommand, 'ZoomIn')" then: - test: @@ -63,13 +63,31 @@ # CommandOffset is 1 + length of the English NavCommand stem (Zoom/Move/Read/Describe), # not the spoken Prefix length — so translations can use a different Prefix word. - if: "starts-with($NavCommand, 'Zoom')" - then: [set_variables: [Prefix: "'nagyítás'", CommandOffset: "5"]] # phrase('zoom' in to see more details) + then: + # the whole phrase is chosen here ("kicsinyítés" is the Hungarian for "zoom out"), so no suffix word is needed below + - test: + - if: "$NavCommand = 'ZoomOut'" + then: [set_variables: [Prefix: "'kicsinyítés'", CommandOffset: "5"]] # phrase('zoom out') + - else_if: "$NavCommand = 'ZoomOutAll'" + then: [set_variables: [Prefix: "'teljes kicsinyítés'", CommandOffset: "5"]] # phrase('zoom out all of the way') + - else_if: "$NavCommand = 'ZoomInAll'" + then: [set_variables: [Prefix: "'teljes nagyítás'", CommandOffset: "5"]] # phrase('zoom in all of the way') + - else: [set_variables: [Prefix: "'nagyítás'", CommandOffset: "5"]] # phrase('zoom' in to see more details) - else_if: "starts-with($NavCommand, 'Move')" then: [set_variables: [Prefix: "'ugrás'", CommandOffset: "5"]] # phrase('move' to next entry in table) - else_if: "starts-with($NavCommand, 'Read')" - then: [set_variables: [Prefix: "'olvasás'", CommandOffset: "5"]] # phrase('read' to next entry in table) + then: + # "olvasás jelenlegi" is not Hungarian; the whole phrase is chosen here (no suffix word needed for 'Current') + - test: + - if: "$NavCommand = 'ReadCurrent'" + then: [set_variables: [Prefix: "'az aktuális elem felolvasása'", CommandOffset: "5"]] # phrase('read current') + - else: [set_variables: [Prefix: "'olvasás'", CommandOffset: "5"]] # phrase('read' to next entry in table) - else_if: "starts-with($NavCommand, 'Describe')" - then: [set_variables: [Prefix: "'leírás'", CommandOffset: "9"]] # phrase('describe' to next entry in table) + then: + - test: + - if: "$NavCommand = 'DescribeCurrent'" + then: [set_variables: [Prefix: "'az aktuális elem leírása'", CommandOffset: "9"]] # phrase('describe current') + - else: [set_variables: [Prefix: "'leírás'", CommandOffset: "9"]] # phrase('describe' to next entry in table) - test: if: "$Prefix != ''" then: @@ -77,20 +95,12 @@ - test: - if: "substring($NavCommand, $CommandOffset) = 'In'" then: [T: ""] # phrase(zoom 'in' to see more details) - - else_if: "substring($NavCommand, $CommandOffset) = 'InAll'" - # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom" - then: [T: "Teljesen nagyítsa ki"] # phrase(zoom 'out all of the way' to see more details) - - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" - then: [T: "ki"] # phrase(zoom 'out' to see more details) - - else_if: "substring($NavCommand, $CommandOffset) = 'OutAll'" - # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom" - then: [T: "teljes táblázat kinagyítása"] # phrase(zoom 'out all of the way' to see more details) + # 'InAll', 'Out' and 'OutAll' are covered by the Prefix chosen above (nothing more to say) - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" then: [T: "jobbra"] # phrase(move to the 'right') - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" then: [T: "balra"] # phrase(move to the 'left') - - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" - then: [T: "jelenlegi"] # phrase(who is the 'current' president) + # 'Current' is covered by the Prefix chosen above (ReadCurrent / DescribeCurrent) - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" then: [T: "a sor elejére"] # phrase(move 'to start of line') - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" @@ -133,25 +143,30 @@ variables: - NumPrecedingSiblings: "count($Child2D/preceding-sibling::*)" replace: - # "in" is not spoken: Hungarian expresses it with a case suffix on the part name, which cannot be added generically, - # and the command word (e.g. "nagyítás") already conveys the direction - - test: - if: "$Move2D != 'in' and $Move2D != 'ban/ben'" - then: [x: "$Move2D"] - test: - if: "$NumPrecedingSiblings=0" - then: [T: "alapja"] # phrase(the 'base' of the power) + then: [T: "alap"] # phrase(the 'base' of the power) - else_if: "$Child2D/preceding-sibling::*[self::m:mprescripts]" # are we before mprescripts and hence are postscripts then: - test: # in postscripts -- base shifts by one if: "$NumPrecedingSiblings mod 2 = 0" - then: [T: "alsó index kezdete"] # phrase(x with 'subscript' 2) - else: [T: "Felső index kezdete"] # phrase(x with 'superscript' 2) + then: [T: "bal alsó index"] # phrase(x with 'pre-subscript' 2) + else: [T: "bal felső index"] # phrase(x with 'pre-superscript' 2) else: - test: if: "$NumPrecedingSiblings mod 2 = 0" - then: [T: "felső index"] # phrase(x with 'pre-superscript' 2) - else: [T: "alsó index"] # phrase(x with 'pre-subscript' 2) + then: [T: "felső index"] # phrase(x with 'superscript' 2) + else: [T: "alsó index"] # phrase(x with 'subscript' 2) + # Hungarian puts the case suffix on the part name ("a számlálóban"), which cannot be built for arbitrary part names. + # So "in" is silent (the command word already says it) and the other words follow the part name as a + # possessive/genitive noun phrase that works for any name: "számláló elhagyása", "nevező eleje", "alsó index vége". + - test: + - if: "$Move2D = 'out of'" + then: [T: "elhagyása"] # phrase('out of' the denominator) + - else_if: "$Move2D = 'start of'" + then: [T: "eleje"] # phrase('start of' the denominator) + - else_if: "$Move2D = 'end of'" + then: [T: "vége"] # phrase('end of' the denominator) - pause: "medium" # Rules for speaking what happens when moving into or out of a notation @@ -166,11 +181,6 @@ - PartNumber: "count($Child2D/preceding-sibling::*)" - PartName: "GetNavigationPartName(name(.), $PartNumber)" replace: - # "in" is not spoken: Hungarian expresses it with a case suffix on the part name, which cannot be added generically, - # and the command word (e.g. "nagyítás") already conveys the direction - - test: - if: "$Move2D != 'in' and $Move2D != 'ban/ben'" - then: [x: "$Move2D"] - test: - if: "$PartName != ''" then: [x: "$PartName"] @@ -193,8 +203,18 @@ then: [x: "SpeakIntentName(name(.), $Verbosity, 'silent')"] - else: [x: "SpeakIntentName(name(.), $Verbosity, 'other')"] else: - - T: "része" # phrase(the 'part' of the expression) + - T: "rész" # phrase(the 'part' of the expression) - x: "count($Child2D/preceding-sibling::*) + 1" + # Hungarian puts the case suffix on the part name ("a számlálóban"), which cannot be built for arbitrary part names. + # So "in" is silent (the command word already says it) and the other words follow the part name as a + # possessive/genitive noun phrase that works for any name: "számláló elhagyása", "nevező eleje", "alsó index vége". + - test: + - if: "$Move2D = 'out of'" + then: [T: "elhagyása"] # phrase('out of' the denominator) + - else_if: "$Move2D = 'start of'" + then: [T: "eleje"] # phrase('start of' the denominator) + - else_if: "$Move2D = 'end of'" + then: [T: "vége"] # phrase('end of' the denominator) - pause: "medium" - name: default-move @@ -1213,16 +1233,15 @@ - test: if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" then: - - T: "nem tud" # phrase('cannot' move right in expression) + - T: "nem tud jobbra" # phrase('cannot' move 'right' in expression) - test: - if: "$NavCommand = 'MoveNext'" then: [T: "mozogni"] # phrase('move' to next entry in table) - else_if: "$NavCommand = 'ReadNext'" then: [T: "olvasni"] # phrase('read' next entry in table) else: [T: "leírni"] # phrase('describe' next entry in table) - - T: "jobbra" # phrase(move 'right') - pause: short - - T: "matematematikai blokk vége" # phrase(move 'end of math') + - T: "matematikai blokk vége" # phrase(move 'end of math') - pause: long - set_variables: [SpeakExpression: "'false'"] @@ -1330,12 +1349,12 @@ if: "preceding-sibling::* and following-sibling::*[1][name(.)='none']" then: - with: - variables: [Move2D: "'ki'", Child2D: "."] + variables: [Move2D: "'out of'", Child2D: "."] replace: [x: ".."] - x: "following-sibling::*[1]" else: - with: - variables: [Move2D: "'ban/ben'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) + variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) replace: [x: ".."] - with: variables: [NavCommand: "'MoveNextZoom'"] @@ -1414,7 +1433,7 @@ - pause: medium - with: variables: - - Move2D: "'end of'" + - Move2D: "'start of'" - Child2D: "$EdgeNode/*[1]" - MatchCounter: $MatchCounter + 1 replace: [x: "$EdgeNode"] @@ -1688,9 +1707,8 @@ then: - test: - if: "$NavCommand = 'ReadCurrent'" - then: [T: "olvassa"] # phrase('read' next entry in table) - else: [T: "írja le"] # phrase('describe' next entry in table) - - T: "jelenlegi" # phrase('current' entry in table) + then: [T: "az aktuális elem felolvasása"] # phrase('read current' entry in table) + else: [T: "az aktuális elem leírása"] # phrase('describe current' entry in table) - pause: long - set_variables: [NavNode: "@id"] From cc5ce5ea061347b7e15096ad6e96abd1b5c7ea92 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sun, 4 Oct 2026 19:17:58 +0200 Subject: [PATCH 09/10] Rules/Languages/hu/navigate.yaml: added audit-ignore with required places Signed-off-by: Attila Hammer --- Rules/Languages/hu/navigate.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Rules/Languages/hu/navigate.yaml b/Rules/Languages/hu/navigate.yaml index ed81a8eb..5a7636d9 100644 --- a/Rules/Languages/hu/navigate.yaml +++ b/Rules/Languages/hu/navigate.yaml @@ -39,7 +39,7 @@ # as they would likely be much more efficient and also cleaner. # Rules for announcing the command -- name: say-command +- name: say-command #audit-ignore tag: "!*" match: "$SayCommand != ''" # value should be '', 'true', or 'false' variables: [Prefix: "''"] @@ -132,7 +132,7 @@ - x: "count($Child2D/preceding-sibling::*)+1" - pause: "medium" -- name: into-or-out-of-mmultiscripts +- name: into-or-out-of-mmultiscripts #audit-ignore tag: "*" match: "$Move2D != '' and (@data-from-mathml='mmultiscripts' or self::m:mmultiscripts)" replace: @@ -170,7 +170,7 @@ - pause: "medium" # Rules for speaking what happens when moving into or out of a notation -- name: into-or-out-of-default +- name: into-or-out-of-default #audit-ignore tag: "*" # saying "out of row n" is not very useful, so skip it # match: "$Move2D != '' and @data-from-mathml and @data-from-mathml != name(.) and count(*)>1 and @data-from-mathml != 'mtable'" From a235b6af168b66829423d1cb9e714b72246c88d2 Mon Sep 17 00:00:00 2001 From: Attila Hammer Date: Sun, 4 Oct 2026 19:20:03 +0200 Subject: [PATCH 10/10] tests/Languages/hu/navigate.rs: add new navigation tests, based with last hungarian navigation translations Signed-off-by: Attila Hammer --- tests/Languages/hu/navigate.rs | 329 ++++++++++++++++++++++++++++++++- 1 file changed, 327 insertions(+), 2 deletions(-) diff --git a/tests/Languages/hu/navigate.rs b/tests/Languages/hu/navigate.rs index 5f43a538..892d6948 100644 --- a/tests/Languages/hu/navigate.rs +++ b/tests/Languages/hu/navigate.rs @@ -9,13 +9,22 @@ use anyhow::Result; use std::panic::{catch_unwind, AssertUnwindSafe}; fn init_nav(mathml: &str) -> Result<()> { + init_nav_with_auto_zoom_out(mathml, false) +} + +/// Same setup as `init_nav`, but lets a test choose the "AutoZoomOut" preference. +/// +/// With AutoZoomOut == "False" the navigation rules stop at the edge of a notation and announce it +/// ("nem tud jobbra mozogni" + the part). With "True" they leave the notation automatically and announce +/// that with the "elhagyása" fragment ("nevező elhagyása"). +fn init_nav_with_auto_zoom_out(mathml: &str, auto_zoom_out: bool) -> Result<()> { set_rules_dir(abs_rules_dir_path())?; set_preference("Language", "hu")?; set_preference("SpeechStyle", "SimpleSpeak")?; set_preference("Verbosity", "Medium")?; set_preference("NavMode", "Enhanced")?; set_preference("NavVerbosity", "Verbose")?; - set_preference("AutoZoomOut", "False")?; + set_preference("AutoZoomOut", if auto_zoom_out { "True" } else { "False" })?; set_preference("Overview", "False")?; set_mathml(mathml)?; Ok(()) @@ -176,7 +185,7 @@ fn no_parts_infix_binomial() -> Result<()> { "#; - assert_zoom_in("ZoomIn", expr, "nagyítás; része 1; 7") + assert_zoom_in("ZoomIn", expr, "nagyítás; rész 1; 7") } #[test] @@ -272,3 +281,319 @@ fn no_intent_mi() -> Result<()> { "#; assert_zoom_in("ZoomIn", expr, "teljesen ráközelített; x") } + +// --- Move2D words (out of / start of / end of) and the zoom out / zoom in all commands --- +// +// Hungarian puts a case suffix on the part name ("a számlálóban"), which cannot be built for arbitrary +// part names. So "in" is silent and the other words follow the part name: "számláló elhagyása", +// "nevező vége", "számláló eleje". These tests check the fragments in order (not the exact separators +// or the speech of the node that is landed on) and that no English navigation word is left over. + +fn nav_speech(commands: &[&str], mathml: &str, auto_zoom_out: bool) -> Result { + init_nav_with_auto_zoom_out(mathml, auto_zoom_out)?; + let mut speech = String::new(); + for command in commands { + speech = do_navigate_command(command)?; + } + Ok(speech) +} + +/// Runs `commands` one after the other and checks the speech of the LAST one: every fragment must occur, +/// in the given order, and no English navigation word may be left over (AutoZoomOut is off). +fn assert_nav_fragments(commands: &[&str], mathml: &str, fragments: &[&str]) -> Result<()> { + assert_nav_fragments_with(commands, mathml, false, fragments) +} + +/// Like `assert_nav_fragments`, but with AutoZoomOut switched on. +fn assert_nav_fragments_auto_zoom_out(commands: &[&str], mathml: &str, fragments: &[&str]) -> Result<()> { + assert_nav_fragments_with(commands, mathml, true, fragments) +} + +fn assert_nav_fragments_with(commands: &[&str], mathml: &str, auto_zoom_out: bool, fragments: &[&str]) -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + let speech = nav_speech(commands, mathml, auto_zoom_out)?; + let mut rest = speech.as_str(); + for fragment in fragments { + match rest.find(fragment) { + Some(pos) => rest = &rest[pos + fragment.len()..], + None => panic!("'{}' not found (in this order) in speech '{}'", fragment, speech), + } + } + let english = ["in", "out", "of", "start", "end", "zoom", "move", "cannot", "read", "describe", "current"]; + for word in speech.split(|c: char| !c.is_alphanumeric()) { + assert!(!english.contains(&word), "English word '{}' left in speech '{}'", word, speech); + } + Ok(()) + })); + report_any_panic(result) +} + +/// Runs `commands` and compares the speech of the LAST one exactly (trailing separators are ignored). +fn assert_nav_exact(commands: &[&str], mathml: &str, auto_zoom_out: bool, expected: &str) -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + let speech = nav_speech(commands, mathml, auto_zoom_out)?; + assert_eq!(expected, speech.trim_end_matches([' ', ',', ';'])); + Ok(()) + })); + report_any_panic(result) +} + +const FRACTION: &str = r#" + + + 1 + 2 + + + "#; + +const POWER: &str = r#" + + + x + 2 + + + "#; + +/// 1/2 + y: the fraction has material to its right, so it is not at the edge of the math. +const FRACTION_PLUS_Y: &str = r#" + + + + 1 + 2 + + + + y + + + "#; + +/// y + 1/2: the fraction has material to its left. +const Y_PLUS_FRACTION: &str = r#" + + + y + + + + 1 + 2 + + + + "#; + +/// Native mmultiscripts: base x, postscripts (subscript 1, superscript 2), prescripts (subscript 3, superscript 4). +/// Children are: x, 1, 2, , 3, 4. Numbers are used for the scripts so the test does not depend +/// on how single letters are spoken. +const MULTISCRIPTS: &str = r#" + + + x + 1 + 2 + + 3 + 4 + + + "#; + +// --- ZoomOut / ZoomOutAll / ZoomInAll command phrases --- + +#[test] +fn zoom_out_of_numerator() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "ZoomOut"], FRACTION, &["kicsinyítés", "számláló elhagyása"]) +} + +#[test] +fn zoom_out_of_power_base() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "ZoomOut"], POWER, &["kicsinyítés", "alap elhagyása"]) +} + +#[test] +fn zoom_out_all_of_numerator() -> Result<()> { + // ZoomOutAll leaves every notation on the way up: the command phrase comes first, then the part that was left + assert_nav_fragments(&["ZoomIn", "ZoomOutAll"], FRACTION, &["teljes kicsinyítés", "számláló elhagyása"]) +} + +#[test] +fn zoom_out_all_of_power_base() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "ZoomOutAll"], POWER, &["teljes kicsinyítés", "alap elhagyása"]) +} + +#[test] +fn zoom_out_at_top_of_math() -> Result<()> { + // first ZoomOut leaves the numerator, the second one reaches the top: "zoomed out all of the way" + assert_nav_fragments(&["ZoomIn", "ZoomOut", "ZoomOut"], FRACTION, &["teljesen kicsinyítve"]) +} + +#[test] +fn zoom_in_all_command_phrase() -> Result<()> { + assert_nav_fragments(&["ZoomInAll"], FRACTION, &["teljes nagyítás"]) +} + +// --- MoveNext / MovePrevious inside a notation and at its edges (AutoZoomOut off) --- + +#[test] +fn move_right_into_denominator() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext"], FRACTION, &["ugrás jobbra", "nevező", "2"]) +} + +#[test] +fn move_right_at_end_of_denominator() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext", "MoveNext"], FRACTION, &["nem tud jobbra mozogni", "nevező vége"]) +} + +#[test] +fn move_left_at_start_of_numerator() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MovePrevious"], FRACTION, &["nem tud balra mozogni", "számláló eleje"]) +} + +#[test] +fn move_right_at_end_of_math_without_auto_zoom_out() -> Result<()> { + // With AutoZoomOut off, the "edge of the notation" rule fires first, even at the right end of the whole + // math: it says only "nem tud jobbra mozogni" (no part name, no "matematikai blokk vége"). + let expr = r#" + + x + + "#; + assert_nav_exact(&["ZoomIn", "MoveNext"], expr, false, "nem tud jobbra mozogni") +} + +#[test] +fn move_left_at_start_of_math_without_auto_zoom_out() -> Result<()> { + let expr = r#" + + x + + "#; + assert_nav_fragments(&["ZoomIn", "MovePrevious"], expr, &["nem tud balra mozogni"]) +} + +// --- AutoZoomOut on: leaving a notation automatically ("out of" -> "elhagyása") --- +// +// With AutoZoomOut == "True" the edge-of-notation rule is skipped. If there is more math beyond the notation, +// MoveNext/MovePrevious leave it automatically and say which part was left. Only at the very end of the math +// the "cannot move" announcement is made, followed by "matematikai blokk vége" / "matematikai blokk kezdete". +// Expected walk through 1/2 + y: ZoomIn (fraction), ZoomIn (numerator), MoveNext (denominator), +// MoveNext (leaves the denominator, lands on "+"). + +#[test] +fn auto_zoom_out_move_right_leaves_denominator() -> Result<()> { + assert_nav_fragments_auto_zoom_out( + &["ZoomIn", "ZoomIn", "MoveNext", "MoveNext"], + FRACTION_PLUS_Y, + &["ugrás jobbra", "nevező elhagyása"], + ) +} + +#[test] +fn auto_zoom_out_move_left_leaves_numerator() -> Result<()> { + // y + 1/2: ZoomIn (y), MoveNext (+), MoveNext (fraction), ZoomIn (numerator), MovePrevious (leaves it) + assert_nav_fragments_auto_zoom_out( + &["ZoomIn", "MoveNext", "MoveNext", "ZoomIn", "MovePrevious"], + Y_PLUS_FRACTION, + &["ugrás balra", "számláló elhagyása"], + ) +} + +#[test] +fn auto_zoom_out_move_right_at_end_of_math() -> Result<()> { + let expr = r#" + + x + + "#; + assert_nav_fragments_auto_zoom_out(&["ZoomIn", "MoveNext"], expr, &["nem tud jobbra mozogni", "matematikai blokk vége"]) +} + +#[test] +fn auto_zoom_out_read_and_describe_right_at_end_of_math() -> Result<()> { + let expr = r#" + + x + + "#; + assert_nav_fragments_auto_zoom_out(&["ZoomIn", "ReadNext"], expr, &["nem tud jobbra olvasni", "matematikai blokk vége"])?; + assert_nav_fragments_auto_zoom_out(&["ZoomIn", "DescribeNext"], expr, &["nem tud jobbra leírni", "matematikai blokk vége"]) +} + +#[test] +fn auto_zoom_out_move_left_at_start_of_math() -> Result<()> { + let expr = r#" + + x + + "#; + assert_nav_fragments_auto_zoom_out(&["ZoomIn", "MovePrevious"], expr, &["matematikai blokk kezdete"]) +} + +// --- mmultiscripts part names --- +// +// Children of the mmultiscripts: base, subscript, superscript, , pre-subscript, pre-superscript. +// The names are "alap", "alsó index", "felső index", "bal alsó index" and "bal felső index". + +#[test] +fn multiscripts_zoom_in_base() -> Result<()> { + assert_nav_fragments(&["ZoomIn"], MULTISCRIPTS, &["nagyítás", "alap", "x"]) +} + +#[test] +fn multiscripts_move_right_to_subscript() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext"], MULTISCRIPTS, &["ugrás jobbra", "alsó index", "1"]) +} + +#[test] +fn multiscripts_move_right_to_superscript() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext", "MoveNext"], MULTISCRIPTS, &["ugrás jobbra", "felső index", "2"]) +} + +#[test] +fn multiscripts_move_right_to_pre_subscript() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext", "MoveNext", "MoveNext"], MULTISCRIPTS, &["ugrás jobbra", "bal alsó index", "3"]) +} + +#[test] +fn multiscripts_move_right_to_pre_superscript() -> Result<()> { + assert_nav_fragments( + &["ZoomIn", "MoveNext", "MoveNext", "MoveNext", "MoveNext"], + MULTISCRIPTS, + &["ugrás jobbra", "bal felső index", "4"], + ) +} + +#[test] +fn multiscripts_move_left_to_base() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "MoveNext", "MovePrevious"], MULTISCRIPTS, &["ugrás balra", "alap", "x"]) +} + +#[test] +fn multiscripts_zoom_out_of_base() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "ZoomOut"], MULTISCRIPTS, &["kicsinyítés", "alap elhagyása"]) +} + +#[test] +fn multiscripts_zoom_out_of_pre_superscript() -> Result<()> { + assert_nav_fragments( + &["ZoomIn", "MoveNext", "MoveNext", "MoveNext", "MoveNext", "ZoomOut"], + MULTISCRIPTS, + &["kicsinyítés", "bal felső index elhagyása"], + ) +} + +// --- ReadCurrent / DescribeCurrent --- + +#[test] +fn read_current() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "ReadCurrent"], FRACTION, &["az aktuális elem felolvasása"]) +} + +#[test] +fn describe_current() -> Result<()> { + assert_nav_fragments(&["ZoomIn", "DescribeCurrent"], FRACTION, &["az aktuális elem leírása"]) +}