@fgv/ts-bcp47 5.0.0-21 → 5.0.0-23

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 (95) hide show
  1. package/package.json +8 -8
  2. package/src/data/bcp/overrides.json +0 -20
  3. package/src/data/iana/language-subtags.json +0 -57439
  4. package/src/data/iana/language-tag-extensions.json +0 -38
  5. package/src/data/unsd/m49.json +0 -3723
  6. package/src/index.ts +0 -29
  7. package/src/packlets/bcp47/bcp47Subtags/converters.ts +0 -33
  8. package/src/packlets/bcp47/bcp47Subtags/index.ts +0 -27
  9. package/src/packlets/bcp47/bcp47Subtags/model.ts +0 -46
  10. package/src/packlets/bcp47/bcp47Subtags/validate.ts +0 -55
  11. package/src/packlets/bcp47/common.ts +0 -85
  12. package/src/packlets/bcp47/helpers.ts +0 -117
  13. package/src/packlets/bcp47/index.ts +0 -38
  14. package/src/packlets/bcp47/languageRegistryData.ts +0 -304
  15. package/src/packlets/bcp47/languageTag.ts +0 -467
  16. package/src/packlets/bcp47/languageTagParser.ts +0 -307
  17. package/src/packlets/bcp47/match/chooser.ts +0 -164
  18. package/src/packlets/bcp47/match/common.ts +0 -57
  19. package/src/packlets/bcp47/match/index.ts +0 -26
  20. package/src/packlets/bcp47/match/similarity.ts +0 -243
  21. package/src/packlets/bcp47/normalization/baseNormalizer.ts +0 -146
  22. package/src/packlets/bcp47/normalization/canonicalNormalizer.ts +0 -102
  23. package/src/packlets/bcp47/normalization/common.ts +0 -84
  24. package/src/packlets/bcp47/normalization/index.ts +0 -27
  25. package/src/packlets/bcp47/normalization/normalizeTag.ts +0 -116
  26. package/src/packlets/bcp47/normalization/preferredTagNormalizer.ts +0 -213
  27. package/src/packlets/bcp47/overrides/converters.ts +0 -58
  28. package/src/packlets/bcp47/overrides/defaultRegistries.ts +0 -40
  29. package/src/packlets/bcp47/overrides/index.ts +0 -25
  30. package/src/packlets/bcp47/overrides/model.ts +0 -33
  31. package/src/packlets/bcp47/overrides/overridesRegistry.ts +0 -104
  32. package/src/packlets/bcp47/validation/baseValidator.ts +0 -132
  33. package/src/packlets/bcp47/validation/common.ts +0 -86
  34. package/src/packlets/bcp47/validation/index.ts +0 -29
  35. package/src/packlets/bcp47/validation/isCanonical.ts +0 -103
  36. package/src/packlets/bcp47/validation/isInPreferredForm.ts +0 -53
  37. package/src/packlets/bcp47/validation/isStrictlyValid.ts +0 -117
  38. package/src/packlets/bcp47/validation/isValid.ts +0 -122
  39. package/src/packlets/bcp47/validation/isWellFormed.ts +0 -102
  40. package/src/packlets/bcp47/validation/validateTag.ts +0 -175
  41. package/src/packlets/iana/common/converters.ts +0 -67
  42. package/src/packlets/iana/common/model.ts +0 -56
  43. package/src/packlets/iana/common/registeredItems.ts +0 -145
  44. package/src/packlets/iana/common/utils.ts +0 -32
  45. package/src/packlets/iana/common/validate.ts +0 -64
  46. package/src/packlets/iana/converters.ts +0 -26
  47. package/src/packlets/iana/defaultRegistries.ts +0 -40
  48. package/src/packlets/iana/index.ts +0 -33
  49. package/src/packlets/iana/jar/converters.ts +0 -26
  50. package/src/packlets/iana/jar/index.ts +0 -27
  51. package/src/packlets/iana/jar/jarConverters.ts +0 -70
  52. package/src/packlets/iana/jar/jarModel.ts +0 -34
  53. package/src/packlets/iana/jar/language-subtags/converters.ts +0 -26
  54. package/src/packlets/iana/jar/language-subtags/index.ts +0 -27
  55. package/src/packlets/iana/jar/language-subtags/model.ts +0 -26
  56. package/src/packlets/iana/jar/language-subtags/registry/converters.ts +0 -40
  57. package/src/packlets/iana/jar/language-subtags/registry/index.ts +0 -26
  58. package/src/packlets/iana/jar/language-subtags/registry/model.ts +0 -171
  59. package/src/packlets/iana/jar/language-subtags/tags/converters.ts +0 -120
  60. package/src/packlets/iana/jar/language-subtags/tags/index.ts +0 -28
  61. package/src/packlets/iana/jar/language-subtags/tags/model.ts +0 -71
  62. package/src/packlets/iana/jar/language-subtags/tags/tagValidation.ts +0 -67
  63. package/src/packlets/iana/jar/language-subtags/tags/validate.ts +0 -106
  64. package/src/packlets/iana/jar/model.ts +0 -26
  65. package/src/packlets/iana/language-subtags/common.ts +0 -46
  66. package/src/packlets/iana/language-subtags/converters.ts +0 -226
  67. package/src/packlets/iana/language-subtags/index.ts +0 -30
  68. package/src/packlets/iana/language-subtags/jarConverters.ts +0 -269
  69. package/src/packlets/iana/language-subtags/model.ts +0 -213
  70. package/src/packlets/iana/language-subtags/scope.ts +0 -222
  71. package/src/packlets/iana/language-subtags/subtagRegistry.ts +0 -136
  72. package/src/packlets/iana/language-subtags/validate.ts +0 -23
  73. package/src/packlets/iana/language-tag-extensions/converters.ts +0 -71
  74. package/src/packlets/iana/language-tag-extensions/extensionsRegistry.ts +0 -92
  75. package/src/packlets/iana/language-tag-extensions/extensionsScope.ts +0 -60
  76. package/src/packlets/iana/language-tag-extensions/index.ts +0 -29
  77. package/src/packlets/iana/language-tag-extensions/jarConverters.ts +0 -91
  78. package/src/packlets/iana/language-tag-extensions/model.ts +0 -67
  79. package/src/packlets/iana/language-tag-extensions/validate.ts +0 -36
  80. package/src/packlets/iana/languageRegistries.ts +0 -57
  81. package/src/packlets/iana/model.ts +0 -26
  82. package/src/packlets/iana/validate.ts +0 -23
  83. package/src/packlets/unsd/areas.ts +0 -105
  84. package/src/packlets/unsd/common.ts +0 -79
  85. package/src/packlets/unsd/csv/converters.ts +0 -82
  86. package/src/packlets/unsd/csv/index.ts +0 -26
  87. package/src/packlets/unsd/csv/model.ts +0 -44
  88. package/src/packlets/unsd/defaultRegistries.ts +0 -40
  89. package/src/packlets/unsd/index.ts +0 -28
  90. package/src/packlets/unsd/regionCodes.ts +0 -144
  91. package/src/packlets/unsd/regions.ts +0 -95
  92. package/src/packlets/utils/index.ts +0 -24
  93. package/src/packlets/utils/jsonHelpers.ts +0 -25
  94. package/src/packlets/utils/public.ts +0 -28
  95. package/src/packlets/utils/validationHelpers.ts +0 -180
