Back to Repositories

Validating I18n Locale Management in uni-app

This test suite validates the internationalization (i18n) functionality in the uni-app framework, focusing on locale handling, message translation, and fallback behavior. The tests ensure proper handling of various language codes and message interpolation across different locales.

Test Coverage Overview

The test suite provides comprehensive coverage of i18n functionality:
  • Locale resolution and normalization for multiple language variants
  • Message translation with variable interpolation
  • Fallback locale behavior
  • Locale change watching mechanism
  • Support for special characters and numeric keys in translations

Implementation Analysis

The testing approach utilizes Jest’s describe/test structure to organize test cases systematically. The implementation leverages TypeScript’s type system for locale definitions and employs dynamic test generation using Object.keys().forEach() to validate multiple locale variants efficiently.

Key patterns include message interpolation testing, locale watcher validation, and handling of edge cases like hyphenated keys and special characters.

Technical Details

Testing infrastructure includes:
  • Jest as the primary testing framework
  • TypeScript for type-safe testing
  • Custom I18n class implementation
  • Mock message objects for different locales
  • Locale mapping configuration for language variants

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Systematic testing of locale variants using programmatic test generation
  • Comprehensive edge case coverage including special characters and numeric keys
  • Clear separation of test cases by functionality
  • Strong type checking with TypeScript interfaces
  • Effective use of Jest’s expect assertions

dcloudio/uni-app

packages/uni-i18n/__tests__/i18n.spec.ts

            
import type { BuiltInLocale } from '../src/index'
import { I18n } from '../src/index'

const messages = {
  en: {
    hello: 'the world',
    helloName: 'Hello {name}',
    empty: '',
    'Hello {0}': 'Hello {0}',
    'hyphen-locale': 'hello hyphen',
    '1234': 'Number-based keys are found',
    '1mixedKey': 'Mixed keys are not found.',
    sálvame: 'save me',
  },
  'zh-Hans': {
    hello: '世界',
    helloName: '你好!{name}',
  },
}

const locales: {
  [name: string]: BuiltInLocale
} = {
  'zh-Hans': 'zh-Hans',
  'zh-CN': 'zh-Hans',
  zh_CN: 'zh-Hans',
  zh: 'zh-Hans',
  zh_cn: 'zh-Hans',
  zh_Hans_CN: 'zh-Hans',
  'zh-CHS': 'zh-Hans',
  'zh-Hans-CN': 'zh-Hans',
  'zh-Hans-TW': 'zh-Hans',
  'zh-CHT': 'zh-Hant',
  'zh-Hant': 'zh-Hant',
  'zh-TW': 'zh-Hant',
  'zh-Hant-TW': 'zh-Hant',
  'zh-HK': 'zh-Hant',
  'zh-Hant-HK': 'zh-Hant',
  'zh-MO': 'zh-Hant',
  'zh-Hant-MO': 'zh-Hant',
  en: 'en',
  'en-SG': 'en',
  'en-US': 'en',
  'en-AU': 'en',
  fr: 'fr',
  'fr-CA': 'fr',
  es: 'es',
  'es-AR': 'es',
  'sv-se': 'en', //fallback
  'ja-CN': 'en', //fallback
}

describe('i18n', () => {
  Object.keys(locales).forEach((name) => {
    test(`locale ${name}`, () => {
      const i18n = new I18n({
        locale: name as BuiltInLocale,
        fallbackLocale: 'en',
        messages: {},
      })
      expect(i18n.getLocale()).toBe(locales[name])
      i18n.setLocale('zh-Hant-HK')
      expect(i18n.getLocale()).toBe('zh-Hant')
    })
  })
  test('watchLocale', () => {
    let i = 0
    function watcher(newLocale: string, oldLocale: string) {
      i++
      expect(newLocale).toBe('zh-Hans')
      expect(oldLocale).toBe('en')
    }
    const i18n = new I18n({
      locale: 'en',
      fallbackLocale: 'en',
      messages: {},
      watcher,
    })
    i18n.watchLocale(watcher)
    i18n.setLocale('zh')
    expect(i).toBe(2)
  })
  test('zh-Hans locale', () => {
    const i18n = new I18n({
      locale: 'zh-Hans',
      fallbackLocale: 'en',
      messages,
    })
    expect(i18n.t('hello')).toBe(messages['zh-Hans'].hello)
    expect(i18n.t('helloName', { name: '世界' })).toBe(
      messages['zh-Hans'].helloName.replace('{name}', '世界')
    )
    expect(i18n.t('helloName', 'en', { name: '世界' })).toBe(
      messages.en.helloName.replace('{name}', '世界')
    )
    expect(i18n.t('Hello {0}', ['world'])).toBe('Hello {0}')
  })
  test('en locale', () => {
    const i18n = new I18n({
      locale: 'en',
      fallbackLocale: 'en',
      messages,
    })
    expect(i18n.t('hello')).toBe(messages.en.hello)
    expect(i18n.t('helloName', { name: 'world' })).toBe(
      messages.en.helloName.replace('{name}', 'world')
    )
    expect(i18n.t('Hello {0}', ['world'])).toBe(
      messages.en['Hello {0}'].replace('{0}', 'world')
    )
    expect(i18n.t('empty')).toBe(messages.en.empty)
    expect(i18n.t('hyphen-locale')).toBe(messages.en['hyphen-locale'])
    expect(i18n.t(1234 as unknown as string)).toBe(messages.en[1234])
    expect(i18n.t('1mixedKey')).toBe(messages.en['1mixedKey'])
    expect(i18n.t('sálvame')).toBe(messages.en['sálvame'])
  })
})