@metasyncsite/translations-client-ts 1.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 ADDED
@@ -0,0 +1,322 @@
1
+ # @metasyncsite/translations-client-ts
2
+
3
+ TypeScript client for pushing and pulling i18n translations to/from the Translation Manager. Works with any Node.js project: Vue, React, Next.js, Nuxt, etc.
4
+
5
+ ---
6
+
7
+ ## Requirements
8
+
9
+ - Node.js >= 18
10
+ - TypeScript >= 5.0 (dev dependency)
11
+
12
+ ---
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @metasyncsite/translations-client-ts
18
+ # or
19
+ pnpm add @metasyncsite/translations-client-ts
20
+ # or
21
+ yarn add @metasyncsite/translations-client-ts
22
+ ```
23
+
24
+ ---
25
+
26
+ ## Local Installation (without npm registry)
27
+
28
+ If the package is not yet published, you have three options:
29
+
30
+ ### Option 1 — file path in package.json (recommended)
31
+
32
+ Build the package first, then reference it by path in your project's `package.json`:
33
+
34
+ ```json
35
+ {
36
+ "dependencies": {
37
+ "@metasyncsite/translations-client-ts": "file:../path/to/translations-client-node-ts"
38
+ }
39
+ }
40
+ ```
41
+
42
+ Then run:
43
+
44
+ ```bash
45
+ npm install
46
+ # or
47
+ pnpm install
48
+ ```
49
+
50
+ > The path must point to the package root (where `package.json` lives). Use a relative or absolute path.
51
+
52
+ ---
53
+
54
+ ### Option 2 — npm pack (portable tarball)
55
+
56
+ Build and pack the package into a `.tgz` file, then install that file in any project:
57
+
58
+ ```bash
59
+ # Inside the package directory
60
+ cd packages/translations-client-node-ts
61
+ npm run build
62
+ npm pack
63
+ # → creates metasyncsite-translations-client-ts-1.0.0.tgz
64
+ ```
65
+
66
+ Then install the tarball in your project:
67
+
68
+ ```bash
69
+ npm install /path/to/metasyncsite-translations-client-ts-1.0.0.tgz
70
+ # or
71
+ pnpm add /path/to/metasyncsite-translations-client-ts-1.0.0.tgz
72
+ ```
73
+
74
+ > Good when you want to share the package without a registry — just copy the `.tgz` file.
75
+
76
+ ---
77
+
78
+ ### Option 3 — npm link (symlink, for active development)
79
+
80
+ Use this when you're actively editing the package and want changes to reflect immediately:
81
+
82
+ ```bash
83
+ # 1. Inside the package directory — register a global symlink
84
+ cd packages/translations-client-node-ts
85
+ npm run build
86
+ npm link
87
+
88
+ # 2. Inside your consuming project — link to it
89
+ cd /path/to/your-project
90
+ npm link @metasyncsite/translations-client-ts
91
+ ```
92
+
93
+ To unlink when done:
94
+
95
+ ```bash
96
+ # Inside your project
97
+ npm unlink @metasyncsite/translations-client-ts
98
+
99
+ # Inside the package directory
100
+ npm unlink
101
+ ```
102
+
103
+ > With `npm link`, you must re-run `npm run build` (or `npm run dev` for watch mode) after each source change.
104
+
105
+ ---
106
+
107
+ ## Build (if working from source)
108
+
109
+ ```bash
110
+ cd packages/translations-client-node-ts
111
+ npm install
112
+ npm run build # compiles src/ → dist/
113
+ ```
114
+
115
+ ---
116
+
117
+ ## Configuration
118
+
119
+ The client reads credentials from environment variables or CLI flags.
120
+
121
+ ### Environment variables
122
+
123
+ Add to your project's `.env`:
124
+
125
+ ```env
126
+ TRANSLATIONS_URL=https://translations.example.com
127
+ TRANSLATIONS_TOKEN=your-api-token
128
+ TRANSLATIONS_LOCALES_PATH=src/locales # optional, default: src/locales
129
+ ```
130
+
131
+ ---
132
+
133
+ ## CLI Usage
134
+
135
+ After installing the package, two CLI commands are available:
136
+
137
+ ### Push (upload local translations → Translation Manager)
138
+
139
+ ```bash
140
+ npx translations-push
141
+ ```
142
+
143
+ With explicit flags:
144
+
145
+ ```bash
146
+ npx translations-push \
147
+ --url https://translations.example.com \
148
+ --token your-api-token \
149
+ --path src/locales \
150
+ --locale en \ # push only this locale (optional)
151
+ --overwrite true \ # overwrite existing keys (default: true)
152
+ --dry-run \ # preview without sending
153
+ --exclude i18n.json,test.json # comma-separated files to skip
154
+ ```
155
+
156
+ ### Pull (download translations from Translation Manager → local files)
157
+
158
+ ```bash
159
+ npx translations-pull
160
+ ```
161
+
162
+ With explicit flags:
163
+
164
+ ```bash
165
+ npx translations-pull \
166
+ --url https://translations.example.com \
167
+ --token your-api-token \
168
+ --path src/locales \
169
+ --locale de \ # pull only this locale (optional)
170
+ --layout grouped \ # force layout: flat | grouped (optional, auto-detected)
171
+ --dry-run # preview without writing files
172
+ ```
173
+
174
+ ### Add to package.json scripts
175
+
176
+ ```json
177
+ {
178
+ "scripts": {
179
+ "translations:push": "translations-push",
180
+ "translations:pull": "translations-pull"
181
+ }
182
+ }
183
+ ```
184
+
185
+ ---
186
+
187
+ ## Locale Layouts
188
+
189
+ The client auto-detects which layout your project uses:
190
+
191
+ | Layout | Structure | Example |
192
+ |--------|-----------|---------|
193
+ | `flat` | One JSON file per locale | `locales/en.json`, `locales/de.json` |
194
+ | `grouped` | One subdirectory per locale | `locales/en/auth.json`, `locales/de/auth.json` |
195
+
196
+ ---
197
+
198
+ ## Programmatic API
199
+
200
+ Import and use the client functions directly in TypeScript:
201
+
202
+ ```ts
203
+ import {
204
+ readLocales,
205
+ detectLayout,
206
+ pushLocale,
207
+ writeGroupedLocale,
208
+ writeFlatLocale,
209
+ } from '@metasyncsite/translations-client-ts'
210
+
211
+ import type {
212
+ LocaleData,
213
+ LocaleGroups,
214
+ PushLocaleParams,
215
+ PushResult,
216
+ } from '@metasyncsite/translations-client-ts'
217
+
218
+ // Read all locales from disk
219
+ const locales: LocaleData[] = readLocales('./src/locales')
220
+
221
+ // Push a single locale
222
+ const result: PushResult = await pushLocale({
223
+ url: 'https://translations.example.com',
224
+ token: 'your-api-token',
225
+ locale: 'en',
226
+ groups: locales[0].groups,
227
+ overwrite: true,
228
+ })
229
+
230
+ console.log(`${result.new} new, ${result.updated} updated, ${result.total} total`)
231
+
232
+ // Detect layout
233
+ const layout = detectLayout('./src/locales') // 'flat' | 'grouped' | null
234
+
235
+ // Write pulled translations to disk
236
+ const groups: LocaleGroups = { auth: { 'login': 'Login', 'logout': 'Logout' } }
237
+ writeGroupedLocale('./src/locales', 'en', groups)
238
+ ```
239
+
240
+ ---
241
+
242
+ ## API Reference
243
+
244
+ ### `readLocales(localesPath, excludeFiles?)`
245
+
246
+ Auto-detects layout and reads all locale files.
247
+
248
+ | Param | Type | Description |
249
+ |-------|------|-------------|
250
+ | `localesPath` | `string` | Absolute or relative path to locales directory |
251
+ | `excludeFiles` | `string[]` | Filenames to skip (e.g. `['index.json']`) |
252
+
253
+ Returns: `LocaleData[]`
254
+
255
+ ---
256
+
257
+ ### `detectLayout(localesPath)`
258
+
259
+ Returns `'flat'`, `'grouped'`, or `null` if the directory doesn't exist or is empty.
260
+
261
+ ---
262
+
263
+ ### `pushLocale(params)`
264
+
265
+ Pushes one locale's groups to the Translation Manager API.
266
+
267
+ | Param | Type | Default |
268
+ |-------|------|---------|
269
+ | `url` | `string` | — |
270
+ | `token` | `string` | — |
271
+ | `locale` | `string` | — |
272
+ | `groups` | `LocaleGroups` | — |
273
+ | `overwrite` | `boolean` | `true` |
274
+
275
+ Returns: `Promise<PushResult>`
276
+
277
+ ---
278
+
279
+ ### `writeGroupedLocale(localesPath, locale, groups)`
280
+
281
+ Writes translations in grouped layout (`locales/{locale}/{group}.json`).
282
+ The special `_json` group is written as `locales/{locale}.json`.
283
+
284
+ Returns: `string[]` — paths of written files.
285
+
286
+ ---
287
+
288
+ ### `writeFlatLocale(localesPath, locale, groups)`
289
+
290
+ Merges all groups and writes `locales/{locale}.json`.
291
+
292
+ Returns: `string[]` — paths of written files.
293
+
294
+ ---
295
+
296
+ ### `unflatten(flat)`
297
+
298
+ Converts dot-notation keys back to a nested object.
299
+
300
+ ```ts
301
+ unflatten({ 'auth.login': 'Login' })
302
+ // → { auth: { login: 'Login' } }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## Types
308
+
309
+ All exported types are available from the main entry point:
310
+
311
+ ```ts
312
+ import type {
313
+ FlatKeys, // Record<string, string>
314
+ LocaleGroups, // Record<string, FlatKeys>
315
+ LocaleLayout, // 'flat' | 'grouped'
316
+ LocaleData, // { locale: string, groups: LocaleGroups }
317
+ PushLocaleParams,
318
+ PushResult, // { new: number, updated: number, total: number }
319
+ PullOptions,
320
+ PushOptions,
321
+ } from '@metasyncsite/translations-client-ts'
322
+ ```
@@ -0,0 +1,10 @@
1
+ import type { PushLocaleParams, PushResult } from './types.js';
2
+ /**
3
+ * Push a single locale's translations to the Translation Manager API.
4
+ */
5
+ export declare function pushLocale({ url, token, locale, groups, overwrite, }: PushLocaleParams): Promise<PushResult>;
6
+ /**
7
+ * Fetch JSON from a Translation Manager API endpoint.
8
+ */
9
+ export declare function fetchJson<T = unknown>(url: string, token: string): Promise<T>;
10
+ //# sourceMappingURL=apiClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiClient.d.ts","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE9D;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAgB,GACjB,EAAE,gBAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmBxC;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAanF"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Push a single locale's translations to the Translation Manager API.
3
+ */
4
+ export async function pushLocale({ url, token, locale, groups, overwrite = true, }) {
5
+ const endpoint = `${url.replace(/\/$/, '')}/api/v1/import`;
6
+ const response = await fetch(endpoint, {
7
+ method: 'POST',
8
+ headers: {
9
+ 'Content-Type': 'application/json',
10
+ Accept: 'application/json',
11
+ Authorization: `Bearer ${token}`,
12
+ },
13
+ body: JSON.stringify({ locale, groups, overwrite }),
14
+ });
15
+ if (!response.ok) {
16
+ const body = await response.text();
17
+ throw new Error(`HTTP ${response.status}: ${body}`);
18
+ }
19
+ return response.json();
20
+ }
21
+ /**
22
+ * Fetch JSON from a Translation Manager API endpoint.
23
+ */
24
+ export async function fetchJson(url, token) {
25
+ const res = await fetch(url, {
26
+ headers: {
27
+ Accept: 'application/json',
28
+ Authorization: `Bearer ${token}`,
29
+ },
30
+ });
31
+ if (!res.ok) {
32
+ throw new Error(`HTTP ${res.status}: ${await res.text()}`);
33
+ }
34
+ return res.json();
35
+ }
36
+ //# sourceMappingURL=apiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../src/apiClient.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAC/B,GAAG,EACH,KAAK,EACL,MAAM,EACN,MAAM,EACN,SAAS,GAAG,IAAI,GACC;IACjB,MAAM,QAAQ,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAA;IAE1D,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;QACrC,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;KACpD,CAAC,CAAA;IAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;QAClC,MAAM,IAAI,KAAK,CAAC,QAAQ,QAAQ,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAA;IACrD,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,EAAyB,CAAA;AAC/C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAc,GAAW,EAAE,KAAa;IACrE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC3B,OAAO,EAAE;YACP,MAAM,EAAE,kBAAkB;YAC1B,aAAa,EAAE,UAAU,KAAK,EAAE;SACjC;KACF,CAAC,CAAA;IAEF,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;IAC5D,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,EAAgB,CAAA;AACjC,CAAC"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=pull.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/bin/pull.ts"],"names":[],"mappings":""}
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { resolve, join } from 'node:path';
4
+ import { detectLayout } from '../langReader.js';
5
+ import { writeGroupedLocale, writeFlatLocale } from '../langWriter.js';
6
+ import { fetchJson } from '../apiClient.js';
7
+ function loadDotEnv(cwd) {
8
+ const envPath = join(cwd, '.env');
9
+ if (!existsSync(envPath)) {
10
+ return;
11
+ }
12
+ const lines = readFileSync(envPath, 'utf8').split('\n');
13
+ for (const line of lines) {
14
+ const trimmed = line.trim();
15
+ if (!trimmed || trimmed.startsWith('#')) {
16
+ continue;
17
+ }
18
+ const eqIndex = trimmed.indexOf('=');
19
+ if (eqIndex === -1) {
20
+ continue;
21
+ }
22
+ const key = trimmed.slice(0, eqIndex).trim();
23
+ const rawValue = trimmed.slice(eqIndex + 1).trim();
24
+ const value = rawValue.replace(/^["']|["']$/g, '');
25
+ if (!(key in process.env)) {
26
+ process.env[key] = value;
27
+ }
28
+ }
29
+ }
30
+ function parseArgs(argv) {
31
+ const args = { flags: {} };
32
+ for (let i = 0; i < argv.length; i++) {
33
+ const arg = argv[i];
34
+ if (arg.startsWith('--')) {
35
+ const eqIdx = arg.indexOf('=');
36
+ const rawKey = eqIdx === -1 ? arg.slice(2) : arg.slice(2, eqIdx);
37
+ const key = rawKey.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
38
+ if (eqIdx !== -1) {
39
+ args.flags[key] = arg.slice(eqIdx + 1);
40
+ }
41
+ else if (argv[i + 1] && !argv[i + 1].startsWith('--')) {
42
+ args.flags[key] = argv[i + 1];
43
+ i++;
44
+ }
45
+ else {
46
+ args.flags[key] = true;
47
+ }
48
+ }
49
+ }
50
+ return args;
51
+ }
52
+ async function main() {
53
+ const cwd = process.cwd();
54
+ loadDotEnv(cwd);
55
+ const { flags } = parseArgs(process.argv.slice(2));
56
+ const url = flags['url'] ?? process.env['TRANSLATIONS_URL'];
57
+ const token = flags['token'] ?? process.env['TRANSLATIONS_TOKEN'];
58
+ const localesPath = resolve(cwd, flags['path'] ??
59
+ process.env['TRANSLATIONS_LOCALES_PATH'] ??
60
+ 'src/locales');
61
+ const onlyLocale = flags['locale'] ?? null;
62
+ const dryRun = flags['dryRun'] === true || flags['dryRun'] === 'true';
63
+ const forcedLayout = flags['layout'] ?? null;
64
+ if (!url || !token) {
65
+ console.error('Error: TRANSLATIONS_URL and TRANSLATIONS_TOKEN are required.');
66
+ process.exit(1);
67
+ }
68
+ const base = url.replace(/\/$/, '');
69
+ const langData = await fetchJson(`${base}/api/v1/languages`, token);
70
+ let locales = langData.data.map((l) => l.code);
71
+ if (onlyLocale) {
72
+ locales = locales.filter((c) => c === onlyLocale);
73
+ if (locales.length === 0) {
74
+ console.warn(`Locale "${onlyLocale}" not found in Translation Manager.`);
75
+ process.exit(0);
76
+ }
77
+ }
78
+ const layout = forcedLayout ?? detectLayout(localesPath) ?? 'grouped';
79
+ const prefix = dryRun ? '[DRY RUN] ' : '';
80
+ console.log(`${prefix}Pulling ${locales.length} locale(s) [${layout} layout]: ${locales.join(', ')}`);
81
+ for (const locale of locales) {
82
+ const data = await fetchJson(`${base}/api/v1/translations/${locale}?format=nested`, token);
83
+ const groups = data.data ?? {};
84
+ const keyCount = Object.values(groups).reduce((sum, g) => sum + Object.keys(g).length, 0);
85
+ if (dryRun) {
86
+ console.log(` ${locale}: ${keyCount} keys across ${Object.keys(groups).length} group(s).`);
87
+ continue;
88
+ }
89
+ const written = layout === 'grouped'
90
+ ? writeGroupedLocale(localesPath, locale, groups)
91
+ : writeFlatLocale(localesPath, locale, groups);
92
+ console.log(` ✓ ${locale}: ${keyCount} keys → ${written.length} file(s) written.`);
93
+ }
94
+ if (!dryRun) {
95
+ console.log('\nDone.');
96
+ }
97
+ }
98
+ main();
99
+ //# sourceMappingURL=pull.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pull.js","sourceRoot":"","sources":["../../src/bin/pull.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=push.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../src/bin/push.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, existsSync } from 'node:fs';
3
+ import { resolve, join } from 'node:path';
4
+ import { readLocales } from '../langReader.js';
5
+ import { pushLocale } from '../apiClient.js';
6
+ function loadDotEnv(cwd) {
7
+ const envPath = join(cwd, '.env');
8
+ if (!existsSync(envPath)) {
9
+ return;
10
+ }
11
+ const lines = readFileSync(envPath, 'utf8').split('\n');
12
+ for (const line of lines) {
13
+ const trimmed = line.trim();
14
+ if (!trimmed || trimmed.startsWith('#')) {
15
+ continue;
16
+ }
17
+ const eqIndex = trimmed.indexOf('=');
18
+ if (eqIndex === -1) {
19
+ continue;
20
+ }
21
+ const key = trimmed.slice(0, eqIndex).trim();
22
+ const rawValue = trimmed.slice(eqIndex + 1).trim();
23
+ const value = rawValue.replace(/^["']|["']$/g, '');
24
+ if (!(key in process.env)) {
25
+ process.env[key] = value;
26
+ }
27
+ }
28
+ }
29
+ function parseArgs(argv) {
30
+ const args = { flags: {}, positional: [] };
31
+ for (let i = 0; i < argv.length; i++) {
32
+ const arg = argv[i];
33
+ if (arg.startsWith('--')) {
34
+ const eqIdx = arg.indexOf('=');
35
+ const rawKey = eqIdx === -1 ? arg.slice(2) : arg.slice(2, eqIdx);
36
+ const key = rawKey.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
37
+ if (eqIdx !== -1) {
38
+ args.flags[key] = arg.slice(eqIdx + 1);
39
+ }
40
+ else if (argv[i + 1] && !argv[i + 1].startsWith('--')) {
41
+ args.flags[key] = argv[i + 1];
42
+ i++;
43
+ }
44
+ else {
45
+ args.flags[key] = true;
46
+ }
47
+ }
48
+ else {
49
+ args.positional.push(arg);
50
+ }
51
+ }
52
+ return args;
53
+ }
54
+ async function main() {
55
+ const cwd = process.cwd();
56
+ loadDotEnv(cwd);
57
+ const { flags } = parseArgs(process.argv.slice(2));
58
+ const url = flags['url'] ?? process.env['TRANSLATIONS_URL'];
59
+ const token = flags['token'] ?? process.env['TRANSLATIONS_TOKEN'];
60
+ const localesPath = resolve(cwd, flags['path'] ??
61
+ process.env['TRANSLATIONS_LOCALES_PATH'] ??
62
+ 'src/locales');
63
+ const onlyLocale = flags['locale'] ?? null;
64
+ const overwrite = flags['overwrite'] !== 'false' && flags['overwrite'] !== false;
65
+ const dryRun = flags['dryRun'] === true || flags['dryRun'] === 'true';
66
+ const excludeFiles = typeof flags['exclude'] === 'string' ? flags['exclude'].split(',') : [];
67
+ if (!url || !token) {
68
+ console.error('Error: TRANSLATIONS_URL and TRANSLATIONS_TOKEN are required.');
69
+ console.error('Set them in .env or pass --url and --token flags.');
70
+ process.exit(1);
71
+ }
72
+ let locales = readLocales(localesPath, excludeFiles);
73
+ if (locales.length === 0) {
74
+ console.warn(`No locale files found in: ${localesPath}`);
75
+ process.exit(0);
76
+ }
77
+ if (onlyLocale) {
78
+ locales = locales.filter((l) => l.locale === onlyLocale);
79
+ if (locales.length === 0) {
80
+ console.warn(`Locale "${onlyLocale}" not found in: ${localesPath}`);
81
+ process.exit(0);
82
+ }
83
+ }
84
+ const prefix = dryRun ? '[DRY RUN] ' : '';
85
+ console.log(`${prefix}Pushing ${locales.length} locale(s): ${locales.map((l) => l.locale).join(', ')}`);
86
+ let totalNew = 0;
87
+ let totalUpdated = 0;
88
+ let totalKeys = 0;
89
+ for (const { locale, groups } of locales) {
90
+ const keyCount = Object.values(groups).reduce((sum, g) => sum + Object.keys(g).length, 0);
91
+ if (dryRun) {
92
+ console.log(` ${locale}: ${keyCount} keys across ${Object.keys(groups).length} group(s).`);
93
+ continue;
94
+ }
95
+ try {
96
+ const data = await pushLocale({ url, token, locale, groups, overwrite });
97
+ totalNew += data.new ?? 0;
98
+ totalUpdated += data.updated ?? 0;
99
+ totalKeys += data.total ?? keyCount;
100
+ console.log(` ✓ ${locale}: ${data.total ?? keyCount} keys — ${data.new ?? 0} new, ${data.updated ?? 0} updated.`);
101
+ }
102
+ catch (err) {
103
+ console.error(` ✗ ${locale}: ${err instanceof Error ? err.message : String(err)}`);
104
+ }
105
+ }
106
+ if (!dryRun) {
107
+ console.log(`\nDone. Total: ${totalKeys} keys — ${totalNew} new, ${totalUpdated} updated.`);
108
+ }
109
+ }
110
+ main();
111
+ //# sourceMappingURL=push.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"push.js","sourceRoot":"","sources":["../../src/bin/push.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAO5C,SAAS,UAAU,CAAC,GAAW;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IAEjC,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QACzB,OAAM;IACR,CAAC;IAED,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;IAEvD,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;QAE3B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACxC,SAAQ;QACV,CAAC;QAED,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;QAEpC,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YACnB,SAAQ;QACV,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAA;QAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;QAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAA;QAElD,IAAI,CAAC,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QAC1B,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,IAAc;IAC/B,MAAM,IAAI,GAAe,EAAE,KAAK,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAA;IAEtD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAA;QAEpB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC9B,MAAM,MAAM,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAA;YAChE,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAA;YAE1E,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YACxC,CAAC;iBAAM,IAAI,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;gBACzD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAA;gBAC9B,CAAC,EAAE,CAAA;YACL,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAA;YACxB,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAA;IACzB,UAAU,CAAC,GAAG,CAAC,CAAA;IAEf,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;IAElD,MAAM,GAAG,GAAI,KAAK,CAAC,KAAK,CAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;IACnF,MAAM,KAAK,GAAI,KAAK,CAAC,OAAO,CAAwB,IAAI,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAA;IACzF,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EACF,KAAK,CAAC,MAAM,CAAwB;QACnC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC;QACxC,aAAa,CAChB,CAAA;IACD,MAAM,UAAU,GAAI,KAAK,CAAC,QAAQ,CAAwB,IAAI,IAAI,CAAA;IAClE,MAAM,SAAS,GAAG,KAAK,CAAC,WAAW,CAAC,KAAK,OAAO,IAAI,KAAK,CAAC,WAAW,CAAC,KAAK,KAAK,CAAA;IAChF,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,IAAI,IAAI,KAAK,CAAC,QAAQ,CAAC,KAAK,MAAM,CAAA;IACrE,MAAM,YAAY,GAChB,OAAO,KAAK,CAAC,SAAS,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IAEzE,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,CAAC,KAAK,CAAC,8DAA8D,CAAC,CAAA;QAC7E,OAAO,CAAC,KAAK,CAAC,mDAAmD,CAAC,CAAA;QAClE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,OAAO,GAAG,WAAW,CAAC,WAAW,EAAE,YAAY,CAAC,CAAA;IAEpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,CAAC,IAAI,CAAC,6BAA6B,WAAW,EAAE,CAAC,CAAA;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAA;QAExD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,CAAC,IAAI,CAAC,WAAW,UAAU,mBAAmB,WAAW,EAAE,CAAC,CAAA;YACnE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAA;IACzC,OAAO,CAAC,GAAG,CACT,GAAG,MAAM,WAAW,OAAO,CAAC,MAAM,eAAe,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC3F,CAAA;IAED,IAAI,QAAQ,GAAG,CAAC,CAAA;IAChB,IAAI,YAAY,GAAG,CAAC,CAAA;IACpB,IAAI,SAAS,GAAG,CAAC,CAAA;IAEjB,KAAK,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAEzF,IAAI,MAAM,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CAAC,KAAK,MAAM,KAAK,QAAQ,gBAAgB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,YAAY,CAAC,CAAA;YAC3F,SAAQ;QACV,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;YACxE,QAAQ,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,CAAA;YACzB,YAAY,IAAI,IAAI,CAAC,OAAO,IAAI,CAAC,CAAA;YACjC,SAAS,IAAI,IAAI,CAAC,KAAK,IAAI,QAAQ,CAAA;YACnC,OAAO,CAAC,GAAG,CACT,OAAO,MAAM,KAAK,IAAI,CAAC,KAAK,IAAI,QAAQ,WAAW,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,IAAI,CAAC,OAAO,IAAI,CAAC,WAAW,CACtG,CAAA;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,OAAO,MAAM,KAAK,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACrF,CAAC;IACH,CAAC;IAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,kBAAkB,SAAS,WAAW,QAAQ,SAAS,YAAY,WAAW,CAAC,CAAA;IAC7F,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { readLocales, readFlatLayout, readGroupedLayout, detectLayout } from './langReader.js';
2
+ export { pushLocale, fetchJson } from './apiClient.js';
3
+ export { unflatten, writeGroupedLocale, writeFlatLocale } from './langWriter.js';
4
+ export type { FlatKeys, LocaleGroups, LocaleLayout, LocaleData, PushLocaleParams, PushResult, PullOptions, PushOptions, } from './types.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAChF,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { readLocales, readFlatLayout, readGroupedLayout, detectLayout } from './langReader.js';
2
+ export { pushLocale, fetchJson } from './apiClient.js';
3
+ export { unflatten, writeGroupedLocale, writeFlatLocale } from './langWriter.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9F,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,SAAS,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
@@ -0,0 +1,22 @@
1
+ import type { LocaleData, LocaleLayout } from './types.js';
2
+ /**
3
+ * Detect which locale layout the locales directory uses.
4
+ *
5
+ * Layout `flat` — one file per locale: locales/en.json, locales/de.json
6
+ * Layout `grouped` — one subdirectory per locale: locales/en/auth.json, locales/de/auth.json
7
+ */
8
+ export declare function detectLayout(localesPath: string): LocaleLayout | null;
9
+ /**
10
+ * Read translations in flat layout (one JSON per locale).
11
+ * All keys are placed in the `_json` group to match Laravel's JSON translation convention.
12
+ */
13
+ export declare function readFlatLayout(localesPath: string, excludeFiles?: string[]): LocaleData[];
14
+ /**
15
+ * Read translations in grouped layout (subdirectory per locale, multiple group files).
16
+ */
17
+ export declare function readGroupedLayout(localesPath: string, excludeFiles?: string[]): LocaleData[];
18
+ /**
19
+ * Auto-detect layout and read all locales from the given path.
20
+ */
21
+ export declare function readLocales(localesPath: string, excludeFiles?: string[]): LocaleData[];
22
+ //# sourceMappingURL=langReader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langReader.d.ts","sourceRoot":"","sources":["../src/langReader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAY,UAAU,EAAgB,YAAY,EAAE,MAAM,YAAY,CAAA;AAiClF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAmBrE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,UAAU,EAAE,CAwB7F;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,UAAU,EAAE,CAiChG;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,GAAG,UAAU,EAAE,CAU1F"}
@@ -0,0 +1,118 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { join, basename, extname } from 'node:path';
3
+ /**
4
+ * Flatten a nested object into dot-notation keys.
5
+ * { auth: { login: 'Login' } } → { 'auth.login': 'Login' }
6
+ */
7
+ function flatten(obj, prefix = '') {
8
+ const result = {};
9
+ for (const [key, value] of Object.entries(obj)) {
10
+ const fullKey = prefix ? `${prefix}.${key}` : key;
11
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
12
+ Object.assign(result, flatten(value, fullKey));
13
+ }
14
+ else {
15
+ result[fullKey] = String(value ?? '');
16
+ }
17
+ }
18
+ return result;
19
+ }
20
+ /**
21
+ * Read and parse a JSON file. Returns an empty object on failure.
22
+ */
23
+ function readJson(filePath) {
24
+ try {
25
+ return JSON.parse(readFileSync(filePath, 'utf8'));
26
+ }
27
+ catch {
28
+ return {};
29
+ }
30
+ }
31
+ /**
32
+ * Detect which locale layout the locales directory uses.
33
+ *
34
+ * Layout `flat` — one file per locale: locales/en.json, locales/de.json
35
+ * Layout `grouped` — one subdirectory per locale: locales/en/auth.json, locales/de/auth.json
36
+ */
37
+ export function detectLayout(localesPath) {
38
+ if (!existsSync(localesPath)) {
39
+ return null;
40
+ }
41
+ const entries = readdirSync(localesPath, { withFileTypes: true });
42
+ const hasJsonFiles = entries.some((e) => e.isFile() && e.name.endsWith('.json'));
43
+ const hasDirs = entries.some((e) => e.isDirectory());
44
+ if (hasDirs) {
45
+ return 'grouped';
46
+ }
47
+ if (hasJsonFiles) {
48
+ return 'flat';
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Read translations in flat layout (one JSON per locale).
54
+ * All keys are placed in the `_json` group to match Laravel's JSON translation convention.
55
+ */
56
+ export function readFlatLayout(localesPath, excludeFiles = []) {
57
+ const entries = readdirSync(localesPath, { withFileTypes: true });
58
+ const result = [];
59
+ for (const entry of entries) {
60
+ if (!entry.isFile() || !entry.name.endsWith('.json')) {
61
+ continue;
62
+ }
63
+ if (excludeFiles.includes(entry.name)) {
64
+ continue;
65
+ }
66
+ const locale = basename(entry.name, '.json');
67
+ const filePath = join(localesPath, entry.name);
68
+ const parsed = readJson(filePath);
69
+ result.push({
70
+ locale,
71
+ groups: { _json: flatten(parsed) },
72
+ });
73
+ }
74
+ return result;
75
+ }
76
+ /**
77
+ * Read translations in grouped layout (subdirectory per locale, multiple group files).
78
+ */
79
+ export function readGroupedLayout(localesPath, excludeFiles = []) {
80
+ const entries = readdirSync(localesPath, { withFileTypes: true });
81
+ const result = [];
82
+ for (const entry of entries) {
83
+ if (!entry.isDirectory()) {
84
+ continue;
85
+ }
86
+ const locale = entry.name;
87
+ const localePath = join(localesPath, locale);
88
+ const groups = {};
89
+ for (const file of readdirSync(localePath)) {
90
+ if (extname(file) !== '.json') {
91
+ continue;
92
+ }
93
+ if (excludeFiles.includes(file)) {
94
+ continue;
95
+ }
96
+ const groupName = basename(file, '.json');
97
+ const parsed = readJson(join(localePath, file));
98
+ groups[groupName] = flatten(parsed);
99
+ }
100
+ if (Object.keys(groups).length > 0) {
101
+ result.push({ locale, groups });
102
+ }
103
+ }
104
+ return result;
105
+ }
106
+ /**
107
+ * Auto-detect layout and read all locales from the given path.
108
+ */
109
+ export function readLocales(localesPath, excludeFiles = []) {
110
+ const layout = detectLayout(localesPath);
111
+ if (!layout) {
112
+ return [];
113
+ }
114
+ return layout === 'grouped'
115
+ ? readGroupedLayout(localesPath, excludeFiles)
116
+ : readFlatLayout(localesPath, excludeFiles);
117
+ }
118
+ //# sourceMappingURL=langReader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langReader.js","sourceRoot":"","sources":["../src/langReader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import type { FlatKeys, LocaleGroups } from './types.js';
2
+ /**
3
+ * Restore dot-notation flat keys back to a nested object.
4
+ * { 'auth.login': 'Login' } → { auth: { login: 'Login' } }
5
+ */
6
+ export declare function unflatten(flat: FlatKeys): Record<string, unknown>;
7
+ /**
8
+ * Write translations for a single locale in grouped layout.
9
+ *
10
+ * Each group becomes its own file: locales/{locale}/{group}.json
11
+ * The special `_json` group is written as locales/{locale}.json (root-level).
12
+ */
13
+ export declare function writeGroupedLocale(localesPath: string, locale: string, groups: LocaleGroups): string[];
14
+ /**
15
+ * Write translations for a single locale in flat layout.
16
+ *
17
+ * All groups are merged into a single locales/{locale}.json file.
18
+ */
19
+ export declare function writeFlatLocale(localesPath: string, locale: string, groups: LocaleGroups): string[];
20
+ //# sourceMappingURL=langWriter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langWriter.d.ts","sourceRoot":"","sources":["../src/langWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAExD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAqBjE;AAeD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GACnB,MAAM,EAAE,CAkBV;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GACnB,MAAM,EAAE,CAYV"}
@@ -0,0 +1,71 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ /**
4
+ * Restore dot-notation flat keys back to a nested object.
5
+ * { 'auth.login': 'Login' } → { auth: { login: 'Login' } }
6
+ */
7
+ export function unflatten(flat) {
8
+ const result = {};
9
+ for (const [dotKey, value] of Object.entries(flat)) {
10
+ const parts = dotKey.split('.');
11
+ let cursor = result;
12
+ for (let i = 0; i < parts.length - 1; i++) {
13
+ const part = parts[i];
14
+ if (typeof cursor[part] !== 'object' || cursor[part] === null) {
15
+ cursor[part] = {};
16
+ }
17
+ cursor = cursor[part];
18
+ }
19
+ cursor[parts[parts.length - 1]] = value;
20
+ }
21
+ return result;
22
+ }
23
+ /**
24
+ * Write a JSON file, creating parent directories as needed.
25
+ */
26
+ function writeJson(filePath, data) {
27
+ const dir = dirname(filePath);
28
+ if (!existsSync(dir)) {
29
+ mkdirSync(dir, { recursive: true });
30
+ }
31
+ writeFileSync(filePath, JSON.stringify(data, null, 2) + '\n', 'utf8');
32
+ }
33
+ /**
34
+ * Write translations for a single locale in grouped layout.
35
+ *
36
+ * Each group becomes its own file: locales/{locale}/{group}.json
37
+ * The special `_json` group is written as locales/{locale}.json (root-level).
38
+ */
39
+ export function writeGroupedLocale(localesPath, locale, groups) {
40
+ const written = [];
41
+ for (const [groupName, flatKeys] of Object.entries(groups)) {
42
+ const nested = unflatten(flatKeys);
43
+ if (groupName === '_json') {
44
+ const filePath = join(localesPath, `${locale}.json`);
45
+ writeJson(filePath, nested);
46
+ written.push(filePath);
47
+ }
48
+ else {
49
+ const filePath = join(localesPath, locale, `${groupName}.json`);
50
+ writeJson(filePath, nested);
51
+ written.push(filePath);
52
+ }
53
+ }
54
+ return written;
55
+ }
56
+ /**
57
+ * Write translations for a single locale in flat layout.
58
+ *
59
+ * All groups are merged into a single locales/{locale}.json file.
60
+ */
61
+ export function writeFlatLocale(localesPath, locale, groups) {
62
+ const merged = {};
63
+ for (const flatKeys of Object.values(groups)) {
64
+ Object.assign(merged, flatKeys);
65
+ }
66
+ const nested = unflatten(merged);
67
+ const filePath = join(localesPath, `${locale}.json`);
68
+ writeJson(filePath, nested);
69
+ return [filePath];
70
+ }
71
+ //# sourceMappingURL=langWriter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"langWriter.js","sourceRoot":"","sources":["../src/langWriter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /** Flat map of dot-notation keys to string values. */
2
+ export type FlatKeys = Record<string, string>;
3
+ /** A locale's translations grouped by file/group name. */
4
+ export type LocaleGroups = Record<string, FlatKeys>;
5
+ /** Detected directory layout of the locales folder. */
6
+ export type LocaleLayout = 'flat' | 'grouped';
7
+ /** A single locale's data as returned by readLocales(). */
8
+ export interface LocaleData {
9
+ locale: string;
10
+ groups: LocaleGroups;
11
+ }
12
+ /** Parameters for pushLocale(). */
13
+ export interface PushLocaleParams {
14
+ /** Base URL of the Translation Manager (e.g. https://translations.example.com). */
15
+ url: string;
16
+ /** API bearer token. */
17
+ token: string;
18
+ /** Locale code, e.g. 'en', 'de'. */
19
+ locale: string;
20
+ /** Translation groups to push. */
21
+ groups: LocaleGroups;
22
+ /** When true, existing keys will be overwritten. Defaults to true. */
23
+ overwrite?: boolean;
24
+ }
25
+ /** Response from the Translation Manager import endpoint. */
26
+ export interface PushResult {
27
+ new: number;
28
+ updated: number;
29
+ total: number;
30
+ }
31
+ /** Parameters for pullLocale() / the pull CLI. */
32
+ export interface PullOptions {
33
+ /** Base URL of the Translation Manager. */
34
+ url: string;
35
+ /** API bearer token. */
36
+ token: string;
37
+ /** Absolute path to the locales directory. */
38
+ localesPath: string;
39
+ /** Restrict pull to a single locale code. */
40
+ locale?: string | null;
41
+ /** Force layout instead of auto-detecting. */
42
+ layout?: LocaleLayout | null;
43
+ /** Dry-run: report what would be written without writing. */
44
+ dryRun?: boolean;
45
+ }
46
+ /** Parameters for pushAll() / the push CLI. */
47
+ export interface PushOptions {
48
+ /** Base URL of the Translation Manager. */
49
+ url: string;
50
+ /** API bearer token. */
51
+ token: string;
52
+ /** Absolute path to the locales directory. */
53
+ localesPath: string;
54
+ /** Restrict push to a single locale code. */
55
+ locale?: string | null;
56
+ /** When true, existing keys will be overwritten. Defaults to true. */
57
+ overwrite?: boolean;
58
+ /** Dry-run: report what would be sent without sending. */
59
+ dryRun?: boolean;
60
+ /** JSON filenames to exclude from reading. */
61
+ excludeFiles?: string[];
62
+ }
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAE7C,0DAA0D;AAC1D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEnD,uDAAuD;AACvD,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,SAAS,CAAA;AAE7C,2DAA2D;AAC3D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,YAAY,CAAA;CACrB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC/B,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAA;IACX,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,oCAAoC;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,MAAM,EAAE,YAAY,CAAA;IACpB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB;AAED,6DAA6D;AAC7D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,MAAM,CAAA;CACd;AAED,kDAAkD;AAClD,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAA;IACX,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,8CAA8C;IAC9C,MAAM,CAAC,EAAE,YAAY,GAAG,IAAI,CAAA;IAC5B,6DAA6D;IAC7D,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAED,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC1B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAA;IACX,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,8CAA8C;IAC9C,WAAW,EAAE,MAAM,CAAA;IACnB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,sEAAsE;IACtE,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,0DAA0D;IAC1D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;CACxB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@metasyncsite/translations-client-ts",
3
+ "version": "1.0.0",
4
+ "description": "Push/pull i18n translations (Vue, React, Next.js, etc.) to the MetaSyncSite Translation Manager — TypeScript edition",
5
+ "type": "module",
6
+ "engines": {
7
+ "node": ">=18.0.0"
8
+ },
9
+ "bin": {
10
+ "translations-push": "./dist/bin/push.js",
11
+ "translations-pull": "./dist/bin/pull.js"
12
+ },
13
+ "main": "./dist/index.js",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "import": "./dist/index.js",
18
+ "types": "./dist/index.d.ts"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsc",
26
+ "dev": "tsc --watch",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^22.0.0",
31
+ "typescript": "^5.4.0"
32
+ },
33
+ "keywords": [
34
+ "i18n",
35
+ "vue-i18n",
36
+ "translations",
37
+ "localization",
38
+ "typescript"
39
+ ]
40
+ }