EEltaCrew
HomeBlog

Why switching language inside the app sometimes does nothing: setApplicationLocales only recreates activities that are on screen

Published 2026-09-16Measured 2026-09-07About 3 min readelta · EltaCrew

"I switched to English and it stayed Korean until I restarted the app." The cause is that AppCompat recreates only the activities that are started at that moment; screens that were stopped behind it come back in the old language. The fix I applied to 8 apps, and five traps that came with it.

The symptom

In the remote app's settings, switching the language to English turned the settings screen English, but going back to the main screen sometimes showed Korean. Killing and relaunching the app fixed it. I wanted to know what "restart fixes it" actually meant.

The cause

AppCompatDelegate.setApplicationLocales() recreates only the activities that are in the started state at that moment. The settings screen is recreated in the new language, but the main screen, stopped behind it, resumes with its old Configuration. On a full restart, Application.onCreate re-applies the locale, so everything looks fine.

That means the bug only exists in apps with more than one activity. A single-activity app is fully refreshed when that one activity is recreated. Of mine, the four remote apps (6 to 7 activities each), the benefits calendar, the bookmark manager, the screen-time app and the lottery app were affected.

The fix

In onActivityResumed of Application.registerActivityLifecycleCallbacks, compare the global selection (AppCompatDelegate.getApplicationLocales()) with the locale in that activity's own Configuration. If they differ, recreate().

Two things matter:

The same class went into 8 apps.

Also found: the locale-persistence switch was missing in 10 apps

Below API 33, AppCompat stores and restores the selected locale only if the manifest declares AppLocalesMetadataHolderService with the autoStoreLocales metadata. Ten apps did not have it. That is why some apps kept the language after restart and others fell back to the system language.

Five traps (every one actually happened)

  1. Hard-coded Korean ignores the language setting. 97 remote-control button labels were literals like android:text="외부입력". All 131 English translations existed in values-en and the screen still showed Korean.
  2. Do not search for Hangul with grep -P '[\x{AC00}-\x{D7A3}]'. In Git Bash under a non-UTF-8 locale it fails with "supports only unibyte and UTF-8 locales" and returns zero matches. A false negative. Use Python.
  3. Korean in the code is not necessarily Korean on screen. Some strings were assigned to variables nobody read and only went to logs. Trace the actual display path to the end before translating.
  4. Do not look resources up by name with getResources().getIdentifier(). R8 strips the names and it breaks in release only.
  5. Read the warnings in the original comments. Above NO_NAME = "(no name)" it said "used for text comparison, do not move to resources"; I moved only the constant and broke the build. To move it, the logic must move too so that display and comparison use the same string.

In-app language applies to activities only

One more. AppCompat's in-app language applies to activity resources. Notifications, home-screen widgets and date formatting done in the background still use the system language, so you end up with an English app sending Korean notifications. Where notifications and widgets are built, wrap a Context with the stored language tag (createConfigurationContext) and read resources from that.

Verification

This is a timing bug, so a passing compile proves nothing. On a real device, walk screen A → settings → change language → back → A by hand.

Summary

AndroidlocalizationAppCompatin-app language

Related

R8 on, build green, app broken: five runtime traps that actually hit across 15 appsI enabled R8 in 15 apps for Play's 2027 code-optimization requirement. Every build passed, but 2 of the first 7 I installed broke at runtime. Gson deserialization, getIdentifier resource lookups, Room _Impl classes, and debuggable builds that silently disable R8: the kinds of failure that never show up in a crash log.One language per page: why I dropped IP-country detection and bilingual pages for browser language plus hreflangSix web services each picked their initial language differently. The one using IP country served English to Googlebot only, so the search-result language could not be controlled; the landing page mixed Korean and English on one page, against Google's "one language per page" expectation. How I unified every site under one rule.Seven rules I rewrote 13 home-screen widgets against, after placing them next to Samsung'sPutting 13 widgets from 5 apps on a real home screen beside Samsung's weather and wallet widgets showed that "the code exists" and "it looks like a shipped product" are very different. System dark mode, designed empty states, previewLayout and label, dp corner radii, and the theme lag of bitmap widgets.