@jarenjs/locales 0.46.4 → 0.49.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ //@ts-check
2
+
3
+ /**
4
+ * The opt-in `Intl` calendar language: the same compiled shape
5
+ * `compileDateLocale` returns, sourced from the platform's ICU data
6
+ * instead of from this repository.
7
+ *
8
+ * It is a separate module and an explicit call because it is the one
9
+ * thing the default may not be. ICU output moves between Node versions
10
+ * and between a browser and a server, so a page rendered from it cannot
11
+ * be compared byte for byte - which is exactly what this site's
12
+ * server-rendering tests do. What it buys instead is every locale the
13
+ * host ships rather than the eleven this repository owns, so a host that
14
+ * needs breadth more than byte stability constructs one and passes it
15
+ * where a `compileDateLocale` result would go.
16
+ *
17
+ * Nothing here runs at module load: the `Intl` objects are built inside
18
+ * the factory, so importing the module allocates nothing and a bundle
19
+ * that never calls it carries no ICU work.
20
+ */
21
+
22
+ import { checkRelativeArguments } from './helpers.js';
23
+
24
+ import { dateMessagesEn } from './dates.js';
25
+
26
+ /** 1970-01-04 was a Sunday: the weekday index every name array starts at. */
27
+ const FIRST_SUNDAY = Date.UTC(1970, 0, 4);
28
+
29
+ /** A reference year; the 15th keeps every month clear of a leap edge. */
30
+ const REFERENCE_YEAR = 2021;
31
+
32
+ /**
33
+ * Read the display names this repository owns, keyed by format name.
34
+ * ICU has no equivalent data - a format name is a JSON Schema fact, not
35
+ * a calendar one - so the `Intl` provider carries the English ones and
36
+ * takes an override.
37
+ * @returns {Record<string, string>}
38
+ */
39
+ function englishFormatNames() {
40
+ /** @type {Record<string, string>} */
41
+ const names = {};
42
+ for (const key of Object.keys(dateMessagesEn)) {
43
+ if (key.startsWith('format/name/'))
44
+ names[key.slice('format/name/'.length)] = /** @type {string} */ (dateMessagesEn[key]);
45
+ }
46
+ return names;
47
+ }
48
+
49
+ /**
50
+ * Read one array of names out of an `Intl.DateTimeFormat`. The calendar
51
+ * is pinned to `gregory` because this suite's dates are proleptic
52
+ * Gregorian; a locale whose default calendar is another one would
53
+ * otherwise name months that no `yyyy-MM-dd` value has.
54
+ * @param {string} locale - The BCP 47 locale tag
55
+ * @param {Intl.DateTimeFormatOptions} options - The name width to read
56
+ * @param {number[]} instants - One reference instant per name
57
+ * @returns {string[]}
58
+ */
59
+ function namesFrom(locale, options, instants) {
60
+ const format = new Intl.DateTimeFormat(locale, {
61
+ ...options,
62
+ calendar: 'gregory',
63
+ timeZone: 'UTC',
64
+ });
65
+ return instants.map((at) => format.format(at));
66
+ }
67
+
68
+ /**
69
+ * Read the two day-period markers. `formatToParts` rather than `format`:
70
+ * the marker is one part of a formatted hour, and slicing it out of the
71
+ * whole string would depend on where the locale puts it.
72
+ * @param {string} locale - The BCP 47 locale tag
73
+ * @returns {string[]} The AM and PM markers
74
+ */
75
+ function meridiemFrom(locale) {
76
+ const format = new Intl.DateTimeFormat(locale, {
77
+ hour: 'numeric',
78
+ hour12: true,
79
+ calendar: 'gregory',
80
+ timeZone: 'UTC',
81
+ });
82
+ const read = (hour) => {
83
+ const part = format.formatToParts(Date.UTC(REFERENCE_YEAR, 0, 1, hour))
84
+ .find((p) => p.type === 'dayPeriod');
85
+ return part === undefined ? '' : part.value;
86
+ };
87
+ const am = read(6);
88
+ const pm = read(18);
89
+ // a locale the host renders on a 24-hour clock has no marker to read;
90
+ // the two tokens still have to render something a pattern can print
91
+ return [am === '' ? 'AM' : am, pm === '' ? 'PM' : pm];
92
+ }
93
+
94
+ /**
95
+ * Build a calendar language from the host's ICU data.
96
+ *
97
+ * The returned shape is the one `compileDateLocale` returns, so a
98
+ * consumer takes either without knowing which: `names` plugs into
99
+ * `compileDateFormat`, and `relative` takes the same signed amount and
100
+ * refuses the same arguments. What differs is only where the text comes
101
+ * from, and therefore whether it is stable across host versions.
102
+ *
103
+ * @param {string} locale - A BCP 47 locale tag (`'nl-NL'`)
104
+ * @param {{formatNames?: Record<string, string>}} [options] - Date-format display names, merged over the English ones
105
+ * @returns {Readonly<import('./dates.js').DateLocale>} The frozen calendar language
106
+ * @example
107
+ * const dates = createIntlDateLocale('hu-HU');
108
+ * compileDateFormat('yyyy MMMM d.', dates.names);
109
+ * dates.relative(-3, 'day'); // '3 napja'
110
+ */
111
+ export function createIntlDateLocale(locale, options = undefined) {
112
+ const monthInstants = [];
113
+ for (let m = 0; m < 12; ++m) monthInstants.push(Date.UTC(REFERENCE_YEAR, m, 15));
114
+ const weekdayInstants = [];
115
+ for (let d = 0; d < 7; ++d) weekdayInstants.push(FIRST_SUNDAY + d * 86400000);
116
+
117
+ const names = Object.freeze({
118
+ months: Object.freeze(namesFrom(locale, { month: 'long' }, monthInstants)),
119
+ monthsShort: Object.freeze(namesFrom(locale, { month: 'short' }, monthInstants)),
120
+ weekdays: Object.freeze(namesFrom(locale, { weekday: 'long' }, weekdayInstants)),
121
+ weekdaysShort: Object.freeze(namesFrom(locale, { weekday: 'short' }, weekdayInstants)),
122
+ meridiem: /** @type {[string, string]} */ (/** @type {unknown} */ (
123
+ Object.freeze(meridiemFrom(locale)))),
124
+ });
125
+
126
+ // one formatter per numeric mode, built here rather than per phrase
127
+ const always = new Intl.RelativeTimeFormat(locale, { numeric: 'always' });
128
+ const auto = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
129
+
130
+ const formatNames = { ...englishFormatNames(), ...(options?.formatNames) };
131
+
132
+ /**
133
+ * Phrase a signed offset through `Intl.RelativeTimeFormat`.
134
+ * @param {number} amount - Whole units, signed
135
+ * @param {string} unit - One of second|minute|hour|day|week|month|year
136
+ * @param {{numeric?: string}} [callOptions] - `numeric: 'auto'` prefers the named days
137
+ * @returns {string}
138
+ */
139
+ function relative(amount, unit, callOptions = undefined) {
140
+ const numeric = checkRelativeArguments(amount, unit, callOptions);
141
+ const format = numeric === 'auto' ? auto : always;
142
+ // a signed zero is a direction ICU would render, and this contract
143
+ // has only the one zero
144
+ const signed = Object.is(amount, -0) ? 0 : amount;
145
+ return format.format(signed, /** @type {Intl.RelativeTimeFormatUnit} */ (unit));
146
+ }
147
+
148
+ /**
149
+ * The display name of a date/time format.
150
+ * @param {string} format - A format name (`'date-time'`)
151
+ * @returns {string|undefined}
152
+ */
153
+ function formatName(format) {
154
+ return typeof format === 'string' && Object.hasOwn(formatNames, format)
155
+ ? formatNames[format]
156
+ : undefined;
157
+ }
158
+
159
+ return Object.freeze({ names, relative, formatName });
160
+ }
package/src/ja.js CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  formatMessageValue,
24
24
  makeNumberRenderer,
