TL;DR. Google Play Console and App Store Connect look like they use the same locale codes, but they don't. Play Console uses BCP-47 (zh-CN, iw, in) while App Store Connect uses Apple's own variants (zh-Hans, he, id). Mixing them up is the single most common way indie devs silently drop a market on submission. The gotchas are consistent: Simplified vs Traditional Chinese, legacy ISO codes Android still expects (iw=Hebrew, in=Indonesian, ji=Yiddish), and Norwegian's split (no vs nb/nn). This post gives you the full 40-locale mapping table, calls out every gotcha with the specific mismatch, and closes with a pre-submission checklist you can run before every release.
Why the codes don't map 1:1
Both stores technically follow the same international standard on paper, BCP-47, but each vendor made independent decisions decades ago about which subset to accept and how to write the exceptions. The result:
- Play Console stayed close to BCP-47 as it evolved and still accepts several ISO 639-1 legacy codes that predate the modern spec (
iw,in,ji). These are the codes Android's ownvalues-*folders use. - App Store Connect uses Apple's variant of BCP-47 that prefers the modern ISO codes (
he,id,yi) and adds explicit script tags for Chinese (zh-Hans,zh-Hant) instead of region tags (zh-CN,zh-TW). Apple's.lprojfolder naming follows the same rules.
The practical impact: when you submit an app to both stores, the same locale has two different codes. Copy-pasting from one to the other silently drops the market. App Store Connect doesn't error on zh-CN, it just doesn't find a match, so your Simplified Chinese screenshots never publish. Same story with Hebrew (iw on Play, he on ASC) and Indonesian (in vs id).
The 40-locale mapping table
This is the complete mapping for every locale both stores support as of 2026. Anywhere the codes are literally identical, we've left the column blank so the mismatches jump out.
The mismatches you actually need to memorize
| Language / Market | Google Play | App Store Connect |
|---|---|---|
| Simplified Chinese (China) | zh-CN | zh-Hans |
| Traditional Chinese (Taiwan) | zh-TW | zh-Hant |
| Hebrew (Israel) | iw | he |
| Indonesian | in | id |
| Yiddish | ji | yi |
| Norwegian (unified) | no | not supported, use nb only |
| Portuguese (Portugal) | pt-PT | pt-PT |
| Portuguese (Brazil) | pt-BR | pt-BR |
The identical pairs (safe to copy across)
| Language / Market | Both stores | Notes |
|---|---|---|
| English (US) | en-US | ASC default; Play calls this "American English" |
| English (UK) | en-GB | Also called "en-AU"-alias in some tooling |
| French (France) | fr-FR | Distinct from Canadian French |
| French (Canada) | fr-CA | ASC treats as full separate locale |
| German | de-DE | Play also accepts bare de |
| Spanish (Spain) | es-ES | Play also accepts es |
| Spanish (Mexico / Latam) | es-MX | ASC key for entire Latin America |
| Japanese | ja | Region tag optional in both |
| Korean | ko | Same |
| Italian | it | Same |
| Russian | ru | Same |
| Turkish | tr | Same |
| Arabic | ar | Both stores treat as one submitted locale, deliver script-shaped per device |
| Thai | th | Same |
| Vietnamese | vi | Same |
| Polish | pl | Same |
| Dutch | nl-NL | ASC uses full tag, Play accepts bare nl |
| Swedish | sv | Same |
| Danish | da | Same |
| Finnish | fi | Same |
| Greek | el | Same |
| Czech | cs | Same |
| Hungarian | hu | Same |
| Romanian | ro | Same |
| Slovak | sk | Same |
| Bulgarian | bg | Same |
| Ukrainian | uk | Same |
| Croatian | hr | Same |
| Malay | ms | Same |
| Hindi | hi | Same |
| Kazakh | kk | Same |
| Catalan | ca | ASC lists as "Catalan"; Play accepts |
If a locale isn't in either table, one of the stores doesn't support it as a top-level submission locale. You'll need to fall back to the closest parent (e.g. Swiss German → German, Persian → Arabic on Play).
The zh-Hans vs zh-CN gotcha (the biggest silent drop)
This one costs more indie devs Simplified Chinese exposure than any other locale-code mistake. The naming split is script-vs-region, not the same information:
zh-CN= "Chinese as spoken in the People's Republic of China" (region tag). This is what Google Play accepts.zh-Hans= "Chinese in Simplified script" (script tag). This is what App Store Connect accepts.
They point at effectively the same market (mainland China uses Simplified script), but the codes are not interchangeable in the submission form. Same story for Traditional Chinese: zh-TW on Play, zh-Hant on ASC. If your Play Console script feeds screenshots to a batch uploader that also targets ASC, translating zh-CN→zh-Hans is a required step, not a "sometimes":
const PLAY_TO_ASC = {
'zh-CN': 'zh-Hans',
'zh-TW': 'zh-Hant',
'iw': 'he',
'in': 'id',
'ji': 'yi',
// no -> nb (Norwegian Bokmål; ASC doesn't accept unified 'no')
'no': 'nb',
};
Anywhere the batch uploader sees a locale code that isn't in this mapping, pass it through unchanged. The mapping exists specifically because BCP-47 evolved but Apple and Google made different choices about which parts to accept.
The iw vs he and in vs id legacy-code trap
These are ISO 639-1 codes that were officially retired more than 30 years ago but never removed from Java's Locale class, so Android kept accepting them for backward compatibility. Modern Apple platforms rejected the legacy codes because they're ambiguous. iw is technically still valid ISO 639-1 for Hebrew, but so is he. Apple picked he; Google kept iw.
- Hebrew: Play
iw, ASChe. Same language; different codes. - Indonesian: Play
in, ASCid. Same language; different codes. This one bites especially hard becauseincan look like an English word ("Indonesian" abbreviated) and reviewers miss it in the code review of a locale table. - Yiddish: Play
ji, ASCyi. Same story; both stores support it if you localize.
Norwegian isn't a legacy-code issue but it's the same class of bug: Play accepts no (macro-language "Norwegian"), ASC does not. You have to submit Bokmål (nb) or Nynorsk (nn) as separate locales. Push no to ASC and the submission silently fails; ASC accepts it in some legacy tooling paths but the App Store display never renders it.
Why the mismatch breaks screenshot upload pipelines
The failure mode is nastier than a hard error would be. When a submission tool tries to push screenshots to ASC using the Play Console codes:
- ASC's API accepts the request. It doesn't reject
zh-CNas invalid, it just doesn't find that locale in the metadata for your app version. - The API returns 200 OK. Because "no matching locale to update" is treated as a no-op success, not an error.
- Your batch upload script reports success. Every locale in the loop returned 200.
- The App Store display shows the default English screenshots for Simplified Chinese, Hebrew, Indonesian, or Norwegian users. You never see the failure until a beta tester in that market reports it.
The concrete cost: a 26% CVR uplift from localized listings (per Apple's own App Store Connect Help docs, "Localize your app's product page", 2025) that you never captured because the code silently didn't match. For an indie with 5,000 monthly Simplified-Chinese installs, that's ~1,300 lost installs every month.
This is exactly the class of bug that killed a customer's Simplified Chinese launch on 2026-03: they had valid PNGs, ASC upload logs said zh-CN succeeded, and the store showed English screenshots for four weeks until they noticed. The fix, retagging the batch as zh-Hans and re-uploading, took ten minutes.
Batch tools that build on the App Store Connect API should assume Play Console codes as input (that's what most translation vendors, MDMs, and CI pipelines emit) and map to ASC codes as an internal step. See our locale-code reference for the full runtime mapping, and iOS .lproj vs Android values-* Locale Codes for the folder-level naming layer this post sits above.
Pre-submission locale-code checklist
Run this before every metadata upload. If any item is unchecked, stop and fix it. Don't hope the batch tool "figures it out".
- ☐ Your locale list uses ASC codes as source-of-truth, and a
PLAY_TO_ASCmap translates Play codes to ASC before hitting the ASC API. - ☐ Every
zh-CN/zh-TWin your Play Console pipeline maps tozh-Hans/zh-Hantfor ASC (never both). - ☐ Hebrew is
hein ASC-side code andiwin Play-side code, and you don't haveiwanywhere in the ASC upload path. - ☐ Indonesian is
idin ASC-side andinin Play-side. Search for the literal string"in"in your locale list and confirm it's the Play list, not the ASC list. - ☐ Norwegian is submitted as
nb(Bokmål) to ASC, not the unifiedno. If you have Nynorsk content too, submit as a second localenn. - ☐ Post-upload, spot-check one screenshot for each mismatch locale (zh-Hans, he, id, nb) on the live App Store in a browser preview. If the screenshot doesn't show up in the localized preview, the code silently didn't match. Retry.
- ☐ Log every ASC API response for locale-specific uploads. "200 OK" isn't proof of success. The response body's
dataobject should show the actual updated locale, and it should match what you sent.
FAQ
What happens if I submit zh-CN to App Store Connect?
The API returns 200 OK but doesn't update any locale (Apple looks for zh-Hans and finds nothing to match). Your Simplified Chinese screenshots don't publish. No error, no warning, silent drop. This is why the retagging step is required, not optional.
Is iw "wrong" for Hebrew?
Not exactly. iw is the ISO 639-1 code Hebrew was assigned in 1974; he is the replacement assigned in 1989. Both are technically valid ISO 639-1 codes today. Apple picked the modern one; Google kept accepting the legacy one for Android backward compatibility. The submission form treats them as different strings, and that's the bug.
Do I need to submit both zh-Hans and zh-CN?
No. Pick one per store. Play Console: zh-CN. App Store Connect: zh-Hans. Never submit both to the same store or ASC will treat them as separate locales and split your review count.
Does Play Console reject zh-Hans?
It depends on where in the API path you use it. The submission form rejects it; the newer publishing API accepts it and normalizes to zh-CN internally. Safe bet: use Play's codes on Play, ASC's codes on ASC. Don't rely on either store's normalization to save you.
What about regional Spanish and French?
Both stores treat regional variants (es-ES, es-MX, fr-FR, fr-CA) as full separate locales, not fallback variants. If you only submit es-ES, users in Mexico and the rest of Latin America see the default (usually English) unless their device language is set to es-ES specifically. Ship both if the market matters.
Where to take this next
If you're building a screenshot pipeline that targets both stores, the concrete next step is a PLAY_TO_ASC constant in your codebase (7 mappings covers 95% of cases) and a spot-check on the live listings after upload. See our locale-code reference for a machine-readable version of the mapping table above, and iOS .lproj vs Android values-* for the folder-level naming layer this problem sits on top of. If you're using a batch localization tool for the translation step, our Manual vs MT vs AI Screenshot Localization comparison covers which method fits which locale-code discipline (AI-generated batches especially benefit from ASC codes as source-of-truth, since the model can enforce the mapping in-loop).