【问题标题】:flutter internationalization using dynamic string in json file使用 json 文件中的动态字符串进行颤振国际化
【发布时间】:2020-09-11 18:57:23
【问题描述】:

到目前为止,我使用的是动态字符串,如本文的解决方案所示: Flutter internationalization - Dynamic strings

这是一个例子:

AppLocalizations.of(context).userAge(18)

在 AppLocalizations.dart 上:

userAge(age) => Intl.message(
  "My age is $age",
  name: "userAge",
  args: [age]);
// Return "My age is 18"

但后来我读到这篇关于 Flutter 国际化的文章:https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2 其中显示了如何使用 json 文件作为字符串的资源文件进行本地化。它看起来更方便,所以我更喜欢使用这种方法,但不知道如何从 json 文件中获取具有动态值的字符串。

有什么办法吗?

【问题讨论】:

    标签: json flutter flutter-intl


    【解决方案1】:
    1. 在您的assets 目录中创建一个文件夹,例如json。将您的语言文件放入其中。

      assets
        json
         - en.json // for English 
         - ru.json  // for Russian
      
    2. 现在在en.json 中,写下你的字符串,例如。

      {
        "myAge": "My age is"
      }
      

      同样,在ru.json

      {
        "myAge": "Мой возраст"
      }
      
    3. 将此添加到pubspec.yaml 文件中(注意空格)

      flutter:
      
        uses-material-design: true
      
        assets:
          - assets/json/
      

      运行flutter pub get


    初步工作已完成。让我们转到代码方面。

    将此样板代码复制到您的文件中:

    Map<String, dynamic> language;
    
    class AppLocalizations {
      static AppLocalizations of(BuildContext context) {
        return Localizations.of<AppLocalizations>(context, AppLocalizations);
      }
    
      String getText(String key) => language[key];
    
      String userAge(int age) => '${getText('myAge')} $age';
    }
    
    class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
      const AppLocalizationsDelegate();
    
      @override
      bool isSupported(Locale locale) => ['en', 'ru'].contains(locale.languageCode);
    
      @override
      Future<AppLocalizations> load(Locale locale) async {
        final string = await rootBundle.loadString('assets/json/${locale.languageCode}.json');
        language = json.decode(string);
        return SynchronousFuture<AppLocalizations>(AppLocalizations());
      }
    
      @override
      bool shouldReload(AppLocalizationsDelegate old) => false;
    }
    

    MaterialApp小部件中设置一些东西:

    void main() {
      runApp(
        MaterialApp(
          locale: Locale('ru'), // switch between "en" and "ru" to see effect
          localizationsDelegates: [const AppLocalizationsDelegate()],
          supportedLocales: [const Locale('en'), const Locale('ru')],
          home: HomePage(),
        ),
      );
    }
    

    现在,您可以简单地使用上面的委托:

    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        var age = AppLocalizations.of(context).userAge(18);
        // prints "My age is 18" for 'en' and "Мой возраст 18" for 'ru' locale. 
        print(age); 
    
        return Scaffold();
      }
    }
    

    【讨论】:

    • 感谢您的回复。问题是我需要资源文件中值的占位符(因为这是我发送给翻译的文件)。我发现一个很好的解决方案是使用 easy_localization 包。我成功地从 json 文件而不是 XML 文件进行翻译。我为此开了一个新帖子:link
    【解决方案2】:

    我尝试了各种实现本地化的解决方案,我遇到的最好的是 VS CodeAndroid Studio/IntelliJ 的 Flutter Intl 插件,由 Localizely.com(非附属)制作。

    有了它,基本上您可以使用市场/插件库安装插件,然后使用菜单选项为您的项目初始化。这会在 lib/l10n/intl_en.arb 中创建一个默认的英语语言环境(这听起来很吓人,但实际上只是 JSON),并在 lib/generated 中设置了所有国际化的脚手架。

    您还必须将以下内容添加到您的依赖项中。

    flutter_localizations:
        sdk: flutter
    

    然后,您可以向该文件添加密钥,它们将自动在您的应用中可用,方法是导入包含名为 S 的类的 generated/l10n.dart

    要使用它,无论您在哪里初始化 MaterialApp,请确保将 S.delegate 传递给 MaterialApp 的 localizationsDelegates 参数(最有可能作为带有 GlobalMaterialLocalizations.delegateGlobalWidgetsLocalizations.delegate 和可能 @ 的数组的一部分) 987654332@。)您还必须将S.delegate.supportedLocales 添加到MaterialApp 的supportedLocales

    要添加更多语言环境,请使用菜单中的选项(至少在 intellij 中)或简单地创建更多 intl_.arb 文件,插件会自动识别并设置相关代码。

    假设您有一个包含以下内容的 intl_en 文件:

    { "name": "Name" }
    

    然后您可以使用 S.of(context).name 在代码中使用该字符串。

    所有这些在localizely的网站上都有更雄辩的解释。


    现在,要在这些 .arb 文件中使用密钥,您只需将其包装在 {...} 中。比如:

    { "choose1OfNumOptions": "Choose 1 of {numoptions} options" }
    

    会导致使用S.of(context).choose1OfNumOptions(numOptions);。我不知道该插件是否支持完整的ARB specification,但它至少支持基本功能。

    另外,我没有使用 Localizely,但它似乎是管理翻译和插件自动集成的一种非常有用的方法,尽管我认为它的价格也非常高得吓人 - 至少对于我的应用程序来说,这种情况发生了拥有大量文字。实际上,我只有一个谷歌表,用于存储我的所有翻译,当需要更新它时,我将其下载为 .tsv 并编写了一个简单的小解析器来写入 .arb 文件。

    【讨论】:

      【解决方案3】:

      要从 JSON 文件中获取具有动态值的字符串,您可以使用

      final age = 18 //user input.
      final ageString = 'user_age'
                         .localisedString()
                         .replaceAll(new RegExp(r'\${age}'), age)
      

      en.json

      {
        "user_age": "My age is ${age}",
        "user_name_age": "My name is ${name} and age is ${age}"
      }
      

      string_extension.dart

      extension Localisation on String {
        String localisedString() {
          return stringBy(this) ?? '';
        }
      }
      

      另外,你可以做类似的事情,

        String localisedString(Map<String, String> args) {
            String str = localisedString();
            args.forEach((key, value) { 
              str = str.replaceAll(new RegExp(r'\${'+key+'}'), value);
            });
            return str;
        }
        
        //usecase
        final userName = 'Spider Man'
        final age = '18'
        final nameAgeString = 'user_name_age'.localisedString({'name': userName, 'age': age})
      

      app_localisation.dart

      Map<String, dynamic> _language;
      
      String stringBy(String key) => _language[key] as String ?? 'null';
      
      class AppLocalisationDelegate extends LocalizationsDelegate {
        const AppLocalisationDelegate();
      
        // override the following method if you want to specify the locale you are supporting.
        final _supportedLocale = ['en'];
        @override
        bool isSupported(Locale locale) => _supportedLocale.contains(locale.languageCode);
      
        @override
        Future load(Locale locale) async {
          String jsonString = await rootBundle
              .loadString("assets/strings/${locale.languageCode}.json");
      
          _language = jsonDecode(jsonString) as Map<String, dynamic>;
          print(_language.toString());
          return SynchronousFuture<AppLocalisationDelegate>(
              AppLocalisationDelegate());
        }
      
        @override
        bool shouldReload(AppLocalisationDelegate old) => false;
      }
      

      【讨论】:

      • 这个答案似乎比公认的要简洁得多。与接受的答案相比,它需要对代码进行更少的更改。
      【解决方案4】:

      flutter_localizations:

      app_en.arb:

      {
        "contactDetailsPopupEmailCopiedMessage": "Copied {email} to clipboard",
        "@contactDetailsPopupEmailCopiedMessage": {
          "description": "Message being displayed in a snackbar upon long-clicking email in contact details popup",
          "placeholders": {
            "email": {
              "type": "String",
              "example": "example@gmail.com"
            }
          }
        }
      }
      

      app_localizations.dart:

      abstract class AppLocalizations {
        /// Message being displayed in a snackbar upon long-clicking email in contact details popup
        ///
        /// In en, this message translates to:
        /// **'Copied {email} to clipboard'**
        String contactDetailsPopupEmailCopiedMessage(String email);
      }
      

      用法:

      l10n.contactDetailsPopupEmailCopiedMessage("example@gmail.com")
      

      更多详情请查看here

      【讨论】:

      • 单个字符串中的多个动态参数怎么样?
      • 酷,你可以通过添加多个参数来做到这一点......
      猜你喜欢
      • 1970-01-01
      • 2019-02-16
      • 2019-06-26
      • 2021-05-17
      • 2019-03-05
      • 1970-01-01
      • 2012-10-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多