25
25
  makeTypeNamer,
26
+ dateNameEntries,
26
27
  } from './helpers.js';
27
28
 
28
29
  //#region Intl singletons
@@ -162,4 +163,52 @@ export const ja = {
162
163
  'contract/stream-error': '操作 {op} のストリームはサーバーエラーで終了しました ({code})',
163
164
  'contract/heartbeat-missed': '操作 {op} のストリームが {ms} ミリ秒間沈黙しました',
164
165
  //#endregion
166
+
167
+ //#region calendar language (the date names, relative phrasing and
168
+ // format display names of @jarenjs/locales' date adapter)
169
+ // Japanese counts through counters and has no plural, so the wide and
170
+ // abbreviated month names are the same string - as CLDR has them.
171
+ ...dateNameEntries({
172
+ months: [
173
+ '1月', '2月', '3月', '4月', '5月', '6月',
174
+ '7月', '8月', '9月', '10月', '11月', '12月',
175
+ ],
176
+ monthsShort: [
177
+ '1月', '2月', '3月', '4月', '5月', '6月',
178
+ '7月', '8月', '9月', '10月', '11月', '12月',
179
+ ],
180
+ weekdays: [
181
+ '日曜日', '月曜日', '火曜日', '水曜日',
182
+ '木曜日', '金曜日', '土曜日',
183
+ ],
184
+ weekdaysShort: [
185
+ '日', '月', '火', '水', '木', '金', '土',
186
+ ],
187
+ meridiem: ['午前', '午後'],
188
+ }),
189
+ 'date/relative/second/past': (p) => `${num(p.value)} 秒前`,
190
+ 'date/relative/second/future': (p) => `${num(p.value)} 秒後`,
191
+ 'date/relative/minute/past': (p) => `${num(p.value)} 分前`,
192
+ 'date/relative/minute/future': (p) => `${num(p.value)} 分後`,
193
+ 'date/relative/hour/past': (p) => `${num(p.value)} 時間前`,
194
+ 'date/relative/hour/future': (p) => `${num(p.value)} 時間後`,
195
+ 'date/relative/day/past': (p) => `${num(p.value)} 日前`,
196
+ 'date/relative/day/future': (p) => `${num(p.value)} 日後`,
197
+ 'date/relative/week/past': (p) => `${num(p.value)} 週間前`,
198
+ 'date/relative/week/future': (p) => `${num(p.value)} 週間後`,
199
+ 'date/relative/month/past': (p) => `${num(p.value)} か月前`,
200
+ 'date/relative/month/future': (p) => `${num(p.value)} か月後`,
201
+ 'date/relative/year/past': (p) => `${num(p.value)} 年前`,
202
+ 'date/relative/year/future': (p) => `${num(p.value)} 年後`,
203
+ 'date/relative/now': '今',
204
+ 'date/relative/yesterday': '昨日',
205
+ 'date/relative/today': '今日',
206
+ 'date/relative/tomorrow': '明日',
207
+ 'format/name/date': '日付',
208
+ 'format/name/time': '時刻',
209
+ 'format/name/date-time': '日時',
210
+ 'format/name/iso-date': 'ISO 日付',
211
+ 'format/name/iso-time': 'ISO 時刻',
212
+ 'format/name/iso-date-time': 'ISO 日時',
213
+ //#endregion
165
214
  };
