Skip to content

Merge Japanese branch into main - #936

Merged
NSoiffer merged 65 commits into
mainfrom
ja
Oct 28, 2026
Merged

NSoiffer merged 65 commits into
mainfrom
ja

Conversation

@NSoiffer

Copy link
Copy Markdown
Collaborator

There is still more work that can be done (especially in unicode-full.yaml), but I was told it is good enough now.

moritz-gross and others added 30 commits August 28, 2026 19:12
add AI seeding for Japanese, created by gpt-5.6 sol
* ja: read fractions in Japanese word order

A fraction of two numbers is read denominator-first in Japanese: 21/22 is
"22 分の 21". The seeded rules emitted numerator, 分の, denominator, so the
tests were asserting that 21/22 is spoken as 22/21. That is a meaning
change, not a wording preference, so the rule and its test move together.

Reference: 山口雄仁・川根深・澤崎陽彦, 日本語による数式読み上げ法の基本構成に
ついて, 日本数学教育学会誌 78(9), 239-247 (1996), item (4): when numerator and
denominator are both plain numbers, use ordinary Japanese word order
(denominator 分の numerator); otherwise keep the written order and say
分数 A オーバー B 分数終了.

Following that split:

- common-fraction (both mn, no decimal separator) now says 分母 分の 分子.
  The English range limits (numerator < 20, denominator 2..10) existed
  because English only has ordinals for small numbers; Japanese has no
  separate ordinal form to build, so the pattern is uniform and ToOrdinal
  is no longer used here.
- the remaining fraction rules keep the written order and now say オーバー
  rather than 分の, which would otherwise claim the opposite order.
- ClearSpeak's general fraction said 分子 との 分数 ... デノミネーター; it now
  uses the same 分数 ... オーバー ... 分数終了 frame.
- 分数終わり -> 分数終了, matching the terminology of the reference (and the
  根号終了 / 上付き終了 markers that will follow).

Also in this file: per-fraction said パーカー, which is a hooded sweatshirt.
"5 meters per second" is 5 メートル 毎 秒.

* ja: keep one definition of "a fraction of two numbers"

The previous commit widened common-fraction, but ClearSpeak copies that
predicate in five other places: the EndFrac test inside fraction-over-simple
(twice, for numerator and denominator) and the four nested-* exponent rules
that ask whether the base is a common fraction. Left alone, 3/128 would count
as a common fraction where it is spoken and not count where those rules look,
so the two would disagree.

The copies now use the same test: both children are mn without a decimal
separator. The English range limits (numerator < 20, denominator 2..10) are
gone for the same reason as before -- they bound what English has ordinals
for, and Japanese builds no ordinal form.

No wording changes here; the phrasing of the exponent rules themselves is a
separate change.
* ja: read exponents as (base) no (exponent) jou

* ja: drop the negative-exponent test from this change

* ja: cover the superscript frame for a complex exponent
A unary + or - was spoken 正の / 負の. Those name a kind of number -- 負の数
is "the negative numbers" as a category -- and are not how -5 is read aloud.
Japanese says マイナス 5, with the same word used for the binary operator;
the reference's worked example reads -b as マイナス b, and its symbol table
gives ± as プラス・オア・マイナス.

Three pairs in ClearSpeak_Rules.yaml (the two PosNegSqRoot rules and
negative_and_positive) and the prefix forms of "plus" and "minus" in
definitions.yaml.

Not touched: SharedRules/general.yaml, where 正の / 負の are correct -- there
they modify 整数 in "the set of all positive integers".
The seeded wording transliterated the English cues and kept the English word
order, which puts the closing cue before the group it closes.

Japanese names the shape when the group opens and postposes the closing cue
(Yamaguchi, Kawane and Sawazaki 1996, item 8):

  (  括弧 / 丸括弧      )  括弧閉じ / 丸括弧閉じ
  [  角括弧             ]  角括弧閉じ
  {  中括弧             }  中括弧閉じ

The Terse/Verbose split is kept where en has one: 丸 (the shape) drops out in
Terse the same way "paren" does in English.

Absolute value: 「絶対値 の x」 is ungrammatical because の attaches the other
way round in Japanese, so "the absolute value of x" would have to be 「x の絶対値」.
The cited reading keeps the written order and simply drops the preposition, so
|x| is 絶対値 x. With AbsEnd the closing cue moves after the contents and the
transliteration リリース ("release", from "end") becomes 閉じ.

audit-translations ja: Rule differences unchanged at 22, untranslated 3681 to 3680.
* ja: use the Japanese geometry terms

* ja: a ray passes through its second point, it does not end there

Verbose said 半直線 A から B まで, which claims the ray stops at B. A ray has A as its endpoint and continues through B, so read it as A を始点として B を通る. A segment does end at its second point, so から...まで stays there; a test now pins the two apart.
ja: read brackets and absolute value in Japanese
ja: read large operators in Japanese word order
ja: read intervals with a verb on each endpoint
ja: use the Japanese mathematical terms
ja: use the Japanese names for the elementary functions and the parts of a complex number
yasumorishima and others added 19 commits September 5, 2026 06:53
`say-command` still set the four prefixes to the English words, so a Japanese
synthesiser read them out as English: "move 右", "zoom イン", "read 右",
"describe 右". They are now 移動 / ズーム / 読み上げ / 説明.

`$CommandOffset` (from ec36e05) already makes this safe: the offset is the
length of the English `$NavCommand` stem, not of the spoken prefix, so the
suffix test still matches. ja already carried the correct offsets (5/5/5/9).
ru, nb, fr, hu and sv translate their prefixes the same way.

Word order is the other half. Japanese puts the target before the verb, and
the particle belongs to the verb, so a new `$Particle` is set beside the
prefix (に for move, を for read and describe) and emitted inside each
direction branch: 右 に 移動, 右 を 読み上げ. Keeping the particle inside the
branch means a command whose suffix matches nothing still falls back to the
bare verb, the way it does in en.

The Zoom commands keep the English order: ズーム + イン is the ordinary
loanword, and the concatenation that produces ズームインを最大にしました
requires the prefix to come first. The two suffix sets are disjoint (Zoom only
produces In/InAll/Out/OutAll, the others only Next/Previous/Current/LineStart/
LineEnd), so splitting the branch loses nothing.

Two other rules in the same file spoke the same words in the other order and
would have contradicted this, so they move too:

- `current` (ReadCurrent / DescribeCurrent) said 読み上げ 現在; it now says
  現在 を読み上げ.
- `move-next-no-auto-zoom-at-edge-math` said 右に for all three verbs, so
  read and describe got the wrong particle; the direction and particle are now
  part of each verb branch (右に移動 / 右を読み上げ / 右を説明 できません).

audit-translations ja: untranslated text 3577 -> 3574; missing rules 0, extra
rules 0. Rule differences 28 -> 30, both in navigate.yaml and both intended:
one "variable difference" for the added `$Particle`, one "structure
difference" for the reordered branch.

Adds tests/Languages/ja/navigate.rs, the first navigation tests for ja. They
assert the whole command prefix -- everything up to the first pause -- because
the description that follows comes from NavigationParts and is not what this
change touches.
Wiring it up needs one line in tests/languages.rs, which is outside
Rules/Languages/ja and tests/Languages/ja.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016JCoREgn1pJzcbdnUx4iuh
…lose marks

Two groups of fixes in the shared rule files.

1. Three fraction rules read the fraction backwards. Japanese puts the
   denominator first: 3/4 is "4 分の 3", so "3 分の 4" is 4/3 -- a different
   number, not just an odd phrasing. ClearSpeak and SimpleSpeak already swap
   the operands (common-fraction), but the mfrac rules in SharedRules/default.yaml
   (used by LiteralSpeak) and the fraction rule in overview.yaml (used by the
   "describe" navigation command) did not.
   - literal-simple and overview-default now say the denominator first.
   - literal-default has explicit start/end markers, so it keeps the written
     order and uses the borrowed オーバー: 分数 A オーバー B 分数終了. It also
     opened with スタート, the English "start".

