Skip to content

Add Simplified Chinese localization (String Catalog) + in-app language switcher - #113

Closed
MJorgin wants to merge 2 commits into
robbietilton:mainfrom
MJorgin:feature/zh-Hans-localization
Closed

MJorgin wants to merge 2 commits into
robbietilton:mainfrom
MJorgin:feature/zh-Hans-localization

Conversation

@MJorgin

@MJorgin MJorgin commented Sep 24, 2026

Copy link
Copy Markdown

What this does

Adds a Simplified Chinese (zh-Hans) localization to Compositor, and a small Language menu so people can override the system language without changing macOS settings.

Two commits:

  1. feat(i18n): localize the interface with a String Catalog (zh-Hans) — adds Compositor/Localizable.xcstrings (source language en, zh-Hans added to knownRegions) and routes user-visible strings through it.
  2. feat(i18n): add a Language menu (System / English / 简体中文) — LanguageSettings stores the override in UserDefaults; the panel reuses the existing FloatingPanelController.

Coverage

  • Menus — File/Edit/View/Select/Image/Filter/Layer items. The dynamic filter and adjustment names are localized through a new localizedName on FilterKind/AdjustmentKind (English raw value stays the catalog key).
  • Tool rail — every tool's name plus the status-bar hint. That hint was one giant nested ternary, which SwiftUI type-infers as String and therefore never localizes; it is now a switch of LocalizedStringKey literals (toolHintMessage).
  • Inspectors — transform, brush, gradient, type, shape, marquee/lasso/wand, levels, hue/saturation, curves, effects, Camera Raw, image/canvas size, trim.
  • Enum pickers — every segmented control that rendered a raw value (sampling, selection mode, blend modes, Camera Raw pages, units, alignment, …) now shows a localized name.
  • Dialogs — the unsaved-changes NSAlert (AppKit strings don't flow through SwiftUI's catalog, so they use String(localized:), with %@ for the document name).

Notes for review

  • extractionState is "legacy" in the catalog. Symbol generation can't produce valid Swift identifiers for keys like %, Type, or the Feather / Feather… pair, and legacy opts those keys out of symbol generation while keeping runtime lookup intact. Build logs before that change were full of This key is too close to a reserved keyword / would generate the same symbol.
  • Chinese terminology follows Simplified Chinese Photoshop (图层, 蒙版, 羽化, 色阶, 曲线, 内容感知填充, …), so the UI reads the way PS users expect.
  • Untranslated keys remain "new" in the catalog for strings that are intentionally left alone (file magic like 8BPS, internal identifiers).

Testing

  • xcodebuild -project Compositor.xcodeproj -scheme Compositor -configuration Debug build succeeds (Xcode 26.1.1, ad-hoc signing).
  • Ran the app with -AppleLanguages (zh-Hans): menus, tool rail, status bar, and the sheets listed above render in Chinese; switching the Language menu to English and back changes the interface after relaunch.
  • Verified the compiled zh-Hans.lproj/Localizable.strings in the built bundle contains the expected keys.

Add Compositor/Localizable.xcstrings (source language en, zh-Hans added to
knownRegions) and route every user-visible string through the catalog.

Coverage:
- menus: File/Edit/View/Select/Image/Filter/Layer items, plus the dynamic
  filter and adjustment names via localizedName on FilterKind/AdjustmentKind
- tool rail names and the status-bar hint for every tool (the giant ternary
  hint became a switch of LocalizedStringKey literals)
- inspectors: transform, brush, gradient, type, shape, marquee/lasso/wand,
  levels, hue/saturation, curves, effects, camera raw, image/canvas size, trim
- every segmented picker that showed an enum rawValue (sampling, selection
  mode, blend modes, camera raw pages, units, alignment, ...)

The catalog uses extractionState "legacy" so Xcode does not try to generate
Swift symbols for keys that cannot produce one ("%", "Type", "Feather" vs
"Feather…").

Chinese terminology follows Simplified Chinese Photoshop (图层, 蒙版, 羽化,
色阶, 曲线, 内容感知填充, ...).
Compositor now follows the system language by default, and the new
"Language…" item in the app menu lets people override it.

- LanguageSettings stores the choice in UserDefaults and writes/removes
  AppleLanguages accordingly; System removes the override
- the panel reuses the existing FloatingPanelController so it matches the
  Keyboard Shortcuts window
- the notice explains that a restart applies the change

The switcher is additive: with no selection, macOS already picks English or
Simplified Chinese from the system preference.
@MJorgin

MJorgin commented Sep 24, 2026

Copy link
Copy Markdown
Author

Quick context: I put this together while setting Compositor up for Simplified Chinese users, and kept the terminology aligned with Simplified Chinese Photoshop (图层 / 蒙版 / 羽化 / 色阶 / 曲线 / 内容感知填充) so it reads naturally to anyone coming from PS.

Happy to change anything you would prefer — if you would rather merge only the translations and drop the in-app Language menu, I can split that out. Same for wording, terminology, or file layout.

The catalog uses extractionState "legacy" deliberately: keys like % , Type, and the Feather / Feather… pair cannot generate valid Swift symbols, and legacy opts them out of symbol generation while keeping runtime lookup working.

Thanks for open-sourcing the app.

@robbietilton

Copy link
Copy Markdown
Owner

Thanks for putting this together, and sorry to turn it down.

Localization is on hold for now, for the same reasons I gave on #57: Compositor is new and it's just me maintaining it. Localization isn't a one-time contribution, since every string I write from then on needs translating, and I can't review translations in languages I don't read, which would mean shipping text I can't verify.

I'll revisit it once things settle, and when I do, a String Catalog is genuinely the useful groundwork. Closing for now.

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.

3 participants