HarmonyOS鸿蒙Next应用中如何实现多语言支持?

HarmonyOS鸿蒙Next应用中如何实现多语言支持? 1.在HarmonyOS应用中如何实现多语言支持? 2.如何根据系统语言自动切换和管理翻译资源?

4 回复

楼主写的很详细,先收藏了,后面有国际化需要再参考~

更多关于HarmonyOS鸿蒙Next应用中如何实现多语言支持?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


解决方案

1. 资源文件组织

entry/src/main/resources/
├── base/
│   ├── element/
│   │   └── string.json    # 默认语言
│   └── media/
├── zh_CN/                 # 简体中文
│   └── element/
│       └── string.json
├── zh_TW/                 # 繁体中文
│   └── element/
│       └── string.json
└── en_US/                 # 英语
    └── element/
        └── string.json

base/element/string.json (默认):

{
  "string": [
    {
      "name": "app_name",
      "value": "My App"
    },
    {
      "name": "welcome_message",
      "value": "Welcome"
    },
    {
      "name": "login",
      "value": "Login"
    },
    {
      "name": "logout",
      "value": "Logout"
    },
    {
      "name": "username",
      "value": "Username"
    },
    {
      "name": "password",
      "value": "Password"
    },
    {
      "name": "settings",
      "value": "Settings"
    },
    {
      "name": "language",
      "value": "Language"
    },
    {
      "name": "item_count",
      "value": "%d items"
    },
    {
      "name": "greeting",
      "value": "Hello, %s!"
    }
  ]
}

zh_CN/element/string.json (简体中文):

{
  "string": [
    {
      "name": "app_name",
      "value": "我的应用"
    },
    {
      "name": "welcome_message",
      "value": "欢迎"
    },
    {
      "name": "login",
      "value": "登录"
    },
    {
      "name": "logout",
      "value": "退出登录"
    },
    {
      "name": "username",
      "value": "用户名"
    },
    {
      "name": "password",
      "value": "密码"
    },
    {
      "name": "settings",
      "value": "设置"
    },
    {
      "name": "language",
      "value": "语言"
    },
    {
      "name": "item_count",
      "value": "%d 个项目"
    },
    {
      "name": "greeting",
      "value": "你好,%s!"
    }
  ]
}

2. 基础多语言使用

import i18n from '@ohos.i18n'

@Entry
@Component
struct I18nBasic {
  @State currentLanguage: string = ''

  aboutToAppear() {
    this.loadCurrentLanguage()
  }

  build() {
    Column({ space: 16 }) {
      Text($r('app.string.app_name'))
        .fontSize(24)
        .fontWeight(FontWeight.Bold)

      Text($r('app.string.welcome_message'))
        .fontSize(18)

      // 当前语言
      Row() {
        Text($r('app.string.language'))
          .layoutWeight(1)
        Text(this.currentLanguage)
          .fontColor('#1890ff')
      }
      .width('100%')
      .padding(16)
      .backgroundColor('#f5f5f5')
      .borderRadius(8)

      // 登录表单
      Column({ space: 12 }) {
        TextInput({ placeholder: $r('app.string.username') })
          .width('100%')

        TextInput({ placeholder: $r('app.string.password') })
          .width('100%')
          .type(InputType.Password)

        Button($r('app.string.login'))
          .width('100%')
      }
      .width('100%')

      Button($r('app.string.settings'))
        .width('100%')
    }
    .padding(16)
  }

  private loadCurrentLanguage() {
    const systemLanguage = i18n.System.getSystemLanguage()
    this.currentLanguage = systemLanguage
    console.log('当前系统语言:', systemLanguage)
  }
}

3. 格式化字符串

@Entry
@Component
struct FormattedStrings {
  @State username: string = '张三'
  @State itemCount: number = 5

