61 Commits

Author SHA1 Message Date
ec062817dc Поднята версия релиза до v0.2.4
All checks were successful
Build Mod Package / build (push) Successful in 11s
2026-04-10 12:14:31 +03:00
f56e10748e Уточнен регистр терминов отдыха 2026-04-10 12:06:47 +03:00
ac1bd03426 Обновить автора на Underslumber Team в meta.lsx и scripts/build.ps1 2026-04-10 01:47:13 +03:00
07f557ca30 Обновлено описание проекта в README 2026-04-09 21:41:42 +03:00
7f189aa741 Ужесточены сценарии обновления перевода для AI-агента 2026-04-09 16:03:02 +03:00
d048a33c55 Уточнил процесс обновления перевода в ACTIONS.md 2026-04-09 13:18:51 +03:00
4e99dfdd92 Объединил автоматизацию локального обновления перевода 2026-04-09 13:03:40 +03:00
5d54da9048 Автоматизировал локальное обновление перевода 2026-04-09 13:02:40 +03:00
d96db4e1f0 Уточнил правило для повторных pending-вопросов 2026-04-09 12:14:36 +03:00
c40712701c Исправлен XPath Version64 в правилах релиза AGENTS.md 2026-04-09 11:59:29 +03:00
c9595312ab Уточнены правила ссылок и переключения веток в AGENTS.md 2026-04-09 11:55:17 +03:00
91e12e4ba1 Повышена версия мода до v0.2.3 в ModuleInfo Version64
All checks were successful
Build Mod Package / build (push) Successful in 16s
2026-04-09 11:35:03 +03:00
921a5a3156 Обновлён перевод russian.xml и синхронизирована зависимость в meta.lsx 2026-04-09 11:32:32 +03:00
2ab32b258c Оптимизированы правила подтверждения действий 2026-04-09 11:23:49 +03:00
175c1dbaed Добавлено предложение обновления зависимости после обновления перевода 2026-04-09 11:16:26 +03:00
e9bfbfe74f Уточнены правила по веткам и предварительному показу небольших правок 2026-04-09 11:13:08 +03:00
6782cfcd87 Merge branch 'feat/sync-parent-meta' 2026-04-09 10:58:42 +03:00
eaf84ad605 Скрипт синхронизации parent meta переведен на загрузку из git 2026-04-09 10:58:18 +03:00
65e3f5b48e Добавлены action и скрипт синхронизации parent meta, уточнены правила подтверждения 2026-04-09 10:54:30 +03:00
9519b92771 Уточнил правило смены ветки: останавливать выполнение до явного решения пользователя 2026-04-09 10:37:19 +03:00
6257027a13 Обновлено описание мода в meta.lsx 2026-04-09 10:34:23 +03:00
cde9194ed5 Разделил правила AGENTS.md на общие и проектные 2026-04-09 08:58:43 +03:00
1c8cf13f67 Уточнен принцип сжатия правил: оптимизация под AI-агента 2026-04-09 08:53:27 +03:00
74a8942999 Уточнен формат ссылки на релиз: [version](url) 2026-04-09 08:51:14 +03:00
7f8f09a3ac Исправлен set-version: обновление только ModuleInfo.Version64 и сужены правила в AGENTS
All checks were successful
Build Mod Package / build (push) Successful in 9s
2026-04-09 08:47:39 +03:00
70f93c3d29 Обновлен перевод russian.xml по upstream: добавлены новые строки и синхронизирована Метка охотника
All checks were successful
Build Mod Package / build (push) Successful in 10s
2026-04-09 08:33:59 +03:00
4aa2e136b2 Уточнить поведение при отсутствии совпадения action 2026-04-09 08:26:40 +03:00
3ae30a5263 Добавить и оптимизировать внутренний ранбук ACTIONS.md 2026-04-09 08:15:43 +03:00
4da26911fe Уточнить политику веток: предлагать ветку в начале и MR/merge в конце 2026-04-09 07:53:33 +03:00
df1daee6ab Перенести комментарий о CI-особенности Divine в build.ps1 2026-04-09 07:49:11 +03:00
9d1a26c8e0 Сжать AGENTS.md и убрать дубли правил публикации 2026-04-09 07:46:53 +03:00
8a4970742c Упростить правила публикации и обновлять только ModuleInfo/Version64 2026-04-09 07:37:55 +03:00
b50a6a2f95 Зафиксировать изменения meta.lsx из BG3 Toolkit и правило релизного тега
All checks were successful
Build Mod Package / build (push) Successful in 2s
2026-04-09 07:29:24 +03:00
4646b51459 Подготовить версию v0.2.1 в meta.lsx
All checks were successful
Build Mod Package / build (push) Successful in 3s
2026-04-09 07:03:15 +03:00
a72b7bc1e1 Добавить скрипт обновления версии meta.lsx перед выпуском 2026-04-09 07:00:35 +03:00
7aca648396 Добавить правило про ссылки на архивы релизов
All checks were successful
Build Mod Package / build (push) Successful in 4s
2026-04-08 23:48:58 +03:00
36129b15d1 Дополнить русскую локализацию по оригинальному english.xml
All checks were successful
Build Mod Package / build (push) Successful in 4s
2026-04-08 23:43:31 +03:00
97ca95ba16 Исправлена кодировка описания мода
All checks were successful
Build Mod Package / build (push) Successful in 15s
2026-04-08 23:23:49 +03:00
ad129e15d5 chore: add metadata.lsf for DnD 5.5e AIO Russian mod GUI 2026-04-08 23:19:00 +03:00
c8371a3fec Обновлена локализация по глоссарию, дополнены черты происхождения и добавлен логотип публикации
All checks were successful
Build Mod Package / build (push) Successful in 55s
2026-04-08 22:20:13 +03:00
321fef2f63 Уточнены ориентиры для перевода в AGENTS.md 2026-04-08 22:06:59 +03:00
eb9ba5eefa Добавлен перевод новых строк локализации для голиафа и стрелка
All checks were successful
Build Mod Package / build (push) Successful in 30s
2026-04-08 21:52:36 +03:00
c0524832d0 Добавлен BOM для meta.lsx в сборке и уточнён перевод " Расщепление разума\
All checks were successful
Build Mod Package / build (push) Successful in 35s
2026-04-08 21:31:40 +03:00
e78610c702 Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
All checks were successful
Build Mod Package / build (push) Successful in 28s
2026-04-08 21:17:13 +03:00
5c0a44a71e Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
All checks were successful
Build Mod Package / build (push) Successful in 49s
2026-04-08 21:16:39 +03:00
c859ddc50c Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml 2026-04-08 21:16:20 +03:00
5f3ca9ae0d Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml 2026-04-08 21:14:33 +03:00
782879d73e Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
All checks were successful
Build Mod Package / build (push) Successful in 46s
2026-04-08 21:12:06 +03:00
d5cd0300da revert 85a7ec546c
revert Исправлен перевод CSV и обновлены правила коммита
2026-04-08 20:40:54 +03:00
85a7ec546c Исправлен перевод CSV и обновлены правила коммита 2026-04-08 20:37:02 +03:00
37d34f8406 Merge pull request 'fix: синхронизировать термины russian.xml с глоссарием' (#2) from fix/translation-glossary-sync-20260408 into main
All checks were successful
Build Mod Package / build (push) Successful in 8s
Reviewed-on: mikhail/bg3-dnd55e-russian-localization#2
2026-04-08 20:10:29 +03:00
fba22ac1aa Merge branch 'main' into fix/translation-glossary-sync-20260408 2026-04-08 20:10:19 +03:00
0d42f9ca34 fix: синхронизировать термины russian.xml с глоссарием 2026-04-08 20:06:26 +03:00
7a410d4124 Merge branch 'main' of https://git.shahovalov.ru/mikhail/bg3-dnd55e-russian-localization
All checks were successful
Build Mod Package / build (push) Successful in 23s
2026-04-08 19:55:46 +03:00
945ede2583 Исправлена работа Invoke-WebRequest на Windows PowerShell 2026-04-08 19:53:47 +03:00
469cd8bc37 docs: add glossary file reference to AGENTS.md 2026-04-08 19:53:10 +03:00
c1ca11ab25 Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
Some checks failed
Build Mod Package / build (push) Failing after 6s
2026-04-08 19:50:19 +03:00
042b1fb43b Update Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml 2026-04-08 16:40:10 +03:00
c49ff67d51 Merge pull request 'feat: обновить глоссарий под BG3' (#1) from feat/glossary-bg3-aligned into main
Reviewed-on: mikhail/bg3-dnd55e-russian-localization#1
2026-04-08 15:09:05 +03:00
2324bf0c26 Merge branch 'main' into feat/glossary-bg3-aligned 2026-04-08 15:08:50 +03:00
f04d14dcf1 feat: обновить глоссарий под BG3 2026-04-08 14:20:23 +03:00
18 changed files with 2463 additions and 617 deletions

View File

@@ -59,7 +59,7 @@ jobs:
throw "Could not find a downloadable LSLib zip asset in the latest release." throw "Could not find a downloadable LSLib zip asset in the latest release."
} }
Invoke-WebRequest -Uri $asset.browser_download_url -OutFile ".tools/lslib/lslib.zip" Invoke-WebRequest -UseBasicParsing -Uri $asset.browser_download_url -OutFile ".tools/lslib/lslib.zip"
Expand-Archive -LiteralPath ".tools/lslib/lslib.zip" -DestinationPath ".tools/lslib" -Force Expand-Archive -LiteralPath ".tools/lslib/lslib.zip" -DestinationPath ".tools/lslib" -Force
- name: Build .pak - name: Build .pak
@@ -157,4 +157,4 @@ jobs:
Accept = "application/json" Accept = "application/json"
} }
Invoke-WebRequest -Method Post -Uri "$apiBase/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($assetName))" -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $zipPath Invoke-WebRequest -UseBasicParsing -Method Post -Uri "$apiBase/releases/$($release.id)/assets?name=$([uri]::EscapeDataString($assetName))" -Headers $uploadHeaders -ContentType "application/octet-stream" -InFile $zipPath

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
build/ build/
build-stage* build-stage*
.tools/ .tools/
.cache/
*.pak *.pak
*.tmp *.tmp
*.temp *.temp

137
ACTIONS.md Normal file
View File

@@ -0,0 +1,137 @@
# ACTIONS.md
VERSION: 3
MODE: machine-first
LANG: ru
ROUTING:
- match_user_request_to_action_id: true
- if_match: propose_action
- if_no_match: ignore_actions_md
- if_no_match_user_message: none
PROPOSE_RULE:
- prompt_template: "Приступить к выполнению '{action_id}'?"
- require_user_confirmation: true
- execute_without_confirmation: false
EXECUTION_BASELINE:
- enforce_agents_md: true
- minimal_non_breaking_changes: true
- steps_count_range: [3, 7]
- before_commit_push: request_user_approval
- prefer_existing_repo_scripts_over_manual_work: true
REPORT_FORMAT:
- done
- changed_files
- checks
- remaining
ACTIONS:
translation:diff:
intent: fetch_upstream_english_and_compare_with_ru
inputs:
- AGENTS.md::Canonical Paths::Upstream English reference
- Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
plan:
- run_scripts/get-upstream-english.ps1_and_wait_until_output_exists
- run_scripts/compare-translation.ps1_after_upstream_download_only
- classify_diff_into_missing_changed_stale
- write_machine_readable_and_markdown_reports_for_local_review
checks:
- xml_valid
- cache_path_gitignored
- local_only_no_ci_workflow_required
- translation_steps_not_parallelized_when_file_dependency_exists
outputs:
- .cache/upstream/english.xml
- build/translation-diff/summary.json
- build/translation-diff/summary.md
- build/translation-diff/candidates.json
translation:apply:
intent: apply_translation_edits_to_russian_xml
inputs:
- Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
- build/translation-diff/candidates.json
- prepared_update_texts_for_updates_and_optional_adds
plan:
- create_temporary_copy_of_russian_xml
- load_candidate_edit_file_and_temporary_xml
- fail_if_add_entry_has_empty_text
- apply_updates_and_optional_new_entries_by_contentuid
- write_utf8_bom_xml_to_temporary_copy
- validate_temporary_xml_via_separate_script
- replace_original_russian_xml_after_successful_validation
- report_changed_entries
checks:
- xml_valid
- contentuid_uniqueness_preserved
- only_requested_entries_changed
- no_partial_replace_on_validation_failure
outputs:
- Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
translation:update:
intent: sync_ru_translation_with_upstream
inputs:
- Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
- glossary/glossary.normalized.json
- AGENTS.md::Canonical Paths::Upstream English reference
plan:
- run_translation:diff_sequentially
- if_summary_has_no_missing_no_version_mismatch_no_stale_report_translation_up_to_date_and_stop
- if_diff_exists_stop_after_generating_build/translation-diff/candidates.json_until_prepared_edits_are_provided_explicitly
- review_build/translation-diff/candidates.json_before_apply
- reuse_glossary_for_term_consistency_when_preparing_texts
- run_translation:apply_only_after_candidate_texts_are_filled_and_explicit_edits_path_is_passed
checks:
- xml_valid
- glossary_consistency
- scope_limited_to_localization_and_allowed_metadata
- no_upstream_download_compare_race_condition
outputs:
- message: translation_up_to_date
- build/translation-diff/summary.json
- build/translation-diff/summary.md
- build/translation-diff/candidates.json
- Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml
- optional: Mods/DnD 5.5e AIO Russian/meta.lsx (release-only)
after_success:
- suggest_action: meta:sync-parent
reason: "Обновить версию зависимости из родительского мода (актуальный Version64 и связанные поля зависимости)."
action:report:
intent: unified_task_report
inputs:
- task_context
- modified_files
- verification_results
plan:
- summarize_done
- list_changed_files
- list_checks
- list_remaining
checks:
- concise
- factual
- no_unverified_claims
outputs:
- final_user_report
meta:sync-parent:
intent: sync_dependency_moduleshortdesc_from_parent_meta
inputs:
- parent_meta_git_url (optional; defaults to upstream)
- Mods/DnD 5.5e AIO Russian/meta.lsx
plan:
- read_parent_moduleinfo_fields
- validate_required_fields_folder_md5_name_publishhandle_uuid_version64
- update_target_dependencies_moduleshortdesc_fields
- validate_xml_structure
- report_changed_fields
checks:
- xml_valid
- required_parent_fields_present
- only_dependencies_moduleshortdesc_changed
outputs:
- Mods/DnD 5.5e AIO Russian/meta.lsx

