@mailbutler/i18n-utils
Version:
Manage i18n localization with static analysis
96 lines (95 loc) • 3.85 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readSrcFiles = readSrcFiles;
exports.extractI18NItemsFromSrcFiles = extractI18NItemsFromSrcFiles;
exports.parseSrcFiles = parseSrcFiles;
const is_valid_glob_1 = __importDefault(require("is-valid-glob"));
const glob_1 = require("glob");
const fs_1 = __importDefault(require("fs"));
function readSrcFiles(src) {
if (!(0, is_valid_glob_1.default)(src)) {
throw new Error(`srcFiles isn't a valid glob pattern.`);
}
if (!Array.isArray(src)) {
src = [src];
}
const targetFiles = src.flatMap((targetFile) => (0, glob_1.globSync)(targetFile)).sort((a, b) => (a > b ? -1 : 1));
if (targetFiles.length === 0) {
throw new Error('srcFiles glob has no files.');
}
return targetFiles.map((f) => {
const fileName = f.replace(process.cwd(), '.');
return { fileName, path: f, content: fs_1.default.readFileSync(f, 'utf8') };
});
}
function* getMatches(file, regExp, captureGroup = 1) {
while (true) {
const match = regExp.exec(file.content);
if (match === null) {
break;
}
const path = match[captureGroup];
const pathAtIndex = file.content.indexOf(path);
const previousCharacter = file.content.charAt(pathAtIndex - 1);
const nextCharacter = file.content.charAt(pathAtIndex + path.length);
const line = (file.content.substring(0, match.index).match(/\n/g) || []).length + 1;
yield {
path,
previousCharacter,
nextCharacter,
file: file.fileName,
line
};
}
}
/**
* Extracts translation keys from methods such as `$t` and `$tc`.
*
* - **regexp pattern**: (?:[$\s.:"'`+\(\[\{]t[cm]?)\(
*
* **description**: Matches the sequence t(, tc( or tm(, optionally with either “$”, SPACE, “.”, “:”, “"”, “'”,
* “`”, "+", "(", "[" or "{" in front of it.
*
* - **regexp pattern**: (["'`])
*
* **description**: 1. capturing group. Matches either “"”, “'”, or “`”.
*
* - **regexp pattern**: ((?:[^\\]|\\.)*?)
*
* **description**: 2. capturing group. Matches anything except a backslash
* *or* matches any backslash followed by any character (e.g. “\"”, “\`”, “\t”, etc.)
*
* - **regexp pattern**: \1
*
* **description**: matches whatever was matched by capturing group 1 (e.g. the starting string character)
*
* @param file a file object
* @returns a list of translation keys found in `file`.
*/
function extractMethodMatches(file) {
const methodRegExp = /(?:[$\s.:"'`+\(\[\{]t[cmt]?)\(\s*?(["'`])((?:[^\\]|\\.)*?)\1/g;
return [...getMatches(file, methodRegExp, 2)];
}
function extractComponentMatches(file) {
const componentRegExp = /(?:<(?:i18n|Translation))(?:.|\n)*?(?:[^:]path=("|'))((?:[^\\]|\\.)*?)\1/gi;
return [...getMatches(file, componentRegExp, 2)];
}
function extractDirectiveMatches(file) {
const directiveRegExp = /v-t="'((?:[^\\]|\\.)*?)'"/g;
return [...getMatches(file, directiveRegExp)];
}
function extractI18NItemsFromSrcFiles(sourceFiles) {
return sourceFiles.reduce((accumulator, file) => {
const methodMatches = extractMethodMatches(file);
const componentMatches = extractComponentMatches(file);
const directiveMatches = extractDirectiveMatches(file);
return [...accumulator, ...methodMatches, ...componentMatches, ...directiveMatches];
}, []);
}
// This is a convenience function for users implementing in their own projects, and isn't used internally
function parseSrcFiles(srcFiles) {
return extractI18NItemsFromSrcFiles(readSrcFiles(srcFiles));
}