Структура данных CLDR

Библиотека Globalize построена поверх набора данных CLDR (Common Locale Data Repository). Именно CLDR предоставляет информацию о локалях, форматах дат, чисел, валют, единиц измерения, правилах множественного числа, календарях и системах письма. Без этих данных Globalize не способен корректно выполнять интернационализацию.

CLDR поддерживается организацией Unicode Consortium и считается стандартным источником локализованных данных для множества платформ и библиотек.

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

Базовая схема взаимодействия выглядит следующим образом:

const Globalize = require("globalize");

Globalize.load(
    require("cldr-data/main/en/numbers"),
    require("cldr-data/supplemental/likelySubtags")
);

const globalize = new Globalize("en");

console.log(globalize.formatNumber(12345.67));

В данном примере:

  • cldr-data содержит JSON-структуры CLDR;
  • Globalize.load() загружает данные;
  • Globalize использует эти структуры для форматирования.

Общая организация CLDR

Структура CLDR разделена на два крупных раздела:

  1. main
  2. supplemental

Каждый из них решает собственную задачу.

Раздел main

Содержит данные конкретных локалей:

main/
    en/
    fr/
    de/
    ru/

Внутри каждой локали находятся файлы:

numbers.json
ca-gregorian.json
currencies.json
timeZoneNames.json

Эти файлы описывают локализованные значения.

Пример:

{
  "main": {
    "en": {
      "numbers": {
        "symbols-numberSystem-latn": {
          "decimal": ".",
          "group": ","
        }
      }
    }
  }
}

Здесь определены:

  • десятичный разделитель;
  • разделитель тысяч.

Раздел supplemental

Содержит общие правила, применяемые ко всем локалям.

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

supplemental/
    likelySubtags.json
    numberingSystems.json
    plurals.json
    currencyData.json

Эти данные отвечают за:

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

Каталог main

Структура локали

Каждая локаль хранится в отдельной директории:

main/en/
main/ru/
main/ja/

Типичное содержимое:

main/en/
    numbers.json
    currencies.json
    ca-gregorian.json
    units.json
    dateFields.json

Каждый файл отвечает за отдельную область интернационализации.


Файл numbers.json

Один из важнейших компонентов CLDR.

Содержит:

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

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

{
  "main": {
    "en": {
      "numbers": {
        "defaultNumberingSystem": "latn",
        "symbols-numberSystem-latn": {
          "decimal": ".",
          "group": ","
        },
        "decimalFormats-numberSystem-latn": {
          "standard": "#,##0.###"
        }
      }
    }
  }
}

Секции внутри numbers.json

defaultNumberingSystem

Определяет используемую систему цифр:

"defaultNumberingSystem": "latn"

Возможные значения:

Значение Описание
latn латинские цифры
arab арабские цифры
thai тайские цифры

symbols-numberSystem-latn

Содержит символы форматирования.

Пример:

{
  "decimal": ".",
  "group": ",",
  "percentSign": "%",
  "plusSign": "+",
  "minusSign": "-"
}

Globalize использует эти значения при выводе чисел.


decimalFormats-numberSystem-latn

Описывает шаблоны чисел.

Пример:

{
  "standard": "#,##0.###"
}

Шаблон интерпретируется следующим образом:

Символ Назначение
# необязательная цифра
0 обязательная цифра
, разделитель групп
. десятичный разделитель

Файл currencies.json

Содержит локализованные названия валют.

Пример:

{
  "main": {
    "en": {
      "numbers": {
        "currencies": {
          "USD": {
            "displayName": "US Dollar",
            "symbol": "$"
          }
        }
      }
    }
  }
}

Globalize использует:

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

Файл ca-gregorian.json

Содержит данные григорианского календаря.

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

{
  "main": {
    "en": {
      "dates": {
        "calendars": {
          "gregorian": {

          }
        }
      }
    }
  }
}

Внутри располагаются:

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

Раздел месяцев

Пример:

{
  "months": {
    "format": {
      "wide": {
        "1": "January",
        "2": "February"
      }
    }
  }
}

Типы представления:

Тип Назначение
wide полное название
abbreviated сокращение
narrow минимальная форма

Форматы даты

Пример:

{
  "dateFormats": {
    "short": "M/d/yy",
    "medium": "MMM d, y",
    "long": "MMMM d, y",
    "full": "EEEE, MMMM d, y"
  }
}

Globalize выбирает формат в зависимости от используемого API.


Файл units.json

Содержит единицы измерения.

Пример:

{
  "main": {
    "en": {
      "units": {
        "long": {
          "length-meter": {
            "displayName": "meters"
          }
        }
      }
    }
  }
}

Используется для:

  • метрических единиц;
  • температур;
  • скоростей;
  • объёмов;
  • масс.

Файл dateFields.json

Содержит локализованные временные интервалы.

Пример:

{
  "day": {
    "displayName": "day"
  }
}

Применяется при работе с относительным временем:

globalize.formatRelativeTime(-1, "day");

Результат:

yesterday

Каталог supplemental

Назначение supplemental-данных

Файлы из supplemental содержат общие правила и алгоритмические данные.

Globalize требует эти данные для:

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

Файл likelySubtags.json

Один из наиболее важных supplemental-файлов.

Позволяет расширять сокращённые локали.

Пример:

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

Если указано:

new Globalize("en");

CLDR автоматически определяет:

en-Latn-US

Это позволяет корректно определить:

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

Файл numberingSystems.json

Содержит описание систем цифр.

Пример:

{
  "supplemental": {
    "numberingSystems": {
      "latn": {
        "digits": "0123456789"
      }
    }
  }
}

Globalize использует эти данные при локализации чисел.


Файл plurals.json

Содержит правила множественного числа.

Пример:

{
  "supplemental": {
    "plurals-type-cardinal": {
      "ru": {
        "pluralRule-count-one":
          "v = 0 and i % 10 = 1 and i % 100 != 11"
      }
    }
  }
}

Для русского языка CLDR хранит сложные правила склонения.

Globalize использует их в сообщениях:

globalize.plural(5);

Результат:

many

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

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

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

Не каждая локаль использует все категории.


Файл currencyData.json

Содержит сведения о валютах.

Пример:

{
  "supplemental": {
    "currencyData": {

    }
  }
}

Внутри располагаются:

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

Файл weekData.json

Определяет региональные правила недели.

Пример:

{
  "supplemental": {
    "weekData": {
      "firstDay": {
        "US": "sun",
        "RU": "mon"
      }
    }
  }
}

Эти данные используются календарями и форматированием дат.


Иерархия данных в CLDR

Принцип вложенности

CLDR использует глубоко вложенные JSON-структуры.

Типичный путь:

main.en.numbers.symbols-numberSystem-latn.decimal

Получение значения через cldrjs:

const Cldr = require("cldrjs");

const cldr = new Cldr("en");

const decimal = cldr.main(
    "numbers/symbols-numberSystem-latn/decimal"
);

console.log(decimal);

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

Все локализованные данные располагаются внутри:

{
  "main": {

  }
}

Следующий уровень — код локали:

{
  "main": {
    "en": {

    }
  }
}

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

Общие данные располагаются отдельно:

{
  "supplemental": {

  }
}

Это позволяет избегать дублирования между локалями.


Механизм наследования локалей

Наследование регионов

CLDR поддерживает каскадное наследование.

Пример:

en-GB

может наследовать данные от:

en

Если значение отсутствует в en-GB, используется значение из базовой локали.


Пример наследования

en

{
  "currency": "USD"
}

en-CA

{
}

При отсутствии значения будет использовано:

USD

Цепочка поиска

Globalize и cldrjs выполняют поиск данных по цепочке:

en-CA
↓
en
↓
root

Локаль root

Назначение root

root — базовая локаль CLDR.

Она содержит:

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

Структура:

main/root/

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

Если значение отсутствует:

ru-KZ
↓
ru
↓
root

Globalize получает данные из root.


Формат путей в CLDR

Использование slash-path

cldrjs применяет строковые пути:

cldr.main(
    "dates/calendars/gregorian/dateFormats/full"
);