2. menclose notation names and two neighbouring rules were still the machine
   translation:
   - 上下矢印 for "up arrow" is an arrow with a head at each end, so uparrow,
     updownarrow, leftrightarrow and both diagonal double ended arrows all
     named the same thing.
   - ダブルは対角矢印を終了 / 二重は対角矢印を終了 ("double is ends diagonal
     arrow") are not sentences.
   - 行動シンボル is an "action symbol"; the actuarial symbol is 保険数理記号.
   - アラビア語の要素のシンボル reads "Arabic-language element symbol"; madrub
     is the factorial symbol written in Arabic script.
   - クロスアウト, ダウン矢印, ダウンの対角, サークル, ラウンドボックス, ボックス and
     ロングディビジョンシンボル are transliterations of the English.
   - 対角形 does not say which diagonal.
   - 詳しくはこちら ("click here for details") was used for "above" in the mover
     and munderover rules, opposite the 下 that is already there.
   - ノルダム is Notre-Dame; a norm is ノルム.
   - エントリー for a matrix entry is 成分, and a coordinate is 点, the word
     geometry.yaml already uses.

Every line that was checked against the en rule beside it is promoted to T:.
Tests cover both fraction paths, the overview path, the menclose marks, above
and below, the norm and the matrix entry.
Translate the Japanese navigation command prefixes
ja: read the remaining fractions denominator-first, and name the menclose marks
* ja: use the source's readings for the comparison and element-of symbols

`unicode.yaml` puts the relation word between the two operands, so the word has
to work there. Several did not.

- `>` said より大きい. より needs its comparand in front of it, so "x は より大きい 5"
  parses as "x is a bigger 5". It also did not match the 小なり already used for
  `<` in the same file. Yamaguchi, Kawane & Sawazaki (1996) give the pair
  小なり / 大なり, and 小なり・オア・イコール for `≤` (the ・ is only a word
  separator and is not read), so `≥` follows as 大なり・オア・イコール.
- `≠` said 等しくない, a predicate with the same problem. The source gives
  ノット・イコール, which pairs with the イコール already used for `=`.
- `∈` and `∊` said 要素の for "element of". の attaches backwards: 要素の A is
  "A of an element". The source gives 要素オブ, the same borrowed オブ this
  translation already uses for "of" elsewhere. The two rules also disagreed with
  each other -- ∈ said 属する where ∊ said 要素の, for the same English.
- The ClearSpeak "In" option said 中へ, which is "into", a direction of motion.
  The source's reading for "in" is the borrowed イン.
- `∾` said 最も肯定的な, "most affirmative"; en is "most positive" in the numeric
  sense, so 最も正の.

Left alone: the negative set relations (∉ ⊄ ⊅) and the subset family
(⊂ ⊃ ⊆ ⊇). The source gives 部分集合オブ for ⊂ but nothing for the superset or
the negated forms, and I did not want to change half a family.

* ja: update the tests that pinned the old relation wording

* ja: sweep the same relations in definitions.yaml and cover the set branches

* ja: flatten the set-builder test expression

* ja: use the set-builder shape the en tests use

* ja: member-of said に属する where unicode.yaml says 元
#757 left the negated membership and the subset family alone: the source gives
部分集合オブ for ⊂ but nothing for the superset or the negated forms, and I did
not want to change half a family. This does the whole family at once.

- ⊂ said 部分集合 and ⊆ said 部分集合または等しい. A bare noun, or a predicate
  that ends the sentence, strands the set that follows it: "A は 部分集合 B"
  reads as "A is a subset" and then a B. The source gives 部分集合オブ for ⊂ and
  部分集合(オブ)・オア・イコール for ⊆, where the オア イコール is the one this
  translation already uses for ≤ (the ・ is only a word separator, not read).
- ⊃ and ⊇ keep the 上位集合 the seed chose, since the source gives no superset
  reading, and take the same オブ so they work in the same position.
- ∉ ⊄ ⊅ ended in ではない / でない, which closes the sentence in front of the set
  the statement is about. The source gives ノット・イコール for ≠, so the negation
  is that same ノット in front of whatever the positive form says.
- The ClearSpeak Member option said 元 on its own -- the same missing-preposition
  shape 要素の had in #757 -- and outside a set the In option said に含まれる,
  whose に attaches to what comes before it and so reverses the relation.
- definitions.yaml carries the same readings for intent, so the eight entries of
  this family are swept with it.

Left alone: the Belongs option (に属する / 属する / 属さない). Its Japanese is a
verb phrase, so making it work between the operands is not a matter of adding
the borrowed オブ; it needs a noun the source does not give. It stays t: in both
polarities, and is the one branch of ∈/∊/∉ this commit does not promote.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzNYYoiVRzKGnDEWTJnuHh
…d order

Three of the navigation announcements were still in the shape en gives them.

- The 2D move announcements spoke $Move2D as it stands, and that variable holds
  an English literal: 'in', 'out of', 'end of', 'start of'. Zooming into x
  squared said "ズーム イン; in 底; x", with the "in" read out in English. Those
  four are the only values this file ever sets, so the two rules that emit it
  now map them (に入る / から出る / の終わり / の始め) and say them after the part
  they apply to, which is where Japanese wants them: "ズーム イン; 底 に入る; x".
- Undo named the verb first: 元に戻す ズームイン is "undo zoom in" word for word.
  Japanese names what is being undone first, ズームイン を 元に戻す.
- The placemarker announcements had the same order -- 読み上げ プレースホルダー 3
  for "read placeholder 3". They now name the placeholder and its number first
  and then take the particle the matching move command already uses (を 読み上げ,
  を 説明, に 移動), the pattern #752 set up. セット becomes 設定, the ordinary
  Japanese for setting a value.

The four rules whose children are reordered carry `# audit-ignore`. The
divergence from en is the point of the change, and without the marker the rule
difference count goes 30 -> 34 and stops being usable as evidence that a PR did
not break the rules. The marker sits at the end of the `- name:` line: that is
the position that works today, and the one 170 of the 298 markers under
Rules/Languages already use. (#742 is deciding which position becomes canonical;
if it moves, these move with the rest.)

This also promotes 底 / 上付き文字 / 下付き文字 to T:, checked against en's base /
superscript / subscript. In en the `phrase(...)` hints on those lines and the
values they annotate do not agree with each other; ja follows the values, which
are what is actually spoken.

audit-translations ja: untranslated text 3447 -> 3436, which is the 8 lines this
changes plus those 3 promotions; rule differences 30 -> 30; missing, extra and
definition counts unchanged at 0.

Tests: four in navigate.rs, with a helper that compares the whole announcement
rather than the command prefix, since none of these three is a prefix.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzNYYoiVRzKGnDEWTJnuHh
ja: make the rest of the set relations work between the operands
ja: navigate.yaml stopped speaking English, and now uses Japanese word order
Lowercase `t:` means untranslated or unverified and uppercase `T:` means
translated and verified, so this is a review rather than bookkeeping. Each of
the twenty-three lines was read against the value the English source carries in
the same rule -- the value, not the `phrase(...)` hint, because those two can
disagree: move-cell-next says "no next column" under a hint that reads "no next
row", and the Japanese follows the value.

Nineteen lines were already right and only change case. Four were the odd one
out among their own siblings and are reworded as well:

  最初の文字にズームイン -> 最初の文字までズームしました
    the other two zoom reports in that same test are sentences
    (ズームインを最大にしました, 文字までズームしました); this one was a bare
    noun phrase and did not read as a report of where the zoom ended up.

  現在のエントリを読む -> 現在のセルを読む
    エントリ appears exactly once in the whole Japanese rule set. The rule
    speaks 行 and 列 straight after it, and the sibling rule says 表, so セル is
    the word these rules already use for the thing being read.

  シンプル -> 簡易
    the three navigation modes are spoken as <word> + モード, and 文字 and 拡張
    are ordinary words while this one was a transliteration between them.

  移動後の式の概要 -> 移動後に式の概要を読み上げる
    the two states of one toggle: the other is the sentence
    移動後に式を読み上げる, and this one did not say what would happen to it.

There is no Japanese appendix for navigation wording, unlike the ClearSpeak
terms, so those four are a judgement call and each one has a test.

audit-translations ja: untranslated 3415 -> 3392, which is exactly the number of
lines promoted; rule differences unchanged at 30; missing, extra and definition
counts unchanged at 0.

Tests: four in navigate.rs. Reaching the first-character branch needs Character
mode, so the helpers grew a mode argument; the sequence is the one
move_inside_leaves in src/navigate.rs uses -- ZoomInAll to the leaf, then one
more ZoomIn to step inside it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzNYYoiVRzKGnDEWTJnuHh
Each of these was translated token by token, in English order, so the Japanese
carries the words and not the meaning. Where a phrase is split across several
`t:` entries the fix moves material between them, which is what the merged
navigation work (#752, #773) already did; the en `phrase(...)` comments are left
alone.

  {x | x > 2}
    集合 すべて x そのようなこと x は 大なり 2
    -> 集合 すべての x ただし x は 大なり 2
    そのようなこと is "such a thing": it translates the words of "such that" and
    none of its work, which is to attach a condition to what was just named.
    ただし is what Japanese mathematical prose uses. 集合 stays in front of the
    variable rather than moving to the head-noun position Japanese would
    normally give it, because in a speech stream it tells the listener what
    they are about to hear, and the first argument can be long ({x ∈ ℤ : x > 5}).

  P(A | B)
    A 与えられた B -> A 条件は B
    与えられた is the past participle "given", which in Japanese modifies the
    noun after it, so B was being described rather than named as the condition.

  a labelled row
    行 1 ラベルを使って 第1式 -> 行 1 ラベルは 第1式
    ラベルを使って is "using a label", which says the row does something with
    one. The label is simply what the row is called.

  f|_a^b
    f 評価される b 同じ式が評価されるマイナス a
    -> f 次の点で評価 b マイナス 同じ式を次の点で評価 a
    評価される is the passive "is evaluated" with nothing to say where, and the
    minus came last, so the subtraction arrived after the thing subtracted.

  round()        ラウンド値 ("round" in katakana + 値) -> 丸めた値
  fenced-group   フェンスグループ, an English term in katakana -> 括弧のまとまり

Each phrase is swept across every file that carries it, including the sibling
branches: unicode.yaml has two more `given`s, general.yaml a second
`evaluated at`, and definitions.yaml the intent mappings for such-that, given
and conditional-probability. The copy of `such that` in unicode-full.yaml is
left alone, since that file is waiting on a MathPlayer seed.

audit-translations ja: untranslated 3415 -> 3400, which is exactly the number of
rule lines promoted; rule differences unchanged at 30; missing, extra and
definition counts unchanged at 0. definitions.yaml entries carry no case marker,
so they do not move that count.

Tests: five new in ja.rs and one updated, since set_builder_member_symbol had
pinned the old reading. Reverting the readings and nothing else turns exactly
those six red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XzNYYoiVRzKGnDEWTJnuHh
ja: mark the reviewed navigation strings as verified
ja: the phrases that were carried over word for word
…athPlayer (#929)

* Add UEB G1 BANA/GTM mode preference and expand Grade-2 contractions.
Adding the preference is a stall tactic while I wait for feedback from ICEB about whether BANA rules are obsolete.

Introduce UEB_G1_BANA_Style (default true) to choose BANA 2019 vs ICEB GTM
1.7.3/1.7.5 grade-1 indicator placement in ueb_cleanup. Implement GTM 1.7
per-symbols-sequence counting with gtm_1_7_mode, and add matching ICEB tests.

Extend handle_contractions for technical vocabulary: UEB 10.8 final-letter
groupsigns, dot-5 through/part, dis/con prefixes, cc/ea exception words, and
a ful-pattern fix. Add ueb_technical_word_contractions (311 words, goldens
cross-checked with brailletranslators.com). Wire the preference through
prefs, fuzz, and docs.

* updated to new BANA and ICEB spec updates. Five tests fail. Still working on them

* removed an option

* fix contractions involving 'be, con, dis' which can only be at the start of a 'word' (where 'word' has a specific UEB meaning)

* Updated to UEB to the latest guidance for grade 1 indicators. This adds new tests and changes a few tests.

Also added more contractions.

* update rc #

* fix new clippy warnings

* fix up Vietanmese tests

* fix str compile issue with no-unsafe feature

* add tests for intervals in German

* Refine German interval descriptions and update translation rules

* Add German ClearSpeak rule and tests for pause intent

* Add German localization for currency symbol placement and corresponding tests

* add another test for euro

* add another test for pound/pfund

* Update German ClearSpeak rules: Add support for principal logarithm (Log) and corresponding tests

* Refine German ClearSpeak fraction translations and update ordinal fraction definitions

* German ClearSpeak: subset & superset

* de: fix chemical state names

* de: fix chemical reaction arrow

* de: fix integer exponentiation

* de: fix exponentiation by arbitrary exponents

* de: add vector norm

* de: fix "transpose"

* replace lamda with lambda (#789)

* replace lamda with lambda

* fix line endings

* fix(pl): przywróć szablony sum i iloczynów po naprawie słowa łączącego (#783)

* Refactor/simplify prefs rs (#780)

* remove pointless cloning

* simplify logic flow of is_user_pref

* simplify path joins

* Add comment to clarify update condition

Added a comment to prevent unnecessary updates if the value hasn't changed.

---------

Co-authored-by: NSoiffer <NSoiffer@users.noreply.github.com>

* ci: add scheduled workflow to alert translators of English rule changes

* Add comment to cron schedule in translator-alert.yml

* de: fix "k-th root"

* de: fix "negativ positiv"

* de: fix RPM singular/plural

* Set CRLF line endings for .rs and .yaml files

* Add support for stdin, inline MathML, and default fallback

* Re-add addon info to user guide

Added back the info on how to use MathCAT as an addon to NVDA, which was accidentally removed.

* Add rule order checks and reporting for shared rule mismatches

* fix ruff

* uv run ruff format  audit_translations/

* Refactor: Replace custom path utilities with `Path` API and update zip methods accordingly (#775)

* Refactor navigation state management: unify position and command stacks into a single history vector (#776)

* Refactor navigation state management: unify position and command stacks into a single history vector

* fix cargo clippy no-unsafe

* simplify `read_prefs_file` (#808)

* Refactor `read_prefs_file` to simplify error handling and optimize section processing

* use with_context(..) from anyhow-crate for error-handling

* Replace `unwrap()` with `let Some(...) else` constructs for safer option handling

* simplify destructuring syntax using tuples

* use elements.rs as chemistry database

* fix redundant call of pref_to_string (#815)

* clean up unused types in definitions and remove outdated docstring (#818)

* remove dead functions set_string_value and set_bool_value (#819)

* replace custom constructor with struct literals for TTSCommandRule (#821)

* Add GitHub Actions workflows to measure and report library size changes

* Refactor library size comment script to improve readability and error handling

* Update README with supported and in-progress speech languages list

* replace buggy Display implementation for Pronounce with Debug trait (#820)

* replace buggy Display implementation for Pronounce with Debug trait

* remove unused `eloquence` field and related code from `Pronounce` struct

* fix outdated syntax

* define ratio (U+2236)

* added/modified comments for functions that deal with function names and parts

* Swap IntentMappings so ":" is argument glue and "|" is verbosity -- this looks much better. All language `definitions.yaml` are updated.

Fix parsing of `IntentMappings`. Parsing now lives in one place, so ratio-style "name : glue" pairs, arity templates, and bracketing speech stay consistent across languages.

This all started as a fix to #709, but then I discovered that the format used in `IntentMappings` was parsed incorrectly in some cases (such as "ratio").

* shorten 'ratio' def after debugging

* fix clippy warnings (about comments)

* Normalize tracked files to match .gitattributes (#843)

* Create rule coverage functionality (#834) (#845)

* v1 of rule coverage report

* Add Python script to generate Markdown and HTML rule coverage reports

* clarify name of logfiles

* Switch rule coverage events to JSONL format

* switch to jinja2 templates

* Exclude Unicode mapping files from rule coverage report and improve report generation

* Exclude definition files from rule coverage report and update related documentation

* Add hit counts and test details to rule coverage report

Enhance the rule coverage report to include hit counts, test details, and hoverable tooltips for matched rules. Improve report formatting and coverage fraction calculation. Update related tests and documentation to reflect these changes.

* fix title

* Simplify rule coverage event recording

* Stream rule coverage events and trim unused report fields

* Suppress unused variable warning when `rule-coverage` feature is disabled

* Migrate `rule_coverage` implementation to a dedicated `rulecoverage` package, separating logic, templates, and CLI.

* uv run ruff format

* simplify dataclasses in Python

* remove "loaded" as statistic for rule coverage

* remove MD report and only keep HTML.
simplify rest accordingly.

* uv ruff format

* Rules/Languages/hu/definitions.yaml: add a new rule, based the english new definitions.yaml rule (the of word related)… (#842)

* Rules/Languages/hu/definitions.yaml: add a new rule, based the english new definitions rule

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Update tests, add navigation test and table separator test

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Rules/Languages/hu/definitions.yaml: added missign navigation related fixes with not merged to the main branch

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* tests/Languages/hu/mtable.rs: added a not merged table separator tests related modification

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* convert tests/Languages/hu/navigate.rs with crlf format

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Rules/Languages/hu/navigate.yaml: remove unneed in suffix after hungarian zoom part translation (the nagyítás word related test)

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* tests/Languages/hu/navigate.rs: adapt the nagyítás (zoom in) related changes to the test file to get correct translation

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Rules/Languages/hu/navigate.yaml: fixed few translations, based with experiences

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Rules/Languages/hu/navigate.yaml: added audit-ignore with required places

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* tests/Languages/hu/navigate.rs: add new navigation tests, based with last hungarian navigation translations

Signed-off-by: Attila Hammer <hammera@pickup.hu>

---------

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* src/canonicalize.rs: apply clippy fixes with two places (#850)

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Fix intent detection for limits and norms (#840)

This commit improves intent recognition for several TeX-derived patterns: it ignores the invisible function-application character when matching limit expressions, treats TeX's gathered environments as lines, and recognizes subscripted norms from TeX4ht and canonicalized MathML shapes. These fixes keep speech and navigation output consistent for common display math cases.

* Improve translator alert workflow by including file links in notifications (#852)

* docs: create the CONTRIBUTING.md file for newcomers (#860)

* deps: upgrade zip to 9.x (#861)

* Add lang-* and braille-* features to choose which rules include-zip embeds (#816)

Every language and braille code has a feature, all on by default through rules-all, so existing builds embed exactly the same rules.zip. A build that turns off default features and names a subset embeds only those directories. en is always kept because the library falls back to it, a subset with no braille code keeps UEB so set_rules_dir still succeeds, and the staged prefs.yaml BrailleCode default is pointed at a kept code when Nemeth was pruned. build.rs fails if a Rules directory has no feature in lang-all or braille-all, so the lists cannot silently fall behind.

Co-authored-by: NSoiffer <NSoiffer@users.noreply.github.com>

* Simplify `is_leaf_any_name` (#829)

* simplify some Vector handling (#828)

* simplify some Vector handling

* undo changes in `is_structural`

* Refactor comma-check logic in `is_leaf_any_name` (#830)

* Remove `\mathmit` as that is non-standard. Fixes undefined italic `pi` in RC not in beta (although the `\mathmit` is in the beta).

Fixes #857

* remove duplicate entries

* Bring IntentMappings “of”/glue syntax from main onto release-candidate.
Use ":" for argument glue and "|" for verbosity, move the function-application
word into FunctionApplicationWord, and sync language definitions plus de/pl
tests and Polish unit-inflection rules so they match.

* fix clippy warnings

* Copy navigation MathML into the temporary document for no-unsafe.

`get_navigation_braille` wraps the focused node in a new `<math>` element that lives in a temporary Package. `copy_mathml` built that copy in the source document, and the result was then parented under the temporary `<math>`.

With the default sxd build an Element is a raw pointer, so that cross-document append still found the node. With --features no-unsafe an Element is only an index into its own document's arena. append_child looks that index up in the parent document, so the temporary document either panicked (index out of bounds) or attached the wrong node. navigation_braille_copies_focused_node_into_temporary_document failed only on the no-unsafe build.

`copy_mathml` and `copy_mathml_recursive` now take the destination Document and create every new element there, including the max-depth leaf. Names, attribute values, and text are copied as strings and re-interned in that document. `get_navigation_braille` passes the temporary document. The navigation intent copy passes `intent.document()`, so it stays in the same document as before.

Note: this is slightly more efficient because the temporary copy is in a different package which gets dropped/cleaned up instead of staying in the arena.

Fixes #827.

* Update the version of zip due to security concerns.
Update the MathCAT version #.

* Store in-memory file keys the same way they are looked up (#877)

* don't ignore changes to Cargo.lock (updated Rust guidance for libraries)

* update version # and zip version -- I thought I had already done this

* fix typos recently introduced into LaTeX unicode file

* Remove the tc: marker from LaTeX output (#876)

* bump version number again after some tweaks

* version # bump

* fixes after bumping zip version

* Add Windows testing matrix to CI workflow for broader platform coverage (#878)

* The rule for
```
"𝟎-𝟗":    # 0x1d7ce - 0x1d7d7
```
was wrong, resulting in a infinite loop (that gets caught).

* bump version #

* The rule for
```
"𝟎-𝟗":    # 0x1d7ce - 0x1d7d7
```
was wrong, resulting in a infinite loop (that gets caught).

* update to actual release. Need to bump number because of NVDA addon store not allowing rc suffix

* Bump anyio from 4.12.1 to 4.14.2 in /PythonScripts (#879)

Bumps [anyio](https://github.com/agronholm/anyio) from 4.12.1 to 4.14.2.
- [Release notes](https://github.com/agronholm/anyio/releases)
- [Commits](https://github.com/agronholm/anyio/compare/4.12.1...4.14.2)

---
updated-dependencies:
- dependency-name: anyio
  dependency-version: 4.14.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump h2 from 4.3.0 to 4.4.1 in /PythonScripts (#880)

Bumps [h2](https://github.com/python-hyper/h2) from 4.3.0 to 4.4.1.
- [Changelog](https://github.com/python-hyper/h2/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/python-hyper/h2/compare/v4.3.0...v4.4.1)

---
updated-dependencies:
- dependency-name: h2
  dependency-version: 4.4.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump hpack from 4.1.0 to 4.2.0 in /PythonScripts (#882)

Bumps [hpack](https://github.com/python-hyper/hpack) from 4.1.0 to 4.2.0.
- [Changelog](https://github.com/python-hyper/hpack/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/python-hyper/hpack/compare/v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: hpack
  dependency-version: 4.2.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump soupsieve from 2.8.3 to 2.9 in /PythonScripts (#883)

Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.8.3 to 2.9.
- [Release notes](https://github.com/facelessuser/soupsieve/releases)
- [Commits](https://github.com/facelessuser/soupsieve/compare/2.8.3...2.9)

---
updated-dependencies:
- dependency-name: soupsieve
  dependency-version: '2.9'
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump idna from 3.11 to 3.15 in /PythonScripts (#884)

Bumps [idna](https://github.com/kjd/idna) from 3.11 to 3.15.
- [Release notes](https://github.com/kjd/idna/releases)
- [Changelog](https://github.com/kjd/idna/blob/master/HISTORY.md)
- [Commits](https://github.com/kjd/idna/compare/v3.11...v3.15)

---
updated-dependencies:
- dependency-name: idna
  dependency-version: '3.15'
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump requests from 2.32.5 to 2.33.0 in /PythonScripts (#885)

Bumps [requests](https://github.com/psf/requests) from 2.32.5 to 2.33.0.
- [Release notes](https://github.com/psf/requests/releases)
- [Changelog](https://github.com/psf/requests/blob/main/HISTORY.md)
- [Commits](https://github.com/psf/requests/compare/v2.32.5...v2.33.0)

---
updated-dependencies:
- dependency-name: requests
  dependency-version: 2.33.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pygments from 2.19.2 to 2.20.0 in /PythonScripts (#886)

Bumps [pygments](https://github.com/pygments/pygments) from 2.19.2 to 2.20.0.
- [Release notes](https://github.com/pygments/pygments/releases)
- [Changelog](https://github.com/pygments/pygments/blob/master/CHANGES)
- [Commits](https://github.com/pygments/pygments/compare/2.19.2...2.20.0)

---
updated-dependencies:
- dependency-name: pygments
  dependency-version: 2.20.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Bump pytest from 9.0.2 to 9.0.3 in /PythonScripts (#887)

Bumps [pytest](https://github.com/pytest-dev/pytest) from 9.0.2 to 9.0.3.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/9.0.2...9.0.3)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 9.0.3
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Added explainer video (#888)

* Refine when and how much fuzzing happens on pushes. This should greatly shorten the runs when doing a push. The weekly long fuzzes should continue unchanged.

* bump crate "anyhow" to 1.0.103 (#868)

* **bump crate "dirs" to 7.0.0** (#869)

* Fix ASCIIMath bold digits translating from sans-serif digits (#874)

* fix wrong check for CheckRuleFiles which was backwards. Should help with speed a little when dealing with unicode-full.yaml

* This fixes a bug that is very unlikely for someone reading math, but sometimes showed up in running the tests in github. The failures were a test-suite pattern, not a normal speech pattern and involve reading a new unicode-full.yaml file.

The panic needs three things at once. The character has to miss the short Unicode table, its rule has to look up another character in the full table, and that inner lookup has to decide the file must be read again while the outer lookup still holds the table. Cyrillic capitals do the first two: А spells the lowercase letter, which also lives only in unicode-full.yaml. Everyday Latin math stays in the short table and never reaches this path.

The third step is the rare one. The full table is read once, then kept. With the default CheckRuleFiles value, Prefs, later lookups do not check the file's timestamp, so the inner lookup does not try to read it again. The CI runs hit that third step about 10% of the time because one test process switches language over and over, clears the file list between tests, and sometimes the next timestamp check disagrees. A screen reader does not do that between a capital and the lowercase letter it spells.

The fix is to stop a nested character lookup from reloading unicode-full.yaml while the outer lookup still holds the table.

replace_single_char inverted the CheckRuleFiles test used by read_files, so the default Prefs value re-stat'd the full table on every short-table miss. The rule is copied *only* for the lookup that reads the file; later lookups borrow it.
This adds a little extra code but avoids a copy (clone) for every unicode-full.yaml lookup.

* canonicalize double exclamation !! to speak as double factorial (#837)

* simplify definitions.rs (#826)

* simplify definitions.rs

* reintroduce manual tweaking of hashmap capacity

* refactor: simplify `is_simple` and `is_trivially_simple` logic (#825)

* refactor: simplify `is_simple` and `is_trivially_simple` logic

* Refactor is_simple function for better readability

Put logical connectors at end like rest of code.

Also align conditions

---------

Co-authored-by: NSoiffer <NSoiffer@users.noreply.github.com>

* Refactor YAML parsing functions (#835)

* refactor some more iterators in speech.rs (#867)

* Merge in 'ru' branch after several updates (#798)

* Ru - Improve wording of Russian docs (#661)

* update acknowledgment for Russian braille

* update version number because added Russian braille

* UEB has a rule for some pseudo-script chars. The match for that rule was overly broad, so I removed scripted letters and numbers. That rule made its way into other braille codes, so I removed them everywhere. All tests pass.

* Added new UEB chars. Many of these are accented letters. I added 'A' (for accented letters) so that the pattern for them looks like:
```
 - "Ñ": [t: "CA⠘⠻L⠝"]             # 0x00D1
```

Added a few tests.

* Translate German absolute-value ending

* Fix German unit plurals

* Fix German pluralization for "ruthe" and add regression test

* Correct German translations for "ruthe" to "rute" and update regression test

* Expand German unit definitions and add regression test for pluralization

* fix some symbols

* fix german set grammar

* typo in example

* Russian Braille fixes

This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs.

* Update Russian Braille rules and mappings

Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes.

* Add Russian language to docs

Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance.

- docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists.
- docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes.
- docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands).
- docs/ru/index.md: reorder supported languages and polish phrasing.

These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization.

* Refactor Russian function braille mapping

Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic.

* Addition of Russian documentation

This commit adds Russian documentation for users, developers and translators.

* typo in example

* Add vector/bar/arc mover rules and tests

Introduce three new mover rules in Rules/Braille/Russian/Russian_Rules.yaml: vector-arrow, vector-bar, and geometry-arc. Each rule detects specific accent characters (arrows: → ⟶ ⇀ ⃗; bars: ¯ ‾ _  ̄; arcs: ⌒ ⏜ ︵) and emits the appropriate braille indicators, using a different form when the base has more than one character. Add corresponding tests in tests/braille/Russian/russian.rs to cover single- and multi-character movers and combined expressions.

* Add Russian braille math functions and limits

Extend Russian braille rules to support additional math functions and large operator limits. Added new rules for large-op-limits, operator/functions (including special handling for div), and div-specific replacements in Rules/Braille/Russian/*. Updated Russian definitions and unicode mappings to include sinh/cosh/tanh/coth, arg/sgn/det/rank/rg, Re/Im, grad/rot/div, gcd/lcm and related braille codes. Updated global Rules/definitions.yaml to recognize new function names. Fixed braille cleanup in src/braille.rs to normalize div encoding. Added unit tests exercising the new function mappings and large operator limits.

* Support system left brace in Russian braille

Introduce handling for system left-brace matrices in the Russian braille rules: add InSystemLeftBrace variable, a system-left-brace-opener rule and a system-left-brace-table rule to detect brace-enclosed mtable as a system. Update row-start logic to emit appropriate braille tokens when InSystemLeftBrace is set. Add tests exercising curly-brace matrices, piecewise function (brace with conditions), and vertical-bar matrices to verify the new behavior.

* Add signed-integer and nested-fraction rules

Add handling for nested fractions and signed integers inside script positions and adjust grouping behavior for scripts in Russian braille rules. Changes introduce a nested-fraction rule for mfrac and new signed-integer-number / signed-integer rules for msub/munder and msup/mover, plus a small grouping test (⠐) when scripts contain grouped mrow with '+' or a fraction. Also add a comprehensive regression test (script_grouping_regressions) covering many sup/sub cases to ensure correct braille output. Files updated: Rules/Braille/Russian/Russian_Rules.yaml and tests/braille/Russian/russian.rs.

* Enhance Russian Braille support with mathematical symbols and rules (#589)

* Refine matrix rule handling by adding support for augmented matrices; update unit tests to include 2x3 and 3x4 augmented matrix scenarios. (#555)

* remove logging statement

* removed uneeded eprintlin!()

* Alternative fix to PR #540.
Fixes #528.

Includes the tests in the PR.

* Add Russian translation

* Fix #585 -- SSML was being escaped.

I earlier added code to prevent a SSML injection attack by escaping HTML. It was done at the wrong point and escape legal SSML. I have moved the check and conversion down to the leaves.

I was very surprised there were no useful tests with SSML. All the main tests set `TTS=None`. I added a test that catches bad escaping. I also added some tests to check for leaf-based SSML attacks and also attribute-based ones.

* bump beta number

* The range for MATHEMATICAL SANS-SERIF DIGITs had MATHEMATICAL MONOSPACE DIGITs as the match part of translate, so if one of the sans-serif digits were used, you ended up in a loop (no match, to it returned itself).

* comment out some debug statements

* Add 02DA (˚) to list of circle-like chars -- I thought I had done this already, so it might in some other branch

* Add source language option to translation audit tool

  Allow audit-translations to compare a target language against any source
  language via --source, defaulting to English for existing workflows. Update
  CLI output, docs, and tests for dynamic source/target labels, including
  Swedish/Norwegian comparison coverage.

  Normalize Rich ANSI output in tests so golden and string assertions remain
  stable when terminal color is forced.

* fix ruff

* fix ruff

* fix ruff

* Core concept names additions and tests -- issue 381 (#592)

* initial addition of core concept names

* defnitions and tests, with a few failing

* minus, plus, volume fixes. Other defined, not appearing in site

* fix clippy warnings

* use "ruamel.yaml>=0.19.1" in uv.lock and pyproject.toml

* handle canonicalize failure in containerized environments (#574)

* add 1 week dependency cooldown for uv (#599)

* fix rules for digits in german (#596)

* update navigation rules for traditional chinese (#588)

* copy english version of navigate.yaml from NVDA2026.1 and translate

* 更新 definitions.yaml 與 navigate.yaml

* update unicode-full.yaml

* 區分完全/已完全放大/縮小

* 移至->移到, 表->表格, 數學式->數學, 退出->離開, 不能->無法, 當前->目前。
overview line 47, SimpleSpeak_Rules.yaml line 39, "根號"->""。mroot.rs 對應修改

* 移到上一格 -> 往上一格,移到下一格 -> 往下一格, 佔位符 -> 書籤,設定佔位符 -> 設定書籤,已是最上層 -> 不在別的式子裡面,上一層是 -> 在後面式子裡面,字元模式 開n 次方根 的讀法,基底 -> 基、底

* 向量 -> 射線

* 基 -> 基底,底 -> 基底

* Internally in definitions.yaml, MathCAT uses "nofix", but that's not
a MathML airity property. Remove it.

* don't say "indexed by", say "sub"

* add some terse and verbose forms of speech

* fix typo

* Added an html file with all the test exprs so that translators have examples to test.

* Fix remaining erros after `definitions.yaml` was updated -- needed to add `function:`

* fix clippy warning

* minor cleanup: remove ASCIIMath-fi reference

* Refactor braille.rs to a BrailleCode trait + registry

Replace the four scattered string-match dispatch sites (cleanup,
get_braille_chars, NeedsToBeGrouped, and highlight knobs) with a single
get_braille_code() registry and a BrailleCode trait implemented per code.
Extract the duplicated UEB/Vietnam/Finnish/Swedish indicator-replacement
closures into one apply_indicator_replacements helper plus a UserTypeforms
struct. Behavior is unchanged (all tests/braille pass); adding a new code
now means one registry entry + one impl block.

Co-authored-by: Cursor <cursoragent@cursor.com>

* improved counting for (likey) 8 bit codes when figuring out braille position

* make sure this is part of repo

* add BrailleDocs to repo (stop ignoring)

* updated data, does not include ASCII letters/digits

* First version of Russian Braille

This commit adds first druft version for Russian Braille.

* typo in example

* Russian Braille fixes

This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs.

* Braille - Latin and Greek letters fixes

This commit adds a layer of Latin and Greek alphabets for Russian mathematical Braille.
* In unicode.yaml Latin and Greek letters now go through the internal markers of the alphabetic mode.
• In braille.rs russian_cleanup turns these markers into real signs:
◦ Latin lowercase: ⠠
◦ Latin uppercase: ⠨
◦ Greek lowercase: ⠰
◦ Greek uppercase: ⠸
Added tests to russian.rs to x+A+y+B, the Latin letter after the number.

* Braille - Roots, powers and scripts

This commit adds Roots, powers and scripts according to the Russian math Braille specs.

* Update Russian Braille rules and mappings

Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes.

* Add Russian language to docs

Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance.

- docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists.
- docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes.
- docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands).
- docs/ru/index.md: reorder supported languages and polish phrasing.

These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization.

* Refactor Russian function braille mapping

Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic.

* Refine Russian wording in translators guide

Clarify and naturalize Russian phrasing in the translators guide: add explicit "на русском языке" to SimpleSpeak examples; change exponent wording to "в квадрате" with example "икс в квадрате"; make fraction wording more natural ("числитель разделить на знаменатель" and example "метр за секунду"); fix "английском" → "английском языке"; and update Braille tooling phrasing to "просмотрщиком Брайля" and "дисплее Брайля" for consistency.

* Add alphabet indicators in russian_cleanup

Insert a preprocessing step that injects braille alphabet indicators before running REPLACE_INDICATORS. A new helper add_russian_alphabet_indicators scans the raw braille and emits specific indicators for 'l' (Latin lowercase), 'u' (Latin uppercase), 'g' (Greek lowercase) and 'v' (Greek uppercase), avoiding repeated indicators by tracking the current alphabet mode. The helper also resets the mode on 'C', 'N', and '#'. This keeps the existing indicator replacement and space-collapsing logic intact while preventing redundant alphabet indicators.

* Russian braille: add GOST symbols and decimals

Add GOST-style number and symbol support for Russian braille: introduce a decimal-comma rule that emits BrailleChars for integer and fractional parts with a decimal indicator, using PUA digit placeholders. Extend unicode mappings with bracket/brace/slash/backslash/pipe characters, Celsius, context-sensitive prime (′/″) handling, PUA digit-to-braille mappings, and a few arrow/connective symbols. Update tests to cover decimal numbers, fractions, set notation, Celsius, and logical arrows, and adjust expected output for geometry primes to reflect the new context-aware prime handling.

* Add vector/bar/arc mover rules and tests

Introduce three new mover rules in Rules/Braille/Russian/Russian_Rules.yaml: vector-arrow, vector-bar, and geometry-arc. Each rule detects specific accent characters (arrows: → ⟶ ⇀ ⃗; bars: ¯ ‾ _  ̄; arcs: ⌒ ⏜ ︵) and emits the appropriate braille indicators, using a different form when the base has more than one character. Add corresponding tests in tests/braille/Russian/russian.rs to cover single- and multi-character movers and combined expressions.

* Add Russian braille math functions and limits

Extend Russian braille rules to support additional math functions and large operator limits. Added new rules for large-op-limits, operator/functions (including special handling for div), and div-specific replacements in Rules/Braille/Russian/*. Updated Russian definitions and unicode mappings to include sinh/cosh/tanh/coth, arg/sgn/det/rank/rg, Re/Im, grad/rot/div, gcd/lcm and related braille codes. Updated global Rules/definitions.yaml to recognize new function names. Fixed braille cleanup in src/braille.rs to normalize div encoding. Added unit tests exercising the new function mappings and large operator limits.

* Support system left brace in Russian braille

Introduce handling for system left-brace matrices in the Russian braille rules: add InSystemLeftBrace variable, a system-left-brace-opener rule and a system-left-brace-table rule to detect brace-enclosed mtable as a system. Update row-start logic to emit appropriate braille tokens when InSystemLeftBrace is set. Add tests exercising curly-brace matrices, piecewise function (brace with conditions), and vertical-bar matrices to verify the new behavior.

* Add Russian chemistry braille rules and tests

Extend Russian braille rules to handle chemical notation: add rules for equilibrium-arrow, reaction operators, repeated charges, charges, and charge-with-subscript mappings (chemical-reaction-operator, chemistry-mhchem-equilibrium-arrow, chemistry-repeated-charge, chemistry-charge, chemistry-charge-with-subscript). Update Rules/Braille/Russian/Russian_Rules.yaml to map arrows, +/-, superscripts/subscripts and reaction operators to appropriate braille sequences. Add tests in tests/braille/Russian/russian.rs (source_chemical_reactions_and_charges) covering reaction arrows, charges, subscripts/superscripts and combined examples.

---------

Co-authored-by: Moritz Groß <hi@mgross.dev>
Co-authored-by: nsoiffer <soiffer@alum.mit.edu>
Co-authored-by: Tuomas Pyorre <82783219+ttpyorre@users.noreply.github.com>
Co-authored-by: V3nky <venkatesh6114@gmail.com>
Co-authored-by: Hon-Jang Yang <hjy1210@users.noreply.github.com>
Co-authored-by: nsoiffer <neil.soiffer@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Russian braille rules for geometry operators

Add three new braille encoding rules for geometry operators (∠, △, ∪) in Russian:
- canonical-prefix-geometry-op: handles geometry operators in canonical form
- geometry-prefix-after-canonical-multiplier: handles operators after canonical multipliers
- geometry-prefix-after-multiplier: handles operators after regular multipliers

Includes corresponding test cases for angles, triangles, unions, perpendicular, and similar symbols.

* Improve Russian braille typeform and mathvariant handling

Refactor Russian braille cleanup to properly handle typeform indicators (bold, italic) and mathvariant attributes. Introduces a dedicated typeform indicator function that manages indicator placement according to Russian Braille ГОСТ Р 58511 standard, separating typeform handling from alphabet indicators. Adds support for automatic typeform indicators based on mathvariant attributes, ensuring proper indicator pairing around content blocks. Updates unicode-full.yaml rules to reflect new typeform codes and adds comprehensive tests for various mathvariant combinations.

* Propagate mathvariant attribute to children

Add mathvariant inheritance from parent elements to children that lack their own mathvariant attribute. This ensures consistent styling throughout nested elements. Includes tests for bold text propagation through mstyle and mrow containers, and verifies that explicit child mathvariants override inherited values.

* Fix clippy warnings and improve code style

- Remove unnecessary borrows in format! and display calls
- Change string slices to arrays in trim_end_matches calls
- Replace expect(&format!(...)) with unwrap_or_else(|_| panic!(...))
- Add clippy allow attributes to suppress false positives in tests
- Add missing newline at end of build.rs

* Remove unnecessary borrows in format macros

Remove redundant `&` reference operators from arguments passed to format! macros and similar functions. These values are automatically borrowed by the format machinery, so explicit borrows are unnecessary and reduce readability.

* Normalize Unicode math digits in decimal numbers

Add normalization of Unicode mathematical alphanumeric symbols (Plane 1 digits) to ASCII digits for number nodes containing decimal points. Skip canonicalize_plane1 processing for already-normalized nodes to prevent double processing.

* Remove unnecessary references and .to_string() calls

Remove unnecessary `&` borrows in format arguments and replace `.to_string()` with direct display. Simplify byte array literals to byte string literals for readability and idiomatic Rust style.

* Remove mathvariant propagation and update tests

Drop implicit propagation of the mathvariant attribute from canonicalization (removed propagate_inherited_mathvariant call and helper functions) and update Russian braille tests to set mathvariant explicitly on elements (replace mstyle with mrow and move attributes to mi). Changes in src/canonicalize.rs and tests/braille/Russian/russian.rs. This makes mathvariant handling explicit in tests and avoids mutating child nodes during canonicalization.

* Remove math digit normalization for mn nodes

Delete the conditional normalization of mathematical digit characters for <mn> text and remove the normalize_math_digits helper from src/canonicalize.rs. Previously the code converted several Unicode math-digit codepoints to ASCII when a decimal point was present.

* Adjust test expectations and unignore tests

Update expected spoken/braille test outputs across multiple language test files to match updated speech rules (punctuation handling, table phrasing, and unit/roman-numeral rendering). Removed several #[ignore] annotations for the previously skipped "ignore_period_and_space" tests. Minor tidy in pl/definitions.rs to avoid unnecessary format! usage and fix EOF newline. Affected files include tests/Languages/* for en, fi, fr, hu, nb, pl, sv, vi, and zh/tw.

* Bump to 0.7.6-beta.8 and update dependencies

Update to version 0.7.6-beta.8 with the following changes:

- Migrate to no-unsafe variants of sxd-document and sxd-xpath
- Add 'no-unsafe' feature flag for unsafe-free builds
- Update dependencies: strum, regex, bitflags, env_logger
- Add tempfile dev-dependency
- Improve build.rs: add rerun-if-changed directives earlier and fix indentation
- Exclude BrailleDocs from package

* Switch to sxd_document_no_unsafe and NameStr

Replace sxd_document/sxd_xpath usages with no-unsafe variants and adapt string types. Key changes in src/braille.rs, src/canonicalize.rs, and src/chemistry.rs: introduce NameStr (feature-gated), use as_str!/as_qname! helpers, convert many attribute_value calls to .as_deref(), update XPath error constructions, and store operator chars as NameStr with PhantomData in OperatorPair. Refactor chemistry tests to helper-based Result flow and use sxd_document_no_unsafe::parser. These edits make the code compatible with the "no-unsafe" pathway and fix lifetime/string mismatches across the codebase.

* Use no-unsafe sxd crates; adapt code & tests

Replace sxd_document/sxd_xpath with sxd_document_no_unsafe and sxd_xpath_no_unsafe and update imports (including parser). Adjust attribute handling and lifetimes: use as_str!, attribute_value(...).as_deref().unwrap_or_default(), and similar changes to avoid unsafe string lifetimes. Refactor tests to return Result, use ? and panic-catching, and update assertions (e.g. is_empty). Re-enable sample MathML in main, tweak timing_test (disable heavy loops, change loop count). Misc small fixes to satisfy the new APIs and lifetime semantics.

* Use no-unsafe sxd libs; refactor TTS & parsing

Switch project XML/XPath handling to sxd_document_no_unsafe / sxd_xpath_no_unsafe and adapt code to use as_str!/as_qname! helpers. Update speech and pretty-print logic to use the new context and node APIs, tighten unicode handling, and add debug checks. Refactor TTS APIs so command value accessors return Result and TTS string builders propagate errors. Update tests and prefs helpers (temporary rules dir helper, prefs test flow). Misc: small capacity tweaks and wording fixes in error messages. Files changed: src/prefs.rs, src/pretty_print.rs, src/speech.rs, src/tts.rs.

* Improve wording in RU user guide

Replace "Трудности в обучении" with "Когнитивные нарушения" in docs/ru/User_guide_for_MathCAT_ru.md to use a more precise and respectful term for users who need shorter spoken output.

* Add Russian GOST-58511 typeform braille rules and tests

Introduce three Russian GOST-58511 typeform/layout braille rules (small-font, letter-spacing, insertion) to Rules/Braille/Russian/Russian_Rules.yaml with their corresponding braille markers. Expand and reorganize tests in tests/braille/Russian/russian.rs: add coverage for the new typeform indicators, interaction cases, menclose/table/braces, and other edge cases; rename several tests to reflect sectioned/aggregated variants. Also adjust expected braille outputs for a few existing tests (sqrt and geometry matrix cases) to match the new rules/formatting.

---------

Co-authored-by: nsoiffer <soiffer@alum.mit.edu>
Co-authored-by: mgros <hi@mgross.dev>
Co-authored-by: Tuomas Pyorre <82783219+ttpyorre@users.noreply.github.com>
Co-authored-by: V3nky <venkatesh6114@gmail.com>
Co-authored-by: Hon-Jang Yang <hjy1210@users.noreply.github.com>
Co-authored-by: nsoiffer <neil.soiffer@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* force bring over the files from main -- merging didn't do the right thing

* Ru - handle lone log/ln; suppress nav announcements (#681)

* ru: handle lone log/ln; suppress nav announcements

Add handling for lone mi elements 'log' and 'ln' in ClearSpeak (supports Verbosity and ClearSpeak_Log LnAsNaturalLog option). Add a navigation rule to avoid announcing "in/out of" for prefix or silent intents that have no NavigationParts. Include unit tests: new ClearSpeak tests for individual log/ln, new navigate tests for suppressed base announcements, and register the navigate test module.

* Russian braille: bracket/arrow mappings & tests

Add contextual bracket-recognition rules (square and angle) to Rules/Braille/Russian/Russian_Rules.yaml and add Unicode braille mappings for ≶, ≷, ⟨, ⟩, ↑, ↓ in Rules/Braille/Russian/unicode.yaml. Extend tests in tests/braille/Russian/russian.rs to cover Russian Braille GOST 2019 order relations, large vertical arrows, and contextual bracket recognition for square and angle brackets, validating the expected braille output.

* Russian braille: GOST fixes and digit grouping (#716)

* Russian braille: GOST fixes and digit grouping

Implement GOST-related Russian braille changes: update YAML rules to refine contextual bracket/angle recognition and remap numeric/private-use codepoints for periodic/number handling. Add division-sign and arrow mappings and an internal digit-group separator. Modify src/braille.rs to ignore the internal separator when detecting digits, replace it with braille point-3 in cleanup, and add add_russian_digit_group_separators (skip for column-arithmetic) to insert grouping separators only for long integers. Update many Russian braille tests to reflect new numeric grouping, arrow/±/÷ behavior, and bracket recognition across structural boundaries.

* Retry CI after GitHub Actions outage

* Russian braille: add unicode mappings and tests

Rename Russian braille typeform rules (small-font, letter-spacing, insertion) and relax angle-bracket matching to include legacy 〈/〉 with adjusted test for selection. Add many verified Unicode braille mappings (℉, ∓, ∕, ⁄, arrows, negation, tilde, ⟂, precomposed vulgar fractions, superscript/subscript digits, etc.) in Rules/Braille/Russian/unicode.yaml. Update rus comments in src/braille.rs for clarity. Rename numerous tests to russian_* and add new unit tests for fraction slashes, precomposed fractions, additional symbols, and script digits (tests/braille/Russian/russian.rs).

* Fix Russian speech rules and expand tests (#767)

* Fix Russian speech rules and expand tests

To clarify the Russian mathematical terminology, the names of letters,
the reading of rare symbols, integrals and geometric objects.

Correct the matching of service words, numerical forms of units
of measurement, and phrases for reviewing matrices and tables. Add compatibility
with 19 character groups and fix MathJax arrow matches.

Extend SimpleSpeak and ClearSpeak checks in the following modes
Terse, Medium, and Verbose, including designed alphabets,
MathType symbols, chemical designations, and capital letter settings.

Update
markers of confirmed transfers to T, OT, and SPELL.
To keep the MathType U+F220–U+F236 table unconfirmed:
a reliable character match has not yet been established.

* Fix Russian unicode rule key

Correct the Russian unicode-full rule entry by changing the key from `spell` to `SPELL`. This aligns the mapping with the expected rule format and preserves the Greek-letter pronunciation for the affected Unicode range.

* Use tuple patterns; update RU navigation tests

Refactor several functions to use tuple assignments and pattern matching for clearer, immutable bindings: canonicalize.rs (first/last child), chemistry.rs (prescripts/postscripts), and tts.rs (before/after lengths). Also update Russian navigation tests in navigate.rs (expected speech strings and a clarifying comment) to reflect the changed phrasing/cue for fractions inside square roots. These are purely refactors and test expectation updates to match the new output wording.

* Refactor variable assignments in several modules

Replace tuple-based temporary returns with explicit variable bindings for clarity and to avoid borrow/lifetime issues. Changes:
- canonicalize.rs: declare first_child/last_child before branch and then convert to elements; simplifies fence-detection logic.
- chemistry.rs: assign prescripts/postscripts to named slices instead of returning a tuple.
- tts.rs: compute before_len/after_len via separate assignments inside match instead of returning a tuple.
No behavioral changes intended; purely refactors for readability and safety.

* Update Russian MathCAT docs

This change refreshes the Russian user and helper documentation, expanding the translator guidance and improving the structure of the setup, navigation, and settings sections. It also standardizes the MathCAT branding in the Russian titles and wording.

* Ru - Fixing conflicts PR #798 (#799)

* Initial commit for French braille.
24 test passes, 3 test failures

* Added a table of atomic numbers for the elements and added a test for atomic numbers in prescripts to bump up their liklihood number. This allows single character elements with pre subscripts that match their atomic number to be considered chemistry.

* renamed test

* Update braille.rs to compile after merging.

Add French to the braille testing file.
New French braille tests.

* Updated to main.  Needed to update the format of the tests to match the others.

* It appears some logic was missing parens -- not sure why this always worked...

* Lots more cleanup of tests, additions of rules.

Now at 78 pass/15 fail.

* fix clippy warning

* More fixes for French braille. Now at 89 correct, 8 fail, with most of the failures being open questions.

* pass through braille chars as is

* UEB has a rule for some pseudo-script chars. The match for that rule was overly broad, so I removed scripted letters and numbers. That rule made its way into other braille codes, so I removed them everywhere. All tests pass.

* Added new UEB chars. Many of these are accented letters. I added 'A' (for accented letters) so that the pattern for them looks like:
```
 - "Ñ": [t: "CA⠘⠻L⠝"]             # 0x00D1
```

Added a few tests.

* change small spaces from 'W' to ''

* add 'H' for Hebrew alphabet indicator

* promote munder/mover/munderover base attrs to element -- prevents error!() message. May want to change the promoted name...

* all primes on chem symbols such as R' -- used to be a reject, now neutral

* Fix leftover value for G1 indicator -- unlike UEB, French braille numbers don't conflict with letters.

Add code to handle `;` special case if there are "blocks" in the expr

* Lots of fixes and addition of tests.
Now at 185 passed; 4 failed

* Add "(eau)" as a state (for French)

Added several more chem arrows.

* add more arrows. French at the moment doesn't seem to distinguish between different arrows that go in the same direction. That seems wrong, but the spec is silent on the alternatives

* removed some intent attrs that were not needed

* uncomment a determinant test after fixing the braille

* Add rules for multiple lines in a script position done via an mtable or a more restricted version for nested munder/mover. They add a "," between lines.

This was done for limit_x_tends_1_14_2_01_corrected and limit_x_tends_1_14_2_02_corrected

* a linebreaks to MathML to make it easier to read

* add more arrow chars -- I thought I did this in a previous commit, but apparently not.

* Add MathJax demo link to developer docs for rendering inspection

* .

* Merge changes (mostly unicode) from hu branch (#688)

* Add few tweaks into the Rules/Languages/hu/definitions.yaml file, Added missing definitions.rs test file the test/Languages/hu directory, and added SimpleSpeak test directory the mroot and menclose tests (#646)

* Do more localization in Rules/Languages/hu/definitions.yaml

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Add in Simplespeak tests the definition, mroot and menclose tests

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* fix some werbs in intervals into rules/Languages/hu/ClearSpeak_rules.yaml and SimpleSpeak_rules.yaml

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Fix tests after interval Clearspeak and SimpleSpeak verb change

Signed-off-by: Attila Hammer <hammera@pickup.hu>

---------

Signed-off-by: Attila Hammer <hammera@pickup.hu>

* Apply Hungarian unicode fixes from PR #673

---------

Signed-off-by: Attila Hammer <hammera@pickup.hu>
Co-authored-by: hammera <hammera@pickup.hu>

* Ru -- merge speech changes into main (#689)

* Ru - Improve wording of Russian docs (#661)

* update acknowledgment for Russian braille

* update version number because added Russian braille

* UEB has a rule for some pseudo-script chars. The match for that rule was overly broad, so I removed scripted letters and numbers. That rule made its way into other braille codes, so I removed them everywhere. All tests pass.

* Added new UEB chars. Many of these are accented letters. I added 'A' (for accented letters) so that the pattern for them looks like:
```
 - "Ñ": [t: "CA⠘⠻L⠝"]             # 0x00D1
```

Added a few tests.

* Translate German absolute-value ending

* Fix German unit plurals

* Fix German pluralization for "ruthe" and add regression test

* Correct German translations for "ruthe" to "rute" and update regression test

* Expand German unit definitions and add regression test for pluralization

* fix some symbols

* fix german set grammar

* typo in example

* Russian Braille fixes

This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs.

* Update Russian Braille rules and mappings

Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes.

* Add Russian language to docs

Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance.

- docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists.
- docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes.
- docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands).
- docs/ru/index.md: reorder supported languages and polish phrasing.

These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization.

* Refactor Russian function braille mapping

Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic.

* Addition of Russian documentation

This commit adds Russian documentation for users, developers and translators.

* typo in example

* Add vector/bar/arc mover rules and tests

Introduce three new mover rules in Rules/Braille/Russian/Russian_Rules.yaml: vector-arrow, vector-bar, and geometry-arc. Each rule detects specific accent characters (arrows: → ⟶ ⇀ ⃗; bars: ¯ ‾ _  ̄; arcs: ⌒ ⏜ ︵) and emits the appropriate braille indicators, using a different form when the base has more than one character. Add corresponding tests in tests/braille/Russian/russian.rs to cover single- and multi-character movers and combined expressions.

* Add Russian braille math functions and limits

Extend Russian braille rules to support additional math functions and large operator limits. Added new rules for large-op-limits, operator/functions (including special handling for div), and div-specific replacements in Rules/Braille/Russian/*. Updated Russian definitions and unicode mappings to include sinh/cosh/tanh/coth, arg/sgn/det/rank/rg, Re/Im, grad/rot/div, gcd/lcm and related braille codes. Updated global Rules/definitions.yaml to recognize new function names. Fixed braille cleanup in src/braille.rs to normalize div encoding. Added unit tests exercising the new function mappings and large operator limits.

* Support system left brace in Russian braille

Introduce handling for system left-brace matrices in the Russian braille rules: add InSystemLeftBrace variable, a system-left-brace-opener rule and a system-left-brace-table rule to detect brace-enclosed mtable as a system. Update row-start logic to emit appropriate braille tokens when InSystemLeftBrace is set. Add tests exercising curly-brace matrices, piecewise function (brace with conditions), and vertical-bar matrices to verify the new behavior.

* Add signed-integer and nested-fraction rules

Add handling for nested fractions and signed integers inside script positions and adjust grouping behavior for scripts in Russian braille rules. Changes introduce a nested-fraction rule for mfrac and new signed-integer-number / signed-integer rules for msub/munder and msup/mover, plus a small grouping test (⠐) when scripts contain grouped mrow with '+' or a fraction. Also add a comprehensive regression test (script_grouping_regressions) covering many sup/sub cases to ensure correct braille output. Files updated: Rules/Braille/Russian/Russian_Rules.yaml and tests/braille/Russian/russian.rs.

* Enhance Russian Braille support with mathematical symbols and rules (#589)

* Refine matrix rule handling by adding support for augmented matrices; update unit tests to include 2x3 and 3x4 augmented matrix scenarios. (#555)

* remove logging statement

* removed uneeded eprintlin!()

* Alternative fix to PR #540.
Fixes #528.

Includes the tests in the PR.

* Add Russian translation

* Fix #585 -- SSML was being escaped.

I earlier added code to prevent a SSML injection attack by escaping HTML. It was done at the wrong point and escape legal SSML. I have moved the check and conversion down to the leaves.

I was very surprised there were no useful tests with SSML. All the main tests set `TTS=None`. I added a test that catches bad escaping. I also added some tests to check for leaf-based SSML attacks and also attribute-based ones.

* bump beta number

* The range for MATHEMATICAL SANS-SERIF DIGITs had MATHEMATICAL MONOSPACE DIGITs as the match part of translate, so if one of the sans-serif digits were used, you ended up in a loop (no match, to it returned itself).

* comment out some debug statements

* Add 02DA (˚) to list of circle-like chars -- I thought I had done this already, so it might in some other branch

* Add source language option to translation audit tool

  Allow audit-translations to compare a target language against any source
  language via --source, defaulting to English for existing workflows. Update
  CLI output, docs, and tests for dynamic source/target labels, including
  Swedish/Norwegian comparison coverage.

  Normalize Rich ANSI output in tests so golden and string assertions remain
  stable when terminal color is forced.

* fix ruff

* fix ruff

* fix ruff

* Core concept names additions and tests -- issue 381 (#592)

* initial addition of core concept names

* defnitions and tests, with a few failing

* minus, plus, volume fixes. Other defined, not appearing in site

* fix clippy warnings

* use "ruamel.yaml>=0.19.1" in uv.lock and pyproject.toml

* handle canonicalize failure in containerized environments (#574)

* add 1 week dependency cooldown for uv (#599)

* fix rules for digits in german (#596)

* update navigation rules for traditional chinese (#588)

* copy english version of navigate.yaml from NVDA2026.1 and translate

* 更新 definitions.yaml 與 navigate.yaml

* update unicode-full.yaml

* 區分完全/已完全放大/縮小

* 移至->移到, 表->表格, 數學式->數學, 退出->離開, 不能->無法, 當前->目前。
overview line 47, SimpleSpeak_Rules.yaml line 39, "根號"->""。mroot.rs 對應修改

* 移到上一格 -> 往上一格,移到下一格 -> 往下一格, 佔位符 -> 書籤,設定佔位符 -> 設定書籤,已是最上層 -> 不在別的式子裡面,上一層是 -> 在後面式子裡面,字元模式 開n 次方根 的讀法,基底 -> 基、底

* 向量 -> 射線

* 基 -> 基底,底 -> 基底

* Internally in definitions.yaml, MathCAT uses "nofix", but that's not
a MathML airity property. Remove it.

* don't say "indexed by", say "sub"

* add some terse and verbose forms of speech

* fix typo

* Added an html file with all the test exprs so that translators have examples to test.

* Fix remaining erros after `definitions.yaml` was updated -- needed to add `function:`

* fix clippy warning

* minor cleanup: remove ASCIIMath-fi reference

* Refactor braille.rs to a BrailleCode trait + registry

Replace the four scattered string-match dispatch sites (cleanup,
get_braille_chars, NeedsToBeGrouped, and highlight knobs) with a single
get_braille_code() registry and a BrailleCode trait implemented per code.
Extract the duplicated UEB/Vietnam/Finnish/Swedish indicator-replacement
closures into one apply_indicator_replacements helper plus a UserTypeforms
struct. Behavior is unchanged (all tests/braille pass); adding a new code
now means one registry entry + one impl block.

Co-authored-by: Cursor <cursoragent@cursor.com>

* improved counting for (likey) 8 bit codes when figuring out braille position

* make sure this is part of repo

* add BrailleDocs to repo (stop ignoring)

* updated data, does not include ASCII letters/digits

* First version of Russian Braille

This commit adds first druft version for Russian Braille.

* typo in example

* Russian Braille fixes

This commit adds fixes to Russian Braille. Now the test with parentheses is passed. Also there are devision and multiplication signs have been modified according to the Russian braille specs.

* Braille - Latin and Greek letters fixes

This commit adds a layer of Latin and Greek alphabets for Russian mathematical Braille.
* In unicode.yaml Latin and Greek letters now go through the internal markers of the alphabetic mode.
• In braille.rs russian_cleanup turns these markers into real signs:
◦ Latin lowercase: ⠠
◦ Latin uppercase: ⠨
◦ Greek lowercase: ⠰
◦ Greek uppercase: ⠸
Added tests to russian.rs to x+A+y+B, the Latin letter after the number.

* Braille - Roots, powers and scripts

This commit adds Roots, powers and scripts according to the Russian math Braille specs.

* Update Russian Braille rules and mappings

Fix YAML replace syntax and extend Russian braille rules: add chemical-formula-operator-bond and prefix-geometry-ops rules, broaden function-name matching (include tg/ctg variants) and add separator markers. Update unicode mappings (degree sign, invisible characters, conditional ∪ mapping, parallel/arrow glyphs) and adjust several function/operator braille outputs. Add/expand tests to cover functions, geometry, matrices and chemical formulas to validate the changes.

* Add Russian language to docs

Add Russian (ru) to the list of supported speech languages and update Russian documentation and translation guidance.

- docs/User_guide_for_MathCAT_en.md, docs/index.md: add Russian (ru) to supported languages lists.
- docs/ru/User_guide_for_MathCAT_ru.md: include Russian in language options, reorder entries and apply minor wording fixes.
- docs/ru/helpers.md: clarify wording, update examples and test instructions to reference ru instead of fr, and improve translation guidance and examples (including test and cargo commands).
- docs/ru/index.md: reorder supported languages and polish phrasing.

These edits enable and document Russian support and improve clarity for translators and developers working on the Russian localization.

* Refactor Russian function braille mapping

Replace hardcoded function-name branches in Russian_Rules.yaml with a DefinitionValue lookup that pulls from a new RussianFunctionBraille mapping. Add RussianFunctionBraille entries to definitions.yaml (private-use codepoints U+E100–U+E10E) and map those codepoints to braille strings in unicode.yaml. Add a test covering tan/cot rendering. This centralizes function braille representations and simplifies the rule logic.

* Refine Russian wording in translators guide

Clarify and naturalize Russian phrasing in the translators guide: add explicit "на русском языке" to SimpleSpeak examples; change exponent wording to "в квадрате" with example "икс в квадрате"; make fraction wording more n…
@moritz-gross

Copy link
Copy Markdown
Collaborator

I'm surprised that the ja branch seems so outdated. Wasn't main merged back into it pretty recently?

Should I take care of that? In the end, the "Files Changed" tab should only show changed related to the Japanese translation, right?

@NSoiffer

Copy link
Copy Markdown
Collaborator Author

Lock file is not right. I'll commit, pull, fix (regenerate), and push.

@NSoiffer
NSoiffer merged commit 6078793 into main Oct 28, 2026
10 of 25 checks passed
@github-project-automation github-project-automation Bot moved this from Triage to Done in MathCAT Project Board Oct 28, 2026
@moritz-gross

Copy link
Copy Markdown
Collaborator

2s
Run cargo build --features no-unsafe --verbose
error: failed to parse lock file at: /home/runner/work/MathCAT/MathCAT/Cargo.lock

Caused by:
  package `defmt-macros` is specified twice in the lockfile
Error: Process completed with exit code 101.

hm so the lockfile was destroyed during the merge. Do cargo build and overwrite it?

@moritz-gross

Copy link
Copy Markdown
Collaborator

btw I just noticed that the "Sync Fork" button on GitHub is branch-specific. So my ja branch was outdated while I tried to look into it locally.

@moritz-gross

Copy link
Copy Markdown
Collaborator

Next time we should do fixes on cargo.lock on the PR branch instead of main, to keep the history cleaner imo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants