Flutter国际化插件i18next的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter国际化插件i18next的使用

简介

i18next 是一个为Dart适配的标准i18next库,支持Flutter的本地化技术。此包仍在开发中,可能会频繁发生破坏性变化。

特性

  • 支持变量、命名空间、上下文
  • 支持简单和多重复数形式
  • 支持复数和上下文回退
  • 支持Locale和命名空间回退
  • 获取字符串或对象树
  • 支持嵌套
  • 支持Flutter的LocalizationsDelegate
  • pubspec.yaml获取资产包本地化数据源

不支持特性

  • Sprintf支持
  • 资源缓存
  • 从服务器获取资源文件
  • 自定义后处理

使用方法

添加依赖

pubspec.yaml文件中添加以下依赖:

dependencies:
  i18next: ^0.5.0

初始化I18NextLocalizationDelegate

在应用中创建并注册I18NextLocalizationDelegate

I18NextLocalizationDelegate(
  locales: widget.locales,
  dataSource: AssetBundleLocalizationDataSource(bundlePath: 'localizations'),
  options: I18NextOptions(...),
),

访问与使用

通过Localizations.of访问i18next实例:

Widget build(BuildContext context) {
  I18Next.of(context).t('key');
  ...
}

或者直接实例化I18Next

I18Next(
  locale,
  resourceStore: ...,
  options: I18NextOptions(...)
);

示例代码

以下是一个完整的示例应用程序,展示了如何使用i18next进行国际化:

main.dart

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:i18next/i18next.dart';
import 'package:intl/intl.dart';

import 'localizations.dart';

void main() => runApp(const MyApp());

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

  final List<Locale> locales = const [
    Locale('en', 'US'),
    Locale('pt', 'BR'),
  ];

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late Locale locale;

  @override
  void initState() {
    super.initState();
    locale = widget.locales.first;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'I18next Demo',
      theme: ThemeData(
        dividerTheme: const DividerThemeData(
          color: Colors.black45,
          space: 32.0,
        ),
      ),
      localizationsDelegates: [
        ...GlobalMaterialLocalizations.delegates,
        I18NextLocalizationDelegate(
          locales: widget.locales,
          dataSource: AssetBundleLocalizationDataSource(bundlePath: 'localizations'),
          options: I18NextOptions(formats: formatters()),
        ),
      ],
      home: MyHomePage(
        supportedLocales: widget.locales,
        onUpdateLocale: updateLocale,
      ),
      locale: locale,
      supportedLocales: widget.locales,
    );
  }

  void updateLocale(Locale newLocale) {
    setState(() {
      locale = newLocale;
    });
  }

  static Map<String, ValueFormatter> formatters() => {
        'uppercase': (value, format, locale, options) => value?.toString().toUpperCase(),
        'lowercase': (value, format, locale, options) => value?.toString().toLowerCase(),
        'datetime': (value, format, locale, options) {
          if (value is! DateTime) return value;
          var dateFormat = format.options['format'];
          dateFormat = dateFormat is String ? dateFormat : 'dd/MM/yyyy';
          return DateFormat(dateFormat, locale.toString()).format(value);
        },
      };
}

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

  final List<Locale> supportedLocales;
  final ValueChanged<Locale> onUpdateLocale;

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

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

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final homepageL10n = HomePageL10n.of(context);
    final counterL10n = CounterL10n.of(context);

    return Scaffold(
      appBar: AppBar(title: Text(homepageL10n.title)),
      body: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          children: <Widget>[
            CupertinoSegmentedControl<Locale>(
              children: {
                for (final e in widget.supportedLocales) e: Text(e.toString())
              },
              groupValue: Localizations.localeOf(context),
              onValueChanged: widget.onUpdateLocale,
            ),
            const Divider(),
            Text(
              homepageL10n.hello(name: 'Name', world: 'Flutter'),
              style: theme.textTheme.titleLarge,
            ),
            Text(
              homepageL10n.today(DateTime.now()),
              style: theme.textTheme.titleSmall,
            ),
            CupertinoSegmentedControl<String>(
              padding: const EdgeInsets.symmetric(vertical: 8),
              children: const {
                'male': Text('MALE'),
                'female': Text('FEMALE'),
                '': Text('OTHER'),
              },
              groupValue: _gender,
              onValueChanged: updateGender,
            ),
            Text(homepageL10n.gendered(_gender)),
            const Divider(),
            Text(
              counterL10n.clicked(_counter),
              style: theme.textTheme.headlineMedium,
            ),
            TextButton(
              onPressed: resetCounter,
              child: Text(counterL10n.resetCounter),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementCounter,
        tooltip: counterL10n.clickMe,
        child: const Icon(Icons.add),
      ),
    );
  }

  void incrementCounter() => setState(() => _counter++);

  void resetCounter() => setState(() => _counter = 0);

  void updateGender(String gender) => setState(() => _gender = gender);
}

