Skip to content

Crowdin Flutter SDK for instant translation delivery Over-The-Air directly to your application

License

Notifications You must be signed in to change notification settings

crowdin/flutter-sdk

Crowdin Flutter SDK

The Crowdin Flutter SDK enables Over-The-Air (OTA) translation updates, delivering new translations from your Crowdin project directly to users without requiring app store updates. The SDK works on top of Flutter's standard localization system (flutter_localizations), providing a seamless bridge between your local ARB files and Crowdin's Content Delivery Network.

Features

  • Seamless integration with Flutter's localization
    • Generates a wrapper class that bridges Flutter's gen_l10n output with Crowdin CDN
    • ARB files are created and maintained by you (the SDK does not generate ARB files)
    • Automatic fallback to local ARB files when cloud translations are unavailable
  • Over-The-Air translation updates
    • Load remote translations from Crowdin Content Delivery Network
    • Built-in caching mechanism (enabled by default) for offline support
    • Network usage configuration (All, only Wi-Fi or Cellular)
  • Real-Time Preview – all the translations that are done in the Editor can be shown in your version of the application in real-time. View the translations already made and the ones you're currently typing in.

How It Works

The Crowdin Flutter SDK works in conjunction with Flutter's standard localization tools:

flowchart LR
    ARB[ARB Files<br/>Created by you] --> GenL10n[Flutter gen_l10n<br/>Generates .dart classes]
    GenL10n --> CrowdinGen[Crowdin Generator<br/>Creates wrapper class]
    CrowdinGen --> Runtime[Runtime<br/>OTA updates + fallback]
Loading

The workflow:

  1. You create and maintain ARB localization files (e.g., app_en.arb, app_es.arb) in your project
  2. Flutter's gen_l10n tool generates Dart localization classes from your ARB files
  3. Crowdin SDK generator (flutter pub run crowdin_sdk:gen) creates a wrapper class that extends Flutter's generated classes
  4. At runtime, the SDK fetches fresh translations from Crowdin CDN and falls back to local ARB files when needed

This architecture ensures your app always has working translations (from local ARB files) while enabling dynamic updates from Crowdin.

Requirements

  • Flutter >=3.22.0
  • Dart >=3.4.0

Setup

To configure Flutter SDK integration you need to:

  • Upload your .arb localization files to Crowdin. If you have ready translations, you can also upload them. Alternatively, you can use the Flutter .ARB String Exporter to export Flutter .arb from your Crowdin project strings.
  • Set up Distribution in Crowdin.
  • Set up SDK and enable Over-The-Air Content Delivery feature in your project.

Distribution is a CDN vault that mirrors the translated content of your project and is required for integration with the Flutter app.

To manage distributions, open the Crowdin project and go to the Translations > Over-The-Air Content Delivery section. You can create as many distributions as you need and select different files for each. You'll need to click the Release button next to the distribution each time you want to send new translations to the app.

To integrate SDK with your application you need to follow the step-by-step instructions:

  • First of all, your Flutter project should be internationalized using the flutter_localizations package. For more detail, see Setting up an internationalized app.

  • Create a project in Crowdin.

  • Upload your app_en.arb file to the created Crowdin project. Optionally, you can also Upload Existing Translations.

    Note: ARB files must be created and maintained manually in your project. The Crowdin SDK does not generate ARB files from Crowdin—it generates a wrapper class to integrate Crowdin translations with Flutter's localization system.

  • Set up a Distribution.

  • Add the crowdin_sdk dependency to your project:

    dependencies:
      crowdin_sdk: ^1.0.0
    
      flutter_localizations:
        sdk: flutter
      intl: any
    
    flutter:
      generate: true
  • Run the following command to generate the Crowdin wrapper class:

    flutter pub run crowdin_sdk:gen

    This generates crowdin_localizations.dart in the same directory as your app_localizations.dart (by default, in your arb-dir directory, e.g., lib/l10n/). This wrapper class extends Flutter's generated localization classes to integrate Crowdin OTA translations.

    Important: Re-run this command whenever you modify the structure of your ARB files (e.g., add/remove keys or change parameters).

  • Update localizationsDelegates in your project:

    import 'package:your_app/l10n/app_localizations.dart';
    import 'package:crowdin_sdk/crowdin_sdk.dart';
    import 'package:your_app/l10n/crowdin_localizations.dart';
    @override
    Widget build(BuildContext context) {
      return MaterialApp(
        // ...
    
        localizationsDelegates: CrowdinLocalization.localizationsDelegates,
        supportedLocales: AppLocalizations.supportedLocales,
        ),
    
        // ...
      );
    }
  • Initialize Crowdin SDK in the main function of your application:

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
    
      await Crowdin.init(
        distributionHash: 'distribution_hash', // Fill in with your distribution hash
        connectionType: InternetConnectionType.any,
        updatesInterval: const Duration(minutes: 15),
      );
    
      // ...
    }
  • Use the Crowdin.loadTranslations function to load translations from Crowdin for the specified locale:

    await Crowdin.loadTranslations(Locale('en'));

