modular_l10n 3.0.0 copy "modular_l10n: ^3.0.0" to clipboard
modular_l10n: ^3.0.0 copied to clipboard

Lightweight locale utilities for Flutter with RTL detection, locale parsing, display names for 70+ languages. Works standalone or with Modular Flutter L10n extension.

modular_l10n #

Pub Version Pub Points Popularity License

Comprehensive locale utilities for Flutter — 177 languages, RTL detection, plural rules, calendar metadata, and more. Zero dependencies.


Use This Package Standalone or With the Extension #

This package provides valuable locale utilities for ANY Flutter app — no extension required!

Works Standalone (Without Extension) #

Feature Description
177 Languages English + native display names for all ISO 639-1 languages
RTL Detection 16 RTL languages including Arabic, Hebrew, Urdu, Persian, and more
Locale Parsing Parse en_US, zh_Hans_CN, sr_Latn_RS, and more
Best Match Algorithm Find closest supported locale from device locale, with fallback chains
Country Names ISO 3166-1 country code to display name (~150 countries)
Plural Rules CLDR plural categories for major language groups
Calendar Metadata First day of week, default calendar system, date order
Number Formats Decimal/grouping separators and numeral system per locale
Writing System Info Logographic, abugida, word spaces, uppercase support
Validation Validate language and country codes; normalize deprecated codes
Zero Dependencies No external packages — pure Dart + Flutter SDK
// Use with any localization solution
if (locale.isRtl) { /* Handle RTL layout */ }

print(locale.nativeName);             // "العربية"
print(locale.fullDisplayName);        // "Arabic (Egypt)"
print(locale.textExpansionFactor);    // 1.25

LocaleUtils.getPluralCategory(locale, 3); // PluralCategory.few  (Arabic)
LocaleUtils.getFirstDayOfWeek(locale);    // 6 (Saturday, Saudi Arabia)

Extra Power With the VS Code Extension #

Pair with the Modular Flutter Localization Extension for a complete localization solution:

Extension Feature What You Get
Code Generation Generates type-safe ML class from ARB files
Modular Organization Organize translations by feature/module
Auto-Regeneration Watch mode — changes to ARB files instantly update code
ICU Message Support Plurals, select, gender, and complex messages
Extract to ARB Right-click strings in code to extract them
Multi-locale Management Easy commands to add/remove locales
// With extension — type-safe translations
Text(ML.of(context).auth.emailLabel)

// With extension + this package — RTL + metadata
Directionality(
  textDirection: ML.of(context).locale.textDirection,
  child: child,
)

Installation #

dependencies:
  modular_l10n: ^3.0.0
flutter pub get

Quick Start #

import 'package:modular_l10n/modular_l10n.dart';

// RTL detection
if (LocaleUtils.isRtl(const Locale('ar'))) { /* ... */ }
if (locale.isRtl) { /* ... */ }

// Parse locale strings
final locale = LocaleUtils.parseLocale('zh_Hans_CN');
// → Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN')

// Normalize deprecated codes
LocaleUtils.normalizeLocale('iw');  // → Locale('he')  (Hebrew)
LocaleUtils.normalizeLocale('in');  // → Locale('id')  (Indonesian)

// Display names
print(const Locale('ar').displayName);        // "Arabic"
print(const Locale('ar').nativeName);         // "العربية"
print(const Locale('ar', 'EG').fullDisplayName); // "Arabic (Egypt)"

// Find best match for device locale
final bestMatch = LocaleUtils.findBestMatch(deviceLocale, supportedLocales);

// With intelligent fallback chain (e.g. nb → no → da)
final bestMatch = LocaleUtils.findBestMatch(
  deviceLocale,
  supportedLocales,
  useFallbackChain: true,
);

RTL Support #

Supported RTL Languages #

Code Language Code Language
ar Arabic ku Kurdish (Sorani)
fa Persian/Farsi ug Uyghur
he Hebrew dv Divehi
ur Urdu ks Kashmiri
ps Pashto yi Yiddish
sd Sindhi ckb Central Kurdish
arc Aramaic syr Syriac
bal Balochi khw Khowar
// Static method
if (LocaleUtils.isRtl(locale)) { /* ... */ }

// Extension
if (locale.isRtl) { /* ... */ }

// In widgets
Directionality(
  textDirection: locale.textDirection,
  child: child,
)

Display Names #

177 languages with both English and native names:

const Locale('ar').displayName  // "Arabic"
const Locale('ar').nativeName   // "العربية"
const Locale('zh').displayName  // "Chinese"
const Locale('zh').nativeName   // "中文"
const Locale('hi').displayName  // "Hindi"
const Locale('hi').nativeName   // "हिन्दी"

Full Display Name with Region #

