diff --git a/.eslintrc.json b/.eslintrc.json index 0d51ff7..c4fee71 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -11,5 +11,11 @@ "rules": { "no-console": 0 } + }, + { + "files": "./lang/*.js", + "rules": { + "quotes": 0 + } }] } diff --git a/README.md b/README.md index 17d4d1f..7fdfd01 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Overview -This library consists of APIs to format and parse numbers, dates, times and file sizes for use in D2L Brightspace. +This library consists of APIs to format and parse numbers, dates, times and file sizes for use in D2L Brightspace. It also provides localization for common terms. > Looking for the older `d2l-intl` library? It's still here [in the `v2.x` branch](https://github.com/BrightspaceUI/intl/tree/v2.x). @@ -28,7 +28,9 @@ import { formatNumber, formatPercent } from '@brightspace-ui/intl/lib/number.js' All of the APIs will automatically detect the document's language via the `lang` attribute on the `` element. They'll also look for various `data-` attributes that will be present in Brightspace pages to access override and timezone information. -## Number Formatting +## Numbers + +### Number Formatting Integer and decimal numbers can be formatted in the user's locale using `formatNumber`. Percentages can be formatted using `formatPercent`. Use the optional `options` parameter for rounding. @@ -65,7 +67,7 @@ const value = formatPercent(0.333, { }); // -> '33.30 %' in en-US ``` -## Number Parsing +### Number Parsing The `parseNumber` method can be used to parse an integer or decimal number written in the user's locale. @@ -75,7 +77,9 @@ import { parseNumber } from '@brightspace-ui/intl/lib/number.js'; const value = parseNumber('-8 942,39'); // -> -8942.39 in fr-CA ``` -## Date/Time Formatting +## Dates & Times + +### Date & Time Formatting Dates and times can be formatted in the user's locale using `formatDate`, `formatTime`, `formatDateTime`, and `formatRelativeDateTime`. @@ -183,7 +187,7 @@ const relativeDateTime = formatRelativeDateTime( ); // If today is 2024-08-22, -> 'last week' in en-US ``` -## Date Parsing +### Date Parsing To parse a date written in the user's locale, use `parseDate`: @@ -196,7 +200,7 @@ date.getMonth(); // -> 8 (months are 0-11) date.getDate(); // -> 23 ``` -## Time Parsing +### Time Parsing To parse a time written in the user's locale, use `parseTime`: @@ -208,7 +212,7 @@ date.getHours(); // -> 14 date.getMinutes(); // -> 5 ``` -## Date/Time Conversion based on user timezone +### Date/Time Conversion based on user timezone To convert an object containing a UTC date to an object containing a local date corresponding to the `data-timezone` attribute: ```javascript @@ -372,7 +376,39 @@ In addition to the Basic Formatting elements, these additional elements may also * `` * `` +### Common Resources + +Some localization resources are common and shared across D2L applications. To use these resources, use the `LocalizeCommon` implemenation: + +```javascript +import { LocalizeCommon } from '@brightspace-ui/intl/lib/localize-commmon.js'; + +const localizerCommon = new LocalizeCommon(); +``` + +#### localizeCharacter + +The localized value of the following characters can be accessed using `localizeCharacter(char)`: +* `'` (apostrophe) +* `&` (ampersand) +* `*` (asterisk) +* `\` (backslash) +* `:` (colon) +* `,` (comma) +* `>` (greater-than sign) +* `<` (less-than sign) +* `#` (number sign) +* `%` (percent sign) +* `|` (pipe) +* `?` (question mark) +* `"` (quotation mark) + +```javascript +const value = localizerCommon.localizeCharacter('&'); // -> 'ampersand' in en-US +``` + ### `onResourcesChange` + Provide an `onResourcesChange` callback function to perform tasks when the document language is changed and updated resources are available: ```javascript diff --git a/intl.serge.json b/intl.serge.json new file mode 100644 index 0000000..ab49bc6 --- /dev/null +++ b/intl.serge.json @@ -0,0 +1,9 @@ +{ + "name": "intl", + "parser_plugin": { + "plugin": "parse_js" + }, + "source_match": "en\\.js$", + "source_dir": "lang", + "output_file_path": "lang/%LANG%.js" + } \ No newline at end of file diff --git a/lang/ar.js b/lang/ar.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/ar.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/cy.js b/lang/cy.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/cy.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/da.js b/lang/da.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/da.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/de.js b/lang/de.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/de.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/en-gb.js b/lang/en-gb.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/en-gb.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/en.js b/lang/en.js new file mode 100644 index 0000000..55af08c --- /dev/null +++ b/lang/en.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", // short name or description of the "'" character + "intl-common:characters:ampersand": "ampersand", // short name or description of the "&" character + "intl-common:characters:asterisk": "asterisk", // short name or description of the "*" character + "intl-common:characters:backslash": "backslash", // short name or description of the "\" character + "intl-common:characters:colon": "colon", // short name or description of the ":" character + "intl-common:characters:comma": "comma", // short name or description of the "," character + "intl-common:characters:greaterThan": "greater-than sign", // short name or description of the ">" character + "intl-common:characters:lessThan": "less-than sign", // short name or description of the "<" character + "intl-common:characters:numberSign": "number sign", // short name or description of the "#" character + "intl-common:characters:percentSign": "percent sign", // short name or description of the "%" character + "intl-common:characters:pipe": "pipe", // short name or description of the "|" character + "intl-common:characters:questionMark": "question mark", // short name or description of the "?" character + "intl-common:characters:quotationMark": "quotation mark", // short name or description of the '"' character +}; diff --git a/lang/es-es.js b/lang/es-es.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/es-es.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/es.js b/lang/es.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/es.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/fr-fr.js b/lang/fr-fr.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/fr-fr.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/fr.js b/lang/fr.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/fr.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/hi.js b/lang/hi.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/hi.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/ja.js b/lang/ja.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/ja.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/ko.js b/lang/ko.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/ko.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/nl.js b/lang/nl.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/nl.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/pt.js b/lang/pt.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/pt.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/sv.js b/lang/sv.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/sv.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/tr.js b/lang/tr.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/tr.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/zh-cn.js b/lang/zh-cn.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/zh-cn.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lang/zh-tw.js b/lang/zh-tw.js new file mode 100644 index 0000000..fd1c3d1 --- /dev/null +++ b/lang/zh-tw.js @@ -0,0 +1,15 @@ +export default { + "intl-common:characters:apostrophe": "apostrophe", + "intl-common:characters:ampersand": "ampersand", + "intl-common:characters:asterisk": "asterisk", + "intl-common:characters:backslash": "backslash", + "intl-common:characters:colon": "colon", + "intl-common:characters:comma": "comma", + "intl-common:characters:greaterThan": "greater-than sign", + "intl-common:characters:lessThan": "less-than sign", + "intl-common:characters:numberSign": "number sign", + "intl-common:characters:percentSign": "percent sign", + "intl-common:characters:pipe": "pipe", + "intl-common:characters:questionMark": "question mark", + "intl-common:characters:quotationMark": "quotation mark", +}; diff --git a/lib/localize-common.js b/lib/localize-common.js new file mode 100644 index 0000000..6d9dbec --- /dev/null +++ b/lib/localize-common.js @@ -0,0 +1,44 @@ +import { Localize } from './localize.js'; + +const characterMap = new Map([ + ['\'', 'apostrophe'], + ['&', 'ampersand'], + ['*', 'asterisk'], + ['\\', 'backslash'], + [':', 'colon'], + [',', 'comma'], + ['>', 'greaterThan'], + ['<', 'lessThan'], + ['#', 'numberSign'], + ['%', 'percentSign'], + ['|', 'pipe'], + ['?', 'questionMark'], + ['"', 'quotationMark'] +]); + +const commonResources = new Map(); +export let commonResourcesImportCount = 0; + +export class LocalizeCommon extends Localize { + + constructor() { + super({ + importFunc: async lang => { + if (commonResources.has(lang)) return commonResources.get(lang); + const resources = (await import(`../lang/${lang}.js`)).default; + commonResourcesImportCount++; + commonResources.set(lang, resources); + return resources; + } + }); + } + + localizeCharacter(char) { + if (!characterMap.has(char)) { + throw new Error(`localizeCharacter() does not support character: "${char}"`); + } + const value = this.localize(`intl-common:characters:${characterMap.get(char)}`); + return value; + } + +} diff --git a/lib/localize.js b/lib/localize.js index c27ae88..06af875 100644 --- a/lib/localize.js +++ b/lib/localize.js @@ -127,8 +127,10 @@ export const getLocalizeClass = (superclass = class {}) => class LocalizeClass e } if (Object.prototype.hasOwnProperty.call(this, 'getLocalizeResources') || Object.prototype.hasOwnProperty.call(this, 'resources')) { const possibleLanguages = this._generatePossibleLanguages(config); - const resourcesPromise = this.getLocalizeResources(possibleLanguages, config); - resourcesLoadedPromises.push(resourcesPromise); + if (config.importFunc) { + const resourcesPromise = this.getLocalizeResources(possibleLanguages, config); + resourcesLoadedPromises.push(resourcesPromise); + } } return Promise.all(resourcesLoadedPromises); } diff --git a/package.json b/package.json index 5339099..b8021ef 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "test": "npm run lint && npm run test:unit" }, "files": [ + "/lang", "/lib", "/helpers" ], diff --git a/test/localize-common.test.js b/test/localize-common.test.js new file mode 100644 index 0000000..dcb9314 --- /dev/null +++ b/test/localize-common.test.js @@ -0,0 +1,36 @@ +import { commonResourcesImportCount, LocalizeCommon } from '../lib/localize-common.js'; +import { expect, fixture } from '@brightspace-ui/testing'; + +describe('LocalizeCommon', () => { + + let localizer; + beforeEach(async() => { + await fixture('
'); + localizer = new LocalizeCommon(); + await localizer.ready; + }); + + afterEach(() => localizer.disconnect()); + + it('should only load common resources once', async() => { + const localizer2 = new LocalizeCommon(); + await localizer2.ready; + expect(commonResourcesImportCount).to.equal(1); + localizer2.disconnect(); + }); + + describe('localizeCharacter', () => { + + it('should localize "&"', async() => { + const localized = localizer.localizeCharacter('&'); + expect(localized).to.equal('ampersand'); + }); + + it('should throw an error for unknown characters', async() => { + expect(() => localizer.localizeCharacter('$')) + .to.throw('localizeCharacter() does not support character: "$"'); + }); + + }); + +});