Adopt String Catalog symbol generation across the app#124
Open
kyleve wants to merge 8 commits into
Open
Conversation
Turn on Xcode's type-safe String Catalog codegen so catalog-backed targets generate LocalizedStringResource symbols — the first step of redoing the symbol-generation migration (supersedes #79) on current main. - Project.swift: set STRING_CATALOG_GENERATE_SYMBOLS=YES in the project base settings so the Tuist-native app/extension targets (Where, WhereWidgets, WhereShareExtension) generate symbols. The SwiftPM package targets (WhereUI, WhereCore, RegionKit, LifecycleKit) generate symbols automatically from the toolchain and need no setting. No behavior change; existing String(localized:) call sites are untouched. Per-module catalog normalization + call-site conversion follow.
LifecycleFailureView reads the generated .failureLaunchTitle / .failureLaunchRetry LocalizedStringResource symbols instead of raw "failure.launch.*" keys with bundle: .module. Both keys are already manual in the module catalog, so the symbols generate; a removed/renamed key becomes a compile error.
Reminder / daily-summary / data-issue-alert notification copy and the two BackupError descriptions now resolve through generated LocalizedStringResource symbols instead of raw keys with bundle: .module. The two String(format:) compositions (summary.notification.regionDays '%1$@ in %2$@', and backup.error.unsupportedFormatVersion '%lld') collapse into type-safe symbol calls (.summaryNotificationRegionDays(count, region.localizedName), .backupErrorUnsupportedFormatVersion(version)); plurals become symbol calls carrying the count (.summaryNotificationDayCount, .dataIssuesNotificationBody). Output is unchanged. All keys are already manual in the module catalog.
Delete the ShareStrings facade; ShareEvidenceView references the extension's generated LocalizedStringResource symbols directly (Text/Label/Button take the bare symbol; other call sites use String(localized: .symbol)). The attachment-header singular/plural picks between the two catalog keys inline. Normalize the catalog keys from stale -> manual so the symbols generate.
Delete the WidgetStrings facade; the two widgets' gallery name/description resolve through the extension's generated symbols via String(localized:). Normalize the catalog keys from stale -> manual so the symbols generate.
Delete the 2,455-line hand-maintained Strings facade. Simple call sites now resolve through Xcode's generated LocalizedStringResource symbols directly (String(localized: .symbol)); the composition/plural/switch cases move to a new WhereFormat helper that composes those symbols. A removed or renamed catalog key is now a compile error instead of a runtime fallback. - Add WhereFormat.swift for the 45 logic-bearing strings (years formatted grouping-free, plurals, enum switches, composed accessibility labels, and the non-catalog coordinate/measurement formatters). - Convert ~412 call sites across 52 files from Strings.* to String(localized: .symbol) / WhereFormat.*. - Normalize the WhereUI catalog so every key generates a symbol: drop two extraction artifacts (empty key, "%lld"), set all keys to manual, and materialize 20 keys that previously existed only via defaultValue (settings search keywords, group headers, etc.). The large catalog diff is a re-sort + extractionState normalization, not content changes. - Replace StringsTests with WhereFormatTests covering the WhereFormat logic. RegionKit is intentionally untouched: its region names are data-driven via regions.json localizationKeys (a documented trade-off), not an enum switch.
Rewrite IntentStrings to compose the module's generated LocalizedStringResource symbols instead of raw "dialog.*" keys with inline defaultValues; its public API is unchanged, so the intents' call sites are untouched. Normalize the 23 dialog keys the helper uses from (none) -> manual so the symbols generate; the App Intents static-metadata keys (framework-extracted literals) stay as-is.
Update the localization guidance to the generated-symbol workflow (add a manual catalog key, reference its .symbol; compose logic in WhereFormat / IntentStrings) and drop references to the deleted Strings/ShareStrings/WidgetStrings facades across the feature + module AGENTS/READMEs, MODULE_AUDIT, and TODOs (the code-gen-the-strings item is now done).
kyleve
force-pushed
the
cursor/string-catalog-symbols
branch
from
July 23, 2026 04:08
c644fca to
d1c9fd5
Compare
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.
What
Redo of the closed #79: adopt Xcode's built-in String Catalog symbol generation across the app and make every string reference type-safe, on top of current
main. The hand-maintained string facades (Strings.swift~2,455 lines,ShareStrings,WidgetStrings) are gone; user-facing copy now resolves through generatedLocalizedStringResourcesymbols, so a typo'd or removed key is a compile error instead of a runtime fallback.This is a fresh redo rather than a rebase of #79:
mainreorganized WhereUI so heavily since then (≈100 files moved/added —Secondary/→Elsewhere/, newManual/,Regions/,Year/, etc.) that a merge produced 26 conflicts and none of #79's per-file conversions survived. See #79 for the original discussion.How it works
STRING_CATALOG_GENERATE_SYMBOLS = YESis set project-wide inProject.swiftfor the Tuist-native app/extension targets. The SwiftPM library targets (WhereUI, WhereCore, LifecycleKit) generate symbols automatically from the toolchain.some.keygenerates a.someKeysymbol onLocalizedStringResource, referenced directly:Text(.tabYear),String(localized: .commonOk).WhereFormat(WhereUI) and the rewrittenIntentStrings(WhereIntents) — not a raw-key facade.Commits (one per module, bisectable)
LifecycleFailureViewuses.failureLaunchTitle/.failureLaunchRetry.String(format:)compositions collapse into type-safe symbol calls (output unchanged).ShareStrings/WidgetStringsfor the extensions' own generated symbols; normalize their catalogsstale→manual.Stringsfacade; convert ~412 call sites across 52 files; addWhereFormatfor the 45 logic-bearing strings; normalize the catalog (drop 2 extraction artifacts, all keys →manual, materialize 20 keys that only existed viadefaultValue); replaceStringsTestswithWhereFormatTests.IntentStringsto compose generated symbols (API unchanged, call sites untouched); normalize the 23 dialog keys →manual.Not converted (by design)
regions.jsonlocalizationKey, resolved dynamically), a documented trade-off in its AGENTS.md. Adopt Xcode String Catalog symbol generation across the app #79 converted an enumswitchthat no longer exists; converting it back would fight the current architecture.Verification
./swiftformat --lintclean.Stuff-iOS-Testsscheme green (all bundles) under Xcode 27 on iPhone 17 / iOS 27.0.Strings./ShareStrings./WidgetStrings.call site is converted (0 residual references).