@reykjavik/webtools 0.1.15 → 0.1.17

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,6 +4,22 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.1.17
8
+
9
+ _2024-03-09_
10
+
11
+ - `@reykjavik/webtools/fixIcelandicLocale`:
12
+ - feat: Add support for `DateTimeFormat`'s `dayPeriod` option
13
+
14
+ ## 0.1.16
15
+
16
+ _2024-03-09_
17
+
18
+ - `@reykjavik/webtools/fixIcelandicLocale`:
19
+ - fix: Add missing `DateTimeFormat.format*ToParts` methods, fix bugs
20
+ - refctor: Reduce code-size and simplify logic by dog-fooding `*ToParts`
21
+ methods internally
22
+
7
23
  ## 0.1.15
8
24
 
9
25
  _2024-03-08_
package/README.md CHANGED
@@ -437,7 +437,7 @@ substituting the `is` locale with `da` (Danish) and apply a few post-hoc fixes
437
437
  to their return values.
438
438
 
439
439
  - `Intl.Collator` and `String.prototype.localeCompare`
440
- - `Intl.NumzberFormat` and `Number.prototype.toLocaleString`
440
+ - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
441
441
  - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
442
442
 
443
443
  This provides usable (but not perfect) results, with some caveats listed
@@ -475,13 +475,12 @@ detection test.)
475
475
  **`Intl.DateTimeFormat` and `toLocaleDateString`:**
476
476
 
477
477
  - The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
478
- print the corresponding Danish initials
478
+ print the corresponding Danish initials.
479
479
  - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
480
480
  will appear in Danish.
481
- - The `timeStyle: 'full'` option prints timezone will appear in Danish
482
- - The `dayPeriod` option is not supported and prints the day-period in Danish.
483
- - Custom formatted `DD.MM.YY` (2-digit year) dates turn into time-like
484
- `DD:MM:YY` strings.
481
+ - The `timeStyle: 'full'` option prints the timezone names in Danish
482
+ - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
483
+ noon.
485
484
 
486
485
  ---
487
486
 
@@ -19,6 +19,7 @@ const mapLocales = (locales) => {
19
19
  }
20
20
  }
21
21
  };
22
+ const combineParts = (parts) => parts.map(({ value }) => value).join('');
22
23
  // ===========================================================================
23
24
  // Collator
24
25
  // ===========================================================================
@@ -43,21 +44,6 @@ _patchedLocaleCompare.$original = _localeCompare;
43
44
  // ===========================================================================
44
45
  // NumberFormat
45
46
  // ===========================================================================
