@ultraq/icu-message-formatter 0.12.0-beta.0 → 0.13.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"icu-message-formatter.es.js","sources":["../source/utilities.js","../source/MessageFormatter.js","../source/pluralTypeHandler.js","../source/selectTypeHandler.js"],"sourcesContent":["/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * Most branch-based type handlers are based around \"cases\".\n * For example, `select` and `plural` compare compare a value\n * to \"case keys\" to choose a subtranslation.\n * \n * This util splits \"matches\" portions provided to the aforementioned\n * handlers into case strings, and extracts any prepended arguments\n * (for example, `plural` supports an `offset:n` argument used for\n * populating the magic `#` variable).\n * \n * @param {String} string\n * @return {Object} The `cases` key points to a map of all cases.\n * The `arguments` key points to a list of prepended arguments.\n */\nexport function parseCases(string) {\n\tconst isWhitespace = ch => /\\s/.test(ch);\n\n\tconst args = [];\n\tconst cases = {};\n\n\tlet currTermStart = 0;\n\tlet latestTerm = null;\n\tlet inTerm = false;\n\n\tlet i = 0;\n\twhile (i < string.length) {\n\t\t// Term ended\n\t\tif (inTerm && (isWhitespace(string[i]) || string[i] === '{')) {\n\t\t\tinTerm = false;\n\t\t\tlatestTerm = string.slice(currTermStart, i);\n\n\t\t\t// We want to process the opening char again so the case will be properly registered.\n\t\t\tif (string[i] === '{') {\n\t\t\t\ti--;\n\t\t\t}\n\t\t}\n\n\t\t// New term\n\t\telse if (!inTerm && !isWhitespace(string[i])) {\n\t\t\tconst caseBody = string[i] === '{';\n\n\t\t\t// If there's a previous term, we can either handle a whole\n\t\t\t// case, or add that as an argument.\n\t\t\tif (latestTerm && caseBody) {\n\t\t\t\tconst branchEndIndex = findClosingBracket(string, i);\n\n\t\t\t\tif (branchEndIndex === -1) {\n\t\t\t\t\tthrow new Error(`Unbalanced curly braces in string: \"${string}\"`);\n\t\t\t\t}\n\n\t\t\t\tcases[latestTerm] = string.slice(i + 1, branchEndIndex); // Don't include the braces\n\n\t\t\t\ti = branchEndIndex; // Will be moved up where needed at end of loop.\n\t\t\t\tlatestTerm = null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (latestTerm) {\n\t\t\t\t\targs.push(latestTerm);\n\t\t\t\t\tlatestTerm = null;\n\t\t\t\t}\n\n\t\t\t\tinTerm = true;\n\t\t\t\tcurrTermStart = i;\n\t\t\t}\n\t\t}\n\t\ti++;\n\t}\n\n\tif (inTerm) {\n\t\tlatestTerm = string.slice(currTermStart);\n\t}\n\n\tif (latestTerm) {\n\t\targs.push(latestTerm);\n\t}\n\n\treturn {\n\t\targs,\n\t\tcases\n\t};\n}\n\n/**\n * Finds the index of the matching closing curly bracket, including through\n * strings that could have nested brackets.\n * \n * @param {String} string\n * @param {Number} fromIndex\n * @return {Number} The index of the matching closing bracket, or -1 if no\n * closing bracket could be found.\n */\nexport function findClosingBracket(string, fromIndex) {\n\tlet depth = 0;\n\tfor (let i = fromIndex + 1; i < string.length; i++) {\n\t\tlet char = string.charAt(i);\n\t\tif (char === '}') {\n\t\t\tif (depth === 0) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t\tdepth--;\n\t\t}\n\t\telse if (char === '{') {\n\t\t\tdepth++;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Split a `{key, type, format}` block into those 3 parts, taking into account\n * nested message syntax that can exist in the `format` part.\n * \n * @param {String} block\n * @return {Array}\n * An array with `key`, `type`, and `format` items in that order, if present\n * in the formatted argument block.\n */\nexport function splitFormattedArgument(block) {\n\treturn split(block.slice(1, -1), ',', 3);\n}\n\n/**\n * Like `String.prototype.split()` but where the limit parameter causes the\n * remainder of the string to be grouped together in a final entry.\n * \n * @private\n * @param {String} string\n * @param {String} separator\n * @param {Number} limit\n * @param {Array} [accumulator=[]]\n * @return {Array}\n */\nfunction split(string, separator, limit, accumulator = []) {\n\tif (!string) {\n\t\treturn accumulator;\n\t}\n\tif (limit === 1) {\n\t\taccumulator.push(string);\n\t\treturn accumulator;\n\t}\n\tlet indexOfDelimiter = string.indexOf(separator);\n\tif (indexOfDelimiter === -1) {\n\t\taccumulator.push(string);\n\t\treturn accumulator;\n\t}\n\tlet head = string.substring(0, indexOfDelimiter).trim();\n\tlet tail = string.substring(indexOfDelimiter + separator.length + 1).trim();\n\taccumulator.push(head);\n\treturn split(tail, separator, limit - 1, accumulator);\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {findClosingBracket, splitFormattedArgument} from './utilities.js';\n\nimport {flatten} from '@ultraq/array-utils';\nimport {memoize} from '@ultraq/function-utils';\n\n/**\n * The main class for formatting messages.\n * \n * @author Emanuel Rabina\n */\nexport default class MessageFormatter {\n\n\t/**\n\t * Creates a new formatter that can work using any of the custom type handlers\n\t * you register.\n\t * \n\t * @param {String} locale\n\t * @param {Object} [typeHandlers={}]\n\t * Optional object where the keys are the names of the types to register,\n\t * their values being the functions that will return a nicely formatted\n\t * string for the data and locale they are given.\n\t */\n\tconstructor(locale, typeHandlers = {}) {\n\n\t\tthis.locale = locale;\n\t\tthis.typeHandlers = typeHandlers;\n\t}\n\n\t/**\n\t * Formats an ICU message syntax string using `values` for placeholder data\n\t * and any currently-registered type handlers.\n\t * \n\t * @param {String} message\n\t * @param {Object} [values={}]\n\t * @return {String}\n\t */\n\tformat = memoize((message, values = {}) => {\n\n\t\treturn flatten(this.process(message, values)).join('');\n\t})\n\n\t/**\n\t * Process an ICU message syntax string using `values` for placeholder data\n\t * and any currently-registered type handlers. The result of this method is\n\t * an array of the component parts after they have been processed in turn by\n\t * their own type handlers. This raw output is useful for other renderers,\n\t * eg: React where components can be used instead of being forced to return\n\t * raw strings.\n\t * \n\t * This method is used by {@link MessageFormatter#format} where it acts as a\n\t * string renderer.\n\t * \n\t * @param {String} message\n\t * @param {Object} [values={}]\n\t * @return {Array}\n\t */\n\tprocess(message, values = {}) {\n\n\t\tif (!message) {\n\t\t\treturn [];\n\t\t}\n\n\t\tlet blockStartIndex = message.indexOf('{');\n\t\tif (blockStartIndex !== -1) {\n\t\t\tlet blockEndIndex = findClosingBracket(message, blockStartIndex);\n\t\t\tif (blockEndIndex !== -1) {\n\t\t\t\tlet block = message.substring(blockStartIndex, blockEndIndex + 1);\n\t\t\t\tif (block) {\n\t\t\t\t\tlet result = [];\n\t\t\t\t\tlet head = message.substring(0, blockStartIndex);\n\t\t\t\t\tif (head) {\n\t\t\t\t\t\tresult.push(head);\n\t\t\t\t\t}\n\t\t\t\t\tlet [key, type, format] = splitFormattedArgument(block);\n\t\t\t\t\tlet body = values[key];\n\t\t\t\t\tif (body === null || body === undefined) {\n\t\t\t\t\t\tbody = '';\n\t\t\t\t\t}\n\t\t\t\t\tlet typeHandler = type && this.typeHandlers[type];\n\t\t\t\t\tresult.push(typeHandler ?\n\t\t\t\t\t\ttypeHandler(body, format, this.locale, values, this.process.bind(this)) :\n\t\t\t\t\t\tbody);\n\t\t\t\t\tlet tail = message.substring(blockEndIndex + 1);\n\t\t\t\t\tif (tail) {\n\t\t\t\t\t\tresult.push(this.process(tail, values));\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`Unbalanced curly braces in string: \"${message}\"`);\n\t\t\t}\n\t\t}\n\t\treturn [message];\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {parseCases} from './utilities.js';\n\nlet pluralFormatter;\n\nlet keyCounter = 0;\n\n// All the special keywords that can be used in `plural` blocks for the various branches\nconst ONE = 'one';\nconst OTHER = 'other';\n\n/**\n * @private\n * @param {String} caseBody\n * @param {Number} value\n * @return {Object} {caseBody: string, numberValues: object}\n */\nfunction replaceNumberSign(caseBody, value) {\n\tlet i = 0;\n\tlet output = '';\n\tlet numBraces = 0;\n\tconst numberValues = {};\n\n\twhile (i < caseBody.length) {\n\t\tif (caseBody[i] === '#' && !numBraces) {\n\t\t\tlet keyParam = `__hashToken${keyCounter++}`;\n\t\t\toutput += `{${keyParam}, number}`;\n\t\t\tnumberValues[keyParam] = value;\n\t\t}\n\t\telse {\n\t\t\toutput += caseBody[i];\n\t\t}\n\n\t\tif (caseBody[i] === '{') {\n\t\t\tnumBraces++;\n\t\t}\n\t\telse if (caseBody[i] === '}') {\n\t\t\tnumBraces--;\n\t\t}\n\n\t\ti++;\n\t}\n\n\treturn {\n\t\tcaseBody: output,\n\t\tnumberValues\n\t};\n}\n\n/**\n * Handler for `plural` statements within ICU message syntax strings. Returns\n * a formatted string for the branch that closely matches the current value.\n * \n * See https://formatjs.io/docs/core-concepts/icu-syntax#plural-format for more\n * details on how the `plural` statement works.\n * \n * @param {String} value\n * @param {String} matches\n * @param {String} locale\n * @param {String} values\n * @param {Function} format\n * @return {String}\n */\nexport default function pluralTypeHandler(value, matches = '', locale, values, format) {\n\tconst { args, cases } = parseCases(matches);\n\n\tlet intValue = parseInt(value);\n\n\targs.forEach((arg) => {\n\t\tif (arg.startsWith('offset:')) {\n\t\t\tintValue -= parseInt(arg.slice('offset:'.length));\n\t\t}\n\t});\n\n\tconst keywordPossibilities = [];\n\n\tif ('PluralRules' in Intl) {\n\t\t// Effectively memoize because instantiation of `Int.*` objects is expensive.\n\t\tif (pluralFormatter === undefined || pluralFormatter.resolvedOptions().locale !== locale) {\n\t\t\tpluralFormatter = new Intl.PluralRules(locale);\n\t\t}\n\n\t\tconst pluralKeyword = pluralFormatter.select(intValue);\n\n\t\t// Other is always added last with least priority, so we don't want to add it here.\n\t\tif (pluralKeyword !== OTHER) {\n\t\t\tkeywordPossibilities.push(pluralKeyword);\n\t\t}\n\t}\n\tif (intValue === 1) {\n\t\tkeywordPossibilities.push(ONE);\n\t}\n\tkeywordPossibilities.push(`=${intValue}`, OTHER);\n\n\tfor (let i = 0; i < keywordPossibilities.length; i++) {\n\t\tconst keyword = keywordPossibilities[i];\n\t\tif (keyword in cases) {\n\t\t\tconst { caseBody, numberValues } = replaceNumberSign(cases[keyword], intValue);\n\t\t\treturn format(caseBody, {\n\t\t\t\t...values,\n\t\t\t\t...numberValues\n\t\t\t});\n\t\t}\n\t}\n\n\treturn value;\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {parseCases} from './utilities.js';\n\nconst OTHER = 'other';\n\n/**\n * Handler for `select` statements within ICU message syntax strings. Returns\n * a formatted string for the branch that closely matches the current value.\n * \n * See https://formatjs.io/docs/core-concepts/icu-syntax#select-format for more\n * details on how the `select` statement works.\n * \n * @param {String} value\n * @param {String} matches\n * @param {String} locale\n * @param {String} values\n * @param {Function} format\n * @return {String}\n */\nexport default function selectTypeHandler(value, matches = '', locale, values, format) {\n\tconst { cases } = parseCases(matches);\n\n\tif (value in cases) {\n\t\treturn format(cases[value], values);\n\t}\n\telse if (OTHER in cases) {\n\t\treturn format(cases[OTHER], values);\n\t}\n\n\treturn value;\n}\n"],"names":["parseCases","string","isWhitespace","ch","test","args","cases","currTermStart","latestTerm","inTerm","i","length","slice","caseBody","branchEndIndex","findClosingBracket","Error","push","fromIndex","depth","char","charAt","splitFormattedArgument","block","split","separator","limit","accumulator","indexOfDelimiter","indexOf","head","substring","trim","tail","MessageFormatter","locale","typeHandlers","memoize","message","values","flatten","process","join","blockStartIndex","blockEndIndex","result","key","type","format","body","undefined","typeHandler","bind","pluralFormatter","keyCounter","ONE","OTHER","replaceNumberSign","value","output","numBraces","numberValues","keyParam","pluralTypeHandler","matches","intValue","parseInt","forEach","arg","startsWith","keywordPossibilities","Intl","resolvedOptions","PluralRules","pluralKeyword","select","keyword","selectTypeHandler"],"mappings":"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"}
1
+ {"version":3,"file":"icu-message-formatter.es.js","sources":["../source/utilities.js","../source/MessageFormatter.js","../source/pluralTypeHandler.js","../source/selectTypeHandler.js"],"sourcesContent":["/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * @typedef ParseCasesResult\n * @property {string[]} args\n * A list of prepended arguments.\n * @property {Record<string,string>} cases\n * A map of all cases.\n */\n\n/**\n * Most branch-based type handlers are based around \"cases\". For example,\n * `select` and `plural` compare compare a value to \"case keys\" to choose a\n * subtranslation.\n * \n * This util splits \"matches\" portions provided to the aforementioned handlers\n * into case strings, and extracts any prepended arguments (for example,\n * `plural` supports an `offset:n` argument used for populating the magic `#`\n * variable).\n * \n * @param {string} string\n * @return {ParseCasesResult}\n */\nexport function parseCases(string) {\n\tconst isWhitespace = ch => /\\s/.test(ch);\n\n\tconst args = [];\n\tconst cases = {};\n\n\tlet currTermStart = 0;\n\tlet latestTerm = null;\n\tlet inTerm = false;\n\n\tlet i = 0;\n\twhile (i < string.length) {\n\t\t// Term ended\n\t\tif (inTerm && (isWhitespace(string[i]) || string[i] === '{')) {\n\t\t\tinTerm = false;\n\t\t\tlatestTerm = string.slice(currTermStart, i);\n\n\t\t\t// We want to process the opening char again so the case will be properly registered.\n\t\t\tif (string[i] === '{') {\n\t\t\t\ti--;\n\t\t\t}\n\t\t}\n\n\t\t// New term\n\t\telse if (!inTerm && !isWhitespace(string[i])) {\n\t\t\tconst caseBody = string[i] === '{';\n\n\t\t\t// If there's a previous term, we can either handle a whole\n\t\t\t// case, or add that as an argument.\n\t\t\tif (latestTerm && caseBody) {\n\t\t\t\tconst branchEndIndex = findClosingBracket(string, i);\n\n\t\t\t\tif (branchEndIndex === -1) {\n\t\t\t\t\tthrow new Error(`Unbalanced curly braces in string: \"${string}\"`);\n\t\t\t\t}\n\n\t\t\t\tcases[latestTerm] = string.slice(i + 1, branchEndIndex); // Don't include the braces\n\n\t\t\t\ti = branchEndIndex; // Will be moved up where needed at end of loop.\n\t\t\t\tlatestTerm = null;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tif (latestTerm) {\n\t\t\t\t\targs.push(latestTerm);\n\t\t\t\t\tlatestTerm = null;\n\t\t\t\t}\n\n\t\t\t\tinTerm = true;\n\t\t\t\tcurrTermStart = i;\n\t\t\t}\n\t\t}\n\t\ti++;\n\t}\n\n\tif (inTerm) {\n\t\tlatestTerm = string.slice(currTermStart);\n\t}\n\n\tif (latestTerm) {\n\t\targs.push(latestTerm);\n\t}\n\n\treturn {\n\t\targs,\n\t\tcases\n\t};\n}\n\n/**\n * Finds the index of the matching closing curly bracket, including through\n * strings that could have nested brackets.\n * \n * @param {string} string\n * @param {number} fromIndex\n * @return {number}\n * The index of the matching closing bracket, or -1 if no closing bracket\n * could be found.\n */\nexport function findClosingBracket(string, fromIndex) {\n\tlet depth = 0;\n\tfor (let i = fromIndex + 1; i < string.length; i++) {\n\t\tlet char = string.charAt(i);\n\t\tif (char === '}') {\n\t\t\tif (depth === 0) {\n\t\t\t\treturn i;\n\t\t\t}\n\t\t\tdepth--;\n\t\t}\n\t\telse if (char === '{') {\n\t\t\tdepth++;\n\t\t}\n\t}\n\treturn -1;\n}\n\n/**\n * Split a `{key, type, format}` block into those 3 parts, taking into account\n * nested message syntax that can exist in the `format` part.\n * \n * @param {string} block\n * @return {string[]}\n * An array with `key`, `type`, and `format` items in that order, if present\n * in the formatted argument block.\n */\nexport function splitFormattedArgument(block) {\n\treturn split(block.slice(1, -1), ',', 3);\n}\n\n/**\n * Like `String.prototype.split()` but where the limit parameter causes the\n * remainder of the string to be grouped together in a final entry.\n * \n * @private\n * @param {string} string\n * @param {string} separator\n * @param {number} limit\n * @param {string[]} accumulator\n * @return {string[]}\n */\nfunction split(string, separator, limit, accumulator = []) {\n\tif (!string) {\n\t\treturn accumulator;\n\t}\n\tif (limit === 1) {\n\t\taccumulator.push(string);\n\t\treturn accumulator;\n\t}\n\tlet indexOfDelimiter = string.indexOf(separator);\n\tif (indexOfDelimiter === -1) {\n\t\taccumulator.push(string);\n\t\treturn accumulator;\n\t}\n\tlet head = string.substring(0, indexOfDelimiter).trim();\n\tlet tail = string.substring(indexOfDelimiter + separator.length + 1).trim();\n\taccumulator.push(head);\n\treturn split(tail, separator, limit - 1, accumulator);\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {findClosingBracket, splitFormattedArgument} from './utilities.js';\n\nimport {flatten} from '@ultraq/array-utils';\nimport {memoize} from '@ultraq/function-utils';\n\n/**\n * @typedef {Record<string,any>} FormatValues\n */\n\n/**\n * @callback ProcessFunction\n * @param {string} message\n * @param {FormatValues} [values={}]\n * @return {any[]}\n */\n\n/**\n * @callback TypeHandler\n * @param {any} value\n * The object which matched the key of the block being processed.\n * @param {string} matches\n * Any format options associated with the block being processed.\n * @param {string} locale\n * The locale to use for formatting.\n * @param {FormatValues} values\n * The object of placeholder data given to the original `format`/`process`\n * call.\n * @param {ProcessFunction} process\n * The `process` function itself so that sub-messages can be processed by type\n * handlers.\n * @return {any | any[]}\n */\n\n/**\n * The main class for formatting messages.\n * \n * @author Emanuel Rabina\n */\nexport default class MessageFormatter {\n\n\t/**\n\t * Creates a new formatter that can work using any of the custom type handlers\n\t * you register.\n\t * \n\t * @param {string} locale\n\t * @param {Record<string,TypeHandler>} [typeHandlers]\n\t * Optional object where the keys are the names of the types to register,\n\t * their values being the functions that will return a nicely formatted\n\t * string for the data and locale they are given.\n\t */\n\tconstructor(locale, typeHandlers = {}) {\n\n\t\tthis.locale = locale;\n\t\tthis.typeHandlers = typeHandlers;\n\t}\n\n\t/**\n\t * Formats an ICU message syntax string using `values` for placeholder data\n\t * and any currently-registered type handlers.\n\t * \n\t * @type {(message: string, values?: FormatValues) => string}\n\t */\n\tformat = memoize((message, values = {}) => {\n\n\t\treturn flatten(this.process(message, values)).join('');\n\t});\n\n\t/**\n\t * Process an ICU message syntax string using `values` for placeholder data\n\t * and any currently-registered type handlers. The result of this method is\n\t * an array of the component parts after they have been processed in turn by\n\t * their own type handlers. This raw output is useful for other renderers,\n\t * eg: React where components can be used instead of being forced to return\n\t * raw strings.\n\t * \n\t * This method is used by {@link MessageFormatter#format} where it acts as a\n\t * string renderer.\n\t * \n\t * @param {string} message\n\t * @param {FormatValues} [values]\n\t * @return {any[]}\n\t */\n\tprocess(message, values = {}) {\n\n\t\tif (!message) {\n\t\t\treturn [];\n\t\t}\n\n\t\tlet blockStartIndex = message.indexOf('{');\n\t\tif (blockStartIndex !== -1) {\n\t\t\tlet blockEndIndex = findClosingBracket(message, blockStartIndex);\n\t\t\tif (blockEndIndex !== -1) {\n\t\t\t\tlet block = message.substring(blockStartIndex, blockEndIndex + 1);\n\t\t\t\tif (block) {\n\t\t\t\t\tlet result = [];\n\t\t\t\t\tlet head = message.substring(0, blockStartIndex);\n\t\t\t\t\tif (head) {\n\t\t\t\t\t\tresult.push(head);\n\t\t\t\t\t}\n\t\t\t\t\tlet [key, type, format] = splitFormattedArgument(block);\n\t\t\t\t\tlet body = values[key];\n\t\t\t\t\tif (body === null || body === undefined) {\n\t\t\t\t\t\tbody = '';\n\t\t\t\t\t}\n\t\t\t\t\tlet typeHandler = type && this.typeHandlers[type];\n\t\t\t\t\tresult.push(typeHandler ?\n\t\t\t\t\t\ttypeHandler(body, format, this.locale, values, this.process.bind(this)) :\n\t\t\t\t\t\tbody);\n\t\t\t\t\tlet tail = message.substring(blockEndIndex + 1);\n\t\t\t\t\tif (tail) {\n\t\t\t\t\t\tresult.push(this.process(tail, values));\n\t\t\t\t\t}\n\t\t\t\t\treturn result;\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tthrow new Error(`Unbalanced curly braces in string: \"${message}\"`);\n\t\t\t}\n\t\t}\n\t\treturn [message];\n\t}\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {parseCases} from './utilities.js';\n\nlet pluralFormatter;\n\nlet keyCounter = 0;\n\n// All the special keywords that can be used in `plural` blocks for the various branches\nconst ONE = 'one';\nconst OTHER = 'other';\n\n/**\n * @private\n * @param {string} caseBody\n * @param {number} value\n * @return {{caseBody: string, numberValues: object}}\n */\nfunction replaceNumberSign(caseBody, value) {\n\tlet i = 0;\n\tlet output = '';\n\tlet numBraces = 0;\n\tconst numberValues = {};\n\n\twhile (i < caseBody.length) {\n\t\tif (caseBody[i] === '#' && !numBraces) {\n\t\t\tlet keyParam = `__hashToken${keyCounter++}`;\n\t\t\toutput += `{${keyParam}, number}`;\n\t\t\tnumberValues[keyParam] = value;\n\t\t}\n\t\telse {\n\t\t\toutput += caseBody[i];\n\t\t}\n\n\t\tif (caseBody[i] === '{') {\n\t\t\tnumBraces++;\n\t\t}\n\t\telse if (caseBody[i] === '}') {\n\t\t\tnumBraces--;\n\t\t}\n\n\t\ti++;\n\t}\n\n\treturn {\n\t\tcaseBody: output,\n\t\tnumberValues\n\t};\n}\n\n/**\n * Handler for `plural` statements within ICU message syntax strings. Returns\n * a formatted string for the branch that closely matches the current value.\n * \n * See https://formatjs.io/docs/core-concepts/icu-syntax#plural-format for more\n * details on how the `plural` statement works.\n *\n * @param {string} value\n * @param {string} matches\n * @param {string} locale\n * @param {Record<string,any>} values\n * @param {(message: string, values?: Record<string,any>) => any[]} process\n * @return {any | any[]}\n */\nexport default function pluralTypeHandler(value, matches = '', locale, values, process) {\n\tconst {args, cases} = parseCases(matches);\n\n\tlet intValue = parseInt(value);\n\n\targs.forEach((arg) => {\n\t\tif (arg.startsWith('offset:')) {\n\t\t\tintValue -= parseInt(arg.slice('offset:'.length));\n\t\t}\n\t});\n\n\tconst keywordPossibilities = [];\n\n\tif ('PluralRules' in Intl) {\n\t\t// Effectively memoize because instantiation of `Int.*` objects is expensive.\n\t\tif (pluralFormatter === undefined || pluralFormatter.resolvedOptions().locale !== locale) {\n\t\t\tpluralFormatter = new Intl.PluralRules(locale);\n\t\t}\n\n\t\tconst pluralKeyword = pluralFormatter.select(intValue);\n\n\t\t// Other is always added last with least priority, so we don't want to add it here.\n\t\tif (pluralKeyword !== OTHER) {\n\t\t\tkeywordPossibilities.push(pluralKeyword);\n\t\t}\n\t}\n\tif (intValue === 1) {\n\t\tkeywordPossibilities.push(ONE);\n\t}\n\tkeywordPossibilities.push(`=${intValue}`, OTHER);\n\n\tfor (let i = 0; i < keywordPossibilities.length; i++) {\n\t\tconst keyword = keywordPossibilities[i];\n\t\tif (keyword in cases) {\n\t\t\tconst {caseBody, numberValues} = replaceNumberSign(cases[keyword], intValue);\n\t\t\treturn process(caseBody, {\n\t\t\t\t...values,\n\t\t\t\t...numberValues\n\t\t\t});\n\t\t}\n\t}\n\n\treturn value;\n}\n","/* \n * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)\n * \n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * \n * http://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {parseCases} from './utilities.js';\n\nconst OTHER = 'other';\n\n/**\n * Handler for `select` statements within ICU message syntax strings. Returns\n * a formatted string for the branch that closely matches the current value.\n * \n * See https://formatjs.io/docs/core-concepts/icu-syntax#select-format for more\n * details on how the `select` statement works.\n * \n * @param {string} value\n * @param {string} matches\n * @param {string} locale\n * @param {Record<string,any>} values\n * @param {(message: string, values?: Record<string,any>) => any[]} process\n * @return {any | any[]}\n */\nexport default function selectTypeHandler(value, matches = '', locale, values, process) {\n\tconst {cases} = parseCases(matches);\n\n\tif (value in cases) {\n\t\treturn process(cases[value], values);\n\t}\n\telse if (OTHER in cases) {\n\t\treturn process(cases[OTHER], values);\n\t}\n\n\treturn value;\n}\n"],"names":["parseCases","string","isWhitespace","ch","test","args","cases","currTermStart","latestTerm","inTerm","i","length","slice","caseBody","branchEndIndex","findClosingBracket","Error","push","fromIndex","depth","char","charAt","splitFormattedArgument","block","split","separator","limit","accumulator","arguments","undefined","indexOfDelimiter","indexOf","head","substring","trim","tail","MessageFormatter","constructor","locale","_this","typeHandlers","_defineProperty","memoize","message","values","flatten","process","join","blockStartIndex","blockEndIndex","result","key","type","format","body","typeHandler","bind","pluralFormatter","keyCounter","ONE","OTHER","replaceNumberSign","value","output","numBraces","numberValues","keyParam","pluralTypeHandler","matches","intValue","parseInt","forEach","arg","startsWith","keywordPossibilities","Intl","resolvedOptions","PluralRules","pluralKeyword","select","keyword","selectTypeHandler"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultraq/icu-message-formatter",
3
- "version": "0.12.0-beta.0",
3
+ "version": "0.13.0",
4
4
  "description": "Format ICU message syntax strings from supplied parameters and your own configurable types",
5
5
  "author": "Emanuel Rabina <emanuelrabina@gmail.com> (http://www.ultraq.net.nz/)",
6
6
  "license": "Apache-2.0",
@@ -17,39 +17,47 @@
17
17
  ],
18
18
  "module": "lib/icu-message-formatter.es.js",
19
19
  "main": "lib/icu-message-formatter.cjs.js",
20
+ "types": "index.d.ts",
20
21
  "sideEffects": false,
21
22
  "scripts": {
22
23
  "format": "eslint --fix \"**/*.js\"",
23
24
  "lint": "eslint \"**/*.js\"",
24
25
  "test": "jest",
25
- "build": "rollup --config && rollup --config rollup.config.dist.js",
26
+ "build": "npm run build:lib && npm run build:dist && npm run build:dts",
27
+ "build:lib": "rollup --config --bundleConfigAsCjs",
28
+ "build:dist": "rollup --config rollup.config.dist.js --bundleConfigAsCjs",
29
+ "build:dts": "tsc source/IcuMessageFormatter.js --allowJs --declaration --emitDeclarationOnly --outDir types",
26
30
  "prepublishOnly": "npm run build"
27
31
  },
28
32
  "dependencies": {
29
- "@babel/runtime": "^7.11.2",
30
- "@ultraq/array-utils": "^2.1.0",
31
- "@ultraq/function-utils": "^0.3.0"
33
+ "@babel/runtime": "^7.22.15",
34
+ "@ultraq/array-utils": "^3.0.1",
35
+ "@ultraq/function-utils": "^0.4.0"
32
36
  },
33
37
  "devDependencies": {
34
- "@babel/core": "^7.14.2",
35
- "@babel/plugin-proposal-class-properties": "^7.13.0",
36
- "@babel/plugin-transform-runtime": "^7.14.2",
37
- "@babel/preset-env": "^7.14.2",
38
- "@formatjs/intl-locale": "^2.4.14",
39
- "@formatjs/intl-numberformat": "^6.1.4",
40
- "@rollup/plugin-babel": "^5.3.0",
41
- "@rollup/plugin-commonjs": "^20.0.0",
42
- "@rollup/plugin-node-resolve": "^13.0.5",
43
- "@types/jest": "^27.0.2",
44
- "babel-eslint": "^10.1.0",
45
- "eslint": "^7.32.0",
46
- "eslint-config-ultraq": "^2.4.0",
47
- "eslint-plugin-compat": "^3.13.0",
48
- "jest": "^27.2.1",
49
- "rollup": "^2.57.0",
50
- "rollup-plugin-terser": "^7.0.2"
38
+ "@babel/core": "^7.22.15",
39
+ "@babel/eslint-parser": "^7.22.15",
40
+ "@babel/eslint-plugin": "^7.22.10",
41
+ "@babel/plugin-transform-class-properties": "^7.22.5",
42
+ "@babel/plugin-transform-runtime": "^7.22.15",
43
+ "@babel/preset-env": "^7.22.15",
44
+ "@formatjs/intl-locale": "^3.3.2",
45
+ "@formatjs/intl-numberformat": "^8.7.0",
46
+ "@rollup/plugin-babel": "^6.0.3",
47
+ "@rollup/plugin-commonjs": "^25.0.4",
48
+ "@rollup/plugin-node-resolve": "^15.2.1",
49
+ "@rollup/plugin-terser": "^0.4.3",
50
+ "@types/jest": "^29.5.4",
51
+ "eslint": "^8.48.0",
52
+ "eslint-config-ultraq": "^3.1.0",
53
+ "eslint-plugin-compat": "^4.2.0",
54
+ "eslint-plugin-import": "^2.28.1",
55
+ "eslint-plugin-jsdoc": "^46.5.1",
56
+ "jest": "^29.6.4",
57
+ "rollup": "^3.29.0",
58
+ "typescript": "^5.2.2"
51
59
  },
52
60
  "engines": {
53
- "node": ">=12"
61
+ "node": ">=18"
54
62
  }
55
63
  }