  build() {
    Column({ space: 16 }) {
      Text('格式化字符串')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      // 单个参数
      Text(this.getFormattedString($r('app.string.greeting'), this.username))
        .fontSize(18)

      // 数字参数
      Text(this.getFormattedString($r('app.string.item_count'), this.itemCount))
        .fontSize(16)

      // 更新数据
      Row({ space: 12 }) {
        Button('-')
          .onClick(() => {
            if (this.itemCount > 0) {
              this.itemCount--
            }
          })

        Text(this.itemCount.toString())
          .fontSize(18)
          .layoutWeight(1)
          .textAlign(TextAlign.Center)

        Button('+')
          .onClick(() => {
            this.itemCount++
          })
      }
      .width('100%')
    }
    .padding(16)
  }

  private getFormattedString(resource: Resource, ...args: any[]): string {
    // 这里简化处理,实际应该使用系统API获取资源字符串并格式化
    // 示例: "Hello, %s!" -> "Hello, 张三!"
    return `Formatted: ${args.join(', ')}`
  }
}

4. 语言切换

import i18n from '@ohos.i18n'
import ConfigurationConstant from '@ohos.app.ability.ConfigurationConstant'

interface LanguageOption {
  code: string
  name: string
  nativeName: string
}

@Entry
@Component
struct LanguageSwitcher {
  @State currentLanguage: string = ''
  @State languages: LanguageOption[] = [
    { code: 'zh-CN', name: 'Chinese Simplified', nativeName: '简体中文' },
    { code: 'zh-TW', name: 'Chinese Traditional', nativeName: '繁體中文' },
    { code: 'en-US', name: 'English (US)', nativeName: 'English (US)' }
  ]

  aboutToAppear() {
    this.loadCurrentLanguage()
  }

  build() {
    Column({ space: 16 }) {
      Text($r('app.string.language'))
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Text(`当前语言: ${this.currentLanguage}`)
        .fontSize(14)
        .fontColor('#666666')

      List() {
        ForEach(this.languages, (lang: LanguageOption) => {
          ListItem() {
            Row({ space: 12 }) {
              Column({ space: 4 }) {
                Text(lang.nativeName)
                  .fontSize(16)
                Text(lang.name)
                  .fontSize(12)
                  .fontColor('#999999')
              }
              .alignItems(HorizontalAlign.Start)
              .layoutWeight(1)

              if (this.isCurrentLanguage(lang.code)) {
                Image($r('sys.media.ohos_ic_public_ok'))
                  .width(24)
                  .height(24)
                  .fillColor('#1890ff')
              }
            }
            .width('100%')
            .padding(16)
            .onClick(() => {
              this.switchLanguage(lang.code)
            })
          }
        })
      }
      .divider({ strokeWidth: 1, color: '#f0f0f0' })
      .layoutWeight(1)

      Text('注意: 语言切换需要重启应用生效')
        .fontSize(12)
        .fontColor('#faad14')
        .padding(12)
        .backgroundColor('#fffbe6')
        .borderRadius(8)
        .width('100%')
    }
    .padding(16)
  }

  private loadCurrentLanguage() {
    this.currentLanguage = i18n.System.getSystemLanguage()
  }

  private isCurrentLanguage(code: string): boolean {
    return this.currentLanguage.startsWith(code.split('-')[0])
  }

  private async switchLanguage(languageCode: string) {
    try {
      // 注意: HarmonyOS应用语言跟随系统
      // 这里仅作演示,实际需要引导用户在系统设置中修改
      console.log('切换语言到:', languageCode)
      
      // 保存用户选择的语言偏好
      await this.saveLanguagePreference(languageCode)

      // 提示用户重启应用
      this.showRestartTip()
    } catch (error) {
      console.error('切换语言失败:', error)
    }
  }

  private async saveLanguagePreference(languageCode: string): Promise<void> {
    // 使用Preferences保存语言偏好
    console.log('保存语言偏好:', languageCode)
  }

  private showRestartTip() {
    // 显示提示对话框
    console.log('请重启应用以应用新语言')
  }
}

5. 国际化工具类

// utils/I18nUtil.ets
import i18n from '@ohos.i18n'
import resourceManager from '@ohos.resourceManager'

