resolveLocale function

String resolveLocale(
  1. List<String> preferences, {
  2. List<String>? supported,
  3. String fallback = 'en-US',
})

Resolves an active locale from an ordered list of preferences (e.g. navigator.languages).

Matches candidate tags in preferences against supported locale tags using:

  1. Exact match: Case-insensitive and hyphen/underscore normalized (e.g. 'en-us' matches 'en-US').
  2. Base language prefix match: Evaluates the primary language subtag (e.g. 'fr-CA' matches 'fr-FR' if only 'fr-FR' is supported).
  3. 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:

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;
}