---
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 = "<math>
                    <mfrac> <mn>1</mn> <mn>2</mn> </mfrac>
                </math>";
    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 = "<math>
                    <mfrac> <mn>17</mn> <mn>10</mn> </mfrac>
                </math>";
    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).

<!-- Добавьте сюда подробности об уведомлениях и процедуре обновления перевода, когда она будет согласована. -->

---

<a id="braille-translation"></a>
## Перевод Брайля

Общий процесс перевода Брайля похож на перевод речи, но есть несколько важных отличий.

Перед работой над правилами Брайля определите, какой стандарт математической записи используется в вашей стране. В некоторых странах существуют установленные системы записи Брайля, а в других применяются обозначения другого типа, например 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, если все узлы имеют одинаковый тип. Допустимые типы:<br>`"simple"` — заданный набор элементов ClearSpeak;<br>`"leaf"` — один из листовых элементов MathML;<br>`"2D"` — двумерный узел, например `mfrac` или `mroot`;<br>`"modified"` — узел с индексом или элементом над ним либо под ним;<br>`"scripts"` — узел с подстрочным и (или) надстрочным индексом;<br>`"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` | Используется элементами-токенами для сложной перестановки индикаторов кода Немета, например прописных букв и начертания шрифта. |
