NOASSERTIONupdated 1mo ago
Let be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke /scripts/swiftui-ctx; do not assume the command is on PATH.
What can you do with Audit Swiftui Localization?
name: audit-swiftui-localization description: Audit macOS SwiftUI localization for correctness, current APIs, and production conventions. Use for that domain or as part of a full audit.
Bundled resource root
Let <swiftui-plugin-root> be the absolute plugin directory two levels above this SKILL.md. Resolve that path before running commands or opening shared references. When these instructions say swiftui-ctx, invoke <swiftui-plugin-root>/scripts/swiftui-ctx; do not assume the command is on PATH.
Audit SwiftUI Localization
AUDIT-ONLY Β· macOS-only Β· SwiftUI-only. Run this on a finished or in-progress macOS SwiftUI
project to detect β and flag β every way localization & internationalization goes wrong: hardcoded
strings that never reach a translator, Text(verbatim:) used both too much and too little, missing
String Catalog (.xcstrings), no translator comments, sentences built by interpolation (no
pluralization / grammar agreement), locale-unaware number/date formatting, and right-to-left-unsafe
layout. Findings are written to disk in the toolkit's unified schema. This is never a from-scratch
localization generator.
The core SwiftUI fact: Text("Save") with a string literal is a LocalizedStringKey and
auto-localizes β the literal becomes the catalog key. The defects are the ways code escapes that
auto-localization (a String variable, verbatim:, String(format:)) or feeds it un-translatable
input (a sentence assembled by interpolation).
β
Correct (grounded, not a placeholder). The consensus shape from swiftui-ctx lookup Text --json
is the (_ key: LocalizedStringKey) overload at 99% (the bare-String/(verbatim) form is the 1%
outlier) β so a string literal, ideally with a translator comment:, is what shipping Mac apps
overwhelmingly write:
// localized literal + translator context β NetNewsWire (macOS-shipping)
Text("label.text.unread", comment: "Unread")
Real permalink (verified, macOS 26 corpus): https://github.com/Ranchero-Software/NetNewsWire/blob/60295842054529c3450b91af15911cecb1a1cc4f/Widget/WidgetBundle.swift#L27
Β· Apple spec via Sosumi doc: https://sosumi.ai/documentation/swiftui/text. Refresh either with
swiftui-ctx lookup Text --json (VERIFY) and swiftui-ctx file <recommended.id> --smart (FIX).
Boundary / seam note (stay in lane)
AttributedStringstyling & markdown rendering belong toaudit-swiftui-typography-text. WhenText(verbatim:)or markdown is about rendering (styling,+concatenation), typography-text is primary; this skill owns only the catalog / translatability angle and emits across_ref: typography-texton a shared site (per<swiftui-plugin-root>/references/_shared/cross-ref-graph.md).- The blanket deprecated-API sweep belongs to
audit-swiftui-api-currency. A legacyNSLocalizedStringis flagged here for its translatability implication; currency owns generic deprecation. Emitcross_ref: api-currencyif both fire. - Date/number
FormatStylefor async-loaded data seams toaudit-swiftui-async-data; RTL mirroring of layout containers seams toaudit-swiftui-layout-and-tables;\.locale/\.layoutDirectionpreview coverage seams toaudit-swiftui-previews. Note in one line, route, do not double-own.
Domain rules (the four that catch most bugs)
- A string literal in
Text/Label/.navigationTitleIS localized; aStringvariable is NOT. TheText(_ key: LocalizedStringKey)overload localizes; theText(_ content: some StringProtocol)overload does not. Passing a variable silently bypasses the catalog (loc-02). verbatim:is a deliberate opt-OUT of localization. Right for a brand name / version / number; wrong for any human-readable UI copy (loc-01) β and its absence on a genuinely non-translatable literal pollutes the catalog (loc-09).- Never assemble a user-facing sentence by interpolation or
+. Plurals and grammar agreement need the String Catalog's variations (%lld, automatic grammar agreement /inflect), not Swift string-building (loc-06). - Never format numbers/dates for display with
String(format:)or a hand-built formatter. Those are locale-unaware; use aFormatStyle/.formatted()(loc-07).
Defect index (loc-01 β¦ loc-10)
id Β· tell Β· severity Β· fix Β· open reference. Severities: hard-fail (build break / never-correct),
warning (compiles but non-native / wrong in another language), advisory (judgment / hygiene).
All fixes are flag β localization corrections are judgment-heavy (key naming, comment wording,
translatable-or-not), so this skill shows the β
and the dev applies it; none are mechanical auto-fixes.
| id | One-line tell | Sev | Fix | Reference |
|---|---|---|---|---|
| loc-01 | Text(verbatim:) wrapping human-readable UI copy β drop verbatim: so it auto-localizes |
warning | flag | strings-and-catalog.md |
| loc-02 | a non-literal String passed to Text(_) / Label / .navigationTitle (StringProtocol overload β not localized) |
warning | flag | strings-and-catalog.md |
| loc-03 | NSLocalizedString(...) legacy macro in SwiftUI code β String(localized:) / inline key |
advisory | flag | strings-and-catalog.md |
| loc-04 | no .xcstrings String Catalog in the project (loose .strings or none) |
advisory | flag | strings-and-catalog.md |
| loc-05 | a UI literal with no comment: for translators |
advisory | flag | strings-and-catalog.md |
| loc-06 | a UI sentence built by interpolation / + β no pluralization / grammar agreement |
warning | flag | plurals-grammar-and-format.md |
| loc-07 | String(format:) / DateFormatter / NumberFormatter for display β locale-unaware β FormatStyle |
warning | flag | plurals-grammar-and-format.md |
| loc-08 | a directional SF Symbol Image(systemName: "β¦left/rightβ¦") doesn't mirror in RTL β .backward/.forward |
warning | flag | rtl-and-layout-direction.md |
| loc-09 | a brand / version / non-translatable literal in Text("β¦") NOT wrapped verbatim: β pollutes the catalog |
advisory | flag | strings-and-catalog.md |
| loc-10 | a hard-coded horizontal .offset(x:) / .position(x:) assumes LTR β mirror-unsafe |
advisory | flag | rtl-and-layout-direction.md |
loc-04 is project-level (file-presence, not a .swift tell β checked in ORIENT). loc-05 and
loc-09 are READ-judgment (no clean regex distinguishes "human-readable copy" from "a brand name") β
they are detected in READ/DETECT, not by the lint runner. No rule is dropped; each is routed below.
The real API, at a glance
Real (use these): Text(_ key: LocalizedStringKey) (literal β auto-localizes),
Text(_:comment:), Text(verbatim:) (opt-out), LocalizedStringKey, LocalizedStringResource
(macOS 13+), String(localized:_:) / String.LocalizationValue (macOS 12+ β doc lives under
/documentation/swift/, not /foundation/), FormatStyle / .formatted(β¦),
@Environment(\.layoutDirection), \.locale, flipsForRightToLeftLayoutDirection(_:),
InflectionRule (macOS 12+, automatic grammar agreement). Catalog format: the String Catalog
(.xcstrings), not legacy .strings/.stringsdict.
Avoid for display: NSLocalizedString (legacy), String(format:), hand-built DateFormatter /
NumberFormatter without a \.locale, raw \(interpolation) for sentences. Floor values are the
reconciled truth in <swiftui-plugin-root>/references/_shared/floors-master.md β read, never restate.
There is no localization hallucination blacklist; if a symbol's existence is in doubt, VERIFY (step 5).
The 8-step audit workflow (execute verbatim)
- ORIENT.
tree/findthe SwiftUI sources. Read the deployment target (project.pbxprojMACOSX_DEPLOYMENT_TARGET, orPackage.swiftplatforms:). Then check loc-04:find . -name '*.xcstrings'β if none (and the app has user-facing copy), the project ships no String Catalog. Note loose.strings/.stringsdictas the legacy form. Record both. - LOCATE. Run the shared hybrid lint runner:
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-localization --dir <sources> --json /tmp/loc.json --sarif /tmp/loc.sarif. It runs this skill's tier-1 grep tells (lint/grep-tells.tsv, loc-01/03/06/07/08/10) + the tier-2 structural ast-grep rule (lint/ast-grep/loc-02-nonliteral-text.ymlβ theString-variable-into-Textcase grep can't express), plus a per-file parse probe, and emits unified JSON + SARIF. Read itsparse_warningsβ a flagged file did not fully parse, so READ those by hand. The runner only LOCATES β never treat a hit as a finding. Engine + rule-file format + degradation:<swiftui-plugin-root>/references/_shared/lint-architecture.md. - READ. Open every located file in full β never pattern-match-and-patch blind. For each
Text/Label/title decide: literal-or-variable, human-readable-or-not, has acomment:. This READ is where loc-05 (missing comment) and loc-09 (a non-translatable literal that should beverbatim:) are judged β the lint can't see meaning. - DETECT. Apply the index. Assign each candidate a confidence; report a finding only at 100% certainty. "Is this literal human-readable UI copy or a brand/version token?" is the load-bearing call for loc-01/loc-09.
- VERIFY. For anything β€ ~70% confidence (a symbol you're unsure exists, a floor you can't place,
a "what do shipping apps actually write?" question) run both evidence sources. (a) Practice β
bash <swiftui-plugin-root>/scripts/swiftui-ctx lookup <api> --json(andswiftui-ctx deprecated <api>for a currency/deprecation rule): read itsconsensus(the canonical shape),recommendedpermalink,introduced_macos,co_occurs_with; alookupexit 3 (not-found, with a did-you-meansuggestion) corroborates a non-existent symbol. (b) Spec β confirm via Sosumi:curl -sSL https://sosumi.ai/<apple-path>usingreferences/source-directory.mdfor the path and<swiftui-plugin-root>/references/_shared/sosumi-reference.mdfor the protocol (neverWebFetchdeveloper.apple.com). Cross-checkintroduced_macosagainstfloors-master.md. The CLI contract is<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md. Promote with the citation or discard. - REPORT. Write each confirmed finding (output contract below). One finding per file, zero-padded,
ordered. Write the run's
_index.md. - FIX. Apply corrections under the fix-safety protocol
(
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md): clean-tree gate, findings-first. Every loc rule isfix_mode: flag-onlyβ leave findingsopenwith the β in## Correct. The β "Correct" is not a hand-written snippet β it is the swiftui-ctx consensus shape, backed by a real macOS-26 example fetched withbash <swiftui-plugin-root>/scripts/swiftui-ctx file <recommended.id> --smartwhose GitHub permalink (plus the Sosumidoc:) goes in## Source. - DOUBLE-CHECK. Re-confirm every citation still resolves and still says the floor it claims. If a reader applied a suggested β and it would introduce a new tell, loop that file back to DETECT.
Confidence gating (load-bearing)
Report a finding only at 100% certainty. Anything β€ ~70% goes to VERIFY (step 5) before it can
become a finding β never emit a speculative finding. loc-01 vs loc-09 turns entirely on the
human-readable-vs-token judgment; when unsure, carry as advisory and say so, don't assert.
Output contract
Inherits the toolkit's unified contract (full schema + body sections + frontmatter keys:
<swiftui-plugin-root>/references/_shared/finding-schema.md β do not restate it). Specialized for this
domain:
- Findings:
swiftui-audits/localization/<context>/NN-slug.md(one finding per file, zero-padded, ordered). Per-run index:swiftui-audits/localization/_index.md. domain: localization. Frontmatter is the canonical schema;fix_modeisflag-onlyfor every loc rule.availabilityreads fromfloors-master.md.sourceis an Apple URL + access date (fetched via Sosumi) orverify against Xcode 26 SDK. Emitcross_refper the seam note on shared sites.
Starter <context> folders (file here whenβ¦):
<context> |
File a finding here when⦠|
|---|---|
hardcoded-strings/ |
a String var escapes localization, or verbatim: is misused either way (loc-01, loc-02, loc-09) |
string-catalog/ |
the project ships no .xcstrings, or a UI literal lacks a translator comment: (loc-04, loc-05) |
legacy-api/ |
a legacy NSLocalizedString macro is used where a literal key / String(localized:) belongs (loc-03) |
plurals-and-grammar/ |
a sentence is built by interpolation/+ so plurals & grammar agreement break (loc-06) |
locale-formatting/ |
numbers or dates are formatted locale-unaware via String(format:) / a hand formatter (loc-07) |
rtl-layout/ |
a directional SF Symbol or a hard-coded horizontal offset/position breaks in RTL (loc-08, loc-10) |
New-folder rule: if a finding does not fit any existing context folder, create a new one under
swiftui-audits/localization/ with a lowercase-hyphen slug naming the sub-category, and note it in the
run's _index.md. Prefer an existing folder when the fit is reasonable; consistency across runs is a
hard requirement. Two runs over the same code produce structurally identical trees.
Reference routing
| File | Open when |
|---|---|
references/strings-and-catalog.md |
literal-vs-variable, verbatim: both ways, String Catalog (.xcstrings), translator comments, String(localized:) (loc-01/02/03/04/05/09) |
references/plurals-grammar-and-format.md |
sentence assembly, pluralization, automatic grammar agreement / inflect, locale-aware number/date FormatStyle (loc-06/07) |
references/rtl-and-layout-direction.md |
RTL: directional SF Symbols, layoutDirection, image mirroring, hard-coded horizontal offsets (loc-08/10) |
references/source-directory.md |
step VERIFY β the Apple/WWDC/practitioner source map fetched via Sosumi |
lint/grep-tells.tsv + lint/ast-grep/*.yml |
step LOCATE β this skill's declarative lint rule set fed to the shared runner (tier-1 grep + tier-2 structural ast-grep); edit here to tune detection |
Shared toolkit references (point in, never restate):
| Shared file | For |
|---|---|
<swiftui-plugin-root>/references/_shared/floors-master.md |
every floor/availability value (the reconciled truth) |
<swiftui-plugin-root>/references/_shared/hallucination-blacklist.md |
the canonical invented-name list |
<swiftui-plugin-root>/references/_shared/finding-schema.md |
the unified finding schema + frontmatter keys |
<swiftui-plugin-root>/references/_shared/fix-safety-protocol.md |
the 8-point fix-safety protocol (step 7) |
<swiftui-plugin-root>/references/_shared/sosumi-reference.md |
the Apple-doc spec fetch protocol (step 5 VERIFY) |
<swiftui-plugin-root>/references/_shared/swiftui-ctx-reference.md |
the practice-corpus CLI contract β lookup/deprecated/file --smart for the consensus shape + permalinked example (steps 5 VERIFY Β· 7 FIX) |
<swiftui-plugin-root>/references/_shared/cross-ref-graph.md |
seam ownership + cross_ref targets (typography-text, api-currency, async-data, previews, layout-and-tables) |
Detection accelerator
bash <swiftui-plugin-root>/scripts/swiftui-lint.sh --skill audit-swiftui-localization --dir <files-or-dir> [--json out.json] [--sarif out.sarif] β the toolkit's one shared hybrid lint engine, fed this
skill's declarative rules: tier-1 grep tells (lint/grep-tells.tsv, loc-01/03/06/07/08/10) +
tier-2 ast-grep (lint/ast-grep/loc-02-nonliteral-text.yml β a String variable passed to
Text(_), which grep cannot distinguish from a literal). It runs a per-file parse probe (surfaces
"did not fully parse" so a structural miss can't look clean), emits unified JSON + SARIF, and
degrades to grep-only with a notice if ast-grep is unreachable (npx --package @ast-grep/cli ast-grep; faster: brew install ast-grep). loc-04 (no .xcstrings) is a project-file check done in
ORIENT, not by the runner; loc-05/loc-09 are READ-judgment. It only LOCATES β always READ each hit in
full before reporting (step 3). The thin scripts/loc-lint.sh is a pointer to this runner. Engine +
rule-file format + JSON/SARIF shape + safety rails:
<swiftui-plugin-root>/references/_shared/lint-architecture.md.
Install
Add Audit Swiftui Localization to your client. Pick the one you use.
npx skills add yigitkonur/plugin-swiftuiInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add yigitkonur/plugin-swiftuiAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/yigitkonur/plugin-swiftui
cp -r plugins/swiftui/skills/audit-swiftui-localization ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
80 / 100
Excellent