46
- const reformatNumber = function (result) {
47
- if (!this.mapped) {
48
- return result;
49
- }
50
- const options = this.super.resolvedOptions();
51
- if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
52
- if (options.currency === 'DKK') {
53
- return result.replace(/kr\./g, 'DKK');
54
- }
55
- if (options.currency === 'ISK') {
56
- return result.replace(/ISK/g, 'kr.');
57
- }
58
- }
59
- return result;
60
- };
61
47
  const reformatNumberParts = function (parts) {
62
48
  if (!this.mapped) {
63
49
  return parts;
@@ -90,10 +76,10 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
90
76
  const numberFormatProto = {
91
77
  constructor: PatchedNumberFormat,
92
78
  format(value) {
93
- return reformatNumber.call(this, this.super.format(value));
79
+ return combineParts(this.formatToParts(value));
94
80
  },
95
81
  formatRange(value1, value2) {
96
- return reformatNumber.call(this, this.super.formatRange(value1, value2));
82
+ return combineParts(this.formatRangeToParts(value1, value2));
97
83
  },
98
84
  formatToParts(value) {
99
85
  return reformatNumberParts.call(this, this.super.formatToParts(value));
@@ -119,83 +105,91 @@ _patchedToLocaleString.$original = _toLocaleString;
119
105
  // ===========================================================================
120
106
  // DateTimeFormat
121
107
  // ===========================================================================
122
- const months = [
123
- ['januar', 'janúar', true],
124
- ['februar', 'febrúar'],
125
- ['marts', 'mars'],
126
- ['april', 'apríl'],
127
- ['maj', 'maí'],
128
- ['juni', 'júní', true],
129
- ['juli', 'júlí', true],
130
- ['august', 'ágúst', true],
131
- // ['september', 'september'],
132
- ['oktober', 'október'],
133
- ['november', 'nóvember', true],
134
- ['december', 'desember', true],
135
- ];
136
- const weekdays = [
137
- ['mandag', 'mánudagur'],
138
- ['tirsdag', 'þriðjudagur', 4],
139
- ['onsdag', 'miðvikudagur'],
140
- ['torsdag', 'fimmtudagur', 4],
141
- ['fredag', 'föstudagur'],
142
- ['lørdag', 'laugardagur'],
143
- ['søndag', 'sunnudagur'],
144
- ];
145
- const reformatDateTime = function (result) {
146
- if (!this.mapped) {
147
- return result;
148
- }
149
- const options = this.super.resolvedOptions();
150
- let mappedResult = result;
151
- let monthMatches = 0;
152
- for (let i = 0, month; (month = months[i]); i++) {
153
- const [da, is, checkShort] = month;
154
- mappedResult = mappedResult.replace(da, is);
155
- if (checkShort && mappedResult === result) {
156
- mappedResult = mappedResult.replace(da.slice(0, 3), is.slice(0, 3));
108
+ const months = {
109
+ jan: 'janúar',
110
+ feb: 'febrúar',
111
+ mar: 'mars',
112
+ apr: 'apríl',
113
+ maj: 'maí',
114
+ jun: 'júní',
115
+ jul: 'júlí',
116
+ aug: 'ágúst',
117
+ // sep: 'september', // is the same
118
+ okt: 'október',
119
+ nov: 'nóvember',
120
+ dec: 'desember',
121
+ };
122
+ const weekdays = {
123
+ man: 'mánudagur',
124
+ tir: 'þriðjudagur',
125
+ ons: 'miðvikudagur',
126
+ tor: 'fimmtudagur',
127
+ fre: 'föstudagur',
128
+ lør: 'laugardagur',
129
+ søn: 'sunnudagur',
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
+ };
142
+ const partMappers = {
143
+ month: (value) => {
144
+ const isl = months[value.slice(0, 3)];
145
+ if (isl) {
146
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
157
147
  }
158
- if (mappedResult !== result) {
159
- monthMatches++;
160
- if (monthMatches >= 2) {
161
- break;
162
- }
148
+ },
149
+ weekday: (value) => {
150
+ const isl = weekdays[value.slice(0, 3)];
151
+ if (isl) {
152
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
163
153
  }
164
- }
165
- result = mappedResult; // reset result
166
- let weekdayMatches = 0;
167
- for (let i = 0, weekday; (weekday = weekdays[i]); i++) {
168
- const [da, is, shortLength] = weekday;
169
- mappedResult = mappedResult.replace(da, is);
170
- if (mappedResult === result) {
171
- mappedResult = mappedResult.replace(da.slice(0, shortLength || 3), is.slice(0, 3));
154
+ },
155
+ era: (value) => {
156
+ if (!value.endsWith('.')) {
157
+ return value.length === 3
158
+ ? `${value[0]}.k.`
159
+ : value[0] === 'f'
160
+ ? 'fyrir Krist'
161
+ : 'eftir Krist';
172
162
  }
173
- if (mappedResult !== result) {
174
- weekdayMatches++;
175
- if (weekdayMatches >= 2) {
176
- break;
177
- }
163
+ },
164
+ dayPeriod: (value, _, options) => {
165
+ const isl = dayPeriods[value];
166
+ if (isl) {
167
+ const [long, narrow] = isl;
168
+ return options.dayPeriod === 'narrow' ? narrow : long;
178
169
  }
179
- }
180
- result = mappedResult;
181
- if (/Kristus/.test(result)) {
182
- result = result.replace(/før Kristus/g, 'fyrir Krist');
183
- result = result.replace(/efter Kristus/g, 'eftir Krist');
184
- }
185
- result = result.replace(/(f|e)Kr/g, '$1.k.');
186
- result = result.replace(/AM/g, 'f.h.');
187
- result = result.replace(/PM/g, 'e.h.');
188
- // convert timestamps from `00.00` to `00:00`
189
- result = result.replace(/(?:^|\s)\d\d\.\d\d(?:\.\d\d)?(?:,|\s|$)/g, (match) => match.replace(/\./g, ':'));
190
- result = result.replace(/ den/g, 'inn');
191
- return result;
170
+ },
171
+ literal: (value, lastType) => {
172
+ if (value === ' den ') {
173
+ return 'inn ';
174
+ }
175
+ else if (value === '.' && (lastType === 'hour' || lastType === 'minute')) {
176
+ return ':';
177
+ }
178
+ },
192
179
  };
193
180
  const reformatDateTimeParts = function (parts) {
194
181
  if (!this.mapped) {
195
182
  return parts;
196
183
  }
197
184
  const options = this.super.resolvedOptions();
198
- // reformat
185
+ parts.forEach((part, idx) => {
186
+ var _a;
187
+ const mapper = partMappers[part.type];
188
+ const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type, options);
189
+ if (newValue != null) {
190
+ part.value = newValue;
191
+ }
192
+ });
199
193
  return parts;
200
194
  };
201
195
  const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
@@ -220,10 +214,10 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
220
214
  const dateTimeFormatProto = {
221
215
  constructor: PatchedDateTimeFormat,
222
216
  format(value) {
223
- return reformatDateTime.call(this, this.super.format(value));
217
+ return combineParts(this.formatToParts(value));
224
218
  },
225
219
  formatRange(value1, value2) {
226
- return reformatDateTime.call(this, this.super.formatRange(value1, value2));
220
+ return combineParts(this.formatRangeToParts(value1, value2));
227
221
  },
228
222
  formatToParts(value) {
229
223
  return reformatDateTimeParts.call(this, this.super.formatToParts(value));
@@ -22,6 +22,7 @@ const mapLocales = (locales) => {
22
22
  }
23
23
  }
24
24
  };
25
+ const combineParts = (parts) => parts.map(({ value }) => value).join('');
25
26
  // ===========================================================================
26
27
  // Collator
27
28
  // ===========================================================================
@@ -47,21 +48,6 @@ exports._patchedLocaleCompare.$original = _localeCompare;
47
48
  // ===========================================================================
48
49
  // NumberFormat
49
50
  // ===========================================================================
50
- const reformatNumber = function (result) {
51
- if (!this.mapped) {
52
- return result;
53
- }
54
- const options = this.super.resolvedOptions();
55
- if (options.style === 'currency' && options.currencyDisplay === 'symbol') {
56
- if (options.currency === 'DKK') {
57
- return result.replace(/kr\./g, 'DKK');
58
- }
59
- if (options.currency === 'ISK') {
60
- return result.replace(/ISK/g, 'kr.');
61
- }
62
- }
63
- return result;
64
- };
65
51
  const reformatNumberParts = function (parts) {
66
52
  if (!this.mapped) {
67
53
  return parts;
@@ -94,10 +80,10 @@ const PatchedNumberFormat = function NumberFormat(locales, options) {
94
80
  const numberFormatProto = {
95
81
  constructor: PatchedNumberFormat,
96
82
  format(value) {
97
- return reformatNumber.call(this, this.super.format(value));
83
+ return combineParts(this.formatToParts(value));
98
84
  },
99
85
  formatRange(value1, value2) {
100
- return reformatNumber.call(this, this.super.formatRange(value1, value2));
86
+ return combineParts(this.formatRangeToParts(value1, value2));
101
87
  },
102
88
  formatToParts(value) {
103
89
  return reformatNumberParts.call(this, this.super.formatToParts(value));
@@ -124,83 +110,91 @@ exports._patchedToLocaleString.$original = _toLocaleString;
124
110
  // ===========================================================================
125
111
  // DateTimeFormat
126
112
  // ===========================================================================
127
- const months = [
128
- ['januar', 'janúar', true],
129
- ['februar', 'febrúar'],
130
- ['marts', 'mars'],
131
- ['april', 'apríl'],
132
- ['maj', 'maí'],
133
- ['juni', 'júní', true],
134
- ['juli', 'júlí', true],
135
- ['august', 'ágúst', true],
136
- // ['september', 'september'],
137
- ['oktober', 'október'],
138
- ['november', 'nóvember', true],
139
- ['december', 'desember', true],
140
- ];
141
- const weekdays = [
142
- ['mandag', 'mánudagur'],
143
- ['tirsdag', 'þriðjudagur', 4],
144
- ['onsdag', 'miðvikudagur'],
145
- ['torsdag', 'fimmtudagur', 4],
146
- ['fredag', 'föstudagur'],
147
- ['lørdag', 'laugardagur'],
148
- ['søndag', 'sunnudagur'],
149
- ];
150
- const reformatDateTime = function (result) {
151
- if (!this.mapped) {
152
- return result;
153
- }
154
- const options = this.super.resolvedOptions();
155
- let mappedResult = result;
156
- let monthMatches = 0;
157
- for (let i = 0, month; (month = months[i]); i++) {
158
- const [da, is, checkShort] = month;
159
- mappedResult = mappedResult.replace(da, is);
160
- if (checkShort && mappedResult === result) {
161
- mappedResult = mappedResult.replace(da.slice(0, 3), is.slice(0, 3));
113
+ const months = {
114
+ jan: 'janúar',
115
+ feb: 'febrúar',
116
+ mar: 'mars',
117
+ apr: 'apríl',
118
+ maj: 'maí',
119
+ jun: 'júní',
120
+ jul: 'júlí',
121
+ aug: 'ágúst',
122
+ // sep: 'september', // is the same
123
+ okt: 'október',
124
+ nov: 'nóvember',
125
+ dec: 'desember',
126
+ };
127
+ const weekdays = {
128
+ man: 'mánudagur',
129
+ tir: 'þriðjudagur',
130
+ ons: 'miðvikudagur',
131
+ tor: 'fimmtudagur',
132
+ fre: 'föstudagur',
133
+ lør: 'laugardagur',
134
+ søn: 'sunnudagur',
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
+ };
147
+ const partMappers = {
148
+ month: (value) => {
149
+ const isl = months[value.slice(0, 3)];
150
+ if (isl) {
151
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
162
152
  }
163
- if (mappedResult !== result) {
164
- monthMatches++;
165
- if (monthMatches >= 2) {
166
- break;
167
- }
153
+ },
154
+ weekday: (value) => {
155
+ const isl = weekdays[value.slice(0, 3)];
156
+ if (isl) {
157
+ return value.endsWith('.') ? `${isl.slice(0, 3)}.` : isl;
168
158
  }
169
- }
170
- result = mappedResult; // reset result
171
- let weekdayMatches = 0;
172
- for (let i = 0, weekday; (weekday = weekdays[i]); i++) {
173
- const [da, is, shortLength] = weekday;
174
- mappedResult = mappedResult.replace(da, is);
175
- if (mappedResult === result) {
176
- mappedResult = mappedResult.replace(da.slice(0, shortLength || 3), is.slice(0, 3));
159
+ },
160
+ era: (value) => {
161
+ if (!value.endsWith('.')) {
162
+ return value.length === 3
163
+ ? `${value[0]}.k.`
164
+ : value[0] === 'f'
165
+ ? 'fyrir Krist'
166
+ : 'eftir Krist';
177
167
  }
178
- if (mappedResult !== result) {
179
- weekdayMatches++;
180
- if (weekdayMatches >= 2) {
181
- break;
182
- }
168
+ },
169
+ dayPeriod: (value, _, options) => {
170
+ const isl = dayPeriods[value];
171
+ if (isl) {
172
+ const [long, narrow] = isl;
173
+ return options.dayPeriod === 'narrow' ? narrow : long;
183
174
  }
184
- }
185
- result = mappedResult;
186
- if (/Kristus/.test(result)) {
187
- result = result.replace(/før Kristus/g, 'fyrir Krist');
188
- result = result.replace(/efter Kristus/g, 'eftir Krist');
189
- }
190
- result = result.replace(/(f|e)Kr/g, '$1.k.');
191
- result = result.replace(/AM/g, 'f.h.');
192
- result = result.replace(/PM/g, 'e.h.');
193
- // convert timestamps from `00.00` to `00:00`
194
- result = result.replace(/(?:^|\s)\d\d\.\d\d(?:\.\d\d)?(?:,|\s|$)/g, (match) => match.replace(/\./g, ':'));
195
- result = result.replace(/ den/g, 'inn');
196
- return result;
175
+ },
176
+ literal: (value, lastType) => {
177
+ if (value === ' den ') {
178
+ return 'inn ';
179
+ }
180
+ else if (value === '.' && (lastType === 'hour' || lastType === 'minute')) {
181
+ return ':';
182
+ }
183
+ },
197
184
  };
198
185
  const reformatDateTimeParts = function (parts) {
199
186
  if (!this.mapped) {
200
187
  return parts;
201
188
  }
202
189
  const options = this.super.resolvedOptions();
203
- // reformat
190
+ parts.forEach((part, idx) => {
191
+ var _a;
192
+ const mapper = partMappers[part.type];
193
+ const newValue = mapper && mapper(part.value, (_a = parts[idx - 1]) === null || _a === void 0 ? void 0 : _a.type, options);
194
+ if (newValue != null) {
195
+ part.value = newValue;
196
+ }
197
+ });
204
198
  return parts;
205
199
  };
206
200
  const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
@@ -225,10 +219,10 @@ const PatchedDateTimeFormat = function DateTimeFormat(locales, options) {
225
219
  const dateTimeFormatProto = {
226
220
  constructor: PatchedDateTimeFormat,
227
221
  format(value) {
228
- return reformatDateTime.call(this, this.super.format(value));
222
+ return combineParts(this.formatToParts(value));
229
223
  },
230
224
  formatRange(value1, value2) {
231
- return reformatDateTime.call(this, this.super.formatRange(value1, value2));
225
+ return combineParts(this.formatRangeToParts(value1, value2));
232
226
  },
233
227
  formatToParts(value) {
234
228
  return reformatDateTimeParts.call(this, this.super.formatToParts(value));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
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",