Flutter for OpenHarmony 国际化集成指南:实现中英文动态切换

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

作者:maaath

一、背景介绍

在移动应用开发中,国际化(Internationalization,简称 i18n)是一项必不可少的功能。对于使用 Flutter for OpenHarmony 进行跨平台开发的开发者而言,如何优雅地实现多语言切换是一个值得深入探讨的话题。本文将以实际项目为例,详细介绍如何在开源鸿蒙跨平台工程中集成国际化能力,实现中英文动态切换,并在模拟器上完成验证。

与纯 Flutter 应用不同,Flutter for OpenHarmony 项目采用混合架构:原生鸿蒙 ArkTS 层负责应用入口和平台能力,Flutter 层负责 UI 渲染。因此,国际化方案需要同时考虑鸿蒙原生层和 Flutter 层的协同工作。本文将重点讲解鸿蒙原生层的国际化实现,为 Flutter 层提供语言切换的基础能力。

二、国际化架构设计

2.1 整体思路

为了实现灵活的语言切换功能,我们设计了以下核心模块:

模块 职责
I18nManager 语言管理器,负责语言状态管理、持久化存储、变更通知
StringManager 字符串资源管理器,提供多语言字符串的统一访问接口
资源文件 按语言目录组织的 JSON 字符串资源

2.2 目录结构

entry/src/main/
├── ets/
│   ├── entryability/
│   │   └── EntryAbility.ets      # 应用入口,初始化国际化
│   ├── pages/
│   │   └── Index.ets             # 主页面,展示国际化效果
│   └── utils/
│       ├── I18nManager.ets       # 国际化管理器
│       └── StringManager.ets     # 字符串管理器
└── resources/
    ├── base/element/string.json  # 默认字符串资源
    ├── en_US/element/string.json # 英文资源
    └── zh_CN/element/string.json # 中文资源

三、核心代码实现

3.1 国际化管理器(I18nManager)

I18nManager 是整个国际化方案的核心,采用单例模式设计,提供语言切换、持久化存储和监听器机制。

import preferences from '@ohos.data.preferences';
import { common } from '@kit.AbilityKit';

const PREFERENCES_NAME = 'i18n_preferences';
const LANGUAGE_KEY = 'app_language';

export class I18nManager {
  private static instance: I18nManager;
  private preferences: preferences.Preferences | null = null;
  private currentLanguage: string = 'zh-CN';
  private listeners: Array<(language: string) => void> = [];

  private constructor() {}

  static getInstance(): I18nManager {
    if (!I18nManager.instance) {
      I18nManager.instance = new I18nManager();
    }
    return I18nManager.instance;
  }

  async init(context: common.UIAbilityContext): Promise<void> {
    try {
      this.preferences = await preferences.getPreferences(context, PREFERENCES_NAME);
      const language: string = await this.preferences.get(LANGUAGE_KEY, 'zh-CN') as string;
      this.currentLanguage = language;
    } catch (error) {
      console.error('I18nManager init error:', JSON.stringify(error));
      this.currentLanguage = 'zh-CN';
    }
  }

  getLanguage(): string {
    return this.currentLanguage;
  }

  isEnglish(): boolean {
    return this.currentLanguage === 'en-US';
  }

  async setLanguage(language: string): Promise<void> {
    if (this.currentLanguage === language) {
      return;
    }

    this.currentLanguage = language;

    if (this.preferences) {
      try {
        await this.preferences.put(LANGUAGE_KEY, language);
        await this.preferences.flush();
      } catch (error) {
        console.error('I18nManager setLanguage error:', JSON.stringify(error));
      }
    }

    this.notifyListeners();
  }

  async switchLanguage(): Promise<void> {
    const newLanguage = this.currentLanguage === 'zh-CN' ? 'en-US' : 'zh-CN';
    await this.setLanguage(newLanguage);
  }

  addLanguageChangeListener(callback: (language: string) => void): void {
    this.listeners.push(callback);
  }

  removeLanguageChangeListener(callback: (language: string) => void): void {
    const index = this.listeners.indexOf(callback);
    if (index > -1) {
      this.listeners.splice(index, 1);
    }
  }

  private notifyListeners(): void {
    this.listeners.forEach(callback => {
      callback(this.currentLanguage);
    });
  }
}

export const i18nManager = I18nManager.getInstance();

代码要点解析:

  1. 单例模式:确保全局只有一个语言管理实例,避免状态不一致
  2. 持久化存储:使用 @ohos.data.preferences 保存用户选择的语言,应用重启后自动恢复
  3. 监听器机制:采用观察者模式,当语言切换时通知所有注册的监听器,实现 UI 自动刷新
  4. 错误处理:所有异步操作都包含 try-catch,确保应用稳定性

