"use strict"; const codes = require("./codes.json"); const supportedLocales = require("./supportedLocales.json"); const removeDiacritics = require("diacritics").remove; const registeredLocales = {}; /* * All codes map to ISO 3166-1 alpha-2 */ const alpha2 = {}, alpha3 = {}, numeric = {}, invertedNumeric = {}; codes.forEach(function (codeInformation) { const s = codeInformation; alpha2[s[0]] = s[1]; alpha3[s[1]] = s[0]; numeric[s[2]] = s[0]; invertedNumeric[s[0]] = s[2]; }); /** * @private * @param {number} code */ function formatNumericCode(code) { return String("000" + (code ? code : "")).slice(-3); } /** * @private * Avoid using obj.hasOwnProperty directly as `hasOwnProperty` could be a * property in itself ({ hasOwnProperty: 1 }) and cause weird bugs * https://eslint.org/docs/rules/no-prototype-builtins */ function hasOwnProperty(object, property) { return Object.prototype.hasOwnProperty.call(object, property); } /** * @private * Pass localeList through a filter and return a newLocaleList obj * with the same structure of the old localeList. * * @param {LocalizedCountryNames} localeList Local List in raw * @param {Function} filter callback to set filter rule * @return {String | String[]} new filtered Local List */ function localeFilter(localeList, filter) { return Object.keys(localeList).reduce(function (newLocaleList, alpha2) { const nameList = localeList[alpha2]; newLocaleList[alpha2] = filter(nameList, alpha2); return newLocaleList; }, {}); } /** * @private * Preserve for getName & getNames * * @param {GetNameOptions.select} type all | official | alias * @param countryNameList string array of country's * official name and alias * @return {String | String[]} of a country name */ function filterNameBy(type, countryNameList) { switch (type) { case "official": return Array.isArray(countryNameList) ? countryNameList[0] : countryNameList; case "all": return typeof countryNameList === "string" ? [countryNameList] : countryNameList; case "alias": return Array.isArray(countryNameList) ? countryNameList[1] || countryNameList[0] : countryNameList; default: throw new TypeError( "LocaleNameType must be one of these: all, official, alias!" ); } } /** * Register countries in browsers' environment: * @param {object} localeData * @example countries.registerLocale(require("i18n-iso-countries/langs/en.json")); * @return void */ exports.registerLocale = function (localeData) { if (!localeData.locale) { throw new TypeError("Missing localeData.locale"); } if (!localeData.countries) { throw new TypeError("Missing localeData.countries"); } registeredLocales[localeData.locale] = localeData.countries; }; /* * @param code Alpha-3 code * @return Alpha-2 code or undefined */ function alpha3ToAlpha2(code) { return alpha3[code]; } exports.alpha3ToAlpha2 = alpha3ToAlpha2; /* * @param code Alpha-2 code * @return Alpha-3 code or undefined */ function alpha2ToAlpha3(code) { return alpha2[code]; } exports.alpha2ToAlpha3 = alpha2ToAlpha3; /* * @param code Alpha-3 code * @return Numeric code or undefined */ function alpha3ToNumeric(code) { return invertedNumeric[alpha3ToAlpha2(code)]; } exports.alpha3ToNumeric = alpha3ToNumeric; /* * @param code Alpha-2 code * @return Numeric code or undefined */ function alpha2ToNumeric(code) { return invertedNumeric[code]; } exports.alpha2ToNumeric = alpha2ToNumeric; /* * @param code Numeric code * @return Alpha-3 code or undefined */ function numericToAlpha3(code) { const padded = formatNumericCode(code); return alpha2ToAlpha3(numeric[padded]); } exports.numericToAlpha3 = numericToAlpha3; /* * @param code Numeric code * @return Alpha-2 code or undefined */ function numericToAlpha2(code) { const padded = formatNumericCode(code); return numeric[padded]; } exports.numericToAlpha2 = numericToAlpha2; /* * @param code ISO 3166-1 alpha-2, alpha-3 or numeric code * @return ISO 3166-1 alpha-3 */ function toAlpha3(code) { if (typeof code === "string") { if (/^[0-9]*$/.test(code)) { return numericToAlpha3(code); } if (code.length === 2) { return alpha2ToAlpha3(code.toUpperCase()); } if (code.length === 3) { return code.toUpperCase(); } } if (typeof code === "number") { return numericToAlpha3(code); } return undefined; } exports.toAlpha3 = toAlpha3; /* * @param code ISO 3166-1 alpha-2, alpha-3 or numeric code * @return ISO 3166-1 alpha-2 */ function toAlpha2(code) { if (typeof code === "string") { if (/^[0-9]*$/.test(code)) { return numericToAlpha2(code); } if (code.length === 2) { return code.toUpperCase(); } if (code.length === 3) { return alpha3ToAlpha2(code.toUpperCase()); } } if (typeof code === "number") { return numericToAlpha2(code); } return undefined; } exports.toAlpha2 = toAlpha2; /** * @param {string | number | Alpha2Code | Alpha3Code} code * @param {String} lang language for country name * @param {GetNameOptions} options * @return {String | String[] | undefined} name */ exports.getName = function (code, lang, options = {}) { if (!("select" in options)) { options.select = "official"; } try { const codeMaps = registeredLocales[lang.toLowerCase()]; const nameList = codeMaps[toAlpha2(code)]; return filterNameBy(options.select, nameList); } catch (err) { return undefined; } }; /** * @param {String} lang language for country names * @param {GetNameOptions} options getNames Options * @return {LocalizedCountryNames} country code * mapped to county name */ exports.getNames = function (lang, options = {}) { if (!("select" in options)) { options.select = "official"; } const localeList = registeredLocales[lang.toLowerCase()]; if (localeList === undefined) return {}; return localeFilter(localeList, function (nameList) { return filterNameBy(options.select, nameList); }); }; /* * @param name name * @param lang language for country name * @return ISO 3166-1 alpha-2 or undefined */ exports.getAlpha2Code = function (name, lang) { const normalizeString = (string) => string.toLowerCase(); const areSimilar = (a, b) => normalizeString(a) === normalizeString(b); try { const codenames = registeredLocales[lang.toLowerCase()]; for (const p in codenames) { if (!hasOwnProperty(codenames, p)) { continue; } if (typeof codenames[p] === "string") { if (areSimilar(codenames[p], name)) { return p; } } if (Array.isArray(codenames[p])) { for (const mappedName of codenames[p]) { if (areSimilar(mappedName, name)) { return p; } } } } return undefined; } catch (err) { return undefined; } }; /* * @param name name * @param lang language for country name * @return ISO 3166-1 alpha-2 or undefined */ exports.getSimpleAlpha2Code = function (name, lang) { const normalizeString = (string) => removeDiacritics(string.toLowerCase()); const areSimilar = (a, b) => normalizeString(a) === normalizeString(b); try { const codenames = registeredLocales[lang.toLowerCase()]; for (const p in codenames) { if (!hasOwnProperty(codenames, p)) { continue; } if (typeof codenames[p] === "string") { if (areSimilar(codenames[p], name)) { return p; } } if (Array.isArray(codenames[p])) { for (const mappedName of codenames[p]) { if (areSimilar(mappedName, name)) { return p; } } } } return undefined; } catch (err) { return undefined; } }; /* * @return Object of alpha-2 codes mapped to alpha-3 codes */ exports.getAlpha2Codes = function () { return alpha2; }; /* * @param name name * @param lang language for country name * @return ISO 3166-1 alpha-3 or undefined */ exports.getAlpha3Code = function (name, lang) { const alpha2 = exports.getAlpha2Code(name, lang); if (alpha2) { return exports.toAlpha3(alpha2); } else { return undefined; } }; /* * @param name name * @param lang language for country name * @return ISO 3166-1 alpha-3 or undefined */ exports.getSimpleAlpha3Code = function (name, lang) { const alpha2 = exports.getSimpleAlpha2Code(name, lang); if (alpha2) { return exports.toAlpha3(alpha2); } else { return undefined; } }; /* * @return Object of alpha-3 codes mapped to alpha-2 codes */ exports.getAlpha3Codes = function () { return alpha3; }; /* * @return Object of numeric codes mapped to alpha-2 codes */ exports.getNumericCodes = function () { return numeric; }; /* * @return Array of registered languages */ exports.langs = function () { return Object.keys(registeredLocales); }; /* * @return Array of supported languages */ exports.getSupportedLanguages = function () { return supportedLocales; }; /* * @param code ISO 3166-1 alpha-2, alpha-3 or numeric code * @return Boolean */ exports.isValid = function (code) { if (!code) { return false; } const coerced = code.toString().toUpperCase(); return ( hasOwnProperty(alpha3, coerced) || hasOwnProperty(alpha2, coerced) || hasOwnProperty(numeric, coerced) ); };