@brightspace-ui/intl 3.17.1 → 3.17.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.
package/README.md CHANGED
@@ -77,7 +77,7 @@ const value = parseNumber('-8 942,39'); // -> -8942.39 in fr-CA
77
77
 
78
78
  ## Date/Time Formatting
79
79
 
80
- Dates and times can be formatted in the user's locale using `formatDate`, `formatTime` and `formatDateTime`.
80
+ Dates and times can be formatted in the user's locale using `formatDate`, `formatTime`, `formatDateTime`, and `formatRelativeDateTime`.
81
81
 
82
82
  Timestamps (milliseconds since the epoch) can be formatted in the user's locale and timezone using `formatDateTimeFromTimestamp`.
83
83
 
@@ -173,6 +173,16 @@ Options:
173
173
  - **full**: includes timezone. e.g. `'1:25 PM EST'`
174
174
  - **medium** or **short**: excludes timezone e.g. `'1:25 PM'`
175
175
 
176
+ To format a date/time in relative time, use `formatRelativeDateTime`:
177
+
178
+ ```javascript
179
+ import { formatRelativeDateTime } from '@brightspace-ui/intl/lib/dateTime.js';
180
+
181
+ const relativeDateTime = formatRelativeDateTime(
182
+ new Date(2024, 8, 18)
183
+ ); // If today is 2024-08-22, -> 'last week' in en-US
184
+ ```
185
+
176
186
  ## Date Parsing
177
187
 
178
188
  To parse a date written in the user's locale, use `parseDate`:
@@ -258,6 +268,124 @@ const separator = getSeparator({ nonBreaking: true }); // -> ',\xa0' in en-US
258
268
  Options:
259
269
  - **nonBreaking**: a Boolean flag, whether to use non-breaking spaces instead of standard spaces; default is `false`
260
270
 