package/src/ko.js CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  formatMessageValue,
27
27
  makeNumberRenderer,
28
28
  makeTypeNamer,
29
+ dateNameEntries,
29
30
  } from './helpers.js';
30
31
 
31
32
  //#region Intl singletons
@@ -165,4 +166,52 @@ export const ko = {
165
166
  'contract/stream-error': '작업 {op}의 스트림이 서버 오류로 종료되었습니다 ({code})',
166
167
  'contract/heartbeat-missed': '작업 {op}의 스트림이 {ms}ms 동안 침묵했습니다',
167
168
  //#endregion
169
+
170
+ //#region calendar language (the date names, relative phrasing and
171
+ // format display names of @jarenjs/locales' date adapter)
172
+ // Korean has no plural and sets no space between a numeral and its
173
+ // counter, the way the rest of this pack already counts characters.
174
+ ...dateNameEntries({
175
+ months: [
176
+ '1월', '2월', '3월', '4월', '5월', '6월',
177
+ '7월', '8월', '9월', '10월', '11월', '12월',
178
+ ],
179
+ monthsShort: [
180
+ '1월', '2월', '3월', '4월', '5월', '6월',
181
+ '7월', '8월', '9월', '10월', '11월', '12월',
182
+ ],
183
+ weekdays: [
184
+ '일요일', '월요일', '화요일', '수요일',
185
+ '목요일', '금요일', '토요일',
186
+ ],
187
+ weekdaysShort: [
188
+ '일', '월', '화', '수', '목', '금', '토',
189
+ ],
190
+ meridiem: ['오전', '오후'],
191
+ }),
192
+ 'date/relative/second/past': (p) => `${num(p.value)}초 전`,
193
+ 'date/relative/second/future': (p) => `${num(p.value)}초 후`,
194
+ 'date/relative/minute/past': (p) => `${num(p.value)}분 전`,
195
+ 'date/relative/minute/future': (p) => `${num(p.value)}분 후`,
196
+ 'date/relative/hour/past': (p) => `${num(p.value)}시간 전`,
197
+ 'date/relative/hour/future': (p) => `${num(p.value)}시간 후`,
198
+ 'date/relative/day/past': (p) => `${num(p.value)}일 전`,
199
+ 'date/relative/day/future': (p) => `${num(p.value)}일 후`,
200
+ 'date/relative/week/past': (p) => `${num(p.value)}주 전`,
201
+ 'date/relative/week/future': (p) => `${num(p.value)}주 후`,
202
+ 'date/relative/month/past': (p) => `${num(p.value)}개월 전`,
203
+ 'date/relative/month/future': (p) => `${num(p.value)}개월 후`,
204
+ 'date/relative/year/past': (p) => `${num(p.value)}년 전`,
205
+ 'date/relative/year/future': (p) => `${num(p.value)}년 후`,
206
+ 'date/relative/now': '지금',
207
+ 'date/relative/yesterday': '어제',
208
+ 'date/relative/today': '오늘',
209
+ 'date/relative/tomorrow': '내일',
210
+ 'format/name/date': '날짜',
211
+ 'format/name/time': '시간',
212
+ 'format/name/date-time': '날짜 및 시간',
213
+ 'format/name/iso-date': 'ISO 날짜',
214
+ 'format/name/iso-time': 'ISO 시간',
215
+ 'format/name/iso-date-time': 'ISO 날짜 및 시간',
216
+ //#endregion
168
217
  };
