Учетный и отображаемый форматы

При интернационализации приложений возникает фундаментальная проблема: данные должны храниться в одном, стабильном и предсказуемом виде, но отображаться — в соответствии с локалью пользователя. В разных странах различаются:

  • формат чисел;
  • правила записи дат;
  • валютные обозначения;
  • порядок компонентов времени;
  • разделители тысяч и дробной части;
  • правила округления.

Библиотека Globalize решает эту задачу через разделение:

  • учетного формата — внутреннего представления данных;
  • отображаемого формата — локализованного пользовательского представления.

Такой подход позволяет избежать ошибок при обработке данных и одновременно обеспечить корректный интерфейс для пользователей разных регионов.


Назначение учетного формата

Учетный формат представляет собой внутренний способ хранения данных, независимый от локали пользователя.

Например:

Тип данных Учетный формат
Числа 12345.67
Дата 2026-05-27
Время 18:45:00
Валюта 1500.25
Timestamp 2026-05-27T18:45:00Z

Основные свойства учетного формата:

  • единообразие;
  • независимость от языка;
  • удобство сериализации;
  • предсказуемость парсинга;
  • отсутствие локальных неоднозначностей.

Пример проблемы без учетного формата

Число:

1,234

может означать:

  • одну тысячу двести тридцать четыре — в США;
  • одну целую двести тридцать четыре тысячных — в Германии.

Если хранить данные в пользовательском виде, возникает риск некорректной обработки.


Назначение отображаемого формата

Отображаемый формат предназначен исключительно для взаимодействия с пользователем.

Один и тот же учетный формат:

12345.67

может отображаться так:

Локаль Результат
en 12,345.67
de 12.345,67
fr 12 345,67
ru 12 345,67

Globalize автоматически выполняет преобразование на основе текущей локали.


Архитектура форматирования в Globalize

Globalize строится поверх данных CLDR (Common Locale Data Repository), содержащих:

  • правила форматирования;
  • локальные стандарты;
  • валютные обозначения;
  • шаблоны дат;
  • региональные особенности.

Типичная схема работы выглядит следующим образом:

Учетные данные
       ↓
Globalize
       ↓
Локализованный вывод

Форматирование чисел

Базовое форматирование

const Globalize = require("globalize");

Globalize.locale("en");

const formatter = Globalize.numberFormatter();

console.log(formatter(12345.67));

Результат:

12,345.67

Для немецкой локали:

Globalize.locale("de");

const formatter = Globalize.numberFormatter();

console.log(formatter(12345.67));

Результат:

12.345,67

Разделение хранения и отображения

Внутри приложения число должно оставаться числом:

const amount = 12345.67;

Нельзя хранить:

"12.345,67"

или:

"12,345.67"

Причины:

  • невозможность безопасных вычислений;
  • ошибки при сериализации;
  • проблемы сортировки;
  • неоднозначный парсинг.

Globalize применяется только на этапе отображения:

const formatter = Globalize.numberFormatter();

const visibleValue = formatter(amount);

Форматирование процентов

Учетное значение:

0.35

Отображение:

const formatter = Globalize.numberFormatter({
    style: "percent"
});

console.log(formatter(0.35));

Результат:

35%

Для разных локалей формат может отличаться:

  • символом процента;
  • пробелами;
  • правилами округления.

Форматирование валют

Учетное хранение

Правильный вариант:

{
    amount: 1500.75,
    currency: "USD"
}

Неправильный вариант:

"$1,500.75"

или:

"1 500,75 ₽"

Отображение валюты

const formatter = Globalize.currencyFormatter("USD");

console.log(formatter(1500.75));

Результат для en:

$1,500.75

Для fr:

1 500,75 $US

Для ru:

1 500,75 $

Настройка точности

const formatter = Globalize.numberFormatter({
    minimumFractionDigits: 2,
    maximumFractionDigits: 2
});

console.log(formatter(12));

Результат:

12.00

Форматирование дат

Учетное хранение дат

Рекомендуемые форматы:

2026-05-27

или:

2026-05-27T14:30:00Z

Использование локальных строк:

27.05.2026

или:

05/27/2026

для хранения данных является ошибкой.


Локализованное отображение дат

const date = new Date(2026, 4, 27);

Globalize.locale("en");

const formatter = Globalize.dateFormatter({
    datetime: "medium"
});

console.log(formatter(date));

Результат:

May 27, 2026

Для русской локали:

27 мая 2026 г.

Краткие и полные форматы

Краткий формат

const formatter = Globalize.dateFormatter({
    skeleton: "yMd"
});

Возможный результат:

27.05.2026

Полный формат

const formatter = Globalize.dateFormatter({
    datetime: "full"
});

Результат:

Wednesday, May 27, 2026

или:

среда, 27 мая 2026 г.

Форматирование времени

const formatter = Globalize.dateFormatter({
    skeleton: "Hm"
});

console.log(formatter(new Date()));

В разных локалях возможны:

  • 24-часовой формат;
  • 12-часовой формат;
  • использование AM/PM;
  • различные разделители.

Парсинг отображаемых данных

Форматирование — только половина задачи. Пользователь вводит данные в локальном формате, а приложение должно преобразовать их в учетный формат.


Парсинг чисел

Немецкая локаль

