@reykjavik/webtools 0.1.16 → 0.1.18

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.
package/CHANGELOG.md CHANGED
@@ -4,7 +4,14 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.1.16
7
+ ## 0.1.18
8
+
9
+ _2024-03-11_
10
+
11
+ - `@reykjavik/webtools/fixIcelandicLocale`:
12
+ - feat: Patch `Intl.PluralRules` and `Intl.ListFormat`
13
+
14
+ ## 0.1.16 – 0.1.17
8
15
 
9
16
  _2024-03-09_
10
17
 
package/README.md CHANGED
@@ -439,6 +439,8 @@ to their return values.
439
439
  - `Intl.Collator` and `String.prototype.localeCompare`
440
440
  - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
441
441
  - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
442
+ - `Intl.PluralRules`
443
+ - `Intl.ListFormat`
442
444
 
443
445
  This provides usable (but not perfect) results, with some caveats listed
444
446
  below.
@@ -449,7 +451,7 @@ your app's entry point:
449
451
  ```ts
450
452
  import '@reykjavik/webtools/fixIcelandicLocale';
451
453
 
452
- // Then continue with your day and use `localeCompare` and Intl.Collator,
454
+ // Then continue with your day and use `localeCompare` and other Intl.* methods
453
455
  // as you normally would. (See "limitations" below.)
454
456
  ```
455
457
 
@@ -479,7 +481,8 @@ detection test.)
479
481
  - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
480
482
  will appear in Danish.
481
483
  - The `timeStyle: 'full'` option prints the timezone names in Danish
482
- - The `dayPeriod` option is not supported and prints the day-period in Danish.
484
+ - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
485
+ noon.
483
486
 
484
487
  ---
485
488
 
@@ -1,4 +1,4 @@
1
- import { _PatchedCollator, _PatchedDateTimeFormat, _patchedLocaleCompare, _PatchedNumberFormat, _patchedToLocaleDateString, _patchedToLocaleString, } from './fixIcelandicLocale.privates.js';
1
+ import { _PatchedCollator, _PatchedDateTimeFormat, _PatchedListFormat, _patchedLocaleCompare, _PatchedNumberFormat, _PatchedPluralRules, _patchedToLocaleDateString, _patchedToLocaleString, } from './fixIcelandicLocale.privates.js';
2
2
  /*
3
3
  Mantra: Partial Icelandic suppoort is better than none. Partial Icelandic
4
4
  suppoort is better than none. Partial Icelandic suppoort is better than
@@ -12,3 +12,11 @@ if (Intl.Collator.supportedLocalesOf(['is']).length < 1) {
12
12
  Intl.DateTimeFormat = _PatchedDateTimeFormat;
13
13
  Date.prototype.toLocaleDateString = _patchedToLocaleDateString;
14
14
  }
15
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
16
+ if (Intl.ListFormat && Intl.ListFormat.supportedLocalesOf(['is']).length < 1) {
17
+ Intl.ListFormat = _PatchedListFormat;
18
+ }
19
+ if (Intl.PluralRules && Intl.PluralRules.supportedLocalesOf(['is']).length < 1) {
20
+ Intl.PluralRules = _PatchedPluralRules;
21
+ }
22
+ /* eslint-enable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
@@ -43,3 +43,19 @@ export declare const _patchedToLocaleDateString: {
43
43
  (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
44
44
  };
45
45
  };
46
+ export declare const _PatchedPluralRules: {
47
+ (locales?: string | string[] | undefined, options?: Intl.PluralRulesOptions | undefined): Intl.PluralRules;
48
+ new (locales?: string | string[] | undefined, options?: Intl.PluralRulesOptions | undefined): Intl.PluralRules;
49
+ supportedLocalesOf(locales: string | string[], options?: {
50
+ localeMatcher?: "best fit" | "lookup" | undefined;
51
+ } | undefined): string[];
52
+ } & {
53
+ $original: typeof Intl.PluralRules;
54
+ };
55
+ export declare const _PatchedListFormat: {
56
+ new (locales?: string | string[] | undefined, options?: Intl.ListFormatOptions | undefined): Intl.ListFormat;
57
+ prototype: Intl.ListFormat;
58
+ supportedLocalesOf(locales: string | string[], options?: Pick<Intl.ListFormatOptions, "localeMatcher"> | undefined): string[];
59
+ } & {
60
+ $original: typeof Intl.ListFormat;
61
+ };
@@ -1,6 +1,5 @@
1
+ var _a;
1
2
  const _Collator = Intl.Collator;
2
- const _NumberFormat = Intl.NumberFormat;
3
- const _DateTimeFormat = Intl.DateTimeFormat;
4
3
  const mapLocales = (locales) => {
5
4
  locales = typeof locales === 'string' ? [locales] : locales || [];
6
5
  for (let i = 0, loc; (loc = locales[i]); i++) {
@@ -44,6 +43,7 @@ _patchedLocaleCompare.$original = _localeCompare;
44
43
  // ===========================================================================
45
44
  // NumberFormat
46
45
  // ===========================================================================
46
+ const _NumberFormat = Intl.NumberFormat;
47
47
  const reformatNumberParts = function (parts) {
48
48
  if (!this.mapped) {
49
49
  return parts;
@@ -128,11 +128,22 @@ const weekdays = {
128
128
  lør: 'laugardagur',
129
129
  søn: 'sunnudagur',
130
130
  };
131
+ const dayPeriods = {
132
+ AM: ['f.h.'],
133
+ PM: ['e.h.'],
134
+ 'om natten': ['að nóttu', 'n.'],
135
+ // Mismatch at 05:00 — da: 'om morgenen', is: 'að nóttu'
136
+ 'om morgenen': ['að morgni', 'mrg.'],
137
+ 'om formiddagen': ['að morgni', 'mrg.'],
138
+ // Mismatch at 12:00 — da: 'om eftermiddagen', is: 'hádegi'
139
+ 'om eftermiddagen': ['síðdegis', 'sd.'],
140
+ 'om aftenen': ['að kvöldi', 'kv.'],
141
+ };
131
142
  const partMappers = {
132
143
  month: (value) => {
133
- const islMonth = months[value.slice(0, 3)];
134
- if (islMonth) {
135
- return value.endsWith('.') ? `${islMonth.slice(0, 3)}.` : islMonth;
144
+ const isl = months[value.slice(0, 3)];
145
+ if (isl) {
146
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
136
147
  }
137
148
  },
138
149
  weekday: (value) => {
@@ -150,8 +161,12 @@ const partMappers = {
150
161
  : 'eftir Krist';
151
162
  }
152
163
  },
153
- dayPeriod: (value) => {
154
- return { AM: 'f.h.', PM: 'e.h.' }[value] || value;
164
+ dayPeriod: (value, _, options) => {
165
+ const isl = dayPeriods[value];
166
+ if (isl) {
167
+ const [long, narrow] = isl;
168
+ return options.dayPeriod === 'narrow' ? narrow : long;
169
+ }
155
170
  },
156
171
  literal: (value, lastType) => {
157
172
  if (value === ' den ') {
@@ -166,16 +181,18 @@ const reformatDateTimeParts = function (parts) {
166
181
  if (!this.mapped) {
167
182
  return parts;
168
183
  }
184
+ const options = this.super.resolvedOptions();
169
185
  parts.forEach((part, idx) => {
170
186
  var _a;
171
187
  const mapper = partMappers[part.type];
172
- const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type);
188
+ const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type, options);
173
189
  if (newValue != null) {
174
190
  part.value = newValue;
175
191
  }
176
192
  });
177
193
  return parts;
178
194
  };
195
+ const _DateTimeFormat = Intl.DateTimeFormat;
179
196
  const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
180
197
  if (!(this instanceof PatchedDateTimeFormat)) {
181
198
  // @ts-expect-error (YOLO! Can't be arsed)
@@ -224,3 +241,72 @@ export const _patchedToLocaleDateString = function toLocaleDateString(locales, o
224
241
  return _PatchedDateTimeFormat(locales, options).format(this);
225
242
  };
226
243
  _patchedToLocaleDateString.$original = _toLocaleDateString;
244
+ // ===========================================================================
245
+ // PluralRules
246
+ // ===========================================================================
247
+ const _PluralRules = Intl.PluralRules;
248
+ let PatchedPluralRules;
249
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
250
+ if (_PluralRules) {
251
+ PatchedPluralRules = (_a = class PluralRules extends _PluralRules {
252
+ pluralIsl(n) {
253
+ return this.ord ? 'other' : n % 10 !== 1 || n % 100 === 11 ? 'other' : 'one';
254
+ }
255
+ constructor(locales, options) {
256
+ const mappedLocales = mapLocales(locales);
257
+ super(mappedLocales || locales, options);
258
+ this.mapped = !!mappedLocales;
259
+ this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
260
+ }
261
+ select(n) {
262
+ if (this.mapped) {
263
+ // Pluralization function for Icelandic
264
+ // Copied over from https://www.npmjs.com/package/translate.js
265
+ return this.pluralIsl(n);
266
+ }
267
+ return super.select(n);
268
+ }
269
+ selectRange(n, n2) {
270
+ if (this.mapped) {
271
+ return this.pluralIsl(n2);
272
+ }
273
+ // @ts-expect-error (TS doesn't know about the .selectRange() method ...yet?)
274
+ return super.selectRange(n, n2);
275
+ }
276
+ },
277
+ _a.$original = _PluralRules,
278
+ _a);
279
+ }
280
+ export const _PatchedPluralRules = PatchedPluralRules;
281
+ // ===========================================================================
282
+ // ListFormat
283
+ // ===========================================================================
284
+ const _ListFormat = Intl.ListFormat;
285
+ let PatchedListFormat;
286
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
287
+ if (_ListFormat) {
288
+ PatchedListFormat = class ListFormat extends _ListFormat {
289
+ constructor(locales, options) {
290
+ const mappedLocales = mapLocales(locales);
291
+ super(mappedLocales || locales, options);
292
+ this.$original = _ListFormat;
293
+ this.mapped = !!mappedLocales;
294
+ }
295
+ format(list) {
296
+ return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
297
+ }
298
+ formatToParts(list) {
299
+ const parts = super.formatToParts(list);
300
+ if (this.mapped) {
301
+ for (const item of parts) {
302
+ const { value } = item;
303
+ if (item.type === 'literal' && (value === ', el.' || value === ', eller')) {
304
+ item.value = ', eða';
305
+ }
306
+ }
307
+ }
308
+ return parts;
309
+ }
310
+ };
311
+ }
312
+ export const _PatchedListFormat = PatchedListFormat;
@@ -14,3 +14,11 @@ if (Intl.Collator.supportedLocalesOf(['is']).length < 1) {
14
14
  Intl.DateTimeFormat = fixIcelandicLocale_privates_js_1._PatchedDateTimeFormat;
15
15
  Date.prototype.toLocaleDateString = fixIcelandicLocale_privates_js_1._patchedToLocaleDateString;
16
16
  }
17
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
18
+ if (Intl.ListFormat && Intl.ListFormat.supportedLocalesOf(['is']).length < 1) {
19
+ Intl.ListFormat = fixIcelandicLocale_privates_js_1._PatchedListFormat;
20
+ }
21
+ if (Intl.PluralRules && Intl.PluralRules.supportedLocalesOf(['is']).length < 1) {
22
+ Intl.PluralRules = fixIcelandicLocale_privates_js_1._PatchedPluralRules;
23
+ }
24
+ /* eslint-enable @typescript-eslint/no-unnecessary-type-assertion, @typescript-eslint/no-unnecessary-condition */
@@ -43,3 +43,19 @@ export declare const _patchedToLocaleDateString: {
43
43
  (locales?: Intl.LocalesArgument, options?: Intl.DateTimeFormatOptions | undefined): string;
44
44
  };