@@ -1,8 +1,7 @@
1
-
2
1
  import babel from '@rollup/plugin-babel';
3
2
  import commonjs from '@rollup/plugin-commonjs';
4
3
  import nodeResolve from '@rollup/plugin-node-resolve';
5
- import {terser} from 'rollup-plugin-terser';
4
+ import terser from '@rollup/plugin-terser';
6
5
 
7
6
  export default {
8
7
  input: 'source/IcuMessageFormatter.js',
@@ -1,9 +1,20 @@
1
+ /*
2
+ * Copyright 2019, Emanuel Rabina (http://www.ultraq.net.nz/)
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
1
16
 
2
17
  export {default as MessageFormatter} from './MessageFormatter.js';
3
18
  export {default as pluralTypeHandler} from './pluralTypeHandler.js';
4
19
  export {default as selectTypeHandler} from './selectTypeHandler.js';
5
- export {
6
- findClosingBracket,
7
- parseCases,
8
- splitFormattedArgument
9
- } from './utilities.js';
20
+ export {findClosingBracket, parseCases, splitFormattedArgument} from './utilities.js';
@@ -19,6 +19,34 @@ import {findClosingBracket, splitFormattedArgument} from './utilities.js';
19
19
  import {flatten} from '@ultraq/array-utils';
20
20
  import {memoize} from '@ultraq/function-utils';
21
21
 
22
+ /**
23
+ * @typedef {Record<string,any>} FormatValues
24
+ */
25
+
26
+ /**
27
+ * @callback ProcessFunction
28
+ * @param {string} message
29
+ * @param {FormatValues} [values={}]
30
+ * @return {any[]}
31
+ */
32
+
33
+ /**
34
+ * @callback TypeHandler
35
+ * @param {any} value
36
+ * The object which matched the key of the block being processed.
37
+ * @param {string} matches
38
+ * Any format options associated with the block being processed.
39
+ * @param {string} locale
40
+ * The locale to use for formatting.
41
+ * @param {FormatValues} values
42
+ * The object of placeholder data given to the original `format`/`process`
43
+ * call.
44
+ * @param {ProcessFunction} process
45
+ * The `process` function itself so that sub-messages can be processed by type
46
+ * handlers.
47
+ * @return {any | any[]}
48
+ */
49
+
22
50
  /**
23
51
  * The main class for formatting messages.
24
52
  *
@@ -30,8 +58,8 @@ export default class MessageFormatter {
30
58
  * Creates a new formatter that can work using any of the custom type handlers
31
59
  * you register.
32
60
  *
33
- * @param {String} locale
34
- * @param {Object} [typeHandlers={}]
61
+ * @param {string} locale
62
+ * @param {Record<string,TypeHandler>} [typeHandlers]
35
63
  * Optional object where the keys are the names of the types to register,
36
64
  * their values being the functions that will return a nicely formatted
37
65
  * string for the data and locale they are given.
@@ -46,14 +74,12 @@ export default class MessageFormatter {
46
74
  * Formats an ICU message syntax string using `values` for placeholder data
47
75
  * and any currently-registered type handlers.
48
76
  *
49
- * @param {String} message
50
- * @param {Object} [values={}]
51
- * @return {String}
77
+ * @type {(message: string, values?: FormatValues) => string}
52
78
  */
53
79
  format = memoize((message, values = {}) => {
54
80
 
55
81
  return flatten(this.process(message, values)).join('');
56
- })
82
+ });
57
83
 
