Общие строки
Как интернационализировать строки, используемые в нескольких компонентах и файлах
Общие строки — это текстовые значения, которые применяются в разных местах вашего приложения — например, в элементах навигации, сообщениях форм или конфигурационных данных. Вместо того чтобы дублировать логику перевода везде, используйте msg для пометки строк и useMessages для их декодирования.
Проблема с общим контентом
Рассмотрим такую конфигурацию навигации, используемую по всему приложению:
// navData.ts
export const navData = [
{
label: 'Главная',
description: 'Главная страница',
href: '/'
},
{
label: 'О компании',
description: 'Сведения о компании',
href: '/about'
}
];Чтобы интернационализировать это, обычно требуется:
- Превратить это в функцию, принимающую функцию перевода
- Обновить все места использования, вызывая функцию с
t - Справляться с возросшей сложностью по всему коду
Это создаёт накладные расходы на поддержку и ухудшает читаемость кода. Функция msg решает проблему: она позволяет помечать строки для перевода прямо в месте использования и декодировать их по мере необходимости.
Быстрый старт
Используйте msg для маркировки строк и useMessages для их декодирования:
// navData.ts — Помечаем строки для перевода
import { msg } from 'gt-next';
export const navData = [
{
label: msg('Главная'),
description: msg('Главная страница'),
href: '/'
},
{
label: msg('О компании'),
description: msg('Сведения о компании'),
href: '/about'
}
];// Component usage - Decode marked strings
import { useMessages } from 'gt-next';
import { navData } from './navData';
function Navigation() {
const m = useMessages();
return (
<nav>
{navData.map((item) => (
<a key={item.href} href={item.href} title={m(item.description)}>
{m(item.label)}
</a>
))}
</nav>
);
}Как работает система общих строк
Система общих строк работает в два этапа:
- Этап маркировки:
msgкодирует строки с метаданными перевода - Этап декодирования:
useMessagesилиgetMessagesдекодируют и переводят строки
// msg() кодирует строку с метаданными
const encoded = msg('Hello, world!');
console.log(encoded); // "Hello, world!:eyIkX2hhc2giOiJkMjA3MDliZGExNjNlZmM2In0="
// useMessages() декодирует и переводит
const m = useMessages();
const translated = m(encoded); // «Hello, world!» на языке пользователяЗакодированные строки из msg нельзя использовать напрямую — их нужно декодировать с помощью useMessages или getMessages.
Использование на клиенте и на сервере
Клиентские компоненты
Используйте хук useMessages:
import { useMessages } from 'gt-next';
const encodedString = msg('Привет, мир!');
function MyComponent() {
const m = useMessages();
return <div>{m(encodedString)}</div>;
}Серверные компоненты
Используйте функцию getMessages:
import { getMessages } from 'gt-next/server';
const encodedString = msg('Hello, world!');
async function MyServerComponent() {
const m = await getMessages();
return <div>{m(encodedString)}</div>;
}Получение исходных строк с помощью decodeMsg
Иногда требуется доступ к исходной строке без перевода — например, для логирования, отладки или сравнения. Используйте decodeMsg, чтобы извлечь оригинальный текст:
import { decodeMsg } from 'gt-next';
const encoded = msg('Привет, мир!');
const original = decodeMsg(encoded); // «Привет, мир!» (исходная строка)
const translated = m(encoded); // «Привет, мир!» (на языке пользователя)
// Полезно для логирования и отладки
console.log('Исходная строка:', decodeMsg(encoded));
console.log('Переведенная строка:', m(encoded));Сценарии использования decodeMsg
- Разработка и отладка: логируйте исходные строки для диагностики
- Обработка падения на запасной вариант: используйте исходный текст, когда перевод не сработал
- Сравнение строк: сравнивайте с известными исходными значениями
- Аналитика: отслеживайте использование исходных строк
// Пример: обработка на случай отказа
function getDisplayText(encodedStr) {
const m = useMessages();
try {
return m(encodedStr);
} catch (error) {
console.warn('Не удалось выполнить перевод, используем исходный текст:', decodeMsg(encodedStr));
return decodeMsg(encodedStr);
}
}Использование переменных
Для строк с динамическим содержимым используйте заполнительные параметры и передавайте переменные:
// Отметьте строку с переменными
const items = 100;
export const pricing = [
{
name: 'Basic',
price: 100,
description: msg('Базовый план включает {items} позиций', { items })
}
];// Use in component
function PricingCard() {
const m = useMessages();
return (
<div>
<h3>{pricing[0].name}</h3>
<p>{m(pricing[0].description)}</p>
</div>
);
}Формат сообщений ICU
Для расширенного форматирования используйте синтаксис ICU:
const count = 10;
const message = msg('В корзине {count, plural, =0 {нет товаров} one {# товар} few {# товара} many {# товаров} other {# товара}}', { count });Подробнее о формате сообщений ICU — в документации Unicode.
Примеры
Настройка навигации
// config/navigation.ts
import { msg } from 'gt-next';
export const mainNav = [
{
label: msg('Главная'),
href: '/',
icon: 'home'
},
{
label: msg('Продукты'),
href: '/products',
icon: 'package'
},
{
label: msg('О нас'),
href: '/about',
icon: 'info'
}
];
export const footerLinks = [
{
title: msg('Компания'),
links: [
{ label: msg('О компании'), href: '/about' },
{ label: msg('Вакансии'), href: '/careers' },
{ label: msg('Контакты'), href: '/contact' }
]
},
{
title: msg('Поддержка'),
links: [
{ label: msg('Центр помощи'), href: '/help' },
{ label: msg('Документация'), href: '/docs' },
{ label: msg('Справочник по API'), href: '/api' }
]
}
];// components/Navigation.tsx
import { useMessages } from 'gt-next';
import { mainNav } from '../config/navigation';
function Navigation() {
const m = useMessages();
return (
<nav>
{mainNav.map((item) => (
<a key={item.href} href={item.href}>
<Icon name={item.icon} />
{m(item.label)}
</a>
))}
</nav>
);
}Настройка формы
// config/forms.ts
import { msg } from 'gt-next';
export const formMessages = {
placeholders: {
email: msg('Введите адрес электронной почты'),
password: msg('Введите пароль'),
message: msg('Введите сообщение...')
},
actions: {
send: msg('Отправить сообщение'),
save: msg('Сохранить изменения'),
cancel: msg('Отмена')
},
validation: {
required: msg('Это поле обязательно для заполнения'),
email: msg('Введите корректный адрес электронной почты'),
minLength: msg('Не менее {min} символов', { min: 8 }),
maxLength: msg('Не более {max} символов', { max: 100 })
},
success: {
saved: msg('Изменения успешно сохранены'),
sent: msg('Сообщение успешно отправлено'),
updated: msg('Профиль обновлён')
},
errors: {
network: msg('Сетевая ошибка — попробуйте ещё раз'),
server: msg('Ошибка сервера — обратитесь в поддержку'),
timeout: msg('Время ожидания истекло — попробуйте ещё раз')
}
};// components/ContactForm.tsx
import { useMessages } from 'gt-next';
import { formMessages } from '../config/forms';
function ContactForm() {
const m = useMessages();
const [errors, setErrors] = useState({});
return (
<form>
<input
type="email"
placeholder={m(formMessages.placeholders.email)}
required
/>
{errors.email && <span>{m(formMessages.validation.email)}</span>}
<button type="submit">
{m(formMessages.actions.send)}
</button>
</form>
);
}Динамическое формирование контента
// utils/productData.ts
import { msg } from 'gt-next';
function mockProducts() {
return [
{ name: 'iPhone 15', company: 'Apple', category: 'Electronics' },
{ name: 'Galaxy S24', company: 'Samsung', category: 'Electronics' }
];
}
export function getProductData() {
const products = mockProducts();
return products.map(product => ({
...product,
description: msg('{name} — продукт категории «{category}» от компании {company}', {
name: product.name,
category: product.category,
company: product.company
})
}));
}// components/ProductList.tsx
import { useMessages } from 'gt-next';
import { getProductData } from '../utils/productData';
function ProductList() {
const m = useMessages();
const products = getProductData();
return (
<div>
{products.map(product => (
<div key={product.name}>
<h3>{product.name}</h3>
<p>{m(product.description)}</p>
</div>
))}
</div>
);
}Распространённые проблемы
Прямое использование кодированных строк
Никогда не используйте результат msg напрямую:
// ❌ Неправильно — используется закодированная строка напрямую
const encoded = msg('Hello, world!');
return <div>{encoded}</div>; // Отображает закодированную строку, а не перевод
// ✅ Правильно — сначала декодируйте строку
const encoded = msg('Hello, world!');
const m = useMessages();
return <div>{m(encoded)}</div>; // Отображает корректный переводДинамический контент в msg()
Строки должны быть известны на этапе сборки:
// ❌ Неправильно — динамический шаблонный литерал
const name = 'John';
const message = msg(`Hello, ${name}`); // Ошибка на этапе сборки
// ✅ Правильно — используйте переменные
const name = 'John';
const message = msg('Hello, {name}', { name });Забыли декодировать
Каждую строку msg необходимо декодировать:
// ❌ Нет декодирования
const config = {
title: msg('Панель управления'),
subtitle: msg('С возвращением')
};
// Позже в компоненте — забыли декодировать
return <h1>{config.title}</h1>; // Отображается закодированная строка
// ✅ Правильно — декодируйте при использовании
const m = useMessages();
return <h1>{m(config.title)}</h1>; // Отображается переведённый заголовокСледующие шаги
- Руководство по словарям — Организация переводов с помощью структурированных данных
- Руководство по языкам — Настройка поддерживаемых языков
- Справочник по API:
- Функция
msg - Хук
useMessages
- Функция
Насколько полезно это руководство?