Интернет-магазин выходит на рынки России, Германии и США. Цена 1500 должна отображаться как "1 500 ₽", "1.500 €" и "$1,500" — в зависимости от локали. Дата "следующая неделя" по-русски звучит "через 7 дней", а по-немецки — "in 7 Tagen". Всё это решает Intl API — без сторонних библиотек.
Intl (Internationalization API) — встроенный набор объектов для форматирования чисел, валют, дат и строк с учётом локали. До Intl для этого нужны были библиотеки вроде moment.js или numeral.js весом по 100+ KB.
// Простое форматирование числа
const num = new Intl.NumberFormat('ru-RU').format(1234567.89)
console.log(num) // '1 234 567,89'
// Валюта
const price = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
}).format(89990)
console.log(price) // '89 990,00 ₽'
// Доллары
const usd = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(89990)
console.log(usd) // '$89,990.00'
// Проценты
const pct = new Intl.NumberFormat('ru-RU', {
style: 'percent',
maximumFractionDigits: 1,
}).format(0.1567)
console.log(pct) // '15,7%'
// Компактный формат
const compact = new Intl.NumberFormat('ru-RU', {
notation: 'compact',
}).format(1500000)
console.log(compact) // '1,5 млн'const date = new Date('2026-03-04T15:30:00')
// Полная дата по-русски
const full = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
}).format(date)
console.log(full) // 'среда, 4 марта 2026 г.'
// Краткий формат
const short = new Intl.DateTimeFormat('ru-RU').format(date)
console.log(short) // '04.03.2026'
// Дата и время
const datetime = new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'short',
timeStyle: 'short',
}).format(date)
console.log(datetime) // '04.03.2026, 15:30'
// Английский формат
const enDate = new Intl.DateTimeFormat('en-US', {
dateStyle: 'full',
}).format(date)
console.log(enDate) // 'Wednesday, March 4, 2026'const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
console.log(rtf.format(-5, 'minute')) // '5 минут назад'
console.log(rtf.format(-1, 'day')) // 'вчера'
console.log(rtf.format(3, 'day')) // 'через 3 дня'
console.log(rtf.format(-2, 'week')) // '2 недели назад'
console.log(rtf.format(1, 'month')) // 'в следующем месяце'
console.log(rtf.format(-1, 'year')) // 'в прошлом году'
// Функция "время назад"
function timeAgo(date) {
const seconds = Math.floor((Date.now() - date.getTime()) / 1000)
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
if (Math.abs(seconds) < 60) return rtf.format(-seconds, 'second')
if (Math.abs(seconds) < 3600) return rtf.format(-Math.floor(seconds / 60), 'minute')
if (Math.abs(seconds) < 86400) return rtf.format(-Math.floor(seconds / 3600), 'hour')
return rtf.format(-Math.floor(seconds / 86400), 'day')
}// Без Collator — неправильная сортировка кириллицы
const names = ['Яков', 'Алексей', 'Борис', 'Анна', 'Ёж']
console.log(names.sort())
// ['Алексей', 'Анна', 'Борис', 'Ёж', 'Яков'] — Ё может попасть не туда!
// С Collator — корректно
const collator = new Intl.Collator('ru-RU')
console.log(names.sort(collator.compare))
// ['Алексей', 'Анна', 'Борис', 'Ёж', 'Яков'] — правильный порядок
// Сортировка нечувствительная к регистру
const ci = new Intl.Collator('ru-RU', { sensitivity: 'base' })
console.log(['Борис', 'анна', 'Алексей'].sort(ci.compare))
// ['Алексей', 'анна', 'Борис']const pr = new Intl.PluralRules('ru-RU')
// Определяет форму слова
function formatItems(count) {
const rule = pr.select(count)
const forms = {
one: 'товар', // 1 товар
few: 'товара', // 2, 3, 4 товара
many: 'товаров', // 5, 6, 11 товаров
other: 'товаров',
}
return `${count} ${forms[rule]}`
}
console.log(formatItems(1)) // '1 товар'
console.log(formatItems(3)) // '3 товара'
console.log(formatItems(5)) // '5 товаров'
console.log(formatItems(11)) // '11 товаров'
console.log(formatItems(21)) // '21 товар'Ошибка 1: создают новый Intl-объект при каждом вызове
// Сломано: создание Intl.NumberFormat дорогостоящая операция
function formatPrice(price) {
return new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' }).format(price)
// ^ каждый раз создаётся новый объект
}
// Исправлено: создать один раз
const priceFormatter = new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' })
const formatPrice = (price) => priceFormatter.format(price)Ошибка 2: сортировка строк без Collator
// Сломано: стандартный sort некорректно сортирует кириллицу
const names = ['Яша', 'Анна', 'Ёж', 'Борис']
names.sort() // ['Анна', 'Борис', 'Ёж', 'Яша'] — Ё может попасть не туда!
// Исправлено:
const collator = new Intl.Collator('ru-RU')
names.sort(collator.compare) // корректный порядокОшибка 3: используют toLocaleDateString() вместо Intl.DateTimeFormat
// Нестабильно: поведение зависит от системных настроек браузера
date.toLocaleDateString() // разный результат на разных машинах
// Исправлено: явная локаль
new Intl.DateTimeFormat('ru-RU', { dateStyle: 'short' }).format(date)
// '04.03.2026' — одинаково вездеФорматирование российских цен, дат и относительного времени через Intl API
// ===== Intl.NumberFormat — цены =====
function formatPrice(amount, currency = 'RUB', locale = 'ru-RU') {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(amount)
}
console.log('=== Форматирование цен ===')
console.log(formatPrice(1500)) // '1 500 ₽'
console.log(formatPrice(89990)) // '89 990 ₽'
console.log(formatPrice(1234567)) // '1 234 567 ₽'
console.log(formatPrice(99, 'USD', 'en-US')) // '$99'
console.log(formatPrice(50, 'EUR', 'de-DE')) // '50 €'
// ===== Intl.DateTimeFormat — даты =====
function formatDate(date, style = 'short') {
return new Intl.DateTimeFormat('ru-RU', { dateStyle: style }).format(date)
}
const today = new Date('2026-03-04T12:00:00')
console.log('\n=== Форматирование дат ===')
console.log(formatDate(today, 'short')) // '04.03.2026'
console.log(formatDate(today, 'medium')) // '4 мар. 2026 г.'
console.log(formatDate(today, 'long')) // '4 марта 2026 г.'
console.log(formatDate(today, 'full')) // 'среда, 4 марта 2026 г.'
// Дата + время
const withTime = new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'short',
timeStyle: 'short',
}).format(today)
console.log('Дата и время:', withTime) // '04.03.2026, 12:00'
// ===== Intl.RelativeTimeFormat — "N дней назад" =====
function timeAgo(date) {
const now = Date.now()
const diffMs = now - date.getTime()
const diffSec = Math.round(diffMs / 1000)
const diffMin = Math.round(diffSec / 60)
const diffHrs = Math.round(diffMin / 60)
const diffDays = Math.round(diffHrs / 24)
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
if (Math.abs(diffSec) < 60) return rtf.format(-diffSec, 'second')
if (Math.abs(diffMin) < 60) return rtf.format(-diffMin, 'minute')
if (Math.abs(diffHrs) < 24) return rtf.format(-diffHrs, 'hour')
return rtf.format(-diffDays, 'day')
}
console.log('\n=== Относительное время ===')
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
console.log(rtf.format(-2, 'minute')) // '2 минуты назад'
console.log(rtf.format(-1, 'hour')) // 'час назад'
console.log(rtf.format(-1, 'day')) // 'вчера'
console.log(rtf.format(-5, 'day')) // '5 дней назад'
console.log(rtf.format(3, 'day')) // 'через 3 дня'
console.log(rtf.format(1, 'week')) // 'через 1 неделю'
// ===== Intl.PluralRules — правила множественного числа =====
console.log('\n=== Множественное число ===')
const pr = new Intl.PluralRules('ru-RU')
function items(n) {
const rule = pr.select(n)
const forms = { one: 'товар', few: 'товара', many: 'товаров', other: 'товаров' }
return `${n} ${forms[rule]}`
}
;[1, 2, 5, 11, 21, 100, 101].forEach(n => console.log(items(n)))
// '1 товар', '2 товара', '5 товаров', '11 товаров', '21 товар', '100 товаров', '101 товар'Интернет-магазин выходит на рынки России, Германии и США. Цена 1500 должна отображаться как "1 500 ₽", "1.500 €" и "$1,500" — в зависимости от локали. Дата "следующая неделя" по-русски звучит "через 7 дней", а по-немецки — "in 7 Tagen". Всё это решает Intl API — без сторонних библиотек.
Intl (Internationalization API) — встроенный набор объектов для форматирования чисел, валют, дат и строк с учётом локали. До Intl для этого нужны были библиотеки вроде moment.js или numeral.js весом по 100+ KB.
// Простое форматирование числа
const num = new Intl.NumberFormat('ru-RU').format(1234567.89)
console.log(num) // '1 234 567,89'
// Валюта
const price = new Intl.NumberFormat('ru-RU', {
style: 'currency',
currency: 'RUB',
}).format(89990)
console.log(price) // '89 990,00 ₽'
// Доллары
const usd = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
}).format(89990)
console.log(usd) // '$89,990.00'
// Проценты
const pct = new Intl.NumberFormat('ru-RU', {
style: 'percent',
maximumFractionDigits: 1,
}).format(0.1567)
console.log(pct) // '15,7%'
// Компактный формат
const compact = new Intl.NumberFormat('ru-RU', {
notation: 'compact',
}).format(1500000)
console.log(compact) // '1,5 млн'const date = new Date('2026-03-04T15:30:00')
// Полная дата по-русски
const full = new Intl.DateTimeFormat('ru-RU', {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
}).format(date)
console.log(full) // 'среда, 4 марта 2026 г.'
// Краткий формат
const short = new Intl.DateTimeFormat('ru-RU').format(date)
console.log(short) // '04.03.2026'
// Дата и время
const datetime = new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'short',
timeStyle: 'short',
}).format(date)
console.log(datetime) // '04.03.2026, 15:30'
// Английский формат
const enDate = new Intl.DateTimeFormat('en-US', {
dateStyle: 'full',
}).format(date)
console.log(enDate) // 'Wednesday, March 4, 2026'const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
console.log(rtf.format(-5, 'minute')) // '5 минут назад'
console.log(rtf.format(-1, 'day')) // 'вчера'
console.log(rtf.format(3, 'day')) // 'через 3 дня'
console.log(rtf.format(-2, 'week')) // '2 недели назад'
console.log(rtf.format(1, 'month')) // 'в следующем месяце'
console.log(rtf.format(-1, 'year')) // 'в прошлом году'
// Функция "время назад"
function timeAgo(date) {
const seconds = Math.floor((Date.now() - date.getTime()) / 1000)
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
if (Math.abs(seconds) < 60) return rtf.format(-seconds, 'second')
if (Math.abs(seconds) < 3600) return rtf.format(-Math.floor(seconds / 60), 'minute')
if (Math.abs(seconds) < 86400) return rtf.format(-Math.floor(seconds / 3600), 'hour')
return rtf.format(-Math.floor(seconds / 86400), 'day')
}// Без Collator — неправильная сортировка кириллицы
const names = ['Яков', 'Алексей', 'Борис', 'Анна', 'Ёж']
console.log(names.sort())
// ['Алексей', 'Анна', 'Борис', 'Ёж', 'Яков'] — Ё может попасть не туда!
// С Collator — корректно
const collator = new Intl.Collator('ru-RU')
console.log(names.sort(collator.compare))
// ['Алексей', 'Анна', 'Борис', 'Ёж', 'Яков'] — правильный порядок
// Сортировка нечувствительная к регистру
const ci = new Intl.Collator('ru-RU', { sensitivity: 'base' })
console.log(['Борис', 'анна', 'Алексей'].sort(ci.compare))
// ['Алексей', 'анна', 'Борис']const pr = new Intl.PluralRules('ru-RU')
// Определяет форму слова
function formatItems(count) {
const rule = pr.select(count)
const forms = {
one: 'товар', // 1 товар
few: 'товара', // 2, 3, 4 товара
many: 'товаров', // 5, 6, 11 товаров
other: 'товаров',
}
return `${count} ${forms[rule]}`
}
console.log(formatItems(1)) // '1 товар'
console.log(formatItems(3)) // '3 товара'
console.log(formatItems(5)) // '5 товаров'
console.log(formatItems(11)) // '11 товаров'
console.log(formatItems(21)) // '21 товар'Ошибка 1: создают новый Intl-объект при каждом вызове
// Сломано: создание Intl.NumberFormat дорогостоящая операция
function formatPrice(price) {
return new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' }).format(price)
// ^ каждый раз создаётся новый объект
}
// Исправлено: создать один раз
const priceFormatter = new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' })
const formatPrice = (price) => priceFormatter.format(price)Ошибка 2: сортировка строк без Collator
// Сломано: стандартный sort некорректно сортирует кириллицу
const names = ['Яша', 'Анна', 'Ёж', 'Борис']
names.sort() // ['Анна', 'Борис', 'Ёж', 'Яша'] — Ё может попасть не туда!
// Исправлено:
const collator = new Intl.Collator('ru-RU')
names.sort(collator.compare) // корректный порядокОшибка 3: используют toLocaleDateString() вместо Intl.DateTimeFormat
// Нестабильно: поведение зависит от системных настроек браузера
date.toLocaleDateString() // разный результат на разных машинах
// Исправлено: явная локаль
new Intl.DateTimeFormat('ru-RU', { dateStyle: 'short' }).format(date)
// '04.03.2026' — одинаково вездеФорматирование российских цен, дат и относительного времени через Intl API
// ===== Intl.NumberFormat — цены =====
function formatPrice(amount, currency = 'RUB', locale = 'ru-RU') {
return new Intl.NumberFormat(locale, {
style: 'currency',
currency,
minimumFractionDigits: 0,
maximumFractionDigits: 0,
}).format(amount)
}
console.log('=== Форматирование цен ===')
console.log(formatPrice(1500)) // '1 500 ₽'
console.log(formatPrice(89990)) // '89 990 ₽'
console.log(formatPrice(1234567)) // '1 234 567 ₽'
console.log(formatPrice(99, 'USD', 'en-US')) // '$99'
console.log(formatPrice(50, 'EUR', 'de-DE')) // '50 €'
// ===== Intl.DateTimeFormat — даты =====
function formatDate(date, style = 'short') {
return new Intl.DateTimeFormat('ru-RU', { dateStyle: style }).format(date)
}
const today = new Date('2026-03-04T12:00:00')
console.log('\n=== Форматирование дат ===')
console.log(formatDate(today, 'short')) // '04.03.2026'
console.log(formatDate(today, 'medium')) // '4 мар. 2026 г.'
console.log(formatDate(today, 'long')) // '4 марта 2026 г.'
console.log(formatDate(today, 'full')) // 'среда, 4 марта 2026 г.'
// Дата + время
const withTime = new Intl.DateTimeFormat('ru-RU', {
dateStyle: 'short',
timeStyle: 'short',
}).format(today)
console.log('Дата и время:', withTime) // '04.03.2026, 12:00'
// ===== Intl.RelativeTimeFormat — "N дней назад" =====
function timeAgo(date) {
const now = Date.now()
const diffMs = now - date.getTime()
const diffSec = Math.round(diffMs / 1000)
const diffMin = Math.round(diffSec / 60)
const diffHrs = Math.round(diffMin / 60)
const diffDays = Math.round(diffHrs / 24)
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
if (Math.abs(diffSec) < 60) return rtf.format(-diffSec, 'second')
if (Math.abs(diffMin) < 60) return rtf.format(-diffMin, 'minute')
if (Math.abs(diffHrs) < 24) return rtf.format(-diffHrs, 'hour')
return rtf.format(-diffDays, 'day')
}
console.log('\n=== Относительное время ===')
const rtf = new Intl.RelativeTimeFormat('ru-RU', { numeric: 'auto' })
console.log(rtf.format(-2, 'minute')) // '2 минуты назад'
console.log(rtf.format(-1, 'hour')) // 'час назад'
console.log(rtf.format(-1, 'day')) // 'вчера'
console.log(rtf.format(-5, 'day')) // '5 дней назад'
console.log(rtf.format(3, 'day')) // 'через 3 дня'
console.log(rtf.format(1, 'week')) // 'через 1 неделю'
// ===== Intl.PluralRules — правила множественного числа =====
console.log('\n=== Множественное число ===')
const pr = new Intl.PluralRules('ru-RU')
function items(n) {
const rule = pr.select(n)
const forms = { one: 'товар', few: 'товара', many: 'товаров', other: 'товаров' }
return `${n} ${forms[rule]}`
}
;[1, 2, 5, 11, 21, 100, 101].forEach(n => console.log(items(n)))
// '1 товар', '2 товара', '5 товаров', '11 товаров', '21 товар', '100 товаров', '101 товар'Напиши функцию formatProduct(price, currency, date), которая принимает цену (число), валюту (строку, например "RUB") и дату добавления (объект Date), и возвращает строку вида "1 500 ₽ · добавлен 04.03.2026". Для форматирования используй Intl.NumberFormat и Intl.DateTimeFormat с локалью ru-RU.
Intl.NumberFormat('ru-RU', { style: 'currency', currency }).format(price) + ' · добавлен ' + Intl.DateTimeFormat('ru-RU', { dateStyle: 'short' }).format(date)