Адреса и почтовые индексы

В работе с адресными данными в интерфейсах, построенных на FormatJS и ICU MessageFormat, ключевую роль играет не только перевод строк, но и корректная структуризация компонентов адреса. Адрес и почтовый индекс являются культурно зависимыми элементами: порядок полей, разделители, формат индекса и даже обязательность отдельных частей варьируются в зависимости от локали.

FormatJS, опираясь на стандарты ICU и встроенные возможности JavaScript Intl, позволяет строить гибкие шаблоны представления адресов и сопутствующих данных без жёсткой привязки к конкретной стране.


Структура адреса как локализуемая сущность

Адрес в интернациональных системах рассматривается не как строка, а как набор полей:

  • страна
  • регион / штат / область
  • город
  • улица
  • номер дома
  • корпус / строение / квартира
  • почтовый индекс

Ключевая особенность международной локализации заключается в том, что порядок этих полей и их обязательность меняются.

Например:

  • в США индекс часто стоит в конце строки
  • в Японии индекс и страна могут располагаться в начале
  • в ряде европейских стран индекс предшествует названию города

FormatJS не навязывает структуру, а позволяет описывать формат через шаблоны ICU MessageFormat.


ICU MessageFormat для адресов

FormatJS использует синтаксис ICU, где адрес может быть описан как шаблон с переменными.

Пример базового шаблона:

const message = new IntlMessageFormat(
  "{country}, {postalCode}, {city}, {street}",
  "en"
);

message.format({
  country: "Germany",
  postalCode: "10115",
  city: "Berlin",
  street: "Invalidenstraße 117"
});

В реальных приложениях такой линейный формат редко используется напрямую, поскольку порядок элементов зависит от локали.

Более корректный подход — использование различных шаблонов для разных локалей.


Локализованные шаблоны адресов

Английская локаль (США)

const usFormat = new IntlMessageFormat(
  "{street}\n{city}, {region} {postalCode}\n{country}",
  "en-US"
);

Типичный результат:

1600 Amphitheatre Parkway
Mountain View, CA 94043
United States

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

const deFormat = new IntlMessageFormat(
  "{street}\n{postalCode} {city}\n{country}",
  "de-DE"
);

Результат:

Invalidenstraße 117
10115 Berlin
Deutschland

Японская локаль

const jpFormat = new IntlMessageFormat(
  "〒{postalCode}\n{country}{region}{city}{street}",
  "ja-JP"
);

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


Почтовые индексы как отдельный слой локализации

Почтовые индексы не являются универсальными: их структура зависит от страны и может включать:

  • только цифры (США, Россия)
  • буквенно-цифровые комбинации (Великобритания, Канада)
  • сегментированную структуру с пробелами

FormatJS не занимается валидацией индексов напрямую, но интегрируется с логикой форматирования и отображения через шаблоны и функции предварительной обработки.


Форматирование почтового индекса через шаблоны

Маскирование и визуальное форматирование

const formatPostalCode = (value, locale) => {
  switch (locale) {
    case "en-GB":
      return value.replace(/^(.{3})(.{3})$/, "$1 $2");
    case "ca-CA":
      return value.toUpperCase().replace(/^(.{3})(.{3})$/, "$1 $2");
    default:
      return value;
  }
};

Далее результат передаётся в FormatJS как переменная:

const msg = new IntlMessageFormat("{postalCode}", "en-GB");

msg.format({
  postalCode: formatPostalCode("SW1A1AA", "en-GB")
});

Интеграция с React Intl

В React-экосистеме FormatJS часто используется через react-intl.

import { FormattedMessage } from "react-intl";

<FormattedMessage
  id="address.block"
  defaultMessage="{street}, {city}, {postalCode}, {country}"
  values={{
    street: streetValue,
    city: cityValue,
    postalCode: postalCodeValue,
    country: countryValue
  }}
/>

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


Условное форматирование адресов

ICU MessageFormat поддерживает условия, что позволяет учитывать наличие или отсутствие компонентов адреса.

const msg = new IntlMessageFormat(
  "{street}\n{city}{postalCode, select, undefined {} other {, {postalCode}}}\n{country}",
  "en"
);

Такой шаблон исключает отображение пустого почтового индекса, предотвращая появление лишних разделителей.


Нормализация адресных данных

Перед передачей в FormatJS данные адреса часто нормализуются:

  • приведение регистра
  • удаление лишних пробелов
  • унификация формата индекса
  • разбиение строки на компоненты

Пример нормализации:

const normalizeAddress = (addr) => ({
  street: addr.street.trim(),
  city: addr.city.trim(),
  region: addr.region?.trim(),
  postalCode: addr.postalCode.replace(/\s+/g, ""),
  country: addr.country.trim()
});

Локализационные особенности почтовых индексов

США

  • формат: 5 цифр или 5+4
  • пример: 10001, 10001-1234

Великобритания

  • формат: переменная длина с пробелом
  • пример: SW1A 1AA

Германия

  • формат: 5 цифр
  • пример: 10115

Канада

  • формат: A1A 1A1
  • чередование букв и цифр

Формирование адресных блоков через композицию

В крупных приложениях адрес редко хранится в одном шаблоне. Вместо этого используется композиция:

const formatAddress = (addr, formatter) => {
  return formatter.format({
    street: addr.street,
    city: addr.city,
    region: addr.region,
    postalCode: addr.postalCode,
    country: addr.country
  });
};

Где formatter выбирается в зависимости от locale:

const getFormatter = (locale) => {
  switch (locale) {
    case "de-DE":
      return deFormat;
    case "ja-JP":
      return jpFormat;
    default:
      return usFormat;
  }
};

Разделители и типографика адресов

Локализация затрагивает не только порядок элементов, но и визуальные разделители:

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

FormatJS через ICU-шаблоны позволяет явно контролировать эти различия, не смешивая логику данных и представления.


Почтовые индексы в динамических интерфейсах

В динамических формах индекс часто влияет на поведение интерфейса:

  • автозаполнение города
  • определение региона
  • валидация формата по маске

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


Связь с Intl API

FormatJS тесно интегрируется с нативным Intl, особенно в части:

  • Intl.DisplayNames (страны и регионы)
  • Intl.Locale (разбор локалей)
  • Intl.NumberFormat (часто используется в индексах с числовым форматированием)

Это позволяет согласовывать отображение адресов с общей локализационной политикой приложения.


Ошибки при локализации адресов

Типичные проблемы при неправильной реализации:

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

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