Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

resolve-accept-language

Package Overview
Dependencies
Maintainers
1
Versions
173
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

resolve-accept-language - npm Package Compare versions

Comparing version 1.1.56 to 2.0.0

4

lib/locale-list.d.ts
import Locale from './locale';
export default class LocaleList {
export default class LocaleList<TLocales extends readonly string[]> {
/** A set of ISO 3166-1 alpha-2 country codes. */

@@ -18,3 +18,3 @@ readonly countries: Set<string>;

*/
constructor(locales: string[]);
constructor(locales: TLocales extends string[] ? TLocales[number][] : TLocales);
}

@@ -0,7 +1,8 @@

import { NormalizeLocale } from './resolve-accept-language';
/** Class to manage a locale identifier using the BCP 47 `language`-`country` format. */
export default class Locale {
export default class Locale<TLocale extends string = string> {
/** The ISO 3166-1 alpha-2 country code. */
readonly countryCode: string;
/** The locale identifier using the BCP 47 `language`-`country` case-normalized format. */
readonly identifier: string;
readonly identifier: NormalizeLocale<TLocale>;
/** The ISO 639-1 alpha-2 language code. */

@@ -8,0 +9,0 @@ readonly languageCode: string;

@@ -0,3 +1,4 @@

import { NormalizeLocale } from './resolve-accept-language';
/** Lookup list used to match the preferred locale based on the value of an `Accept-Language` HTTP header. */
export default class LookupList {
export default class LookupList<TLocales extends readonly string[]> {
/** The list of locales used to get the match during the lookup. */

@@ -22,23 +23,21 @@ private localeList;

*/
constructor(acceptLanguageHeader: string, locales: string[]);
constructor(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]);
/**
* Get the top (highest-ranked) locale by language.
* Get the top locale-based match if available.
*
* @param languageCode - An ISO 639-1 alpha-2 language code.
*
* @returns The top locale with the specified language.
* @returns The top locale-based match or undefined when there is no match.
*/
getTopByLanguage(languageCode: string): string | undefined;
getLocaleBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
/**
* Get the top (highest-ranked) locale or language.
* Get the language-based match if available.
*
* @returns The top match, which can either be a locale or a language.
* @returns The language-based match or undefined when there is no match.
*/
getTopLocaleOrLanguage(): string | undefined;
getLanguageBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
/**
* Get the top (highest-ranked) related locale.
* Get the related-locale-based match if available.
*
* @returns The top related locale.
* @returns The related-locale-based match or undefined when there is no match.
*/
getTopRelatedLocale(): string | undefined;
getRelatedLocaleBasedMatch(): NormalizeLocale<TLocales[number]> | undefined;
/**

@@ -74,9 +73,9 @@ * Add a language in the data object matching its quality.

/**
* Get the top (highest-ranked) entry from a dataset object entries.
* Get a match from a data object.
*
* @param dataObjectEntries - The object entries of a dataset object.
* @param dataObject - A data object.
*
* @returns The top entry from a dataset object entries.
* @returns A match or undefined when there is no match.
*/
private getTop;
private getMatch;
}
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
var locale_1 = require("./locale");
var locale_list_1 = require("./locale-list");

@@ -13,3 +23,4 @@ /** Lookup list used to match the preferred locale based on the value of an `Accept-Language` HTTP header. */

*/
function LookupList(acceptLanguageHeader, locales) {
function LookupList(acceptLanguageHeader, locales, defaultLocale) {
var _this = this;
/**

@@ -25,12 +36,12 @@ * Data object where the properties are quality (in string format) and their values a set containing locale

this.relatedLocaleLanguagesByQuality = {};
this.localeList = new locale_list_1.default(locales);
var directiveStrings = acceptLanguageHeader
// Put the default locale first so that it will be more likely to be matched.
this.localeList = new locale_list_1.default(__spreadArray([
defaultLocale
], locales.filter(function (locale) { return locale !== defaultLocale; }), true));
var directives = acceptLanguageHeader
.split(',')
.map(function (directiveString) { return directiveString.trim(); });
for (var _i = 0, directiveStrings_1 = directiveStrings; _i < directiveStrings_1.length; _i++) {
var directiveString = directiveStrings_1[_i];
var directive = this.getDirective(directiveString);
if (directive === undefined) {
continue; // No match for this directive.
}
.map(function (directiveString) { return _this.getDirective(directiveString.trim()); })
.filter(function (directive) { return directive !== undefined; });
for (var _i = 0, directives_1 = directives; _i < directives_1.length; _i++) {
var directive = directives_1[_i];
var locale = directive.locale, languageCode = directive.languageCode, quality = directive.quality;

@@ -56,36 +67,35 @@ // If the language is not supported, skip to the next match.

/**
* Get the top (highest-ranked) locale by language.
* Get the top locale-based match if available.
*
* @param languageCode - An ISO 639-1 alpha-2 language code.
*
* @returns The top locale with the specified language.
* @returns The top locale-based match or undefined when there is no match.
*/
LookupList.prototype.getTopByLanguage = function (languageCode) {
var _a;
return (_a = this.localeList.objects.find(function (locale) { return locale.languageCode === languageCode; })) === null || _a === void 0 ? void 0 : _a.identifier;
LookupList.prototype.getLocaleBasedMatch = function () {
var match = this.getMatch(this.localesAndLanguagesByQuality);
return match && locale_1.default.isLocale(match)
? match
: undefined;
};
/**
* Get the top (highest-ranked) locale or language.
* Get the language-based match if available.
*
* @returns The top match, which can either be a locale or a language.
* @returns The language-based match or undefined when there is no match.
*/
LookupList.prototype.getTopLocaleOrLanguage = function () {
var localesAndLanguagesByQuality = Object.entries(this.localesAndLanguagesByQuality);
if (localesAndLanguagesByQuality.length === 0) {
return undefined;
}
return this.getTop(localesAndLanguagesByQuality);
LookupList.prototype.getLanguageBasedMatch = function () {
var match = this.getMatch(this.localesAndLanguagesByQuality);
return match && !locale_1.default.isLocale(match)
? this.localeList.objects.find(function (locale) { return locale.languageCode === match; })
.identifier
: undefined;
};
/**
* Get the top (highest-ranked) related locale.
* Get the related-locale-based match if available.
*
* @returns The top related locale.
* @returns The related-locale-based match or undefined when there is no match.
*/
LookupList.prototype.getTopRelatedLocale = function () {
var relatedLocaleLanguagesByQuality = Object.entries(this.relatedLocaleLanguagesByQuality);
if (relatedLocaleLanguagesByQuality.length === 0) {
return undefined;
}
var topRelatedLocaleLanguage = this.getTop(relatedLocaleLanguagesByQuality);
return this.getTopByLanguage(topRelatedLocaleLanguage);
LookupList.prototype.getRelatedLocaleBasedMatch = function () {
var match = this.getMatch(this.relatedLocaleLanguagesByQuality);
return match
? this.localeList.objects.find(function (locale) { return locale.languageCode === match; })
.identifier
: undefined;
};

@@ -141,3 +151,3 @@ /**

* - The wildcard character "*", as per RFC 2616 (section 14.4), should match any unmatched language tag.
* - Language tags that starts with a wildcard (e.g. "*-CA") should match the first supported locale of a country.
* - Language tags that starts with a wildcard (e.g., "*-CA") should match the first supported locale of a country.
* - A quality value equivalent to "0", as per RFC 2616 (section 3.9), should be considered as "not acceptable".

@@ -157,10 +167,13 @@ */

/**
* Get the top (highest-ranked) entry from a dataset object entries.
* Get a match from a data object.
*
* @param dataObjectEntries - The object entries of a dataset object.
* @param dataObject - A data object.
*
* @returns The top entry from a dataset object entries.
* @returns A match or undefined when there is no match.
*/
LookupList.prototype.getTop = function (dataObjectEntries) {
return dataObjectEntries.sort().reverse()[0][1].values().next().value;
LookupList.prototype.getMatch = function (dataObject) {
var dataObjectEntries = Object.entries(dataObject);
return dataObjectEntries.length === 0
? undefined
: dataObjectEntries.sort().reverse()[0][1].values().next().value;
};

@@ -167,0 +180,0 @@ return LookupList;

@@ -0,7 +1,17 @@

/** The type matches. */
export type MatchType = 'localeBased' | 'languageBased' | 'relatedLocaleBased' | 'defaultLocale';
/** Match type enumeration. */
export declare const MATCH_TYPES: {
readonly [K in MatchType]: K;
};
/** Type to normalize the locale format. */
export type NormalizeLocale<Remainder extends string> = Remainder extends `${infer LanguageCode}-${infer CountryCode}` ? `${Lowercase<LanguageCode>}-${Uppercase<CountryCode>}` : Remainder;
/** Resolve the preferred locale from an HTTP `Accept-Language` header. */
export declare class ResolveAcceptLanguage {
export declare class ResolveAcceptLanguage<TLocales extends readonly string[] = string[]> {
/** The default locale. */
private defaultLocale;
/** The locale-based match, if applicable. */
private localeBasedMatch;
/** The language-based match, if applicable. */
private languageBasedMatch;
/** The locale-based match, if applicable. */
private localeBasedMatch;
/** The related-locale-based match, if applicable. */

@@ -18,39 +28,15 @@ private relatedLocaleBasedMatch;

*/
constructor(acceptLanguageHeader: string, locales: string[]);
constructor(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]);
/**
* Is the best match language-based?
* Get the type of match.
*
* @returns True if the best match language-based, otherwise false.
* @returns The type of match.
*/
bestMatchIsLanguageBased(): boolean;
getMatchType(): MatchType;
/**
* Is the best match locale-based?
* Get the matching locale.
*
* @returns True if the best match locale-based, otherwise false.
* @returns The matching locale.
*/
bestMatchIsLocaleBased(): boolean;
/**
* Is the best match related-locale-based?
*
* @returns True if the best match related-locale-based, otherwise false.
*/
bestMatchIsRelatedLocaleBased(): boolean;
/**
* Get the locale which was the best match.
*
* @returns The locale which was the best match.
*/
getBestMatch(): string | undefined;
/**
* Was a match found when resolving the preferred locale?
*
* @returns True when a match is found, otherwise false.
*/
hasMatch(): boolean;
/**
* Did the resolution of the preferred locale find no match?
*
* @returns True when there is no match, otherwise false.
*/
hasNoMatch(): boolean;
getMatch(): NormalizeLocale<TLocales[number]>;
}

@@ -77,3 +63,3 @@ /**

*/
declare const resolveAcceptLanguage: <TLocales extends readonly string[]>(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]) => TLocales[number];
declare const resolveAcceptLanguage: <TLocales extends readonly string[]>(acceptLanguageHeader: string, locales: TLocales extends string[] ? TLocales[number][] : TLocales, defaultLocale: TLocales[number]) => NormalizeLocale<TLocales[number]>;
export default resolveAcceptLanguage;
"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ResolveAcceptLanguage = void 0;
exports.ResolveAcceptLanguage = exports.MATCH_TYPES = void 0;
var locale_1 = require("./locale");
var lookup_list_1 = require("./lookup-list");
/** Match type enumeration. */
exports.MATCH_TYPES = {
localeBased: 'localeBased',
languageBased: 'languageBased',
relatedLocaleBased: 'relatedLocaleBased',
defaultLocale: 'defaultLocale',
};
/** Resolve the preferred locale from an HTTP `Accept-Language` header. */

@@ -26,66 +24,58 @@ var ResolveAcceptLanguage = /** @class */ (function () {

*/
function ResolveAcceptLanguage(acceptLanguageHeader, locales) {
var lookupList = new lookup_list_1.default(acceptLanguageHeader, locales);
var topLocaleOrLanguage = lookupList.getTopLocaleOrLanguage();
if (topLocaleOrLanguage === undefined) {
this.relatedLocaleBasedMatch = lookupList.getTopRelatedLocale();
}
else {
if (locale_1.default.isLocale(topLocaleOrLanguage)) {
this.localeBasedMatch = topLocaleOrLanguage;
function ResolveAcceptLanguage(acceptLanguageHeader, locales, defaultLocale) {
// Check if the locales are valid.
locales.forEach(function (locale) {
if (!locale_1.default.isLocale(locale, false)) {
throw new Error("invalid locale identifier '".concat(locale, "'"));
}
else {
this.languageBasedMatch = lookupList.getTopByLanguage(topLocaleOrLanguage);
}
});
// Check if the default locale is valid.
if (!locale_1.default.isLocale(defaultLocale, false)) {
throw new Error("invalid default locale identifier '".concat(defaultLocale, "'"));
}
// Check if the default locale is included in the locales.
if (!locales.some(function (locale) { return locale.toLowerCase() === defaultLocale.toLowerCase(); })) {
throw new Error('the default locale must be included in the locales');
}
this.defaultLocale = new locale_1.default(defaultLocale).identifier;
var lookupList = new lookup_list_1.default(acceptLanguageHeader, locales, defaultLocale);
// Check if the match if locale based.
this.localeBasedMatch = lookupList.getLocaleBasedMatch();
if (this.localeBasedMatch) {
return;
}
// Check if the match is language based.
this.languageBasedMatch = lookupList.getLanguageBasedMatch();
if (this.languageBasedMatch) {
return;
}
// Check if the match is related-locale based.
this.relatedLocaleBasedMatch = lookupList.getRelatedLocaleBasedMatch();
if (this.relatedLocaleBasedMatch) {
return;
}
}
/**
* Is the best match language-based?
* Get the type of match.
*
* @returns True if the best match language-based, otherwise false.
* @returns The type of match.
*/
ResolveAcceptLanguage.prototype.bestMatchIsLanguageBased = function () {
return this.languageBasedMatch !== undefined;
ResolveAcceptLanguage.prototype.getMatchType = function () {
return this.localeBasedMatch
? exports.MATCH_TYPES.localeBased
: this.languageBasedMatch
? exports.MATCH_TYPES.languageBased
: this.relatedLocaleBasedMatch
? exports.MATCH_TYPES.relatedLocaleBased
: exports.MATCH_TYPES.defaultLocale;
};
/**
* Is the best match locale-based?
* Get the matching locale.
*
* @returns True if the best match locale-based, otherwise false.
* @returns The matching locale.
*/
ResolveAcceptLanguage.prototype.bestMatchIsLocaleBased = function () {
return this.localeBasedMatch !== undefined;
ResolveAcceptLanguage.prototype.getMatch = function () {
var _a, _b, _c;
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);
};
/**
* Is the best match related-locale-based?
*
* @returns True if the best match related-locale-based, otherwise false.
*/
ResolveAcceptLanguage.prototype.bestMatchIsRelatedLocaleBased = function () {
return this.relatedLocaleBasedMatch !== undefined;
};
/**
* Get the locale which was the best match.
*
* @returns The locale which was the best match.
*/
ResolveAcceptLanguage.prototype.getBestMatch = function () {
var _a, _b;
return (_b = (_a = this.localeBasedMatch) !== null && _a !== void 0 ? _a : this.languageBasedMatch) !== null && _b !== void 0 ? _b : this.relatedLocaleBasedMatch;
};
/**
* Was a match found when resolving the preferred locale?
*
* @returns True when a match is found, otherwise false.
*/
ResolveAcceptLanguage.prototype.hasMatch = function () {
return this.getBestMatch() === undefined ? false : true;
};
/**
* Did the resolution of the preferred locale find no match?
*
* @returns True when there is no match, otherwise false.
*/
ResolveAcceptLanguage.prototype.hasNoMatch = function () {
return !this.hasMatch();
};
return ResolveAcceptLanguage;

@@ -115,24 +105,4 @@ }());

var resolveAcceptLanguage = function (acceptLanguageHeader, locales, defaultLocale) {
var localesIncludeDefault = false;
locales.forEach(function (locale) {
if (!locale_1.default.isLocale(locale, false)) {
throw new Error("invalid locale identifier '".concat(locale, "'"));
}
if (locale.toLowerCase() === defaultLocale.toLocaleLowerCase()) {
localesIncludeDefault = true;
}
});
if (!locale_1.default.isLocale(defaultLocale, false)) {
throw new Error("invalid default locale identifier '".concat(defaultLocale, "'"));
}
if (!localesIncludeDefault) {
throw new Error('the default locale must be included in the locales');
}
var rankedLocales = __spreadArray([defaultLocale], locales.filter(function (locale) { return locale !== defaultLocale; }), true);
var resolveAcceptLanguage = new ResolveAcceptLanguage(acceptLanguageHeader, rankedLocales);
if (resolveAcceptLanguage.hasMatch()) {
return resolveAcceptLanguage.getBestMatch();
}
return new locale_1.default(defaultLocale).identifier;
return new ResolveAcceptLanguage(acceptLanguageHeader, locales, defaultLocale).getMatch();
};
exports.default = resolveAcceptLanguage;
{
"name": "resolve-accept-language",
"version": "1.1.56",
"version": "2.0.0",
"description": "Resolve the preferred locale based on the value of an `Accept-Language` HTTP header.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -9,3 +9,3 @@ # resolve-accept-language

Resolve the preferred locale based on the value of an `Accept-Language` HTTP header.
Resolve the best locale based on the value of an `Accept-Language` HTTP header.

@@ -25,4 +25,16 @@ ## Usage

/**
* The API is well documented from within your IDE using TSDoc. The arguments are as follows:
*
* 1) The HTTP accept-language header.
* 2) The available locales (they must contain the default locale).
* 3) The default locale.
*/
console.log(
resolveAcceptLanguage('fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001', ['en-US', 'fr-CA'], 'en-US')
resolveAcceptLanguage(
'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001',
// The `as const` is optional for TypeScript but gives better typing.
['en-US', 'fr-CA'] as const,
'en-US'
)
)

@@ -42,30 +54,21 @@ ```

```ts
import { ResolveAcceptLanguage } from 'resolve-accept-language'
import { MATCH_TYPES, ResolveAcceptLanguage } from 'resolve-accept-language'
/**
* If you are planning to have a "default locale", make sure to add it first in the provided locale list.
* By doing this, your match result will be identical to `resolveAcceptLanguage` as it always checks the
* default locale first.
*/
const resolveAcceptLanguage = new ResolveAcceptLanguage('fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001', [
'en-US',
'fr-CA',
])
const resolveAcceptLanguage = new ResolveAcceptLanguage(
'fr-CA;q=0.01,en-CA;q=0.1,en-US;q=0.001' as const,
['en-US', 'fr-CA'],
'fr-CA'
)
if (resolveAcceptLanguage.hasMatch()) {
const locale = resolveAcceptLanguage.getBestMatch() as string
console.log(`A locale was matched: ${locale}`)
console.log(`A locale was matched: ${resolveAcceptLanguage.getMatch()}`)
if (resolveAcceptLanguage.bestMatchIsLocaleBased()) {
console.log('The match is locale-based')
} else if (resolveAcceptLanguage.bestMatchIsLanguageBased()) {
console.log('The match is language-based')
} else if (resolveAcceptLanguage.bestMatchIsRelatedLocaleBased()) {
console.log('The match is related-locale-based')
}
if (resolveAcceptLanguage.getMatchType() !== MATCH_TYPES.localeBased) {
console.log('The match is locale-based')
} else if (resolveAcceptLanguage.getMatchType() !== MATCH_TYPES.languageBased) {
console.log('The match is language-based')
} else if (resolveAcceptLanguage.getMatchType() !== MATCH_TYPES.relatedLocaleBased) {
console.log('The match is related-locale-based')
} else if (resolveAcceptLanguage.getMatchType() !== MATCH_TYPES.defaultLocale) {
console.log('The match is the default locale')
}
if (resolveAcceptLanguage.hasNoMatch()) {
console.log('No match found :(')
}
```

@@ -72,0 +75,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc