resolveLocale function
Resolves an active locale from an ordered list of preferences (e.g. navigator.languages).
Matches candidate tags in preferences against supported locale tags using:
- Exact match: Case-insensitive and hyphen/underscore normalized (e.g.
'en-us'matches'en-US'). - Base language prefix match: Evaluates the primary language subtag (e.g.
'fr-CA'matches'fr-FR'if only'fr-FR'is supported). - Fallback: If no match is found, returns
fallback(defaults to'en-US').
final chosen = resolveLocale(
['fr-CA', 'fr-FR', 'en'],
supported: ['en-US', 'fr-FR'],
fallback: 'en-US',
);
// chosen: "fr-FR"
See also:
- BloomI18n, which uses locale tags for translation routing.
- parseAcceptLanguage, for extracting preference lists from HTTP headers.
Implementation
String resolveLocale(
List<String> preferences, {
List<String>? supported,
String fallback = 'en-US',
}) {
if (supported == null || supported.isEmpty) {
return preferences.isNotEmpty ? preferences.first : fallback;
}
final normalizedSupported = supported.map((s) => s.replaceAll('_', '-').toLowerCase()).toList();
// 1. Exact match
for (final pref in preferences) {
final normPref = pref.replaceAll('_', '-').toLowerCase();
final idx = normalizedSupported.indexOf(normPref);
if (idx != -1) return supported[idx];
}
// 2. Base language match (e.g. 'fr' for 'fr-CA')
for (final pref in preferences) {
final basePref = pref.replaceAll('_', '-').split('-').first.toLowerCase();
for (int i = 0; i < normalizedSupported.length; i++) {
final baseSupported = normalizedSupported[i].split('-').first;
if (baseSupported == basePref) {
return supported[i];
}
}
}
return fallback;
}