LocaleUtils.getFullDisplayName(const Locale('ar', 'EG'));
// → "Arabic (Egypt)"

LocaleUtils.getFullNativeDisplayName(const Locale('ar', 'EG'));
// → "العربية (مصر)"

LocaleUtils.getFullDisplayName(
  const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN'),
);
// → "Chinese (China)"

Country Names #

LocaleUtils.getCountryName('US');  // "United States"
LocaleUtils.getCountryName('EG');  // "Egypt"
LocaleUtils.getCountryName('SA');  // "Saudi Arabia"

const Locale('ar', 'EG').countryDisplayName;  // "Egypt"

Locale Parsing & Matching #

Parse Locale Strings #

LocaleUtils.parseLocale('en');          // Locale('en')
LocaleUtils.parseLocale('en_US');       // Locale('en', 'US')
LocaleUtils.parseLocale('en-US');       // Locale('en', 'US')
LocaleUtils.parseLocale('zh_Hans');     // Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans')
LocaleUtils.parseLocale('zh_Hans_CN');  // Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans', countryCode: 'CN')
LocaleUtils.parseLocale('sr_Latn_RS');  // Locale.fromSubtags(languageCode: 'sr', scriptCode: 'Latn', countryCode: 'RS')

Find Best Matching Locale #

Priority: exact match → language + country → language only.

final supported = [Locale('en'), Locale('en', 'GB'), Locale('ar'), Locale('de')];

LocaleUtils.findBestMatch(Locale('en', 'GB'), supported);  // Locale('en', 'GB')
LocaleUtils.findBestMatch(Locale('ar', 'SA'), supported);  // Locale('ar')
LocaleUtils.findBestMatch(Locale('fr'), supported);         // null

Fallback Chains #

When useFallbackChain: true, falls back through related/intelligible languages:

LocaleUtils.findBestMatch(
  const Locale('nb'),  // Norwegian Bokmål
  [Locale('no'), Locale('da'), Locale('en')],
  useFallbackChain: true,
);
// → Locale('no')  — Norwegian is a valid fallback for Bokmål

// Built-in chains:
// nb → no → nn → da → sv
// hr → sr → bs → sl
// ms → id
LocaleUtils.getRelatedLocales(const Locale('hr'));
// → [Locale('sr'), Locale('bs'), Locale('sl')]

Validation & Normalization #

LocaleUtils.isValidLanguageCode('en');   // true
LocaleUtils.isValidLanguageCode('xyz');  // false
LocaleUtils.isValidCountryCode('US');    // true
LocaleUtils.isValidCountryCode('XX');    // false
LocaleUtils.isValidLocale(const Locale('en', 'US'));  // true

// Deprecated code mapping
LocaleUtils.normalizeLocale('iw');  // Locale('he')  — Hebrew
LocaleUtils.normalizeLocale('in');  // Locale('id')  — Indonesian
LocaleUtils.normalizeLocale('jw');  // Locale('jv')  — Javanese
LocaleUtils.normalizeLocale('ji');  // Locale('yi')  — Yiddish
LocaleUtils.normalizeLocale('mo');  // Locale('ro')  — Romanian
LocaleUtils.normalizeLocale('sh');  // Locale('sr')  — Serbian
LocaleUtils.normalizeLocale('tl');  // Locale('fil') — Filipino

Plural Rules #

Different languages have different plural forms. This is critical for correct localization.

// Arabic has 6 plural forms: zero, one, two, few, many, other
LocaleUtils.getPluralCategory(const Locale('ar'), 0);   // PluralCategory.zero
LocaleUtils.getPluralCategory(const Locale('ar'), 1);   // PluralCategory.one
LocaleUtils.getPluralCategory(const Locale('ar'), 2);   // PluralCategory.two
LocaleUtils.getPluralCategory(const Locale('ar'), 5);   // PluralCategory.few
LocaleUtils.getPluralCategory(const Locale('ar'), 15);  // PluralCategory.many

// English: one, other
LocaleUtils.getPluralCategory(const Locale('en'), 1);   // PluralCategory.one
LocaleUtils.getPluralCategory(const Locale('en'), 5);   // PluralCategory.other

// Russian: one, few, many
LocaleUtils.getPluralCategory(const Locale('ru'), 1);   // PluralCategory.one
LocaleUtils.getPluralCategory(const Locale('ru'), 3);   // PluralCategory.few
LocaleUtils.getPluralCategory(const Locale('ru'), 11);  // PluralCategory.many

// Chinese, Japanese, Korean: no plural (always other)
LocaleUtils.getPluralCategory(const Locale('zh'), 5);   // PluralCategory.other

