From 578732b31167b1d34c2d85a2c9a89e420a880e50 Mon Sep 17 00:00:00 2001 From: Danil <81031453+Kostenkov-2021@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:29:02 +0300 Subject: [PATCH 1/2] Addition of Russian documentation This commit adds Russian documentation for users, developers and translators. --- docs/_data/languages.yml | 2 + docs/ru/README.md | 33 + docs/ru/User_guide_for_MathCAT_ru.md | 130 ++++ docs/ru/callers.md | 186 ++++++ docs/ru/developers.md | 130 ++++ docs/ru/helpers.md | 494 +++++++++++++++ docs/ru/index.md | 207 +++++++ docs/ru/nav-commands.md | 54 ++ .../new_translators_guide_MathCAT_revised.md | 568 ++++++++++++++++++ docs/ru/users.md | 203 +++++++ 10 files changed, 2007 insertions(+) create mode 100644 docs/ru/README.md create mode 100644 docs/ru/User_guide_for_MathCAT_ru.md create mode 100644 docs/ru/callers.md create mode 100644 docs/ru/developers.md create mode 100644 docs/ru/helpers.md create mode 100644 docs/ru/index.md create mode 100644 docs/ru/nav-commands.md create mode 100644 docs/ru/new_translators_guide_MathCAT_revised.md create mode 100644 docs/ru/users.md diff --git a/docs/_data/languages.yml b/docs/_data/languages.yml index 70456389..4b69c28f 100644 --- a/docs/_data/languages.yml +++ b/docs/_data/languages.yml @@ -2,3 +2,5 @@ en: name: English fi: # Finnish name: Suomi +ru: # Russian + name: Русский diff --git a/docs/ru/README.md b/docs/ru/README.md new file mode 100644 index 00000000..7d47b36b --- /dev/null +++ b/docs/ru/README.md @@ -0,0 +1,33 @@ +# Документация MathCAT + +Документация собирается с помощью [Jekyll](https://jekyllrb.com/) с темой Cayman и публикуется на GitHub Pages. + +## Поддержка нескольких языков + +Переводы находятся в подкаталогах языков (например, `ru/` для русского языка). Каждая переведённая страница должна содержать front matter с полями `lang` и `ref`: + +```yaml +--- +layout: default +lang: ru +ref: users +title: Руководство пользователя MathCat +--- +``` + +- `lang` — код языка страницы; +- `ref` — общий идентификатор, связывающий переводы одной и той же страницы (например, поле `ref: users` должно быть и в `users.md`, и в `ru/users.md`). + +Переключатель языков в `_layouts/default.html` использует эти поля для создания ссылок между переводами. Он отображается только на страницах, у которых задано поле `ref`. + +Чтобы зарегистрировать новый язык, добавьте его в `_data/languages.yml`. + +## Локальная разработка + +Запустите _Jekyll_ из корневого каталога репозитория: + +``` +docker run --rm -v "${PWD}/docs:/srv/jekyll" -p 4000:4000 jekyll/jekyll bash -c "bundle install && bundle exec jekyll serve --host 0.0.0.0" +``` + +Затем откройте http://localhost:4000, http://localhost:4000/fi/users и т. д. diff --git a/docs/ru/User_guide_for_MathCAT_ru.md b/docs/ru/User_guide_for_MathCAT_ru.md new file mode 100644 index 00000000..43e23081 --- /dev/null +++ b/docs/ru/User_guide_for_MathCAT_ru.md @@ -0,0 +1,130 @@ +--- +layout: default +lang: ru +ref: user-guide +title: Руководство пользователя MathCAT +--- +# Руководство пользователя MathCAT + +MathCAT — инструмент, который используется вместе с программой экранного доступа. С помощью MathCAT математические выражения можно озвучивать и читать на дисплее Брайля. + + +Если вы используете программу экранного доступа NVDA, установите MathCAT как дополнение. [Скачать дополнение MathCAT для NVDA можно здесь.](https://nvda-addons.org/) + +Если вы используете JAWS, MathCAT уже встроен в программу, поэтому устанавливать его как дополнение не нужно. + +## Начало работы с MathCAT + +Используйте программу экранного доступа на веб-странице или в электронной книге как обычно. Когда вы дойдёте до математического выражения, программа экранного доступа автоматически прочитает его. Чтобы изучить выражение подробнее, включите режим навигации, нажав пробел. В NVDA для этого также можно нажать NVDA+Alt+M. Чтобы выйти из режима навигации, нажмите Escape. + +### Наиболее распространённые сочетания клавиш + +- Для перемещения влево, вправо, вверх или вниз внутри математического выражения используйте клавиши-стрелки. +- Для перемещения между ячейками таблицы используйте Ctrl+стрелка. +- Чтобы перейти к началу выражения, нажмите Home, а чтобы перейти к концу — End. +- Чтобы услышать текущую позицию, нажмите пробел. +- Чтобы изменить режим навигации, нажмите Shift+стрелка вверх или Shift+стрелка вниз. Режимы навигации описаны в разделе «Навигация». + +При навигации по выражению можно нажать Ctrl+C, чтобы скопировать код MathML текущего узла выражения. + +В математических выражениях доступно гораздо больше возможностей навигации. Все функции описаны в разделе «Все команды навигации». + +## Настройка MathCAT + +MathCAT можно настроить в соответствии с вашими потребностями. Чтобы найти параметры, нажмите NVDA+N, затем выберите «Параметры», а после этого — «Настройки MathCAT». В списке категорий есть три пункта: «Речь», «Навигация» и «Брайль». + +### Речь + +Ниже перечислены параметры речи и доступные для них значения с краткими описаниями. Значение по умолчанию, которое используется, если вы ничего не выбрали, указано в квадратных скобках. + +- Особые потребности: + - \[Незрячие.\] Однозначное озвучивание. + - Слабовидящие. Более краткое озвучивание. + - Трудности в обучении. Более краткое озвучивание. +- Язык: (по умолчанию используется язык программы экранного доступа) + - \[Английский (en)\] + - Испанский (es) + - Индонезийский (id) + - Шведский (sv) + - Вьетнамский (vi) + - Китайский, традиционное письмо (zh-tw) +- Стиль речи: + - \[ClearSpeak.\] Выражения озвучиваются примерно так, как их произнёс бы учитель на уроке. + - SimpleSpeak. Выражения озвучиваются более кратко. Иногда такое чтение может быть неоднозначным. +- Подробность: + - Кратко. Опускаются дополнительные слова, например артикли и предлоги в английской фразе «the square root of x». + - \[Средне.\] Компромисс между кратким и подробным режимами. + - Подробно. Озвучиваются все слова. Чтение является однозначным. +- Скорость чтения математических выражений (MathRate): + - \[100\], можно задать значение от 1 до 1000. Определяет скорость чтения математических выражений относительно скорости программы экранного доступа. Значение задаётся в процентах: 100 соответствует той же скорости, меньшее значение замедляет чтение, а большее — ускоряет. +- Коэффициент длительности пауз: + - \[1\], можно задать значение от 0 до 10. Определяет длительность пауз при чтении математических выражений. +- Звуковой сигнал для математических выражений: + - \[Нет.\] + - Звуковой сигнал. Перед каждым математическим выражением и после него воспроизводится звуковой сигнал. +- Химия: + - \[Читать.\] Химические формулы озвучиваются, например $H_2O$ читается как «аш два о». + +### Навигация + +MathCAT позволяет подробно изучать выражение с помощью навигации, то есть перемещаться по нему и читать его по частям. В настройках навигации можно выбрать способ перемещения и требуемую степень детализации. + +Ниже перечислены параметры и доступные для них значения с краткими описаниями. Значение по умолчанию, которое используется, если вы ничего не выбрали, указано в квадратных скобках. + +- Режим навигации при входе в выражение: + - \[Расширенный режим.\] Перемещение между математически значимыми частями выражения, например числителем, знаменателем, степенями и выражениями в скобках. + - Простой режим. Перемещение между словами, кроме случаев, когда встречается определённое выражение, например квадратный корень. Тогда оно читается целиком. + - Посимвольный режим. Перемещение между словами или числами. Увеличьте детализацию, чтобы читать каждую букву или цифру отдельно. + +Чтобы изменить режим навигации при перемещении по выражению, используйте Shift+стрелка вверх для перехода от простого режима к расширенному или от посимвольного к простому. Нажатие Shift+стрелка вниз переводит из расширенного режима в простой или из простого в посимвольный. Таким образом, перемещение вверх даёт более общий обзор, а вниз — более подробное представление. + +Можно установить флажок, чтобы режим навигации сбрасывался при каждом входе в выражение. По умолчанию флажок снят. + +- Озвучивание после перемещения: + - \[Читать.\] Читает часть выражения, в которой вы находитесь. + - Описывать. Даёт обзор выбранного выражения. + +Можно установить флажок, чтобы режим озвучивания после перемещения сбрасывался при каждом входе в выражение. По умолчанию флажок установлен. + +- Автоматически уменьшать детализацию после чтения части выражения, например корня: + - \[Включено.\] Флажок установлен. + - Выключено. Флажок снят. +- Подробность навигации: + - Кратко. Опускаются дополнительные слова, например артикли и предлоги в английской фразе «the square root of x». + - \[Средне.\] Компромисс между кратким и подробным режимами. + - Подробно. Озвучиваются все слова. Чтение является однозначным. + +### Брайль + +Ниже перечислены параметры Брайля и доступные для них значения. Значение по умолчанию, которое используется, если вы ничего не выбрали, указано в квадратных скобках. + +- Математическая нотация для отображения на дисплее Брайля: + - \[Немет.\] + - Шведская. + - Вьетнамская. +- Точки 7 и 8 обозначают текущую позицию в режиме навигации: + - Первые символы. + - \[Конечные точки.\] + +## Все команды навигации + +В таблице перечислены команды для навигации по математическому выражению. В первом столбце указана клавиша. В остальных столбцах описано действие при нажатии самой клавиши, Ctrl+клавиша, Shift+клавиша и Ctrl+Shift+клавиша. + +Примечание: табличная математика — это математическое содержимое с табличной структурой, например матрица или система уравнений. По таким выражениям можно перемещаться как по таблице. + +| Клавиша | Без модификатора | \+ Ctrl | \+ Shift | + Ctrl + Shift | +| :--- | :--- | :--- | :--- | :--- | +| Стрелка влево | Перейти к предыдущему элементу | В таблице: перейти к предыдущей ячейке.
В табличном выражении: перейти к предыдущему элементу.
Примечание: также можно использовать Ctrl+Alt+стрелка влево. | Прочитать предыдущий элемент | Описать предыдущий элемент | +| Стрелка вправо | Перейти к следующему элементу | В таблице: перейти к следующей ячейке.
В табличном выражении: перейти к следующему элементу.
Примечание: также можно использовать Ctrl+Alt+стрелка вправо. | Прочитать следующий элемент | Описать следующий элемент | +| Стрелка вверх | Уменьшить детализацию | В таблице: перейти к ячейке выше.
В табличном выражении: перейти к элементу выше.
Примечание: также можно использовать Ctrl+Alt+стрелка вверх. | Перейти к более общему режиму навигации: расширенному, простому или посимвольному | Уменьшить детализацию до минимальной | +| Стрелка вниз | Увеличить детализацию | В таблице: перейти к ячейке ниже.
В табличном выражении: перейти к элементу ниже.
Примечание: также можно использовать Ctrl+Alt+стрелка вниз. | Перейти к более подробному режиму навигации: расширенному, простому или посимвольному | Увеличить детализацию до максимальной | +| Enter | Сообщить текущую позицию | Сообщить полную текущую позицию |   |   | +| Цифры
1–10 (0 означает 10) | Перейти к метке позиции | Установить метку позиции | Прочитать содержимое метки позиции | Описать содержимое метки позиции | +| Пробел | Прочитать текущий элемент | Прочитать текущую ячейку | Переключить режим речи между чтением и описанием | Описать текущий элемент | +| Home | Перейти к началу выражения | Перейти к началу строки | В табличном выражении: перейти к началу столбца.

В столбце: перейти к верхнему элементу | | +| End | Перейти к концу выражения | Перейти к концу строки | В табличном выражении: перейти к концу столбца.

