@el-j/google-sheet-translations 1.0.2 → 1.2.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 (43) hide show
  1. package/README.md +129 -1
  2. package/dist/getSpreadSheetData.d.ts.map +1 -1
  3. package/dist/getSpreadSheetData.js +124 -75
  4. package/dist/getSpreadSheetData.js.map +1 -1
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +11 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/utils/auth.js +1 -1
  10. package/dist/utils/auth.js.map +1 -1
  11. package/dist/utils/configurationHandler.d.ts +44 -0
  12. package/dist/utils/configurationHandler.d.ts.map +1 -1
  13. package/dist/utils/configurationHandler.js +8 -2
  14. package/dist/utils/configurationHandler.js.map +1 -1
  15. package/dist/utils/publicSheetReader.d.ts +17 -0
  16. package/dist/utils/publicSheetReader.d.ts.map +1 -0
  17. package/dist/utils/publicSheetReader.js +106 -0
  18. package/dist/utils/publicSheetReader.js.map +1 -0
  19. package/dist/utils/rateLimiter.d.ts +15 -0
  20. package/dist/utils/rateLimiter.d.ts.map +1 -0
  21. package/dist/utils/rateLimiter.js +55 -0
  22. package/dist/utils/rateLimiter.js.map +1 -0
  23. package/dist/utils/sheetProcessor.d.ts +20 -6
  24. package/dist/utils/sheetProcessor.d.ts.map +1 -1
  25. package/dist/utils/sheetProcessor.js +54 -19
  26. package/dist/utils/sheetProcessor.js.map +1 -1
  27. package/dist/utils/spreadsheetCreator.d.ts +23 -0
  28. package/dist/utils/spreadsheetCreator.d.ts.map +1 -0
  29. package/dist/utils/spreadsheetCreator.js +121 -0
  30. package/dist/utils/spreadsheetCreator.js.map +1 -0
  31. package/dist/utils/spreadsheetUpdater.d.ts +1 -1
  32. package/dist/utils/spreadsheetUpdater.d.ts.map +1 -1
  33. package/dist/utils/spreadsheetUpdater.js +8 -14
  34. package/dist/utils/spreadsheetUpdater.js.map +1 -1
  35. package/dist/utils/validateEnv.d.ts +7 -3
  36. package/dist/utils/validateEnv.d.ts.map +1 -1
  37. package/dist/utils/validateEnv.js +22 -10
  38. package/dist/utils/validateEnv.js.map +1 -1
  39. package/dist/utils/wait.d.ts +5 -2
  40. package/dist/utils/wait.d.ts.map +1 -1
  41. package/dist/utils/wait.js +8 -10
  42. package/dist/utils/wait.js.map +1 -1
  43. package/package.json +92 -91
