Flutter应用程序的本地化的方法

674 阅读4分钟

最近发布的Flutter 3,以其美妙的开发体验和在许多平台上运行的单一代码库的承诺,已经成为许多iOS和Android应用程序的瑞士军刀,同时桌面和Web应用程序也慢慢加快了步伐。

当你发布一个将在全球范围内使用的应用程序时,只用一种语言并不能为每个最终用户提供相同的体验。虽然英语是使用最广泛的语言之一,但翻译应用程序使其更容易被所有用户接受和理解。出于这个原因,我们将在本文中学习Flutter应用程序的本地化。

我们将建立什么?

我们将尝试通过使用一个每当你创建Flutter项目时都会派上用场的反例应用程序来理解本地化,并在反例中做一些改变来演示本地化。

我们将使用Flutter本地化包成功地将我们的应用程序翻译成另一种语言,包括单词和短语的插值以及单数和复数的正确翻译。

内容表

项目配置

  • 将所需的依赖性添加到pubspec.yaml:

    environment:
      sdk: ">=2.16.1 <3.0.0"
    
    dependencies:
      flutter:
        sdk: flutter
      flutter_localizations:
        sdk: flutter
      intl: ^0.17.0  
      cupertino_icons: ^1.0.2
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
      flutter_lints: ^1.0.0
    
    flutter:
      generate: true
      uses-material-design: true
    
    

如上所述,修改你的pubspec.yaml 文件。flutter localizations 包括一个本地的本地化包和intl ,可以实现国际化和本地化,包括消息翻译、复数和性别。generate: true 行对于本地化包的自动代码生成至关重要,可以节省大量的时间。

创建一个l10n.yaml 文件到我们项目的根部;即lib/l10n.yaml 。这个文件指定了我们的翻译文件的位置,以及自动生成的Dart文件的名称。

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

默认情况下,Flutter本地化将其翻译存储在ARB(应用程序资源包)文件中。这些只是像JSON一样的键值配对的文件。arb 让我们先为我们的默认英语创建一个名为app_en.arb 的文件,并将其放入lib/l10n/app_en.arb

由于我们要支持多种语言,我们必须为我们的应用程序支持的每一种语言创建一个单独的文件。在这篇文章中,我们将只支持英语和印地语作为一个例子。

你可以根据你想要支持的语言数量添加尽可能多的arb 文件。就目前而言,让我们做两个独立的arb 文件,因为在这个例子中我们只支持两个地区性语言。

lib/l10n/app_en.arb

{
  "appTitle": "Demo App"
}

lib/l10n/app_hi.arb

{
  "appTitle": "डेमो ऐप"
}

接下来,让我们把本地化添加到MaterialApp

import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo'),
    );
  }
}

AppLocalizations.localizationsDelegates 是负责对我们的应用程序进行本地化。Flutter团队已经为Flutter部件、Material和Cupertino提供了本地化服务。例如,如果你使用 ,打开 ,你会发现对话已经被翻译成设备的当地语言,而不需要本地化文件。使用 ,Flutter只在检测到新的地区语言并将其添加到 's ,才会重建我们应用程序的用户界面。showDatePicker() DatePicker AppLocalizations.supportedLocales MaterialApp supportedLocales

为了支持iOS中的本地化,我们需要对Info.plist 进行以下修改。

<key>CFBundleLocalizations</key>
<array>
    <string>en</string>
    <string>hi</string>
</array>

本地化代码的生成

为了使用我们之前添加的ARB文件中的翻译,我们需要生成与ARB文件交替的Dart文件,可以在我们想使用本地化值的地方导入。要生成这些文件,只需在完成上述配置修改后启动应用程序。一旦代码生成完成,你应该看到以下文件。

  • .dart_tool/flutter_gen/gen_l10n/app_localizations.dart
  • .dart_tool/flutter_gen/gen_l10n/app_localizations_en.dart
  • .dart_tool/flutter_gen/gen_l10n/app_localizations_hi.dart

注意:如果本地化代码的生成没有自动发生,运行flutter gen-l10n ,就可以了。

如果你打开app_localizations.dart ,你会看到该文件包含一个抽象类AppLocalizations ,其中有localizationsDelegatessupportedLocales ,以及你在ARB文件中添加的定位值。其他文件app_localizations_en.dartapp_localizations_hi.dart 包含了扩展AppLocalizations 的类,这些类具有特定于本地的变量和方法。

让我们把我们的应用程序本地化

为了使用AppLocalizaions 类,你必须首先导入这个类。

import 'package:flutter_gen/gen_l10n/app_localizations.dart';

