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.

422 lines (421 loc) 15 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); 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 var src_exports = {}; __export(src_exports, { SheetManager: () => SheetManager }); module.exports = __toCommonJS(src_exports); var import_google_spreadsheet = require("google-spreadsheet"); var import_google_auth_library = require("google-auth-library"); var import_fs = __toESM(require("fs")); var import_path = __toESM(require("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 import_google_auth_library.JWT({ email: clientEmail, key: privateKey, scopes: SCOPES }); this.doc = new import_google_spreadsheet.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 (!import_fs.default.existsSync(directoryPath)) { import_fs.default.mkdirSync(directoryPath, { recursive: true }); } Object.keys(data).forEach((key) => { const fileName = `${key}.json`; const filePath = import_path.default.join(directoryPath, fileName); import_fs.default.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 (!import_fs.default.existsSync(directoryPath)) { throw new Error(`Directory not found: ${directoryPath}`); } const result = {}; const files = import_fs.default.readdirSync(directoryPath).filter((f) => f.endsWith(".json")); for (const file of files) { const language = file.replace(".json", ""); const filePath = import_path.default.join(directoryPath, file); const content = import_fs.default.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 (import_fs.default.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] || ""]) ) })); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { SheetManager }); //# sourceMappingURL=index.js.map