Это эквивалентно глубокому доступу к JSON.


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

Компактность

cldr.main("numbers/defaultNumberingSystem");

Унификация API

Одинаковый доступ ко всем разделам.

Динамическая навигация

Пути можно формировать программно.


Загрузка данных в Globalize

Метод Globalize.load

Главный механизм регистрации CLDR-данных.

Пример:

Globalize.load(
    require("cldr-data/main/en/numbers"),
    require("cldr-data/main/en/ca-gregorian"),
    require("cldr-data/supplemental/likelySubtags")
);

Что происходит при загрузке

Globalize:

  1. передаёт данные в cldrjs;
  2. регистрирует JSON-структуры;
  3. строит внутренний индекс;
  4. создаёт механизм поиска.

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

CLDR допускает загрузку только нужных компонентов.

Пример:

Globalize.load(
    require("cldr-data/main/en/numbers")
);

Однако многие API потребуют дополнительные supplemental-файлы.


Минимальные наборы CLDR для разных задач

Только числа

Минимальный набор:

Globalize.load(
    require("cldr-data/main/en/numbers"),
    require("cldr-data/supplemental/numberingSystems"),
    require("cldr-data/supplemental/likelySubtags")
);

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

Дополнительно необходимы:

require("cldr-data/main/en/ca-gregorian")
require("cldr-data/supplemental/timeData")
require("cldr-data/supplemental/weekData")

Работа с сообщениями

Необходимы:

require("cldr-data/supplemental/plurals")

Структура пакета cldr-data

Организация npm-пакета

После установки:

npm install cldr-data

структура выглядит так:

node_modules/
    cldr-data/
        main/
        supplemental/

Пример доступа

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

Оптимизация размера данных

Проблема объёма CLDR

Полный набор CLDR очень велик.

Он содержит:

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

Выборочная загрузка

Обычно загружаются только нужные локали.

Пример:

Globalize.load(
    require("cldr-data/main/en/numbers"),
    require("cldr-data/main/ru/numbers")
);

Bundling

При использовании Webpack или Vite часто создаются отдельные бандлы локалей.

Например:

locales/en.js
locales/ru.js
locales/de.js

Это уменьшает размер основного приложения.


CLDR и Unicode

Связь с Unicode

CLDR тесно интегрирован с Unicode.

Используются:

  • языковые теги BCP 47;
  • Unicode locale identifiers;
  • стандарты ICU.

Пример идентификатора локали

zh-Hant-TW

Расшифровка:

Часть Значение
zh китайский язык
Hant традиционная письменность
TW Тайвань

CLDR умеет интерпретировать такие идентификаторы автоматически.


Внутреннее представление данных в Globalize

Нормализация

После загрузки Globalize нормализует структуры CLDR.

Например:

new Globalize("en-US");

может быть преобразовано во внутренний формат:

language: en
script: Latn
territory: US

Кэширование

Globalize активно кэширует:

  • шаблоны;
  • правила;
  • парсеры;
  • форматтеры.

Это снижает стоимость повторных операций.


Работа через cldrjs

Прямой доступ к данным

Globalize использует cldrjs, но к данным можно обращаться напрямую.

Пример:

const Cldr = require("cldrjs");

const cldr = new Cldr("ru");

console.log(
    cldr.main(
        "dates/calendars/gregorian/months/format/wide/1"
    )
);

Результат:

январь

Метод main()

Используется для доступа к локализованным данным:

cldr.main(path);

Метод supplemental()

Используется для общих правил:

cldr.supplemental(path);

Пример:

cldr.supplemental("plurals-type-cardinal");

Особенности структуры CLDR

Избыточность ради совместимости

Некоторые данные дублируются между разделами.

Это сделано для:

  • совместимости;
  • производительности;
  • независимости модулей.

Глубокая вложенность

CLDR использует очень детализированную иерархию.

Причины:

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

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

Каждый аспект локализации вынесен отдельно:

Файл Назначение
numbers.json числа
currencies.json валюты
ca-gregorian.json календарь
units.json единицы
timeZoneNames.json временные зоны

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