Flutter 国际化

flutter 项目中,使用 AppLocalizations.of(context)!.translate(Strings.appName) 实现国际化,但是这里的 context 并不总能获取,另外及时能获取 context,这种写法也很不方便,需要可以在任意地方,方便的获取国际化内容

方案

一个优雅的解决方案是创建一个可以全局访问的单例服务,它持有顶层的 BuildContext,从而让我们可以在项目的任何地方调用一个简洁的函数来获取国际化文本。

请注意: 直接将一个普通的 BuildContext 存为全局变量是错误且危险的,因为 BuildContext 与特定的 Widget 相关联,当 Widget 从树中移除时,该 context 就会失效,可能导致程序崩溃或内存泄漏。

下面我将为您提供一个安全且推荐的实现方案。

推荐方案:使用 GlobalKey 和服务单例模式 (Service Singleton)
这个方案的核心是利用 MaterialApp 的 navigatorKey。这个 GlobalKey 持有的 context 是一个在应用生命周期内几乎一直有效的顶层 context,用它来获取 AppLocalizations 是非常安全的。

第 1 步:创建本地化服务类和便捷函数
我们将创建一个单例服务来管理 GlobalKey,并提供一个全局的顶层函数 tr() 作为便捷访问的入口。
创建一个localization_service.dart

// lib/common/localization/localization_service.dart

import 'package:flutter/material.dart';
import 'strings.dart';
import 'localization.dart';

/// 一个全局的便捷函数,用于获取国际化字符串。
///
/// 用法: tr(Strings.appName)
String tr(String key) => LocalizationService.instance.translate(key);

/// 一个单例服务,用于提供全局的国际化文本访问。
/// 它通过持有 MaterialApp 的 navigatorKey 来安全地获取顶层 context。
class LocalizationService {
  // 私有构造函数
  LocalizationService._();

  // 单例实例
  static final LocalizationService _instance = LocalizationService._();

  // 提供一个公共的访问点
  static LocalizationService get instance => _instance;

  // GlobalKey,将由 main.dart 传入
  late GlobalKey<NavigatorState> navigatorKey;

  /// 核心翻译方法
  String translate(String key) {
    // 尝试通过 navigatorKey 获取当前的 BuildContext
    final context = navigatorKey.currentContext;

    // 如果 context 存在,就用它来翻译
    if (context != null) {
      // 使用 of(context)? 安全调用,即使在极端情况下获取不到 AppLocalizations 也能避免崩溃
      return AppLocalizations.of(context)?.translate(key) ?? key;
    } else {
      // 如果 context 不存在(例如在应用启动的第一帧),则返回 key 本身作为降级方案。
      // 或者,你可以根据默认语言返回一个值。
      debugPrint('Warning: LocalizationService context is null. Using key as fallback.');
      return key;
    }
  }
}

第 2 步:在 main.dart 中集成服务
应用入口 (main.dart) 中初始化这个服务,并将 GlobalKey 传递给它

// lib/main.dart

import 'package:flutter/material.dart';
import 'package:task_manager/common/localization/localization.dart'; // 导入你的 AppLocalizations
import 'package:task_manager/common/localization/localization_service.dart'; // 导入我们刚创建的服务
// ... 其他的 import

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

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

  @override
  Widget build(BuildContext context) {
    // 1. 创建 GlobalKey
    final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

    // 2. 将 GlobalKey 赋值给我们的单例服务
    LocalizationService.instance.navigatorKey = navigatorKey;

    return MaterialApp(
      // 3. 将同一个 key 设置给 MaterialApp 的 navigatorKey
      navigatorKey: navigatorKey,

      // ... 其他 MaterialApp 的配置 ...
      title: 'Seven Calendar', // 你可以保留一个默认的标题
      supportedLocales: const [
        Locale('en', ''),
        Locale('zh', ''),
      ],
      localizationsDelegates: const [
        AppLocalizations.delegate,
        // ... 其他 delegates
      ],
      // ... home, theme, etc.
    );
  }
}

第 3 步:在项目任意位置方便地使用
现在,您已经完成了所有设置。在项目中的任何文件里,无论是 Widget、ViewModel 还是工具类,只要导入我们的服务文件,就可以直接使用 tr() 函数。

// 无需传入 context
import 'package:task_manager/common/localization/localization_service.dart'; // 导入新服务
import 'package:task_manager/common/localization/strings.dart'; // 导入 Strings

// 可以在 Widget 中使用
class SomeWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 直接调用 tr(),非常简洁!
    return Text(tr(Strings.appName));
  }
}

// 也可以在任何其他类中使用
class MyViewModel {
  String getPageTitle() {
    // 比如在 ViewModel 中获取标题,无需 context
    return tr(Strings.editEvent);
  }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容