Flutter实现双商店本地化字符串切换方案咨询
方案一:生成不同类名的本地化实例,通过状态管理切换
这是最直接且无需修改intl包的方案,步骤如下:
配置两套本地化生成规则
在lib/l10n和lib/l10n_2目录下分别创建l10n.yaml,指定不同的生成类名:lib/l10n/l10n.yaml:arb-dir: . template-arb-file: app_en.arb output-localization-file: app_localizations.dart class-name: Shop1Localizationslib/l10n_2/l10n.yaml:arb-dir: . template-arb-file: app_en.arb output-localization-file: app_localizations_2.dart class-name: Shop2Localizations
执行
flutter gen-l10n后,会生成Shop1Localizations和Shop2Localizations两个独立类,各自对应一套字符串表。全局维护门店切换状态
创建一个简单的状态管理类(基于ChangeNotifier):import 'package:flutter/foundation.dart'; import 'package:your_project/l10n/app_localizations.dart'; import 'package:your_project/l10n_2/app_localizations_2.dart'; import 'package:flutter/material.dart'; class ShopLocalizationProvider extends ChangeNotifier { bool _isShop1 = true; bool get isShop1 => _isShop1; void toggleShop() { _isShop1 = !_isShop1; notifyListeners(); } dynamic getCurrentLocalizations(BuildContext context) { return _isShop1 ? Shop1Localizations.of(context) : Shop2Localizations.of(context); } }在
main.dart中用ChangeNotifierProvider包裹根Widget,让全局能访问这个状态。业务代码中复用调用逻辑
在需要展示字符串的Widget中,通过状态获取当前本地化实例,直接调用相同键名的方法:final provider = Provider.of<ShopLocalizationProvider>(context); final localizations = provider.getCurrentLocalizations(context); // 示例:调用相同键名的字符串 Text(localizations.homeTitle)点击切换按钮时,调用
provider.toggleShop()即可触发所有使用本地化的Widget刷新。
方案二:自定义LocalizationsDelegate动态加载资源
如果不想生成两个类,可以自定义Delegate手动加载不同目录的资源文件:
创建通用本地化类
class AppLocalizations { final Map<String, String> _strings; AppLocalizations(this._strings); static AppLocalizations? of(BuildContext context) { return Localizations.of<AppLocalizations>(context, AppLocalizations); } String get homeTitle => _strings['home_title'] ?? ''; // 其他字符串键... }实现可切换资源的Delegate
import 'dart:convert'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> { final String resourceDir; // 传入l10n或l10n_2目录路径 AppLocalizationsDelegate(this.resourceDir); @override bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode); @override Future<AppLocalizations> load(Locale locale) async { final jsonString = await rootBundle.loadString( '$resourceDir/app_${locale.languageCode}.arb', ); final Map<String, dynamic> jsonMap = json.decode(jsonString); final strings = jsonMap.map((key, value) => MapEntry(key, value.toString())); return AppLocalizations(strings); } @override bool shouldReload(AppLocalizationsDelegate old) => old.resourceDir != resourceDir; }动态切换Delegate
在状态类中维护当前资源目录,然后在MaterialApp的localizationsDelegates中动态传入对应Delegate:// 状态类中 String _currentResourceDir = 'lib/l10n'; void toggleShop() { _currentResourceDir = _currentResourceDir == 'lib/l10n' ? 'lib/l10n_2' : 'lib/l10n'; notifyListeners(); } // MaterialApp配置中 localizationsDelegates: [ AppLocalizationsDelegate(provider.currentResourceDir), GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ],切换门店时调用
toggleShop(),MaterialApp会自动重新加载对应目录的本地化资源。
关于依赖冲突的说明
不要尝试修改intl包的本地依赖,flutter_localizations本身依赖官方托管的intl包,强行替换必然导致版本冲突。上面的两个方案完全基于官方API实现,无需修改intl包,可规避依赖问题。
内容的提问来源于stack exchange,提问作者timyau

