본문으로 건너뛰기
cccskills
무료GitHub에 공개

vibe-strings

The localization pipeline — VibeStrings.h registry conventions, the extract-strings.sh preprocessor pass and extractionState rules, catalog and InfoPlist.xcstrings mechanics, authoring translations and the untranslated-keys release gate, translation terminology and per-language conventions, the pseudolocale audit, and the localized App Store product page. Use when adding or rewording any user-facing string, translating catalog keys, editing VibeStrings.h or the .xcstrings catalogs, debugging make strings / make check-strings, testing a language, or updating App Store copy or screenshots.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

실행 점수 미검증

설치 방법 보기

포함된 파일(1)

  • SKILL.md15.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Localization pipeline

The always-loaded rules live in the root AGENTS.md (every string is a STR_* in VibeStrings.h; make strings after touching UI strings; display names are never identifiers). This skill holds the machinery.

Languages and catalogs

English is the source language; every other language is whatever the catalogs contain — don't hardcode the list anywhere, read it from Resources/Localizable.xcstrings (jq -r '[.strings[].localizations | keys] | flatten | unique'). Extraction preserves translated text. normalize() updates English defaults and marks other languages needs_review when the English changes. Translations follow Apple's own macOS terminology for standard menu items rather than literal translation (German File is Ablage, not Datei), while DJ terms keep the loanwords the hardware uses (FX, Delay, and PITCH outside German, which uses TEMPO). Test a language with open <app> --args -AppleLanguages '(xx)'.

knownRegions stays at XcodeGen's default (Base, en) and that is not a bug: it doesn't gate which languages ship. The XCStrings compiler emits every language present in the catalogs, CFBundle negotiates on the .lproj directories actually in the bundle, and XcodeGen derives knownRegions from localized file references so a spec key for it is ignored. It only affects Xcode's project UI and which languages -exportLocalizations defaults to.

Authoring translations

Coverage is checked by make check-translations and by nothing else. make check-strings is a round-trip diff — it re-runs sync+normalize on a copy and diffs, answering "does the catalog match the source" — and sync never writes a language other than en, so a key with only an en unit round-trips byte-identically and passes. The build is no better: xcstringstool compile exits 0 on a partial key and emits an xx.lproj without it, the lookup misses, and the macro's default value renders English in that locale alone. Both are silent, so an untranslated key ships English in every other locale.

vibe-libs' tools/strings/check-translations.sh covers every catalog Vibe's config lists — Localizable.xcstrings, InfoPlist.xcstrings and ThemeNames.xcstrings — and tests for missing any catalog language, not for "has only en". A key translated into a few languages to check layout would pass the weaker test while looking done. The required language set is the union across keys in Localizable.xcstrings. Adding a language there makes it required in all three authored catalogs. The widget catalog is derived from widget.* and checked by make check-strings. needs_review units are reported, never fatal — a reword flips them by design and they keep shipping. Both release paths gate on it via asc_require_translations, before the archive.

Translations are authored directly into the catalog JSON — there is no export/import round trip. Each unit is localizations.<lang>.stringUnit = {"state": "translated", "value": …}, with the language keys sorted within each entry. Formatting never needs matching by hand: merge with any tool, then run make strings, which re-serializes the whole catalog canonically (jq --indent 2). When merging programmatically, assert per language that the set of languages exactly matches the catalog's and that every %@/format specifier from the English value survived — missing or mismatched specifiers can lose information, print invalid values or crash.

Conventions (the settings.permissions*, *always_on_top and playlist.grant.* keys are worked examples of all of these):

  • Register follows modern Apple style per language: informal where Apple is informal (German du, Spanish tú, Italian tu, Dutch je, Hungarian te), formal elsewhere (French vous, Russian/Ukrainian/Bulgarian вы-forms, Greek, Indonesian Anda, Korean/Japanese polite).
  • Quote styles around %@ and names are the locale's own: „…“ (de, cs, sk, bg), „…” (pl, hu, ro, hr), « … » spaced (fr), «…» unspaced (ru, uk, nb, el), ”…” (sv, fi), ‘…’ (nl, ko), 「…」 (ja, zh-Hant), “…” (zh-Hans and the rest).
  • ~/Music is named by its localized Finder name (Musik, Musique, Música, Hudba, Zenék, ミュージック, 音乐/音樂, …), never transliterated.
  • Ellipsis is the real … character, unspaced, in every language (Öffnen…).
  • Menu vs. checkbox casing: where a language title-cases menu items (English, Turkish), the paired sentence-case settings key differs (Her Zaman Üstte / Her zaman üstte); most European languages use sentence case in both, so the pair is identical.