В столбце: перейти к нижнему элементу | | +| Backspace | Вернуться к предыдущей позиции | | | | + +## Есть замечания или предложения по MathCAT? + + diff --git a/docs/ru/callers.md b/docs/ru/callers.md new file mode 100644 index 00000000..c189234e --- /dev/null +++ b/docs/ru/callers.md @@ -0,0 +1,186 @@ +--- +layout: default +lang: ru +ref: callers +title: Интеграция MathCAT +--- +# MathCAT: средство обеспечения доступности математики + + +## Информация для разработчиков ассистивных технологий и пользователей библиотеки + +При использовании MathCAT вызовы обычно выполняются в следующем порядке: +1. Задаётся расположение каталога `Rules` MathCAT с помощью [SetRulesDir]. +2. С помощью вызовов [`SetPreference`] задаются необходимые ассистивной технологии настройки. Обычно указываются `Language` и используемый движок `TTS`, если он есть. Настоятельно рекомендуется задать движок. +3. MathML передаётся с помощью [`SetMathML`]. +4. Ассистивная технология вызывает [`GetSpokenText`] для получения текста для озвучивания и [`GetBraille`] для получения Брайля в Unicode. Если указан идентификатор узла, соответствующие ячейки Брайля будут выделены. + +Для навигации можно вызывать: +* [`DoNavigateKeyPress`] — принимает события клавиатуры; +* [`DoNavigateCommand`] — принимает команды, в которые внутри программы преобразуются события клавиатуры. + +Оба вызова возвращают строку для озвучивания. +Для выделения текущего узла навигации используются атрибуты `id`. Если они ещё не заданы, +[`SetMathML`] возвращает строку MathML с атрибутами `id` у всех узлов, для которых такие атрибуты отсутствовали. +Текущий узел можно получить с помощью: +* [`GetNavigationMathMLId`]; +* [`GetNavigationMathML`] — возвращает строку MathML выбранного узла. + +Примечание: оба вызова также возвращают второе целое число. Это смещение символа в листовом узле. +Оно необходимо для посимвольной навигации по листовым узлам из нескольких символов, например `sin` и `1234`. +Сейчас значение всегда равно `0`: эта функция требует дальнейшей разработки. + +Текущие значения настроек можно получить с помощью вызова [`GetPreference`]. + +Все функции могут возвращать код ошибки. + +Примечание: MathCAT выполняет значительную работу по исправлению некачественного MathML. В частности, генераторы MathML часто разделяют на части числа с запятыми и точками. MathCAT пытается собрать их обратно, но для этого должен знать региональные правила использования разделителей групп цифр и десятичных разделителей. Например, в США запись `1,234.0` является допустимым числом, а в Европе — нет, поскольку запятая используется как десятичный разделитель. Региональные настройки определяются страной, для которой создан документ, а не языком озвучивания математических выражений. Допустимый вид числа задают настройки `BlockSeparators` и `DecimalSeparators`. Вызывающая программа должна устанавливать эти значения, если они известны. По умолчанию используется американский формат чисел. + +## Для пользователей Rust +MathCAT написан на Rust. Достаточно собрать MathCAT и добавить в файл `Cargo.toml` своего проекта запись наподобие следующей: +``` +[dependencies.MathCAT] +mathcat = 0.2.0 # проверьте актуальную версию и используйте её +``` + +Точные сигнатуры функций с комментариями: +``` +/// Задаёт каталог Rules. +/// ВАЖНО: это должен быть самый первый вызов MathCAT, если только не задана переменная среды MathCATRulesDir. +pub fn set_rules_dir(dir: String) -> Result<()> + +/// Возвращает номер версии сборки из Cargo.toml. +pub fn get_version() -> String + +/// Заменяет ранее заданный MathML. +/// Возвращает канонический MathML с атрибутами 'id' у всех узлов, у которых не было идентификатора. +/// Идентификаторы можно использовать для синхронного выделения, если настройка API `Bookmark` равна true. +pub fn set_mathml(mathml_str: String) -> Result + +/// Возвращает текст для озвучивания ранее заданного MathML. +/// При озвучивании учитываются настройки ассистивной технологии и пользователя. +pub fn get_spoken_text() -> Result + +/// Возвращает текст для озвучивания обзора ранее заданного MathML. +/// При озвучивании учитываются настройки ассистивной технологии и пользователя. +/// Примечание: сейчас реализация минимальна, и использовать её не следует. +pub fn get_overview_text() -> Result + +/// Возвращает значение указанной настройки. +/// Если `name` не является известной настройкой, возвращается None. +pub fn get_preference(name: impl AsRef) -> Result + +/// Задаёт настройку MathCAT. Имя настройки должно быть известным MathCAT именем. +/// В зависимости от настройки значением должна быть строка или число. +/// Список известных пользовательских настроек приведён в пользовательской документации MathCAT. +/// Ниже перечислены распространённые настройки, которые задаются программами и недоступны пользователю: +/// * TTS -- SSML, SAPI5, None +/// * Pitch -- нормализовано относительно '1.0' +/// * Rate -- слов в минуту; должно соответствовать текущей скорости речи. +/// Отдельная пользовательская настройка "MathRate" задаёт относительное изменение этой скорости в процентах. +/// * Volume -- по умолчанию 100 +/// * Voice -- используемый голос (не реализовано) +/// * Gender -- выбор любого голоса указанного пола (не реализовано) +/// * Bookmark -- задайте `true`, если возвращаемая речь должна содержать `mark`/`bookmark` +/// для синхронного выделения +/// * CheckRuleFiles -- проверяет, изменились ли файлы правил после предыдущего вызова. Возможные значения: +/// "All", "Prefs" (по умолчанию; только системный и пользовательский файлы prefs.yaml) +/// и "None". Переход от "All" к "None" ускоряет работу примерно на 40 %, +/// а переход от "Prefs" к "None" -- примерно на 10 %. +/// +/// Эти настройки управляют озвучиванием и изменением высоты тона для прописных букв: +/// * CapitalLetters_UseWord -- произносить "cap" или подходящий для языка аналог [по умолчанию: true] +/// * CapitalLetters_Pitch -- изменять высоту тона вокруг прописной буквы +/// (нормализовано относительно '1.0'; значение '1.0' по умолчанию ничего не меняет) +/// * CapitalLetters_Beep -- создаёт фиктивный тег SSML audio с audio src='beep.mp4'; +/// в NVDA он служит признаком для воспроизведения сигнала +/// +/// * IntentErrorRecovery -- определяет поведение при недопустимых значениях `intent` в MathML. +/// Возможные значения: "Error" и "IgnoreIntent" (по умолчанию) +/// +/// Важно: имена и значения настроек чувствительны к регистру. +/// +/// Эту функцию можно вызывать несколько раз, чтобы задать разные значения. +/// Значения сохраняются и действуют после вызовов [`set_mathml`]. +/// Значение можно перезаписать, повторно вызвав функцию с другим аргументом. +/// +/// FIXME: Некоторые настройки относятся и к API, и к пользователю. Для переопределения следует +/// использовать что-то наподобие '!name'. Пока не реализовано. +pub fn set_preference(name: impl AsRef, value: impl AsRef) -> Result<()> + +/// Перемещает текущий узел в соответствии с кодом клавиши и клавишами-модификаторами +/// или в некоторых случаях сообщает значение. +/// `key` -- [код клавиши](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#constants_for_keycode_value) +/// для клавиши (в JavaScript -- `ev.key_code`). +/// Возвращает текст для озвучивания нового текущего узла. +pub fn do_navigate_keypress(key: usize, shift_key: bool, control_key: bool, alt_key: bool, meta_key: bool) -> Result + +/// Перемещает текущий узел в соответствии с командой навигации. +/// Это более высокоуровневый интерфейс, чем `do_navigate_keypress`, предназначенный для приложений, +/// которые самостоятельно интерпретируют нажатия клавиш. +/// Допустимые команды: +/// * Стандартные команды перемещения: +/// `MovePrevious`, `MoveNext`, `MoveStart`, `MoveEnd`, `MoveLineStart`, `MoveLineEnd` +/// * Перемещение в таблице или элементарных математических выражениях: +/// `MoveCellPrevious`, `MoveCellNext`, `MoveCellUp`, `MoveCellDown`, `MoveColumnStart`, `MoveColumnEnd` +/// * Переход к дочерним узлам или выход к родительским: +/// `ZoomIn`, `ZoomOut`, `ZoomOutAll`, `ZoomInAll` +/// * Отмена последней команды перемещения: +/// `MoveLastLocation` +/// * Команды чтения (стандартное озвучивание): +/// `ReadPrevious`, `ReadNext`, `ReadCurrent`, `ReadCellCurrent`, `ReadStart`, `ReadEnd`, `ReadLineStart`, `ReadLineEnd` +/// * Команды описания (обзор): +/// `DescribePrevious`, `DescribeNext`, `DescribeCurrent` +/// * Информация о позиции: +/// `WhereAmI`, `WhereAmIAll` +/// * Изменение режима навигации с циклическим переходом вверх или вниз: +/// `ToggleZoomLockUp`, `ToggleZoomLockDown` +/// * Озвучивание текущего режима навигации: +/// `ToggleSpeakMode` +/// +/// Доступны 10 меток позиции, которые можно устанавливать, читать, описывать и использовать для перехода. +/// * Установка: +/// `SetPlacemarker0`, `SetPlacemarker1`, `SetPlacemarker2`, `SetPlacemarker3`, `SetPlacemarker4`, `SetPlacemarker5`, `SetPlacemarker6`, `SetPlacemarker7`, `SetPlacemarker8`, `SetPlacemarker9` +/// * Чтение: +/// `Read0`, `Read1`, `Read2`, `Read3`, `Read4`, `Read5`, `Read6`, `Read7`, `Read8`, `Read9` +/// * Описание: +/// `Describe0`, `Describe1`, `Describe2`, `Describe3`, `Describe4`, `Describe5`, `Describe6`, `Describe7`, `Describe8`, `Describe9` +/// * Переход: +/// `MoveTo0`, `MoveTo1`, `MoveTo2`, `MoveTo3`, `MoveTo4`, `MoveTo5`, `MoveTo6`, `MoveTo7`, `MoveTo8`, `MoveTo9` +/// +/// После завершения навигации вызовите функцию с командой `Exit`. +pub fn do_navigate_command(command: impl AsRef) -> Result + +/// Возвращает MathML, связанный с текущим узлом навигации. +/// Результат содержит `id` узла и смещение относительно этого узла, начиная с нуля (пока не реализовано). +/// Смещение требуется для токенов из нескольких символов. +pub fn get_navigation_mathml() -> Result<(String, usize)> + +/// Возвращает `id` и смещение, начиная с нуля, связанные с текущим узлом навигации. +/// `offset` пока не реализовано. +/// Смещение требуется для токенов из нескольких символов. +pub fn get_navigation_mathml_id() -> Result<(String, usize)> + + +/// Преобразует ошибку, возвращённую set_mathml и другими функциями, в полезную для отображения строку. +pub fn errors_to_string(e:&Error) -> String + +``` + +## Для пользователей Python +Можно создать собственный интерфейс Python или воспользоваться интерфейсом из связанного проекта [MathCATForPython](https://github.com/NSoiffer/MathCATForPython). В нём используется пакет Rust pyo3. + +Интерфейс Python в целом аналогичен интерфейсу Rust. В Python используется CamelCase, а не принятый в Rust snake_case. Например, функция `set_rules_dir` в интерфейсе Python называется `SetRulesDir`. Вызов функции следует оборачивать в конструкцию: +``` +try: + ... +except Exception as e: + ... # зарегистрировать ошибку 'e' в журнале +``` + + +## Для веб-разработчиков +Я собрал версию WebAssembly. В ней есть несколько компромиссов, а в процессе сборки требуется вручную вносить некоторые изменения. Этот процесс необходимо автоматизировать. Версия находится в проекте [MathCatDemo](https://github.com/NSoiffer/MathCATDemo). Проект собирает веб-страницу для демонстрационных целей, поэтому это не чистая сборка для веба. Тем не менее он показывает, как можно реализовать такую сборку. + +## Для пользователей C/C++ +Интерфейс C/C++ находится в связанном проекте [MathCatForC](https://github.com/NSoiffer/MathCATForC). У Rust и C разные менеджеры памяти, поэтому интерфейс несколько неудобен: память необходимо освобождать. Эту особенность можно скрыть, обернув вызовы небольшой функцией, как это сделано для `SetMathCatPreference` в [примере кода](https://github.com/NSoiffer/MathCATForC/blob/main/c-example/test.cpp). В остальном пользоваться интерфейсом просто. Если вы знаете более удачный способ решить проблему с памятью, сообщите мне или отправьте pull request. Для меня как программиста Rust это новая область. diff --git a/docs/ru/developers.md b/docs/ru/developers.md new file mode 100644 index 00000000..57749a3d --- /dev/null +++ b/docs/ru/developers.md @@ -0,0 +1,130 @@ +--- +layout: default +lang: ru +ref: developers +title: Руководство разработчика +--- +# Руководство разработчика + +В этом руководстве приведена техническая информация для разработчиков, работающих с кодовой базой MathCAT. + +## Предварительные требования + +Для разработки MathCAT необходимо установить Rust. Если вы ещё этого не сделали: + +1. [Скачайте и установите Rust](https://www.rust-lang.org/tools/install). +2. Клонируйте репозиторий MathCAT. +3. Откройте каталог проекта в интегрированной среде разработки (IDE). + +## Работа с Cargo + +Cargo — система сборки и менеджер пакетов Rust. Ниже приведены основные команды. + +### Сборка проекта + +```bash +# Собрать проект в режиме отладки +cargo build + +# Собрать проект в режиме выпуска (с оптимизацией) +cargo build --release +``` + +### Запуск проекта + +```bash +# Запустить основной исполняемый файл +cargo run + +# Запустить с указанными аргументами +cargo run -- +``` + +### Управление зависимостями + +Зависимости задаются в `Cargo.toml`. Cargo автоматически скачивает их и управляет ими. + +```bash +# Обновить зависимости до последних совместимых версий +cargo update +``` + +## Тестирование + +Тестирование необходимо для поддержания качества кода и предотвращения ошибок в существующей функциональности после внесения изменений. + +### Запуск тестов + +```bash +# Запустить все тесты +cargo test + +# Запустить указанный тест +cargo test test_name +``` + +### Написание тестов + +Тесты MathCAT проверяют, что выражения MathML преобразуются в ожидаемый текст для озвучивания. Пример: + +```rust +#[test] +fn test_simple_fraction() { + let expr = " + + 1 + 2 + + "; + test("en", "SimpleSpeak", expr, "1 half"); +} +``` + +### Покрытие тестами + +Покрытие тестами помогает определить, какие части кода проверяются тестами, а для каких частей необходимо добавить проверки. + +
+Использование grcov в macOS + +В этом подходе для создания отчётов о покрытии тестами используются `llvm-cov` и `grcov`. [grcov](https://github.com/mozilla/grcov) должен работать и в других операционных системах, но может потребовать некоторых изменений путей LLVM и конфигурации. + +**Однократная настройка:** + +```bash +# Установить необходимые компоненты +rustup component add llvm-tools-preview +cargo install grcov +``` + +**Создание отчёта о покрытии:** + +```bash +# Задать переменную среды для данных профилирования +export LLVM_PROFILE_FILE="target/coverage/%p-%m.profraw" + +# Запустить тесты со сбором данных о покрытии +RUSTFLAGS="-Cinstrument-coverage" cargo test + +# Пример: запустить один тест +# RUSTFLAGS="-Cinstrument-coverage" cargo test Languages::zh::tw::units::without_prefix_powers_of_2 + +# Создать HTML-отчёт +grcov . \ + --source-dir . \ + --binary-path ./target/debug/deps \ + -t html \ + --branch \ + --ignore-not-existing \ + --ignore "target/*" \ + -o target/coverage/html + +# Открыть отчёт в браузере +open target/coverage/html/index.html +``` + +
+ +**Альтернатива: интеграция с IDE** + +Во многих IDE для Rust, например RustRover или VS Code, есть встроенная поддержка анализа покрытия тестами. diff --git a/docs/ru/helpers.md b/docs/ru/helpers.md new file mode 100644 index 00000000..bd63b7c4 --- /dev/null +++ b/docs/ru/helpers.md @@ -0,0 +1,494 @@ +--- +layout: default +lang: ru +ref: helpers +title: Руководство переводчика и разработчика правил +--- +# Руководство переводчика и разработчика правил + +## Информация для разработчиков правил и переводчиков MathCAT +Эта страница находится в процессе подготовки. + +## Начало работы +Если вы планируете участвовать в разработке MathCAT, используйте GitHub: +1. Создайте форк репозитория MathCAT `github.com/NSoiffer/MathCAT`. +2. Клонируйте форк, чтобы получить локальную копию для работы. +3. Переключитесь на созданную для вашей работы ветку, обычно названную кодом языка перевода, и работайте в ней. + +Если вы ещё не знакомы с этими действиями, найдите одно из многочисленных руководств. Они достаточно просты, поэтому отсутствие опыта не должно вас останавливать. + + +## Переводчикам на другие языки +Если вы хотите перевести MathCAT, свяжитесь с @NSoiffer. Он подготовит начальный вариант перевода, который может значительно сэкономить время. В результате будут созданы файлы в каталоге `Rules/Languages/xx`, где `xx` — код языка, например `fr`, `de` или `el`. Перевод выполняется в этом каталоге. Необходимо отредактировать четыре категории файлов: +1. `definitions.yaml`: содержит переводы числительных, в том числе количественных и порядковых. Проверьте начальный перевод и внесите необходимые исправления. Эти числительные используются, например, при озвучивании фразы «три пятых». На некотором этапе образование числительных в языках становится регулярным, поэтому некоторые списки в файле можно сократить, а некоторые следует дополнить. Подробнее см. английские комментарии в файле. +2. Файлы `xxx_Rules.yaml`, сейчас это `ClearSpeak_Rules.yaml` и `SimpleSpeak_Rules.yaml`. Они соответствуют разным стилям речи. Настоятельно рекомендуется сначала выбрать только один стиль. Эти файлы обычно содержат слова, описывающие структуру выражения, например «дробь» и «степень», а также связующие слова. Поскольку стили речи во многом похожи, существует также каталог `SharedRules` с общими файлами правил. Они подключаются в `ClearSpeak_Rules.yaml` и `SimpleSpeak_Rules.yaml` с помощью правил `- include: file_name`. Их тоже необходимо перевести. +
+
+Примечание: диалог настроек MathCAT ищет файлы с именами вида `XXX_Rules.yaml` и добавляет их в раскрывающийся список для языка. Использовать имена SimpleSpeak и ClearSpeak необязательно. Если вы хотите перевести только один стиль, например SimpleSpeak, но не хотите удалять `ClearSpeak_Rules.yaml`, переименуйте его, например, в `ClearSpeak_Rules.yaml.untranslated`. +
+
+Эти файлы содержат автоматически созданные начальные переводы. Хотя текст уже переведён, используется ключ `t:`, а не `T:` с прописной буквы. Каждый перевод необходимо проверить и только после этого изменить ключ на вариант с прописной буквы. Подробнее об автоматическом переводе см. ниже. + + * В некоторых языках нет смысла произносить эквиваленты слов «the» и, возможно, «of» во фразе «the square root of x». В таком случае замените их пустыми строками. + * В некоторых языках меняется порядок слов. Переставляйте слова свободно, но внимательно следите за отступами: в YAML они значимы. + * В некоторых языках могут потребоваться слова, отсутствующие в английской версии, до или после существующих фраз. Добавляйте их при необходимости. Условное добавление выполняется с помощью `test`. Если нужна помощь, свяжитесь с @NSoiffer. + * Паузы между словами и фразами могут значительно улучшить понятность речи. Исходные паузы выбраны для английского языка. Настройте их в соответствии со звучанием синтезаторов вашего языка. Паузы легко добавлять, удалять и изменять. Их длительность масштабируется в соответствии с текущей скоростью речи. +3. Файлы Unicode: `unicode.yaml` и `unicode-full.yaml`. Они содержат такие символы, как `<` и `∫`. + * Начните с перевода `unicode.yaml`. В нём находится подавляющее большинство используемых математических символов. Сейчас список основан на практическом опыте, но в дальнейшем планируется уточнить его по статистике из реальных книг. В `unicode.yaml` около 270 символов, примерно 50 из которых — греческие буквы. Как и в файлах правил речи, здесь есть автоматически созданные начальные переводы. Проверьте их и измените `t:` на `T:`. Подробнее об автоматическом переводе см. ниже. + * В `unicode-full.yaml` тысячи строк. Вернитесь к нему после завершения остальных переводов и работайте столько, сколько сможете: большинство этих символов встречаются только в сложной математике и даже там используются редко. Наиболее важны: + * некоторые стрелки, начиная с 0x2190; + * символы из блока математических операторов 0x2200–0x22ff; + * некоторые диакритические знаки 0x2d8–0x2dd; + * некоторые простые чёрные и белые фигуры, начиная с 0x25a0 и 0x2b1a. +4. Файлы навигации `navigate.yaml` и `overview.yaml`. Переводите только `navigate.yaml`: `overview.yaml` ещё не готов к использованию. Многие слова в `navigate.yaml` многократно повторяются, поэтому удобно применять глобальный поиск и замену. В дальнейшем файл планируется переработать и выделить повторяющиеся слова. + +__ПРИМЕЧАНИЕ__: Сейчас почти завершён переход правил на использование `intent`. Сложная логика распознавания абсолютных величин, определителей и других конструкций будет перенесена в независимый от языка каталог `intent`. Это упростит перевод, поскольку правилу потребуется сопоставлять только тег `absolute-value` или `determinant`. Тесты также следует вынести в независимый от языка каталог `intent`. + +### Пометка переведённого текста +Эти файлы имеют формат YAML, описанный ниже. +Во всех этих файлах переводимый текст задаётся ключом YAML `t`, а в редких случаях — `ot`, `ct`, `spell`, `pronounce` и `IfThenElse`. После проверки перевода запишите ключ прописными буквами, например `T` или `IFTHENELSE`, чтобы обозначить переведённый фрагмент. + +Например, в `unicode.yaml` есть два правила: +``` + - "=": [t: "equals"] # 0x3d + - ">": # 0x3e + - test: + if: "$Verbosity!='Terse'" + then: [t: "is"] + - t: "greater than" +``` +При переводе на французский язык слова после `t:` заменяются примерно так: +``` + - "=": [T: "égale"] # 0x3d + + - ">": # 0x3e + - test: + if: "$Verbosity!='Terse'" + then: [T: "est"] + - T: "supérieur à" +``` + +Примечание: иногда значение `IfThenElse` не требует перевода, но ключ всё равно следует изменить, чтобы было видно, что строка проверена. В следующем примере перевод не нужен, поскольку части `then` и `else`, соответственно `count(*/*[1])` и `$LineCountTry`, не являются словами: +``` + - LineCount: "IfThenElse($LineCountTry=0, count(*/*[1]), $LineCountTry)" +``` + +Подробнее о содержимом файлов правил см. ниже. + +### Примечание о переведённых файлах +Для получения начального перевода файлов Unicode используются переводы MathPlayer и SRE, а также Google Translate. +Если переводы SRE и MathPlayer совпадают либо перевод есть только в одном из них и совпадает с Google Translate, в комментарий в конце строки добавляется только исходный английский вариант. Например: +``` + - "!": [t: "factorielle"] # 0x21 (en: 'factorial') +``` + +Если переводы MathPlayer и SRE различаются, выбирается вариант, совпадающий с Google Translate, а другой вариант включается в комментарий. Например: +``` + else: [t: "parenthèse gauche"] # (en: 'left paren', MathPlayer: 'parenthèse ouvrante') +``` +Если не совпадает ни один перевод, выбирается один из вариантов, а остальные приводятся в комментарии. Например: +``` + else: [t: "parenthèse gauche"] # (en: 'open paren', MathPlayer: 'parenthèse ouvrante', google: 'parenthèse ouverte') +``` +Наконец, если перевод отсутствует, используется Google Translate и добавляется комментарий `google translation`. Вероятность неудачного перевода в таком случае заметно выше, поэтому внимательно проверяйте эти строки. Пример, где доступен только Google Translate: +``` + then: [t: "ligne verticale"] # (en: 'vertical line', google translation) +``` + + +### Проверка перевода +Если вы используете NVDA, начатый перевод можно сразу проверить. Предполагается, что дополнение MathCAT уже установлено: +1. Скопируйте новый каталог перевода в `%AppData%\nvda\addons\MathCAT\globalPlugins\MathCAT\Rules\Languages`. +2. Запустите NVDA и откройте меню настроек MathCAT: параметры NVDA, затем «Настройки MathCAT...». +3. Выберите новый язык в раскрывающемся списке `Languages`. +4. Проверьте озвучивание. Хорошим источником примеров служат страницы Википедии. +5. Если произошла ошибка, часто выражающаяся в отсутствии речи, откройте журнал NVDA из подменю «Сервис». Ошибка должна быть указана там. Объяснение сообщений об ошибках приведено ниже. +6. MathCAT должен заметить изменение файла и перезагрузить его. Сейчас это не работает для файлов, подключённых с помощью `include`, например для файлов каталога `Shared`. После изменения такого файла перезагрузите MathCAT через «Сервис: Перезагрузить плагины» в NVDA или перезапустите NVDA. + +Перевод диалога настроек выполняется отдельно от перевода речи. Им занимаются добровольцы, которые переводят и другие дополнения. Подробнее см. [эту рассылку](https://groups.io/g/nvda-translations). + +### Автоматические тесты перевода +Тестирование очень важно. MathCAT написан на Rust и содержит множество автоматических тестов, использующих встроенную систему тестирования Rust. Чтобы писать и проверять собственные тесты, [скачайте и установите Rust](https://www.rust-lang.org/tools/install). Знать Rust необязательно: достаточно заменить некоторые английские строки ожидаемыми строками на вашем языке. + +Предположим, что вы переводите на французский язык с кодом `fr`. + +Сначала откройте файл `languages.rs` в каталоге тестов и добавьте строку `mod fr;` после `mod en;` или аналогичной строки другого языка. + +В каталоге `tests\Languages` находятся файл `en.rs` и каталог `en`. +1. Скопируйте `en.rs` в `fr.rs`. +2. Скопируйте каталог `en` в `fr`. +3. Если вы выбрали только один стиль речи, например SimpleSpeak, отредактируйте `fr.rs`: удалите строки, начиная с `mod ClearSpeak {` и заканчивая соответствующей строкой `}`. Удалите подкаталог `ClearSpeak` из каталога `fr`. +4. Желательно перевести все файлы, но на начальном этапе можно ограничиться несколькими. В `fr.rs` закомментируйте каждый непереведённый файл, добавив `//` в начало строки. Например, если вы не перевели файл SimpleSpeak `geometry.yaml`, строка должна выглядеть так: `// mod geometry;`. +5. Начните редактировать файлы: сначала глобально замените `"en"` на `"fr"`, а затем замените английские строки подходящими французскими или другими строками. + +Пример теста: +``` +#[test] +fn common_fraction_half() { + let expr = " + 1 2 + "; + test("en", "SimpleSpeak", expr, "1 half"); +} +``` +Для французского языка строка `test` будет выглядеть так: +``` + test("fr", "SimpleSpeak", expr, "un demi"); +``` + +После перевода нескольких тестов запустите автоматическую проверку. +Сначала убедитесь, что английские тесты работают: +``` +cargo test Languages::en +``` +Затем запустите свои тесты. В нашем примере: +``` +cargo test Languages::fr +``` +MathCAT добавляет паузы. В ожидаемых строках тестов они записываются символами `,` и `;`. При необходимости добавьте или удалите эти символы в ожидаемом результате. Если паузы звучат неуместно, добавьте или удалите `pause: xxx` в соответствующем месте одного из файлов `Rules\fr`. + +__Совет__: возможно, быстрее всего сначала запустить тесты на своём языке, не меняя ожидаемые результаты. Все тесты завершатся с ошибкой, но в сообщениях будет показана речь, созданная MathCAT на вашем языке. _Если она верна_, просто замените ей английский текст. После обработки всех ошибок снова запустите тесты. В идеальном случае ошибок больше не будет. + + +### Поддержание перевода в актуальном состоянии +Раздел предстоит написать... + +Со временем планируется создать инструмент, который будет: +1. предупреждать об отсутствующих переводах; +2. предупреждать о правилах из каталога `en`, которые не были скопированы в другой язык, вероятно из-за добавления новых английских правил. + +Такие инструменты будут искать переведённый и непереведённый текст. + + +## Переводчикам Брайля +Если вы хотите добавить поддержку новой системы записи Брайля, скорее всего, придётся начать с нуля, если только она не похожа на уже существующую. +Создайте три файла `.yaml` в каталоге `Rules\Braille\your-braille-language` по образцу файлов из других каталогов Брайля: +1. `xxx_Rules.yaml`, где `xxx` — имя новой системы записи Брайля. Файл содержит правила преобразования MathML в Брайль. +2. `unicode.yaml` — перевод наиболее распространённых символов Брайля. Используйте `Nemeth\unicode.yaml` как отправную точку. Замените `t: xxx` подходящими значениями для вашей системы. Вероятно, для символов, зависящих от контекста, потребуется удалить часть логики или добавить собственную. Например, в коде Немета запятая представляется по-разному внутри числа и вне его. +3. `unicode-full.yaml` — переводы остальных символов. + +Два отдельных файла Unicode нужны потому, что небольшой файл с распространёнными символами ускоряет запуск. Цель этого файла — охватить 99,99 % используемых символов. + +Для UEB и кода Немета потребовалось написать на Rust код очистки. Если для вашего перевода Брайля тоже нужна очистка, создайте issue, чтобы мы вместе могли реализовать необходимый код. + +Перевод Брайля можно сразу проверить. Используйте приведённые выше инструкции для перевода языка, где предлагается скопировать файлы в `%AppData%\nvda\addons\MathCAT\globalPlugins\MathCAT\Rules\Languages`, но замените `Languages` на `Braille`. Остальные действия почти не отличаются. + +Для автоматического тестирования следуйте приведённым выше инструкциям. Текущие тесты взяты из руководств по коду Немета и UEB. Можно поступить так же. Примеры тестов Брайля находятся в каталогах Nemeth и UEB. + +## Понимание сообщений об ошибках MathCAT +Если ошибка вызвана проблемой в правиле, при непосредственном запуске MathCAT сообщение выводится в консоль терминала, а при использовании NVDA — в журнал NVDA. + +Сообщения об ошибках могут показаться сложными. Ниже приведены пример и объяснение. + +Библиотека, которую MathCAT использует для чтения файлов YAML, не сохраняет номера строк, поэтому MathCAT не может сообщить строку с ошибкой. +Вместо этого указываются имя файла, а также значения `name` и `tag` правила в этом файле. +Затем рекурсивно перечисляются разделы правила, в которых обнаружена ошибка. + +Например, ниже приведено сообщение об ошибке, созданное намеренной заменой `test:` на `textx:`: +``` +caused by: in file "...\\MathCAT\\Rules\\Languages\\en\\ClearSpeak_Rules.yaml" +caused by: value for 'replace' in rule (fraction: fraction-over-simple). Replacements: + - test: + if: "$ClearSpeak_Fractions='FracOver'" + then: + - testx: + if: "$Verbosity!='Terse'" + then: [ot: the] + - t: fraction + - x: "*[1]" + - t: over + - x: "*[2]" + - test: + if: "$ClearSpeak_Fractions='OverEndFrac' or ($ClearSpeak_Fractions='EndFrac' and not( ($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and *[1][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] and *[2][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] ) )" + then: + - pause: short + - t: end fraction + - pause: short +caused by: replacement #1 of 5 +caused by: replacement #1 of 2 +caused by: Unknown 'replace' command (testx) with value: if: "$Verbosity!='Terse'" then: [ot: the] +``` +Первые две строки содержат имя файла, а также значения `tag` и `name`. Соответствующее правило: +``` +- name: fraction-over-simple + tag: fraction + match: + - "($ClearSpeak_Fractions='Over' or $ClearSpeak_Fractions='FracOver' or $ClearSpeak_Fractions='OverEndFrac') or" + - "( not($ClearSpeak_Fractions='General' or $ClearSpeak_Fractions='GeneralEndFrac') and" + - " (IsNode(*[1],'simple') and IsNode(*[2],'simple')) )" # simple fraction in ClearSpeak spec + replace: + - test: + if: "$ClearSpeak_Fractions='FracOver'" + then: + - testx: + if: "$Verbosity!='Terse'" + then: [{ot: "the"}] + - t: "fraction" + - x: "*[1]" + - t: "over" + - x: "*[2]" + - test: + # very ugly!!! -- replicate nested ordinal fraction as they are an exception + if: "$ClearSpeak_Fractions='OverEndFrac' or ($ClearSpeak_Fractions='EndFrac' and not( ($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and *[1][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] and *[2][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] ) )" + then: + - pause: short + - t: "end fraction" + - pause: short +``` + +Следующая часть сообщения, `caused by: replacement #1 of 5`, указывает, что проблема находится в первой замене, то есть первом элементе с `-`. +Строка `caused by: replacement #1 of 2` означает, что ошибка находится в первой части этой замены. +Последняя строка сообщает о неизвестной команде замены: `Unknown 'replace' command (testx) with value`. Теперь проблему можно исправить. +Часто сообщение об ошибке удобнее читать снизу вверх. + + +## Разработчикам Rust +Раздел предстоит написать... + +`build.rs` и файлы в `src`. + +## Тестирование +Независимо от того, разрабатываете вы код или пишете правила, крайне важно писать и запускать тесты. Они позволяют убедиться, что новый код работает и не нарушает существующую функциональность. + +Каталог `tests` похож на каталог `Rules`. Если вы переводчик, см. инструкции в соответствующем разделе выше. + +Rust поддерживает тестирование с помощью команды `cargo test`. Подробнее о тестах и покрытии тестами см. в [руководстве разработчика](developers.md). + + +## Файлы +MathCAT считывает важную информацию из следующих файлов: +* Rules + * `intent.yaml` — правила, определяющие намерение автора по MathML. Они используются разными стилями речи на разных языках, чтобы не дублировать процесс определения смысла, и добавляют к MathML атрибут `intent`. + * `definitions.yaml` — различные списки для канонизации MathCAT, то есть определения правильной структуры, и сопоставления правил. Например, `TrigFunctionNames` содержит имена тригонометрических функций, такие как `tan` и `lim`. + * `prefs.yaml` — значения по умолчанию для доступных настроек. MathCAT также ищет этот файл в зависящем от платформы пользовательском каталоге, чтобы отдельные пользователи могли задавать собственные значения. + * Windows: `%AppData%\prefs.yaml`. + * Linux: `$XDG_CONFIG_HOME` или `$HOME/.config`. + * `definitions.yaml` — независимые от языка определения, например имена тригонометрических функций. +* Rules/[lang] + * `Unicode.yaml` — длинный список способов произнесения встречающихся символов Unicode. Не используется для строк из нескольких символов. + * `XXX_rules.yaml` — правила озвучивания математических выражений. MathCAT сканирует каждый подкаталог `Rules`, ищет файлы с суффиксом `_rules.yaml` и добавляет их в список доступных пользователю вариантов. Часть `XXX` должна соответствовать стилю речи. Например, `ClearSpeak_rules.yaml` и `MathSpeak_rules.yaml` добавляют варианты стиля речи ClearSpeak и MathSpeak. + * `definitions.yaml` — зависящие от языка определения, например способы произнесения порядковых числительных: «первый», «половина» и т. д. + * `navigate.yaml` — правила, определяющие результат каждой команды навигации и соответствующий текст для озвучивания. + +Подкаталог `lang` должен соответствовать двухбуквенному коду языка или комбинации языка и региона согласно [стандарту ISO](https://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes). Например, в каталоге `Rules` есть подкаталог `en`. Если требуется региональный вариант речи, можно добавить подкаталог региона, например `gb`, который будет использоваться для языка `en-gb`. + +MathCAT сначала читает правила основного языка, а затем правила региона. Региональные правила заменяют существующие правила или добавляются к ним в соответствующих языковых файлах `Unicode.yaml` и `XXX_rules.yaml`. + +MathCAT ищет каталог `Rules` в следующих местах: +1. В каталоге, заданном переменной среды `MathCATRulesDir`. +2. В соседнем с исполняемым файлом подкаталоге Rules. В Windows это обычно `C:\Program Files\MathCAT\Rules`. + +# Формат файлов +Файлы, как следует из их суффикса, имеют [формат YAML](https://lzone.de/cheat-sheet/YAML). Если вы ещё не знакомы с YAML, это надмножество JSON с более удобными для чтения и записи вариантами синтаксиса. + +## Введение в YAML +Основные типы YAML: +* скалярные типы, например целые и вещественные числа, а также строки. Строки можно заключать в одинарные или двойные кавычки, а в некоторых случаях оставлять без кавычек; +* массивы, которые внутри строки записываются как `["a", "b", "c"]`; +* словари или отображения, которые внутри строки записываются как `{key: value, foo: bar}`. + +Комментарии начинаются с символа `#` и продолжаются до конца строки. Блочных комментариев в YAML нет. + +В более подробном синтаксисе YAML для массивов вместо скобок используются отступы, поэтому приведённый выше массив выглядит так: +``` + - a + - b + - c +``` +Обратите внимание, что в такой форме строки не требуется заключать в кавычки, хотя для некоторых текстов кавычки необходимы. + +Словарь в подробной форме выглядит так: +``` + key: value + foo: bar +``` + +Ниже приведён более реалистичный пример из определений Unicode с несколькими вариантами записи. +Обратите внимание на отступы: все элементы, сдвинутые вправо относительно предыдущей строки, являются вложенными элементами массива или словаря. +``` +# Два способа задать простую замену для символа '∞'. +# Для краткости и ясности предпочтительна первая форма. + - "∞": [t: "infinity"] # 0x221e + - '∞': + - t: infinity # 0x222e + +# Несколько способов задать более сложное определение с проверкой. +# Компактная форма использует синтаксис JSON для значения. +- 0x003C: [test: {if:Verbosity!='terse', then: [t: is]}, t: "less than"] + +# Эта форма подчёркивает, что выполняются два действия: проверка и вывод "less than". +- 0x003C: + - test: [{if: Verbosity!='terse', then: {t: is}}] + - t: "less than" + +# Эта форма немного подробнее, но ясно показывает части проверки. +- 0x003C: + - test: + if: Verbosity!='terse' + then: [t: is] + - t: less than + +# Самая подробная форма YAML. +- 0x003C: + - test: + if: Verbosity!='terse' + then: + - t: is + - t: less than +``` +Все формы допустимы, но второй и третий варианты предпочтительны как хороший компромисс между краткостью и ясностью. + +Примечание: все файлы YAML начинаются с `---`, обозначающего начало документа. + +## Основные части правила речи + +``` +# rule: +# name: # имя правила; сочетание name и tag должно быть уникальным +# tag: +# variables: [{name: value}, ...] +# - name -- строка; value -- выражение XPath, возвращающее строку, число или логическое значение +# - внутри правила значение доступно как $name +# - значение переменной устанавливается _до_ проверки "match", поэтому его можно использовать в match +# - переменная действует в течение сопоставления +# match: # выражение XPath для сопоставления +# - может быть одной строкой или +# - массивом строк, которые объединяются для удобства чтения +# replace: [replacements], где replacements содержит один из следующих вариантов +# - t: некоторый текст +# 'T' означает, что текст переведён +# - ct: объединить текст без пробела перед ним +# 'CT' означает, что текст переведён +# - ot: необязательный текст; не использовать, если результат содержит повторяющиеся слова +# 'OT' означает, что текст переведён +# Например, не следует получать "t raised to the the fraction with ...." +# Если сделать "the" необязательным в правиле дроби, повторения не будет +# - x: некоторое выражение XPath в виде строки +# - test: обычные значения if/then/else с двумя особенностями: +# вместо 'then:' и 'else:' можно использовать 'then_test:' и 'else_test:' +# Это позволяет избежать дополнительного уровня 'test:' +# можно задать любое количество пар if/else_if; +# они проверяются по порядку до получения истинного значения +# Значение "test:" может быть массивом ключей if/else_if/else +# или для удобства единственным набором if/then/else. +# Если используется массив, первым элементом должен быть 'if', +# промежуточными и иногда последним -- 'else_if', +# а необязательным последним элементом -- 'else' или 'else_test' +# if: некоторое выражение XPath +# then: [replacements] +# then_test: [replacements] используется вместо 'then:', чтобы не добавлять 'test:' после 'then:' +# else: [replacements] # необязательно +# else_test: # необязательно; используется вместо 'else:', чтобы не добавлять 'test:' после 'else:' +# - with: +# variables: [name: value, ...] значения переменных, заданные во время выполнения этого предложения +# replace: [replacements] +# - intent: +# name: string имя правила intent +# children: дочерние узлы правила intent +# - insert: +# nodes: XPath, вычисляемый в узлы +# replace: [replacements] значения, вставляемые между всеми узлами +# - translate: XPath позволяет озвучить выражение в середине правила; используется WhereAmI для навигации +# - set_variables: [var: value, ...] определения глобальных переменных +# Они доступны программе после выполнения правил. Сейчас используются для навигации, меняющей состояние. +# - pause: строка или число # "short", "medium", "long", "auto" или число миллисекунд +# - rate: строка/число или словарь с одной или двумя записями +# value: вещественное число с необязательным % +# replace: [replacements] # значения TTS должны ограничивать область содержимого +# - volume: строка/число или словарь с одной или двумя записями +# value: вещественное число с необязательным % +# replace: [replacements] # значения TTS должны ограничивать область содержимого +# - pitch: строка/число или словарь с одной или двумя записями +# value: вещественное число с необязательным % +# replace: [replacements] # значения TTS должны ограничивать область содержимого +# - gender: строка/число или словарь с одной или двумя записями +# value: "male" # или "female" +# replace: [replacements] # значения TTS должны ограничивать область содержимого +# - voice: строка/число или словарь с одной или двумя записями +# value: строка +# replace: [replacements] # значения TTS должны ограничивать область содержимого +# - spell: строка с XPath, обычно одной буквой, произносимой как буква: `"'a'"` +# - bookmark: некоторый XPath в виде строки; возвращает 'id' для синхронного выделения +``` + +Примечание: для `pause` значение `auto` вычисляет длительность паузы с учётом сложности окружающих частей. Чем они сложнее, тем длиннее пауза, но не более установленного предела. Слушателю необходимо дать время осмыслить и разделить две части, если одна или обе достаточно сложны. + +Помимо именованных правил, файл правил речи поддерживает подключение других файлов правил речи. Благодаря этому разные стили могут использовать общую функциональность. Подключение записывается вместо правила речи: +``` + - include: file_name +``` +Файл может содержать любое количество подключений. Их содержимое обрабатывается так, как будто оно находилось в исходном файле. Подключаемый файл может находиться в текущем каталоге обрабатываемого файла правил или в каталоге по относительному пути. + +## Файлы Unicode + +Файлы Unicode — упрощённые версии правил речи. Они облегчают описание правил для символов Unicode и заметно ускоряют работу. Правила листовых элементов, например `mo`, переопределяют определения из файлов Unicode. Однако в целом правила речи для символов Unicode следует помещать в файл Unicode. + +Как и правила речи, файлы Unicode имеют формат YAML. Основное отличие состоит в том, что для определения правила используется только символ. Задавать имя правила, имя тега и выражение сопоставления не требуется. Значением правила может быть любое допустимое значение `replace:` из правил речи. + +Большинство правил очень просты. Например: +``` + - "+": [t: plus] # 0x2b +``` +Это правило преобразует символ `+` в строку `plus`. + +Более сложное правило: +``` + - "[": # 0x5b + - test: + if: $SpeechStyle = 'ClearSpeak' + then: [t: open bracket] + else: [t: left bracket] +``` +Результат этого правила зависит от текущей настройки стиля речи. + +Файлы Unicode, как и файлы правил речи, можно совместно использовать с помощью `- include: file_name`. + + +## Файлы Prefs + +Примечание: настройки, например настройки ClearSpeak, представлены словарём внутри записи `ClearSpeak` файла YAML. Это затрудняет задание и чтение значения. +Поэтому имя преобразуется в строку с разделителем `_`. +Например, _имя_ настройки `Fraction` раздела `ClearSpeak` — `ClearSpeak_Fraction`. +Именно его следует использовать для задания значения через API и доступа к значению в `ClearSpeak_Rules.yaml`. + + +## Файлы Definition + + +## XPath +Во многих частях правил речи используется XPath. Это распространённый и хорошо документированный способ выбора частей XML-документа. В интернете есть множество руководств. Если вы ещё не знакомы с XPath, рекомендуется прочитать одно из них. В MathCAT используется немного расширенная реализация XPath 1.0. + +В MathCAT обычно применяются лишь некоторые возможности XPath, а также несколько специальных функций. Ниже кратко описаны распространённые варианты использования XPath. + +| Запись | Значение | +| ----- | ---- | +| `*` | Соответствует всем дочерним узлам. | +| `[...]` | Выбирает узлы из текущего сопоставления. | +| `*[1]` | Выбирает первый дочерний узел. | +| `*[self::m:mn]` | Выбирает все дочерние элементы `mn`. Префикс `m` показывает, что элемент находится в пространстве имён MathML. | +| `*[1][self::m:mn]` | Выбирает первый дочерний узел, если он является элементом `mn`. | +| `*[1][self::m:mo][text()='-']` | Выбирает первый дочерний узел, если он является элементом `mo` с содержимым `-`. Можно записать `*[1][text()='-']`, поскольку другие узлы, скорее всего, не содержат `-`, но такое содержимое допустимо для `mtext`, поэтому безопаснее указать имя элемента. | +| `count(*[2]/*)` | Количество дочерних узлов второго дочернего узла. | +| `count(preceding-sibling::*)+1` | Количество одноуровневых узлов перед текущим элементом плюс один. | + +MathCAT добавляет специальные функции, упрощающие написание правил: + +| Функция | Значение | +| ----- | ---- | +| `IsNode(nodes, type)` | Возвращает true, если все узлы имеют одинаковый тип. Допустимые типы:
`"simple"` — заданный набор элементов ClearSpeak;
`"leaf"` — один из листовых элементов MathML;
`"2D"` — двумерный узел, например `mfrac` или `mroot`;
`"modified"` — узел с индексом или элементом над ним либо под ним;
`"scripts"` — узел с подстрочным и (или) надстрочным индексом;
`"common_fraction"` — целочисленные числитель и знаменатель. | +| `ToOrdinal` | | +| `ToCommonFraction` | | +| `IsBracketed(openChar, closeChar, requiresComma)` | | +| `BaseNode(node)` | Рекурсивно возвращает основу узла с индексами. | +| `IsInDefinition(node, name)` | Возвращает true, если узел входит в список `name`, заданный в `definitions.yaml`. | +| `IfThenElse(test, then-part, else-part)` | Возвращает `then-part`, если условие истинно, иначе возвращает `else-part`. Все аргументы являются XPath. | +| `DistanceFromLeaf(node, left_side, treat_2d_elements_as_tokens)` | Возвращает расстояние от текущего узла до крайнего левого или правого листа: для символа 0, для токена 1. Если `left_side` равно `true`, обход выполняется по крайним левым дочерним узлам до листа. Если `treat_2d_elements_as_tokens` равно `true`, двумерные обозначения, например дроби, считаются листами. | +| `EdgeNode(node, "left"/"right", stopNodeName)` | Возвращает узел остановки, если исходный узел находится у левого или правого края указанного предка. Значением `stopNodeName` также может быть `"2D"`. Если соответствие не найдено, возвращается исходный узел. Если `stopNodeName=="math"`, учитывается пунктуация, поскольку она не является частью математического выражения. | +| `DEBUG(xpath)` | Добавляет полезные для диагностики сведения в отладочный вывод. | + +Следующие функции используются правилами кода Немета: + +| Функция | Значение | +| ----- | ---- | +| `NestingChars` | Используется правилами `mfrac`, `msqrt` и `mroot`, чтобы повторять символы нужное количество раз. | +| `BrailleChars` | Используется элементами-токенами для сложной перестановки индикаторов кода Немета, например прописных букв и начертания шрифта. | diff --git a/docs/ru/index.md b/docs/ru/index.md new file mode 100644 index 00000000..8d06d475 --- /dev/null +++ b/docs/ru/index.md @@ -0,0 +1,207 @@ +--- +layout: default +lang: ru +ref: index +title: MathCAT +--- +# MathCAT: средство обеспечения доступности математики + +— библиотека, которая преобразует MathML в: + +* текст для озвучивания со встроенными командами речевого синтезатора; +* Брайль (код Немета, технический вариант UEB, а в дальнейшем и другие математические системы записи Брайля); +* представление для навигации по математическим выражениям, в том числе с возможностью обзора. + +Цель MathCAT — предоставить программам экранного доступа и другим вспомогательным технологиям удобную библиотеку для качественного озвучивания MathML и преобразования MathML в Брайль. MathCAT продолжает идеи MathPlayer (подробнее об этом будет сказано ниже) и использует накопленный в этом проекте опыт, чтобы ещё лучше озвучивать математические выражения, представлять их в Брайле и обеспечивать навигацию по ним. В MathCAT применяются новые идеи, которые разрабатывает [рабочая группа MathML](https://mathml-refresh.github.io/charter-drafts/math-2020.html): они позволяют авторам указывать смысл использованного обозначения. Например, $(3, 6)$ может обозначать точку на плоскости, открытый интервал или даже сокращённую запись наибольшего общего делителя. Если эта информация передана в MathML, MathCAT использует её для более естественного озвучивания. + +Предстоит сделать: подключить сторонние библиотеки для поддержки общего подмножества математических команд TeX и ASCIIMath. + + +# Документация для разных пользователей MathCAT + +MathCAT используют разные аудитории с различными потребностями и задачами. Подробности приведены в соответствующих разделах документации: +* Пользователи вспомогательных технологий: [информация о доступных настройках](users.md). +* Разработчики вспомогательных технологий и пользователи библиотеки: [информация об API MathCAT](callers.md). +* Переводчики и авторы правил: [информация о файлах, которые необходимо перевести](helpers.md). +* Разработчики MathCAT: [информация о процессе разработки и тестировании](developers.md). + +# Некоторые технические подробности +MathCAT написан на Rust и может использоваться из многих языков программирования. На данный момент есть интерфейсы для: +* [C/C++](https://github.com/NSoiffer/MathCATForC); +* [Python](https://github.com/NSoiffer/MathCATForPython) — этот интерфейс используют [дополнение NVDA](https://addons.nvda-project.org/addons/MathCAT.en.html) и программа экранного доступа [Orca](https://help.gnome.org/users/orca/stable) для Linux, написанные на Python; +* [Java](https://github.com/mwhapples/MathCAT4J) — сейчас этот интерфейс используется для экспериментов с MathCAT в [BrailleBlaster](https://www.brailleblaster.org/); +* [WebAssembly (Wasm, отчасти похожий на JavaScript)](https://github.com/NSoiffer/MathCATDemo/) — этот интерфейс используется в веб-демонстрации MathCAT. + +MathCAT применяет несколько эвристик, чтобы исправлять некачественный MathML и приводить его к рекомендованному виду. Например, конвертеры TeX и WYSIWYG-редакторы могут разделить число «1,234» в выражении «1,234+1» по запятой. MathCAT распознаёт такую ситуацию и объединяет число в один элемент `mn`. Другие исправления затрагивают структуру: MathCAT создаёт элементы `mrow` с учётом словаря операторов MathML и при необходимости добавляет невидимые знаки применения функции, умножения, сложения (для смешанных дробей) и разделители (например, между $i$ и $j$ в $a\_{ij}$). Это упрощает генерацию речи и кода Немета, а также может быть полезно другим приложениям. Сейчас очистка MathML не доступна через API, но в дальнейшем может стать ещё одной функцией MathCAT. В целом MathCAT исправляет MathML достаточно осторожно. Иногда результат может оказаться неверным, однако ожидается, что правильных исправлений будет значительно больше. Поиск типичных ошибок в конвертерах MathML и исправление некачественного MathML остаются постоянной задачей проекта. + +## Текущее состояние (обновлено 27.03.2026) + +MathCAT активно развивается. DAISY принимает деятельное участие в разработке, и вклад новых участников приветствуется. MathCAT распространяется с открытым исходным кодом. [Репозиторий проекта доступен на GitHub](https://github.com/daisy/MathCAT). [О проблемах дополнения MathCAT, относящихся к NVDA, можно сообщить здесь](https://github.com/daisy/MathCATForPython/issues). + +* MathCAT поддерживает озвучивание и навигацию для английского, немецкого, испанского, финского, индонезийского, норвежского, шведского, вьетнамского и китайского языков (традиционное письмо). +* MathCAT поддерживает код Немета, UEB, CMU, вьетнамскую систему записи Брайля, а также немецкую и австрийскую системы записи LaTeX и ASCIIMath. +* Существует [дополнение NVDA](https://addons.nvda-project.org/addons/MathCAT.en.html). Оно может заменить MathPlayer для пользователей английского языка и поддерживаемых переводов. Начиная с NVDA 2026.1 MathCAT встроен в NVDA, поэтому скачивать дополнение не нужно. + +Преобразование в код Немета в MathCAT значительно качественнее, чем в MathPlayer и других конвертерах MathML → код Немета. Оно также интегрировано с навигацией: точки 7 и 8 обозначают текущий узел, а маршрутизация курсора Брайля работает во время навигации. Благодаря высокому качеству вывода [BrailleBlaster](https://www.brailleblaster.org/) использует MathCAT для преобразования MathML в код Немета и UEB. + +Ряд других разработчиков вспомогательных технологий также включили MathCAT в свои продукты. Среди них особенно выделяется Vispero/JAWS. Сейчас JAWS поддерживает озвучивание MathCAT на английском и испанском языках, а также вывод в коде Немета и UEB. В дальнейшем появятся другие языки и системы записи Брайля. В средстве просмотра математических выражений JAWS доступны все предусмотренные MathCAT команды озвучивания и навигации, в том числе навигации по Брайлю. Настройки MathCAT находятся в центре настроек JAWS. + +Другие вспомогательные технологии, использующие MathCAT: + +* программа экранного доступа Orca для Linux использует MathCAT для озвучивания, навигации и вывода в Брайле; +* Dolphin EasyReader использует MathCAT; +* Kurzweil 3000 использует озвучивание MathCAT и одновременно выделяет соответствующее подвыражение двумя цветами, обеспечивая наглядное визуальное сопровождение для зрячих пользователей; +* Microsoft объявила, что Экранный диктор будет использовать MathCAT в одной из будущих версий. + +[_Другим компаниям_: если вы включили MathCAT в свой продукт и хотите, чтобы он был упомянут здесь, напишите мне по электронной почте или создайте issue с предложением обновить документацию.] + +Была разработана [демонстрационная версия](https://nsoiffer.github.io/MathCATDemo/), которая показывает некоторые возможности MathCAT и помогает при отладке. Сообщайте, пожалуйста, обо всех найденных ошибках. Эта демонстрация _не_ отражает типичный способ взаимодействия пользователей вспомогательных технологий с MathCAT, но показывает функции, которые такие технологии потенциально могут предоставить конечным пользователям: выделение озвучиваемого фрагмента, навигацию и вывод в Брайле. + +Планы дальнейшей работы: + +* Добавить другие языки. Если вы хотите перевести MathCAT на язык, который пока не поддерживается, создайте issue в [репозитории MathCAT на GitHub](https://github.com/daisy/MathCAT/issues). +* Поддержать дополнительные системы записи Брайля. Для добавления такой поддержки нужны три составляющие: + * спецификация системы; + * возможность задавать вопросы специалисту по соответствующей системе записи Брайля; + * человек, готовый перенести не менее 200 примеров из спецификации в используемый для тестов формат: MathML и строку символов Брайля в Unicode. Это может занять 30 часов или больше. + + Если вы можете собрать всё необходимое, создайте issue в [репозитории MathCAT на GitHub](https://github.com/daisy/MathCAT/issues). +* Реализовать преобразование _из_ Брайля _в_ MathML. +* Разработать двумерные варианты систем записи Брайля для многострочных обновляемых дисплеев Брайля, например Monarch и Canute 360. + + + +## Почему MathCAT? + +MathCAT — продолжение MathPlayer. Я начал разрабатывать функции доступности MathPlayer в компании Design Science в 2004 году вскоре после прихода в компанию. В то время MathPlayer был главным образом подключаемым модулем C++ для Internet Explorer (IE), отображавшим MathML на веб-страницах. Долгое время это была наиболее полная из доступных реализаций MathML. Первоначальную работу над отображением математических выражений выполнили основатель Design Science Пол Топпинг и технический директор компании, ныне покойный Роберт Майнер. Позже по ряду причин IE отказался от интерфейса, который MathPlayer использовал для отображения, и не предложил ему замену: веб переходил к использованию JavaScript в браузере и отказывался от рисков безопасности, связанных с внешним кодом. После этого MathPlayer стал библиотекой, предназначенной исключительно для обеспечения доступности и вызываемой другими программами, главным образом NVDA. MathPlayer был проприетарным, но распространялся бесплатно. + +В начале 2017 года я покинул Design Science. Позже в том же году WIRIS приобрела компанию. Я предложил бесплатно исправлять ошибки MathPlayer, и сначала эта инициатива получила поддержку. Но когда пришло время выпускать новую версию, многие сотрудники, работавшие в компании во время приобретения, уже ушли, а оставшаяся команда не была заинтересована в поддержке MathPlayer. Окончательное решение было принято только в конце 2020 года. В 2021 году я начал работать над заменой MathPlayer. В качестве дополнительной задачи я решил изучить Rust и реализовал проект на нём. Rust — низкоуровневый язык со строгой типизацией и безопасной работой с памятью, но без автоматической сборки мусора или подсчёта ссылок. Его часто называют более безопасной заменой C/C++. + +Rust достаточно эффективен. На компьютере с Core i7-770K (мощным по меркам примерно 2017 года процессором) для выражения среднего размера + + + + e + + + + 1 + 2 + + + + + ( + + + + x + + μ + + σ + + + ) + + + 2 + + + + + +требуется около 4 мс, чтобы создать строку ClearSpeak на английском языке +"_e raised to the exponent, negative 1 half times; open paren; the fraction with numerator; x minus mu; and denominator sigma; close paren squared, end exponent_" +и строку в коде Немета "⠑⠘⠤⠹⠂⠌⠆⠼⠈⠡⠷⠹⠭⠤⠨⠍⠌⠨⠎⠼⠾⠘⠘⠆". +Примерно 2 мс занимают очистка MathML, 1 мс — генерация речи, ещё 1 мс — генерация Брайля. Сюда входит проверка актуальности всех файлов правил, которая оказывается достаточно затратной. Эту проверку можно отключить с помощью настройки: она нужна главным образом при отладке. Если проверка отключена, время уменьшается до 2,3 мс. +На более мощном процессоре Intel Core Ultra 9 285 2025 года генерация речи и Брайля в одном потоке занимает около 1 мс. + +
+Показать MathML этого выражения +
+<math>
+  <mrow>
+    <msup>
+      <mi>e</mi>
+      <mrow>
+        <mo>−</mo>
+        <mfrac>
+          <mn>1</mn>
+          <mn>2</mn>
+        </mfrac>
+        <msup>
+          <mrow>
+            <mrow>
+              <mo>(</mo>
+              <mrow>
+                <mfrac>
+                  <mrow>
+                    <mi>x</mi>
+                    <mo>−</mo>
+                    <mi>μ</mi>
+                  </mrow>
+                  <mi>σ</mi>
+                </mfrac>
+              </mrow>
+              <mo>)</mo>
+            </mrow>
+          </mrow>
+          <mn>2</mn>
+        </msup>
+      </mrow>
+    </msup>
+  </mrow>
+</math>
+
+
+ +Для генерации речи и Брайля MathCAT использует внешние правила. +Их загрузка занимает около 40 мс. Она выполняется только при первом использовании правил или после изменения стиля речи, языка либо другой внешней настройки. Ещё 50 мс требуется для загрузки полных файлов Unicode для речи и Брайля, однако исследования показали, что в подавляющем большинстве англоязычных математических материалов для школьного обучения используются лишь немногие символы. +Судя по книгам по математике с открытым исходным кодом, первоначально загружаемого набора должно быть достаточно как минимум для 99,99 % символов, встречающихся в выражениях англоязычных школьных учебников математики. + +Размер библиотеки составляет около 3 МБ. + +Если вы разрабатываете решение для работы непосредственно в браузере, то есть используете JavaScript или другой браузерный язык, MathCAT, вероятно, не будет оптимальным выбором. При этом я, скорее всего, выделю из [MathCATDemo](https://github.com/NSoiffer/MathCATDemo/) интерфейс JavaScript, на котором построена демонстрационная версия. Пока обратите внимание на [Speech Rule Engine](https://github.com/zorkow/speech-rule-engine) (SRE) Фолькера Зорге. Он написан на TypeScript и, вероятно, подойдёт для браузерного решения, если вам не требуется Брайль. MathCAT поддерживает несколько систем записи Брайля и как минимум для кода Немета обеспечивает более качественный результат. + +# Благодарности + +Проекту помогали многие люди, и я очень признателен им за это! + +* Дэвид Карлайл — оказал неоценимую помощь с некоторыми выражениями сопоставления XPath. +* Сьюзан Джолли — терпеливо консультировала по генерации кода Немета и UEB, а также давала обратную связь о правильных и ошибочных результатах. Кроме того, она помогла мне при разработке эвристик для химических формул. +* Элейн А. Мур — помогла определить, что следует и не следует произносить для химических формул, а также какие записи имеют смысл с точки зрения химии. +* Ричард Орм — полностью разработал диалог настроек MathCAT для NVDA. +* Сэм Дули, Мюррей Сарджент и Фолькер Зорге — предоставили таблицы перевода символов в код Немета и тесты этого кода. +* Мориц Гросс ([Math4VIP](https://www.math4vip.de/)) — работал над различными частями кодовой базы Rust и создал инструмент Python для отслеживания прогресса локализации. + +Переводчики: + +* Китайский язык (традиционное письмо) — Хон-Джанг Янг. +* Финский язык — Сами Мяяття, Accessibility Library Celia и независимый специалист Эсси Вииппола. +* Немецкий язык — Назли Анджич, Роберт Граф и Пол Либбрехт (IU International University of Applied Sciences). +* Индонезийский язык — доктор Пинта Дениянти Сампоэрно, магистр наук; доктор Мейлиасари, бакалавр педагогических наук, магистр наук; Ари Хендарно, бакалавр педагогических наук, магистр компьютерных наук. +* Норвежский язык — Марте Гьельстад, Национальная библиотека Норвегии, Kvile. +* Русский язык — Данил Костенков. +* Испанский язык — Ноэлия Руис Мартинес, которая также помогала в разработке дополнения NVDA, и Мария Алло Рольдан. +* Шведский язык — Тим Арбореалис Лётберг, Шведское агентство доступных медиа (MTM), и Андерс Эклунд, SPSM. +* Вьетнамский язык — Данг Хоай Фук и Транг Фам. +* Другие языки??? — присоединяйтесь, чтобы я мог указать вас здесь... + +Первоначальный перевод многих символов Брайля для систем записи, разработанных в 2024 году и позднее, был существенно упрощён благодаря таблице, которую мне предоставил Георгиос Курупретроглу и над которой работала большая команда. Для получения подробной информации обратитесь к: + +* [MathBrailleCodes Repository](https://access.uoa.gr/mathbraille/index.php/en/), Speech and Accessibility Lab, National and Kapodistrian University of Athens, Greece: P. Riga, T. Antonakopoulou, D. Kouvaras, S. Lentas and G. Kouroupetroglou (2021) “[The BrailleMathCodes Repository](https://access.uoa.gr/mathbraille/index.php/en/)”, Proceedings of the 4th International Workshop on “[Digitization and e-Inclusion in Mathematics and Science 2021](https://workshop.sciaccess.net/deims2021/DEIMS2021_Proceedings.zip)” DEIMS2021, February 18-19, 2021, Tokyo, pp. 105-114. + +Спасибо всем участникам! + +# Обо мне + +Я работаю над обеспечением доступности математических выражений с 2002 года. В то время я занимался WYSIWYG-редактором формул Mathematica и другими элементами пользовательского интерфейса. Профессор Джон Гарднер, потерявший зрение пятнадцатью годами ранее, спросил, могу ли я сделать интерфейс Mathematica доступным. Мне удалось выполнить, вероятно, около 80 % работы, но компания не захотела продолжать проект. В конечном итоге я ушёл, а компания удалила код. Так начался мой путь в сфере доступности: шаг вперёд, шаг назад и снова вперёд, потому что возможность дать _каждому_ шанс открыть для себя радость математики и науки наполнила мою жизнь смыслом. + +Затем я перешёл в компанию Design Science, Inc (DSI), заинтересованную в обеспечении доступности математики. Незадолго до этого DSI разработала MathPlayer — подключаемый модуль для IE6, отображавший MathML. Я добавлял в него новые функции и при поддержке компании подал заявку на грант NSF для обеспечения доступности MathPlayer. Заявка была одобрена, а работа оказалась весьма успешной. В последующие годы я продолжал развивать MathPlayer. Однако из соображений безопасности Internet Explorer удалил интерфейс, от которого зависел MathPlayer. Возникает соблазн сказать, что именно это и погубило IE... После этого MathPlayer стал дополнением NVDA, предназначенным исключительно для обеспечения доступности. Дальнейшая работа по гранту IES совместно с ETS помогла усовершенствовать возможности MathPlayer. Финансируемые грантом пользовательские исследования дали ценную информацию. + +Подробнее об истории MathPlayer и появлении MathCAT вы можете узнать в разделе [«Почему MathCAT?»](#why-mathcat). + +На протяжении всего этого времени я стремился обеспечить поддержку математических выражений в интернете и сделать их доступными. Работая в Wolfram Research, я помог запустить проект W3C MathML и с тех пор участвую в деятельности рабочей группы. Сейчас я являюсь сопредседателем рабочей группы W3C Math. За эти годы я также участвовал в работе нескольких других комитетов и активно добивался включения доступности математических выражений в их стандарты. Среди таких групп — NIMAS, EPUB и PDF/UA. + +Для меня большая честь, что в 2023 году Национальная федерация слепых присудила мне премию Джейкоба Болотина в размере $25 000. Из этой суммы я пожертвовал $15 000 проекту _Open Collective_ для улучшения поддержки MathML в браузерах. [По этой ссылке можно узнать, как помочь улучшить поддержку MathML в браузерах](https://opencollective.com/mathml-core-support). diff --git a/docs/ru/nav-commands.md b/docs/ru/nav-commands.md new file mode 100644 index 00000000..e26f526e --- /dev/null +++ b/docs/ru/nav-commands.md @@ -0,0 +1,54 @@ +--- +layout: default +lang: ru +ref: nav-commands +title: Команды навигации MathCAT +--- +# Команды навигации MathCAT и соответствующие сочетания клавиш + +При навигации используются два независимых режима: +* Режим озвучивания после перемещения: выражение читается или описывается, то есть после каждого перемещения озвучивается его краткое описание или обзор. +* Режим навигации: перемещение по подвыражениям, небольшим фрагментам или символам. Сочетания Shift+стрелка вниз и Shift+стрелка вверх циклически переключают более подробные и более общие режимы. + +Объяснение этих режимов приведено после таблицы в разделе [«Режимы навигации»](#navigation-modes). + +Примечание: при навигации по выражению сочетание Ctrl+C в NVDA копирует математическое содержимое текущего узла в формате MathML, LaTeX, ASCIIMath или текста для озвучивания. + + +## Таблица команд навигации + +| Клавиша | Без модификатора | + Ctrl | + Shift | + Ctrl + Shift | +| --- | --- | --- | --- | --- | +| **Стрелка влево** | Перейти к предыдущему элементу | В таблице: перейти к предыдущей ячейке.
В столбиковой записи: перейти к предыдущей цифре.
Примечание: также можно использовать Ctrl+Alt+стрелка влево. | Прочитать предыдущий элемент | Описать предыдущий элемент | +| **Стрелка вправо** | Перейти к следующему элементу | В таблице: перейти к следующей ячейке.
В столбиковой записи: перейти к следующей цифре.
Примечание: также можно использовать Ctrl+Alt+стрелка вправо. | Прочитать следующий элемент | Описать следующий элемент | +| **Стрелка вверх** | Уменьшить детализацию | В таблице: перейти к ячейке выше.
В столбиковой записи: перейти к цифре выше.
Примечание: также можно использовать Ctrl+Alt+стрелка вверх. | Перейти к более общему режиму навигации: расширенному, простому или посимвольному | Уменьшить детализацию до минимальной | +| **Стрелка вниз** | Увеличить детализацию | В таблице: перейти к ячейке ниже.
В столбиковой записи: перейти к цифре ниже.
Примечание: также можно использовать Ctrl+Alt+стрелка вниз. | Перейти к более подробному режиму навигации: расширенному, простому или посимвольному | Увеличить детализацию до максимальной | +| **Enter** | Сообщить текущую позицию | Сообщить полную текущую позицию |   |   | +| **Цифры**
**1–10 (0 означает 10)** | Перейти к метке позиции | Установить метку позиции | Прочитать содержимое метки позиции | Описать содержимое метки позиции | +| **Пробел** | Прочитать текущий элемент | Прочитать текущую ячейку | Переключить режим речи между чтением и описанием | Описать текущий элемент | +| **Home** | Перейти к началу выражения | Перейти к началу строки | Перейти к началу столбца.
Перейти к верхней цифре. | _Пока не реализовано:_ прочитать от начала выражения | +| **End** | Перейти к концу выражения | Перейти к концу строки | Перейти к концу столбца.
Перейти к нижней цифре. | _Пока не реализовано:_ прочитать до конца выражения | +| **Backspace** | Вернуться к предыдущей позиции |   |   |   | + + +## Режимы навигации + +MathCAT поддерживает три режима навигации: расширенный, простой и посимвольный. Первые два режима следуют смыслу озвученного выражения, если только не выбран режим LiteralSpeech. Например, в выражении $\vert x+y \rvert > 0$ вертикальные линии, обозначающие абсолютную величину, не читаются буквально. Вместо них произносится «модуль». При увеличении детализации MathCAT сразу перейдёт к чтению «x плюс y». В посимвольном режиме при перемещении по выражению будут последовательно произнесены «вертикальная линия», «x», «плюс», «y», «вертикальная линия», «больше», «ноль». + +* _Расширенный режим_: перемещение между математически значимыми фрагментами: операторами, ограничителями и операндами. +* _Простой режим_: перемещение по словам. Когда встречается двумерное обозначение, например дробь или корень, оно читается целиком. Увеличение детализации позволяет изучить двумерное обозначение в том же режиме. После уменьшения детализации или выхода из двумерного обозначения выполняется возврат на внешний, более общий уровень навигации. +* _Посимвольный режим_: в действительности включает два полезных режима — режим слов и режим символов. Чтобы перейти к настоящему посимвольному режиму, увеличьте детализацию. Перемещение выполняется по словам или символам. Разница заметна в многозначных числах и именах функций из нескольких символов, например `sin`. В остальных случаях навигация по словам и символам работает одинаково. В обоих вариантах детализация дробей и других подобных элементов увеличивается автоматически. + +## Типичный сценарий использования + +Обычно навигация начинается с первого члена выражения, после чего пользователь при необходимости перемещается вправо. Для перехода между уровнями используются стрелки вверх и вниз. Для перемещения по элементам таблицы используется Alt+Ctrl+стрелка. + +Нажатие Backspace возвращает к предыдущей позиции, что не всегда совпадает с перемещением влево. Например, если стрелка вправо вывела вас из дроби, Backspace вернёт в ту же позицию знаменателя, а стрелка влево перейдёт ко всей дроби. + +Вероятно, большую часть времени вам будет удобнее один определённый режим навигации. Его можно задать в настройках MathCAT. +Однако в любой момент навигации режим можно переключить с помощью Shift+стрелка вверх или Shift+стрелка вниз. +Это полезно, поскольку у каждого режима навигации есть свои преимущества и ограничения. + +## Благодарности +Одна из версий этого документа была подготовлена в рамках проекта ClearSpeak. +ClearSpeak поддерживался Институтом педагогических наук Министерства образования США в рамках гранта R324A110355, предоставленного Educational Testing Service. diff --git a/docs/ru/new_translators_guide_MathCAT_revised.md b/docs/ru/new_translators_guide_MathCAT_revised.md new file mode 100644 index 00000000..a44bb640 --- /dev/null +++ b/docs/ru/new_translators_guide_MathCAT_revised.md @@ -0,0 +1,568 @@ +--- +layout: default +lang: ru +ref: new-translators-guide +title: Руководство переводчика MathCAT +--- +# Руководство переводчика MathCAT + +В этом руководстве объясняется, как создать или обновить перевод MathCAT. В качестве примера целевого языка используется португальский. + +Большая часть руководства посвящена **переводу речи (TTS)**. Если вы работаете над Брайлем, всё равно сначала прочитайте описание основного процесса: настройка среды и тестирование во многом похожи. Затем перейдите к разделу [«Перевод Брайля»](#braille-translation), чтобы узнать об отличиях. + +Для качественного перевода **необязательно** быть опытным программистом. Некоторые успешные переводчики начинали с базовыми знаниями программирования без опыта работы с YAML и Rust. Важнее всего хорошо знать целевой язык, понимать, как на нём обычно произносят или записывают математические выражения, и тщательно проверять результат. + +Знание математики будет очень полезно. Необязательно быть профессиональным математиком, но необходимо замечать неестественные, неясные или математически неверные формулировки. Хорошей отправной точкой будет владение языком на уровне носителя и уверенное знание математики примерно на уровне университета. + +## Оценка трудозатрат + +Полный перевод требует времени, особенно если вы стремитесь сделать его надёжным и удобным. + +Судя по реальным проектам, тщательный перевод TTS может занять около **300–450 часов**, а перевод Брайля — около **160–240 часов**. Сроки сильно зависят от качества исходной заготовки, наличия похожего языка и вашего опыта программирования. + +## Нужна помощь в начале работы? + +Это руководство написали Марте Гьельстад, Тим Арбореалис Лётберг и Андерс Эклунд, создавшие норвежский и шведский переводы MathCAT. Если вам нужна помощь, напишите нам: + +**[mathcat-wg@daisylists.org](mailto:mathcat-wg@daisylists.org)** + +--- + +## Рекомендуемый процесс + +Работу над переводом MathCAT удобно организовать так: + +1. Настроить рабочую копию. +2. Обновить и проверить перевод с помощью аудита. +3. Как можно раньше начать слушать речь. +4. Добавить и запустить тесты. +5. Изучить режимы речи и уровни подробности. +6. Работать с файлами правил. +7. Попросить математика проверить перевод. +8. Провести пользовательское тестирование. +9. Отправить перевод. +10. Поддерживать перевод в актуальном состоянии. + +--- + +## Шаг 1. Настройте рабочую копию + +### Получите репозиторий + +MathCAT размещён на GitHub. + +Если вы раньше не использовали GitHub, не позволяйте этому вас остановить. Для перевода нужна лишь небольшая часть его возможностей: получить копию проекта, редактировать файлы и сохранять изменения. + +Часто проще всего начать с **GitHub Desktop**: +[https://desktop.github.com/](https://desktop.github.com/) + +Основной процесс: + +1. Создайте форк репозитория MathCAT. +2. Клонируйте его на свой компьютер. +3. Работайте в собственной ветке, например `pt`. + +Этого достаточно для начала. + +### Установите необходимые инструменты + +Потребуется несколько инструментов. Поначалу список может показаться длинным, но у каждого инструмента есть ясное назначение. + +* Git: [https://git-scm.com/](https://git-scm.com/) +* GitHub Desktop: [https://desktop.github.com/](https://desktop.github.com/) +* Редактор кода, например VS Code: [https://code.visualstudio.com/](https://code.visualstudio.com/) +* Rust: [https://rust-lang.org/tools/install/](https://rust-lang.org/tools/install/) +* NVDA: [https://www.nvaccess.org/download/](https://www.nvaccess.org/download/) +* MathCAT, установленный из магазина дополнений NVDA. + +Rust требуется только для запуска автоматических тестов. Чтобы переводить MathCAT, изучать программирование на Rust не нужно. + +--- + +## Шаг 2. Обновите перевод и выполните аудит + +Перед началом перевода убедитесь, что файлы вашего языка соответствуют текущей структуре английских файлов. Если пропустить этот шаг, можно потратить время на исправление проблем, вызванных устаревшими файлами. + +### Используйте инструмент аудита + +Документация: +[https://github.com/daisy/MathCAT/blob/main/PythonScripts/audit_translations/README.md](https://github.com/daisy/MathCAT/blob/main/PythonScripts/audit_translations/README.md) + +Используйте инструмент аудита, чтобы найти отсутствующие правила в файлах `pt`, и добавьте их. Подробные инструкции приведены в документации инструмента, поэтому начните с неё. + +### По возможности начните с похожего языка + +Если уже существует грамматически очень похожий язык, часто лучше начать с него, а не с английского или автоматически созданного перевода. + +Например, норвежский перевод был основан на шведском. Это может значительно сэкономить время, особенно если грамматика и порядок слов систематически отличаются от английских. Но перед переводом на свой язык всё равно используйте инструмент аудита для поиска отсутствующих правил. + +--- + +## Шаг 3. Как можно раньше начните слушать речь + +Слушайте результат по мере работы, чтобы быстро замечать ошибки. Перевод может выглядеть безупречно в файле YAML, но при озвучивании оказаться неестественным, повторяющимся или неясным. После перевода каждого файла полезно прослушать несколько выражений с помощью программы экранного доступа. + +### Настройте NVDA для локальных правил + +1. Скачайте NVDA: [https://www.nvaccess.org/download/](https://www.nvaccess.org/download/). +2. В NVDA откройте **Сервис → Магазин дополнений** и установите MathCAT. +3. Создайте копию каталога своего языка, например `pt`, и добавьте её в: + +```text +%AppData%\nvda\addons\MathCAT\globalPlugins\MathCAT\Rules\Languages +``` + +Теперь локальный перевод можно слушать в NVDA. + +### Проверяйте реальные выражения + +Для практической проверки речи возьмите выражения MathML из файлов тестов Rust и поместите их в простой HTML-документ. Затем откройте файл и прослушайте его в NVDA. + +Вносите по одному изменению, снова слушайте результат и делайте заметки. Так удобно изучать реальное поведение правил. + +--- + +## Шаг 4. Добавьте и запустите тесты + +Автоматические тесты — важнейшая часть процесса. Они помогают обнаруживать синтаксические ошибки, документируют ожидаемую речь и позволяют убедиться, что будущие изменения MathCAT незаметно не нарушат перевод. Полезно переводить тесты по мере работы, а не откладывать их напоследок. + +Откройте файл `languages.rs` в `MathCAT/tests` и добавьте строку, в случае португальского языка: + +```rust +mod pt; +``` + +Затем в каталоге `MathCAT/tests/Languages`: + +1. Скопируйте `en.rs` в `pt.rs`. +2. Скопируйте каталог `en` в `pt`. + +Если сначала вы выбрали только один стиль речи, другой можно удалить или закомментировать. + +В каждом тесте замените `"en"` на `"pt"`. + +После этого запустите: + +```bash +cargo test Languages::pt +``` + +Вероятно, при первом запуске многие тесты завершатся с ошибкой. Это нормально. + +### Понимание ошибок тестов + +Типичное сообщение об ошибке выглядит примерно так: + +```rust +left: "1 half" +right: "1 halv" +``` + +Главное здесь — сравнение: + +* `left` — ожидаемый текст, записанный в тесте; +* `right` — текст, фактически созданный MathCAT. + +Если созданный текст верен, скопируйте его в тест. Если неверен, исправьте правило. + +Иногда быстрее запустить тесты до перевода всех ожидаемых результатов. Сообщения об ошибках покажут, что MathCAT уже создаёт на вашем языке, и правильные строки можно будет перенести в тесты. + +### Пример простого теста + +```rust +#[test] +fn common_fraction_half() -> Result<()> { + let expr = " + 1 2 + "; + test("pt", "ClearSpeak", expr, "um meio")?; + return Ok(()); +} +``` + +Здесь: + +* `common_fraction_half` — имя теста; +* `expr` содержит выражение MathML; +* `"pt"` — код языка; +* `"ClearSpeak"` — режим речи; +* `"um meio"` — ожидаемый текст для озвучивания. + +### Пример с настройками + +Некоторые тесты используют `test_prefs`, чтобы проверять правило с заданными настройками. + +```rust +#[test] +fn common_fraction_tenths() -> Result<()> { + let expr = " + 17 10 + "; + test_prefs("en", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Auto")], expr, "17 tenths")?; + test_prefs("en", "ClearSpeak", vec![("Verbosity", "Medium"), ("ClearSpeak_Fractions", "Ordinal")], expr, "17 tenths")?; + return Ok(()); +} +``` + +В таких тестах: + +* `test_prefs` запускает обработку выражения с заданными настройками; +* `vec![]` содержит эти настройки; +* `("Verbosity", "Medium")` задаёт уровень подробности; +* `("ClearSpeak_Fractions", "Auto")` задаёт настройку ClearSpeak. + +В некоторых местах MathCAT также добавляет паузы. В строках тестов они обозначаются знаками препинания, например запятыми и точками с запятой. Если результаты почти совпадают, всегда проверяйте, не связана ли разница с паузами, а не с формулировкой. + +--- + +## Шаг 5. Изучите режимы речи и уровни подробности + +Есть два основных режима речи: ClearSpeak и SimpleSpeak. В статье Нила [A Comparison of Different Styles of Speech for Mathematics](https://scholarworks.calstate.edu/downloads/5t34sv64c) хорошо объясняется различие между ними. Кратко: + +* ClearSpeak должен быть похож на то, как учитель произносит математическое выражение на уроке. +* SimpleSpeak компактно озвучивает простые выражения. Например, $\frac{x}{y} + 1$ читается как «x over y plus one». Слова, обозначающие начало и конец дроби, не используются, потому что числитель и знаменатель просты. Выражение $\frac{x}{y+1}$ читается как «fraction, x over y plus one, end fraction». Здесь такие слова используются, поскольку знаменатель не является простым. + +Вы можете перевести оба стиля речи или сосредоточиться на одном. В зависимости от принятого способа произнесения математических выражений в целевом языке переведённые стили могут различаться сильнее или слабее английских. + +MathCAT также поддерживает уровни подробности **Terse**, **Medium** и **Verbose**. Переводчик сам определяет, насколько они должны различаться в его языке. Иногда естественный перевод объединяет два английских варианта в один, например если в целевом языке нет слова `the`. В других случаях для одного английского варианта может существовать несколько способов произнести выражение. Это нормально. Ясность важнее искусственно созданных различий. + +В ClearSpeak доступно много настроек, используемых в правилах и тестах. Подробнее см. [настройки ClearSpeak](https://github.com/daisy/MathCAT/blob/main/docs/ClearSpeakRulesAndPreferences.docx). Сейчас эти настройки не видны пользователям в настройках MathCAT в NVDA. + +--- + +## Шаг 6. Работайте с файлами правил + +### Какие файлы требуется перевести? + +Основная работа выполняется в файлах правил YAML. При переводе на португальский язык файлы находятся в каталоге `MathCAT\Rules\Languages\pt`: + +* `ClearSpeak_Rules.yaml` +* `definitions.yaml` +* `navigate.yaml` +* `overview.yaml` (необязательно) +* `SimpleSpeak_Rules.yaml` +* `unicode.yaml` +* `unicode-full.yaml` (частично необязательно) + +В подкаталоге `MathCAT\Rules\Languages\pt\SharedRules` находятся небольшие правила, из которых состоят правила в `ClearSpeak_Rules.yaml` и `SimpleSpeak_Rules.yaml`. Все эти файлы также требуется перевести. + +### С чего начать + +Для знакомства с принципами работы правил удобно начать, например, с `ClearSpeak_Rules.yaml` и нескольких файлов из `SharedRules`. + +Как указано выше, многие необходимые изменения будут обнаружены с помощью тестов. Однако файлы `unicode.yaml`, `unicode-full.yaml`, `navigate.yaml` и `definitions.yaml` недостаточно хорошо покрыты тестами и требуют особого внимания при ручной проверке. + +Не тратьте много времени на `unicode-full.yaml` в начале работы. Он содержит несколько тысяч строк, а большинство символов крайне редко встречаются на практике. Некоторые из них даже не отображаются в VS Code. Оставьте этот файл напоследок: достаточно просмотреть его и проверить наиболее узнаваемые символы. + +Файл `overview.yaml` также менее важен, чем остальные, поскольку не влияет непосредственно на чтение математических выражений. Однако он предоставляет программам экранного доступа краткие структурные обзоры выражений, поэтому со временем его стоит проверить. + +### Что делать + +Основная задача — найти все ключи YAML `t:`, соответствующие произносимым текстовым строкам. Строчная буква в ключе `t:` означает, что текст ещё не проверен переводчиком. Проверьте автоматически созданный перевод или измените его. После проверки замените ключ на `T:`, чтобы отметить обработанный фрагмент. + +Тот же принцип применяется ко всем ключам `ct:`, `ot:`, `spell:`, `pronounce:` и `IfThenElse:`, однако они встречаются гораздо реже, чем `t:`. + +### Адаптация и создание собственных правил + +Тесты покрывают большинство правил. Однако в португальском или другом языке могут существовать грамматические особенности, которых нет в английском и которые не охвачены тестами. Поэтому полезно просмотреть все правила и убедиться в их корректности. + +При грамматических различиях простой замены текстовых строк недостаточно. Необходимо изменить правила или добавить новые. Для совершенно новых правил полезно написать тесты, подтверждающие правильность результата. Не бойтесь пробовать разные варианты. + +Ниже приведён краткий обзор структуры правила MathCAT. Каждое правило описывает условия применения и произносимый текст. Основные части: + +* `name` — имя правила; +* `tag` — элемент MathML, к которому применяется правило; +* `match` — условие применения правила; +* `replace` — результат и действия MathCAT. + +Внутри `replace` часто используются следующие команды: + +* `t:` — произносимый текст; +* `ct:` — присоединить текст без пробела перед ним; +* `ot:` — необязательный текст для предотвращения повторений; +* `x:` — выражение XPath; +* `test:` — условная логика; +* `pause:` — пауза в речи; +* `bookmark:` — синхронное выделение. + +#### Пример 1 + +```yaml +- name: squared + tag: power + match: "*[2][self::m:mn][.='2'] and $ClearSpeak_Exponents = 'Auto'" + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - t: "squared" # phrase(7 'squared' equals 49) +``` + +Это правило означает: если степень имеет показатель 2 и настройка `ClearSpeak_Exponents` равна `Auto`, следует вывести «основание в квадрате». Например, $x^2$ озвучивается как «x squared». + +Обратите внимание: + +* `match` определяет выражения, к которым применяется правило. +* Выражение после `x` — это XPath. Оно получает первый дочерний узел элемента степени, то есть основание. +* Строка после `t` должна быть переведена. После проверки перевода измените `t` на прописную `T`. + +#### Пример 2 + +```yaml +- name: fraction-over-text + tag: fraction + match: + - "not($ClearSpeak_Fractions='General' or $ClearSpeak_Fractions='GeneralEndFrac') and" + - "( " + - " ((*[1][self::m:mi or self::m:mtext][string-length(.)>1]) or " + - " (*[1][self::m:mrow][count(*)=3][ " + - " *[1][self::m:mn] and " + - " *[2][self::m:mo][.='⁢'] and " + - " *[3][self::m:mi or self::m:mtext][string-length(.)>1] ]) ) and" + - " ((*[2][self::m:mi or self::m:mtext][string-length(.)>1]) or " + - " (*[2][self::m:mrow][count(*)=3][ " + - " *[1][self::m:mn] and " + - " *[2][self::m:mo][.='⁢'] and " + - " *[3][self::m:mi or self::m:mtext][string-length(.)>1] ]) )" + - ")" + replace: + - x: "*[1]" + - t: "over" # phrase(the fraction 3 'over' 4) + - x: "*[2]" + - test: + if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='OverEndFrac'" + then: + - pause: short + - t: "end fraction" # phrase(7 over 8 'end fraction') + - pause: short +``` + +Для применения правила должны выполняться все условия из `match`. + +В обычной формулировке: + +* настройка `ClearSpeak_Fractions` не должна иметь значение `General` или `GeneralEndFrac`, **и** +* числитель должен быть текстовой строкой длиной более одного символа или числом, неявно умноженным на такую строку, **и** +* знаменатель должен удовлетворять тем же условиям, что и числитель. + +Если все условия выполнены, дробь озвучивается как «числитель над знаменателем». Если `ClearSpeak_Fractions` имеет значение `EndFrac` или `OverEndFrac`, также добавляется короткая пауза перед фразой «конец дроби». + +Например, выражение $\frac{\text{meter}}{\text{second}}$ озвучивается как «meter over second». + +--- + +## Шаг 7. Попросите математика проверить перевод + +Когда перевод станет достаточно полным, попросите математика, учителя математики или другого специалиста-предметника, владеющего языком как родным, проверить представительный набор результатов. + +Полезно подготовить несколько примеров выражений вместе с расшифровками текста MathCAT. Попросите проверяющего отметить всё, что звучит математически странно, неоднозначно или непривычно. + +Такая проверка важна: перевод может быть грамматически верным, но не похожим на настоящую математическую речь. Специалист часто замечает проблемы в терминологии, именах символов и способах озвучивания крупных структур. + +При этом не всегда возможно идеально удовлетворить каждое пожелание. Особенности программ экранного доступа и структуры MathCAT могут означать, что наиболее элегантная с математической точки зрения формулировка не является самой удобной. + +--- + +## Шаг 8. Проведите пользовательское тестирование + +Если вы сами не являетесь опытным пользователем программы экранного доступа, пользовательское тестирование обязательно. + +Лучше всего привлекать незрячих пользователей, уверенно работающих с математикой, особенно специалистов в области STEM. Предоставьте им локальную версию MathCAT с переводом и HTML-файл с представительными выражениями MathML. + +Сначала полезно дать пользователям возможность самостоятельно попробовать перевод. Затем встретьтесь с ними и разберите дополнительные примеры вместе. Спрашивайте не только о правильности формулировок, но и о ясности структуры, полезности пауз и соответствии привычному способу восприятия. + +Устная математика оценивается не только по корректности: она должна быть удобна в реальной работе с программой экранного доступа. На практике обратная связь пользователей часто важнее теоретических предпочтений в грамматике, особенно если спор касается ясности или навигации, а не математического смысла. + +--- + +## Шаг 9. Отправьте перевод + +Перед созданием pull request или передачей перевода полезно выполнить итоговую проверку. Это уменьшит количество лишних исправлений и упростит рецензирование. + +### Итоговый список проверок + +Проверьте следующие пункты. + +#### Файлы и структура + +* Файлы вашего языка соответствуют текущей структуре английских файлов. +* Правила не пропущены: инструмент аудита запущен. +* В переводе нет очевидных остатков английского текста. + +#### Состояние перевода + +* Во всём проверенном тексте `t:` заменено на `T:`, `ct:` на `CT:`, `ot:` на `OT:` и т. д. +* Вы осознанно проверили формулировки, а не только использовали автоматический перевод. + +#### Речь + +* Вы прослушали представительный набор выражений в NVDA. +* На вашем языке результат звучит естественно и однозначно. +* Паузы и структура хорошо воспринимаются на слух. + +#### Тесты + +* Для вашего языка добавлены тесты. +* Все тесты проходят: + + ```bash + cargo test Languages::pt + ``` + +* Ожидаемые результаты тестов соответствуют задуманной речи, включая паузы. + +#### Экспертная проверка + +* Математик или другой специалист проверил представительный набор результатов. +* Основные проблемы терминологии и структуры устранены. + +#### Пользовательское тестирование + +* Проведено хотя бы некоторое тестирование с пользователями программ экранного доступа. +* Учтена обратная связь о ясности и удобстве использования. + +### Отправка перевода + +После выполнения списка проверок: + +1. **Создайте коммит с изменениями** в своей ветке, например `pt`. +2. **Отправьте ветку** на GitHub. +3. **Создайте pull request** в основной репозиторий MathCAT. + +В pull request полезно указать: + +* краткое описание перевода; +* завершённые части, например ClearSpeak, SimpleSpeak и тесты; +* известные ограничения и области, требующие дальнейшей работы. + +После этого сопровождающие проекта проверят работу и могут предложить изменения перед слиянием. + +### Когда перевод станет доступен пользователям? + +После слияния перевода с MathCAT: + +* он войдёт в будущие выпуски MathCAT; +* пользователи смогут выбрать язык в поддерживаемых программах экранного доступа, например NVDA. + +Чтобы использовать перевод сразу, не дожидаясь официального выпуска: + +* продолжайте использовать каталог локальных правил в NVDA или +* установите разрабатываемую версию MathCAT, содержащую ваши изменения. + +--- + +## Шаг 10. Поддерживайте перевод в актуальном состоянии + +Строго говоря, перевод никогда не бывает «завершён». Даже после слияния со временем появятся небольшие улучшения, особенно когда пользователи начнут работать с реальными материалами. + +Кроме того, английская версия активно развивается, и новые функции необходимо добавлять в перевод. Для синхронизации используйте [инструмент аудита перевода](https://github.com/daisy/MathCAT/blob/main/PythonScripts/audit_translations/README.md). + + + +--- + + +## Перевод Брайля + +Общий процесс перевода Брайля похож на перевод речи, но есть несколько важных отличий. + +Перед работой над правилами Брайля определите, какой стандарт математической записи используется в вашей стране. В некоторых странах существуют установленные системы записи Брайля, а в других применяются обозначения другого типа, например LaTeX или ASCIIMath. Решите заранее, какому стандарту должен следовать MathCAT. + +Если уже существует похожая система Брайля, её можно использовать как отправную точку. В противном случае, скорее всего, придётся начинать почти с нуля. + +### Файлы Брайля + +Для новой системы записи Брайля создайте три файла `.yaml` в `Rules\Braille\your-braille-language`: + +1. `xxx_Rules.yaml`, где `xxx` — имя системы записи Брайля. Файл содержит правила преобразования MathML в Брайль. +2. `unicode.yaml` — переводы наиболее распространённых символов. +3. `unicode-full.yaml` — переводы остальных символов. + +Два файла Unicode используются по практической причине: небольшой файл с распространёнными символами ускоряет запуск. + +### Пример шведского языка + +Шведский перевод Брайля следует официальной шведской системе математической записи Брайля. Это шеститочечный стандарт, предназначенный для печатных материалов. Устоявшегося восьмиточечного математического стандарта не было, поэтому перевод основывался на единственном признанном стандарте. + +Работа над шведским переводом также показала, что исходные стандарты не всегда достаточно строги для программного обеспечения. Некоторые печатные стандарты допускают расстановку пробелов в соответствии с печатным оригиналом. Для MathCAT этого недостаточно. Правила должны быть явными, поэтому иногда потребуется устранить несогласованности и принять однозначные решения о пробелах. + +### Тестирование Брайля + +Основной принцип похож на тестирование TTS, но вместо строк речи проверяются символы Брайля в Unicode. + +Удобно писать тесты Rust с ожидаемым выводом Брайля из национального стандарта или согласованных примеров. Запускайте тесты, изучайте ошибки и дорабатывайте правила до совпадения результата. + +В шведском переводе использовались примеры непосредственно из шведского стандарта Брайля, особенно для арифметики, дробей, подстрочных и надстрочных индексов, а также основных функций. Это помогло прочно связать правила с реальным стандартом. + +### Особые сложности Брайля + +При работе с Брайлем часто возникают проблемы, не встречающиеся в переводе речи. Например: + +* стандарт может быть неполным или несогласованным; +* стандарт может предполагать печатный контекст, а не автоматически создаваемый вывод; +* у некоторых символов может не быть однозначного устоявшегося представления; +* правила пробелов могут потребовать большей строгости, чем предполагает исходный стандарт. + +### Пользовательское тестирование Брайля + +Пользовательское тестирование Брайля так же важно, как тестирование речи. + +При работе над шведским переводом тестировщики использовали NVDA вместе со средством просмотра Брайля, чтобы контролировать результат во время удалённых сеансов. Это позволяло наблюдать вывод, даже если сами рецензенты читали его на физическом дисплее Брайля. + +Как и для речи, важна не только техническая правильность. Результат должен быть читаемым, согласованным и полезным в реальной работе. + +--- + +## Приложение + +### Практические советы для эффективной работы + +Инструменты искусственного интеллекта могут быть полезны при подготовке чернового перевода, поиске альтернативных формулировок и разборе сложного правила. Они также могут помочь при написании и редактировании правил. + +Однако относитесь к ним как к помощникам, а не как к авторитетным источникам. Автоматический перевод всегда требует проверки человеком, а изменения правил — тестирования на реальном выводе. + +В целом полезно вносить по одному изменению, часто запускать тесты и при возможности обсуждать сложные случаи с другими переводчиками или специалистами. + +### Устранение проблем + +Если что-то работает не так, как ожидалось, сначала проверьте отступы. Ошибки YAML распространены, и их бывает неожиданно трудно заметить. При использовании VS Code полезно установить расширение YAML, помогающее находить такие ошибки. + +Также внимательно изучайте результаты тестов и снова слушайте речь. Многие проблемы легче услышать, чем увидеть. + +### XPath + +Во многих частях правил речи используется XPath. Это распространённый и хорошо документированный способ выбора частей XML-документа. В интернете есть множество руководств. Если вы ещё не знакомы с XPath, рекомендуется прочитать одно из них. В MathCAT используется немного расширенная реализация XPath 1.0. + +В MathCAT обычно применяются лишь некоторые возможности XPath, а также несколько специальных функций. Ниже кратко описаны распространённые варианты использования XPath. + +| Запись | Значение | +| --- | --- | +| `*` | Соответствует всем дочерним узлам. | +| `[...]` | Выбирает узлы из текущего сопоставления. | +| `*[1]` | Выбирает первый дочерний узел. | +| `*[self::m:mn]` | Выбирает все дочерние элементы `mn`. Префикс `m` показывает, что элемент находится в пространстве имён MathML. | +| `*[1][self::m:mn]` | Выбирает первый дочерний узел, если он является элементом `mn`. | +| `*[1][self::m:mo][text()='-']` | Выбирает первый дочерний узел, если он является элементом `mo` с содержимым `-`. Можно записать `*[1][text()='-']`, поскольку другие узлы, скорее всего, не содержат `-`, но такое содержимое допустимо для `mtext`, поэтому безопаснее указать имя элемента. | +| `count(*[2]/*)` | Количество дочерних узлов второго дочернего узла. | +| `count(preceding-sibling::*)+1` | Количество одноуровневых узлов перед текущим элементом плюс один. | + +MathCAT добавляет специальные функции, упрощающие написание правил: + +| Функция | Значение | +| --- | --- | +| `IsNode(nodes, type)` | Возвращает true, если все узлы имеют одинаковый тип. Допустимые типы:
`"simple"` — заданный набор элементов ClearSpeak;
`"leaf"` — один из листовых элементов MathML;
`"2D"` — двумерный узел, например `mfrac` или `mroot`;
`"modified"` — узел с индексом или элементом над ним либо под ним;
`"scripts"` — узел с подстрочным и (или) надстрочным индексом;
`"common_fraction"` — целочисленные числитель и знаменатель. | +| `ToOrdinal` | | +| `ToCommonFraction` | | +| `IsBracketed(openChar, closeChar, requiresComma)` | | +| `BaseNode(node)` | Рекурсивно возвращает основу узла с индексами. | +| `IsInDefinition(node, name)` | Возвращает true, если узел входит в список `name`, заданный в `definitions.yaml`. | +| `IfThenElse(test, then-part, else-part)` | Возвращает `then-part`, если условие истинно, иначе возвращает `else-part`. Все аргументы являются выражениями XPath. | +| `DistanceFromLeaf(node, left_side, treat_2d_elements_as_tokens)` | Возвращает расстояние от текущего узла до крайнего левого или правого листа: для символа 0, для токена 1. Если `left_side` равно `true`, обход выполняется по крайним левым дочерним узлам до листа, иначе по правым. Если `treat_2d_elements_as_tokens` равно `true`, двумерные обозначения, например дроби, считаются одиночными токенами, подобными листам. | +| `EdgeNode(node, "left"/"right", stopNodeName)` | Возвращает узел остановки, если исходный узел находится у левого или правого края указанного предка. Значением `stopNodeName` также может быть `"2D"`. Если соответствие не найдено, возвращается исходный узел. Если `stopNodeName` равно `"math"`, учитывается пунктуация, поскольку она не является частью математического выражения. | +| `DEBUG(xpath)` | Добавляет полезные для диагностики сведения в отладочный вывод. | + +Следующие функции используются правилами кода Немета: + +| Функция | Значение | +| --- | --- | +| `NestingChars` | Используется правилами `mfrac`, `msqrt` и `mroot`, чтобы повторять символы нужное количество раз. | +| `BrailleChars` | Используется элементами-токенами для сложной перестановки индикаторов кода Немета, например прописных букв и начертания шрифта. | diff --git a/docs/ru/users.md b/docs/ru/users.md new file mode 100644 index 00000000..29d4fa9f --- /dev/null +++ b/docs/ru/users.md @@ -0,0 +1,203 @@ +--- +layout: default +lang: ru +ref: users +title: Руководство пользователя MathCAT +--- +# Логотип MathCAT Руководство пользователя + +## Рекомендации по выбору голоса + +В NVDA можно использовать разные синтезаторы речи. Для выбора откройте `Параметры:Настройки...` в NVDA, а затем выберите категорию `Речь`. Обычно доступны как минимум три варианта: eSpeak NG, Microsoft Speech API и голоса Windows OneCore. Все синтезаторы работают, но голоса Windows OneCore неправильно произносят «a», поэтому рекомендуется выбрать другой вариант. В частности, хорошей заменой голосам OneCore служит Microsoft Speech API. + +## Информация для пользователей MathCAT + +MathCAT поддерживает ряд настроек озвучивания, Брайля и навигации. Они описаны ниже. +Пока поддерживаются не все настройки. Для каждой настройки указано текущее состояние поддержки. Символ ✓ перед настройкой означает, что она поддерживается хотя бы частично. + +Примечание: в NVDA настройки задаются в диалоге параметров MathCAT. Чтобы открыть его, перейдите в параметры NVDA, выберите «Параметры», а затем «Настройки MathCAT...». Настройки разделены на три категории: «Речь», «Навигация» и «Брайль». Такое же разделение используется ниже. + +MathCAT поддерживает несколько режимов навигации. Способ входа в режим навигации и выхода из него зависит от используемой вспомогательной технологии (подробнее в списке ниже). MathCAT принимает те же команды и сочетания клавиш, что и MathPlayer. Они [перечислены в этом документе](nav-commands.md). +Документация описывает множество полезных способов навигации по математическим выражениям. Чтобы быстро начать работу: + +* Используйте клавиши-стрелки для перемещения влево, вправо, вверх и вниз по структуре математического выражения, например для входа в дробь и выхода из неё. +* Внутри таблицы нажимайте Ctrl+стрелка для перемещения по ячейкам. +* Нажимайте Home и End для перехода к началу и концу выражения. +* Нажимайте пробел для озвучивания текущей позиции. +* Нажимайте Shift+стрелка вверх или Shift+стрелка вниз для изменения режима навигации (см. [документацию по навигации](nav-commands.md)). + +Чтобы начать навигацию: + +* NVDA: нажмите NVDA+Alt+M или пробел для входа в режим навигации по математическому выражению, а для выхода нажмите Escape. + +Навигация MathCAT работает одинаково в Word и в браузере. + +При навигации по выражению сочетание Ctrl+C в NVDA копирует математическое содержимое текущего узла. Поддерживаются следующие форматы: + +* MathML (по умолчанию); +* LaTeX; +* ASCIIMath; +* текст для озвучивания. + +## Список настроек + +Ниже перечислены настройки. Большинство из них принимают только ограниченный набор значений, который указан в описании. +Значение по умолчанию приведено в \[квадратных скобках\]. + +### Настройки речи + +* ✓Impairment: [Blindness] + * Значения: Blindness, LowVision, LearningDisability. + * Описание: определяет, следует ли устранять неоднозначность некоторых обозначений при озвучивании. + * Состояние: основное внимание уделялось значению Blindness, но другие значения также частично поддерживаются. Эту поддержку необходимо улучшить. + +* ✓Language: [en] + * Значения: любой известный код языка и подкод, например `en-uk`. + [Список вариантов приведён на этом сайте](https://www.venea.net/web/culture_code). + * Описание: определяет используемый язык. + Если региональный вариант не найден среди правил озвучивания, используется основной язык. Если не найдены и правила для основного языка, используется английский язык (`en`). + * Состояние: сейчас поддерживаются только английский, испанский, финский, индонезийский, шведский, вьетнамский и китайский языки. + Другие языки будут добавляться с помощью добровольцев. + +* ✓SpeechStyle: [ClearSpeak] + * Значения: любой реализованный стиль речи. Сейчас доступны только ClearSpeak и SimpleSpeak. + * Описание: стиль речи, то есть согласованный подход к озвучиванию выражения. + * ClearSpeak был разработан ETS для важных экзаменов, например SAT. [Подробности спецификации ClearSpeak приведены в этом документе Word](../ClearSpeakRulesAndPreferences.docx). + * SimpleSpeak стремится сократить озвучивание: простые выражения, например $\frac{a}{b}$, читаются быстро, без обрамляющих слов («a over b»). Они отличаются от более сложных выражений, например $\frac{a}{b+1}$, в которых обрамляющие слова используются всегда («fraction a over b plus 1 end fraction»). + * Состояние: сейчас реализованы только ClearSpeak и SimpleSpeak, но в дальнейшем, вероятно, будет реализован MathSpeak. + +* ✓Verbosity: [Medium] + * Значения: Terse, Medium, Verbose. + * Описание: определяет количество дополнительных слов при озвучивании. Например, в подробном режиме квадратный корень читается как «the square root of x», а в кратком — как «square root x». + * Состояние: настройка поддерживается, но со временем, вероятно, будет дорабатываться. + +* ✓MathRate: [100] + * Значения: число от 1 до 100. + * Описание: изменяет относительную скорость речи в процентах от стандартной скорости речевого синтезатора. Значение `100` означает, что математические выражения читаются с той же скоростью, что и обычный текст. + Настройка работает только в реализациях, которые указывают MathCAT создавать разметку речевого синтезатора, например SSML. + * Состояние: настройка должна работать в NVDA. + +* ✓PauseFactor: [50] + * Значения: число от 0 до 100. + * Описание: изменяет относительную длительность добавляемых MathCAT пауз. Значение 0 отключает все паузы, а значение 100 увеличивает обычную длительность пауз в десять раз. + Настройка работает только в реализациях, которые указывают MathCAT создавать разметку речевого синтезатора, например SSML. + * Состояние: настройка должна работать в NVDA. + +* ✓SpeechSound: [None] + * Значения: None, Beep. + * Описание: перед чтением выражения и после него воспроизводится звуковой сигнал. + * Состояние: настройка должна работать в NVDA. + +* SubjectArea: [General] + * Состояние: настройка использовалась в MathPlayer, но пока не реализована. Я жду дальнейшего обсуждения в рабочей группе MathML: возможно, эта настройка будет использоваться для задания разных значений `intent` по умолчанию. + +* Chemistry: [SpellOut] + * Значения: SpellOut, AsCompound, Off. + * Описание: определяет способ чтения химических формул. Примеры для $\mathrm{H}_2\mathrm{O}$: + * ✓SpellOut: «H 2 O» (настройка подробности определяет, произносятся ли слова `sub` и `super`); + * AsCompound: «Water»; + * ✓Off: «H sub 2 O». + * Состояние: реализовано множество эвристик для определения того, является ли запись химической формулой. Распознавание химических обозначений не всегда очевидно, поэтому MathCAT иногда может не распознать формулу или по ошибке принять другую запись за химическую. Работа группы MathML может существенно упростить авторам явное указание химических формул. + +SpeechOverrides: + +* ✓CapitalLetters: "cap" # слово-префикс для прописных букв, если оно не задано в unicode.yaml; пустая строка передаёт обработку программе экранного доступа +* LeftParen: "" # переопределение слова +* RightParen: "" # переопределение слова + +ClearSpeak содержит ряд настроек. Они предназначены для авторов, но могут задаваться и пользователями, хотя обычно не слишком им полезны. + +* ✓CapitalLetters: Auto, SayCaps или изменение высоты тона +* ✓AbsoluteValue: Auto, AbsEnd, Cardinality, Determinant +* ✓Fraction: Auto, Ordinal, Over, FracOver, General, EndFrac, GeneralEndFrac, OverEndFrac, Per +* ✓Exponent: Auto, Ordinal, OrdinalPower, AfterPower +* ✓Roots: Auto, PosNegSqRoot, RootEnd, PosNegSqRootEnd +* ✓Functions: Auto, None +* ✓Trig: Auto, TrigInverse, ArcTrig +* ✓Log: Auto, LnAsNaturalLog +* ✓ImpliedTimes: Auto, MoreImpliedTimes, None +* ✓Paren: Auto, Speak, SpeakNestingLevel, Silent, CoordPoint, Interval +* ✓Matrix: Auto, SpeakColNum, SilentColNum, EndMatrix, Vector, EndVector, Combinatorics +* ✓MultiLineLabel: Auto, Case, Constraint, Equation, Line, None, Row, Step +* ✓MultiLineOverview: Auto, None +* ✓MultiLinePausesBetweenColumns: Short, Long +* ✓Sets: Auto, woAll, SilentBracket +* ✓MultSymbolX: Auto, By, Cross +* ✓MultSymbolDot: Auto, Dot +* ✓TriangleSymbol: Auto, Delta +* ✓Ellipses: Auto, AndSoOn +* ✓VerticalLine: Auto, SuchThat, Divides, Given +* ✓SetMemberSymbol: Auto, Belongs, Element, Member +* ✓Prime: Auto, Angle, Length +* ✓CombinationPermutation: Auto, ChoosePermute +* ✓Bar: Auto, Bar, Conjugate, Mean + +### Настройки навигации (см. [документацию по навигации](nav-commands.md)) + +* ✓NavMode: Enhanced — Enhanced, Simple, Character. +* ResetNavMode: false — запоминать и использовать предыдущее значение. +* Overview: false — озвучивать выражение или давать его описание и обзор. +* ResetOverView: true — запоминать и использовать предыдущее значение. +* ✓NavVerbosity: Medium — Terse, Medium, Full (слова для произнесения команды навигации). +* ✓AutoZoomOut: true — автоматически уменьшать детализацию двумерных выражений. Если настройка отключена, для принудительного уменьшения детализации используйте Shift+стрелка. + * `true`: если вы находитесь у края двумерного выражения, например дроби или надстрочного индекса, и пытаетесь выйти из него влево или вправо, перемещение разрешается, а уровень детализации устанавливается в соответствии с предыдущим или следующим элементом. + * `false`: перемещение влево или вправо за край двумерного выражения запрещено. Чтобы переместиться, необходимо уменьшить детализацию, возможно несколько раз, пока вы не перестанете находиться у края. +* CopyMathAS: определяет формат копирования математического содержимого текущего узла навигации: MathML, LaTeX, ASCIIMath или текст для озвучивания. + + +### Настройки Брайля + +* ✓BrailleCode: [Nemeth] + * Значения: любая реализованная система записи Брайля. + * Описание: используемая система математической записи Брайля. + * Состояние: сейчас поддерживаются ASCIIMath, ASCIIMath-Finnish, CMU, LaTeX, код Немета, шведская система, UEB и вьетнамская система. Поддержка других систем записи Брайля зависит от помощи новых участников. +* ✓BrailleNavHighlight: [EndPoints] + * Значения: Off, FirstChar, EndPoints, All. + * Описание: выделяет выбранный узел навигации точками 7 и 8. +* UEB: + * ✓START_MODE: [Grade2] + * Значения: Grade1, Grade2. + * Описание: предполагаемый начальный режим UEB. Значение Grade1 означает, что используется режим фрагмента Grade 1. + * ✓UseSpacesAroundAllOperators: [false] + * Значения: true/false. + * Описание: рекомендации UEB предполагают, что в младших классах может быть полезно добавлять пробелы вокруг таких операторов, как `+` и `-`. Обычно пробелы добавляются только вокруг операторов отношений, например `=` и `<`. + +Во многих системах записи Брайля можно определять пользовательские символы. MathCAT предоставляет для этого несколько настроек. + +В коде Немета определены начертания Bold, Italic, SansSerif и Script, но не определено начертание DoubleStruck (Blackboard Bold). +Здесь можно задать определяемое транскрибатором изменение начертания. По умолчанию DoubleStruck сопоставляется с Italic. + +* Nemeth: + * ✓SansSerif: "⠠⠨" + * ✓Bold: "⠸" + * ✓DoubleStruck: "⠨" + * ✓Script: "⠈" + * ✓Italic: "⠨" + +В [руководстве UEB по техническим материалам](https://iceb.org/Guidelines_for_Technical_Material_2008-10.pdf) рекомендуется обычно обрабатывать Fraktur и DoubleStruck как Script. +Вместо этого здесь можно задать пользовательский префиксный индикатор начертания. +Примечание: префиксы с первого по пятый: "⠈⠼", "⠘⠼", "⠸⠼", "⠐⠼", "⠨⠼". + +* UEB: + * ✓DoubleStruck: "⠈" [script] + * ✓Fraktur: "⠈" [script] + * ✓SansSerif: "⠈⠼" [первый определяемый транскрибатором префиксный индикатор начертания] + * ✓GreekVariant: "⠨" [по умолчанию Greek] + +Набор определяемых символов для вьетнамской системы ещё обсуждается. Вероятно, некоторые значения изменятся. + +* Vietnam: + * ✓UseDropNumbers: [false] + * Значения: true, false. + * Описание: опускает цифры на строку ниже в простых числовых дробях. + * ✓DoubleStruck: "⠈" [script] + * ✓Fraktur: "⠈" [script] + * ✓SansSerif: "⠈⠼" [первый определяемый транскрибатором префиксный индикатор начертания] + * ✓GreekVariant: "⠨" [по умолчанию Greek] + +### Другие настройки + +MathCAT исправляет некачественный MathML. В MathML числа часто размечаются неправильно. Чтобы исправить их корректно, MathCAT должен знать региональные настройки: символы, которые могут разделять группы цифр, и символы десятичного разделителя. Обычно ассистивная технология задаёт эти параметры на основе кода страны в документе. Но код страны может отсутствовать, и тогда ассистивной технологии приходится определять формат по коду языка. + +* DecimalSeparators: "." # [по умолчанию] +* BlockSeparators: ", \u00A0\u202F" # [по умолчанию; включает два варианта неразрывных пробелов] From ce7c227566f6248e788a61b00d7cf251eb6eea11 Mon Sep 17 00:00:00 2001 From: Danil <81031453+Kostenkov-2021@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:04:35 +0300 Subject: [PATCH 2/2] First version of Russian Braille This commit adds first druft version for Russian Braille. --- Rules/Braille/Russian/Russian_Rules.yaml | 317 +++++++++++++++++++++++ Rules/Braille/Russian/definitions.yaml | 2 + Rules/Braille/Russian/unicode-full.yaml | 301 +++++++++++++++++++++ Rules/Braille/Russian/unicode.yaml | 289 +++++++++++++++++++++ src/braille.rs | 34 +++ tests/braille.rs | 4 + tests/braille/Russian/russian.rs | 37 +++ 7 files changed, 984 insertions(+) create mode 100644 Rules/Braille/Russian/Russian_Rules.yaml create mode 100644 Rules/Braille/Russian/definitions.yaml create mode 100644 Rules/Braille/Russian/unicode-full.yaml create mode 100644 Rules/Braille/Russian/unicode.yaml create mode 100644 tests/braille/Russian/russian.rs diff --git a/Rules/Braille/Russian/Russian_Rules.yaml b/Rules/Braille/Russian/Russian_Rules.yaml new file mode 100644 index 00000000..1effd8c4 --- /dev/null +++ b/Rules/Braille/Russian/Russian_Rules.yaml @@ -0,0 +1,317 @@ +--- +# Russian mathematical braille rules. + +- + name: whitespace + tag: "!*" + match: "not(self::m:math) and not($MatchingWhitespace) and (@data-previous-space-width >= 0.25 or @data-following-space-width >= 0.25)" + replace: + - with: + variables: [MatchingWhitespace: "true()"] + replace: + - test: + - if: "@data-previous-space-width > 1.1" + then: [t: "⠀"] + - else_if: "@data-previous-space-width >= 0.25" + then: [t: "W"] + - x: "." + - test: + - if: "@data-following-space-width > 1.1" + then: [t: "⠀"] + - else_if: "@data-following-space-width >= 0.25" + then: [t: "W"] + +- + name: omission-intent + tag: "!*" + match: "contains(@intent, ':blank')" + replace: [t: "⠀"] + +- + name: unicode-override + tag: "*" + match: "@data-unicode" + replace: [x: "@data-unicode"] + +- + name: default + tag: math + match: "." + variables: + - RowStart: "''" + - RowEnd: "''" + - MatchingWhitespace: "false()" + replace: [x: "*"] + +- + name: default + tag: [mrow, mstyle, semantics] + match: "." + replace: [x: "*[1] | *[position()>1]"] + +- + name: no-content + tag: [math, mrow] + match: "not(*)" + replace: [t: "⠀"] + +- + name: default + tag: msqrt + match: "." + replace: + - t: "⠩⠱" + - x: "*[1]" + - t: "⠹" + +- + name: default + tag: mroot + match: "." + replace: + - t: "⠩" + - x: "*[2]" + - t: "⠱" + - x: "*[1]" + - t: "⠹" + +- + name: default + tag: mfrac + match: "." + replace: + - t: "⠆" + - x: "*[1]" + - t: "⠀⠳" + - x: "*[2]" + - t: "⠰" + +- + name: binomial-frac + tag: mrow + match: "IsBracketed(., '(', ')') and *[2][self::m:mfrac][@linethickness=0]" + replace: + - x: "*[1]" + - t: "⠨⠉⠡" + - x: "*[2]/*[2]" + - t: "⠌" + - x: "*[2]/*[1]" + - x: "*[3]" + +- + name: binomial-table + tag: mrow + match: "IsBracketed(., '(', ')') and *[2][self::m:mtable][count(*)=2 and count(*[1])=1] and contains(@intent, 'binomial(')" + replace: + - x: "*[1]" + - t: "⠨⠉⠡" + - x: "*[2]/*[2]/*[1]/*[1]" + - t: "⠌" + - x: "*[2]/*[1]/*[1]/*[1]" + - x: "*[3]" + +- + name: default-matrix + tag: mrow + variables: + - RowStart: "*[1]" + - RowEnd: "*[3]" + match: + - "*[2][self::m:mtable] and" + - (IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')) + replace: [x: "*[2]"] + +- + name: default + tag: mtable + match: "." + replace: + - t: "⠞⠃⠇" + - x: "*" + +- + name: default + tag: [mtr, mlabeledtr] + match: "." + replace: + - test: + if: "preceding-sibling::*" + then: [t: "⠨⠳"] + - x: $RowStart + - test: + if: .[self::m:mlabeledtr] + then: [x: "*[position()>1]"] + else: [x: "*"] + - x: $RowEnd + +- + name: default + tag: mtd + match: "." + replace: + - test: + if: "*" + then: + - test: + if: "preceding-sibling::*" + then: [t: "W"] + - x: "*" + +- + name: single-char-exceptions + tag: msup + match: "*[2][translate(., \"'*`ª°²³´¹º′″‴‵‶‷⁗\",'')='']" + replace: [x: "*"] + +- + name: default + tag: [msub, munder] + match: "." + replace: + - x: "*[1]" + - test: + if: "self::m:munder" + then: [t: "⠨⠡"] + else: [t: "⠡"] + - x: "*[2]" + - t: "⠱" + +- + name: default + tag: [msup, mover] + match: "." + replace: + - x: "*[1]" + - test: + if: "self::m:mover" + then: [t: "⠨⠌"] + else: [t: "⠌"] + - x: "*[2]" + - t: "⠱" + +- + name: default + tag: [msubsup, munderover] + match: "count(*)=3" + replace: + - x: "*[1]" + - test: + if: "self::m:munderover" + then: [t: "⠨⠡"] + else: [t: "⠡"] + - x: "*[2]" + - test: + if: "self::m:munderover" + then: [t: "⠨⠌"] + else: [t: "⠌"] + - x: "*[3]" + - t: "⠱" + +- + name: default + tag: mmultiscripts + match: "." + replace: [x: "*"] + +- + name: default + tag: menclose + match: "." + replace: + - t: "⠣" + - x: "*" + - t: "⠜" + +- + name: default + tag: mo + match: "." + replace: + - x: "text()" + +- + name: default + tag: mn + match: "." + replace: + - x: "BrailleChars(., 'Russian')" + +- + name: functions + tag: mi + match: "IsInDefinition(., 'Speech', 'FunctionNames')" + replace: + - test: + - if: ".='arcsin'" + then: [t: "⠫⠁⠎"] + - else_if: ".='arccos'" + then: [t: "⠫⠁⠉"] + - else_if: ".='arctg' or .='arctan'" + then: [t: "⠫⠁⠞"] + - else_if: ".='arcctg' or .='arccotan' or .='arccot'" + then: [t: "⠫⠁⠉⠞"] + - else_if: ".='sin'" + then: [t: "⠫⠎"] + - else_if: ".='cos'" + then: [t: "⠫⠉"] + - else_if: ".='tg' or .='tan'" + then: [t: "⠫⠞"] + - else_if: ".='ctg' or .='cotan' or .='cot'" + then: [t: "⠫⠉⠞"] + - else_if: ".='log'" + then: [t: "⠫⠇"] + - else_if: ".='ln'" + then: [t: "⠫⠇⠝"] + - else_if: ".='lg'" + then: [t: "⠫⠇⠛"] + - else_if: ".='lim'" + then: [t: "⠫⠇⠍"] + - else_if: ".='min'" + then: [t: "⠫⠍⠝"] + - else_if: ".='max'" + then: [t: "⠫⠍⠭"] + - else_if: ".='exp'" + then: [t: "⠫⠑"] + else: [x: "BrailleChars(., 'Russian')"] + +- + name: default + tag: [mi, mtext] + match: "." + replace: + - x: "BrailleChars(., 'Russian')" + +- + name: default + tag: ms + match: "." + replace: + - test: + if: "string(@lquote)!=''" + then: [x: "@lquote"] + else: [t: "⠦"] + - x: "BrailleChars(., 'Russian')" + - test: + if: "string(@rquote)!=''" + then: [x: "@rquote"] + else: [t: "⠴"] + +- + name: default-children + tag: "*" + match: "*" + replace: + - x: "*" + +- + name: default-no-children + tag: "*" + match: "text()" + replace: + - x: "text()" + +- + name: default-no-text + tag: "*" + match: "." + replace: [t: ""] diff --git a/Rules/Braille/Russian/definitions.yaml b/Rules/Braille/Russian/definitions.yaml new file mode 100644 index 00000000..4d0c77ef --- /dev/null +++ b/Rules/Braille/Russian/definitions.yaml @@ -0,0 +1,2 @@ +--- +- include: "../definitions.yaml" diff --git a/Rules/Braille/Russian/unicode-full.yaml b/Rules/Braille/Russian/unicode-full.yaml new file mode 100644 index 00000000..f78feac3 --- /dev/null +++ b/Rules/Braille/Russian/unicode-full.yaml @@ -0,0 +1,301 @@ +--- + - "⋇": [tc: "1⠌⠯⠦"] # 0x22C7 (Division times) + - "⊩": [tc: "⠸⠳⠿⠸⠒"] # 0x22A9 (Forces) + - "ℏ": [tc: "⠈⠒⠓"] # t: "B"F (reduced Plank's constant) + - "ⅆ": [tc: "⠙"] # 0x2146 + - "ⅇ": [tc: "⠑"] # 0x2147 + - "ⅈ": [tc: "⠊"] # 0x2148 + - "⨯": [tc: "⠐⠦"] # U+2A2F(VECTOR OR CROSS PRODUCT) -- make the same as 0x00D7 (Multiplication sign) + + + - "¼": [tc: "#N⠁N⠌N⠙"] # 0x00BC (Vulgar Fraction One Quarter) + - "½": [tc: "#N⠁N⠌N⠃"] # 0x00BD (Vulgar Fraction One Half) + - "¾": [tc: "#N⠉N⠌N⠙"] # 0x00BE (Vulgar Fraction Three Quarters) + - "⅐": [tc: "#N⠁N⠌N⠛"] # 0x2150 (Vulgar Fraction One Seventh) + - "⅑": [tc: "#N⠁N⠌N⠊"] # 0x2151 (Vulgar Fraction One Ninth) + - "⅒": [tc: "#N⠁N⠌N⠁N⠚"] # 0x2152 (Vulgar Fraction One Tenth) + - "⅓": [tc: "#N⠁N⠌N⠉"] # 0x2153 (Vulgar Fraction One Third) + - "⅔": [tc: "#N⠃N⠌N⠉"] # 0x2154 (Vulgar Fraction Two Thirds) + - "⅕": [tc: "#N⠁N⠌N⠑"] # 0x2155 (Vulgar Fraction One Fifth) + - "⅖": [tc: "#N⠃N⠌N⠑"] # 0x2156 (Vulgar Fraction Two Fifths) + - "⅗": [tc: "#N⠉N⠌N⠑"] # 0x2157 (Vulgar Fraction Three Fifths) + - "⅘": [tc: "#N⠙N⠌N⠑"] # 0x2158 (Vulgar Fraction Four Fifths) + - "⅙": [tc: "#N⠁N⠌N⠋"] # 0x2159 (Vulgar Fraction One Sixth) + - "⅚": [tc: "#N⠑N⠌N⠋"] # 0x215A (Vulgar Fraction Five Sixths) + - "⅛": [tc: "#N⠁N⠌N⠓"] # 0x215B (Vulgar Fraction One Eighth) + - "⅜": [tc: "#N⠉N⠌N⠓"] # 0x215C (Vulgar Fraction Three Eighths) + - "⅝": [tc: "#N⠑N⠌N⠓"] # 0x215D (Vulgar Fraction Five Eighths) + - "⅞": [tc: "#N⠛N⠌N⠓"] # 0x215E (Vulgar Fraction Seven Eighths) + - "↉": [tc: "#N⠚N⠌N⠑"] # 0x2189 (Vulgar Fraction Zero Thirds) + + + + - "ℊ": [tc: "TL⠛⠁"] # 0x210a (Script Small G) + - "ℋ": [tc: "TCL⠓⠁"] # 0x210b (Script Capital H) + - "ℒ": [tc: "TCL⠇"] # 0x2113 (Script Capital L) + - "ℓ": [tc: "TL⠇"] # 0x2113 (Script Small L) + - "℘": [tc: "TCL⠏"] # 0x2118 (Script Capital P) + - "ℛ": [tc: "TCL⠗"] # 0x211B (Script Capital R) + - "ℯ": [tc: "TL⠑"] # 0x212F (Script Small E) + - "ℰ": [tc: "TCL⠑"] # 0x2130 (Script Capital E) + - "ℱ": [tc: "TCL⠋"] # 0x2131 (Script Capital F) + - "ℳ": [tc: "TCL⠍"] # 0x2133 (Script Capital M) + - "ℴ": [tc: "TL⠕"] # 0x21334 (Script Small O) + - "ℌ": [tc: "DCL⠓"] # 0x210C (Fraktur Capital H) + - "ℑ": [tc: "DCL⠊"] # 0x2111 (Fraktur Capital I) + - "ℜ": [tc: "DCL⠗"] # 0x211C (Fraktur Capital R) + - "ℨ": [tc: "DCL⠵"] # 0x2128 (Fraktur Capital Z) + - "ℭ": [tc: "DCL⠉"] # 0x22DC (Fraktur Capital C) + + - "ℂ": [tc: "𝔹CL⠉"] # 0x2102 + - "ℍ": [tc: "𝔹CL⠓"] # 0x210d + - "ℕ": [tc: "𝔹CL⠝"] # 0x2115 + - "ℙ": [tc: "𝔹CL⠏"] # 0x2119 + - "ℚ": [tc: "𝔹CL⠟"] # 0x211a + - "ℝ": [tc: "𝔹CL⠗"] # 0x211d + - "ℤ": [tc: "𝔹CL⠵"] # 0x2124 + + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - tc: "B" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da + - tc: "BGL" + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + + - "𝔄-𝔜": # 0x1d504 - 0x1d51d ('z' version is reserved) + - tc: "DC" + - spell: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - tc: "D" + - spell: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝕬-𝖅": # 0x1D56C - 0x1D585 + - tc: "BDC" + - spell: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - tc: "D" + - spell: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + + # double struck (blackboard bold) chars in math alphabetic block and also MathType private use area + # Some of these are reserved because they were used in Plane 0 -- that shouldn't be an issue other than causing the other chars to not display + - "𝔸-𝕐": # 0x1d504 - 0x1d51d ('z' version is reserved) + - tc: "DC" + - spell: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - tc: "D" + - spell: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - tc: "D" + - spell: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + + # script chars in math alphabetic block and also MathType private use area + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - tc: "TsC" + - spell: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'abcdefghijklmnopqrstuvwxyza')" + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - tc: "Ts" + - spell: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" + + # bold script chars in math alphabetic block + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - tc: "BTs" + - spell: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - tc: "BTs" + - spell: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - tc: "B" + - spell: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - tc: "B" + - spell: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + # don't include italics + - tc: "C" + - spell: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + # don't include italics + - spell: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + - tc: "BIC" + - spell: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + - spell: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - spell: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - spell: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + # - tc: "italic" + - spell: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + # - tc: "italic" + - spell: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + # - tc: "bold italic" + - test: + if: "$IgnoreBold" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + # - tc: "bold italic" + - test: + if: "$IgnoreBold" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - tc: "C" + - spell: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - spell: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝚤𝚥": # 0x1d6a4, 0x1d6a5 + - spell: "translate('.', '𝚤𝚥', 'ij')" # not sure what else these should be + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝛁": # 0x1d6c1 + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝜵', '∇')" + + - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": # 0x1D6DB - 0x1D6E1 + - test: + if: "not($IgnoreBold)" + then: [tc: "B"] + - spell: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + - tc: "IC" + - spell: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝛼-𝜔": # 0x1d6fc - 0x1d714 + - spell: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓𝜔', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + + - "𝛻": # 0x1d6fb + - spell: "translate('.', '𝜵', '∇')" + + - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": # 0x1d715 - 0x1d71b + # - tc: "italic" + - spell: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + # - tc: "bold italic" + - tc: 'BIC⠁' + - spell: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝜶-𝝎": # 0x1d736 - 0x1d74e + - tc: "BI" + - spell: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 + - tc: "BI" + - spell: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εκθφρπ')" + + - "𝜵": # 0x1d735 + - tc: "BI" + - spell: "translate('.', '𝜵', '∇')" + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - tc: "BSC" + - spell: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝝰-𝞈": # 0x1d770 - 0x1d788 + - tc: "BS" + - spell: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇𝞈', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝞉𝞊𝞋𝞌𝞍𝞎𝞏": # 0x1d789 - 0x1d78f + - tc: "B" + - spell: "translate('.', '𝞉𝞊𝞋𝞌𝞍𝞎𝞏', '∂εθκφρπ')" + + - "𝝯": # 0x1d76f + - tc: "B" + - spell: "translate('.', '𝜵', '∇')" + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + # - tc: "bold italic" + - test: + if: "$IgnoreBold" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 + # - tc: "bold italic" + - test: + if: "$IgnoreBold" + then: [tc: I] + else: [tc: "BI"] + - spell: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 + - tc: "B" + - spell: "translate('.', '𝟃𝟄𝟅𝟆𝟇𝟈𝟉', '∂εθκφρπ')" + + - "𝞩": [tc: "B"] # 0x1d7a9 + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - tc: "B" + - spell: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 + - tc: "BS" + - spell: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - tc: "S" + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - spell: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + diff --git a/Rules/Braille/Russian/unicode.yaml b/Rules/Braille/Russian/unicode.yaml new file mode 100644 index 00000000..1f2c733c --- /dev/null +++ b/Rules/Braille/Russian/unicode.yaml @@ -0,0 +1,289 @@ +--- +# Russian mathematical braille characters +- ".": [t: "⠠⠲"] +- ",": [t: "⠠⠂"] +- "!": [t: "⠠⠖"] +- "?": [t: "⠠⠢"] +- ":": [t: "⠠⠒"] +- ";": [t: "⠠⠆"] +- "{": [t: "⠠⠪"] +- "}": [t: "⠠⠕"] +- "+": [t: "⠀⠖"] +- "-": [t: "⠀⠤"] +- "=": [t: "⠀⠶"] +- ">": [t: "⠀⠕⠀"] +- "<": [t: "⠀⠪⠀"] +- "%": [t: "⠼⠴"] +- "±": [t: "⠀⠖⠤"] +- ";": [t: "⠠⠢"] +- "Ϳ": [t: "⠰⠊"] +- "·": [t: "⠰"] +- "α": [t: "⠰⠁"] +- "β": [t: "⠰⠃"] +- "γ": [t: "⠰⠛"] +- "δ": [t: "⠰⠙"] +- "ε": [t: "⠰⠑"] +- "ζ": [t: "⠰⠵"] +- "η": [t: "⠰⠚"] +- "θ": [t: "⠰⠓"] +- "ι": [t: "⠰⠊"] +- "κ": [t: "⠰⠅"] +- "λ": [t: "⠰⠇"] +- "μ": [t: "⠰⠍"] +- "ν": [t: "⠰⠝"] +- "ξ": [t: "⠰⠭"] +- "ο": [t: "⠰⠕"] +- "π": [t: "⠰⠏"] +- "ρ": [t: "⠰⠗"] +- "σ": [t: "⠰⠎"] +- "τ": [t: "⠰⠞"] +- "υ": [t: "⠰⠥"] +- "φ": [t: "⠰⠋"] +- "χ": [t: "⠰⠉"] +- "ψ": [t: "⠰⠽"] +- "ω": [t: "⠰⠺"] +- "Α": [t: "⠸⠁"] +- "Β": [t: "⠸⠃"] +- "Γ": [t: "⠸⠛"] +- "Δ": [t: "⠸⠙"] +- "Ε": [t: "⠸⠑"] +- "Ζ": [t: "⠸⠵"] +- "Η": [t: "⠸⠚"] +- "Θ": [t: "⠸⠓"] +- "Ι": [t: "⠸⠊"] +- "Κ": [t: "⠸⠅"] +- "Λ": [t: "⠸⠇"] +- "Μ": [t: "⠸⠍"] +- "Ν": [t: "⠸⠝"] +- "Ξ": [t: "⠸⠭"] +- "Ο": [t: "⠸⠕"] +- "Π": [t: "⠸⠏"] +- "Ρ": [t: "⠸⠗"] +- "Σ": [t: "⠸⠎"] +- "Τ": [t: "⠸⠞"] +- "Υ": [t: "⠸⠥"] +- "Φ": [t: "⠸⠋"] +- "Χ": [t: "⠸⠉"] +- "Ψ": [t: "⠸⠽"] +- "Ω": [t: "⠸⠺"] +- "ϐ": [t: "⠰⠃"] +- "ϑ": [t: "⠰⠓"] +- "ϕ": [t: "⠰⠋"] +- "ϖ": [t: "⠰⠏"] +- "ϱ": [t: "⠰⠗"] +- "′": [t: "⠔"] +- "″": [t: "⠔⠔"] +- "‴": [t: "⠔⠔⠔"] +- "∀": [t: "⠫⠄"] +- "∂": [t: "⠹"] +- "∃": [t: "⠫⠢"] +- "∅": [t: "⠈⠴"] +- "∇": [t: "⠫⠴"] +- "∈": [t: "⠀⠐⠪⠀"] +- "∉": [t: "⠘⠪"] +- "∋": [t: "⠀⠕⠂⠀"] +- "∌": [t: "⠈⠕⠂⠄"] +- "∎": [t: "⠀⠸⠇⠀"] +- "∏": [t: "⠸⠏"] +- "∑": [t: "⠸⠎"] +- "−": [t: "⠀⠤"] +- "∓": [t: "⠀⠤⠖"] +- "∔": [t: "⠈⠖"] +- "∕": [t: "⠠⠌"] +- "∖": [t: "⠀⠰⠤"] +- "∗": [t: "⠔"] +- "∘": [t: "⠴"] +- "√": [t: "⠩⠱"] +- "∛": [t: "⠩⠒⠱"] +- "∜": [t: "⠩⠲⠱"] +- "∞": [t: "⠻"] +- "∠": [t: "⠸⠪"] +- "∣": [t: "⠸"] +- "∤": [t: "⠀⠼⠀"] +- "∥": [t: "⠸⠸"] +- "∧": [t: "⠀⠰⠢"] +- "∨": [t: "⠀⠰⠔"] +- "∩": [t: "⠀⠰⠲"] +- "∪": [t: "⠀⠰⠴"] +- "∫": [t: "⠮"] +- "∬": [t: "⠮⠮"] +- "∭": [t: "⠮⠮⠮"] +- "∮": [t: "⠮⠴"] +- "∯": [t: "⠮⠮⠴"] +- "∴": [t: "⠀⠠⠡⠀"] +- "∵": [t: "⠀⠈⠌⠀"] +- "∶": [t: "⠀⠳"] +- "∸": [t: "⠈⠤"] +- "∼": [t: "⠀⠢"] +- "≅": [t: "⠀⠢"] +- "≈": [t: "⠀⠢⠢"] +- "≐": [t: "⠀⠒⠕"] +- "≔": [t: "⠀⠶⠒"] +- "≕": [t: "⠀⠶⠒"] +- "≖": [t: "⠀⠶⠴"] +- "≗": [t: "⠀⠶⠴"] +- "≛": [t: "⠀⠶⠆"] +- "≟": [t: "⠶⠢"] +- "≠": [t: "⠀⠾"] +- "≡": [t: "⠀⠰⠶"] +- "≢": [t: "⠀⠰⠾"] +- "≤": [t: "⠀⠪⠶"] +- "≥": [t: "⠀⠕⠶"] +- "≪": [t: "⠀⠪⠪⠀"] +- "≫": [t: "⠀⠕⠕⠀"] +- "≺": [t: "⠀⠒⠪⠀"] +- "≻": [t: "⠀⠕⠒⠀"] +- "≼": [t: "⠀⠒⠪⠶"] +- "≽": [t: "⠀⠕⠒⠶"] +- "⊂": [t: "⠀⠯⠀"] +- "⊃": [t: "⠀⠹⠀"] +- "⊄": [t: "⠈⠯"] +- "⊅": [t: "⠈⠹"] +- "⊆": [t: "⠀⠯⠶"] +- "⊇": [t: "⠀⠹⠶"] +- "⊕": [t: "⠀⠰⠖"] +- "⊥": [t: "⠼⠄"] +- "⊨": [t: "⠼⠎"] +- "⊼": [t: "⠰⠌"] +- "⊽": [t: "⠘⠡"] +- "⋅": [t: "⠄"] +- "⋆": [t: "⠔"] +- "⋮": [t: "⠠⠲⠲⠲"] +- "⋯": [t: "⠠⠲⠲⠲"] +- "⋰": [t: "⠠⠲⠲⠲"] +- "⋱": [t: "⠠⠲⠲⠲"] +- "△": [t: "⠸⠙"] +- "◻": [t: "⠶"] +- "♮": [t: "⠻⠨⠝⠻"] + +# Latin letters and digits used for mathematical identifiers. +- "a": [t: "⠁"] +- "A": [t: "C⠁"] +- "b": [t: "⠃"] +- "B": [t: "C⠃"] +- "c": [t: "⠉"] +- "C": [t: "C⠉"] +- "d": [t: "⠙"] +- "D": [t: "C⠙"] +- "e": [t: "⠑"] +- "E": [t: "C⠑"] +- "f": [t: "⠋"] +- "F": [t: "C⠋"] +- "g": [t: "⠛"] +- "G": [t: "C⠛"] +- "h": [t: "⠓"] +- "H": [t: "C⠓"] +- "i": [t: "⠊"] +- "I": [t: "C⠊"] +- "j": [t: "⠚"] +- "J": [t: "C⠚"] +- "k": [t: "⠅"] +- "K": [t: "C⠅"] +- "l": [t: "⠇"] +- "L": [t: "C⠇"] +- "m": [t: "⠍"] +- "M": [t: "C⠍"] +- "n": [t: "⠝"] +- "N": [t: "C⠝"] +- "o": [t: "⠕"] +- "O": [t: "C⠕"] +- "p": [t: "⠏"] +- "P": [t: "C⠏"] +- "q": [t: "⠟"] +- "Q": [t: "C⠟"] +- "r": [t: "⠗"] +- "R": [t: "C⠗"] +- "s": [t: "⠎"] +- "S": [t: "C⠎"] +- "t": [t: "⠞"] +- "T": [t: "C⠞"] +- "u": [t: "⠥"] +- "U": [t: "C⠥"] +- "v": [t: "⠧"] +- "V": [t: "C⠧"] +- "w": [t: "⠺"] +- "W": [t: "C⠺"] +- "x": [t: "⠭"] +- "X": [t: "C⠭"] +- "y": [t: "⠽"] +- "Y": [t: "C⠽"] +- "z": [t: "⠵"] +- "Z": [t: "C⠵"] +- "0": [t: "N⠚"] +- "1": [t: "N⠁"] +- "2": [t: "N⠃"] +- "3": [t: "N⠉"] +- "4": [t: "N⠙"] +- "5": [t: "N⠑"] +- "6": [t: "N⠋"] +- "7": [t: "N⠛"] +- "8": [t: "N⠓"] +- "9": [t: "N⠊"] + +# Russian literary braille letters for mtext and Cyrillic identifiers. +- "а": [t: "⠁"] +- "А": [t: "C⠁"] +- "б": [t: "⠃"] +- "Б": [t: "C⠃"] +- "в": [t: "⠺"] +- "В": [t: "C⠺"] +- "г": [t: "⠛"] +- "Г": [t: "C⠛"] +- "д": [t: "⠙"] +- "Д": [t: "C⠙"] +- "е": [t: "⠑"] +- "Е": [t: "C⠑"] +- "ё": [t: "⠡"] +- "Ё": [t: "C⠡"] +- "ж": [t: "⠚"] +- "Ж": [t: "C⠚"] +- "з": [t: "⠵"] +- "З": [t: "C⠵"] +- "и": [t: "⠊"] +- "И": [t: "C⠊"] +- "й": [t: "⠯"] +- "Й": [t: "C⠯"] +- "к": [t: "⠅"] +- "К": [t: "C⠅"] +- "л": [t: "⠇"] +- "Л": [t: "C⠇"] +- "м": [t: "⠍"] +- "М": [t: "C⠍"] +- "н": [t: "⠝"] +- "Н": [t: "C⠝"] +- "о": [t: "⠕"] +- "О": [t: "C⠕"] +- "п": [t: "⠏"] +- "П": [t: "C⠏"] +- "р": [t: "⠗"] +- "Р": [t: "C⠗"] +- "с": [t: "⠎"] +- "С": [t: "C⠎"] +- "т": [t: "⠞"] +- "Т": [t: "C⠞"] +- "у": [t: "⠥"] +- "У": [t: "C⠥"] +- "ф": [t: "⠋"] +- "Ф": [t: "C⠋"] +- "х": [t: "⠓"] +- "Х": [t: "C⠓"] +- "ц": [t: "⠉"] +- "Ц": [t: "C⠉"] +- "ч": [t: "⠟"] +- "Ч": [t: "C⠟"] +- "ш": [t: "⠱"] +- "Ш": [t: "C⠱"] +- "щ": [t: "⠭"] +- "Щ": [t: "C⠭"] +- "ъ": [t: "⠷"] +- "Ъ": [t: "C⠷"] +- "ы": [t: "⠮"] +- "Ы": [t: "C⠮"] +- "ь": [t: "⠾"] +- "Ь": [t: "C⠾"] +- "э": [t: "⠪"] +- "Э": [t: "C⠪"] +- "ю": [t: "⠳"] +- "Ю": [t: "C⠳"] +- "я": [t: "⠫"] +- "Я": [t: "C⠫"] diff --git a/src/braille.rs b/src/braille.rs index b944578f..6b259776 100644 --- a/src/braille.rs +++ b/src/braille.rs @@ -50,6 +50,7 @@ pub fn braille_mathml(mathml: Element, nav_node_id: &str) -> Result<(String, usi "CMU" => cmu_cleanup(pref_manager, braille_string), "Finnish" => finnish_cleanup(pref_manager, braille_string), "Swedish" => swedish_cleanup(pref_manager, braille_string), + "Russian" => russian_cleanup(pref_manager, braille_string), "LaTeX" => LaTeX_cleanup(pref_manager, braille_string), "ASCIIMath" => ASCIIMath_cleanup(pref_manager, braille_string), "ASCIIMath-fi" => ASCIIMath_cleanup(pref_manager, braille_string), @@ -2167,6 +2168,38 @@ fn swedish_cleanup(pref_manager: Ref, raw_braille: String) -> return result.to_string(); } +fn russian_cleanup(_pref_manager: Ref, raw_braille: String) -> String { + static REPLACE_INDICATORS: LazyLock = LazyLock::new(|| Regex::new(r"([BCILNW#])").unwrap()); + static COLLAPSE_SPACES: LazyLock = LazyLock::new(|| Regex::new(r"⠀+").unwrap()); + + let mut raw_braille_without_repeated_number_indicators = String::with_capacity(raw_braille.len()); + let mut previous_char_was_digit = false; + for ch in raw_braille.chars() { + if ch == 'N' && previous_char_was_digit { + previous_char_was_digit = false; + continue; + } + raw_braille_without_repeated_number_indicators.push(ch); + previous_char_was_digit = matches!(ch, '⠚' | '⠁' | '⠃' | '⠉' | '⠙' | '⠑' | '⠋' | '⠛' | '⠓' | '⠊'); + } + + let result = REPLACE_INDICATORS.replace_all(&raw_braille_without_repeated_number_indicators, |cap: &Captures| { + match &cap[0] { + "B" => "⠸", + "C" => "⠠", + "I" => "⠨", + "L" => "", + "N" => "⠼", + "W" => "⠀", + "#" => "", + _ => "", + } + }); + return COLLAPSE_SPACES.replace_all(&result, "⠀") + .trim_matches('⠀') + .to_string(); +} + #[allow(non_snake_case)] fn LaTeX_cleanup(_pref_manager: Ref, raw_braille: String) -> String { static REMOVE_SPACE: LazyLock = LazyLock::new(|| Regex::new(r" ([\^_,;)\]}])").unwrap()); // '^', '_', ',', ';', ')', ']', '}' @@ -2330,6 +2363,7 @@ impl BrailleChars { "Vietnam" => BrailleChars:: get_braille_vietnam_chars(node, text_range), "Swedish" => BrailleChars:: get_braille_ueb_chars(node, text_range), // FIX: need to figure out what to implement "Finnish" => BrailleChars:: get_braille_ueb_chars(node, text_range), // FIX: need to figure out what to implement + "Russian" => BrailleChars:: get_braille_ueb_chars(node, text_range), _ => return Err(sxd_xpath::function::Error::Other(format!("get_braille_chars: unknown braille code '{code}'"))) }; return match result { diff --git a/tests/braille.rs b/tests/braille.rs index 12b70cb8..2fc04fff 100644 --- a/tests/braille.rs +++ b/tests/braille.rs @@ -24,6 +24,10 @@ mod braille { mod vi; } + mod Russian { + mod russian; + } + mod LaTeX { mod augenbit; mod other; diff --git a/tests/braille/Russian/russian.rs b/tests/braille/Russian/russian.rs new file mode 100644 index 00000000..2897a371 --- /dev/null +++ b/tests/braille/Russian/russian.rs @@ -0,0 +1,37 @@ +use crate::common::*; +use anyhow::Result; + +#[test] +fn numbers_and_operators() -> Result<()> { + let expr = r#"5+12=17"#; + test_braille("Russian", expr, "⠼⠑⠀⠖⠼⠁⠃⠀⠶⠼⠁⠛")?; + return Ok(()); +} + +#[test] +fn fraction() -> Result<()> { + let expr = r#"x2"#; + test_braille("Russian", expr, "⠆⠭⠀⠳⠼⠃⠰")?; + return Ok(()); +} + +#[test] +fn scripts_and_root() -> Result<()> { + let expr = r#"x2+y"#; + test_braille("Russian", expr, "⠭⠌⠼⠃⠱⠀⠖⠩⠱⠽⠹")?; + return Ok(()); +} + +#[test] +fn cyrillic_text() -> Result<()> { + let expr = r#"угол"#; + test_braille("Russian", expr, "⠥⠛⠕⠇")?; + return Ok(()); +} + +#[test] +fn nested_fraction_and_root() -> Result<()> { + let expr = r#"x+1yx-y"#; + test_braille("Russian", expr, "⠆⠭⠀⠖⠩⠱⠆⠼⠁⠀⠳⠽⠰⠹⠀⠳⠭⠀⠤⠽⠰")?; + return Ok(()); +}