@el-j/google-sheet-translations 2.2.0-beta.2 → 2.2.0-beta.4
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/dist/action-entrypoint.d.ts.map +1 -1
- package/dist/esm/index.js +540 -38
- package/dist/index.d.ts +9 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +550 -37
- package/dist/utils/auth.d.ts +44 -4
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/docIngester.d.ts +111 -0
- package/dist/utils/docIngester.d.ts.map +1 -0
- package/dist/utils/docParser.d.ts +77 -0
- package/dist/utils/docParser.d.ts.map +1 -0
- package/dist/utils/driveDocScanner.d.ts +58 -0
- package/dist/utils/driveDocScanner.d.ts.map +1 -0
- package/dist/utils/driveFolderScanner.d.ts.map +1 -1
- package/dist/utils/driveImageSync.d.ts.map +1 -1
- package/dist/utils/driveProjectIndex.d.ts +39 -0
- package/dist/utils/driveProjectIndex.d.ts.map +1 -1
- package/dist/utils/getDriveTranslations.d.ts +46 -0
- package/dist/utils/getDriveTranslations.d.ts.map +1 -1
- package/dist/utils/spreadsheetCreator.d.ts +2 -2
- package/dist/utils/spreadsheetCreator.d.ts.map +1 -1
- package/dist/utils/validateEnv.d.ts +7 -3
- package/dist/utils/validateEnv.d.ts.map +1 -1
- package/package.json +18 -18
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var index_exports = {};
|
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
DEFAULT_IMAGE_EXTENSIONS: () => DEFAULT_IMAGE_EXTENSIONS,
|
|
34
34
|
DEFAULT_WAIT_SECONDS: () => DEFAULT_WAIT_SECONDS,
|
|
35
|
+
buildGoogleAuth: () => buildGoogleAuth,
|
|
35
36
|
buildManifest: () => buildManifest,
|
|
36
37
|
convertFromDataJsonFormat: () => convertFromDataJsonFormat,
|
|
37
38
|
convertToDataJsonFormat: () => convertToDataJsonFormat,
|
|
@@ -39,6 +40,9 @@ __export(index_exports, {
|
|
|
39
40
|
createLocaleMapping: () => createLocaleMapping,
|
|
40
41
|
createSpreadsheet: () => createSpreadsheet,
|
|
41
42
|
default: () => index_default,
|
|
43
|
+
entriesToSeedKeys: () => entriesToSeedKeys,
|
|
44
|
+
entriesToTranslationData: () => entriesToTranslationData,
|
|
45
|
+
exportDoc: () => exportDoc,
|
|
42
46
|
filterValidLocales: () => filterValidLocales,
|
|
43
47
|
findLocalChanges: () => findLocalChanges,
|
|
44
48
|
getGoogleTranslateCode: () => getGoogleTranslateCode,
|
|
@@ -50,16 +54,23 @@ __export(index_exports, {
|
|
|
50
54
|
getSpreadSheetData: () => getSpreadSheetData,
|
|
51
55
|
getTranslationSummary: () => getTranslationSummary,
|
|
52
56
|
handleBidirectionalSync: () => handleBidirectionalSync,
|
|
57
|
+
inferLocaleFromDocName: () => inferLocaleFromDocName,
|
|
58
|
+
ingestDoc: () => ingestDoc,
|
|
53
59
|
isValidLocale: () => isValidLocale,
|
|
54
60
|
manageDriveTranslations: () => manageDriveTranslations,
|
|
55
61
|
mergeMultipleTranslationData: () => mergeMultipleTranslationData,
|
|
56
62
|
mergeSheets: () => mergeSheets,
|
|
57
63
|
normalizeExtension: () => normalizeExtension,
|
|
58
64
|
normalizeLocaleCode: () => normalizeLocaleCode,
|
|
65
|
+
normalizePrivateKey: () => normalizePrivateKey,
|
|
66
|
+
parseDocContent: () => parseDocContent,
|
|
59
67
|
processRawRows: () => processRawRows,
|
|
68
|
+
readManifest: () => readManifest,
|
|
60
69
|
readPublicSheet: () => readPublicSheet,
|
|
61
70
|
resolveLocaleWithFallback: () => resolveLocaleWithFallback,
|
|
71
|
+
scanDriveFolderForDocs: () => scanDriveFolderForDocs,
|
|
62
72
|
scanDriveFolderForSpreadsheets: () => scanDriveFolderForSpreadsheets,
|
|
73
|
+
slugifyKey: () => slugifyKey,
|
|
63
74
|
syncDriveImages: () => syncDriveImages,
|
|
64
75
|
updateSpreadsheetWithLocalChanges: () => updateSpreadsheetWithLocalChanges,
|
|
65
76
|
validateCredentials: () => validateCredentials,
|
|
@@ -85,13 +96,20 @@ var import_google_auth_library = require("google-auth-library");
|
|
|
85
96
|
|
|
86
97
|
// src/utils/validateEnv.ts
|
|
87
98
|
function validateCredentials() {
|
|
99
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
100
|
+
return {
|
|
101
|
+
GOOGLE_CLIENT_EMAIL: process.env.GOOGLE_CLIENT_EMAIL ?? "",
|
|
102
|
+
GOOGLE_PRIVATE_KEY: process.env.GOOGLE_PRIVATE_KEY ?? ""
|
|
103
|
+
};
|
|
104
|
+
}
|
|
88
105
|
const requiredVars = ["GOOGLE_CLIENT_EMAIL", "GOOGLE_PRIVATE_KEY"];
|
|
89
106
|
const missing = requiredVars.filter((v) => !process.env[v]);
|
|
90
107
|
if (missing.length > 0) {
|
|
91
108
|
throw new Error(
|
|
92
109
|
`Missing required environment variables: ${missing.join(", ")}
|
|
93
110
|
|
|
94
|
-
Make sure these are set in your .env file or environment
|
|
111
|
+
Make sure these are set in your .env file or environment.
|
|
112
|
+
Alternatively, set GOOGLE_APPLICATION_CREDENTIALS for Workload Identity Federation.`
|
|
95
113
|
);
|
|
96
114
|
}
|
|
97
115
|
return {
|
|
@@ -100,6 +118,21 @@ Make sure these are set in your .env file or environment.`
|
|
|
100
118
|
};
|
|
101
119
|
}
|
|
102
120
|
function validateEnv() {
|
|
121
|
+
const spreadsheetId = process.env.GOOGLE_SPREADSHEET_ID;
|
|
122
|
+
if (!spreadsheetId) {
|
|
123
|
+
throw new Error(
|
|
124
|
+
`Missing required environment variable: GOOGLE_SPREADSHEET_ID
|
|
125
|
+
|
|
126
|
+
Make sure this is set in your .env file or environment.`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
130
|
+
return {
|
|
131
|
+
GOOGLE_CLIENT_EMAIL: process.env.GOOGLE_CLIENT_EMAIL ?? "",
|
|
132
|
+
GOOGLE_PRIVATE_KEY: process.env.GOOGLE_PRIVATE_KEY ?? "",
|
|
133
|
+
GOOGLE_SPREADSHEET_ID: spreadsheetId
|
|
134
|
+
};
|
|
135
|
+
}
|
|
103
136
|
const requiredVars = [
|
|
104
137
|
"GOOGLE_CLIENT_EMAIL",
|
|
105
138
|
"GOOGLE_PRIVATE_KEY",
|
|
@@ -110,24 +143,43 @@ function validateEnv() {
|
|
|
110
143
|
throw new Error(
|
|
111
144
|
`Missing required environment variables: ${missingVars.join(", ")}
|
|
112
145
|
|
|
113
|
-
Make sure these are set in your .env file or environment
|
|
146
|
+
Make sure these are set in your .env file or environment.
|
|
147
|
+
Alternatively, set GOOGLE_APPLICATION_CREDENTIALS for Workload Identity Federation.`
|
|
114
148
|
);
|
|
115
149
|
}
|
|
116
150
|
return {
|
|
117
151
|
GOOGLE_CLIENT_EMAIL: process.env.GOOGLE_CLIENT_EMAIL,
|
|
118
152
|
GOOGLE_PRIVATE_KEY: process.env.GOOGLE_PRIVATE_KEY,
|
|
119
|
-
GOOGLE_SPREADSHEET_ID:
|
|
153
|
+
GOOGLE_SPREADSHEET_ID: spreadsheetId
|
|
120
154
|
};
|
|
121
155
|
}
|
|
122
156
|
|
|
123
157
|
// src/utils/auth.ts
|
|
158
|
+
function normalizePrivateKey(key) {
|
|
159
|
+
let normalized = key;
|
|
160
|
+
const outer = key.trim();
|
|
161
|
+
if (outer.startsWith('"') && outer.endsWith('"') || outer.startsWith("'") && outer.endsWith("'")) {
|
|
162
|
+
normalized = outer.slice(1, -1);
|
|
163
|
+
}
|
|
164
|
+
normalized = normalized.replace(/\\n/g, "\n");
|
|
165
|
+
normalized = normalized.replace(/\r\n/g, "\n");
|
|
166
|
+
return normalized;
|
|
167
|
+
}
|
|
168
|
+
function buildGoogleAuth(scopes, credentials) {
|
|
169
|
+
if (credentials) {
|
|
170
|
+
return new import_google_auth_library.GoogleAuth({ credentials, scopes });
|
|
171
|
+
}
|
|
172
|
+
return new import_google_auth_library.GoogleAuth({ scopes });
|
|
173
|
+
}
|
|
124
174
|
function createAuthClient() {
|
|
175
|
+
if (process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
176
|
+
return buildGoogleAuth(["https://www.googleapis.com/auth/spreadsheets"]);
|
|
177
|
+
}
|
|
125
178
|
const { GOOGLE_CLIENT_EMAIL, GOOGLE_PRIVATE_KEY } = validateCredentials();
|
|
126
|
-
const normalizedKey = GOOGLE_PRIVATE_KEY
|
|
127
|
-
return
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
scopes: ["https://www.googleapis.com/auth/spreadsheets"]
|
|
179
|
+
const normalizedKey = normalizePrivateKey(GOOGLE_PRIVATE_KEY);
|
|
180
|
+
return buildGoogleAuth(["https://www.googleapis.com/auth/spreadsheets"], {
|
|
181
|
+
client_email: GOOGLE_CLIENT_EMAIL,
|
|
182
|
+
private_key: normalizedKey
|
|
131
183
|
});
|
|
132
184
|
}
|
|
133
185
|
|
|
@@ -1114,7 +1166,6 @@ async function createSpreadsheet(authClient, options = {}) {
|
|
|
1114
1166
|
targetLocales = DEFAULT_TARGET_LOCALES,
|
|
1115
1167
|
seedKeys = STARTER_KEYS
|
|
1116
1168
|
} = options;
|
|
1117
|
-
await authClient.authorize();
|
|
1118
1169
|
const createRes = await withRetry(
|
|
1119
1170
|
() => authClient.request({
|
|
1120
1171
|
url: "https://sheets.googleapis.com/v4/spreadsheets",
|
|
@@ -1225,13 +1276,13 @@ GOOGLE_SPREADSHEET_ID=${id}
|
|
|
1225
1276
|
async function getSpreadSheetData(_docTitle, options = {}, _refreshDepth = 0) {
|
|
1226
1277
|
const config = normalizeConfig(options);
|
|
1227
1278
|
const baseDelayMs = config.waitSeconds * 1e3;
|
|
1228
|
-
const
|
|
1229
|
-
if (
|
|
1279
|
+
const docTitle2 = _docTitle ?? [];
|
|
1280
|
+
if (docTitle2.length === 0) {
|
|
1230
1281
|
console.warn("No sheet titles provided, cannot process spreadsheet data");
|
|
1231
1282
|
return {};
|
|
1232
1283
|
}
|
|
1233
|
-
if (!
|
|
1234
|
-
|
|
1284
|
+
if (!docTitle2.includes("i18n")) {
|
|
1285
|
+
docTitle2.push("i18n");
|
|
1235
1286
|
}
|
|
1236
1287
|
const finalTranslations = {};
|
|
1237
1288
|
const allLocales = /* @__PURE__ */ new Set();
|
|
@@ -1268,9 +1319,9 @@ async function getSpreadSheetData(_docTitle, options = {}, _refreshDepth = 0) {
|
|
|
1268
1319
|
"No spreadsheet ID provided. Set GOOGLE_SPREADSHEET_ID or pass spreadsheetId in options."
|
|
1269
1320
|
);
|
|
1270
1321
|
}
|
|
1271
|
-
console.log(`Processing ${
|
|
1322
|
+
console.log(`Processing ${docTitle2.length} sheets: ${docTitle2.join(", ")}`);
|
|
1272
1323
|
await Promise.all(
|
|
1273
|
-
|
|
1324
|
+
docTitle2.map(async (title) => {
|
|
1274
1325
|
let rows;
|
|
1275
1326
|
try {
|
|
1276
1327
|
rows = await withRetry(
|
|
@@ -1303,7 +1354,7 @@ async function getSpreadSheetData(_docTitle, options = {}, _refreshDepth = 0) {
|
|
|
1303
1354
|
);
|
|
1304
1355
|
}
|
|
1305
1356
|
}
|
|
1306
|
-
console.log(`Processing ${
|
|
1357
|
+
console.log(`Processing ${docTitle2.length} sheets: ${docTitle2.join(", ")}`);
|
|
1307
1358
|
const doc = new import_google_spreadsheet2.GoogleSpreadsheet(spreadsheetId, serviceAuthClient);
|
|
1308
1359
|
try {
|
|
1309
1360
|
await withRetry(() => doc.loadInfo(true), "loadInfo", baseDelayMs);
|
|
@@ -1311,7 +1362,7 @@ async function getSpreadSheetData(_docTitle, options = {}, _refreshDepth = 0) {
|
|
|
1311
1362
|
throw new Error(`Failed to load spreadsheet "${spreadsheetId}"`, { cause: err });
|
|
1312
1363
|
}
|
|
1313
1364
|
await Promise.all(
|
|
1314
|
-
|
|
1365
|
+
docTitle2.map(async (title) => {
|
|
1315
1366
|
const sheet = doc.sheetsByTitle[title];
|
|
1316
1367
|
if (!sheet) {
|
|
1317
1368
|
console.warn(`Sheet "${title}" not found in the document`);
|
|
@@ -1436,23 +1487,22 @@ async function getMultipleSpreadSheetsData(docTitles, options = {}) {
|
|
|
1436
1487
|
}
|
|
1437
1488
|
|
|
1438
1489
|
// src/utils/driveFolderScanner.ts
|
|
1439
|
-
var import_google_auth_library2 = require("google-auth-library");
|
|
1440
1490
|
var SPREADSHEET_MIME = "application/vnd.google-apps.spreadsheet";
|
|
1441
1491
|
var FOLDER_MIME = "application/vnd.google-apps.folder";
|
|
1442
1492
|
var DRIVE_FILES_URL = "https://www.googleapis.com/drive/v3/files";
|
|
1493
|
+
var DRIVE_SCOPES = ["https://www.googleapis.com/auth/drive.readonly"];
|
|
1443
1494
|
async function getAccessToken(credentials) {
|
|
1444
1495
|
const clientEmail = credentials?.GOOGLE_CLIENT_EMAIL ?? process.env.GOOGLE_CLIENT_EMAIL;
|
|
1445
1496
|
const privateKey = credentials?.GOOGLE_PRIVATE_KEY ?? process.env.GOOGLE_PRIVATE_KEY;
|
|
1446
|
-
|
|
1497
|
+
let driveCredentials;
|
|
1498
|
+
if (clientEmail && privateKey) {
|
|
1499
|
+
driveCredentials = { client_email: clientEmail, private_key: normalizePrivateKey(privateKey) };
|
|
1500
|
+
} else if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
1447
1501
|
throw new Error(
|
|
1448
|
-
"Google Drive credentials required: GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY"
|
|
1502
|
+
"Google Drive credentials required: set GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY, or set GOOGLE_APPLICATION_CREDENTIALS for Workload Identity Federation."
|
|
1449
1503
|
);
|
|
1450
1504
|
}
|
|
1451
|
-
const
|
|
1452
|
-
const auth = new import_google_auth_library2.GoogleAuth({
|
|
1453
|
-
credentials: { client_email: clientEmail, private_key: normalizedKey },
|
|
1454
|
-
scopes: ["https://www.googleapis.com/auth/drive.readonly"]
|
|
1455
|
-
});
|
|
1505
|
+
const auth = buildGoogleAuth(DRIVE_SCOPES, driveCredentials);
|
|
1456
1506
|
const client = await auth.getClient();
|
|
1457
1507
|
const tokenResponse = await client.getAccessToken();
|
|
1458
1508
|
return tokenResponse.token;
|
|
@@ -1527,7 +1577,6 @@ var import_node_fs6 = require("node:fs");
|
|
|
1527
1577
|
var import_node_path5 = require("node:path");
|
|
1528
1578
|
var import_promises4 = require("node:stream/promises");
|
|
1529
1579
|
var import_node_stream = require("node:stream");
|
|
1530
|
-
var import_google_auth_library3 = require("google-auth-library");
|
|
1531
1580
|
var DEFAULT_IMAGE_MIME_TYPES = [
|
|
1532
1581
|
"image/jpeg",
|
|
1533
1582
|
"image/jpg",
|
|
@@ -1551,19 +1600,19 @@ function normalizeExtension(name) {
|
|
|
1551
1600
|
if (ext === "jpeg") ext = "jpg";
|
|
1552
1601
|
return `${base}.${ext}`;
|
|
1553
1602
|
}
|
|
1603
|
+
var DRIVE_SCOPES2 = ["https://www.googleapis.com/auth/drive.readonly"];
|
|
1554
1604
|
async function getAccessToken2(credentials) {
|
|
1555
1605
|
const clientEmail = credentials?.GOOGLE_CLIENT_EMAIL ?? process.env.GOOGLE_CLIENT_EMAIL;
|
|
1556
1606
|
const privateKey = credentials?.GOOGLE_PRIVATE_KEY ?? process.env.GOOGLE_PRIVATE_KEY;
|
|
1557
|
-
|
|
1607
|
+
let driveCredentials;
|
|
1608
|
+
if (clientEmail && privateKey) {
|
|
1609
|
+
driveCredentials = { client_email: clientEmail, private_key: normalizePrivateKey(privateKey) };
|
|
1610
|
+
} else if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {
|
|
1558
1611
|
throw new Error(
|
|
1559
|
-
"Google Drive credentials required: GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY"
|
|
1612
|
+
"Google Drive credentials required: set GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY, or set GOOGLE_APPLICATION_CREDENTIALS for Workload Identity Federation."
|
|
1560
1613
|
);
|
|
1561
1614
|
}
|
|
1562
|
-
const
|
|
1563
|
-
const auth = new import_google_auth_library3.GoogleAuth({
|
|
1564
|
-
credentials: { client_email: clientEmail, private_key: normalizedKey },
|
|
1565
|
-
scopes: ["https://www.googleapis.com/auth/drive.readonly"]
|
|
1566
|
-
});
|
|
1615
|
+
const auth = buildGoogleAuth(DRIVE_SCOPES2, driveCredentials);
|
|
1567
1616
|
const client = await auth.getClient();
|
|
1568
1617
|
const tokenResponse = await client.getAccessToken();
|
|
1569
1618
|
return tokenResponse.token;
|
|
@@ -1848,6 +1897,7 @@ function buildManifest(options) {
|
|
|
1848
1897
|
locales,
|
|
1849
1898
|
defaultLocale: options.defaultLocale,
|
|
1850
1899
|
spreadsheets: options.spreadsheets,
|
|
1900
|
+
docs: options.docs,
|
|
1851
1901
|
outputDirectory: options.outputDirectory,
|
|
1852
1902
|
flatten: options.flatten,
|
|
1853
1903
|
projectMetadata: options.projectMetadata
|
|
@@ -1861,11 +1911,385 @@ function writeManifest(manifest, manifestPath) {
|
|
|
1861
1911
|
import_node_fs8.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
1862
1912
|
console.log(`[driveProjectIndex] Wrote project manifest \u2192 ${manifestPath}`);
|
|
1863
1913
|
}
|
|
1914
|
+
function readManifest(manifestPath) {
|
|
1915
|
+
try {
|
|
1916
|
+
const content = import_node_fs8.default.readFileSync(manifestPath, "utf8");
|
|
1917
|
+
return JSON.parse(content);
|
|
1918
|
+
} catch {
|
|
1919
|
+
return void 0;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
// src/utils/driveDocScanner.ts
|
|
1924
|
+
var import_google_auth_library2 = require("google-auth-library");
|
|
1925
|
+
var DOC_MIME = "application/vnd.google-apps.document";
|
|
1926
|
+
var FOLDER_MIME3 = "application/vnd.google-apps.folder";
|
|
1927
|
+
var DRIVE_FILES_URL3 = "https://www.googleapis.com/drive/v3/files";
|
|
1928
|
+
async function getAccessToken3(credentials) {
|
|
1929
|
+
const clientEmail = credentials?.GOOGLE_CLIENT_EMAIL ?? process.env.GOOGLE_CLIENT_EMAIL;
|
|
1930
|
+
const privateKey = credentials?.GOOGLE_PRIVATE_KEY ?? process.env.GOOGLE_PRIVATE_KEY;
|
|
1931
|
+
if (!clientEmail || !privateKey) {
|
|
1932
|
+
throw new Error(
|
|
1933
|
+
"Google Drive credentials required: GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY"
|
|
1934
|
+
);
|
|
1935
|
+
}
|
|
1936
|
+
const normalizedKey = privateKey.replace(/\\n/g, "\n");
|
|
1937
|
+
const auth = new import_google_auth_library2.GoogleAuth({
|
|
1938
|
+
credentials: { client_email: clientEmail, private_key: normalizedKey },
|
|
1939
|
+
scopes: ["https://www.googleapis.com/auth/drive.readonly"]
|
|
1940
|
+
});
|
|
1941
|
+
const client = await auth.getClient();
|
|
1942
|
+
const tokenResponse = await client.getAccessToken();
|
|
1943
|
+
return tokenResponse.token;
|
|
1944
|
+
}
|
|
1945
|
+
async function listFilesInFolder3(folderId, mimeType, token) {
|
|
1946
|
+
const results = [];
|
|
1947
|
+
let pageToken;
|
|
1948
|
+
do {
|
|
1949
|
+
const query = `'${folderId}' in parents and mimeType = '${mimeType}' and trashed = false`;
|
|
1950
|
+
const params = new URLSearchParams({
|
|
1951
|
+
q: query,
|
|
1952
|
+
fields: "nextPageToken,files(id,name,mimeType,modifiedTime)",
|
|
1953
|
+
pageSize: "1000"
|
|
1954
|
+
});
|
|
1955
|
+
if (pageToken) params.set("pageToken", pageToken);
|
|
1956
|
+
const response = await fetch(`${DRIVE_FILES_URL3}?${params.toString()}`, {
|
|
1957
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
1958
|
+
});
|
|
1959
|
+
if (!response.ok) {
|
|
1960
|
+
const text = await response.text();
|
|
1961
|
+
throw new Error(`Drive API error ${response.status}: ${text}`);
|
|
1962
|
+
}
|
|
1963
|
+
const data = await response.json();
|
|
1964
|
+
results.push(...data.files);
|
|
1965
|
+
pageToken = data.nextPageToken;
|
|
1966
|
+
} while (pageToken);
|
|
1967
|
+
return results;
|
|
1968
|
+
}
|
|
1969
|
+
function inferLocaleFromDocName(name) {
|
|
1970
|
+
const baseName = name.replace(/\.[^.]+$/, "");
|
|
1971
|
+
const match = baseName.match(/_([a-zA-Z]{2,3}(?:[-_][a-zA-Z]{2,4})?)$/);
|
|
1972
|
+
if (!match) return void 0;
|
|
1973
|
+
const candidate = match[1].replace("_", "-");
|
|
1974
|
+
const parts = candidate.split("-");
|
|
1975
|
+
if (parts.length === 2) {
|
|
1976
|
+
return `${parts[0].toLowerCase()}-${parts[1].toUpperCase()}`;
|
|
1977
|
+
}
|
|
1978
|
+
return parts[0].toLowerCase();
|
|
1979
|
+
}
|
|
1980
|
+
async function scanFolder2(folderId, folderPath, token, recursive, nameFilter, seen = /* @__PURE__ */ new Set()) {
|
|
1981
|
+
console.log(
|
|
1982
|
+
`[driveDocScanner] Scanning folder: ${folderId} (path: "${folderPath}")`
|
|
1983
|
+
);
|
|
1984
|
+
const docs = await listFilesInFolder3(folderId, DOC_MIME, token);
|
|
1985
|
+
const results = [];
|
|
1986
|
+
for (const file of docs) {
|
|
1987
|
+
if (seen.has(file.id)) continue;
|
|
1988
|
+
seen.add(file.id);
|
|
1989
|
+
if (nameFilter && !nameFilter.test(file.name)) continue;
|
|
1990
|
+
results.push({
|
|
1991
|
+
id: file.id,
|
|
1992
|
+
name: file.name,
|
|
1993
|
+
folderPath,
|
|
1994
|
+
mimeType: file.mimeType,
|
|
1995
|
+
modifiedTime: file.modifiedTime,
|
|
1996
|
+
sourceLocale: inferLocaleFromDocName(file.name)
|
|
1997
|
+
});
|
|
1998
|
+
}
|
|
1999
|
+
if (recursive) {
|
|
2000
|
+
const subfolders = await listFilesInFolder3(folderId, FOLDER_MIME3, token);
|
|
2001
|
+
for (const folder of subfolders) {
|
|
2002
|
+
const subPath = folderPath ? `${folderPath}/${folder.name}` : folder.name;
|
|
2003
|
+
const subResults = await scanFolder2(
|
|
2004
|
+
folder.id,
|
|
2005
|
+
subPath,
|
|
2006
|
+
token,
|
|
2007
|
+
recursive,
|
|
2008
|
+
nameFilter,
|
|
2009
|
+
seen
|
|
2010
|
+
);
|
|
2011
|
+
results.push(...subResults);
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
return results;
|
|
2015
|
+
}
|
|
2016
|
+
async function scanDriveFolderForDocs(options) {
|
|
2017
|
+
const { folderId, recursive = true, nameFilter, credentials } = options;
|
|
2018
|
+
const token = await getAccessToken3(credentials);
|
|
2019
|
+
return scanFolder2(folderId, "", token, recursive, nameFilter);
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
// src/utils/docIngester.ts
|
|
2023
|
+
var import_google_auth_library3 = require("google-auth-library");
|
|
2024
|
+
var import_google_spreadsheet3 = require("google-spreadsheet");
|
|
2025
|
+
|
|
2026
|
+
// src/utils/docParser.ts
|
|
2027
|
+
function slugifyKey(text) {
|
|
2028
|
+
return text.toLowerCase().replace(/[^\w]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
|
|
2029
|
+
}
|
|
2030
|
+
function parseDocContent(content, options = {}) {
|
|
2031
|
+
const { strategy = "heading", defaultSheetName = "content" } = options;
|
|
2032
|
+
if (strategy === "marker") return parseWithMarkers(content, defaultSheetName);
|
|
2033
|
+
if (strategy === "numbered") return parseNumbered(content, defaultSheetName);
|
|
2034
|
+
return parseWithHeadings(content, defaultSheetName);
|
|
2035
|
+
}
|
|
2036
|
+
function parseWithHeadings(content, defaultSheetName) {
|
|
2037
|
+
const lines = content.split("\n");
|
|
2038
|
+
const entries = [];
|
|
2039
|
+
let currentSheet = defaultSheetName;
|
|
2040
|
+
let currentKey = null;
|
|
2041
|
+
const valueLines = [];
|
|
2042
|
+
function flushEntry() {
|
|
2043
|
+
if (currentKey !== null) {
|
|
2044
|
+
const value = valueLines.join("\n").trim();
|
|
2045
|
+
if (value) {
|
|
2046
|
+
entries.push({ sheetName: currentSheet, key: currentKey, value });
|
|
2047
|
+
}
|
|
2048
|
+
currentKey = null;
|
|
2049
|
+
valueLines.length = 0;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
for (const rawLine of lines) {
|
|
2053
|
+
const line = rawLine.trimEnd();
|
|
2054
|
+
if (line.startsWith("# ")) {
|
|
2055
|
+
flushEntry();
|
|
2056
|
+
currentSheet = slugifyKey(line.slice(2).trim()) || defaultSheetName;
|
|
2057
|
+
currentKey = null;
|
|
2058
|
+
valueLines.length = 0;
|
|
2059
|
+
} else if (line.startsWith("## ")) {
|
|
2060
|
+
flushEntry();
|
|
2061
|
+
currentKey = slugifyKey(line.slice(3).trim());
|
|
2062
|
+
valueLines.length = 0;
|
|
2063
|
+
} else if (currentKey !== null) {
|
|
2064
|
+
valueLines.push(line);
|
|
2065
|
+
}
|
|
2066
|
+
}
|
|
2067
|
+
flushEntry();
|
|
2068
|
+
return entries;
|
|
2069
|
+
}
|
|
2070
|
+
function parseWithMarkers(content, defaultSheetName) {
|
|
2071
|
+
const MARKER_RE = /\[\[key:([^\]]{1,200})\]\]/g;
|
|
2072
|
+
const entries = [];
|
|
2073
|
+
const segments = content.split(MARKER_RE);
|
|
2074
|
+
for (let i = 1; i < segments.length; i += 2) {
|
|
2075
|
+
const keyPath = segments[i].trim();
|
|
2076
|
+
const value = (segments[i + 1] ?? "").trim();
|
|
2077
|
+
if (!keyPath || !value) continue;
|
|
2078
|
+
const dotIdx = keyPath.indexOf(".");
|
|
2079
|
+
let sheetName;
|
|
2080
|
+
let key;
|
|
2081
|
+
if (dotIdx !== -1) {
|
|
2082
|
+
sheetName = slugifyKey(keyPath.slice(0, dotIdx));
|
|
2083
|
+
key = slugifyKey(keyPath.slice(dotIdx + 1));
|
|
2084
|
+
} else {
|
|
2085
|
+
sheetName = defaultSheetName;
|
|
2086
|
+
key = slugifyKey(keyPath);
|
|
2087
|
+
}
|
|
2088
|
+
if (sheetName && key) {
|
|
2089
|
+
entries.push({ sheetName, key, value });
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
return entries;
|
|
2093
|
+
}
|
|
2094
|
+
function parseNumbered(content, defaultSheetName) {
|
|
2095
|
+
const entries = [];
|
|
2096
|
+
let counter = 0;
|
|
2097
|
+
const paragraphs = content.split(/\n{2,}/);
|
|
2098
|
+
for (const para of paragraphs) {
|
|
2099
|
+
const value = para.replace(/^[#\s]+/, "").trim();
|
|
2100
|
+
if (value) {
|
|
2101
|
+
counter++;
|
|
2102
|
+
entries.push({
|
|
2103
|
+
sheetName: defaultSheetName,
|
|
2104
|
+
key: `item_${counter}`,
|
|
2105
|
+
value
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
return entries;
|
|
2110
|
+
}
|
|
2111
|
+
|
|
2112
|
+
// src/utils/docIngester.ts
|
|
2113
|
+
async function getDriveExportToken(credentials) {
|
|
2114
|
+
const clientEmail = credentials?.GOOGLE_CLIENT_EMAIL ?? process.env.GOOGLE_CLIENT_EMAIL;
|
|
2115
|
+
const privateKey = credentials?.GOOGLE_PRIVATE_KEY ?? process.env.GOOGLE_PRIVATE_KEY;
|
|
2116
|
+
if (!clientEmail || !privateKey) {
|
|
2117
|
+
throw new Error(
|
|
2118
|
+
"Google Drive credentials required: GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY"
|
|
2119
|
+
);
|
|
2120
|
+
}
|
|
2121
|
+
const normalizedKey = privateKey.replace(/\\n/g, "\n");
|
|
2122
|
+
const auth = new import_google_auth_library3.GoogleAuth({
|
|
2123
|
+
credentials: { client_email: clientEmail, private_key: normalizedKey },
|
|
2124
|
+
scopes: ["https://www.googleapis.com/auth/drive.readonly"]
|
|
2125
|
+
});
|
|
2126
|
+
const client = await auth.getClient();
|
|
2127
|
+
const tokenResponse = await client.getAccessToken();
|
|
2128
|
+
return tokenResponse.token;
|
|
2129
|
+
}
|
|
2130
|
+
async function exportDoc(docId, credentials) {
|
|
2131
|
+
const token = await getDriveExportToken(credentials);
|
|
2132
|
+
const base = `https://www.googleapis.com/drive/v3/files/${docId}/export`;
|
|
2133
|
+
const mdRes = await fetch(
|
|
2134
|
+
`${base}?mimeType=text%2Fmarkdown`,
|
|
2135
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
2136
|
+
);
|
|
2137
|
+
if (mdRes.ok) return mdRes.text();
|
|
2138
|
+
const txtRes = await fetch(
|
|
2139
|
+
`${base}?mimeType=text%2Fplain`,
|
|
2140
|
+
{ headers: { Authorization: `Bearer ${token}` } }
|
|
2141
|
+
);
|
|
2142
|
+
if (txtRes.ok) return txtRes.text();
|
|
2143
|
+
const errText = await txtRes.text();
|
|
2144
|
+
throw new Error(
|
|
2145
|
+
`Failed to export doc ${docId}: HTTP ${txtRes.status} \u2013 ${errText}`
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
2148
|
+
function entriesToSeedKeys(entries) {
|
|
2149
|
+
const keys = {};
|
|
2150
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2151
|
+
for (const entry of entries) {
|
|
2152
|
+
const base = `${entry.sheetName}.${entry.key}`;
|
|
2153
|
+
const count = (counts.get(base) ?? 0) + 1;
|
|
2154
|
+
counts.set(base, count);
|
|
2155
|
+
const finalKey = count > 1 ? `${base}_${count}` : base;
|
|
2156
|
+
keys[finalKey] = entry.value;
|
|
2157
|
+
}
|
|
2158
|
+
return keys;
|
|
2159
|
+
}
|
|
2160
|
+
function entriesToTranslationData(entries, locale) {
|
|
2161
|
+
const data = {};
|
|
2162
|
+
data[locale] = {};
|
|
2163
|
+
const counts = /* @__PURE__ */ new Map();
|
|
2164
|
+
for (const entry of entries) {
|
|
2165
|
+
const sheetName = entry.sheetName;
|
|
2166
|
+
const entryKey = entry.key;
|
|
2167
|
+
if (sheetName === "__proto__" || sheetName === "constructor" || sheetName === "prototype" || entryKey === "__proto__" || entryKey === "constructor" || entryKey === "prototype") {
|
|
2168
|
+
continue;
|
|
2169
|
+
}
|
|
2170
|
+
if (!data[locale][sheetName]) {
|
|
2171
|
+
data[locale][sheetName] = {};
|
|
2172
|
+
}
|
|
2173
|
+
const base = `${sheetName}::${entryKey}`;
|
|
2174
|
+
const count = (counts.get(base) ?? 0) + 1;
|
|
2175
|
+
counts.set(base, count);
|
|
2176
|
+
const finalKey = count > 1 ? `${entryKey}_${count}` : entryKey;
|
|
2177
|
+
data[locale][sheetName][finalKey] = entry.value;
|
|
2178
|
+
}
|
|
2179
|
+
return data;
|
|
2180
|
+
}
|
|
2181
|
+
function docTitle(name) {
|
|
2182
|
+
return name.replace(/_[a-zA-Z]{2,3}(?:[-_][a-zA-Z]{2,4})?$/, "").trim() || name;
|
|
2183
|
+
}
|
|
2184
|
+
async function ingestDoc(docFile, options = {}) {
|
|
2185
|
+
const {
|
|
2186
|
+
targetLocales,
|
|
2187
|
+
keyStrategy = "heading",
|
|
2188
|
+
updateMode = "create-only",
|
|
2189
|
+
credentials,
|
|
2190
|
+
existingEntry,
|
|
2191
|
+
waitSeconds = 1
|
|
2192
|
+
} = options;
|
|
2193
|
+
const sourceLocale = docFile.sourceLocale ?? "en";
|
|
2194
|
+
const entry = existingEntry ? { ...existingEntry, modifiedTime: docFile.modifiedTime } : {
|
|
2195
|
+
id: docFile.id,
|
|
2196
|
+
name: docFile.name,
|
|
2197
|
+
folderPath: docFile.folderPath,
|
|
2198
|
+
generatedFromDoc: true,
|
|
2199
|
+
sourceLocale,
|
|
2200
|
+
modifiedTime: docFile.modifiedTime
|
|
2201
|
+
};
|
|
2202
|
+
const hasLinkedSheet = !!entry.linkedSpreadsheetId;
|
|
2203
|
+
const shouldRefresh = updateMode === "refresh-if-newer" && hasLinkedSheet && !!docFile.modifiedTime && !!existingEntry?.lastIngestedAt && new Date(docFile.modifiedTime) > new Date(existingEntry.lastIngestedAt);
|
|
2204
|
+
if (hasLinkedSheet && !shouldRefresh) {
|
|
2205
|
+
console.log(
|
|
2206
|
+
`[docIngester] Skipping "${docFile.name}" \u2013 linked spreadsheet is already up-to-date.`
|
|
2207
|
+
);
|
|
2208
|
+
return { action: "skipped", entry };
|
|
2209
|
+
}
|
|
2210
|
+
console.log(
|
|
2211
|
+
`[docIngester] Exporting doc "${docFile.name}" (id: ${docFile.id})\u2026`
|
|
2212
|
+
);
|
|
2213
|
+
const content = await exportDoc(docFile.id, credentials);
|
|
2214
|
+
const sheetBaseName = slugifyKey(docTitle(docFile.name)) || "content";
|
|
2215
|
+
const entries = parseDocContent(content, {
|
|
2216
|
+
strategy: keyStrategy,
|
|
2217
|
+
defaultSheetName: sheetBaseName
|
|
2218
|
+
});
|
|
2219
|
+
if (entries.length === 0) {
|
|
2220
|
+
console.warn(
|
|
2221
|
+
`[docIngester] Doc "${docFile.name}" produced no translation entries \u2013 skipping.`
|
|
2222
|
+
);
|
|
2223
|
+
return { action: "skipped", entry };
|
|
2224
|
+
}
|
|
2225
|
+
if (!hasLinkedSheet) {
|
|
2226
|
+
const authClient2 = createAuthClient();
|
|
2227
|
+
const seedKeys = entriesToSeedKeys(entries);
|
|
2228
|
+
const title = docTitle(docFile.name);
|
|
2229
|
+
const { spreadsheetId: spreadsheetId2 } = await createSpreadsheet(authClient2, {
|
|
2230
|
+
title,
|
|
2231
|
+
sourceLocale,
|
|
2232
|
+
targetLocales,
|
|
2233
|
+
seedKeys
|
|
2234
|
+
});
|
|
2235
|
+
entry.linkedSpreadsheetId = spreadsheetId2;
|
|
2236
|
+
entry.lastIngestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2237
|
+
console.log(
|
|
2238
|
+
`[docIngester] Created spreadsheet ${spreadsheetId2} from doc "${docFile.name}".`
|
|
2239
|
+
);
|
|
2240
|
+
return { action: "created", entry };
|
|
2241
|
+
}
|
|
2242
|
+
const authClient = createAuthClient();
|
|
2243
|
+
const spreadsheetId = entry.linkedSpreadsheetId;
|
|
2244
|
+
const doc = new import_google_spreadsheet3.GoogleSpreadsheet(spreadsheetId, authClient);
|
|
2245
|
+
await doc.loadInfo();
|
|
2246
|
+
const changes = entriesToTranslationData(entries, sourceLocale);
|
|
2247
|
+
await updateSpreadsheetWithLocalChanges(
|
|
2248
|
+
doc,
|
|
2249
|
+
changes,
|
|
2250
|
+
waitSeconds,
|
|
2251
|
+
false,
|
|
2252
|
+
// autoTranslate – formulas already exist in non-base columns
|
|
2253
|
+
{},
|
|
2254
|
+
false
|
|
2255
|
+
);
|
|
2256
|
+
entry.lastIngestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2257
|
+
console.log(
|
|
2258
|
+
`[docIngester] Refreshed spreadsheet ${spreadsheetId} from doc "${docFile.name}".`
|
|
2259
|
+
);
|
|
2260
|
+
return { action: "refreshed", entry };
|
|
2261
|
+
}
|
|
1864
2262
|
|
|
1865
2263
|
// src/utils/getDriveTranslations.ts
|
|
1866
2264
|
function sanitizeFolderName(name) {
|
|
1867
2265
|
return name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "") || "sheet";
|
|
1868
2266
|
}
|
|
2267
|
+
async function moveSpreadsheetToFolder(spreadsheetId, folderId) {
|
|
2268
|
+
const clientEmail = process.env.GOOGLE_CLIENT_EMAIL;
|
|
2269
|
+
const rawPrivateKey = process.env.GOOGLE_PRIVATE_KEY;
|
|
2270
|
+
let credentials;
|
|
2271
|
+
if (clientEmail && rawPrivateKey) {
|
|
2272
|
+
credentials = { client_email: clientEmail, private_key: normalizePrivateKey(rawPrivateKey) };
|
|
2273
|
+
}
|
|
2274
|
+
const driveAuth = buildGoogleAuth(
|
|
2275
|
+
["https://www.googleapis.com/auth/drive.file"],
|
|
2276
|
+
credentials
|
|
2277
|
+
);
|
|
2278
|
+
const fileRes = await driveAuth.request({
|
|
2279
|
+
url: `https://www.googleapis.com/drive/v3/files/${spreadsheetId}`,
|
|
2280
|
+
params: { fields: "parents" }
|
|
2281
|
+
});
|
|
2282
|
+
const parentIds = fileRes.data.parents ?? [];
|
|
2283
|
+
await driveAuth.request({
|
|
2284
|
+
url: `https://www.googleapis.com/drive/v3/files/${spreadsheetId}`,
|
|
2285
|
+
method: "PATCH",
|
|
2286
|
+
params: {
|
|
2287
|
+
addParents: folderId,
|
|
2288
|
+
...parentIds.length > 0 ? { removeParents: parentIds.join(",") } : {},
|
|
2289
|
+
fields: "id,parents"
|
|
2290
|
+
}
|
|
2291
|
+
});
|
|
2292
|
+
}
|
|
1869
2293
|
async function manageDriveTranslations(options) {
|
|
1870
2294
|
const {
|
|
1871
2295
|
driveFolderId,
|
|
@@ -1883,7 +2307,14 @@ async function manageDriveTranslations(options) {
|
|
|
1883
2307
|
projectName,
|
|
1884
2308
|
domain,
|
|
1885
2309
|
defaultLocale,
|
|
1886
|
-
projectMetadata
|
|
2310
|
+
projectMetadata,
|
|
2311
|
+
// Doc ingestion options
|
|
2312
|
+
scanForDocs = false,
|
|
2313
|
+
docNameFilter,
|
|
2314
|
+
docSourceLocale,
|
|
2315
|
+
docKeyStrategy,
|
|
2316
|
+
docUpdateMode,
|
|
2317
|
+
docTargetLocales
|
|
1887
2318
|
} = options;
|
|
1888
2319
|
if (syncImages && !imageOutputPath) {
|
|
1889
2320
|
throw new Error(
|
|
@@ -1914,6 +2345,36 @@ async function manageDriveTranslations(options) {
|
|
|
1914
2345
|
if (!name) return true;
|
|
1915
2346
|
return spreadsheetNameFilter.test(name);
|
|
1916
2347
|
}) : allIds;
|
|
2348
|
+
if (driveFolderId && filteredIds.length === 0 && translationOptions.autoCreate !== false) {
|
|
2349
|
+
console.log(
|
|
2350
|
+
`[manageDriveTranslations] Drive folder "${driveFolderId}" contains no spreadsheets. Bootstrapping a new spreadsheet\u2026`
|
|
2351
|
+
);
|
|
2352
|
+
const authClient = createAuthClient();
|
|
2353
|
+
const bootstrapTitle = translationOptions.spreadsheetTitle ?? "google-sheet-translations";
|
|
2354
|
+
const created = await createSpreadsheet(authClient, {
|
|
2355
|
+
title: bootstrapTitle,
|
|
2356
|
+
sourceLocale: translationOptions.sourceLocale,
|
|
2357
|
+
targetLocales: translationOptions.targetLocales
|
|
2358
|
+
});
|
|
2359
|
+
console.log(`[manageDriveTranslations] \u2705 Spreadsheet created: ${created.url}`);
|
|
2360
|
+
try {
|
|
2361
|
+
await moveSpreadsheetToFolder(created.spreadsheetId, driveFolderId);
|
|
2362
|
+
console.log(
|
|
2363
|
+
`[manageDriveTranslations] \u2705 Spreadsheet moved into Drive folder "${driveFolderId}"`
|
|
2364
|
+
);
|
|
2365
|
+
} catch (moveErr) {
|
|
2366
|
+
console.warn(
|
|
2367
|
+
`[manageDriveTranslations] \u26A0\uFE0F Could not move spreadsheet into Drive folder:`,
|
|
2368
|
+
moveErr.message
|
|
2369
|
+
);
|
|
2370
|
+
console.warn(
|
|
2371
|
+
` Please move spreadsheet "${created.spreadsheetId}" into folder "${driveFolderId}" manually.`
|
|
2372
|
+
);
|
|
2373
|
+
}
|
|
2374
|
+
filteredIds.push(created.spreadsheetId);
|
|
2375
|
+
discoveredNames.set(created.spreadsheetId, bootstrapTitle);
|
|
2376
|
+
discoveredFolderPaths.set(created.spreadsheetId, "");
|
|
2377
|
+
}
|
|
1917
2378
|
let translations;
|
|
1918
2379
|
const spreadsheetEntries = [];
|
|
1919
2380
|
const baseOutputDir = translationOptions.translationsOutputDir ?? "translations";
|
|
@@ -1967,8 +2428,48 @@ async function manageDriveTranslations(options) {
|
|
|
1967
2428
|
});
|
|
1968
2429
|
}
|
|
1969
2430
|
let manifest;
|
|
2431
|
+
let docIngestResults;
|
|
2432
|
+
const docEntries = [];
|
|
2433
|
+
const resolvedManifestPath = manifestPath ?? import_node_path8.default.join(baseOutputDir, "i18n-manifest.json");
|
|
2434
|
+
if (driveFolderId && scanForDocs) {
|
|
2435
|
+
const previousManifest = readManifest(resolvedManifestPath);
|
|
2436
|
+
const docScanOptions = {
|
|
2437
|
+
folderId: driveFolderId
|
|
2438
|
+
};
|
|
2439
|
+
const discoveredDocs = await scanDriveFolderForDocs(docScanOptions);
|
|
2440
|
+
console.log(
|
|
2441
|
+
`[manageDriveTranslations] Found ${discoveredDocs.length} doc(s) in Drive folder`
|
|
2442
|
+
);
|
|
2443
|
+
docIngestResults = [];
|
|
2444
|
+
for (const docFile of discoveredDocs) {
|
|
2445
|
+
if (docNameFilter && !docNameFilter.test(docFile.name)) continue;
|
|
2446
|
+
if (!docFile.sourceLocale && docSourceLocale) {
|
|
2447
|
+
docFile.sourceLocale = docSourceLocale;
|
|
2448
|
+
}
|
|
2449
|
+
const existingEntry = previousManifest?.docs?.find(
|
|
2450
|
+
(d) => d.id === docFile.id
|
|
2451
|
+
);
|
|
2452
|
+
const ingesterOptions = {
|
|
2453
|
+
targetLocales: docTargetLocales,
|
|
2454
|
+
keyStrategy: docKeyStrategy,
|
|
2455
|
+
updateMode: docUpdateMode,
|
|
2456
|
+
existingEntry,
|
|
2457
|
+
waitSeconds: translationOptions.waitSeconds
|
|
2458
|
+
};
|
|
2459
|
+
try {
|
|
2460
|
+
const result = await ingestDoc(docFile, ingesterOptions);
|
|
2461
|
+
docEntries.push(result.entry);
|
|
2462
|
+
docIngestResults.push({ docName: docFile.name, action: result.action });
|
|
2463
|
+
} catch (err) {
|
|
2464
|
+
console.error(
|
|
2465
|
+
`[manageDriveTranslations] Failed to ingest doc "${docFile.name}":`,
|
|
2466
|
+
err
|
|
2467
|
+
);
|
|
2468
|
+
if (existingEntry) docEntries.push(existingEntry);
|
|
2469
|
+
}
|
|
2470
|
+
}
|
|
2471
|
+
}
|
|
1970
2472
|
if (shouldCreateManifest) {
|
|
1971
|
-
const resolvedManifestPath = manifestPath ?? import_node_path8.default.join(baseOutputDir, "i18n-manifest.json");
|
|
1972
2473
|
manifest = buildManifest({
|
|
1973
2474
|
translations,
|
|
1974
2475
|
spreadsheets: spreadsheetEntries,
|
|
@@ -1977,11 +2478,12 @@ async function manageDriveTranslations(options) {
|
|
|
1977
2478
|
projectName,
|
|
1978
2479
|
domain,
|
|
1979
2480
|
defaultLocale,
|
|
1980
|
-
projectMetadata
|
|
2481
|
+
projectMetadata,
|
|
2482
|
+
docs: docEntries.length > 0 ? docEntries : void 0
|
|
1981
2483
|
});
|
|
1982
2484
|
writeManifest(manifest, resolvedManifestPath);
|
|
1983
2485
|
}
|
|
1984
|
-
return { translations, spreadsheetIds: filteredIds, imageSync, manifest };
|
|
2486
|
+
return { translations, spreadsheetIds: filteredIds, imageSync, manifest, docIngestResults };
|
|
1985
2487
|
}
|
|
1986
2488
|
|
|
1987
2489
|
// src/index.ts
|
|
@@ -1990,12 +2492,16 @@ var index_default = getSpreadSheetData;
|
|
|
1990
2492
|
0 && (module.exports = {
|
|
1991
2493
|
DEFAULT_IMAGE_EXTENSIONS,
|
|
1992
2494
|
DEFAULT_WAIT_SECONDS,
|
|
2495
|
+
buildGoogleAuth,
|
|
1993
2496
|
buildManifest,
|
|
1994
2497
|
convertFromDataJsonFormat,
|
|
1995
2498
|
convertToDataJsonFormat,
|
|
1996
2499
|
createAuthClient,
|
|
1997
2500
|
createLocaleMapping,
|
|
1998
2501
|
createSpreadsheet,
|
|
2502
|
+
entriesToSeedKeys,
|
|
2503
|
+
entriesToTranslationData,
|
|
2504
|
+
exportDoc,
|
|
1999
2505
|
filterValidLocales,
|
|
2000
2506
|
findLocalChanges,
|
|
2001
2507
|
getGoogleTranslateCode,
|
|
@@ -2007,16 +2513,23 @@ var index_default = getSpreadSheetData;
|
|
|
2007
2513
|
getSpreadSheetData,
|
|
2008
2514
|
getTranslationSummary,
|
|
2009
2515
|
handleBidirectionalSync,
|
|
2516
|
+
inferLocaleFromDocName,
|
|
2517
|
+
ingestDoc,
|
|
2010
2518
|
isValidLocale,
|
|
2011
2519
|
manageDriveTranslations,
|
|
2012
2520
|
mergeMultipleTranslationData,
|
|
2013
2521
|
mergeSheets,
|
|
2014
2522
|
normalizeExtension,
|
|
2015
2523
|
normalizeLocaleCode,
|
|
2524
|
+
normalizePrivateKey,
|
|
2525
|
+
parseDocContent,
|
|
2016
2526
|
processRawRows,
|
|
2527
|
+
readManifest,
|
|
2017
2528
|
readPublicSheet,
|
|
2018
2529
|
resolveLocaleWithFallback,
|
|
2530
|
+
scanDriveFolderForDocs,
|
|
2019
2531
|
scanDriveFolderForSpreadsheets,
|
|
2532
|
+
slugifyKey,
|
|
2020
2533
|
syncDriveImages,
|
|
2021
2534
|
updateSpreadsheetWithLocalChanges,
|
|
2022
2535
|
validateCredentials,
|