/** * utils.js * @file General purpose utilities * @description General purpose utilities. */ const path = require('path'); const fs = require('fs').promises; /** * The general purpose utilities. */ class Utils { /** * @function getLocalizedPath * @description Returns a localized file path accoring to the locale. * * Localized files are searched in subfolders of a given path, e.g. * * root/ * ├── base/ // base path to files * │ ├── example.html // default file * │ └── de/ // de language folder * │ │ └── example.html // de localized file * │ └── de-AT/ // de-AT locale folder * │ │ └── example.html // de-AT localized file * * Files are matched with the locale in the following order: * 1. Locale match, e.g. locale `de-AT` matches file in folder `de-AT`. * 2. Language match, e.g. locale `de-AT` matches file in folder `de`. * 3. Default; file in base folder is returned. * * @param {String} defaultPath The absolute file path, which is also * the default path returned if localization is not available. * @param {String} locale The locale. * @returns {Promise} The object contains: * - `path`: The path to the localized file, or the original path if * localization is not available. * - `subdir`: The subdirectory of the localized file, or undefined if * there is no matching localized file. */ static async getLocalizedPath(defaultPath, locale) { // Get file name and paths const file = path.basename(defaultPath); const basePath = path.dirname(defaultPath); // If locale is not set return default file if (!locale) { return { path: defaultPath }; } // Check file for locale exists const localePath = path.join(basePath, locale, file); const localeFileExists = await Utils.fileExists(localePath); // If file for locale exists return file if (localeFileExists) { return { path: localePath, subdir: locale }; } // Check file for language exists const language = locale.split('-')[0]; const languagePath = path.join(basePath, language, file); const languageFileExists = await Utils.fileExists(languagePath); // If file for language exists return file if (languageFileExists) { return { path: languagePath, subdir: language }; } // Return default file return { path: defaultPath }; } /** * @function fileExists * @description Checks whether a file exists. * @param {String} path The file path. * @returns {Promise} Is true if the file can be accessed, false otherwise. */ static async fileExists(path) { try { await fs.access(path); return true; } catch (e) { return false; } } /** * @function isPath * @description Evaluates whether a string is a file path (as opposed to a URL for example). * @param {String} s The string to evaluate. * @returns {Boolean} Returns true if the evaluated string is a path. */ static isPath(s) { return /(^\/)|(^\.\/)|(^\.\.\/)/.test(s); } /** * Flattens an object and crates new keys with custom delimiters. * @param {Object} obj The object to flatten. * @param {String} [delimiter='.'] The delimiter of the newly generated keys. * @param {Object} result * @returns {Object} The flattened object. **/ static flattenObject(obj, parentKey, delimiter = '.', result = {}) { for (const key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { const newKey = parentKey ? parentKey + delimiter + key : key; if (typeof obj[key] === 'object' && obj[key] !== null) { this.flattenObject(obj[key], newKey, delimiter, result); } else { result[newKey] = obj[key]; } } } return result; } } module.exports = Utils;