package/src/nl.js CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  makeNumberRenderer,
24
24
  makePluralPicker,
25
25
  makeTypeNamer,
26
+ dateNameEntries,
26
27
  } from './helpers.js';
27
28
 
28
29
  //#region Intl singletons
@@ -166,4 +167,52 @@ export const nl = {
166
167
  'contract/stream-error': 'de stream van operatie {op} is geëindigd met een serverfout ({code})',
167
168
  'contract/heartbeat-missed': 'de stream van operatie {op} is {ms} ms stil gebleven',
168
169
  //#endregion
170
+
171
+ //#region calendar language (the date names, relative phrasing and
172
+ // format display names of @jarenjs/locales' date adapter)
173
+ // 'uur' and 'jaar' do not take a plural after a numeral, so those two
174
+ // units repeat the same noun rather than pretending to agree.
175
+ ...dateNameEntries({
176
+ months: [
177
+ 'januari', 'februari', 'maart', 'april', 'mei', 'juni',
178
+ 'juli', 'augustus', 'september', 'oktober', 'november', 'december',
179
+ ],
180
+ monthsShort: [
181
+ 'jan', 'feb', 'mrt', 'apr', 'mei', 'jun',
182
+ 'jul', 'aug', 'sep', 'okt', 'nov', 'dec',
183
+ ],
184
+ weekdays: [
185
+ 'zondag', 'maandag', 'dinsdag', 'woensdag',
186
+ 'donderdag', 'vrijdag', 'zaterdag',
187
+ ],
188
+ weekdaysShort: [
189
+ 'zo', 'ma', 'di', 'wo', 'do', 'vr', 'za',
190
+ ],
191
+ meridiem: ['a.m.', 'p.m.'],
192
+ }),
193
+ 'date/relative/second/past': (p) => `${num(p.value)} ${plural(p.value, 'seconde', 'seconden')} geleden`,
194
+ 'date/relative/second/future': (p) => `over ${num(p.value)} ${plural(p.value, 'seconde', 'seconden')}`,
195
+ 'date/relative/minute/past': (p) => `${num(p.value)} ${plural(p.value, 'minuut', 'minuten')} geleden`,
196
+ 'date/relative/minute/future': (p) => `over ${num(p.value)} ${plural(p.value, 'minuut', 'minuten')}`,
197
+ 'date/relative/hour/past': (p) => `${num(p.value)} ${plural(p.value, 'uur', 'uur')} geleden`,
198
+ 'date/relative/hour/future': (p) => `over ${num(p.value)} ${plural(p.value, 'uur', 'uur')}`,
199
+ 'date/relative/day/past': (p) => `${num(p.value)} ${plural(p.value, 'dag', 'dagen')} geleden`,
200
+ 'date/relative/day/future': (p) => `over ${num(p.value)} ${plural(p.value, 'dag', 'dagen')}`,
201
+ 'date/relative/week/past': (p) => `${num(p.value)} ${plural(p.value, 'week', 'weken')} geleden`,
202
+ 'date/relative/week/future': (p) => `over ${num(p.value)} ${plural(p.value, 'week', 'weken')}`,
203
+ 'date/relative/month/past': (p) => `${num(p.value)} ${plural(p.value, 'maand', 'maanden')} geleden`,
204
+ 'date/relative/month/future': (p) => `over ${num(p.value)} ${plural(p.value, 'maand', 'maanden')}`,
205
+ 'date/relative/year/past': (p) => `${num(p.value)} ${plural(p.value, 'jaar', 'jaar')} geleden`,
206
+ 'date/relative/year/future': (p) => `over ${num(p.value)} ${plural(p.value, 'jaar', 'jaar')}`,
207
+ 'date/relative/now': 'nu',
208
+ 'date/relative/yesterday': 'gisteren',
209
+ 'date/relative/today': 'vandaag',
210
+ 'date/relative/tomorrow': 'morgen',
211
+ 'format/name/date': 'datum',
212
+ 'format/name/time': 'tijd',
213
+ 'format/name/date-time': 'datum en tijd',
214
+ 'format/name/iso-date': 'ISO-datum',
215
+ 'format/name/iso-time': 'ISO-tijd',
216
+ 'format/name/iso-date-time': 'ISO-datum en -tijd',
217
+ //#endregion
169
218
  };
