HarmonyOS从入门到精通:国际化指南

引言

国际化(i18n)是应用开发中的重要环节,可以让应用支持多种语言和地区。鸿蒙系统提供了完整的国际化解决方案,本文将详细介绍如何在鸿蒙应用中实现国际化。

基础配置

1. 资源文件配置

resources目录下创建多语言资源文件:

// resources/en_US/string.json
{
  "hello": "Hello",
  "welcome": "Welcome to HarmonyOS",
  "settings": {
    "title": "Settings",
    "language": "Language",
    "theme": "Theme"
  },
  "errors": {
    "network": "Network error",
    "unknown": "Unknown error"
  }
}

// resources/zh_CN/string.json
{
  "hello": "你好",
  "welcome": "欢迎使用鸿蒙系统",
  "settings": {
    "title": "设置",
    "language": "语言",
    "theme": "主题"
  },
  "errors": {
    "network": "网络错误",
    "unknown": "未知错误"
  }
}

2. 资源管理器配置

import i18n from '@ohos.i18n'

class I18nManager {
  private static instance: I18nManager
  private currentLocale: string
  
  private constructor() {
    // 获取系统语言
    this.currentLocale = i18n.getSystemLanguage()
  }
  
  static getInstance(): I18nManager {
    if (!I18nManager.instance) {
      I18nManager.instance = new I18nManager()
    }
    return I18nManager.instance
  }
  
  // 获取当前语言
  getCurrentLocale(): string {
    return this.currentLocale
  }
  
  // 设置语言
  setLocale(locale: string) {
    if (this.isValidLocale(locale)) {
      this.currentLocale = locale
      // 触发语言变更事件
      this.notifyLocaleChange()
    }
  }
  
  // 验证语言代码
  private isValidLocale(locale: string): boolean {
    const supportedLocales = ['en_US', 'zh_CN', 'ja_JP', 'ko_KR']
    return supportedLocales.includes(locale)
  }
  
  // 通知语言变更
  private notifyLocaleChange() {
    // 发送语言变更事件
    globalThis.eventHub.emit('localeChange', this.currentLocale)
  }
}

文本国际化

1. 基础文本翻译

@Entry
@Component
struct LocalizedText {
  @State currentLocale: string = I18nManager.getInstance().getCurrentLocale()
  
  aboutToAppear() {
    // 监听语言变更
    globalThis.eventHub.on('localeChange', (locale) => {
      this.currentLocale = locale
    })
  }
  
  build() {
    Column() {
      // 使用翻译文本
      Text($r('app.string.hello'))
        .fontSize(20)
      
      Text($r('app.string.welcome'))
        .fontSize(16)
        .margin({ top: 10 })
      
      // 设置按钮
      Button($r('app.string.settings.title'))
        .onClick(() => {
          this.showSettings()
        })
    }
    .width('100%')
    .padding(20)
  }
  
  private showSettings() {
    // 显示设置页面
  }
}

2. 格式化文本

class TextFormatter {
  // 数字格式化
  static formatNumber(value: number, locale: string): string {
    return new Intl.NumberFormat(locale).format(value)
  }
  
  // 货币格式化
  static formatCurrency(value: number, locale: string, currency: string): string {
    return new Intl.NumberFormat(locale, {
      style: 'currency',
      currency: currency
    }).format(value)
  }
  
  // 日期格式化
  static formatDate(date: Date, locale: string, options?: Intl.DateTimeFormatOptions): string {
    return new Intl.DateTimeFormat(locale, options).format(date)
  }
  
  // 复数形式
  static formatPlural(count: number, locale: string, forms: {
    zero?: string,
    one: string,
    other: string
  }): string {
    const pluralRules = new Intl.PluralRules(locale)
    const form = pluralRules.select(count)
    return forms[form] || forms.other
  }
}

// 使用示例
@Entry
@Component
struct FormattedText {
  @State currentLocale: string = I18nManager.getInstance().getCurrentLocale()
  @State count: number = 0
  
  build() {
    Column() {
      // 数字格式化
      Text(TextFormatter.formatNumber(1234567.89, this.currentLocale))
      
      // 货币格式化
      Text(TextFormatter.formatCurrency(99.99, this.currentLocale, 'USD'))
      
      // 日期格式化
      Text(TextFormatter.formatDate(new Date(), this.currentLocale, {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
      }))
      
      // 复数形式
      Text(TextFormatter.formatPlural(this.count, this.currentLocale, {
        zero: '没有消息',
        one: '1条消息',
        other: `${this.count}条消息`
      }))
      
      Button('+1')
        .onClick(() => {
          this.count++
        })
    }
    .width('100%')
    .padding(20)
  }
}