@@ -1,307 +0,0 @@
1
- /*
2
- * Copyright (c) 2022 Erik Fortune
3
- *
4
- * Permission is hereby granted, free of charge, to any person obtaining a copy
5
- * of this software and associated documentation files (the "Software"), to deal
6
- * in the Software without restriction, including without limitation the rights
7
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- * copies of the Software, and to permit persons to whom the Software is
9
- * furnished to do so, subject to the following conditions:
10
- *
11
- * The above copyright notice and this permission notice shall be included in all
12
- * copies or substantial portions of the Software.
13
- *
14
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- * SOFTWARE.
21
- */
22
-
23
- import * as Iana from '../iana';
24
-
25
- import { Result, Success, allSucceed, fail, succeed } from '@fgv/ts-utils';
26
-
27
- import { Validate } from './bcp47Subtags';
28
- import { ExtensionSingleton, ExtensionSubtag } from './bcp47Subtags/model';
29
- import { ISubtags } from './common';
30
-
31
- /**
32
- * @internal
33
- */
34
- interface IParserState {
35
- readonly iana: Iana.LanguageRegistries;
36
- readonly tag: string;
37
- readonly subtags: string[];
38
- readonly parsedSubtags: ISubtags;
39
- next?: string;
40
- }
41
-
42
- /**
43
- * @public
44
- */
45
- export class LanguageTagParser {
46
- // eslint-disable-next-line @typescript-eslint/no-empty-function
47
- /* c8 ignore next */
48
- private constructor() {}
49
-
50
- /**
51
- * Parses a string representation of a BCP-47 ({@link https://www.rfc-editor.org/rfc/rfc5646.html | RFC 5646})
52
- * language tag, to produce a {@link Bcp47.Subtags | Subtags} object which
53
- * breaks out each of the subtags.
54
- * @param tag - The `string` language tag to be parsed.
55
- * @param iana - Optional {@link Iana.LanguageRegistries | IANA language registries}
56
- * to be used.
57
- * @returns `Success` with the resulting {@link Bcp47.Subtags | subtags}
58
- * or `Failure` with details if an error occurs.
59
- * @public
60
- */
61
- public static parse(tag: string, iana?: Iana.LanguageRegistries): Result<ISubtags> {
62
- /* c8 ignore next */
63
- iana = iana ?? Iana.DefaultRegistries.languageRegistries;
64
- const status: IParserState = {
65
- tag,
66
- iana,
67
- subtags: tag.split('-'),
68
- parsedSubtags: {}
69
- };
70
- status.next = status.subtags.shift();
71
-
72
- return allSucceed(
73
- [
74
- this._parseGrandfatheredTag(status),
75
- this._parsePrimaryLanguage(status),
76
- this._parseExtlang(status),
77
- this._parseScript(status),
78
- this._parseRegion(status),
79
- this._parseVariants(status),
80
- this._parseExtensions(status),
81
- this._parsePrivateSubtags(status),
82
- this._parseTagEnd(status)
83
- ],
84
- status
85
- ).onSuccess(() => {
86
- return succeed(status.parsedSubtags);
87
- });
88
- }
89
-
90
- /**
91
- * Determines if the entire tag matches a registered grandfathered tag.
92
- * @param state - The {@link Bcp47.ParserState | current state} of the
93
- * parse operation.
94
- * @returns `Success` with the updated {@link Bcp47.Subtags | subtags}
95
- * if a grandfathered tag is found, or `Success` with the supplied subtags
96
- * if no matching grandfathered tag is found.
97
- * @internal
98
- */
99
- protected static _parseGrandfatheredTag(state: IParserState): Success<ISubtags> {
100
- const grandfathered = state.iana.subtags.grandfathered.tryGet(state.tag);
101
- if (grandfathered) {
102
- state.parsedSubtags.grandfathered = state.tag as Iana.LanguageSubtags.GrandfatheredTag;
103
- // we consumed the whole thing
104
- state.subtags.splice(0, state.subtags.length);
105
- state.next = undefined;
106
- }
107
- return succeed(state.parsedSubtags);
108
- }
109
-
110
- /**
111
- * Parses the primary language subtag of a supplied language tag.
112
- * @param state - The {@link Bcp47.ParserState | current state} of the
113
- * parse operation.
114
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
115
- * updated to include the primary language subtag, or `Failure` with details if an error
116
- * occurs.
117
- * @internal
118
- */
119
- protected static _parsePrimaryLanguage(state: IParserState): Result<ISubtags> {
120
- // primary language subtag is required unless the entire tag is grandfathered or consists
121
- // of only private tags
122
- if (state.iana.subtags.languages.isWellFormed(state.next)) {
123
- state.parsedSubtags.primaryLanguage = state.next;
124
- state.next = state.subtags.shift();
125
- return succeed(state.parsedSubtags);
126
- } else if (state.parsedSubtags.grandfathered !== undefined) {
127
- return succeed(state.parsedSubtags);
128
- } else if (Validate.privateUsePrefix.isWellFormed(state.next)) {
129
- // just return with no primary language and the private tag
130
- // parser will be invoked by the parent flow.
131
- return succeed(state.parsedSubtags);
132
- }
133
- return fail(`${state.tag}: no primary language subtag`);
134
- }
135
-
136
- /**
137
- * Parses the extlang subtag(s) of a supplied language tag.
138
- * @param state - The {@link Bcp47.ParserState | current state} of the
139
- * parse operation.
140
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
141
- * updated to include extlang subtags if present, or `Failure` with details if an error
142
- * occurs.
143
- * @internal
144
- */
145
- protected static _parseExtlang(state: IParserState): Result<ISubtags> {
146
- // optional extlangs subtags
147
- while (state.iana.subtags.extlangs.isWellFormed(state.next)) {
148
- if (state.parsedSubtags.extlangs === undefined) {
149
- state.parsedSubtags.extlangs = [state.next];
150
- } else if (state.parsedSubtags.extlangs.length < 3) {
151
- state.parsedSubtags.extlangs.push(state.next);
152
- } else {
153
- return fail(`${state.next}: too many extlang subtags`);
154
- }
155
- state.next = state.subtags.shift();
156
- }
157
- return succeed(state.parsedSubtags);
158
- }
159
-
160
- /**
161
- * Parses the script subtag of a supplied language tag.
162
- * @param state - The {@link Bcp47.ParserState | current state} of the
163
- * parse operation.
164
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
165
- * updated to include the script subtag if present, or `Failure` with details if an error
166
- * occurs.
167
- * @internal
168
- */
169
- protected static _parseScript(state: IParserState): Result<ISubtags> {
170
- // optional script subtag
171
- if (state.iana.subtags.scripts.isWellFormed(state.next)) {
172
- state.parsedSubtags.script = state.next;
173
- state.next = state.subtags.shift();
174
- }
175
- return succeed(state.parsedSubtags);
176
- }
177
-
178
- /**
179
- * Parses the region subtag of a supplied language tag.
180
- * @param state - The {@link Bcp47.ParserState | current state} of the
181
- * parse operation.
182
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
183
- * updated to include the region subtag if present, or `Failure` with details if an error
184
- * occurs.
185
- * @internal
186
- */
187
- protected static _parseRegion(state: IParserState): Result<ISubtags> {
188
- // optional region subtag
189
- if (state.iana.subtags.regions.isWellFormed(state.next)) {
190
- state.parsedSubtags.region = state.next;
191
- state.next = state.subtags.shift();
192
- }
193
- return succeed(state.parsedSubtags);
194
- }
195
-
196
- /**
197
- * Parses the variant subtag(s) of a supplied language tag.
198
- * @param state - The {@link Bcp47.ParserState | current state} of the
199
- * parse operation.
200
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
201
- * updated to include the variant subtags if present, or `Failure` with details if an error
202
- * occurs.
203
- * @internal
204
- */
205
- protected static _parseVariants(state: IParserState): Result<ISubtags> {
206
- // optional variant subtags
207
- while (state.iana.subtags.variants.isWellFormed(state.next)) {
208
- if (state.parsedSubtags.variants === undefined) {
209
- state.parsedSubtags.variants = [state.next];
210
- } else {
211
- state.parsedSubtags.variants.push(state.next);
212
- }
213
- state.next = state.subtags.shift();
214
- }
215
- return succeed(state.parsedSubtags);
216
- }
217
-
218
- /**
219
- * Parses the extension subtag(s) of a supplied language tag.
220
- * @param state - The {@link Bcp47.ParserState | current state} of the
221
- * parse operation.
222
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
223
- * updated to include the extension subtags if present, or `Failure` with details if an error
224
- * occurs.
225
- * @internal
226
- */
227
- protected static _parseExtensions(state: IParserState): Result<ISubtags> {
228
- // optional extension subtags
229
- while (state.next !== undefined && Validate.extensionSingleton.isWellFormed(state.next)) {
230
- const singleton: ExtensionSingleton = state.next as ExtensionSingleton;
231
- const values: ExtensionSubtag[] = [];
232
- state.next = state.subtags.shift();
233
-
234
- while (Validate.extensionSubtag.isWellFormed(state.next)) {
235
- values.push(state.next as ExtensionSubtag);
236
- state.next = state.subtags.shift();
237
- }
238
- if (
239
- state.next !== undefined &&
240
- !Validate.extensionSingleton.isWellFormed(state.next) &&
241
- !Validate.privateUsePrefix.isWellFormed(state.next)
242
- ) {
243
- return fail(`${state.next}: malformed extension subtag`);
244
- } else if (values.length < 1) {
245
- return fail(`${state.tag}: extension '${singleton}' must have at least one subtag.`);
246
- }
247
-
248
- const value = values.join('-') as ExtensionSubtag;
249
- if (state.parsedSubtags.extensions === undefined) {
250
- state.parsedSubtags.extensions = [{ singleton, value }];
251
- } else {
252
- state.parsedSubtags.extensions.push({ singleton, value });
253
- }
254
- }
255
- return succeed(state.parsedSubtags);
256
- }
257
-
258
- /**
259
- * Parses the private use subtags of a supplied language tag.
260
- * @param state - The {@link Bcp47.ParserState | current state} of the
261
- * parse operation.
262
- * @returns `Success` with supplied {@link Bcp47.Subtags | subtags}
263
- * updated to include the private-use subtags if present, or `Failure` with details if an error
264
- * occurs.
265
- * @internal
266
- */
267
- protected static _parsePrivateSubtags(state: IParserState): Result<ISubtags> {
268
- // optional private use subtags
269
- if (state.next !== undefined && Validate.privateUsePrefix.isWellFormed(state.next)) {
270
- const values: string[] = [];
271
- state.next = state.subtags.shift();
272
-
273
- while (
274
- state.next &&
275
- state.next.length > 1 &&
276
- Iana.LanguageSubtags.Validate.extendedLanguageRange.isWellFormed(state.next)
277
- ) {
278
- values.push(state.next);
279
- state.next = state.subtags.shift();
280
- }
281
-
282
- if (state.next !== undefined) {
283
- return fail(`${state.next}: malformed private-use subtag`);
284
- } else if (values.length < 1) {
285
- return fail(`${state.tag}: private-use tag must have at least one subtag.`);
286
- }
287
-
288
- state.parsedSubtags.privateUse = values as Iana.LanguageSubtags.ExtendedLanguageRange[];
289
- }
290
- return succeed(state.parsedSubtags);
291
- }
292
-
293
- /**
294
- * Verifies {@link Bcp47.ParserState | parser state} at the end of a parse operation.
295
- * @param state - The {@link Bcp47.ParserState | current state} of the
296
- * parse operation.
297
- * @returns `Success` if the tag was fully consumed, or `Failure` with details
298
- * if unexpected subtags remain to be parsed.
299
- * @internal
300
- */
301
- protected static _parseTagEnd(state: IParserState): Result<ISubtags> {
302
- if (state.next !== undefined) {
303
- return fail(`${state.next}: unexpected subtag`);
304
- }
305
- return succeed(state.parsedSubtags);
306
- }
307
- }
@@ -1,164 +0,0 @@
1
- /*
2
- * Copyright (c) 2022 Erik Fortune
3
- *
4
- * Permission is hereby granted, free of charge, to any person obtaining a copy
5
- * of this software and associated documentation files (the "Software"), to deal
6
- * in the Software without restriction, including without limitation the rights
7
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- * copies of the Software, and to permit persons to whom the Software is
9
- * furnished to do so, subject to the following conditions:
10
- *
11
- * The above copyright notice and this permission notice shall be included in all
12
- * copies or substantial portions of the Software.
13
- *
14
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- * SOFTWARE.
21
- */
22
-
23
- import * as Iana from '../../iana';
24
-
25
- import { ISubtags } from '../common';
26
- import { LanguageTag } from '../languageTag';
27
- import { LanguageSimilarityMatcher } from './similarity';
28
-
29
- /**
30
- * Represents a single matching filtered language.
31
- * @public
32
- */
33
- export interface IMatchingLanguage {
34
- /**
35
- * Numeric indication of how well the language matches,
36
- * from perfect (`1.0`) to not at all (`0.0`). When
37
- * matching an ordered list, languages at the front of
38
- * the desired language list are always higher quality.
39
- */
40
- quality: number;
41
- /**
42
- * The `string` tag of the matched language.
43
- */
44
- tag: string;
45
- /**
46
- * The matched {@link Bcp47.LanguageTag | language tag}.
47
- */
48
- languageTag: LanguageTag;
49
- }
50
-
51
- /**
52
- * Options for {@link Bcp47.choose | language tag list filter} functions.
53
- * @public
54
- */
55
- export interface ILanguageChooserOptions {
56
- /**
57
- * Indicates whether to return the matching language from the
58
- * desired list or the available list. Default is `'availableLanguage'`.
59
- */
60
- use?: 'desiredLanguage' | 'availableLanguage';
61
-
62
- /**
63
- * Indicates how to filter the language list - `'primaryLanguage'`
64
- * indicates the each primary language should appear only once in
65
- * the list in its most similar form. A filter value of `'none'`
66
- * reports all matching variants of any primary language in order
67
- * of similarity. Default is `'primaryLanguage'`
68
- */
69
- filter?: 'primaryLanguage' | 'none';
70
-
71
- /**
72
- * An optional {@link Bcp47.LanguageSpec | language specification}
73
- * indicating a language to be returned if the filter call would
74
- * otherwise return an empty list (i.e. no languages match).
75
- */
76
- ultimateFallback?: string | ISubtags | LanguageTag;
77
- }
78
-
79
- /**
80
- * Default values for a{@link Bcp47.LanguageFilterOptions}.
81
- * @public
82
- */
83
- export const defaultLanguageChooserOptions: ILanguageChooserOptions = Object.freeze({
84
- use: 'availableLanguage',
85
- filter: 'primaryLanguage'
86
- });
87
-
88
- /**
89
- * Helper to compare a list of 'desired' languages to a list of 'available' language
90
- * and return the intersection in order of preference, taking tag semantics into account.
91
- * @public
92
- */
93
- export class LanguageChooser {
94
- /**
95
- * @internal
96
- */
97
- protected readonly _matcher: LanguageSimilarityMatcher;
98
-
99
- public constructor(iana?: Iana.LanguageRegistries) {
100
- this._matcher = new LanguageSimilarityMatcher(iana);
101
- }
102
-
103
- public chooseLanguageTagsWithDetails(
104
- desiredLanguages: LanguageTag[],
105
- availableLanguages: LanguageTag[],
106
- options?: ILanguageChooserOptions
107
- ): IMatchingLanguage[] {
108
- const matched = new Map<string, IMatchingLanguage>();
109
- const decrement = desiredLanguages.length < 10 ? 0.1 : 1.0 / desiredLanguages.length;
110
- let base = 1.0;
111
-
112
- // fill any missing fields from the default
113
- options = { ...defaultLanguageChooserOptions, ...options };
114
-
115
- for (const want of desiredLanguages) {
116
- base -= decrement;
117
- for (const have of availableLanguages) {
118
- const similarity = this._matcher.matchLanguageTags(want, have);
119
- if (similarity > 0.0) {
120
- const quality = base + similarity * decrement;
121
- const languageTag = options.use === 'availableLanguage' ? have : want;
122
- const tag = languageTag.tag;
123
- const key = options.filter === 'primaryLanguage' ? languageTag.subtags.primaryLanguage ?? tag : tag;
124
-
125
- const match: IMatchingLanguage = {
126
- quality,
127
- tag,
128
- languageTag
129
- };
130
-
131
- const existing = matched.get(key);
132
- if (!existing || existing.quality < quality) {
133
- matched.set(key, match);
134
- }
135
- }
136
- }
137
- }
138
-
139
- const values = Array.from(matched.values());
140
- if (values.length > 1) {
141
- // want descending order
142
- values.sort((m1, m2) => m2.quality - m1.quality);
143
- } else if (values.length === 0 && options.ultimateFallback) {
144
- const languageTag =
145
- options.ultimateFallback instanceof LanguageTag
146
- ? options.ultimateFallback
147
- : LanguageTag.create(options.ultimateFallback).orDefault();
148
- if (languageTag) {
149
- return [{ languageTag, tag: languageTag.tag, quality: 0 }];
150
- }
151
- }
152
- return values;
153
- }
154
-
155
- public filterLanguageTags(
156
- desiredLanguages: LanguageTag[],
157
- availableLanguages: LanguageTag[],
158
- options?: ILanguageChooserOptions
159
- ): LanguageTag[] {
160
- return this.chooseLanguageTagsWithDetails(desiredLanguages, availableLanguages, options).map(
161
- (t) => t.languageTag
162
- );
163
- }
164
- }
@@ -1,57 +0,0 @@
1
- /*
2
- * Copyright (c) 2022 Erik Fortune
3
- *
4
- * Permission is hereby granted, free of charge, to any person obtaining a copy
5
- * of this software and associated documentation files (the "Software"), to deal
6
- * in the Software without restriction, including without limitation the rights
7
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- * copies of the Software, and to permit persons to whom the Software is
9
- * furnished to do so, subject to the following conditions:
10
- *
11
- * The above copyright notice and this permission notice shall be included in all
12
- * copies or substantial portions of the Software.
13
- *
14
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- * SOFTWARE.
21
- */
22
- /* eslint-disable @rushstack/typedef-var */
23
-
24
- /**
25
- * Penalty for a mismatched value for some subtags.
26
- * @public
27
- */
28
- export const subtagMismatchPenalty = {
29
- private: 0.05,
30
- extension: 0.04,
31
- variant: 0.1
32
- };
33
-
34
- /**
35
- * Common levels of match quality for a single language match.
36
- * @public
37
- */
38
- export const tagSimilarity = {
39
- exact: 1.0,
40
- variant: 0.9,
41
- region: 0.8,
42
- macroRegion: 0.65,
43
- neutralRegion: 0.5,
44
- preferredAffinity: 0.45,
45
- affinity: 0.4,
46
- preferredRegion: 0.35,
47
- sibling: 0.3,
48
- undetermined: 0.1,
49
- none: 0
50
- };
51
-
52
- /**
53
- * Numeric representation of the quality of a language match.
54
- * Range is 0 (no match) to 1 (exact match).
55
- * @public
56
- */
57
- export type TagSimilarity = keyof typeof tagSimilarity;
@@ -1,26 +0,0 @@
1
- /*
2
- * Copyright (c) 2022 Erik Fortune
3
- *
4
- * Permission is hereby granted, free of charge, to any person obtaining a copy
5
- * of this software and associated documentation files (the "Software"), to deal
6
- * in the Software without restriction, including without limitation the rights
7
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
- * copies of the Software, and to permit persons to whom the Software is
9
- * furnished to do so, subject to the following conditions:
10
- *
11
- * The above copyright notice and this permission notice shall be included in all
12
- * copies or substantial portions of the Software.
13
- *
14
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
- * SOFTWARE.
21
- */
22
- // istanbul ignore file
23
-
24
- export { ILanguageChooserOptions, IMatchingLanguage, LanguageChooser } from './chooser';
25
- export * from './common';
26
- export { LanguageSimilarityMatcher } from './similarity';