之后,你可以使用AppLocalizations.of(context).appTitle 来访问locale值。我们的MaterialApp 现在看起来像这样。

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: AppLocalizations.localizationsDelegates,
      supportedLocales: AppLocalizations.supportedLocales,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: AppLocalizations.of(context).appTitle),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Padding(
              padding:  EdgeInsets.only(top: 32),
              child: Text(
                'Flutter is Awesome',
                style:  TextStyle(fontSize: 24),
              ),
            ),
            Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text('You have pushed this button :',
                      style: Theme.of(context).textTheme.headline6),
                  Text(
                    '$_counter times',
                    style: Theme.of(context).textTheme.headline4,
                  )
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

恭喜你,我们的应用程序的标题现在已经成功地被本地化了。我们只需要更新其他的String 值。

为国际化插值单词和短语

插值就是把某样东西插入另一样东西中。当你想让一个词或文本在所有支持的地区的默认语言(如英语)中出现时,插值在本地化中很有用。例如,在我们的演示应用程序中,我们想显示一段文字 "Flutter is awesome",但问题是我们想让Flutter这个词在用户的设备上无论使用什么语言都是英文。

让我们把插值的句子添加到我们的locale ARB文件中。

lib/l10n/app_en.arb:

{
  "appTitle": "Demo App",
  "appDescription": "{flutter} is Awesome",
  "@appDescription": {
    "placeholders": {
      "flutter": {
        "type": "String",
        "example": "Flutter"
      }
    }
  },
}

lib/l10n/app_hi.arb:

{
  "appTitle": "डेमो ऐप",
  "appDescription": "{flutter} बहुत बढ़िया है",
}

现在,我们要消耗我们添加到ARB文件中的插值文本描述。由于我们要在多个地方访问AppLocalizations ,我们将制作一个实例变量_locale ,并在didChangeDependencies() 中初始化它。

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late AppLocalizations _local;

  @override
  void didChangeDependencies() {
    _local = AppLocalizations.of(context);
    super.didChangeDependencies();
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_local.appTitle),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 32),
              child: Text(
                _local.appDescription('Flutter'),
                style: const TextStyle(fontSize: 24),
              ),
            ),
            Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text("You have pushed this button :",
                      style: Theme.of(context).textTheme.headline6),
                  Text(
                    "$_counter",
                    style: Theme.of(context).textTheme.headline4,
                  )
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: "Increment",
        child: const Icon(Icons.add),
      ),
    );
  }
}

运行应用程序,你会看到无论你的设备设置成什么语言,"Flutter "这个词都会是英文的。

单数和复数支持

在本地化过程中,我们经常要处理单数和复数的问题。例如 - "Congrats🎉, you won a coupon," 或者,"Congrats🎉, you won two coupon." 需要注意的是,不同的语言对复数的处理方式不同,在处理复数时你需要谨慎,因为你需要对你所翻译的语言有一些了解。

让我们进行必要的修改,以便在我们的本地化文件中支持复数。

lib/l10n/app_en.arb:

{
  "appTitle": "Demo App",
  "appDescription": "{flutter} is Awesome",
  "@appDescription": {
    "placeholders": {
      "flutter": {
        "type": "String",
        "example": "Flutter"
      }
    }
  },
  "counterText": "You have pushed this button :",
  "counter": "{count,plural, =0{0} =1{1 time} other{{count} times}}",
  "@counter": {
    "placeholders": {
      "count": {
        "type": "int",
        "example": "count"
      }
    }
  },
  "counterButtonText": "Increment"
}

lib/l10n/app_hi.arb:

{
  "appTitle": "डेमो ऐप",
  "appDescription": "{flutter} बहुत बढ़िया है",
  "counterText": "आपने यह बटन दबा दिया है :",
  "counter": "{count,plural, =0{0} =1{1 बार} other{{count} बार}}",
  "counterButtonText": "जोड़ें"
}

现在,我们已经在我们的两个ARB文件的关键counter ,并且还添加了counterButtonText ,将被用作增量按钮的工具提示,我们可以写这个。

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;
  late AppLocalizations _local;

  @override
  void didChangeDependencies() {
    _local = AppLocalizations.of(context);
    super.didChangeDependencies();
  }

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(_local.appTitle),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(top: 32),
              child: Text(
                _local.appDescription('Flutter'),
                style: const TextStyle(fontSize: 24),
              ),
            ),
            Expanded(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(_local.counterText,
                      style: Theme.of(context).textTheme.headline6),
                  Text(
                    _local.counter(_counter),
                    style: Theme.of(context).textTheme.headline4,
                  )
                ],
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: _local.counterButtonText,
        child: const Icon(Icons.add),
      ),
    );
  }
}

注意我们是如何将_counter 值传递给AppLocalizations.counter() ,它最终将检查该值是单数还是复数,它将基于此返回一个String 值。

总结

在本教程中,你学会了如何将你的flutter应用程序本地化,使其更容易被用户接受。我希望你继续尝试新的东西

现在,我们已经把一切都准备好了,你所要做的就是运行应用程序并享受。

祝您好运!翩翩起舞快乐!

如果您有任何问题,请随时发表。我们欢迎任何反馈。