// Extension
locale.pluralCategory(3);  // PluralCategory
Group Languages Forms
No plural Chinese, Japanese, Korean, Thai, Indonesian, … other
Germanic English, German, Dutch, Swedish, … one, other
French-like French, Portuguese, Hindi, Bengali, … one, other (0 is singular)
Slavic Russian, Ukrainian, Croatian, Serbian, … one, few, many
Slavic alt Polish, Czech, Slovak one, few, other
Arabic Arabic zero, one, two, few, many, other
Celtic Irish, Welsh zero, one, two, few, other

Number Formats #

final fmt = LocaleUtils.getNumberFormat(const Locale('ar'));
fmt.decimalSeparator;   // '٫'
fmt.groupingSeparator;  // '٬'
fmt.percentSign;        // '٪'
fmt.numeralSystem;      // NumeralSystem.arabicIndic

final fmt2 = LocaleUtils.getNumberFormat(const Locale('de'));
fmt2.decimalSeparator;  // ','
fmt2.groupingSeparator; // '.'

// Get just the numeral system
LocaleUtils.getNumeralSystem(const Locale('ar'));  // NumeralSystem.arabicIndic
LocaleUtils.getNumeralSystem(const Locale('fa'));  // NumeralSystem.persianUrdu
LocaleUtils.getNumeralSystem(const Locale('th'));  // NumeralSystem.thai

// Extension
locale.numeralSystem;

Calendar & Date Metadata #

// First day of week (1=Monday, 6=Saturday, 7=Sunday)
LocaleUtils.getFirstDayOfWeek(const Locale('en', 'US'));  // 7 (Sunday)
LocaleUtils.getFirstDayOfWeek(const Locale('ar', 'SA'));  // 6 (Saturday)
LocaleUtils.getFirstDayOfWeek(const Locale('de'));         // 1 (Monday)

// Default calendar system
LocaleUtils.getDefaultCalendar(const Locale('ar', 'SA'));  // CalendarSystem.islamic
LocaleUtils.getDefaultCalendar(const Locale('fa'));         // CalendarSystem.persian
LocaleUtils.getDefaultCalendar(const Locale('th'));         // CalendarSystem.buddhist
LocaleUtils.getDefaultCalendar(const Locale('he'));         // CalendarSystem.hebrew

// Date order
LocaleUtils.getDateOrder(const Locale('en', 'US'));  // DateOrder.mdy
LocaleUtils.getDateOrder(const Locale('en', 'GB'));  // DateOrder.dmy
LocaleUtils.getDateOrder(const Locale('ja'));         // DateOrder.ymd

// Extensions
locale.firstDayOfWeek;
locale.calendarSystem;
locale.dateOrder;

Writing System Info #

final ws = LocaleUtils.getWritingSystem(const Locale('zh'));
ws.isLogographic;    // true  — affects word count, text wrapping, search
ws.usesWordSpaces;   // false — Chinese doesn't use spaces between words
ws.hasUpperCase;     // false — no uppercase/lowercase distinction

final wsAr = LocaleUtils.getWritingSystem(const Locale('ar'));
wsAr.isRtl;          // true
wsAr.hasUpperCase;   // false

final wsTh = LocaleUtils.getWritingSystem(const Locale('th'));
wsTh.isAbugida;      // true  — Thai is an abugida script
wsTh.usesWordSpaces; // false

// Text expansion factor (relative to English)
LocaleUtils.getTextExpansionFactor(const Locale('de'));  // ~1.3  (German is ~30% longer)
LocaleUtils.getTextExpansionFactor(const Locale('fi'));  // ~1.4  (Finnish is verbose)
LocaleUtils.getTextExpansionFactor(const Locale('ar'));  // ~1.25
LocaleUtils.getTextExpansionFactor(const Locale('zh'));  // ~0.5  (Chinese is compact)
LocaleUtils.getTextExpansionFactor(const Locale('ja'));  // ~0.6

// Extension
locale.writingSystem;
locale.textExpansionFactor;

Locale Info #

Get all metadata for a locale in one call:

final info = LocaleUtils.getLocaleInfo(const Locale('ar', 'EG'));
print(info.englishName);    // "Arabic"
print(info.nativeName);     // "العربية"
print(info.textDirection);  // TextDirection.rtl
print(info.scriptCode);     // "Arab"
print(info.countryCode);    // "EG"

API Reference #

LocaleUtils — Static Methods #

