feat(i18n): add self-contained Russian localization - #410
Conversation
|
Important Review skippedToo many files! This PR contains 569 files, which is 269 over the limit of 300. To get a review, reduce the PR to 300 files or fewer by splitting it into smaller PRs or changing its base branch. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (569)
You can disable this status message by setting the 📝 WalkthroughWalkthroughThe PR adds Claude certification translation scopes, sharded publication, Russian translation quality audits, expanded Russian README localization, and runtime support for translated certification lessons with English fallbacks. ChangesInternationalization pipeline
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.github/workflows/translate.yml (1)
261-264: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winHandle never-created paths separately.
git add -f -- "$SLICE" "$CACHE"fails when a path matches neither the worktree nor the index, so a legitimate no-op fails the job. Stage each path only when it exists or has tracked entries.git add -falready stages deletions under a tracked$SLICE; do not add-A.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/translate.yml around lines 261 - 264, Update the staging logic surrounding the shown publish-copy commands to handle never-created $SLICE and $CACHE paths separately: invoke git add -f for each path only when it exists in the worktree or has tracked index entries, while retaining deletion staging for tracked $SLICE paths and not adding -A.
🧹 Nitpick comments (6)
scripts/tests/test_audit_ru_translations.py (1)
63-70: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueStart the audit with
sys.executable.
"python3"resolves through PATH, so the subprocess can run a different interpreter than the test runner.sys.executablekeeps both on the same interpreter and also removes the Ruff S607 partial-path finding.♻️ Proposed refactor
+import sys + def run_audit(self, root: Path, *args: str) -> subprocess.CompletedProcess[str]: return subprocess.run( - ["python3", str(SCRIPT), "--root", str(root), *args], + [sys.executable, str(SCRIPT), "--root", str(root), *args], text=True,🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/tests/test_audit_ru_translations.py` around lines 63 - 70, Update the run_audit method to invoke SCRIPT using sys.executable instead of the hardcoded "python3" command, preserving the existing arguments and subprocess behavior.Source: Linters/SAST tools
scripts/audit_ru_translations.py (1)
33-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueReuse the module-level
FENCE_REinstead of the inline duplicate.Line 81 recompiles the same pattern that
FENCE_REalready holds, andFENCE_REhas no other use.FENCE_RE.match(candidate)behaves identically for a single line. This removes the duplication and the per-line compile lookup.♻️ Proposed refactor
- match = re.match(r"^ {0,3}(`{3,}|~{3,})([^\n]*)$", candidate) + match = FENCE_RE.match(candidate)Also applies to: 79-81
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@scripts/audit_ru_translations.py` at line 33, Update the fence parsing logic around the line-79–81 candidate check to reuse the module-level FENCE_RE via FENCE_RE.match(candidate), removing the inline re.compile call while preserving identical matching behavior.site/lesson.html (1)
3554-3556: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLocalize the quiz notice text.
The notice renders only when the page language is not English, but the text is hardcoded English. A Russian reader sees an English sentence inside translated prose. Map the notice text by language code, and keep English as the fallback.
♻️ Proposed change to localize the notice
- if (certificationLesson && document.documentElement.lang !== 'en') { - html += '<div class="quiz-language-note">Certification quizzes remain in English.</div>'; - } + if (certificationLesson && document.documentElement.lang !== 'en') { + var QUIZ_NOTES = { ru: 'Вопросы сертификации остаются на английском языке.' }; + var note = QUIZ_NOTES[document.documentElement.lang] || 'Certification quizzes remain in English.'; + html += '<div class="quiz-language-note">' + escapeHtml(note) + '</div>'; + }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@site/lesson.html` around lines 3554 - 3556, Update the certificationLesson notice block to select the quiz-language message from the page language code, providing the appropriate localized text and retaining English as the fallback. Keep the existing non-English display condition and append the selected message through the existing html construction.site/tests/runtime-i18n.test.js (1)
166-192: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMount the picker to cover the option list.
getElementByIdreturnsnullat line 177, somountnever runs. The test only exercises theinitelse-branch. The test name claims the picker accepts Russian and is not hidden, but the assertions do not check the rendered option list. The main change insite/lang-picker.jsis the certification filter inrenderListat line 96, which stays uncovered.Return a stub host element with a
classList,appendChild, andquerySelector, then assert that the list containsruand omitstr.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@site/tests/runtime-i18n.test.js` around lines 166 - 192, Update the test setup around getElementById and the DOMContentLoaded callback so the picker mounts against a stub host implementing classList, appendChild, and querySelector. Capture the rendered option list, then assert it includes the Russian option and excludes the Turkish option, exercising renderList’s certification-language filtering while preserving the existing current-language assertion.docs/i18n.md (1)
68-71: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDocument the combined local cache file.
scripts/translate_lessons.pyline 50 returnsi18n/<lang>/.translate-cache.jsonfor a core run without--phase. This section lists only the per-phase and certification cache files. Add the combined cache so a local full run is not surprising.📝 Proposed wording
Every lesson is keyed by the `sha256` of its English source. Core phase jobs use `i18n/<lang>/.cache/<phase>.json`; the Claude certification job uses -`i18n/<lang>/.cache/certifications-claude.json`. Caches are written per lesson -and **published to the `translations` branch when the job finishes**. So: +`i18n/<lang>/.cache/certifications-claude.json`. A full local core run without +`--phase` uses the single combined `i18n/<lang>/.translate-cache.json` instead. +Caches are written per lesson and **published to the `translations` branch when +the job finishes**. So:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/i18n.md` around lines 68 - 71, The cache documentation in docs/i18n.md omits the combined local cache returned by scripts/translate_lessons.py for core runs without --phase. Update the cache-file list near the phase and certification entries to include i18n/<lang>/.translate-cache.json, while preserving the existing published-cache descriptions..github/workflows/translate.yml (1)
68-93: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winDeclare
set -euo pipefailin the prepare step.The regression tests run this script with
bash -euo pipefail -c(seescripts/test_translate_workflow.pyline 209 and line 235). The workflow itself runs with the default GitHub shell, which enables-ebut not-uor-o pipefail. The tested behavior and the production behavior therefore differ.Without
pipefail, a failure offindorgrepin thePHASE_LISTpipeline is not fatal.PHASE_LISTthen becomes empty,CORE_SLICESbecomes[], and thetranslatejob is skipped instead of failing.♻️ Proposed change
run: | + set -euo pipefail # Pushes cover every publishable source. Manual runs retain the prior # core default and can explicitly select certifications/claude or all.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/translate.yml around lines 68 - 93, Add set -euo pipefail at the start of the prepare step’s multiline shell script before the SELECTED_SCOPE logic. Ensure the existing PHASE_LIST pipeline and subsequent variable references execute with strict error, unset-variable, and pipeline-failure handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@i18n/ru/README.md`:
- Around line 993-997: Replace the Unicode box-drawing diagram for the outputs/
directory in the README with a Mermaid or SVG representation showing prompts/
and skills/ (including SKILL.md), then regenerate the README while preserving
the documented structure.
In `@languages.json`:
- Around line 67-68: Before enabling ci for the nllb Russian entry in
languages.json, add and publish the required i18n/ru/.quality/manifest.json so
it is present in the translations branch and all Russian shards pass the quality
gate.
In `@scripts/audit_ru_translations.py`:
- Around line 180-189: Update the metadata checks in the translation-audit
function by removing inline code spans and Markdown link destinations from
target_prose before assigning metadata_surface. Keep visible link text and
surrounding prose intact, then run the existing English metadata label/value
checks against the cleaned first 15 lines.
In `@scripts/test_translate_workflow.py`:
- Around line 363-373: Update the invoke helper to preserve the integer status
returned directly by TRANSLATOR.main() when no SystemExit is raised, while
retaining the existing SystemExit handling for raised exits. Ensure
reviewed-target failures returning 2 are reported as nonzero instead of falling
through to code = 0.
In `@scripts/tests/test_audit_ru_translations.py`:
- Around line 167-176: Update test_target_must_be_nonempty_utf8 so each
payload’s manifest target_sha256 is replaced with that payload’s SHA-256 before
run_audit, allowing validation to reach the content checks. Assert the specific
empty-target and invalid-UTF-8 detail for the corresponding payload instead of
only checking structurally_invalid.
In `@site/content-source.js`:
- Around line 95-111: Update loadLessonDocument so translationUrl(path,
requested) is invoked inside the promise chain, ensuring synchronous
invalid-language errors are converted into rejections handled by the existing
catch fallback. Preserve the current behavior for valid translations and English
embedded or canonical content.
In `@site/lang-picker.js`:
- Around line 208-215: Update isLessonPage() to identify lesson pages from the
pathname or page filename rather than the presence of the path query parameter.
Preserve path-link handling for lesson.html while ensuring init() still calls
applyDir(current()) on index.html even when path and lang query parameters are
present.
---
Outside diff comments:
In @.github/workflows/translate.yml:
- Around line 261-264: Update the staging logic surrounding the shown
publish-copy commands to handle never-created $SLICE and $CACHE paths
separately: invoke git add -f for each path only when it exists in the worktree
or has tracked index entries, while retaining deletion staging for tracked
$SLICE paths and not adding -A.
---
Nitpick comments:
In @.github/workflows/translate.yml:
- Around line 68-93: Add set -euo pipefail at the start of the prepare step’s
multiline shell script before the SELECTED_SCOPE logic. Ensure the existing
PHASE_LIST pipeline and subsequent variable references execute with strict
error, unset-variable, and pipeline-failure handling.
In `@docs/i18n.md`:
- Around line 68-71: The cache documentation in docs/i18n.md omits the combined
local cache returned by scripts/translate_lessons.py for core runs without
--phase. Update the cache-file list near the phase and certification entries to
include i18n/<lang>/.translate-cache.json, while preserving the existing
published-cache descriptions.
In `@scripts/audit_ru_translations.py`:
- Line 33: Update the fence parsing logic around the line-79–81 candidate check
to reuse the module-level FENCE_RE via FENCE_RE.match(candidate), removing the
inline re.compile call while preserving identical matching behavior.
In `@scripts/tests/test_audit_ru_translations.py`:
- Around line 63-70: Update the run_audit method to invoke SCRIPT using
sys.executable instead of the hardcoded "python3" command, preserving the
existing arguments and subprocess behavior.
In `@site/lesson.html`:
- Around line 3554-3556: Update the certificationLesson notice block to select
the quiz-language message from the page language code, providing the appropriate
localized text and retaining English as the fallback. Keep the existing
non-English display condition and append the selected message through the
existing html construction.
In `@site/tests/runtime-i18n.test.js`:
- Around line 166-192: Update the test setup around getElementById and the
DOMContentLoaded callback so the picker mounts against a stub host implementing
classList, appendChild, and querySelector. Capture the rendered option list,
then assert it includes the Russian option and excludes the Turkish option,
exercising renderList’s certification-language filtering while preserving the
existing current-language assertion.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: a431d7d5-68ad-416b-ac35-a2b89fa74bca
📒 Files selected for processing (18)
.github/workflows/curriculum.yml.github/workflows/translate.yml.gitignoredocs/i18n.mdi18n/ru/README.mdlanguages.jsonscripts/audit_ru_translations.pyscripts/build_readme_i18n.pyscripts/readme_translations.pyscripts/test_translate_workflow.pyscripts/tests/test_audit_ru_translations.pyscripts/tests/test_readme_i18n.pyscripts/translate_lessons.pysite/build.jssite/content-source.jssite/lang-picker.jssite/lesson.htmlsite/tests/runtime-i18n.test.js
What this PR does
Adds a self-contained, production-ready Russian localization: runtime/CI/tooling plus the complete reviewed corpus of 536 translated lesson documents in this PR.
Kind of change
Included translation corpus
i18n/ru/phases/**/docs/ru.mdi18n/ru/certifications/claude/lessons/**/docs/ru.mdi18n/ru/.quality/manifest.jsonwith complete source/target hashes and structured review evidenceREADME.md,STYLE_GUIDE.md, andGLOSSARY.mdThe PR contains 557 changed files total: 536 translated documents and 21 localization/runtime/CI/quality files. Generated caches are intentionally excluded.
Runtime and quality infrastructure
translationsbranch as an incremental-publication fallback;Validation
--checkChecklist
Phase / lesson
All 503 current phase lessons and all 33 Claude certification lessons.
Notes for reviewer
The 536 translated documents, manifest, glossary, and style guide are byte-identical to the independently approved corpus. The Russian README is deterministically regenerated from the current source and has separate fail-closed tests.
Two trailing-space sequences inside fenced blocks are intentionally preserved because they are byte-identical to canonical English protected content. Removing them would violate the exact fenced-block integrity contract.