Flutter 从本地化 JSON 文件解析数组

Posted

技术标签:

【中文标题】Flutter 从本地化 JSON 文件解析数组【英文标题】:Flutter parse array from localization JSON file 【发布时间】:2021-01-19 22:26:50 【问题描述】:

我正在尝试将一组城市名称添加到我的本地化 json 文件中,但在将其转换为字符串后,我无法将其解码回数组。

zh-CN.json


  "title" : "My account",
  "name" : "John",
  "cities" : ["Paris", "Lyon", "Nice"]

AppLocalization.dart

class AppLocalizations 
  final Locale locale;

  AppLocalizations(this.locale);

  static AppLocalizations of(BuildContext context) 
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  

  static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationsDelegate();

  Map<String, String> _localizationStrings;

  Future<bool> load() async 
    String jsonString = await rootBundle.loadString(
        'assets/translations/$locale.languageCode-$locale.countryCode.json');

    Map<String, dynamic> jsonMap = json.decode(jsonString);

    _localizationStrings = jsonMap.map((key, value) 
      return MapEntry(key, value.toString());
    );
    return true;
  

  Future<void> setLocale(Locale locale) async 
    final SharedPreferences _prefs = await SharedPreferences.getInstance();
    final _languageCode = locale.languageCode;
    await _prefs.setString('locale', _languageCode);
    print('locale saved!');
  

  static Future<Locale> getLocale() async 
    final SharedPreferences _prefs = await SharedPreferences.getInstance();
    final String _languageCode = _prefs.getString('locale');
    if (_languageCode == null) return null;

    Locale _locale;
    _languageCode == 'en'
        ? _locale = Locale('en', 'US')
        : _locale = Locale('ar', 'EG');
    return _locale;
  

  String translate(String key) 
    return _localizationStrings[key];
  


class _AppLocalizationsDelegate
    extends LocalizationsDelegate<AppLocalizations> 
  const _AppLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) 
    return ['en', 'ar'].contains(locale.languageCode);
  

  @override
  Future<AppLocalizations> load(Locale locale) async 
    AppLocalizations localization = AppLocalizations(locale);
    await localization.load();
    return localization;
  

  @override
  bool shouldReload(LocalizationsDelegate<AppLocalizations> old) 
    return false;
  

在我的小部件中,我尝试通过 List&lt;String&gt; _cities = AppLocalization.of(context).translate('cities'); 访问数组

这可行,如果我打印 _cities.toString() 它会打印 [Paris, Lyon, Nice]。

问题

当我尝试使用 json.decode(_cities) 将 _cities 解码为数组时,我总是收到格式错误 Unhandled Exception: FormatException: Unexpected character (at character 2)。

我相信数组在这个函数中被转换为字符串

_localizationStrings = jsonMap.map((key, value) 
  return MapEntry(key, value.toString());
);

如何将其解析回数组??

我愿意接受各种建议。谢谢

【问题讨论】:

【参考方案1】:

我知道这可能不是最好的方法,但这只是我找到的一种方法。您还可以将您的值保存在不同的 json 资产文件中并使用 json.decode() 正常解析它,但我需要这个来更轻松地处理本地化。

所以我找到了一个简单的解决方案,而不是向本地化类添加功能。

我从本地化文件(与任何其他本地化字符串一样)中将小部件中的数组作为字符串获取,如下所示:

var list = AppLocalization.of(context).translate('cities');//this will return [Paris, Lyon, Nice] as a string

然后我执行以下操作:

list = list.replaceAll(RegExp("\\[|\\]"), ''); //removes square brackets from the string
var cityList = list.split(','); //splits the city names by the comma as a 
print(cityList); //prints [Paris, Lyon, Nice] as List<String>

我还实现了另一种获取嵌套 json 数组的方法,例如


  "title" : "My account",
  "name" : "John",
  "cities" : ["Paris", "Lyon", "Nice"],
  "areas" : 
  
     "paris" : ["area1", "area2", "area3"], 
     "lyon": ["area1", "area2", "area3"]
  

我这样做是为了获得巴黎地区...

  var areas = AppLocalization.of(context).translate('areas'); //this gets the whole map as a string.
  areas = areas.replaceAll(RegExp("\\[|\\]|\\|\\"), '');//this removes leading and trailing [ ]
  var splitValues = areas.split(':');
  var mapValues = Map.fromIterable(test, key: (item) => splitValues[0],value: (item) => item.split(','),);

    var parisAreas = mapValues.map((key, value) =>
    MapEntry<String, List<String>>(key, List<String>.from(value)));

【讨论】:

【参考方案2】:

如果您想以数组的形式访问本地化数据,请不要将数组展平为字符串。根据您的用例,将此类数据存储在本地化文件中可能并不合适。

但是,如果您仍想将列表存储在本地化文件中,那么我建议您不要将解码后的 json 映射展平并返回动态值而不是 translate 中的字符串。当您检索本地化值时,您必须将其类型转换为您期望的类型,因此您可能需要添加一些辅助函数(见下文)来稍微干燥它。

import 'dart:convert';

var cities = '''

  \"cities\" : [\"Paris\", \"Lyon\", \"Nice\"]

''';

class Localization 
  final Map<String,dynamic> data;
  
  Localization(this.data);

  String localizedString(String key) 
    return data[key] as String;
  

  List<String> localizedList(String key) 
    return List<String>.from(data[key]);
  


void main()
  var data = jsonDecode(cities); //  jsonDecode(cities);
  var l = Localization(data);

  for(var city in l.localizedList("cities")) 
    print("$city");
      

【讨论】:

解决方案不起作用,因为 _localizationStrings[key] 的类型是 List 所以我得到一个错误,但为了让它工作我做了以下Map&lt;String, dynamic&gt; _localizationStrings; instead of Map&lt;String, String&gt; _localizationStrings; 在这里我删除了 toString on value _localizationStrings = jsonMap.map((key, value) return MapEntry(key, value); ); List&lt;dynamic&gt; localizedList(String key) return _localizationStrings[key] as List&lt;String&gt;; 在我的小部件中我现在必须将返回的 List 转换为 Listlist = list.cast&lt;String&gt;().toList(); 但是我对本地化类中的所有这些变化感到不自在。我的用例是使用列表来填充下拉菜单(两种语言的城市名称),但列表比示例中的要大得多,所以我不想对其进行硬编码。也许有更好的解决方案? 感谢您的帮助,但新答案也不能解决问题,因为我从本地化 JSON 文件中获取了数组字符串。我找到了一个更优雅的解决方案,我会发布它。

以上是关于Flutter 从本地化 JSON 文件解析数组的主要内容,如果未能解决你的问题,请参考以下文章

使用 Flutter 中的类解析 JSON 文件

解析 JSON 文件(本地存储),从而快速创建对象数组

如何在 Flutter 中解析这个 JSON 数组?

flutter : 嵌套的 json 解析列表

Flutter 如何从 JSON 中列出具有多个子项的数组

Flutter - 解析多个 json 数组并设置为列表