crowdin / crowdin/mobile-sdk-android

Improve Locale Matching

Open
#373 0 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.