You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter中如何像Android一样根据Locale选择对应本地化文件(非ARB格式)

Flutter中如何像Android一样根据Locale选择对应本地化文件(非ARB格式)

我之前把Android项目迁移到Flutter时,也遇到过一模一样的需求——不想局限于官方推荐的ARB文件做本地化,希望延续Android那种按Locale对应文件夹存放资源的逻辑,其实Flutter完全可以实现,下面是具体的实现思路和步骤:

一、先规划你的资源文件结构

完全模仿Android的raw文件夹结构,在Flutter的assets目录下创建对应Locale的子文件夹:

assets/
├─ raw/                # 默认资源文件夹(对应Android的raw)
│  ├─ app_data.json
│  └─ tutorial_audio.mp3
├─ raw-de/             # 德语资源文件夹(对应Android的raw-de)
│  ├─ app_data.json
│  └─ tutorial_audio.mp3
├─ raw-fr/             # 法语资源文件夹(对应Android的raw-fr)
│  ├─ app_data.json
│  └─ tutorial_audio.mp3
└─ raw-zh-CN/          # 中文简体资源文件夹(如果需要区分地区)
   ├─ app_data.json
   └─ tutorial_audio.mp3

二、在pubspec.yaml中声明所有资源

要确保Flutter能识别这些资源,在pubspec.yamlflutter节点下添加资源声明,用通配符可以更灵活地覆盖所有Locale文件夹:

flutter:
  assets:
    - assets/raw/          # 声明默认资源
    - assets/raw-*/        # 通配符匹配所有以raw-开头的Locale文件夹

三、实现根据Locale加载资源的工具类

封装一个工具类来处理Locale匹配、资源加载和 fallback 逻辑,这样代码更复用,也更清晰:

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

class LocalizedAssetLoader {
  final BuildContext context;

  LocalizedAssetLoader(this.context);

  // 加载文本类资源(如JSON、TXT)
  Future<String> loadStringAsset(String filename) async {
    final currentLocale = Localizations.localeOf(context);
    // 定义资源路径的优先级:完整Locale(如zh-CN)> 语言码(如zh)> 默认资源
    final possiblePaths = [
      'assets/raw-${currentLocale.toString()}/$filename',
      'assets/raw-${currentLocale.languageCode}/$filename',
      'assets/raw/$filename',
    ];

    // 依次尝试加载路径,找到第一个可用的资源
    for (final path in possiblePaths) {
      try {
        return await rootBundle.loadString(path);
      } catch (_) {
        // 当前路径加载失败,继续尝试下一个
        continue;
      }
    }
    // 所有路径都失败时抛出异常
    throw Exception('无法加载本地化资源:$filename');
  }

  // 加载二进制类资源(如音频、图片)
  Future<ByteData> loadBinaryAsset(String filename) async {
    final currentLocale = Localizations.localeOf(context);
    final possiblePaths = [
      'assets/raw-${currentLocale.toString()}/$filename',
      'assets/raw-${currentLocale.languageCode}/$filename',
      'assets/raw/$filename',
    ];

    for (final path in possiblePaths) {
      try {
        return await rootBundle.load(path);
      } catch (_) {
        continue;
      }
    }
    throw Exception('无法加载二进制本地化资源:$filename');
  }
}

四、在业务代码中使用工具类

在有BuildContext的地方,直接实例化工具类并加载资源即可:

// 示例:加载JSON资源
Future<void> loadAppData() async {
  final loader = LocalizedAssetLoader(context);
  try {
    final jsonContent = await loader.loadStringAsset('app_data.json');
    // 处理JSON内容
    print('加载到本地化数据:$jsonContent');
  } catch (e) {
    print('资源加载失败:$e');
  }
}

// 示例:加载音频资源
Future<void> loadTutorialAudio() async {
  final loader = LocalizedAssetLoader(context);
  try {
    final audioData = await loader.loadBinaryAsset('tutorial_audio.mp3');
    // 播放音频或做其他处理
  } catch (e) {
    print('音频加载失败:$e');
  }
}

五、拓展:支持手动切换Locale

如果你的App支持用户手动切换语言(而不是仅跟随系统Locale),只需要修改工具类的Locale来源即可——比如用状态管理(Provider/Riverpod)保存当前选中的Locale,替换Localizations.localeOf(context)

// 假设用Riverpod管理当前Locale
final currentLocaleProvider = StateProvider<Locale>((ref) => const Locale('en'));

// 修改工具类的构造方法,接收Locale参数
class LocalizedAssetLoader {
  final Locale currentLocale;

  LocalizedAssetLoader(this.currentLocale);

  // 后续的load方法中,直接使用this.currentLocale即可
  Future<String> loadStringAsset(String filename) async {
    final possiblePaths = [
      'assets/raw-${currentLocale.toString()}/$filename',
      'assets/raw-${currentLocale.languageCode}/$filename',
      'assets/raw/$filename',
    ];
    // ... 其余逻辑不变
  }
}

// 使用时从状态管理中获取Locale
final currentLocale = ref.watch(currentLocaleProvider);
final loader = LocalizedAssetLoader(currentLocale);

这种方式完全复刻了Android的资源组织逻辑,不需要依赖ARB文件,非常适合从Android迁移过来的项目,能最大程度保持资源结构的一致性。

内容来源于stack exchange

火山引擎 最新活动