add Russian translations for README and ARCHITECTURE

This commit is contained in:
Horizontal42 2026-06-11 18:32:06 +03:00
parent f55266f70b
commit 041a0f4983
3 changed files with 255 additions and 0 deletions

120
ARCHITECTURE.ru.md Normal file
View file

@ -0,0 +1,120 @@
# Архитектура
[English](ARCHITECTURE.md)
Приложение состоит из двух частей: **движок вычислений на TypeScript + UI** в WebView2
и тонкая **оболочка на Rust** (Tauri 2) для всего, что касается ОС.
```
src/
engine/ сам калькулятор (чистый TS, без DOM)
lexer.ts строка → сырые лексемы (числа, слова, символы)
vocab-data.ts таблицы слов en+ru: единицы, валюты, операторы
vocab.ts объединённый поиск по таблицам
unitdata.ts коэффициенты конвертации (базовые единицы на измерение)
extraunits.ts скорость/давление/энергия/мощность/частота/топливо + крипто
registry.ts фраза → токен; строится один раз при запуске
tokenizer.ts лексемы + реестр → семантические токены
parser.ts токены → AST (Pratt-парсер, терпимый к «мусору»)
evaluator.ts AST + контекст документа → значения с единицами
formatter.ts значения → строки для отображения
datetime.ts карта часовых поясов, календарная арифметика
index.ts фасад SumEngine + API расширений + итоги листа
ui/
editor.ts обвязка CodeMirror 6: подсветка, оверлей результатов, копирование
app.css темы (светлая/тёмная через CSS-переменные)
main.ts запуск приложения: документы, настройки, строка статуса, dnd
storage.ts команды Tauri / запасной вариант на localStorage
extensions.ts запускает пользовательские .js-файлы через движок
i18n.ts строки интерфейса (en/ru)
src-tauri/
src/main.rs трей, скрытие в трей, команды хранилища, загрузка курсов
(open.er-api.com + CoinGecko) с кэшем, резервные копии,
миграция папки данных, открытие файлов
```
## Как строка превращается в результат
1. **Lexer** разбивает `"5 дюймов в см"` на лексемы: `5`, `дюймов`, `в`, `см`.
Тот же lexer сегментирует фразы из словаря, чтобы совпадение было последовательным.
2. **Registry** хранит все фразы, которые понимает движок, по первой лексеме.
Побеждает наиболее длинное совпадение; регистрозависимые записи (`m` vs `M`, `kB`)
проверяются раньше регистронезависимых, а неоднозначная нижняя форма (`mm` vs `Mm`)
просто не получает нечувствительный к регистру запасной вариант.
3. **Tokenizer** проходит по лексемам, спрашивает реестр и выдаёт типизированные токены.
Несколько контекстных хаков: например, `in` сразу после числа в конце строки
означает дюймы (`6 ft 3 in`).
4. **Parser** — Pratt-парсер, терпимый к шуму: неизвестные слова отбрасываются
до разбора (поэтому `потратил 20 на пиццу плюс 5` работает) — кроме слов
сразу после оператора конвертации, которые могут называть часовой пояс.
Приоритет от наименьшего к наибольшему: конвертации на уровне строки →
поразрядные → аддитивные → мультипликативные (включая `20% от x`) →
унарные → постфиксные (единицы, `%`, `!`, `^`) → первичные.
5. **Evaluator** вычисляет тройки `{ value: Decimal, unit }`. Операнды одного
измерения конвертируются через базовые единицы (`toBase`/`fromBase`);
температуры несут смещение, единицы расхода топлива могут быть обратными
(mpg ↔ л/100км). Контекст документа предоставляет переменные и результаты
предыдущих строк для `сумма`/`avg`/`prev`.
6. **Formatter** отображает значение в формате единицы, с точностью и группировкой
из настроек, или в hex/binary/дроби/римских цифрах и т.д.
Числа — `decimal.js` везде, никаких артефактов двоичной арифметики.
## Как добавлять вещи
**Единицу** — добавь строку в `UNIT_DATA` в `unitdata.ts` (id, измерение, коэффициент
к базовой единице измерения) и её названия в `vocab-data.ts` в соответствующей
категории (`"myunit.variants": "name,names,имя"`, `"myunit.format": "sym"`).
Для совершенно нового измерения расширь объединение `Dimension` в `types.ts`
и используй `extraunits.ts` — фразы живут прямо там.
**Валюту** — фиатные коды берутся из таблицы `Currency` в `vocab-data.ts`;
крипто идёт в `CRYPTO` в `extraunits.ts` (код, id CoinGecko, фразы, снимок цены)
плюс зеркальный список в `src-tauri/src/main.rs`.
**Оператор/функцию** — зарегистрируй фразу в `buildRegistry()` (`registry.ts`),
добавь токен/узел AST если нужен новый синтаксис, реализуй в `evaluator.ts`.
Большинство слов-операторов — это данные, а не код.
**Город часового пояса** — одна строка в карте `Z` в `datetime.ts`.
## Подводные камни движка
- Конфликты фраз реальны: одиночные буквы регистрозависимы по замыслу
(`m` метр / `M` миллион, `k` тысяча / `K` кельвин). Генерация префиксов
фильтруется там, где пересекается со словами — например, секунды получают
только ms/µs/ns/ps, потому что символ «аттосекунды» затенил бы слово `as`.
- `и`/`или` двойного назначения: `и` складывает (`5 и 3` = 8), `или` — побитовое.
`от/без/на` работают только с процентом в левой части; иначе это просто текст.
- Курсы валют — «единиц за 1 USD»; соотношение единицы валюты вычисляется при
токенизации из текущей карты курсов, поэтому обновление курсов пересчитывает
всё при следующей оценке.
- Реестр не пересобирается никогда; расширения мутируют его (добавляют фразы/функции)
при запуске.
## Особенности UI
- CodeMirror встраивает свою базовую тему во время выполнения *после* `app.css`,
поэтому всё, что должно побеждать (отступ контента для колонки результатов,
цвет курсора), живёт в расширении `EditorView.theme(...)`, а не в стилях.
- Колонка результатов — абсолютно позиционированный оверлей; каждый результат
размещается по координатам `view.lineBlockAt(...)` и перерисовывается при
прокрутке/изменении геометрии. Клик для копирования читает `data-value`.
- Оценка движка синхронная на каждое нажатие клавиши — полный разбор листа
занимает менее миллисекунды, поэтому нет дебаунса и воркеров.
## Хранилище
- `settings.json` всегда находится в `%APPDATA%/app.summarum.calc`.
- `documents.json` (все листы) + `backups/` живут либо там, либо в папке данных,
выбранной пользователем (путь хранится в настройках).
- Ежедневный снимок при запуске (`backups/documents-YYYY-MM-DD.json`, хранится 14);
удалённые листы становятся `.numi`-файлами в `backups/deleted/` и удаляются по возрасту.
Сохранение откладывается на 400 мс.
## Тесты
`npm test` запускает ~94 vitest-кейса по движку (`src/engine/*.test.ts`):
все классы выражений, оба языка, детерминированные инжектированные курсы,
плюс набор регрессий по всем известным исправленным багам.
UI проверяется вручную; движок — там где живёт вся сложность.

