Flutter本地化翻译管理插件applanga_flutter的使用

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

Flutter本地化翻译管理插件applanga_flutter的使用

Applanga SDK for Flutter 是一个强大的工具,它可以帮助开发者轻松实现应用的国际化和本地化。通过与 Applanga 平台的集成,开发者可以更方便地管理应用中的文本资源,并且支持通过空中更新(Over-the-Air Updates, OTA)来即时更新应用内的文字内容。

目录


基本用法

Applanga CLI 安装

对于 Applanga Command Line Interface (CLI) 的安装,请参考官方文档:Applanga CLI 文档

在 Flutter 中不需要通过 CLI 初始化项目,applanga_flutter 会自动处理这些步骤。你可以随时修改 .applanga.json 文件来自定义 CLI 配置。

本地化准备

applanga_flutterflutter_localizations 包兼容良好。请按照官方国际化指南添加 flutter_localizationsintl 到你的依赖项中,在 pubspec.yaml 中启用生成器,并创建 l10n.yaml 文件以及第一个基础语言的 .arb 文件,例如:

{
  "@@locale": "en",
}

将本地化代理添加到你的 MaterialApp

const MaterialApp(
  localizationsDelegates: AppLocalizations.localizationsDelegates,
  supportedLocales: AppLocalizations.supportedLocales,
  home: MyApp(),
)

pubspec.yaml 底部添加 API Token:

applanga_flutter:
  access_token: xxxxxxxxxxxxxxxx

你可以从项目的仪表板上的设置页面获取 API Token。这个令牌用于拉取和推送新的翻译。

拉取 & 推送新翻译

要执行推送或拉取命令,你需要先设置 Applanga CLI

  • 使用 dart run applanga_flutter:pull 下载所有新字符串并将它们保存到相应的 arb 文件中。
  • 使用 dart run applanga_flutter:push 将所有新增加的字符串上传到 Applanga 仪表板。

默认情况下,基础语言 .arb 文件是唯一的真相来源。因此,pull 操作会为除基础语言外的所有语言获取并保存翻译,而 push 操作只会更新尚未上传到仪表板的基础语言字符串及其元数据(对 ICU 字符串很重要)。你可以在 .applanga.json 中更改此行为。

推荐使用 dart run applanga_flutter:push --force 来确保基础语言及其元数据的完整更新。


可选的空中更新

空中更新适用于 Android 和 iOS。

iOS 注意事项

确保在 info.plist 中添加了支持的语言。更多信息请参见Flutter 国际化教程

添加 Applanga 的设置文件

  1. 在 Applanga 项目概览中点击 [Prepare Release] 按钮,然后点击 [Get Settings File] 获取 Applanga 设置文件
  2. Applanga 设置文件 添加到 Android 模块的资源 res/raw 目录。
  3. 同样将 Applanga 设置文件 添加到 iOS 模块的主要目标。打开 Xcode 中的 iOS 模块,并将设置文件拖入项目中,确保选中你要应用的目标。

生成并替换 AppLocalizationsClass

生成 ApplangaLocalizationsClass

dart run applanga_flutter:generate

MaterialApp 中添加类并替换旧的代理和语言环境:

import 'package:applanga_flutter/applanga_flutter.dart';

const MaterialApp(
  localizationsDelegates: ApplangaLocalizations.localizationsDelegates,
  supportedLocales: ApplangaLocalizations.supportedLocales,
  localeListResolutionCallback: ApplangaLocalizations.localeListResolutionCallback,
  home: MyApp(),
),

你可以像往常一样获取翻译:AppLocalizations.of(context)!.helloWorld

ApplangaWidget

建议将 ApplangaWidget 放置为 widget 树的顶级 widget,以便在异步更新空中传输时通知所有子 widget。这对于更好的截图体验也很有帮助。

void main() async {
  runApp(
    const ApplangaWidget(child: MyApp()),
  );
}

默认语言

默认情况下,Applanga 的 OTA 字符串是在运行时懒加载的。如果用户更改应用程序语言,Applanga 会为所选语言获取所有新的翻译。这可能会导致用户界面出现意外的行为,特别是在短时间内有大量新的翻译变更时。如果你有一个自定义的语言切换器,可以在应用程序启动前预加载所有常用语言以避免延迟。

pubspec.yaml 中添加默认语言,例如:

applanga_flutter:
  ...
  update_languages: [en, en_US, es, es_CL]

默认组

如果你使用了分组,可以定义所有默认组,这些组将在应用程序启动时下载。默认情况下只下载 main 组。

我们遵循与 update_languages 相同的模式:

applanga_flutter:
  ...
  update_groups: [main, chapter1, chapter2]

手动 OTA 更新

ApplangaFlutter 在应用程序启动时为默认语言和默认组获取所有新的翻译。你也可以编程启动一个新的更新,并定义你的语言或组。

// 自定义语言和组
ApplangaFlutter.update({languages: ['en_US'], groups: ['main', 'chapter2']});

