Mr.AndroidShin / Dev Tools

Field log · Localization

My app shipped English-only — and I'd translated everything

Thirteen languages of strings.xml, every folder in place, the app perfect in every locale on my own device. On the Play Store, users kept seeing English. The bug wasn't in my translations at all — it was in how the App Bundle splits them.

This one is nasty precisely because everything looks correct. You do the localization work properly: values-ko, values-ja, values-es, a dozen more, every key translated. You switch your phone's language, launch the app, and it's flawless. You ship. Then a user messages you that the app is in English even though their phone is in Korean, and you have no idea how that's even possible, because you can see the Korean strings sitting right there in your project.

The culprit: per-language splits in the App Bundle

When you publish an Android App Bundle, Google Play doesn't send every user your entire app. It generates optimized splits and delivers only what each device needs — the right screen density, the right ABI, and the right language. That last one is the trap. By default the bundle carries each locale as its own configuration split, and the Play Store delivers a device only the language split matching its system locale at install time.

On your own phone, testing from Android Studio, you install the universal build with every language included, so everything works. Real users installing from the Store get a language-stripped copy. If the delivery of non-default language splits isn't set up the way you expect — or if a device's locale doesn't exactly match a split you shipped — the user falls back to your default values/ language, which for many of us is English. Your translations exist; they just never got delivered to that device.

The tell that points straight at this: it's perfect from Android Studio and wrong from the Store. Any bug that only appears on the Play Store copy and never on your direct install is almost always an App Bundle split issue — language, density, or ABI. Same code, different delivered artifact.

How I confirmed it

The confirmation is worth doing rather than guessing, because "the Store copy differs" has a few possible causes. The App Bundle format lets you build the exact APKs the Store would generate for a given device and install those instead of the universal build. When I generated the split APKs and installed the set for a Korean device configuration — rather than the all-languages universal APK — I reproduced the English fallback locally. That was the proof: the code was fine, the delivered APK set for that locale simply didn't contain, or didn't apply, the Korean split. Reproducing a Store-only bug on your own machine turns a mystery into a fixable defect.

The fixes

There are two reliable directions, and which you want depends on how much you care about download size.

Option 1 — turn off language splitting

The bluntest, most predictable fix: tell the bundle not to split by language, so every install includes every language exactly like your universal build did.

android {
    bundle {
        language {
            enableSplit = false
        }
    }
}

Now every user gets all your translations regardless of their locale at install time, and the class of bug disappears. The cost is a slightly larger download, since everyone carries every language. For an app with modest string resources — which is most apps — that cost is tiny and the reliability is worth it. This is what I reach for first.

Option 2 — keep splits, deliver languages on demand

If you localize heavily and download size genuinely matters, keep the splits but handle language delivery deliberately: fetch additional languages at runtime through the Play delivery APIs when a user selects a locale you didn't ship by default, and make sure your in-app language switcher requests the split it needs before applying it. This keeps installs lean but adds real complexity, and it's only worth it when your translated resources are large enough to matter.

The takeaway

The mental model that would have saved me hours: with App Bundles, "it's in my project" and "it's on the user's device" are two different statements. The bundle is a recipe, not the meal — Google Play cooks a per-device APK set from it, and anything that's optional (languages, densities, native libs) can be absent from a given device's serving. When something is present in your source but missing for real users, stop looking at your code and start looking at what the bundle actually delivered. For localization specifically, if you want certainty over a few hundred kilobytes, disabling the language split is the boring answer that just works.

Gradle DSL and Play delivery options evolve; verify the current bundle configuration syntax against the official Android documentation before relying on a specific snippet.