我最近参与了一个令人兴奋的项目,涉及创建一个能够在不同语言之间切换以吸引更广泛受众的网站。这使我更好地理解了“本地化”这一概念,通常涉及调整内容,使其对不同语言和地区的用户具有相关性、可访问性和亲和力。

本地化不仅仅是翻译文字,更是创造一种让用户无论使用何种语言都感觉宾至如归的体验。例如,像亚马逊这样的全球平台使语言切换如此流畅,以至于几乎感觉像是魔法般的。除了提升用户体验,这一功能还在扩大业务范围、与全球客户建立更紧密联系方面发挥着至关重要的作用。

目录

什么是i18n,为什么要使用它?

i18n,即国际化(internationalization)的缩写,意味着一个应用程序支持多种语言。”i18n”源自于在”internationalization”中第一个”i”和最后一个”n”之间有18个字母这一事实。这一切都是为了使您的应用程序能够适应全球受众,通过处理文本翻译、日期和数字格式化、管理货币以及适应地区习俗来实现。

通过启用国际化,您的应用程序不仅成为一个工具,更是一个能直接契合用户偏好和文化的包容平台。

让我们开始吧

我们将创建一个非常简单的演示多语言Web应用程序,其中包含一个暗黑模式切换功能,以演示如何实现这一概念。

先决条件

  1. React的基础知识 – 您应该了解如何创建组件、管理状态以及使用像useStateuseEffect这样的Hooks。如果您对React还不熟悉,我建议您从官方React文档开始学习,打下坚实的基础

  2. 国际化概念的熟悉度 – 了解国际化(i18n)的基本知识及其重要性将为您的项目提供背景。本文的早期部分涵盖了基本要素。

  3. Tailwind CSS – 我们将使用 Tailwind CSS 进行样式设计。这是一个以工具优先的 CSS 框架,帮助您在不离开 HTML 的情况下构建现代响应式设计。如果您不熟悉,可以查看 Tailwind 的文档

  4. Node.js – 确保您的系统上安装了 Node.js 以处理依赖项。您可以从 Node.js 下载最新版本。

  5. 包管理器 – 需要使用 npm(随 Node.js 一起包含)或 yarn 来管理项目依赖项

我们将使用的工具

  1. 代码编辑器

  2. 本地化库: react-i18next

  3. 图标库: hero-icons

步骤 1:如何设置项目

初始化项目

使用 Vite 快速设置:

npm create vite@latest multilingual-demo

按照终端中显示的说明进行操作,选择 React 和 TypeScript 进行开发,如下图所示:

安装依赖项

在终端中运行以下命令以安装该项目所需的依赖项:

npm install i18next react-i18next i18next-browser-languagedetector i18next-http-backend heroicons 
npm install tailwindcss postcss autoprefixer  
npx tailwindcss init

配置 TailwindCSS

更新 tailwind.config.ts 文件:

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: ["./src/**/*.{js,jsx,ts,tsx}"],
  darkMode: "class", // 为了我们的暗模式功能
  theme: {
    container: {
      center: true,
      padding: "1.25rem",
      screens: {
        sm: "1200px",
      },
    },
    extend: {},
  },
  plugins: [],
};

将 TailwindCSS 添加到 src/index.css

@tailwind base;  
@tailwind components;  
@tailwind utilities;

步骤 2:如何使用 i18next 设置国际化

初始化 i18next

src 文件夹中创建一个 i18n.tsx 文件并配置 i18next:

import i18next from "i18next";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";

i18next.use(LanguageDetector).use(initReactI18next).use(Backend).init({
  returnObjects: true,
  fallbackLng: "en", // 如果所选语言未配置,则回退的语言
  debug: true, // 为了让我们看到错误
  //   lng: "en", // 默认语言为英语
});

让我们快速看一下这个文件的内容,因为它在启用翻译功能中发挥了关键作用。这个文件负责设置翻译过程的核心,并确保语言切换功能在您的应用中顺利工作。

  • i18next:我们用于翻译的核心国际化库。

  • LanguageDetector:根据浏览器设置自动帮助我们检测用户的首选语言。

  • initReactI18next:负责将 i18next 插件与 React 集成,并提供像 useTranslation Hook 和其他实用工具的 Hooks。

  • Backend:从外部源动态获取翻译数据。在这种情况下,我们将使用 JSON 文件。

将此文件导入到 main.tsx 文件中:

//main.tsx

import React, { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
import "./i18n.tsx";  //在这里导入

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <React.Suspense fallback="loading">
      <App />
    </React.Suspense>
  </StrictMode>
);

创建翻译文件

public/locales 目录中,为每种语言创建子文件夹(例如,enfr)并包含 translation.json 文件:

en/translation.json

{
    "greeting": "Welcome to the Language Playground",
    "detail": {
        "line1": "Did you know that over 7,000 languages are spoken worldwide?",
        "line2": "This Playground demonstrates how web applications can support users in multiple languages, making them accessible and inclusive to people from different backgrounds."
    }
}

fr/translation.json

{
    "greeting": "Bienvenue sur le terrain de jeu linguistique",
    "detail": {
        "line1": "Saviez-vous que plus de 7 000 langues sont parlées dans le monde ?",
        "line2": "Ce terrain de jeu démontre comment les applications web peuvent prendre en charge les utilisateurs dans plusieurs langues, les rendant accessibles et inclusives aux personnes de différents horizons."
    }
}

在这里,您可以添加尽可能多的语言及其翻译文件,这些文件将提供给 i18next。请注意,JSON 文件中的键与在网站上显示时使用的引用相同。

步骤 3:如何构建组件

src 目录中创建一个 components 文件夹,并添加以下组件:

