Хватит оборачивать строки в вызовы функций
Проблема с t("Hello, {name}", { name })
Если вам когда-нибудь приходилось интернационализировать React-приложение, вы наверняка писали что-то вроде этого:
const gt = useGT();
return <p>{gt("Hello, {name}! You have {count} items.", { name, count })}</p>;Это работает. Но выглядит громоздко. Вы вручную пишете синтаксис ICU MessageFormat, дублируете каждое имя переменной в объекте параметров и вытаскиваете t из Hook, которому нужен контекст React. Для вещи, которая должна быть всего лишь тонкой прослойкой над вашими существующими строками, это слишком много лишней возни.
И дальше только хуже. Как только вам нужен перевод вне React-компонента — во вспомогательной функции, обработчике событий, серверном действии, — приходится искать обходные пути, потому что Hook там не работают.
Шаблонные литералы должны работать как есть
Вот как выглядит тот же код с макросом t:
import { t } from "gt-react/browser";
return <p>{t`Hello, ${name}! You have ${count} items.`}</p>;Вот и всё. Стандартный синтаксис шаблонных литералов JavaScript. Никаких заполнителей ICU, никакого объекта параметров, никакого Hook, никакого провайдера контекста. Вы пишете строку так же, как любую шаблонную строку, а всё остальное берёт на себя компилятор.
На этапе сборки компилятор GT преобразует:
t`Hello, ${name}!`в:
t("Hello, {0}!", { "0": name })Тегированный шаблонный литерал — это просто синтаксический сахар: поведение во время выполнения полностью идентично вызову t() со строкой. Но для разработчика это гораздо удобнее.
Больше никакой зависимости от контекста React
Главное изменение здесь — не синтаксис, а архитектура. Функция t, экспортируемая из gt-react/browser, не использует контекст React. Ей не нужен Hook. Её не нужно вызывать внутри компонента.
Это значит, что вы можете использовать t в:
- Обработчиках событий:
onClick={() => alert(tSaved!)} - Вспомогательных функциях:
function formatError(code) { return tError: $} - Константах и конфигурации:
const LABELS = { save: tSave, cancel: tCancel} - Везде, где JavaScript выполняется на клиенте
Старый подход — useGT(), возвращающий функцию, привязанную к контексту React, — был узким местом. Он вынуждал завязывать перевод на React, хотя на самом деле речь идёт просто о строках.
Глобальная регистрация
Если вы не хотите импортировать t в каждый файл, можно зарегистрировать его глобально:
// В точке входа вашего приложения
import "gt-react/macros";Это задаёт globalThis.t, благодаря чему тегированный шаблонный литерал становится доступен везде без импорта. Компилятор достаточно умён, чтобы это распознать: если t уже импортирован из источника GT, он не добавит дублирующий импорт. Если же нет и вы использовали t как тегированный шаблонный литерал, компилятор сам добавит импорт.
Конкатенация тоже поддерживается
Раскрытие макроса не ограничивается тегированными шаблонными литералами. Оно также поддерживает шаблонные литералы, передаваемые в качестве аргументов, и конкатенацию строк:
// Шаблонный литерал как аргумент — также преобразуется
t(`Welcome back, ${user}`)
// Конкатенация строк — также преобразуется
t("Hello, " + name + "! Welcome.")Оба варианта приводятся к одному и тому же вызову t("...", { ... }) на этапе сборки.
Начало работы
1. Установите последнюю версию gt-react
npm install gt-react@latest2. Используйте макрос t
Либо импортируйте его напрямую:
import { t } from "gt-react/browser";
export function Greeting({ name }) {
return <p>{t`Hello, ${name}!`}</p>;
}Или зарегистрируйте глобально и обойдитесь без импортов:
// app/layout.tsx или точка входа
import "gt-react/macros";// В любом месте вашего приложения
export function Greeting({ name }) {
return <p>{t`Hello, ${name}!`}</p>;
}3. Вот и всё
Компилятор GT автоматически выполняет это преобразование во время сборки. Дополнительная настройка не требуется — раскрытие макросов включено по умолчанию.
Макрос t — небольшое изменение в API, но за ним стоит более важный сдвиг: переводы должны быть естественной частью JavaScript, а не выглядеть как обходное решение для конкретного фреймворка. Пишите строки как обычно. Остальное сделает инструментарий.