crowdin / crowdin/mobile-sdk-android
Improve Locale Matching
- Dominant language
- Kotlin
- Stars
- 130
- Forks
- 60
- PR merge metrics
- No merged PRs in 30d
Description
## Current limitations
`getMatchedCode()` is a plain string matcher: exact `language-country` → hardcoded `es-419` group → bare language code. Compared to native Android resolution it lacks:
- **CLDR region groups** other than `es-419` (`en-GB → en-001`, `pt-AO → pt-PT`, `zh-MO → zh-Hant`)
- **Script matching** — a `zh-HK` (Traditional) device can resolve to `zh-CN` (Simplified) via bare `zh`
- **Multi-locale support** — only the first device locale is checked, not the full `LocaleList`
## What the bump unlocks
- **API 24+:** `ULocale.addLikelySubtags()` (script inference) and `LocaleList` (all user locales)
- **API 31+:** `android.icu.util.LocaleMatcher` — the full CLDR algorithm the platform itself uses
Bundling ICU4J below API 31 is not an option (~10 MB). Plan assumes minSdk 24+.
## Proposal
Replace `getMatchedCode()` with a `LocaleResolver` (used by all three call sites):
1. **Normalize** Crowdin codes and device locales to BCP-47 `ULocale`, maximized via `addLikelySubtags()`; keep the `iw→he`, `in→id` mapping at this boundary.
2. **Walk the full `configuration.locales` list** — first user locale that matches wins.
3. **Per locale:** exact match → CLDR parent-locale chain (`es-MX → es-419 → es`, `en-IN → en-001 → en`) → same language **and script** match → next locale. Generate the parent table (~150 CLDR entries) with a build script instead of hand-maintained region sets.
4. **API 31+ fast path (optional):** delegate to `LocaleMatcher`, keep the manual pipeline below 31.
5. **Log the result:** `Resolved language: es-419 (device: es-MX, rule: parent-locale)` — today it must be inferred from file paths.
## Compatibility
- Changes are additive (more locales resolve to a better variant). One intentional break: `zh-Hans ↔ zh-Hant` cross-matching via bare `zh` stops — release-note it.
- `withPreferredLanguageCode()` stays as the explicit override.
## Tests
Table-driven matrix (device LocaleList × project languages → expected code): es-419 regressions, `en-001`, `pt-PT`, `zh-Hans/Hant`, multi-locale fallthrough. On API 31+, assert the manual pipeline agrees with `LocaleMatcher`.
Contributor guide
Research direction
Start by locating getMatchedCode() and its three call sites, then inspect how withPreferredLanguageCode() and configuration.locales are currently handled. Review the proposed ULocale and LocaleMatcher APIs before defining the manual pipeline and generated parent table. Done means the table-driven matrix covers the listed locale cases, including multi-locale fallthrough, and API 31 behavior agrees with the manual pipeline.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, kotlin
- Domain
- internationalization, localization, mobile-dev
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100