45
45
  };
46
+ export declare const _PatchedPluralRules: {
47
+ (locales?: string | string[] | undefined, options?: Intl.PluralRulesOptions | undefined): Intl.PluralRules;
48
+ new (locales?: string | string[] | undefined, options?: Intl.PluralRulesOptions | undefined): Intl.PluralRules;
49
+ supportedLocalesOf(locales: string | string[], options?: {
50
+ localeMatcher?: "best fit" | "lookup" | undefined;
51
+ } | undefined): string[];
52
+ } & {
53
+ $original: typeof Intl.PluralRules;
54
+ };
55
+ export declare const _PatchedListFormat: {
56
+ new (locales?: string | string[] | undefined, options?: Intl.ListFormatOptions | undefined): Intl.ListFormat;
57
+ prototype: Intl.ListFormat;
58
+ supportedLocalesOf(locales: string | string[], options?: Pick<Intl.ListFormatOptions, "localeMatcher"> | undefined): string[];
59
+ } & {
60
+ $original: typeof Intl.ListFormat;
61
+ };
@@ -1,9 +1,8 @@
1
1
  "use strict";
2
+ var _a;
2
3
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._patchedToLocaleDateString = exports._PatchedDateTimeFormat = exports._patchedToLocaleString = exports._PatchedNumberFormat = exports._patchedLocaleCompare = exports._PatchedCollator = void 0;
4
+ exports._PatchedListFormat = exports._PatchedPluralRules = exports._patchedToLocaleDateString = exports._PatchedDateTimeFormat = exports._patchedToLocaleString = exports._PatchedNumberFormat = exports._patchedLocaleCompare = exports._PatchedCollator = void 0;
4
5
  const _Collator = Intl.Collator;
