Nuxt.js 国际化支持

学习目标

通过本章节的学习,你将能够:

  • 掌握 Nuxt.js 中国际化插件的集成方法
  • 理解如何配置和管理语言文件
  • 学会实现动态语言切换功能
  • 了解如何配置路由国际化
  • 掌握日期、数字等格式化的方法
  • 了解国际化的最佳实践

核心知识点讲解

什么是国际化?

国际化(Internationalization,简称 i18n)是指设计和开发应用程序时,使其能够适应不同语言和地区的需求,而无需进行工程上的改变。国际化的目标是让应用程序能够轻松地支持多语言和多地区。

Nuxt.js 中的国际化方案

Nuxt.js 提供了多种国际化方案,其中最常用的是使用 @nuxtjs/i18n 模块。这个模块基于 Vue I18n 库,提供了完整的国际化功能。

安装和配置 @nuxtjs/i18n 模块

步骤

  1. 安装 @nuxtjs/i18n 模块
  2. nuxt.config.js 中配置国际化选项
  3. 创建语言文件

示例:安装模块

# 使用 npm
npm install @nuxtjs/i18n

# 使用 yarn
yarn add @nuxtjs/i18n

# 使用 pnpm
pnpm add @nuxtjs/i18n

示例:在 nuxt.config.js 中配置

export default {
  modules: [
    '@nuxtjs/i18n'
  ],
  i18n: {
    // 支持的语言
    locales: [
      {
        code: 'zh',
        iso: 'zh-CN',
        name: '中文',
        file: 'zh-CN.js'
      },
      {
        code: 'en',
        iso: 'en-US',
        name: 'English',
        file: 'en-US.js'
      }
    ],
    // 语言文件的目录
    langDir: 'locales/',
    // 默认语言
    defaultLocale: 'zh',
    // 路由国际化配置
    strategy: 'prefix_and_default',
    // 检测浏览器语言
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n_redirected',
      alwaysRedirect: true,
      fallbackLocale: 'zh'
    },
    // 自定义路径
    pages: {
      about: {
        zh: '/about',
        en: '/about-us'
      }
    }
  }
}

创建语言文件

locales 目录下创建语言文件:

示例locales/zh-CN.js

export default {
  welcome: '欢迎来到我的网站',
  about: '关于我们',
  contact: '联系我们',
  home: '首页',
  blog: '博客',
  products: '产品',
  services: '服务',
  login: '登录',
  register: '注册',
  logout: '退出登录',
  search: '搜索',
  submit: '提交',
  cancel: '取消',
  save: '保存',
  delete: '删除',
  edit: '编辑',
  create: '创建',
  update: '更新',
  name: '姓名',
  email: '邮箱',
  password: '密码',
  confirmPassword: '确认密码',
  phone: '电话',
  address: '地址',
  message: '留言',
  send: '发送',
  thanks: '谢谢',
  success: '成功',
  error: '错误',
  warning: '警告',
  info: '信息',
  loading: '加载中...',
  noData: '暂无数据',
  pageNotFound: '页面不存在',
  serverError: '服务器错误',
  backToHome: '返回首页',
  retry: '重试',
  continue: '继续',
  finish: '完成',
  next: '下一步',
  previous: '上一步',
  // 日期相关
  date: {
    format: 'YYYY-MM-DD',
    today: '今天',
    yesterday: '昨天',
    tomorrow: '明天',
    monday: '周一',
    tuesday: '周二',
    wednesday: '周三',
    thursday: '周四',
    friday: '周五',
    saturday: '周六',
    sunday: '周日',
    january: '一月',
    february: '二月',
    march: '三月',
    april: '四月',
    may: '五月',
    june: '六月',
    july: '七月',
    august: '八月',
    september: '九月',
    october: '十月',
    november: '十一月',
    december: '十二月'
  },
  // 数字相关
  number: {
    currency: '¥{{ value }}',
    percentage: '{{ value }}%',
    decimal: '{{ value }}'
  }
}

示例locales/en-US.js

