-
Notifications
You must be signed in to change notification settings - Fork 0
Migration From layrz_models
The i18n engine has been extracted from layrz_models into its own standalone package, layrz_i18n. This guide covers everything you need to know to update your app.
Previously, if your app needed translations, you had to depend on layrz_models, which pulled in a large dependency tree:
-
dio(HTTP client) -
web_socket_channel(WebSocket support) -
latlong2(geographic coordinates) -
layrz_icons(icon package) -
recase(string transformation utilities) -
layrz_logging(logging)
layrz_i18n is completely standalone. It depends only on:
-
package:flutter/widgets.dart(the Flutter SDK) -
freezed_annotation,json_annotation(code generation) -
collection,web(small utilities)
If your app only needs translations, you can now skip layrz_models entirely and use layrz_i18n directly, resulting in a smaller app bundle and faster dependency resolution.
Update pubspec.yaml:
dependencies:
layrz_i18n: ^1.0.0Before:
import 'package:layrz_models/layrz_models.dart';After:
import 'package:layrz_i18n/layrz_i18n.dart';- If your app uses other parts of
layrz_models(models, API clients, etc.), keep it inpubspec.yaml. - If you only used it for i18n, you can remove it.
Important: Both packages can coexist during migration, because layrz_models still ships its own copy of the i18n engine. However, you must wire only one localizations delegate into your WidgetsApp or MaterialApp:
localizationsDelegates: [
LayrzI18n.delegate(
languages: languages,
supportedLocales: supportedLocales,
fallbackLocale: const Locale('en'),
),
// NOT both LayrzI18nDelegate and LayrzAppLocalizationsDelegate
],The following symbols have been renamed. Use find-and-replace to update your code, being careful to replace longer names first:
| Old (layrz_models) | New (layrz_i18n) |
|---|---|
LayrzAppLocalizations |
LayrzI18n |
LayrzAppLocalizationsDelegate |
LayrzI18nDelegate |
debugCheckHasLayrzAppLocalizations |
debugCheckHasLayrzI18n |
Example find-and-replace commands (in order of longest first):
# In your project root, replace all occurrences:
sed -i 's/debugCheckHasLayrzAppLocalizations/debugCheckHasLayrzI18n/g' lib/**/*.dart
sed -i 's/LayrzAppLocalizationsDelegate/LayrzI18nDelegate/g' lib/**/*.dart
sed -i 's/LayrzAppLocalizations/LayrzI18n/g' lib/**/*.dartThese names have not changed and require no updates:
-
t(),tc(),te(),tce()— translation methods -
hasTranslation()— check if a key exists -
of(),maybeOf()— retrieve the instance from context -
getClosestLocale()— locale matching -
setDeveloperMode()— debug mode control -
detectedLocale— system locale property -
delegate()— LocalizationsDelegate factory -
load()— internal loader -
context.i18n,context.maybeI18n— context extensions
The te() method no longer accepts an EdgeInsets padding parameter:
Before:
i18n.te(
'key',
style: TextStyle(fontSize: 16),
padding: EdgeInsets.all(4), // ← This parameter is gone
)After:
i18n.te(
'key',
style: TextStyle(fontSize: 16),
)The padding parameter was declared but never used in the original engine, so removing it has no functional impact. If you were using it, simply delete the argument.
These classes and methods remain exclusively in layrz_models and are not available in layrz_i18n:
-
I18nKey— API model for translation keys -
I18nKeyInput— input payload for upserting keys -
I18nTranslation— API model for translations -
I18nTranslationInput— input payload for translations -
Language— API model for languages -
LanguageInput— input payload for languages -
I18nKeyHistory— revision history model -
AvailableLanguage.fragment— GraphQL fragment -
AvailableLanguage.fetchAll()— GraphQL query to load languages from the Layrz API
The biggest breaking change is likely to be AvailableLanguage.fetchAll(). If your app loads languages from the Layrz API at startup, you must choose one of two approaches:
If you already depend on layrz_models for other reasons, continue using fetchAll():
import 'package:layrz_models/layrz_models.dart';
import 'package:layrz_i18n/layrz_i18n.dart';
final languages = await AvailableLanguage.fetchAll(uri: Uri.parse('https://api.example.com'));
runApp(MyApp(languages: languages));Then pass the loaded languages into the LayrzI18n.delegate():
class MyApp extends StatelessWidget {
final List<AvailableLanguage?> languages;
const MyApp({required this.languages});
@override
Widget build(BuildContext context) {
final supportedLocales = languages
.whereType<AvailableLanguage>()
.map((l) => l.getLocale())
.toList();
return WidgetsApp(
locale: LayrzI18n.getClosestLocale(
supportedLocales: supportedLocales,
fallbackLocale: const Locale('en'),
),
localizationsDelegates: [
LayrzI18n.delegate(
languages: languages,
supportedLocales: supportedLocales,
fallbackLocale: const Locale('en'),
),
],
supportedLocales: supportedLocales,
builder: (context, child) => ...,
);
}
}Write your own language loader. The AvailableLanguage in layrz_i18n has a fromJson() constructor, so you can deserialize from any HTTP client:
import 'package:layrz_i18n/layrz_i18n.dart';
Future<List<AvailableLanguage>> loadLanguages(Uri uri) async {
// Use any HTTP client you prefer (http, dio, etc.)
final response = await http.get(uri);
if (response.statusCode != 200) {
return [];
}
final json = jsonDecode(response.body) as Map<String, dynamic>;
final languagesList = json['result'] as List<dynamic>;
return languagesList
.map((item) => AvailableLanguage.fromJson(item))
.toList();
}Then use it at startup:
void main() async {
final languages = await loadLanguages(Uri.parse('https://api.example.com/languages'));
runApp(MyApp(languages: languages));
}The following changes are intentional and documented in the 1.0.0 changelog. If your app relied on the old behaviour, you may need to adjust:
Old behavior (buggy): interpolate first, then split:
message: "You have {count} | You have {count} items"
tc('key', 5, {'count': 'apple | fruit'}) → "You have apple | fruit items" (WRONG)
New behavior (correct): split first, then interpolate:
tc('key', 5, {'count': 'apple | fruit'}) → "You have apple | fruit items" (CORRECT)
If your translations or argument values contain ' | ', behavior will now be correct. If code relied on the old broken order, update your translations to not include ' | ' in argument values.
Old behavior: the delegate always returned true, forcing a rebuild even if nothing changed.
New behavior: compares languages, supportedLocales, and fallbackLocale by value; returns false if all are unchanged.
This is a performance improvement. If your app unknowingly depended on the forced reload, you may see fewer rebuilds (the correct behavior).
Old behavior:
Locale loc = LayrzAppLocalizations.getClosestLocale(
prevLanguage: 'en-',
supportedLocales: [Locale('en')],
fallbackLocale: Locale('en'),
);
// Result: Locale('en', '') — INVALID (empty region code)New behavior:
parseLocale('en-') → Locale('en') // VALIDMalformed locale codes like 'en-' or 'en_' now produce valid Locale objects. If you were working around this bug, you can remove the workaround.
Old behavior (with makeCollectionsUnmodifiable: true): returned wrapped in EqualUnmodifiableMapView, which added allocations on every access.
New behavior (with makeCollectionsUnmodifiable: false): returned as a plain Map<String, String>.
Important: do not mutate the returned map. It is shared across all uses of that language. If you need to modify it, make a copy:
// ✓ OK: read-only usage
final msg = availableLanguage.messages['greeting'];
// ✗ WRONG: don't mutate
availableLanguage.messages['greeting'] = 'Bonjour!';
// ✓ OK: make a copy if you need to modify
final mutableMessages = Map<String, String>.from(availableLanguage.messages);
mutableMessages['greeting'] = 'Bonjour!';layrz_i18n introduces two new conveniences:
Instead of LayrzI18n.of(context).t('key'), you can now write:
context.i18n.t('key') // asserts if LayrzI18n is not available
context.maybeI18n?.t('key') // returns null if not availableSee Context Extension for details.
The parseLocale(String? code) function is now exported and can be used standalone:
import 'package:layrz_i18n/layrz_i18n.dart';
final locale = parseLocale('en-US'); // Locale('en', 'US')See Locale Detection for the full reference.
-
Add dependency — Add
layrz_i18n: ^1.0.0topubspec.yamland runflutter pub get. -
Update imports — Replace
package:layrz_modelswithpackage:layrz_i18nin all i18n-related files. -
Rename symbols — Replace the three renamed classes:
-
debugCheckHasLayrzAppLocalizations→debugCheckHasLayrzI18n -
LayrzAppLocalizationsDelegate→LayrzI18nDelegate -
LayrzAppLocalizations→LayrzI18n
-
-
Remove
te()padding — Delete anypadding: ...arguments fromte()ortce()calls. -
Handle language loading — If you use
AvailableLanguage.fetchAll(), choose Option 1 (keep layrz_models) or Option 2 (write your own loader). -
Test thoroughly — Pay special attention to:
- Plural forms (
tc(),tce()) - Argument values containing
' | ' - Locale detection and fallback behavior
- Rich text rendering (
te(),tce())
- Plural forms (
-
Clean up — If you no longer need
layrz_models, remove it frompubspec.yamland delete unused imports.
Refer to the full layrz_i18n documentation for more details on any topic.
Made with ❤️ by Golden M, Inc.