package/src/pt.js CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  makeNumberRenderer,
25
25
  makePluralPicker,
26
26
  makeTypeNamer,
27
+ dateNameEntries,
27
28
  } from './helpers.js';
28
29
 
29
30
  //#region Intl singletons
@@ -167,4 +168,50 @@ export const pt = {
167
168
  'contract/stream-error': 'o fluxo da operação {op} terminou com um erro do servidor ({code})',
168
169
  'contract/heartbeat-missed': 'o fluxo da operação {op} ficou em silêncio por {ms} ms',
169
170
  //#endregion
171
+
172
+ //#region calendar language (the date names, relative phrasing and
173
+ // format display names of @jarenjs/locales' date adapter)
174
+ ...dateNameEntries({
175
+ months: [
176
+ 'janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho',
177
+ 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro',
178
+ ],
179
+ monthsShort: [
180
+ 'jan.', 'fev.', 'mar.', 'abr.', 'mai.', 'jun.',
181
+ 'jul.', 'ago.', 'set.', 'out.', 'nov.', 'dez.',
182
+ ],
183
+ weekdays: [
184
+ 'domingo', 'segunda-feira', 'terça-feira', 'quarta-feira',
185
+ 'quinta-feira', 'sexta-feira', 'sábado',
186
+ ],
187
+ weekdaysShort: [
188
+ 'dom.', 'seg.', 'ter.', 'qua.', 'qui.', 'sex.', 'sáb.',
189
+ ],
190
+ meridiem: ['a.m.', 'p.m.'],
191
+ }),
192
+ 'date/relative/second/past': (p) => `há ${num(p.value)} ${plural(p.value, 'segundo', 'segundos')}`,
193
+ 'date/relative/second/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'segundo', 'segundos')}`,
194
+ 'date/relative/minute/past': (p) => `há ${num(p.value)} ${plural(p.value, 'minuto', 'minutos')}`,
195
+ 'date/relative/minute/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'minuto', 'minutos')}`,
196
+ 'date/relative/hour/past': (p) => `há ${num(p.value)} ${plural(p.value, 'hora', 'horas')}`,
197
+ 'date/relative/hour/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'hora', 'horas')}`,
198
+ 'date/relative/day/past': (p) => `há ${num(p.value)} ${plural(p.value, 'dia', 'dias')}`,
199
+ 'date/relative/day/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'dia', 'dias')}`,
200
+ 'date/relative/week/past': (p) => `há ${num(p.value)} ${plural(p.value, 'semana', 'semanas')}`,
201
+ 'date/relative/week/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'semana', 'semanas')}`,
202
+ 'date/relative/month/past': (p) => `há ${num(p.value)} ${plural(p.value, 'mês', 'meses')}`,
203
+ 'date/relative/month/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'mês', 'meses')}`,
204
+ 'date/relative/year/past': (p) => `há ${num(p.value)} ${plural(p.value, 'ano', 'anos')}`,
205
+ 'date/relative/year/future': (p) => `dentro de ${num(p.value)} ${plural(p.value, 'ano', 'anos')}`,
206
+ 'date/relative/now': 'agora',
207
+ 'date/relative/yesterday': 'ontem',
208
+ 'date/relative/today': 'hoje',
209
+ 'date/relative/tomorrow': 'amanhã',
210
+ 'format/name/date': 'data',
211
+ 'format/name/time': 'hora',
212
+ 'format/name/date-time': 'data e hora',
213
+ 'format/name/iso-date': 'data ISO',
214
+ 'format/name/iso-time': 'hora ISO',
215
+ 'format/name/iso-date-time': 'data e hora ISO',
216
+ //#endregion
170
217
  };