58
84
  /**
59
85
  * Process an ICU message syntax string using `values` for placeholder data
@@ -66,9 +92,9 @@ export default class MessageFormatter {
66
92
  * This method is used by {@link MessageFormatter#format} where it acts as a
67
93
  * string renderer.
68
94
  *
69
- * @param {String} message
70
- * @param {Object} [values={}]
71
- * @return {Array}
95
+ * @param {string} message
96
+ * @param {FormatValues} [values]
97
+ * @return {any[]}
72
98
  */
73
99
  process(message, values = {}) {
74
100
 
@@ -111,10 +111,10 @@ describe('MessageFormatter', function() {
111
111
  }}
112
112
  }`;
113
113
 
114
- expect(formatter.format(message, { 'gender_of_host': 'male', host: 'John', 'num_guests': 115, guest: 'you' })).toBe('John invites you and 115 other people to his party.');
115
- expect(formatter.format(message, { 'gender_of_host': 'female', host: 'John', 'num_guests': 1, guest: 'you' })).toBe('John invites you to her party.');
116
- expect(formatter.format(message, { 'gender_of_host': 'other', host: 'John', 'num_guests': 2, guest: 'you' })).toBe('John invites you and one other person to their party.');
117
- expect(formatter.format(message, { 'gender_of_host': 'other', host: 'John', 'num_guests': 12345, guest: 'you' })).toBe('John invites you and 12345 other people to their party.');
114
+ expect(formatter.format(message, {'gender_of_host': 'male', host: 'John', 'num_guests': 115, guest: 'you'})).toBe('John invites you and 115 other people to his party.');
115
+ expect(formatter.format(message, {'gender_of_host': 'female', host: 'John', 'num_guests': 1, guest: 'you'})).toBe('John invites you to her party.');
116
+ expect(formatter.format(message, {'gender_of_host': 'other', host: 'John', 'num_guests': 2, guest: 'you'})).toBe('John invites you and one other person to their party.');
117
+ expect(formatter.format(message, {'gender_of_host': 'other', host: 'John', 'num_guests': 12345, guest: 'you'})).toBe('John invites you and 12345 other people to their party.');
118
118
  });
119
119
 
120
120
  test('Select inside plural', function() {
@@ -142,12 +142,12 @@ describe('MessageFormatter', function() {
142
142
  }}
143
143
  }`;