export class I18nUtil {
  private static resMgr?: resourceManager.ResourceManager

  /**
   * 初始化资源管理器
   */
  static async init(): Promise<void> {
    this.resMgr = await resourceManager.getResourceManager(getContext())
  }

  /**
   * 获取字符串资源
   */
  static async getString(resId: number): Promise<string> {
    if (!this.resMgr) {
      await this.init()
    }

    try {
      return await this.resMgr!.getStringValue(resId)
    } catch (error) {
      console.error('获取字符串资源失败:', error)
      return ''
    }
  }

  /**
   * 获取格式化字符串
   */
  static async getFormattedString(resId: number, ...args: any[]): Promise<string> {
    let str = await this.getString(resId)
    
    // 替换占位符 %s, %d
    args.forEach((arg, index) => {
      if (typeof arg === 'number') {
        str = str.replace('%d', arg.toString())
      } else {
        str = str.replace('%s', arg.toString())
      }
    })

    return str
  }

  /**
   * 获取复数形式
   */
  static async getPluralString(
    resId: number,
    count: number
  ): Promise<string> {
    if (!this.resMgr) {
      await this.init()
    }

    try {
      return await this.resMgr!.getPluralStringValue(resId, count)
    } catch (error) {
      console.error('获取复数字符串失败:', error)
      return ''
    }
  }

  /**
   * 获取当前语言
   */
  static getCurrentLanguage(): string {
    return i18n.System.getSystemLanguage()
  }

  /**
   * 获取当前地区
   */
  static getCurrentRegion(): string {
    return i18n.System.getSystemRegion()
  }

  /**
   * 获取当前区域设置
   */
  static getCurrentLocale(): string {
    return i18n.System.getSystemLocale()
  }

  /**
   * 判断是否为RTL语言
   */
  static isRTL(): boolean {
    const language = this.getCurrentLanguage()
    const rtlLanguages = ['ar', 'he', 'fa', 'ur']
    return rtlLanguages.includes(language)
  }

  /**
   * 格式化日期
   */
  static formatDate(date: Date, format?: string): string {
    const locale = this.getCurrentLocale()
    const dateFormat = new i18n.DateTimeFormat(locale, {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit'
    })
    return dateFormat.format(date)
  }

  /**
   * 格式化时间
   */
  static formatTime(date: Date): string {
    const locale = this.getCurrentLocale()
    const timeFormat = new i18n.DateTimeFormat(locale, {
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    })
    return timeFormat.format(date)
  }

  /**
   * 格式化数字
   */
  static formatNumber(number: number): string {
    const locale = this.getCurrentLocale()
    const numberFormat = new i18n.NumberFormat(locale)
    return numberFormat.format(number)
  }

  /**
   * 格式化货币
   */
  static formatCurrency(amount: number, currency: string = 'CNY'): string {
    const locale = this.getCurrentLocale()
    const currencyFormat = new i18n.NumberFormat(locale, {
      style: 'currency',
      currency: currency
    })
    return currencyFormat.format(amount)
  }
}

// 使用示例
@Entry
@Component
struct I18nUtilDemo {
  @State formattedDate: string = ''
  @State formattedNumber: string = ''
  @State formattedCurrency: string = ''

  async aboutToAppear() {
    await I18nUtil.init()
    
    this.formattedDate = I18nUtil.formatDate(new Date())
    this.formattedNumber = I18nUtil.formatNumber(123456.789)
    this.formattedCurrency = I18nUtil.formatCurrency(9999.99)
  }

  build() {
    Column({ space: 16 }) {
      Text('国际化工具')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Column({ space: 8 }) {
        Row() {
          Text('当前语言:')
            .width(100)
          Text(I18nUtil.getCurrentLanguage())
        }
        .width('100%')

        Row() {
          Text('当前区域:')
            .width(100)
          Text(I18nUtil.getCurrentLocale())
        }
        .width('100%')

        Row() {
          Text('日期格式:')
            .width(100)
          Text(this.formattedDate)
        }
        .width('100%')

        Row() {
          Text('数字格式:')
            .width(100)
          Text(this.formattedNumber)
        }
        .width('100%')

        Row() {
          Text('货币格式:')
            .width(100)
          Text(this.formattedCurrency)
        }
        .width('100%')
      }
      .width('100%')
      .padding(16)
      .backgroundColor('#f5f5f5')
      .borderRadius(8)
    }
    .padding(16)
  }
}

