Over-the-air translation updates in Flutter
Flutter's built-in localization compiles your text into the app, so every change needs a new release. This guide loads your litei18n translations when the app starts instead, with the bundled copy as a fallback.
Setup steps
Create a release
In litei18n, commit your translations and create a release on the Releases page (Starter plan and above). Copy your project ID from the release URL shown there. Export your translations as JSON too, and add them to your app as the bundled copy.
Load the release when the app starts
Add the http package to pubspec.yaml, put your exported JSON files in assets/i18n/ and list that folder under flutter: assets: in pubspec.yaml, or the app cannot read them. This class reads the bundled file first, then replaces its values with the latest release when the network allows.
lib/translations.dartimport 'dart:convert'; import 'package:flutter/services.dart' show rootBundle; import 'package:http/http.dart' as http; const projectId = 'YOUR_PROJECT_ID'; const releases = 'https://ttksowxkfdohtprtfarf.supabase.co/storage/v1/object/public/releases/locales'; class Translations { Translations(this._values); final Map<String, String> _values; String t(String key) => _values[key] ?? key; static Future<Translations> load(String locale) async { // The copy bundled with the app: works offline and on first launch. final values = Map<String, String>.from( jsonDecode(await rootBundle.loadString('assets/i18n/$locale.json')), ); try { final res = await http .get(Uri.parse('$releases/$projectId/latest/$locale.json')) .timeout(const Duration(seconds: 5)); if (res.statusCode == 200) { values.addAll(Map<String, String>.from(jsonDecode(utf8.decode(res.bodyBytes)))); } } catch (_) { // Offline or slow: keep the bundled copy. } return Translations(values); } }Use the strings
Load the translations before runApp and pass them down, or keep them in the state management you already use. A missing key shows its own name, so it is easy to spot.
lib/main.dartimport 'package:flutter/material.dart'; import 'translations.dart'; Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); final tr = await Translations.load('es'); runApp(MyApp(tr: tr)); } // In a widget: Text(tr.t('home.title'))Change text without a new build
Edit a translation, commit it and create a new release. The latest URL now serves the new text, and your app picks it up the next time it loads translations. No new build, no app store review.
Good to know
- Release files are cached on a CDN, so a new release can take a few minutes to show everywhere.
- To pin an app version to one release, use the versioned URL (…/v3/es.json) instead of …/latest/es.json.
- To keep the downloaded text offline, save the response with shared_preferences or path_provider and read it before the bundled file next time.
Start shipping globally in 5 minutes
Join developers who ship their apps in every language with litei18n.
Free plan available · No credit card required · Set up in under 5 minutes