144
144
 
145
- expect(formatter.format(message, { 'gender_of_host': 'female', host: 'John', 'num_guests': 1, guest: 'you' })).toBe('John invites you to her party.');
146
- expect(formatter.format(message, { 'gender_of_host': 'other', host: 'John', 'num_guests': 2, guest: 'you' })).toBe('John invites you and one other person to their party.');
145
+ expect(formatter.format(message, {'gender_of_host': 'female', host: 'John', 'num_guests': 1, guest: 'you'})).toBe('John invites you to her party.');
146
+ expect(formatter.format(message, {'gender_of_host': 'other', host: 'John', 'num_guests': 2, guest: 'you'})).toBe('John invites you and one other person to their party.');
147
147
 
148
148
  // number sign only works in the immediately corresponding layer! Can't use a nested one.
149
- expect(formatter.format(message, { 'gender_of_host': 'male', host: 'John', 'num_guests': 115, guest: 'you' })).toBe('John invites you and # other people to his party.');
150
- expect(formatter.format(message, { 'gender_of_host': 'other', host: 'John', 'num_guests': 12345, guest: 'you' })).toBe('John invites you and # other people to their party.');
149
+ expect(formatter.format(message, {'gender_of_host': 'male', host: 'John', 'num_guests': 115, guest: 'you'})).toBe('John invites you and # other people to his party.');
150
+ expect(formatter.format(message, {'gender_of_host': 'other', host: 'John', 'num_guests': 12345, guest: 'you'})).toBe('John invites you and # other people to their party.');
151
151
  });