关键要点

  1. 资源组织: 按语言代码组织资源文件
  2. $r引用: 使用$r(‘app.string.xxx’)引用字符串资源
  3. 系统语言: 应用语言跟随系统设置
  4. 格式化: 支持日期、数字、货币的本地化格式
  5. RTL支持: 考虑从右到左书写的语言

最佳实践

  1. 完整翻译: 所有语言资源保持一致
  2. 占位符: 使用%s、%d等占位符支持动态内容
  3. 复数处理: 使用复数资源处理数量相关文本
  4. 文化差异: 注意不同地区的文化和习惯
  5. 测试: 在不同语言环境下充分测试

在HarmonyOS鸿蒙Next应用中实现多语言支持,主要使用资源文件管理。在resources目录下创建elementstring.json文件,按语言分类存放字符串资源。例如,zh_CNen_US目录分别对应中英文。在string.json中定义键值对。在应用代码中,通过$r('app.string.key')引用字符串资源。系统会根据当前设备语言设置自动加载对应资源文件。

在HarmonyOS Next应用中实现多语言支持,主要依赖资源管理框架和$r$rawfile等引用能力。以下是核心实现步骤:

1. 创建与管理多语言资源文件

  • 目录结构:在entry/src/main/resources目录下,按语言代码(如zh-CNen-US)创建子目录,再在子目录内创建elementmedia等文件夹。
  • 字符串资源:在对应语言的element目录下创建string.json文件,定义键值对。例如:
    // zh-CN/string.json
    {
      "string": [
        {"name": "app_name", "value": "我的应用"}
      ]
    }
    
    // en-US/string.json
    {
      "string": [
        {"name": "app_name", "value": "My App"}
      ]
    }
    
  • 其他资源:图片、音频等文件可放入对应语言的media目录,系统会根据语言自动匹配。

2. 在应用中使用多语言资源

  • UI中引用:在ArkUI中使用$r('app.string.app_name')直接引用字符串,系统会自动根据当前语言环境加载对应资源。
  • 代码中引用:可通过ResourceManager API动态获取:
    const context = getContext(this) as common.UIAbilityContext;
    const resourceManager = context.resourceManager;
    let value = await resourceManager.getString($r('app.string.app_name').id);
    

3. 自动语言切换与管理

  • 系统自动匹配:应用启动时,HarmonyOS会根据设备系统语言设置,自动从resources目录匹配最接近的语言资源(例如系统为zh-Hans-CN,会优先匹配zh-CN目录)。
  • 动态监听与响应:应用可监听系统语言变化事件,并更新UI:
    import { I18n } from '[@ohos](/user/ohos).i18n';
    
    // 获取当前系统语言
    let currentLanguage = I18n.getSystemLanguage();
    
    // 监听语言变化(需在UIAbility或页面中合理管理事件订阅)
    // 系统语言变更时,应用框架会自动重新加载资源,但界面可能需要手动刷新。
    
  • 手动切换语言:应用内部可设置特定语言,覆盖系统默认行为,但这通常需要自行实现资源重载和界面刷新逻辑。

4. 注意事项

  • 资源ID一致性:所有语言版本的string.json中,相同name的键必须存在,确保引用不会失败。
  • 回退机制:如果当前系统语言无对应资源目录,框架会按语言相似性回退(如en-GB回退到en-US,最终到resources根目录的默认资源)。
  • ArkUI声明式UI:在.ets文件中使用$r引用资源是推荐做法,框架会自动处理语言切换时的更新。

通过以上步骤,即可在HarmonyOS Next应用中实现多语言支持,并依靠系统机制自动完成语言匹配与切换。

回到顶部