Under the ISO 8601 option, dateTimeLocale is the 'sv' sync sentinel, so any spelled-out weekday/month name rendered with currentLocale() prints in Swedish regardless of UI language. Follow-up to #9013/#9055 covering the surfaces those PRs didn't: spelled-out names now follow the UI language (isoTextLocale), while numeric dates and clock times keep currentLocale(). - scheduled-list-page: locale signal -> isoTextLocale ?? currentLocale (all 6 localeDate labels are spelled-out; fixes the reproduced 'ons, 15 juli' leak) - dialog-focus-session-edit.formatSelectedDate: weekday+month long - habit-tracker.dateRangeLabel: month short - scheduled-date-group.pipe: work-view group-header tooltip weekday - worklog (formatDayStr via mapArchiveToWorklog): day-header weekday Inlines the isoTextLocale ?? currentLocale expression (not #9055's textLocale) so this PR shares no files with #9055 and is conflict-free / mergeable in any order. Adds an ISO regression test to scheduled-date-group.pipe.spec and updates mocks so the new isoTextLocale() calls resolve. Also adds docs/handover.md.
8.5 KiB
Handover: ISO 8601 "spelled-out date names leak Swedish" work
Last updated: 2026-07-16. This document hands off two related, in-flight pieces of work to a fresh agent with no prior context.
TL;DR
Super Productivity's ISO 8601 date-format option stores dateTimeLocale = 'sv'
as a backward-compatible sync sentinel (it yields YYYY-MM-DD + a 24h clock).
Any code that renders spelled-out weekday/month names (e.g. Wed, July) using
that configured locale therefore prints them in Swedish (ons, juli),
regardless of the app's UI language. The correct behavior: spelled-out names follow
the UI language; numeric dates and clock times keep the configured locale (so ISO
stays YYYY-MM-DD and 24h is preserved).
There are two branches:
| Branch | PR | Scope | State |
|---|---|---|---|
feat/https-github-com-super-productivity-su-726a40 |
#9055 | Recurring-task cluster (start-date value, quick-setting weekday, repeat-info util, add-task-bar chips) | Pushed, browser-verified, awaiting CI + merge |
fix/iso-spelled-out-date-leaks (current) |
not opened yet | 5 more leak sites found during #9055 verification | Committed locally, needs push + PR + verify |
Background: the fix chain
- #8991 (merged, issue #8987): localized ISO weekday labels only in the custom
Schedule/Habits/Planner components, via
DateTimeFormatService.isoTextLocale(). - #9013 (merged): fixed the Material
<mat-calendar>app-wide by overridingCustomDateAdapter.getDayOfWeekNames/getMonthNames/formatto swap toisoTextLocale(). - #9055 (open): this session's first PR — the recurring-task dialog + add-task-bar.
- This branch: the remaining spelled-out leaks found by browser-testing #9055.
Key DateTimeFormatService API (in src/app/core/date-time-format/)
currentLocale()→ the configured locale (thesvsentinel under ISO). Use for numeric dates and clock times.isoTextLocale()→string | null. Returns the UI language only when the ISO (sv) option is active, elsenull. This is the shared primitive (on master).textLocale()→isoTextLocale() ?? currentLocale(). Added by #9055, NOT on master. It is the canonical "spelled-out names" locale.
Why this branch inlines isoTextLocale() ?? currentLocale() instead of using
textLocale(): textLocale only exists on the #9055 branch. To keep this PR
independent and conflict-free with #9055 (no shared files, no DateTimeFormatService
change, mergeable in any order), it inlines the same expression — consistent with how
#8991/#9013 already inline isoTextLocale checks. After both merge, these inline
expressions could be simplified to textLocale(), but that is optional cleanup.
This branch's 5 fixes (fix/iso-spelled-out-date-leaks, based on master)
All follow the rule spelled-out → UI language (isoTextLocale() ?? currentLocale());
numeric → currentLocale():
src/app/pages/scheduled-list-page/scheduled-list-page.component.ts— thelocalesignal now prefersisoTextLocale(). All 6localeDateusages in its template are spelled-out (EE, d MMM), so this one change fixes all of them. This is the leak that was reproduced on screen: "ons, 15 juli" under German UI..../metric/dialog-focus-session-edit/dialog-focus-session-edit.component.ts—formatSelectedDate()(weekdaylong+ monthlong) → UI language..../simple-counter/habit-tracker/habit-tracker.component.ts—dateRangeLabel(monthshort) → UI language. (The habit-tracker weekday column was already guarded by #8991; only this range label leaked.)src/app/ui/pipes/scheduled-date-group.pipe.ts— work-view group-header[title]tooltip (weekdayshort+ numeric). Low-visibility; applies UI language to the whole compact formatter (documented tradeoff: the numeric part also follows UI language here rather than splitting and losing the locale-native separator).src/app/features/worklog/worklog.service.ts— passes UI language intomapArchiveToWorklog→formatDayStr(weekdayshortworklog day headers). Verified thatlocaleparam feeds only the spelled-out weekday there.
Spec mocks updated so the new isoTextLocale() calls don't throw
(TypeError: isoTextLocale is not a function): scheduled-date-group.pipe.spec.ts (+ a new
ISO regression test) and worklog.service.spec.ts. The habit-tracker spec already mocked
isoTextLocale.
NOT leaks (verified guarded / different mechanism — do not touch): habit-tracker weekday
column, planner-day dayLabel, schedule-week/month (all isoTextLocale-guarded); the
formatMonthDay-based pipes (short-planned-at, local-date-str, short-date2) and the
focus-session chart label are numeric; planner-calendar-nav uses the browser locale
(toLocaleDateString(undefined, …)) — a separate, pre-existing, non-sv concern.
How the audit was done (repeat it if you suspect more leaks)
A single-line grep toLocaleDateString … currentLocale is insufficient — leaks hide
behind the localeDate pipe, multi-line Intl.DateTimeFormat(locale, …) where locale
is assigned earlier, and util indirection (formatDayStr, getWeekdaysMin). Search
instead for the spelled-out format tokens and then trace the locale source:
# spelled-out Intl options in .ts
grep -rEn "weekday: *'(long|short|narrow)'|month: *'(long|short|narrow)'" src/app --include='*.ts' | grep -v spec
# spelled-out localeDate pipe usages in templates (E / MMM / EEEE / MMMM)
grep -rEn "localeDate: *'[^']*(E|MMM)" src/app --include='*.html'
For each hit, confirm whether the locale is isoTextLocale()-guarded (safe), the browser
default undefined (separate concern), or raw currentLocale()/locale() (a leak).
Verification status
- #9055 (branch
feat/https-…-726a40): browser-verified against the Cloudflare PR preview — recur dialog start-date value shows Frenchmer. 15 juil. 2026/ GermanMi., 15. Juli 2026(not Swedish), and the quick-setting dropdown shows GermanJede Woche am Mittwoch. Unit tests green; awaiting CI + merge. - This branch: unit-tested (
scheduled-date-group.pipe15 incl. new ISO test;habit-tracker4;worklog.service4). Not yet browser-verified. No dedicated spec forscheduled-list-page(routed page, heavy TestBed); rely on browser verification.
What's left (do these)
- Push this branch and open the PR (base
master):# push is blocked in the sandbox; the user runs it via the `! ` prefix: ! git push -u origin fix/iso-spelled-out-date-leaks gh pr create --repo super-productivity/super-productivity --base master \ --head fix/iso-spelled-out-date-leaks \ --title "fix(locale): localize remaining ISO 8601 spelled-out date names (#8987)" \ --body-file <a body summarizing the 5 sites> - Browser-verify this branch on its fresh Cloudflare preview (URL is posted as a PR
comment ~1-2 min after push): Settings → Datetime format = ISO 8601, Language =
Deutsch; then check (a) the "Wiederkehrend" page (
/#/scheduled-list) date labels read GermanMi., 15. Julinotons, 15 juli; (b) habit-tracker range label; (c) a focus-session-edit dialog date; (d) worklog day headers. - Merge order does not matter between #9055 and this branch (no shared files). If you
later want the inline expressions unified to
textLocale(), do it after #9055 merges. - Re-verify #9055's round-2 surfaces (
getTaskRepeatInfoTextrepeat-info text + add-task-bar date/deadline chips) on #9055's latest preview — the first verification only covered its commit 1. These are unit-tested and mechanism-proven, so this is confirmation, not a blocker.
Incidental finding (separate, pre-existing, NOT part of either PR)
src/assets/i18n/fr.json uses single-brace placeholders ({weekdayStr},
{dateDayStr}, {dayAndMonthStr}) where ngx-translate needs double {{ }} — so the
French recurring quick-setting labels render the literal placeholder
("Chaque semaine le {weekdayStr}"). en.json and de.json are correct. Project rule:
only edit en.json (other locales come from the translation pipeline), so this should
be reported upstream, not fixed in code here.
Commands
npm run checkFile <file> # prettier + lint one file (run on every changed .ts)
npm run test:file <spec> # single Karma spec (~real Chrome)
git push # blocked in sandbox — user runs `! git push`