152
152
  });
153
153
  });
@@ -26,9 +26,9 @@ const OTHER = 'other';
26
26
 
27
27
  /**
28
28
  * @private
29
- * @param {String} caseBody
30
- * @param {Number} value
31
- * @return {Object} {caseBody: string, numberValues: object}
29
+ * @param {string} caseBody
30
+ * @param {number} value
31
+ * @return {{caseBody: string, numberValues: object}}
32
32
  */
33
33
  function replaceNumberSign(caseBody, value) {
34
34
  let i = 0;
@@ -68,16 +68,16 @@ function replaceNumberSign(caseBody, value) {
68
68
  *
69
69
  * See https://formatjs.io/docs/core-concepts/icu-syntax#plural-format for more
70
70
  * details on how the `plural` statement works.
71
- *
72
- * @param {String} value
73
- * @param {String} matches
74
- * @param {String} locale
75
- * @param {String} values
76
- * @param {Function} format
77
- * @return {String}
71
+ *
72
+ * @param {string} value
73
+ * @param {string} matches
74
+ * @param {string} locale
75
+ * @param {Record<string,any>} values
76
+ * @param {(message: string, values?: Record<string,any>) => any[]} process
77
+ * @return {any | any[]}
78
78
  */
79
- export default function pluralTypeHandler(value, matches = '', locale, values, format) {
80
- const { args, cases } = parseCases(matches);
79
+ export default function pluralTypeHandler(value, matches = '', locale, values, process) {
80
+ const {args, cases} = parseCases(matches);
81
81
 
82
82
  let intValue = parseInt(value);
83
83
 
@@ -110,8 +110,8 @@ export default function pluralTypeHandler(value, matches = '', locale, values, f
110
110
  for (let i = 0; i < keywordPossibilities.length; i++) {
111
111
  const keyword = keywordPossibilities[i];
112
112
  if (keyword in cases) {
113
- const { caseBody, numberValues } = replaceNumberSign(cases[keyword], intValue);
114
- return format(caseBody, {
113
+ const {caseBody, numberValues} = replaceNumberSign(cases[keyword], intValue);
114
+ return process(caseBody, {
115
115
  ...values,
116
116
  ...numberValues
117
117
  });
@@ -35,15 +35,15 @@ describe('pluralTypeHandler', function() {
35
35
  }`;
36
36
 
37
37
  test('=n', function() {
38
- let result = formatter.format(message, { value: 2 });
38
+ let result = formatter.format(message, {value: 2});
39
39
  expect(result).toBe('Stop thinking about that delicious duo of donuts');
40
40
 
41
- result = formatter.format(message, { value: 3 });
41
+ result = formatter.format(message, {value: 3});
42
42
  expect(result).toBe('Stop thinking about that triumphant trio of donuts');
43
43
  });
44
44
 
45
45
  test('other', function() {
46
- let result = formatter.format(message, { value: 1000 });
46
+ let result = formatter.format(message, {value: 1000});
47
47
  expect(result).toBe('Stop thinking about those donuts');
48
48
  });
49
49
 
@@ -60,13 +60,13 @@ describe('pluralTypeHandler', function() {
60
60
 
61
61
  test('No matching branch', function() {
62
62
  let message = 'I have {cows, plural, one {a cow} two {some cows}}';
63
- let result = formatter.format(message, { cows: 7 });
63
+ let result = formatter.format(message, {cows: 7});
64
64
  expect(result).toBe('I have 7');
65
65
  });
66
66
 
67
67
  test('Keyword without branch', function() {
68
68
  let message = 'I have {cows, plural, other}';
69
- let result = formatter.format(message, { cows: 7 });
69
+ let result = formatter.format(message, {cows: 7});
70
70
  expect(result).toBe('I have 7');
71
71
  });
72
72
  });
@@ -78,7 +78,7 @@ describe('pluralTypeHandler', function() {
78
78
  let formatter = new MessageFormatter('en-NZ', {
79
79
  plural: pluralTypeHandler
80
80
  });
81
- let result = formatter.format(message, { days: 7 });
81
+ let result = formatter.format(message, {days: 7});
82
82
  expect(result).toBe('7 days...');
83
83
  });
84
84
 
@@ -88,7 +88,7 @@ describe('pluralTypeHandler', function() {
88
88
  number: numberTypeHandler,
89
89
  plural: pluralTypeHandler
90
90
  });
91
- let result = formatter.format(message, { days: 1000 });
91
+ let result = formatter.format(message, {days: 1000});
92
92
  expect(result).toBe('1,000 days...');
93
93
  });
94
94
 
@@ -100,7 +100,7 @@ describe('pluralTypeHandler', function() {
100
100
  other {ID: # {count, plural,
101
101
  other {Count: #}
102
102
  }}
103
- }`, { id: 5, count: 11 });
103
+ }`, {id: 5, count: 11});
104
104
  expect(result).toBe('ID: 5 Count: 11');
105
105
  });
106
106
  });
@@ -113,28 +113,28 @@ describe('pluralTypeHandler', function() {
113
113
  test('If unspecified, offset is null', function() {
114
114
  let message = '{days, plural, one {one day} =2 {two days} other {# days}}...';
115
115
 
116
- let result = formatter.format(message, { days: 5 });
116
+ let result = formatter.format(message, {days: 5});
117
117
  expect(result).toBe('5 days...');
118
118
  });
119
119
 
120
120
  test('Specified offset will be applied via =X branch', function() {
121
121
  let message = '{days, plural, offset:3 one {one day} =2 {two days} other {# days}}...';
122
122
 
123
- let result = formatter.format(message, { days: 5 });
123
+ let result = formatter.format(message, {days: 5});
124
124
  expect(result).toBe('two days...');
125
125
  });
126
126
 
127
127
  test('Specified offset will be applied via number sign', function() {
128
128
  let message = '{days, plural, offset:1 one {one day} =2 {two days} other {# days}}...';
129
129
 
130
- let result = formatter.format(message, { days: 5 });
130
+ let result = formatter.format(message, {days: 5});
131
131
  expect(result).toBe('4 days...');
132
132
  });
133
133
 
134
134
  test('Specified offset will be applied via `one` keyword', function() {
135
135
  let message = '{days, plural, offset:4 one {one day} =2 {two days} other {# days}}...';
136
136
 
137
- let result = formatter.format(message, { days: 5 });
137
+ let result = formatter.format(message, {days: 5});
138
138
  expect(result).toBe('one day...');
139
139
  });
140
140
  });
@@ -154,27 +154,27 @@ describe('pluralTypeHandler', function() {
154
154
  }`;
155
155
 
156
156
  test('zero', function() {
157
- let result = formatterAr.format(messageAr, { value: 0 });
157
+ let result = formatterAr.format(messageAr, {value: 0});
158
158
  expect(result).toBe('ZERO');
159
159
  });
160
160
 
161
161
  test('one', function() {
162
- let result = formatterAr.format(messageAr, { value: 1 });
162
+ let result = formatterAr.format(messageAr, {value: 1});
163
163
  expect(result).toBe('ONE');
164
164
  });
165
165
 
166
166
  test('two', function() {
167
- let result = formatterAr.format(messageAr, { value: 2 });
167
+ let result = formatterAr.format(messageAr, {value: 2});
168
168
  expect(result).toBe('TWO');
169
169
  });
170
170
 
171
171
  test('few', function() {
172
- let result = formatterAr.format(messageAr, { value: 3 });
172
+ let result = formatterAr.format(messageAr, {value: 3});
173
173
  expect(result).toBe('FEW');
174
174
  });
175
175
 
176
176
  test('other', function() {
177
- let result = formatterAr.format(messageAr, { value: 17 });
177
+ let result = formatterAr.format(messageAr, {value: 17});
178
178
  expect(result).toBe('OTHER');
179
179
  });
180
180
  });
@@ -25,21 +25,21 @@ const OTHER = 'other';
25
25
  * See https://formatjs.io/docs/core-concepts/icu-syntax#select-format for more
26
26
  * details on how the `select` statement works.
27
27
  *
28
- * @param {String} value
29
- * @param {String} matches
30
- * @param {String} locale
31
- * @param {String} values
32
- * @param {Function} format
33
- * @return {String}
28
+ * @param {string} value
29
+ * @param {string} matches
30
+ * @param {string} locale
31
+ * @param {Record<string,any>} values
32
+ * @param {(message: string, values?: Record<string,any>) => any[]} process
33
+ * @return {any | any[]}
34
34
  */
35
- export default function selectTypeHandler(value, matches = '', locale, values, format) {
36
- const { cases } = parseCases(matches);
35
+ export default function selectTypeHandler(value, matches = '', locale, values, process) {
36
+ const {cases} = parseCases(matches);
37
37
 
38
38
  if (value in cases) {
39
- return format(cases[value], values);
39
+ return process(cases[value], values);
40
40
  }
41
41
  else if (OTHER in cases) {
42
- return format(cases[OTHER], values);
42
+ return process(cases[OTHER], values);
43
43
  }
44
44
 
45
45
  return value;
@@ -31,12 +31,12 @@ describe('selectTypeHandler', function() {
31
31
  let message = '{mood, select, happy {Good} sad {bad} other {Ambivalent}} morning';
32
32
 
33
33
  test('Explicit branch', function() {
34
- let result = formatter.format(message, { mood: 'happy' });
34
+ let result = formatter.format(message, {mood: 'happy'});
35
35
  expect(result).toBe('Good morning');
36
36
  });
37
37
 
38
38
  test('Fallback branch', function() {
39
- let result = formatter.format(message, { mood: 'angry' });
39
+ let result = formatter.format(message, {mood: 'angry'});
40
40
  expect(result).toBe('Ambivalent morning');
41
41
  });
42
42
  });
@@ -45,7 +45,7 @@ describe('selectTypeHandler', function() {
45
45
 
46
46
  test('No matching branch', function() {
47
47
  let message = 'I am feeling {mood, select, happy {good}}';
48
- let result = formatter.format(message, { mood: 'ambivalent' });
48
+ let result = formatter.format(message, {mood: 'ambivalent'});
49
49
  expect(result).toBe('I am feeling ambivalent');
50
50
  });
51
51
  });