5
- const _NumberFormat = Intl.NumberFormat;
6
- const _DateTimeFormat = Intl.DateTimeFormat;
7
6
  const mapLocales = (locales) => {
8
7
  locales = typeof locales === 'string' ? [locales] : locales || [];
9
8
  for (let i = 0, loc; (loc = locales[i]); i++) {
@@ -48,6 +47,7 @@ exports._patchedLocaleCompare.$original = _localeCompare;
48
47
  // ===========================================================================
49
48
  // NumberFormat
50
49
  // ===========================================================================
50
+ const _NumberFormat = Intl.NumberFormat;
51
51
  const reformatNumberParts = function (parts) {
52
52
  if (!this.mapped) {
53
53
  return parts;
@@ -133,11 +133,22 @@ const weekdays = {
133
133
  lør: 'laugardagur',
134
134
  søn: 'sunnudagur',
135
135
  };
136
+ const dayPeriods = {
137
+ AM: ['f.h.'],
138
+ PM: ['e.h.'],
139
+ 'om natten': ['að nóttu', 'n.'],
140
+ // Mismatch at 05:00 — da: 'om morgenen', is: 'að nóttu'
141
+ 'om morgenen': ['að morgni', 'mrg.'],
142
+ 'om formiddagen': ['að morgni', 'mrg.'],
143
+ // Mismatch at 12:00 — da: 'om eftermiddagen', is: 'hádegi'
144
+ 'om eftermiddagen': ['síðdegis', 'sd.'],
145
+ 'om aftenen': ['að kvöldi', 'kv.'],
146
+ };
136
147
  const partMappers = {
137
148
  month: (value) => {
138
- const islMonth = months[value.slice(0, 3)];
139
- if (islMonth) {
140
- return value.endsWith('.') ? `${islMonth.slice(0, 3)}.` : islMonth;
149
+ const isl = months[value.slice(0, 3)];
150
+ if (isl) {
151
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
141
152
  }
142
153
  },
143
154
  weekday: (value) => {
@@ -155,8 +166,12 @@ const partMappers = {
155
166
  : 'eftir Krist';
156
167
  }
157
168
  },
158
- dayPeriod: (value) => {
159
- return { AM: 'f.h.', PM: 'e.h.' }[value] || value;
169
+ dayPeriod: (value, _, options) => {
170
+ const isl = dayPeriods[value];
171
+ if (isl) {
172
+ const [long, narrow] = isl;
173
+ return options.dayPeriod === 'narrow' ? narrow : long;
174
+ }
160
175
  },
161
176
  literal: (value, lastType) => {
162
177
  if (value === ' den ') {
@@ -171,16 +186,18 @@ const reformatDateTimeParts = function (parts) {
171
186
  if (!this.mapped) {
172
187
  return parts;
173
188
  }
189
+ const options = this.super.resolvedOptions();
174
190
  parts.forEach((part, idx) => {
175
191
  var _a;
176
192
  const mapper = partMappers[part.type];
177
- const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type);
193
+ const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type, options);
178
194
  if (newValue != null) {
179
195
  part.value = newValue;
180
196
  }
181
197
  });
182
198
  return parts;
183
199
  };
200
+ const _DateTimeFormat = Intl.DateTimeFormat;
184
201
  const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
185
202
  if (!(this instanceof PatchedDateTimeFormat)) {
186
203
  // @ts-expect-error (YOLO! Can't be arsed)
@@ -230,3 +247,72 @@ const _patchedToLocaleDateString = function toLocaleDateString(locales, options)
230
247
  };
231
248
  exports._patchedToLocaleDateString = _patchedToLocaleDateString;
232
249
  exports._patchedToLocaleDateString.$original = _toLocaleDateString;
250
+ // ===========================================================================
251
+ // PluralRules
252
+ // ===========================================================================
253
+ const _PluralRules = Intl.PluralRules;
254
+ let PatchedPluralRules;
255
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
256
+ if (_PluralRules) {
257
+ PatchedPluralRules = (_a = class PluralRules extends _PluralRules {
258
+ pluralIsl(n) {
259
+ return this.ord ? 'other' : n % 10 !== 1 || n % 100 === 11 ? 'other' : 'one';
260
+ }
261
+ constructor(locales, options) {
262
+ const mappedLocales = mapLocales(locales);
263
+ super(mappedLocales || locales, options);
264
+ this.mapped = !!mappedLocales;
265
+ this.ord = (options === null || options === void 0 ? void 0 : options.type) === 'ordinal';
266
+ }
267
+ select(n) {
268
+ if (this.mapped) {
269
+ // Pluralization function for Icelandic
270
+ // Copied over from https://www.npmjs.com/package/translate.js
271
+ return this.pluralIsl(n);
272
+ }
273
+ return super.select(n);
274
+ }
275
+ selectRange(n, n2) {
276
+ if (this.mapped) {
277
+ return this.pluralIsl(n2);
278
+ }
279
+ // @ts-expect-error (TS doesn't know about the .selectRange() method ...yet?)
280
+ return super.selectRange(n, n2);
281
+ }
282
+ },
283
+ _a.$original = _PluralRules,
284
+ _a);
285
+ }
286
+ exports._PatchedPluralRules = PatchedPluralRules;
287
+ // ===========================================================================
288
+ // ListFormat
289
+ // ===========================================================================
290
+ const _ListFormat = Intl.ListFormat;
291
+ let PatchedListFormat;
292
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
293
+ if (_ListFormat) {
294
+ PatchedListFormat = class ListFormat extends _ListFormat {
295
+ constructor(locales, options) {
296
+ const mappedLocales = mapLocales(locales);
297
+ super(mappedLocales || locales, options);
298
+ this.$original = _ListFormat;
299
+ this.mapped = !!mappedLocales;
300
+ }
301
+ format(list) {
302
+ return this.mapped ? combineParts(this.formatToParts(list)) : super.format(list);
303
+ }
304
+ formatToParts(list) {
305
+ const parts = super.formatToParts(list);
306
+ if (this.mapped) {
307
+ for (const item of parts) {
308
+ const { value } = item;
309
+ if (item.type === 'literal' && (value === ', el.' || value === ', eller')) {
310
+ item.value = ', eða';
311
+ }
312
+ }
313
+ }
314
+ return parts;
315
+ }
316
+ };
317
+ }
318
+ exports._PatchedListFormat = PatchedListFormat;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
5
5
  "main": "index.js",
6
6
  "repository": "ssh://git@github.com:reykjavikcity/webtools.git",