本教程深入探讨 LinguiJS t 宏在 react 应用中无法正确翻译的常见问题。文章解释了 Trans 组件和 t 宏的工作原理差异,并提供了通过 useLingui 钩子和 msg 宏(defineMessage 的别名)结合 i18n._ 方法实现正确国际化翻译的专业解决方案和示例代码。
LinguiJS 国际化基础回顾
linguijs 是一个功能强大的国际化库,它通过宏和 react 组件提供了一套完整的解决方案。在 react 应用中,我们通常会遇到两种主要的翻译方式:
- <Trans> 组件:这是一个 React 组件,用于在 JSX 中直接包裹需要翻译的文本。它会自动订阅语言上下文的变化,并在语言切换时重新渲染其内容。例如:<Trans>hello world</Trans>。
- t 宏(或 msg 宏):这些是 Babel/typescript 宏,用于在编译时从源代码中提取可翻译的消息。它们的主要作用是生成一个 MessageDescriptor 对象,该对象包含了消息的 ID、默认信息等元数据。例如:thello react或 `msg`hello react.
问题剖析:t 宏为何不直接生效?
在提供的场景中,<Trans>hello world</Trans> 能够正确翻译,而 DataComponent data={thello react} /> 中的 t 宏却未能生效。这背后的核心原因在于 t 宏(或 msg 宏)的运行时行为与 React 组件的生命周期及上下文管理机制。
当您在 JSX 中直接使用 <Trans> 组件时,@lingui/react 包内部会处理语言上下文的订阅,确保组件在语言变化时重新渲染并显示正确的翻译。然而,t 宏在编译时会将模板字符串转换为一个 MessageDescriptor 对象。例如,thello react`在编译后可能类似于{ id: ‘hello react’, message: ‘hello react’ }`。
当您将 thello react`的结果作为dataprop 传递给DataComponent时,DataComponent接收到的是一个静态的MessageDescriptor对象,而不是一个已翻译的字符串。更重要的是,DataComponent本身并没有订阅 LinguiJS 的语言上下文,因此它无法感知当前语言环境的变化,也无法主动调用翻译函数来解析这个MessageDescriptor`。
简而言之,t 宏负责定义和提取消息,但实际的翻译(将 MessageDescriptor 转换为目标语言字符串)需要在运行时通过 LinguiJS 提供的 i18n 实例来完成。
解决方案:结合 useLingui 和 msg 宏
为了解决这个问题,我们需要确保:
- 传递给组件的是一个可供翻译的 MessageDescriptor。
- 接收该 MessageDescriptor 的组件能够访问 LinguiJS 的 i18n 实例,并在语言变化时触发翻译。
LinguiJS 提供了 useLingui 钩子来解决第二个问题,而 msg 宏(defineMessage 的别名)则非常适合用于生成可传递的 MessageDescriptor。
1. 使用 msg 宏生成 MessageDescriptor
将 index.tsx 中 t 宏的用法替换为 msg 宏。msg 宏(等同于 defineMessage)明确地表示我们正在定义一个消息描述符,而不是期望它立即进行翻译。
index.tsx 修改示例:
import React from "react"; import ReactDOM from "react-dom/client"; import reportWebVitals from "./reportWebVitals"; import { StoreProvider } from "./components/store-provider"; import createStore from "./store"; import { LanguageProvider } from "./components/language-provider"; import { Langswitcher } from "./components/lang-switcher"; import { DataComponent } from "./components/DataComponent"; import { Trans } from "@lingui/macro"; import { msg } from "@lingui/macro"; // 引入 msg 宏 const store = createStore(); const root = ReactDOM.createRoot( document.getElementById("root") as htmlElement ); root.render( <React.StrictMode> <StoreProvider store={store}> <LanguageProvider> <LangSwitcher /> <p> <Trans>hello world</Trans> </p> {/* 将 t`hello react` 替换为 msg`hello react` */} <DataComponent data={msg`hello react`} /> </LanguageProvider> </StoreProvider> </React.StrictMode> ); reportWebVitals();
2. 在 DataComponent 中使用 useLingui 钩子进行翻译
useLingui 钩子允许 React 函数组件访问当前的 i18n 实例,并订阅语言上下文的变化。当语言切换时,使用 useLingui 的组件会自动重新渲染。通过 i18n._(messageDescriptor) 方法,我们可以将传入的 MessageDescriptor 翻译成当前语言的字符串。
DataComponent.tsx 修改示例:
import { MessageDescriptor } from "@lingui/core"; import { useLingui } from "@lingui/react"; // 引入 useLingui 钩子 import React from "react"; type props = { data: MessageDescriptor; // 明确指定 data 为 MessageDescriptor 类型 }; export const DataComponent: React.FC<props> = ({ data }) => { const { i18n } = useLingui(); // 使用 useLingui 钩子获取 i18n 实例 // 使用 i18n._ 方法翻译 MessageDescriptor return <p>{i18n._(data)}</p>; };
经过这些修改,index.tsx 将 msg 宏生成的 MessageDescriptor 传递给 DataComponent。DataComponent 则通过 useLingui 钩子获取 i18n 实例,并在每次渲染(包括语言切换导致的重新渲染)时,使用 i18n._(data) 将 MessageDescriptor 翻译成当前语言的字符串,从而实现正确的国际化显示。
关键概念与最佳实践
- Trans 组件:适用于在 JSX 中直接嵌入可翻译文本的场景,它会自动处理上下文和重新渲染。
- useLingui 钩子:是 React 函数组件中进行动态翻译的核心。当你需要在组件内部基于逻辑翻译文本,或者需要将 MessageDescriptor 翻译成字符串时,都应该使用 useLingui 来获取 i18n 实例。
- msg 宏(defineMessage):主要用于定义一个 MessageDescriptor 对象。当你想在组件之间传递一个“待翻译”的消息,或者在非 React 组件(如 redux reducer、工具函数)中定义消息时,它非常有用。
- MessageDescriptor:这是 LinguiJS 中表示一个可翻译消息的通用结构。它包含了消息的 ID、默认文本、上下文等信息。t 和 msg 宏都会在编译时生成这种对象。
- i18n._(messageDescriptor):这是 i18n 实例上的核心翻译方法,它接收一个 MessageDescriptor 并返回当前语言环境下的翻译字符串。
理解这些概念对于在 LinguiJS 和 React 中构建健壮的国际化应用至关重要。避免直接将 t 宏的输出(一个 MessageDescriptor)视为已翻译的字符串,尤其是在跨组件传递时。始终确保在需要显示翻译文本的地方,有 i18n 实例可用,并调用其翻译方法。
总结
lingui 库的 t 宏在 React 应用中不生效的问题,通常是由于未能正确地在运行时将 MessageDescriptor 转换为翻译字符串。解决方案是利用 msg 宏生成 MessageDescriptor,并在需要显示翻译文本的 React 组件中,通过 useLingui 钩子获取 i18n 实例,然后使用 i18n._() 方法进行实际的翻译。这种模式确保了翻译的动态性和对语言切换的响应性,从而实现了 LinguiJS 在 React 应用中的完整国际化能力。
评论(已关闭)
评论已关闭