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

language-tags

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

language-tags - npm Package Compare versions

Comparing version 2.0.0 to 2.0.1

225

lib/index.js

@@ -9,116 +9,135 @@ /**

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _Tag = _interopRequireDefault(require("./Tag.js"));
var _Subtag = _interopRequireDefault(require("./Subtag.js"));
var _index = _interopRequireDefault(require("language-subtag-registry/data/json/index.json"));
var _registry = _interopRequireDefault(require("language-subtag-registry/data/json/registry.json"));
var _meta = _interopRequireDefault(require("language-subtag-registry/data/json/meta.json"));
var _macrolanguage = _interopRequireDefault(require("language-subtag-registry/data/json/macrolanguage.json"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var tags = function tags(tag) {
return new _Tag.default(tag);
import Tag from './Tag.js';
import Subtag from './Subtag.js';
import index from 'language-subtag-registry/data/json/index.json' with { type: 'json' };
import registry from 'language-subtag-registry/data/json/registry.json' with { type: 'json' };
import meta from 'language-subtag-registry/data/json/meta.json' with { type: 'json' };
import macrolanguages from 'language-subtag-registry/data/json/macrolanguage.json' with { type: 'json' };
const tags = function(tag) {
return new Tag(tag);
};
var _default = exports.default = tags;
tags.check = function (tag) {
return new _Tag.default(tag).valid();
export default tags;
tags.check = function(tag) {
return new Tag(tag).valid();
};
tags.types = function (subtag) {
var types = _index.default[subtag.toLowerCase()];
if (!types) {
return [];
}
return Object.keys(types).filter(function (type) {
return type !== 'grandfathered' && type !== 'redundant';
});
tags.types = function(subtag) {
var types = index[subtag.toLowerCase()];
if (!types) {
return [];
}
return Object.keys(types).filter(function(type) {
return type !== 'grandfathered' && type !== 'redundant';
});
};
tags.subtags = function (subtags) {
var result = [];
if (!Array.isArray(subtags)) {
subtags = [subtags];
}
subtags.forEach(function (subtag) {
tags.types(subtag).forEach(function (type) {
result.push(new _Subtag.default(subtag, type));
});
});
return result;
tags.subtags = function(subtags) {
var result = [];
if (!Array.isArray(subtags)) {
subtags = [subtags];
}
subtags.forEach(function(subtag) {
tags.types(subtag).forEach(function(type) {
result.push(new Subtag(subtag, type));
});
});
return result;
};
tags.filter = function (subtags) {
return subtags.filter(function (subtag) {
return !tags.types(subtag).length;
});
tags.filter = function(subtags) {
return subtags.filter(function(subtag) {
return !tags.types(subtag).length;
});
};
tags.search = function (query, all) {
var test;
if ('function' === typeof query.test) {
test = function test(description) {
return query.test(description);
};
// If the query is all lowercase, make a case-insensitive match.
} else if (query.toLowerCase() === query) {
test = function test(description) {
return -1 !== description.toLowerCase().indexOf(query);
};
} else {
test = function test(description) {
return -1 !== description.indexOf(query);
};
}
return _registry.default.filter(function (record) {
if (!record.Subtag && !all) {
return false;
}
return record.Description.some(test);
tags.search = function(query, all) {
var test;
// Sort by matched description string length.
// This is a quick way to push precise matches towards the top.
}).sort(function (a, b) {
return Math.min.apply(Math, a.Description.filter(test).map(function (description) {
return description.length;
})) - Math.min.apply(Math, b.Description.filter(test).map(function (description) {
return description.length;
}));
}).map(function (record) {
if (record.Subtag) {
return new _Subtag.default(record.Subtag, record.Type);
}
return new _Tag.default(record.Tag);
});
if ('function' === typeof query.test) {
test = function(description) {
return query.test(description);
};
// If the query is all lowercase, make a case-insensitive match.
} else if (query.toLowerCase() === query) {
test = function(description) {
return -1 !== description.toLowerCase().indexOf(query);
};
} else {
test = function(description) {
return -1 !== description.indexOf(query);
};
}
return registry.filter(function(record) {
if (!record.Subtag && !all) {
return false;
}
return record.Description.some(test);
// Sort by matched description string length.
// This is a quick way to push precise matches towards the top.
}).sort(function(a, b) {
return Math.min.apply(Math, a.Description.filter(test).map(function(description) {
return description.length;
})) - Math.min.apply(Math, b.Description.filter(test).map(function(description) {
return description.length;
}));
}).map(function(record) {
if (record.Subtag) {
return new Subtag(record.Subtag, record.Type);
}
return new Tag(record.Tag);
});
};
tags.languages = function (macrolanguage) {
var i,
l,
record,
results = [];
macrolanguage = macrolanguage.toLowerCase();
if (!_macrolanguage.default[macrolanguage]) {
throw new Error('\'' + macrolanguage + '\' is not a macrolanguage.');
}
for (i = 0, l = _registry.default.length; i < l; i++) {
record = _registry.default[i];
if (record.Macrolanguage === macrolanguage) {
results.push(new _Subtag.default(record.Subtag, record.Type));
}
}
return results;
tags.languages = function(macrolanguage) {
var i, l, record, results = [];
macrolanguage = macrolanguage.toLowerCase();
if (!macrolanguages[macrolanguage]) {
throw new Error('\'' + macrolanguage + '\' is not a macrolanguage.');
}
for (i = 0, l = registry.length; i < l; i++) {
record = registry[i];
if (record.Macrolanguage === macrolanguage) {
results.push(new Subtag(record.Subtag, record.Type));
}
}
return results;
};
tags.language = function (subtag) {
return tags.type(subtag, 'language');
tags.language = function(subtag) {
return tags.type(subtag, 'language');
};
tags.region = function (subtag) {
return tags.type(subtag, 'region');
tags.region = function(subtag) {
return tags.type(subtag, 'region');
};
tags.type = function (subtag, type) {
var types = typeof subtag === 'string' && _index.default[subtag.toLowerCase()];
if (types && types[type]) {
return new _Subtag.default(subtag, type);
}
return null;
tags.type = function(subtag, type) {
var types = typeof subtag === 'string' && index[subtag.toLowerCase()];
if (types && types[type]) {
return new Subtag(subtag, type);
}
return null;
};
tags.date = function () {
return _meta.default['File-Date'];
};
tags.date = function() {
return meta['File-Date'];
};

@@ -9,128 +9,114 @@ /**

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = _interopRequireDefault(require("language-subtag-registry/data/json/index.json"));
var _registry = _interopRequireDefault(require("language-subtag-registry/data/json/registry.json"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
var Subtag = exports.default = /*#__PURE__*/function () {
/**
* @param {string} subtag
* @param {string} type
*/
function Subtag(subtag, type) {
_classCallCheck(this, Subtag);
var types, i, record;
import index from 'language-subtag-registry/data/json/index.json' with { type: 'json' };
import registry from 'language-subtag-registry/data/json/registry.json' with { type: 'json' };
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
subtag = subtag.toLowerCase();
type = type.toLowerCase();
function error(code, message) {
var err;
err = new Error(message);
err.code = code;
err.subtag = subtag;
throw err;
}
types = _index.default[subtag];
if (!types) {
error(Subtag.ERR_NONEXISTENT, 'Non-existent subtag \'' + subtag + '\'.');
}
i = types[type];
if (!i && 0 !== i) {
error(Subtag.ERR_NONEXISTENT, 'Non-existent subtag \'' + subtag + '\' of type \'' + type + '\'.');
}
record = _registry.default[i];
if (!record.Subtag) {
error(Subtag.ERR_TAG, '\'' + subtag + '\' is a \'' + type + '\' tag.');
}
this.data = {
subtag: subtag,
record: record,
type: type
};
}
return _createClass(Subtag, [{
key: "type",
value: function type() {
return this.data.type;
}
}, {
key: "descriptions",
value: function descriptions() {
// Every record has one or more descriptions (stored as an array).
return this.data.record.Description;
}
}, {
key: "preferred",
value: function preferred() {
var type,
preferred = this.data.record['Preferred-Value'];
if (preferred) {
type = this.data.type;
if (type === 'extlang') {
type = 'language';
}
return new Subtag(preferred, type);
}
return null;
}
}, {
key: "script",
value: function script() {
var script = this.data.record['Suppress-Script'];
if (script) {
return new Subtag(script, 'script');
}
return null;
}
}, {
key: "scope",
value: function scope() {
return this.data.record.Scope || null;
}
}, {
key: "deprecated",
value: function deprecated() {
return this.data.record.Deprecated || null;
}
}, {
key: "added",
value: function added() {
return this.data.record.Added;
}
}, {
key: "comments",
value: function comments() {
// Comments don't always occur for records, so switch to an empty array if missing.
return this.data.record.Comments || [];
}
}, {
key: "format",
value: function format() {
var subtag = this.data.subtag;
switch (this.data.type) {
case 'region':
return subtag.toUpperCase();
case 'script':
return subtag[0].toUpperCase() + subtag.slice(1);
}
return subtag;
}
}, {
key: "toString",
value: function toString() {
return this.format();
}
}]);
}();
_defineProperty(Subtag, "ERR_NONEXISTENT", 1);
_defineProperty(Subtag, "ERR_TAG", 2);
export default class Subtag {
static ERR_NONEXISTENT = 1;
static ERR_TAG = 2;
/**
* @param {string} subtag
* @param {string} type
*/
constructor (subtag, type) {
var types, i, record;
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
subtag = subtag.toLowerCase();
type = type.toLowerCase();
function error (code, message) {
var err;
err = new Error(message);
err.code = code;
err.subtag = subtag;
throw err;
}
types = index[subtag];
if (!types) {
error(Subtag.ERR_NONEXISTENT, 'Non-existent subtag \'' + subtag + '\'.');
}
i = types[type];
if (!i && 0 !== i) {
error(Subtag.ERR_NONEXISTENT, 'Non-existent subtag \'' + subtag + '\' of type \'' + type + '\'.');
}
record = registry[i];
if (!record.Subtag) {
error(Subtag.ERR_TAG, '\'' + subtag + '\' is a \'' + type + '\' tag.');
}
this.data = { subtag, record, type };
}
type () {
return this.data.type;
}
descriptions () {
// Every record has one or more descriptions (stored as an array).
return this.data.record.Description;
}
preferred () {
var type, preferred = this.data.record['Preferred-Value'];
if (preferred) {
type = this.data.type;
if (type === 'extlang') {
type = 'language';
}
return new Subtag(preferred, type);
}
return null;
}
script () {
var script = this.data.record['Suppress-Script'];
if (script) {
return new Subtag(script, 'script');
}
return null;
}
scope () {
return this.data.record.Scope || null;
}
deprecated () {
return this.data.record.Deprecated || null;
}
added () {
return this.data.record.Added;
}
comments () {
// Comments don't always occur for records, so switch to an empty array if missing.
return this.data.record.Comments || [];
}
format () {
var subtag = this.data.subtag;
switch (this.data.type) {
case 'region':
return subtag.toUpperCase();
case 'script':
return subtag[0].toUpperCase() + subtag.slice(1);
}
return subtag;
}
toString () {
return this.format();
}
}

