字符串

如何使用 useGT 和 getGT 为纯文本字符串实现国际化

字符串翻译无需使用 JSX 即可直接获取文本译文,尤其适用于属性、对象字段以及纯文本值。在同步组件中使用 useGT,在异步组件中使用 getGT

同步与异步用法

  • 同步组件:用于 React 组件的 useGT 钩子(hook)
  • 异步组件:用于异步组件的 getGT 异步函数

快速上手

同步式组件

import { useGT } from 'gt-next';

function MyComponent() {
  const t = useGT();
  return (
    <input 
      placeholder={t('请输入您的邮箱')}
      title={t('邮箱地址输入框')}
    />
  );
}

异步组件

import { getGT } from 'gt-next/server';

async function MyServerComponent() {
  const t = await getGT();
  return (
    <input 
      placeholder={t('请输入您的邮箱')}
      title={t('邮箱地址输入框')}
    />
  );
}

何时使用字符串翻译

当你需要纯文本而非 JSX 时,字符串翻译是最佳选择:

HTML 属性

const t = useGT();

<input 
  placeholder={t('搜索产品...')}
  aria-label={t('产品搜索输入框')}
  title={t('输入以搜索我们的产品目录')}
/>

对象的属性

const t = useGT();

const user = {
  name: 'John',
  role: 'admin',
  bio: t('拥有5年React开发经验的资深软件工程师'),
  status: t('目前可接项目')
};

配置与常量

const t = useGT();

const navigationItems = [
  { label: t('首页'), href: '/' },
  { label: t('产品'), href: '/products' },
  { label: t('联系我们'), href: '/contact' }
];

何时改用 <T>

<T> 组件 用于 JSX 内容:

// ✅ 对 JSX 内容使用 <T>
<T><p>欢迎来到<strong>我们的商店</strong>!</p></T>

// ✅ 对纯文本使用字符串翻译
<input placeholder={t('搜索产品')} />

使用变量

基本变量

将占位符替换为动态值:

const t = useGT();
const itemCount = 5;

// 带占位符的字符串
const message = t('您的购物车中有 {count} 件商品', { count: itemCount });
// 结果:"您的购物车中有 5 件商品"

多个变量

const t = useGT();
const order = { id: 'ORD-123', total: 99.99, date: '2024-01-15' };

const confirmation = t(
  '订单 {orderId} 金额 ${total} 已于 {date} 下单',
  { 
    orderId: order.id, 
    total: order.total, 
    date: order.date 
  }
);

ICU 消息格式

如需高级格式化,请使用 ICU 语法:

const t = useGT();
translate('购物车中有{count, plural, =0 {没有商品} =1 {一件商品} other {{count}件商品}}', { count: 10 });

参阅Unicode 文档了解更多 ICU 消息格式相关内容。

示例

表单输入项

import { useGT } from 'gt-next';

function ContactForm() {
  const t = useGT();
  
  return (
    <form>
      <input 
        type="email"
        placeholder={t('请输入您的邮箱地址')}
        aria-label={t('邮箱输入框')}
      />
      <textarea 
        placeholder={t('请告诉我们您的项目情况...')}
        aria-label={t('项目描述')}
      />
      <button type="submit">
        {t('发送消息')}
      </button>
    </form>
  );
}

导航栏

import { useGT } from 'gt-next';

function Navigation() {
  const t = useGT();
  
  const menuItems = [
    { label: t('首页'), href: '/', icon: 'home' },
    { label: t('关于我们'), href: '/about', icon: 'info' },
    { label: t('服务'), href: '/services', icon: 'briefcase' },
    { label: t('联系我们'), href: '/contact', icon: 'mail' }
  ];

  return (
    <nav>
      {menuItems.map((item) => (
        <a key={item.href} href={item.href} title={item.label}>
          <Icon name={item.icon} />
          {item.label}
        </a>
      ))}
    </nav>
  );
}

动态内容工厂

// utils/productData.js
export function getProductMessages(t) {
  return {
    categories: [
      { id: 'electronics', name: t('电子产品') },
      { id: 'clothing', name: t('服装') },
      { id: 'books', name: t('图书') }
    ],
    statusMessages: {
      available: t('现货供应,即刻发货'),
      backordered: t('暂时缺货 - 2-3周内发货'),  
      discontinued: t('该商品已停产')
    },
    errors: {
      notFound: t('商品未找到'),
      outOfStock: t('抱歉,该商品目前缺货')
    }
  };
}

// components/ProductCard.jsx
import { useGT } from 'gt-next';
import { getProductMessages } from '../utils/productData';

function ProductCard({ product }) {
  const t = useGT();
  const messages = getProductMessages(t);
  
  return (
    <div>
      <h3>{product.name}</h3>
      <p>{messages.statusMessages[product.status]}</p>
      <span>{messages.categories.find(c => c.id === product.categoryId)?.name}</span>
    </div>
  );
}

带有元数据的服务器组件

import { getGT } from 'gt-next/server';

export async function generateMetadata({ params }) {
  const t = await getGT();
  
  return {
    title: t('产品目录 - 找到您所需的产品'),
    description: t('浏览我们丰富的优质产品系列'),
    openGraph: {
      title: t('选购我们的产品'),
      description: t('发现热门商品的超值优惠')
    }
  };
}

export default async function ProductPage() {
  const t = await getGT();
  
  return (
    <div>
      <h1>{t('精选产品')}</h1>
      <p>{t('查看我们最新最受欢迎的商品')}</p>
    </div>
  );
}

常见问题

运行时的动态内容

字符串必须在构建阶段确定——无法翻译运行时的动态内容:

// ❌ 动态内容无法正常工作
function MyComponent() {
  const [userMessage, setUserMessage] = useState('');
  const t = useGT();
  
  return <p>{t(userMessage)}</p>; // 这会失败
}

// ✅ 使用预定义字符串
function MyComponent() {
  const [messageType, setMessageType] = useState('welcome');
  const t = useGT();
  
  const messages = {
    welcome: t('欢迎使用我们的应用!'),
    goodbye: t('感谢您的访问!')
  };
  
  return <p>{messages[messageType]}</p>;
}

Hook 规则违规

使用 useGT 时请遵循 React Hook 规则:

// ❌ 不要有条件地调用 hooks
function MyComponent({ showMessage }) {
  if (showMessage) {
    const t = useGT(); // 违反 Hook 规则
    return <p>{t('你好!')}</p>;
  }
  return null;
}

// ✅ 始终在顶层调用 hooks
function MyComponent({ showMessage }) {
  const t = useGT();
  
  if (showMessage) {
    return <p>{t('你好!')}</p>;
  }
  return null;
}

同步与异步常见混淆

为你的组件类型选择正确的函数:

// ❌ 错误:在异步组件中使用 useGT
export default async function AsyncComponent() {
  const t = useGT(); // 这样不起作用
  return <p>{t('Hello')}</p>;
}

// ✅ 正确:在异步组件中使用 getGT  
export default async function AsyncComponent() {
  const t = await getGT();
  return <p>{t('Hello')}</p>;
}

// ✅ 正确:在同步组件中使用 useGT
export default function SyncComponent() {
  const t = useGT();
  return <p>{t('Hello')}</p>;
}

如需在运行时翻译的动态内容,请参见动态内容指南

后续步骤

这份指南怎么样?

字符串