3.2 字符串管理器(StringManager)

StringManager 封装了多语言字符串资源,提供统一的访问接口。

import { i18nManager } from './I18nManager';

export class StringManager {
  private static instance: StringManager;
  private stringResources: Record<string, Record<string, string>> = {
    'zh-CN': {
      'app_name': '开源鸿蒙',
      'welcome': '欢迎使用开源鸿蒙',
      'settings': '设置',
      'language': '语言',
      'switch_language': '切换语言',
      'current_language': '当前语言',
      'chinese': '中文',
      'english': '英文',
      'description': '这是一个支持中英文切换的开源鸿蒙应用',
      'flutter_view': 'Flutter 页面',
      'about': '关于',
      'version': '版本'
    },
    'en-US': {
      'app_name': 'OpenHarmony',
      'welcome': 'Welcome to OpenHarmony',
      'settings': 'Settings',
      'language': 'Language',
      'switch_language': 'Switch Language',
      'current_language': 'Current Language',
      'chinese': 'Chinese',
      'english': 'English',
      'description': 'This is an OpenHarmony app with Chinese/English switching support',
      'flutter_view': 'Flutter Page',
      'about': 'About',
      'version': 'Version'
    }
  };

  private constructor() {}

  static getInstance(): StringManager {
    if (!StringManager.instance) {
      StringManager.instance = new StringManager();
    }
    return StringManager.instance;
  }

  getString(key: string): string {
    const currentLang = i18nManager.getLanguage();
    const langStrings = this.stringResources[currentLang] || this.stringResources['zh-CN'];
    return langStrings[key] || key;
  }

  getStringByLang(key: string, lang: string): string {
    const langStrings = this.stringResources[lang] || this.stringResources['zh-CN'];
    return langStrings[key] || key;
  }

  getAllLanguages(): string[] {
    return Object.keys(this.stringResources);
  }
}

export const stringManager = StringManager.getInstance();

3.3 资源文件配置

鸿蒙系统支持基于目录的语言资源管理,在 resources 目录下创建不同语言的子目录:

中文资源(zh_CN/element/string.json):

{
  "string": [
    {
      "name": "module_desc",
      "value": "模块描述"
    },
    {
      "name": "app_name",
      "value": "开源鸿蒙示例"
    },
    {
      "name": "welcome",
      "value": "欢迎"
    },
    {
      "name": "settings",
      "value": "设置"
    },
    {
      "name": "language",
      "value": "语言"
    },
    {
      "name": "switch_language",
      "value": "切换语言"
    },
    {
      "name": "current_language",
      "value": "当前语言:中文"
    },
    {
      "name": "description",
      "value": "支持国际化的开源鸿蒙应用"
    }
  ]
}

英文资源(en_US/element/string.json):

{
  "string": [
    {
      "name": "module_desc",
      "value": "module description"
    },
    {
      "name": "app_name",
      "value": "OpenHarmony Demo"
    },
    {
      "name": "welcome",
      "value": "Welcome"
    },
    {
      "name": "settings",
      "value": "Settings"
    },
    {
      "name": "language",
      "value": "Language"
    },
    {
      "name": "switch_language",
      "value": "Switch Language"
    },
    {
      "name": "current_language",
      "value": "Current: English"
    },
    {
      "name": "description",
      "value": "OpenHarmony App with i18n Support"
    }
  ]
}

3.4 应用入口初始化

EntryAbility.ets 中初始化国际化管理器:

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import { i18nManager } from '../utils/I18nManager';
import { Want, AbilityConstant } from '@kit.AbilityKit';

export default class EntryAbility extends FlutterAbility {
  async onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): Promise<void> {
    super.onCreate(want, launchParam);
    await i18nManager.init(this.context);
  }

  configureFlutterEngine(flutterEngine: FlutterEngine): void {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
  }
}

3.5 页面集成

在主页面中集成国际化功能,实现语言切换按钮和动态文本显示:

import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
import { i18nManager } from '../utils/I18nManager';
import { stringManager } from '../utils/StringManager';

let storage = LocalStorage.getShared()
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'

@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext
  @LocalStorageLink('viewId') viewId: string = "";
  @State currentLanguage: string = 'zh-CN'
  @State displayText: string = ''

  aboutToAppear() {
    this.currentLanguage = i18nManager.getLanguage();
    this.updateDisplayText();
    i18nManager.addLanguageChangeListener(this.onLanguageChange.bind(this))
  }

  aboutToDisappear() {
    i18nManager.removeLanguageChangeListener(this.onLanguageChange.bind(this))
  }

  onLanguageChange(language: string) {
    this.currentLanguage = language
    this.updateDisplayText()
  }

  updateDisplayText() {
    this.displayText = stringManager.getString('welcome') + '\n' +
                        stringManager.getString('description')
  }

  async switchLanguage() {
    await i18nManager.switchLanguage()
  }

  build() {
    Column() {
      Row() {
        Text(stringManager.getString('app_name'))
          .fontSize(24)
          .fontWeight(FontWeight.Bold)
          .margin({ top: 20, left: 20 })

        Blank()

        Button(stringManager.getString('switch_language'))
          .margin({ top: 20, right: 20 })
          .onClick(() => {
            this.switchLanguage()
          })
      }
      .width('100%')

      Row() {
        Text(stringManager.getString('current_language') + ': ')
          .fontSize(16)
        Text(this.currentLanguage === 'zh-CN' ?
             stringManager.getString('chinese') :
             stringManager.getString('english'))
          .fontSize(16)
          .fontColor('#007DFF')
      }
      .margin({ top: 10 })

      Column() {
        Text(this.displayText)
          .fontSize(18)
          .textAlign(TextAlign.Center)
          .margin({ top: 20 })
      }
      .margin({ top: 20 })

      Divider().margin({ top: 20 })

      Text(stringManager.getString('flutter_view'))
        .fontSize(16)
        .margin({ top: 10 })

      Column() {
        FlutterPage({ viewId: this.viewId })
          .width('100%')
          .layoutWeight(1)
      }
    }
    .width('100%')
    .height('100%')
  }

  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS)
    return true
  }
}

页面实现要点:

  1. 状态变量:使用 @State 装饰器声明响应式变量,语言切换时自动触发 UI 刷新
  2. 生命周期管理:在 aboutToAppear 注册监听器,在 aboutToDisappear 移除监听器,避免内存泄漏
  3. 动态文本:所有显示文本都通过 stringManager.getString() 获取,确保语言切换后文本同步更新

四、运行验证

4.1 构建与部署

使用 hvigor 构建工具编译项目:

hvigorw assembleHap --mode module -p product=default -p module=entry@default

构建成功后,通过 hdc 工具安装到模拟器:

hdc install -r entry/build/default/outputs/default/entry-default-unsigned.hap

启动应用:

hdc shell aa start -a EntryAbility -b com.example.oh_demo8

4.2 运行效果

应用启动后,界面显示中文内容。点击右上角的"切换语言"按钮,界面即时切换为英文显示。再次点击,切换回中文。

运行日志验证:

SetLocale language tag: zh-CN, select language: zh-CN
SetLanguage: zh, colorMode: light, deviceAccess: 1

日志显示语言设置成功,国际化功能正常运行。

4.3 运行截图

在这里插入图片描述

图1:中文界面展示

在这里插入图片描述

图2:英文界面展示

五、最佳实践与注意事项

5.1 语言代码规范

鸿蒙系统采用标准的语言区域代码格式:

  • 中文(简体):zh-CNzh-Hans
  • 英文:en-USen

建议在项目中统一使用 zh-CNen-US 格式,保持一致性。

5.2 持久化存储时机

语言选择应在用户主动切换时立即持久化,而非应用退出时保存。这样可以确保:

  • 应用异常退出时不会丢失用户设置
  • 多进程场景下语言状态一致

5.3 监听器内存管理

务必在组件销毁时移除监听器,否则会导致:

  • 内存泄漏
  • 已销毁组件的回调被触发,引发异常

5.4 与 Flutter 层的通信

如需将语言切换同步到 Flutter 层,可以通过 MethodChannel 或 EventChannel 实现:

// Flutter 端
static const MethodChannel _channel = MethodChannel('i18n_channel');

Future<void> switchLanguage() async {
  await _channel.invokeMethod('switchLanguage');
}
// 鸿蒙端
methodChannel.setMethodCallHandler((call: MethodCall) => {
  if (call.method === 'switchLanguage') {
    i18nManager.switchLanguage();
  }
  return null;
});

六、总结

本文详细介绍了在 Flutter for OpenHarmony 跨平台工程中集成国际化能力的完整方案。通过设计 I18nManager 和 StringManager 两个核心模块,实现了语言状态的统一管理、持久化存储和动态切换。该方案具有以下优势:

  1. 架构清晰:职责分离,易于维护和扩展
  2. 响应式更新:基于监听器机制,语言切换后 UI 自动刷新
  3. 持久化支持:用户语言偏好自动保存,应用重启后恢复
  4. 扩展性强:支持轻松添加更多语言

希望本文能为开源鸿蒙跨平台开发者提供有价值的参考,助力构建更加国际化的应用。

感谢各位阅读!

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