@el-j/google-sheet-translations 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +58 -55
  2. package/dist/action-entrypoint.d.ts +2 -0
  3. package/dist/action-entrypoint.d.ts.map +1 -0
  4. package/dist/esm/index.js +1226 -0
  5. package/dist/esm/package.json +1 -0
  6. package/dist/index.d.ts +8 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1288 -41
  9. package/dist/utils/dataConverter/findLocalChanges.d.ts +10 -1
  10. package/dist/utils/dataConverter/findLocalChanges.d.ts.map +1 -1
  11. package/dist/utils/localeNormalizer.d.ts +53 -2
  12. package/dist/utils/localeNormalizer.d.ts.map +1 -1
  13. package/dist/utils/spreadsheetUpdater.d.ts +6 -1
  14. package/dist/utils/spreadsheetUpdater.d.ts.map +1 -1
  15. package/dist/utils/translationHelpers.d.ts +107 -0
  16. package/dist/utils/translationHelpers.d.ts.map +1 -0
  17. package/package.json +21 -14
  18. package/dist/constants.js +0 -9
  19. package/dist/constants.js.map +0 -1
  20. package/dist/getSpreadSheetData.js +0 -170
  21. package/dist/getSpreadSheetData.js.map +0 -1
  22. package/dist/index.js.map +0 -1
  23. package/dist/types.js +0 -3
  24. package/dist/types.js.map +0 -1
  25. package/dist/utils/auth.js +0 -23
  26. package/dist/utils/auth.js.map +0 -1
  27. package/dist/utils/configurationHandler.js +0 -29
  28. package/dist/utils/configurationHandler.js.map +0 -1
  29. package/dist/utils/dataConverter/convertFromDataJsonFormat.js +0 -47
  30. package/dist/utils/dataConverter/convertFromDataJsonFormat.js.map +0 -1
  31. package/dist/utils/dataConverter/convertToDataJsonFormat.js +0 -51
  32. package/dist/utils/dataConverter/convertToDataJsonFormat.js.map +0 -1
  33. package/dist/utils/dataConverter/findLocalChanges.js +0 -39
  34. package/dist/utils/dataConverter/findLocalChanges.js.map +0 -1
  35. package/dist/utils/fileWriter.js +0 -119
  36. package/dist/utils/fileWriter.js.map +0 -1
  37. package/dist/utils/getFileLastModified.js +0 -23
  38. package/dist/utils/getFileLastModified.js.map +0 -1
  39. package/dist/utils/isDataJsonNewer.js +0 -40
  40. package/dist/utils/isDataJsonNewer.js.map +0 -1
  41. package/dist/utils/localeFilter.js +0 -49
  42. package/dist/utils/localeFilter.js.map +0 -1
  43. package/dist/utils/localeNormalizer.js +0 -145
  44. package/dist/utils/localeNormalizer.js.map +0 -1
  45. package/dist/utils/publicSheetReader.js +0 -109
  46. package/dist/utils/publicSheetReader.js.map +0 -1
  47. package/dist/utils/rateLimiter.js +0 -55
  48. package/dist/utils/rateLimiter.js.map +0 -1
  49. package/dist/utils/readDataJson.js +0 -29
  50. package/dist/utils/readDataJson.js.map +0 -1
  51. package/dist/utils/sheetProcessor.js +0 -121
  52. package/dist/utils/sheetProcessor.js.map +0 -1
  53. package/dist/utils/spreadsheetCreator.js +0 -121
  54. package/dist/utils/spreadsheetCreator.js.map +0 -1
  55. package/dist/utils/spreadsheetUpdater.js +0 -194
  56. package/dist/utils/spreadsheetUpdater.js.map +0 -1
  57. package/dist/utils/syncManager.js +0 -62
  58. package/dist/utils/syncManager.js.map +0 -1
  59. package/dist/utils/validateEnv.js +0 -41
  60. package/dist/utils/validateEnv.js.map +0 -1
  61. package/dist/utils/wait.js +0 -19
  62. package/dist/utils/wait.js.map +0 -1
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.withRetry = withRetry;
4
- const promises_1 = require("node:timers/promises");
5
- const DEFAULT_RETRIES = 3;
6
- const DEFAULT_MAX_DELAY_MS = 30000;
7
- /**
8
- * Returns true when the error looks like a Google Sheets API rate-limit or
9
- * transient server error (HTTP 429 or 503).
10
- */
11
- function isRateLimitError(err) {
12
- if (!err || typeof err !== 'object')
13
- return false;
14
- const e = err;
15
- const response = e['response'];
16
- const status = typeof e['status'] === 'number'
17
- ? e['status']
18
- : typeof response?.['status'] === 'number'
19
- ? response['status']
20
- : undefined;
21
- return status === 429 || status === 503;
22
- }
23
- /**
24
- * Calls `fn` and, on a rate-limit error (HTTP 429 / 503), retries with
25
- * exponential back-off. Any other error is re-thrown immediately.
26
- *
27
- * @param fn - The async operation to execute (and potentially retry)
28
- * @param label - Human-readable label used in warning logs
29
- * @param baseDelayMs - Base back-off delay in milliseconds (default: 1 000)
30
- * @param retries - Maximum number of retry attempts (default: 3)
31
- * @param maxDelayMs - Back-off ceiling in milliseconds (default: 30 000)
32
- * @returns The resolved value of `fn`
33
- * @throws The last error if all retries are exhausted, or any non-rate-limit error immediately
34
- */
35
- async function withRetry(fn, label, baseDelayMs = 1000, retries = DEFAULT_RETRIES, maxDelayMs = DEFAULT_MAX_DELAY_MS) {
36
- for (let attempt = 0; attempt <= retries; attempt++) {
37
- try {
38
- return await fn();
39
- }
40
- catch (err) {
41
- if (!isRateLimitError(err) || attempt === retries)
42
- throw err;
43
- const backoff = Math.min(baseDelayMs * 2 ** attempt, maxDelayMs);
44
- console.warn(`[rate-limit] ${label}: retry ${attempt + 1}/${retries} in ${backoff} ms`);
45
- await (0, promises_1.setTimeout)(backoff);
46
- }
47
- }
48
- // The loop above always returns (on success) or throws (on exhausted retries or
49
- // non-rate-limit errors). This line is here solely to satisfy TypeScript's
50
- // control-flow analysis — it cannot actually be reached at runtime.
51
- /* c8 ignore next */
52
- throw new Error('withRetry: unreachable');
53
- }
54
- exports.default = withRetry;
55
- //# sourceMappingURL=rateLimiter.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rateLimiter.js","sourceRoot":"","sources":["../../src/utils/rateLimiter.ts"],"names":[],"mappings":";;AAkCA,8BAwBC;AA1DD,mDAA2D;AAE3D,MAAM,eAAe,GAAG,CAAC,CAAC;AAC1B,MAAM,oBAAoB,GAAG,KAAM,CAAC;AAEpC;;;GAGG;AACH,SAAS,gBAAgB,CAAC,GAAY;IACrC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAClD,MAAM,CAAC,GAAG,GAA8B,CAAC;IACzC,MAAM,QAAQ,GAAG,CAAC,CAAC,UAAU,CAAwC,CAAC;IACtE,MAAM,MAAM,GACX,OAAO,CAAC,CAAC,QAAQ,CAAC,KAAK,QAAQ;QAC9B,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACb,CAAC,CAAC,OAAO,QAAQ,EAAE,CAAC,QAAQ,CAAC,KAAK,QAAQ;YACzC,CAAC,CAAE,QAAQ,CAAC,QAAQ,CAAY;YAChC,CAAC,CAAC,SAAS,CAAC;IACf,OAAO,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;GAWG;AACI,KAAK,UAAU,SAAS,CAC9B,EAAoB,EACpB,KAAa,EACb,WAAW,GAAG,IAAK,EACnB,OAAO,GAAG,eAAe,EACzB,UAAU,GAAG,oBAAoB;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC;YACJ,OAAO,MAAM,EAAE,EAAE,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,OAAO,KAAK,OAAO;gBAAE,MAAM,GAAG,CAAC;YAC7D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,GAAG,CAAC,IAAI,OAAO,EAAE,UAAU,CAAC,CAAC;YACjE,OAAO,CAAC,IAAI,CACX,gBAAgB,KAAK,WAAW,OAAO,GAAG,CAAC,IAAI,OAAO,OAAO,OAAO,KAAK,CACzE,CAAC;YACF,MAAM,IAAA,qBAAK,EAAC,OAAO,CAAC,CAAC;QACtB,CAAC;IACF,CAAC;IACD,gFAAgF;IAChF,4EAA4E;IAC5E,oEAAoE;IACpE,oBAAoB;IACpB,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;AAC3C,CAAC;AAED,kBAAe,SAAS,CAAC"}
@@ -1,29 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.readDataJson = readDataJson;
7
- const node_fs_1 = __importDefault(require("node:fs"));
8
- const convertFromDataJsonFormat_1 = require("../utils/dataConverter/convertFromDataJsonFormat");
9
- /**
10
- * Reads and parses the languageData.json file
11
- * @param dataJsonPath - Path to languageData.json
12
- * @returns Parsed languageData.json contents, or null if file doesn't exist or is invalid
13
- */
14
- function readDataJson(dataJsonPath) {
15
- try {
16
- if (!node_fs_1.default.existsSync(dataJsonPath)) {
17
- return null;
18
- }
19
- const dataJsonContent = node_fs_1.default.readFileSync(dataJsonPath, 'utf8');
20
- const dataJson = JSON.parse(dataJsonContent);
21
- // Convert from languageData.json format to TranslationData
22
- return (0, convertFromDataJsonFormat_1.convertFromDataJsonFormat)(dataJson);
23
- }
24
- catch (error) {
25
- console.warn("Error reading or parsing languageData.json:", error);
26
- return null;
27
- }
28
- }
29
- //# sourceMappingURL=readDataJson.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"readDataJson.js","sourceRoot":"","sources":["../../src/utils/readDataJson.ts"],"names":[],"mappings":";;;;;AASA,oCAeC;AAxBD,sDAAyB;AAEzB,gGAA6F;AAE7F;;;;GAIG;AACH,SAAgB,YAAY,CAAC,YAAoB;IAChD,IAAI,CAAC;QACJ,IAAI,CAAC,iBAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACb,CAAC;QAED,MAAM,eAAe,GAAG,iBAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAE7C,2DAA2D;QAC3D,OAAO,IAAA,qDAAyB,EAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,OAAO,CAAC,IAAI,CAAC,6CAA6C,EAAE,KAAK,CAAC,CAAC;QACnE,OAAO,IAAI,CAAC;IACb,CAAC;AACF,CAAC"}
@@ -1,121 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.processRawRows = processRawRows;
4
- exports.processSheet = processSheet;
5
- const rateLimiter_1 = require("./rateLimiter");
6
- const localeFilter_1 = require("./localeFilter");
7
- const localeNormalizer_1 = require("./localeNormalizer");
8
- /**
9
- * Core row-processing logic shared by both the authenticated and public sheet paths.
10
- * Accepts pre-fetched rows (as plain objects) and returns the same
11
- * {@link SheetProcessingResult} shape that `processSheet` produces.
12
- * Contains only pure computation — no API calls are made here.
13
- *
14
- * @param rows - Array of row objects keyed by column header
15
- * @param sheetTitle - The sheet tab name (used as namespace key in translations)
16
- * @returns Processing result containing translations and locales
17
- */
18
- async function processRawRows(rows, sheetTitle) {
19
- const result = {
20
- translations: {},
21
- locales: [],
22
- localeMapping: {},
23
- originalMapping: {},
24
- success: false,
25
- };
26
- try {
27
- if (!rows || rows.length === 0) {
28
- console.warn(`No rows found in sheet "${sheetTitle}"`);
29
- return result;
30
- }
31
- // Extract header information
32
- const headerRow = Object.keys(rows[0]).map((key) => key.toLowerCase());
33
- console.log(`Header row for sheet "${sheetTitle}":`, headerRow);
34
- const keyColumn = headerRow[0];
35
- const validLocales = (0, localeFilter_1.filterValidLocales)(headerRow, keyColumn);
36
- if (validLocales.length === 0) {
37
- console.warn(`No valid locale columns found in sheet "${sheetTitle}"`);
38
- return result;
39
- }
40
- // Create locale mapping for normalization
41
- const originalHeaders = Object.keys(rows[0]); // Keep original case
42
- const { normalizedLocales, localeMapping, originalMapping } = (0, localeNormalizer_1.createLocaleMapping)(originalHeaders, keyColumn);
43
- // Store the mappings in the result
44
- result.localeMapping = localeMapping;
45
- result.originalMapping = originalMapping;
46
- // Process each normalized locale
47
- for (const normalizedLocale of normalizedLocales) {
48
- // Find the original header for this normalized locale
49
- const originalHeader = localeMapping[normalizedLocale];
50
- if (!originalHeader)
51
- continue;
52
- const languageCells = rows.map((row) => {
53
- // Look for the key column (case-insensitive)
54
- const keyField = Object.keys(row).find((k) => k.toLowerCase() === keyColumn);
55
- if (!keyField || !row[keyField] || !row[originalHeader]) {
56
- return {}; // Skip rows without key or translation
57
- }
58
- const rowLocal = {};
59
- // Convert key to lowercase
60
- rowLocal[row[keyField].toString().toLowerCase()] = row[originalHeader];
61
- return rowLocal;
62
- });
63
- // Filter out empty objects before combining
64
- const nonEmptyLanguageCells = languageCells.filter((cell) => Object.keys(cell).length > 0);
65
- // Combine all keys into one object
66
- const prepareObj = {};
67
- prepareObj[sheetTitle] = nonEmptyLanguageCells.reduce((acc, cell) => Object.assign(acc, cell), {});
68
- // Use normalized locale as the key in translations
69
- if (result.translations[normalizedLocale]) {
70
- result.translations[normalizedLocale] = {
71
- ...result.translations[normalizedLocale],
72
- ...prepareObj,
73
- };
74
- }
75
- else {
76
- result.translations[normalizedLocale] = { ...prepareObj };
77
- }
78
- }
79
- result.locales = normalizedLocales;
80
- result.success = true;
81
- }
82
- catch (error) {
83
- console.error(`Error processing sheet "${sheetTitle}":`, error);
84
- }
85
- return result;
86
- }
87
- /**
88
- * Fetches rows from a Google Sheet and extracts translation data.
89
- * The underlying `getRows` API call is automatically retried on rate-limit
90
- * responses (HTTP 429 / 503) using exponential back-off.
91
- *
92
- * @param sheet The Google Spreadsheet worksheet to process
93
- * @param sheetTitle The title of the sheet being processed
94
- * @param rowLimit Maximum number of rows to fetch
95
- * @param baseDelayMs Base back-off delay in ms for retrying rate-limited requests
96
- * @returns Processing result containing translations and locales
97
- */
98
- async function processSheet(sheet, sheetTitle, rowLimit, baseDelayMs = 1000) {
99
- const emptyResult = {
100
- translations: {},
101
- locales: [],
102
- localeMapping: {},
103
- originalMapping: {},
104
- success: false,
105
- };
106
- try {
107
- const googleRows = await (0, rateLimiter_1.withRetry)(() => sheet.getRows({ limit: rowLimit }), `getRows: ${sheetTitle}`, baseDelayMs);
108
- if (!googleRows || googleRows.length === 0) {
109
- console.warn(`No rows found in sheet "${sheetTitle}"`);
110
- return emptyResult;
111
- }
112
- // Convert GoogleSpreadsheetRow objects to plain SheetRow objects, then reuse shared logic
113
- const rows = googleRows.map((row) => row.toObject());
114
- return processRawRows(rows, sheetTitle);
115
- }
116
- catch (error) {
117
- console.error(`Error processing sheet "${sheetTitle}":`, error);
118
- return emptyResult;
119
- }
120
- }
121
- //# sourceMappingURL=sheetProcessor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sheetProcessor.js","sourceRoot":"","sources":["../../src/utils/sheetProcessor.ts"],"names":[],"mappings":"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"}
@@ -1,121 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createSpreadsheet = createSpreadsheet;
4
- const google_spreadsheet_1 = require("google-spreadsheet");
5
- const rateLimiter_1 = require("./rateLimiter");
6
- /** Column index (0-based) → spreadsheet letter (A, B…Z, AA, AB…). */
7
- function colLetter(index) {
8
- let result = '';
9
- let i = index;
10
- do {
11
- // Each digit in base-26 maps to a letter A–Z
12
- result = String.fromCharCode(65 + (i % 26)) + result;
13
- // Move to the next more-significant "digit", adjusting for 1-based indexing
14
- i = Math.floor(i / 26) - 1;
15
- } while (i >= 0);
16
- return result;
17
- }
18
- const DEFAULT_TARGET_LOCALES = ['de', 'fr', 'es', 'it', 'pt', 'ja', 'zh'];
19
- const STARTER_KEYS = {
20
- 'app.name': 'My App',
21
- 'app.description': 'A great application',
22
- 'nav.home': 'Home',
23
- 'nav.about': 'About',
24
- 'nav.contact': 'Contact',
25
- 'common.save': 'Save',
26
- 'common.cancel': 'Cancel',
27
- 'common.loading': 'Loading…',
28
- 'common.error': 'An error occurred',
29
- 'common.success': 'Success!',
30
- };
31
- /**
32
- * Creates a new Google Spreadsheet seeded with translation starter content and
33
- * GOOGLETRANSLATE formulas for every non-source locale.
34
- *
35
- * Returns the spreadsheet ID and URL.
36
- */
37
- async function createSpreadsheet(authClient, options = {}) {
38
- const { title = 'google-sheet-translations', sourceLocale = 'en', targetLocales = DEFAULT_TARGET_LOCALES, seedKeys = STARTER_KEYS, } = options;
39
- // ── Step 1: Create the spreadsheet via the Sheets REST API ─────────────
40
- // google-spreadsheet v4 does not expose a static create method, so we use
41
- // the underlying JWT client to make a direct API call.
42
- await authClient.authorize();
43
- const createRes = await (0, rateLimiter_1.withRetry)(() => authClient.request({
44
- url: 'https://sheets.googleapis.com/v4/spreadsheets',
45
- method: 'POST',
46
- data: {
47
- properties: { title },
48
- sheets: [
49
- { properties: { title: '__welcome__', index: 0 } },
50
- { properties: { title: 'i18n', index: 1 } },
51
- ],
52
- },
53
- }), 'createSpreadsheet');
54
- const spreadsheetId = createRes.data.spreadsheetId;
55
- const url = `https://docs.google.com/spreadsheets/d/${spreadsheetId}/edit`;
56
- // ── Step 2: Open the spreadsheet with google-spreadsheet ───────────────
57
- const doc = new google_spreadsheet_1.GoogleSpreadsheet(spreadsheetId, authClient);
58
- await (0, rateLimiter_1.withRetry)(() => doc.loadInfo(), 'loadInfo after create');
59
- // ── Step 3: Populate __welcome__ sheet ─────────────────────────────────
60
- const welcomeSheet = doc.sheetsByTitle['__welcome__'];
61
- if (welcomeSheet) {
62
- await (0, rateLimiter_1.withRetry)(() => welcomeSheet.loadCells('A1:B20'), 'loadCells welcome');
63
- const lines = [
64
- ['📊 Google Sheet Translations', ''],
65
- ['', ''],
66
- ['Package:', '@el-j/google-sheet-translations'],
67
- ['Docs:', 'https://el-j.github.io/google-sheet-translations/'],
68
- ['', ''],
69
- ['Your Spreadsheet ID:', spreadsheetId],
70
- ['URL:', url],
71
- ['', ''],
72
- ['Add to your .env file:', ''],
73
- ['GOOGLE_SPREADSHEET_ID=' + spreadsheetId, ''],
74
- ['', ''],
75
- ['How this works:', ''],
76
- ['1. The "i18n" sheet (and any other sheet you add) holds your translation keys.', ''],
77
- ['2. Column A = key, Column B = source language, other columns = auto-translated.', ''],
78
- ['3. Run getSpreadSheetData([\'i18n\']) in your project to sync to local files.', ''],
79
- ['4. Add more sheets for different pages/features of your app.', ''],
80
- ['5. Use syncLocalChanges: true (default) to push new keys back to this spreadsheet.', ''],
81
- ];
82
- for (let r = 0; r < lines.length; r++) {
83
- for (let c = 0; c < lines[r].length; c++) {
84
- const cell = welcomeSheet.getCell(r, c);
85
- cell.value = lines[r][c];
86
- }
87
- }
88
- await (0, rateLimiter_1.withRetry)(() => welcomeSheet.saveUpdatedCells(), 'saveWelcome');
89
- }
90
- // ── Step 4: Populate i18n sheet with headers + GOOGLETRANSLATE rows ────
91
- const i18nSheet = doc.sheetsByTitle['i18n'];
92
- if (i18nSheet) {
93
- const allLocales = [sourceLocale, ...targetLocales];
94
- // Row 1 = headers
95
- await (0, rateLimiter_1.withRetry)(() => i18nSheet.setHeaderRow(['key', ...allLocales]), 'setHeaderRow');
96
- // Build rows: source column has real text; other columns get GOOGLETRANSLATE formula.
97
- // Source is always column B (index 1 after 'key')
98
- const sourceColLetter = colLetter(1); // B
99
- const rows = Object.entries(seedKeys).map(([key, sourceValue]) => {
100
- const row = { key, [sourceLocale]: sourceValue };
101
- targetLocales.forEach((locale, idx) => {
102
- const targetColLetter = colLetter(2 + idx); // C, D, E, …
103
- row[locale] =
104
- `=GOOGLETRANSLATE(INDIRECT("${sourceColLetter}"&ROW());$${sourceColLetter}$1;${targetColLetter}$1)`;
105
- });
106
- return row;
107
- });
108
- await (0, rateLimiter_1.withRetry)(() => i18nSheet.addRows(rows), 'addRows i18n');
109
- }
110
- console.log('');
111
- console.log('✅ New spreadsheet created!');
112
- console.log(` Title : ${title}`);
113
- console.log(` URL : ${url}`);
114
- console.log(` ID : ${spreadsheetId}`);
115
- console.log('');
116
- console.log(' Add this to your .env file (or environment):');
117
- console.log(` GOOGLE_SPREADSHEET_ID=${spreadsheetId}`);
118
- console.log('');
119
- return { spreadsheetId, url };
120
- }
121
- //# sourceMappingURL=spreadsheetCreator.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spreadsheetCreator.js","sourceRoot":"","sources":["../../src/utils/spreadsheetCreator.ts"],"names":[],"mappings":"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"}
@@ -1,194 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateSpreadsheetWithLocalChanges = updateSpreadsheetWithLocalChanges;
4
- const rateLimiter_1 = require("./rateLimiter");
5
- const localeNormalizer_1 = require("./localeNormalizer");
6
- /** Converts a 0-based column index to a spreadsheet column letter (A, B, ..., Z, AA, AB, ...) */
7
- function columnIndexToLetter(index) {
8
- let result = '';
9
- let i = index;
10
- do {
11
- result = String.fromCharCode(65 + (i % 26)) + result;
12
- i = Math.floor(i / 26) - 1;
13
- } while (i >= 0);
14
- return result;
15
- }
16
- /**
17
- * Updates the Google Spreadsheet with new keys from local data
18
- *
19
- * When autoTranslate is enabled:
20
- * - For each new key added to the spreadsheet, the system checks which languages have translations
21
- * - For languages missing translations, it automatically adds Google Translate formulas
22
- * - The formula format is: =GOOGLETRANSLATE(INDIRECT(sourceColumn&ROW());$sourceColumn$1;targetColumn$1)
23
- * - This dynamic formula uses cell references for language codes and automatically adapts to the correct row
24
- *
25
- * Example:
26
- * If a new key "welcome" has an English translation in column B but no German translation in column C,
27
- * and autoTranslate is enabled, the system will add:
28
- * =GOOGLETRANSLATE(INDIRECT("B"&ROW());$B$1;C$1) to the German column
29
- *
30
- * @param doc - The Google Spreadsheet instance
31
- * @param changes - Object containing new keys to add to the spreadsheet
32
- * @param waitSeconds - Base back-off delay in seconds for retrying rate-limited API calls
33
- * @param autoTranslate - Whether to add Google Translate formulas for missing translations (default: false)
34
- * @param localeMapping - Mapping from normalized locale codes to original spreadsheet headers
35
- * @returns Promise that resolves when the update is complete
36
- */
37
- async function updateSpreadsheetWithLocalChanges(doc, changes, waitSeconds, autoTranslate = false, localeMapping = {}) {
38
- console.log("Updating spreadsheet with local changes...");
39
- const baseDelayMs = waitSeconds * 1000;
40
- // Process each sheet in the changes object
41
- for (const sheetTitle of new Set(Object.values(changes).flatMap(locale => Object.keys(locale)))) {
42
- console.log(`Processing sheet: ${sheetTitle}`);
43
- const sheet = doc.sheetsByTitle[sheetTitle];
44
- if (!sheet) {
45
- console.warn(`Sheet "${sheetTitle}" not found in the document, cannot update`);
46
- continue;
47
- }
48
- // Get all rows from the sheet (retries automatically on rate-limit)
49
- const rows = await (0, rateLimiter_1.withRetry)(() => sheet.getRows(), `getRows: ${sheetTitle}`, baseDelayMs);
50
- if (!rows || rows.length === 0) {
51
- console.warn(`No rows found in sheet "${sheetTitle}", cannot update`);
52
- continue;
53
- }
54
- // Extract header information
55
- const rowObject = rows[0].toObject();
56
- const headerRow = Object.keys(rowObject).map(key => key.toLowerCase());
57
- const keyColumn = headerRow[0]; // First column is the key
58
- // Get all locales from the headerRow except the key column
59
- const locales = headerRow.filter(key => key !== keyColumn);
60
- // Map of existing keys to their row indices
61
- const existingKeys = new Map();
62
- rows.forEach((row, index) => {
63
- const rowData = row.toObject();
64
- const keyField = Object.keys(rowData).find(k => k.toLowerCase() === keyColumn);
65
- if (keyField && rowData[keyField]) {
66
- // Store the key in lowercase for case-insensitive comparison
67
- existingKeys.set(rowData[keyField].toString().toLowerCase(), index);
68
- }
69
- });
70
- // New keys to add to the sheet
71
- const newKeys = new Map();
72
- // Track which locales have values for each new key (for auto-translation)
73
- const keyLocalesMap = new Map();
74
- // Collect all new keys and their translations
75
- for (const locale of Object.keys(changes)) {
76
- if (!changes[locale]?.[sheetTitle])
77
- continue;
78
- const localeData = changes[locale][sheetTitle];
79
- for (const key of Object.keys(localeData)) {
80
- const keyLower = key.toLowerCase();
81
- if (!existingKeys.has(keyLower)) {
82
- if (!newKeys.has(keyLower)) {
83
- newKeys.set(keyLower, { [keyColumn]: key });
84
- // Initialize map for this key's locale values
85
- keyLocalesMap.set(keyLower, new Map());
86
- }
87
- // Find the exact header for this locale using the mapping
88
- let localeHeader = (0, localeNormalizer_1.getOriginalHeaderForLocale)(locale, localeMapping);
89
- // Fallback to case-insensitive search in headerRow if mapping lookup fails
90
- if (!localeHeader) {
91
- localeHeader = headerRow.find(h => h.toLowerCase() === locale.toLowerCase());
92
- }
93
- if (localeHeader) {
94
- const theKey = newKeys.get(keyLower);
95
- if (!theKey) {
96
- console.warn(`Key "${key}" not found in newKeys map, skipping...`);
97
- continue;
98
- }
99
- const value = String(localeData[key]);
100
- // Use the correct header name for the locale value
101
- theKey[localeHeader] = value;
102
- // Store this locale value for potential auto-translation
103
- const localesForKey = keyLocalesMap.get(keyLower);
104
- if (localesForKey) {
105
- localesForKey.set(locale.toLowerCase(), localeHeader);
106
- }
107
- }
108
- }
109
- else {
110
- // Update existing key with new translation
111
- const rowIndex = existingKeys.get(keyLower);
112
- const row = rows[rowIndex];
113
- // Find the exact header for this locale using the mapping
114
- let localeHeader = (0, localeNormalizer_1.getOriginalHeaderForLocale)(locale, localeMapping);
115
- // Fallback to case-insensitive search if mapping lookup fails
116
- if (!localeHeader) {
117
- localeHeader = Object.keys(row.toObject()).find(h => h.toLowerCase() === locale.toLowerCase());
118
- }
119
- if (localeHeader) {
120
- // Use set() method instead of direct property assignment to avoid TS errors
121
- row.set(localeHeader, String(localeData[key]));
122
- try {
123
- await (0, rateLimiter_1.withRetry)(() => row.save(), `save row ${rowIndex} in ${sheetTitle}`, baseDelayMs);
124
- }
125
- catch (err) {
126
- console.error(`Failed to save row for key "${keyLower}" in sheet "${sheetTitle}":`, err);
127
- }
128
- }
129
- }
130
- }
131
- }
132
- // Add new keys to the sheet
133
- if (newKeys.size > 0) {
134
- console.log(`Adding ${newKeys.size} new keys to sheet ${sheetTitle}...`);
135
- // Apply auto-translation for new keys if enabled
136
- if (autoTranslate) {
137
- // Process each new key
138
- for (const [keyLower, rowData] of newKeys.entries()) {
139
- const localesWithValues = keyLocalesMap.get(keyLower);
140
- if (localesWithValues && localesWithValues.size > 0) {
141
- // Pick the first locale with a value as the source for translation
142
- const [, sourceHeader] = [...localesWithValues.entries()][0];
143
- // Find the cell reference for the source value
144
- // In Google Sheets formulas, we need to use column letters (A, B, C...) and row numbers
145
- // For each locale that doesn't have a value, add a GOOGLETRANSLATE formula
146
- for (const localeHeader of locales) {
147
- const localeLower = localeHeader.toLowerCase();
148
- // Skip if this locale already has a value.
149
- // rowData keys may be mixed-case (e.g. "en-GB"), so use a
150
- // case-insensitive lookup instead of a direct key access.
151
- const rowDataKey = Object.keys(rowData).find(k => k.toLowerCase() === localeLower);
152
- if (localesWithValues.has(localeLower) || (rowDataKey && rowData[rowDataKey])) {
153
- continue;
154
- }
155
- // Find the exact case-preserved header name
156
- const exactHeaderName = headerRow.find(h => h.toLowerCase() === localeLower);
157
- if (exactHeaderName) {
158
- // Create Google Translate formula referring to the source column
159
- // Since we don't know the exact row number yet, we'll use a special placeholder
160
- // that will be replaced with the actual cell reference after the rows are added
161
- // headerRow is fully lowercased; normalise sourceHeader to match so
162
- // that mixed-case originals (e.g. "en-GB") are found correctly.
163
- const sourceHeaderIndex = headerRow.indexOf(sourceHeader.toLowerCase());
164
- // Get the column index for the target locale
165
- const targetHeaderIndex = headerRow.indexOf(exactHeaderName);
166
- // Guard against unexpected out-of-range indices
167
- if (sourceHeaderIndex < 0 || targetHeaderIndex < 0) {
168
- continue;
169
- }
170
- const sourceColumnLetter = columnIndexToLetter(sourceHeaderIndex);
171
- const targetColumnLetter = columnIndexToLetter(targetHeaderIndex);
172
- // Create improved Google Translate formula using INDIRECT and cell references
173
- // This formula dynamically references:
174
- // - INDIRECT(sourceColumn&ROW()) for the source text
175
- // - $sourceColumn$1 for the source language code from header
176
- // - targetColumn$1 for the target language code from header
177
- rowData[exactHeaderName] = `=GOOGLETRANSLATE(INDIRECT("${sourceColumnLetter}"&ROW());$${sourceColumnLetter}$1;${targetColumnLetter}$1)`;
178
- }
179
- }
180
- }
181
- }
182
- }
183
- const newRows = Array.from(newKeys.values());
184
- // Add new rows in chunks to keep individual requests manageable
185
- const CHUNK_SIZE = 5;
186
- for (let i = 0; i < newRows.length; i += CHUNK_SIZE) {
187
- const chunk = newRows.slice(i, i + CHUNK_SIZE);
188
- await (0, rateLimiter_1.withRetry)(() => sheet.addRows(chunk), `addRows chunk ${Math.floor(i / CHUNK_SIZE) + 1} in ${sheetTitle}`, baseDelayMs);
189
- }
190
- }
191
- }
192
- console.log("Finished updating spreadsheet with local changes.");
193
- }
194
- //# sourceMappingURL=spreadsheetUpdater.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"spreadsheetUpdater.js","sourceRoot":"","sources":["../../src/utils/spreadsheetUpdater.ts"],"names":[],"mappings":"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"}
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleBidirectionalSync = handleBidirectionalSync;
4
- const findLocalChanges_1 = require("./dataConverter/findLocalChanges");
5
- const spreadsheetUpdater_1 = require("./spreadsheetUpdater");
6
- const isDataJsonNewer_1 = require("./isDataJsonNewer");
7
- const readDataJson_1 = require("./readDataJson");
8
- /**
9
- * Checks if local changes need to be synced to the spreadsheet and performs the sync if needed
10
- * @param doc Google Spreadsheet document
11
- * @param dataJsonPath Path to the languageData.json file
12
- * @param translationsOutputDir Directory containing translation output files
13
- * @param syncLocalChanges Whether sync is enabled
14
- * @param autoTranslate Whether to auto-generate Google Translate formulas
15
- * @param spreadsheetData Current data from the spreadsheet
16
- * @param waitSeconds Time to wait between API calls
17
- * @param localeMapping Mapping from normalized locale codes to original spreadsheet headers
18
- * @returns Sync operation result
19
- */
20
- async function handleBidirectionalSync(doc, dataJsonPath, translationsOutputDir, syncLocalChanges, autoTranslate, spreadsheetData, waitSeconds, localeMapping = {}) {
21
- const result = {
22
- shouldRefresh: false,
23
- hasChanges: false
24
- };
25
- // Check if languageData.json exists and read it
26
- const localData = (0, readDataJson_1.readDataJson)(dataJsonPath);
27
- const dataJsonExists = localData !== null;
28
- // Check if we need to sync local changes to the spreadsheet
29
- const shouldSyncToSheet = syncLocalChanges &&
30
- dataJsonExists &&
31
- (0, isDataJsonNewer_1.isDataJsonNewer)(dataJsonPath, translationsOutputDir);
32
- if (!shouldSyncToSheet || !localData) {
33
- return result;
34
- }
35
- console.log("Local languageData.json is newer than translation files. Checking for changes...");
36
- // Find differences between local data and spreadsheet data
37
- const changes = (0, findLocalChanges_1.findLocalChanges)(localData, spreadsheetData);
38
- // Check if there are any actual changes
39
- const hasChanges = Object.keys(changes).length > 0 &&
40
- Object.keys(changes).some(locale => Object.keys(changes[locale]).length > 0);
41
- if (!hasChanges) {
42
- console.log("No local changes found that need to be synced to the spreadsheet.");
43
- return result;
44
- }
45
- const localesCount = Object.keys(changes).length;
46
- const keysCount = Object.values(changes)
47
- .flatMap(l => Object.values(l))
48
- .flatMap(s => Object.keys(s)).length;
49
- console.log(`Found local changes: ${localesCount} locale(s), ~${keysCount} key(s) to sync to the spreadsheet.`);
50
- // Update the spreadsheet with the changes, passing the autoTranslate option and locale mapping
51
- try {
52
- await (0, spreadsheetUpdater_1.updateSpreadsheetWithLocalChanges)(doc, changes, waitSeconds, autoTranslate, localeMapping);
53
- result.shouldRefresh = true;
54
- result.hasChanges = true;
55
- }
56
- catch (err) {
57
- console.error("Failed to sync local changes to spreadsheet:", err);
58
- // Do not set shouldRefresh; return unchanged result to avoid stale refresh loop
59
- }
60
- return result;
61
- }
62
- //# sourceMappingURL=syncManager.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"syncManager.js","sourceRoot":"","sources":["../../src/utils/syncManager.ts"],"names":[],"mappings":";;AA2BA,0DA6DC;AAtFD,uEAAoE;AACpE,6DAAyE;AACzE,uDAAoD;AACpD,iDAA8C;AAU9C;;;;;;;;;;;GAWG;AACI,KAAK,UAAU,uBAAuB,CAC5C,GAAsB,EACtB,YAAoB,EACpB,qBAA6B,EAC7B,gBAAyB,EACzB,aAAsB,EACtB,eAAgC,EAChC,WAAmB,EACnB,gBAAwC,EAAE;IAE1C,MAAM,MAAM,GAAe;QAC1B,aAAa,EAAE,KAAK;QACpB,UAAU,EAAE,KAAK;KACjB,CAAC;IAEF,gDAAgD;IAChD,MAAM,SAAS,GAAG,IAAA,2BAAY,EAAC,YAAY,CAAC,CAAC;IAC7C,MAAM,cAAc,GAAG,SAAS,KAAK,IAAI,CAAC;IAE1C,4DAA4D;IAC5D,MAAM,iBAAiB,GAAG,gBAAgB;QACzC,cAAc;QACd,IAAA,iCAAe,EAAC,YAAY,EAAE,qBAAqB,CAAC,CAAC;IAEtD,IAAI,CAAC,iBAAiB,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,kFAAkF,CAAC,CAAC;IAEhG,2DAA2D;IAC3D,MAAM,OAAO,GAAG,IAAA,mCAAgB,EAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IAE7D,wCAAwC;IACxC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;QACjD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAClC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CACvC,CAAC;IAEH,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;QACjF,OAAO,MAAM,CAAC;IACf,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;SACtC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;SAC9B,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACtC,OAAO,CAAC,GAAG,CAAC,wBAAwB,YAAY,gBAAgB,SAAS,qCAAqC,CAAC,CAAC;IAEhH,+FAA+F;IAC/F,IAAI,CAAC;QACJ,MAAM,IAAA,sDAAiC,EAAC,GAAG,EAAE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,CAAC,CAAC;QACjG,MAAM,CAAC,aAAa,GAAG,IAAI,CAAC;QAC5B,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,OAAO,CAAC,KAAK,CAAC,8CAA8C,EAAE,GAAG,CAAC,CAAC;QACnE,gFAAgF;IACjF,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC"}