[Regression]: Locale silently ignored on Android — toString() emits "zh_TW" but native forLanguageTag() accepts BCP-47 only
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 84/100
Research direction
Start in geocoding_android-5.1.0/lib/src/geocoding_android.dart:164 and compare the locale identifier with android/src/main/kotlin/com/baseflow/geocoding/proxies/LocaleProxyApi.kt:17. Check the existing package tests, then verify that country and script locales reach the native API as valid BCP-47 tags and that Android localization is preserved without affecting iOS.
Written by the indexing model from the issue text.
Description
Is there an existing issue for this?
- I have searched the existing issues — found none covering this.
Affected platform(s)
- Android
- iOS
Old behavior
On geocoding_android 4.x, passing a Locale with a country subtag worked: the returned placemarks were localized to that locale. The native side converted the identifier manually (LocaleConverter.java, splitting the string on _), which accepted the underscore form that the Dart side produces.
Current behavior
On geocoding_android 5.x the locale is silently ignored — results come back in the device's default language. The call succeeds, no exception, nothing observable at the call site.
Root cause is a mismatch between the producer and the consumer of the identifier string.
Dart side serializes with toString():
// geocoding_android-5.1.0/lib/src/geocoding_android.dart:164
final Locale? nativeLocale =
locale != null ? Locale(identifier: locale.toString()) : null;
Flutter's Locale.toString() joins subtags with an underscore — Locale('zh','TW').toString() == "zh_TW".
Native side parses with forLanguageTag():
// geocoding_android/android/src/main/kotlin/com/baseflow/geocoding/proxies/LocaleProxyApi.kt:17
return Locale.forLanguageTag(identifier);
Locale.forLanguageTag accepts BCP-47 only. Per the Javadoc: "If the specified language tag contains any ill-formed subtags, the first such subtag and all following subtags are ignored." zh_TW is a single ill-formed subtag, so the entire tag is dropped and an empty (und) Locale is returned.
Verified on JDK 21:
forLanguageTag("zh_TW") -> language="" country="" toLanguageTag="und"
forLanguageTag("zh-TW") -> language="zh" country="TW" toLanguageTag="zh-TW"
forLanguageTag("zh_Hant_TW") -> language="" country="" toLanguageTag="und"
forLanguageTag("en_US") -> language="" country="" toLanguageTag="und"
Note that GeocoderProxyApi passes this non-null but empty Locale straight to Geocoder(context, locale), so it does not fall back to the null-locale path — the request goes out with an undetermined locale.
This affects every locale carrying a country or script subtag (en_US, pt_BR, zh_Hant_TW, …). A language-only locale such as Locale('ja') happens to still work, since "ja" is already a well-formed tag.
Side note: the README still documents the identifier format as [languageCode]_[countryCode] (underscore) — which is now precisely the form that fails.
Steps to reproduce
- On Android, set the device language to something other than the locale you are about to request (e.g. device in English).
- Call
placemarkFromCoordinates(lat, lng, locale: const Locale('zh', 'TW'))— any locale with a country subtag reproduces it. - Observe the returned
Placemarkfields: they are in the device language, not the requested one. No error is raised. - Repeat with
const Locale('zh-TW')(single subtag, sotoString()already yields a hyphen) — the result is correctly localized.
Code sample
Code sample
// Ignored on Android (serializes to "zh_TW" -> forLanguageTag -> und):
final ignored = await Geocoding().placemarkFromCoordinates(
25.0330, 121.5654,
locale: const Locale('zh', 'TW'),
);
// Honored (serializes to "zh-TW"):
final honored = await Geocoding().placemarkFromCoordinates(
25.0330, 121.5654,
locale: const Locale('zh-TW'),
);
Suggested fix
Serialize with toLanguageTag() instead of toString() on the Dart side:
Locale(identifier: locale.toLanguageTag()) // "zh-TW"
toLanguageTag() emits exactly BCP-47, which is what forLanguageTag expects. One line, no native change required.
iOS is unaffected either way — geocoding_darwin uses Locale(identifier:), and Foundation parses "zh_TW" and "zh-TW" identically (verified on macOS: both yield language=zh region=TW). So switching to toLanguageTag() is safe for both platforms.
Workaround for users
Pass the tag as a single-subtag Dart Locale, so that toString() already produces the hyphenated form:
const Locale('zh-TW') // toString() == "zh-TW" -> honored
const Locale('zh', 'TW') // toString() == "zh_TW" -> silently ignored
Current version
geocoding: 5.0.0 / geocoding_android: 5.1.0 (latest at time of writing) / geocoding_platform_interface: 5.0.0. Still present on main.
Last version without regression
geocoding_android: 4.0.1
- Dominant language
- Dart
- Stars
- 153
- Forks
- 92
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from Baseflow/flutter-geocoding
-
Difficulty 1/5 1-3 hours Newbie friendliness 84/100
Baseflow/flutter-geocoding#311 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
Baseflow/flutter-geocoding#287 · 1 comment ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Baseflow/flutter-geocoding#286 · 2 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Baseflow/flutter-geocoding#257 · 2 comments ·
-
Difficulty 4/5 3-5 days Newbie friendliness 25/100
Baseflow/flutter-geocoding#247 · 1 comment · 1 reaction ·
All issues in Baseflow/flutter-geocoding
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
code quality good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Suwayomi/Suwayomi-Tsumiru#479 ·
-
Build Failure: agora_rtc_engine compiled against android-31 while dependencies require android-34+ Open
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 92/100
conceptadev/noir#95 ·