Easy Localization API翻译不稳定问题排查求助
问题排查:Flutter easy_localization 切换语言不稳定
我在Flutter项目中使用easy_localization 3.0.3和easy_localization_loader 2.0.1,通过自定义的SmartNetworkAssetLoader从API获取翻译文件,但切换语言时翻译功能时常不稳定,无法完整翻译整个应用。请帮忙排查该问题是SmartNetworkAssetLoader代码存在问题,还是库本身的问题?
自定义资源加载器:SmartNetworkAssetLoader
class SmartNetworkAssetLoader extends AssetLoader { final Function localeUrl; final Duration timeout; final String assetsPath; final Duration localCacheDuration; SmartNetworkAssetLoader( {required this.localeUrl, this.timeout = const Duration(seconds: 30), required this.assetsPath, this.localCacheDuration = const Duration(days: 1)}); @override Future<Map<String, dynamic>> load(String localePath, Locale locale) async { var string = ''; print("localPath $localePath"); print("locale $locale"); // 尝试加载本地已保存的翻译文件 if (await localTranslationExists(locale.toString())) { string = await loadFromLocalFile(locale.toString()); } // 无本地缓存或加载失败,检查网络并下载文件 if (string == '' && await isInternetConnectionAvailable()) { string = await loadFromNetwork(locale.toString()); } // 缓存过期或无网络,但优先使用本地缓存而非资产文件 if (string == '' && await localTranslationExists(locale.toString(), ignoreCacheDuration: false)) { string = await loadFromLocalFile(locale.toString()); } // 仍无内容则加载本地资产文件 if (string == '') { String localeString = locale.toString(); // 将下划线替换为连字符 String convertedLocale = localeString.replaceAll('_', '-'); string = await rootBundle .loadString('$assetsPath/$convertedLocale.json'); } // 返回解析后的JSON return json.decode(string); } Future<bool> localeExists(String localePath) => Future.value(true); Future<bool> isInternetConnectionAvailable() async { try{ ConnectivityResult connectivityResult = await (Connectivity().checkConnectivity()); if (connectivityResult == ConnectivityResult.none) return false; final result = await Future.any( [InternetAddress.lookup('google.com'), Future.delayed(timeout)]); if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { return true; } }catch(e) { print(e); } return false; } Future<String> loadFromNetwork(String localeName) async { String url = localeUrl(localeName); print("loccaleName$localeName"); print("url $url"); url = '$url$localeName'; print("final Url $url"); try { final response = await Future.any([http.get(Uri.parse(url)), Future.delayed(timeout)]); if (response != null && response.statusCode == 200) { var content = utf8.decode(response.bodyBytes); log("content1234 $content"); try { var jsonResponse = json.decode(content); if (jsonResponse != null && jsonResponse['data'] != null) { // 仅提取"data"字段内容 var dataContent = jsonResponse['data']; // 转换回JSON字符串 var dataContentString = json.encode(dataContent); print("data content: $dataContentString"); print("localeName $localeName"); // 保存翻译内容 await saveTranslation(localeName, dataContentString); return dataContentString; } } catch (e) { showFalseSnackbar("$e"); print("Error decoding JSON: $e"); } }else{ showFalseSnackbar("response null or ${response.statusCode}"); } } catch (e) { showFalseSnackbar("$e"); print(e.toString()); } return ''; } Future<bool> localTranslationExists(String localeName, {bool ignoreCacheDuration = false}) async { var translationFile = await getFileForLocale(localeName); if (!await translationFile.exists()) { return false; } // 不检查文件时效 if (!ignoreCacheDuration) { var difference = DateTime.now().difference(await translationFile.lastModified()); if (difference > (localCacheDuration)) { return false; } } return true; } Future<String> loadFromLocalFile(String localeName) async { return await (await getFileForLocale(localeName)).readAsString(); } Future<void> saveTranslation(String localeName, String content) async { try{ var file = File(await getFilenameForLocale(localeName)); await file.create(recursive: true); return print('saved'); }catch(e) { print("error creating file $e"); } } Future<String> get _localPath async { final directory = await paths.getTemporaryDirectory(); return directory.path; } Future<String> getFilenameForLocale(String localeName) async { return '${await _localPath}/translations/$localeName.json'; } Future<File> getFileForLocale(String localeName) async { return File(await getFilenameForLocale(localeName)); } }
main.dart 初始化代码
runApp((EasyLocalization( supportedLocales:[ Locale('en', 'US'), Locale('es', 'ES') ], path: 'assets/translations', fallbackLocale: const Locale('en', 'US'), assetLoader: SmartNetworkAssetLoader( assetsPath: 'assets/translations', localCacheDuration: const Duration(days: 1), localeUrl: (String localeName) => Constants.appLangUrl, timeout: const Duration(seconds: 30), ), child: const MyApp())));
语言切换下拉按钮及相关逻辑
下拉按钮代码
DropdownButton<String>( value: state.selectedValue, onChanged: (newValue) async { context.read<LoginBloc>().add(DropDownButtonClickedEvent(newValue!)); await changeLanguageHandler(context, newValue); }, items: state.list ?.map<DropdownMenuItem<String>>((element) { return DropdownMenuItem<String>( value: element, child: Text( element, style: const TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.w400, ), ), ); }).toList(), icon: Image.asset( "assets/images/arrowdown.png", width: 16.w, height: 7.h, ), ),
语言切换处理函数
Future<void> changeLanguageHandler( BuildContext context, String language) async { try { await LocalizationChecker.changeLanguage(context, language); setState(() {}); } catch (error) { print("error $error"); } }
LocalizationChecker 类
class LocalizationChecker { static changeLanguage(BuildContext context, String language) { // Locale? currentLocal = EasyLocalization.of(context)!.currentLocale; print("inside localization"); if (language == 'Spanish') { EasyLocalization.of(context)!.setLocale(const Locale('es', 'ES')); } else { EasyLocalization.of(context)!.setLocale(const Locale('en', 'US')); } } }
问题排查与修复建议
1. SmartNetworkAssetLoader 代码的核心问题
- 缓存未写入内容:
saveTranslation方法仅创建了文件,但没有将content写入文件,导致本地缓存始终为空。每次切换语言都需要重新请求网络或 fallback 到资产文件,直接引发不稳定。
修复代码:Future<void> saveTranslation(String localeName, String content) async { try{ var file = File(await getFilenameForLocale(localeName)); await file.create(recursive: true); await file.writeAsString(content); // 新增:写入翻译内容 print('saved'); }catch(e) { print("error creating file $e"); } } - 网络超时处理漏洞:使用
Future.any处理超时后,若触发超时,response会是null,但代码直接访问response.statusCode会引发空指针异常,导致网络加载流程中断。
修复:final result = await Future.any([http.get(Uri.parse(url)), Future.delayed(timeout, () => null)]); if (result is http.Response && result.statusCode == 200) { // 后续解析逻辑 } - Locale格式一致性问题:
Locale('en','US').toString()返回en_US,但加载本地资产时替换为en-US,缓存文件命名使用en_US,需确保API返回的locale格式与缓存命名一致,否则会导致缓存无法命中。
2. 语言切换逻辑的潜在问题
- 异步操作未等待:
setLocale是异步方法,但LocalizationChecker.changeLanguage是同步方法,未等待切换完成就调用setState,会导致UI更新不及时,出现部分未翻译内容。
修复:class LocalizationChecker { static Future<void> changeLanguage(BuildContext context, String language) async { print("inside localization"); if (language == 'Spanish') { await EasyLocalization.of(context)!.setLocale(const Locale('es', 'ES')); } else { await EasyLocalization.of(context)!.setLocale(const Locale('en', 'US')); } } } - Widget未监听Locale变化:确保所有需要翻译的Widget都使用
EasyLocalization的tr方法,若使用状态管理(如Bloc),需在Locale变化时触发状态更新。
3. 库本身的可能性
easy_localization 3.0.3属于稳定版本,若上述问题修复后仍不稳定,可尝试:
- 升级到最新兼容版本,排查是否存在已知的Locale切换Bug
- 检查项目中是否存在多个
EasyLocalization实例,或Context获取错误的情况
内容的提问来源于stack exchange,提问作者Nns_ninteyFIve
相关产品推荐
相关产品推荐

