Skip to content
Merged
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
25 changes: 25 additions & 0 deletions .changeset/6611-de-auth-device-passive-register.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
---
'@object-ui/i18n': patch
---

The German pack's `auth.device.*` in-flight states move to the passive register
(objectui#6611, triage ruling 2026-08-27, option 2): `approving` `Genehmige…` →
`Wird genehmigt…`, `denying` `Ablehne…` → `Wird abgelehnt…`, `loading` `Lade…` →
`Wird geladen…`.

Graded a bug, not a taste call: `Ablehne…` was not a grammatical German form.
`ablehnen` is a separable-prefix verb, so the first-person singular is *ich lehne
ab* — the pack shipped broken German on `DeviceAuthPage.tsx` to users today.
Re-derived the ruling's premise against the tree before touching anything: the
`de` pack's dominant in-flight register is measurably the passive — 40 of 61
`en`-bare-gerund keys render `Wird …`, against 21 that don't (a Unicode-aware,
position-aware scan, positive-controlled on `common.loading` → `Wird geladen…`),
close to triage's "roughly 37" and the same conclusion either way. `loading` was
the sole member of objectui#5972's merged `Loading…` group carved out by name in
`LOADING_GROUP_FORKS` (`packages/i18n/src/__tests__/ellipsis-glyph-3878.test.ts`),
because converging it alone while `approving`/`denying` stayed first-person would
have manufactured a new same-screen inconsistency. Moving the whole namespace to
the passive removes that fork rather than maintaining it, so the exemption row is
deleted and the pin now asserts zero forks.

