| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277 | const loader = require("./loader.js").loader;const phrasebook = require("./phrasebook.js").phrasebook;/** * This class represents languages library. This store its location on the  * server, and create loaders for them. It could also load langiage  * library from json file. */class languages {    /**     * @var {string}       * This represents path to directory where phrasebooks had been stored.     */    #path;    /**     * @var {Map}     * This store languages and its files on server.      */    #libs;    /**     * @var {bool}     * This store that directory is in the local file system, or remote     * server. When true, resources would be loaded by node:fs. When      * false, resources would be fetched.     */    #local;    /**     * This create new languages library. Next, languages could be added to     * the library by command, or by loading index file.     *      * @throws {TypeError} - When parameters is not in correct format.     *      * @param {string} path - Path to phrasebooks on the server or filesystem.     * @param {bool} local - True when phrasebooks dirs would be loaded by      *                       node:fs module. False when would be fetch.     */    constructor(path, local = false) {        if (typeof(path) !== "string") {            throw new TypeError("Path to the phrasebooks must be string.");        }        if (typeof(local) !== "boolean") {            throw new TypeError("Local must be bool variable.");        }        this.#local = local;        this.#path = path;        this.#libs = new Map();    }    /**     * This add new language to the library by name. Name must be in form     * like POSIX locale, like en_US, or pl_PL. That mean first two letter     * mest be ISO 639-1 and second two letters mst be in ISO 3166-1 alpha-2     * 2 letter country code format.     *      * @see https://www.loc.gov/standards/iso639-2/php/code_list.php     * @see https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes     * @see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2     * @see https://en.wikipedia.org/wiki/Locale_(computer_software)     *      * @throws {TypeError} - When tpes of the parameters is not correct.     *      * @param {string} name - Name of the language, like "en_US".     * @param {string} file - Name of the file in the directory.     * @return {languages} - Instnace of this class to chain.     */    add(name, file) {        if (typeof(name) !== "string") {            throw new TypeError("Name of the language must be sting.");        }        if (typeof(file) !== "string") {            throw new TypeError("File on in the directory must be string.");        }        if (this.#libs.has(name)) {            console.error("Language \"" + name + "\" already loaded.");            console.error("It could not being loaded twice.");            return this;        }        if (!this.#valid_locale(name)) {            console.error("Language name \"" + name + "\" invalid formated.");            console.error("It could not being loaded.")            return this;        }                this.#libs.set(name, file);        return this;    }    /**     * This load all phrasebook given in the index file. Index must be     * JSON file, which contain one object. That object properties must be     * languages names in the notation like in add function. Valus of that     * properties musts being strings which contains names of the phrasebook     * files in the path directory.     *      * @example ``` { "pl_PL": "polish.json", "en_US": "english.json" } ```     *      * @see add     *      * @param {string} index - Index file in the phrasebook directory.     * @return {languages} - New languages instance with loaded index.     */    async load(index) {        if (typeof(index) !== "string") {            throw new TypeError("Name of index file is not string.");        }        const response = await this.#load_index(index);        const result = new languages(this.#path, this.#local);                Object.keys(response).forEach(name => {            if (typeof(name) !== "string") {                console.error("Name of the language must be string.");                console.error("Check languages index.");                console.error("Skipping it.")                return;            }            if (typeof(response[name]) !== "string") {                console.error("Name of phrasebook file must be string.");                console.error("Check languages index.");                console.error("Skipping it.");                return;            }            result.add(name, response[name]);        });        return result;    }    /**     * This load index object. That check, and when content must be loaded     * from local filesystem, it use node:fs, or when it must be fetched from     * remote, then use fetch API.     *      * @param {string} index - Name of the index file in library.      * @returns {object} - Loaded index file content.      */    async #load_index(index) {        const path = this.#full_path(index);        if (this.#local) {            let fs = null;            NODE: fs = require("node:fs/promises");                        if (fs === null) {                throw new Error("Could not use ndoe:fs in browser.");            }            return JSON.parse(                await fs.readFile(path, { encoding: "utf-8" })            );        }        const request = await fetch(path);        return await request.json();    }    /**     * This check that language exists in languages library.     *      * @param {string} name - Name of the language to check.     * @return {bool} - True when language exists, false when not     */    has(name) {        return this.#libs.has(name);    }    /**     * This return all avairable languages.     *      * @return {Array} - List of all avairable languages.     */    get avairable() {        const alls = new Array();        this.#libs.keys().forEach(name => {            alls.push(name);        });        return alls;    }    /**     * @returns {string} - Default      */    get default() {        const avairable = this.avairable;        if (avairable.length === 0) {            throw new Error("Languages list is empty. Can not load default.");        }        return avairable[0];    }    /**     * This load phrasebook with give name.     *      * @throws {TypeError} - Param type is not correct.     * @throws {RangeError} - Language not exists in libs.     *      * @param {string} name - Name of the language to load.      * @returns {phrasebook} - Phrasebook loaded from the file.     */    select(name) {        if (typeof(name) !== "string") {            throw new TypeError("Name of the language must be string.");        }        if (!this.has(name)) {            DEBUG: throw new RangeError(                "Not found language \"" + name + "\"."            );            return new phrasebook(new Map());        }                const file = this.#libs.get(name);        const path = this.#full_path(file);        return new loader(path, this.#local).load();    }    /**     * This return full path to the file.     *      * @param {string} name - Name of the file to get its path     * @return {string} - Full path of the file     */    #full_path(name) {        let glue = "/";        if (this.#path[this.#path.length - 1] === glue) {            glue = "";        }        return this.#path + glue + name;    }    /**     * This check that format is valid POSIX like locale.     *      * @param {string} name - Name to check format of.     * @return {bool} - True when format is valid, false when not.     */    #valid_locale(name) {        const splited = name.split("_");        if (splited.length !== 2) {            return false;        }        const first = splited[0];        const second = splited[1];        if (first.toLowerCase() !== first || first.length !== 2) {            return false;        }        if (second.toUpperCase() !== second || second.length !== 2) {            return false;        }        return true;    }}exports.languages = languages;
 |