// 使用默认语言和默认组进行默认更新调用
ApplangaFlutter.update();

草案模式

注意:草案模式仅适用于 Android 和 iOS。

Applanga 的草案模式可以通过多点触摸手势激活,在 iOS 构建中开箱即用,但对于 Android,你需要转发输入事件给 SDK,这可以在自定义 MainActivity 中完成如下所示:

class MainActivity : FlutterActivity() {
    override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        GeneratedPluginRegistrant.registerWith(flutterEngine);
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
        com.applanga.applanga_flutter.ApplangaFlutterPlugin.dispatchTouchEvent(ev, this)
        return super.dispatchTouchEvent(ev)
    }
}

在 AndroidManifest 中的 manifest 标签内添加以下权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

要通过代码触发草案模式对话框,可以调用 ApplangaFlutter.showDraftModeDialog();

截图菜单

注意:截图菜单仅适用于 Android 和 iOS - 必须先启用草案模式。

一旦进入草案模式,你可以通过两指下滑或通过代码显示或隐藏截图菜单:ApplangaFlutter.setScreenShotMenuVisible(bool);

为了获得良好的截图体验,确保用 ApplangaScreenshotScope 或添加 ApplangaScreenshotScopeMixin 包装所有屏幕或小部件,如下面的例子所示。

使用 ApplangaScreenshotScope

class MyDrawer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ApplangaScreenshotScope(child: Drawer(
      // ...
    ));
  }
}

或者将 ApplangaScreenshotScopeMixin 添加到屏幕的状态:

class _HomeScreenState extends State<HomeScreen> with ApplangaScreenshotScopeMixin {
  // ...
}

为了获得更好的截图体验,建议使用 ApplangaWidgetApplangaScreenshotScope(或 ApplangaScreenshotScopeMixin)。

自动化截图上传

在运行Flutter 集成测试时,你可以简单地捕获截图:

await ApplangaFlutter.I.captureScreenshotWithTag("main");

请阅读Screenshot Menu以改善截图中的字符串位置收集。示例包含了一个展示自动截图用法的集成测试。

显示 ID 模式

启用 Applanga 的显示 ID 模式将返回翻译键而不是实际的翻译。这对于调试字符串位置很有帮助,也用于提高屏幕上的字符串检测准确性。

await ApplangaFlutter.I.setShowIdModeEnabled(true);
// 或者
await ApplangaFlutter.I.setShowIdModeEnabled(false);

自动设置文件更新

这在执行拉取请求后会自动启用。要手动执行此操作,请使用以下命令:

dart run applanga_flutter:update_settingsfiles

要禁用自动行为,请在 pubspec.yaml 中添加:

applanga_flutter:
  access_token: xxxx
  update_settingsfiles_on_pull: false 

对于空中更新,Applanga 设置文件是必需的。它包含仪表板上所有语言的最新翻译。最好在应用发布前更新此文件。Applanga 只会获取新的翻译。如果设置文件是最新的,第一次(自动)ApplangaFlutter.update 将导致非常轻量级的 GET 请求。如果设置文件较旧,则获取请求将包含更多信息。

分支

如果你的项目是一个分支项目,请使用至少版本 3.0.47 的 applanga_flutter 并更新你的设置文件。你可以在 pubspec.yaml 中定义默认分支:

applanga_flutter:
  access_token: xxxx
  branch_id: xxxx

你可以在 Applanga 仪表板的项目设置中找到分支 ID。在更改 pubspec.yaml 中的分支 ID 后,你需要运行 dart run applanga_flutter:generate 以重新生成你的 Applanga 配置。如果你更改了默认分支,你也需要手动下载并更新你的设置文件。如果设置文件的默认分支与 pubspec.yaml 中指定的默认分支不同,applanga_flutter 将抛出异常。

默认分支用于应用程序启动和更新调用。要确认分支工作正常,请查找日志行:Branching is enabled.

有关分支的更多信息,请参阅这里

启用自定义语言回退

你可以在 pubspec.yaml 中配置 Flutter 的自定义语言回退。当 SDK 需要用指定语言翻译一个键时,它会使用提供的顺序。这覆盖了任何其他系统或默认回退,但仅限于那些语言。其他语言根据 custom_language_fallback 值(或未设置时的默认值)指定的回退工作。回退仅覆盖顶级语言,因此不可能“嵌套”自定义回退。

applanga_flutter:
  custom_language_fallback:
    es-CL: [fr, es-US, de, es]
    de-AT: [es, de-AT, de]

示例代码

import 'package:applanga_flutter/applanga_flutter.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    const MyApp(),
  );
}

class MyApp extends StatefulWidget {
  final Locale? startupLocale;

  const MyApp({Key? key, this.startupLocale}) : super(key: key);

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

class _MyAppState extends State<MyApp> {
  late Locale? _currentLocale;

  @override
  void initState() {
    super.initState();
    _currentLocale = widget.startupLocale;
  }

