Использование Globalize в React-приложениях строится вокруг идеи отделения локализационной логики от UI-компонентов. React отвечает за рендеринг и управление состоянием, Globalize — за форматирование дат, чисел, валют, склонений и сообщений на основе CLDR-данных.
Ключевая особенность архитектуры заключается в том, что Globalize не является «реактивной» библиотекой. Изменение локали требует явного обновления состояния React, что делает интеграцию предсказуемой и прозрачной.
Перед использованием Globalize необходимо загрузить CLDR-данные и инициализировать локали.
import Globalize from "globalize";
import likelySubtags from "cldr-core/supplemental/likelySubtags.json";
import plurals from "cldr-core/supplemental/plurals.json";
import numbers from "cldr-numbers-full/main/ru/numbers.json";
import currency from "cldr-numbers-full/main/ru/currencies.json";
import caGregorian from "cldr-dates-full/main/ru/ca-gregorian.json";
import timeZoneNames from "cldr-dates-full/main/ru/timeZoneNames.json";
Globalize.load(
likelySubtags,
plurals,
numbers,
currency,
caGregorian,
timeZoneNames
);
Globalize.locale("ru");
Инициализация должна выполняться один раз на уровне приложения, до рендеринга компонентов.
В React-проектах локаль обычно хранится в состоянии верхнего уровня или в контексте.
import React, { createContext, useContext, useState } from "react";
import Globalize from "globalize";
const LocaleContext = createContext();
export function LocaleProvider({ children }) {
const [locale, setLocale] = useState("ru");
const changeLocale = (nextLocale) => {
Globalize.locale(nextLocale);
setLocale(nextLocale);
};
return (
<LocaleContext.Provider value={{ locale, changeLocale }}>
{children}
</LocaleContext.Provider>
);
}
export const useLocale = () => useContext(LocaleContext);
Важный момент: вызов Globalize.locale() изменяет
глобальное состояние библиотеки, поэтому синхронизация с
React-состоянием обязательна.
Globalize предоставляет фабрики форматтеров, которые создаются один раз для конкретной локали.
import Globalize from "globalize";
const numberFormatter = Globalize.numberFormatter();
const currencyFormatter = Globalize.currencyFormatter("USD");
export function Price({ value }) {
return (
<span>
{currencyFormatter(value)}
</span>
);
}
При смене локали форматтеры необходимо пересоздавать, иначе они будут использовать устаревшие правила.
Для корректной работы с динамической сменой локали используется
useMemo или useEffect.
import React, { useMemo } from "react";
import Globalize from "globalize";
import { useLocale } from "./LocaleProvider";
export function useNumberFormatter() {
const { locale } = useLocale();
return useMemo(() => {
Globalize.locale(locale);
return Globalize.numberFormatter();
}, [locale]);
}
Использование в компоненте:
export function Stats({ value }) {
const formatNumber = useNumberFormatter();
return <div>{formatNumber(value)}</div>;
}
Globalize поддерживает форматирование дат через CLDR-данные.
const dateFormatter = Globalize.dateFormatter({ datetime: "medium" });
export function DateView({ date }) {
return <time>{dateFormatter(new Date(date))}</time>;
}
При работе с React важно учитывать, что объект Date
должен быть валидным и заранее нормализованным.
Одной из ключевых возможностей Globalize является работа с правилами множественного числа.
const pluralGenerator = Globalize.pluralGenerator();
export function Message({ count }) {
const form = pluralGenerator(count);
const messages = {
one: "сообщение",
few: "сообщения",
many: "сообщений",
other: "сообщений"
};
return (
<span>{count} {messages[form]}</span>
);
}
В React-компонентах такие конструкции часто инкапсулируются в отдельные утилиты для повторного использования.
Globalize поддерживает ICU-подобные шаблоны через messageFormatter.
const messages = {
welcome: "Добро пожаловать, {name}",
};
const messageFormatter = Globalize.messageFormatter(messages.welcome);
export function Welcome({ name }) {
return <h1>{messageFormatter({ name })}</h1>;
}
Для масштабных приложений шаблоны обычно выносятся в отдельные JSON-файлы по локалям.
Смена локали требует обновления контекста и пересоздания форматтеров.
export function LanguageSwitcher() {
const { locale, changeLocale } = useLocale();
return (
<select
value={locale}
onCha nge={(e) => changeLocale(e.target.value)}
>
<option value="ru">Русский</option>
<option value="en">English</option>
<option value="de">Deutsch</option>
</select>
);
}
После смены локали все компоненты, использующие форматтеры через хук, автоматически перерендериваются.
Globalize создаёт форматтеры, которые могут быть дорогими при частом создании. В React важно минимизировать их пересоздание.
Практика:
useMemoПример кеширования:
const cache = new Map();
function getCurrencyFormatter(locale, currency) {
const key = `${locale}-${currency}`;
if (!cache.has(key)) {
Globalize.locale(locale);
cache.set(key, Globalize.currencyFormatter(currency));
}
return cache.get(key);
}
Компоненты-обёртки позволяют унифицировать форматирование.
export function Money({ value, currency }) {
const { locale } = useLocale();
const formatter = useMemo(() => {
Globalize.locale(locale);
return Globalize.currencyFormatter(currency);
}, [locale, currency]);
return <span>{formatter(value)}</span>;
}
Такой подход снижает дублирование логики в интерфейсе.
В крупных приложениях CLDR-данные могут загружаться динамически.
async function loadLocaleData(locale) {
const [numbers, currencies] = await Promise.all([
import(`cldr-numbers-full/main/${locale}/numbers.json`),
import(`cldr-numbers-full/main/${locale}/currencies.json`)
]);
Globalize.load(numbers, currencies);
Globalize.locale(locale);
}
React-компоненты должны учитывать состояние загрузки локализационных данных.
При серверном рендеринге важно синхронизировать локаль между сервером и клиентом.
На сервере:
Globalize.locale("ru");
const formatted = Globalize.numberFormatter()(1234);
На клиенте:
Globalize.locale(window.__INITIAL_LOCALE__);
Несоответствие локали приводит к расхождению HTML при гидратации.
Интеграция React и Globalize обычно строится по слоям:
Такая структура позволяет изолировать изменения локали от бизнес-логики компонентов и обеспечивает предсказуемость поведения интерфейса при переключении языков.