Формат JSON данных

Библиотека Globalize использует формат JSON как основной механизм хранения локализационных данных. Все переводы, правила форматирования чисел, валют, дат и сообщений подключаются в виде JSON-структур, совместимых со стандартом CLDR (Common Locale Data Repository).

JSON-файлы в Globalize выполняют несколько задач:

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

Структура JSON играет ключевую роль в корректной работе всей системы интернационализации.


Архитектура локализационных данных

Источник данных CLDR

Globalize не содержит встроенных локализаций. Вместо этого библиотека использует данные проекта Unicode CLDR.

Стандарт CLDR предоставляет:

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

Все эти данные поставляются в формате JSON.

Типичная схема подключения:

const Globalize = require("globalize");

Globalize.load(
    require("cldr-data/main/ru/numbers.json"),
    require("cldr-data/main/ru/ca-gregorian.json"),
    require("cldr-data/main/ru/currencies.json"),
    require("cldr-data/supplemental/likelySubtags.json"),
    require("cldr-data/supplemental/numberingSystems.json")
);

Структура JSON-файлов

Общая схема

Большинство JSON-файлов CLDR имеют вложенную структуру:

{
  "main": {
    "ru": {
      "numbers": {
        ...
      }
    }
  }
}

Компоненты структуры:

Ключ Назначение
main Основной контейнер
ru Код локали
numbers Категория данных

Локали в JSON

Кодировка локалей

Globalize использует стандарт BCP 47.

Примеры:

Локаль Назначение
en Английский
en-US Английский США
en-GB Английский Великобритании
ru Русский
ru-KZ Русский Казахстана
kk Казахский
fr-CA Французский Канады

JSON для чисел

Структура numbers.json

Файл numbers.json отвечает за:

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

Пример структуры:

{
  "main": {
    "ru": {
      "numbers": {
        "defaultNumberingSystem": "latn",
        "symbols-numberSystem-latn": {
          "decimal": ",",
          "group": " ",
          "percentSign": "%",
          "plusSign": "+",
          "minusSign": "-"
        },
        "decimalFormats-numberSystem-latn": {
          "standard": "#,##0.###"
        }
      }
    }
  }
}

Разделители чисел

В разных странах используются разные символы.

Россия

{
  "decimal": ",",
  "group": " "
}

Формат:

1 234 567,89

США

{
  "decimal": ".",
  "group": ","
}

Формат:

1,234,567.89

JSON для валют

currencies.json

Файл хранит:

  • обозначения валют;
  • символы;
  • названия;
  • формы отображения.

Пример:

{
  "main": {
    "ru": {
      "numbers": {
        "currencies": {
          "USD": {
            "displayName": "доллар США",
            "symbol": "$"
          },
          "EUR": {
            "displayName": "евро",
            "symbol": "€"
          }
        }
      }
    }
  }
}

Использование валютных данных

const Globalize = require("globalize");

Globalize.locale("ru");

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

console.log(formatter(1000));

Результат:

1 000,00 $

JSON для дат

ca-gregorian.json

Файл отвечает за:

  • названия месяцев;
  • дни недели;
  • шаблоны времени;
  • форматы календаря.

Пример структуры:

{
  "main": {
    "ru": {
      "dates": {
        "calendars": {
          "gregorian": {
            "months": {
              "format": {
                "wide": {
                  "1": "январь",
                  "2": "февраль"
                }
              }
            }
          }
        }
      }
    }
  }
}

Форматы дат в JSON

Шаблоны

CLDR использует собственную систему шаблонов.

Символ Значение
y Год
M Месяц
d День
E День недели
H Часы
m Минуты
s Секунды

Пример:

{
  "dateFormats": {
    "short": "dd.MM.y",
    "medium": "d MMM y 'г'."
  }
}

JSON для сообщений

Пользовательские переводы

Globalize поддерживает собственные словари сообщений.

Пример JSON:

{
  "hello": "Привет",
  "bye": "До свидания",
  "welcome": "Добро пожаловать"
}

Подключение:

Globalize.loadMessages({
    ru: {
        hello: "Привет"
    }
});

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

Globalize.locale("ru");

const translate = Globalize.messageFormatter("hello");

console.log(translate());

Вложенные структуры сообщений

Иерархия ключей

JSON позволяет создавать сложные словари.

Пример:

{
  "auth": {
    "login": {
      "title": "Вход",
      "button": "Авторизоваться"
    }
  }
}

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

Globalize.messageFormatter("auth/login/title");

ICU MessageFormat в JSON

Параметризованные сообщения

Globalize поддерживает ICU MessageFormat.

Пример:

{
  "greeting": "Здравствуйте, {name}"
}

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

const formatter = Globalize.messageFormatter("greeting");

console.log(formatter({
    name: "Алексей"
}));

Множественное число

pluralization

Для разных языков правила отличаются.

Пример JSON:

{
  "items": "{count, plural, one {# товар} few {# товара} many {# товаров} other {# товара}}"
}

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

const formatter = Globalize.messageFormatter("items");

formatter({ count: 1 });
formatter({ count: 3 });
formatter({ count: 10 });

Категории множественного числа

Формы plural

CLDR использует категории:

Категория Назначение
zero Ноль
one Один
two Два
few Несколько
many Много
other Остальные

Русский язык активно использует:

  • one
  • few
  • many

Supplemental JSON

Дополнительные данные

Помимо основных файлов используются supplemental-файлы.

Примеры:

Файл Назначение
likelySubtags.json Расширение локалей
timeData.json Форматы времени
weekData.json Первый день недели
currencyData.json Информация о валютах
plurals.json Правила множественного числа

likelySubtags.json

Автоматическое расширение локали

Пример:

{
  "supplemental": {
    "likelySubtags": {
      "ru": "ru-Cyrl-RU",
      "en": "en-Latn-US"
    }
  }
}

Globalize может автоматически определить:

  • регион;
  • письменность;
  • дополнительные параметры локали.

JSON для единиц измерения

units.json

Файл содержит:

  • километры;
  • килограммы;
  • литры;
  • температуры;
  • скорости.

Пример:

{
  "units": {
    "length-kilometer": {
      "displayName": "километры"
    }
  }
}

Формат хранения сообщений

Рекомендации по структуре

Крупные проекты обычно используют модульную структуру.

Пример:

locales/
    ru/
        auth.json
        profile.json
        cart.json
    en/
        auth.json
        profile.json
        cart.json

Объединение JSON-файлов

Слияние локализаций

Пример:

const ruAuth = require("./ru/auth.json");
const ruCart = require("./ru/cart.json");

Globalize.loadMessages({
    ru: {
        ...ruAuth,
        ...ruCart
    }
});

Проблемы дублирования ключей

Конфликты

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

{
  "title": "Главная"
}

Во втором файле:

{
  "title": "Профиль"
}

При объединении один ключ перезапишет другой.


Пространства имён

Рекомендуется использовать namespaces.

Пример:

{
  "home": {
    "title": "Главная"
  },
  "profile": {
    "title": "Профиль"
  }
}

JSON и производительность

Размер CLDR

Полный набор CLDR очень большой.

Возможные проблемы:

  • медленная загрузка;
  • рост bundle size;
  • увеличение времени парсинга.

Оптимизация JSON

Частичная загрузка

Лучше подключать только нужные данные.

Плохой подход:

require("cldr-data");

Хороший подход:

require("cldr-data/main/ru/numbers.json");

Lazy Loading

Пример динамической загрузки:

async function loadLocale(locale) {
    const messages = await import(`./locales/${locale}.json`);

    Globalize.loadMessages({
        [locale]: messages.default
    });
}

JSON и сборщики проектов

Webpack

JSON импортируется автоматически:

import messages from "./ru.json";

Vite

import messages from "./locales/ru.json";

Node.js

const messages = require("./ru.json");

Валидация JSON

Проверка синтаксиса

Ошибочный JSON:

{
  "hello": "Привет",
}

Ошибка вызвана лишней запятой.


Валидный JSON

