Skip the optional xcstringstool compile (e.g. on a non-macOS machine)
September 2, 2026 · View on GitHub
Rockxy uses native Xcode String Catalogs (.xcstrings). The catalogs checked
into the repository are the single source of truth for every translation. There is
an app-language picker under Settings › Appearance › Language. System Default
follows the macOS language; choosing another bundled language updates the running
Rockxy UI immediately without stopping capture or reopening the app.
The picker reads the localizations available in the application bundle instead of hardcoding a language list, so a newly contributed and shipped catalog appears there automatically.
Where the strings live
| Catalog | Covers |
|---|---|
Rockxy/Localizable.xcstrings | All in-app UI strings |
Rockxy/InfoPlist.xcstrings | Info.plist user-facing text (e.g. the Local Network usage prompt) |
The source language is English (en). The first shipped translation is
Simplified Chinese (zh-Hans). knownRegions in
Rockxy.xcodeproj/project.pbxproj lists every language the project builds.
What contributions are welcome
Rockxy accepts localization contributions for every language, including:
- inaccurate, unnatural, overly literal, or culturally inappropriate wording,
- text that is too long, clipped, or unclear in its actual UI context,
- incorrect technical terminology or inconsistent wording,
- missing translator context and corrections to the English source,
- complete translations for a new language.
You do not need to be a programmer or have Xcode to report or repair a small translation. If you prefer not to edit a catalog, open a Translation Improvement issue with the language, where the text appears, the current wording, your proposed wording, and why it is better.
Writing localizable code
Use compile-time-extractable APIs so Xcode can find every string automatically:
Text("Start Proxy") // SwiftUI literals auto-localize
String(localized: "Request Body Options") // explicit localization
LocalizedStringResource("Make Rockxy reachable from the device")
Rules that keep extraction clean and translations safe:
-
No dynamic keys. Never build a key from a runtime value (
String(localized: String.LocalizationValue(name)),"\(count) item\(count == 1 ? "" : "s")"). The compiler cannot extract these and translators never see them. Pass aLocalizedStringResourceliteral into helpers instead of aString. -
Pluralization uses Apple's automatic grammar agreement, not a hand-rolled
"s"suffix:String(localized: "^[\(count) request](inflect: true)")For languages such as Chinese that have no plural inflection, the translated string is used verbatim — which is exactly why the ternary approach was unsafe.
-
Never translate identifiers. URLs, routes, MIME types, header names (
Content-Type), format tokens, code fragments, keyboard notation (⌘K), file extensions, and product/protocol names stay verbatim. If a whole string is an identifier, mark the catalog entry Don't Translate ("shouldTranslate": false), or keep it out of a localized API. -
Comments are for translators. Add a
commentto any catalog entry whose meaning is ambiguous out of context, and to flag tokens that must stay verbatim.
Translation principles
- Translate meaning in context. Prefer natural product language in the target language over word-for-word English structure.
- Preserve technical meaning. A polished sentence that changes how a proxy, certificate, rule, or destructive action behaves is not an acceptable translation.
- Keep the UI concise. Match the tone of a native macOS application and check that labels, buttons, menus, tables, and settings do not truncate.
- Be consistent. Reuse established translations for the same action or concept.
- Review generated drafts. Machine translation may be used as a starting point, but do not submit bulk-generated catalog output without fluent human review.
- Protect user data. Redact private URLs, headers, credentials, tokens, request bodies, and captured traffic from screenshots or examples.
Repairing an existing translation
Translation repair PRs are welcome, including PRs that change only one phrase. A small, focused correction is easier to review and does not need unrelated catalog entries, Swift tests, SwiftLint, SwiftFormat, or a changelog entry.
Without Xcode
For a small repair, GitHub's web editor or any UTF-8 text editor is enough:
- Fork the repository and create a branch from
develop. - Search
Rockxy/Localizable.xcstringsorRockxy/InfoPlist.xcstringsfor the exact text currently shown in the app. - In that entry's
localizationsobject, change only thevaluefor your locale. Do not rename the English key, reorder the whole file, or reformat unrelated JSON. - Run
python3 .github/tools/validate_xcstrings.py --no-compile. This needs only Python 3 and works without Xcode on macOS, Linux, and Windows (where the command may bepythoninstead ofpython3). - Open a PR against
developand complete the localization section of the PR template.
With Xcode
- Open
Rockxy.xcodeprojand select the affected.xcstringscatalog. - Filter by the target language or search for the English key/current translation.
- Edit only the affected localized value and review every variation shown by Xcode.
- Run the validator and verify the changed UI in Rockxy.
- Open a focused PR against
develop.
For either workflow, include the locale, affected screen or catalog key, and a short reason the wording is better. Useful evidence can be native-speaker knowledge, a platform terminology reference, a screenshot showing context or truncation, or a clear explanation of the technical meaning. A public citation is helpful when one exists, but it is not required for ordinary native-language corrections.
If the English source is wrong, say so explicitly. Changing an English key can affect every locale, so update the impacted translations or keep the source change in a separate PR that can receive broader review.
Adding a new language
Anyone may contribute a language through a normal reviewed pull request:
- Choose the standard locale identifier used by Apple, such as
de,fr,ja,ko,zh-Hant, orpt-BR. Use a region-specific locale only when the wording genuinely differs from the base language. - Open
Rockxy/Localizable.xcstringsandRockxy/InfoPlist.xcstringsin Xcode. - Add the same language to both catalogs from the catalog's + control.
Confirm Xcode also added the locale to
knownRegionsinRockxy.xcodeproj/project.pbxproj. - Translate every translatable entry. Preserve, exactly:
- placeholders and their order (
%@,%lld, positional%1$@/%2$lld), - plural / device variations,
- leading/trailing whitespace and newlines,
- Markdown and HTML fragments,
- keyboard notation, file extensions, and technical/product terms.
- placeholders and their order (
- Run
python3 .github/tools/validate_xcstrings.py --require <locale>until it is clean. The default validator also discovers and checks every locale present in either catalog. - Build Rockxy, select the new language under Settings › Appearance › Language, and inspect the important surfaces listed below.
- Open a PR against
develop. A maintainer reviews every.xcstringschange (see.github/CODEOWNERS) — this protects placeholder integrity and the shared English source, and does not block your contribution.
Translations may land as a first pass and be refined later, but a language should not be advertised as supported until its runtime coverage is complete — every translatable entry has a non-empty value.
What must remain unchanged
Unless the term has an established localized product spelling, preserve these exactly:
- placeholders and their order (
%@,%lld,%1$@,%2$lld), - protocol and product names (
HTTP,HTTPS,WebSocket,GraphQL,MCP,Rockxy), - header names, MIME types, URLs, routes, file extensions, and code fragments,
- keyboard notation and shortcuts,
- Markdown/HTML structure, intentional whitespace, and newlines.
If a technical token must move to make the sentence natural, use positional placeholders so its semantic argument order remains unambiguous; do not change the placeholder's conversion type.
Simplified Chinese terminology
Several English words carry more than one meaning in Rockxy, so the correct
Simplified Chinese depends on the English source context, not the word alone.
Never blanket-replace a Chinese term across the catalog — decide per entry, guided
by the English key. The validator enforces the high-confidence rules below as
deterministic forbidden- and required-term checks (see zh_hans_glossary_errors in
.github/tools/validate_xcstrings.py, mirrored in StringCatalogCoverageTests).
Required terms include 状态码, 脱敏, and token where the English source context
selects those meanings.
| English context | Use | Not | Notes |
|---|---|---|---|
| HTTP status code | 状态码 | 代码 | Compact table/breakpoint labels use Compact HTTP status code, whose English value stays Code. The bare Code key is retired because it collided with the VS Code app name. |
| VS Code app name (Open with menu) | Code (verbatim) | 代码 | An app/product name. Rendered with Text(verbatim:), never localized. |
| AI model token(s) — usage, context window, streaming | token (verbatim) | 令牌 | Model tokens are counted, not credentials. |
| Auth / pairing / access / sharing token | 令牌 | token | Credentials — keep 令牌. |
| The named Compose feature | Compose (verbatim) | 编写 | A product surface name. “user-authored / user-written” text still translates to 编写. |
| redact / redaction / redacted | 脱敏 / 已脱敏 | 遮盖 / 隐去 | One term everywhere for the privacy action. |
| certificate pinning | 证书固定 | 证书锁定 | The UI action to pin a request/favorite still uses 固定. |
| Protobuf wire format | 线格式 | 线路格式 | The Protobuf binary encoding. |
The Code example is the reason context matters: the same four letters are an HTTP
status-code column, a text-editor app name, and a source-code noun. Read the English
source and, when the meaning is genuinely ambiguous, add a translator comment to the
catalog entry rather than guessing.
The validator also checks direct, non-interpolated String(localized:) literals in
Rockxy/ against Localizable.xcstrings. Adding a runtime lookup without its catalog
entry is therefore a validation error instead of an English fallback in a shipped locale.
Runtime review
Catalog validation proves structure and placeholder safety, but it cannot judge language quality or layout. Before marking a new-language PR ready, select the language in Rockxy and review at least:
- onboarding and permission dialogs,
- Settings, including Appearance › Language,
- the main capture toolbar, traffic list, filters, and empty states,
- request/response inspectors and error messages,
- rules, breakpoints, certificate, import/export, and destructive confirmations,
- menus, tooltips, keyboard shortcuts, and
Info.plistpermission prompts.
Look for clipping, overlapping controls, untranslated English, incorrect plural or format substitutions, and wording that changes the action's meaning. Attach focused, redacted screenshots to the PR when a reviewer cannot infer the UI context from the catalog key alone.
Validation
Run the deterministic validator before pushing. It needs no secrets or third-party packages, so it runs the same way locally and in CI:
# Validate both catalogs and every locale discovered in either catalog
python3 .github/tools/validate_xcstrings.py
# Require a specific language (or several) explicitly
python3 .github/tools/validate_xcstrings.py --require zh-Hans,de
# Skip the optional xcstringstool compile (e.g. on a non-macOS machine)
python3 .github/tools/validate_xcstrings.py --no-compile
The validator checks: valid JSON, sourceLanguage == en, matching locale coverage
across both catalogs, full coverage for every discovered or explicitly required
language, parity with the Xcode project's knownRegions, no empty values, variation
parity, and placeholder parity between the source and every translation. When
Xcode's toolchain is available it also compiles each catalog with
xcrun xcstringstool compile. You can compile a catalog by hand too:
xcrun xcstringstool compile --output-directory /tmp/xcstrings Rockxy/Localizable.xcstrings
CI runs this validator on every pull request in the Localization Catalogs job.
Because the check carries no secrets and no write access, it runs safely on fork
PRs without exposing them to privileged jobs.
Translation platforms are mirrors only
If the project ever adopts an external translation platform, it is only a mirror.
The Git-tracked .xcstrings catalogs remain canonical. Any platform integration must
land translations the same way a human does — by opening a reviewed pull request that
edits the catalogs — and must never push directly to the repository or bypass the
maintainer review gate. Configure such tooling through the project owner; do not add
platform credentials or account details to the repository.