@@ -9,394 +9,398 @@ /**

Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _index = _interopRequireDefault(require("language-subtag-registry/data/json/index.json"));
var _registry = _interopRequireDefault(require("language-subtag-registry/data/json/registry.json"));
var _Subtag = _interopRequireDefault(require("./Subtag.js"));
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
var Tag = exports.default = /*#__PURE__*/function () {
/** @param {string} tag */
function Tag(tag) {
_classCallCheck(this, Tag);
var types;
import index from 'language-subtag-registry/data/json/index.json' with { type: 'json' };
import registry from 'language-subtag-registry/data/json/registry.json' with { type: 'json' };
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
tag = tag.trim().toLowerCase();
this.data = {
tag: tag
};
import Subtag from './Subtag.js';
// Check if the input tag is grandfathered or redundant.
types = _index.default[tag];
if (types && (types.grandfathered || types.redundant)) {
this.data.record = _registry.default[types.grandfathered || types.redundant];
}
}
return _createClass(Tag, [{
key: "preferred",
value: function preferred() {
var preferred = this.data.record['Preferred-Value'];
if (preferred) {
return new Tag(preferred);
}
return null;
}
export default class Tag {
static ERR_DEPRECATED = 1;
static ERR_NO_LANGUAGE = 2;
static ERR_UNKNOWN = 3;
static ERR_TOO_LONG = 4;
static ERR_EXTRA_REGION = 5;
static ERR_EXTRA_EXTLANG = 6;
static ERR_EXTRA_SCRIPT = 7;
static ERR_DUPLICATE_VARIANT = 8;
static ERR_WRONG_ORDER = 9;
static ERR_SUPPRESS_SCRIPT = 10;
static ERR_SUBTAG_DEPRECATED = 11;
static ERR_EXTRA_LANGUAGE = 12;
/** @return {Subtag[]} */
}, {
key: "subtags",
value: function subtags() {
var codes,
data = this.data,
subtags = [];
/** @param {string} tag */
constructor (tag) {
var types;
// No subtags if the tag is grandfathered.
if (data.record && this.type() === 'grandfathered') {
return subtags;
}
codes = data.tag.split('-');
if (!codes.length) {
return subtags;
}
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
tag = tag.trim().toLowerCase();
// Try and find the language tag.
codes.some(function (code, i) {
var types;
this.data = { tag };
// Singletons and anything after are unhandled.
if (code.length < 2) {
return true; // Stop the loop (stop processing after a singleton).
}
types = _index.default[code];
// Check if the input tag is grandfathered or redundant.
types = index[tag];
if (types && (types.grandfathered || types.redundant)) {
this.data.record = registry[types.grandfathered || types.redundant];
}
}
// Check for non-existent tag.
if (!types) {
return; // Skip to the next item.
}
preferred () {
var preferred = this.data.record['Preferred-Value'];
// Check against undefined because value could be 0.
// Language subtags may only appear at the beginning of the tag, otherwise the subtag type is indeterminate.
if (0 === i && undefined !== types.language) {
subtags.push(new _Subtag.default(code, 'language'));
return;
}
switch (code.length) {
case 2:
// Should be a region.
if (types.region) {
subtags.push(new _Subtag.default(code, 'region'));
if (preferred) {
return new Tag(preferred);
}
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new _Subtag.default(code, 'language'));
}
break;
case 3:
// Could be a numeric region code e.g. '001' for 'World'.
if (types.region) {
subtags.push(new _Subtag.default(code, 'region'));
} else if (types.extlang) {
subtags.push(new _Subtag.default(code, 'extlang'));
return null;
}
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new _Subtag.default(code, 'language'));
}
break;
case 4:
// Could be a numeric variant.
if (types.variant) {
subtags.push(new _Subtag.default(code, 'variant'));
} else if (types.script) {
subtags.push(new _Subtag.default(code, 'script'));
}
break;
default:
// Should be a variant.
if (types.variant) {
subtags.push(new _Subtag.default(code, 'variant'));
}
break;
}
});
return subtags;
}
}, {
key: "language",
value: function language() {
return this.find('language');
}
}, {
key: "region",
value: function region() {
return this.find('region');
}
}, {
key: "script",
value: function script() {
return this.find('script');
}
/** @return {Subtag[]} */
subtags () {
var codes, data = this.data, subtags = [];
/** @param {string} type */
}, {
key: "find",
value: function find(type) {
var i,
l,
subtag,
subtags = this.subtags();
for (i = 0, l = subtags.length; i < l; i++) {
subtag = subtags[i];
if (subtag.type() === type) {
return subtag;
}
}
}
}, {
key: "valid",
value: function valid() {
return this.errors().length < 1;
}
}, {
key: "errors",
value: function errors() {
var error,
subtags,
data = this.data,
errors = [];
error = function error(code, subtag) {
var err, message;
switch (code) {
case Tag.ERR_DEPRECATED:
message = 'The tag \'' + data.tag + '\' is deprecated.';
// No subtags if the tag is grandfathered.
if (data.record && this.type() === 'grandfathered') {
return subtags;
}
// Note that a record that contains a 'Deprecated' field and no corresponding 'Preferred-Value' field has no replacement mapping (RFC 5646 section 3.1.6).
if (data.record['Preferred-Value']) {
message += ' Use \'' + data.record['Preferred-Value'] + '\' instead.';
}
break;
case Tag.ERR_SUBTAG_DEPRECATED:
message = 'The subtag \'' + subtag + '\' is deprecated.';
break;
case Tag.ERR_NO_LANGUAGE:
if (!data.tag) {
message = 'Empty tag.';
} else {
message = 'Missing language tag in \'' + data.tag + '\'.';
}
break;
case Tag.ERR_UNKNOWN:
message = 'Unknown code \'' + subtag + '\'';
break;
case Tag.ERR_TOO_LONG:
message = 'The private-use subtag \'' + subtag + '\' is too long.';
break;
case Tag.ERR_EXTRA_LANGUAGE:
case Tag.ERR_EXTRA_EXTLANG:
case Tag.ERR_EXTRA_REGION:
case Tag.ERR_EXTRA_SCRIPT:
message = 'Extra ' + subtag.type() + ' subtag \'' + subtag + '\' found.';
break;
case Tag.ERR_DUPLICATE_VARIANT:
message = 'Duplicate variant subtag \'' + subtag + '\' found.';
break;
case Tag.ERR_WRONG_ORDER:
message = 'The subtag \'' + subtag[0] + '\' should not appear before \'' + subtag[1] + '\'.';
break;
case Tag.ERR_SUPPRESS_SCRIPT:
message = 'The script subtag \'' + subtag + '\' is the same as the language suppress-script.';
break;
}
err = new Error(message);
err.code = code;
err.tag = data.tag;
err.subtag = subtag;
errors.push(err);
};
codes = data.tag.split('-');
if (!codes.length) {
return subtags;
}
// Check if the tag is grandfathered and if the grandfathered tag is deprecated (e.g. no-nyn).
if (data.record) {
if (data.record.Deprecated) {
error(Tag.ERR_DEPRECATED);
}
// Try and find the language tag.
codes.some(function (code, i) {
var types;
// Only check every subtag if the tag is not explicitly listed as grandfathered or redundant.
return errors;
}
// Singletons and anything after are unhandled.
if (code.length < 2) {
return true; // Stop the loop (stop processing after a singleton).
}
// Check that all subtag codes are meaningful.
data.tag.split('-').some(function (code, i, codes) {
var types;
types = index[code];
// Ignore anything after a singleton.
if (code.length < 2) {
// Check that each private-use subtag is within the maximum allowed length.
codes.slice(i).forEach(function (code) {
if (code.length > 8) {
error(Tag.ERR_TOO_LONG, code);
}
});
return true;
}
types = _index.default[code];
if (!types) {
error(Tag.ERR_UNKNOWN, code);
}
return false; // Continue to the next item.
});
// Check for non-existent tag.
if (!types) {
return; // Skip to the next item.
}
// Check that first tag is a language tag.
subtags = this.subtags();
if (!subtags.length || 'language' !== subtags[0].type()) {
error(Tag.ERR_NO_LANGUAGE);
return errors;
}
// Check against undefined because value could be 0.
// Language subtags may only appear at the beginning of the tag, otherwise the subtag type is indeterminate.
if (0 === i && undefined !== types.language) {
subtags.push(new Subtag(code, 'language'));
return;
}
// Check for more than one of some types and for deprecation.
subtags.forEach(function (subtag, i) {
var type = subtag.type(),
language,
script,
found = this;
if (subtag.deprecated()) {
error(Tag.ERR_SUBTAG_DEPRECATED, subtag);
}
if (found[type]) {
found[type].push(subtag);
}
switch (type) {
case 'language':
if (found.language.length > 1) {
error(Tag.ERR_EXTRA_LANGUAGE, subtag);
}
break;
case 'region':
if (found.region.length > 1) {
error(Tag.ERR_EXTRA_REGION, subtag);
}
break;
case 'extlang':
if (found.extlang.length > 1) {
error(Tag.ERR_EXTRA_EXTLANG, subtag);
}
break;
case 'script':
if (found.script.length > 1) {
error(Tag.ERR_EXTRA_SCRIPT, subtag);
switch (code.length) {
case 2:
// Check if script is same as language suppress-script.
} else {
language = subtags[0];
if ('language' === language.type()) {
script = language.script();
if (script && script.format() === subtag.format()) {
error(Tag.ERR_SUPPRESS_SCRIPT, subtag);
}
}
}
break;
case 'variant':
if (found.variant.length > 1 && found.variant.some(function (variant) {
return variant.format() === subtag.format();
})) {
error(Tag.ERR_DUPLICATE_VARIANT, subtag);
}
}
}, {
language: [],
extlang: [],
variant: [],
script: [],
region: []
});
// Should be a region.
if (types.region) {
subtags.push(new Subtag(code, 'region'));
// Check for correct order.
subtags.forEach(function (subtag, i, subtags) {
var priority = this,
next = subtags[i + 1];
if (next && priority[subtag.type()] > priority[next.type()]) {
error(Tag.ERR_WRONG_ORDER, [subtag, next]);
}
}, {
language: 4,
extlang: 5,
script: 6,
region: 7,
variant: 8
});
return errors;
}
}, {
key: "type",
value: function type() {
var record = this.data.record;
if (record) {
return record.Type;
}
return 'tag';
}
}, {
key: "added",
value: function added() {
var record = this.data.record;
return record && record.Added;
}
}, {
key: "deprecated",
value: function deprecated() {
var record = this.data.record;
return record && record.Deprecated;
}
}, {
key: "descriptions",
value: function descriptions() {
var record = this.data.record;
if (record && record.Description) {
return record.Description;
}
return [];
}
}, {
key: "format",
value: function format() {
var tag = this.data.tag;
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new Subtag(code, 'language'));
}
// Format according to algorithm defined in RFC 5646 section 2.1.1.
return tag.split('-').reduce(function (p, c, i, a) {
if (i === 0) {
return c;
}
if (a[i - 1].length === 1) {
return p + '-' + c;
}
switch (c.length) {
case 2:
return p + '-' + c.toUpperCase();
case 4:
return p + '-' + c[0].toUpperCase() + c.substr(1);
}
return p + '-' + c;
});
}
}]);
}();
_defineProperty(Tag, "ERR_DEPRECATED", 1);
_defineProperty(Tag, "ERR_NO_LANGUAGE", 2);
_defineProperty(Tag, "ERR_UNKNOWN", 3);
_defineProperty(Tag, "ERR_TOO_LONG", 4);
_defineProperty(Tag, "ERR_EXTRA_REGION", 5);
_defineProperty(Tag, "ERR_EXTRA_EXTLANG", 6);
_defineProperty(Tag, "ERR_EXTRA_SCRIPT", 7);
_defineProperty(Tag, "ERR_DUPLICATE_VARIANT", 8);
_defineProperty(Tag, "ERR_WRONG_ORDER", 9);
_defineProperty(Tag, "ERR_SUPPRESS_SCRIPT", 10);
_defineProperty(Tag, "ERR_SUBTAG_DEPRECATED", 11);
_defineProperty(Tag, "ERR_EXTRA_LANGUAGE", 12);
break;
case 3:
// Could be a numeric region code e.g. '001' for 'World'.
if (types.region) {
subtags.push(new Subtag(code, 'region'));
} else if (types.extlang) {
subtags.push(new Subtag(code, 'extlang'));
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new Subtag(code, 'language'));
}
break;
case 4:
// Could be a numeric variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
} else if (types.script) {
subtags.push(new Subtag(code, 'script'));
}
break;
default:
// Should be a variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
}
break;
}
});
return subtags;
}
language () {
return this.find('language');
}
region () {
return this.find('region');
}
script () {
return this.find('script');
}
/** @param {string} type */
find (type) {
var i, l, subtag, subtags = this.subtags();
for (i = 0, l = subtags.length; i < l; i++) {
subtag = subtags[i];
if (subtag.type() === type) {
return subtag;
}
}
}
valid () {
return this.errors().length < 1;
}
errors () {
var error, subtags, data = this.data, errors = [];
error = function (code, subtag) {
var err, message;
switch (code) {
case Tag.ERR_DEPRECATED:
message = 'The tag \'' + data.tag + '\' is deprecated.';
// Note that a record that contains a 'Deprecated' field and no corresponding 'Preferred-Value' field has no replacement mapping (RFC 5646 section 3.1.6).
if (data.record['Preferred-Value']) {
message += ' Use \'' + data.record['Preferred-Value'] + '\' instead.';
}
break;
case Tag.ERR_SUBTAG_DEPRECATED:
message = 'The subtag \'' + subtag + '\' is deprecated.';
break;
case Tag.ERR_NO_LANGUAGE:
if (!data.tag) {
message = 'Empty tag.';
} else {
message = 'Missing language tag in \'' + data.tag + '\'.';
}
break;
case Tag.ERR_UNKNOWN:
message = 'Unknown code \'' + subtag + '\'';
break;
case Tag.ERR_TOO_LONG:
message = 'The private-use subtag \'' + subtag + '\' is too long.';
break;
case Tag.ERR_EXTRA_LANGUAGE:
case Tag.ERR_EXTRA_EXTLANG:
case Tag.ERR_EXTRA_REGION:
case Tag.ERR_EXTRA_SCRIPT:
message = 'Extra ' + subtag.type() + ' subtag \'' + subtag + '\' found.';
break;
case Tag.ERR_DUPLICATE_VARIANT:
message = 'Duplicate variant subtag \'' + subtag + '\' found.';
break;
case Tag.ERR_WRONG_ORDER:
message = 'The subtag \'' + subtag[0] + '\' should not appear before \'' + subtag[1] + '\'.';
break;
case Tag.ERR_SUPPRESS_SCRIPT:
message = 'The script subtag \'' + subtag + '\' is the same as the language suppress-script.';
break;
}
err = new Error(message);
err.code = code;
err.tag = data.tag;
err.subtag = subtag;
errors.push(err);
};
// Check if the tag is grandfathered and if the grandfathered tag is deprecated (e.g. no-nyn).
if (data.record) {
if (data.record.Deprecated) {
error(Tag.ERR_DEPRECATED);
}
// Only check every subtag if the tag is not explicitly listed as grandfathered or redundant.
return errors;
}
// Check that all subtag codes are meaningful.
data.tag.split('-').some(function (code, i, codes) {
var types;
// Ignore anything after a singleton.
if (code.length < 2) {
// Check that each private-use subtag is within the maximum allowed length.
codes.slice(i).forEach(function (code) {
if (code.length > 8) {
error(Tag.ERR_TOO_LONG, code);
}
});
return true;
}
types = index[code];
if (!types) {
error(Tag.ERR_UNKNOWN, code);
}
return false; // Continue to the next item.
});
// Check that first tag is a language tag.
subtags = this.subtags();
if (!subtags.length || 'language' !== subtags[0].type()) {
error(Tag.ERR_NO_LANGUAGE);
return errors;
}
// Check for more than one of some types and for deprecation.
subtags.forEach(function (subtag, i) {
var type = subtag.type(), language, script, found = this;
if (subtag.deprecated()) {
error(Tag.ERR_SUBTAG_DEPRECATED, subtag);
}
if (found[type]) {
found[type].push(subtag);
}
switch (type) {
case 'language':
if (found.language.length > 1) {
error(Tag.ERR_EXTRA_LANGUAGE, subtag);
}
break;
case 'region':
if (found.region.length > 1) {
error(Tag.ERR_EXTRA_REGION, subtag);
}
break;
case 'extlang':
if (found.extlang.length > 1) {
error(Tag.ERR_EXTRA_EXTLANG, subtag);
}
break;
case 'script':
if (found.script.length > 1) {
error(Tag.ERR_EXTRA_SCRIPT, subtag);
// Check if script is same as language suppress-script.
} else {
language = subtags[0];
if ('language' === language.type()) {
script = language.script();
if (script && script.format() === subtag.format()) {
error(Tag.ERR_SUPPRESS_SCRIPT, subtag);
}
}
}
break;
case 'variant':
if (found.variant.length > 1 && found.variant.some(function (variant) {
return variant.format() === subtag.format();
})) {
error(Tag.ERR_DUPLICATE_VARIANT, subtag);
}
}
}, {
language: [],
extlang: [],
variant: [],
script: [],
region: []
});
// Check for correct order.
subtags.forEach(function (subtag, i, subtags) {
var priority = this, next = subtags[i + 1];
if (next && priority[subtag.type()] > priority[next.type()]) {
error(Tag.ERR_WRONG_ORDER, [subtag, next]);
}
}, {
language: 4,
extlang: 5,
script: 6,
region: 7,
variant: 8
});
return errors;
}
type () {
var record = this.data.record;
if (record) {
return record.Type;
}
return 'tag';
}
added () {
var record = this.data.record;
return record && record.Added;
}
deprecated () {
var record = this.data.record;
return record && record.Deprecated;
}
descriptions () {
var record = this.data.record;
if (record && record.Description) {
return record.Description;
}
return [];
}
format () {
var tag = this.data.tag;
// Format according to algorithm defined in RFC 5646 section 2.1.1.
return tag.split('-').reduce(function (p, c, i, a) {
if (i === 0) {
return c;
}
if (a[i - 1].length === 1) {
return p + '-' + c;
}
switch (c.length) {
case 2:
return p + '-' + c.toUpperCase();
case 4:
return p + '-' + c[0].toUpperCase() + c.substr(1);
}
return p + '-' + c;
});
}
}
{
"name": "language-tags",
"version": "2.0.0",
"version": "2.0.1",
"description": "Work with IANA language tags.",

@@ -18,9 +18,4 @@ "main": "lib/index.js",

"scripts": {
"prepublishOnly": "npm run build",
"prebuild": "rimraf lib && mkdirp lib",
"build": "babel src -d lib",
"lint": "eslint ./src ./test",
"tests-only": "c8 mocha",
"pretest": "npm run lint",
"test": "npm run tests-only"
"lint": "eslint ./lib ./test",
"test": "c8 mocha"
},

@@ -39,10 +34,6 @@ "keywords": [

"devDependencies": {
"@babel/cli": "^7.22.10",
"@babel/preset-env": "^7.22.10",
"coveralls": "^3.1.1",
"eslint": "^9.17.0",
"mkdirp": "^3.0.1",
"mocha": "^11.0.1",
"c8": "^10.1.3",
"rimraf": "^6.0.1"
"c8": "^10.1.3"
},

@@ -53,4 +44,4 @@ "files": [

"engines": {
"node": ">=0.10"
"node": ">=22"
}
}
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