@@ -0,0 +1,17 @@
1
+ import type { SheetRow } from "../types";
2
+ /**
3
+ * Reads rows from a *publicly accessible* Google Spreadsheet sheet without
4
+ * requiring any service-account credentials or API key.
5
+ *
6
+ * The spreadsheet must be shared with **"Anyone with link can view"** (or
7
+ * broader). Works via Google's Visualization (gviz) query endpoint which
8
+ * is available at no cost for public sheets.
9
+ *
10
+ * @param spreadsheetId - The Google Spreadsheet ID (from the URL)
11
+ * @param sheetName - The sheet tab name to fetch
12
+ * @returns An array of row objects keyed by column header
13
+ * @throws If the sheet is not accessible or the response cannot be parsed
14
+ */
15
+ export declare function readPublicSheet(spreadsheetId: string, sheetName: string): Promise<SheetRow[]>;
16
+ export default readPublicSheet;
17
+ //# sourceMappingURL=publicSheetReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicSheetReader.d.ts","sourceRoot":"","sources":["../../src/utils/publicSheetReader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAiFzC;;;;;;;;;;;;GAYG;AACH,wBAAsB,eAAe,CACpC,aAAa,EAAE,MAAM,EACrB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,QAAQ,EAAE,CAAC,CAiDrB;AAED,eAAe,eAAe,CAAC"}
@@ -0,0 +1,106 @@
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.readPublicSheet = readPublicSheet;
7
+ const node_https_1 = __importDefault(require("node:https"));
8
+ const node_http_1 = __importDefault(require("node:http"));
9
+ /**
10
+ * Fetches the raw response body from a URL using the built-in http/https module.
11
+ * Follows a single redirect if the server issues one (3xx).
12
+ */
13
+ function fetchUrl(url) {
14
+ return new Promise((resolve, reject) => {
15
+ const client = url.startsWith("https") ? node_https_1.default : node_http_1.default;
16
+ const req = client.get(url, (res) => {
17
+ // Follow one redirect (e.g. Google auth redirect)
18
+ if (res.statusCode !== undefined &&
19
+ res.statusCode >= 300 &&
20
+ res.statusCode < 400 &&
21
+ res.headers.location) {
22
+ fetchUrl(res.headers.location).then(resolve).catch(reject);
23
+ return;
24
+ }
25
+ if (res.statusCode !== undefined && res.statusCode >= 400) {
26
+ reject(new Error(`HTTP ${res.statusCode} while fetching ${url}`));
27
+ return;
28
+ }
29
+ let data = "";
30
+ res.on("data", (chunk) => {
31
+ data += chunk.toString();
32
+ });
33
+ res.on("end", () => resolve(data));
34
+ res.on("error", reject);
35
+ });
36
+ req.on("error", reject);
37
+ req.end();
38
+ });
39
+ }
40
+ /**
41
+ * Strips the JSONP wrapper that the Google Visualization API adds and parses
42
+ * the inner JSON object.
43
+ *
44
+ * Expected wrapper format:
45
+ * `/*O_o*\/\ngoogle.visualization.Query.setResponse({...});`
46
+ */
47
+ function parseGvizResponse(raw) {
48
+ const match = raw.match(/google\.visualization\.Query\.setResponse\((\{[\s\S]*?\})\)/);
49
+ if (!match) {
50
+ throw new Error('Unexpected response format from Google Visualization API. ' +
51
+ 'Make sure the spreadsheet is shared as "Anyone with link can view".');
52
+ }
53
+ return JSON.parse(match[1]);
54
+ }
55
+ /**
56
+ * Reads rows from a *publicly accessible* Google Spreadsheet sheet without
57
+ * requiring any service-account credentials or API key.
58
+ *
59
+ * The spreadsheet must be shared with **"Anyone with link can view"** (or
60
+ * broader). Works via Google's Visualization (gviz) query endpoint which
61
+ * is available at no cost for public sheets.
62
+ *
63
+ * @param spreadsheetId - The Google Spreadsheet ID (from the URL)
64
+ * @param sheetName - The sheet tab name to fetch
65
+ * @returns An array of row objects keyed by column header
66
+ * @throws If the sheet is not accessible or the response cannot be parsed
67
+ */
68
+ async function readPublicSheet(spreadsheetId, sheetName) {
69
+ const url = `https://docs.google.com/spreadsheets/d/${encodeURIComponent(spreadsheetId)}` +
70
+ `/gviz/tq?tqx=out:json&sheet=${encodeURIComponent(sheetName)}`;
71
+ let raw;
72
+ try {
73
+ raw = await fetchUrl(url);
74
+ }
75
+ catch (err) {
76
+ throw new Error(`Failed to fetch public sheet "${sheetName}" from spreadsheet "${spreadsheetId}"`, { cause: err });
77
+ }
78
+ let data;
79
+ try {
80
+ data = parseGvizResponse(raw);
81
+ }
82
+ catch (err) {
83
+ throw new Error(`Failed to parse response for sheet "${sheetName}" in spreadsheet "${spreadsheetId}"`, { cause: err });
84
+ }
85
+ if (data.status !== "ok") {
86
+ const message = data.errors?.[0]?.message ?? "Unknown error";
87
+ throw new Error(`Google Visualization API returned an error for sheet "${sheetName}": ${message}`);
88
+ }
89
+ if (!data.table) {
90
+ return [];
91
+ }
92
+ const { cols, rows } = data.table;
93
+ const headers = cols.map((col) => col.label || col.id);
94
+ return rows
95
+ .filter((row) => row && row.c)
96
+ .map((row) => {
97
+ const obj = {};
98
+ for (let i = 0; i < headers.length; i++) {
99
+ const cell = row.c?.[i];
100
+ obj[headers[i]] = cell?.v != null ? String(cell.v) : "";
101
+ }
102
+ return obj;
103
+ });
104
+ }
105
+ exports.default = readPublicSheet;
106
+ //# sourceMappingURL=publicSheetReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publicSheetReader.js","sourceRoot":"","sources":["../../src/utils/publicSheetReader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Calls `fn` and, on a rate-limit error (HTTP 429 / 503), retries with
3
+ * exponential back-off. Any other error is re-thrown immediately.
4
+ *
5
+ * @param fn - The async operation to execute (and potentially retry)
6
+ * @param label - Human-readable label used in warning logs
7
+ * @param baseDelayMs - Base back-off delay in milliseconds (default: 1 000)
8
+ * @param retries - Maximum number of retry attempts (default: 3)
9
+ * @param maxDelayMs - Back-off ceiling in milliseconds (default: 30 000)
10
+ * @returns The resolved value of `fn`
11
+ * @throws The last error if all retries are exhausted, or any non-rate-limit error immediately
12
+ */
13
+ export declare function withRetry<T>(fn: () => Promise<T>, label: string, baseDelayMs?: number, retries?: number, maxDelayMs?: number): Promise<T>;
14
+ export default withRetry;
15
+ //# sourceMappingURL=rateLimiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rateLimiter.d.ts","sourceRoot":"","sources":["../../src/utils/rateLimiter.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;;;GAWG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAChC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,KAAK,EAAE,MAAM,EACb,WAAW,SAAQ,EACnB,OAAO,SAAkB,EACzB,UAAU,SAAuB,GAC/B,OAAO,CAAC,CAAC,CAAC,CAkBZ;AAED,eAAe,SAAS,CAAC"}
@@ -0,0 +1,55 @@
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
@@ -0,0 +1 @@
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,5 +1,5 @@
1
1
  import type { GoogleSpreadsheetWorksheet } from "google-spreadsheet";
