Socket
Socket
Sign inDemoInstall

accept-language

Package Overview
Dependencies
Maintainers
1
Versions
52
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

accept-language - npm Package Compare versions

Comparing version 2.0.1 to 2.0.3

.editorconfig

294

index.js
/**
* Private variables
* Dependencies
*/
var bcp47 = require('bcp47');
var acceptLanguageSyntax = /((([a-zA-Z]+(-[a-zA-Z]+)?)|\*)(;q=[0-1](\.[0-9]+)?)?)*/g
, isLanguage = /^[a-z]{2}$/
, isRegion = /^[a-z]{2}$/i;
/**
* Prototype
* Object's size
*/
Object.size = function(object) {
var size = 0, key;
for (key in object) {
if (object.hasOwnProperty(key)) size++;
}
return size;
};
var acceptLanguage = exports = module.exports = {};
/**
* Languague codes
* AcceptLanguage
*/
var AcceptLanguage = function() {};
acceptLanguage._languages = [];
/**
* Default code
* Language tags
*
* @type {Objects}
* @public
*/
AcceptLanguage.prototype.languageTags_ = {};
acceptLanguage.defaultLanguage = null;
/**
* Prune languages that aren't defined
* Default language tag
*
* @param {Array.<language>} languages
* @return {Array.<language>}
* @api public
* @type {String}
*/
AcceptLanguage.prototype.defaultLanguageTag = null;
function prune(languages) {
var _this = this;
if(acceptLanguage._languages.length > 0) {
languages = languages.filter(function(language) {
var filter = false;
if(acceptLanguage._languages.indexOf(language.language) === -1) {
return false;
}
return true;
});
/**
* Prune language tags that aren't defined
*
* @param {Array.<languageTag>} languageTags
* @return {Array.<{ value: String, quality: Number }>}
* @private
*/
AcceptLanguage.prototype.prune_ = function(languageTags) {
var this_ = this;
if(Object.size(this.languageTags_) > 0) {
languageTags = languageTags
.filter(function(languageTag) {
var language = languageTag.language;
// Filter non-defined language tags.
if(typeof this_.languageTags_[language] === 'undefined') {
return false;
}
// Filter tags with only language sub tags that doesn't
// have the supported language. E.g. our defined set is
// ['en'] but th acceptLanguageString is ['es']. So we
// filter away ['es'].
if(!languageTag.region) {
if(!this_.languageTags_[language].hasOnlyLanguage) {
return false;
}
}
return true;
})
.map(function(languageTag) {
var language = languageTag.language;
if(languageTag.region) {
var regionIndex = this_.languageTags_[language].regions.indexOf(languageTag.region);
var hasRegion = true;
if(regionIndex === -1) {
hasRegion = false;
regionIndex = 0;
}
// It should return the first matching region language tag
// only if it doesn't contain ony root language tag.
// So if the define language tags are ['es-419', 'es-US']
// and the Accept-Language string is ['es-ES']. We should
// return 'es-419', because it has the biggest priority.
//
// Whenever it matches only language subtag and not region
// tags and there exist one root language tag. We should
// return the root language tag. E.g. If we have the set
// ['es', 'es-419'] and the Accept-Language string is
// 'es-ES'. Then we should return just ['es'].
//
// Wheneve it matches both language and region subtag it
// should return that matched language tag, regardless if
// there exist any root only languae subtag. E.g. If we
// have the set ['es', 'es-419', 'es-PO'] and the Accept-
// Language header is 'es-419'. Then we should return
// ['es-419'].
if(typeof this_.languageTags_[language].values[regionIndex] !== 'undefined') {
if(hasRegion || !this_.languageTags_[language].onlyLanguageValue) {
return {
value: this_.languageTags_[language].values[regionIndex],
language: language,
region: this_.languageTags_[language].regions[regionIndex] || null,
quality: languageTag.quality
};
}
}
return {
value: this_.languageTags_[language].onlyLanguageValue,
language: language,
region: null,
quality: languageTag.quality
};
}
return languageTag;
});
}
// If no codes matches the defined set. Return
// the default language if it is set
if(languages.length === 0 && acceptLanguage.defaultLanguage) {
return [acceptLanguage.defaultLanguage];
// If no language tags matches the defined set
if(languageTags.length === 0 && this_.defaultLanguageTag) {
return [this_.defaultLanguageTag];
}
return languages;
return languageTags;
};
/**
* Define codes
* Define languages
*
* @param {Array.<code>} codes
* @param {Array.<String>} languageTags
* @return {void}
* @throws {TypeError}
* @api public
* @public
*/
AcceptLanguage.prototype.languageTags = function(languageTags) {
var this_ = this;
exports.languages = function(languages) {
var _this = this;
// Reset language tags
this.languageTags_ = {};
languages.forEach(function(language) {
if(typeof language !== 'string') {
throw new TypeError('First parameter must be an array of strings');
languageTags.forEach(function(languageTagString) {
var languageTag = bcp47.parse(languageTagString);
if(!languageTag) {
throw new TypeError('Your language tag (' + languageTagString + ') are not bcp47 compliant. For more info https://tools.ietf.org/html/bcp47.');
}
if(!isLanguage.test(language)) {
throw new TypeError('First parameter must be an array consisting of languague codes. Wrong syntax if code: ' + code);
var language = languageTag.langtag.language.language;
var region = languageTag.langtag.region;
if(!this_.languageTags_[language]) {
this_.languageTags_[language] = {
values: region ? [languageTagString] : [],
regions: region ? [region] : [],
onlyLanguageValue: null
};
}
// Store language
_this._languages.push(language);
else {
if(region) {
this_.languageTags_[language].values.push(languageTagString);
this_.languageTags_[language].regions.push(region);
}
}
if(!region) {
this_.languageTags_[language].onlyLanguageValue = languageTagString;
}
});
var defaultLanguageTag = bcp47.parse(languageTags[0]);
this.defaultLanguageTag = {
value: languageTags[0],
language: defaultLanguageTag.langtag.language.language,
region: defaultLanguageTag.langtag.region,
quality: 1.0
};
};
/**
* Default language if no-match occurs
*
* @param {String} language
* @returns {void}
* @throws {TypeError}
* @api public
*/
exports.default = function(language) {
if(typeof language === 'string') {
var properties = language.split('-');
if(properties.length === 0) {
throw new TypeError('The string you provided is not a locale code string');
}
language = {
language: properties[0],
region: properties[1],
quality: 1
};
}
if(typeof language !== 'object') {
throw new TypeError('First parameter must be an object');
}
if(typeof language.language !== 'string') {
throw new TypeError('Property code must be a string and can\'t be undefined');
}
if(!isLanguage.test(language.language)) {
throw new TypeError('Property code must consist of two lowercase letters [a-z]');
}
if(typeof language.region === 'string' && !isRegion.test(language.region)) {
throw new TypeError('Property region must consist of two case-insensitive letters [a-zA-Z]');
}
// Set language quality to 1.0
language.quality = 1.0;
this.defaultLanguage = language;
};
/**
* Parse accept language string
*
* @param {String} acceptLanguage
* @return {Array.<language>}
* @api public
* @param {String} string Accept-Language string
* @return {Array.<{ value: String, quality: Number }>}
* @public
*/
exports.parse = function(acceptLanguage) {
if(typeof acceptLanguage === 'object' && acceptLanguage.headers) {
acceptLanguage = acceptLanguage.headers['accept-language'];
AcceptLanguage.prototype.parse = function(string) {
if(typeof string !== 'string' || string.length === 0) {
return this.defaultLanguageTag ? [this.defaultLanguageTag] : [];
}
if(typeof acceptLanguage !== 'string') {
return this.defaultLanguage ? [this.defaultLanguage] : [];
}
var strings = (acceptLanguage || '').match(acceptLanguageSyntax);
var languages = strings.map(function(match) {
if(!match){
return;
var languageTags = string.split(',');
languageTags = languageTags.map(function(languageTagString) {
languageTagString = languageTagString.replace(/\s+/, '');
var components = languageTagString.split(';');
var languageTag = bcp47.parse(components[0]);
if(!languageTag) {
return null;
}
var bits = match.split(';');
var ietf = bits[0].split('-');
return {
language: ietf[0],
region: ietf[1],
quality: bits[1] ? parseFloat(bits[1].split('=')[1]) : 1.0
value: components[0],
language: languageTag.langtag.language.language,
region: languageTag.langtag.region,
quality: components[1] ? parseFloat(components[1].split('=')[1]) : 1.0
};
})
.filter(function(language) {
return language;
// Filter non-defined language tags
.filter(function(languageTag) {
return languageTag;
})
// Sort language tags
.sort(function(a, b) {

@@ -160,4 +224,28 @@ return b.quality - a.quality;

return prune(languages);
return this.prune_(languageTags);
};
/**
* Get most suitable language tag
*
* @param {String} string Accept-Language string
* @return {String}
* @public
*/
AcceptLanguage.prototype.get = function(string) {
return this.parse(string)[0].value;
};
/**
* For use as a single-ton
*/
module.exports = new AcceptLanguage();
/**
* For use as a non-singleton
*/
module.exports.AcceptLanguage = AcceptLanguage;
{
"name": "accept-language",
"version": "2.0.1",
"version": "2.0.3",
"description": "HTTP Accept-Language parser for node",
"main": "index.js",
"scripts": {
"test": "./bin/test"
"test": "./binaries/test"
},

@@ -9,0 +9,0 @@ "repository": {

@@ -6,3 +6,3 @@ accept-language [![Build Status](https://travis-ci.org/tinganho/node-accept-language.png)](https://travis-ci.org/tinganho/node-accept-language)

`accept-language` parses HTTP Accept-Language header and returns a consumable array of language codes.
`accept-language` parses HTTP Accept-Language header and returns the most likely language tag or a consumable array of language tags.

@@ -17,84 +17,45 @@ ### Installation:

```
```javascript
var acceptLanguage = require('accept-language');
var locales = acceptLanguage.parse('en-GB,en;q=0.8,sv');
accepLanguage.languageTags(['en-US', 'zh-CN']);
console.log(accepLanguage.get('en-GB,en;q=0.8,sv'));
// outputs: 'en-US'
console.log(locales);
```
var language = acceptLanguage.parse('en-GB,en;q=0.8,sv');
console.log(language);
Output:
```
/*
[
{
value: 'en-US',
language: "en",
region: "GB",
region: "US",
quality: 1.0
},
{
language: "sv",
region: undefined,
quality: 1.0
},
{
language: "en",
region: undefined,
quality: 0.8
}
];
*/
```
### Recommended usage with L10ns:
L10ns is internationalization workflow and formatting tool. This library was specifically built for [L10ns](http://l10ns.org).
Filter non-defined language codes:
### API
#### accepLanguage.languageTags(Array languageTags);
Define your language tags in highest priority comes first. The language tags must comply with [BCP47 standard](https://tools.ietf.org/html/bcp47). I.e. all language tags `en`, `en-US` and `zh-Hant-TW` are working.
```javascript
acceptLanguage.languageTags(['en-US', 'zh-CN']);
```
var acceptLanguage = require('accept-language');
acceptLanguage.languages(['en', 'zh']);
var locales = acceptLanguage.parse('en-GB,en;q=0.8,sv');
console.log(locales);
#### accepLanguage.get(String acceptLanguageString);
Get the most likely language tag given an `Accept-Language` string. In order for it to work you must set all your language tags first.
```javascript
acceptLanguage.get('en-GB,en;q=0.8,sv'));
```
Output:
#### accepLanguage.parse(String acceptLanguageString);
Parse an `Accept-Language` string and get a consumable array. In order for it to work you must set all your language tags first.
```javascript
acceptLanguage.get('en-GB,en;q=0.8,sv'));
```
[
{
language: "en",
region: "GB",
quality: 1.0
},
{
language: "en",
region: undefined,
quality: 0.8
}
];
```
Use default value:
```
var acceptLanguage = require('accept-language');
acceptLanguage.default({
language: 'en',
region: 'US'
// No need to specify quality
});
acceptLanguage.languages(['en', 'zh']);
var locales = acceptLanguage.parse('fr-CA');
console.log(locales);
```
Output:
```
[
{
language: "en",
region: "US",
quality: 1.0
}
];
```
The output is always sorted with the highest quality first.
### License
MIT
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