asc localize metadata
Use this skill to pull English (or any source locale) App Store metadata, translate it with LLM, and push translations back to App Store Connect — all automated.
Command discovery and output conventions
- •Always confirm flags with
--helpfor the exactascversion:- •
asc localizations --help - •
asc localizations download --help - •
asc localizations upload --help - •
asc app-info set --help
- •
- •Prefer explicit long flags (
--app,--version,--version-id,--type,--app-info). - •Default output is JSON; use
--output tableonly for human verification steps. - •Prefer deterministic ID-based operations. Do not "pick the first row" via
head -1unless the user explicitly agrees.
Preconditions
- •Auth configured (
asc auth loginorASC_*env vars) - •Know your app ID (
asc apps listto find it) - •At least one locale (typically en-US) already has metadata in App Store Connect
Supported Locales
App Store Connect locales for version and app-info localizations:
ar-SA, ca, cs, da, de-DE, el, en-AU, en-CA, en-GB, en-US, es-ES, es-MX, fi, fr-CA, fr-FR, he, hi, hr, hu, id, it, ja, ko, ms, nl-NL, no, pl, pt-BR, pt-PT, ro, ru, sk, sv, th, tr, uk, vi, zh-Hans, zh-Hant
Two Types of Metadata
Version Localizations (per-release)
Fields: description, keywords, whatsNew, supportUrl, marketingUrl, promotionalText
App Info Localizations (app-level, persistent)
Fields: name, subtitle, privacyPolicyUrl, privacyChoicesUrl, privacyPolicyText
Workflow
Step 1: Resolve IDs
# Find app ID asc apps list --output table # Find latest version ID asc versions list --app "APP_ID" --state READY_FOR_DISTRIBUTION --output table # or for editable version: asc versions list --app "APP_ID" --state PREPARE_FOR_SUBMISSION --output table # Find app info ID (for app-level fields like name/subtitle) asc app-infos list --app "APP_ID" --output table
Notes:
- •Version-localization fields (description, keywords, whatsNew, etc.) are per-version.
- •App-info fields (name, subtitle, privacy URLs/text) are app-level and use
--type app-info. - •If you only have names (app name, version string) and need IDs deterministically, use
asc-id-resolver.
Step 2: Download source locale
# Download version localizations to local .strings files # (description, keywords, whatsNew, promotionalText, supportUrl, marketingUrl, ...) asc localizations download --version "VERSION_ID" --path "./localizations" # Download app-info localizations to local .strings files # (name, subtitle, privacyPolicyUrl, privacyChoicesUrl, privacyPolicyText, ...) asc localizations download --app "APP_ID" --type app-info --app-info "APP_INFO_ID" --path "./app-info-localizations"
This creates files like ./localizations/en-US.strings and ./app-info-localizations/en-US.strings. If download is unavailable, read fields individually:
# List version localizations to see existing locales and their content asc localizations list --version "VERSION_ID" --output table
Step 3: Translate with LLM
For each target locale, translate the source text. Follow these rules:
Translation Guidelines
- •description: Translate naturally, adapt tone to local market. Keep formatting (line breaks, bullet points, emoji). Stay within 4000 chars.
- •keywords: Do NOT literally translate. Research what users in that locale would search for. Comma-separated, max 100 chars total. No duplicates, no app name (Apple adds it automatically).
- •whatsNew: Translate release notes. Keep it concise. Max 4000 chars.
- •promotionalText: Translate marketing hook. Max 170 chars. This can be updated without a new version.
- •subtitle: Translate or adapt tagline. Max 30 chars — this is very tight, may need creative adaptation.
- •name: Usually keep the original app name. Only translate if the user explicitly asks. Max 30 chars.
LLM Translation Prompt Template
For each target locale, use this approach:
Translate the following App Store metadata from {source_locale} to {target_locale}.
Rules:
- description: Natural, fluent translation. Preserve formatting (line breaks, bullets, emoji). Max 4000 chars.
- keywords: Do NOT literally translate. Choose keywords native speakers would search for in the App Store. Comma-separated, max 100 chars total. Do not include the app name.
- whatsNew: Translate release notes naturally. Max 4000 chars.
- promotionalText: Translate marketing tagline. Max 170 chars.
- subtitle: Adapt tagline creatively to fit 30 chars max.
- name: Keep the original app name unless explicitly requested to translate it. Max 30 chars.
- Respect cultural context. A playful tone in English may need adjustment for formal markets (e.g., ja, de-DE).
Source ({source_locale}):
description: """
{description}
"""
keywords: {keywords}
whatsNew: """
{whatsNew}
"""
promotionalText: {promotionalText}
name: {name}
subtitle: {subtitle}
Step 4: Upload translations
Option A: Via .strings files (bulk)
Create a .strings file per locale in the appropriate directory.
Version localization example:
// nl-NL.strings "description" = "Je app-beschrijving hier"; "keywords" = "wiskunde,kinderen,tafels,leren"; "whatsNew" = "Bugfixes en verbeteringen"; "promotionalText" = "Leer de tafels van vermenigvuldiging!";
Then upload version localizations:
asc localizations upload --version "VERSION_ID" --path "./localizations"
App-info localization example:
// nl-NL.strings "subtitle" = "Leer tafels spelenderwijs";
Then upload app-info localizations:
asc localizations upload --app "APP_ID" --type app-info --app-info "APP_INFO_ID" --path "./app-info-localizations"
Option B: Via individual commands (fine control)
# Version localization fields (fine control). # Prefer passing the explicit version ID for determinism. asc app-info set --app "APP_ID" --version-id "VERSION_ID" --locale "nl-NL" \ --description "Je beschrijving..." \ --keywords "wiskunde,kinderen,tafels" \ --whats-new "Bugfixes en verbeteringen"
For app-level fields:
# Subtitle/name (app-info localization) is managed via app-info localizations. # Use the app-info localization .strings + upload flow (there is no `asc app-infos localizations ...` command). # # 1) Edit: ./app-info-localizations/nl-NL.strings # "subtitle" = "Leer tafels spelenderwijs"; # # 2) Upload: asc localizations upload --app "APP_ID" --type app-info --app-info "APP_INFO_ID" --path "./app-info-localizations"
Step 5: Verify
# Check all locales are present asc localizations list --version "VERSION_ID" --output table # Check app info localizations asc localizations list --app "APP_ID" --type app-info --app-info "APP_INFO_ID" --output table
Character Limits (enforce before upload!)
| Field | Limit |
|---|---|
| Name | 30 |
| Subtitle | 30 |
| Keywords | 100 (comma-separated) |
| Description | 4000 |
| What's New | 4000 |
| Promotional Text | 170 |
Always validate translated text fits within limits before uploading. Truncated text looks unprofessional. If translation exceeds the limit, shorten it — do not truncate mid-sentence.
Full Example: Add nl-NL and ru to Roxy Math
# 1) Resolve IDs deterministically (do not auto-pick the "first" row) # If you only have names, use asc-id-resolver skill. asc apps list --output table APP_ID="APP_ID_HERE" asc versions list --app "$APP_ID" --state PREPARE_FOR_SUBMISSION --output table VERSION_ID="VERSION_ID_HERE" asc app-infos list --app "$APP_ID" --output table APP_INFO_ID="APP_INFO_ID_HERE" # 2) Download English source (or your chosen source locale) asc localizations download --version "$VERSION_ID" --path "./localizations" asc localizations download --app "$APP_ID" --type app-info --app-info "$APP_INFO_ID" --path "./app-info-localizations" # 3) Read en-US.strings, translate to nl-NL and ru (LLM step) # 4) Write nl-NL.strings and ru.strings to: # - ./localizations/ (version localization fields) # - ./app-info-localizations/ (subtitle/name/privacy fields) # 5) Upload all asc localizations upload --version "$VERSION_ID" --path "./localizations" asc localizations upload --app "$APP_ID" --type app-info --app-info "$APP_INFO_ID" --path "./app-info-localizations" # 6) Verify asc localizations list --version "$VERSION_ID" --output table asc localizations list --app "$APP_ID" --type app-info --app-info "$APP_INFO_ID" --output table
Agent Behavior
- •Always start by reading the source locale — never translate from memory or assumptions.
- •Check existing localizations first — don't overwrite existing translations unless the user asks to update them.
- •Version vs app-info is different — version fields live under
--version "VERSION_ID"; subtitle/name/privacy live under--app ... --type app-info. - •Prefer deterministic IDs — do not select IDs via
head -1unless explicitly requested; use--output tablefor selection orasc-id-resolver. - •Validate character limits before uploading. Count characters for each field. If over limit, re-translate shorter.
- •Keywords are special — do not literally translate. Research locale-appropriate search terms. Think like a user searching the App Store in that language.
- •Show the user translations before uploading — present a summary table of all fields × locales for approval. Do not push without confirmation.
- •Process one locale at a time if translating many languages — easier to review and catch errors.
- •If upload fails for a locale, log the error, continue with other locales, report all failures at the end.
- •For updates to existing localizations — download current, show diff of what will change, get approval, then upload.
Notes
- •Version localizations are tied to a specific version. Create the version first if it doesn't exist.
- •
promotionalTextcan be updated anytime without a new version submission. - •
whatsNewis only relevant for updates, not the first version. - •Use
asc-id-resolverskill if you only have app/version names instead of IDs. - •Use
asc-metadata-syncskill for non-translation metadata operations. - •For subscription/IAP display name localization, use
asc-subscription-localizationskill instead.