@ultraq/icu-message-formatter 0.12.0 → 0.14.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.
Files changed (32) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/README.md +5 -6
  3. package/dist/icu-message-formatter.browser.es.min.js +2 -0
  4. package/dist/icu-message-formatter.browser.es.min.js.map +1 -0
  5. package/dist/icu-message-formatter.browser.min.js +2 -0
  6. package/dist/icu-message-formatter.browser.min.js.map +1 -0
  7. package/dist/icu-message-formatter.cjs +473 -0
  8. package/dist/icu-message-formatter.cjs.map +1 -0
  9. package/dist/icu-message-formatter.d.cts +153 -0
  10. package/dist/icu-message-formatter.d.ts +153 -0
  11. package/dist/icu-message-formatter.js +466 -0
  12. package/dist/icu-message-formatter.js.map +1 -0
  13. package/package.json +43 -26
  14. package/dist/icu-message-formatter.es.min.js +0 -2
  15. package/dist/icu-message-formatter.es.min.js.map +0 -1
  16. package/dist/icu-message-formatter.min.js +0 -2
  17. package/dist/icu-message-formatter.min.js.map +0 -1
  18. package/lib/icu-message-formatter.cjs.js +0 -476
  19. package/lib/icu-message-formatter.cjs.js.map +0 -1
  20. package/lib/icu-message-formatter.es.js +0 -460
  21. package/lib/icu-message-formatter.es.js.map +0 -1
  22. package/rollup.config.dist.js +0 -37
  23. package/rollup.config.js +0 -35
  24. package/source/IcuMessageFormatter.js +0 -9
  25. package/source/MessageFormatter.js +0 -112
  26. package/source/MessageFormatter.test.js +0 -153
  27. package/source/pluralTypeHandler.js +0 -122
  28. package/source/pluralTypeHandler.test.js +0 -188
  29. package/source/selectTypeHandler.js +0 -46
  30. package/source/selectTypeHandler.test.js +0 -59
  31. package/source/utilities.js +0 -166
  32. package/source/utilities.test.js +0 -123
