@lionrapid/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +159 -0
  2. package/dist/adapters/android-xml.d.ts +20 -0
  3. package/dist/adapters/android-xml.d.ts.map +1 -0
  4. package/dist/adapters/android-xml.js +101 -0
  5. package/dist/adapters/android-xml.js.map +1 -0
  6. package/dist/adapters/arb.d.ts +17 -0
  7. package/dist/adapters/arb.d.ts.map +1 -0
  8. package/dist/adapters/arb.js +45 -0
  9. package/dist/adapters/arb.js.map +1 -0
  10. package/dist/adapters/content.d.ts +13 -0
  11. package/dist/adapters/content.d.ts.map +1 -0
  12. package/dist/adapters/content.js +24 -0
  13. package/dist/adapters/content.js.map +1 -0
  14. package/dist/adapters/index.d.ts +11 -0
  15. package/dist/adapters/index.d.ts.map +1 -0
  16. package/dist/adapters/index.js +34 -0
  17. package/dist/adapters/index.js.map +1 -0
  18. package/dist/adapters/ios-strings.d.ts +15 -0
  19. package/dist/adapters/ios-strings.d.ts.map +1 -0
  20. package/dist/adapters/ios-strings.js +54 -0
  21. package/dist/adapters/ios-strings.js.map +1 -0
  22. package/dist/adapters/json.d.ts +12 -0
  23. package/dist/adapters/json.d.ts.map +1 -0
  24. package/dist/adapters/json.js +31 -0
  25. package/dist/adapters/json.js.map +1 -0
  26. package/dist/adapters/nested.d.ts +9 -0
  27. package/dist/adapters/nested.d.ts.map +1 -0
  28. package/dist/adapters/nested.js +37 -0
  29. package/dist/adapters/nested.js.map +1 -0
  30. package/dist/adapters/po.d.ts +23 -0
  31. package/dist/adapters/po.d.ts.map +1 -0
  32. package/dist/adapters/po.js +197 -0
  33. package/dist/adapters/po.js.map +1 -0
  34. package/dist/adapters/types.d.ts +46 -0
  35. package/dist/adapters/types.d.ts.map +1 -0
  36. package/dist/adapters/types.js +16 -0
  37. package/dist/adapters/types.js.map +1 -0
  38. package/dist/adapters/xcstrings.d.ts +20 -0
  39. package/dist/adapters/xcstrings.d.ts.map +1 -0
  40. package/dist/adapters/xcstrings.js +77 -0
  41. package/dist/adapters/xcstrings.js.map +1 -0
  42. package/dist/adapters/xliff.d.ts +22 -0
  43. package/dist/adapters/xliff.d.ts.map +1 -0
  44. package/dist/adapters/xliff.js +83 -0
  45. package/dist/adapters/xliff.js.map +1 -0
  46. package/dist/adapters/yaml.d.ts +16 -0
  47. package/dist/adapters/yaml.d.ts.map +1 -0
  48. package/dist/adapters/yaml.js +51 -0
  49. package/dist/adapters/yaml.js.map +1 -0
  50. package/dist/api-client.d.ts +123 -0
  51. package/dist/api-client.d.ts.map +1 -0
  52. package/dist/api-client.js +236 -0
  53. package/dist/api-client.js.map +1 -0
  54. package/dist/cli.d.ts +24 -0
  55. package/dist/cli.d.ts.map +1 -0
  56. package/dist/cli.js +365 -0
  57. package/dist/cli.js.map +1 -0
  58. package/dist/commands/context.d.ts +38 -0
  59. package/dist/commands/context.d.ts.map +1 -0
  60. package/dist/commands/context.js +70 -0
  61. package/dist/commands/context.js.map +1 -0
  62. package/dist/commands/doctor.d.ts +27 -0
  63. package/dist/commands/doctor.d.ts.map +1 -0
  64. package/dist/commands/doctor.js +87 -0
  65. package/dist/commands/doctor.js.map +1 -0
  66. package/dist/commands/init.d.ts +36 -0
  67. package/dist/commands/init.d.ts.map +1 -0
  68. package/dist/commands/init.js +111 -0
  69. package/dist/commands/init.js.map +1 -0
  70. package/dist/commands/pull.d.ts +35 -0
  71. package/dist/commands/pull.d.ts.map +1 -0
  72. package/dist/commands/pull.js +123 -0
  73. package/dist/commands/pull.js.map +1 -0
  74. package/dist/commands/push.d.ts +37 -0
  75. package/dist/commands/push.d.ts.map +1 -0
  76. package/dist/commands/push.js +63 -0
  77. package/dist/commands/push.js.map +1 -0
  78. package/dist/commands/scan.d.ts +46 -0
  79. package/dist/commands/scan.d.ts.map +1 -0
  80. package/dist/commands/scan.js +68 -0
  81. package/dist/commands/scan.js.map +1 -0
  82. package/dist/commands/snapshot.d.ts +48 -0
  83. package/dist/commands/snapshot.d.ts.map +1 -0
  84. package/dist/commands/snapshot.js +52 -0
  85. package/dist/commands/snapshot.js.map +1 -0
  86. package/dist/commands/types.d.ts +38 -0
  87. package/dist/commands/types.d.ts.map +1 -0
  88. package/dist/commands/types.js +82 -0
  89. package/dist/commands/types.js.map +1 -0
  90. package/dist/commands/validate.d.ts +28 -0
  91. package/dist/commands/validate.d.ts.map +1 -0
  92. package/dist/commands/validate.js +48 -0
  93. package/dist/commands/validate.js.map +1 -0
  94. package/dist/config.d.ts +80 -0
  95. package/dist/config.d.ts.map +1 -0
  96. package/dist/config.js +279 -0
  97. package/dist/config.js.map +1 -0
  98. package/dist/exit-codes.d.ts +26 -0
  99. package/dist/exit-codes.d.ts.map +1 -0
  100. package/dist/exit-codes.js +42 -0
  101. package/dist/exit-codes.js.map +1 -0
  102. package/dist/index.d.ts +8 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +8 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/output.d.ts +29 -0
  107. package/dist/output.d.ts.map +1 -0
  108. package/dist/output.js +38 -0
  109. package/dist/output.js.map +1 -0
  110. package/dist/scan/canonical.d.ts +33 -0
  111. package/dist/scan/canonical.d.ts.map +1 -0
  112. package/dist/scan/canonical.js +57 -0
  113. package/dist/scan/canonical.js.map +1 -0
  114. package/dist/scan/discover.d.ts +13 -0
  115. package/dist/scan/discover.d.ts.map +1 -0
  116. package/dist/scan/discover.js +95 -0
  117. package/dist/scan/discover.js.map +1 -0
  118. package/dist/scan/scanner.d.ts +32 -0
  119. package/dist/scan/scanner.d.ts.map +1 -0
  120. package/dist/scan/scanner.js +137 -0
  121. package/dist/scan/scanner.js.map +1 -0
  122. package/dist/types-gen/extract.d.ts +22 -0
  123. package/dist/types-gen/extract.d.ts.map +1 -0
  124. package/dist/types-gen/extract.js +148 -0
  125. package/dist/types-gen/extract.js.map +1 -0
  126. package/dist/types-gen/generate.d.ts +10 -0
  127. package/dist/types-gen/generate.d.ts.map +1 -0
  128. package/dist/types-gen/generate.js +39 -0
  129. package/dist/types-gen/generate.js.map +1 -0
  130. package/package.json +71 -0
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared flatten/unflatten for nested key/value formats (JSON, YAML). Nested
3
+ * objects become dot-notation keys (`home.cta`) and rebuild on serialize,
4
+ * preserving insertion order. Values convert through the ContentUnit helpers.
5
+ */
6
+ import { toContentUnit, fromContentUnit } from './content.js';
7
+ export function flattenObject(obj, prefix, out) {
8
+ for (const [k, v] of Object.entries(obj)) {
9
+ const key = prefix ? `${prefix}.${k}` : k;
10
+ if (Array.isArray(v)) {
11
+ throw new Error(`Array values are not supported (key "${key}").`);
12
+ }
13
+ if (v !== null && typeof v === 'object') {
14
+ flattenObject(v, key, out);
15
+ }
16
+ else {
17
+ out.push({ key, value: toContentUnit(String(v)) });
18
+ }
19
+ }
20
+ }
21
+ export function unflattenEntries(entries) {
22
+ const root = {};
23
+ for (const entry of entries) {
24
+ const parts = entry.key.split('.');
25
+ let node = root;
26
+ for (let i = 0; i < parts.length - 1; i += 1) {
27
+ const part = parts[i];
28
+ if (typeof node[part] !== 'object' || node[part] === null) {
29
+ node[part] = {};
30
+ }
31
+ node = node[part];
32
+ }
33
+ node[parts[parts.length - 1]] = fromContentUnit(entry.value);
34
+ }
35
+ return root;
36
+ }
37
+ //# sourceMappingURL=nested.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nested.js","sourceRoot":"","sources":["../../src/adapters/nested.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE9D,MAAM,UAAU,aAAa,CAC3B,GAA4B,EAC5B,MAAc,EACd,GAAuB;IAEvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,GAAG,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1C,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CAAC,wCAAwC,GAAG,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ,EAAE,CAAC;YACxC,aAAa,CAAC,CAA4B,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrD,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,OAA2B;IAC1D,MAAM,IAAI,GAA4B,EAAE,CAAC;IACzC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,IAAI,GAAG,IAAI,CAAC;QAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;YACvB,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBAC1D,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC;YAClB,CAAC;YACD,IAAI,GAAG,IAAI,CAAC,IAAI,CAA4B,CAAC;QAC/C,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC,GAAG,eAAe,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAChE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * PO / gettext adapter (PHP, Python, WordPress themes, C/Unix).
3
+ *
4
+ * In gettext the `msgid` (source text) *is* the key and `msgstr` is the
5
+ * translation. We parse singular entries with their extracted comments (`#.`),
6
+ * multi-line continuation strings, and standard escapes. The header entry
7
+ * (`msgid ""`) is skipped.
8
+ *
9
+ * Plurals and contexts flatten to keyed entries (the same approach the Android
10
+ * adapter uses for `<plurals>`):
11
+ * - `msgid_plural` / `msgstr[n]` → one entry per form, keyed `msgid[n]`
12
+ * (`item[0]`, `item[1]`, …).
13
+ * - `msgctxt` → the gettext-canonical `msgctxt<EOT>msgid` composite key, so
14
+ * same-text strings in different contexts stay distinct.
15
+ * Both round-trip: a file the CLI wrote parses back to the same entries.
16
+ */
17
+ import type { FormatAdapter, TranslationEntry } from './types.js';
18
+ export declare class PoAdapter implements FormatAdapter {
19
+ readonly format: "po";
20
+ parse(content: string): TranslationEntry[];
21
+ serialize(entries: TranslationEntry[]): string;
22
+ }
23
+ //# sourceMappingURL=po.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"po.d.ts","sourceRoot":"","sources":["../../src/adapters/po.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA8ClE,qBAAa,SAAU,YAAW,aAAa;IAC7C,QAAQ,CAAC,MAAM,OAAiB;IAEhC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAuF1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,MAAM;CA6C/C"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * PO / gettext adapter (PHP, Python, WordPress themes, C/Unix).
3
+ *
4
+ * In gettext the `msgid` (source text) *is* the key and `msgstr` is the
5
+ * translation. We parse singular entries with their extracted comments (`#.`),
6
+ * multi-line continuation strings, and standard escapes. The header entry
7
+ * (`msgid ""`) is skipped.
8
+ *
9
+ * Plurals and contexts flatten to keyed entries (the same approach the Android
10
+ * adapter uses for `<plurals>`):
11
+ * - `msgid_plural` / `msgstr[n]` → one entry per form, keyed `msgid[n]`
12
+ * (`item[0]`, `item[1]`, …).
13
+ * - `msgctxt` → the gettext-canonical `msgctxt<EOT>msgid` composite key, so
14
+ * same-text strings in different contexts stay distinct.
15
+ * Both round-trip: a file the CLI wrote parses back to the same entries.
16
+ */
17
+ import { toContentUnit, fromContentUnit } from './content.js';
18
+ /** gettext's context/message separator (EOT), as used in compiled .mo keys. */
19
+ const CTX = String.fromCharCode(4); // gettext EOT context separator
20
+ const PLURAL_KEY_RE = /^(.*)\[(\d+)\]$/;
21
+ function unescape(s) {
22
+ return s.replace(/\\(.)/g, (_, c) => {
23
+ if (c === 'n')
24
+ return '\n';
25
+ if (c === 't')
26
+ return '\t';
27
+ if (c === 'r')
28
+ return '\r';
29
+ return c;
30
+ });
31
+ }
32
+ function escape(s) {
33
+ return s
34
+ .replace(/\\/g, '\\\\')
35
+ .replace(/"/g, '\\"')
36
+ .replace(/\n/g, '\\n')
37
+ .replace(/\t/g, '\\t');
38
+ }
39
+ function quoted(line) {
40
+ const m = line.match(/"((?:\\.|[^"\\])*)"/);
41
+ return m ? unescape(m[1]) : '';
42
+ }
43
+ /** Split a flattened key back into [context?, msgid]. */
44
+ function splitContext(key) {
45
+ const i = key.indexOf(CTX);
46
+ return i === -1 ? [undefined, key] : [key.slice(0, i), key.slice(i + 1)];
47
+ }
48
+ export class PoAdapter {
49
+ format = 'po';
50
+ parse(content) {
51
+ const blocks = [];
52
+ let cur = null;
53
+ let target = null;
54
+ let pendingComments = [];
55
+ const append = (text) => {
56
+ if (!cur || target === null)
57
+ return;
58
+ if (typeof target === 'number') {
59
+ (cur.plurals ??= [])[target] = (cur.plurals[target] ?? '') + text;
60
+ }
61
+ else if (target === 'msgctxt')
62
+ cur.msgctxt = (cur.msgctxt ?? '') + text;
63
+ else if (target === 'msgid')
64
+ cur.msgid = (cur.msgid ?? '') + text;
65
+ else if (target === 'msgid_plural')
66
+ cur.msgidPlural = (cur.msgidPlural ?? '') + text;
67
+ else
68
+ cur.msgstr = (cur.msgstr ?? '') + text;
69
+ };
70
+ const flush = () => {
71
+ if (cur)
72
+ blocks.push(cur);
73
+ cur = null;
74
+ target = null;
75
+ };
76
+ for (const raw of content.split(/\r?\n/)) {
77
+ const line = raw.trim();
78
+ if (line === '') {
79
+ flush();
80
+ pendingComments = [];
81
+ continue;
82
+ }
83
+ if (line.startsWith('#')) {
84
+ if (line.startsWith('#.'))
85
+ pendingComments.push(line.slice(2).trim());
86
+ continue;
87
+ }
88
+ if (!cur) {
89
+ cur = pendingComments.length ? { comment: pendingComments.join(' ') } : {};
90
+ pendingComments = [];
91
+ }
92
+ const pluralStr = line.match(/^msgstr\[(\d+)\]/);
93
+ if (line.startsWith('msgctxt')) {
94
+ cur.msgctxt = quoted(line);
95
+ target = 'msgctxt';
96
+ }
97
+ else if (line.startsWith('msgid_plural')) {
98
+ cur.msgidPlural = quoted(line);
99
+ target = 'msgid_plural';
100
+ }
101
+ else if (line.startsWith('msgid')) {
102
+ cur.msgid = quoted(line);
103
+ target = 'msgid';
104
+ }
105
+ else if (pluralStr) {
106
+ const idx = Number(pluralStr[1]);
107
+ (cur.plurals ??= [])[idx] = quoted(line);
108
+ target = idx;
109
+ }
110
+ else if (line.startsWith('msgstr')) {
111
+ cur.msgstr = quoted(line);
112
+ target = 'msgstr';
113
+ }
114
+ else if (line.startsWith('"')) {
115
+ append(quoted(line));
116
+ }
117
+ }
118
+ flush();
119
+ const out = [];
120
+ for (const b of blocks) {
121
+ if (!b.msgid)
122
+ continue; // header (msgid "") or malformed
123
+ const base = b.msgctxt ? `${b.msgctxt}${CTX}${b.msgid}` : b.msgid;
124
+ const withComment = (key, value, comment) => ({
125
+ key,
126
+ value: toContentUnit(value),
127
+ ...(comment ? { comment } : {}),
128
+ });
129
+ if (b.plurals || b.msgidPlural !== undefined) {
130
+ const forms = b.plurals ?? [];
131
+ const count = Math.max(forms.length, 2);
132
+ for (let i = 0; i < count; i++) {
133
+ const raw = forms[i];
134
+ const value = raw && raw.length > 0 ? raw : i === 0 ? b.msgid : (b.msgidPlural ?? b.msgid);
135
+ out.push(withComment(`${base}[${i}]`, value, i === 0 ? b.comment : undefined));
136
+ }
137
+ }
138
+ else {
139
+ const value = b.msgstr && b.msgstr.length > 0 ? b.msgstr : b.msgid;
140
+ out.push(withComment(base, value, b.comment));
141
+ }
142
+ }
143
+ return out;
144
+ }
145
+ serialize(entries) {
146
+ const lines = [
147
+ 'msgid ""',
148
+ 'msgstr ""',
149
+ '"Content-Type: text/plain; charset=UTF-8\\n"',
150
+ '',
151
+ ];
152
+ for (let i = 0; i < entries.length;) {
153
+ const pluralMatch = entries[i].key.match(PLURAL_KEY_RE);
154
+ if (pluralMatch) {
155
+ const base = pluralMatch[1];
156
+ // Consume consecutive entries sharing this plural base.
157
+ const forms = [];
158
+ for (; i < entries.length; i++) {
159
+ const m = entries[i].key.match(PLURAL_KEY_RE);
160
+ if (!m || m[1] !== base)
161
+ break;
162
+ forms.push({
163
+ index: Number(m[2]),
164
+ value: fromContentUnit(entries[i].value),
165
+ comment: entries[i].comment,
166
+ });
167
+ }
168
+ forms.sort((a, b) => a.index - b.index);
169
+ const [ctx, msgid] = splitContext(base);
170
+ const comment = forms.find((f) => f.comment)?.comment;
171
+ if (comment)
172
+ lines.push(`#. ${comment}`);
173
+ if (ctx !== undefined)
174
+ lines.push(`msgctxt "${escape(ctx)}"`);
175
+ lines.push(`msgid "${escape(msgid)}"`);
176
+ lines.push(`msgid_plural "${escape(msgid)}"`);
177
+ for (const f of forms)
178
+ lines.push(`msgstr[${f.index}] "${escape(f.value)}"`);
179
+ lines.push('');
180
+ }
181
+ else {
182
+ const entry = entries[i];
183
+ const [ctx, msgid] = splitContext(entry.key);
184
+ if (entry.comment)
185
+ lines.push(`#. ${entry.comment}`);
186
+ if (ctx !== undefined)
187
+ lines.push(`msgctxt "${escape(ctx)}"`);
188
+ lines.push(`msgid "${escape(msgid)}"`);
189
+ lines.push(`msgstr "${escape(fromContentUnit(entry.value))}"`);
190
+ lines.push('');
191
+ i++;
192
+ }
193
+ }
194
+ return lines.join('\n');
195
+ }
196
+ }
197
+ //# sourceMappingURL=po.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"po.js","sourceRoot":"","sources":["../../src/adapters/po.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Format adapter contract. Every translation file format the CLI supports
3
+ * implements this: parse a file's text into a flat list of keyed ContentUnit
4
+ * entries, and serialize such a list back to the format's text.
5
+ *
6
+ * Adapters must be **round-trip stable**: for a file the CLI itself wrote,
7
+ * `parse(serialize(entries))` reproduces `entries`.
8
+ *
9
+ * Most formats are *per-locale* (one file = one locale). A few (e.g. Apple's
10
+ * `.xcstrings` String Catalog) are *multi-locale* (one file holds every
11
+ * locale); those set `multiLocale` and implement `mergeLocale`, and the
12
+ * commands read-modify-write a single catalog instead of writing one file per
13
+ * locale.
14
+ */
15
+ import type { ContentUnit } from '@lionrapid/core';
16
+ import type { FileFormat } from '../config.js';
17
+ /** One translatable string: a stable `key` (the file's own name/path) + value. */
18
+ export interface TranslationEntry {
19
+ key: string;
20
+ value: ContentUnit;
21
+ /** Developer comment/description carried by some formats (.arb, .po, .xcstrings). */
22
+ comment?: string;
23
+ }
24
+ export interface ParseOptions {
25
+ /** For multi-locale formats, which locale's strings to extract. */
26
+ locale?: string;
27
+ }
28
+ export interface SerializeOptions {
29
+ /** Target locale being written (formats like .xcstrings/.arb may record it). */
30
+ locale?: string;
31
+ /** Source locale, recorded by bilingual formats (e.g. XLIFF `source-language`). */
32
+ sourceLocale?: string;
33
+ }
34
+ export interface FormatAdapter {
35
+ readonly format: FileFormat;
36
+ parse(content: string, options?: ParseOptions): TranslationEntry[];
37
+ serialize(entries: TranslationEntry[], options?: SerializeOptions): string;
38
+ /** True for single-file, multi-locale catalogs (e.g. .xcstrings). */
39
+ readonly multiLocale?: boolean;
40
+ /**
41
+ * Inject `entries` for `locale` into an existing catalog (or a fresh one when
42
+ * `existing` is null), preserving other locales. Required when `multiLocale`.
43
+ */
44
+ mergeLocale?(existing: string | null, entries: TranslationEntry[], locale: string): string;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,kFAAkF;AAClF,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,WAAW,CAAC;IACnB,qFAAqF;IACrF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,gBAAgB,EAAE,CAAC;IACnE,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IAC3E,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,WAAW,CAAC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;CAC5F"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Format adapter contract. Every translation file format the CLI supports
3
+ * implements this: parse a file's text into a flat list of keyed ContentUnit
4
+ * entries, and serialize such a list back to the format's text.
5
+ *
6
+ * Adapters must be **round-trip stable**: for a file the CLI itself wrote,
7
+ * `parse(serialize(entries))` reproduces `entries`.
8
+ *
9
+ * Most formats are *per-locale* (one file = one locale). A few (e.g. Apple's
10
+ * `.xcstrings` String Catalog) are *multi-locale* (one file holds every
11
+ * locale); those set `multiLocale` and implement `mergeLocale`, and the
12
+ * commands read-modify-write a single catalog instead of writing one file per
13
+ * locale.
14
+ */
15
+ export {};
16
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/adapters/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Apple `.xcstrings` adapter (String Catalog).
3
+ *
4
+ * Unlike `.strings`, a String Catalog is a single JSON file holding *every*
5
+ * locale, so it is a `multiLocale` adapter: `parse` extracts one locale,
6
+ * `mergeLocale` injects a locale into an existing catalog (preserving the
7
+ * others), and `serialize` emits a fresh single-locale catalog.
8
+ *
9
+ * For the source language, a key with no explicit `stringUnit` uses the key
10
+ * itself as its value (Xcode's convention).
11
+ */
12
+ import type { FormatAdapter, ParseOptions, SerializeOptions, TranslationEntry } from './types.js';
13
+ export declare class XcstringsAdapter implements FormatAdapter {
14
+ readonly format: "xcstrings";
15
+ readonly multiLocale = true;
16
+ parse(content: string, options?: ParseOptions): TranslationEntry[];
17
+ mergeLocale(existing: string | null, entries: TranslationEntry[], locale: string): string;
18
+ serialize(entries: TranslationEntry[], options?: SerializeOptions): string;
19
+ }
20
+ //# sourceMappingURL=xcstrings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcstrings.d.ts","sourceRoot":"","sources":["../../src/adapters/xcstrings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA4ClG,qBAAa,gBAAiB,YAAW,aAAa;IACpD,QAAQ,CAAC,MAAM,cAAwB;IACvC,QAAQ,CAAC,WAAW,QAAQ;IAE5B,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,gBAAgB,EAAE;IAqBlE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,gBAAgB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM;IAazF,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM;CAQ3E"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Apple `.xcstrings` adapter (String Catalog).
3
+ *
4
+ * Unlike `.strings`, a String Catalog is a single JSON file holding *every*
5
+ * locale, so it is a `multiLocale` adapter: `parse` extracts one locale,
6
+ * `mergeLocale` injects a locale into an existing catalog (preserving the
7
+ * others), and `serialize` emits a fresh single-locale catalog.
8
+ *
9
+ * For the source language, a key with no explicit `stringUnit` uses the key
10
+ * itself as its value (Xcode's convention).
11
+ */
12
+ import { toContentUnit, fromContentUnit } from './content.js';
13
+ function parseCatalog(content) {
14
+ const trimmed = content.trim();
15
+ if (trimmed === '')
16
+ return { sourceLanguage: 'en', version: '1.0', strings: {} };
17
+ let data;
18
+ try {
19
+ data = JSON.parse(trimmed);
20
+ }
21
+ catch (err) {
22
+ throw new Error(`Invalid .xcstrings (JSON): ${err instanceof Error ? err.message : String(err)}`);
23
+ }
24
+ if (data === null || typeof data !== 'object') {
25
+ throw new Error('Expected a String Catalog object.');
26
+ }
27
+ const cat = data;
28
+ return {
29
+ sourceLanguage: cat.sourceLanguage ?? 'en',
30
+ version: cat.version ?? '1.0',
31
+ strings: cat.strings ?? {},
32
+ };
33
+ }
34
+ export class XcstringsAdapter {
35
+ format = 'xcstrings';
36
+ multiLocale = true;
37
+ parse(content, options) {
38
+ const catalog = parseCatalog(content);
39
+ const locale = options?.locale ?? catalog.sourceLanguage;
40
+ const isSource = locale === catalog.sourceLanguage;
41
+ const out = [];
42
+ for (const [key, entry] of Object.entries(catalog.strings)) {
43
+ if (entry.shouldTranslate === false)
44
+ continue;
45
+ const unit = entry.localizations?.[locale]?.stringUnit;
46
+ let value = unit?.value;
47
+ if (value === undefined && isSource)
48
+ value = key; // Xcode: key is the source value
49
+ if (value === undefined)
50
+ continue; // not translated for this locale
51
+ out.push({
52
+ key,
53
+ value: toContentUnit(value),
54
+ ...(entry.comment ? { comment: entry.comment } : {}),
55
+ });
56
+ }
57
+ return out;
58
+ }
59
+ mergeLocale(existing, entries, locale) {
60
+ const catalog = existing ? parseCatalog(existing) : { sourceLanguage: 'en', version: '1.0', strings: {} };
61
+ for (const entry of entries) {
62
+ const str = (catalog.strings[entry.key] ??= {});
63
+ if (entry.comment && !str.comment)
64
+ str.comment = entry.comment;
65
+ const localizations = (str.localizations ??= {});
66
+ localizations[locale] = {
67
+ stringUnit: { state: 'translated', value: fromContentUnit(entry.value) },
68
+ };
69
+ }
70
+ return `${JSON.stringify(catalog, null, 2)}\n`;
71
+ }
72
+ serialize(entries, options) {
73
+ const locale = options?.locale ?? 'en';
74
+ return this.mergeLocale(JSON.stringify({ sourceLanguage: locale, version: '1.0', strings: {} }), entries, locale);
75
+ }
76
+ }
77
+ //# sourceMappingURL=xcstrings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xcstrings.js","sourceRoot":"","sources":["../../src/adapters/xcstrings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * XLIFF adapter (enterprise / CAT-tool interchange — Trados, memoQ, Crowdin,
3
+ * Lokalise export).
4
+ *
5
+ * One file per locale. Each translation unit's `id` is the key; the value is the
6
+ * `<target>` text (falling back to `<source>` for an untranslated source file).
7
+ * We read both XLIFF **1.2** (`<trans-unit>`) and **2.0** (`<unit><segment>`),
8
+ * and write 1.2 (the most broadly supported). Parsing is regex-based (no XML
9
+ * dep), matching the Android adapter's approach; plain-text segments are handled
10
+ * (inline `<g>`/`<x/>` markup is out of scope for now).
11
+ *
12
+ * Round-trip note: the CLI's per-locale target file carries only the value, so
13
+ * serialize mirrors it into `<source>` and `<target>` — like the JSON/ARB
14
+ * adapters, which also store just the translated value.
15
+ */
16
+ import type { FormatAdapter, SerializeOptions, TranslationEntry } from './types.js';
17
+ export declare class XliffAdapter implements FormatAdapter {
18
+ readonly format: "xliff";
19
+ parse(content: string): TranslationEntry[];
20
+ serialize(entries: TranslationEntry[], options?: SerializeOptions): string;
21
+ }
22
+ //# sourceMappingURL=xliff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xliff.d.ts","sourceRoot":"","sources":["../../src/adapters/xliff.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA+BpF,qBAAa,YAAa,YAAW,aAAa;IAChD,QAAQ,CAAC,MAAM,UAAoB;IAEnC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAkB1C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM;CAoB3E"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * XLIFF adapter (enterprise / CAT-tool interchange — Trados, memoQ, Crowdin,
3
+ * Lokalise export).
4
+ *
5
+ * One file per locale. Each translation unit's `id` is the key; the value is the
6
+ * `<target>` text (falling back to `<source>` for an untranslated source file).
7
+ * We read both XLIFF **1.2** (`<trans-unit>`) and **2.0** (`<unit><segment>`),
8
+ * and write 1.2 (the most broadly supported). Parsing is regex-based (no XML
9
+ * dep), matching the Android adapter's approach; plain-text segments are handled
10
+ * (inline `<g>`/`<x/>` markup is out of scope for now).
11
+ *
12
+ * Round-trip note: the CLI's per-locale target file carries only the value, so
13
+ * serialize mirrors it into `<source>` and `<target>` — like the JSON/ARB
14
+ * adapters, which also store just the translated value.
15
+ */
16
+ import { toContentUnit, fromContentUnit } from './content.js';
17
+ // trans-unit (1.2) or unit (2.0); capture id + inner body.
18
+ const UNIT_RE = /<(?:trans-unit|unit)\b[^>]*\bid="([^"]*)"[^>]*>([\s\S]*?)<\/(?:trans-unit|unit)>/g;
19
+ const SOURCE_RE = /<source\b[^>]*>([\s\S]*?)<\/source>/;
20
+ const TARGET_RE = /<target\b[^>]*>([\s\S]*?)<\/target>/;
21
+ const NOTE_RE = /<note\b[^>]*>([\s\S]*?)<\/note>/;
22
+ function decode(s) {
23
+ return s
24
+ .replace(/&lt;/g, '<')
25
+ .replace(/&gt;/g, '>')
26
+ .replace(/&quot;/g, '"')
27
+ .replace(/&apos;/g, "'")
28
+ .replace(/&#(\d+);/g, (m, n) => {
29
+ const code = Number(n);
30
+ // Leave out-of-range refs untouched rather than throwing on bad input.
31
+ return code >= 0 && code <= 0x10ffff ? String.fromCodePoint(code) : m;
32
+ })
33
+ .replace(/&amp;/g, '&'); // last, so &amp;lt; → &lt;
34
+ }
35
+ function encode(s) {
36
+ return s
37
+ .replace(/&/g, '&amp;')
38
+ .replace(/</g, '&lt;')
39
+ .replace(/>/g, '&gt;')
40
+ .replace(/"/g, '&quot;');
41
+ }
42
+ export class XliffAdapter {
43
+ format = 'xliff';
44
+ parse(content) {
45
+ const out = [];
46
+ for (const m of content.matchAll(UNIT_RE)) {
47
+ const id = m[1];
48
+ const body = m[2];
49
+ const target = body.match(TARGET_RE)?.[1];
50
+ const source = body.match(SOURCE_RE)?.[1];
51
+ const raw = target && target.trim().length > 0 ? target : (source ?? '');
52
+ const note = body.match(NOTE_RE)?.[1];
53
+ out.push({
54
+ key: id,
55
+ value: toContentUnit(decode(raw)),
56
+ ...(note ? { comment: decode(note) } : {}),
57
+ });
58
+ }
59
+ return out;
60
+ }
61
+ serialize(entries, options) {
62
+ const srcLang = options?.sourceLocale ?? 'en';
63
+ const trgLang = options?.locale ?? 'und';
64
+ const lines = [
65
+ '<?xml version="1.0" encoding="UTF-8"?>',
66
+ '<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">',
67
+ ` <file source-language="${encode(srcLang)}" target-language="${encode(trgLang)}" datatype="plaintext" original="messages">`,
68
+ ' <body>',
69
+ ];
70
+ for (const entry of entries) {
71
+ const value = encode(fromContentUnit(entry.value));
72
+ lines.push(` <trans-unit id="${encode(entry.key)}">`);
73
+ lines.push(` <source>${value}</source>`);
74
+ lines.push(` <target>${value}</target>`);
75
+ if (entry.comment)
76
+ lines.push(` <note>${encode(entry.comment)}</note>`);
77
+ lines.push(' </trans-unit>');
78
+ }
79
+ lines.push(' </body>', ' </file>', '</xliff>', '');
80
+ return lines.join('\n');
81
+ }
82
+ }
83
+ //# sourceMappingURL=xliff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xliff.js","sourceRoot":"","sources":["../../src/adapters/xliff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * YAML adapter — follows the Rails convention where the top-level key is the
3
+ * locale (`en:` → translations). On parse we descend into the locale root; on
4
+ * serialize we wrap the nested tree under the target locale. Nested keys
5
+ * flatten to dot notation, like the JSON adapter.
6
+ *
7
+ * (Plain locale-less YAML — e.g. some Vue-i18n setups — is best served by the
8
+ * JSON adapter; this adapter assumes the Rails/locale-rooted shape.)
9
+ */
10
+ import type { FormatAdapter, ParseOptions, SerializeOptions, TranslationEntry } from './types.js';
11
+ export declare class YamlAdapter implements FormatAdapter {
12
+ readonly format: "yaml";
13
+ parse(content: string, options?: ParseOptions): TranslationEntry[];
14
+ serialize(entries: TranslationEntry[], options?: SerializeOptions): string;
15
+ }
16
+ //# sourceMappingURL=yaml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yaml.d.ts","sourceRoot":"","sources":["../../src/adapters/yaml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAOlG,qBAAa,WAAY,YAAW,aAAa;IAC/C,QAAQ,CAAC,MAAM,SAAmB;IAElC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,gBAAgB,EAAE;IA2BlE,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM;CAK3E"}