  @override
  Widget build(BuildContext context) {
    return ApplangaWidget(
      child: Column(
        children: [
          Expanded(
            child: MaterialApp(
              title: 'Flutter Demo',
              locale: _currentLocale,
              localizationsDelegates: ApplangaLocalizations.localizationsDelegates,
              supportedLocales: ApplangaLocalizations.supportedLocales,
              localeListResolutionCallback: ApplangaLocalizations.localeListResolutionCallback,
              theme: ThemeData(
                primarySwatch: Colors.blue,
              ),
              home: const MyHomePage(),
            ),
          ),
          Directionality(
            textDirection: TextDirection.ltr,
            child: Wrap(
              spacing: 8,
              children: ApplangaLocalizations.supportedLocales
                  .map(
                    (locale) => ElevatedButton(
                      child: Text(
                        locale.toString(),
                        style: TextStyle(
                          fontWeight: (_currentLocale == locale)
                              ? FontWeight.bold
                              : FontWeight.normal,
                          textScaleFactor: (_currentLocale == locale) ? 1.3 : 1,
                        ),
                      ),
                      onPressed: () {
                        setState(() {
                          _currentLocale = locale;
                        });
                      },
                    ),
                  )
                  .toList(),
            ),
          ),
        ],
      ),
    );
  }
}

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

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

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

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          AppLocalizations.of(context).homePageTitle(DateTime.now()),
          key: const ValueKey('homePageTitle'),
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text(
              AppLocalizations.of(context)
                  .youHavePushedTheButtonXTimes(_counter, 'thumb'),
              style: Theme.of(context).textTheme.headlineMedium,
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 8),
            ElevatedButton.icon(
              onPressed: () {
                Navigator.of(context).push(MaterialPageRoute(
                  builder: (context) => const SecondPage(),
                ));
              },
              label: Text(AppLocalizations.of(context).goToSecondPage),
              icon: const Icon(Icons.arrow_forward),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: AppLocalizations.of(context).increment,
        child: const Icon(Icons.add),
      ),
    );
  }
}

更多关于Flutter本地化翻译管理插件applanga_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter本地化翻译管理插件applanga_flutter的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何使用 applanga_flutter 插件进行 Flutter 应用本地化翻译的示例代码案例。applanga_flutter 是一个方便管理 Flutter 应用本地化的插件,支持从 Applanga 平台动态获取翻译内容。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 applanga_flutter 依赖:

dependencies:
  flutter:
    sdk: flutter
  applanga_flutter: ^最新版本号  # 请替换为当前最新版本号

然后运行 flutter pub get 来获取依赖。

2. 配置 Applanga

在你的 Flutter 项目中,你需要配置 Applanga 的 API 密钥和项目 ID。这通常在 main.dart 或初始化文件中完成。

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

void main() {
  // 初始化 Applanga
  Applanga.init(
    apiKey: '你的API密钥',
    projectId: '你的项目ID',
    languageCode: 'en', // 默认语言
    fallbackLanguageCode: 'en', // 备选语言
    // 如果需要,可以配置更多参数,比如自定义的 API 端点等
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

3. 使用翻译功能

在需要使用翻译的地方,你可以使用 Applanga.translate 方法。下面是一个简单的示例,展示如何在按钮文本中使用翻译。

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String> _translateButtonLabel() async {
    // 从Applanga获取翻译
    return await Applanga.translate('button.label');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Applanga Demo'),
      ),
      body: Center(
        child: FutureBuilder<String>(
          future: _translateButtonLabel(),
          builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              if (snapshot.hasError) {
                return Text('Error: ${snapshot.error}');
              } else {
                return ElevatedButton(
                  onPressed: () {},
                  child: Text(snapshot.data ?? 'Loading...'),
                );
              }
            } else {
              return CircularProgressIndicator();
            }
          },
        ),
      ),
    );
  }
}

4. 更新语言

你也可以让用户在应用内切换语言。以下是一个简单的示例,展示如何通过按钮切换语言。

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

class LanguageSwitcher extends StatefulWidget {
  @override
  _LanguageSwitcherState createState() => _LanguageSwitcherState();
}

class _LanguageSwitcherState extends State<LanguageSwitcher> {
  void _changeLanguage(String languageCode) {
    Applanga.setLanguageCode(languageCode);
    // 通常这里可以刷新UI或重建某些组件以显示新的语言内容
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: () => _changeLanguage('zh'),
          child: Text('中文'),
        ),
        SizedBox(width: 20),
        ElevatedButton(
          onPressed: () => _changeLanguage('en'),
          child: Text('English'),
        ),
      ],
    );
  }
}

总结

上述代码展示了如何在 Flutter 应用中使用 applanga_flutter 插件进行本地化翻译管理。你可以根据需要进一步定制和扩展这些示例代码。记得将 你的API密钥你的项目ID 替换为你在 Applanga 平台获取的实际值。

回到顶部