export default {
  welcome: 'Welcome to my website',
  about: 'About Us',
  contact: 'Contact Us',
  home: 'Home',
  blog: 'Blog',
  products: 'Products',
  services: 'Services',
  login: 'Login',
  register: 'Register',
  logout: 'Logout',
  search: 'Search',
  submit: 'Submit',
  cancel: 'Cancel',
  save: 'Save',
  delete: 'Delete',
  edit: 'Edit',
  create: 'Create',
  update: 'Update',
  name: 'Name',
  email: 'Email',
  password: 'Password',
  confirmPassword: 'Confirm Password',
  phone: 'Phone',
  address: 'Address',
  message: 'Message',
  send: 'Send',
  thanks: 'Thank you',
  success: 'Success',
  error: 'Error',
  warning: 'Warning',
  info: 'Info',
  loading: 'Loading...',
  noData: 'No data',
  pageNotFound: 'Page not found',
  serverError: 'Server error',
  backToHome: 'Back to home',
  retry: 'Retry',
  continue: 'Continue',
  finish: 'Finish',
  next: 'Next',
  previous: 'Previous',
  // Date related
  date: {
    format: 'MM/DD/YYYY',
    today: 'Today',
    yesterday: 'Yesterday',
    tomorrow: 'Tomorrow',
    monday: 'Monday',
    tuesday: 'Tuesday',
    wednesday: 'Wednesday',
    thursday: 'Thursday',
    friday: 'Friday',
    saturday: 'Saturday',
    sunday: 'Sunday',
    january: 'January',
    february: 'February',
    march: 'March',
    april: 'April',
    may: 'May',
    june: 'June',
    july: 'July',
    august: 'August',
    september: 'September',
    october: 'October',
    november: 'November',
    december: 'December'
  },
  // Number related
  number: {
    currency: '${{ value }}',
    percentage: '{{ value }}%',
    decimal: '{{ value }}'
  }
}

在组件中使用国际化

在组件中,你可以使用 $t 方法来翻译文本:

<template>
  <div>
    <h1>{{ $t('welcome') }}</h1>
    <nav>
      <nuxt-link to="/" class="nav-link">{{ $t('home') }}</nuxt-link>
      <nuxt-link to="/about" class="nav-link">{{ $t('about') }}</nuxt-link>
      <nuxt-link to="/contact" class="nav-link">{{ $t('contact') }}</nuxt-link>
    </nav>
  </div>
</template>

<script>
export default {
  name: 'HomePage'
}
</script>

<style scoped>
.nav-link {
  margin-right: 20px;
  text-decoration: none;
  color: #333;
}

.nav-link:hover {
  color: #1890ff;
}
</style>

动态语言切换

你可以使用 $i18n 对象来实现动态语言切换:

<template>
  <div class="language-switcher">
    <button 
      v-for="locale in $i18n.locales" 
      :key="locale.code"
      :class="{ active: $i18n.locale === locale.code }"
      @click="switchLanguage(locale.code)"
    >
      {{ locale.name }}
    </button>
  </div>
</template>

<script>
export default {
  name: 'LanguageSwitcher',
  methods: {
    switchLanguage(locale) {
      this.$i18n.locale = locale
    }
  }
}
</script>

<style scoped>
.language-switcher {
  display: flex;
  gap: 10px;
}

button {
  padding: 5px 10px;
  border: 1px solid #ddd;
  background-color: #f0f0f0;
  border-radius: 4px;
  cursor: pointer;
}

button.active {
  background-color: #1890ff;
  color: white;
  border-color: #1890ff;
}

button:hover {
  background-color: #e0e0e0;
}

button.active:hover {
  background-color: #40a9ff;
}
</style>

路由国际化

@nuxtjs/i18n 模块提供了路由国际化功能,支持多种路由策略:

  1. prefix:为所有路由添加语言前缀
  2. prefix_except_default:只为非默认语言添加前缀
  3. prefix_and_default:为所有路由添加前缀,包括默认语言
  4. no_prefix:不添加语言前缀

示例:在 nuxt.config.js 中配置路由国际化

export default {
  i18n: {
    // 路由策略
    strategy: 'prefix_and_default',
    // 自定义路由
    pages: {
      about: {
        zh: '/about',
        en: '/about-us'
      },
      contact: {
        zh: '/contact',
        en: '/contact-us'
      }
    }
  }
}

