lingotags
Version:
[](https://opensource.org/licenses/MIT) [](https://www.npmjs.com/package/lingotags)
232 lines • 9.18 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGlobalKeyCounter = getGlobalKeyCounter;
exports.setGlobalKeyCounter = setGlobalKeyCounter;
exports.escapeJson = escapeJson;
exports.logVerbose = logVerbose;
exports.writeOutputFile = writeOutputFile;
exports.generateUniqueKey = generateUniqueKey;
exports.extractTagContent = extractTagContent;
exports.extractTagName = extractTagName;
exports.getRelativePath = getRelativePath;
exports.shouldProcessElement = shouldProcessElement;
exports.processFileContent = processFileContent;
exports.writeManifest = writeManifest;
exports.revertFromManifest = revertFromManifest;
exports.findMaxExistingKey = findMaxExistingKey;
exports.initializeKeyCounter = initializeKeyCounter;
exports.importTranslations = importTranslations;
exports.exportTranslations = exportTranslations;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
let globalKeyCounter = 0;
function getGlobalKeyCounter() {
return globalKeyCounter;
}
function setGlobalKeyCounter(value) {
globalKeyCounter = value;
}
function escapeJson(str) {
return str
.replace(/"/g, '\\"')
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/\t/g, "\\t");
}
function logVerbose(verbose, message) {
if (verbose) {
console.log(message);
}
}
function writeOutputFile(outputFile, output) {
const relativeOutput = {};
for (const [filePath, matches] of Object.entries(output)) {
const relativePath = getRelativePath(filePath);
relativeOutput[relativePath] = matches;
}
fs_1.default.writeFileSync(outputFile, JSON.stringify(relativeOutput, null, 2), "utf-8");
}
function generateUniqueKey() {
return `unique_key_${++globalKeyCounter}`;
}
function extractTagContent(html) {
// Remove all JSX expressions first (including nested ones)
const withoutJSX = html.replace(/{[^{}]*}/g, "");
// Remove HTML/JSX tags while preserving text content
const withoutTags = withoutJSX.replace(/<[^>]+>/g, "");
// Clean up residual characters and whitespace
return withoutTags
.replace(/["']/g, "") // Remove quotes
.replace(/\s+/g, " ") // Collapse whitespace
.replace(/^ | $/g, "") // Trim edges
.trim();
}
function extractTagName(html) {
// Updated regex to handle JSX tags with namespace prefixes and custom elements
const match = html.match(/<([a-zA-Z0-9-_:]+)(\s|>)/);
return match ? match[1].toLowerCase() : "";
}
function getRelativePath(absolutePath) {
const appIndex = absolutePath.indexOf("app");
if (appIndex !== -1) {
return absolutePath.slice(appIndex).replace(/\\/g, "/");
}
return path_1.default.basename(absolutePath);
}
function shouldProcessElement(rawHtml) {
const staticContent = extractTagContent(rawHtml);
const hasStaticText = staticContent.length > 0;
const hasJSX = /{.*?}/.test(rawHtml);
return hasStaticText && (!hasJSX || (hasJSX && staticContent !== ""));
}
function processFileContent(fileContent, searchPatterns) {
const originalContent = fileContent;
let modifiedContent = fileContent;
const matches = [];
const keyNumberMap = new Map();
// First pass: Find existing keys (for backward compatibility)
searchPatterns.forEach((pattern) => {
const patternMatches = [...fileContent.matchAll(pattern)];
patternMatches.forEach((match) => {
const keyMatch = match[0].match(/data-i18n-key="([^"]+)"/);
if (keyMatch) {
const fullKey = keyMatch[1];
const numberedMatch = fullKey.match(/unique_key_(\d+)/);
if (numberedMatch) {
const keyNum = parseInt(numberedMatch[1], 10);
keyNumberMap.set(fullKey, keyNum);
if (keyNum > globalKeyCounter) {
globalKeyCounter = keyNum;
}
}
const content = extractTagContent(match[0]);
matches.push({
key: fullKey,
tag: extractTagName(match[0]),
content,
});
}
});
});
// Second pass: Process tags that don't already have translation keys
searchPatterns.forEach((pattern) => {
const patternMatches = [...fileContent.matchAll(pattern)];
patternMatches.forEach((match) => {
const tagContent = match[0];
// Skip if this already has a data-i18n-key or t() function
if (tagContent.includes("data-i18n-key=") || tagContent.includes("{t("))
return;
if (!shouldProcessElement(tagContent))
return;
const uniqueKey = generateUniqueKey();
let content = extractTagContent(tagContent)
.replace(/\s{2,}/g, " ")
.trim();
const tagName = extractTagName(tagContent);
if (tagName === "button") {
const buttonContent = extractTagContent(tagContent);
const cleanContent = buttonContent
.replace(/>/g, " ")
.replace(/{.*?}/g, "")
.trim();
content = cleanContent || extractTagContent(tagContent);
}
else {
content = extractTagContent(tagContent);
}
// Extract the inner text part
const innerTextMatch = tagContent.match(/<[^>]*>([\s\S]*?)<\/[^>]*>/);
if (innerTextMatch && innerTextMatch[1]) {
// Replace the inner text with t() function call
const innerText = innerTextMatch[1].trim();
if (innerText) {
const replacement = tagContent.replace(innerText, `{t('${uniqueKey}')}`);
modifiedContent = modifiedContent.replace(tagContent, replacement);
matches.push({
key: uniqueKey,
tag: extractTagName(tagContent),
content,
});
}
}
});
});
return { modifiedContent, matches, originalContent };
}
const DEFAULT_MANIFEST = "lingotags-manifest.json";
function writeManifest(manifestPath = DEFAULT_MANIFEST, initialKeyCounter, fileChanges) {
const manifest = {
initialKeyCounter,
changes: fileChanges.map(({ filePath, original, modified }) => ({
filePath,
originalContent: original,
modifiedContent: modified,
})),
};
fs_1.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
}
function revertFromManifest(manifestPath = "lingotags-manifest.json") {
const resolvedPath = path_1.default.resolve(process.cwd(), manifestPath);
if (!fs_1.default.existsSync(resolvedPath)) {
throw new Error(`Manifest file not found: ${resolvedPath}`);
}
const manifest = JSON.parse(fs_1.default.readFileSync(resolvedPath, "utf-8"));
manifest.changes.forEach(({ filePath, originalContent }) => {
if (fs_1.default.existsSync(filePath)) {
fs_1.default.writeFileSync(filePath, originalContent, "utf-8");
}
});
setGlobalKeyCounter(manifest.initialKeyCounter);
fs_1.default.unlinkSync(resolvedPath);
}
function findMaxExistingKey(content) {
const keyRegex = /unique_key_(\d+)/g;
let maxKey = 0;
let match;
while ((match = keyRegex.exec(content)) !== null) {
const keyNum = parseInt(match[1], 10);
if (keyNum > maxKey)
maxKey = keyNum;
}
return maxKey;
}
function initializeKeyCounter(files) {
globalKeyCounter = 0;
}
function importTranslations(localeFile) {
try {
if (fs_1.default.existsSync(localeFile)) {
const content = fs_1.default.readFileSync(localeFile, 'utf-8');
return JSON.parse(content);
}
}
catch (error) {
console.error(`Error importing translations: ${error.message}`);
}
return {};
}
function exportTranslations(localeFile, translations, merge = true) {
try {
const localeDir = path_1.default.dirname(localeFile);
if (!fs_1.default.existsSync(localeDir)) {
fs_1.default.mkdirSync(localeDir, { recursive: true });
}
let finalTranslations = translations;
// If merge is true and the file exists, merge with existing translations
if (merge && fs_1.default.existsSync(localeFile)) {
const existingTranslations = importTranslations(localeFile);
finalTranslations = {
...existingTranslations,
...translations,
};
}
fs_1.default.writeFileSync(localeFile, JSON.stringify(finalTranslations, null, 2), 'utf-8');
}
catch (error) {
console.error(`Error exporting translations: ${error.message}`);
}
}
//# sourceMappingURL=utils.js.map