@@ -1,166 +0,0 @@
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
- */
16
-
17
- /**
18
- * Most branch-based type handlers are based around "cases".
19
- * For example, `select` and `plural` compare compare a value
20
- * to "case keys" to choose a subtranslation.
21
- *
22
- * This util splits "matches" portions provided to the aforementioned
23
- * handlers into case strings, and extracts any prepended arguments
24
- * (for example, `plural` supports an `offset:n` argument used for
25
- * populating the magic `#` variable).
26
- *
27
- * @param {String} string
28
- * @return {Object} The `cases` key points to a map of all cases.
29
- * The `arguments` key points to a list of prepended arguments.
30
- */
31
- export function parseCases(string) {
32
- const isWhitespace = ch => /\s/.test(ch);
33
-
34
- const args = [];
35
- const cases = {};
36
-
37
- let currTermStart = 0;
38
- let latestTerm = null;
39
- let inTerm = false;
40
-
41
- let i = 0;
42
- while (i < string.length) {
43
- // Term ended
44
- if (inTerm && (isWhitespace(string[i]) || string[i] === '{')) {
45
- inTerm = false;
46
- latestTerm = string.slice(currTermStart, i);
47
-
48
- // We want to process the opening char again so the case will be properly registered.
49
- if (string[i] === '{') {
50
- i--;
51
- }
52
- }
53
-
54
- // New term
55
- else if (!inTerm && !isWhitespace(string[i])) {
56
- const caseBody = string[i] === '{';
57
-
58
- // If there's a previous term, we can either handle a whole
59
- // case, or add that as an argument.
60
- if (latestTerm && caseBody) {
61
- const branchEndIndex = findClosingBracket(string, i);
62
-
63
- if (branchEndIndex === -1) {
64
- throw new Error(`Unbalanced curly braces in string: "${string}"`);
65
- }
66
-
67
- cases[latestTerm] = string.slice(i + 1, branchEndIndex); // Don't include the braces
68
-
69
- i = branchEndIndex; // Will be moved up where needed at end of loop.
70
- latestTerm = null;
71
- }
72
- else {
73
- if (latestTerm) {
74
- args.push(latestTerm);
75
- latestTerm = null;
76
- }
77
-
78
- inTerm = true;
79
- currTermStart = i;
80
- }
81
- }
82
- i++;
83
- }
84
-
85
- if (inTerm) {
86
- latestTerm = string.slice(currTermStart);
87
- }
88
-
89
- if (latestTerm) {
90
- args.push(latestTerm);
91
- }
92
-
93
- return {
94
- args,
95
- cases
96
- };
97
- }
98
-
99
- /**
100
- * Finds the index of the matching closing curly bracket, including through
101
- * strings that could have nested brackets.
102
- *
103
- * @param {String} string
104
- * @param {Number} fromIndex
105
- * @return {Number} The index of the matching closing bracket, or -1 if no
106
- * closing bracket could be found.
107
- */
108
- export function findClosingBracket(string, fromIndex) {
109
- let depth = 0;
110
- for (let i = fromIndex + 1; i < string.length; i++) {
111
- let char = string.charAt(i);
112
- if (char === '}') {
113
- if (depth === 0) {
114
- return i;
115
- }
116
- depth--;
117
- }
118
- else if (char === '{') {
119
- depth++;
120
- }
121
- }
122
- return -1;
123
- }
124
-
125
- /**
126
- * Split a `{key, type, format}` block into those 3 parts, taking into account
127
- * nested message syntax that can exist in the `format` part.
128
- *
129
- * @param {String} block
130
- * @return {Array}
131
- * An array with `key`, `type`, and `format` items in that order, if present
132
- * in the formatted argument block.
133
- */
134
- export function splitFormattedArgument(block) {
135
- return split(block.slice(1, -1), ',', 3);
136
- }
137
-
138
- /**
139
- * Like `String.prototype.split()` but where the limit parameter causes the
140
- * remainder of the string to be grouped together in a final entry.
141
- *
142
- * @private
143
- * @param {String} string
144
- * @param {String} separator
145
- * @param {Number} limit
146
- * @param {Array} [accumulator=[]]
147
- * @return {Array}
148
- */
149
- function split(string, separator, limit, accumulator = []) {
150
- if (!string) {
151
- return accumulator;
152
- }
153
- if (limit === 1) {
154
- accumulator.push(string);
155
- return accumulator;
156
- }
157
- let indexOfDelimiter = string.indexOf(separator);
158
- if (indexOfDelimiter === -1) {
159
- accumulator.push(string);
160
- return accumulator;
161
- }
162
- let head = string.substring(0, indexOfDelimiter).trim();
163
- let tail = string.substring(indexOfDelimiter + separator.length + 1).trim();
164
- accumulator.push(head);
165
- return split(tail, separator, limit - 1, accumulator);
166
- }
@@ -1,123 +0,0 @@
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
- */
16
-
17
- /* eslint-env jest */
18
- import { parseCases } from './utilities';
19
-
20
- /**
21
- * Tests for the `parseCases` util.
22
- */
23
- describe('parseCases', function() {
24
- describe('empty', function() {
25
- test('empty string', function() {
26
- let result = parseCases('');
27
- expect(result.args).toStrictEqual([]);
28
- expect(result.cases).toStrictEqual({});
29
- });
30
- });
31
-
32
- describe('basic case support', function() {
33
- test('single case', function() {
34
- let result = parseCases('key {case string!}');
35
- expect(result.args).toStrictEqual([]);
36
- expect(result.cases).toStrictEqual({key: 'case string!'});
37
- });
38
-
39
- test('fails if cant close case', function() {
40
- expect(() => {
41
- parseCases('key1 {{case1}');
42
- }).toThrowError();
43
- });
44
-
45
- test('multiple cases', function() {
46
- let result = parseCases('key1 {case1} key2 {case2} key3 {case3}');
47
- expect(result.args).toStrictEqual([]);
48
- expect(result.cases).toStrictEqual({ key1: 'case1', key2: 'case2', key3: 'case3' });
49
- });
50
-
51
- test('multiple cases with symbols', function() {
52
- let result = parseCases('=key1 {case1} &key2 {case2} key3 {case3}');
53
- expect(result.args).toStrictEqual([]);
54
- expect(result.cases).toStrictEqual({ '=key1': 'case1', '&key2': 'case2', key3: 'case3' });
55
- });
56
-
57
- test('multiple cases with inconsistent whitespace', function() {
58
- let result = parseCases(`key1 {case1}
59
-
60
-
61
- key2 {case2}
62
- key3 {case3}`);
63
- expect(result.args).toStrictEqual([]);
64
- expect(result.cases).toStrictEqual({ key1: 'case1', key2: 'case2', key3: 'case3' });
65
- });
66
-
67
- test('multiple cases with minimal whitespace', function() {
68
- let result = parseCases(`key1{case1}key2{case2}key3{case3}`);
69
- expect(result.args).toStrictEqual([]);
70
- expect(result.cases).toStrictEqual({ key1: 'case1', key2: 'case2', key3: 'case3' });
71
- });
72
-
73
- test('multiple cases with complex bodies', function() {
74
- let result = parseCases(`key1 {{}{}{}{{{{}}}}}
75
-
76
-
77
- key2 {=key1 {case1} &key2 {case2} key3 {case3}}
78
- key3 {}`);
79
- expect(result.args).toStrictEqual([]);
80
- expect(result.cases).toStrictEqual({ key1: '{}{}{}{{{{}}}}', key2: '=key1 {case1} &key2 {case2} key3 {case3}', key3: '' });
81
- });
82
- });
83
-
84
- describe('basic arg support', function() {
85
- test('single arg', function() {
86
- let result = parseCases('arg1');
87
- expect(result.args).toStrictEqual(['arg1']);
88
- });
89
-
90
- test('multiple args', function() {
91
- let result = parseCases('arg1 arg2 arg3');
92
- expect(result.args).toStrictEqual(['arg1', 'arg2', 'arg3']);
93
- });
94
-
95
- test('multiple args with inconsistent whitespace', function() {
96
- let result = parseCases(`arg1
97
-
98
-
99
- arg2 arg3`);
100
- expect(result.args).toStrictEqual(['arg1', 'arg2', 'arg3']);
101
- });
102
- });
103
-
104
- describe('arg and cases support', function() {
105
- test('multiple args and cases', function() {
106
- let result = parseCases(`
107
- offset:1 something:else
108
- =0 {{host} does not give a party.}
109
- =1 {{host} invites {guest} to her party.}
110
- =2 {{host} invites {guest} and one other person to her party.}
111
- other {{host} invites {guest} and # other people to her party.}
112
- `);
113
-
114
- expect(result.args).toStrictEqual(['offset:1', 'something:else']);
115
- expect(result.cases).toStrictEqual({
116
- '=0': '{host} does not give a party.',
117
- '=1': '{host} invites {guest} to her party.',
118
- '=2': '{host} invites {guest} and one other person to her party.',
119
- other: '{host} invites {guest} and # other people to her party.'
120
- });
121
- });
122
- });
123
- });