Skip to content

fix: honor the selected app locale everywhere (All-in-one) - #6044

Closed
eliotcougar wants to merge 9 commits into
2dust:masterfrom
eliotcougar:codex/fix-app-locale-context
Closed

fix: honor the selected app locale everywhere (All-in-one)#6044
eliotcougar wants to merge 9 commits into
2dust:masterfrom
eliotcougar:codex/fix-app-locale-context

Conversation

@eliotcougar

@eliotcougar eliotcougar commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Context and series

Fixes #6005. For real this time.

This is part 3 of 3 extracted from the original combined localization audit in #6021.

  • Part 1 — fix: sync locale resources and translations (1/3) #6024 (merged): synchronized the locale catalogs, filled missing translations, corrected divergent meanings, and marked invariant technical values non-translatable.
  • Part 2 — fix: localize user-facing and accessibility text (2/3) #6025 (merged): extracted hardcoded user-facing text, introduced dedicated accessibility descriptions, and localized the resulting resources.
  • Part 3 — this PR: makes the selected app language authoritative in every Android context, keeps locale-neutral data across service/UI boundaries, and contains the final translation and wording polish.

Root cause: why present translations looked missing

The affected resources were present and correctly named. The bug came from which Context resolved them.

The previous implementation stored the chosen language in MMKV and wrapped BaseComponentActivity with a custom locale configuration. That changed resources obtained from the activity, including most direct Compose stringResource(...) calls, but it did not establish an application-wide per-app locale.

Several kinds of text were produced elsewhere:

  • AndroidViewModel and repository code used the raw Application context.
  • app-picker background work switched to applicationContext.
  • VPN, proxy-only, root, test, subscription-update, and Quick Settings services had their own contexts.
  • connection-test results and other status values were formatted in the service/daemon layer and sent to the UI as finished strings.
  • some localized strings were cached in retained UI state before an activity recreation.
  • dialog actions used android.R.string.ok and android.R.string.cancel, which belong to framework resources rather than the app's locale catalogs.

Those contexts continued to use the device locale. A single Compose screen could therefore combine correctly localized strings from its activity with Persian, English, or another device-language string created outside that activity. This explains both otherwise puzzling observations from #6021:

  1. only some strings were wrong on a screen; and
  2. changing the device language changed those strings even though the app language and the locale XML entry did not change.

The values-bqi-rIR resource directory was not itself the cause. bqi-rIR is the Android resource-qualifier form; the runtime per-app locale API instead receives the BCP 47 language tag bqi-IR. The old code treated the stored resource-style code as the entire locale mechanism and never synchronized it with Android's app-locale APIs.

This implementation follows Android's current per-app language guidance and uses ContextCompat.getContextForLanguage for resource access outside an AppCompatActivity.

Locale infrastructure

Use the platform and AndroidX app-locale APIs

  • Changes the Compose activity base from ComponentActivity to AppCompatActivity, as required for backward-compatible AppCompatDelegate.setApplicationLocales(...) handling.
  • Adds a centralized AppLocaleManager for setting, reading, migrating, and applying the selected locale.
  • Separates the existing stored setting code from the proper runtime language tag:
    • zh-rCN -> zh-CN
    • zh-rTW -> zh-TW
    • bqi-rIR -> bqi-IR
  • Preserves the existing MMKV preference so upgrades do not discard the user's chosen language.
  • Synchronizes changes made through either the in-app picker or Android 13+ system app-language settings.
  • Performs the documented one-time handoff from the legacy preference to AppCompat/framework locale storage.

Declare supported languages correctly

  • Enables generated LocaleConfig support and explicitly filters it to the nine maintained locales.
  • Declares English as the unqualified resource locale through resources.properties.
  • Adds AppCompat locale auto-storage for Android 12 and older.
  • Makes the language list available in Android 13+ system settings without maintaining a second manual locale-config file.
  • Uses an AppCompat parent theme required by the new activity base while preserving the existing day/night window styling.

Apply the locale outside activities

  • Resolves Application and BaseViewModel resources through a locale-aware context.
  • Applies the selected locale to VPN, proxy-only, root, test, subscription-update, and Quick Settings service contexts.
  • Uses localized resources in MainRepository, including the synthetic All group.
  • Captures and refreshes the localized unknown-app label around app-picker background work.
  • Removes the former custom MyContextWrapper; locale behavior now has one AndroidX/platform-backed owner.

Keep core and service data locale-neutral

Part 2 initially exposed more localized failures, but subsequent upstream architecture deliberately removed Android resources from /core. This PR preserves that separation.

  • CoreServiceManager no longer formats connection-test UI sentences.
  • The service sends a locale-neutral ConnectionTestResult containing delay, diagnostic detail, country code, and IP address.
  • MainViewModel formats that result only at the UI boundary using the currently selected app locale.
  • The raw technical error remains suitable for Logcat while the visible sentence and empty-detail fallback are localized.
  • Country codes remain untouched; only the missing-value label is localized.
  • Main-screen status is represented as semantic MainStatus state instead of retained localized strings, so activity recreation or a locale change cannot leave stale text behind.
  • update-check and Logcat-sharing exceptions are logged with their diagnostic detail, while users see a stable localized failure message.

Other localization and UI polish