语言选择器

创建LanguageSelector组件 – 包含一个select元素,帮助用户动态切换语言:

import { useEffect, useState } from "react";
import i18next from "i18next";
import { useTranslation } from "react-i18next";

type languageOption = { language: string; code: string };

const languageOptions: languageOption[] = [
  {
    language: "English",
    code: "en",
  },
  { language: "French", code: "fr" },
  { language: "German", code: "de" },
  { language: "Spanish", code: "es" },
  { language: "Arabic", code: "ar" },
  { language: "Yoruba", code: "yo" },
];

const LanguageSelector = () => {
  // 从i18next检测到的或默认语言设置初始语言
  const [language, setLanguage] = useState(i18next.language);

  const { i18n } = useTranslation();

  const handleLanguageChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const selectedLanguage = e.target.value;
    setLanguage(selectedLanguage);
    i18next.changeLanguage(selectedLanguage); // 在i18next中更新语言
  };

  useEffect(() => {
    document.body.dir = i18n.dir(); // 将页面设置为从左到右或从右到左
  }, [i18n, i18n.language]);

  return (
    <select
      id="language"
      value={language}
      onChange={handleLanguageChange}
      className="p-2 border border-gray-300 rounded-md shadow-sm focus:border-indigo-500 focus:ring focus:ring-indigo-200 focus:ring-opacity-50
        dark:bg-gray-800 dark:border-gray-600 dark:text-gray-200 dark:focus:border-indigo-400 dark:focus:ring-indigo-700 dark:focus:ring-opacity-50"
    >
      {languageOptions.map(({ language, code }, key) => (
        <option value={code} key={key}>
          {language}
        </option>
      ))}
    </select>
  );
};

export default LanguageSelector;
  • 使用i18next检测到的语言或默认设置的语言初始化语言。

  • useTranslation钩子公开了来自实例,用于与国际化设置进行交互。

  • handleLanguageChange函数用于更新用户选择的语言。当用户从下拉菜单中选择新语言时触发。

实现文本方向

HTML中的dir属性是确保网络应用程序具有可访问性和包容性的关键功能,特别是在处理文本方向不同的语言时。例如:

  • 从左到右(LTR):大多数语言,包括英语、法语和西班牙语,都遵循这个方向。

    从右到左(RTL):阿拉伯语和希伯来语等语言需要将文本对齐和布局翻转,以保持可读性和文化背景。

为了在我们的应用中实现这一点,我们将document.body.dir设置为从中获取的dir,同时使用useEffect钩子监听语言选择的更改

深色模式切换

创建DarkModeToggle组件,以便用户可以在浅色模式和深色模式之间切换。

import { useEffect, useState } from "react";
import { SunIcon, MoonIcon } from "@heroicons/react/solid";

const DarkModeToggle = () => {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    // 在首次加载时检查本地存储或系统偏好
    const isDark =
      localStorage.getItem("theme") === "dark" ||
      (!localStorage.getItem("theme") &&
        window.matchMedia("(prefers-color-scheme: dark)").matches);
    setDarkMode(isDark);
    document.documentElement.classList.toggle("dark", isDark);
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.documentElement.classList.toggle("dark", !darkMode);
    localStorage.setItem("theme", !darkMode ? "dark" : "light");
  };

  return (
    <button
      aria-label="Toggle dark mode"
      onClick={toggleDarkMode}
      className="p-1 rounded"
    >
      {darkMode ? (
        <SunIcon
          className="w-6 h-6 text-yellow-500 "
          onClick={toggleDarkMode}
        />
      ) : (
        <MoonIcon className="w-6 h-6 text-gray-900 " onClick={toggleDarkMode} />
      )}
    </button>
  );
};

export default DarkModeToggle;

标题组件

Header组件作为DarkModeTogglelanguageSelector组件的父组件。

import DarkModeToggle from "./DarkModeToggle";
import LanguageSelector from "./LanguageSelector";

const Header = () => {
  return (
    <header className="container flex justify-between">
      <DarkModeToggle />
      <LanguageSelector />
    </header>
  );
};

export default Header;

第四步:主应用程序组件

src/app文件中包括以下内容:

import { useTranslation } from "react-i18next";
import Header from "./components/Header";

const App = () => {
  const { t } = useTranslation();

  const line1 = t("detail.line1");
  const line2 = t("detail.line2");

  return (
    <div className="h-[100vh] bg-white text-black dark:bg-gray-900 dark:text-white py-8">
      <Header />
      <div className="container text-center max-w-2xl mt-28">
        <h1 className="text-4xl font-bold">{t("greeting")}</h1>
        <p className="mt-8">{line1}</p>
        <p className="mt-2">{line2}</p>
      </div>
    </div>
  );
};

export default App;
  • 来自react-i18nextuseTranslation Hook暴露了t函数,用于获取翻译文本。

  • 它基于来自您的翻译文件的键(例如en.jsonfr.json)来获取翻译字符串。

通过遵循这些步骤,您的应用现在应该已经完全集成了翻译,功能正常。这是我们应用的最终结果:

查看在线演示和源代码在GitHub

结论

创建允许用户选择其首选语言的网站不仅仅是技术上的成就,更是朝着使网络更具包容性和友好迈出的一步。

通过将国际化(i18n)与React-i18next等工具以及使用Tailwind CSS进行样式设置相结合,您可以构建灵活、用户友好且适合全球受众的应用程序。

在这个项目中,我们介绍了设置i18n、添加语言切换器以及包含“深色模式”以提高可用性的步骤。

参考资料

https://react.i18next.com/

https://www.youtube.com/watch?v=dltHi9GWMIo