LOCALIZATION.md
This guide describes how Mos 4.0 handles localisation, how to extend it, and how to keep every language in sync with new features such as the Buttons panel and shortcut catalog.
Mos ships Xcode’s string catalogs instead of legacy .strings files. There are two catalogs and they must remain separate:
| File | Scope | Notes |
|---|---|---|
Mos/Localizable.xcstrings | Strings referenced from Swift (NSLocalizedString) | Keys are either human-readable phrases ("Auth", "Current Version") or camelCase identifiers that mirror code constants (e.g. appExpose, categoryFunctionKeys, shortcut identifiers in SystemShortcut.Shortcut). Do not rename identifiers—Swift enums and persistence depend on them. |
Mos/mul.lproj/Main.xcstrings | Interface Builder (storyboards / XIBs) | Keys are Interface Builder Object IDs (2AK-Pu-mot.title). Xcode regenerates them on build. Never hand-edit Object IDs. |
mul.lproj is the canonical bundle for storyboard strings because we support many locales. Xcode fans out per-language nibs at build time, so do not split Main.xcstrings into multiple folders manually.
The project still targets macOS 10.13, so always use NSLocalizedString(_:comment:) in Swift rather than String(localized:).
Both catalogs expose the same set of locales (plus the English source column):
de, el, ja, ko, ru, tr, uk, zh-Hans, zh-Hant, zh-Hant-HK, zh-Hant-TW
To verify coverage and catch untranslated rows:
python3 - <<'PY'
import json, pathlib
catalog = json.loads(pathlib.Path("Mos/Localizable.xcstrings").read_text())
dupes = [(k, lang) for k, row in catalog["strings"].items()
for lang, unit in row["localizations"].items()
if lang != "en" and unit["stringUnit"]["value"] == row["localizations"]["en"]["stringUnit"]["value"]]
print(f"{len(dupes)} duplicate values (same as English). Sample:", dupes[:10])
PY
Run the same script against Mos/mul.lproj/Main.xcstrings if you suspect orphaned storyboard strings. A “duplicate” is acceptable when the official term matches English (e.g. “Launchpad”), but treat the report as a to-do list for translators.
The 4.0 release added ~40 strings across the Buttons panel, shortcut catalog, onboarding, and the refreshed Preferences UI. Make sure every locale is updated before tagging a new build.
Use the terminology Apple ships in System Preferences / System Settings. Never swap in colloquial variants.
| English | 简体中文 | 繁體中文 | 日本語 | Notes |
|---|---|---|---|---|
| Preferences | 偏好设置 | 偏好設定 | 環境設定 | NOT “设置” or “Settings” |
| Accessibility | 辅助功能 | 輔助使用 | アクセシビリティ | System feature name |
| Mission Control | 任务控制 | Mission Control | ミッションコントロール | Use Apple’s official translation even if it matches English |
| Trackpad | 触控板 | 觸控式軌跡板 | トラックパッド | Hardware name |
Keep modifier symbols intact and append translated names only if the locale expects it:
✓ "⌘ Command" "⌥ Option" "⌃ Control" "⇧ Shift"
✗ "Command" "Option键" "Ctrl"
Localizable.xcstrings)// Step 1 – Add the string in code
button.title = NSLocalizedString("Auth", comment: "Authorization button title")
// Step 2 – Build once (⌘B) so Xcode refreshes the catalog
// Step 3 – Open Localizable.xcstrings and fill every locale column
// Step 4 – Keep the key identical; do not rename identifiers already used in code
Shortcut catalog: When adding a new SystemShortcut.Shortcut, make sure its identifier property becomes a key in Localizable.xcstrings. The Buttons panel and popup menus rely on NSLocalizedString(identifier, …) to show the translated shortcut name.
Main.xcstrings)Base.lproj.mul.lproj/Main.xcstrings.Localizable.xcstrings match the Swift constants (SystemShortcut identifiers, category names, etc.).zh-Hans), 台湾 (zh-Hant-TW), 香港 (zh-Hant-HK).Localizable.xcstrings and Main.xcstrings.# Pretty-print a catalog for manual diffing
jq '.' Mos/Localizable.xcstrings | less
# List storyboard keys that no longer exist in Base.lproj
python3 Scripts/find_orphan_storyboard_keys.py # create alongside other scripts if needed
# Export catalogs for external translators (creates XLIFF)
xcodebuild -exportLocalizations -project Mos.xcodeproj -localizationPath DerivedData/Localizations
# Reimport translated XLIFF back into the project
xcodebuild -importLocalizations -project Mos.xcodeproj -localizationPath DerivedData/Localizations
Shortcut/SystemShortcut.swift defines identifiers used in menus and the Buttons panel.Windows/PreferencesWindow/ButtonsView/, especially ButtonTableCellView.swift and PreferencesButtonsViewController.swift.Windows/WelcomeWindow and Windows/IntroductionWindow for onboarding copy.buttonsLog, eventMonitor, etc. in Localizable.xcstrings.When new modules land (e.g. gesture editor, theme manager), append them here so translators know where to look.
Keeping localisation healthy is part of every feature cycle. Touch the catalog as soon as you add UI copy, run the duplicate check before merging, and leave notes for community translators whenever the wording carries product context. That habit keeps all 12 languages launch-ready the moment we ship a new build.