Method Returns Description
isRtl(locale) bool RTL check
getTextDirection(locale) TextDirection LTR or RTL
parseLocale(string) Locale Parse locale string
normalizeLocale(string) Locale Parse + normalize deprecated codes
findBestMatch(locale, list, {useFallbackChain}) Locale? Best matching locale
getRelatedLocales(locale) List<Locale> Intelligible language fallbacks
getDisplayName(locale) String English language name
getNativeName(locale) String Native language name
getFullDisplayName(locale) String English name with region
getFullNativeDisplayName(locale) String Native name with region
getCountryName(code) String? Country name from ISO 3166-1 code
getLocaleInfo(locale) LocaleInfo All metadata bundled
getPluralCategory(locale, n) PluralCategory CLDR plural form for number
getNumberFormat(locale) NumberFormatInfo Decimal/grouping separators, numeral system
getNumeralSystem(locale) NumeralSystem Numeral system (Latin, Arabic-Indic, etc.)
getFirstDayOfWeek(locale) int 1=Mon, 6=Sat, 7=Sun
getDefaultCalendar(locale) CalendarSystem Gregorian, Islamic, Persian, etc.
getDateOrder(locale) DateOrder dmy / mdy / ymd
getWritingSystem(locale) WritingSystemInfo Script properties
getTextExpansionFactor(locale) double UI text expansion relative to English
isValidLanguageCode(code) bool Valid ISO 639 language code
isValidCountryCode(code) bool Valid ISO 3166-1 country code
isValidLocale(locale) bool Valid locale

LocaleUtils — Properties #

Property Type Description
rtlLanguages Set<String> All RTL language codes

Locale Extension #

All LocaleUtils methods are also available directly on Locale:

locale.isRtl
locale.textDirection
locale.displayName
locale.nativeName
locale.fullDisplayName
locale.fullNativeDisplayName
locale.countryDisplayName
locale.isValid
locale.pluralCategory(n)
locale.numeralSystem
locale.firstDayOfWeek
locale.calendarSystem
locale.dateOrder
locale.writingSystem
locale.textExpansionFactor

Integration Examples #

With flutter_localizations #

import 'package:modular_l10n/modular_l10n.dart';

MaterialApp(
  locale: currentLocale,
  supportedLocales: AppLocalizations.supportedLocales,
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  builder: (context, child) {
    return Directionality(
      textDirection: currentLocale.textDirection,
      child: child!,
    );
  },
)

Locale Switcher Widget #

DropdownButton<Locale>(
  value: currentLocale,
  items: supportedLocales.map((locale) {
    return DropdownMenuItem(
      value: locale,
      child: Row(
        children: [
          Text(locale.nativeName),
          const SizedBox(width: 8),
          if (locale.isRtl)
            const Icon(Icons.format_textdirection_r_to_l, size: 16),
        ],
      ),
    );
  }).toList(),
  onChanged: (locale) {
    if (locale != null) onLocaleChanged(locale);
  },
)

With Provider #

class LocaleNotifier extends ChangeNotifier {
  Locale _locale = const Locale('en');
  Locale get locale => _locale;

  void setLocale(Locale locale) {
    _locale = locale;
    notifyListeners();
  }
}

With Riverpod #

final localeProvider = StateProvider<Locale>((ref) => const Locale('en'));

// Change locale
ref.read(localeProvider.notifier).state = const Locale('ar');

With Bloc/Cubit #

class LocaleCubit extends Cubit<Locale> {
  LocaleCubit() : super(const Locale('en'));
  void setLocale(Locale locale) => emit(locale);
}

Performance #

  • All data maps are const — resolved at compile time, zero runtime allocation
  • RTL check is O(1) const Set lookup
  • All methods are static — no object instantiation
  • No external dependencies

VS Code Extension (Optional) #

The Modular Flutter Localization VS Code Extension adds code generation on top of this package:

Feature Description
Type-Safe Code Generation Generates ML class with autocomplete for all translations
Modular Organization Organize translations by feature (auth, home, profile, etc.)
Watch Mode Auto-regenerate code when ARB files change
ICU Messages Full support for plurals, select, gender, date/number formatting
Extract to ARB Right-click strings in code to move them to ARB files

👉 Get the VS Code Extension


Philosophy #

This package does not handle locale switching or state management. It only provides information about locales. Use your preferred state management solution (Provider, Riverpod, Bloc, GetX, etc.) for switching.

Why? You know your app's architecture best. No vendor lock-in, stays lightweight, you maintain full control.


Issues & Contributions #

License #

MIT License — see the LICENSE file for details.


Made with care by Utanium

0
likes
160
points
131
downloads

Documentation

Documentation
API reference

Publisher

verified publisherutanium.org

Weekly Downloads

Lightweight locale utilities for Flutter with RTL detection, locale parsing, display names for 70+ languages. Works standalone or with Modular Flutter L10n extension.

Repository (GitHub)
View/report issues

Topics

#localization #l10n #i18n #rtl

License

MIT (license)

Dependencies

flutter

More

Packages that depend on modular_l10n