🚀 Big News:Socket Has Acquired Secure Annex.Learn More
Socket
Book a DemoSign in
Socket

language-tags

Package Overview
Dependencies
Maintainers
1
Versions
17
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
1.0.5
to
1.0.6
+6
-6
lib/index.js

@@ -11,7 +11,7 @@ /**

var Tag = require('./Tag');
var Subtag = require('./Subtag');
var Tag = require('./Tag.js');
var Subtag = require('./Subtag.js');
var index = require('language-subtag-registry/data/json/index');
var registry = require('language-subtag-registry/data/json/registry');
var index = require('language-subtag-registry/data/json/index.json');
var registry = require('language-subtag-registry/data/json/registry.json');

@@ -109,3 +109,3 @@ var tags = function(tag) {

macrolanguage = macrolanguage.toLowerCase();
if (!require('language-subtag-registry/data/json/macrolanguage')[macrolanguage]) {
if (!require('language-subtag-registry/data/json/macrolanguage.json')[macrolanguage]) {
throw new Error('\'' + macrolanguage + '\' is not a macrolanguage.');

@@ -143,3 +143,3 @@ }

tags.date = function() {
return require('language-subtag-registry/data/json/meta')['File-Date'];
return require('language-subtag-registry/data/json/meta.json')['File-Date'];
};

@@ -11,115 +11,116 @@ /**

var index = require('language-subtag-registry/data/json/index');
var registry = require('language-subtag-registry/data/json/registry');
var index = require('language-subtag-registry/data/json/index.json');
var registry = require('language-subtag-registry/data/json/registry.json');
module.exports = Subtag;
class Subtag {
static ERR_NONEXISTENT = 1;
static ERR_TAG = 2;
Subtag.ERR_NONEXISTENT = 1;
Subtag.ERR_TAG = 2;
/**
* @param {string} subtag
* @param {string} type
*/
constructor (subtag, type) {
var types, i, record;
function Subtag(subtag, type) {
var types, i, record, error;
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
subtag = subtag.toLowerCase();
type = type.toLowerCase();
// 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;
error = function(code, message) {
var err;
err = new Error(message);
err.code = code;
err.subtag = subtag;
throw 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 + '\'.');
}
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 };
}
i = types[type];
if (!i && 0 !== i) {
error(Subtag.ERR_NONEXISTENT, 'Non-existent subtag \'' + subtag + '\' of type \'' + type + '\'.');
type () {
return this.data.type;
}
record = registry[i];
if (!record.Subtag) {
error(Subtag.ERR_TAG, '\'' + subtag + '\' is a \'' + type + '\' tag.');
descriptions () {
// Every record has one or more descriptions (stored as an array).
return this.data.record.Description;
}
this.data = {};
this.data.subtag = subtag;
this.data.record = record;
this.data.type = type;
}
preferred () {
var type, preferred = this.data.record['Preferred-Value'];
Subtag.prototype.type = function() {
return this.data.type;
};
if (preferred) {
type = this.data.type;
if (type === 'extlang') {
type = 'language';
}
Subtag.prototype.descriptions = function() {
return new Subtag(preferred, type);
}
// Every record has one or more descriptions (stored as an array).
return this.data.record.Description;
};
return null;
}
Subtag.prototype.preferred = function() {
var type, preferred = this.data.record['Preferred-Value'];
script () {
var script = this.data.record['Suppress-Script'];
if (preferred) {
type = this.data.type;
if (type === 'extlang') {
type = 'language';
if (script) {
return new Subtag(script, 'script');
}
return new Subtag(preferred, type);
return null;
}
return null;
};
scope () {
return this.data.record.Scope || null;
}
Subtag.prototype.script = function() {
var script = this.data.record['Suppress-Script'];
deprecated () {
return this.data.record.Deprecated || null;
}
if (script) {
return new Subtag(script, 'script');
added () {
return this.data.record.Added;
}
return null;
};
comments () {
// Comments don't always occur for records, so switch to an empty array if missing.
return this.data.record.Comments || [];
}
Subtag.prototype.scope = function() {
return this.data.record.Scope || null;
};
format () {
var subtag = this.data.subtag;
Subtag.prototype.deprecated = function() {
return this.data.record.Deprecated || null;
};
switch (this.data.type) {
case 'region':
return subtag.toUpperCase();
case 'script':
return subtag[0].toUpperCase() + subtag.slice(1);
}
Subtag.prototype.added = function() {
return this.data.record.Added;
};
return subtag;
}
Subtag.prototype.comments = function() {
// Comments don't always occur for records, so switch to an empty array if missing.
return this.data.record.Comments || [];
};
Subtag.prototype.format = function() {
var subtag = this.data.subtag;
switch (this.data.type) {
case 'region':
return subtag.toUpperCase();
case 'script':
return subtag[0].toUpperCase() + subtag.substr(1);
toString () {
return this.format();
}
}
return subtag;
};
Subtag.prototype.toString = function() {
return this.format();
};
module.exports = Subtag;
+309
-305

@@ -11,396 +11,400 @@ /**

var index = require('language-subtag-registry/data/json/index');
var registry = require('language-subtag-registry/data/json/registry');
var index = require('language-subtag-registry/data/json/index.json');
var registry = require('language-subtag-registry/data/json/registry.json');
var Subtag = require('./Subtag');
var Subtag = require('./Subtag.js');
module.exports = Tag;
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;
Tag.ERR_DEPRECATED = 1;
Tag.ERR_NO_LANGUAGE = 2;
Tag.ERR_UNKNOWN = 3;
Tag.ERR_TOO_LONG = 4;
Tag.ERR_EXTRA_REGION = 5;
Tag.ERR_EXTRA_EXTLANG = 6;
Tag.ERR_EXTRA_SCRIPT = 7;
Tag.ERR_DUPLICATE_VARIANT = 8;
Tag.ERR_WRONG_ORDER = 9;
Tag.ERR_SUPPRESS_SCRIPT = 10;
Tag.ERR_SUBTAG_DEPRECATED = 11;
Tag.ERR_EXTRA_LANGUAGE = 12;
/** @param {string} tag */
constructor (tag) {
var types;
function Tag(tag) {
var types;
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
tag = tag.trim().toLowerCase();
// Lowercase for consistency (case is only a formatting convention, not a standard requirement).
tag = tag.trim().toLowerCase();
this.data = { tag };
this.data = {};
this.data.tag = tag;
// 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 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];
}
}
}
Tag.prototype.preferred = function() {
var preferred = this.data.record['Preferred-Value'];
preferred () {
var preferred = this.data.record['Preferred-Value'];
if (preferred) {
return new Tag(preferred);
}
if (preferred) {
return new Tag(preferred);
}
return null;
};
Tag.prototype.subtags = function() {
var codes, data = this.data, subtags = [];
// No subtags if the tag is grandfathered.
if (data.record && this.type() === 'grandfathered') {
return subtags;
return null;
}
codes = data.tag.split('-');
if (!codes.length) {
return subtags;
}
/** @return {Subtag[]} */
subtags () {
var codes, data = this.data, subtags = [];
// Try and find the language tag.
codes.some(function(code, i) {
var types;
// Singletons and anything after are unhandled.
if (code.length < 2) {
return true; // Stop the loop (stop processing after a singleton).
// No subtags if the tag is grandfathered.
if (data.record && this.type() === 'grandfathered') {
return subtags;
}
types = index[code];
// Check for non-existent tag.
if (!types) {
return; // Skip to the next item.
codes = data.tag.split('-');
if (!codes.length) {
return subtags;
}
// 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;
}
// Try and find the language tag.
codes.some(function (code, i) {
var types;
switch (code.length) {
case 2:
// Singletons and anything after are unhandled.
if (code.length < 2) {
return true; // Stop the loop (stop processing after a singleton).
}
// Should be a region.
if (types.region) {
subtags.push(new Subtag(code, 'region'));
types = index[code];
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new Subtag(code, 'language'));
// Check for non-existent tag.
if (!types) {
return; // Skip to the next item.
}
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) {
// 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;
}
break;
case 4:
switch (code.length) {
case 2:
// Could be a numeric variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
} else if (types.script) {
subtags.push(new Subtag(code, 'script'));
}
// Should be a region.
if (types.region) {
subtags.push(new Subtag(code, 'region'));
break;
default:
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new Subtag(code, 'language'));
}
// Should be a variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
}
break;
case 3:
break;
}
});
// 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'));
return subtags;
};
// Error case: language subtag in the wrong place.
} else if (types.language) {
subtags.push(new Subtag(code, 'language'));
}
Tag.prototype.language = function() {
return this.find('language');
};
break;
case 4:
Tag.prototype.region = function() {
return this.find('region');
};
// Could be a numeric variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
} else if (types.script) {
subtags.push(new Subtag(code, 'script'));
}
Tag.prototype.script = function() {
return this.find('script');
};
break;
default:
Tag.prototype.find = function(type) {
var i, l, subtag, subtags = this.subtags();
// Should be a variant.
if (types.variant) {
subtags.push(new Subtag(code, 'variant'));
}
for (i = 0, l = subtags.length; i < l; i++) {
subtag = subtags[i];
break;
}
});
if (subtag.type() === type) {
return subtag;
}
return subtags;
}
};
Tag.prototype.valid = function() {
return this.errors().length < 1;
};
language () {
return this.find('language');
}
Tag.prototype.errors = function() {
var error, subtags, data = this.data, errors = [];
region () {
return this.find('region');
}
error = function(code, subtag) {
var err, message;
script () {
return this.find('script');
}
switch (code) {
case Tag.ERR_DEPRECATED:
message = 'The tag \'' + data.tag + '\' is deprecated.';
/** @param {string} type */
find (type) {
var i, l, subtag, subtags = this.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.';
}
for (i = 0, l = subtags.length; i < l; i++) {
subtag = subtags[i];
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 + '\'.';
if (subtag.type() === type) {
return subtag;
}
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);
};
valid () {
return this.errors().length < 1;
}
// 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);
}
errors () {
var error, subtags, data = this.data, errors = [];
// Only check every subtag if the tag is not explicitly listed as grandfathered or redundant.
return errors;
}
error = function (code, subtag) {
var err, message;
// Check that all subtag codes are meaningful.
data.tag.split('-').some(function(code, i, codes) {
var types;
switch (code) {
case Tag.ERR_DEPRECATED:
message = 'The tag \'' + data.tag + '\' is deprecated.';
// Ignore anything after a singleton.
if (code.length < 2) {
// 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.';
}
// 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);
}
});
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 + '\'.';
}
return true;
}
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;
}
types = index[code];
if (!types) {
error(Tag.ERR_UNKNOWN, code);
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;
}
return false; // Continue to the next item.
});
// Check that all subtag codes are meaningful.
data.tag.split('-').some(function (code, i, codes) {
var types;
// 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;
}
// Ignore anything after a singleton.
if (code.length < 2) {
// Check for more than one of some types and for deprecation.
subtags.forEach(function(subtag, i) {
var type = subtag.type(), language, script, found = this;
// 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);
}
});
if (subtag.deprecated()) {
error(Tag.ERR_SUBTAG_DEPRECATED, subtag);
}
return true;
}
if (found[type]) {
found[type].push(subtag);
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;
}
switch (type) {
case 'language':
if (found.language.length > 1) {
error(Tag.ERR_EXTRA_LANGUAGE, subtag);
}
// Check for more than one of some types and for deprecation.
subtags.forEach(function (subtag, i) {
var type = subtag.type(), language, script, found = this;
break;
case 'region':
if (found.region.length > 1) {
error(Tag.ERR_EXTRA_REGION, subtag);
if (subtag.deprecated()) {
error(Tag.ERR_SUBTAG_DEPRECATED, subtag);
}
break;
case 'extlang':
if (found.extlang.length > 1) {
error(Tag.ERR_EXTRA_EXTLANG, subtag);
if (found[type]) {
found[type].push(subtag);
}
break;
case 'script':
if (found.script.length > 1) {
error(Tag.ERR_EXTRA_SCRIPT, subtag);
switch (type) {
case 'language':
if (found.language.length > 1) {
error(Tag.ERR_EXTRA_LANGUAGE, 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 '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: []
});
break;
case 'variant':
if (found.variant.length > 1 && found.variant.some(function(variant) {
return variant.format() === subtag.format();
})) {
error(Tag.ERR_DUPLICATE_VARIANT, subtag);
// 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: [],
extlang: [],
variant: [],
script: [],
region: []
});
}, {
language: 4,
extlang: 5,
script: 6,
region: 7,
variant: 8
});
// Check for correct order.
subtags.forEach(function(subtag, i, subtags) {
var priority = this, next = subtags[i + 1];
return errors;
}
if (next && priority[subtag.type()] > priority[next.type()]) {
error(Tag.ERR_WRONG_ORDER, [subtag, next]);
type () {
var record = this.data.record;
if (record) {
return record.Type;
}
}, {
language: 4,
extlang: 5,
script: 6,
region: 7,
variant: 8
});
return errors;
};
return 'tag';
}
Tag.prototype.type = function() {
var record = this.data.record;
added () {
var record = this.data.record;
if (record) {
return record.Type;
return record && record.Added;
}
return 'tag';
};
deprecated () {
var record = this.data.record;
Tag.prototype.added = function() {
var record = this.data.record;
return record && record.Deprecated;
}
return record && record.Added;
};
descriptions () {
var record = this.data.record;
Tag.prototype.deprecated = function() {
var record = this.data.record;
if (record && record.Description) {
return record.Description;
}
return record && record.Deprecated;
};
return [];
}
Tag.prototype.descriptions = function() {
var record = this.data.record;
format () {
var tag = this.data.tag;
if (record && record.Description) {
return record.Description;
}
// 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;
}
return [];
};
if (a[i - 1].length === 1) {
return p + '-' + c;
}
Tag.prototype.format = function() {
var tag = this.data.tag;
switch (c.length) {
case 2:
return p + '-' + c.toUpperCase();
case 4:
return p + '-' + c[0].toUpperCase() + c.substr(1);
}
// 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;
});
};
module.exports = Tag;
{
"name": "language-tags",
"version": "1.0.5",
"implements": ["CommonJS/Modules/1.0"],
"version": "1.0.6",
"implements": [
"CommonJS/Modules/1.0"
],
"description": "Work with IANA language tags.",
"main": "lib/index.js",
"homepage": "https://github.com/mattcg/language-tags",
"author": "Matthew Caruana Galizia <m@m.cg>",
"author": "Matthew Caruana Galizia <mattcg@gmail.com>",
"repository": {

@@ -29,9 +31,12 @@ "type": "git",

"dependencies": {
"language-subtag-registry": "~0.3.2"
"language-subtag-registry": "^0.3.20"
},
"devDependencies": {
"mocha": "~2.3.4",
"mocha": "~6.2.0",
"istanbul": "~0.4.2",
"coveralls": "~2.11.6"
}
"coveralls": "~3.0.5"
},
"files": [
"/lib"
]
}

@@ -6,3 +6,3 @@ # IANA Language Tags for JavaScript #

Based on [BCP 47](http://tools.ietf.org/html/bcp47) ([RFC 5646](http://tools.ietf.org/html/rfc5646)) and the latest [IANA language subtag registry](http://www.iana.org/assignments/language-subtag-registry).
Based on [BCP 47](https://www.rfc-editor.org/info/bcp47) ([RFC 5646](https://www.rfc-editor.org/rfc/rfc5646.html)) and the latest [IANA language subtag registry](http://www.iana.org/assignments/language-subtag-registry).

@@ -9,0 +9,0 @@ This project will be updated as the standards change.

Sorry, the diff of this file is not supported yet

language: node_js
node_js:
- "4.1"
- "4.0"
- "0.12"
- "0.10"
branches:
only:
- master
script:
- "make test"
- "make test-coveralls"
test: lib test/lib node_modules
TEST_LIB_PATH="../../lib" ./node_modules/.bin/_mocha \
--timeout 3000 \
--reporter spec \
--check-leaks \
--ui tdd \
--recursive
test-coverage: lib test/lib node_modules
TEST_LIB_PATH="../../lib" ./node_modules/.bin/istanbul \
cover ./node_modules/.bin/_mocha \
-- \
--timeout 3000 \
--reporter spec \
--check-leaks \
--ui tdd \
--recursive
view-coverage: test-coverage
open coverage/lcov-report/index.html
test-coveralls: test-coverage
cat coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
node_modules: package.json
npm install
touch $@
clean:
rm -rf coverage
.PHONY: test test-coverage test-coveralls view-coverage clean
/**
* @author Matthew Caruana Galizia <m@m.cg>
* @license MIT: http://mattcg.mit-license.org/
* @copyright Copyright (c) 2013, Matthew Caruana Galizia
*/
/*jshint node:true*/
/*global test, suite*/
'use strict';
var assert = require('assert');
var tags = require(process.env.TEST_LIB_PATH);
suite('tags', function() {
test('date() returns file date', function() {
assert(/\d{4}\-\d{2}\-\d{2}/.test(tags.date()));
});
test('type() returns subtag by type', function() {
var subtag;
subtag = tags.type('Latn', 'script');
assert(subtag);
assert.equal(subtag.format(), 'Latn');
assert.equal(subtag.type(), 'script');
assert.equal(tags.type('en', 'script'), null);
});
test('region() returns subtag by region', function() {
var subtag;
subtag = tags.region('IQ');
assert(subtag);
assert.equal(subtag.format(), 'IQ');
assert.equal(subtag.type(), 'region');
assert.equal(tags.region('en'), null);
});
test('language() returns subtag by language', function() {
var subtag;
subtag = tags.language('en');
assert(subtag);
assert.equal(subtag.format(), 'en');
assert.equal(subtag.type(), 'language');
assert.equal(tags.language('GB'), null);
});
test('languages() returns all languages for macrolanguage', function() {
var subtags, err;
subtags = tags.languages('zh');
assert(subtags.length > 0);
try {
assert.equal(tags.languages('en'));
} catch (e) {
err = e;
}
assert(err);
assert.equal(err.message, '\'en\' is not a macrolanguage.');
});
test('search() matches descriptions', function() {
var subtags;
subtags = tags.search('Maltese');
assert(subtags.length > 0);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'mt');
assert.equal(subtags[1].type(), 'language');
assert.equal(subtags[1].format(), 'mdl');
assert.equal(subtags[2].type(), 'extlang');
assert.equal(subtags[2].format(), 'mdl');
subtags = tags.search('Gibberish');
assert.deepEqual(subtags, []);
});
test('search() puts exact match at the top', function() {
var subtags;
subtags = tags.search('Dari');
assert(subtags.length > 0);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'prs');
});
test('subtags() returns subtags', function() {
var subtags;
subtags = tags.subtags('whatever');
assert.deepEqual(subtags, []);
subtags = tags.subtags('mt');
assert.equal(subtags.length, 2);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'mt');
assert.equal(subtags[1].type(), 'region');
assert.equal(subtags[1].format(), 'MT');
});
test('check() checks tag validity', function() {
assert(tags.check('en'));
assert(!tags.check('mo'));
});
test('gets tag', function() {
var tag;
tag = tags('en');
assert(tag);
tag = tags('en-gb');
assert(tag);
assert.equal(tag.format(), 'en-GB');
});
});
/**
* @author Matthew Caruana Galizia <m@m.cg>
* @license MIT: http://mattcg.mit-license.org/
* @copyright Copyright (c) 2013, Matthew Caruana Galizia
*/
/*jshint node:true*/
/*global test, suite*/
'use strict';
var assert = require('assert');
var Subtag = require(process.env.TEST_LIB_PATH + '/Subtag');
suite('Subtag', function() {
test('subtag.type() returns type', function() {
assert.equal(new Subtag('zh', 'language').type(), 'language');
assert.equal(new Subtag('IQ', 'region').type(), 'region');
});
test('subtag.descriptions() returns descriptions', function() {
assert.deepEqual(new Subtag('IQ', 'region').descriptions(), ['Iraq']);
assert.deepEqual(new Subtag('vsv', 'extlang').descriptions(), ['Valencian Sign Language', 'Llengua de signes valenciana']);
});
test('subtag.preferred() returns preferred subtag', function() {
var subtag, preferred;
// Extlang
subtag = new Subtag('vsv', 'extlang');
preferred = subtag.preferred();
assert(preferred);
assert.equal(preferred.type(), 'language');
assert.equal(preferred.format(), 'vsv');
// Language
// Moldovan -> Romanian
subtag = new Subtag('mo', 'language');
preferred = subtag.preferred();
assert(preferred);
assert.equal(preferred.type(), 'language');
assert.equal(preferred.format(), 'ro');
// Region
// Burma -> Myanmar
subtag = new Subtag('BU', 'region');
preferred = subtag.preferred();
assert(preferred);
assert.equal(preferred.type(), 'region');
assert.equal(preferred.format(), 'MM');
// Variant
subtag = new Subtag('heploc', 'variant');
preferred = subtag.preferred();
assert(preferred);
assert.equal(preferred.type(), 'variant');
assert.equal(preferred.format(), 'alalc97');
// Should return null if no preferred value.
// Latin America and the Caribbean
subtag = new Subtag('419', 'region');
assert.equal(subtag.preferred(), null);
});
test('subtag.script() returns suppress-script as subtag', function() {
var subtag, script;
subtag = new Subtag('en', 'language');
script = subtag.script();
assert(script);
assert.equal(script.type(), 'script');
assert.equal(script.format(), 'Latn');
// Should return null if no script.
// A macrolanguage like 'zh' should have no suppress-script.
subtag = new Subtag('zh', 'language');
script = subtag.script();
assert.equal(script, null);
});
test('subtag.scope() returns scope', function() {
assert.equal(new Subtag('zh', 'language').scope(), 'macrolanguage');
assert.equal(new Subtag('nah', 'language').scope(), 'collection');
assert.equal(new Subtag('en', 'language').scope(), null);
assert.equal(new Subtag('IQ', 'region').scope(), null);
});
test('subtag.deprecated() returns deprecation date if available', function() {
// German Democratic Republic
assert.equal(new Subtag('DD', 'region').deprecated(), '1990-10-30');
assert.equal(new Subtag('DE', 'region').deprecated(), null);
});
test('subtag.added() returns date added', function() {
assert.equal(new Subtag('DD', 'region').added(), '2005-10-16');
assert.equal(new Subtag('DG', 'region').added(), '2009-07-29');
});
test('subtag.comments() returns comments', function() {
// Yugoslavia
assert.deepEqual(new Subtag('YU', 'region').comments(), ['see BA, HR, ME, MK, RS, or SI']);
});
test('subtag.format() formats subtag according to conventions', function() {
// Language
assert.equal(new Subtag('en', 'language').format(), 'en');
assert.equal(new Subtag('EN', 'language').format(), 'en');
// Region
assert.equal(new Subtag('GB', 'region').format(), 'GB');
assert.equal(new Subtag('gb', 'region').format(), 'GB');
// Script
assert.equal(new Subtag('Latn', 'script').format(), 'Latn');
assert.equal(new Subtag('latn', 'script').format(), 'Latn');
});
});
/**
* @author Matthew Caruana Galizia <m@m.cg>
* @license MIT: http://mattcg.mit-license.org/
* @copyright Copyright (c) 2013, Matthew Caruana Galizia
*/
/*jshint node:true*/
/*global test, suite*/
'use strict';
var assert = require('assert');
var Tag = require(process.env.TEST_LIB_PATH + '/Tag');
suite('Tag', function() {
test('tag.type() returns \'grandfathered\'', function() {
// Classified as grandfathered in the registry.
assert.equal(new Tag('en-GB-oed').type(), 'grandfathered');
});
test('tag.type() returns \'redundant\'', function() {
// Classified as redundant in the registry.
assert.equal(new Tag('az-Arab').type(), 'redundant');
assert.equal(new Tag('uz-Cyrl').type(), 'redundant');
assert.equal(new Tag('zh-cmn-Hant').type(), 'redundant');
});
test('tag.type() returns \'tag\'', function() {
// Maltese (mt) is a subtag but valid as a standalone tag.
assert.equal(new Tag('mt').type(), 'tag');
});
test('tag.subtags() returns subtags with correct type', function() {
var tag, subtags;
tag = new Tag('en');
subtags = tag.subtags();
assert.equal(subtags.length, 1);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'en');
// Lowercase - lookup should be case insensitive.
tag = new Tag('en-mt');
subtags = tag.subtags();
assert.equal(subtags.length, 2);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'en');
assert.equal(subtags[1].type(), 'region');
assert.equal(subtags[1].format(), 'MT');
tag = new Tag('en-mt-arab');
subtags = tag.subtags();
assert.equal(subtags.length, 3);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'en');
assert.equal(subtags[1].type(), 'region');
assert.equal(subtags[1].format(), 'MT');
assert.equal(subtags[2].type(), 'script');
assert.equal(subtags[2].format(), 'Arab');
});
test('tag.subtags() returns only existent subtags', function() {
var tag, subtags;
tag = new Tag('hello');
assert.deepEqual(tag.subtags(), []);
tag = new Tag('en-hello');
subtags = tag.subtags();
assert.equal(subtags.length, 1);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'en');
});
test('tag.subtags() handles private tags', function() {
var tag, subtags;
tag = new Tag('en-GB-x-Beano');
subtags = tag.subtags();
assert.equal(subtags.length, 2);
assert.equal(subtags[0].type(), 'language');
assert.equal(subtags[0].format(), 'en');
assert.equal(subtags[1].type(), 'region');
assert.equal(subtags[1].format(), 'GB');
});
test('tag.subtags() returns empty array for grandfathered tag', function() {
var tag, subtags;
tag = new Tag('en-GB-oed');
assert.equal(tag.type(), 'grandfathered');
subtags = tag.subtags();
assert.deepEqual(subtags, []);
assert.equal(undefined, tag.region());
assert.equal(undefined, tag.language());
});
test('tag.subtags() returns array for redundant tag', function() {
var tag, subtags;
tag = new Tag('az-Arab');
assert.equal(tag.type(), 'redundant');
subtags = tag.subtags();
assert.equal(2, subtags.length);
assert.equal(subtags[0].format(), 'az');
assert.equal(subtags[1].format(), 'Arab');
});
test('tag.errors() returns error for deprecated grandfathered tag', function() {
var tag, errs, err;
// Grandfathered and deprecated, therefore invalid.
tag = new Tag('art-lojban');
assert.equal(tag.type(), 'grandfathered');
assert(tag.deprecated());
errs = tag.errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_DEPRECATED);
assert.equal(err.tag, 'art-lojban');
});
test('tag.errors() returns error for deprecated redundant tag', function() {
var tag, errs, err;
// Redundant and deprecated, therefore invalid.
tag = new Tag('zh-cmn');
assert.equal(tag.type(), 'redundant');
assert(tag.deprecated());
errs = tag.errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_DEPRECATED);
assert.equal(err.tag, 'zh-cmn');
});
test('tag.errors() returns error if contains deprecated subtags', function() {
var errs, err;
// Moldovan (mo) is deprecated as a language.
errs = new Tag('mo').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_SUBTAG_DEPRECATED);
assert.equal(err.message, 'The subtag \'mo\' is deprecated.');
// Neutral Zone (NT) is deprecated as a region.
errs = new Tag('en-NT').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_SUBTAG_DEPRECATED);
assert.equal(err.message, 'The subtag \'NT\' is deprecated.');
});
test('tag.errors() returns empty array for valid tag', function() {
assert.equal(new Tag('en').errors().length, 0);
});
test('tag.errors() returns error if no language tag and not grandfathered or redundant', function() {
var errs, err;
// Test with empty tag.
errs = new Tag('').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_NO_LANGUAGE);
assert.equal(err.message, 'Empty tag.');
errs = new Tag('IQ-Arab').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_NO_LANGUAGE);
assert.equal(err.message, 'Missing language tag in \'iq-arab\'.');
errs = new Tag('419').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_NO_LANGUAGE);
assert.equal(err.message, 'Missing language tag in \'419\'.');
});
test('tag.errors() returns error if language subtag not at front of tag', function() {
var errs, err;
errs = new Tag('GB-en').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_NO_LANGUAGE);
assert.equal(err.message, 'Missing language tag in \'gb-en\'.');
});
test('tag.errors() returns error if more than one language subtag appears', function() {
var errs, err;
errs = new Tag('en-en').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_LANGUAGE);
assert.equal(err.message, 'Extra language subtag \'en\' found.');
errs = new Tag('en-en-GB').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_LANGUAGE);
assert.equal(err.message, 'Extra language subtag \'en\' found.');
errs = new Tag('ko-en').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_LANGUAGE);
assert.equal(err.message, 'Extra language subtag \'en\' found.');
});
test('tag.errors() returns error if more than one region subtag appears', function() {
var errs, err;
errs = new Tag('en-GB-GB').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_REGION);
assert.equal(err.message, 'Extra region subtag \'GB\' found.');
errs = new Tag('ko-mt-mt').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_REGION);
assert.equal(err.message, 'Extra region subtag \'MT\' found.');
});
test('tag.errors() returns error if more than one script subtag appears', function() {
var errs, err;
errs = new Tag('mt-Arab-Arab').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_SCRIPT);
assert.equal(err.message, 'Extra script subtag \'Arab\' found.');
errs = new Tag('en-Cyrl-Latn').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_SCRIPT);
assert.equal(err.message, 'Extra script subtag \'Latn\' found.');
// First error should be regarding suppress-script, second should be regarding extra script.
errs = new Tag('en-Latn-Cyrl').errors();
assert.equal(errs.length, 2);
err = errs[0];
assert.equal(err.code, Tag.ERR_SUPPRESS_SCRIPT);
assert.equal(err.message, 'The script subtag \'Latn\' is the same as the language suppress-script.');
err = errs[1];
assert.equal(err.code, Tag.ERR_EXTRA_SCRIPT);
assert.equal(err.message, 'Extra script subtag \'Cyrl\' found.');
},
'tag.errors() returns error if more than one extlang subtag appears', function() {
var errs, err;
errs = new Tag('en-asp-bog').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_EXTRA_EXTLANG);
assert.equal(err.message, 'Extra extlang subtag \'bog\' found.');
});
test('tag.errors() returns error if a duplicate variant subtag appears', function() {
var errs, err;
errs = new Tag('ca-valencia-valencia').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_DUPLICATE_VARIANT);
assert.equal(err.message, 'Duplicate variant subtag \'valencia\' found.');
});
test('tag.errors() returns error if private-use subtag contains more than 8 characters', function() {
var errs, err;
// i.e. more than 8 in each component, not in total.
errs = new Tag('en-x-more-than-eight-chars').errors();
assert.equal(errs.length, 0);
errs = new Tag('en-x-morethaneightchars').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_TOO_LONG);
assert.equal(err.message, 'The private-use subtag \'morethaneightchars\' is too long.');
});
test('tag.errors() returns error if script subtag is same as language suppress-script', function() {
var errs, err;
errs = new Tag('gsw-Latn').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_SUPPRESS_SCRIPT);
assert.equal(err.message, 'The script subtag \'Latn\' is the same as the language suppress-script.');
errs = new Tag('en-Latn-GB').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_SUPPRESS_SCRIPT);
assert.equal(err.message, 'The script subtag \'Latn\' is the same as the language suppress-script.');
});
test('tag.errors() returns error if subtags are in wrong order', function() {
var errs, err;
errs = new Tag('mt-MT-Arab').errors();
assert.equal(errs.length, 1);
err = errs[0];
assert.equal(err.code, Tag.ERR_WRONG_ORDER);
assert.equal(err.message, 'The subtag \'MT\' should not appear before \'Arab\'.');
});
test('tag.valid() returns true for valid tag', function() {
assert(new Tag('en').valid());
assert(new Tag('en-GB').valid());
assert(new Tag('gsw').valid());
assert(new Tag('de-CH').valid());
});
test('tag.valid() returns true for subtag followed by private tag', function() {
assert(new Tag('en-x-whatever').valid());
});
test('tag.valid() returns true for non-deprecated grandfathered tag', function() {
var tag;
// Grandfathered but not deprecated, therefore valid.
tag = new Tag('i-default');
assert.equal(tag.type(), 'grandfathered');
assert(!tag.deprecated());
assert(tag.valid());
});
test('tag.valid() returns true for non-deprecated redundant tag', function() {
var tag;
// Redundant but not deprecated, therefore valid.
tag = new Tag('zh-Hans');
assert.equal(tag.type(), 'redundant');
assert(!tag.deprecated());
assert(tag.valid());
tag = new Tag('es-419');
assert.equal(tag.type(), 'redundant');
assert(!tag.deprecated());
assert(tag.valid());
});
test('tag.valid() returns false for non-existent tag', function() {
assert(!new Tag('zzz').valid());
assert(!new Tag('zzz-Latn').valid());
assert(!new Tag('en-Lzzz').valid());
});
test('tag.valid() returns false for deprecated grandfathered tag', function() {
var tag;
// Grandfathered and deprecated, therefore invalid.
tag = new Tag('art-lojban');
assert.equal(tag.type(), 'grandfathered');
assert(tag.deprecated());
assert(!tag.valid());
});
test('tag.valid() returns false for deprecated redundant tag', function() {
var tag;
// Redundant and deprecated, therefore invalid.
tag = new Tag('zh-cmn');
assert.equal(tag.type(), 'redundant');
assert(tag.deprecated());
assert(!tag.valid());
tag = new Tag('zh-cmn-Hans');
assert.equal(tag.type(), 'redundant');
assert(tag.deprecated());
assert(!tag.valid());
});
test('tag.valid() returns false if contains deprecated subtags', function() {
// Moldovan (mo) is deprecated as a language.
assert(!new Tag('mo').valid());
// Neutral Zone (NT) is deprecated as a region.
assert(!new Tag('en-NT').valid());
});
test('tag.valid() returns false for tag with redundant script subtag', function() {
// Swiss German (gsw) has a suppress script of Latn.
assert(!new Tag('gsw-Latn').valid());
});
test('tag.valid() returns false if tag contains no language tag and is not grandfathered or redundant', function() {
assert(!new Tag('IQ-Arab').valid());
assert(!new Tag('419').valid());
});
test('tag.valid() returns false if language subtag is not front of tag', function() {
assert(!new Tag('GB-en').valid());
});
test('tag.valid() returns false if more than one language subtag appears', function() {
assert(!new Tag('en-en').valid());
assert(!new Tag('ko-en').valid());
});
test('tag.valid() returns false if more than one region subtag appears', function() {
assert(!new Tag('en-001-gb').valid());
assert(!new Tag('gb-001').valid());
});
test('tag.valid() returns false if more than one extlang subtag appears', function() {
assert(!new Tag('en-asp-bog').valid());
});
test('tag.valid() returns false if more than one script subtag appears', function() {
assert(!new Tag('arb-Latn-Cyrl').valid());
});
test('tag.valid() returns false if a duplicate variant subtag appears', function() {
assert(!new Tag('ca-valencia-valencia').valid());
});
test('tag.valid() returns false if private-use subtag contains more than 8 characters', function() {
// i.e. more than 8 in each component, not in total.
assert(new Tag('en-x-more-than-eight-chars').valid());
assert(!new Tag('en-x-morethaneightchars').valid());
});
test('tag.valid() returns false if script subtag is same as language suppress-script', function() {
assert(!new Tag('en-Latn').valid());
assert(!new Tag('en-GB-Latn').valid());
assert(!new Tag('gsw-Latn').valid());
});
test('tag.deprecated() returns deprecation date when available', function() {
var tag;
// Redundant and deprecated.
tag = new Tag('zh-cmn-Hant');
assert.equal(tag.type(), 'redundant');
assert.equal(tag.deprecated(), '2009-07-29');
// Redundant but not deprecated.
tag = new Tag('zh-Hans');
assert.equal(tag.type(), 'redundant');
assert(!tag.deprecated());
// Grandfathered and deprecated.
tag = new Tag('zh-xiang');
assert.equal(tag.type(), 'grandfathered');
assert.equal(tag.deprecated(), '2009-07-29');
// Grandfathered but not deprecated.
tag = new Tag('i-default');
assert.equal(tag.type(), 'grandfathered');
assert(!tag.deprecated());
});
test('tag.added() returns add date when available', function() {
var tag;
// Redundant and deprecated.
tag = new Tag('zh-cmn-Hant');
assert.equal(tag.type(), 'redundant');
assert.equal(tag.added(), '2005-07-15');
// Redundant but not deprecated.
tag = new Tag('zh-Hans');
assert.equal(tag.type(), 'redundant');
assert(!tag.deprecated());
assert.equal(tag.added(), '2003-05-30');
// Grandfathered and deprecated.
tag = new Tag('zh-xiang');
assert.equal(tag.type(), 'grandfathered');
assert.equal(tag.added(), '1999-12-18');
// Grandfathered but not deprecated.
tag = new Tag('i-default');
assert.equal(tag.type(), 'grandfathered');
assert(!tag.deprecated());
assert.equal(tag.added(), '1998-03-10');
});
test('tag.descriptions() returns descriptions when available', function() {
var tag;
tag = new Tag('i-default');
assert.equal(tag.type(), 'grandfathered');
assert(!tag.deprecated());
assert.deepEqual(tag.descriptions(), ['Default Language']);
// Otherwise returns an empty array.
assert.deepEqual(new Tag('en').descriptions(), []);
});
test('tag.format() formats tag according to conventions', function() {
assert.equal(new Tag('en').format(), 'en');
assert.equal(new Tag('En').format(), 'en');
assert.equal(new Tag('EN').format(), 'en');
assert.equal(new Tag('eN').format(), 'en');
assert.equal(new Tag('en-gb').format(), 'en-GB');
assert.equal(new Tag('en-gb-oed').format(), 'en-GB-oed');
assert.equal(new Tag('az-latn').format(), 'az-Latn');
assert.equal(new Tag('ZH-hant-hK').format(), 'zh-Hant-HK');
});
test('tag.preferred() returns preferred tag if available', function() {
var tag = new Tag('zh-cmn-Hant');
assert.equal(tag.type(), 'redundant');
assert(tag.deprecated());
assert(tag.preferred());
assert.equal(tag.preferred().format(), 'cmn-Hant');
assert.equal(new Tag('zh-Hans').preferred(), null);
});
test('tag.region() and tag.language() return subtags for redundant tags', function() {
var tag;
tag = new Tag('es-419');
assert.deepEqual(tag.region().descriptions(), ['Latin America and the Caribbean']);
assert.deepEqual(tag.language().descriptions(), ['Spanish', 'Castilian']);
tag = new Tag('sgn-NL');
assert.deepEqual(tag.region().descriptions(), ['Netherlands']);
assert.deepEqual(tag.language().descriptions(), ['Sign languages']);
});
});