@genesys225/fast-text-language-detection
Version:
Language detection with facebook fast-text model
593 lines (543 loc) • 12.6 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('path'), require('fs')) :
typeof define === 'function' && define.amd ? define(['path', 'fs'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.myLib = factory(global.require$$0, global.require$$0$1));
})(this, (function (require$$0, require$$0$1) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0);
var require$$0__default$1 = /*#__PURE__*/_interopDefaultLegacy(require$$0$1);
function commonjsRequire (path) {
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
}
var bindings = {exports: {}};
/**
* Module dependencies.
*/
var sep = require$$0__default["default"].sep || '/';
/**
* Module exports.
*/
var fileUriToPath_1 = fileUriToPath;
/**
* File URI to Path function.
*
* @param {String} uri
* @return {String} path
* @api public
*/
function fileUriToPath (uri) {
if ('string' != typeof uri ||
uri.length <= 7 ||
'file://' != uri.substring(0, 7)) {
throw new TypeError('must pass in a file:// URI to convert to a file path');
}
var rest = decodeURI(uri.substring(7));
var firstSlash = rest.indexOf('/');
var host = rest.substring(0, firstSlash);
var path = rest.substring(firstSlash + 1);
// 2. Scheme Definition
// As a special case, <host> can be the string "localhost" or the empty
// string; this is interpreted as "the machine from which the URL is
// being interpreted".
if ('localhost' == host) host = '';
if (host) {
host = sep + sep + host;
}
// 3.2 Drives, drive letters, mount points, file system root
// Drive letters are mapped into the top of a file URI in various ways,
// depending on the implementation; some applications substitute
// vertical bar ("|") for the colon after the drive letter, yielding
// "file:///c|/tmp/test.txt". In some cases, the colon is left
// unchanged, as in "file:///c:/tmp/test.txt". In other cases, the
// colon is simply omitted, as in "file:///c/tmp/test.txt".
path = path.replace(/^(.+)\|/, '$1:');
// for Windows, we need to invert the path separators from what a URI uses
if (sep == '\\') {
path = path.replace(/\//g, '\\');
}
if (/^.+\:/.test(path)) ; else {
// unix path…
path = sep + path;
}
return host + path;
}
/**
* Module dependencies.
*/
(function (module, exports) {
var fs = require$$0__default$1["default"],
path = require$$0__default["default"],
fileURLToPath = fileUriToPath_1,
join = path.join,
dirname = path.dirname,
exists =
(fs.accessSync &&
function(path) {
try {
fs.accessSync(path);
} catch (e) {
return false;
}
return true;
}) ||
fs.existsSync ||
path.existsSync,
defaults = {
arrow: process.env.NODE_BINDINGS_ARROW || ' → ',
compiled: process.env.NODE_BINDINGS_COMPILED_DIR || 'compiled',
platform: process.platform,
arch: process.arch,
nodePreGyp:
'node-v' +
process.versions.modules +
'-' +
process.platform +
'-' +
process.arch,
version: process.versions.node,
bindings: 'bindings.node',
try: [
// node-gyp's linked version in the "build" dir
['module_root', 'build', 'bindings'],
// node-waf and gyp_addon (a.k.a node-gyp)
['module_root', 'build', 'Debug', 'bindings'],
['module_root', 'build', 'Release', 'bindings'],
// Debug files, for development (legacy behavior, remove for node v0.9)
['module_root', 'out', 'Debug', 'bindings'],
['module_root', 'Debug', 'bindings'],
// Release files, but manually compiled (legacy behavior, remove for node v0.9)
['module_root', 'out', 'Release', 'bindings'],
['module_root', 'Release', 'bindings'],
// Legacy from node-waf, node <= 0.4.x
['module_root', 'build', 'default', 'bindings'],
// Production "Release" buildtype binary (meh...)
['module_root', 'compiled', 'version', 'platform', 'arch', 'bindings'],
// node-qbs builds
['module_root', 'addon-build', 'release', 'install-root', 'bindings'],
['module_root', 'addon-build', 'debug', 'install-root', 'bindings'],
['module_root', 'addon-build', 'default', 'install-root', 'bindings'],
// node-pre-gyp path ./lib/binding/{node_abi}-{platform}-{arch}
['module_root', 'lib', 'binding', 'nodePreGyp', 'bindings']
]
};
/**
* The main `bindings()` function loads the compiled bindings for a given module.
* It uses V8's Error API to determine the parent filename that this function is
* being invoked from, which is then used to find the root directory.
*/
function bindings(opts) {
// Argument surgery
if (typeof opts == 'string') {
opts = { bindings: opts };
} else if (!opts) {
opts = {};
}
// maps `defaults` onto `opts` object
Object.keys(defaults).map(function(i) {
if (!(i in opts)) opts[i] = defaults[i];
});
// Get the module root
if (!opts.module_root) {
opts.module_root = exports.getRoot(exports.getFileName());
}
// Ensure the given bindings name ends with .node
if (path.extname(opts.bindings) != '.node') {
opts.bindings += '.node';
}
// https://github.com/webpack/webpack/issues/4175#issuecomment-342931035
var requireFunc =
typeof __webpack_require__ === 'function'
? __non_webpack_require__
: commonjsRequire;
var tries = [],
i = 0,
l = opts.try.length,
n,
b,
err;
for (; i < l; i++) {
n = join.apply(
null,
opts.try[i].map(function(p) {
return opts[p] || p;
})
);
tries.push(n);
try {
b = opts.path ? requireFunc.resolve(n) : requireFunc(n);
if (!opts.path) {
b.path = n;
}
return b;
} catch (e) {
if (e.code !== 'MODULE_NOT_FOUND' &&
e.code !== 'QUALIFIED_PATH_RESOLUTION_FAILED' &&
!/not find/i.test(e.message)) {
throw e;
}
}
}
err = new Error(
'Could not locate the bindings file. Tried:\n' +
tries
.map(function(a) {
return opts.arrow + a;
})
.join('\n')
);
err.tries = tries;
throw err;
}
module.exports = exports = bindings;
/**
* Gets the filename of the JavaScript file that invokes this function.
* Used to help find the root directory of a module.
* Optionally accepts an filename argument to skip when searching for the invoking filename
*/
exports.getFileName = function getFileName(calling_file) {
var origPST = Error.prepareStackTrace,
origSTL = Error.stackTraceLimit,
dummy = {},
fileName;
Error.stackTraceLimit = 10;
Error.prepareStackTrace = function(e, st) {
for (var i = 0, l = st.length; i < l; i++) {
fileName = st[i].getFileName();
if (fileName !== __filename) {
if (calling_file) {
if (fileName !== calling_file) {
return;
}
} else {
return;
}
}
}
};
// run the 'prepareStackTrace' function above
Error.captureStackTrace(dummy);
dummy.stack;
// cleanup
Error.prepareStackTrace = origPST;
Error.stackTraceLimit = origSTL;
// handle filename that starts with "file://"
var fileSchema = 'file://';
if (fileName.indexOf(fileSchema) === 0) {
fileName = fileURLToPath(fileName);
}
return fileName;
};
/**
* Gets the root directory of a module, given an arbitrary filename
* somewhere in the module tree. The "root directory" is the directory
* containing the `package.json` file.
*
* In: /home/nate/node-native-module/lib/index.js
* Out: /home/nate/node-native-module
*/
exports.getRoot = function getRoot(file) {
var dir = dirname(file),
prev;
while (true) {
if (dir === '.') {
// Avoids an infinite loop in rare cases, like the REPL
dir = process.cwd();
}
if (
exists(join(dir, 'package.json')) ||
exists(join(dir, 'node_modules'))
) {
// Found the 'package.json' file or 'node_modules' dir; we're done
return dir;
}
if (prev === dir) {
// Got to the top
throw new Error(
'Could not find module root given file: "' +
file +
'". Do you have a `package.json` file? '
);
}
// Try the parent dir next
prev = dir;
dir = join(dir, '..');
}
};
}(bindings, bindings.exports));
const FastText = bindings.exports('fasttext');
var main = FastText;
/* internal requirements */
const path = require$$0__default["default"];
/* external requirements */
const { Classifier } = main;
// from benchmark-testing/results/reliability_list_<verison>.json
const reliabilityList = [
'th',
'ml',
'my',
'ta',
'te',
'pa',
'am',
'kn',
'gu',
'si',
'bo',
'dv',
'ja',
'el',
'he',
'ko',
'hy',
'bn',
'mr',
'en',
'zh',
'tr',
'ru',
'de',
'ug',
'vi',
'eo',
'ka',
'hi',
'it',
'ar',
'fr',
'hu',
'lo',
'pl',
'km',
'es',
'fi',
'pt',
'mk',
'uk',
'ur',
'nl',
'lt',
'cs',
];
const languageIsoCodes = [
'af',
'als',
'am',
'an',
'ar',
'arz',
'as',
'ast',
'av',
'az',
'azb',
'ba',
'bar',
'bcl',
'be',
'bg',
'bh',
'bn',
'bo',
'bpy',
'br',
'bs',
'bxr',
'ca',
'cbk',
'ce',
'ceb',
'ckb',
'co',
'cs',
'cv',
'cy',
'da',
'de',
'diq',
'dsb',
'dty',
'dv',
'el',
'eml',
'en',
'eo',
'es',
'et',
'eu',
'fa',
'fi',
'fr',
'frr',
'fy',
'ga',
'gd',
'gl',
'gn',
'gom',
'gu',
'gv',
'he',
'hi',
'hif',
'hr',
'hsb',
'ht',
'hu',
'hy',
'ia',
'id',
'ie',
'ilo',
'io',
'is',
'it',
'ja',
'jbo',
'jv',
'ka',
'kk',
'km',
'kn',
'ko',
'krc',
'ku',
'kv',
'kw',
'ky',
'la',
'lb',
'lez',
'li',
'lmo',
'lo',
'lrc',
'lt',
'lv',
'mai',
'mg',
'mhr',
'min',
'mk',
'ml',
'mn',
'mr',
'mrj',
'ms',
'mt',
'mwl',
'my',
'myv',
'mzn',
'nah',
'nap',
'nds',
'ne',
'new',
'nl',
'nn',
'no',
'oc',
'or',
'os',
'pa',
'pam',
'pfl',
'pl',
'pms',
'pnb',
'ps',
'pt',
'qu',
'rm',
'ro',
'ru',
'rue',
'sa',
'sah',
'sc',
'scn',
'sco',
'sd',
'sh',
'si',
'sk',
'sl',
'so',
'sq',
'sr',
'su',
'sv',
'sw',
'ta',
'te',
'tg',
'th',
'tk',
'tl',
'tr',
'tt',
'tyv',
'ug',
'uk',
'ur',
'uz',
'vec',
'vep',
'vi',
'vls',
'vo',
'wa',
'war',
'wuu',
'xal',
'xmf',
'yi',
'yo',
'yue',
'zh',
];
// Some characters can misconstrude the results
// Removing \n and : because of this issue: https://github.com/indix/whatthelang/issues/12
/**
*
* @param { string } text
* @returns
*/
function formatInput(text) {
return text.replace(/[\n:]/g, '')
}
/* the API class */
class LanguageDetection {
constructor(options = {}) {
this.options = Object.assign(
{},
{
model: path.join(__dirname, '..', 'model', 'fast-text-lid-model.bin'),
},
options
);
this.classifier = new Classifier(this.options.model);
this.languageIsoCodes = languageIsoCodes;
}
predict(text, k = 1) {
return new Promise((resolve, reject) => {
this.classifier.predict(formatInput(text), k, (err, res) => {
if (err) reject(err);
else {
res = res.map((item) => {
const lang = item.label.replace(/^__label__/, '');
return {
lang,
prob: item.value,
isReliableLanguage: reliabilityList.includes(lang),
}
});
resolve(res);
}
});
})
}
}
var src = LanguageDetection;
return src;
}));