Skip to content

Localize Apple What’s New headlines from release notes - #2512

Open
kavemang wants to merge 3 commits into
ryanbr:mainfrom
kavemang:fix/ios-whats-new-headlines-2037
Open

kavemang wants to merge 3 commits into
ryanbr:mainfrom
kavemang:fix/ios-whats-new-headlines-2037

Conversation

@kavemang

@kavemang kavemang commented Sep 26, 2026 •

Copy link
Copy Markdown

Refs #2037.

Localizes the newest Apple What's New headline through the release-note generator and writes its supplied translations into the Apple catalog. Historical Apple headlines remain English literals. The generator demotes the previous headline lookup when adding a release, so the large Swift releases array keeps exactly one localized-title call instead of accumulating calls across history. Android's existing resource-based headlines are retained.

This takes the maintainer's newest-title-only alternative to the Release-build concern. No Apple Release build or timing comparison was performed on this Linux host, and Debug CI is not presented as Release-performance evidence.

Traditional Chinese remains a separate source: zh-Hant maps one to one rather than aliasing Simplified Chinese. The existing distinct historical headline translations are preserved, and the current v12.0 headline now has an explicit Traditional translation too. An audit checked all 13 affected release-note sources against their catalog values and confirmed distinct Chinese scripts.

Validation:

  • All 21 focused generator tests passed, including insertion and rerun with one localized Apple title, correct quote escaping and preserved item literals.
  • Regeneration is byte-idempotent; release text is unchanged after ignoring the lookup wrappers, and the generated Swift has exactly one localized-title call.
  • Swift syntax parsing, comment hygiene, diff-scoped i18n and whitespace checks passed locally. Normal main merge was clean; guarded parity regeneration passed with 289 known findings.
  • All six expected checks passed at 5731c628f3c2ad253d89f90e17e3c3b6dea7a75c: native macOS Debug build with StrandTests, iOS Debug simulator build, Android APK/full unit suite, Tools Python, hygiene and i18n. The stable six-check roster, unchanged head and mergeable state were rechecked. Apple CI run. No signing files or generated Xcode project are included.

@ryanbr

ryanbr commented Sep 27, 2026

Copy link
Copy Markdown
Owner

Holding this one, for a single reason that is worth fixing at the source rather than in the data.

First, the parts I checked and found fine. The generator learning to write Apple catalog entries is the most useful thing here: localizing the What's New title by hand is a step that gets skipped when the version is pre-bumped, so automating it removes a real footgun from the release flow. And I went looking for a compile-time problem in AppChangelog.swift, because that file has form (it compiles every past entry, and the iOS release leg once went from 11 to 27 minutes against a 40 minute timeout). There is nothing there: this branch's iOS leg ran 7m23s, against 6m42s and 9m22s on two other PRs merged the same day. Twelve String(localized:) wrappers cost nothing measurable.

The problem is this line:

APPLE_LOCALES = {"de": "de", "es": "es", "fr": "fr", "pt-PT": "pt-PT",
                 "pl": "pl", "ru": "ru", "it": "it", "zh-Hans": "zh", "zh-Hant": "zh"}

Both Apple Chinese locales read the same zh source, so all twelve headlines land Simplified text in the Traditional slot. All 12 of 12 new catalog entries have zh-Hant byte-identical to zh-Hans, and the text is Simplified: 变成, 强调色, 训练负荷, 教练, 压力, where Traditional wants 變成, 強調色, 訓練負荷, 教練, 壓力. For comparison, only 35 of 270 existing zh-Hans/zh-Hant pairs in that catalog coincide, and those are short strings where the two scripts genuinely agree.

It is structural rather than a slip. Android ships one Chinese locale, Apple ships two, and title_locales in the release notes only ever had zh because it was written for Android. Teaching the generator to fill nine Apple locales from eight sources left one target with no source, and the alias closed the gap quietly. So it will repeat on every future release, and the i18n gate cannot catch it: the key is present and non-empty, which is all that gate asks.

What would land this: a zh-Hant key in title_locales, carrying Traditional titles, and APPLE_LOCALES mapping it one to one. That is twelve real translations, which is why I would rather ask than invent them.

If shipping Simplified as the Traditional fallback is the call you would prefer to make, that is defensible, and plenty of apps do it. But it should be a comment in the generator saying so, not an alias that reads like an oversight, because the rest of this catalog holds the higher standard.

Worth saying: your #2248, which I rebased and merged today, supplies zh-Hans and zh-Hant separately and correctly by hand. That is most of why I read this as a gap in the tool rather than in the care.

@kavemang
kavemang force-pushed the fix/ios-whats-new-headlines-2037 branch from bcd372e to a349738 Compare September 28, 2026 20:28
@ryanbr

ryanbr commented Oct 4, 2026

Copy link
Copy Markdown
Owner

Thanks @kavemang. Localising the What's New headline is the right gap to close: the items were already translatable and the title was the one line of that screen still reaching a reader in English.

One concern, and it is specific to which CI leg pays for it.

The cost lands on the Release build, which no PR check runs

AppChangelog.swift is 2514 lines, and this wraps 12 titles in String(localized:) inside the one large static let releases: [Release] = [...] literal. That turns 12 literals into 12 function calls the type-checker has to resolve inside a single large array expression, which is the shape Swift's type-checker handles worst.

That file is already the reason the iOS leg of fork-release.yml went from about 11 minutes to about 27, against a 40-minute timeout, because it compiles every past entry on every build. The headroom there is thinner than it looks.

app-build.yml builds -configuration Debug on both legs (lines 129 and 142), so the 17 green checks on this PR do not exercise the Release configuration where the optimiser and the type-checker actually interact. The checks passing is not evidence about the leg that is near the limit.

Three ways to settle it, in the order I would try them:

  • Measure it. A Release-configuration build of NOOPiOS on this branch, against one on main, would turn this from a worry into a number. If the delta is small, merge it and I will stop asking.
  • Localise only the newest title. The What's New sheet shows the current release; the historical titles are reached rarely if at all. That is 1 call instead of 12, and it also avoids permanently adding changelog history to the translation surface, where every future release adds another entry nobody reads in any language.
  • Keep the array literal and move the titles out, so appchangelog-gen.py emits them into a separate structure that is not part of the same type-checked literal.

No objection to the change itself, and if the Release timing comes back flat then the first option settles it on the spot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants