Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 19 additions & 13 deletions .claude/skills/translate-page/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ exist because each of them was broken once and cost real work.
## Inputs

- A page path under `docs/en/`, or a page reported by
`uv run python scripts/translation_status.py` as `missing` or `stale`.
`uv run translation-status` as `missing` or `stale`.
- A target language directory, e.g. `docs/fi/`.

## Before translating
Expand Down Expand Up @@ -79,7 +79,7 @@ The stamp records the git blob hash of the English source the translation was
written against. Write it with the helper, never by hand:

```bash
uv run python scripts/stamp_translation.py docs/fi/user-guide/hardware.md
uv run stamp-translation docs/fi/user-guide/hardware.md
```

**Stamp only when you have actually translated.** A stamp updated without real
Expand All @@ -90,6 +90,12 @@ fixing a typo), the English source did not change: leave the stamp alone.

## Adding a language to the site

`check-glossary` and `check-typography` accept a fixed set of locales, and those
registries live in the `halos-docs-tools` package, not in this repository. A new
locale needs an entry in each, a release of that package, and a bump of the pin
in `pyproject.toml`. Until that lands both commands reject the locale, while
`translation-status` reads `mkdocs.yml` and starts failing the gate immediately.

When a locale is added to `mkdocs.yml`, check the language selector too. The
Material theme caps the open menu at `10rem`, which fits five entries at the
site's font size; the sixth language onward scrolls out of sight behind a
Expand All @@ -105,9 +111,9 @@ scrollbar that gives no hint anything is below it.
```

24rem clears thirteen entries; the viewport term keeps the menu on screen on a
short display. The same block is in the HALPI2 and HALMET repositories — keep
the three identical, and add it to any further site that gains a second
language.
short display. The same block is in the HALPI2, HALMET, SH-RPi and SH-ESP32
repositories — keep the four identical, and add it to any further site
that gains a second language.

Verify by measuring rather than by eye: open the site, read the rule's
`max-height` off the stylesheet, and compare it against the list's natural
Expand All @@ -117,22 +123,22 @@ is captured.

## Verifying

All four, every time:
All five, every time:

```bash
uv run mkdocs build --strict
uv run python scripts/check_anchors.py site
uv run python scripts/translation_status.py
uv run python scripts/check_glossary.py fi
uv run python scripts/check_typography.py fi
uv run check-anchors site
uv run translation-status --check
uv run check-glossary fi
uv run check-typography fi
```

**Leave every anchor fragment in its English form while translating**, then map
them all at once once the language is complete and the site has been built:

```bash
uv run python scripts/map_anchors.py site fi # report
uv run python scripts/map_anchors.py site fi --apply # rewrite
uv run map-anchors site fi # report
uv run map-anchors site fi --apply # rewrite
```

The mapping is positional — the nth heading of the English page and the nth
Expand All @@ -144,7 +150,7 @@ the text is in another language.
whatever they already say, so the terminology looks consistent right up until a
reviewer finds the same connector under two names on adjacent pages. Every
language so far shipped that mistake, and each time it landed on the last pages
translated, once the glossary had stopped being opened. `check_glossary.py`
translated, once the glossary had stopped being opened. `check-glossary`
reports terms the glossary prescribes and the pages never use — the signature of
a rival word having quietly taken over.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- run: uv sync
- run: uv sync --locked
- run: uv run mkdocs build --strict
- uses: actions/upload-pages-artifact@v3
with:
Expand Down
109 changes: 16 additions & 93 deletions .github/workflows/translation-status.yml
Original file line number Diff line number Diff line change
@@ -1,109 +1,32 @@
name: Translation Status

# No paths filter. The gate is a property of the whole repository, not of a
# diff, and a required check that never runs on a pull request touching none of
# the filtered paths leaves that pull request unmergeable forever.
on:
pull_request:
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'scripts/**'
push:
branches: [main]
workflow_dispatch:

# The called workflow inherits this token, so the comment needs
# pull-requests: write here. Omit it and the run still gates; only the comment
# is skipped.
permissions:
contents: read
pull-requests: write

concurrency:
group: translation-status-${{ github.ref }}
cancel-in-progress: true
# Pull requests only. On push, github.ref is refs/heads/main for every merge,
# so cancelling lets one merge kill the run checking the one before it -- and
# a cancelled run is grey, not red, so nobody is told.
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
status:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
# Full history: the report resolves the stamped blob to show the
# English diff since a translation was written.
fetch-depth: 0

- uses: astral-sh/setup-uv@v5
- run: uv sync

- name: Report translation status
run: |
# tee, not plain redirection: a report only in the job summary is
# invisible in the logs, which is where you look when it misbehaves.
uv run python scripts/translation_status.py --format markdown --diff \
| tee report.md
cat report.md >> "$GITHUB_STEP_SUMMARY"

- name: Comment on the pull request
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ github.token }}
PR: ${{ github.event.number }}
run: |
# Only the English pages this PR actually touches. Which paths a PR
# touched is a fact, so a PR editing only translations says nothing.
pages=$(git diff --name-only \
"origin/${{ github.base_ref }}...HEAD" -- 'docs/en/**/*.md' \
| sed 's|^docs/en/||')
if [ -z "$pages" ]; then
echo "No English pages touched; nothing to report."
exit 0
fi

# shellcheck disable=SC2086
uv run python scripts/translation_status.py \
--format markdown --diff --only-pages $pages > comment.md

# GitHub rejects comment bodies over 65536 characters (HTTP 422).
# Wide PRs produce reports far beyond that; fall back to the
# summary without diffs and point at the job summary instead.
if [ "$(wc -c < comment.md)" -gt 60000 ]; then
# shellcheck disable=SC2086
uv run python scripts/translation_status.py \
--format markdown --only-pages $pages > comment.md
{
echo ""
echo "_Diffs omitted: the full report exceeds GitHub's comment size limit._"
echo "_See the workflow run's job summary for the complete report._"
} >> comment.md
fi
printf '\n<!-- translation-status -->\n' >> comment.md

existing=$(gh api "repos/${{ github.repository }}/issues/$PR/comments" \
--jq 'map(select(.body | contains("<!-- translation-status -->"))) | .[0].id // empty')
if [ -n "$existing" ]; then
gh api "repos/${{ github.repository }}/issues/comments/$existing" \
-X PATCH -F body=@comment.md --silent
echo "Updated comment $existing"
else
gh api "repos/${{ github.repository }}/issues/$PR/comments" \
-F body=@comment.md --silent
echo "Created comment"
fi

# Last, because unlike a stale translation a broken anchor is actual
# breakage and fails the run — and the report above must still be
# published when it does.
- name: Check anchors
run: |
uv run mkdocs build --strict
# PIPESTATUS, not $?: piping into tee would otherwise mask the
# checker's exit status behind tee's.
set +e
uv run python scripts/check_anchors.py site | tee anchors.txt
broken=${PIPESTATUS[0]}
set -e
{
echo ""
echo "## Anchor check"
echo ""
echo '```'
cat anchors.txt
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
exit "$broken"
# The called workflow builds the site, checks its anchors, and fails the run
# when any translation is stale, missing, unstamped or orphaned. It judges the
# whole repository, not the diff, so an edit to an English page needs its
# translations re-stamped in the same pull request.
translation-status:
uses: halos-org/shared-workflows/.github/workflows/translation-status.yml@main
18 changes: 17 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,31 @@ This repository contains the HALPI2 User Guide documentation, built with MkDocs
- `uv run mkdocs serve` - Start local dev server (http://127.0.0.1:8000)
- `uv run mkdocs build --strict` - Build the documentation (output goes to `./site`)

**Translation checkers**, from the `halos-docs-tools` package pinned in
`pyproject.toml`.

CI runs three of them — `mkdocs build --strict`, `check-anchors site` and
`translation-status --check`. The rest are local-only; nothing enforces them.
The gate judges the whole repository as merged with `main`, so a branch that is
clean locally can still go red after `main` moves.

- `uv run translation-status` - Which translations are current, stale, missing, unstamped or orphaned. Always exits 0
- `uv run translation-status --check` - The same, exiting non-zero when any is behind. This is the gate
- `uv run stamp-translation <path>` - Record the English blob a translation was written against
- `uv run check-anchors site` - Internal links whose target anchor does not exist
- `uv run check-glossary <locale>` / `uv run check-typography <locale>` - Per-language conventions
- `uv run map-anchors site <locale>` - Report English fragments that should become translated ids; `--apply` rewrites them

## Documentation Structure

- `mkdocs.yml` - MkDocs configuration and navigation structure
- `docs/` - All markdown content organized by section:
- `docs/en/` - English content, the source every translation is written from:
- `getting-started/` - Quick start and installation guides
- `user-guide/` - System operation, hardware, interfaces, software
- `technical-reference/` - Detailed hardware specs and technical docs
- `software-development/` - Daemon, integration, Ubuntu installation
- `appendices/` - Design files, schematics, errata
- `docs/<locale>/` - Translations, one directory per locale, mirroring `docs/en/`
- `docs/stylesheets/extra.css` - Custom CSS (Hat Labs branding)
- `docs/assets/` - Logo and shared assets
- `docs/overrides/` - MkDocs Material theme overrides
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ requires-python = ">=3.11"
dependencies = [
"mkdocs-material>=9.5",
"mkdocs-static-i18n>=1.3.1",
"halos-docs-tools @ git+https://github.com/halos-org/docs-tools@v0.1.0",
]
100 changes: 0 additions & 100 deletions scripts/check_anchors.py

This file was deleted.

Loading
Loading