日期、数字等格式化

Vue I18n 提供了日期、数字等格式化功能,你可以在组件中使用这些功能:

示例:日期格式化

<template>
  <div>
    <p>{{ $d(new Date(), 'short') }}</p>
    <p>{{ $d(new Date(), 'long') }}</p>
    <p>{{ $d(new Date(), 'custom') }}</p>
  </div>
</template>

<script>
export default {
  name: 'DateFormatExample'
}
</script>

示例:数字格式化

<template>
  <div>
    <p>{{ $n(123456.78) }}</p>
    <p>{{ $n(123456.78, 'currency') }}</p>
    <p>{{ $n(0.75, 'percentage') }}</p>
  </div>
</template>

<script>
export default {
  name: 'NumberFormatExample'
}
</script>

示例:在语言文件中配置格式化选项

// locales/zh-CN.js
export default {
  // 其他翻译...
  datetimeFormats: {
    short: {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    },
    long: {
      year: 'numeric',
      month: 'long',
      day: 'numeric',
      weekday: 'long',
      hour: 'numeric',
      minute: 'numeric'
    },
    custom: {
      year: 'numeric',
      month: '2-digit',
      day: '2-digit',
      hour: '2-digit',
      minute: '2-digit',
      second: '2-digit'
    }
  },
  numberFormats: {
    currency: {
      style: 'currency',
      currency: 'CNY',
      minimumFractionDigits: 2
    },
    percentage: {
      style: 'percent',
      minimumFractionDigits: 1
    },
    decimal: {
      style: 'decimal',
      minimumFractionDigits: 2,
      maximumFractionDigits: 2
    }
  }
}

实用案例分析

案例一:多语言博客网站

场景描述:创建一个支持多语言的博客网站,用户可以在不同语言之间切换,并且博客内容也会根据选择的语言显示。

实现方案

<template>
  <div class="blog-page">
    <!-- 语言切换器 -->
    <div class="language-switcher">
      <button 
        v-for="locale in $i18n.locales" 
        :key="locale.code"
        :class="{ active: $i18n.locale === locale.code }"
        @click="switchLanguage(locale.code)"
      >
        {{ locale.name }}
      </button>
    </div>

    <!-- 博客文章列表 -->
    <div class="blog-list">
      <div v-for="post in posts" :key="post.id" class="blog-post">
        <h2>{{ post.title }}</h2>
        <p class="post-meta">
          {{ $d(new Date(post.createdAt), 'long') }}
        </p>
        <p class="post-excerpt">{{ post.excerpt }}</p>
        <nuxt-link :to="`/blog/${post.slug}`">{{ $t('readMore') }}</nuxt-link>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'BlogPage',
  data() {
    return {
      posts: []
    }
  },
  async fetch() {
    // 根据当前语言获取博客文章
    const locale = this.$i18n.locale
    this.posts = await this.$axios.$get(`/api/blog/posts?locale=${locale}`)
  },
  methods: {
    switchLanguage(locale) {
      this.$i18n.locale = locale
      // 切换语言后重新获取数据
      this.fetch()
    }
  }
}
</script>

