温馨提示:本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦!

HarmonyOS NEXT 应用国际化与本地化指南:打造全球化应用

1. 国际化基础

1.1 基本概念

概念说明实现方式示例
国际化(i18n)支持多语言文本翻译中英文切换
本地化(l10n)适应地区格式适配日期格式
区域设置地区配置系统设置zh-CN, en-US

1.2 国际化配置

// i18n.config.ts
interface I18nConfig {
  defaultLocale: string;
  supportedLocales: string[];
  fallbackLocale: string;
  loadPath: string;
}

class I18nManager {
  private static instance: I18nManager;
  private config: I18nConfig;
  private translations: Map<string, any> = new Map();
  private currentLocale: string;
  
  private constructor() {
    this.config = {
      defaultLocale: 'en-US',
      supportedLocales: ['en-US', 'zh-CN', 'ja-JP'],
      fallbackLocale: 'en-US',
      loadPath: '/resources/i18n/'
    };
  }
  
  static getInstance(): I18nManager {
    if (!this.instance) {
      this.instance = new I18nManager();
    }
    return this.instance;
  }
  
  // 初始化国际化
  async initialize(): Promise<void> {
    // 加载系统语言设置
    this.currentLocale = await this.getSystemLocale();
    
    // 加载翻译资源
    await this.loadTranslations(this.currentLocale);
  }
  
  // 切换语言
  async changeLocale(locale: string): Promise<void> {
    if (!this.config.supportedLocales.includes(locale)) {
      throw new Error(`Unsupported locale: ${locale}`);
    }
    
    this.currentLocale = locale;
    await this.loadTranslations(locale);
    this.notifyLocaleChange();
  }
}

2. 多语言支持

2.1 翻译资源管理

// translations.ts
interface TranslationResource {
  [key: string]: string | TranslationResource;
}

class TranslationManager {
  private resources: Map<string, TranslationResource> = new Map();
  
  // 加载翻译资源
  async loadResources(locale: string): Promise<void> {
    try {
      const resource = await this.fetchTranslations(locale);
      this.resources.set(locale, resource);
    } catch (error) {
      console.error(`Failed to load translations for ${locale}:`, error);
      throw error;
    }
  }
  
  // 获取翻译文本
  translate(
    key: string, 
    params?: object, 
    locale?: string
  ): string {
    const currentLocale = locale || I18nManager.getInstance().currentLocale;
    const resource = this.resources.get(currentLocale);
    
    if (!resource) {
      return key;
    }
    
    let value = this.getNestedValue(resource, key);
    
    if (typeof value !== 'string') {
      return key;
    }
    
    // 替换参数
    if (params) {
      value = this.interpolateParams(value, params);
    }
    
    return value;
  }
  
  // 参数插值
  private interpolateParams(
    text: string, 
    params: object
  ): string {
    return text.replace(/\{(\w+)\}/g, (_, key) => 
      params[key]?.toString() || ''
    );
  }
}

2.2 组件国际化

// i18n.component.ets
@Component
struct I18nText {
  @State text: string = '';
  private key: string;
  private params?: object;
  
  aboutToAppear() {
    // 监听语言变化
    this.updateText();
    I18nManager.getInstance().onLocaleChange(() => {
      this.updateText();
    });
  }
  
  private updateText() {
    this.text = TranslationManager.getInstance()
      .translate(this.key, this.params);
  }
  
  build() {
    Text(this.text)
  }
}

// 使用示例
@Component
struct WelcomeScreen {
  build() {
    Column() {
      I18nText({
        key: 'welcome.title',
        params: { name: 'User' }
      })
      
      I18nText({
        key: 'welcome.message'
      })
    }
  }
}

3. 本地化适配

3.1 日期时间格式化

class DateTimeFormatter {
  private locale: string;
  
  constructor(locale: string) {
    this.locale = locale;
  }
  
  // 格式化日期
  formatDate(
    date: Date, 
    format?: Intl.DateTimeFormatOptions
  ): string {
    const options = format || {
      year: 'numeric',
      month: 'long',
      day: 'numeric'
    };
    
    return new Intl.DateTimeFormat(
      this.locale, 
      options
    ).format(date);
  }
  
  // 格式化时间
  formatTime(
    date: Date, 
    format?: Intl.DateTimeFormatOptions
  ): string {
    const options = format || {
      hour: 'numeric',
      minute: 'numeric',
      hour12: true
    };
    
    return new Intl.DateTimeFormat(
      this.locale, 
      options
    ).format(date);
  }
  
  // 格式化相对时间
  formatRelative(date: Date): string {
    const rtf = new Intl.RelativeTimeFormat(this.locale, {
      numeric: 'auto'
    });
    
    const diff = date.getTime() - Date.now();
    const days = Math.round(diff / (1000 * 60 * 60 * 24));
    
    return rtf.format(days, 'day');
  }
}

3.2 数字和货币格式化

class NumberFormatter {
  private locale: string;
  
  constructor(locale: string) {
    this.locale = locale;
  }
  
  // 格式化数字
  formatNumber(
    value: number, 
    options?: Intl.NumberFormatOptions
  ): string {
    return new Intl.NumberFormat(
      this.locale, 
      options
    ).format(value);
  }
  
  // 格式化货币
  formatCurrency(
    value: number, 
    currency: string
  ): string {
    return new Intl.NumberFormat(this.locale, {
      style: 'currency',
      currency
    }).format(value);
  }
  
  // 格式化百分比
  formatPercent(value: number): string {
    return new Intl.NumberFormat(this.locale, {
      style: 'percent'
    }).format(value);
  }
}

4. 资源管理

4.1 多语言资源管理

class ResourceManager {
  private static readonly RESOURCE_PATH = '/resources';
  private resources: Map<string, any> = new Map();
  
  // 加载资源
  async loadResources(locale: string): Promise<void> {
    try {
      // 加载字符串资源
      const strings = await this.loadStrings(locale);
      
      // 加载图片资源
      const images = await this.loadImages(locale);
      
      // 加载其他资源
      const others = await this.loadOtherResources(locale);
      
      this.resources.set(locale, {
        strings,
        images,
        others
      });
    } catch (error) {
      console.error(`Failed to load resources for ${locale}:`, error);
      throw error;
    }
  }
  
  // 获取资源
  getResource(
    key: string, 
    type: string, 
    locale?: string
  ): any {
    const currentLocale = locale || I18nManager.getInstance().currentLocale;
    const resources = this.resources.get(currentLocale);
    
    if (!resources || !resources[type]) {
      return null;
    }
    
    return resources[type][key];
  }
}

4.2 资源加载优化

class ResourceLoader {
  private static cache: Map<string, any> = new Map();
  
  // 预加载资源
  static async preloadResources(
    locale: string
  ): Promise<void> {
    const resources = [
      this.preloadStrings(locale),
      this.preloadImages(locale),
      this.preloadFonts(locale)
    ];
    
    await Promise.all(resources);
  }
  
  // 懒加载资源
  static async lazyLoadResource(
    key: string,
    type: string,
    locale: string
  ): Promise<any> {
    const cacheKey = `${locale}:${type}:${key}`;
    
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    const resource = await this.loadResource(key, type, locale);
    this.cache.set(cacheKey, resource);
    
    return resource;
  }
}

5. 最佳实践

5.1 国际化组件封装

// i18n.decorator.ts
function I18n(options: I18nOptions = {}) {
  return function (target: any) {
    return class extends target {
      private i18n = I18nManager.getInstance();
      
      aboutToAppear() {
        // 监听语言变化
        this.i18n.onLocaleChange(() => {
          this.updateI18n();
        });
        
        if (super.aboutToAppear) {
          super.aboutToAppear();
        }
      }
      
      private updateI18n() {
        // 更新组件的国际化文本
        if (options.props) {
          options.props.forEach(prop => {
            if (this[prop]) {
              this[prop] = this.i18n.translate(this[prop]);
            }
          });
        }
      }
    }
  }
}

// 使用示例
@I18n({
  props: ['title', 'message']
})
@Component
struct LocalizedComponent {
  @State title: string = 'welcome.title';
  @State message: string = 'welcome.message';
  
  build() {
    Column() {
      Text(this.title)
      Text(this.message)
    }
  }
}

5.2 本地化测试

class LocalizationTester {
  // 测试翻译完整性
  static async testTranslations(
    locales: string[]
  ): Promise<TestResult> {
    const results = [];
    
    for (const locale of locales) {
      const missing = await this.findMissingTranslations(locale);
      results.push({
        locale,
        missing,
        complete: missing.length === 0
      });
    }
    
    return {
      success: results.every(r => r.complete),
      results
    };
  }
  
  // 测试格式化
  static async testFormatting(
    locale: string
  ): Promise<TestResult> {
    const tests = [
      this.testDateFormatting(locale),
      this.testNumberFormatting(locale),
      this.testCurrencyFormatting(locale)
    ];
    
    const results = await Promise.all(tests);
    
    return {
      success: results.every(r => r.success),
      results
    };
  }
}

5.3 最佳实践建议

  1. 文本管理

    • 使用键值对管理文本
    • 避免硬编码字符串
    • 维护统一的翻译文件
  2. 资源组织

    • 按语言分类资源
    • 实现资源懒加载
    • 优化资源加载性能
  3. 格式适配

    • 使用标准格式化工具
    • 考虑不同地区习惯
    • 处理特殊字符
  4. 测试验证

    • 测试所有支持的语言
    • 验证格式化结果
    • 检查资源完整性
  5. 性能优化

    • 实现资源缓存
    • 优化加载策略
    • 减少运行时开销

通过合理的国际化和本地化策略,可以使应用更好地适应全球市场。在实际开发中,要注意平衡功能完整性和性能表现,确保良好的用户体验。


全栈若城
1 声望2 粉丝