After receiving the translations, change the app locale as usual and the translations from Crowdin will be applied.

Configuration

Config option Description
distributionHash Crowdin Distribution Hash
connectionType Network type to be used for translations download. Supported values are any, wifi, mobileData, ethernet
updatesInterval Translations update interval. Translations will not be updated more frequently than the designated time interval (default minimum is 15 minutes). Instead, it will use previously cached translations

Translation Loading and Caching

When to call loadTranslations()

The Crowdin.loadTranslations() method fetches translations from Crowdin's CDN. You should call it:

  • When the user changes the app language
  • On app launch to fetch the latest translations (optional—cached translations are used automatically)
  • When you want to manually refresh translations from Crowdin

Important: You do NOT need to call loadTranslations() every time you access a translation. Once loaded, translations are available synchronously through the standard Flutter localization API (AppLocalizations.of(context)).

How caching works

  • First call: Downloads translations from Crowdin CDN and caches them locally
  • Subsequent calls: Uses cached translations if the updatesInterval hasn't expired
  • Persistence: Cache survives app restarts, reducing network requests and improving performance
  • Updates: Automatically checks for new translations based on your updatesInterval setting

Translation flow and fallback behavior

The SDK provides automatic fallback to ensure your app always has working translations:

  1. Primary: Tries to use translations from Crowdin CDN (if loaded successfully)
  2. Fallback: Uses local ARB files if cloud translations are unavailable

Fallback occurs when:

  • Network is unavailable during loadTranslations()
  • Cloud translation structure doesn't match local (e.g., parameters added/removed)
  • Translation key is missing in cloud distribution
  • Parsing error in cloud translation

Important: If the translation structure in Crowdin changes significantly (e.g., different parameters), the SDK cannot process it and automatically falls back to local ARB files. To handle structure changes, you must update your local ARB files, re-run flutter pub run crowdin_sdk:gen, and rebuild your app.

Real-Time Preview

All translations done in the Crowdin Editor can be displayed in your version of the application in real-time. See the translations that have already been done and the ones you're typing.

Note: Real-Time Preview feature should not be used in production builds. Currently, this feature is available only for Android and iOS applications.

Setup

Add the following code to the Crowdin initialization:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Crowdin.init(
    distributionHash: 'distribution_hash',
    connectionType: InternetConnectionType.any,
    updatesInterval: const Duration(minutes: 15),
    withRealTimeUpdates: true, // use this parameter for enable/disable real-time preview functionality
    authConfigurations: CrowdinAuthConfig(
     clientId: 'clientId', // your clientId from Crowdin OAuth app
     clientSecret: 'clientSecret', // your client secret from Crowdin OAuth app
     redirectUri: 'redirectUri', // your redirect uri from Crowdin OAuth app
     organizationName: 'organizationName' // optional (only for Crowdin Enterprise)
    ),
  );

  // ...
}

Wrap your app root widget with the CrowdinRealTimePreviewWidget:

@override
Widget build(BuildContext context) {
  return CrowdinRealTimePreviewWidget(
    child: MaterialApp(
      // ...

      localizationsDelegates: CrowdinLocalization.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      ),

      // ...
    );
  }
}

For OAuth App the redirect URL should match your app scheme. For example, for scheme <data android:scheme="crowdintest" />, redirect URL in Crowdin should be crowdintest://. Specify project.translation scope for the OAuth app on Crowdin.

For Android app, declare the following intent filter in android/app/src/main/AndroidManifest.xml:

<manifest ...>
<!-- ... other tags -->
<application ...>
  <activity ...>
    <!-- ... other tags -->

    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <!-- Accepts URIs that begin with https://YOUR_HOST -->
      <data android:scheme="[YOUR_SCHEME]"/>
    </intent-filter>
    
  </activity>
</application>
</manifest>

For iOS app, declare the scheme in ios/Runner/Info.plist:

<?xml ...>
<!-- ... other tags -->
<plist>
  <dict>
    <!-- ... other tags -->

    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLSchemes</key>
        <array>
          <string>[YOUR_SCHEME]</string>
        </array>
      </dict>
    </array>

    <!-- ... other tags -->
  </dict>
</plist>

Config options

Config option Description
withRealTimeUpdates Enable Real-Time Preview feature
authConfigurations CrowdinAuthConfig class that contains parameters for OAuth authorization
clientId Crowdin OAuth Client ID
clientSecret Crowdin OAuth Client Secret
redirectUri Crowdin OAuth redirect URL
organizationName An Organization domain name (for Crowdin Enterprise users only)

For more information about OAuth authorization in Crowdin, please check this article.

Note: To easily run your app in the Crowdin Editor, you can use Crowdin Appetize integration. It allows your translators to run this app in the Editor, see more context, and provide better translations.

Notes

Best Practices

  • Call loadTranslations() strategically: Use it when changing language or to fetch the latest updates, not on every translation access. Translations are synchronous after loading.
  • Keep local ARB files as source of truth: Always test with local ARB files before relying on cloud translations. They serve as your fallback.
  • Configure updatesInterval appropriately: Balance translation freshness with network usage based on your update frequency needs.

Important Clarifications

  • ARB file management: You must create and maintain ARB files manually. The Crowdin SDK generator creates wrapper classes, not ARB files.

  • CDN and local files: The CDN feature does not update your local ARB files. If you want to add new translations or modify structures, update the ARB files manually and re-run flutter pub run crowdin_sdk:gen.

  • Structural changes: When translation structures change in Crowdin (e.g., adding/removing parameters), you must update your local ARB files and rebuild the app. The SDK will automatically fall back to local ARB if cloud structures don't match.

  • Caching behavior: Once the SDK receives translations, they're stored on the device for future sessions to minimize network requests. Storage time can be configured via updatesInterval.

  • CDN delay: CDN caches all translations in release, so even when new translations are released in Crowdin, CDN may return them with a delay.

  • Since some languages have different language codes maintained by the intl package and by Crowdin (for example, intl uses "es" for the Spanish language, and Crowdin uses "es-ES"). For the following intl language codes Crowdin SDK uses equivalent language codes:

    • Armenian - hy: hy-AM
    • Chinese Simplified - zh: zh-CN
    • Gujarati - gu: gu-IN
    • Nepali - ne: ne-NP
    • Portuguese - pt: pt-PT
    • Punjabi - pa: pa-IN
    • Sinhala - si: si-LK
    • Spanish - es: es-ES
    • Swedish - sv: sv-SE
    • Urdu (India) - ur: ur-IN
  • synthetic-package default: Starting from v1.0.0, the SDK defaults to synthetic-package: false to align with Flutter 3.32+ deprecation of the synthetic flutter_gen package. If you're upgrading from an earlier version, see the Migration Guide:

    • Add synthetic-package: false to your l10n.yaml (or rely on the new default)
    • Update imports from package:flutter_gen/gen_l10n/... to your local path (e.g., package:your_app/l10n/...)
    • Run flutter pub run crowdin_sdk:gen to regenerate

Contributing

If you would like to contribute, please read the Contributing Guidelines.

Seeking Assistance

If you find any problems or would like to suggest a feature, please feel free to submit an issue on GitHub at the Issues Page.

Security

Crowdin Flutter SDK CDN feature is built with security in mind, which means minimal access possible from the end-user is required. When you decide to use Crowdin Flutter SDK, please make sure you’ve made the following information accessible to your end-users.

  • We use the advantages of Amazon Web Services (AWS) for our computing infrastructure. AWS has ISO 27001 certification and has completed multiple SSAE 16 audits. All the translations are stored at AWS servers.
  • When you use Crowdin Flutter SDK CDN – translations are uploaded to Amazon CloudFront to be delivered to the app and speed up the download. Keep in mind that your users download translations without any additional authentication.
  • We use encryption to keep your data private while in transit.
  • We do not store any Personally Identifiable Information (PII) about the end-user, but you can decide to develop the opt-out option inside your application to make sure your users have full control.

License

The Crowdin Flutter SDK is licensed under the MIT License.
See the LICENSE file distributed with this work for additional 
information regarding copyright ownership.

Except as contained in the LICENSE file, the name(s) of the above copyright 
holders shall not be used in advertising or otherwise to promote the sale, 
use or other dealings in this Software without prior written authorization.

About

Crowdin Flutter SDK for instant translation delivery Over-The-Air directly to your application

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Contributors 5

Languages