The interface speaks Slovak

969 strings, an instance default and a per-person choice, and no half-done corner:
the admin prose is translated too. Design and reasoning: LLeMbas.wiki/Translations.

KEYED BY THE ENGLISH SENTENCE

A missing entry renders the key, which is the English -- so an untranslated string
looks as it always did, an English instance is byte-for-byte 1.6.0, and a
half-finished catalogue is a half-translated page rather than a page of dotted key
names. The cost is that editing an English sentence orphans its translation
silently, which is what tests/test_translations.py asserts in both directions.

No gettext: .po -> .mo is a build step and this project does not have one.

A JINJA GLOBAL, AND THEREFORE A CONTEXTVAR

`t()` is a global for the reason `brand` already documents -- render() is bypassed
by 25 TemplateResponse calls and 8 get_template().render() calls, the latter being
the SSE frames, which have no Request at all. A global is bound once at import and
the language is per person, so the active language is a ContextVar set per request.

🚨 `get_current_user` had to become `async def`. FastAPI runs a sync dependency in a
threadpool, and anyio copies the context in and discards it on the way out -- so
the language was set where nothing could see it and every page rendered in the
instance's language whatever anybody had chosen, with no error anywhere.

NOT TRANSLATED, ON PURPOSE

Everything a model reads: the 60 prompt fragments, and the dates in harness.py,
schedule/runner.py and schedule/compile.py. Only `i18n.stamp` is localised, and
only where a person reads it -- with the *format* translatable as well as the
words, because "26. septembra 2026" is a different pattern rather than the same one
with different words in it. A process locale is not an option: global, not
thread-safe, two people's pages at once.

A second fragment telling models to answer in the reader's language was written
during this work and removed: `core.style` has said it since long before, and
test_an_empty_override_turns_a_fragment_off caught the duplicate.

THE BULK PASS

