@el-j/google-sheet-translations 1.3.3 → 2.0.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 (60) 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 +2 -1
  10. package/dist/utils/dataConverter/findLocalChanges.d.ts.map +1 -1
  11. package/dist/utils/localeNormalizer.d.ts +31 -0
  12. package/dist/utils/localeNormalizer.d.ts.map +1 -1
  13. package/dist/utils/translationHelpers.d.ts +107 -0
  14. package/dist/utils/translationHelpers.d.ts.map +1 -0
  15. package/package.json +21 -14
  16. package/dist/constants.js +0 -9
  17. package/dist/constants.js.map +0 -1
  18. package/dist/getSpreadSheetData.js +0 -170
  19. package/dist/getSpreadSheetData.js.map +0 -1
  20. package/dist/index.js.map +0 -1
  21. package/dist/types.js +0 -3
  22. package/dist/types.js.map +0 -1
  23. package/dist/utils/auth.js +0 -23
  24. package/dist/utils/auth.js.map +0 -1
  25. package/dist/utils/configurationHandler.js +0 -29
  26. package/dist/utils/configurationHandler.js.map +0 -1
  27. package/dist/utils/dataConverter/convertFromDataJsonFormat.js +0 -47
  28. package/dist/utils/dataConverter/convertFromDataJsonFormat.js.map +0 -1
  29. package/dist/utils/dataConverter/convertToDataJsonFormat.js +0 -51
  30. package/dist/utils/dataConverter/convertToDataJsonFormat.js.map +0 -1
  31. package/dist/utils/dataConverter/findLocalChanges.js +0 -70
  32. package/dist/utils/dataConverter/findLocalChanges.js.map +0 -1
  33. package/dist/utils/fileWriter.js +0 -119
  34. package/dist/utils/fileWriter.js.map +0 -1
  35. package/dist/utils/getFileLastModified.js +0 -23
  36. package/dist/utils/getFileLastModified.js.map +0 -1
  37. package/dist/utils/isDataJsonNewer.js +0 -40
  38. package/dist/utils/isDataJsonNewer.js.map +0 -1
  39. package/dist/utils/localeFilter.js +0 -49
  40. package/dist/utils/localeFilter.js.map +0 -1
  41. package/dist/utils/localeNormalizer.js +0 -176
  42. package/dist/utils/localeNormalizer.js.map +0 -1
  43. package/dist/utils/publicSheetReader.js +0 -109
  44. package/dist/utils/publicSheetReader.js.map +0 -1
  45. package/dist/utils/rateLimiter.js +0 -55
  46. package/dist/utils/rateLimiter.js.map +0 -1
  47. package/dist/utils/readDataJson.js +0 -29
  48. package/dist/utils/readDataJson.js.map +0 -1
  49. package/dist/utils/sheetProcessor.js +0 -121
  50. package/dist/utils/sheetProcessor.js.map +0 -1
  51. package/dist/utils/spreadsheetCreator.js +0 -121
  52. package/dist/utils/spreadsheetCreator.js.map +0 -1
  53. package/dist/utils/spreadsheetUpdater.js +0 -227
  54. package/dist/utils/spreadsheetUpdater.js.map +0 -1
  55. package/dist/utils/syncManager.js +0 -62
  56. package/dist/utils/syncManager.js.map +0 -1
  57. package/dist/utils/validateEnv.js +0 -41
  58. package/dist/utils/validateEnv.js.map +0 -1
  59. package/dist/utils/wait.js +0 -19
  60. 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,227 +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
