resolve-accept-language 2.1.17 → 2.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2021 Avansai
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Avansai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,105 +1,105 @@
1
- # resolve-accept-language
2
-
3
- [![License](https://img.shields.io/npm/l/make-coverage-badge.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
4
- [![npm download](https://img.shields.io/npm/dw/resolve-accept-language.svg?color=brightgreen)](https://www.npmjs.com/package/resolve-accept-language)
5
- ![Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen.svg)
6
- ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)
7
-
8
- Resolve the best locale based on the value of an `Accept-Language` HTTP header.
9
-
10
- ## Usage
11
-
12
- Add the package as a dependency:
13
-
14
- ```
15
- npm install resolve-accept-language
16
- ```
17
-
18
- Code example:
19
-
20
- ```ts
21
- import resolveAcceptLanguage from 'resolve-accept-language'
22
-
23
- /**
24
- * The API is well documented from within your IDE using TSDoc. The arguments are as follows:
25
- *
26
- * 1) The HTTP accept-language header.
27
- * 2) The available locales (they must contain the default locale).
28
- * 3) The default locale.
29
- */
30
- console.log(
31
- resolveAcceptLanguage(
32
- 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001',
33
- // The `as const` is optional for TypeScript but gives better typing.
34
- ['en-US', 'fr-CA'] as const,
35
- 'en-US'
36
- )
37
- )
38
- ```
39
-
40
- Output:
41
-
42
- ```
43
- fr-CA
44
- ```
45
-
46
- ## Advanced use cases
47
-
48
- You may want to control exactly the behavior depending on the type of match. For example, you could want to display a language picker on your home page if the match is not satisfactory. In those cases, you will need to use the `ResolveAcceptLanguage` class instead. It offers more visibility into the selection process while matching a locale:
49
-
50
- ```ts
51
- import { MATCH_TYPES, ResolveAcceptLanguage } from 'resolve-accept-language'
52
-
53
- const resolveAcceptLanguage = new ResolveAcceptLanguage(
54
- 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001' as const,
55
- ['en-US', 'fr-CA'],
56
- 'fr-CA'
57
- )
58
-
59
- console.log(`A locale was matched: ${resolveAcceptLanguage.getMatch()}`)
60
-
61
- if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.localeBased) {
62
- console.log('The match is locale-based')
63
- } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.languageBased) {
64
- console.log('The match is language-based')
65
- } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.relatedLocaleBased) {
66
- console.log('The match is related-locale-based')
67
- } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.defaultLocale) {
68
- console.log('The match is the default locale')
69
- }
70
- ```
71
-
72
- ## How does the resolver work?
73
-
74
- As per RFC 4647, this package uses the "lookup" matching scheme. This means that it will always produce exactly one match for a given request.
75
-
76
- The matching strategy will use the following logic:
77
-
78
- 1. The default locale (when provided) will always be put as the first locale being evaluated since it is considered the highest quality content available. Otherwise, the locales will be evaluated in the order provided, where the first is the highest quality and the last the lowest.
79
- 2. All locales and languages are extracted from the HTTP header and sorted by quality factor. Locales and languages that are in the HTTP header but not in scope are discarded.
80
- 3. Three different matching patterns (based on the HTTP header's quality factor and order of the provided locales):
81
- 1. If there were any matches, get the highest-ranked (quality factor) locale or language code:
82
- 1. **Locale-based match**: Is the highest-ranked a locale? If yes, this is the best match.
83
- 2. **Language-based match**: Otherwise, find the first locale that matches the highest-ranked language.
84
- 2. **Related-locale-based match**: If there is no match, find the first locale with a language that matches the highest-ranked language of locales that were not in scope. This is a bit of a "fuzzy match", but the presumption is that it's better to show content in a language that can be understood even if the country is wrong.
85
- 4. When using `resolveAcceptLanguage` return the default locale as a last resort option.
86
-
87
- ## Why another `Accept-Language` package?
88
-
89
- The `Accept-Language` header has been around since 1999. Like many other standards that deal with languages, the header is based
90
- on BCP 47 language tags. Language tags can be as simple as `fr` (non-country specific French) or more complex, for example,
91
- `sr-Latn-RS` would represent Latin script Serbian.
92
-
93
- One of the main challenges is that BCP 47 language tags can be either overly simple or too complex. This is one of the problems this
94
- library will try to address by focusing on locales identifiers using the `language`-`country` format instead of trying to provide
95
- full BCP 47 language tags support. The main reasons for this:
96
-
97
- - Using 2 letter language codes is rarely sufficient. Without being explicit about the targeted country for a given language, it is impossible to provide the right format for some content such as dates and numbers. Also, while languages are similar across countries, there are different ways to say the same thing. Our hypothesis is that by better targeting the audience, the user experience will improve.
98
- - About 99% of all cases can be covered using the `language`-`country` format. We could possibly extend script support in the future given a valid use case, but in the meantime, our goal is to keep this library as simple as possible, while providing the best matches.
99
-
100
- ## Additional references
101
-
102
- - Matching of Language Tags ([RFC 4647](https://tools.ietf.org/html/rfc4647))
103
- - Tags for Identifying Languages ([RFC 4646](https://tools.ietf.org/html/rfc4646))
104
- - The Accept-Language request-header field ([RFC 2616 section 14.4](https://tools.ietf.org/html/rfc2616#section-14.4))
105
- - Quality values ([RFC 2616 section 3.9](https://tools.ietf.org/html/rfc2616#section-3.9))
1
+ # resolve-accept-language
2
+
3
+ [![License](https://img.shields.io/npm/l/make-coverage-badge.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
4
+ [![npm download](https://img.shields.io/npm/dw/resolve-accept-language.svg?color=brightgreen)](https://www.npmjs.com/package/resolve-accept-language)
5
+ ![Coverage](https://img.shields.io/badge/Coverage-100%25-brightgreen.svg)
6
+ ![Dependencies](https://img.shields.io/badge/dependencies-0-brightgreen.svg)
7
+
8
+ Resolve the best locale based on the value of an `Accept-Language` HTTP header.
9
+
10
+ ## Usage
11
+
12
+ Add the package as a dependency:
13
+
14
+ ```
15
+ npm install resolve-accept-language
16
+ ```
17
+
18
+ Code example:
19
+
20
+ ```ts
21
+ import resolveAcceptLanguage from 'resolve-accept-language'
22
+
23
+ /**
24
+ * The API is well documented from within your IDE using TSDoc. The arguments are as follows:
25
+ *
26
+ * 1) The HTTP accept-language header.
27
+ * 2) The available locales (they must contain the default locale).
28
+ * 3) The default locale.
29
+ */
30
+ console.log(
31
+ resolveAcceptLanguage(
32
+ 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001',
33
+ // The `as const` is optional for TypeScript but gives better typing.
34
+ ['en-US', 'fr-CA'] as const,
35
+ 'en-US'
36
+ )
37
+ )
38
+ ```
39
+
40
+ Output:
41
+
42
+ ```
43
+ fr-CA
44
+ ```
45
+
46
+ ## Advanced use cases
47
+
48
+ You may want to control exactly the behavior depending on the type of match. For example, you could want to display a language picker on your home page if the match is not satisfactory. In those cases, you will need to use the `ResolveAcceptLanguage` class instead. It offers more visibility into the selection process while matching a locale:
49
+
50
+ ```ts
51
+ import { MATCH_TYPES, ResolveAcceptLanguage } from 'resolve-accept-language'
52
+
53
+ const resolveAcceptLanguage = new ResolveAcceptLanguage(
54
+ 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001' as const,
55
+ ['en-US', 'fr-CA'],
56
+ 'fr-CA'
57
+ )
58
+
59
+ console.log(`A locale was matched: ${resolveAcceptLanguage.getMatch()}`)
60
+
61
+ if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.localeBased) {
62
+ console.log('The match is locale-based')
63
+ } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.languageBased) {
64
+ console.log('The match is language-based')
65
+ } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.relatedLocaleBased) {
66
+ console.log('The match is related-locale-based')
67
+ } else if (resolveAcceptLanguage.getMatchType() === MATCH_TYPES.defaultLocale) {
68
+ console.log('The match is the default locale')
69
+ }
70
+ ```
71
+
72
+ ## How does the resolver work?
73
+
74
+ As per RFC 4647, this package uses the "lookup" matching scheme. This means that it will always produce exactly one match for a given request.
75
+
76
+ The matching strategy will use the following logic:
77
+
78
+ 1. The default locale (when provided) will always be put as the first locale being evaluated since it is considered the highest quality content available. Otherwise, the locales will be evaluated in the order provided, where the first is the highest quality and the last the lowest.
79
+ 2. All locales and languages are extracted from the HTTP header and sorted by quality factor. Locales and languages that are in the HTTP header but not in scope are discarded.
80
+ 3. Three different matching patterns (based on the HTTP header's quality factor and order of the provided locales):
81
+ 1. If there were any matches, get the highest-ranked (quality factor) locale or language code:
82
+ 1. **Locale-based match**: Is the highest-ranked a locale? If yes, this is the best match.
83
+ 2. **Language-based match**: Otherwise, find the first locale that matches the highest-ranked language.
84
+ 2. **Related-locale-based match**: If there is no match, find the first locale with a language that matches the highest-ranked language of locales that were not in scope. This is a bit of a "fuzzy match", but the presumption is that it's better to show content in a language that can be understood even if the country is wrong.
85
+ 4. When using `resolveAcceptLanguage` return the default locale as a last resort option.
86
+
87
+ ## Why another `Accept-Language` package?
88
+
89
+ The `Accept-Language` header has been around since 1999. Like many other standards that deal with languages, the header is based
90
+ on BCP 47 language tags. Language tags can be as simple as `fr` (non-country specific French) or more complex, for example,
91
+ `sr-Latn-RS` would represent Latin script Serbian.
92
+
93
+ One of the main challenges is that BCP 47 language tags can be either overly simple or too complex. This is one of the problems this
94
+ library will try to address by focusing on locales identifiers using the `language`-`country` format instead of trying to provide
95
+ full BCP 47 language tags support. The main reasons for this:
96
+
97
+ - Using 2 letter language codes is rarely sufficient. Without being explicit about the targeted country for a given language, it is impossible to provide the right format for some content such as dates and numbers. Also, while languages are similar across countries, there are different ways to say the same thing. Our hypothesis is that by better targeting the audience, the user experience will improve.
98
+ - About 99% of all cases can be covered using the `language`-`country` format. We could possibly extend script support in the future given a valid use case, but in the meantime, our goal is to keep this library as simple as possible, while providing the best matches.
99
+
100
+ ## Additional references
101
+
102
+ - Matching of Language Tags ([RFC 4647](https://tools.ietf.org/html/rfc4647))
103
+ - Tags for Identifying Languages ([RFC 4646](https://tools.ietf.org/html/rfc4646))
104
+ - The Accept-Language request-header field ([RFC 2616 section 14.4](https://tools.ietf.org/html/rfc2616#section-14.4))
105
+ - Quality values ([RFC 2616 section 3.9](https://tools.ietf.org/html/rfc2616#section-3.9))
@@ -0,0 +1,19 @@
1
+ import Locale from './locale';
2
+ export default class LocaleList<TLocales extends readonly string[]> {
3
+ /** A set of ISO 3166-1 alpha-2 country codes. */
4
+ readonly countries: Set<string>;
5
+ /** A set of ISO 639-1 alpha-2 language codes. */
6
+ readonly languages: Set<string>;
7
+ /** A set of locale identifiers using the BCP 47 `language`-`country` case-normalized format. */
8
+ readonly locales: Set<string>;
9
+ /** A list of locale objects. */
10
+ readonly objects: Locale[];
11
+ /**
12
+ * Create a list of locale identifiers.
13
+ *
14
+ * @param locales - An array of locale identifiers using the BCP 47 `language`-`country` format.
15
+ *
16
+ * @throws Will throw an error if one of the locale's format is invalid.
17
+ */
18
+ constructor(locales: TLocales extends string[] ? TLocales[number][] : TLocales);
19
+ }
@@ -0,0 +1,32 @@
1
+ import Locale from './locale';
2
+ var LocaleList = /** @class */ (function () {
3
+ /**
4
+ * Create a list of locale identifiers.
5
+ *
6
+ * @param locales - An array of locale identifiers using the BCP 47 `language`-`country` format.
7
+ *
8
+ * @throws Will throw an error if one of the locale's format is invalid.
9
+ */
10
+ function LocaleList(locales) {
11
+ var _this = this;
12
+ /** A set of ISO 3166-1 alpha-2 country codes. */
13
+ this.countries = new Set();
14
+ /** A set of ISO 639-1 alpha-2 language codes. */
15
+ this.languages = new Set();
16
+ /** A set of locale identifiers using the BCP 47 `language`-`country` case-normalized format. */
17
+ this.locales = new Set();
18
+ /** A list of locale objects. */
19
+ this.objects = [];
20
+ locales.forEach(function (locale) {
21
+ var localeObject = new Locale(locale);
22
+ if (!_this.locales.has(localeObject.identifier)) {
23
+ _this.objects.push(localeObject);
24
+ _this.locales.add(localeObject.identifier);
25
+ _this.languages.add(localeObject.languageCode);
26
+ _this.countries.add(localeObject.countryCode);
27
+ }
28
+ });
29
+ }
30
+ return LocaleList;
31
+ }());
32
+ export default LocaleList;
@@ -0,0 +1,39 @@
1
+ import { NormalizeLocale } from './resolve-accept-language';
2
+ /** Class to manage a locale identifier using the BCP 47 `language`-`country` format. */
3
+ export default class Locale<TLocale extends string = string> {
4
+ /** The ISO 3166-1 alpha-2 country code. */
5
+ readonly countryCode: string;
6
+ /** The locale identifier using the BCP 47 `language`-`country` case-normalized format. */
7
+ readonly identifier: NormalizeLocale<TLocale>;
8
+ /** The ISO 639-1 alpha-2 language code. */
9
+ readonly languageCode: string;
10
+ /**
11
+ * Create a new `Locale` object.
12
+ *
13
+ * @param identifier - A locale identifier using the BCP 47 `language`-`country` format (case insensitive).
14
+ *
15
+ * @throws An error if the `identifier` format is invalid.
16
+ */
17
+ constructor(identifier: string);
18
+ /**
19
+ * Is a given string an ISO 3166-1 alpha-2 country code.
20
+ *
21
+ * @param countryCode - An ISO 3166-1 alpha-2 country code.
22
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
23
+ */
24
+ static isCountryCode(countryCode: string, caseNormalized?: boolean): boolean;
25
+ /**
26
+ * Is a given string an ISO 639-1 alpha-2 language code.
27
+ *
28
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
29
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
30
+ */
31
+ static isLanguageCode(languageCode: string, caseNormalized?: boolean): boolean;
32
+ /**
33
+ * Is a given string a locale identifier following the BCP 47 `language`-`country` format.
34
+ *
35
+ * @param identifier - A potential locale identify to verify.
36
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
37
+ */
38
+ static isLocale(identifier: string, caseNormalized?: boolean): boolean;
39
+ }
@@ -0,0 +1,54 @@
1
+ /** Class to manage a locale identifier using the BCP 47 `language`-`country` format. */
2
+ var Locale = /** @class */ (function () {
3
+ /**
4
+ * Create a new `Locale` object.
5
+ *
6
+ * @param identifier - A locale identifier using the BCP 47 `language`-`country` format (case insensitive).
7
+ *
8
+ * @throws An error if the `identifier` format is invalid.
9
+ */
10
+ function Locale(identifier) {
11
+ if (!Locale.isLocale(identifier, false)) {
12
+ throw new Error("invalid locale identifier '".concat(identifier, "'"));
13
+ }
14
+ var _a = identifier.split('-'), languageCode = _a[0], countryCode = _a[1];
15
+ this.languageCode = languageCode.toLowerCase();
16
+ this.countryCode = countryCode.toUpperCase();
17
+ this.identifier = "".concat(this.languageCode, "-").concat(this.countryCode);
18
+ }
19
+ /**
20
+ * Is a given string an ISO 3166-1 alpha-2 country code.
21
+ *
22
+ * @param countryCode - An ISO 3166-1 alpha-2 country code.
23
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
24
+ */
25
+ Locale.isCountryCode = function (countryCode, caseNormalized) {
26
+ if (caseNormalized === void 0) { caseNormalized = true; }
27
+ var regExp = new RegExp(/^[A-Z]{2}$/, caseNormalized ? undefined : 'i');
28
+ return regExp.test(countryCode);
29
+ };
30
+ /**
31
+ * Is a given string an ISO 639-1 alpha-2 language code.
32
+ *
33
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
34
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
35
+ */
36
+ Locale.isLanguageCode = function (languageCode, caseNormalized) {
37
+ if (caseNormalized === void 0) { caseNormalized = true; }
38
+ var regExp = new RegExp(/^[a-z]{2}$/, caseNormalized ? undefined : 'i');
39
+ return regExp.test(languageCode);
40
+ };
41
+ /**
42
+ * Is a given string a locale identifier following the BCP 47 `language`-`country` format.
43
+ *
44
+ * @param identifier - A potential locale identify to verify.
45
+ * @param caseNormalized - Should we verify if the identifier is using the case-normalized format?
46
+ */
47
+ Locale.isLocale = function (identifier, caseNormalized) {
48
+ if (caseNormalized === void 0) { caseNormalized = true; }
49
+ var regExp = new RegExp(/^[a-z]{2}-[A-Z]{2}$/, caseNormalized ? undefined : 'i');
50
+ return regExp.test(identifier);
51
+ };
52
+ return Locale;
53
+ }());
54
+ export default Locale;
@@ -0,0 +1,79 @@
1
+ import { NormalizeLocale } from './resolve-accept-language';
2
+ /** Lookup list used to match the preferred locale based on the value of an `Accept-Language` HTTP header. */
3
+ export default class LookupList<TLocales extends readonly string[]> {
4
+ /** The list of locales used to get the match during the lookup. */
5
+ private localeList;
6
+ /**
7
+ * Data object where the properties are quality (in string format) and their values a set containing locale
8
+ * identifiers using the `language`-`country` format and ISO 639-1 alpha-2 language code.
9
+ */
10
+ private localesAndLanguagesByQuality;
11
+ /**
12
+ * Data object where the properties are quality (in string format) and their value a set of ISO 639-1 alpha-2
13
+ * language code.
14
+ */
15
+ private relatedLocaleLanguagesByQuality;
16
+ /**
17
+ * Create a new `LookupList` object.
18
+ *
19
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
20
+ * @param locales - An array of locale identifiers. The order will be used for matching where the first identifier will be more
21
+ * likely to be matched than the last identifier.
22
+ */
23
+ constructor(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]);
24
+ /**
25
+ * Get the top locale-based match if available.
26
+ *
27
+ * @returns The top locale-based match or undefined when there is no match.
28
+ */
29
+ getLocaleBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
30
+ /**
31
+ * Get the language-based match if available.
32
+ *
33
+ * @returns The language-based match or undefined when there is no match.
34
+ */
35
+ getLanguageBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
36
+ /**
37
+ * Get the related-locale-based match if available.
38
+ *
39
+ * @returns The related-locale-based match or undefined when there is no match.
40
+ */
41
+ getRelatedLocaleBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
42
+ /**
43
+ * Add a language in the data object matching its quality.
44
+ *
45
+ * @param quality - The HTTP header's quality factor associated with a language.
46
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
47
+ */
48
+ private addLanguage;
49
+ /**
50
+ * Add a locale in the data object matching its quality.
51
+ *
52
+ * @param quality - The HTTP header's quality factor associated with a locale.
53
+ * @param identifier - A locale identifier using the BCP 47 `language`-`country` case-normalized format.
54
+ */
55
+ private addLocale;
56
+ /**
57
+ * Add a related locale's language in the data object matching its quality.
58
+ *
59
+ * @param quality - The HTTP header's quality factor associated with a related locale's language.
60
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
61
+ */
62
+ private addRelatedLocaleLanguage;
63
+ /**
64
+ * Get a directive object from a directive string.
65
+ *
66
+ * @param directiveString - The string representing a directive, extracted from the HTTP header.
67
+ *
68
+ * @returns A `Directive` object or `undefined` if the string's format is invalid.
69
+ */
70
+ private getDirective;
71
+ /**
72
+ * Get a match from a data object.
73
+ *
74
+ * @param dataObject - A data object.
75
+ *
76
+ * @returns A match or undefined when there is no match.
77
+ */
78
+ private getMatch;
79
+ }
@@ -0,0 +1,207 @@
1
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
2
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
3
+ if (ar || !(i in from)) {
4
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
5
+ ar[i] = from[i];
6
+ }
7
+ }
8
+ return to.concat(ar || Array.prototype.slice.call(from));
9
+ };
10
+ import Locale from './locale';
11
+ import LocaleList from './locale-list';
12
+ /** Lookup list used to match the preferred locale based on the value of an `Accept-Language` HTTP header. */
13
+ var LookupList = /** @class */ (function () {
14
+ /**
15
+ * Create a new `LookupList` object.
16
+ *
17
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
18
+ * @param locales - An array of locale identifiers. The order will be used for matching where the first identifier will be more
19
+ * likely to be matched than the last identifier.
20
+ */
21
+ function LookupList(acceptLanguageHeader, locales, defaultLocale) {
22
+ var _this = this;
23
+ /**
24
+ * Data object where the properties are quality (in string format) and their values a set containing locale
25
+ * identifiers using the `language`-`country` format and ISO 639-1 alpha-2 language code.
26
+ */
27
+ this.localesAndLanguagesByQuality = {};
28
+ /**
29
+ * Data object where the properties are quality (in string format) and their value a set of ISO 639-1 alpha-2
30
+ * language code.
31
+ */
32
+ this.relatedLocaleLanguagesByQuality = {};
33
+ // Put the default locale first so that it will be more likely to be matched.
34
+ this.localeList = new LocaleList(__spreadArray([
35
+ defaultLocale
36
+ ], locales.filter(function (locale) { return locale !== defaultLocale; }), true));
37
+ var directives = acceptLanguageHeader
38
+ .split(',')
39
+ .map(function (directiveString) { return _this.getDirective(directiveString.trim()); })
40
+ .filter(function (directive) { return directive !== undefined; });
41
+ // Check if 'es-419' exists in the directives
42
+ var es419DirectiveIndex = directives.findIndex(function (directive) { return directive.locale === 'es-419'; });
43
+ if (es419DirectiveIndex >= 0) {
44
+ // Remove 'es-419' from the directive.
45
+ var es419Directive_1 = directives[es419DirectiveIndex];
46
+ directives.splice(es419DirectiveIndex, 1);
47
+ // Replace `es-419` by the common Latin American spanish variants supported by browsers.
48
+ var latinAmericanLocales = [
49
+ 'es-AR', // Spanish Argentina
50
+ 'es-CL', // Spanish Chile
51
+ 'es-CO', // Spanish Colombia
52
+ 'es-CR', // Spanish Costa Rica
53
+ 'es-HN', // Spanish Honduras
54
+ 'es-MX', // Spanish Mexico
55
+ 'es-PE', // Spanish Peru
56
+ 'es-US', // Spanish United States
57
+ 'es-UY', // Spanish Uruguay
58
+ 'es-VE', // Spanish Venezuela
59
+ ];
60
+ latinAmericanLocales.forEach(function (locale) {
61
+ directives.push({
62
+ languageCode: 'es',
63
+ locale: locale,
64
+ quality: es419Directive_1.quality,
65
+ });
66
+ });
67
+ }
68
+ for (var _i = 0, directives_1 = directives; _i < directives_1.length; _i++) {
69
+ var directive = directives_1[_i];
70
+ var locale = directive.locale, languageCode = directive.languageCode, quality = directive.quality;
71
+ // If the language is not supported, skip to the next match.
72
+ if (!this.localeList.languages.has(languageCode)) {
73
+ continue;
74
+ }
75
+ // If there is no country code (while the language is supported), add the language preference.
76
+ if (!locale) {
77
+ this.addLanguage(quality, languageCode);
78
+ continue;
79
+ }
80
+ // If the locale is not supported, but the locale's language is, add to locale language preference.
81
+ if (!this.localeList.locales.has(locale) && this.localeList.languages.has(languageCode)) {
82
+ this.addRelatedLocaleLanguage(quality, languageCode);
83
+ continue;
84
+ }
85
+ // If the locale is supported, add the locale preference.
86
+ this.addLocale(quality, locale);
87
+ }
88
+ }
89
+ /**
90
+ * Get the top locale-based match if available.
91
+ *
92
+ * @returns The top locale-based match or undefined when there is no match.
93
+ */
94
+ LookupList.prototype.getLocaleBasedMatch = function () {
95
+ var match = this.getMatch(this.localesAndLanguagesByQuality);
96
+ return match && Locale.isLocale(match)
97
+ ? match
98
+ : undefined;
99
+ };
100
+ /**
101
+ * Get the language-based match if available.
102
+ *
103
+ * @returns The language-based match or undefined when there is no match.
104
+ */
105
+ LookupList.prototype.getLanguageBasedMatch = function () {
106
+ var match = this.getMatch(this.localesAndLanguagesByQuality);
107
+ return match && !Locale.isLocale(match)
108
+ ? this.localeList.objects.find(function (locale) { return locale.languageCode === match; })
109
+ .identifier
110
+ : undefined;
111
+ };
112
+ /**
113
+ * Get the related-locale-based match if available.
114
+ *
115
+ * @returns The related-locale-based match or undefined when there is no match.
116
+ */
117
+ LookupList.prototype.getRelatedLocaleBasedMatch = function () {
118
+ var match = this.getMatch(this.relatedLocaleLanguagesByQuality);
119
+ return match
120
+ ? this.localeList.objects.find(function (locale) { return locale.languageCode === match; })
121
+ .identifier
122
+ : undefined;
123
+ };
124
+ /**
125
+ * Add a language in the data object matching its quality.
126
+ *
127
+ * @param quality - The HTTP header's quality factor associated with a language.
128
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
129
+ */
130
+ LookupList.prototype.addLanguage = function (quality, languageCode) {
131
+ if (!this.localesAndLanguagesByQuality[quality]) {
132
+ this.localesAndLanguagesByQuality[quality] = new Set();
133
+ }
134
+ this.localesAndLanguagesByQuality[quality].add(languageCode);
135
+ };
136
+ /**
137
+ * Add a locale in the data object matching its quality.
138
+ *
139
+ * @param quality - The HTTP header's quality factor associated with a locale.
140
+ * @param identifier - A locale identifier using the BCP 47 `language`-`country` case-normalized format.
141
+ */
142
+ LookupList.prototype.addLocale = function (quality, identifier) {
143
+ if (!this.localesAndLanguagesByQuality[quality]) {
144
+ this.localesAndLanguagesByQuality[quality] = new Set();
145
+ }
146
+ this.localesAndLanguagesByQuality[quality].add(identifier);
147
+ };
148
+ /**
149
+ * Add a related locale's language in the data object matching its quality.
150
+ *
151
+ * @param quality - The HTTP header's quality factor associated with a related locale's language.
152
+ * @param languageCode - An ISO 639-1 alpha-2 language code.
153
+ */
154
+ LookupList.prototype.addRelatedLocaleLanguage = function (quality, languageCode) {
155
+ if (!this.relatedLocaleLanguagesByQuality[quality]) {
156
+ this.relatedLocaleLanguagesByQuality[quality] = new Set();
157
+ }
158
+ this.relatedLocaleLanguagesByQuality[quality].add(languageCode);
159
+ };
160
+ /**
161
+ * Get a directive object from a directive string.
162
+ *
163
+ * @param directiveString - The string representing a directive, extracted from the HTTP header.
164
+ *
165
+ * @returns A `Directive` object or `undefined` if the string's format is invalid.
166
+ */
167
+ LookupList.prototype.getDirective = function (directiveString) {
168
+ /**
169
+ * The regular expression is excluding certain directives due to the inability to configure those options in modern
170
+ * browsers today (also those options seem unpractical):
171
+ *
172
+ * - The wildcard character "*", as per RFC 2616 (section 14.4), should match any unmatched language tag.
173
+ * - Language tags that starts with a wildcard (e.g., "*-CA") should match the first supported locale of a country.
174
+ * - A quality value equivalent to "0", as per RFC 2616 (section 3.9), should be considered as "not acceptable".
175
+ * - We hardcode the support for the `419` UN M49 code (as country code) representing Latin America to support `es-419`.
176
+ */
177
+ var directiveMatch = directiveString.match(/^((?<matchedLanguageCode>([a-z]{2}))(-(?<matchedCountryCode>[a-z]{2}|419))?)(;q=(?<matchedQuality>(1(\.0{0,3})?)|(0(\.\d{0,3})?)))?$/i);
178
+ if (!(directiveMatch === null || directiveMatch === void 0 ? void 0 : directiveMatch.groups)) {
179
+ return undefined; // No regular expression match.
180
+ }
181
+ var _a = directiveMatch.groups, matchedLanguageCode = _a.matchedLanguageCode, matchedCountryCode = _a.matchedCountryCode, matchedQuality = _a.matchedQuality;
182
+ var languageCode = matchedLanguageCode.toLowerCase();
183
+ var countryCode = matchedCountryCode ? matchedCountryCode.toUpperCase() : undefined;
184
+ // Only `es-419` is supported in browsers - if any other languages are using `419` we filter them out.
185
+ if (countryCode === '419' && languageCode !== 'es') {
186
+ return undefined;
187
+ }
188
+ var quality = matchedQuality === undefined ? '1' : Number.parseFloat(matchedQuality).toString(); // Remove trailing zeros.
189
+ var locale = countryCode ? "".concat(languageCode, "-").concat(countryCode) : undefined;
190
+ return { languageCode: languageCode, locale: locale, quality: quality };
191
+ };
192
+ /**
193
+ * Get a match from a data object.
194
+ *
195
+ * @param dataObject - A data object.
196
+ *
197
+ * @returns A match or undefined when there is no match.
198
+ */
199
+ LookupList.prototype.getMatch = function (dataObject) {
200
+ var dataObjectEntries = Object.entries(dataObject);
201
+ return dataObjectEntries.length === 0
202
+ ? undefined
203
+ : dataObjectEntries.sort().reverse()[0][1].values().next().value;
204
+ };
205
+ return LookupList;
206
+ }());
207
+ export default LookupList;
@@ -0,0 +1,63 @@
1
+ /** The type matches. */
2
+ export type MatchType = 'localeBased' | 'languageBased' | 'relatedLocaleBased' | 'defaultLocale';
3
+ /** Match type enumeration. */
4
+ export declare const MATCH_TYPES: {
5
+ readonly [K in MatchType]: K;
6
+ };
7
+ /** Type to normalize the locale format. */
8
+ export type NormalizeLocale<Remainder extends string> = Remainder extends `${infer LanguageCode}-${infer CountryCode}` ? `${Lowercase<LanguageCode>}-${Uppercase<CountryCode>}` : Remainder;
9
+ /** Resolve the preferred locale from an HTTP `Accept-Language` header. */
10
+ export declare class ResolveAcceptLanguage<TLocales extends readonly string[] = string[]> {
11
+ /** The default locale. */
12
+ private defaultLocale;
13
+ /** The locale-based match, if applicable. */
14
+ private localeBasedMatch;
15
+ /** The language-based match, if applicable. */
16
+ private languageBasedMatch;
17
+ /** The related-locale-based match, if applicable. */
18
+ private relatedLocaleBasedMatch;
19
+ /**
20
+ * Create a new `ResolveAcceptLanguage` object.
21
+ *
22
+ * All locale identifiers provided as parameters must following the BCP 47 `language`-`country` (case insensitive).
23
+ *
24
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
25
+ * @param locales - An array of locale identifiers. The order will be used for matching where the first identifier will be more
26
+ * likely to be matched than the last identifier.
27
+ */
28
+ constructor(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]);
29
+ /**
30
+ * Get the type of match.
31
+ *
32
+ * @returns The type of match.
33
+ */
34
+ getMatchType(): MatchType;
35
+ /**
36
+ * Get the matching locale.
37
+ *
38
+ * @returns The matching locale.
39
+ */
40
+ getMatch(): NormalizeLocale<TLocales[number]>;
41
+ }
42
+ /**
43
+ * Resolve the preferred locale from an HTTP `Accept-Language` header.
44
+ *
45
+ * All locale identifiers provided as parameters must following the BCP 47 `language`-`country` (case insensitive).
46
+ *
47
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
48
+ * @param locales - An array of locale identifiers that must include the default locale. The order will be used for matching where
49
+ * the first identifier will be more likely to be matched than the last identifier.
50
+ * @param defaultLocale - The default locale identifier when no match is found.
51
+ *
52
+ * @returns The locale identifier which was the best match, in case-normalized format.
53
+ *
54
+ * @example
55
+ * // returns 'fr-CA'
56
+ * resolveAcceptLanguage(
57
+ * 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001',
58
+ * ['en-US', 'fr-CA'],
59
+ * 'en-US'
60
+ * )
61
+ */
62
+ declare const resolveAcceptLanguage: <TLocales extends readonly string[]>(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]) => NormalizeLocale<TLocales[number]>;
63
+ export default resolveAcceptLanguage;
@@ -0,0 +1,103 @@
1
+ import Locale from './locale';
2
+ import LookupList from './lookup-list';
3
+ /** Match type enumeration. */
4
+ export var MATCH_TYPES = {
5
+ localeBased: 'localeBased',
6
+ languageBased: 'languageBased',
7
+ relatedLocaleBased: 'relatedLocaleBased',
8
+ defaultLocale: 'defaultLocale',
9
+ };
10
+ /** Resolve the preferred locale from an HTTP `Accept-Language` header. */
11
+ var ResolveAcceptLanguage = /** @class */ (function () {
12
+ /**
13
+ * Create a new `ResolveAcceptLanguage` object.
14
+ *
15
+ * All locale identifiers provided as parameters must following the BCP 47 `language`-`country` (case insensitive).
16
+ *
17
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
18
+ * @param locales - An array of locale identifiers. The order will be used for matching where the first identifier will be more
19
+ * likely to be matched than the last identifier.
20
+ */
21
+ function ResolveAcceptLanguage(acceptLanguageHeader, locales, defaultLocale) {
22
+ // Check if the locales are valid.
23
+ locales.forEach(function (locale) {
24
+ if (!Locale.isLocale(locale, false)) {
25
+ throw new Error("invalid locale identifier '".concat(locale, "'"));
26
+ }
27
+ });
28
+ // Check if the default locale is valid.
29
+ if (!Locale.isLocale(defaultLocale, false)) {
30
+ throw new Error("invalid default locale identifier '".concat(defaultLocale, "'"));
31
+ }
32
+ // Check if the default locale is included in the locales.
33
+ if (!locales.some(function (locale) { return locale.toLowerCase() === defaultLocale.toLowerCase(); })) {
34
+ throw new Error('the default locale must be included in the locales');
35
+ }
36
+ this.defaultLocale = new Locale(defaultLocale).identifier;
37
+ var lookupList = new LookupList(acceptLanguageHeader, locales, defaultLocale);
38
+ // Check if the match if locale based.
39
+ this.localeBasedMatch = lookupList.getLocaleBasedMatch();
40
+ if (this.localeBasedMatch) {
41
+ return;
42
+ }
43
+ // Check if the match is language based.
44
+ this.languageBasedMatch = lookupList.getLanguageBasedMatch();
45
+ if (this.languageBasedMatch) {
46
+ return;
47
+ }
48
+ // Check if the match is related-locale based.
49
+ this.relatedLocaleBasedMatch = lookupList.getRelatedLocaleBasedMatch();
50
+ if (this.relatedLocaleBasedMatch) {
51
+ return;
52
+ }
53
+ }
54
+ /**
55
+ * Get the type of match.
56
+ *
57
+ * @returns The type of match.
58
+ */
59
+ ResolveAcceptLanguage.prototype.getMatchType = function () {
60
+ return this.localeBasedMatch
61
+ ? MATCH_TYPES.localeBased
62
+ : this.languageBasedMatch
63
+ ? MATCH_TYPES.languageBased
64
+ : this.relatedLocaleBasedMatch
65
+ ? MATCH_TYPES.relatedLocaleBased
66
+ : MATCH_TYPES.defaultLocale;
67
+ };
68
+ /**
69
+ * Get the matching locale.
70
+ *
71
+ * @returns The matching locale.
72
+ */
73
+ ResolveAcceptLanguage.prototype.getMatch = function () {
74
+ var _a, _b, _c;
75
+ return ((_c = (_b = (_a = this.localeBasedMatch) !== null && _a !== void 0 ? _a : this.languageBasedMatch) !== null && _b !== void 0 ? _b : this.relatedLocaleBasedMatch) !== null && _c !== void 0 ? _c : this.defaultLocale);
76
+ };
77
+ return ResolveAcceptLanguage;
78
+ }());
79
+ export { ResolveAcceptLanguage };
80
+ /**
81
+ * Resolve the preferred locale from an HTTP `Accept-Language` header.
82
+ *
83
+ * All locale identifiers provided as parameters must following the BCP 47 `language`-`country` (case insensitive).
84
+ *
85
+ * @param acceptLanguageHeader - The value of an HTTP request `Accept-Language` header (also known as a "language priority list").
86
+ * @param locales - An array of locale identifiers that must include the default locale. The order will be used for matching where
87
+ * the first identifier will be more likely to be matched than the last identifier.
88
+ * @param defaultLocale - The default locale identifier when no match is found.
89
+ *
90
+ * @returns The locale identifier which was the best match, in case-normalized format.
91
+ *
92
+ * @example
93
+ * // returns 'fr-CA'
94
+ * resolveAcceptLanguage(
95
+ * 'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001',
96
+ * ['en-US', 'fr-CA'],
97
+ * 'en-US'
98
+ * )
99
+ */
100
+ var resolveAcceptLanguage = function (acceptLanguageHeader, locales, defaultLocale) {
101
+ return new ResolveAcceptLanguage(acceptLanguageHeader, locales, defaultLocale).getMatch();
102
+ };
103
+ export default resolveAcceptLanguage;
package/package.json CHANGED
@@ -1,68 +1,73 @@
1
- {
2
- "name": "resolve-accept-language",
3
- "version": "2.1.17",
4
- "description": "Resolve the preferred locale based on the value of an `Accept-Language` HTTP header.",
5
- "keywords": [
6
- "accept-language",
7
- "RFC 4647",
8
- "locale",
9
- "language tags",
10
- "RFC 4646",
11
- "BCP 47",
12
- "RFC 2616",
13
- "resolve",
14
- "detect",
15
- "intl",
16
- "i18n",
17
- "internationalization"
18
- ],
19
- "repository": {
20
- "type": "git",
21
- "url": "git+https://github.com/Avansai/resolve-accept-language.git"
22
- },
23
- "license": "MIT",
24
- "author": "Avansai (https://avansai.com)",
25
- "main": "lib/resolve-accept-language.js",
26
- "types": "lib/resolve-accept-language.d.ts",
27
- "files": [
28
- "lib"
29
- ],
30
- "scripts": {
31
- "build": "npm run prettier && npm run lint-fix && rm -Rf ./lib && tsc && npm run test",
32
- "lint-check": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx --ext .json .",
33
- "lint-fix": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx --ext .json --fix .",
34
- "lint-print-config": "eslint --print-config ./eslintrc.yaml",
35
- "prettier": "prettier --write .",
36
- "release": "dotenv -- release-it --only-version",
37
- "test": "jest --coverage"
38
- },
39
- "devDependencies": {
40
- "@release-it/conventional-changelog": "8.0.1",
41
- "@types/jest": "29.5.12",
42
- "@typescript-eslint/eslint-plugin": "6.21.0",
43
- "@typescript-eslint/parser": "6.21.0",
44
- "dotenv-cli": "7.3.0",
45
- "eslint": "8.56.0",
46
- "eslint-config-prettier": "9.1.0",
47
- "eslint-import-resolver-node": "0.3.9",
48
- "eslint-import-resolver-typescript": "3.6.1",
49
- "eslint-plugin-import": "2.29.1",
50
- "eslint-plugin-jest": "27.6.3",
51
- "eslint-plugin-json-files": "4.1.0",
52
- "eslint-plugin-prefer-arrow-functions": "3.2.4",
53
- "eslint-plugin-prettier": "5.1.3",
54
- "eslint-plugin-tsdoc": "0.2.17",
55
- "eslint-plugin-unicorn": "51.0.1",
56
- "jest": "29.7.0",
57
- "prettier": "3.2.5",
58
- "prettier-plugin-organize-imports": "3.2.4",
59
- "prettier-plugin-sh": "0.14.0",
60
- "release-it": "17.0.3",
61
- "ts-jest": "29.1.2",
62
- "ts-node": "10.9.2",
63
- "typescript": "5.3.3"
64
- },
65
- "engines": {
66
- "node": "^14.18.1 || >=16.0.0"
67
- }
68
- }
1
+ {
2
+ "name": "resolve-accept-language",
3
+ "version": "2.1.19",
4
+ "description": "Resolve the preferred locale based on the value of an `Accept-Language` HTTP header.",
5
+ "keywords": [
6
+ "accept-language",
7
+ "RFC 4647",
8
+ "locale",
9
+ "language tags",
10
+ "RFC 4646",
11
+ "BCP 47",
12
+ "RFC 2616",
13
+ "resolve",
14
+ "detect",
15
+ "intl",
16
+ "i18n",
17
+ "internationalization"
18
+ ],
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Avansai/resolve-accept-language.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Avansai (https://avansai.com)",
25
+ "type": "module",
26
+ "main": "lib/cjs/index.js",
27
+ "module": "lib/esm/index.js",
28
+ "types": "lib/esm/resolve-accept-language.d.ts",
29
+ "files": [
30
+ "lib"
31
+ ],
32
+ "scripts": {
33
+ "build": "npm run prettier && npm run lint-fix && rm -Rf ./lib && tsc -p tsconfig.cjs.json && tsc -p tsconfig.esm.json && npm run test",
34
+ "lint-check": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx --ext .json .",
35
+ "lint-fix": "eslint --fix .",
36
+ "lint-print-config": "eslint --print-config ./eslintrc.yaml",
37
+ "prettier": "prettier --write .",
38
+ "release": "dotenv -- release-it --only-version",
39
+ "test": "jest --coverage"
40
+ },
41
+ "devDependencies": {
42
+ "@release-it/conventional-changelog": "8.0.1",
43
+ "@types/jest": "29.5.12",
44
+ "@typescript-eslint/eslint-plugin": "7.0.2",
45
+ "@typescript-eslint/parser": "7.0.2",
46
+ "dotenv-cli": "7.3.0",
47
+ "eslint": "8.57.0",
48
+ "eslint-config-prettier": "9.1.0",
49
+ "eslint-import-resolver-node": "0.3.9",
50
+ "eslint-import-resolver-typescript": "3.6.1",
51
+ "eslint-plugin-import": "2.29.1",
52
+ "eslint-plugin-jest": "27.9.0",
53
+ "eslint-plugin-jsdoc": "48.2.0",
54
+ "eslint-plugin-json-files": "4.1.0",
55
+ "eslint-plugin-prefer-arrow-functions": "3.2.4",
56
+ "eslint-plugin-prettier": "5.1.3",
57
+ "eslint-plugin-tsdoc": "0.2.17",
58
+ "eslint-plugin-unicorn": "51.0.1",
59
+ "globals": "14.0.0",
60
+ "jest": "29.7.0",
61
+ "jsonc-eslint-parser": "2.4.0",
62
+ "prettier": "3.2.5",
63
+ "prettier-plugin-organize-imports": "3.2.4",
64
+ "prettier-plugin-sh": "0.14.0",
65
+ "release-it": "17.1.1",
66
+ "ts-jest": "29.1.2",
67
+ "ts-node": "10.9.2",
68
+ "typescript": "5.3.3"
69
+ },
70
+ "engines": {
71
+ "node": "^14.18.1 || >=16.0.0"
72
+ }
73
+ }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes