Skip to content

fix(branding): sign the release announcement with the fork's author - #958

Merged
Deeds67 merged 1 commit into
mainfrom
fix/branding-author-name
Aug 7, 2026
Merged

fix(branding): sign the release announcement with the fork's author#958
Deeds67 merged 1 commit into
mainfrom
fix/branding-author-name

Conversation

@Deeds67

@Deeds67 Deeds67 commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to #957. That PR translated the release sign-off for the seven locales the product ships in. The rest still said "Your friend, Alex" — upstream's author — in 47 more locales.

Why the existing rebrand couldn't catch it

patch_i18n step 3 swaps the upstream product name. The sign-off names a person, so the string contains no product name at all: every leak scan in verify-branding.sh and test-i18n-branding.sh looked straight past it, and a rename only ever turns "Your friend, Alex" into "Your friend, Alex".

Scope

Locales total 89
Define version_announcement_closing 55
Already branded by #957 8 (en, en_GB, de, es, fr, it, nl, pl)
Still naming upstream's author 47
Don't define the key 34 — already resolve to the branded en.json, untouched

Weblate translators transliterated the name into their own scripts, so there was no single token to match: Твой друг Алекс, صديقك، أليكس, 당신의 친구, Alex가, เพื่อนของคุณ อเล็กซ์, Tavs draugs, Alekss.

Approach

Substituting at build time rather than hand-writing 47 more override entries, because upstream keeps gaining locales and each new one would otherwise ship unbranded until someone noticed.

  • config.jsonauthor.name, author.signoff_key, and upstream.author_names (15 spellings, ordered so no entry is a substring of a later one, or Alex would leave Pierre가).
  • apply-branding.shswap_author_name() applies them with a literal split/join, scoped to the sign-off key alone. The name appears in no other key in any of the 89 locales, so the narrow scope costs nothing and stops a token as short as Alex reaching an unrelated translation. Runs after the override merge, so fix(branding): brand the fork's own wording across every locale #957's seven hand-written translations still win and this is a no-op there.

The name stays Latin in every script. That matches how the fork already leaves "Noodle Gallery" in Latin inside Japanese and Arabic strings, matches the seven shipped overrides, and avoids shipping 15 unproofread spellings of a real person's name.

af gets an override instead of the swap — upstream's Afrikaans is half-untranslated ("Jou friend, Alex"), so a name swap alone would leave "Jou friend, Pierre".

The part that makes it stick

A substitution list only covers spellings it already knows. So verify-branding.sh asserts the outcome, not the substitution: every locale defining the key must name the fork's author. A future Weblate sync introducing an unlisted transliteration survives the swap without that name and fails the build instead of shipping.

Verified by simulating exactly that — a Georgian შენი მეგობარი, ალექსი:

FAIL: ka sign-off does not name 'Pierre': 'შენი მეგობარი, ალექსი'
      — add its spelling to .upstream.author_names

Tests

test-i18n-branding.sh gains: the ordering invariant (asserted, not trusted to a comment), one case per substitution class (Cyrillic, Korean particle, Latvian declension, Thai, CJK, Afrikaans override, and a no-double-apply check), a whole-file outcome scan, and a fixture proving the swap cannot escape its key.

ok:   15 author spellings ordered so none consumes a longer one
ok:   55 locales define the sign-off, all name 'Pierre'
ok:   sign-off swapped, 'Alex'/'Aleks' in other keys untouched

Full gate green locally — branding/scripts/gallery-branding-check.sh (applies branding in a temp worktree, then verifies):

i18n: release sign-off names 'Pierre' in all 55 locales that define it
=== Gallery branding check passed ===

Not covered

The sentence around the name is still upstream's Weblate translation in the 47 locales, unreviewed by a native speaker — same caveat as #957. Only the name is changed; the surrounding grammar is untouched, and a proper noun swap preserves it in every language here.

The in-app release announcement is signed by a person, not the product, so
the brand swap in patch_i18n could never fix it: upstream signs it "Your
friend, Alex" and a rename only ever produces "Your friend, Alex" again.
PR #957 hand-translated the sign-off for the seven locales the product
ships in; the other 47 that define the key still named upstream's author,
in 15 different transliterations ("Твой друг Алекс", "صديقك، أليكس",
"당신의 친구, Alex가").

Swap it at build time instead of enumerating locales. config.json gains
the fork's author name and every known spelling of upstream's, ordered so
no entry is a substring of a later one; swap_author_name() applies them
with a literal split/join, scoped to the sign-off key alone — the name
appears in no other key in any of the 89 locales, so the narrow scope
costs nothing and keeps a token as short as "Alex" from reaching an
unrelated translation. It runs after the override merge, so the seven
hand-written translations still win and this is a no-op there.

The 34 locales that never define the key already resolve to the branded
en.json and are untouched.

Keeping the name in Latin across every script matches how the fork
already leaves "Noodle Gallery" in Latin inside Japanese and Arabic
strings, and avoids shipping 15 unproofread spellings of a real name.

Afrikaans gets an override rather than the swap: upstream's string is
half-untranslated ("Jou friend, Alex"), so a name swap alone would leave
"Jou friend, Pierre".

The substitution can only cover spellings it knows, and upstream gains
locales continuously, so verify-branding.sh now asserts the OUTCOME —
every locale defining the key must name the fork's author. A future
transliteration the list has never seen survives the swap without that
name and fails the build instead of shipping. test-i18n-branding.sh
covers each substitution class, the ordering invariant, and a fixture
proving the swap cannot escape its key.
@Deeds67 Deeds67 added the changelog:fix Bug fix for changelog label Aug 7, 2026
@Deeds67
Deeds67 merged commit f725ff1 into main Aug 7, 2026
45 of 46 checks passed
Deeds67 added a commit that referenced this pull request Aug 10, 2026
…958)

The in-app release announcement is signed by a person, not the product, so
the brand swap in patch_i18n could never fix it: upstream signs it "Your
friend, Alex" and a rename only ever produces "Your friend, Alex" again.
PR #957 hand-translated the sign-off for the seven locales the product
ships in; the other 47 that define the key still named upstream's author,
in 15 different transliterations ("Твой друг Алекс", "صديقك، أليكس",
"당신의 친구, Alex가").

Swap it at build time instead of enumerating locales. config.json gains
the fork's author name and every known spelling of upstream's, ordered so
no entry is a substring of a later one; swap_author_name() applies them
with a literal split/join, scoped to the sign-off key alone — the name
appears in no other key in any of the 89 locales, so the narrow scope
costs nothing and keeps a token as short as "Alex" from reaching an
unrelated translation. It runs after the override merge, so the seven
hand-written translations still win and this is a no-op there.

The 34 locales that never define the key already resolve to the branded
en.json and are untouched.

Keeping the name in Latin across every script matches how the fork
already leaves "Noodle Gallery" in Latin inside Japanese and Arabic
strings, and avoids shipping 15 unproofread spellings of a real name.

Afrikaans gets an override rather than the swap: upstream's string is
half-untranslated ("Jou friend, Alex"), so a name swap alone would leave
"Jou friend, Pierre".

The substitution can only cover spellings it knows, and upstream gains
locales continuously, so verify-branding.sh now asserts the OUTCOME —
every locale defining the key must name the fork's author. A future
transliteration the list has never seen survives the swap without that
name and fails the build instead of shipping. test-i18n-branding.sh
covers each substitution class, the ordering invariant, and a fixture
proving the swap cannot escape its key.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

changelog:fix Bug fix for changelog

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant