阿里云主机折上折
  • 微信号
Current Site:Index > The underlying mechanism of internationalization support

The underlying mechanism of internationalization support

Author:Chuan Chen 阅读数:24409人阅读 分类: Vue.js

Underlying Mechanism of Internationalization Support

Vue 3's internationalization functionality is implemented through the underlying APIs provided by @vue/runtime-core, with core logic concentrated in the createI18n function and accompanying compiler transformations. The internationalization system is designed as a three-layer architecture: message compilation-time processing, runtime resolution, and dynamic update mechanisms.

Compile-Time Message Transformation

During the Single-File Component (SFC) compilation phase, the v-t directive and $t calls in templates are specially processed. The compiler converts static messages into internationalizable data structures:

<!-- Original template -->
<p>{{ $t('message.hello') }}</p>

<!-- Compiled code -->
import { createVNode as _createVNode } from "vue"
export function render(_ctx, _cache) {
  return _createVNode("p", null, [
    _ctx.$t('message.hello')
  ])
}

The compiler recognizes three message formats:

  1. Simple strings: 'login.title'
  2. Parameterized templates: 'welcome {name}'
  3. Plural forms: 'cart.items | {count} item'

Runtime Message Resolution System

The instance created by createI18n is injected into the component tree via Vue's provide/inject mechanism:

// Create i18n instance
const i18n = createI18n({
  locale: 'zh-CN',
  messages: {
    'zh-CN': {
      button: {
        submit: '提交'
      }
    },
    'en-US': {
      button: {
        submit: 'Submit'
      }
    }
  }
})

// Install plugin
app.use(i18n)

The core of runtime resolution is the __translate function, which handles the following logic:

  1. Path resolution: Splits 'button.submit' into ['button', 'submit']
  2. Locale chain lookup: Attempts to find zh when zh-CN is not available
  3. Parameter interpolation: Processes placeholders in the form of {name}
  4. Plural handling: Processes different quantity forms based on the | separator

Dynamic Locale Switching Implementation

Locale switching is driven by the reactivity system, with the locale value implemented as reactive using ref:

const i18n = {
  get locale() {
    return _locale.value
  },
  set locale(newLocale) {
    _locale.value = newLocale
  }
}

When components obtain the i18n instance via inject, a dependency relationship is established:

// Component obtaining i18n instance
const i18n = inject('i18n')

// Mechanism to trigger re-rendering
watch(() => i18n.locale, () => {
  // Triggers updates for components using $t
})

Plural Rule Processing Engine

Plural handling is based on the Unicode CLDR specification, with built-in plural classification rules for common languages:

// Russian plural rules
function ruPluralRule(choice) {
  const rem100 = choice % 100
  if (rem100 > 10 && rem100 < 20) return 2
  const rem10 = choice % 10
  if (rem10 === 1) return 0
  if (rem10 >= 2 && rem10 <= 4) return 1
  return 2
}

// Message definition
messages: {
  ru: {
    apple: 'яблоко | яблока | яблок'
  }
}

DateTime Localization

Date formatting is deeply integrated via Intl.DateTimeFormat:

const dateFormats = {
  'en-US': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric'
    }
  },
  'ja-JP': {
    short: {
      year: 'numeric', month: 'short', day: 'numeric',
      era: 'short'
    }
  }
}

// Usage
$d(new Date(), 'short')

Component-Level Localization Control

Component-level control is achieved via the useI18n Composition API:

import { useI18n } from 'vue-i18n'

export default {
  setup() {
    const { t, locale } = useI18n({
      inheritLocale: false,
      locale: 'ja',
      messages: {
        ja: { /* Component-specific messages */ }
      }
    })

    return { t, locale }
  }
}

Custom Formatter Extension

Custom formatting logic can be extended via the formatters option:

const i18n = createI18n({
  formatters: {
    uppercase: (value) => value.toUpperCase(),
    price: (value, locale, arg) => {
      return new Intl.NumberFormat(locale, {
        style: 'currency',
        currency: arg
      }).format(value)
    }
  }
})

// Usage in templates
{{ $n(100, 'price', 'USD') }}

Asynchronous Message Loading Strategy

Dynamic message loading is implemented via the loadLocaleMessages function:

async function loadLocaleMessages(i18n, locale) {
  const messages = await import(`./locales/${locale}.json`)
  i18n.setLocaleMessage(locale, messages)
  return nextTick()
}

// Load when switching locales
watch(locale, (newLocale) => {
  loadLocaleMessages(i18n, newLocale)
})

Type Safety Integration

TypeScript type definitions enhance the development experience:

declare module 'vue-i18n' {
  interface DefineLocaleMessage {
    login: {
      title: string
      username: string
      password: string
    }
  }
  
  interface DefineDateTimeFormat {
    short: DateTimeFormatOptions
  }
}

Performance Optimization Measures

  1. Message caching: Parsed messages are stored in a WeakMap cache
  2. Lazy evaluation: Only actually used messages are parsed
  3. Tree-shaking optimization: Unused language packs are excluded by build tools
  4. Batch updates: Locale switches are processed in batches using nextTick
const messageCache = new WeakMap()

function getCachedMessage(key, locale) {
  if (!messageCache.has(locale)) {
    messageCache.set(locale, new Map())
  }
  const localeCache = messageCache.get(locale)
  if (localeCache.has(key)) {
    return localeCache.get(key)
  }
  // ...Parsing logic
  localeCache.set(key, parsed)
  return parsed
}

Server-Side Rendering Adaptation

Special handling is required for SSR environments:

// Synchronize state during client-side hydration
if (isClient && __SSR__) {
  const initialState = window.__INITIAL_STATE__
  if (initialState.i18n) {
    i18n.locale = initialState.i18n.locale
    i18n.fallbackLocale = initialState.i18n.fallbackLocale
  }
}

// Server-side collection of used messages
const usedMessages = new Set()
const originalT = i18n.global.t
i18n.global.t = (...args) => {
  usedMessages.add(args[0])
  return originalT(...args)
}

Debugging Tool Integration

Detailed warning messages are provided in development mode:

function warnMissing(key, locale) {
  if (__DEV__) {
    console.warn(
      `Missing translation for key: "${key}" in locale: "${locale}"`
    )
  }
}

// Check for undefined message paths
function resolveMessage(key, locale) {
  const res = resolveObjectPath(key, messages[locale])
  if (res === undefined) {
    warnMissing(key, locale)
  }
  return res
}

Custom Directive Implementation

Underlying logic of the v-t directive:

const vTDirective = {
  mounted(el, binding) {
    const i18n = el._i18n = inject('i18n')
    el._locale = computed(() => i18n.locale)
    updateText(el, binding)
  },
  updated(el, binding) {
    updateText(el, binding)
  }
}

function updateText(el, binding) {
  const { value, modifiers } = binding
  const path = typeof value === 'string' ? value : value.path
  const args = typeof value === 'object' ? value.args : {}
  el.textContent = el._i18n.t(path, args)
}

Error Handling Mechanism

Multi-layer error recovery strategies are provided:

  1. Fallback locale chain: zh-CN → zh → en
  2. Missing key fallback: button.submitsubmit
  3. Silent mode: No errors in production
  4. Error callback: Configurable missingHandler
const i18n = createI18n({
  missing: (locale, key) => {
    if (typeof __INTLIFY_WARN__ !== 'undefined' && __INTLIFY_WARN__) {
      warn(`Missing ${key} in ${locale}`)
    }
    return key // Returns the key name as a last resort
  }
})

Integration with Vue Router

Route meta information drives locale switching:

router.beforeEach((to) => {
  const newLocale = to.meta.locale || 'en'
  if (i18n.locale !== newLocale) {
    i18n.locale = newLocale
    document.documentElement.lang = newLocale
  }
})

Browser Language Auto-Detection

Initial language detection using the Navigator API:

function detectBrowserLocale() {
  const nav = typeof navigator !== 'undefined' ? navigator : {}
  const lang = nav.language || nav.userLanguage || 'en'
  return lang.split('-')[0]
}

const i18n = createI18n({
  locale: detectBrowserLocale(),
  fallbackLocale: 'en'
})

Unit Testing Support

Test utility functions are provided:

import { createTestI18n } from '@vue-i18n/test-utils'

const wrapper = mount(Component, {
  global: {
    plugins: [createTestI18n({
      locale: 'test',
      messages: { test: { hello: 'Test Hello' } }
    })]
  }
})

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.