2
- import type { TranslationData } from "../types";
2
+ import type { SheetRow, TranslationData } from "../types";
3
3
  /**
4
4
  * Result of processing a single sheet
5
5
  */
@@ -11,12 +11,26 @@ export interface SheetProcessingResult {
11
11
  success: boolean;
12
12
  }
13
13
  /**
14
- * Processes a single Google Sheet and extracts translation data
15
- * @param sheet The Google Spreadsheet worksheet to process
14
+ * Core row-processing logic shared by both the authenticated and public sheet paths.
15
+ * Accepts pre-fetched rows (as plain objects) and returns the same
16
+ * {@link SheetProcessingResult} shape that `processSheet` produces.
17
+ * Contains only pure computation — no API calls are made here.
18
+ *
19
+ * @param rows - Array of row objects keyed by column header
20
+ * @param sheetTitle - The sheet tab name (used as namespace key in translations)
21
+ * @returns Processing result containing translations and locales
22
+ */
23
+ export declare function processRawRows(rows: SheetRow[], sheetTitle: string): Promise<SheetProcessingResult>;
24
+ /**
25
+ * Fetches rows from a Google Sheet and extracts translation data.
26
+ * The underlying `getRows` API call is automatically retried on rate-limit
27
+ * responses (HTTP 429 / 503) using exponential back-off.
28
+ *
29
+ * @param sheet The Google Spreadsheet worksheet to process
16
30
  * @param sheetTitle The title of the sheet being processed
17
- * @param rowLimit Maximum number of rows to fetch
18
- * @param waitSeconds Time to wait after processing
31
+ * @param rowLimit Maximum number of rows to fetch
32
+ * @param baseDelayMs Base back-off delay in ms for retrying rate-limited requests
19
33
  * @returns Processing result containing translations and locales
20
34
  */
21
- export declare function processSheet(sheet: GoogleSpreadsheetWorksheet, sheetTitle: string, rowLimit: number, waitSeconds: number): Promise<SheetProcessingResult>;
35
+ export declare function processSheet(sheet: GoogleSpreadsheetWorksheet, sheetTitle: string, rowLimit: number, baseDelayMs?: number): Promise<SheetProcessingResult>;
22
36
  //# sourceMappingURL=sheetProcessor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sheetProcessor.d.ts","sourceRoot":"","sources":["../../src/utils/sheetProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAY,eAAe,EAAE,MAAM,UAAU,CAAC;AAK1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACrC,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CACjC,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,qBAAqB,CAAC,CA6FhC"}
