国际化(i18n)是应用开发中的重要环节,可以让应用支持多种语言和地区。鸿蒙系统提供了完整的国际化解决方案,本文将详细介绍如何在鸿蒙应用中实现国际化。
在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": "未知错误"
}
}
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)
}
}
@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() {
// 显示设置页面
}
}
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)
}
}
@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%')
}
}
@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)
}
}
}
// 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)
}
}
}
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')
// 播放音频
}
}
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小时后"
}
}
}
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'))
}
}
}
@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
}
}
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))
}
}
本文详细介绍了鸿蒙系统的国际化实现方法,包括文本翻译、布局适配、媒体资源管理和数据格式化等内容。通过合理运用这些特性,开发者可以构建出支持多语言、适应不同地区的应用。在实际开发中,要注意性能优化和用户体验,确保应用在不同语言环境下都能正常运行。