No key added or removed, no `en` value moves, no other locale touched.
76 changes: 46 additions & 30 deletions packages/i18n/src/__tests__/ellipsis-glyph-3878.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -287,24 +287,35 @@ describe('objectui#3878 — the ten packs spell the ellipsis U+2026 and only U+2
* `ar-participle-orthography-6610.test.ts` — which also pins the three `ar`
* values where the yāʾ is grammatically CORRECT and must not be swept.
*
* ## The `de` fork, and why it is exempted BY NAME
* ## The `de` fork, and why it WAS exempted BY NAME (resolved by objectui#6611)
*
* `auth.device.loading` is `Lade…` and stays `Lade…`. It is not an oversight and
* it is not this pass's to settle: it is the one member of the group whose
* outlier spelling is coherent with its own screen. `apps/console`'s
* `DeviceAuthPage.tsx` renders all three of that namespace's in-flight states,
* and de writes all three in the same first-person voice — `Genehmige…`,
* `Ablehne…`, `Lade…` — while `approving`/`denying` are OUTSIDE this group (their
* `en` is `Approving…`/`Denying…`). Converging `loading` alone would leave that
* one screen reading `Genehmige… / Ablehne… / Wird geladen…`: a new same-screen
* inconsistency manufactured by the very pass meant to remove one. Resolving it
* the other way — moving the whole namespace to the passive — is a copy-voice
* decision over keys this card does not fence in.
* `auth.device.loading` used to be `Lade…`, held out of the group by name. It
* was not an oversight and was not this pass's to settle: it was the one
* member of the group whose outlier spelling was coherent with its own
* screen. `apps/console`'s `DeviceAuthPage.tsx` renders all three of that
* namespace's in-flight states, and de wrote all three in the same
* first-person voice — `Genehmige…`, `Ablehne…`, `Lade…` — while
* `approving`/`denying` are OUTSIDE this group (their `en` is
* `Approving…`/`Denying…`). Converging `loading` alone would have left that
* one screen reading `Genehmige… / Ablehne… / Wird geladen…`: a new
* same-screen inconsistency manufactured by the very pass meant to remove
* one. The alternative — moving the whole namespace to the passive — was a
* copy-voice decision over keys the #5972 card did not fence in, so #5972
* reported the fork instead of picking, and pinned it as a named exemption.
*
* So it is reported as a fork and pinned as an exemption. The pin asserts both
* halves: the value, and that it still DIFFERS from the converged rendering. If
* someone later converges it, this block goes red and the exemption row has to
* be deleted — a deliberate act, which is the point.
* objectui#6611 is that later decision: triage ruled toward the passive,
* measuring it as the pack's dominant in-flight register (roughly 37 of the
* pack's bare-gerund-`en` keys render `Wird …`, against the namespace's three
* first-person outliers). It also carried a second, independent defect —
* `Ablehne…` was not a grammatical German form at all (`ablehnen` is a
* separable-prefix verb; the first-person singular is *ich lehne ab*, not
* *ablehne*) — which meant either resolution direction had to touch it. The
* namespace converged (`Genehmige…` → `Wird genehmigt…`, `Ablehne…` →
* `Wird abgelehnt…`, `Lade…` → `Wird geladen…`), `auth.device.loading` is no
* longer an outlier, and the `LOADING_GROUP_FORKS` row above is gone. If a
* future edit reintroduces a per-language split in this group, this block
* goes red again, and a new named exemption is the deliberate way out — same
* as before.
*
* Contrast `approvalsInbox.loadingMore`, which the card floated as a possible
* second fork (de `Lädt…`, ko `불러오는 중…`, on the theory that a *continuation*
Expand DownExpand Up@@ -343,10 +354,13 @@ const LOADING_GROUP = [
* Members held OUT of the uniformity rule, with the reason. See the fork note
* above. Exempting by name — rather than by loosening the rule — keeps the
* waiver countable and makes removing it a visible edit.
*
* Empty as of objectui#6611, which resolved the one row this array ever held
* (`de`/`auth.device.loading`) by converging the whole `auth.device.*`
* namespace to the passive rather than by keeping the carve-out. See the
* resolved fork note above for why.
*/
const LOADING_GROUP_FORKS: ReadonlyArray<{ lang: Lang; key: string; value: string }> = [
{ lang: 'de', key: 'auth.device.loading', value: 'Lade…' },
];
const LOADING_GROUP_FORKS: ReadonlyArray<{ lang: Lang; key: string; value: string }> = [];

describe('objectui#5972 — each pack renders the merged `Loading…` group exactly one way', () => {
it('derives the group from en and finds exactly the ten keys pinned above', () => {
Expand DownExpand Up@@ -409,10 +423,19 @@ describe('objectui#5972 — each pack renders the merged `Loading…` group exac
});

it('pins the forked values so keeping them stays a deliberate act', () => {
// Both halves matter. The value pins what the fork actually says; the
// inequality pins that it is still a fork. Converge it later and this goes
// red until the row is removed, so the waiver cannot outlive its reason.
expect(LOADING_GROUP_FORKS).toHaveLength(1);
// Both halves matter, for whatever rows exist. The value pins what the
// fork actually says; the inequality pins that it is still a fork.
// Converge a row later and this goes red until it is removed, so a
// waiver cannot outlive its reason.
//
// Zero as of objectui#6611: the one row this array ever held (`de` /
// `auth.device.loading`) was resolved by converging the whole
// `auth.device.*` namespace to the passive rather than by keeping the
// carve-out — see the resolved fork note above LOADING_GROUP_FORKS.
// Pinning the count, rather than leaving this an unchecked no-op loop,
// means a row silently reappearing here still gets caught by name the
// next time this test is read, not just by the uniformity test above.
expect(LOADING_GROUP_FORKS).toHaveLength(0);

for (const { lang, key, value } of LOADING_GROUP_FORKS) {
const byKey = new Map(PACKS[lang]);
Expand All@@ -424,12 +447,5 @@ describe('objectui#5972 — each pack renders the merged `Loading…` group exac
`${lang} ${key} now matches the converged rendering — delete its LOADING_GROUP_FORKS row`,
).not.toBe(converged);
}

// The de first-person voice this fork is coherent with. Both keys are
// outside the group (their `en` is not `Loading…`), so nothing in this file
// rules on them; they are asserted because they are the fork's whole reason,
// and if they ever move to the passive the fork stops being justified.
expect(builtInLocales.de.auth.device.approving).toBe('Genehmige…');
expect(builtInLocales.de.auth.device.denying).toBe('Ablehne…');
});
});
6 changes: 3 additions & 3 deletions packages/i18n/src/locales/de.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -2013,20 +2013,20 @@ const de = {
approveWarning: "Genehmigen Sie nur, wenn Sie diese Verbindung soeben selbst gestartet haben. Nach der Genehmigung kann diese Laufzeitumgebung auf die privaten Pakete Ihrer Organisation zugreifen.",
loggedInAs: "Angemeldet als {{email}}",
approve: "Gerät genehmigen",
approving: "Genehmige…",
approving: "Wird genehmigt…",
approvedTitle: "Gerät autorisiert",
approvedDescription: "Sie können zum Gerät zurückkehren — es sollte sich in Kürze anmelden.",
approveSuccess: "Gerät autorisiert",
approveSuccessDescription: "Sie können dieses Fenster schließen.",
approveFailed: "Genehmigung fehlgeschlagen",
deny: "Anfrage ablehnen",
denying: "Ablehne…",
denying: "Wird abgelehnt…",
deniedTitle: "Zugriff verweigert",
deniedDescription: "Dem Gerät wird kein Zugriff gewährt.",
denyFailed: "Anfrage konnte nicht abgelehnt werden",
invalidTitle: "Ungültiger Gerätelink",
invalidDescription: "In der URL wurde kein Gerätecode angegeben.",
loading: "Lade…",
loading: "Wird geladen…",
cancel: "Abbrechen",
// objectui#3546 slice three — device-authorization plugin switched off.
disabledTitle: "Geräteautorisierung nicht aktiviert",
Expand Down
Loading