@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.
- package/README.md +58 -55
- package/dist/action-entrypoint.d.ts +2 -0
- package/dist/action-entrypoint.d.ts.map +1 -0
- package/dist/esm/index.js +1226 -0
- package/dist/esm/package.json +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1288 -41
- package/dist/utils/dataConverter/findLocalChanges.d.ts +2 -1
- package/dist/utils/dataConverter/findLocalChanges.d.ts.map +1 -1
- package/dist/utils/localeNormalizer.d.ts +31 -0
- package/dist/utils/localeNormalizer.d.ts.map +1 -1
- package/dist/utils/translationHelpers.d.ts +107 -0
- package/dist/utils/translationHelpers.d.ts.map +1 -0
- package/package.json +21 -14
- package/dist/constants.js +0 -9
- package/dist/constants.js.map +0 -1
- package/dist/getSpreadSheetData.js +0 -170
- package/dist/getSpreadSheetData.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
- package/dist/utils/auth.js +0 -23
- package/dist/utils/auth.js.map +0 -1
- package/dist/utils/configurationHandler.js +0 -29
- package/dist/utils/configurationHandler.js.map +0 -1
- package/dist/utils/dataConverter/convertFromDataJsonFormat.js +0 -47
- package/dist/utils/dataConverter/convertFromDataJsonFormat.js.map +0 -1
- package/dist/utils/dataConverter/convertToDataJsonFormat.js +0 -51
- package/dist/utils/dataConverter/convertToDataJsonFormat.js.map +0 -1
- package/dist/utils/dataConverter/findLocalChanges.js +0 -70
- package/dist/utils/dataConverter/findLocalChanges.js.map +0 -1
- package/dist/utils/fileWriter.js +0 -119
- package/dist/utils/fileWriter.js.map +0 -1
- package/dist/utils/getFileLastModified.js +0 -23
- package/dist/utils/getFileLastModified.js.map +0 -1
- package/dist/utils/isDataJsonNewer.js +0 -40
- package/dist/utils/isDataJsonNewer.js.map +0 -1
- package/dist/utils/localeFilter.js +0 -49
- package/dist/utils/localeFilter.js.map +0 -1
- package/dist/utils/localeNormalizer.js +0 -176
- package/dist/utils/localeNormalizer.js.map +0 -1
- package/dist/utils/publicSheetReader.js +0 -109
- package/dist/utils/publicSheetReader.js.map +0 -1
- package/dist/utils/rateLimiter.js +0 -55
- package/dist/utils/rateLimiter.js.map +0 -1
- package/dist/utils/readDataJson.js +0 -29
- package/dist/utils/readDataJson.js.map +0 -1
- package/dist/utils/sheetProcessor.js +0 -121
- package/dist/utils/sheetProcessor.js.map +0 -1
- package/dist/utils/spreadsheetCreator.js +0 -121
- package/dist/utils/spreadsheetCreator.js.map +0 -1
- package/dist/utils/spreadsheetUpdater.js +0 -227
- package/dist/utils/spreadsheetUpdater.js.map +0 -1
- package/dist/utils/syncManager.js +0 -62
- package/dist/utils/syncManager.js.map +0 -1
- package/dist/utils/validateEnv.js +0 -41
- package/dist/utils/validateEnv.js.map +0 -1
- package/dist/utils/wait.js +0 -19
- 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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"}
|