package/src/ru.js CHANGED
@@ -13,10 +13,10 @@
13
13
  * Globalization mechanics (the pack-authoring pattern - see
14
14
  * packages/validate/docs/ERROR-MESSAGES.md):
15
15
  * - `Intl.PluralRules` picks plural categories. Russian counts split
16
- * one/few/many, but every counted noun here follows "менее/более"
17
- * and therefore reads in the genitive - where only the 'one'
18
- * category differs ("21 символа" / "22 символов"), so a two-form
19
- * helper suffices,
16
+ * one/few/many; every counted noun following "менее/более" reads in
17
+ * the genitive, where only the 'one' category differs ("21 символа" /
18
+ * "22 символов") and a two-form helper suffices, while the
19
+ * relative-time nouns take the accusative and need all three,
20
20
  * - `Intl.NumberFormat` renders numeric limits the Russian way,
21
21
  * - `Intl.ListFormat` renders enum alternatives ("a, b или c"),
22
22
  * all held as module-level singletons (allocation discipline).
@@ -25,8 +25,10 @@
25
25
  import {
26
26
  formatMessageValue,
27
27
  makeNumberRenderer,
28
+ makePluralForms,
28
29
  makePluralPicker,
29
30
  makeTypeNamer,
31
+ dateNameEntries,
30
32
  } from './helpers.js';
31
33
 
32
34
  //#region Intl singletons