1213 sites wrapped by a one-off script that only touched patterns it could not
misread. It got three wrong in a way that mattered -- `t('…')` inside `attr="…"`
where the sentence held an apostrophe, closing the Jinja string and 500ing two
pages whose partials no test renders. tests/test_translations.py now compiles all
110 templates. It also wrapped the product's own name, an SSH key header, a
keystroke hint and an example URL, all taken back out: a string is not
translatable just because it is a string.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-26 14:46:33 +00:00
co-authored by Claude Opus 5
parent da0797ccad
commit a16510aba8
104 changed files with 3833 additions and 1788 deletions
+23 -29
View File
@@ -13,26 +13,23 @@
card follows.
#}
<div class="field">
<label class="field__label" for="schedule-title">Name</label>
<label class="field__label" for="schedule-title">{{ t("Name") }}</label>
<input class="input" id="schedule-title" name="title" required maxlength="200"
value="{{ form.title }}"
placeholder="Monday build check">
<p class="field__hint">What this appears as in the list.</p>
placeholder="{{ t('Monday build check') }}">
<p class="field__hint">{{ t("What this appears as in the list.") }}</p>
</div>
<div class="field">
<label class="field__label" for="schedule-instruction">What should happen</label>
<label class="field__label" for="schedule-instruction">{{ t("What should happen") }}</label>
<textarea class="input" id="schedule-instruction" name="instruction" rows="5" required
placeholder="Check whether the build is passing and summarise anything that broke since last week."
placeholder="{{ t('Check whether the build is passing and summarise anything that broke since last week.') }}"
>{{ form.instruction }}</textarea>
<p class="field__hint">
Written for a model that will read it with no conversation around it, so say
the whole thing. Nobody will be there to answer a question about it.
</p>
<p class="field__hint">{{ t("Written for a model that will read it with no conversation around it, so say the whole thing. Nobody will be there to answer a question about it.") }}</p>
</div>
<div class="field">
<label class="field__label" for="schedule-target">Where the result goes</label>
<label class="field__label" for="schedule-target">{{ t("Where the result goes") }}</label>
<select class="select" id="schedule-target" name="target">
{% for value, label in targets %}
<option value="{{ value }}" {{ 'selected' if form.target == value }}>
@@ -43,26 +40,26 @@
</div>
<fieldset class="schedule-repeat">
<legend class="field__label">When</legend>
<legend class="field__label">{{ t("When") }}</legend>
<label class="checkbox">
<input type="radio" name="repeat" value="once"
{{ 'checked' if form.repeat == 'once' }}>
<span>Once</span>
<span>{{ t("Once") }}</span>
</label>
<label class="checkbox">
<input type="radio" name="repeat" value="every"
{{ 'checked' if form.repeat == 'every' }}>
<span>Every so often</span>
<span>{{ t("Every so often") }}</span>
</label>
<label class="checkbox">
<input type="radio" name="repeat" value="calendar"
{{ 'checked' if form.repeat == 'calendar' }}>
<span>On particular days</span>
<span>{{ t("On particular days") }}</span>
</label>
<div class="field">
<label class="field__label" for="schedule-start">Starting</label>
<label class="field__label" for="schedule-start">{{ t("Starting") }}</label>
<div class="btn-row">
<input class="input" type="date" id="schedule-start" name="start_date">
<input class="input" type="time" name="start_time" value="09:00">
@@ -74,7 +71,7 @@
</div>
<div class="field" data-repeat="every">
<label class="field__label" for="schedule-every">Repeat every</label>
<label class="field__label" for="schedule-every">{{ t("Repeat every") }}</label>
<div class="btn-row">
<input class="input" type="number" id="schedule-every" name="every_amount"
min="1" value="{{ form.every_amount }}" style="max-width: 8rem">
@@ -87,7 +84,7 @@
</div>
<div class="field" data-repeat="calendar">
<label class="field__label">On these days</label>
<label class="field__label">{{ t("On these days") }}</label>
<div class="checkbox-row">
{% for index, name in weekday_names %}
<label class="checkbox">
@@ -97,32 +94,29 @@
</label>
{% endfor %}
</div>
<p class="field__hint">Leave all of them unticked for every day.</p>
<p class="field__hint">{{ t("Leave all of them unticked for every day.") }}</p>
<label class="field__label" for="schedule-times">At</label>
<label class="field__label" for="schedule-times">{{ t("At") }}</label>
<input class="input" id="schedule-times" name="times"
value="{{ form.times or '09:00' }}" placeholder="09:00, 17:00">
<p class="field__hint">
One or more times, separated by commas. These are wall-clock times: 09:00
stays 09:00 when the clocks change.
</p>
<p class="field__hint">{{ t("One or more times, separated by commas. These are wall-clock times: 09:00 stays 09:00 when the clocks change.") }}</p>
<label class="field__label" for="schedule-monthdays">Only on these dates</label>
<label class="field__label" for="schedule-monthdays">{{ t("Only on these dates") }}</label>
<input class="input" id="schedule-monthdays" name="month_days"
value="{{ form.month_days }}" placeholder="1, 15">
<p class="field__hint">Days of the month, if you want it narrower. Optional.</p>
<p class="field__hint">{{ t("Days of the month, if you want it narrower. Optional.") }}</p>
</div>
</fieldset>
<div class="field">
<label class="field__label" for="schedule-count">Stop after</label>
<label class="field__label" for="schedule-count">{{ t("Stop after") }}</label>
<input class="input" type="number" id="schedule-count" name="count" min="0"
value="{{ form.count }}" style="max-width: 10rem">
<p class="field__hint">Number of runs. Leave it at 0 to keep going until you stop it.</p>
<p class="field__hint">{{ t("Number of runs. Leave it at 0 to keep going until you stop it.") }}</p>
</div>
<div class="field">
<label class="field__label" for="schedule-until">Or stop on</label>
<label class="field__label" for="schedule-until">{{ t("Or stop on") }}</label>
<input class="input" type="date" id="schedule-until" name="until">
<p class="field__hint">Optional. Nothing runs after this date.</p>
<p class="field__hint">{{ t("Optional. Nothing runs after this date.") }}</p>
</div>
@@ -35,7 +35,7 @@
<div class="btn-row">
<form method="post" action="/api/schedules/{{ schedule.id }}/run">
<button class="btn btn--sm" type="submit" title="Run it now without using up the next scheduled run">
<button class="btn btn--sm" type="submit" title="{{ t('Run it now without using up the next scheduled run') }}">
{{ icon("bolt", "icon--sm") }} Run now
</button>
</form>
@@ -59,10 +59,7 @@
The schedule was removed but its chat was kept, which is the default when
somebody deletes one. Said plainly rather than left as an empty bar.
#}
<p class="nav-empty">
This chat belonged to a schedule that has been removed. It is kept as a
record of what was done.
</p>
<p class="nav-empty">{{ t("This chat belonged to a schedule that has been removed. It is kept as a record of what was done.") }}</p>
{% endif %}
</div>
</div>
+5 -8
View File
@@ -12,22 +12,19 @@
<form method="post" action="/api/schedules/{{ schedule.id }}" class="card">
{% include "schedules/_form.html" %}
<div class="btn-row">
<button class="btn btn--primary" type="submit">Save</button>
<button class="btn btn--primary" type="submit">{{ t("Save") }}</button>
<a class="btn" href="/scheduled">Cancel</a>
</div>
</form>
<div class="card">
<h2 class="card__title">Remove</h2>
<p class="card__lede">
Stops it running. Its chat is kept by default and becomes an ordinary one,
so the transcript of everything it has already done stays where it is.
</p>
<h2 class="card__title">{{ t("Remove") }}</h2>
<p class="card__lede">{{ t("Stops it running. Its chat is kept by default and becomes an ordinary one, so the transcript of everything it has already done stays where it is.") }}</p>
<form method="post" action="/api/schedules/{{ schedule.id }}/delete"
data-confirm="Remove this schedule? It will stop running.">
data-confirm="{{ t('Remove this schedule? It will stop running.') }}">
<label class="checkbox">
<input type="checkbox" name="keep_chat" value="1" checked>
<span>Keep its chat</span>
<span>{{ t("Keep its chat") }}</span>
</label>
<button class="btn btn--danger btn--sm" type="submit">
{{ icon("trash", "icon--sm") }} Remove schedule
+6 -12
View File
@@ -11,19 +11,13 @@
{% endblock %}
{% block schedules_content %}
<p class="admin-lede">
Work that runs on its own, whether or not you are here. Each one has its own
chat, and replies into it every time it comes round.
</p>
<p class="admin-lede">{{ t("Work that runs on its own, whether or not you are here. Each one has its own chat, and replies into it every time it comes round.") }}</p>
{% if not schedules %}
<div class="empty">
{{ icon("clock", "empty__mark") }}
<h2 class="empty__title">Nothing scheduled</h2>
<p class="empty__text">
Set something to run later — a daily summary, a check every Monday
morning, a reminder in an hour.
</p>
<h2 class="empty__title">{{ t("Nothing scheduled") }}</h2>
<p class="empty__text">{{ t("Set something to run later — a daily summary, a check every Monday morning, a reminder in an hour.") }}</p>
</div>
{% else %}
<ul class="model-list">
@@ -43,13 +37,13 @@
{% endif %}
</div>
<div class="btn-row">
{% if item.row.target == "report" %}<span class="badge">files a report</span>{% endif %}
{% if item.row.target == "report" %}<span class="badge">{{ t("files a report") }}</span>{% endif %}
{# Which of these nobody typed. A schedule is invisible until it fires, so
the list is where a model's decision is checkable at all -- and the
reader needs to be able to tell one apart from their own without
opening it. #}
{% if item.row.origin == "model" %}<span class="badge">set up for you</span>{% endif %}
{% if not item.row.enabled %}<span class="badge badge--warning">paused</span>{% endif %}
{% if item.row.origin == "model" %}<span class="badge">{{ t("set up for you") }}</span>{% endif %}
{% if not item.row.enabled %}<span class="badge badge--warning">{{ t("paused") }}</span>{% endif %}
<a class="btn btn--sm" href="/scheduled/{{ item.row.id }}/edit">Edit</a>
</div>
</li>
+4 -4
View File
@@ -18,9 +18,9 @@
#}
<form method="post" action="/api/schedules/describe" class="card">
<div class="field">
<label class="field__label" for="describe">Say it in your own words</label>
<label class="field__label" for="describe">{{ t("Say it in your own words") }}</label>
<textarea class="input" id="describe" name="request" rows="3" required
placeholder="Every Monday morning, check whether the build is passing and write me a report."
placeholder="{{ t('Every Monday morning, check whether the build is passing and write me a report.') }}"
>{{ described }}</textarea>
<p class="field__hint">
Times are read as {{ timezone }}. Say how often, and what should happen.
@@ -59,9 +59,9 @@
{% if models %}
<div class="field">
<label class="field__label" for="schedule-model">Model</label>
<label class="field__label" for="schedule-model">{{ t("Model") }}</label>
<select class="select" id="schedule-model" name="model_id">
<option value="">Whatever is default at the time</option>
<option value="">{{ t("Whatever is default at the time") }}</option>
{% for model in models %}
<option value="{{ model.model_id }}">{{ model.display_name }}</option>
{% endfor %}