1
+ {"version":3,"file":"sheetProcessor.d.ts","sourceRoot":"","sources":["../../src/utils/sheetProcessor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAK1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACtC,YAAY,EAAE,eAAe,CAAC;IAC9B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CACpC,IAAI,EAAE,QAAQ,EAAE,EAChB,UAAU,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,CAAC,CAwFhC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAClC,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,WAAW,SAAQ,GAChB,OAAO,CAAC,qBAAqB,CAAC,CA4BhC"}
@@ -1,34 +1,35 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processRawRows = processRawRows;
3
4
  exports.processSheet = processSheet;
4
- const wait_1 = require("./wait");
5
+ const rateLimiter_1 = require("./rateLimiter");
5
6
  const localeFilter_1 = require("./localeFilter");
6
7
  const localeNormalizer_1 = require("./localeNormalizer");
7
8
  /**
8
- * Processes a single Google Sheet and extracts translation data
9
- * @param sheet The Google Spreadsheet worksheet to process
10
- * @param sheetTitle The title of the sheet being processed
11
- * @param rowLimit Maximum number of rows to fetch
12
- * @param waitSeconds Time to wait after processing
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)
13
16
  * @returns Processing result containing translations and locales
14
17
  */
15
- async function processSheet(sheet, sheetTitle, rowLimit, waitSeconds) {
18
+ async function processRawRows(rows, sheetTitle) {
16
19
  const result = {
17
20
  translations: {},
18
21
  locales: [],
19
22
  localeMapping: {},
20
23
  originalMapping: {},
21
- success: false
24
+ success: false,
22
25
  };
23
26
  try {
24
- const rows = await sheet.getRows({ limit: rowLimit });
25
27
  if (!rows || rows.length === 0) {
26
28
  console.warn(`No rows found in sheet "${sheetTitle}"`);
27
29
  return result;
28
30
  }
29
31
  // Extract header information
30
- const rowObject = rows[0].toObject();
31
- const headerRow = Object.keys(rowObject).map(key => key.toLowerCase());
32
+ const headerRow = Object.keys(rows[0]).map((key) => key.toLowerCase());
32
33
  console.log(`Header row for sheet "${sheetTitle}":`, headerRow);
33
34
  const keyColumn = headerRow[0];
34
35
  const validLocales = (0, localeFilter_1.filterValidLocales)(headerRow, keyColumn);
@@ -37,22 +38,20 @@ async function processSheet(sheet, sheetTitle, rowLimit, waitSeconds) {
37
38
  return result;
38
39
  }
39
40
  // Create locale mapping for normalization
40
- const originalHeaders = Object.keys(rowObject); // Keep original case
41
+ const originalHeaders = Object.keys(rows[0]); // Keep original case
41
42
  const { normalizedLocales, localeMapping, originalMapping } = (0, localeNormalizer_1.createLocaleMapping)(originalHeaders, keyColumn);
42
43
  // Store the mappings in the result
43
44
  result.localeMapping = localeMapping;
44
45
  result.originalMapping = originalMapping;
45
- // Convert rows to data objects
46
- const cells = rows.map(row => row.toObject());
47
46
  // Process each normalized locale
48
47
  for (const normalizedLocale of normalizedLocales) {
49
48
  // Find the original header for this normalized locale
50
49
  const originalHeader = localeMapping[normalizedLocale];
51
50
  if (!originalHeader)
52
51
  continue;
53
- const languageCells = cells.map((row) => {
52
+ const languageCells = rows.map((row) => {
54
53
  // Look for the key column (case-insensitive)
55
- const keyField = Object.keys(row).find(k => k.toLowerCase() === keyColumn);
54
+ const keyField = Object.keys(row).find((k) => k.toLowerCase() === keyColumn);
56
55
  if (!keyField || !row[keyField] || !row[originalHeader]) {
57
56
  return {}; // Skip rows without key or translation
58
57
  }
@@ -62,13 +61,16 @@ async function processSheet(sheet, sheetTitle, rowLimit, waitSeconds) {
62
61
  return rowLocal;
63
62
  });
64
63
  // Filter out empty objects before combining
65
- const nonEmptyLanguageCells = languageCells.filter(cell => Object.keys(cell).length > 0);
64
+ const nonEmptyLanguageCells = languageCells.filter((cell) => Object.keys(cell).length > 0);
66
65
  // Combine all keys into one object
67
66
  const prepareObj = {};
68
67
  prepareObj[sheetTitle] = nonEmptyLanguageCells.reduce((acc, cell) => Object.assign(acc, cell), {});
69
68
  // Use normalized locale as the key in translations
70
69
  if (result.translations[normalizedLocale]) {
71
- result.translations[normalizedLocale] = { ...result.translations[normalizedLocale], ...prepareObj };
70
+ result.translations[normalizedLocale] = {
71
+ ...result.translations[normalizedLocale],
72
+ ...prepareObj,
73
+ };
72
74
  }
73
75
  else {
74
76
  result.translations[normalizedLocale] = { ...prepareObj };
@@ -76,11 +78,44 @@ async function processSheet(sheet, sheetTitle, rowLimit, waitSeconds) {
76
78
  }
77
79
  result.locales = normalizedLocales;
78
80
  result.success = true;
79
- await (0, wait_1.wait)(waitSeconds, `after processing sheet: ${sheetTitle}`);
80
81
  }
81
82
  catch (error) {
82
83
  console.error(`Error processing sheet "${sheetTitle}":`, error);
83
84
  }
84
85
  return result;
85
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
+ }
86
121
  //# sourceMappingURL=sheetProcessor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sheetProcessor.js","sourceRoot":"","sources":["../../src/utils/sheetProcessor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sheetProcessor.js","sourceRoot":"","sources":["../../src/utils/sheetProcessor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { JWT } from "google-auth-library";
2
+ export interface CreateSpreadsheetOptions {
3
+ /** Spreadsheet title (default: "google-sheet-translations") */
4
+ title?: string;
5
+ /** Source locale column header – translations in this locale are real text (default: "en") */
6
+ sourceLocale?: string;
7
+ /** Additional locale columns – filled with GOOGLETRANSLATE formulas (default: common languages) */
8
+ targetLocales?: string[];
9
+ /** Seed keys: key → source-locale value. If omitted a starter template is used. */
10
+ seedKeys?: Record<string, string>;
11
+ }
12
+ export interface CreatedSpreadsheetInfo {
13
+ spreadsheetId: string;
14
+ url: string;
15
+ }
16
+ /**
17
+ * Creates a new Google Spreadsheet seeded with translation starter content and
18
+ * GOOGLETRANSLATE formulas for every non-source locale.
19
+ *
20
+ * Returns the spreadsheet ID and URL.
21
+ */
22
+ export declare function createSpreadsheet(authClient: JWT, options?: CreateSpreadsheetOptions): Promise<CreatedSpreadsheetInfo>;
23
+ //# sourceMappingURL=spreadsheetCreator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spreadsheetCreator.d.ts","sourceRoot":"","sources":["../../src/utils/spreadsheetCreator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAiB/C,MAAM,WAAW,wBAAwB;IACxC,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mGAAmG;IACnG,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,sBAAsB;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;CACZ;AAiBD;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACtC,UAAU,EAAE,GAAG,EACf,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,sBAAsB,CAAC,CA8GjC"}
@@ -0,0 +1,121 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spreadsheetCreator.js","sourceRoot":"","sources":["../../src/utils/spreadsheetCreator.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,7 @@ import type { TranslationData } from "../types";
16
16
  *
17
17
  * @param doc - The Google Spreadsheet instance
18
18
  * @param changes - Object containing new keys to add to the spreadsheet
19
- * @param waitSeconds - Number of seconds to wait between API calls
19
+ * @param waitSeconds - Base back-off delay in seconds for retrying rate-limited API calls
20
20
  * @param autoTranslate - Whether to add Google Translate formulas for missing translations (default: false)
21
21
  * @param localeMapping - Mapping from normalized locale codes to original spreadsheet headers
22
22
  * @returns Promise that resolves when the update is complete
@@ -1 +1 @@
1
- {"version":3,"file":"spreadsheetUpdater.d.ts","sourceRoot":"","sources":["../../src/utils/spreadsheetUpdater.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAehD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iCAAiC,CACnD,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,aAAa,UAAQ,EACrB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAC3C,OAAO,CAAC,IAAI,CAAC,CA2Mf"}
1
+ {"version":3,"file":"spreadsheetUpdater.d.ts","sourceRoot":"","sources":["../../src/utils/spreadsheetUpdater.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAehD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,iCAAiC,CACnD,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,eAAe,EACxB,WAAW,EAAE,MAAM,EACnB,aAAa,UAAQ,EACrB,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GAC3C,OAAO,CAAC,IAAI,CAAC,CA8Mf"}