Globalize.locale("de");

const parser = Globalize.numberParser();

console.log(parser("12.345,67"));

Результат:

12345.67

Парсинг процентов

const parser = Globalize.numberParser({
    style: "percent"
});

console.log(parser("35%"));

Результат:

0.35

Парсинг дат

const parser = Globalize.dateParser({
    skeleton: "yMd"
});

console.log(parser("27.05.2026"));

Результатом будет объект Date.


Проблема смешивания форматов

Наиболее опасная ошибка — смешивание учетного и отображаемого представления.


Ошибочный подход

const total = "12.345,67";

const result = total * 2;

Результат:

NaN

Правильный подход

const parser = Globalize.numberParser();

const value = parser("12.345,67");

const result = value * 2;

Локализация пользовательского ввода

Пример формы

Пользователь из Германии вводит:

1.234,50

Внутри приложения должно храниться:

1234.5

Алгоритм:

  1. Получение строки от пользователя.
  2. Парсинг через Globalize.
  3. Сохранение в учетном формате.
  4. Повторное форматирование при выводе.

Работа с несколькими локалями

Переключение локали

Globalize.locale("en");
Globalize.locale("fr");
Globalize.locale("ru");

После смены локали форматирование автоматически меняется.


Динамическое отображение

function renderPrice(price, locale) {
    Globalize.locale(locale);

    const formatter = Globalize.currencyFormatter("EUR");

    return formatter(price);
}

Учетный формат в API

При обмене данными между клиентом и сервером необходимо использовать только нейтральные форматы.


JSON и локализация

Правильный вариант:

{
    "price": 1234.56,
    "date": "2026-05-27"
}

Неправильный вариант:

{
    "price": "1.234,56",
    "date": "27.05.2026"
}

ISO-форматы

ISO 8601

Стандартный формат даты:

2026-05-27T14:30:00Z

Преимущества:

  • международный стандарт;
  • отсутствие неоднозначностей;
  • удобство сортировки;
  • совместимость с API;
  • корректная обработка часовыми поясами.

Часовые пояса и отображение

Учетное время часто хранится в UTC:

2026-05-27T12:00:00Z

Но пользователь должен видеть локальное время.

Globalize форматирует дату с учетом локали, однако преобразование временных зон обычно выполняется отдельно.


Разделение ответственности

Практическая архитектура обычно выглядит так:

Уровень Формат
База данных ISO / UTC
API JSON / ISO
Бизнес-логика Числа и Date
Интерфейс Localized string
Пользовательский ввод Localized string

Повторное использование форматтеров

Создание форматтеров — относительно дорогая операция.

Неэффективно:

function render(value) {
    return Globalize.numberFormatter()(value);
}

Лучше:

const formatter = Globalize.numberFormatter();

function render(value) {
    return formatter(value);
}

Кэширование форматтеров

Для крупных приложений используется кэш:

const cache = {};

function getFormatter(locale) {
    if (!cache[locale]) {
        Globalize.locale(locale);

        cache[locale] =
            Globalize.numberFormatter();
    }

    return cache[locale];
}

Форматы и валидация

Важно понимать различие между:

  • проверкой корректности;
  • локализованным парсингом.

Проверка после парсинга

const parser = Globalize.numberParser();

const value = parser(input);

if (isNaN(value)) {
    console.log("Некорректное число");
}

Типичные ошибки

Хранение локализованных строк

Ошибка:

"1 234,56 ₽"

вместо:

1234.56

Ручное форматирование

Ошибка:

price.toFixed(2).replace(".", ",")

Проблемы:

  • отсутствие учета локали;
  • неправильные разделители;
  • ошибки валют;
  • несовместимость с международными форматами.

Использование parseFloat

Ошибка:

parseFloat("12.345,67")

Результат:

12.345

Globalize корректно учитывает локаль.


Интеграция с пользовательским интерфейсом

Типичный цикл работы:

Пользовательский ввод
        ↓
Парсинг через Globalize
        ↓
Учетный формат
        ↓
Бизнес-логика
        ↓
Форматирование через Globalize
        ↓
Интерфейс

Практический пример

Ввод цены

Globalize.locale("fr");

const parser =
    Globalize.numberParser();

const formatter =
    Globalize.currencyFormatter("EUR");

const userInput = "1 250,75";

const value = parser(userInput);

console.log(value);

Результат:

1250.75

Отображение:

console.log(formatter(value));

Результат:

1 250,75 €

Преимущества разделения форматов

Учетный формат обеспечивает

  • надежное хранение;
  • корректные вычисления;
  • совместимость API;
  • предсказуемую сериализацию;
  • простую обработку данных.

Отображаемый формат обеспечивает

  • привычный интерфейс;
  • соответствие региональным стандартам;
  • корректное восприятие данных;
  • снижение количества пользовательских ошибок;
  • поддержку международных пользователей.

Роль Globalize в архитектуре приложения

Globalize не заменяет внутренние форматы хранения данных. Библиотека выступает промежуточным слоем между:

  • внутренним представлением данных;
  • локализованным интерфейсом.

Именно такое разделение делает международные приложения:

  • масштабируемыми;
  • предсказуемыми;
  • устойчивыми к ошибкам локализации;
  • удобными для пользователей разных стран.