PackageTrack
Sign in Get early access

i18n_extension

Translation and Internationalization (i18n) for Flutter. Easy to use for both large and small projects. Uses Dart extensions to reduce boilerplate.

15.1.1 30K downloads/mo #1852 most downloaded on pub.dev marcglasberg/i18n_extension

What this package is like to depend on

Last release 3 months ago

21 May 2026

Release timing varies

gaps range from 3 weeks to 7 months

Some releases are documented

notes for 37 of 90 stable releases

1 version withdrawn

withdrawn after publishing

7 years old

100 releases · first in 2019

4 releases in the last 12 months

see the full history below

Release timeline

100 releases · Oct 2019 to May 2026
2020 2021 2022 2023 2024 2025 2026
Release Pre-release Withdrawn

Releases

latest 60 of 100
  1. 15.1.1 21 May 2026
    Release notes
    • Fixed multi-locale fallback to properly handle device language preferences. The system now correctly checks all device locales (not just the first) against all supported locales. When a device has multiple language preferences (e.g., French primary, German secondary), and the primary language is not supported but a secondary language is, the app will now correctly use the first supported language from the device's preference list.
    Open source →
  2. 15.1.0 01 Nov 2025

    Nothing published for this version

  3. 15.0.7 29 Sep 2025

    Nothing published for this version

  4. 15.0.6 29 Sep 2025

    Nothing published for this version

  5. 15.0.5 10 Apr 2025
    Release notes
    • Optionally, you can now set the supportedLocales of your app in the I18n widget. For example, if your app supports American English and Standard Spanish, you'd use: supportedLocales: [Locale('en', 'US'), Locale('es')], or supportedLocales: ['en-US'.asLocale, 'es'.asLocale].

      If you do set I18n.supportedLocales, you must add the line supportedLocales: I18n.supportedLocales to your MaterialApp (or CupertinoApp) widget, like this:

      void main() {
        WidgetsFlutterBinding.ensureInitialized();
      
        runApp(I18n(
            initialLocale: ...,
            supportedLocales: ['en-US'.asLocale, 'es'.asLocale], // Here!
            localizationsDelegates: [ ... ],
            child: AppCore(),
          ));
        }
      }
      
      class AppCore extends StatelessWidget {
        Widget build(BuildContext context) {
          return MaterialApp(
            locale: I18n.locale,
            supportedLocales: I18n.supportedLocales, // Here!
            localizationsDelegates: I18n.localizationsDelegates,
            ...
          ),
      

      By providing I18n.supportedLocales, only those supported locales will be considered when recording missing translations. In other words, unsupported locales will not be recorded as missing translations.

    • Breaking Change: The Translations.missingTranslationCallback signature has changed. This will only affect you if you've defined your own callback, which is unlikely. If your code does break, just update it to the new signature, which is an easy fix. Also, note that it now returns a boolean. Only if it returns true, the missing translation be recorded to the Translations.missingTranslations map.

    Open source →
  6. 15.0.4 26 Jan 2025

    Nothing published for this version

  7. 15.0.3 04 Jan 2025

    Nothing published for this version

  8. 15.0.2 02 Jan 2025

    Nothing published for this version

  9. 15.0.1 01 Jan 2025

    Nothing published for this version

  10. 15.0.0 19 Dec 2024

    Nothing published for this version

  11. 15.0.0-dev.1 18 Dec 2024 pre-release

    Nothing published for this version

  12. 14.1.0 16 Dec 2024
    Release notes

    Version 14 brings important improvements, like new interpolation methods, useful extensions, improved standardization, and loading translations from files and from the web, with the cost of a few breaking changes that are easy to fix. Please, follow the instructions below to upgrade your code.

    • Breaking Change: Now, you must have a single (no more than one) I18n widget in your entire widget tree, and it must always be put ABOVE the MaterialApp (or CupertinoApp) widget, in the tree. There, it will be able to provide translations to all your routes and dialogs.

    • Breaking Change: You must now add the line locale: I18n.locale to your MaterialApp (or CupertinoApp) widget, like this:

      MaterialApp(
         locale: I18n.locale,
         ...      
      
    • Breaking Change: Because of the way Flutter works, you have to make sure your I18n widget is NOT declared in the same widget as the MaterialApp, but in a parent widget. For example, this is WRONG:

      Widget build(BuildContext context) {
        return I18n( // Wrong!
          child: MaterialApp(
            home: MyScreen(),
      

      Instead, this is how your main.dart file could look like:

      import 'package:i18n_extension/i18n_extension.dart';
      import 'package:flutter_localizations/flutter_localizations.dart';
             
      void main() {
        WidgetsFlutterBinding.ensureInitialized();
        runApp(MyApp());
      }
      
      class MyApp extends StatelessWidget {
        Widget build(BuildContext context) {
          return I18n( // I18n in a parent widget!
            child: AppCore(),
          );
        }
      }
      
      class AppCore extends StatelessWidget {
        Widget build(BuildContext context) {
          return MaterialApp( // MaterialApp is here!
            locale: I18n.locale, // Locale declaration is here!
            localizationsDelegates: [ ... ],
            supportedLocales: [ ... ],
            home: ...
          ),
      

      Another good alternative is declaring the I18n widget directly inside the runApp call, in your main function:

      void main() {
        WidgetsFlutterBinding.ensureInitialized();
        runApp(I18n(child: AppCore())); // I18n in a parent widget!
      }
      
      class AppCore extends StatelessWidget {
        Widget build(BuildContext context) {
          return MaterialApp( // MaterialApp is here!
            locale: I18n.locale, // Locale declaration is here!
            localizationsDelegates: ...
          ),
      
    • Breaking Change: The MaterialApp (or CupertinoApp) widget contains, internally, a Localizations widget, which is used by Flutter to provide translations to all native Flutter widgets. The I18n widget will now automatically keep in sync with this Localizations widget, so that when you change the locale in I18n (with context.locale = 'en-US'.asLocale, for example), it will also change automatically in the Localizations widget. This means that Localizations.of(context).locale will always return the same result as I18n.of(context).locale, as context.locale, and as I18n.locale.

      If you previously had your own logic to change the native Localizations locale by changing the locale parameter of the MaterialApp widget, you can now remove it, as this is not necessary anymore.

    • Breaking Change: Language codes must now respect the BCP47 standard, when you define your translations.
      For example, you should now use en-US instead of the old en_us format. Other valid code examples are: en, es-419, hi-Deva-IN and zh-Hans-CN.

      This is an example of a WRONG translation definition:

      // Will throw: Locale "en_us" should be "en-US" 
      Translations.byText('en_us') + // Wrong!
         {
            'en_us': 'Hello, how are you?',  // Wrong!
            'pt_br': 'Olá, como vai você?',  // Wrong!
            'es': '¿Hola! Cómo estás?',
            'fr': 'Salut, comment ca va?',
            'de': 'Hallo, wie geht es dir?',
         };
      

      To help you upgrade, a TranslationsException error will be thrown when you use the old code format. The error message will be something like: Locale "en_us" should be "en-US"

      This is an example of a VALID and correct translation definition:

      Translations.byText('en-US') +
         {
            'en-US': 'Hello, how are you?',
            'pt-BR': 'Olá, como vai você?',
            'es': '¿Hola! Cómo estás?',
            'fr': 'Salut, comment ca va?',
            'de': 'Hallo, wie geht es dir?',
         };
      

      Note: If your translations are defined manually in the code, you can quickly fix this by doing a few Search and Replace commands in your IDE to fix the language codes, one for each of your supported languages, for example, replacing 'en_us' with 'en-US' etc.

    • New extension Locale.format can be used to return the string representation of the Locale as a valid BCP47 language tag (compatible with the Unicode Locale Identifier (ULI) syntax). If the locale is not valid, format may return an invalid tag, or may return string "und" (undefined).
      The language code, script code, and country code will be separated by a hyphen, and any lowercase/uppercase issues will be fixed. For example:

      var locale = Locale('en', 'us');
      print(locale.format()); // en-US, which is a valid BCP47 language tag
      print(locale.toString()); // en_US
      print(locale.toLanguageTag()); // en-us
      

      Using format is recommended over toString and toLanguageTag (both natively provided by the Locale class). In more detail:

      • Locale.format() returns the string representation of the Locale as a valid BCP47 language tag, fixing any lowercase/uppercase issues and separating components with a hyphen. Allows specifying a different separator. For example, Locale('en', 'us').format() returns en-US, and Locale('en', 'US').format(separator: '|') returns en|US.

      • Locale.toString() returns the language, script and country codes separated by an underscore. For example, Locale('en', 'us').toString() returns en_us and Locale('en', 'US').toString() returns en_US.

      • Locale.toLanguageTag() returns the language code and the country code separated by a hyphen, but does not fix case. For example, Locale('en', 'us').toLanguageTag() returns en-us, and Locale('en', 'US').toLanguageTag() returns en-US.

    • New extension String.asLocale can be used to convert a String containing a BCP47 language tag to a Locale object. For example: Locale locale = 'pt-BR'.asLocale;. If the string is not a valid BCP47 language, asLocale will try to fix it. For example, the following lines are all equivalent and result in the same locale:

      var locale = Locale('en', 'US'); 
      var locale = 'en-US'.asLocale; 
      var locale = 'en_US'.asLocale; 
      var locale = 'en-us'.asLocale; 
      var locale = 'EN-US'.asLocale; 
      var locale = 'en US'.asLocale; 
      var locale = 'en|US'.asLocale; 
      var locale = 'en.uS'.asLocale; 
      var locale = 'eN,US'.asLocale; 
      var locale = 'en;US'.asLocale; 
      var locale = 'en, US'.asLocale; 
      

      However, it will only fix the most common errors, by fixing lowercase/uppercase issues, removing spaces, and converting all these separators: - _ | . , ; to hyphens. If it can’t fix it, it will return an invalid Locale, or maybe Locale('und'), meaning the locale is undefined.

      Note that String.asLocale can be used whenever you previously used Locale constructors. For example, instead of:

      supportedLocales: [
        Locale('en', 'US'),
        Locale('es', 'ES'),
      ],
      

      You can now write:

      supportedLocales: [
        'en-US'.asLocale,
        'es-ES'.asLocale,    
      ],
      
    • New extension String.asLanguageTag can be used to try and normalize String language tags to the BCP47 standard (which is compatible with the Unicode Locale Identifier (ULI) syntax). It fixes casing (uppercase and lowercase), removes spaces, and turns underscores into hyphens. As such, it can be used to convert the old format language tags to the new ones. For example: 'en_us'.asLanguageTag returns 'en-US'.

    • Interpolations. You can now do string interpolations by replacing placeholders with values, with the args function:

      // Hello John and Mary
      'Hello {} and {}'.i18n.args('John', 'Mary');
      
      // Also works with iterables
      'Hello {} and {}'.i18n.args(['John', 'Mary']);
      
      // Named placeholders
      'Hello {name} and {other}'.i18n.args({'name': 'John', 'other': 'Mary'});
      
      // Numbered placeholders
      'Hello {1} and {2}'.i18n.args({1: 'John', 2: 'Mary'});
      
      // And you can mix placeholder types
      'Hello {name}, meet with {} and {other} to explore {1} and {2}.'.i18n.args('Charlie', {'name': 'Alice', 'other': 'Bob'}, {1: 'Paris', 2: 'London'});
      

      For all the details, check the README.md file.

    • Breaking Change: Previously, you could also do string interpolation by using sprintf specifiers, like %s, %1$s, %d etc., and providing a list of values to fill them. This is still supported:

      // Hello John and Mary
      'Hello %s and %s'.i18n.fill(['John', 'Mary']);
      
      // Hello John and Mary
      'Hello %1$s and %2$s'.i18n.fill(['John', 'Mary']);  
      
      // Hello Mary and John  
      'Hello %2$s and %1$s'.i18n.fill(['John', 'Mary']);  
      

      However, you can now also provide the values directly, without having to wrap them in a list:

      'Hello %s and %s'.i18n.fill('John', 'Mary');
      'Hello %1$s and %2$s'.i18n.fill('John', 'Mary');
      'Hello %2$s and %1$s'.i18n.fill('John', 'Mary');
      

      The breaking change part of it is that, previously, if you wanted to use the fill extension you needed to declare it yourself in your translations files. Now, that's not necessary anymore, as this extension is provided out of the box. For this reason, if you declared the fill extension yourself, you now need to remove it. Otherwise, the compiler will complain that the extension is declared twice. If you still want to keep your old declaration, change its name.

    • Auto saving the locale. Some apps may allow the user to change the language/locale of the app, from inside the app. You'd usually create some widget that presents the list of available locales, and then set it with context.locale = 'es-ES'.asLocale; or similar.

      If you want that user choice to be saved between app restarts, simply set the autoSaveLocale parameter to true:

      I18n(
        autoSaveLocale: true,
        child: AppCore(),
        ...
      

      This will automatically save changes to the locale in the device's storage (shared preferences), and restore it when the app restarts. Note the locale is read asynchronously, which may result in a one frame flicker of the default system locale, before the saved locale is restored. If you want to avoid this flicker, you can explicitly preload the locale yourself by doing initialLocale: await I18n.loadLocale() when the app starts.

      void main() async {
        WidgetsFlutterBinding.ensureInitialized();
       
        runApp(
          I18n(
            initialLocale: await I18n.loadLocale(),
            autoSaveLocale: true, 
            child: AppCore(),    
            ...
      

      Note: While usually not needed, you can also manually load, save and delete the locale from the shared preferences, at any later time, by using the provided static functions: var locale = await I18n.loadLocale(), I18n.saveLocale(locale) and I18n.deleteLocale().

    • You can get the current locale by using the context:

      Locale locale = context.locale;
      Locale locale = I18n.of(context).locale;
      

      However, you can also get the locale statically, allowing you to use it in non-widget code:

      // Get a `Locale` object, like Locale('en', 'US') 
      Locale locale = I18n.locale;
      
      // Or get a BCP47 language tag string, like 'en-US'
      String languageTag = I18n.languageTag;
      String languageTag = I18n.locale.format();
      
      // Or get a locale string with a specific separator, like 'en|US'
      String languageTag = I18n.locale.format(separator: '|');
      
      // Or get only the lowercase language code part of the locale, like 'en'.
      String language = I18n.language;
      

      Note, using I18n.localeStr is deprecated. It returns a lowercase string with underscores, like en_us.

    • To change the current locale, do this:

      context.locale = Locale('pt', 'BR');
      
      // Or
      context.locale = 'pt-BR'.asLocale;
      
      // Or
      I18n.of(context).locale = 'pt-BR'.asLocale;
      

      To reset the current locale back to the default system locale, do this:

      context.locale = null;
      
      // Or
      context.resetLocale();
      
      // Or
      I18n.of(context).locale = null;
      
      // Or
      I18n.of(context).resetLocale();
      

      Note, any of the above will change the current locale for your widgets using the i18n_extension, and also for native Flutter widgets.

    • Breaking Change: The fallback rules changed a little bit. What happens when you don’t provide the translations for the current locale? For example, suppose your current locale is Spanish, but you have only provided translations for English and French. Fallback behavior is now more intuitive and aligned with common sense. In most cases, it will do exactly what you’d expect. However, if you want all the details, check the README.md file.

    • Load translations from files or the web.

      If you want to load translations from .json files in your assets directory, create a folder and add some translation files like this:

      assets
      └── translations
          ├── en-US.json
          ├── es-ES.json
          ├── zh-Hans-CN.json
          └── pt.json  
      

      You can also use .po files:

      assets
      └── translations
          ├── en-US.po
          ├── es-ES.po
          ├── zh-Hans-CN.po
          └── pt.po  
      

      Don't forget to declare your assets directory in your pubspec.yaml:

      flutter:
        assets:
          - assets/translations/
      

      Then, you can load the translations using Translations.byFile():

      extension MyTranslations on String {
        static final _t = Translations.byFile('en-US', dir: 'assets/translations');     
        String get i18n => localize(this, _t);  
      }
      

      The above code will asynchronously load all the translations from the .json and .po files present in the assets/translations directory, and then rebuild your widgets with those new translations.

      Similarly, Translations.byHttp() allows you to load translations from .json or .po files in the web. Use it like this:

      extension MyTranslations on String {
      
        static final _t = Translations.byHttp('en-US', 
          url: 'https://example.com/translations', 
          resources: ['en-US.json', 'es.json', 'pt-BR.po', 'fr.po']);
        );
           
        String get i18n => localize(this, _t);  
      }       
      

      IMPORTANT: Since rebuilding widgets when the translations finish loading can cause a visible flicker, you can optionally avoid that by preloading the translations before running your app. To that end, first create a load() method in your MyTranslations extension:

      extension MyTranslations on String {
        static final _t = Translations.byFile('en-US', dir: 'assets/translations');  
        String get i18n => localize(this, _t);  
        
        static Future<void> load() => _t.load(); // Here!  
      }
      

      And then, in your main() method, call MyTranslations.load() before running the app:

      void main() async {
        WidgetsFlutterBinding.ensureInitialized();
        
        await MyTranslations.load(); // Here!
        
        runApp(
          I18n(
            initialLocale: await I18n.loadLocale(),
            autoSaveLocale: true,
            child: AppCore(),
          ),
        );
      }
      

      Another alternative is using a FutureBuilder:

      return FutureBuilder(
        future: MyTranslations.load(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
          return MyWidget(...);
        } else {
          return const Center(child: CircularProgressIndicator());
        } ...
      

      Try running the <a href="https://github.com/marcglasberg/i18n_extension/blob/master/example/lib/6_load_by_file_example/main.dart"> load by file example</a>, and
      the <a href="https://github.com/marcglasberg/i18n_extension/blob/master/example/lib/7_load_by_http_example/main.dart"> load by http example</a>, and the

    Open source →
  13. 14.0.1 16 Dec 2024

    Nothing published for this version

  14. 14.0.0 16 Dec 2024

    Nothing published for this version

  15. 14.0.0-dev.3 15 Dec 2024 pre-release

    Nothing published for this version

  16. 14.0.0-dev.2 13 Dec 2024 pre-release

    Nothing published for this version

  17. 14.0.0-dev.1 12 Dec 2024 pre-release

    Nothing published for this version

  18. 13.0.2 29 Oct 2024

    Nothing published for this version

  19. 13.0.1 29 Oct 2024

    Nothing published for this version

  20. 13.0.0 29 Oct 2024

    Nothing published for this version

  21. 12.0.1 14 May 2024
    Release notes
    • Compatible with Flutter 3.22.0 and Dart 3.4.0
    Open source →
  22. 12.0.0 13 May 2024

    Nothing published for this version

  23. 11.0.12 03 Mar 2024

    Nothing published for this version

  24. 11.0.11 18 Feb 2024

    Nothing published for this version

  25. 11.0.10 18 Feb 2024

    Nothing published for this version

  26. 11.0.9 18 Feb 2024

    Nothing published for this version

  27. 11.0.8 16 Feb 2024

    Nothing published for this version

  28. 11.0.7 16 Feb 2024

    Nothing published for this version

  29. 11.0.6 16 Feb 2024

    Nothing published for this version

  30. 11.0.5 16 Feb 2024

    Nothing published for this version

  31. 11.0.4 16 Feb 2024

    Nothing published for this version

  32. 11.0.3 16 Feb 2024

    Nothing published for this version

  33. 11.0.2 16 Feb 2024

    Nothing published for this version

  34. 11.0.1 15 Feb 2024

    Nothing published for this version

  35. 11.0.0 15 Feb 2024

    Nothing published for this version

  36. 11.0.0-dev.1 15 Feb 2024 pre-release

    Nothing published for this version

  37. 10.0.3 11 Feb 2024
    Release notes
    • The importer library developed by Johann Bauer is now independently available as a standalone package. You can find it at https://pub.dev/packages/i18n_extension_importer. This new package offers capabilities for importing translations in both .PO and .JSON formats. It also includes the GetStrings exporting utility, which is a useful script designed to automate the export of all translatable strings from your project.

    • Removed unused packages that were previously used by the removed importer.

    Open source →
  38. 10.0.2 24 Jan 2024

    Nothing published for this version

  39. 10.0.1 16 Nov 2023

    Nothing published for this version

  40. 10.0.0 16 Nov 2023

    Nothing published for this version

  41. 9.0.2 31 May 2023
    Release notes
    • Flutter 3.10 e Dart 3.0.0

    • Removed the importer library developed by Johann Bauer, so that users of i18n_extension don’t need to import the analyzer and other unnecessary dependencies. See version [10.0.2] above.

    Open source →
  42. 9.0.1 31 May 2023 withdrawn

    Nothing published for this version

  43. 9.0.0 12 May 2023

    Nothing published for this version

  44. 8.0.0 27 Feb 2023
    Release notes
    • Breaking change: Removed dependency on analyzer and gettext_parser. The getStrings doesn’t work in this version.
    Open source →
  45. 8.0.0-dev.1 27 Feb 2023 pre-release

    Nothing published for this version

  46. 7.0.0 16 Feb 2023

    Nothing published for this version

  47. 6.0.0 15 Nov 2022
    Release notes
    • Analyzer and sprintf version bump.
    Open source →
  48. 5.0.1 29 Aug 2022
    Release notes
    • Analyzer version bump.
    Open source →
  49. 5.0.0 13 May 2022
    Release notes
    • Flutter 3.0
    Open source →
  50. 4.2.1 04 Mar 2022
    Release notes
    • The localizePlural method now accepts any object (not only an integer anymore). It will convert that object into an integer, and use that result. Se the method documentation for more information. To make use of it, you may declare your plural() methods as String plural(value) => localizePlural(value, this, _t); from now on. Example: 'This is one item'.plural(2) is now the same as writing 'This is one item'.plural('2').
    Open source →
  51. 4.2.0 22 Dec 2021

    Nothing published for this version

  52. 4.1.3 19 Sep 2021
    Release notes
    • Bump version. Docs improvement.
    Open source →
  53. 4.1.2 08 Sep 2021

    Nothing published for this version

  54. 4.1.1 22 Aug 2021
    Release notes
    • .po importer fix.
    Open source →
  55. 4.1.0 06 Jun 2021
    Release notes
    • Removed useless uses-material-design: true.
    • Bumped dependencies versions (in special args: ^2.0.0).
    Open source →
  56. 4.1.0-dev.1 02 Jun 2021 pre-release

    Nothing published for this version

  57. 4.1.0-dev.0 20 May 2021 pre-release

    Nothing published for this version

  58. 4.0.3 14 Apr 2021
    Release notes
    • Plural support for the .PO importer.
    Open source →
  59. 4.0.2 07 Apr 2021
    Release notes
    • Downgraded args: 1.6.0 to be compatible with flutter_driver.
    • Better NNBD.
    Open source →
  60. 4.0.1 07 Apr 2021

    Nothing published for this version

Every package, every release, already written down.

The archive is open and free. Watching your own project is what we are building next.

Browse the archive