devsite/node_modules/bcp-47-normalize/lib/index.js

199 lines
4.6 KiB
JavaScript
Raw Permalink Normal View History

2024-07-08 01:49:38 +00:00
'use strict'
var bcp47 = require('bcp-47')
var match = require('bcp-47-match')
var matches = require('./matches.json')
var fields = require('./fields.json')
var defaults = require('./defaults.json')
var many = require('./many.json')
module.exports = normalize
var own = {}.hasOwnProperty
var collator = new Intl.Collator()
var emptyExtraFields = {
variants: [],
extensions: [],
privateuse: [],
irregular: null,
regular: null
}
function normalize(value, options) {
var settings = options || {}
// 1. normalize and lowercase the tag (`sgn-be-fr` -> `sfb`).
var schema = bcp47.parse(String(value || '').toLowerCase(), settings)
var tag = bcp47.stringify(schema)
var index = -1
var key
if (!tag) {
return tag
}
// 2. Do fancy, expensive replaces (`ha-latn-gh` -> `ha-gh`).
while (++index < matches.length) {
if (match.extendedFilter(tag, matches[index].from).length) {
replace(schema, matches[index].from, matches[index].to)
tag = bcp47.stringify(schema)
}
}
// 3. Do basic field replaces (`en-840` -> `en-us`).
index = -1
while (++index < fields.length) {
if (remove(schema, fields[index].from.field, fields[index].from.value)) {
add(schema, fields[index].to.field, fields[index].to.value)
}
}
// 4. Remove defaults (`nl-nl` -> `nl`).
tag = bcp47.stringify(Object.assign({}, schema, emptyExtraFields))
index = -1
while (++index < defaults.length) {
if (tag === defaults[index]) {
replace(
schema,
defaults[index],
defaults[index].split('-').slice(0, -1).join('-')
)
tag = bcp47.stringify(Object.assign({}, schema, emptyExtraFields))
}
}
// 5. Sort extensions on singleton.
schema.extensions.sort(compareSingleton)
// 6. Warn if fields (currently only regions) should be updated but have
// multiple choices.
if (settings.warning) {
for (key in many) {
if (own.call(many[key], schema[key])) {
settings.warning(
'Deprecated ' +
key +
' `' +
schema[key] +
'`, expected one of `' +
many[key][schema[key]].join('`, `') +
'`',
null,
7
)
}
}
}
// 7. Add proper casing back.
// Format script (ISO 15924) as titlecase (example: `Latn`):
if (schema.script) {
schema.script =
schema.script.charAt(0).toUpperCase() + schema.script.slice(1)
}
// Format region (ISO 3166) as uppercase (note: this doesnt affect numeric
// codes, which is fine):
if (schema.region) {
schema.region = schema.region.toUpperCase()
}
return bcp47.stringify(schema)
}
function replace(schema, from, to) {
var left = bcp47.parse(from)
var right = bcp47.parse(to)
var removed = []
var key
// Remove values from `from`:
for (key in left) {
if (left[key] && left[key].length && remove(schema, key, left[key])) {
removed.push(key)
}
}
// Add values from `to`:
for (key in right) {
// Only add values that are defined on `to`, and that were either removed by
// `from` or are currently empty.
if (
right[key] &&
right[key].length &&
(removed.indexOf(key) > -1 || !schema[key] || !schema[key].length)
) {
add(schema, key, right[key])
}
}
}
function remove(object, key, value) {
var removed = false
var current
var result
var index
var item
/* istanbul ignore else - this is currently done by wrapping code, so else is
* never reached.
* However, that could change in the future, so leave this guard here. */
if (value) {
current = object[key]
result = current
if (current && typeof current === 'object') {
result = []
index = -1
while (++index < current.length) {
item = current[index]
if (value.indexOf(item) < 0) {
result.push(item)
} else {
removed = true
}
}
} else if (current === value) {
result = null
removed = true
}
object[key] = result
}
return removed
}
function add(object, key, value) {
var current = object[key]
var list
var index
var item
if (current && typeof current === 'object') {
list = [].concat(value)
index = -1
while (++index < list.length) {
item = list[index]
/* istanbul ignore else - this currently cant happen, but guard for the
* future. */
if (current.indexOf(item) < 0) {
current.push(item)
}
}
} else {
object[key] = value
}
}
function compareSingleton(left, right) {
return collator.compare(left.singleton, right.singleton)
}