language-tags
Advanced tools
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(); | ||
} | ||
} |
748
lib/Tag.js
@@ -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" | ||
} | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
4
25497
528