UNPKG

sheets-translate-to-json

Version:

A Node.js library that facilitates the retrieval and conversion of previously translated Google Sheets spreadsheets into structured JSON files, optimizing the management of localized resources.

388 lines (387 loc) 13.2 kB
var __async = (__this, __arguments, generator) => { return new Promise((resolve, reject) => { var fulfilled = (value) => { try { step(generator.next(value)); } catch (e) { reject(e); } }; var rejected = (value) => { try { step(generator.throw(value)); } catch (e) { reject(e); } }; var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected); step((generator = generator.apply(__this, __arguments)).next()); }); }; // src/index.ts import { GoogleSpreadsheet } from "google-spreadsheet"; import { JWT } from "google-auth-library"; import fs from "fs"; import path from "path"; var SheetManager = class { constructor(privateKey, clientEmail, sheetId) { const SCOPES = [ "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive.file" ]; this.jwt = new JWT({ email: clientEmail, key: privateKey, scopes: SCOPES }); this.doc = new GoogleSpreadsheet(sheetId, this.jwt); } init(userPath, sheetNames) { return __async(this, null, function* () { try { yield this.jwt.authorize(); if (sheetNames && sheetNames.length > 0) { const combinedData = {}; for (const sheetName of sheetNames) { const data = yield this.readByName(sheetName); if (data && Object.keys(data).length > 0) { Object.keys(data).forEach((language) => { if (!combinedData[language]) { combinedData[language] = {}; } this.mergeNestedObjects(combinedData[language], data[language]); }); } else { console.warn(`No data found in sheet: ${sheetName}`); } } if (Object.keys(combinedData).length > 0) { this.write(combinedData, userPath); } } else { const allSheetsData = yield this.readAllSheets(); const combinedData = {}; Object.keys(allSheetsData).forEach((sheetName) => { const sheetData = allSheetsData[sheetName]; Object.keys(sheetData).forEach((language) => { if (!combinedData[language]) { combinedData[language] = {}; } this.mergeNestedObjects(combinedData[language], sheetData[language]); }); }); if (Object.keys(combinedData).length > 0) { this.write(combinedData, userPath); } else { console.error("No data found in the sheet"); } } } catch (err) { console.error("Error during initialization:", err); } }); } read(sheetPosition = 0) { return __async(this, null, function* () { if (sheetPosition < 0) { sheetPosition = 0; } yield this.doc.loadInfo(); const sheet = this.doc.sheetsByIndex[sheetPosition]; return this.processSheet(sheet); }); } readByName(sheetName) { return __async(this, null, function* () { yield this.doc.loadInfo(); const sheet = this.doc.sheetsByTitle[sheetName]; if (!sheet) { throw new Error(`Sheet with name "${sheetName}" not found`); } return this.processSheet(sheet); }); } readAllSheets() { return __async(this, null, function* () { yield this.doc.loadInfo(); const allData = {}; for (const sheet of this.doc.sheetsByIndex) { try { const data = yield this.processSheet(sheet); if (data && Object.keys(data).length > 0) { allData[sheet.title] = data; } } catch (error) { console.error(`Error processing sheet "${sheet.title}":`, error); } } return allData; }); } processSheet(sheet) { return __async(this, null, function* () { yield sheet.loadHeaderRow(); const colTitles = sheet.headerValues; const rows = yield sheet.getRows({ limit: sheet.rowCount }); const result = {}; rows.forEach((row) => { const keyValue = row.get(colTitles[0]); if (!keyValue || keyValue.trim() === "") { return; } colTitles.slice(1).forEach((title) => { const key = keyValue.trim(); const value = row.get(title); const cleanValue = value && value.trim() !== "" ? value.trim() : void 0; if (!result[title]) { result[title] = {}; } if (key.includes(".")) { this.setNestedValue(result[title], key, cleanValue); } else { result[title][key] = cleanValue; } }); }); return result; }); } setNestedValue(obj, keyPath, value) { const keys = keyPath.split("."); let current = obj; for (let i = 0; i < keys.length - 1; i++) { const key = keys[i]; if (!current[key] || typeof current[key] !== "object") { current[key] = {}; } current = current[key]; } const finalKey = keys[keys.length - 1]; current[finalKey] = value; } mergeNestedObjects(target, source) { for (const key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { const sourceValue = source[key]; const targetValue = target[key]; if (typeof sourceValue === "object" && sourceValue !== null && typeof targetValue === "object" && targetValue !== null) { this.mergeNestedObjects(targetValue, sourceValue); } else { target[key] = sourceValue; } } } } write(data, directoryPath) { if (!fs.existsSync(directoryPath)) { fs.mkdirSync(directoryPath, { recursive: true }); } Object.keys(data).forEach((key) => { const fileName = `${key}.json`; const filePath = path.join(directoryPath, fileName); fs.writeFile(filePath, JSON.stringify(data[key], null, 2), (err) => { if (err) { console.error(`Error writing file ${filePath}:`, err); return; } console.log(`File written: ${filePath}`); }); }); } // Méthode utilitaire pour lister toutes les feuilles disponibles listSheets() { return __async(this, null, function* () { yield this.doc.loadInfo(); return this.doc.sheetsByIndex.map((sheet) => sheet.title); }); } readLocal(directoryPath) { if (!fs.existsSync(directoryPath)) { throw new Error(`Directory not found: ${directoryPath}`); } const result = {}; const files = fs.readdirSync(directoryPath).filter((f) => f.endsWith(".json")); for (const file of files) { const language = file.replace(".json", ""); const filePath = path.join(directoryPath, file); const content = fs.readFileSync(filePath, "utf-8"); result[language] = JSON.parse(content); } return result; } push(directoryPath, sheetName) { return __async(this, null, function* () { const localData = this.readLocal(directoryPath); if (Object.keys(localData).length === 0) { console.warn("No JSON files found in directory"); return; } yield this.doc.loadInfo(); let sheet; if (sheetName) { sheet = this.doc.sheetsByTitle[sheetName]; if (!sheet) { sheet = yield this.doc.addSheet({ title: sheetName }); } } else { sheet = this.doc.sheetsByIndex[0]; } const languages = Object.keys(localData); const flatData = this.flattenAllLanguages(localData); yield sheet.clear(); yield sheet.setHeaderRow(["key", ...languages]); const rows = flatData.map((entry) => { const row = { key: entry.key }; for (const lang of languages) { row[lang] = entry.values[lang] || ""; } return row; }); if (rows.length > 0) { yield sheet.addRows(rows); } console.log(`Pushed ${rows.length} keys in ${languages.length} languages to sheet "${sheet.title}"`); }); } sync(_0) { return __async(this, arguments, function* (directoryPath, options = {}) { const { strategy = "merge", sheetName, createSheet = false } = options; yield this.doc.loadInfo(); const result = { added: { local: 0, remote: 0 }, updated: { local: 0, remote: 0 }, languages: [] }; let remoteData; let sheet; if (sheetName) { sheet = this.doc.sheetsByTitle[sheetName]; if (!sheet) { if (createSheet) { sheet = yield this.doc.addSheet({ title: sheetName }); remoteData = {}; } else { throw new Error(`Sheet "${sheetName}" not found. Use createSheet: true to create it.`); } } else { remoteData = yield this.processSheet(sheet); } } else { sheet = this.doc.sheetsByIndex[0]; remoteData = yield this.processSheet(sheet); } let localData = {}; if (fs.existsSync(directoryPath)) { localData = this.readLocal(directoryPath); } const allLanguages = /* @__PURE__ */ new Set([ ...Object.keys(remoteData), ...Object.keys(localData) ]); result.languages = [...allLanguages]; const mergedData = {}; const remoteFlat = this.flattenSheetData(remoteData); const localFlat = this.flattenSheetData(localData); const allKeys = /* @__PURE__ */ new Set([...Object.keys(remoteFlat), ...Object.keys(localFlat)]); for (const compositeKey of allKeys) { const remoteValues = remoteFlat[compositeKey] || {}; const localValues = localFlat[compositeKey] || {}; const isNewLocal = !remoteFlat[compositeKey]; const isNewRemote = !localFlat[compositeKey]; if (isNewLocal) result.added.local++; if (isNewRemote) result.added.remote++; for (const lang of allLanguages) { if (!mergedData[lang]) mergedData[lang] = {}; const remoteVal = remoteValues[lang]; const localVal = localValues[lang]; let finalVal; if (strategy === "local") { finalVal = localVal != null ? localVal : remoteVal; } else if (strategy === "remote") { finalVal = remoteVal != null ? remoteVal : localVal; } else { if (localVal && remoteVal && localVal !== remoteVal) { finalVal = localVal; if (!isNewLocal && !isNewRemote) result.updated.local++; } else { finalVal = localVal != null ? localVal : remoteVal; } } if (finalVal !== void 0) { const keyPath = compositeKey; if (keyPath.includes(".")) { this.setNestedValue(mergedData[lang], keyPath, finalVal); } else { mergedData[lang][keyPath] = finalVal; } } } } this.write(mergedData, directoryPath); const mergedLanguages = Object.keys(mergedData); const flatMerged = this.flattenAllLanguages(mergedData); yield sheet.clear(); yield sheet.setHeaderRow(["key", ...mergedLanguages]); const rows = flatMerged.map((entry) => { const row = { key: entry.key }; for (const lang of mergedLanguages) { row[lang] = entry.values[lang] || ""; } return row; }); if (rows.length > 0) { yield sheet.addRows(rows); } result.updated.remote = flatMerged.length; console.log(`Sync complete: ${result.added.local} new local keys, ${result.added.remote} new remote keys`); return result; }); } flattenNestedObject(obj, prefix = "") { const result = {}; for (const key in obj) { if (!Object.prototype.hasOwnProperty.call(obj, key)) continue; const value = obj[key]; const fullKey = prefix ? `${prefix}.${key}` : key; if (typeof value === "object" && value !== null) { Object.assign(result, this.flattenNestedObject(value, fullKey)); } else if (typeof value === "string") { result[fullKey] = value; } } return result; } flattenSheetData(data) { const result = {}; for (const lang of Object.keys(data)) { const flat = this.flattenNestedObject(data[lang]); for (const key of Object.keys(flat)) { if (!result[key]) result[key] = {}; result[key][lang] = flat[key]; } } return result; } flattenAllLanguages(data) { const languages = Object.keys(data); const allKeys = /* @__PURE__ */ new Set(); const flatByLang = {}; for (const lang of languages) { flatByLang[lang] = this.flattenNestedObject(data[lang]); for (const key of Object.keys(flatByLang[lang])) { allKeys.add(key); } } const sortedKeys = [...allKeys].sort(); return sortedKeys.map((key) => ({ key, values: Object.fromEntries( languages.map((lang) => [lang, flatByLang[lang][key] || ""]) ) })); } }; export { SheetManager }; //# sourceMappingURL=index.mjs.map