布局国际化

1. 自适应布局

@Entry
@Component
struct LocalizedLayout {
  @State isRTL: boolean = false
  
  aboutToAppear() {
    // 检查是否为RTL语言
    this.isRTL = i18n.isRTL(I18nManager.getInstance().getCurrentLocale())
  }
  
  build() {
    Column() {
      // 自适应方向的行布局
      Row() {
        Image($r('app.media.icon'))
          .width(40)
          .height(40)
        
        Text($r('app.string.title'))
          .fontSize(16)
          .margin({
            left: this.isRTL ? 0 : 10,
            right: this.isRTL ? 10 : 0
          })
      }
      .width('100%')
      .direction(this.isRTL ? Direction.Rtl : Direction.Ltr)
      
      // 自适应边距
      Button($r('app.string.action'))
        .margin({
          start: 20,
          end: 20
        })
    }
    .width('100%')
  }
}

2. RTL支持

@Component
struct RTLSupportExample {
  @State isRTL: boolean = false
  
  build() {
    Column() {
      // 支持RTL的列表
      List() {
        ListItem() {
          Row() {
            Image($r('app.media.arrow'))
              .width(20)
              .height(20)
              .rotate({
                angle: this.isRTL ? 180 : 0
              })
            
            Text($r('app.string.item'))
              .fontSize(16)
              .margin({
                start: 10
              })
          }
          .width('100%')
          .direction(this.isRTL ? Direction.Rtl : Direction.Ltr)
        }
      }
      .width('100%')
      
      // RTL感知的滑动视图
      Swiper() {
        ForEach([1, 2, 3], (item) => {
          Text(`Page ${item}`)
            .width('100%')
            .height('100%')
            .backgroundColor(Color.Gray)
            .textAlign(TextAlign.Center)
        })
      }
      .width('100%')
      .height(200)
      .loop(false)
      .direction(this.isRTL ? SwiperDirection.Rtl : SwiperDirection.Ltr)
    }
  }
}

图片和媒体国际化

1. 图片资源管理

// resources/en_US/media/
// resources/zh_CN/media/

class LocalizedImageLoader {
  static getImage(name: string): Resource {
    const locale = I18nManager.getInstance().getCurrentLocale()
    return $r(`app.media.${locale}.${name}`)
  }
  
  static getImagePath(name: string): string {
    const locale = I18nManager.getInstance().getCurrentLocale()
    return `resources/${locale}/media/${name}`
  }
}

// 使用示例
@Entry
@Component
struct LocalizedImage {
  build() {
    Column() {
      Image(LocalizedImageLoader.getImage('banner'))
        .width('100%')
        .height(200)
        .objectFit(ImageFit.Cover)
      
      Image(LocalizedImageLoader.getImage('welcome'))
        .width(100)
        .height(100)
    }
  }
}

2. 媒体内容本地化

class LocalizedMediaManager {
  static getVideoSource(name: string): VideoSource {
    const locale = I18nManager.getInstance().getCurrentLocale()
    return {
      src: `resources/${locale}/videos/${name}`,
      poster: `resources/${locale}/images/${name}_poster`
    }
  }
  
  static getAudioSource(name: string): string {
    const locale = I18nManager.getInstance().getCurrentLocale()
    return `resources/${locale}/audio/${name}`
  }
}

// 使用示例
@Entry
@Component
struct LocalizedMedia {
  build() {
    Column() {
      Video({
        src: LocalizedMediaManager.getVideoSource('tutorial').src,
        poster: LocalizedMediaManager.getVideoSource('tutorial').poster
      })
        .width('100%')
        .height(200)
        .controls(true)
      
      Button($r('app.string.play_audio'))
        .onClick(() => {
          this.playAudio()
        })
    }
  }
  
  private async playAudio() {
    const audioSource = LocalizedMediaManager.getAudioSource('welcome')
    // 播放音频
  }
}

数据国际化

1. 日期和时间

class DateTimeFormatter {
  static formatDateTime(date: Date, locale: string, options?: Intl.DateTimeFormatOptions): string {
    const formatter = new Intl.DateTimeFormat(locale, {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      hour: 'numeric',
      minute: 'numeric',
      second: 'numeric',
      ...options
    })
    return formatter.format(date)
  }
  
