const iso6391 = require('./1')
const iso6392 = require('./2')
const iso6393 = require('./3')
/**
* The following constants below are intended to capture the
* ISO 639 Part 1 & 2 Special Codes
* @see {@link https://en.wikipedia.org/wiki/ISO_639-2#Special_situations}
* @see {@link https://en.wikipedia.org/wiki/ISO_639-3#Special_codes}
*/
/**
* The 'und' special language code is intended for cases where the language
* in the data has not yet been identified.
* @public
* @const
* @type {String}
*/
const SPECIAL_UNDETERMINED_LANGUAGE = 'und'
/**
* The 'zxx' special language code is intended for usage when language is
* not present at all such as the sound of an animal.
* @public
* @const
* @type {String}
*/
const SPECIAL_NO_LINGUISTIC_CONTENT = 'zxx'
/**
* The 'mul' special language code is intended for usage when multiple languages
* are present and a single language code is required.
* @public
* @const
* @type {String}
*/
const SPECIAL_NO_MULTIPLE_LANGUAGES = 'mul'
/**
* The 'mis' special language code is intended for "miscellaneous" languages
* that are not yet identified in any ISO standard.
* @public
* @const
* @type {String}
*/
const SPECIAL_UNCODED_LANGUAGES = 'mis'
/**
* A mapping of ISO-639 (1, 2, 3) codes to names
* @public
* @type {Object}
*/
const codes = [].concat(iso6391.codes, iso6392.codes, iso6393.codes)
.reduce((exports, item) => Object.assign(exports, {
[item.code]: {
scope: item.scope || null,
code: item.code,
name: item.name || null,
type: item.type || null
}
}), {})
/**
* Performs a look up for a set of ISO 639 Part 1, 2, & 3
* codes. Results may include "scopes" and "types" as well
* including the language code and the human readable name.
*
* Callers can "filter" results by supplying a string that is compared
* to the code of each language code item, a regular expression that tests
* the name of a language code, or a `filter` object to filter on
* properties like `topic` and `type` in similar ways.
*
* Example:
* // the following will find entries with 'en' language code
* lookup('en') // [ { scope: null, code: 'en', name: 'English', type: null } ]
*
* // the following will find entries with name 'English'
* lookup('English') // [ { scope: 'individual', code: 'eng', name: 'English', type: 'living' }, ... ]
*
* // the following will find entries that contain the word 'English'
* lookup('*English*') // <24 entries>
*
* // the following will find entries that contain the word 'english' (case insensitive)
* lookup('*english*', true) // 24 entries
*
* @public
* @param {Object|String} filter
* @param {?(Object|Boolean)} opts
* @param {?(Object|Boolean)} opts.code
* @param {?(Object|Boolean)} opts.name
* @param {?(Object|Boolean)} opts.type
* @param {?(Object|Boolean)} opts.scope
* @param {?(Boolean)} opts.insensitive
* @return {Array<Object>}
*/
function lookup(filter, opts) {
if ('boolean' === typeof opts) {
opts = { insensitive: opts }
}
if (!opts || 'object' !== typeof opts) {
opts = {}
}
if ('string' === typeof filter) {
filter = { code: filter }
} else if (filter instanceof RegExp) {
filter = { name: filter }
if (!/^\^/.test(filter.name.source)) {
filter.name.compile('^' + filter.name.source, filter.name.flags)
}
if (!/\$$/.test(filter.name.source)) {
filter.name.compile(filter.name.source + '$', filter.name.flags)
}
} else if (!filter || 'object' !== typeof filter) {
filter = {}
}
const alias = { code: 'name' }
const keys = ['scope', 'code', 'name', 'type']
return Object.values(codes).reverse().filter((item) => {
let result = true
if (!item || 'object' !== typeof item) {
return false
}
for (const key of keys) {
if (key in filter && !test(filter[key], item[key])) {
if (alias[key] in item && test(filter[key], item[alias[key]])) {
result = true
} else {
result = false
break
}
}
}
return result
})
function test(left, right) {
if ('string' === typeof left) {
// allow loose things like '*english*' to turn into /^.*english.*$/[i]
const regex = left.replace(/\*/g, '.*').replace(/[.]+\*/g, '.*')
left = RegExp(`^${regex}$`, opts.insensitive ? 'i' : '')
}
return left.test(right)
}
}
/**
* Module exports.
*/
module.exports = Object.assign(codes, {
SPECIAL_UNDETERMINED_LANGUAGE,
SPECIAL_NO_LINGUISTIC_CONTENT,
SPECIAL_NO_MULTIPLE_LANGUAGES,
SPECIAL_UNCODED_LANGUAGES,
lookup
})