如何使用国际化 (i18n) 构建多语言 React 应用

发布: (2025年12月8日 GMT+8 15:03)
5 min read
原文: Dev.to

Source: Dev.to

什么是国际化 (i18n)?

国际化 (i18n) 是指在设计和开发应用时,使其能够轻松适配不同语言和地区,而无需进行工程上的更改的过程。

i18n 这个缩写来源于单词 “internationalization” 中首字母 i 与末字母 n 之间恰好有 18 个字母。

i18n 的关键要素

  • 翻译 – 将文本字符串转换为不同语言
  • 本地化 – 为特定地区调整内容(日期、数字、货币)
  • 复数化 – 正确处理每种语言的单数/复数形式

为什么 i18n 对你的 React 应用很重要

  • 全球覆盖 – 用用户的母语触达他们,打开新市场
  • 更佳用户体验 – 用户更倾向于使用自己语言的应用(研究显示 75 % 的用户更愿意用母语购买产品)
  • 竞争优势 – 许多应用缺乏完善的国际化,这为你提供了优势
  • 法律要求 – 某些国家要求应用提供本地语言版本
  • SEO 效果 – 多语言内容提升不同地区的搜索排名
  • 转化率提升 – 当内容使用用户语言时,用户更可能完成操作

前置条件

  • 基础的 React 与 React Hooks 知识
  • 已在机器上安装 Node.js 与 npm
  • 一个代码编辑器,如 VS Code

项目初始化

使用 Vite 创建一个新的 React 应用:

npm create vite@latest react-i18n-demo -- --template react
cd react-i18n-demo

安装所需的 i18n 包:

npm install i18next react-i18next i18next-browser-languagedetector

启动开发服务器:

npm run dev

你的应用现在应该已经在 (此处会显示地址) 运行。

注意: 本教程不使用任何样式库。为简化起见,样式写在内联,但在真实项目中应将 CSS 拆分到独立文件,或使用 Tailwind CSS 等框架。

替换默认 CSS

/* src/index.css */
:root {
  font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
  line-height: 1.5;
  font-weight: 400;
}

问题:硬编码字符串

典型的 React 组件中硬编码了英文字符串:

import { useState } from 'react';

function ProductCard({ product }) {
  return (
    <>
      {/* ❌ Hardcoded English strings */}
      <h3>{product.name}</h3>

      <p>Price: ${product.price}</p>

      <p>In stock: {product.stock} items</p>

      {product.stock === 0 && (
        <p>Out of stock</p>
      )}

      {product.stock === 1 && (
        <p>Only 1 item left!</p>
      )}

      {product.stock > 1 && (
        <p>In stock</p>
      )}

      <button>Add to cart</button>
    </>
  );
}

export default ProductCard;

这种写法的问题

  • 没有翻译支持 – 所有文字仅为英文
  • 维护困难 – 修改文字需要编辑多个组件
  • 缺少复数化 – “1 item” 与 “2 items” 写死了
  • 没有本地化 – 日期和数字格式固定
  • 难以扩展 – 添加新语言必须修改每个组件

解决方案:react-i18next

react-i18next 是 React 国际化最流行、最强大的方案,提供:

  • 简单的翻译 API
  • 自动语言检测
  • 复数化支持
  • 嵌套翻译
  • 日期、数字、货币格式化
  • 翻译文件的懒加载
  • TypeScript 支持

如何创建翻译文件

utils/locales 文件夹下,以独立的 JavaScript 文件组织翻译:

utils/
  locales/
    en/
      translation.js
    es/
      translation.js
    fr/
      translation.js

英文 (utils/locales/en/translation.js)

export default {
  en: {
    translation: {
      "product.price": "Price",
      "product.inStock": "In stock: {{count}} items",
      "product.outOfStock": "Out of stock",
      "product.onlyOneLeft": "Only 1 item left!",
      "product.addToCart": "Add to cart",

      "profile.welcome": "Welcome, {{name}}!",
      "profile.memberSince": "Member since: {{date}}",
      "profile.loyaltyPoints": "You have {{points}} loyalty points",
      "profile.orders_zero": "You have no orders",
      "profile.orders_one": "You have 1 order",
      "profile.orders_other": "You have {{count}} orders",
      "profile.editProfile": "Edit profile",
    },
  },
};

西班牙文 (utils/locales/es/translation.js)

export default {
  es: {
    translation: {
      "product.price": "Precio",
      "product.inStock": "En stock: {{count}} artículos",
      "product.outOfStock": "Agotado",
      "product.onlyOneLeft": "¡Solo queda 1 artículo!",
      "product.addToCart": "Añadir al carrito",

      "profile.welcome": "¡Bienvenido, {{name}}!",
      "profile.memberSince": "Miembro desde: {{date}}",
      "profile.loyaltyPoints": "Tienes {{points}} puntos de fidelidad",
      "profile.orders_zero": "No tienes pedidos",
      "profile.orders_one": "Tienes 1 pedido",
      "profile.orders_other": "Tienes {{count}} pedidos",
      "profile.editProfile": "Editar perfil",
    },
  },
};

法文 (utils/locales/fr/translation.js)

export default {
  fr: {
    translation: {
      "product.price": "Prix",
      "product.inStock": "En stock: {{count}} articles",
      "product.outOfStock": "Rupture de stock",
      "product.onlyOneLeft": "Plus qu'un article !",
      "product.addToCart": "Ajouter au panier",

      "profile.welcome": "Bienvenue, {{name}} !",
      "profile.memberSince": "Membre depuis : {{date}}",
      "profile.loyaltyPoints": "Vous avez {{points}} points de fidélité",
      "profile.orders_zero": "Vous n'avez aucune commande",
      "profile.orders_one": "Vous avez 1 commande",
      "profile.orders_other": "Vous avez {{count}} commandes",
      "profile.editProfile": "Modifier le profil",
    },
  },
};
Back to Blog

相关文章

阅读更多 »