- * If a sheet named `sheetTitle` does not yet exist in the document and `localeMapping` is
26
- * non-empty, the sheet is **created automatically** with "key" as the first column followed by
27
- * the original locale-header names from `localeMapping`. This ensures that new feature sheets
28
- * (e.g. "ui") are bootstrapped on the first sync without requiring manual spreadsheet setup.
29
- *
30
- * Example:
31
- * If a new key "welcome" has an English translation in column B but no German translation in column C,
32
- * and autoTranslate is enabled, the system will add:
33
- * =GOOGLETRANSLATE(INDIRECT("B"&ROW());$B$1;C$1) to the German column
34
- *
35
- * @param doc - The Google Spreadsheet instance
36
- * @param changes - Object containing new keys to add to the spreadsheet
37
- * @param waitSeconds - Base back-off delay in seconds for retrying rate-limited API calls
38
- * @param autoTranslate - Whether to add Google Translate formulas for missing translations (default: false)
39
- * @param localeMapping - Mapping from normalized locale codes to original spreadsheet headers
40
- * @returns Promise that resolves when the update is complete
41
- */
42
- async function updateSpreadsheetWithLocalChanges(doc, changes, waitSeconds, autoTranslate = false, localeMapping = {}) {
43
- console.log("Updating spreadsheet with local changes...");
44
- const baseDelayMs = waitSeconds * 1000;
45
- // Process each sheet in the changes object
46
- for (const sheetTitle of new Set(Object.values(changes).flatMap(locale => Object.keys(locale)))) {
47
- console.log(`Processing sheet: ${sheetTitle}`);
48
- // Allow re-assignment: sheet may be auto-created below
49
- let sheet = doc.sheetsByTitle[sheetTitle];
50
- if (!sheet) {
51
- const localeHeaders = Object.values(localeMapping);
52
- if (localeHeaders.length === 0) {
53
- console.warn(`Sheet "${sheetTitle}" not found in the document, cannot update`);
54
- continue;
55
- }
56
- console.log(`Sheet "${sheetTitle}" not found — creating it with ${localeHeaders.length} locale column(s).`);
57
- sheet = await (0, rateLimiter_1.withRetry)(() => doc.addSheet({ title: sheetTitle, headerValues: ['key', ...localeHeaders] }), `addSheet: ${sheetTitle}`, baseDelayMs);
58
- }
59
- // Safety guard — should never be reached, but satisfies TypeScript
60
- if (!sheet) {
61
- console.warn(`Sheet "${sheetTitle}" could not be found or created, skipping.`);
62
- continue;
63
- }
64
- // Get all rows from the sheet (retries automatically on rate-limit)
65
- const rows = await (0, rateLimiter_1.withRetry)(() => sheet.getRows(), `getRows: ${sheetTitle}`, baseDelayMs);
66
- // Determine column headers.
67
- // Normal path: derive from the first data row (most reliable).
68
- // Empty-sheet path: the sheet was just auto-created (or had all rows deleted);
69
- // reconstruct from localeMapping so we can still add new keys correctly.
70
- let headerRow;
71
- let originalHeaders;
72
- if (rows.length > 0) {
73
- originalHeaders = Object.keys(rows[0].toObject());
74
- headerRow = originalHeaders.map(h => h.toLowerCase());
75
- }
76
- else {
77
- const localeHeaders = Object.values(localeMapping);
78
- if (localeHeaders.length === 0) {
79
- console.warn(`No rows found in sheet "${sheetTitle}", cannot update`);
80
- continue;
81
- }
82
- // Reconstruct headers from the locale mapping (original case preserved)
83
- originalHeaders = ['key', ...localeHeaders];
84
- headerRow = originalHeaders.map(h => h.toLowerCase());
85
- }
86
- const keyColumn = headerRow[0]; // First column is always the key column
87
- // Get all locales from the headerRow except the key column
88
- const locales = headerRow.filter(key => key !== keyColumn);
89
- // Map of existing keys to their row indices (empty when rows.length === 0)
90
- const existingKeys = new Map();
91
- rows.forEach((row, index) => {
92
- const rowData = row.toObject();
93
- const keyField = Object.keys(rowData).find(k => k.toLowerCase() === keyColumn);
94
- if (keyField && rowData[keyField]) {
95
- // Store the key in lowercase for case-insensitive comparison
96
- existingKeys.set(rowData[keyField].toString().toLowerCase(), index);
97
- }
98
- });
99
- // New keys to add to the sheet
100
- const newKeys = new Map();
101
- // Track which locales have values for each new key (for auto-translation)
102
- const keyLocalesMap = new Map();
103
- // Collect all new keys and their translations
104
- for (const locale of Object.keys(changes)) {
105
- if (!changes[locale]?.[sheetTitle])
106
- continue;
107
- const localeData = changes[locale][sheetTitle];
108
- for (const key of Object.keys(localeData)) {
109
- const keyLower = key.toLowerCase();
110
- if (!existingKeys.has(keyLower)) {
111
- if (!newKeys.has(keyLower)) {
112
- newKeys.set(keyLower, { [keyColumn]: key });
113
- // Initialize map for this key's locale values
114
- keyLocalesMap.set(keyLower, new Map());
115
- }
116
- // Find the exact header for this locale using the mapping
117
- let localeHeader = (0, localeNormalizer_1.getOriginalHeaderForLocale)(locale, localeMapping);
118
- // Fallback: language-family prefix match against original-case sheet headers
119
- // e.g. locale 'en' finds column header 'en-US' (not the lowercase 'en-us')
120
- if (!localeHeader) {
121
- const localeLang = (0, localeNormalizer_1.getLanguagePrefix)(locale);
122
- localeHeader = originalHeaders.find(h => (0, localeNormalizer_1.getLanguagePrefix)(h) === localeLang);
123
- }
124
- if (localeHeader) {
125
- const theKey = newKeys.get(keyLower);
126
- if (!theKey) {
127
- console.warn(`Key "${key}" not found in newKeys map, skipping...`);
128
- continue;
129
- }
130
- const value = String(localeData[key]);
131
- // Use the correct header name for the locale value
132
- theKey[localeHeader] = value;
133
- // Store this locale value for potential auto-translation
134
- const localesForKey = keyLocalesMap.get(keyLower);
135
- if (localesForKey) {
136
- localesForKey.set(locale.toLowerCase(), localeHeader);
137
- }
138
- }
139
- }
140
- else {
141
- // Update existing key with new translation
142
- const rowIndex = existingKeys.get(keyLower);
143
- const row = rows[rowIndex];
144
- // Find the exact header for this locale using the mapping
145
- let localeHeader = (0, localeNormalizer_1.getOriginalHeaderForLocale)(locale, localeMapping);
146
- // Fallback: language-family prefix match against the row's own keys
147
- // e.g. locale 'en' finds column header 'en-US' on the existing row
148
- if (!localeHeader) {
149
- const localeLang = (0, localeNormalizer_1.getLanguagePrefix)(locale);
150
- localeHeader = Object.keys(row.toObject()).find(h => (0, localeNormalizer_1.getLanguagePrefix)(h) === localeLang);
151
- }
152
- if (localeHeader) {
153
- // Use set() method instead of direct property assignment to avoid TS errors
154
- row.set(localeHeader, String(localeData[key]));
155
- try {
156
- await (0, rateLimiter_1.withRetry)(() => row.save(), `save row ${rowIndex} in ${sheetTitle}`, baseDelayMs);
157
- }
158
- catch (err) {
159
- console.error(`Failed to save row for key "${keyLower}" in sheet "${sheetTitle}":`, err);
160
- }
161
- }
162
- }
163
- }
164
- }
165
- // Add new keys to the sheet
166
- if (newKeys.size > 0) {
167
- console.log(`Adding ${newKeys.size} new keys to sheet ${sheetTitle}...`);
168
- // Apply auto-translation for new keys if enabled
169
- if (autoTranslate) {
170
- // Process each new key
171
- for (const [keyLower, rowData] of newKeys.entries()) {
172
- const localesWithValues = keyLocalesMap.get(keyLower);
173
- if (localesWithValues && localesWithValues.size > 0) {
174
- // Pick the first locale with a value as the source for translation
175
- const [, sourceHeader] = [...localesWithValues.entries()][0];
176
- // Find the cell reference for the source value
177
- // In Google Sheets formulas, we need to use column letters (A, B, C...) and row numbers
178
- // For each locale that doesn't have a value, add a GOOGLETRANSLATE formula
179
- for (const localeHeader of locales) {
180
- const localeLower = localeHeader.toLowerCase();
181
- // Skip if this locale already has a value.
182
- // rowData keys may be mixed-case (e.g. "en-GB"), so use a
183
- // case-insensitive lookup instead of a direct key access.
184
- const rowDataKey = Object.keys(rowData).find(k => k.toLowerCase() === localeLower);
185
- if (localesWithValues.has(localeLower) || (rowDataKey && rowData[rowDataKey])) {
186
- continue;
187
- }
188
- // Find the exact case-preserved header name
189
- const exactHeaderName = headerRow.find(h => h.toLowerCase() === localeLower);
190
- if (exactHeaderName) {
191
- // Create Google Translate formula referring to the source column
192
- // Since we don't know the exact row number yet, we'll use a special placeholder
193
- // that will be replaced with the actual cell reference after the rows are added
194
- // headerRow is fully lowercased; normalise sourceHeader to match so
195
- // that mixed-case originals (e.g. "en-GB") are found correctly.
196
- const sourceHeaderIndex = headerRow.indexOf(sourceHeader.toLowerCase());
197
- // Get the column index for the target locale
198
- const targetHeaderIndex = headerRow.indexOf(exactHeaderName);
199
- // Guard against unexpected out-of-range indices
200
- if (sourceHeaderIndex < 0 || targetHeaderIndex < 0) {
201
- continue;
202
- }
203
- const sourceColumnLetter = columnIndexToLetter(sourceHeaderIndex);
204
- const targetColumnLetter = columnIndexToLetter(targetHeaderIndex);
205
- // Create improved Google Translate formula using INDIRECT and cell references
206
- // This formula dynamically references:
207
- // - INDIRECT(sourceColumn&ROW()) for the source text
208
- // - $sourceColumn$1 for the source language code from header
209
- // - targetColumn$1 for the target language code from header
210
- rowData[exactHeaderName] = `=GOOGLETRANSLATE(INDIRECT("${sourceColumnLetter}"&ROW());$${sourceColumnLetter}$1;${targetColumnLetter}$1)`;
211
- }
212
- }
213
- }
214
- }
215
- }
216
- const newRows = Array.from(newKeys.values());
217
- // Add new rows in chunks to keep individual requests manageable
218
- const CHUNK_SIZE = 5;
219
- for (let i = 0; i < newRows.length; i += CHUNK_SIZE) {
220
- const chunk = newRows.slice(i, i + CHUNK_SIZE);
221
- await (0, rateLimiter_1.withRetry)(() => sheet.addRows(chunk), `addRows chunk ${Math.floor(i / CHUNK_SIZE) + 1} in ${sheetTitle}`, baseDelayMs);
222
- }
223
- }
224
- }
225
- console.log("Finished updating spreadsheet with local changes.");
226
- }
227
- //# 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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"}