  static formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, locale: string): string {
    const formatter = new Intl.RelativeTimeFormat(locale, {
      numeric: 'auto'
    })
    return formatter.format(value, unit)
  }
}

// 使用示例
@Entry
@Component
struct DateTimeExample {
  @State currentLocale: string = I18nManager.getInstance().getCurrentLocale()
  @State currentDate: Date = new Date()
  
  build() {
    Column() {
      // 完整日期时间
      Text(DateTimeFormatter.formatDateTime(this.currentDate, this.currentLocale))
      
      // 相对时间
      Text(DateTimeFormatter.formatRelativeTime(-1, 'day', this.currentLocale)) // "昨天"
      Text(DateTimeFormatter.formatRelativeTime(2, 'hour', this.currentLocale)) // "2小时后"
    }
  }
}

2. 数字和货币

class NumberFormatter {
  static formatDecimal(value: number, locale: string, options?: Intl.NumberFormatOptions): string {
    return new Intl.NumberFormat(locale, options).format(value)
  }
  
  static formatPercentage(value: number, locale: string): string {
    return new Intl.NumberFormat(locale, {
      style: 'percent',
      minimumFractionDigits: 2
    }).format(value)
  }
  
  static formatCurrency(value: number, locale: string, currency: string): string {
    return new Intl.NumberFormat(locale, {
      style: 'currency',
      currency: currency
    }).format(value)
  }
}

// 使用示例
@Entry
@Component
struct NumberExample {
  @State currentLocale: string = I18nManager.getInstance().getCurrentLocale()
  
  build() {
    Column() {
      // 十进制数
      Text(NumberFormatter.formatDecimal(1234567.89, this.currentLocale))
      
      // 百分比
      Text(NumberFormatter.formatPercentage(0.1234, this.currentLocale))
      
      // 货币
      Text(NumberFormatter.formatCurrency(99.99, this.currentLocale, 'USD'))
      Text(NumberFormatter.formatCurrency(99.99, this.currentLocale, 'CNY'))
      Text(NumberFormatter.formatCurrency(99.99, this.currentLocale, 'EUR'))
    }
  }
}

最佳实践

1. 语言切换

@Entry
@Component
struct LanguageSelector {
  @State currentLocale: string = I18nManager.getInstance().getCurrentLocale()
  
  build() {
    Column() {
      // 语言选择器
      Select([
        { value: 'en_US', text: 'English' },
        { value: 'zh_CN', text: '中文' },
        { value: 'ja_JP', text: '日本語' },
        { value: 'ko_KR', text: '한국어' }
      ])
        .selected(this.currentLocale)
        .onSelect((index, value) => {
          this.changeLanguage(value)
        })
      
      // 当前语言显示
      Text($r('app.string.current_language'))
        .fontSize(16)
        .margin({ top: 20 })
    }
    .width('100%')
    .padding(20)
  }
  
  private changeLanguage(locale: string) {
    I18nManager.getInstance().setLocale(locale)
    this.currentLocale = locale
  }
}

2. 性能优化

class I18nCache {
  private static cache: Map<string, Map<string, string>> = new Map()
  
  static getString(key: string, locale: string): string {
    if (!this.cache.has(locale)) {
      this.cache.set(locale, new Map())
    }
    
    const localeCache = this.cache.get(locale)
    if (!localeCache.has(key)) {
      const value = this.loadString(key, locale)
      localeCache.set(key, value)
    }
    
    return localeCache.get(key)
  }
  
  private static loadString(key: string, locale: string): string {
    // 从资源文件加载字符串
    return ''
  }
  
  static clearCache() {
    this.cache.clear()
  }
}

// 使用缓存的文本组件
@Component
struct CachedText {
  @Prop key: string
  @State locale: string = I18nManager.getInstance().getCurrentLocale()
  
  build() {
    Text(I18nCache.getString(this.key, this.locale))
  }
}

总结

本文详细介绍了鸿蒙系统的国际化实现方法,包括文本翻译、布局适配、媒体资源管理和数据格式化等内容。通过合理运用这些特性,开发者可以构建出支持多语言、适应不同地区的应用。在实际开发中,要注意性能优化和用户体验,确保应用在不同语言环境下都能正常运行。

你可能感兴趣的:(笔记,harmonyos,鸿蒙,harmonyos,华为,鸿蒙,国际化,多语言,artts,实践)