207
AGENTS.md
View File

@@ -1,152 +1,91 @@
# AGENTS.md # AGENTS.md
## Project Overview ## General Rules (MUST)
This repository contains a standalone Russian localization mod for **Baldur's Gate 3**: ### Git Collaboration Policy (General)
- Commit/push only after explicit user approval.
- After approval: commit and push immediately.
- Branch switch prompt (`fix/*` or `feat/*`): ask at dialogue start; reuse the explicit user decision for all subsequent fix/feature tasks in the same dialogue.
- Pending clarification/approval question: ask once, in a single short message; do not repeat the same pending question in a separate final message.
- After finishing work in `fix/*` or `feat/*`: propose either
1. creating an MR into `main`, or
2. merging to `main` immediately and deleting the `fix/*`/`feat/*` branch.
- If push fails: retry up to 2 more times with 3s pause.
- Approval prompts for pending actions: short direct phrasing, no soft/opening phrases; response format is mandatory:
- binary action: yes/no question.
- multiple actions/combinations: numbered options only.
- Mod name: `DnD 5.5e All-in-One BEYOND Russian Localization` ### Cleanup (General)
- Mod folder: `Mods/DnD 5.5e AIO Russian` - Do not leave temporary/debug artifacts in repo.
- Base/original mod dependency: `DnD 5.5e All-in-One BEYOND` - Remove additional debug/temp dirs unless user asked to keep them.
- Original mod repository: `https://github.com/Yoonmoonsik/dnd55e`
- Original dependency UUID: `897914ef-5c96-053c-44af-0be823f895fe`
This repository is for the localization mod only. It must not gain gameplay logic, Script Extender files, or unrelated assets. ### Rules Maintenance (General)
- For changes to rules files (`AGENTS.md`, `ACTIONS.md`): prefer optimized, compressed edits for AI-agent execution (machine-readable, unambiguous).
- Keep rule updates minimal and non-duplicative: merge overlapping points, remove redundancy, preserve intent.
## Repository Rules ### Communication (General)
- Project file links in user-facing Markdown: relative paths, `/` separators, spaces encoded as `%20`.
- Keep the repository source-only. ## Project-Specific Rules (MUST)
- Do not commit `.pak` artifacts.
- Do not commit temporary build outputs.
- Do not add gameplay or script content unrelated to localization/release packaging.
- Keep the localization folder and metadata consistent with the packaged mod.
## Current Important Paths ### Scope
- Repository purpose: standalone Russian localization mod only.
- Allowed domain: localization content + packaging/release metadata.
- Forbidden: gameplay logic, Script Extender content, unrelated assets.
- Keep repository source-only.
- Never commit `.pak` or temporary build artifacts.
### Canonical Paths
- Mod sources: `Mods/DnD 5.5e AIO Russian` - Mod sources: `Mods/DnD 5.5e AIO Russian`
- Localization XML: `Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml` - Russian localization: `Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml`
- Mod metadata: `Mods/DnD 5.5e AIO Russian/meta.lsx` - Mod metadata: `Mods/DnD 5.5e AIO Russian/meta.lsx`
- Build script (single source of build truth): `scripts/build.ps1`
- CI workflow: `.gitea/workflows/build.yml` - CI workflow: `.gitea/workflows/build.yml`
- Main build script: `scripts/build.ps1` - Glossary (primary terminology reference): `glossary/glossary.normalized.json`
- Action catalog and command playbooks: `ACTIONS.md`
- Upstream English reference: `https://github.com/Yoonmoonsik/dnd55e/blob/main/Mods/DnD2024_897914ef-5c96-053c-44af-0be823f895fe/Localization/English/english.xml`
## Build And Release Model ### Packaging Invariants
- `.pak` must contain only BG3 mod structure under `Mods/...`.
- Required content in `.pak`:
- `Mods/DnD 5.5e AIO Russian/meta.lsx`
- `Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml`
- Must not leak into `.pak`: `.git`, `.gitea`, `scripts`, `tools`, `.tools`, `build`, staging dirs.
- Staging for packaging must be in `%TEMP%`, not in dot-prefixed repo dirs.
The authoritative build logic lives in: ### Build/CI Contract
- CI workflow stays thin:
1. prepare workspace
2. download Divine
3. call `scripts/build.ps1`
4. publish tag archive
- Expected build outputs:
- `build/DnD 5.5e AIO Russian.pak`
- `build/info.json`
- `build/DnD 5.5e AIO Russian <tag>.zip` (for tag builds)
- Release ZIP must include only `.pak` + `info.json`.
- CI triggers: tag `v*` and manual dispatch; not every push to `main`.
- `scripts/build.ps1` ### Version/Release Rules
- Read release version only from `save/region[@id="Config"]/node[@id="root"]/children/node[@id="ModuleInfo"]/attribute[@id="Version64"]` via explicit XML parsing.
- `PublishVersion` must not be changed during release preparation.
- Release tag must match the source-of-truth version.
- Decision logic before tagging:
1. If `ModuleInfo/Version64` was manually changed (e.g. BG3 Toolkit), use matching tag and release.
2. If `ModuleInfo/Version64` equals latest released version, bump version first (e.g. `scripts/set-version.ps1 -VersionTag <tag>`), commit, then create/push tag.
- `scripts/build.ps1` derives release `Version64` from tag and writes it to generated `info.json` and staged `meta.lsx`.
The Gitea workflow should stay thin and only: ### info.json Contract
- Top-level keys: `Mods`, `MD5`.
- Per-mod keys: `Author`, `Name`, `Folder`, `Version`, `Description`, `UUID`, `Created`, `Dependencies`, `Group`.
- `Dependencies` is an array of UUIDs.
- Current dependency UUID: `897914ef-5c96-053c-44af-0be823f895fe`.
1. prepare the workspace ### Git Collaboration Policy (Project-Specific)
2. download `Divine` - Commit messages and comments: Russian.
3. call `scripts/build.ps1` - Commit message content: what was done (not what should be done).
4. publish the release zip for tag builds - If changes affect `.pak` contents or build/release flow: propose releasing next version.
- For released versions in user-facing messages: provide direct archive link in Markdown format `[version](url)` when derivable (acceptable immediately after tag push, even before CI finishes).
### Current Build Outputs ### Cleanup (Project-Specific)
- Ignored/temp patterns include: `build/`, `build-stage*`, `.tools/`, `*.pak`.
The build script produces:
- `build/DnD 5.5e AIO Russian.pak`
- `build/info.json`
- `build/DnD 5.5e AIO Russian <tag>.zip` for tagged builds
The release zip is expected to contain:
- the built `.pak`
- `info.json`
## Packaging Notes
The package must contain only the BG3 mod structure under `Mods/...`.
Verified expected extracted `.pak` structure:
- `Mods/DnD 5.5e AIO Russian/meta.lsx`
- `Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml`
Do not allow `.git`, `.gitea`, `scripts`, `tools`, `.tools`, `build`, or staging directories into the `.pak`.
## Important Packaging Behavior
There is a runner-specific packaging quirk:
- `Divine` can produce a broken 48-byte `.pak` on the CI runner depending on the source path.
- Current mitigation is implemented in `scripts/build.ps1`.
- The script uses staged sources and fallback packaging attempts.
- Staging is performed in `%TEMP%`, not in a dot-prefixed directory inside the repo.
If packaging breaks again, debug the source path and unpack the resulting `.pak` locally to verify actual contents.
## Versioning
Version displayed by BG3ModManager should be derived from the release tag.
Current behavior:
- `scripts/build.ps1` derives `Version64` from tags like `v0.1.0`
- the computed version is written into:
- generated `info.json`
- staged `meta.lsx` before packaging
Do not manually hardcode release versions in the committed `meta.lsx` for each release if CI can derive them from tags.
## info.json Expectations
`info.json` is generated during build and should remain aligned with BG3/BG3ModManager expectations.
Current expected shape:
- top-level `Mods`
- top-level `MD5`
- per-mod fields:
- `Author`
- `Name`
- `Folder`
- `Version`
- `Description`
- `UUID`
- `Created`
- `Dependencies`
- `Group`
Current dependency model:
- `Dependencies` is an array of dependency UUIDs
- current dependency UUID:
- `897914ef-5c96-053c-44af-0be823f895fe`
## CI Trigger Policy
Current workflow policy:
- run on tags `v*`
- run on manual dispatch
- do not run on every push to `main`
## Git / Collaboration Preferences
User preference:
- after making changes, ask for permission before committing
- if the user approves, commit and push immediately
- for significant changes, propose moving work into a separate branch
- feature/fix branches must use the prefix `feat/` or `fix/`
- after finishing work in a `feat/` or `fix/` branch, propose merging it back into `main`
- comments and commit messages should be written in Russian
- commit messages should describe what was done, not what should be done
- if changes affect files that go into the final `.pak`, or change the build/release process, propose releasing the next version
- if push fails, retry up to two more times with a 3-second pause between attempts
Do not auto-commit or auto-push without explicit user approval.
## Cleanup Expectations
Temporary directories and debug artifacts should not remain in the repository.
Ignored paths currently include:
- `build/`
- `build-stage*`
- `.tools/`
- `*.pak`
If local debugging creates additional temporary folders, remove them when done unless the user explicitly wants to keep them.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<save> <save>
<version major="4" minor="8" revision="0" build="500"/> <version major="4" minor="8" revision="0" build="500"/>
<region id="Config"> <region id="Config">
<node id="root"> <node id="root">
<children> <children>
<node id="Dependencies"> <node id="Conflicts"/>
<children> <node id="Dependencies">
<node id="ModuleShortDesc"> <children>
<attribute id="Folder" type="LSString" value=""/> <node id="ModuleShortDesc">
<attribute id="MD5" type="LSString" value=""/> <attribute id="Folder" type="LSString" value="DnD2024_897914ef-5c96-053c-44af-0be823f895fe"/>
<attribute id="Name" type="LSString" value="DnD 5.5e All-in-One BEYOND"/> <attribute id="MD5" type="LSString" value="ac497558b070a635abecd7d23d4a3125"/>
<attribute id="PublishHandle" type="uint64" value="0"/> <attribute id="Name" type="LSString" value="DnD 5.5e All-in-One BEYOND"/>
<attribute id="UUID" type="guid" value="897914ef-5c96-053c-44af-0be823f895fe"/> <attribute id="PublishHandle" type="uint64" value="4419649"/>
<attribute id="Version64" type="int64" value="36028797018963968"/> <attribute id="UUID" type="guid" value="897914ef-5c96-053c-44af-0be823f895fe"/>
</node> <attribute id="Version64" type="int64" value="144396678084952064"/>
</children> </node>
</children>
</node>
<node id="ModuleInfo">
<attribute id="Author" type="LSString" value="Underslumber Team"/>
<attribute id="CharacterCreationLevelName" type="FixedString" value=""/>
<attribute id="Description" type="LSString" value="Русский перевод мода DnD 5.5e All-in-One BEYOND. Перевод ещё в разработке: AI помогает быстро обновлять тексты, а финальные правки и качество мы проверяем вручную."/>
<attribute id="FileSize" type="uint64" value="2488095"/>
<attribute id="Folder" type="LSString" value="DnD 5.5e AIO Russian"/>
<attribute id="LobbyLevelName" type="FixedString" value=""/>
<attribute id="MD5" type="LSString" value="c0a8f3412870277331306e0719fc6f77"/>
<attribute id="MenuLevelName" type="FixedString" value=""/>
<attribute id="Name" type="LSString" value="DnD 5.5e All-in-One BEYOND Russian Localization"/>
<attribute id="NumPlayers" type="uint8" value="4"/>
<attribute id="PhotoBooth" type="FixedString" value=""/>
<attribute id="PublishHandle" type="uint64" value="5965149"/>
<attribute id="StartupLevelName" type="FixedString" value=""/>
<attribute id="UUID" type="FixedString" value="6401e84d-daf2-416d-adeb-99c03a2487a6"/>
<attribute id="Version64" type="int64" value="281483566645248"/>
<children>
<node id="PublishVersion">
<attribute id="Version64" type="int64" value="281477124194304"/>
</node>
<node id="Scripts">
<children>
<node id="Script">
<attribute id="UUID" type="FixedString" value="1953f77d-a201-45d7-a194-9b84c34b8461"/>
<children>
<node id="Parameters">
<children>
<node id="Parameter">
<attribute id="MapKey" type="FixedString" value="HardcoreOnly"/>
<attribute id="Type" type="int32" value="1"/>
<attribute id="Value" type="LSString" value="0"/>
</node>
</children>
</node>
</children>
</node>
<node id="Script">
<attribute id="UUID" type="FixedString" value="0d6510f5-50a3-4ecd-83d8-134c9a640324"/>
<children>
<node id="Parameters">
<children>
<node id="Parameter">
<attribute id="MapKey" type="FixedString" value="HardcoreOnly"/>
<attribute id="Type" type="int32" value="1"/>
<attribute id="Value" type="LSString" value="0"/>
</node>
</children>
</node>
</children>
</node>
</children>
</node>
</children>
</node>
</children>
</node> </node>
<node id="ModuleInfo"> </region>
<attribute id="Author" type="LSString" value="MikhailRaw"/>
<attribute id="CharacterCreationLevelName" type="FixedString" value=""/>
<attribute id="Description" type="LSString" value="Русская локализация мода, который добавляет и обновляет контент в соответствии с правилами DnD 5.5e и другими источниками, включая предыстории, классы, таланты, расы, заклинания и многое другое. Это отдельный мод локализации и он требует установленный оригинальный мод."/>
<attribute id="FileSize" type="uint64" value="0"/>
<attribute id="Folder" type="LSString" value="DnD 5.5e AIO Russian"/>
<attribute id="LobbyLevelName" type="FixedString" value=""/>
<attribute id="MD5" type="LSString" value=""/>
<attribute id="MenuLevelName" type="FixedString" value=""/>
<attribute id="Name" type="LSString" value="DnD 5.5e All-in-One BEYOND Russian Localization"/>
<attribute id="NumPlayers" type="uint8" value="4"/>
<attribute id="PhotoBooth" type="FixedString" value=""/>
<attribute id="PublishHandle" type="uint64" value="0"/>
<attribute id="StartupLevelName" type="FixedString" value=""/>
<attribute id="UUID" type="FixedString" value="6401e84d-daf2-416d-adeb-99c03a2487a6"/>
<attribute id="Version64" type="int64" value="36028797018963968"/>
<children>
<node id="PublishVersion">
<attribute id="Version64" type="int64" value="36028797018963968"/>
</node>
</children>
</node>
</children>
</node>
</region>
</save> </save>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@@ -1,12 +1,11 @@
# DnD 5.5e All-in-One BEYOND Russian Localization # DnD 5.5e All-in-One BEYOND Russian Localization
Русский перевод мода **DnD 5.5e All-in-One BEYOND** для **Baldur's Gate 3**. Русская локализация [**DnD 5.5e All-in-One BEYOND**](https://github.com/Yoonmoonsik/dnd55e) для **Baldur's Gate 3**.
Оригинальный репозиторий: Публикация на [mod.io](https://mod.io/g/baldursgate3/m/dnd-55e-all-in-one-beyond-russian-localization).
[Yoonmoonsik/dnd55e](https://github.com/Yoonmoonsik/dnd55e)
## О моде ## О моде
**DnD 5.5e All-in-One BEYOND** переносит в Baldur's Gate 3 контент и механики, вдохновлённые Dungeons & Dragons 5.5e. Мод расширяет игру новыми и переработанными элементами, включая классы, предыстории, таланты, расы, заклинания и другие связанные системы. Русский перевод мода **DnD 5.5e All-in-One BEYOND**, который переносит в **Baldur's Gate 3** широкий пласт контента и правил **D&D 5.5e / PHB 2024**.
Этот проект предназначен для русской локализации оригинального мода и его текстового контента. Локализация поддерживается в темпе с апстримом, который сейчас развивается в сторону более полного охвата классов, рас, предысторий, фитов и заклинаний.

View File

@@ -1,133 +1,862 @@
{ {
"AC": "КБ",
"Ability": "характеристика",
"Ability Check": "проверка характеристики", "Ability Check": "проверка характеристики",
"Ability Checks": "проверки характеристик",
"Acid Splash": "Всплеск кислоты",
"Acid splash": "Всплеск кислоты",
"Acolyte": "Послушник",
"Acrobatics": "Акробатика",
"Actor": "Артистичный",
"Advantage": "преимущество", "Advantage": "преимущество",
"Aid": "Подмога", "Aid": "Помощь",
"Armor Class": "класс доспеха", "Alarm": "Сигнал тревоги",
"Attack Action": "действие «Атака»", "Alert": "Бдительный",
"Alter self": "Смена обличья",
"Animal Friendship": "Дружба с животными",
"Animal Handling": "Дрессировка",
"Animal friendship": "Дружба с животными",
"Animal messenger": "Почтовое животное",
"Animal shapes": "Превращение в животных",
"Animate dead": "Восставший труп",
"Animate objects": "Оживление вещей",
"Antilife shell": "Преграда жизни",
"Antimagic field": "Преграда магии",
"Antipathy/sympathy": "Антипатия/симпатия",
"Arcana": "Магия",
"Arcane Vigor": "Мистическая бодрость",
"Arcane eye": "Магический глаз",
"Arcane gate": "Магические врата",
"Arcane lock": "Волшебный замок",
"Armor": "броня",
"Armor Class": "класс брони",
"Armor of Agathys": "Броня Агафиса",
"Armour": "броня",
"Armour Class": "класс брони",
"Arms of Hadar": "Руки Хадара",
"Astral projection": "Проекция в астрал",
"Athlete": "Атлетичный",
"Athletics": "Атлетика",
"Attack": "атака",
"Attack Action": "действие Атака",
"Attack Roll": "бросок атаки", "Attack Roll": "бросок атаки",
"Bardic Inspiration": "Вдохновение барда", "Attack Rolls": "броски атаки",
"Bardic Inspiration die": "кубик Вдохновения барда", "Augury": "Гадание",
"Aura of Vitality": "Аура жизненной силы",
"Aura of life": "Аура жизни",
"Aura of purity": "Аура очищения",
"Aura of vitality": "Аура живучести",
"Awaken": "Пробуждение разума",
"Background": "происхождение",
"Backgrounds": "происхождения",
"Bane": "Порча",
"Banishing smite": "Изгоняющая кара",
"Banishment": "Изгнание",
"Barbarian": "варвар",
"Bard": "бард",
"Bardic Inspiration": "Бардовское вдохновение",
"Bardic Inspiration die": "Заряд Бардовского вдохновения",
"Barkskin": "Дубовая кора",
"Battle Inspiration": "Боевое вдохновение",
"Battleaxe": "Боевой топор",
"Beacon of hope": "Маяк надежды",
"Beast sense": "Животные чувства",
"Beguiling Magic": "Чарующая магия", "Beguiling Magic": "Чарующая магия",
"Bestow Curse": "Проклятие",
"Bestow Curse: Additional Damage": "Наложение проклятия: Дополнительный урон",
"Bestow Curse: Attack Disadvantage": "Наложение проклятия: Помеха атакам",
"Bestow Curse: Charisma Disadvantage": "Наложение проклятия: Помеха Харизме",
"Bestow Curse: Constitution Disadvantage": "Наложение проклятия: Помеха Телосложению",
"Bestow Curse: Dexterity Disadvantage": "Наложение проклятия: Помеха Ловкости",
"Bestow Curse: Dread": "Наложение проклятия: Ужас",
"Bestow Curse: Intelligence Disadvantage": "Наложение проклятия: Помеха Интеллекту",
"Bestow Curse: Strength Disadvantage": "Наложение проклятия: Помеха Силе",
"Bestow Curse: Wisdom Disadvantage": "Наложение проклятия: Помеха Мудрости",
"Bestow curse": "Проклятие",
"Bigbys hand": "Длань Бигби",
"Blade Ward": "Оберег от оружия",
"Blade barrier": "Стена клинков",
"Blade ward": "Защита от оружия",
"Bless": "Благословение",
"Blessed Healer": "Благословенный целитель", "Blessed Healer": "Благословенный целитель",
"Blessed Strikes": "Благословенные удары", "Blessed Strikes": "Благословенные удары",
"Blight": "Усыхание",
"Blinded": "Ослеплённый",
"Blinding smite": "Ослепляющая кара",
"Blindness/deafness": "Глухота/слепота",
"Blink": "Мерцание",
"Blowgun": "Духовая трубка",
"Blur": "Размытый образ",
"Bonus Action": "бонусное действие", "Bonus Action": "бонусное действие",
"Bonus Unarmed Strike": "Бонусная безоружная атака", "Bonus Unarmed Strike": "Бонусная безоружная атака",
"Booming Blade": "Громовой клинок",
"Branding smite": "Клеймящая кара",
"Brutal Strike": "Жестокий удар", "Brutal Strike": "Жестокий удар",
"Cantrip": "заговор", "Burning hands": "Огненные ладони",
"Cantrips": "заговоры", "Bursting Sinew": "Взрыв сухожилий",
"Call lightning": "Призыв молнии",
"Calm emotions": "Умиротворение",
"Cantrip": "фокус",
"Cantrips": "фокусы",
"Chain lightning": "Пляшущая молния",
"Channel Divinity": "Направление божественной энергии", "Channel Divinity": "Направление божественной энергии",
"Charger": "Налётчик",
"Charisma": "Харизма",
"Charlatan": "Шарлатан",
"Charm Person": "Приворот гуманоида",
"Charm person": "Приворот гуманоида",
"Charmed": "Очарованный",
"Chill Touch": "Пробирающий до костей холод",
"Chill touch": "Леденящее прикосновение",
"Chromatic Orb": "Хроматический шар",
"Chromatic Orb: Acid": "Хроматический шар: кислота",
"Chromatic Orb: Cold": "Хроматический шар: холод",
"Chromatic Orb: Fire": "Хроматический шар: огонь",
"Chromatic Orb: Lightning": "Хроматический шар: молния",
"Chromatic Orb: Poison": "Хроматический шар: яд",
"Chromatic Orb: Thunder": "Хроматический шар: гром",
"Chromatic orb": "Цветной шарик",
"Circle Forms": "Облики круга", "Circle Forms": "Облики круга",
"Circle of death": "Круг смерти",
"Circle of power": "Круг силы",
"Circle of the Moon Spells": "Заклинания круга Луны", "Circle of the Moon Spells": "Заклинания круга Луны",
"Clairvoyance": "Подсматривание",
"Class": "класс",
"Cleave": "Рассечение", "Cleave": "Рассечение",
"Cleric": "жрец",
"Clone": "Двойник",
"Cloud of Daggers": "Облако кинжалов",
"Cloud of daggers": "Облако кинжалов",
"Cloudkill": "Облако смерти",
"Club": "Дубинка",
"College of Lore": "Коллегия Знаний",
"College of Swords": "Коллегия Мечей",
"College of Valor": "Коллегия Отваги",
"College of Valour": "Коллегия Отваги",
"Color Spray": "Цветные брызги",
"Color spray": "Сверкающие брызги",
"Colour Spray": "Цветные брызги",
"Command": "Приказ",
"Commune": "Общение",
"Commune with nature": "Общение с природой",
"Compelled duel": "Вызов на дуэль",
"Comprehend languages": "Понимание языков",
"Compulsion": "Принуждение",
"Concentration": "концентрация", "Concentration": "концентрация",
"Countercharm": "Контрочарование", "Condition": "Состояние",
"Cutting Words": "Едкие слова", "Cone of Cold": "Конус холода",
"Cone of cold": "Конус холода",
"Confusion": "Смятение",
"Conjure animals": "Призыв животных",
"Conjure barrage": "Призыв заграждения",
"Conjure celestial": "Призыв небожителя",
"Conjure elemental": "Призыв элементаля",
"Conjure fey": "Призыв феи",
"Conjure minor elementals": "Призыв малых элементалей",
"Conjure volley": "Призыв залпа",
"Conjure woodland beings": "Призыв лесных обитателей",
"Constitution": "Выносливость",
"Contact other plane": "Связь с иным миром",
"Contagion": "Заражение",
"Contagion: Blinding Sickness": "Заражение: Ослепляющая болезнь",
"Contagion: Filth Fever": "Заражение: Грязная лихорадка",
"Contagion: Flesh Rot": "Заражение: Гниль плоти",
"Contagion: Mindfire": "Заражение: Огонь разума",
"Contagion: Seizure": "Заражение: Припадок",
"Contagion: Slimy Doom": "Заражение: Склизкая гибель",
"Contingency": "Предосторожность",
"Continual flame": "Вечный огонь",
"Control water": "Власть над водами",
"Control weather": "Власть над погодой",
"Cordon of arrows": "Завеса стрел",
"Countercharm": "Контрчары",
"Counterspell": "Контрзаклинание",
"Cover": "укрытие",
"Create Water": "Создание воды",
"Create food and water": "Сотворение пищи и воды",
"Create or destroy water": "Сотворение или уничтожение воды",
"Create undead": "Сотворение нежити",
"Creation": "Сотворение",
"Creature": "Существо",
"Criminal": "Преступник",
"Crossbow Expert": "Эксперт в арбалетах",
"Crossbow, hand": "Арбалет, ручной",
"Crossbow, heavy": "Арбалет, тяжёлый",
"Crossbow, light": "Арбалет, лёгкий",
"Crown of madness": "Корона безумия",
"Crusader's mantle": "Мантия крестоносца",
"Cure Wounds": "Исцеление ран",
"Cure wounds": "Лечение ран",
"Cutting Words": "Острые слова",
"D20 Test": "проверка к20", "D20 Test": "проверка к20",
"Dagger": "Кинжал",
"Damage": "урон",
"Damage Roll": "бросок урона",
"Dancing Lights": "Пляшущие огоньки",
"Dancing lights": "Пляшущие огоньки",
"Danger Sense": "Чувство опасности", "Danger Sense": "Чувство опасности",
"Darkness": "Тьма",
"Darkvision": "Ночное зрение",
"Dart": "Дротик",
"Dash": "Рывок", "Dash": "Рывок",
"Daylight": "Дневной свет",
"Deafened": "Оглохший",
"Death Saving Throw": "спасбросок от смерти",
"Death Saving Throws": "спасброски от смерти",
"Death ward": "Защита от смерти",
"Deception": "Обман",
"Defensive Duelist": "Оборонительный дуэлянт",
"Deflect Attacks": "Отклонение атак", "Deflect Attacks": "Отклонение атак",
"Delayed blast fireball": "Замедленный огненный шар",
"Demiplane": "Демиплан",
"Destructive wave": "Разрушительная волна",
"Detect evil and good": "Обнаружение добра и зла",
"Detect magic": "Обнаружение магии",
"Detect poison and disease": "Обнаружение болезней и яда",
"Detect thoughts": "Обнаружение мыслей",
"Dexterity": "Ловкость",
"Difficulty Class": "сложность",
"Dimension door": "Переносящая дверь",
"Disadvantage": "помеха", "Disadvantage": "помеха",
"Disciple of Life": "Ученик жизни", "Disciple of Life": "Ученик жизни",
"Disengage": "Отход", "Disengage": "Отступление",
"Divine Order": "Божественный порядок", "Disguise self": "Маскировка",
"Disintegrate": "Распад",
"Dispel evil and good": "Рассеивание добра и зла",
"Dispel magic": "Рассеивание магии",
"Dissonant Whispers": "Диссонирующий шепот",
"Dissonant whisper": "Диссонирующий шёпот",
"Divination": "Предсказание",
"Divine Order": "Божественный приказ",
"Divine Spark": "Божественная искра", "Divine Spark": "Божественная искра",
"Divine favor": "Божественное благоволение",
"Divine word": "Божественное слово",
"Dodge": "Уклонение",
"Dominate beast": "Подчинение зверя",
"Dominate monster": "Подчинение чудовища",
"Dominate person": "Подчинение личности",
"Dragonborn": "драконорождённый",
"Drawmijs instant summons": "Дромиджево появление",
"Dream": "Вещий сон",
"Druid": "друид",
"Druidcraft": "Искусство друидов",
"Druidic": "Друидический", "Druidic": "Друидический",
"Dual Wielder": "Двойное оружие",
"Dungeon Delver": "Исследователь подземелий",
"Durable": "Стойкий",
"Dwarf": "дварф",
"Earthquake": "Землетрясение",
"Eldritch Blast": "Потусторонний разряд",
"Eldritch Hex": "Потусторонний сглаз",
"Eldritch Hexed: Charisma": "Потусторонний сглаз: Харизма",
"Eldritch Hexed: Constitution": "Потусторонний сглаз: Телосложение",
"Eldritch Hexed: Dexterity": "Потусторонний сглаз: Ловкость",
"Eldritch Hexed: Intelligence": "Потусторонний сглаз: Интеллект",
"Eldritch Hexed: Strength": "Потусторонний сглаз: Сила",
"Eldritch Hexed: Wisdom": "Потусторонний сглаз: Мудрость",
"Eldritch Strike": "Мистический удар", "Eldritch Strike": "Мистический удар",
"Eldritch blast": "Мистический заряд",
"Elemental Adept": "Стихийный адепт",
"Elemental Fury": "Стихийная ярость", "Elemental Fury": "Стихийная ярость",
"Elemental weapon": "Стихийное оружие",
"Elf": "эльф",
"Empowered Strikes": "Усиленные удары", "Empowered Strikes": "Усиленные удары",
"Enemy": "Враг",
"Enhance Ability": "Усиление характеристики",
"Enhance ability": "Улучшение характеристики",
"Enlarge/reduce": "Увеличение/уменьшение",
"Ensnaring strike": "Опутывающий удар",
"Entangle": "Опутывание",
"Entertainer": "Артист",
"Enthrall": "Речь златоуста",
"Etherealness": "Эфирность",
"Evard's black tentacles": "Эвардовы чёрные щупальца",
"Evasion": "Ускользание", "Evasion": "Ускользание",
"Exhaustion": "истощение",
"Expeditious retreat": "Поспешное отступление",
"Expertise": "Мастерство",
"Extra Attack": "Дополнительная атака", "Extra Attack": "Дополнительная атака",
"Eyebite": "Разящее око",
"Fabricate": "Изготовление",
"Faerie Fire": "Фейское сияние",
"Faerie fire": "Огонь фей",
"Faithful Steed": "Верный скакун",
"False Life": "Ложная жизнь",
"False life": "Ложная жизнь",
"Fear": "Ужас",
"Feat": "черта",
"Feat: Fade Away": "Черта: Исчезновение",
"Feat: Fey Touched": "Черта: Затронутый феями",
"Feather Fall": "Плавное падение",
"Feather fall": "Падение пёрышком",
"Feats": "черты",
"Feature": "особенность",
"Features": "особенности",
"Feeblemind": "Слабоумие",
"Feign death": "Притворная смерть",
"Fighter": "воин",
"Find Familiar": "Призыв фамильяра",
"Find Steed": "Обретение скакуна",
"Find familiar": "Поиск фамильяра",
"Find steed": "Поиск скакуна",
"Find the path": "Поиск пути",
"Find traps": "Поиск ловушек",
"Finesse": "фехтовальное",
"Finger of death": "Перст смерти",
"Fire Bolt": "Огненный снаряд",
"Fire bolt": "Огненный снаряд",
"Fire shield": "Огненный щит",
"Fire storm": "Огненная буря",
"Fireball": "Огненный шар",
"Flail": "Цеп",
"Flame Blade": "Горящий клинок",
"Flame blade": "Горящий клинок",
"Flame strike": "Небесный огонь",
"Flaming Sphere": "Пылающая сфера",
"Flaming sphere": "Пылающий шар",
"Flesh to stone": "Окаменение",
"Fly": "Полёт",
"Fog Cloud": "Облако тумана",
"Fog cloud": "Туманное облако",
"Folk Hero": "Народный герой",
"Font of Inspiration": "Источник вдохновения", "Font of Inspiration": "Источник вдохновения",
"Forbiddance": "Запрет",
"Forcecage": "Узилище",
"Forceful Blow": "Мощный удар", "Forceful Blow": "Мощный удар",
"Fount of Moonlight": "Фонтан лунного света", "Foresight": "Предвидение",
"Fount of Moonlight": "Источник лунного света",
"Freedom of movement": "Свобода перемещения",
"Frenzy": "Неистовство", "Frenzy": "Неистовство",
"Friends": "Дружба",
"Frightened": "Испуганный",
"Gaseous form": "Газообразная форма",
"Gate": "Врата",
"Geas": "Обет",
"Gentle repose": "Нетленные останки",
"Giant insect": "Гигантское насекомое",
"Glaive": "Глефа",
"Glibness": "Находчивость",
"Globe of invulnerability": "Сфера неуязвимости",
"Glyph of warding": "Охранные руны",
"Gnome": "гном",
"Goodberry": "Вкусные ягоды",
"Grapple": "захват",
"Grappled": "Схваченный",
"Grappler": "Борец",
"Grasping vine": "Цепкая лоза",
"Graze": "Задевание", "Graze": "Задевание",
"Grease": "Скольжение",
"Great Weapon Master": "Мастер большого оружия",
"Greataxe": "Секира",
"Greatclub": "Палица",
"Greater invisibility": "Высшая невидимость",
"Greater restoration": "Высшее восстановление",
"Greatsword": "Двуручный меч",
"Green-Flame Blade": "Клинок зелёного пламени",
"Guardian of faith": "Страж веры",
"Guards and wards": "Стражи",
"Guidance": "Наставление", "Guidance": "Наставление",
"Guiding Bolt": "Направляющий луч",
"Guiding bolt": "Направленный снаряд",
"Guild Artisan": "Гильдейский ремесленник",
"Gust of Wind": "Порыв ветра",
"Gust of wind": "Порыв ветра",
"HP": "ОЗ",
"Hail of Thorns": "Град шипов",
"Hail of thorns": "Град шипов",
"Halberd": "Алебарда",
"Half-Elf": "полуэльф",
"Half-Orc": "полуорк",
"Halfling": "полурослик",
"Hallow": "Святилище",
"Hallucinatory terrain": "Мираж",
"Hamstring Blow": "Удар по сухожилиям", "Hamstring Blow": "Удар по сухожилиям",
"Handaxe": "Ручной топор",
"Harm": "Поражение",
"Haste": "Ускорение",
"Heal": "Полное исцеление",
"Healer": "Лекарь",
"Healing": "лечение",
"Healing Word": "Исцеляющее слово",
"Healing word": "Лечащее слово",
"Heat metal": "Раскалённый металл",
"Heavily Armored": "Знаток тяжёлых доспехов",
"Heavy": "тяжёлое",
"Heavy Armor": "тяжёлые доспехи", "Heavy Armor": "тяжёлые доспехи",
"Heavy Armor Master": "Мастер тяжёлых доспехов",
"Heightened Focus": "Обострённый фокус", "Heightened Focus": "Обострённый фокус",
"Hellish rebuke": "Адское возмездие",
"Help": "Помощь",
"Heroes feast": "Пир героев",
"Heroic Inspiration": "Героическое вдохновение", "Heroic Inspiration": "Героическое вдохновение",
"Heroic Warrior": "Героический воин", "Heroic Warrior": "Героический воин",
"Hit Points": "очки здоровья", "Heroism": "Героизм",
"Hex": "Сглаз",
"Hex (Charisma)": "Сглаз (Харизма)",
"Hex (Constitution)": "Сглаз (Телосложение)",
"Hex (Dexterity)": "Сглаз (Ловкость)",
"Hex (Intelligence)": "Сглаз (Интеллект)",
"Hex (Strength)": "Сглаз (Сила)",
"Hex (Wisdom)": "Сглаз (Мудрость)",
"Hide": "Спрятаться",
"History": "История",
"Hit Dice": "кости хитов",
"Hit Die": "кость хитов",
"Hit Point": "ОЗ",
"Hit Points": "ОЗ",
"Hold Person": "Паралич гуманоида",
"Hold monster": "Удержание чудовища",
"Hold person": "Удержание личности",
"Holy aura": "Аура святости",
"Human": "человек",
"Hunger of Hadar": "Голод Хадара",
"Hunter's Mark": "Метка охотника",
"Hunter's mark": "Метка охотника",
"Hypnotic pattern": "Гипнотический узор",
"Ice Knife": "Ледяной нож",
"Ice storm": "Град",
"Identify": "Опознание",
"Illusory script": "Невидимое письмо",
"Imprisonment": "Заточение",
"Improved Combat Superiority": "Усовершенствованное превосходство в бою", "Improved Combat Superiority": "Усовершенствованное превосходство в бою",
"Improved Warding Flare": "Улучшенная защитная вспышка", "Improved Warding Flare": "Улучшенная защитная вспышка",
"Incapacitated": "Недееспособный",
"Incendiary cloud": "Воспламеняющаяся туча",
"Indomitable": "Несгибаемая воля", "Indomitable": "Несгибаемая воля",
"Inflict Wounds": "Нанесение ран",
"Inflict wounds": "Нанесение ран",
"Initiative": "инициатива",
"Insect plague": "Нашествие насекомых",
"Insight": "Проницательность",
"Inspiring Leader": "Воодушевляющий лидер",
"Instinctive Pounce": "Интуитивный рывок", "Instinctive Pounce": "Интуитивный рывок",
"Intelligence": "Интеллект",
"Intimidation": "Запугивание",
"Investigation": "Расследование",
"Invisibility": "Невидимость",
"Invisible": "Невидимый",
"Invoke Duplicity": "Призыв двойника", "Invoke Duplicity": "Призыв двойника",
"Jack of All Trades": "Мастер на все руки",
"Jack-of-all-Trades": "Мастер на все руки",
"Javelin": "Метательное копьё",
"Jump": "Прыжок",
"Keen Mind": "Отличная память",
"Knock": "Стук",
"Lance": "Длинное копьё",
"Legend lore": "Знание легенд",
"Leomunds secret chest": "Леомундов потайной сундук",
"Leomunds tiny hut": "Леомундова хижина",
"Lesser Restoration": "Низшее восстановление",
"Lesser restoration": "Малое восстановление",
"Level": "уровень",
"Levitate": "Левитация",
"Light": "Свет", "Light": "Свет",
"Long Rest": "длительный отдых", "Light hammer": "Лёгкий молот",
"Magic Action": "действие «Магия»", "Lightly Armored": "Знаток лёгких доспехов",
"Lightning arrow": "Молниевая стрела",
"Lightning bolt": "Молния",
"Linguist": "Языковед",
"Locate animals or plants": "Поиск животных или растений",
"Locate creature": "Поиск существа",
"Locate object": "Поиск предмета",
"Long Rest": "Долгий отдых",
"Longbow": "Длинный лук",
"Longstrider": "Скороход",
"Longsword": "Длинный меч",
"Lucky": "Везунчик",
"Mace": "Булава",
"Mage Armor": "Магический доспех",
"Mage Armour": "Магический доспех",
"Mage Hand": "Магическая рука",
"Mage Slayer": "Убийца магов",
"Mage armor": "Доспехи мага",
"Mage hand": "Магическая рука",
"Magic Action": "действие Магия",
"Magic Initiate": "Посвящённый в магию",
"Magic Initiate (Cleric)": "Посвященный в магию: жрец",
"Magic Initiate (Sorcerer)": "Посвященный в магию: чародей",
"Magic Initiate (Warlock)": "Посвященный в магию: колдун",
"Magic Initiate (Wizard)": "Посвященный в магию: волшебник",
"Magic Missile": "Волшебная стрела",
"Magic Weapon": "Волшебное оружие",
"Magic action": "действие Магия",
"Magic circle": "Магический круг",
"Magic jar": "Волшебный сосуд",
"Magic missile": "Волшебная стрела",
"Magic mouth": "Волшебные уста",
"Magic weapon": "Магическое оружие",
"Magician": "Маг", "Magician": "Маг",
"Major image": "Образ",
"Mantle of Inspiration": "Мантия вдохновения", "Mantle of Inspiration": "Мантия вдохновения",
"Martial Adept": "Воинский адепт",
"Martial Arts": "Боевые искусства", "Martial Arts": "Боевые искусства",
"Martial weapons": "воинское оружие", "Martial weapons": "воинское оружие",
"Mass Healing Word": "Массовое исцеляющее слово",
"Mass cure wounds": "Множественное лечение ран",
"Mass heal": "Множественное полное исцеление",
"Mass healing word": "Множественное лечащее слово",
"Mass suggestion": "Множественное внушение",
"Mastery Properties": "свойства мастерства", "Mastery Properties": "свойства мастерства",
"Mastery Properties: Cleave": "Свойства мастерства: Рассечение",
"Mastery Properties: Graze": "Свойства мастерства: Скользящий удар",
"Mastery Properties: Nick": "Свойства мастерства: Надрез",
"Mastery Properties: Push": "Свойства мастерства: Толчок",
"Mastery Properties: Sap": "Свойства мастерства: Истощение",
"Mastery Properties: Slow": "Свойства мастерства: Замедление",
"Mastery Properties: Topple": "Свойства мастерства: Опрокидывание",
"Mastery Properties: Vex": "Свойства мастерства: Отвлечение",
"Maul": "Молот",
"Maze": "Лабиринт",
"Medicine": "Медицина",
"Medium Armor": "средние доспехи", "Medium Armor": "средние доспехи",
"Medium Armor Master": "Мастер средних доспехов",
"Meld into stone": "Слияние с камнем",
"Melee Attack": "рукопашная атака",
"Melf's Acid Arrow": "Кислотная стрела Мелфа",
"Melf's acid arrow": "Кислотная стрела Мелфа",
"Mending": "Починка",
"Message": "Сообщение",
"Meteor swarm": "Метеоритный дождь",
"Mind Magic": "Магия разума", "Mind Magic": "Магия разума",
"Mind Sliver": "Расщепление разума",
"Mind Spike": "Пронзание разума",
"Mind blank": "Сокрытие разума",
"Mindless Rage": "Безрассудная ярость", "Mindless Rage": "Безрассудная ярость",
"Minor Illusion": "Малая иллюзия",
"Minor illusion": "Малая иллюзия",
"Mirage arcane": "Таинственный мираж",
"Mirror Image": "Зеркальное отражение",
"Mirror image": "Отражения",
"Mislead": "Фальшивый двойник",
"Misty step": "Туманный шаг",
"Mobile": "Подвижный",
"Moderately Armored": "Знаток средних доспехов",
"Modify memory": "Изменение памяти",
"Monk": "монах",
"Moonbeam": "Лунный свет",
"Moonlight Step": "Лунный шаг", "Moonlight Step": "Лунный шаг",
"Moonlight Step Charge": "заряд «Лунного шага»", "Moonlight Step Charge": "заряд «Лунного шага»",
"Mordenkainens faithful hound": "Верный пёс Морденкайнена",
"Mordenkainens magnificent mansion": "Великолепный особняк Морденкайнена",
"Mordenkainens private sanctum": "Кабинет Морденкайнена",
"Mordenkainens sword": "Меч Морденкайнена",
"Morningstar": "Моргенштерн",
"Mounted Combatant": "Верховой боец",
"Move earth": "Движение почвы",
"Nature": "Природа",
"Necrotic Damage": "урон некротической энергией", "Necrotic Damage": "урон некротической энергией",
"Net": "Сеть",
"Nick": "Надрез", "Nick": "Надрез",
"Opportunity Attack": "атака по возможности", "Noble": "Дворянин",
"Opportunity Attacks": "атаки по возможности", "Nondetection": "Необнаружимость",
"Patient Defense": "Выжидательная оборона", "Nystuls magic aura": "Нистулова ложная аура",
"Observant": "Внимательный",
"Opportunity Attack": "внеочередная атака",
"Opportunity Attacks": "внеочередные атаки",
"Origin Feat: Alert": "Черта происхождения: Бдительный",
"Origin Feat: Magic Initiate (Cleric)": "Черта происхождения: Посвященный в магию (Жрец)",
"Origin Feat: Magic Initiate (Wizard)": "Черта происхождения: Посвященный в магию (Волшебник)",
"Origin Feat: Magic Initiate (Druid)": "Черта происхождения: Посвященный в магию (Друид)",
"Origin Feat: Healer": "Черта происхождения: Лекарь",
"Origin Feat: Lucky": "Черта происхождения: Везунчик",
"Origin Feat: Tavern Brawler": "Черта происхождения: Драчун",
"Origin Feat: Musician": "Черта происхождения: Музыкант",
"Origin Feat: Savage Attacker": "Черта происхождения: Дикий атакующий",
"Origin Feat: Skilled": "Черта происхождения: Одаренный",
"Origin Feat: Tough": "Черта происхождения: Крепкий",
"Otilukes freezing sphere": "Отилюков ледяной шар",
"Otilukes resilient sphere": "Отилюков упругий шар",
"Ottos irresistible dance": "Неудержимая пляска Отто",
"Outlander": "Чужеземец",
"Paladin": "паладин",
"Paralyzed": "Парализованный",
"Pass without Trace": "Бесследный шаг",
"Pass without trace": "Бесследное передвижение",
"Passwall": "Создание прохода",
"Patient Defense": "Терпеливая оборона",
"Perception": "Внимание",
"Performance": "Исполнение",
"Persuasion": "Убеждение",
"Petrified": "Окаменевший",
"Phantasmal Force": "Сила фантазма",
"Phantasmal force": "Воображаемая сила",
"Phantasmal killer": "Воображаемый убийца",
"Phantom Steed": "Призрачный скакун",
"Phantom steed": "Призрачный скакун",
"Pike": "Пика",
"Planar ally": "Планарный союзник",
"Planar binding": "Планарные узы",
"Plane shift": "Уход в иной мир",
"Plant growth": "Рост растений",
"Poison Spray": "Брызги яда", "Poison Spray": "Брызги яда",
"Poison spray": "Ядовитые брызги",
"Poisoned": "Отравленный",
"Polearm Master": "Мастер древкового оружия",
"Polymorph": "Превращение",
"Potent Spellcasting": "Мощное заклинание", "Potent Spellcasting": "Мощное заклинание",
"Power word heal": "Слово Силы: исцеление",
"Power word kill": "Слово Силы: смерть",
"Power word stun": "Слово Силы: оглушение",
"Prayer of Healing": "Исцеляющий молебен",
"Prayer of healing": "Молебен лечения",
"Preserve Life": "Сохранение жизни", "Preserve Life": "Сохранение жизни",
"Prestidigitation": "Фокусы",
"Primal Knowledge": "Первозданное знание", "Primal Knowledge": "Первозданное знание",
"Primal Order": "Первозданный уклад", "Primal Order": "Изначальный приказ",
"Primal Strike": "Первобытный удар", "Primal Strike": "Первобытный удар",
"Prismatic spray": "Радужные брызги",
"Prismatic wall": "Радужная стена",
"Produce Flame": "Создание пламени", "Produce Flame": "Создание пламени",
"Proficiency Bonus": "бонус мастерства", "Produce flame": "Сотворение пламени",
"Proficiency": "умение",
"Proficiency Bonus": "бонус умения",
"Proficient": "владение", "Proficient": "владение",
"Programmed illusion": "Заданная иллюзия",
"Project image": "Проекция",
"Prone": "Сбитый с ног",
"Protection from Evil and Good": "Защита от зла и добра",
"Protection from energy": "Защита от энергии",
"Protection from evil and good": "Защита от добра и зла",
"Protection from poison": "Защита от яда",
"Protector": "Защитник", "Protector": "Защитник",
"Purify food and drink": "Очищение пищи и питья",
"Purity of Body": "Чистота тела", "Purity of Body": "Чистота тела",
"Push": "Отталкивание", "Push": "Отталкивание",
"Quarterstaff": "Боевой посох",
"Radiant Damage": "урон излучением", "Radiant Damage": "урон излучением",
"Rage": "Ярость", "Rage": "Ярость",
"Raise dead": "Оживление",
"Ranged Attack": "дальнобойная атака",
"Ranger": "следопыт",
"Rapier": "Рапира",
"Rarys telepathic bond": "Ментальная связь Рэри",
"Ray of Sickness": "Луч болезни",
"Ray of enfeeblement": "Луч слабости",
"Ray of frost": "Луч холода",
"Ray of sickness": "Луч болезни",
"Reaction": "реакция", "Reaction": "реакция",
"Reapply Hex": "Повторно наложить Сглаз",
"Reapply Hex (Charisma)": "Повторно наложить Сглаз (Харизма)",
"Reapply Hex (Constitution)": "Повторно наложить Сглаз (Телосложение)",
"Reapply Hex (Dexterity)": "Повторно наложить Сглаз (Ловкость)",
"Reapply Hex (Intelligence)": "Повторно наложить Сглаз (Интеллект)",
"Reapply Hex (Strength)": "Повторно наложить Сглаз (Сила)",
"Reapply Hex (Wisdom)": "Повторно наложить Сглаз (Мудрость)",
"Reckless Attack": "Безрассудная атака", "Reckless Attack": "Безрассудная атака",
"Redirect Attacks": "Перенаправление атак", "Redirect Attacks": "Перенаправление атак",
"Regenerate": "Регенерация",
"Reincarnate": "Реинкарнация",
"Relentless Rage": "Неудержимая ярость", "Relentless Rage": "Неудержимая ярость",
"Religion": "Религия",
"Remarkable Athlete": "Выдающийся атлет", "Remarkable Athlete": "Выдающийся атлет",
"Remove Curse": "Снятие проклятия",
"Remove curse": "Снятие проклятья",
"Resilient": "Устойчивый",
"Resistance": "Сопротивление", "Resistance": "Сопротивление",
"Restrained": "Опутанный",
"Resurrection": "Воскрешение",
"Retaliation": "Возмездие", "Retaliation": "Возмездие",
"Reverse gravity": "Изменение тяготения",
"Revivify": "Возрождение",
"Ritual Caster": "Ритуальный заклинатель",
"Rogue": "плут",
"Rope trick": "Трюк с верёвкой",
"Rule: One Spell with a Spell Slot per Turn": "Правило: одно заклинание с ячейкой за ход",
"Sacred Flame": "Священное пламя", "Sacred Flame": "Священное пламя",
"Sacred flame": "Священное пламя",
"Sage": "Мудрец",
"Sanctuary": "Убежище",
"Sap": "Утомление", "Sap": "Утомление",
"Saving Throw": "спасбросок", "Savage Attacker": "Дикий атакующий",
"Saving Throw": "испытание",
"Saving Throw DC": "КС испытания",
"Saving Throws": "испытания",
"Scimitar": "Скимитар",
"Scorching Ray": "Опаляющий луч",
"Scorching ray": "Палящий луч",
"Scrying": "Наблюдение",
"Sear Undead": "Выжигание нежити", "Sear Undead": "Выжигание нежити",
"Searing smite": "Палящая кара",
"Second Wind": "Второе дыхание", "Second Wind": "Второе дыхание",
"See Invisibility": "Видение невидимого",
"See invisibility": "Видение невидимого",
"Seeming": "Притворство",
"Self-Restoration": "Самоисцеление", "Self-Restoration": "Самоисцеление",
"Shillelagh": "Дубинка", "Sending": "Послание",
"Short Rest": "короткий отдых", "Sentinel": "Страж",
"Sequester": "Изоляция",
"Shapechange": "Полное превращение",
"Sharpshooter": "Меткий стрелок",
"Shatter": "Разбивающий звук",
"Shield": "Щит",
"Shield Master": "Мастер щитов",
"Shield of Faith": "Щит веры",
"Shield of faith": "Щит веры",
"Shillelagh": "Шиллейла",
"Shocking Grasp": "Шоковое прикосновение",
"Shocking grasp": "Электрошок",
"Short Rest": "Короткий отдых",
"Shortbow": "Короткий лук",
"Shortsword": "Короткий меч",
"Sickle": "Серп",
"Silence": "Тишина",
"Silent image": "Безмолвный образ",
"Simulacrum": "Подобие",
"Skilled": "Одаренный",
"Skilled: Acrobatics": "Одаренный: Акробатика",
"Skilled: Animal Handling": "Одаренный: Уход за животными",
"Skilled: Arcana": "Одаренный: Магия",
"Skilled: Athletics": "Одаренный: Атлетика",
"Skilled: Deception": "Одаренный: Обман",
"Skilled: History": "Одаренный: История",
"Skilled: Insight": "Одаренный: Проницательность",
"Skilled: Intimidation": "Одаренный: Запугивание",
"Skilled: Investigation": "Одаренный: Расследование",
"Skilled: Medicine": "Одаренный: Медицина",
"Skilled: Nature": "Одаренный: Природа",
"Skilled: Perception": "Одаренный: Восприятие",
"Skilled: Performance": "Одаренный: Выступление",
"Skilled: Persuasion": "Одаренный: Убеждение",
"Skilled: Religion": "Одаренный: Религия",
"Skilled: Sleight of Hand": "Одаренный: Ловкость рук",
"Skilled: Stealth": "Одаренный: Скрытность",
"Skilled: Survival": "Одаренный: Выживание",
"Skulker": "Проныра",
"Sleep": "Сон",
"Sleet storm": "Метель",
"Sleight of Hand": "Ловкость рук",
"Sling": "Праща",
"Slow": "Замедление", "Slow": "Замедление",
"Slow Fall": "Замедление падения", "Slow Fall": "Замедление падения",
"Spare the Dying": "Пощада умирающему", "Snilloc's Snowball Swarm": "Снежный шквал Сниллока",
"Spell Slot": "ячейка заклинаний", "Soldier": "Солдат",
"Sorcerer": "чародей",
"Sorcerous Burst": "Чародейский выброс",
"Spare the Dying": "Уход за умирающим",
"Spare the dying": "Уход за умирающим",
"Speak with Animals": "Разговор с животными",
"Speak with Dead": "Разговор с мертвым",
"Speak with animals": "Разговор с животными",
"Speak with dead": "Разговор с мёртвыми",
"Speak with plants": "Разговор с растениями",
"Spear": "Копьё",
"Spell Save DC": "КС испытаний против заклинаний",
"Spell Slot": "ячейка заклинания",
"Spell Sniper": "Меткие заклинания",
"Spellcasting": "накладывание заклинаний",
"Spider climb": "Паук",
"Spike growth": "Шипы",
"Spirit Guardians": "Призрачные стражи",
"Spirit guardians": "Духовные стражи",
"Spiritual Weapon": "Призрачное оружие",
"Spiritual weapon": "Божественное оружие",
"Stable": "Стабилизированный",
"Staggering smite": "Оглушающая кара",
"Starry Wisp": "Звёздная искра", "Starry Wisp": "Звёздная искра",
"Step of the Wind": "Шаг ветра", "Stealth": "Скрытность",
"Steed Action Point": "Очко действия скакуна",
"Steed Attack": "Атака скакуна",
"Steel Wind Strike": "Удар стального ветра",
"Step of the Wind": "Поступь ветра",
"Stinking Cloud": "Смрадное облако",
"Stinking cloud": "Зловонное облако",
"Stone shape": "Изменение формы камня",
"Stoneskin": "Каменная кожа",
"Storm of vengeance": "Гроза гнева",
"Strength": "Сила",
"Stunned": "Ошеломлённый",
"Suggestion": "Внушение",
"Sunbeam": "Солнечный луч",
"Sunburst": "Солнечный ожог",
"Survival": "Выживание",
"Swift quiver": "Быстрый колчан",
"Sword Burst": "Вспышка мечей",
"Symbol": "Знак",
"Synaptic Static": "Синаптический разряд",
"Tactical Master": "Мастер тактики", "Tactical Master": "Мастер тактики",
"Tactical Mind": "Тактический ум", "Tactical Mind": "Тактический ум",
"Tactical Shift": "Тактический манёвр", "Tactical Shift": "Тактический манёвр",
"Temporary Hit Points": "временные очки здоровья", "Tasha's Hideous Laughter": "Отвратительный смех Таши",
"Tasha's Mind Whip": "Психическая плеть Таши",
"Tasha's hideous laughter": "Отвратительный смех Таши",
"Tavern Brawler": "Драчун",
"Telekinesis": "Телекинез",
"Telepathy": "Телепатия",
"Teleport": "Телепортация",
"Teleportation circle": "Круг телепортации",
"Temporary Hit Point": "временный ОЗ",
"Temporary Hit Points": "временные ОЗ",
"Tensers floating disk": "Тензеров парящий диск",
"Thaumaturge": "Чудотворец", "Thaumaturge": "Чудотворец",
"Thaumaturgy": "Чудотворство", "Thaumaturgy": "Тавматургия",
"Thorn Whip": "Колючий бич", "Thorn Whip": "Терновый хлыст",
"Thorn whip": "Терновый кнут",
"Throw": "Метнуть",
"Thunderclap": "Раскат грома", "Thunderclap": "Раскат грома",
"Thunderous smite": "Громовая кара",
"Thunderwave": "Громовая волна",
"Tiefling": "тифлинг",
"Time stop": "Остановка времени",
"Toll the Dead": "Погребальный звон", "Toll the Dead": "Погребальный звон",
"Tongues": "Языки",
"Topple": "Опрокидывание", "Topple": "Опрокидывание",
"Tough": "Крепкий",
"Transport via plants": "Путешествие через растения",
"Tree stride": "Древесный путь",
"Tricksters Transposition": "Перемещение обманщика", "Tricksters Transposition": "Перемещение обманщика",
"Trident": "Трезубец",
"True Strike": "Верный удар",
"True polymorph": "Истинное превращение",
"True resurrection": "Истинное воскрешение",
"True seeing": "Истинное зрение",
"True strike": "Верный удар",
"Tsunami": "Цунами",
"Unarmored Defense": "Защита без доспехов", "Unarmored Defense": "Защита без доспехов",
"Unarmored Movement": "Бездоспешное передвижение", "Unarmored Movement": "Движение без доспехов",
"Unarmoured Defence": "Защита без доспехов",
"Unarmoured Movement": "Движение без доспехов",
"Uncanny Metabolism": "Необычный метаболизм", "Uncanny Metabolism": "Необычный метаболизм",
"Unconscious": "Бессознательный",
"Unfettered Mind": "Свободный разум", "Unfettered Mind": "Свободный разум",
"Unseen servant": "Невидимый слуга",
"Urchin": "Беспризорник",
"Vampiric Touch": "Касание вампира",
"Vampiric touch": "Прикосновение вампира",
"Vex": "Досада", "Vex": "Досада",
"Vicious Mockery": "Язвительная насмешка",
"Vicious mockery": "Злая насмешка",
"Vitriolic Sphere": "Едкий шар",
"Wall of fire": "Огненная стена",
"Wall of force": "Силовая стена",
"Wall of ice": "Ледяная стена",
"Wall of stone": "Каменная стена",
"Wall of thorns": "Терновая стена",
"War Caster": "Боевой заклинатель",
"War pick": "Боевая кирка",
"Warden": "Страж", "Warden": "Страж",
"Warding Flare": "Защитная вспышка", "Warding Flare": "Защитная вспышка",
"Weapon Mastery": "мастерство владения оружием", "Warding bond": "Охраняющая связь",
"Warhammer": "Боевой молот",
"Warlock": "колдун",
"Water breathing": "Подводное дыхание",
"Water walk": "Хождение по воде",
"Weapon Master": "Мастер оружия",
"Weapon Mastery": "Мастерство владения оружием",
"Web": "Паутина",
"Weird": "Смертный ужас",
"Whip": "Кнут",
"Wild Resurgence": "Возрождение дикого облика", "Wild Resurgence": "Возрождение дикого облика",
"Wild Shape": "Дикий облик", "Wild Shape": "Дикий облик",
"Wild Shape Charge": "заряд «Дикого облика»", "Wild Shape Charge": "заряд «Дикого облика»",
"Word of Radiance": "Сияющее слово" "Wind walk": "Хождение по ветру",
} "Wind wall": "Стена ветров",
"Wisdom": "Мудрость",
"Wish": "Исполнение желаний",
"Witch Bolt": "Ведьмовской разряд",
"Witch bolt": "Ведьмин снаряд",
"Wizard": "волшебник",
"Word of Radiance": "Слово сияния",
"Word of recall": "Слово возврата",
"Wrathful smite": "Гневная кара",
"Zone of truth": "Область истины"
}

View File

@@ -0,0 +1,210 @@
param(
[string]$RussianPath = "Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml",
[Parameter(Mandatory = $true)]
[string]$EditsPath
)
$ErrorActionPreference = "Stop"
function Read-XmlDocument {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$resolvedPath = [System.IO.Path]::GetFullPath($Path)
if (-not (Test-Path -LiteralPath $resolvedPath)) {
throw "XML file was not found: '$resolvedPath'."
}
[xml]$xml = Get-Content -LiteralPath $resolvedPath -Raw
if ($null -eq $xml.SelectSingleNode('/contentList')) {
throw "XML file does not contain '/contentList': '$resolvedPath'."
}
return @{
Path = $resolvedPath
Xml = $xml
}
}
function Get-ContentNodeMap {
param(
[Parameter(Mandatory = $true)]
[xml]$Xml
)
$map = @{}
foreach ($node in $Xml.SelectNodes('/contentList/content')) {
$contentUid = [string]$node.GetAttribute("contentuid")
if ([string]::IsNullOrWhiteSpace($contentUid)) {
continue
}
if ($map.ContainsKey($contentUid)) {
throw "Duplicate contentuid found in target XML: '$contentUid'."
}
$map[$contentUid] = $node
}
return $map
}
function Assert-UniqueEditContentUid {
param(
[AllowEmptyCollection()]
[Parameter(Mandatory = $true)]
[System.Collections.Generic.HashSet[string]]$Seen,
[Parameter(Mandatory = $true)]
[string]$ContentUid,
[Parameter(Mandatory = $true)]
[string]$Section
)
if (-not $Seen.Add($ContentUid)) {
throw "Edits file contains duplicate contentuid '$ContentUid' in '$Section'."
}
}
$russianDocument = Read-XmlDocument -Path $RussianPath
$temporaryRussianPath = "$($russianDocument.Path).tmp"
$validateScriptPath = Join-Path $PSScriptRoot "validate-translation-xml.ps1"
if (-not (Test-Path -LiteralPath $validateScriptPath)) {
throw "Validation script was not found: '$validateScriptPath'."
}
if (Test-Path -LiteralPath $temporaryRussianPath) {
Remove-Item -LiteralPath $temporaryRussianPath -Force
}
Copy-Item -LiteralPath $russianDocument.Path -Destination $temporaryRussianPath -Force
$russianDocument = Read-XmlDocument -Path $temporaryRussianPath
$resolvedEditsPath = [System.IO.Path]::GetFullPath($EditsPath)
if (-not (Test-Path -LiteralPath $resolvedEditsPath)) {
throw "Edits file was not found: '$resolvedEditsPath'."
}
$edits = Get-Content -LiteralPath $resolvedEditsPath -Raw | ConvertFrom-Json -Depth 10
if ($null -eq $edits) {
throw "Edits file is empty or invalid JSON: '$resolvedEditsPath'."
}
$contentListNode = $russianDocument.Xml.SelectSingleNode('/contentList')
if ($null -eq $contentListNode) {
throw "Target russian.xml does not contain '/contentList': '$($russianDocument.Path)'."
}
$nodeMap = Get-ContentNodeMap -Xml $russianDocument.Xml
$updatedEntries = New-Object System.Collections.Generic.List[string]
$addedEntries = New-Object System.Collections.Generic.List[string]
$seenEditContentUids = [System.Collections.Generic.HashSet[string]]::new()
$updates = @()
if ($edits.PSObject.Properties.Name -contains "updates" -and $null -ne $edits.updates) {
$updates = @($edits.updates)
}
foreach ($edit in $updates) {
$contentUid = [string]$edit.contentuid
if ([string]::IsNullOrWhiteSpace($contentUid)) {
throw "Each update entry must contain non-empty 'contentuid'."
}
Assert-UniqueEditContentUid -Seen $seenEditContentUids -ContentUid $contentUid -Section "updates"
if (-not $nodeMap.ContainsKey($contentUid)) {
throw "Target russian.xml does not contain contentuid '$contentUid' for update."
}
$node = $nodeMap[$contentUid]
if ($edit.PSObject.Properties.Name -contains "version" -and -not [string]::IsNullOrWhiteSpace([string]$edit.version)) {
$node.SetAttribute("version", [string]$edit.version)
}
if ($edit.PSObject.Properties.Name -contains "text") {
if ([string]::IsNullOrWhiteSpace([string]$edit.text)) {
throw "Update entry '$contentUid' must contain non-empty 'text' when provided."
}
$node.InnerText = [string]$edit.text
}
$updatedEntries.Add($contentUid) | Out-Null
}
$adds = @()
if ($edits.PSObject.Properties.Name -contains "adds" -and $null -ne $edits.adds) {
$adds = @($edits.adds)
}
if (($updates.Count -eq 0) -and ($adds.Count -eq 0)) {
if (Test-Path -LiteralPath $temporaryRussianPath) {
Remove-Item -LiteralPath $temporaryRussianPath -Force
}
Write-Host "[apply-translation-edits.ps1] No edits requested. Original russian.xml left unchanged."
return
}
foreach ($edit in $adds) {
$contentUid = [string]$edit.contentuid
$version = [string]$edit.version
$text = [string]$edit.text
if ([string]::IsNullOrWhiteSpace($contentUid)) {
throw "Each add entry must contain non-empty 'contentuid'."
}
Assert-UniqueEditContentUid -Seen $seenEditContentUids -ContentUid $contentUid -Section "adds"
if ($nodeMap.ContainsKey($contentUid)) {
throw "Target russian.xml already contains contentuid '$contentUid'; use 'updates' instead of 'adds'."
}
if ([string]::IsNullOrWhiteSpace($version)) {
throw "Add entry '$contentUid' must contain non-empty 'version'."
}
if ([string]::IsNullOrWhiteSpace($text)) {
throw "Add entry '$contentUid' must contain non-empty 'text'."
}
$newNode = $russianDocument.Xml.CreateElement("content")
$newNode.SetAttribute("contentuid", $contentUid)
$newNode.SetAttribute("version", $version)
$newNode.InnerText = $text
[void]$contentListNode.AppendChild($newNode)
$nodeMap[$contentUid] = $newNode
$addedEntries.Add($contentUid) | Out-Null
}
$settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = New-Object System.Text.UTF8Encoding($true)
$settings.Indent = $true
$settings.IndentChars = " "
$settings.NewLineChars = "`n"
$settings.NewLineHandling = [System.Xml.NewLineHandling]::Replace
$writer = [System.Xml.XmlWriter]::Create($russianDocument.Path, $settings)
try {
$russianDocument.Xml.WriteTo($writer)
} finally {
$writer.Dispose()
}
try {
& $validateScriptPath -XmlPath $temporaryRussianPath
Move-Item -LiteralPath $temporaryRussianPath -Destination $RussianPath -Force
} finally {
if (Test-Path -LiteralPath $temporaryRussianPath) {
Remove-Item -LiteralPath $temporaryRussianPath -Force
}
}
Write-Host "[apply-translation-edits.ps1] Updated entries: $($updatedEntries.Count); Added entries: $($addedEntries.Count)."
if ($updatedEntries.Count -gt 0) {
Write-Host "[apply-translation-edits.ps1] Updated contentuid: $($updatedEntries -join ', ')"
}
if ($addedEntries.Count -gt 0) {
Write-Host "[apply-translation-edits.ps1] Added contentuid: $($addedEntries -join ', ')"
}

View File

@@ -7,8 +7,8 @@ param(
[string]$ArchiveBaseName = "DnD 5.5e AIO Russian", [string]$ArchiveBaseName = "DnD 5.5e AIO Russian",
[string]$ModName = "DnD 5.5e All-in-One BEYOND Russian Localization", [string]$ModName = "DnD 5.5e All-in-One BEYOND Russian Localization",
[string]$ModUuid = "6401e84d-daf2-416d-adeb-99c03a2487a6", [string]$ModUuid = "6401e84d-daf2-416d-adeb-99c03a2487a6",
[string]$ModAuthor = "MikhailRaw", [string]$ModAuthor = "Underslumber Team",
[string]$ModDescription = "Russian Localization", [string]$ModDescription = "Русская локализация мода, который добавляет и обновляет контент в соответствии с правилами DnD 5.5e и другими источниками, включая предыстории, классы, таланты, расы, заклинания и многое другое. Это отдельный мод локализации и он требует установленный оригинальный мод.",
[string]$ModVersion64 = "36028797018963968", [string]$ModVersion64 = "36028797018963968",
[string]$ModGroup = "6401e84d-daf2-416d-adeb-99c03a2487a6", [string]$ModGroup = "6401e84d-daf2-416d-adeb-99c03a2487a6",
[string]$DependencyUuid = "897914ef-5c96-053c-44af-0be823f895fe", [string]$DependencyUuid = "897914ef-5c96-053c-44af-0be823f895fe",
@@ -103,7 +103,8 @@ if (-not (Test-Path -LiteralPath $stagedMetaPath)) {
$stagedMetaContent = Get-Content -LiteralPath $stagedMetaPath -Raw $stagedMetaContent = Get-Content -LiteralPath $stagedMetaPath -Raw
$stagedMetaContent = $stagedMetaContent -replace '(<attribute id="Version64" type="int64" value=")\d+("/>)', "`${1}$resolvedVersion64`${2}" $stagedMetaContent = $stagedMetaContent -replace '(<attribute id="Version64" type="int64" value=")\d+("/>)', "`${1}$resolvedVersion64`${2}"
Set-Content -LiteralPath $stagedMetaPath -Value $stagedMetaContent -Encoding utf8 $utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($stagedMetaPath, $stagedMetaContent, $utf8Bom)
Write-Host "[build.ps1] Staged source tree:" Write-Host "[build.ps1] Staged source tree:"
Get-ChildItem -Recurse $stagingPath | Select-Object FullName, Length | Format-Table -AutoSize Get-ChildItem -Recurse $stagingPath | Select-Object FullName, Length | Format-Table -AutoSize
@@ -112,6 +113,8 @@ if (Test-Path -LiteralPath $tempPackagePath) {
Remove-Item -LiteralPath $tempPackagePath -Force Remove-Item -LiteralPath $tempPackagePath -Force
} }
# CI quirk: Divine can occasionally emit a broken ~48-byte package for some source roots.
# Mitigation: try staged/mods/workspace sources and accept only outputs that look valid by size.
$packageAttempts = @( $packageAttempts = @(
[ordered]@{ Name = "staging-root"; Source = $stagingPath }, [ordered]@{ Name = "staging-root"; Source = $stagingPath },
[ordered]@{ Name = "mods-root"; Source = $modsPath }, [ordered]@{ Name = "mods-root"; Source = $modsPath },

View File

@@ -0,0 +1,209 @@
param(
[string]$EnglishPath = ".cache/upstream/english.xml",
[string]$RussianPath = "Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml",
[string]$OutputDir = "build/translation-diff"
)
$ErrorActionPreference = "Stop"
function Get-LocalizationEntries {
param(
[Parameter(Mandatory = $true)]
[string]$Path
)
$resolvedPath = [System.IO.Path]::GetFullPath($Path)
if (-not (Test-Path -LiteralPath $resolvedPath)) {
throw "Localization XML was not found: '$resolvedPath'."
}
[xml]$xml = Get-Content -LiteralPath $resolvedPath -Raw
$nodes = $xml.SelectNodes('/contentList/content')
if ($null -eq $nodes) {
throw "Localization XML does not contain '/contentList/content' nodes: '$resolvedPath'."
}
$entries = @{}
foreach ($node in $nodes) {
$contentUid = [string]$node.GetAttribute("contentuid")
if ([string]::IsNullOrWhiteSpace($contentUid)) {
throw "Localization XML contains a content node without 'contentuid': '$resolvedPath'."
}
if ($entries.ContainsKey($contentUid)) {
throw "Localization XML contains duplicate contentuid '$contentUid': '$resolvedPath'."
}
$version = [string]$node.GetAttribute("version")
if ([string]::IsNullOrWhiteSpace($version)) {
throw "Localization XML contains contentuid '$contentUid' with empty 'version': '$resolvedPath'."
}
$entries[$contentUid] = [ordered]@{
contentuid = $contentUid
version = $version
text = [string]$node.InnerText
}
}
return $entries
}
$resolvedOutputDir = [System.IO.Path]::GetFullPath($OutputDir)
New-Item -ItemType Directory -Path $resolvedOutputDir -Force | Out-Null
$englishEntries = Get-LocalizationEntries -Path $EnglishPath
$russianEntries = Get-LocalizationEntries -Path $RussianPath
$missingInRussian = New-Object System.Collections.Generic.List[object]
$versionMismatch = New-Object System.Collections.Generic.List[object]
$staleOnlyInRussian = New-Object System.Collections.Generic.List[object]
foreach ($contentUid in ($englishEntries.Keys | Sort-Object)) {
$englishEntry = $englishEntries[$contentUid]
if (-not $russianEntries.ContainsKey($contentUid)) {
$missingInRussian.Add([pscustomobject]@{
contentuid = $contentUid
englishVersion = $englishEntry.version
englishText = $englishEntry.text
}) | Out-Null
continue
}
$russianEntry = $russianEntries[$contentUid]
if ($englishEntry.version -ne $russianEntry.version) {
$versionMismatch.Add([pscustomobject]@{
contentuid = $contentUid
englishVersion = $englishEntry.version
russianVersion = $russianEntry.version
englishText = $englishEntry.text
russianText = $russianEntry.text
}) | Out-Null
}
}
foreach ($contentUid in ($russianEntries.Keys | Sort-Object)) {
if (-not $englishEntries.ContainsKey($contentUid)) {
$russianEntry = $russianEntries[$contentUid]
$staleOnlyInRussian.Add([pscustomobject]@{
contentuid = $contentUid
russianVersion = $russianEntry.version
russianText = $russianEntry.text
}) | Out-Null
}
}
$summary = [ordered]@{
generatedAt = (Get-Date).ToString("o")
englishPath = [System.IO.Path]::GetFullPath($EnglishPath)
russianPath = [System.IO.Path]::GetFullPath($RussianPath)
englishCount = $englishEntries.Count
russianCount = $russianEntries.Count
missingInRussianCount = $missingInRussian.Count
versionMismatchCount = $versionMismatch.Count
staleOnlyInRussianCount = $staleOnlyInRussian.Count
missingInRussian = $missingInRussian
versionMismatch = $versionMismatch
staleOnlyInRussian = $staleOnlyInRussian
}
$summaryJsonPath = Join-Path $resolvedOutputDir "summary.json"
$summaryMdPath = Join-Path $resolvedOutputDir "summary.md"
$candidatesJsonPath = Join-Path $resolvedOutputDir "candidates.json"
$summary | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $summaryJsonPath -Encoding utf8
$candidates = [ordered]@{
generatedAt = (Get-Date).ToString("o")
source = [ordered]@{
englishPath = [System.IO.Path]::GetFullPath($EnglishPath)
russianPath = [System.IO.Path]::GetFullPath($RussianPath)
}
updates = @(
$versionMismatch | ForEach-Object {
[ordered]@{
contentuid = $_.contentuid
version = $_.englishVersion
text = $_.russianText
englishText = $_.englishText
russianVersion = $_.russianVersion
}
}
)
adds = @(
$missingInRussian | ForEach-Object {
[ordered]@{
contentuid = $_.contentuid
version = $_.englishVersion
text = ""
englishText = $_.englishText
}
}
)
}
$candidates | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $candidatesJsonPath -Encoding utf8
$isUpToDate = ($missingInRussian.Count -eq 0) -and ($versionMismatch.Count -eq 0) -and ($staleOnlyInRussian.Count -eq 0)
$mdLines = @(
"# Translation diff summary",
"",
"- Generated: $($summary.generatedAt)",
"- English entries: $($summary.englishCount)",
"- Russian entries: $($summary.russianCount)",
"- Missing in Russian: $($summary.missingInRussianCount)",
"- Version mismatches: $($summary.versionMismatchCount)",
"- Stale only in Russian: $($summary.staleOnlyInRussianCount)",
""
)
if ($isUpToDate) {
$mdLines += "Перевод уже актуален, дополнительные действия не требуются."
} else {
$mdLines += ""
$mdLines += "## Agent workflow"
$mdLines += "1. Refresh upstream cache: ``scripts/get-upstream-english.ps1``"
$mdLines += "2. Refresh diff reports: ``scripts/compare-translation.ps1``"
$mdLines += "3. Fill translated texts in ``build/translation-diff/candidates.json``"
$mdLines += "4. Apply only prepared edits: ``scripts/apply-translation-edits.ps1 -EditsPath build/translation-diff/candidates.json``"
}
$mdLines += ""
$mdLines += "## Missing in Russian"
if ($missingInRussian.Count -eq 0) {
$mdLines += "- none"
} else {
$mdLines += ($missingInRussian | Select-Object -First 50 | ForEach-Object {
"- ``$($_.contentuid)`` v$($_.englishVersion): $($_.englishText)"
})
}
$mdLines += ""
$mdLines += "## Version mismatches"
if ($versionMismatch.Count -eq 0) {
$mdLines += "- none"
} else {
$mdLines += ($versionMismatch | Select-Object -First 50 | ForEach-Object {
"- ``$($_.contentuid)`` en=v$($_.englishVersion), ru=v$($_.russianVersion)"
})
}
$mdLines += ""
$mdLines += "## Stale only in Russian"
if ($staleOnlyInRussian.Count -eq 0) {
$mdLines += "- none"
} else {
$mdLines += ($staleOnlyInRussian | Select-Object -First 50 | ForEach-Object {
"- ``$($_.contentuid)`` v$($_.russianVersion): $($_.russianText)"
})
}
Set-Content -LiteralPath $summaryMdPath -Value $mdLines -Encoding utf8
Write-Host "[compare-translation.ps1] Summary written to '$summaryJsonPath' and '$summaryMdPath'."
Write-Host "[compare-translation.ps1] Editable candidate file written to '$candidatesJsonPath'."
Write-Host "[compare-translation.ps1] Missing=$($missingInRussian.Count); VersionMismatch=$($versionMismatch.Count); StaleOnlyInRussian=$($staleOnlyInRussian.Count)."
if ($isUpToDate) {
Write-Host "[compare-translation.ps1] Перевод уже актуален, дополнительные действия не требуются."
}

View File

@@ -0,0 +1,44 @@
param(
[string]$UpstreamEnglishUrl = "https://raw.githubusercontent.com/Yoonmoonsik/dnd55e/main/Mods/DnD2024_897914ef-5c96-053c-44af-0be823f895fe/Localization/English/english.xml",
[string]$OutputPath = ".cache/upstream/english.xml",
[switch]$Force
)
$ErrorActionPreference = "Stop"
if ([string]::IsNullOrWhiteSpace($UpstreamEnglishUrl)) {
throw "UpstreamEnglishUrl must not be empty."
}
$resolvedOutputPath = [System.IO.Path]::GetFullPath($OutputPath)
$outputDirectory = Split-Path -Parent $resolvedOutputPath
if (-not (Test-Path -LiteralPath $outputDirectory)) {
New-Item -ItemType Directory -Path $outputDirectory -Force | Out-Null
}
$requestParameters = @{
Uri = $UpstreamEnglishUrl
OutFile = $resolvedOutputPath
UseBasicParsing = $true
TimeoutSec = 120
}
if ($Force) {
$requestParameters["Headers"] = @{
"Cache-Control" = "no-cache"
}
}
Invoke-WebRequest @requestParameters
[xml]$englishXml = Get-Content -LiteralPath $resolvedOutputPath -Raw
$rootNode = $englishXml.SelectSingleNode('/contentList')
if ($null -eq $rootNode) {
throw "Downloaded file is not a valid BG3 localization XML: '$resolvedOutputPath'."
}
$contentCount = $englishXml.SelectNodes('/contentList/content').Count
$fileInfo = Get-Item -LiteralPath $resolvedOutputPath
Write-Host "[get-upstream-english.ps1] Saved upstream english.xml to '$resolvedOutputPath'."
Write-Host "[get-upstream-english.ps1] Entries: $contentCount; Size: $($fileInfo.Length) bytes; Updated: $($fileInfo.LastWriteTime.ToString("o"))."

63
scripts/set-version.ps1 Normal file
View File

@@ -0,0 +1,63 @@
param(
[Parameter(Mandatory = $true)]
[string]$VersionTag,
[string]$MetaPath = "Mods/DnD 5.5e AIO Russian/meta.lsx"
)
$ErrorActionPreference = "Stop"
function Convert-VersionTagToVersion64 {
param(
[string]$Tag
)
$normalized = $Tag
if ($normalized.StartsWith("v")) {
$normalized = $normalized.Substring(1)
}
if ($normalized -notmatch '^\d+(\.\d+){0,3}$') {
throw "Version tag '$Tag' is invalid. Expected format: vX.Y.Z or X.Y.Z"
}
$parts = $normalized.Split(".")
$numbers = @(0, 0, 0, 0)
for ($i = 0; $i -lt $parts.Length; $i++) {
$numbers[$i] = [int]$parts[$i]
}
return ([int64]$numbers[0] -shl 55) -bor ([int64]$numbers[1] -shl 47) -bor ([int64]$numbers[2] -shl 31) -bor [int64]$numbers[3]
}
$resolvedMetaPath = [System.IO.Path]::GetFullPath($MetaPath)
if (-not (Test-Path -LiteralPath $resolvedMetaPath)) {
throw "meta.lsx was not found: '$resolvedMetaPath'."
}
$resolvedVersion64 = Convert-VersionTagToVersion64 -Tag $VersionTag
$metaContent = Get-Content -LiteralPath $resolvedMetaPath -Raw
[xml]$metaXml = $metaContent
# Explicitly target ModuleInfo/Version64 via XML path to avoid touching Dependencies/PublishVersion.
$moduleInfoVersionNode = $metaXml.SelectSingleNode('/save/region/node/children/node[@id="ModuleInfo"]/attribute[@id="Version64" and @type="int64"]')
if ($null -eq $moduleInfoVersionNode) {
throw "ModuleInfo/Version64 attribute was not found in '$resolvedMetaPath'."
}
# Replace only the Version64 attribute that appears inside ModuleInfo before its <children> block.
$moduleInfoVersionPattern = '(?s)(<node id="ModuleInfo">\s*(?:(?!<children>).)*?<attribute id="Version64" type="int64" value=")\d+("/>)'
if ($metaContent -notmatch $moduleInfoVersionPattern) {
throw "ModuleInfo/Version64 attribute was not found in '$resolvedMetaPath'."
}
$updatedMeta = [regex]::Replace(
$metaContent,
$moduleInfoVersionPattern,
"`${1}$resolvedVersion64`${2}",
1
)
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
[System.IO.File]::WriteAllText($resolvedMetaPath, $updatedMeta, $utf8Bom)
Write-Host "[set-version.ps1] Updated '$resolvedMetaPath' to Version64=$resolvedVersion64 (from tag '$VersionTag')."

View File

@@ -0,0 +1,91 @@
param(
[string]$ParentMetaUrl = "https://raw.githubusercontent.com/Yoonmoonsik/dnd55e/main/Mods/DnD2024_897914ef-5c96-053c-44af-0be823f895fe/meta.lsx",
[string]$TargetMetaPath = "Mods/DnD 5.5e AIO Russian/meta.lsx"
)
$ErrorActionPreference = "Stop"
$resolvedTargetMetaPath = [System.IO.Path]::GetFullPath($TargetMetaPath)
if (-not (Test-Path -LiteralPath $resolvedTargetMetaPath)) {
throw "Target meta.lsx was not found: '$resolvedTargetMetaPath'."
}
if ([string]::IsNullOrWhiteSpace($ParentMetaUrl)) {
throw "ParentMetaUrl must not be empty."
}
try {
$parentResponse = Invoke-WebRequest -Uri $ParentMetaUrl -UseBasicParsing -TimeoutSec 60
} catch {
throw "Failed to download parent meta.lsx from '$ParentMetaUrl': $($_.Exception.Message)"
}
$parentRaw = $parentResponse.Content
$targetRaw = Get-Content -LiteralPath $resolvedTargetMetaPath -Raw
[xml]$parentXml = $parentRaw
[xml]$targetXml = $targetRaw
$parentModuleInfo = $parentXml.SelectSingleNode('/save/region/node/children/node[@id="ModuleInfo"]')
if ($null -eq $parentModuleInfo) {
throw "ModuleInfo node was not found in parent meta downloaded from '$ParentMetaUrl'."
}
$requiredFields = @("Folder", "MD5", "Name", "PublishHandle", "UUID", "Version64")
$sourceValues = @{}
foreach ($field in $requiredFields) {
$node = $parentModuleInfo.SelectSingleNode("attribute[@id='$field']")
if ($null -eq $node) {
throw "Required parent ModuleInfo attribute '$field' is missing in meta downloaded from '$ParentMetaUrl'."
}
$value = $node.GetAttribute("value")
if ([string]::IsNullOrWhiteSpace($value)) {
throw "Required parent ModuleInfo attribute '$field' has empty value in meta downloaded from '$ParentMetaUrl'."
}
$sourceValues[$field] = $value
}
$targetDependencyNode = $targetXml.SelectSingleNode('/save/region/node/children/node[@id="Dependencies"]/children/node[@id="ModuleShortDesc"]')
if ($null -eq $targetDependencyNode) {
throw "Dependencies/ModuleShortDesc node was not found in target meta: '$resolvedTargetMetaPath'."
}
$changedFields = @()
foreach ($field in $requiredFields) {
$targetAttr = $targetDependencyNode.SelectSingleNode("attribute[@id='$field']")
if ($null -eq $targetAttr) {
throw "Target Dependencies/ModuleShortDesc attribute '$field' is missing in '$resolvedTargetMetaPath'."
}
$currentValue = $targetAttr.GetAttribute("value")
$newValue = [string]$sourceValues[$field]
if ($currentValue -ne $newValue) {
$targetAttr.SetAttribute("value", $newValue)
$changedFields += $field
}
}
if ($changedFields.Count -eq 0) {
Write-Host "[sync-parent-meta.ps1] No changes needed. Target dependency data is already up to date."
} else {
$utf8Bom = New-Object System.Text.UTF8Encoding($true)
$settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = $utf8Bom
$settings.Indent = $true
$settings.IndentChars = " "
$settings.NewLineChars = "`n"
$settings.NewLineHandling = [System.Xml.NewLineHandling]::Replace
$writer = [System.Xml.XmlWriter]::Create($resolvedTargetMetaPath, $settings)
try {
$targetXml.WriteTo($writer)
} finally {
$writer.Dispose()
}
Write-Host ("[sync-parent-meta.ps1] Updated fields: " + ($changedFields -join ", "))
}

View File

@@ -0,0 +1,71 @@
param(
[string]$RussianPath = "Mods/DnD 5.5e AIO Russian/Localization/Russian/russian.xml",
[string]$EnglishPath = ".cache/upstream/english.xml",
[string]$OutputDir = "build/translation-diff",
[string]$EditsPath = ""
)
$ErrorActionPreference = "Stop"
$getUpstreamScriptPath = Join-Path $PSScriptRoot "get-upstream-english.ps1"
$compareScriptPath = Join-Path $PSScriptRoot "compare-translation.ps1"
$applyScriptPath = Join-Path $PSScriptRoot "apply-translation-edits.ps1"
foreach ($scriptPath in @($getUpstreamScriptPath, $compareScriptPath, $applyScriptPath)) {
if (-not (Test-Path -LiteralPath $scriptPath)) {
throw "Required script was not found: '$scriptPath'."
}
}
$resolvedOutputDir = [System.IO.Path]::GetFullPath($OutputDir)
$resolvedProvidedEditsPath = ""
if (-not [string]::IsNullOrWhiteSpace($EditsPath)) {
$resolvedProvidedEditsPath = [System.IO.Path]::GetFullPath($EditsPath)
}
$workingDiffDir = Join-Path $env:TEMP ("bg3-translation-update-" + [guid]::NewGuid().ToString("N"))
New-Item -ItemType Directory -Path $workingDiffDir -Force | Out-Null
try {
& $getUpstreamScriptPath -OutputPath $EnglishPath -Force
& $compareScriptPath -EnglishPath $EnglishPath -RussianPath $RussianPath -OutputDir $workingDiffDir
New-Item -ItemType Directory -Path $resolvedOutputDir -Force | Out-Null
Get-ChildItem -LiteralPath $workingDiffDir | ForEach-Object {
$destinationPath = Join-Path $resolvedOutputDir $_.Name
if ($resolvedProvidedEditsPath -and ([System.IO.Path]::GetFullPath($destinationPath) -eq $resolvedProvidedEditsPath) -and (Test-Path -LiteralPath $resolvedProvidedEditsPath)) {
return
}
Copy-Item -LiteralPath $_.FullName -Destination $resolvedOutputDir -Recurse -Force
}
$summaryJsonPath = Join-Path $workingDiffDir "summary.json"
if (-not (Test-Path -LiteralPath $summaryJsonPath)) {
throw "Translation summary was not found: '$summaryJsonPath'."
}
$summary = Get-Content -LiteralPath $summaryJsonPath -Raw | ConvertFrom-Json -Depth 10
$hasDiff = ($summary.missingInRussianCount -gt 0) -or ($summary.versionMismatchCount -gt 0) -or ($summary.staleOnlyInRussianCount -gt 0)
if (-not $hasDiff) {
Write-Host "[update-translation.ps1] Перевод уже актуален, дополнительные действия не требуются."
return
}
if ([string]::IsNullOrWhiteSpace($EditsPath)) {
Write-Host "[update-translation.ps1] Найдены изменения перевода. Подготовьте правки в '$([System.IO.Path]::GetFullPath((Join-Path $resolvedOutputDir "candidates.json")))' и затем запустите повторно с '-EditsPath'."
return
}
$effectiveEditsPath = [System.IO.Path]::GetFullPath($EditsPath)
if (-not (Test-Path -LiteralPath $effectiveEditsPath)) {
throw "Prepared edits file was not found: '$effectiveEditsPath'."
}
& $applyScriptPath -RussianPath $RussianPath -EditsPath $effectiveEditsPath
Write-Host "[update-translation.ps1] Обновление перевода завершено. Результат записан в '$([System.IO.Path]::GetFullPath($RussianPath))'."
} finally {
if (Test-Path -LiteralPath $workingDiffDir) {
Remove-Item -LiteralPath $workingDiffDir -Recurse -Force
}
}

View File

@@ -0,0 +1,41 @@
param(
[Parameter(Mandatory = $true)]
[string]$XmlPath
)
$ErrorActionPreference = "Stop"
$resolvedXmlPath = [System.IO.Path]::GetFullPath($XmlPath)
if (-not (Test-Path -LiteralPath $resolvedXmlPath)) {
throw "XML file was not found: '$resolvedXmlPath'."
}
[xml]$xml = Get-Content -LiteralPath $resolvedXmlPath -Raw
$contentListNode = $xml.SelectSingleNode('/contentList')
if ($null -eq $contentListNode) {
throw "XML validation failed: missing '/contentList' in '$resolvedXmlPath'."
}
$contentNodes = $xml.SelectNodes('/contentList/content')
if ($null -eq $contentNodes -or $contentNodes.Count -lt 1) {
throw "XML validation failed: no '/contentList/content' entries found in '$resolvedXmlPath'."
}
$seen = [System.Collections.Generic.HashSet[string]]::new()
foreach ($node in $contentNodes) {
$contentUid = [string]$node.GetAttribute("contentuid")
if ([string]::IsNullOrWhiteSpace($contentUid)) {
throw "XML validation failed: found content node without 'contentuid' in '$resolvedXmlPath'."
}
if (-not $seen.Add($contentUid)) {
throw "XML validation failed: duplicate contentuid '$contentUid' in '$resolvedXmlPath'."
}
$version = [string]$node.GetAttribute("version")
if ([string]::IsNullOrWhiteSpace($version)) {
throw "XML validation failed: contentuid '$contentUid' has empty 'version' in '$resolvedXmlPath'."
}
}
Write-Host "[validate-translation-xml.ps1] XML is valid: '$resolvedXmlPath'. Entries=$($contentNodes.Count)."