271
+ ## Language Localization
272
+
273
+ The `Localize` class allows text to be displayed in the user's preferred language.
274
+
275
+ ### Resources
276
+
277
+ Each resource is comprised of a name and a message, which must be provided as a key-value pair in a JavaScript object:
278
+
279
+ ```javascript
280
+ { myMessage: "This is my message" }
281
+ ```
282
+
283
+ #### Name
284
+
285
+ Names should succinctly and uniquely describe the text being localized. `camelCase` is recommended, although `snake_case` and `kebab-case` are also supported.
286
+
287
+ For large projects, resources may be grouped using the `:` character. For example: `parentGroup:subGroup:messageName`.
288
+
289
+ #### Message
290
+
291
+ Messages must conform to the [ICU Message Syntax](https://formatjs.io/docs/core-concepts/icu-syntax/) format. It supports features such as: [simple arguments](https://formatjs.io/docs/core-concepts/icu-syntax/#simple-argument), the [`select` format](https://formatjs.io/docs/core-concepts/icu-syntax/#select-format) and [pluralization](https://formatjs.io/docs/core-concepts/icu-syntax/#plural-format).
292
+
293
+ > **Note:** Avoid using the ICU Message Syntax number, date and time formatting functionality. Brightspace allows customization of how these are localized, so use `formatNumber`, `formatDate` and `formatTime` instead.
294
+
295
+ ### Using `Localize`
296
+
297
+ Import `Localize` and create a new instance. The `importFunc` option is required. It will be passed a language tag which can be used to fetch resources:
298
+
299
+ ```javascript
300
+ import { Localize } from '@brightspace-ui/intl/lib/localize.js';
301
+
302
+ const localizer = new Localize({
303
+ importFunc: async lang => (await import(`../lang/${lang}.js`)).default
304
+ });
305
+ ```
306
+
307
+ Wait for resources to be available before attempting to use them:
308
+ ```javascript
309
+ await localizer.ready;
310
+ ```
311
+
312
+ ### `localize()`
313
+
314
+ The `localize()` method is used to localize a message.
315
+
316
+ If the message contains arguments, provide replacement values in the second parameter:
317
+
318
+ ```javascript
319
+ const helloText = localizer.localize('hello', { firstName: 'Mary' });
320
+ ```
321
+
322
+ ### `localizeHTML()`
323
+
324
+ Rich formatting can be included in messages and safely converted to HTML with the `localizeHTML()` method.
325
+
326
+ #### Basic Formatting
327
+
328
+ The following formatting elements are supported out-of-the-box:
329
+
330
+ * `<p>paragraphs</p>`
331
+ * `line<br></br>breaks` (note the end tag is required)
332
+ * `<b>bold</b>`
333
+ * `<strong>strong</strong>`
334
+ * `<i>italic</i>`
335
+ * `<em>emphasis</em>`
336
+
337
+ Remember that `<strong>` is for content of greater importance (browsers show this visually using bold), while `<b>` only bolds the text visually without increasing its importance.
338
+
339
+ Similarly `<em>` *emphasizes* a particular piece of text (browsers show this visually using italics), whereas `<i>` only italicizes the text visually without emphasis.
340
+
341
+ Example:
342
+
343
+ ```javascript
344
+ {
345
+ myMessage: "This is <b>bold</b> but <em>not</em> all that <strong>important</strong>."
346
+ }
347
+ ```
348
+
349
+ #### Advanced Formatting
350
+
351
+ More advanced formatting can be achieved by providing replacement methods for custom tags, which are similar to arguments:
352
+
353
+ ```javascript
354
+ {
355
+ goHome: "Go <homeLink>home</homeLink>"
356
+ }
357
+ ```
358
+
359
+ Then, import `localizeMarkup`:
360
+ ```javascript
361
+ import { localizeMarkup } from '@brightspace-ui/intl/lib/localize.js';
362
+ ```
363
+
364
+ and provide a tag replacement method:
365
+ ```javascript
366
+ localizer.localizeHTML('goHome', {
367
+ homeLink: chunks => localizeMarkup`<d2l-link href="/home">${chunks}</d2l-link>`
368
+ });
369
+ ```
370
+ In addition to the Basic Formatting elements, these additional elements may also be used in replacement methods:
371
+
372
+ * `<d2l-link>`
373
+ * `<d2l-tooltip-help>`
374
+
375
+ ### `onResourcesChange`
376
+ Provide an `onResourcesChange` callback function to perform tasks when the document language is changed and updated resources are available:
377
+
378
+ ```javascript
379
+ const localizer = new Localize({
380
+ onResourcesChange: () => document.title = localizer.localize('pageTitle')
381
+ });
382
+ ```
383
+
384
+ To stop listening for changes, disconnect the instance:
385
+ ```javascript
386
+ localizer.disconnect()
387
+ ```
388
+
261
389
  ## Developing and Contributing
262
390
 
263
391
  After cloning the repo, run `npm install` to install dependencies.
package/lib/localize.js CHANGED
@@ -41,8 +41,8 @@ export const getLocalizeClass = (superclass = class {}) => class LocalizeClass e
41
41
  const allResources = {};
42
42
  const resolvedLocales = new Set();
43
43
  for (const { language, resources } of localizeResources) {
44
- for (const [key, value] of Object.entries(resources)) {
45
- allResources[key] = { language, value };
44
+ for (const [name, value] of Object.entries(resources)) {
45
+ allResources[name] = { language, value };
46
46
  resolvedLocales.add(language);
47
47
  }
48
48
  }
@@ -80,15 +80,15 @@ export const getLocalizeClass = (superclass = class {}) => class LocalizeClass e
80
80
  this.#connected = false;
81
81
  }
82
82
 
83
- localize(key) {
83
+ localize(name, replacements) {
84
84
 
85
- const { language, value } = this.localize.resources?.[key] ?? {};
85
+ const { language, value } = this.localize.resources?.[name] ?? {};
86
86
  if (!value) return '';
87
87
 
88
88
  let params = {};
89
- if (arguments.length > 1 && arguments[1]?.constructor === Object) {
89
+ if (replacements?.constructor === Object) {
90
90
  // support for key-value replacements as a single arg
91
- params = arguments[1];
91
+ params = replacements;
92
92
  } else {
93
93
  // legacy support for localize-behavior replacements as many args
94
94
  for (let i = 1; i < arguments.length; i += 2) {
@@ -112,9 +112,9 @@ export const getLocalizeClass = (superclass = class {}) => class LocalizeClass e
112
112
  return formattedMessage;
113
113
  }
114
114
 
115
- localizeHTML(key, params = {}) {
115
+ localizeHTML(name, replacements = {}) {
116
116
 
117
- const { language, value } = this.localize.resources?.[key] ?? {};
117
+ const { language, value } = this.localize.resources?.[name] ?? {};
118
118
  if (!value) return '';
119
119
 
120
120
  const translatedMessage = new IntlMessageFormat(value, language);
@@ -127,7 +127,7 @@ export const getLocalizeClass = (superclass = class {}) => class LocalizeClass e
127
127
  i: chunks => LocalizeClass.#localizeMarkup`<i>${chunks}</i>`,
128
128
  p: chunks => LocalizeClass.#localizeMarkup`<p>${chunks}</p>`,
129
129
  strong: chunks => LocalizeClass.#localizeMarkup`<strong>${chunks}</strong>`,
130
- ...params
130
+ ...replacements
131
131
  });
132
132
  validateMarkup(unvalidated);
133
133
  formattedMessage = unvalidated;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brightspace-ui/intl",
3
- "version": "3.17.1",
3
+ "version": "3.17.2",
4
4
  "description": "Internationalization APIs for number, date, time and file size formatting and parsing in D2L Brightspace.",
5
5
  "main": "lib/number.js",
6
6
  "type": "module",