View file

@ -1,5 +1,7 @@
# Summarum
[Русский](README.ru.md)
**A calculator that works like a notepad.** Type what you're thinking — every line
gets a live answer in the right column. No buttons, no formula syntax, just text.

133
README.ru.md Normal file
View file

@ -0,0 +1,133 @@
# Summarum
[English](README.md)
**Калькулятор, который работает как блокнот.** Пишешь что думаешь — каждая строка сразу даёт ответ в правой колонке. Никаких кнопок, никакого синтаксиса формул, просто текст.
```
# Поездка в Лиссабон
билеты $420
отель $95 * 4 ночи
еда 30€ в день * 4 дня и музеи 25€
итого → $1,066.97
5 дюймов в см → 12.7 cm
100 км/ч в миль/ч → 62.14 mph
$2000 в рублях → 144,060 ₽
скидка 20% от 1500 → 1,200
сегодня + 6 недель → 22 Июля 2026
время в Токио → 14:25
```
Работает на **английском и русском одновременно**: `5 метров в см`, `20$ в евро`, `сумма` — переключать язык не нужно.
## Установка
Скачай последний `Summarum_*_x64-setup.exe` из [Releases](../../releases), запусти — готово. Весь установщик около 2 МБ.
Требования:
- **Windows 10 (1803+) или Windows 11**, 64-бит.
- **[WebView2 Runtime](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)** —
уже встроен в Windows 11 и большинство обновлённых Windows 10; если отсутствует,
установщик скачает его автоматически.
- Интернет нужен только для живых курсов валют — всё остальное работает офлайн
(курсы берутся из кэша или встроенного снимка).
## Что понимает
**Обычная математика** — `2 + 2`, `8 / (45 - 20%)`, скобки, `^`, `sqrt`, `sin`,
факториалы. Точная десятичная арифметика: `0.1 + 0.2` это `0.3`, а не `0.30000000000000004`.
**Слова как операторы** — `12 плюс 4`, `20 делить на 4`, `5 и 3`. Текст вокруг
математики игнорируется: `потратил 20 на пиццу и 5 на кофе` даст 25.
**Единицы** — длина, масса, объём, площадь, температура, время, углы, данные
(`1 ГиБ в МБ`), CSS px/em, скорость, давление, энергия, мощность, частота, расход топлива:
```
6 футов в метрах 1 атм в мм рт.ст. 8 л/100км в mpg
2 часа 30 мин в мин 150 л.с. в кВт 32 px в em
```
**Деньги** — ~150 валют и топ-20 криптовалют с живыми курсами
(обновляются раз в час, кэшируются для офлайна). `$100 + 20%`, `5 eth в USD`,
`9800 рублей в долларах`.
**Проценты по-человечески** — `20% от 80`, `скидка 20% от 1500`, `5 это сколько % от 25`,
`20% от какого числа равно 30`.
**Даты и время** — `сегодня + 2 недели`, `завтра - сегодня`, `время в Нью-Йорке`,
`сейчас в unix`, `1750000000 как дата`.
**Для программистов** — `0xFF & 0x0F`, `1 << 4`, `255 в hex`, `в binary`,
плюс `в дроби` (0.75 → 3/4) и `в roman` (2026 → MMXXVI).
**Листы с переменными** — переменные (`аренда = $500`), нарастающие итоги
(`сумма`, `avg`, `prev`), заголовки `#`, комментарии `//`. Строка статуса всегда
показывает итог листа; выдели несколько строк — увидишь их сумму.
## Удобные мелочи
- **Ctrl+Alt+N** (настраивается — поле в настройках записывает нажатые клавиши)
показывает/скрывает окно из любого места. Закрытие прячет в трей.
- **Клик по результату** копирует его. Копирование строки копирует её *вместе с ответом*
(`аренда * 3 = $1,500`). **Ctrl+Shift+C** копирует весь лист с ответами.
- **Потяни разделитель** чтобы изменить ширину колонки результатов.
- **Брось файл `.numi`, `.sum`, `.txt` или `.md`** в окно — откроется как лист.
- **Резервные копии автоматические**: ежедневный снимок всех листов (хранятся 14 дней),
удалённые листы лежат в корзине 14 дней (настраивается) — верни перетащив обратно.
Настройки → Backups откроет папку.
- **Папка данных настраивается** — укажи OneDrive/Dropbox и листы синхронизируются
между машинами.
- Светлая/тёмная тема, интерфейс en/ru, настраиваемая точность и разделители.
## Расширения
Брось `.js`-файл в папку расширений (Настройки → Extensions folder) и перезапусти.
API совместим с расширениями Numi:
```js
numi.setVariable("ндс", { double: 20 });
numi.addUnit({ id: "этаж", phrases: "этаж,этажей", baseUnitId: "meter",
format: "эт", ratio: 3 });
numi.addFunction({ id: "гипот", phrases: "гипот" },
(v) => ({ double: Math.hypot(v[0].double, v[1].double) }));
```
Тогда `5 этажей в метрах` → `15 m` и `гипот(3; 4)` → `5`.
## Баги и идеи
Открой [issue](../../issues). Строка того что написал и что ожидал увидеть — достаточно.
## Для разработчиков
Устройство движка, структура проекта и как добавлять единицы/валюты:
см. [ARCHITECTURE.ru.md](ARCHITECTURE.ru.md).
```bash
npm install
npm test # тесты движка
npm run tauri dev # запустить приложение
npm run tauri build # собрать установщик
```
Для сборки нужны:
- [Node.js](https://nodejs.org/) 22.6 или новее
- [Rust](https://rustup.rs/) (stable, MSVC toolchain)
- [Visual Studio Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/)
с компонентом «Desktop development with C++» (нужен линковщик MSVC)
- Если что-то не собирается — см. [Tauri prerequisites](https://tauri.app/start/prerequisites/)
Стек: [Tauri 2](https://tauri.app) + TypeScript + CodeMirror 6.
## Благодарности
Вдохновлён отличным [Numi](https://numi.app) от Дмитрия Николаева —
если ты на macOS, купи его. Summarum — независимый проект, написанный с нуля для Windows.
## Лицензия
[MIT](LICENSE)