How many languages your app declares, and why counting them is harder than it looks

The App Store's language field repeats one code, treats empty as unknown, and describes the binary rather than your listing. Three traps in a number that looks trivial.

5 min
localization · benchmarks · metadata

Apple's public API returns a list of the languages an app declares. Counting them looks like a one-liner, and three separate things about that field make the obvious implementation wrong.

It matters because the number gets compared. "This app supports 30 languages, mine supports 8" is a reasonable thing to want to know, and it is only useful if both sides were counted the same way.

Trap one: one code appears twice

languageCodesISO2A contains duplicate entries, and .length is therefore not the number of languages.

Verified against live data: ZH is the only code that ever repeats. It appears twice for apps shipping both Chinese scripts — Simplified and Traditional. The frequency is not marginal:

Search termRows with a duplicated code
photo editor21 of 26
weather12 of 27

One large weather app returns 55 entries and 54 distinct codes. A naive count reports 55.

So the count has to be over distinct codes. And the second consequence is subtler: because the field repeats ZH rather than distinguishing zh-Hans from zh-Hant, it cannot tell you which script an app ships. Appstro's language coverage therefore gives ZH no link through to the metadata writer — there is no defensible answer to "which Chinese".

Trap two: empty means unknown, not zero

An app returning an empty array has not told you it supports no languages. It has told you nothing.

Sampled across 2,110 apps, exactly one returned []. That rarity is the point: it is clearly a gap in the response rather than a real property of an app, and scoring it as zero would put that app at the bottom of every comparison it appears in.

This is the same rule that runs through every cohort tool here, and it is worth stating as a general principle rather than a special case: absence and zero are different facts, and rendering them the same is how a tool invents a finding. A missing value is reported as missing and excluded from the denominator.

Three traps in counting declared languages Three ways a language count goes wrong A repeated code [EN, FR, DE, ZH, ZH] 5 entries · 4 languages ZH appears twice for both Chinese scripts An empty array [ ] unknown, not zero 1 of 2,110 apps sampled The wrong object the binary’s languages not the listing’s an app can declare 20 and show English everywhere
Three ways the obvious implementation of a language count goes wrong.

Trap three: this is the binary, not your listing

The biggest one, and it is a category error rather than an arithmetic one.

languageCodesISO2A describes what the app binary declares. It says nothing about whether your App Store listing is localized. An app can declare twenty languages in its bundle and still show an English name and description in every one of Apple's storefronts — the two are set in different places and neither implies the other.

Those are two genuinely different projects with different costs:

Binary languagesListing localization
What it changesThe UI a user sees inside the appThe page a stranger reads before installing
Where it is setYour app's resourcesApp Store Connect metadata
What the API showslanguageCodesISO2AThe name and description per storefront
Who it reachesPeople who already installedPeople deciding whether to

Appstro carries that as a non-dismissible banner rather than a footnote, and links across to the localization audit, which checks the other half by comparing each storefront's name and description against the US listing.

If you only do one, the listing is usually the higher-leverage half, because it acts on people who have not installed yet.

What a fair comparison needs

Once the counting is right, the comparison still has to be against something meaningful. A count on its own has the same problem a rating count has: 8 languages is a lot for a two-person utility and very little for a keyboard app.

The cohort here is the apps returned for a search term, which is a rough proxy for "apps competing for the same person". It is rough on purpose and worth knowing the limits of:

  • It is one search, one storefront. Search a different term and the cohort changes.
  • It mixes scales. A result row routinely contains a company with a localization team and someone shipping alone. The median absorbs that better than the mean, which is why the page reports a median.
  • It is a description of what exists, not a target. "The cohort ships 14 languages" is a fact about the row. Whether you should ship 14 depends on where your users actually are, which the row does not know.

What the comparison genuinely surfaces is the specific gap: not "you are five behind" but "eight of the ten apps here ship Japanese and you do not". A named language is something you can decide about. A deficit number is not.

Codes with no name

The lookup table covers 190 language codes. A code outside it renders as the raw uppercase code with a "code only" marker.

That is deliberately ugly. The alternative is inventing a plausible language name for a code nobody verified, which is a small lie that looks like data. An unfamiliar two-letter code shown as itself is honest and immediately checkable.

What this cannot tell you

  • Not the quality of any translation. A declared language is a claim in a manifest. Whether the app reads naturally in Korean is not visible from outside, and no count can stand in for a native speaker.
  • Not which Chinese script. The field collapses both into ZH.
  • Not whether adding a language pays. The cohort tells you what is normal in your category; it cannot tell you where your users are. Localization priority answers a narrower version of that from rating volume.
  • Not whether the listing is localized. Different field, different tool, different project.
  • Not a zero. An empty array is unknown, and it is reported that way.

The work

Run your app through language coverage and read two things: the distinct count against your cohort's, and the specific languages the cohort ships that you do not. Then check whether your listing is localized in the storefronts that already have your users — those are different questions, and the second one usually matters more.

Tools this post uses

Read next