<style scoped>
.blog-page {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.language-switcher {
  display: flex;
  gap: 10px;
  margin-bottom: 30px;
}

button {
  padding: 5px 10px;
  border: 1px solid #ddd;
  background-color: #f0f0f0;
  border-radius: 4px;
  cursor: pointer;
}

button.active {
  background-color: #1890ff;
  color: white;
  border-color: #1890ff;
}

.blog-list {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.blog-post {
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 20px;
  background-color: #f9f9f9;
}

.post-meta {
  color: #666;
  font-size: 14px;
  margin-bottom: 10px;
}

.post-excerpt {
  line-height: 1.6;
  margin-bottom: 20px;
}

a {
  color: #1890ff;
  text-decoration: none;
}

a:hover {
  color: #40a9ff;
  text-decoration: underline;
}
</style>

案例二:多语言电商网站

场景描述:创建一个支持多语言的电商网站,用户可以在不同语言之间切换,并且产品信息、价格等也会根据选择的语言显示。

实现方案

<template>
  <div class="product-page">
    <!-- 语言切换器 -->
    <div class="language-switcher">
      <button 
        v-for="locale in $i18n.locales" 
        :key="locale.code"
        :class="{ active: $i18n.locale === locale.code }"
        @click="switchLanguage(locale.code)"
      >
        {{ locale.name }}
      </button>
    </div>

    <!-- 产品信息 -->
    <div class="product-info">
      <h1>{{ product.name }}</h1>
      <div class="price">{{ $n(product.price, 'currency') }}</div>
      <p class="description">{{ product.description }}</p>
      <button class="add-to-cart">{{ $t('addToCart') }}</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ProductPage',
  data() {
    return {
      product: {}
    }
  },
  async fetch() {
    // 根据当前语言获取产品信息
    const locale = this.$i18n.locale
    const productId = this.$route.params.id
    this.product = await this.$axios.$get(`/api/products/${productId}?locale=${locale}`)
  },
  methods: {
    switchLanguage(locale) {
      this.$i18n.locale = locale
      // 切换语言后重新获取数据
      this.fetch()
    }
  }
}
</script>

<style scoped>
.product-page {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.language-switcher {
  display: flex;
  gap: 10px;
  margin-bottom: 30px;
}

button {
  padding: 5px 10px;
  border: 1px solid #ddd;
  background-color: #f0f0f0;
  border-radius: 4px;
  cursor: pointer;
}

button.active {
  background-color: #1890ff;
  color: white;
  border-color: #1890ff;
}

.product-info {
  display: flex;
  gap: 40px;
}

.price {
  font-size: 24px;
  font-weight: bold;
  color: #ff4d4f;
  margin: 20px 0;
}

.description {
  line-height: 1.6;
  margin-bottom: 30px;
}

.add-to-cart {
  background-color: #1890ff;
  color: white;
  border: none;
  padding: 12px 24px;
  font-size: 16px;
  border-radius: 4px;
  cursor: pointer;
}

.add-to-cart:hover {
  background-color: #40a9ff;
}
</style>

国际化最佳实践

1. 语言文件管理

  • 模块化语言文件:将语言文件按照功能模块进行拆分
  • 使用命名空间:使用命名空间来组织翻译键
  • 保持语言文件同步:确保所有语言文件包含相同的翻译键
  • 使用工具管理翻译:考虑使用专业的翻译管理工具

2. 性能优化

  • 延迟加载语言文件:只加载当前语言的文件
  • 使用缓存:缓存翻译结果
  • 避免频繁切换语言:减少语言切换的次数

3. 用户体验

  • 保存用户语言偏好:使用 cookie 或 localStorage 保存用户的语言选择
  • 提供明确的语言切换方式:在页面顶部或导航栏提供语言切换器
  • 保持 UI 一致性:确保不同语言版本的 UI 布局一致
  • 考虑文本长度:不同语言的文本长度可能不同,需要考虑 UI 布局的适应性

4. 内容管理

  • 支持多语言内容:确保后端 API 支持多语言内容
  • 提供翻译工具:为内容管理员提供翻译工具
  • 考虑 RTL 语言:如果支持阿拉伯语等 RTL(从右到左)语言,需要特别处理

5. 测试

  • 测试所有语言版本:确保所有语言版本都能正常工作
  • 检查翻译质量:确保翻译准确、自然
  • 测试边界情况:测试文本长度、日期格式等边界情况

总结

国际化是现代 Web 应用的重要功能之一,它可以帮助你的应用触达全球用户。通过本章节的学习,你已经掌握了:

  • 在 Nuxt.js 中集成和配置 @nuxtjs/i18n 模块
  • 创建和管理语言文件
  • 实现动态语言切换功能
  • 配置路由国际化
  • 使用日期、数字等格式化功能
  • 国际化的最佳实践

在实际开发中,你应该根据应用的具体需求,选择合适的国际化方案,并不断优化和完善国际化功能。良好的国际化设计可以提升用户体验,扩大应用的受众范围,为你的应用带来更多的用户。

« 上一篇 Nuxt.js 错误处理机制 下一篇 » Nuxt.js 模块化开发