@@ -42,6 +44,13 @@ const listFormat = new Intl.ListFormat('ru', { style: 'long', type: 'disjunction
42
44
  */
43
45
  const plural = makePluralPicker(pluralRules);
44
46
 
47
+ /**
48
+ * Pick the accusative form a counted relative-time noun takes: 'one'
49
+ * recurs at 21, 31 ... and 'few' at 22-24, so the three forms follow the
50
+ * CLDR category rather than the number's last digit.
51
+ */
52
+ const caseForms = makePluralForms(pluralRules);
53
+
45
54
  /**
46
55
  * Render a numeric limit through the Russian number format; non-numbers
47
56
  * (e.g. an unresolved $data pointer) render as-is.
@@ -177,4 +186,54 @@ export const ru = {
177
186
  'contract/stream-error': 'поток операции {op} завершился ошибкой сервера ({code})',
178
187
  'contract/heartbeat-missed': 'поток операции {op} молчал {ms} мс',
179
188
  //#endregion
189
+
190
+ //#region calendar language (the date names, relative phrasing and
191
+ // format display names of @jarenjs/locales' date adapter)
192
+ // The month names are the FORMAT (genitive) forms, because the array
193
+ // feeds a date pattern ('d MMMM yyyy' reads '27 июля 2026'), not a
194
+ // standalone label. Both 'назад' and 'через' take the accusative, so
195
+ // one set of counted forms serves past and future alike.
196
+ ...dateNameEntries({
197
+ months: [
198
+ 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня',
199
+ 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря',
200
+ ],
201
+ monthsShort: [
202
+ 'янв.', 'февр.', 'мар.', 'апр.', 'мая', 'июн.',
203
+ 'июл.', 'авг.', 'сент.', 'окт.', 'нояб.', 'дек.',
204
+ ],
205
+ weekdays: [
206
+ 'воскресенье', 'понедельник', 'вторник', 'среда',
207
+ 'четверг', 'пятница', 'суббота',
208
+ ],
209
+ weekdaysShort: [
210
+ 'вс', 'пн', 'вт', 'ср', 'чт', 'пт', 'сб',
211
+ ],
212
+ meridiem: ['AM', 'PM'],
213
+ }),
214
+ 'date/relative/second/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'секунду', few: 'секунды', other: 'секунд' })} назад`,
215
+ 'date/relative/second/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'секунду', few: 'секунды', other: 'секунд' })}`,
216
+ 'date/relative/minute/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'минуту', few: 'минуты', other: 'минут' })} назад`,
217
+ 'date/relative/minute/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'минуту', few: 'минуты', other: 'минут' })}`,
218
+ 'date/relative/hour/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'час', few: 'часа', other: 'часов' })} назад`,
219
+ 'date/relative/hour/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'час', few: 'часа', other: 'часов' })}`,
220
+ 'date/relative/day/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'день', few: 'дня', other: 'дней' })} назад`,
221
+ 'date/relative/day/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'день', few: 'дня', other: 'дней' })}`,
222
+ 'date/relative/week/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'неделю', few: 'недели', other: 'недель' })} назад`,
223
+ 'date/relative/week/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'неделю', few: 'недели', other: 'недель' })}`,
224
+ 'date/relative/month/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'месяц', few: 'месяца', other: 'месяцев' })} назад`,
225
+ 'date/relative/month/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'месяц', few: 'месяца', other: 'месяцев' })}`,
226
+ 'date/relative/year/past': (p) => `${num(p.value)} ${caseForms(p.value, { one: 'год', few: 'года', other: 'лет' })} назад`,
227
+ 'date/relative/year/future': (p) => `через ${num(p.value)} ${caseForms(p.value, { one: 'год', few: 'года', other: 'лет' })}`,
228
+ 'date/relative/now': 'сейчас',
229
+ 'date/relative/yesterday': 'вчера',
230
+ 'date/relative/today': 'сегодня',
231
+ 'date/relative/tomorrow': 'завтра',
232
+ 'format/name/date': 'дата',
233
+ 'format/name/time': 'время',
234
+ 'format/name/date-time': 'дата и время',
235
+ 'format/name/iso-date': 'дата ISO',
236
+ 'format/name/iso-time': 'время ISO',
237
+ 'format/name/iso-date-time': 'дата и время ISO',
238
+ //#endregion
180
239
  };
package/src/tr.js CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  formatMessageValue,
26
26
  makeNumberRenderer,
27
27
  makeTypeNamer,
28
+ dateNameEntries,
28
29
  } from './helpers.js';
29
30
 
30
31
  //#region Intl singletons
@@ -164,4 +165,52 @@ export const tr = {
164
165
  'contract/stream-error': '{op} işleminin akışı bir sunucu hatasıyla sona erdi ({code})',
165
166
  'contract/heartbeat-missed': '{op} işleminin akışı {ms} ms boyunca sessiz kaldı',
166
167
  //#endregion
168
+
169
+ //#region calendar language (the date names, relative phrasing and
170
+ // format display names of @jarenjs/locales' date adapter)
171
+ // Turkish nouns stay singular after a numeral, and 'once'/'sonra'
172
+ // follow the noun, so no suffix ever attaches to interpolated text.
173
+ ...dateNameEntries({
174
+ months: [
175
+ 'Ocak', 'Şubat', 'Mart', 'Nisan', 'Mayıs', 'Haziran',
176
+ 'Temmuz', 'Ağustos', 'Eylül', 'Ekim', 'Kasım', 'Aralık',
177
+ ],
178
+ monthsShort: [
179
+ 'Oca', 'Şub', 'Mar', 'Nis', 'May', 'Haz',
180
+ 'Tem', 'Ağu', 'Eyl', 'Eki', 'Kas', 'Ara',
181
+ ],
182
+ weekdays: [
183
+ 'Pazar', 'Pazartesi', 'Salı', 'Çarşamba',
184
+ 'Perşembe', 'Cuma', 'Cumartesi',
185
+ ],
186
+ weekdaysShort: [
187
+ 'Paz', 'Pzt', 'Sal', 'Çar', 'Per', 'Cum', 'Cmt',
188
+ ],
189
+ meridiem: ['ÖÖ', 'ÖS'],
190
+ }),
191
+ 'date/relative/second/past': (p) => `${num(p.value)} saniye önce`,
192
+ 'date/relative/second/future': (p) => `${num(p.value)} saniye sonra`,
193
+ 'date/relative/minute/past': (p) => `${num(p.value)} dakika önce`,
194
+ 'date/relative/minute/future': (p) => `${num(p.value)} dakika sonra`,
195
+ 'date/relative/hour/past': (p) => `${num(p.value)} saat önce`,
196
+ 'date/relative/hour/future': (p) => `${num(p.value)} saat sonra`,
197
+ 'date/relative/day/past': (p) => `${num(p.value)} gün önce`,
198
+ 'date/relative/day/future': (p) => `${num(p.value)} gün sonra`,
199
+ 'date/relative/week/past': (p) => `${num(p.value)} hafta önce`,
200
+ 'date/relative/week/future': (p) => `${num(p.value)} hafta sonra`,
201
+ 'date/relative/month/past': (p) => `${num(p.value)} ay önce`,
202
+ 'date/relative/month/future': (p) => `${num(p.value)} ay sonra`,
203
+ 'date/relative/year/past': (p) => `${num(p.value)} yıl önce`,
204
+ 'date/relative/year/future': (p) => `${num(p.value)} yıl sonra`,
205
+ 'date/relative/now': 'şimdi',
206
+ 'date/relative/yesterday': 'dün',
207
+ 'date/relative/today': 'bugün',
208
+ 'date/relative/tomorrow': 'yarın',
209
+ 'format/name/date': 'tarih',
210
+ 'format/name/time': 'saat',
211
+ 'format/name/date-time': 'tarih ve saat',
212
+ 'format/name/iso-date': 'ISO tarih',
213
+ 'format/name/iso-time': 'ISO saat',
214
+ 'format/name/iso-date-time': 'ISO tarih ve saat',
215
+ //#endregion
167
216
  };
package/src/zh-tw.js CHANGED
@@ -26,6 +26,7 @@ import {
26
26
  formatMessageValue,
27
27
  makeNumberRenderer,
28
28
  makeTypeNamer,
29
+ dateNameEntries,
29
30
  } from './helpers.js';
30
31
 
31
32
  //#region Intl singletons
@@ -165,4 +166,52 @@ export const zhTW = {
165
166
  'contract/stream-error': '操作 {op} 的串流以伺服器錯誤結束({code})',
166
167
  'contract/heartbeat-missed': '操作 {op} 的串流沉默了 {ms} 毫秒',
167
168
  //#endregion
169
+
170
+ //#region calendar language (the date names, relative phrasing and
171
+ // format display names of @jarenjs/locales' date adapter)
172
+ // No plural, and the wide and abbreviated month names are the same
173
+ // string - as CLDR has them.
174
+ ...dateNameEntries({
175
+ months: [
176
+ '1月', '2月', '3月', '4月', '5月', '6月',
177
+ '7月', '8月', '9月', '10月', '11月', '12月',
178
+ ],
179
+ monthsShort: [
180
+ '1月', '2月', '3月', '4月', '5月', '6月',
181
+ '7月', '8月', '9月', '10月', '11月', '12月',
182
+ ],
183
+ weekdays: [
184
+ '星期日', '星期一', '星期二', '星期三',
185
+ '星期四', '星期五', '星期六',
186
+ ],
187
+ weekdaysShort: [
188
+ '週日', '週一', '週二', '週三', '週四', '週五', '週六',
189
+ ],
190
+ meridiem: ['上午', '下午'],
191
+ }),
192
+ 'date/relative/second/past': (p) => `${num(p.value)} 秒前`,
193
+ 'date/relative/second/future': (p) => `${num(p.value)} 秒後`,
194
+ 'date/relative/minute/past': (p) => `${num(p.value)} 分鐘前`,
195
+ 'date/relative/minute/future': (p) => `${num(p.value)} 分鐘後`,
196
+ 'date/relative/hour/past': (p) => `${num(p.value)} 小時前`,
197
+ 'date/relative/hour/future': (p) => `${num(p.value)} 小時後`,
198
+ 'date/relative/day/past': (p) => `${num(p.value)} 天前`,
199
+ 'date/relative/day/future': (p) => `${num(p.value)} 天後`,
200
+ 'date/relative/week/past': (p) => `${num(p.value)} 週前`,
201
+ 'date/relative/week/future': (p) => `${num(p.value)} 週後`,
202
+ 'date/relative/month/past': (p) => `${num(p.value)} 個月前`,
203
+ 'date/relative/month/future': (p) => `${num(p.value)} 個月後`,
204
+ 'date/relative/year/past': (p) => `${num(p.value)} 年前`,
205
+ 'date/relative/year/future': (p) => `${num(p.value)} 年後`,
206
+ 'date/relative/now': '現在',
207
+ 'date/relative/yesterday': '昨天',
208
+ 'date/relative/today': '今天',
209
+ 'date/relative/tomorrow': '明天',
210
+ 'format/name/date': '日期',
211
+ 'format/name/time': '時間',
212
+ 'format/name/date-time': '日期與時間',
213
+ 'format/name/iso-date': 'ISO 日期',
214
+ 'format/name/iso-time': 'ISO 時間',
215
+ 'format/name/iso-date-time': 'ISO 日期與時間',
216
+ //#endregion
168
217
  };