{
  "hello": "Привет"
}

Ограничения JSON

Что нельзя использовать

JSON не поддерживает:

  • комментарии;
  • функции;
  • undefined;
  • trailing commas;
  • вычисления;
  • ссылки на переменные.

Кодировка файлов

UTF-8

Локализационные JSON-файлы рекомендуется хранить в UTF-8.

Проблемы неправильной кодировки:

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

JSON и экранирование

Специальные символы

Пример:

{
  "quote": "Он сказал: \"Привет\""
}

Переносы строк

{
  "text": "Первая строка\nВторая строка"
}

Организация больших локализаций

Разделение по модулям

Практика крупных приложений:

i18n/
    ru/
        common.json
        errors.json
        dashboard.json
        settings.json

Версионирование переводов

Контроль изменений

Рекомендуется:

  • хранить JSON в Git;
  • использовать code review;
  • проверять отсутствующие ключи;
  • поддерживать синхронизацию локалей.

Проверка отсутствующих переводов

Пример проверки

function hasTranslation(messages, key) {
    return key.split(".").reduce((obj, part) => {
        return obj && obj[part];
    }, messages);
}

JSON и безопасность

Потенциальные риски

Опасности:

  • загрузка недоверенных JSON;
  • XSS через сообщения;
  • внедрение HTML.

Небезопасный пример

{
  "message": "<script>alert('XSS')</script>"
}

Безопасный вывод

element.textContent = message;

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

Пример настройки

{
  "decimalFormats-numberSystem-latn": {
    "standard": "#,##0.###"
  }
}

Globalize интерпретирует шаблон и автоматически форматирует числа.


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

JSON-конфигурация

{
  "percentFormats-numberSystem-latn": {
    "standard": "#,##0%"
  }
}

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

const formatter = Globalize.percentFormatter();

formatter(0.25);

Результат:

25%

Форматирование дат через JSON

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

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

formatter(new Date());

Все шаблоны берутся из JSON-файлов CLDR.


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

Дополнительные JSON-конфигурации

Иногда создаются собственные локализационные файлы:

{
  "currency": "KZT",
  "timezone": "Asia/Almaty",
  "dateFormat": "dd.MM.yyyy"
}

Локализация ошибок

errors.json

Пример:

{
  "network": {
    "timeout": "Время ожидания истекло",
    "offline": "Нет подключения к сети"
  }
}

Поддержка нескольких языков

Общая структура проекта

locales/
    en/
    ru/
    kk/
    de/

Практика именования ключей

Рекомендуемый стиль

Хороший пример:

{
  "auth.login.button": "Войти"
}

Или:

{
  "auth": {
    "login": {
      "button": "Войти"
    }
  }
}

Антипаттерны

Использование текстов как ключей

Плохой пример:

{
  "Нажмите сюда": "Нажмите сюда"
}

Проблемы:

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

Кэширование JSON

Browser Cache

Локализации часто кэшируются браузером.

Пример:

fetch("/locales/ru.json");

При правильных HTTP-заголовках JSON может храниться в кэше длительное время.


Минификация JSON

Production-сборка

JSON можно минифицировать:

{"hello":"Привет"}

Это уменьшает размер передаваемых данных.


Работа с большими переводами

Масштабирование

При тысячах ключей используются:

  • автоматические генераторы;
  • платформы переводов;
  • CI-проверки;
  • линтеры локализаций;
  • схемы JSON validation.

JSON Schema

Валидация структуры

Пример схемы:

{
  "type": "object",
  "properties": {
    "hello": {
      "type": "string"
    }
  }
}

Схемы позволяют автоматически проверять корректность локализаций.


Связь Globalize и JSON

Основной принцип работы

Globalize полностью построен вокруг JSON-структур:

  1. CLDR предоставляет данные в JSON.
  2. Globalize загружает JSON.
  3. Библиотека строит formatter-объекты.
  4. Formatter использует локализационные шаблоны.
  5. Приложение получает локализованный вывод.

JSON в Globalize является не просто форматом хранения, а фундаментом всей системы интернационализации.