Is there an existing issue for this?
Affected platform(s)
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
Placemark fields: they are in the device language, not the requested one. No error is raised.
- Repeat with
const Locale('zh-TW') (single subtag, so toString() 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
Is there an existing issue for this?
Affected platform(s)
Old behavior
On
geocoding_android4.x, passing aLocalewith 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_android5.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():Flutter's
Locale.toString()joins subtags with an underscore —Locale('zh','TW').toString() == "zh_TW".Native side parses with
forLanguageTag():Locale.forLanguageTagaccepts 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_TWis a single ill-formed subtag, so the entire tag is dropped and an empty (und) Locale is returned.Verified on JDK 21:
Note that
GeocoderProxyApipasses this non-null but empty Locale straight toGeocoder(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 asLocale('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
placemarkFromCoordinates(lat, lng, locale: const Locale('zh', 'TW'))— any locale with a country subtag reproduces it.Placemarkfields: they are in the device language, not the requested one. No error is raised.const Locale('zh-TW')(single subtag, sotoString()already yields a hyphen) — the result is correctly localized.Code sample
Code sample
Suggested fix
Serialize with
toLanguageTag()instead oftoString()on the Dart side:toLanguageTag()emits exactly BCP-47, which is whatforLanguageTagexpects. One line, no native change required.iOS is unaffected either way —
geocoding_darwinusesLocale(identifier:), and Foundation parses"zh_TW"and"zh-TW"identically (verified on macOS: both yieldlanguage=zh region=TW). So switching totoLanguageTag()is safe for both platforms.Workaround for users
Pass the tag as a single-subtag Dart
Locale, so thattoString()already produces the hyphenated form:Current version
geocoding: 5.0.0/geocoding_android: 5.1.0(latest at time of writing) /geocoding_platform_interface: 5.0.0. Still present onmain.Last version without regression
geocoding_android: 4.0.1