Repository navigation
Add localization system plus alternative English dialect - #243
Merged
Merged
Conversation
Empty Localizable.xcstrings per UI package and for the app target, defaultLocalization on the package, en-GB as a known region, and the test plans pinned to en so hosted tests never follow the runner's language. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Xcode updates the String Catalogs after an IDE build, but xcodebuild only emits the compiler's .stringsdata. scripts/localize sync builds the app and runs `xcrun xcstringstool sync` over that data so the catalogs can be kept current (and checked in CI) from the command line. verify keeps catalogs in UI targets and rejects empty translations, which render blank rather than falling back to English; status summarises per-language progress. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
OperationFailure and the repositories now carry data only; the new BrewRepositoryError names a malformed brew output, and BrewUIComponents/Copy/BrewErrorCopy is the one place those enums are worded. Replaces four duplicated userMessage(for:) switches. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every literal in the shared components now goes through String(localized:bundle:comment:) or Text(verbatim:), and the target's catalog is synced from the compiler's extraction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds bundle: #bundle to every String(localized:) call, converts view literals, and replaces LocalizedStringKey-typed properties with String since a key cannot carry a bundle. Test support no longer mirrors copy. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Base copy is US English ("catalog"); the en-GB catalog will carry the
UK spelling once seeded.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DoctorCopy.warningPreamble stays verbatim on purpose: it mirrors brew doctor's own English output. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
SidebarItem.title is now the one String behind the sidebar rows, the View menu and the window titles. The Debug menu uses Text(verbatim:) so developer-only labels never reach the catalog. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
The lint rules have bypasses and a new test imports an undeclared direct module dependency.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 1
Open (4)
What changed in this PR
Adds String Catalog localisation across the app and Swift packages, including seeded en-GB translations.
Changes:
- Adds localisation lint rules, catalog verification and CI integration.
- Moves UI copy and error presentation into UI layers.
- Updates views, view models, tests and documentation for localized resources.
Reported checks pass; UI and E2E tests were not run.
| File | Description |
|---|---|
| Tools/BrewUILint/Tests/BrewUILintTests/TestSupport/LintHarness.swift | Adds lint harnesses. |
| Tools/BrewUILint/Tests/BrewUILintTests/LocalizedCopyRuleTests.swift | Tests localized-copy linting. |
| Tools/BrewUILint/Tests/BrewUILintTests/LocalizationLayerRuleTests.swift | Tests layer restrictions. |
| Tools/BrewUILint/Sources/BrewUILint/Rules/LocalizedCopyRule.swift | Adds copy lint rule. |
| Tools/BrewUILint/Sources/BrewUILint/Rules/LocalizationLayerRule.swift | Adds layer lint rule. |
| Tools/BrewUILint/Sources/BrewUILint/Core/Runner.swift | Collects copy parameters. |
| Tools/BrewUILint/Sources/BrewUILint/Core/RuleRegistry.swift | Registers new rules. |
| Tools/BrewUILint/Sources/BrewUILint/Core/RuleContext.swift | Shares lint context. |
| Tools/BrewUILint/Sources/BrewUILint/Core/CopyParameterCollector.swift | Finds localized parameters. |
| Tests/BrewUIComponentsTests/BrewErrorCopyTests.swift | Tests error copy mapping. |
| Tests/BrewUIComponentsTests/BrewActionButtonAppearanceTests.swift | Resolves localized button titles. |
| Tests/BrewFeatureInstalledTests/UpgradesViewModelScopeTests.swift | Updates localized expectations. |
| Tests/BrewFeatureInstalledTests/InstalledViewModelTests.swift | Updates error expectations. |
| Tests/BrewFeatureInstalledTests/InstalledViewModelScopeTests.swift | Updates localized expectations. |
| Tests/BrewFeatureInstalledTests/InstalledViewModelPresentationTests.swift | Updates presentation expectations. |
| Tests/BrewFeatureInstalledTests/InstalledUpgradeBusyPresentationTests.swift | Updates failure construction. |
| Tests/BrewFeatureInstalledTests/InstalledUninstallBusyPresentationTests.swift | Updates failure construction. |
| Tests/BrewFeatureDoctorTests/DoctorViewModelTests.swift | Updates localized expectations. |
| Tests/BrewFeatureDiscoverTests/DiscoverViewModelTests.swift | Updates localized expectations. |
| Tests/BrewFeatureConfigTests/ConfigViewModelTests.swift | Resolves localized section titles. |
| Tests/BrewCoreTests/OperationFailureTests.swift | Updates failure model tests. |
| Tests/BrewCLITests/SerialBrewCommandCenterTests.swift | Updates failure assertions. |
| Tests/BrewCLITests/SerialBrewCommandCenterFailureTests.swift | Updates failure extraction. |
| Sources/BrewUIComponents/Views/PreviewCopy.swift | Adds preview localization helper. |
| Sources/BrewUIComponents/Views/PackageDetailSubviews.swift | Accepts localized headings. |
| Sources/BrewUIComponents/Views/NoteCallout.swift | Supports localized and verbatim text. |
| Sources/BrewUIComponents/Views/LastUpdatedLabel.swift | Localizes relative time. |
| Sources/BrewUIComponents/Views/CommandBlockView.swift | Localizes command-block copy. |
| Sources/BrewUIComponents/Views/BrewActionButton.swift | Localizes button metadata. |
| Sources/BrewUIComponents/Views/AsyncContentView.swift | Localizes retry action. |
| Sources/BrewUIComponents/Resources/Localizable.xcstrings | Adds component catalog. |
| Sources/BrewUIComponents/Copy/BrewErrorCopy.swift | Centralizes UI error wording. |
| Sources/BrewUIComponents/Commands/SearchCommands.swift | Localizes Find command. |
| Sources/BrewUIComponents/Commands/RefreshCommands.swift | Localizes Refresh command. |
| Sources/BrewServicesTestSupport/InstalledPackagesTestSupport.swift | Removes duplicated copy helpers. |
| Sources/BrewRepositoryInterfaces/RepositoryError.swift | Adds typed repository error. |
| Sources/BrewRepositories/BrewInstalledPackagesRepository.swift | Reports malformed output explicitly. |
| Sources/BrewFeatureSelfUpgrade/Views/SelfUpgradeBanner.swift | Localizes banner copy. |
| Sources/BrewFeatureSelfUpgrade/ViewModels/SelfUpgradePresentation.swift | Localizes upgrade presentation. |
| Sources/BrewFeatureSelfUpgrade/Resources/Localizable.xcstrings | Adds self-upgrade catalog. |
| Sources/BrewFeatureInstalled/Views/UpgradesSidebarBadge.swift | Localizes upgrade badge. |
| Sources/BrewFeatureInstalled/Views/UpgradesPackagesView.swift | Localizes upgrade states. |
| Sources/BrewFeatureInstalled/Views/UpgradesHeaderView.swift | Localizes upgrade controls. |
| Sources/BrewFeatureInstalled/Views/InstalledUpgradesColumns.swift | Localizes search prompts. |
| Sources/BrewFeatureInstalled/Views/InstalledPackagesView.swift | Localizes installed-package UI. |
| Sources/BrewFeatureInstalled/Views/InstalledPackageDetailView.swift | Localizes package details. |
| Sources/BrewFeatureInstalled/Views/InstalledPackageDetailSubviewSections.swift | Localizes metadata sections. |
| Sources/BrewFeatureInstalled/Views/InstalledOutdatedBadge.swift | Localizes outdated badge. |
| Sources/BrewFeatureInstalled/Views/InstalledListRowView.swift | Marks technical text verbatim. |
| Sources/BrewFeatureInstalled/Views/InstalledDeprecatedBadge.swift | Localizes deprecated badge. |
| Sources/BrewFeatureInstalled/ViewModels/UpgradesViewModel.swift | Carries localized upgrade resources. |
| Sources/BrewFeatureInstalled/ViewModels/UpgradesUpToDateCopy.swift | Localizes up-to-date copy. |
| Sources/BrewFeatureInstalled/ViewModels/UpgradePackageItem.swift | Localizes upgrade titles. |
| Sources/BrewFeatureInstalled/ViewModels/UninstallPackageItem.swift | Localizes uninstall copy. |
| Sources/BrewFeatureInstalled/ViewModels/PackageDetailMetadataItem.swift | Localizes metadata values. |
| Sources/BrewFeatureInstalled/ViewModels/InstalledViewModel.swift | Carries localized package counts. |
| Sources/BrewFeatureInstalled/ViewModels/InstalledPackageDetailViewModel.swift | Uses shared error copy. |
| Sources/BrewFeatureInstalled/ViewModels/InstalledListRowViewModel.swift | Localizes accessibility state. |
| Sources/BrewFeatureInstalled/Resources/Localizable.xcstrings | Adds installed-feature catalog. |
| Sources/BrewFeatureDoctor/Views/DoctorView.swift | Localizes Doctor UI. |
| Sources/BrewFeatureDoctor/Views/DoctorSeverityStyle.swift | Localizes severity names. |
| Sources/BrewFeatureDoctor/Views/DoctorIssueRowView.swift | Localizes fix badge. |
| Sources/BrewFeatureDoctor/Views/DoctorIssueDetailView.swift | Localizes issue details. |
| Sources/BrewFeatureDoctor/Views/DoctorCopy.swift | Documents verbatim brew output. |
| Sources/BrewFeatureDoctor/ViewModels/DoctorViewModel.swift | Uses shared error copy. |
| Sources/BrewFeatureDoctor/ViewModels/DoctorIssueItem.swift | Localizes accessibility annotation. |
| Sources/BrewFeatureDoctor/Resources/Localizable.xcstrings | Adds Doctor catalog. |
| Sources/BrewFeatureDiscover/Views/DiscoverPackagesView.swift | Localizes Discover UI. |
| Sources/BrewFeatureDiscover/Views/DiscoverPackageDetailView.swift | Localizes package details. |
| Sources/BrewFeatureDiscover/Views/DiscoverListRowView.swift | Localizes install metrics. |
| Sources/BrewFeatureDiscover/Views/DiscoverInstalledBadge.swift | Localizes installed badge. |
| Sources/BrewFeatureDiscover/ViewModels/DiscoverViewModel.swift | Carries localized headings. |
| Sources/BrewFeatureDiscover/ViewModels/DiscoverPackageDetailViewModel.swift | Uses shared error copy. |
| Sources/BrewFeatureDiscover/ViewModels/DiscoverListRowViewModel.swift | Localizes accessibility state. |
| Sources/BrewFeatureDiscover/Resources/Localizable.xcstrings | Adds Discover catalog. |
| Sources/BrewFeatureConsole/Views/ConsoleToolbar.swift | Localizes console controls. |
| Sources/BrewFeatureConsole/Views/ConsoleStatusBar.swift | Localizes console status. |
| Sources/BrewFeatureConsole/Views/ConsoleCommands.swift | Localizes console commands. |
| Sources/BrewFeatureConsole/Views/ConsoleBody.swift | Localizes empty state. |
| Sources/BrewFeatureConsole/ViewModels/ConsoleStatusPresentation.swift | Localizes phase labels. |
| Sources/BrewFeatureConsole/Resources/Localizable.xcstrings | Adds console catalog. |
| Sources/BrewFeatureConfig/Views/ConfigView.swift | Localizes configuration UI. |
| Sources/BrewFeatureConfig/ViewModels/ConfigViewModel+Sections.swift | Carries localized section titles. |
| Sources/BrewFeatureConfig/ViewModels/ConfigViewModel.swift | Uses shared error copy. |
| Sources/BrewFeatureConfig/ViewModels/ConfigSectionItem.swift | Stores localized titles. |
| Sources/BrewFeatureConfig/Resources/Localizable.xcstrings | Adds configuration catalog. |
| Sources/BrewCore/Operations/OperationFailure.swift | Removes UI copy from core errors. |
| scripts/localize | Adds catalog synchronization and verification. |
| README.md | Documents translations. |
| Package.swift | Adds package localization resources and dependencies. |
| Homebrew/Views/SidebarItem.swift | Localizes sidebar titles. |
| Homebrew/Views/MainSidebarView.swift | Uses localized sidebar titles. |
| Homebrew/SelfUpgrade/HelperSelfUpgradeHandoff.swift | Localizes handoff errors. |
| Homebrew/Localizable.xcstrings | Adds app catalog and en-GB seed. |
| Homebrew/Features/MainWindow/Views/MainWindowView.swift | Localizes window navigation copy. |
| Homebrew/Features/CrashReporting/CrashReportDialog.swift | Localizes crash dialog. |
| Homebrew/Debug/DebugMenuCommands.swift | Marks debug copy verbatim. |
| Homebrew/BrewApp.swift | Localizes help menu. |
| Homebrew.xcodeproj/project.pbxproj | Registers en-GB region. |
| Brewfile | Adds jq tooling. |
| Brew-Unit.xctestplan | Fixes unit-test locale. |
| Brew-UI.xctestplan | Fixes UI-test locale. |
| ARCHITECTURE.md | Documents localization architecture. |
| AGENTS.md | Documents localization workflow. |
| .github/workflows/swift_quality.yml | Verifies and reports catalogs. |
| .github/workflows/pr_build_test.yml | Checks catalog synchronization. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
BrewUILint gains localized_copy (every literal is Text(bundle:comment:), String(localized:bundle:comment:) or Text(verbatim:)) and localization_layer (no localisation outside BrewUIComponents, BrewFeature* and the app). The build workflow checks the catalogs against the code after its Xcode build, the Swift Quality job runs scripts/localize verify and writes the translation status to the step summary. Also points the lint and build workflows at Homebrew/ — the app folder was renamed from Brew/ and the workflows never followed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the first translations (Licence, catalogue, and the sidebar titles in the app catalog so macOS lists English (UK) for the app) and documents the workflow for translators and contributors. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
graeme
force-pushed
the
add-localization-system
branch
from
September 22, 2026 13:26
e6ad468 to
7ab192c
Compare
3 tasks done
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 and why
The app could not be translated. User-facing copy sat as bare literals in views, and some of it sat
below the UI layer in the core and repository packages, where no catalog would ever reach it. This
adds the machinery and moves every string into it.
Each UI target now owns a
Localizable.xcstringscatalog: theHomebrewapp target for the window,sidebar and menus, plus
BrewUIComponentsand eachBrewFeature*package. That is 238 keys, eachwith a translator comment saying where it appears and what any
%@or%lldstands for. Copycrosses layers as
LocalizedStringResourceso the bundle and comment travel with the key, and theerror wording that used to live in the domain layers is now worded in
BrewUIComponents/Copy/BrewErrorCopy. Homebrew's own output stays verbatim English.Two BrewUILint rules hold the line.
localized_copyrequiresbundle: #bundleand acomment:atevery call site, since a package string without
#bundlesilently never localises.localization_layerrejects localising calls outside the UI packages.scripts/localizewraps Xcode's own tools:syncupdates the catalogs from a build,verifyfailson a catalog outside the UI layer, a key with no comment, an empty translation, or a language the app
bundle does not ship, and
statusreports coverage into the CI job summary. Swift Quality runsverify, and Build and Test runssync --checkafter the build so copy cannot drift from itscatalog. en-GB is seeded, 8 of 238 keys, to prove the path end to end. The README documents adding a
language, translating a string and changing English copy.
Validation
macOS 26.5, Apple silicon, Xcode 26.5, Swift 6.3.2.
scripts/test: pass, 1104 BrewKit tests and 30 BrewUILint testsSwiftFormat lint, SwiftLint strict and BrewUILint over the production tree: clean
scripts/localize sync --checkandscripts/localize verify: passNot run:
scripts/test-uiandscripts/test-e2eI followed the conventions and workflow, checked for duplicate PRs and kept this change focused.
I added regression coverage for bug fixes or explained why automated coverage is impractical, and reported the relevant validation above.
Screenshots
Not applicable. Every string keeps its exact English wording and en-GB matches it's meaning in alternative dialect, so
there is no before and after to show.
AI assistance
Claude Opus 5 via Claude Code wrote the extraction, the lint rules, the script and the docs,
directed and reviewed by me commit by commit. The checks above ran in that session.
ü§ñ Generated with Claude Code