Units and connection status

  • Localizes the millisecond unit used by TCPing and real-delay results in profile rows.
  • Formats the connection-test result with that same localized unit.
  • Rephrases the success result naturally in every locale.
  • Clarifies the connected-state prompt and allows a line break in locales where the combined text is too long.
  • Uses a localized Unknown value when the native-core version, country, or IP value is unavailable instead of leaking an English sentinel.

Framework and cached UI text

  • Replaces framework OK and Cancel resources with app-owned translations in dialogs and notifications.
  • Recomputes locale-sensitive app-picker and main-screen state instead of retaining strings from the old language.
  • Keeps core and service messages structured until the presentation layer can apply the active locale.

Settings and menu wording

  • Clarifies that per-app proxy auto-selection selects apps that should be proxied.
  • Shortens Subscription group settings to Subscriptions across locales.
  • Localizes “format” in the DNS hosts title while retaining the literal domain:address,… syntax.
  • Standardizes URL capitalization.
  • Corrects stale Arabic labels that still described numbered workflow steps.
  • Reviews the complete locale catalogs for awkward, overly literal, or calque-like wording while preserving xray-core identifiers, protocol names, raw keys, and other technical terms.

Routing clarity

  • Moves the rule-combination explanation out of individual fields so it appears once.
  • States the actual behavior: populated domain, IP, port, network, source, protocol, and process conditions are combined with AND; rules are evaluated top-to-bottom and the first matching rule applies.
  • Leaves only the comma-separated-entry hint inside the applicable fields.
  • Applies the same explanation in all maintained locales.

Subscription proxy-chain clarity

  • Renames the two bottom subscription fields to Entry proxy and Exit proxy.
  • Adds localized supporting text below each field explaining that the selected proxy is inserted before or after every profile in the subscription as the entry or exit link in that profile's proxy chain.
  • Keeps the mapping aligned with the actual chain construction: the stored previous profile is the entry hop and the stored next profile is the exit hop.

Resource consistency

  • English, Arabic, Bangla, Bakhtiari, Persian, Russian, Vietnamese, Simplified Chinese, and Traditional Chinese each contain exactly 446 resources.
  • All catalogs retain the same identifiers, resource types, order, physical row alignment, translatability metadata, formatter metadata, and format placeholders.
  • Non-translatable values are identical in every catalog.
  • String arrays remain unwrapped with matching rows.

Impact

  • Selecting a language in v2rayNG now affects activities, Compose, ViewModels, repositories, services, notifications, the Quick Settings tile, and UI text received from the daemon path.
  • Language change applies immediately without the need to restart the app.
  • Android 13+ system language settings and the in-app picker remain synchronized.
  • Existing users retain their selected language during migration.
  • Mixed app-language/device-language screens no longer occur merely because a string was created outside an activity.
  • The core remains free of Android resource dependencies.
  • The final UI text is shorter, clearer, and more consistent across all nine locales.

Use AndroidX and framework per-app locale APIs, migrate the existing language preference, and expose the generated locale config to Android 13+ settings.

Resolve strings through the selected app locale in application, ViewModel, launcher, tile, and service contexts. Replace framework OK/Cancel resources and refresh retained localized UI state after locale changes.
Review all 443 resources across Arabic, Bengali, Bakhtiari, Persian, Russian, Vietnamese, Simplified Chinese, and Traditional Chinese. Correct semantic mismatches against their UI call sites, replace awkward or untranslated visual text, preserve xray-core field names and protocol identifiers, and keep every catalog row- and array-aligned.

Bakhtiari orthography and lexicon references: https://www.diva-portal.org/smash/get/diva2:1231755/FULLTEXT02.pdf and https://uu.diva-portal.org/smash/record.jsf?pid=diva2:758171. Unit terminology reference: https://cldr.unicode.org/translation/units/unit-names-and-patterns.
@eliotcougar
eliotcougar marked this pull request as ready for review August 8, 2026 22:36
@hosseinabaspanah

Copy link
Copy Markdown
Contributor

@eliotcougar Great and thank you for your help, and for addressing my report and improving the program and the program languages.

@2dust

2dust commented Aug 9, 2026

Copy link
Copy Markdown
Owner

感谢
但是此 pr 又是存在一个很难 review 的问题,ai 处理了太多东西。
还是要拆分成多份 pr

  1. AppLocaleManager
  2. string 调整
  3. 其他

* @return Version string of the V2Ray core, or null if it cannot be read.
*/
fun getLibVersion(): String {
fun getLibVersion(): String? {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里不要反复的改了,上次已经回滚过一次

@eliotcougar eliotcougar Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version string rendering moved from core to UI (About and Update screens)

@eliotcougar

Copy link
Copy Markdown
Contributor Author

感谢 但是此 pr 又是存在一个很难 review 的问题,ai 处理了太多东西。 还是要拆分成多份 pr

Sigh... It's not AI, it's me...
I though if the majority of changes are about AppLocaleManager, and the rest are cleanup localized in strings.xml, it may be okay.

Alright, I'll split the cleanup work from the #6005 fix.

@2dust

2dust commented Aug 9, 2026

Copy link
Copy Markdown
Owner

如果不是ai ,也不要在一个 pr 中 提交如此多的修改。
非常难审查和测试

@eliotcougar eliotcougar changed the title fix: honor the selected app locale everywhere (3/3) fix: honor the selected app locale everywhere (All-in-one) Aug 9, 2026
@eliotcougar
eliotcougar deleted the codex/fix-app-locale-context branch August 15, 2026 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sync translation file from values

3 participants