Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
578732b
Addition of Russian documentation
Kostenkov-2021 Jun 1, 2026
ce7c227
First version of Russian Braille
Kostenkov-2021 Jun 1, 2026
0ce956d
typo in example
NSoiffer Jun 1, 2026
065255f
Enhance Russian Braille support with mathematical symbols and rules (…
Kostenkov-2021 Jun 25, 2026
aa7d161
Ru - Add signed-integer and nested-fraction rules (#610)
Kostenkov-2021 Jun 25, 2026
68acf20
Ru - Add braille label marks and tests (#616)
Kostenkov-2021 Aug 1, 2026
1668344
Merge branch 'main' into ru
NSoiffer Aug 2, 2026
d5e460c
Merge branch 'main' into ru
NSoiffer Aug 3, 2026
6e2bc86
Merge branch 'main' into ru
NSoiffer Aug 3, 2026
cf0f8f4
Fix Russian braille chars for no-unsafe InternedString API
NSoiffer Aug 3, 2026
ef9e74f
Ru - Russian Braille tests addition and enhance no-unsafe builds with…
Kostenkov-2021 Aug 3, 2026
7c8bee2
add else clause to make build.rs not check the rules dir if 'include-…
NSoiffer Aug 3, 2026
2bea116
Merge origin/ru; keep local Rules/src, origin Russian braille tests
NSoiffer Aug 3, 2026
8538575
missing remove of CHEM_STATE if not chemistry
NSoiffer Aug 4, 2026
44ab000
remove #[ignore] from tensor() test -- fixed via chemistry fix
NSoiffer Aug 4, 2026
f1f5983
Removed `#ignore[]` from `test_up_to_date`.
NSoiffer Aug 4, 2026
98a9fe7
Up the sleep time after a change. This failed on github but not local…
NSoiffer Aug 4, 2026
070d9db
turn off panic handling for tests. I can't see what test_up_to_date i…
NSoiffer Aug 4, 2026
308a239
guard 'use std::panic'
NSoiffer Aug 4, 2026
a100b11
copy and then modify prefs.rs so all prefs are present on the CI machine
NSoiffer Aug 4, 2026
0c9ec31
move back to 5ms as timeout was not the problem
NSoiffer Aug 4, 2026
ff8a6a8
better version of suggested by AI that does not require turning it o…
NSoiffer Aug 4, 2026
e87d74f
update acknowledgment for Russian braille
NSoiffer Aug 4, 2026
66a78c6
update version number because added Russian braille
NSoiffer Aug 4, 2026
37f1d19
UEB has a rule for some pseudo-script chars. The match for that rule …
NSoiffer Aug 5, 2026
62ea156
Added new UEB chars. Many of these are accented letters. I added 'A' …
NSoiffer Aug 5, 2026
697579d
Translate German absolute-value ending
moritz-gross Aug 6, 2026
98fbc3e
Merge pull request #650 from moritz-gross/fix/de-absolute-value-end
moritz-gross Aug 6, 2026
c39d867
Fix German unit plurals
moritz-gross Aug 6, 2026
871a153
Merge remote-tracking branch 'upstream/main' into fix/de-unit-plurals
moritz-gross Aug 6, 2026
c0b575f
Merge pull request #651 from moritz-gross/fix/de-unit-plurals
moritz-gross Aug 6, 2026
bc97cbe
Fix German pluralization for "ruthe" and add regression test
moritz-gross Aug 6, 2026
71e068f
Correct German translations for "ruthe" to "rute" and update regressi…
moritz-gross Aug 6, 2026
206c0dc
Merge pull request #652 from moritz-gross/fix/typo-rute-ruthe
moritz-gross Aug 6, 2026
8afdcc7
Expand German unit definitions and add regression test for pluralization
moritz-gross Aug 6, 2026
9bc557d
Merge pull request #653 from moritz-gross/fix/more-weird-unit-plurals
moritz-gross Aug 6, 2026
31858f4
fix some symbols
moritz-gross Aug 6, 2026
0d7a362
Merge pull request #654 from moritz-gross/fix/some-unicode-full-symbols
moritz-gross Aug 6, 2026
d588bdc
fix german set grammar
moritz-gross Aug 6, 2026
3608ca9
Merge pull request #655 from moritz-gross/fix/german-set-grammar
moritz-gross Aug 6, 2026
45aefb2
make uptack speak as 'perpendicular' since so many editors generate i…
NSoiffer Aug 7, 2026
a363707
Merge branch 'main' of https://github.com/daisy/MathCAT
NSoiffer Aug 7, 2026
a42901b
update AGENTS.md to include GitHub CLI check
moritz-gross Aug 9, 2026
ce27413
Merge pull request #664 from moritz-gross/add-gh-cli-to-agents-md
moritz-gross Aug 9, 2026
acd5cfb
Fix assertions in chemistry.rs that could never fail (#551) (#665)
yasumorishima Aug 10, 2026
d154365
remove duplicate definition of convert_last_char_to_number (#663)
moritz-gross Aug 10, 2026
87da432
Refactor `is_complex` logic for clarity and add unit tests for YAML c…
moritz-gross Aug 11, 2026
9fd30c1
merging in latest update to Russian (avoiding all the other things th…
NSoiffer Aug 11, 2026
d1a3927
Refactor `RulesFor` to use `strum_macros::Display` and remove custom …
moritz-gross Aug 11, 2026
468054a
missing ! before condition (#657)
moritz-gross Aug 11, 2026
1c87cc6
simplify TTS using strum (#667)
moritz-gross Aug 11, 2026
b230d91
Exclude `definitions.yaml` from audits and update related tests and d…
moritz-gross Aug 11, 2026
4096a29
Remove unused `error` field and error handling logic from `SpeechRule…
moritz-gross Aug 12, 2026
ab01a5d
Remove `error` field and related checks from `PreferenceManager` (#669)
moritz-gross Aug 12, 2026
bfb6b07
Refactor `strum` dependency to use `derive` feature and replace `stru…
moritz-gross Aug 12, 2026
ca0e9dd
Recognize lg as an ASCIIMath function (#672)
luantaraschi Aug 12, 2026
42ec87e
regex XML_COMMENT is buggy, and xml comments are ignored by parser an…
moritz-gross Aug 12, 2026
d0c900b
update translation of traditioanl chinese based on english version da…
hjy1210 Aug 12, 2026
9c2a725
fix sqrt rule that emitted no speech for 'square root' if not terse. …
NSoiffer Aug 12, 2026
843e554
Merge branch 'main' of https://github.com/daisy/MathCAT
NSoiffer Aug 12, 2026
2730e7c
Add initial translation for the navigation rules
NSoiffer Aug 12, 2026
ec36e05
add `CommandOffset` instead of using `string-length($Prefix)+1` so th…
NSoiffer Aug 12, 2026
94e6bf1
Add few tweaks into the Rules/Languages/hu/definitions.yaml file, Add…
hammera Aug 11, 2026
b89632e
Rules/Languages/Hu/unicode.yaml: Remove 0x2034, 0x2217, 0x2237, 0x226…
hammera Aug 12, 2026
4dba893
Rules/Languages/hu/unicode-full.yaml: Fix translator audit errors aft…
hammera Aug 12, 2026
49bd791
Rules/Languages/hu/navigate.yaml: sync condition differences after af…
hammera Aug 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,5 +57,3 @@ Rules/*/*/*.zip
docs/_site
docs/.jekyll-cache
docs/Gemfile.lock

docs/nb_of_norway/
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ but add common mistakes of AI agents here instead.
- Keep code/rule changes focused and validate with targeted tests first: `cargo test <relevant-tests>`
- do not do any git commands unless explicitly asked for
- Rust coverage is in `target/coverage/`.
- When working with GitHub, e.g. looking at PRs and issues, check if the GitHub CLI is installed (`gh --version`).

## Fuzzing (`fuzz/` + cargo-fuzz)
- Install: `cargo install cargo-fuzz`, use a **nightly** toolchain (`rustup run nightly cargo fuzz …`).
Expand Down
5 changes: 2 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "mathcat"
version = "0.7.6-beta.8"
version = "0.7.6-beta.9"
authors = ["Neil Soiffer <soiffer@alum.mit.edu>"]
license = "MIT"
description = "MathCAT: Math Capable Assistive Technology ('Speech and braille from MathML')"
Expand All @@ -23,8 +23,7 @@ sxd-document-no-unsafe = "0.4.2"
sxd-xpath-no-unsafe = "0.5.1"
yaml-rust = "0.4"
# yaml-rust = { version = "0.11", package = "yaml-rust2" }
strum = "0.28"
strum_macros = "0.28"
strum = { version = "0.28", features = ["derive"] }
anyhow = "1.0"
regex = "1.12.3"
dirs = "6.0"
Expand Down
4 changes: 4 additions & 0 deletions PythonScripts/audit_translations/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ The tool automatically adjusts its matching logic based on the file type:
* Matches rules based on character/range keys.
* *Examples:* `unicode.yaml`, `unicode-full.yaml` (keys like `a-z`, `!`, `0-9`).

`definitions.yaml` is intentionally excluded from audits *for now*. It does not have the same semantics
as normal rules, so the tool ignores it during automatic file discovery and when it is passed to
`--file`.

---

### ⚙️ Usage & Commands
Expand Down
21 changes: 17 additions & 4 deletions PythonScripts/audit_translations/auditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ def get_rules_dir(rules_dir: str | None = None) -> Path:
return package_dir.parent.parent / "Rules" / "Languages"


def is_definitions_file(file_path: str | Path) -> bool:
"""Return if the file name is definitions.yaml, which is not yet supported."""
return Path(file_path).name == "definitions.yaml"


def get_yaml_files(lang_dir: Path, region_dir: Path | None = None) -> list[Path]:
"""Get all YAML files to audit for a language, including region overrides."""
files: set[Path] = set()
Expand All @@ -39,12 +44,13 @@ def collect_from(directory: Path, root: Path) -> None:
if not directory.exists():
return
for f in directory.glob("*.yaml"):
if f.name != "prefs.yaml": # Skip prefs.yaml as it's not translated
if f.name != "prefs.yaml" and not is_definitions_file(f):
files.add(f.relative_to(root))
shared_dir = directory / "SharedRules"
if shared_dir.exists():
for f in shared_dir.glob("*.yaml"):
files.add(f.relative_to(root))
if not is_definitions_file(f):
files.add(f.relative_to(root))

collect_from(lang_dir, lang_dir)
if region_dir:
Expand Down Expand Up @@ -144,7 +150,11 @@ def audit_language(
verbose: bool = False,
source_language: str = "en",
) -> int:
"""Audit translations for a specific language. Returns total issue count."""
"""Audit translations for a specific language and return the total issue count.

``specific_file`` is the relative file path supplied by the CLI's ``--file``
option. When set, the audit is limited to that file.
"""
rules_dir_path = get_rules_dir(rules_dir)

source_base_language, source_region = split_language_into_base_and_region(source_language)
Expand All @@ -168,7 +178,10 @@ def audit_language(
raise AuditError(f"Target region directory not found: {translated_region_dir}")

# Get list of files to audit
files = [specific_file] if specific_file else get_yaml_files(source_dir, source_region_dir)
if specific_file:
files = [] if is_definitions_file(Path(specific_file)) else [specific_file]
else:
files = get_yaml_files(source_dir, source_region_dir)

print_audit_header(language, len(files), source_language)

Expand Down
28 changes: 28 additions & 0 deletions PythonScripts/audit_translations/tests/test_auditor.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,34 @@ def test_get_yaml_files_includes_region(tmp_path) -> None:
assert set(files) == {Path("base.yaml"), Path("SharedRules/shared.yaml"), Path("unicode.yaml")}


def test_get_yaml_files_ignores_definitions(tmp_path) -> None:
"""Definitions files are excluded from automatic audit discovery."""
lang_dir = tmp_path / "lang"
shared_dir = lang_dir / "SharedRules"
shared_dir.mkdir(parents=True)
(lang_dir / "rules.yaml").write_text("---", encoding="utf-8")
(lang_dir / "definitions.yaml").write_text("---", encoding="utf-8")
(shared_dir / "definitions.yaml").write_text("---", encoding="utf-8")

assert get_yaml_files(lang_dir) == [Path("rules.yaml")]


def test_audit_language_ignores_explicit_definitions_file(tmp_path, fixed_console_width) -> None:
"""Passing definitions.yaml through --file produces an empty audit."""
rules_dir = tmp_path / "Rules" / "Languages"
(rules_dir / "en").mkdir(parents=True)
(rules_dir / "de").mkdir(parents=True)

with console.capture() as capture:
total_issues = audit_language("de", specific_file="definitions.yaml", rules_dir=str(rules_dir))
output = strip_ansi(capture.get())

assert total_issues == 0
assert "Files to check: 0" in output
assert "Files checked" in output
assert "definitions.yaml" not in output


def test_list_languages_includes_region_codes(tmp_path) -> None:
"""
Ensures list_languages reports region variants.
Expand Down
4 changes: 2 additions & 2 deletions Rules/Braille/ASCIIMath/definitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"lambda", "Lambda", "and", "or", "if",
"lim", "Lim",
"sin", "cos", "tan", "arcsin", "arccos", "arctan", "sinh", "cosh", "tanh", "cot", "coth", "sech", "csch", "sec", "csc",
"log", "ln", "abs", "norm", "floor", "ceil",
"log", "lg", "ln", "abs", "norm", "floor", "ceil",
"Sin", "Cos", "Tan", "Arcsin", "Arccos", "Arctan", "Sinh", "Cosh", "Tanh", "Cot", "Sec", "Csc",
"Log", "Ln", "det", "exp", "dim", "mod", "gcd", "lcm", "lub", "glb", "min", "max",
}
Expand All @@ -30,4 +30,4 @@

- UnderAccents: {
"¯": "ul", "⏟": "ubrace",
}
}
30 changes: 15 additions & 15 deletions Rules/Braille/CMU/CMU_Rules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,7 @@
-
name: single-char-exceptions # 4.3.1
tag: msup
match: "*[2][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
match: "*[2][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
replace:
- x: "*"
- test:
Expand All @@ -609,7 +609,7 @@
-
name: single-char-exceptions # 4.3.1
tag: msubsup
match: "*[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
match: "*[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
replace:
- x: "*[1]"
- x: "*[3]"
Expand All @@ -633,8 +633,8 @@
name: number-char-exceptions # 4.3.1 (FIX: degree doesn't use dot-3 for chemistry or temp/angles/etc)
tag: [msub, msup]
match:
- "*[2][translate(., \"'*`+-∞ª°²³´¹º‘’“”‟‵‶‷\",'')=''] or"
- "*[2][self::m:mrow and count(*)=2 and *[1][self::m:mn] and *[2][self::m:mo][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']]"
- "*[2][translate(., \"'*`+-∞´°‘’“”‟‵‶‷\",'')=''] or"
- "*[2][self::m:mrow and count(*)=2 and *[1][self::m:mn] and *[2][self::m:mo][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']]"
replace:
- x: "*[1]"
- test:
Expand Down Expand Up @@ -779,7 +779,7 @@
replace:
- x: "*[1]"
- test: # special case superscript postscripts that preceed the prescripts
if: "$Postscripts and not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
if: "$Postscripts and not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[2]"
- test:
Expand All @@ -805,7 +805,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Prescripts[1][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Prescripts[1][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Prescripts[1][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Prescripts[1]"
- test:
Expand Down Expand Up @@ -835,7 +835,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Prescripts[2][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Prescripts[2][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Prescripts[2][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Prescripts[2]"
- test:
Expand Down Expand Up @@ -868,7 +868,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Prescripts[3][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Prescripts[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Prescripts[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Prescripts[3]"
- test:
Expand Down Expand Up @@ -898,7 +898,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Prescripts[4][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Prescripts[4][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Prescripts[4][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Prescripts[4]"
- test:
Expand Down Expand Up @@ -935,7 +935,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Postscripts[1][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Postscripts[1][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Postscripts[1][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[1]"
- test:
Expand All @@ -950,7 +950,7 @@
else:
- x: "$Postscripts[1]"
- test:
if: "not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')!='']"
if: "not($Postscripts[2][self::m:none]) and $Postscripts[2][translate(., \"′″‴⁗'*`+-´°‘’“”‟‵‶‷\",'')!='']"
then:
- t: "⠡"
- test:
Expand Down Expand Up @@ -991,7 +991,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Postscripts[3][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Postscripts[3][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Postscripts[3][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[3]"
- test:
Expand Down Expand Up @@ -1021,7 +1021,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Postscripts[4][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Postscripts[4][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Postscripts[4][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[4]"
- test:
Expand Down Expand Up @@ -1054,7 +1054,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Postscripts[5][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Postscripts[5][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Postscripts[5][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[5]"
- test:
Expand Down Expand Up @@ -1084,7 +1084,7 @@
# degree doesn't use dot-3 for chemistry, temp, angles -- can't know that so we make a wild guess here
if: "not($Postscripts[6][.='°'] and *[1][self::m:mn or @data-chem-element])"
then: [t: "⠄"]
- else_if: "$Postscripts[6][translate(., \"'*`+-ª°²³´¹º‘’“”‟‵‶‷\",'')='']"
- else_if: "$Postscripts[6][translate(., \"'*`+-´°‘’“”‟‵‶‷\",'')='']"
then:
- x: "$Postscripts[6]"
- test:
Expand Down
Loading
Loading