@tandem-language-exchange/content-store 1.1.2 → 1.2.1
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 +163 -20
- package/dist/chunk-EQ3DSPTJ.js +86 -0
- package/dist/chunk-EQ3DSPTJ.js.map +1 -0
- package/dist/{chunk-JBFJU4JA.js → chunk-LOCC2BXB.js} +118 -13
- package/dist/chunk-LOCC2BXB.js.map +1 -0
- package/dist/chunk-OTZLCMZ6.js +396 -0
- package/dist/chunk-OTZLCMZ6.js.map +1 -0
- package/dist/{chunk-YOREZCXB.js → chunk-PQJ2MGH7.js} +180 -29
- package/dist/chunk-PQJ2MGH7.js.map +1 -0
- package/dist/client/cli.js +31 -8
- package/dist/client/cli.js.map +1 -1
- package/dist/client/{fetch-bundles.js → fetch-content-bundles.js} +8 -7
- package/dist/client/fetch-content-bundles.js.map +1 -0
- package/dist/client/fetch-merged-translation-bundles.js +40 -0
- package/dist/client/fetch-merged-translation-bundles.js.map +1 -0
- package/dist/client/fetch-translation-bundles.js +43 -0
- package/dist/client/fetch-translation-bundles.js.map +1 -0
- package/dist/index-kfqHGgMO.d.ts +118 -0
- package/dist/index.d.ts +1 -1
- package/dist/node.browser.js +20 -6
- package/dist/node.browser.js.map +1 -1
- package/dist/node.d.ts +18 -5
- package/dist/node.js +527 -41
- package/dist/node.js.map +1 -1
- package/package.json +25 -11
- package/dist/chunk-JBFJU4JA.js.map +0 -1
- package/dist/chunk-UWGOF36L.js +0 -85
- package/dist/chunk-UWGOF36L.js.map +0 -1
- package/dist/chunk-YOREZCXB.js.map +0 -1
- package/dist/client/fetch-bundles.js.map +0 -1
- package/dist/index-DJXkO17k.d.ts +0 -83
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/shared/s3.ts
|
|
4
|
+
import {
|
|
5
|
+
S3Client,
|
|
6
|
+
PutObjectCommand,
|
|
7
|
+
GetObjectCommand
|
|
8
|
+
} from "@aws-sdk/client-s3";
|
|
9
|
+
var ContentStore = class {
|
|
10
|
+
client;
|
|
11
|
+
bucket;
|
|
12
|
+
constructor(cfg) {
|
|
13
|
+
this.client = new S3Client({
|
|
14
|
+
region: cfg.region,
|
|
15
|
+
credentials: {
|
|
16
|
+
accessKeyId: cfg.accessKeyId,
|
|
17
|
+
secretAccessKey: cfg.secretAccessKey
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
this.bucket = cfg.bucket;
|
|
21
|
+
}
|
|
22
|
+
async upload(key, data) {
|
|
23
|
+
await this.client.send(
|
|
24
|
+
new PutObjectCommand({
|
|
25
|
+
Bucket: this.bucket,
|
|
26
|
+
Key: key,
|
|
27
|
+
Body: JSON.stringify(data, null, 2),
|
|
28
|
+
ContentType: "application/json"
|
|
29
|
+
})
|
|
30
|
+
);
|
|
31
|
+
return key;
|
|
32
|
+
}
|
|
33
|
+
/** Raw UTF-8 body (e.g. Lingohub file bytes as text). */
|
|
34
|
+
async uploadRaw(key, body, contentType) {
|
|
35
|
+
await this.client.send(
|
|
36
|
+
new PutObjectCommand({
|
|
37
|
+
Bucket: this.bucket,
|
|
38
|
+
Key: key,
|
|
39
|
+
Body: body,
|
|
40
|
+
ContentType: contentType
|
|
41
|
+
})
|
|
42
|
+
);
|
|
43
|
+
return key;
|
|
44
|
+
}
|
|
45
|
+
async download(key) {
|
|
46
|
+
const response = await this.client.send(
|
|
47
|
+
new GetObjectCommand({ Bucket: this.bucket, Key: key })
|
|
48
|
+
);
|
|
49
|
+
const body = await response.Body?.transformToString();
|
|
50
|
+
if (!body) throw new Error(`Empty response for key: ${key}`);
|
|
51
|
+
return JSON.parse(body);
|
|
52
|
+
}
|
|
53
|
+
/** Raw UTF-8 body from S3 (no JSON.parse). */
|
|
54
|
+
async downloadRaw(key) {
|
|
55
|
+
const response = await this.client.send(
|
|
56
|
+
new GetObjectCommand({ Bucket: this.bucket, Key: key })
|
|
57
|
+
);
|
|
58
|
+
const body = await response.Body?.transformToString();
|
|
59
|
+
if (!body) throw new Error(`Empty response for key: ${key}`);
|
|
60
|
+
return body;
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
var buildCmsObjectKey = (cms, contentType) => {
|
|
64
|
+
return `${cms}-${contentType}.json`;
|
|
65
|
+
};
|
|
66
|
+
var buildTranslationObjectKey = (project, fileName, locale) => {
|
|
67
|
+
return `lingohub-${project}.${fileName.replaceAll("[locale]", locale)}`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// src/shared/lingohub.ts
|
|
71
|
+
var defaultLocales = ["en", "fr", "de", "es", "it", "pt-br", "ru", "zh-hans", "zh-hant", "ko", "ja"];
|
|
72
|
+
var localeMapping = {
|
|
73
|
+
ios: {
|
|
74
|
+
"pt-br": "pt",
|
|
75
|
+
"zh-hans": "zh-Hans",
|
|
76
|
+
"zh-hant": "zh-Hant"
|
|
77
|
+
},
|
|
78
|
+
android: {
|
|
79
|
+
"pt-br": "pt",
|
|
80
|
+
"zh-hans": "zh-Hans-CN",
|
|
81
|
+
"zh-hant": "zh-TW"
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var allProjects = {
|
|
85
|
+
"tandem-(new-website)": [
|
|
86
|
+
{
|
|
87
|
+
fileName: "Website.[locale].json",
|
|
88
|
+
type: "json"
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
"tandem-(website)": [
|
|
92
|
+
{
|
|
93
|
+
fileName: "[locale].json",
|
|
94
|
+
type: "json"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
fileName: "AI.[locale].json",
|
|
98
|
+
type: "json"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
fileName: "languages.[locale].json",
|
|
102
|
+
type: "json"
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
"tandem": [
|
|
106
|
+
{
|
|
107
|
+
fileName: "InfoPlist.[locale].strings",
|
|
108
|
+
type: "strings",
|
|
109
|
+
localeMapping: localeMapping.ios
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
fileName: "Localizable.[locale].strings",
|
|
113
|
+
type: "strings",
|
|
114
|
+
localeMapping: localeMapping.ios
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
fileName: "MainiPad.[locale].strings",
|
|
118
|
+
type: "strings",
|
|
119
|
+
localeMapping: localeMapping.ios
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
fileName: "Main.[locale].strings",
|
|
123
|
+
type: "strings",
|
|
124
|
+
localeMapping: localeMapping.ios
|
|
125
|
+
}
|
|
126
|
+
],
|
|
127
|
+
"tandem-(android)": [
|
|
128
|
+
{
|
|
129
|
+
fileName: "accessibility_localizable.[locale].xml",
|
|
130
|
+
type: "xml",
|
|
131
|
+
localeMapping: localeMapping.android
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
fileName: "call_localizable.[locale].xml",
|
|
135
|
+
type: "xml",
|
|
136
|
+
localeMapping: localeMapping.android
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
fileName: "cert_localizable.[locale].xml",
|
|
140
|
+
type: "xml",
|
|
141
|
+
localeMapping: localeMapping.android
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
fileName: "chat_localizable.[locale].xml",
|
|
145
|
+
type: "xml",
|
|
146
|
+
localeMapping: localeMapping.android
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
fileName: "checklist_localizable.[locale].xml",
|
|
150
|
+
type: "xml",
|
|
151
|
+
localeMapping: localeMapping.android
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
fileName: "clubs_localizable.[locale].xml",
|
|
155
|
+
type: "xml",
|
|
156
|
+
localeMapping: localeMapping.android
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
fileName: "common_localizable.[locale].xml",
|
|
160
|
+
type: "xml",
|
|
161
|
+
localeMapping: localeMapping.android
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
fileName: "community_localizable.[locale].xml",
|
|
165
|
+
type: "xml",
|
|
166
|
+
localeMapping: localeMapping.android
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
fileName: "correction_localizable.[locale].xml",
|
|
170
|
+
type: "xml",
|
|
171
|
+
localeMapping: localeMapping.android
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
fileName: "country_names.[locale].xml",
|
|
175
|
+
type: "xml",
|
|
176
|
+
localeMapping: localeMapping.android
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
fileName: "emoji_localizable.[locale].xml",
|
|
180
|
+
type: "xml",
|
|
181
|
+
localeMapping: localeMapping.android
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
fileName: "errors_localizable.[locale].xml",
|
|
185
|
+
type: "xml",
|
|
186
|
+
localeMapping: localeMapping.android
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
fileName: "expressions_localizable.[locale].xml",
|
|
190
|
+
type: "xml",
|
|
191
|
+
localeMapping: localeMapping.android
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
fileName: "gif_localizable.[locale].xml",
|
|
195
|
+
type: "xml",
|
|
196
|
+
localeMapping: localeMapping.android
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
fileName: "guidelines_localizable.[locale].xml",
|
|
200
|
+
type: "xml",
|
|
201
|
+
localeMapping: localeMapping.android
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
fileName: "languages_localizable.[locale].xml",
|
|
205
|
+
type: "xml",
|
|
206
|
+
localeMapping: localeMapping.android
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
fileName: "localizable.[locale].xml",
|
|
210
|
+
type: "xml",
|
|
211
|
+
localeMapping: localeMapping.android
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
fileName: "localizable2.[locale].xml",
|
|
215
|
+
type: "xml",
|
|
216
|
+
localeMapping: localeMapping.android
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
fileName: "login_localizable.[locale].xml",
|
|
220
|
+
type: "xml",
|
|
221
|
+
localeMapping: localeMapping.android
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
fileName: "myprofile_localizable.[locale].xml",
|
|
225
|
+
type: "xml",
|
|
226
|
+
localeMapping: localeMapping.android
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
fileName: "onb_localizable.[locale].xml",
|
|
230
|
+
type: "xml",
|
|
231
|
+
localeMapping: localeMapping.android
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
fileName: "parties_localizable.[locale].xml",
|
|
235
|
+
type: "xml",
|
|
236
|
+
localeMapping: localeMapping.android
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
fileName: "pro_localizable.[locale].xml",
|
|
240
|
+
type: "xml",
|
|
241
|
+
localeMapping: localeMapping.android
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
fileName: "pro_screen_localizable.[locale].xml",
|
|
245
|
+
type: "xml",
|
|
246
|
+
localeMapping: localeMapping.android
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
fileName: "push_notification_localizable.[locale].xml",
|
|
250
|
+
type: "xml",
|
|
251
|
+
localeMapping: localeMapping.android
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
fileName: "reporting_localizable.[locale].xml",
|
|
255
|
+
type: "xml",
|
|
256
|
+
localeMapping: localeMapping.android
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
fileName: "translation_localizable.[locale].xml",
|
|
260
|
+
type: "xml",
|
|
261
|
+
localeMapping: localeMapping.android
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
"tandem-(web-invites)": [
|
|
265
|
+
{
|
|
266
|
+
fileName: "[locale].json",
|
|
267
|
+
type: "json"
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
// src/shared/translationResource.ts
|
|
273
|
+
import path from "path";
|
|
274
|
+
|
|
275
|
+
// src/shared/utils.ts
|
|
276
|
+
import convert from "xml-js";
|
|
277
|
+
import set from "lodash.set";
|
|
278
|
+
import merge from "lodash.merge";
|
|
279
|
+
var transformObjectToFlat = (data) => {
|
|
280
|
+
const result = {};
|
|
281
|
+
const flatten = (obj, path2 = []) => {
|
|
282
|
+
Object.entries(obj).forEach(([key, value]) => {
|
|
283
|
+
if (typeof value === "object") {
|
|
284
|
+
flatten(value, path2.concat(key));
|
|
285
|
+
} else {
|
|
286
|
+
result[path2.concat(key).join(".")] = value;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
flatten(data);
|
|
291
|
+
return result;
|
|
292
|
+
};
|
|
293
|
+
var convertXMLToJS = (xml) => {
|
|
294
|
+
const converted = convert.xml2js(xml, {
|
|
295
|
+
ignoreComment: true,
|
|
296
|
+
ignoreDeclaration: true,
|
|
297
|
+
ignoreInstruction: true,
|
|
298
|
+
compact: true
|
|
299
|
+
});
|
|
300
|
+
let mapped = {};
|
|
301
|
+
converted.resources.string.forEach((item) => {
|
|
302
|
+
mapped = {
|
|
303
|
+
...mapped,
|
|
304
|
+
[item._attributes.name]: item._text
|
|
305
|
+
};
|
|
306
|
+
});
|
|
307
|
+
return mapped;
|
|
308
|
+
};
|
|
309
|
+
var parseIOSStrings = (strings) => {
|
|
310
|
+
const parsedObj = {};
|
|
311
|
+
strings.split("\n").filter((line) => line.startsWith('"') && line.endsWith(";")).map((line) => line.trim().slice(0, -1)).forEach((line) => {
|
|
312
|
+
let [key, value] = line.split(" = ");
|
|
313
|
+
if (!key || !value) return;
|
|
314
|
+
key = key.slice(1, -1);
|
|
315
|
+
value = value.slice(1, -1);
|
|
316
|
+
parsedObj[key] = value;
|
|
317
|
+
});
|
|
318
|
+
return parsedObj;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// src/shared/translationResource.ts
|
|
322
|
+
function contentTypeForTranslationKey(objectKey) {
|
|
323
|
+
if (objectKey.endsWith(".json")) return "application/json; charset=utf-8";
|
|
324
|
+
if (objectKey.endsWith(".xml")) return "application/xml; charset=utf-8";
|
|
325
|
+
if (objectKey.endsWith(".strings")) return "text/plain; charset=utf-8";
|
|
326
|
+
return "application/octet-stream";
|
|
327
|
+
}
|
|
328
|
+
function parseTranslationResourceRaw(raw, resource) {
|
|
329
|
+
if (resource.type === "json") {
|
|
330
|
+
return JSON.parse(raw);
|
|
331
|
+
}
|
|
332
|
+
if (resource.type === "strings") {
|
|
333
|
+
return parseIOSStrings(raw);
|
|
334
|
+
}
|
|
335
|
+
if (resource.type === "xml") {
|
|
336
|
+
return convertXMLToJS(raw);
|
|
337
|
+
}
|
|
338
|
+
throw new Error(`Unsupported resource type: ${resource.type}`);
|
|
339
|
+
}
|
|
340
|
+
function toFlatStringMap(parsed) {
|
|
341
|
+
if (parsed === null || parsed === void 0) return {};
|
|
342
|
+
if (typeof parsed === "string") return { value: parsed };
|
|
343
|
+
if (typeof parsed !== "object") return { value: String(parsed) };
|
|
344
|
+
if (Array.isArray(parsed)) {
|
|
345
|
+
const out2 = {};
|
|
346
|
+
parsed.forEach((v, i) => {
|
|
347
|
+
out2[String(i)] = typeof v === "object" && v !== null ? JSON.stringify(v) : String(v);
|
|
348
|
+
});
|
|
349
|
+
return out2;
|
|
350
|
+
}
|
|
351
|
+
const flat = transformObjectToFlat(parsed);
|
|
352
|
+
const out = {};
|
|
353
|
+
for (const [k, v] of Object.entries(flat)) {
|
|
354
|
+
if (v === null || v === void 0) {
|
|
355
|
+
out[k] = "";
|
|
356
|
+
} else if (typeof v === "object") {
|
|
357
|
+
out[k] = JSON.stringify(v);
|
|
358
|
+
} else {
|
|
359
|
+
out[k] = String(v);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return out;
|
|
363
|
+
}
|
|
364
|
+
function translationJsonOutputPath(outputDir, objectKey) {
|
|
365
|
+
if (objectKey.endsWith(".json")) {
|
|
366
|
+
return path.resolve(outputDir, objectKey);
|
|
367
|
+
}
|
|
368
|
+
return path.resolve(outputDir, `${objectKey}.json`);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/shared/config.ts
|
|
372
|
+
import dotenv from "dotenv";
|
|
373
|
+
dotenv.config({ path: ".env.local" });
|
|
374
|
+
dotenv.config();
|
|
375
|
+
var config = {
|
|
376
|
+
s3: {
|
|
377
|
+
bucket: process.env.CONTENT_STORE_S3_BUCKET ?? "",
|
|
378
|
+
region: process.env.CONTENT_STORE_S3_REGION ?? "eu-central-1",
|
|
379
|
+
accessKeyId: process.env.AWS_ACCESS_KEY ?? "",
|
|
380
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? ""
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
export {
|
|
385
|
+
config,
|
|
386
|
+
ContentStore,
|
|
387
|
+
buildCmsObjectKey,
|
|
388
|
+
buildTranslationObjectKey,
|
|
389
|
+
defaultLocales,
|
|
390
|
+
allProjects,
|
|
391
|
+
contentTypeForTranslationKey,
|
|
392
|
+
parseTranslationResourceRaw,
|
|
393
|
+
toFlatStringMap,
|
|
394
|
+
translationJsonOutputPath
|
|
395
|
+
};
|
|
396
|
+
//# sourceMappingURL=chunk-OTZLCMZ6.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/s3.ts","../src/shared/lingohub.ts","../src/shared/translationResource.ts","../src/shared/utils.ts","../src/shared/config.ts"],"sourcesContent":["import {\n S3Client,\n PutObjectCommand,\n GetObjectCommand,\n} from '@aws-sdk/client-s3';\nimport type { S3Config } from './types';\n\nexport class ContentStore {\n private client: S3Client;\n private bucket: string;\n\n constructor(cfg: S3Config) {\n this.client = new S3Client({\n region: cfg.region,\n credentials: {\n accessKeyId: cfg.accessKeyId,\n secretAccessKey: cfg.secretAccessKey,\n },\n });\n this.bucket = cfg.bucket;\n }\n\n async upload(key: string, data: unknown): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: JSON.stringify(data, null, 2),\n ContentType: 'application/json',\n }),\n );\n return key;\n }\n\n /** Raw UTF-8 body (e.g. Lingohub file bytes as text). */\n async uploadRaw(\n key: string,\n body: string,\n contentType: string,\n ): Promise<string> {\n await this.client.send(\n new PutObjectCommand({\n Bucket: this.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n }),\n );\n return key;\n }\n\n async download(key: string): Promise<unknown> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n );\n const body = await response.Body?.transformToString();\n if (!body) throw new Error(`Empty response for key: ${key}`);\n return JSON.parse(body);\n }\n\n /** Raw UTF-8 body from S3 (no JSON.parse). */\n async downloadRaw(key: string): Promise<string> {\n const response = await this.client.send(\n new GetObjectCommand({ Bucket: this.bucket, Key: key }),\n );\n const body = await response.Body?.transformToString();\n if (!body) throw new Error(`Empty response for key: ${key}`);\n return body;\n }\n}\n\n/** {cms}-{contentType}.json (always points at the latest version) */\nexport const buildCmsObjectKey = (cms: string, contentType: string): string => {\n return `${cms}-${contentType}.json`;\n}\n\nexport const buildTranslationObjectKey = (project:string, fileName: string, locale: string): string => {\n return `lingohub-${project}.${fileName.replaceAll('[locale]', locale)}`;\n}\n","export const defaultLocales = ['en', 'fr', 'de', 'es', 'it', 'pt-br', 'ru', 'zh-hans', 'zh-hant', 'ko', 'ja'];\n\nconst localeMapping = {\n ios: {\n 'pt-br': 'pt',\n 'zh-hans': 'zh-Hans',\n 'zh-hant': 'zh-Hant',\n },\n android: {\n 'pt-br': 'pt',\n 'zh-hans': 'zh-Hans-CN',\n 'zh-hant': 'zh-TW',\n },\n};\n\nexport type LingohubResource = {\n fileName: string;\n type: 'json'|'strings'|'xml';\n localeMapping?: Record<string, string>;\n}\n\nexport const allProjects: Record<string, LingohubResource[]> = {\n 'tandem-(new-website)': [\n {\n fileName: 'Website.[locale].json',\n type: 'json'\n }\n ],\n 'tandem-(website)': [\n {\n fileName: '[locale].json',\n type: 'json'\n },\n {\n fileName: 'AI.[locale].json',\n type: 'json'\n },\n {\n fileName: 'languages.[locale].json',\n type: 'json'\n }\n ],\n 'tandem': [\n {\n fileName: 'InfoPlist.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'Localizable.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'MainiPad.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n },\n {\n fileName: 'Main.[locale].strings',\n type: 'strings',\n localeMapping: localeMapping.ios,\n }\n ],\n 'tandem-(android)': [\n {\n fileName: 'accessibility_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'call_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'cert_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'chat_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'checklist_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'clubs_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'common_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'community_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'correction_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'country_names.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'emoji_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'errors_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'expressions_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'gif_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'guidelines_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'languages_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'localizable2.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'login_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'myprofile_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'onb_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'parties_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'pro_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'pro_screen_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'push_notification_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'reporting_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n },\n {\n fileName: 'translation_localizable.[locale].xml',\n type: 'xml',\n localeMapping: localeMapping.android,\n }\n\n ],\n 'tandem-(web-invites)': [\n {\n fileName: '[locale].json',\n type: 'json'\n }\n ]\n};\n","import path from 'node:path';\nimport { convertXMLToJS, parseIOSStrings, transformObjectToFlat } from './utils';\nimport type { LingohubResource } from './lingohub';\n\n/** Content-Type for raw Lingohub bodies stored in S3. */\nexport function contentTypeForTranslationKey(objectKey: string): string {\n if (objectKey.endsWith('.json')) return 'application/json; charset=utf-8';\n if (objectKey.endsWith('.xml')) return 'application/xml; charset=utf-8';\n if (objectKey.endsWith('.strings')) return 'text/plain; charset=utf-8';\n return 'application/octet-stream';\n}\n\n/**\n * Parses a raw Lingohub file body from S3 into structured data.\n */\nexport function parseTranslationResourceRaw(\n raw: string,\n resource: LingohubResource,\n): unknown {\n if (resource.type === 'json') {\n return JSON.parse(raw) as unknown;\n }\n\n if (resource.type === 'strings') {\n return parseIOSStrings(raw);\n }\n\n if (resource.type === 'xml') {\n return convertXMLToJS(raw);\n }\n\n throw new Error(`Unsupported resource type: ${resource.type}`);\n}\n\n/**\n * Normalizes parsed translation data to a flat string map for merging (duplicate keys: last wins).\n */\nexport function toFlatStringMap(parsed: unknown): Record<string, string> {\n if (parsed === null || parsed === undefined) return {};\n if (typeof parsed === 'string') return { value: parsed };\n if (typeof parsed !== 'object') return { value: String(parsed) };\n if (Array.isArray(parsed)) {\n const out: Record<string, string> = {};\n parsed.forEach((v, i) => {\n out[String(i)] =\n typeof v === 'object' && v !== null ? JSON.stringify(v) : String(v);\n });\n return out;\n }\n\n const flat = transformObjectToFlat(parsed as Record<string, unknown>);\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(flat)) {\n if (v === null || v === undefined) {\n out[k] = '';\n } else if (typeof v === 'object') {\n out[k] = JSON.stringify(v);\n } else {\n out[k] = String(v);\n }\n }\n return out;\n}\n\n/** Where to write normalized JSON for one translation object key (avoids `.json.json`). */\nexport function translationJsonOutputPath(\n outputDir: string,\n objectKey: string,\n): string {\n if (objectKey.endsWith('.json')) {\n return path.resolve(outputDir, objectKey);\n }\n return path.resolve(outputDir, `${objectKey}.json`);\n}\n","import convert from 'xml-js';\nimport set from 'lodash.set';\nimport merge from 'lodash.merge';\n\nexport const transformObjectToNested = (data: Record<string, unknown>): Record<string, unknown> => {\n const result: Record<string, unknown> = {};\n\n Object.entries(data).forEach(([key, value]) => {\n const tempObject = {};\n set(tempObject, key, value);\n merge(result, tempObject);\n });\n\n return result;\n};\n\nexport const transformObjectToFlat = (data: Record<string, any>): Record<string, any> => { // eslint-disable-line @typescript-eslint/no-explicit-any\n const result: Record<string, unknown> = {};\n\n const flatten = (obj: Record<string, any>, path: string[] = []) => { // eslint-disable-line @typescript-eslint/no-explicit-any\n Object.entries(obj).forEach(([key, value]) => {\n if (typeof value === 'object') {\n flatten(value, path.concat(key));\n } else {\n result[path.concat(key).join('.')] = value;\n }\n });\n };\n\n flatten(data);\n\n return result;\n}\n\nexport const convertXMLToJS = (xml: string): Record<string, string> => {\n const converted = convert.xml2js(xml, {\n ignoreComment: true,\n ignoreDeclaration: true,\n ignoreInstruction: true,\n compact: true,\n }) as {\n resources: {\n string: {\n _attributes: { name: string },\n _text: 'User does not exist'\n }[];\n }\n };\n\n let mapped = {};\n converted.resources.string.forEach((item) => {\n mapped = {\n ...mapped,\n [item._attributes.name]: item._text,\n };\n });\n\n return mapped;\n};\n\nexport const parseIOSStrings = (strings: string): Record<string, string> => {\n const parsedObj: Record<string, string> = {};\n strings\n .split('\\n')\n .filter((line) => line.startsWith('\"') && line.endsWith(';'))\n .map((line) => line.trim().slice(0, -1))\n .forEach((line) => {\n let [key, value] = line.split(' = ');\n if (!key || !value) return;\n key = key.slice(1, -1);\n value = value.slice(1, -1);\n parsedObj[key] = value;\n });\n\n return parsedObj;\n};","import dotenv from 'dotenv';\nimport type { S3Config, CMSProvider } from './types';\n\ndotenv.config({ path: '.env.local' });\ndotenv.config();\n\nexport type { CMSProvider, S3Config };\n\nexport interface SharedConfig {\n s3: S3Config;\n}\n\nexport const config: SharedConfig = {\n s3: {\n bucket: process.env.CONTENT_STORE_S3_BUCKET ?? '',\n region: process.env.CONTENT_STORE_S3_REGION ?? 'eu-central-1',\n accessKeyId: process.env.AWS_ACCESS_KEY ?? '',\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? '',\n }\n};\n"],"mappings":"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","names":["path","out"]}
|