localizations.dart

import 'package:flutter/material.dart';
import 'package:i18next/i18next.dart';

class HomePageL10n {
  static HomePageL10n of(BuildContext context) {
    return HomePageL10n(I18Next.of(context));
  }

  final I18Next i18next;

  HomePageL10n(this.i18next);

  String get title => i18next.t('home:title');

  String hello({required String name, required String world}) =>
      i18next.t('home:hello', arguments: {'name': name, 'world': world});

  String today(DateTime date) =>
      i18next.t('home:today', arguments: {'now': date});

  String gendered(String gender) =>
      i18next.t('home:gendered', context: gender);
}

class CounterL10n {
  static CounterL10n of(BuildContext context) {
    return CounterL10n(I18Next.of(context));
  }

  final I18Next i18next;

  CounterL10n(this.i18next);

  String clicked(int count) =>
      i18next.t('counter:clicked', count: count);

  String get resetCounter => i18next.t('counter:resetCounter');

  String get clickMe => i18next.t('counter:clickMe');
}

localizations/en_US.json

{
  "home": {
    "title": "Home Page",
    "hello": "Hello {{name}} from {{world}}!",
    "today": "Today is {{now, datetime(format:dd/MM/yyyy)}}",
    "gendered": "{{context, male:A boyfriend, female:A girlfriend, A friend}}"
  },
  "counter": {
    "clicked": "{{count}} clicks",
    "resetCounter": "Reset Counter",
    "clickMe": "Click Me"
  }
}

localizations/pt_BR.json

{
  "home": {
    "title": "Página Inicial",
    "hello": "Olá {{name}} do {{world}}!",
    "today": "Hoje é {{now, datetime(format:dd/MM/yyyy)}}",
    "gendered": "{{context, male:Um namorado, female:Uma namorada, Um amigo}}"
  },
  "counter": {
    "clicked": "{{count}} cliques",
    "resetCounter": "Redefinir Contador",
    "clickMe": "Clique Aqui"
  }
}

以上代码展示了如何在Flutter项目中使用i18next进行国际化。你可以根据需要扩展和修改这些代码以适应你的具体需求。


更多关于Flutter国际化插件i18next的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter国际化插件i18next的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,虽然i18next这个库本身并不是专门为Flutter设计的(i18next更常见于JavaScript/React生态),但Flutter有一个非常强大且广泛使用的国际化插件——flutter_localizations。不过,如果你确实想使用类似i18next功能的插件,你可以考虑一些第三方库,比如flutter_i18n,它提供了类似的功能。但请注意,下面我将重点展示如何使用Flutter内置的国际化支持。

以下是一个使用flutter_localizationsIntl包进行国际化的简单示例:

  1. 添加依赖: 在你的pubspec.yaml文件中添加intl依赖:

    dependencies:
      flutter:
        sdk: flutter
      intl: ^0.17.0 # 请检查最新版本
    
  2. 创建翻译文件: 为不同的语言创建ARB(Application Resource Bundle)文件。例如,messages_en.arbmessages_zh.arb

    messages_en.arb

    {
      "welcome_message": "Welcome to Flutter!"
    }
    

    messages_zh.arb

    {
      "welcome_message": "欢迎来到Flutter!"
    }
    
  3. 生成Dart类: 使用flutter pub run intl_translation:extract_to_arb命令提取字符串,然后使用flutter pub run intl_translation:generate_from_arb生成Dart类。这将生成一个包含所有翻译的文件,比如l10n/messages_all.dart

  4. 设置MaterialApp: 在你的MaterialApp中配置本地化支持。

    import 'package:flutter/material.dart';
    import 'package:flutter_gen/gen_l10n/app_localizations.dart';
    
    void main() {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          supportedLocales: [
            Locale('en', ''), // 英语
            Locale('zh', ''), // 中文
          ],
          localizationsDelegates: [
            AppLocalizations.delegate,
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
          ],
          locale: Locale('en'), // 默认语言
          home: MyHomePage(),
        );
      }
    }
    
    class MyHomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final AppLocalizations localizations = AppLocalizations.of(context)!;
        return Scaffold(
          appBar: AppBar(
            title: Text(localizations.welcomeMessage),
          ),
          body: Center(
            child: Text(localizations.welcomeMessage),
          ),
        );
      }
    }
    
  5. 使用翻译: 在你的UI组件中,通过AppLocalizations.of(context)!.yourKey访问翻译文本。

这个示例展示了如何使用Flutter内置的国际化支持来创建多语言应用。如果你确实想使用类似i18next的第三方库,你可能需要搜索Flutter社区中是否有类似的实现,并遵循该库的文档进行设置和使用。不过,对于大多数Flutter应用来说,内置的flutter_localizationsIntl包已经足够强大和灵活。

回到顶部