fix(branding): sign the release announcement with the fork's author - #958
Merged
Conversation
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
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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_i18nstep 3 swaps the upstream product name. The sign-off names a person, so the string contains no product name at all: every leak scan inverify-branding.shandtest-i18n-branding.shlooked straight past it, and a rename only ever turns "Your friend, Alex" into "Your friend, Alex".Scope
version_announcement_closingen,en_GB,de,es,fr,it,nl,pl)en.json, untouchedWeblate 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.json—author.name,author.signoff_key, andupstream.author_names(15 spellings, ordered so no entry is a substring of a later one, orAlexwould leavePierre가).apply-branding.sh—swap_author_name()applies them with a literalsplit/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 asAlexreaching 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.
afgets 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.shasserts 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
შენი მეგობარი, ალექსი:Tests
test-i18n-branding.shgains: 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.Full gate green locally —
branding/scripts/gallery-branding-check.sh(applies branding in a temp worktree, then verifies):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.