To verify a translation landed without eyeballing: rebuild, direct-exec the binary with -AppleLanguages '(xx)' in argv, and assert through the debug channel (dump_menu shows live localized titles — the menu bar reading Ablage confirms the language took). The vibe-debug skill has the mechanics.

The VibeStrings.h registry

Each entry is one line: NSLS(key, value, comment), a local macro that lifts out the fixed NSLocalizedStringWithDefaultValue(key, nil, NSBundle.mainBundle, …) scaffolding, with columns padded per #pragma mark section so the file reads as a table. Keys are symbolic and stable (menu.file, label.bpm, waveform.style.detailed), never the English text, so rewording a button doesn't orphan its translations: extraction rewrites the catalog's en from the new default and flips every other language to needs_review — they keep shipping while awaiting review — rather than minting a new key. The exception is a reword that changes the format specifiers, or moves the string to another surface with another meaning: that takes a NEW key, and the old entry is deleted from the header and the catalog. Kept translations would still expect the old arguments. make check-strings rejects mismatches for the format-specifier forms checked by check_specifiers().

The English lives in the macro's default value: it is the enforced source of the catalog's en values (xcstringstool sync alone only stamps en on first sight, so the script copies a changed default over it) AND the fallback if a lookup ever misses, so the app can never render a raw menu.file. Key prefixes: menu.*, transport.* (shared by menu items and the transport buttons' a11y labels), label.*, a11y.*, error.*, waveform.style.*, settings.*.

Call sites

Call sites use a STR_* macro and nothing more — no key, no English text, no translator comment inline:

NSMenu *fileMenu = Submenu(mainMenu, STR_MENU_FILE).submenu;
NSString *bpmText = [NSString stringWithFormat:STR_LABEL_BPM, formatted];

VibeNotLocalized(s) (in the pch, no runtime effect) marks a user-visible string deliberately kept in English — format acronyms, layout punctuation, instrument-scale glyphs, product names, titles AppKit never draws. Every unwrapped @"..." in UI code should be one or the other.

Working the display-names-are-never-identifiers rule: WaveformRenderer splits +styleIdentifier (stable; the registry key and the NSUserDefaults value) from +displayName (localized, display only). The VibeAudioFileFormat names (AudioFileFormat.h) are the inverse case: never localized, because they are compared with isEqualToString: and archived into the metadata cache. Locale-dependent numbers (kHz, BPM, pitch %) go through Formatters' decimalString:fractionDigits: / signedPercentString:, not %.1f.

Extraction pipeline

The scripts are vibe-libs' tools/strings, and Vibe's config is Resources/strings-config.sh. It names the registry, the catalogs, the swept sources, and the widget's subset. The piece's AGENTS.md documents each key. A change to a script lands in vibe-libs first, with a case in its self-test.

make check-strings fails when the catalog differs from extraction. Removing the last call site does not remove a registry macro. Delete the unused macro too. vibe-libs' tools/strings/extract-strings.sh first runs VibeStrings.h through the C preprocessor — it builds a throwaway TU referencing every STR_* and clang -Es it — because xcstringstool matches localization macros by name AND arity, and a three-argument NSLS(key, value, comment) fits no shape it knows; even -s NSLS extracts zero keys. Parsing the real expansion is exact where a regex over the header would quietly mis-parse, and clang fails loudly on a malformed entry.

It then extracts from that expansion plus every other first-party .m/.mm/.h (so a stray inline NSLocalizedString can't hide), syncs, and normalizes: source-language units get promoted new → translated (the XCStrings compiler emits a .strings file only for translated units, so without this the app would ship no en.lproj/Localizable.strings at all), and every live key is marked extractionState: manual. The manual mark is a shield, not bookkeeping: an Xcode build emits no .stringsdata for ObjC, so Xcode's own catalog pass sees every non-manual key as unreferenced, flags each one stale — one warning apiece — and writes the stale marks into the checked-in catalog. manual declares a key externally managed and Xcode then leaves it alone. But xcstringstool sync honors manual too — it skips such keys entirely, no comment updates and no staleness — so the script strips the marks just before syncing and re-applies them after; a key whose VibeStrings.h entry was deleted comes through that gap marked stale and stays visible (in --check diffs and as a single honest Xcode warning) until it is deleted from the catalog.

Extraction is deliberately NOT a build phase: Xcode has no build-time String Catalog extraction for Objective-C (clang emits .stringsdata only for Swift; the only xcstrings build task is compile), and a phase rewriting a checked-in file would flip VIBE_GIT_DIRTY on every build. Keep the stock NSLocalizedString* names — a custom macro would need LOCALIZED_STRING_MACRO_NAMES, which no shipped Xcode binary reads. One consequence of the registry living in a header: a macro nothing references still yields a catalog key, so delete the entry when the last call site goes. One key, one default: a key extracted with two different values fails both make strings and make check-strings, since sync would only warn, keep one, and render it at the other's call sites in every language. Two surfaces that want the same text share one macro.

ThemeNames.xcstrings

Resources/ThemeNames.xcstrings (table ThemeNames) is the third catalog and, like InfoPlist.xcstrings, is hand-managed — extraction never touches it, and no STR_* macro is involved. Keys are built-in theme identifiers (technical); the value overlays the theme JSON's English name per language via localizedStringForKey:value:table: in AppSettings.displayNameForThemeIdentifier:. A theme with no entry ships its JSON name everywhere — a theme added by pull request needs no catalog work. Adding a translation means adding the key with every catalog language; make check-translations covers this catalog on the same terms as the other two.

InfoPlist.xcstrings

Resources/InfoPlist.xcstrings carries the bundle name, the copyright, and the CFBundleTypeName values. It also carries the local-network usage description. Its keys are plist keys, EXCEPT the document-type names, which are keyed on the English type name — that is how Launch Services looks them up, and it does work (lsregister -dump shows the localized names). It is covered by make check-translations on the same terms as Localizable.xcstrings, which matters because its failure mode is even quieter: a missing type name shows up only in the Finder's Get Info and Open With, nowhere in the app. CFBundleName is added to this catalog by Xcode's build, not by hand: it is kept with the same value in every language so the entry stays stable instead of reappearing as an untranslated new unit on the next build. The copyright reaches the About window only through objectForInfoDictionaryKey:; plain infoDictionary[…] does NOT apply InfoPlist.strings.

Pseudolocale audit — finding strings that escaped

Build a pseudolocale from the catalog (bracket + accent + pad every value, copying format specifiers verbatim), xcstringstool compile … -l en-XA it into the built app's Contents/Resources, rename the output to Localizable.strings, re-sign (codesign -f -s - --preserve-metadata=entitlements), and launch with --args -AppleLanguages '(en-XA)'. Anything rendering without brackets never went through the bundle. Padding also surfaces layout overflow — the menu bar and the 96pt PITCH label are the tightest spots.

App Store product page

The App Store product pages are localized too, from Assets/app-store/ (copy, screenshot captions, and generated screenshots per catalog language AND per platform, copy/<lang>/<platform>/ — format in its README; versions are per platform in ASC, so macOS and iOS each need their own text). make appstore-validate-copy validates it; make appstore-upload-metadata uploads it via the vibe-release skill's shared API key. The catalog remains the source of which languages exist; bg ships in-app only, because the App Store has no Bulgarian product page.

copy/<lang>/<platform>/whats-new.txt must be rewritten for every release in every locale — App Store Connect blocks submission when any locale lacks it — and should stay in step with the new CHANGELOG.md section: same features, App Store voice, each locale's own terminology (the same register and quote conventions as Authoring translations above).

The exception is a platform's FIRST version, which takes no release notes at all. ASC refuses the field outright (Attribute 'whatsNew' cannot be edited at this time), so the uploader omits it and says so. The file still has to exist for the other platform. Under Universal Purchase one version string can be one platform's first version and the other's tenth.

Screenshot captions are the one place a translation can fail the build. They are burned into the image, so a caption that will not fit shrinks toward 72% of nominal and then errors out rather than rendering unreadably small. macOS captions are a headline plus a subhead; iOS is headline-only at 1.9x nominal, which makes it much the tighter of the two — expect some languages to need a looser translation rather than a literal one, and note the iPhone canvas binds before the iPad. appstore-validate-copy measures whether text fits, not whether it breaks well. ja and zh wrap at any character (kinsoku is deliberately unimplemented), so a caption can split a word across lines and still validate. Look at the rendered CJK screenshots and shorten until each sits on one line.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Check and document Vibe's audio quality and conformance. Runs public conformance and torture corpora (IETF CELLAR FLAC files, ISO/IEC 14496-26 Layer III, FFmpeg FATE samples, McGill WAVE and AIFF samples, EBU BW64 tests, Chromium and Firefox media tests, TagLib test data) through the real decoders. Valid files must match their FLAC MD5 or reference decode within the ISO accuracy limits, decode to the length the handle states and seek exactly. Every file must finish with finite samples. Also regenerates docs/audio-quality.md's charts and its corpus results matrix. Use after changing a decoder, a parser, the file-open path or the resampler, before a release, when asked whether decoding is correct or conformant, or to update the audio quality page.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

Launch, drive, inspect, and visually verify the Vibe app — macOS, and the iOS simulator loop (launch-ios.sh, the debug-ios.sh command channel, the drive-ios.sh touch driver for taps and drags, silent flags, screenshots, host-side log streaming). Use whenever a change needs end-to-end verification, a screenshot of the running app, playback/UI state inspection, or appearance (light/dark) testing.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

vibe-perf

무료

Measure Vibe's performance and compare two versions of the code. VibeBenchComponents drives the production decode, open, seek, resample, pitch, waveform, tempo/key, meter, metadata and cache code in-process with no app, counting retired instructions as well as wall and CPU time; perf.py builds it at any git ref and runs two refs interleaved (make bench-components); profile.sh finds one benchmark's hotspots; --analyze proves an analyzer change exact. Also owns the performance page (docs/performance.md): component charts per release (make bench-components-releases), app benchmarks (make bench-app), both (make bench-releases), redraw (make bench-report). Use before and after any change that could move cost, to find where time goes, to choose between implementations, to back a performance claim with numbers, or when asked to update, rerun or regenerate the performance page for a release, pre-release or new machine.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

vibe-pr

무료

How to write a Vibe pull request title and description — simple, clear English that says what the change does, its benefit, and why, with pros and cons, charted results, and screenshots where they apply. Use when opening a pull request, or writing or rewriting a PR title or description.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

Build, sign, notarize, and ship Vibe on both platforms — the macOS Developer ID (make release) and App Store (make appstore-build) paths, the iOS App Store path (make appstore-build-ios), the GitHub release publish (make github-release), the Homebrew tap bump (make brew-set-version), the localized product-page metadata upload (make appstore-upload-metadata), the shared App Store Connect API key and its Admin-role requirement, and the signing traps each script preflights. Use when cutting a release, distributing a build, shipping or TestFlighting the iOS app, updating App Store copy or screenshots, or debugging a signing/notarization/upload failure.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

Stress, soak, fuzz, and torture the running Vibe app against a folder of real audio files — seeded random controller actions without pointer input, with consistency, leak, hang, and crash oracles, a single-playlist skip/seek torture suite for the delivery races that only open when transport outruns the metadata scan, a deterministic cloud-loading scenario suite over a fake file provider (download ordering, the foreground hold, the open deadline), plus the sanitizer (ASan/UBSan/TSan) and malloc-debug build matrix. Use for soak or endurance runs, memory-leak and resource-growth hunting, race hunting, fuzzing the file-loading path, hammering skips and seeks on a large playlist, testing cloud/placeholder loading order, or minimizing a failing run to a repro.

한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.

cmicali/vibe492026년 10월 24일 업데이트

cmicali의 스킬 모두 보기

이 스킬의 문제 신고