@langapi/mcp-server 1.0.6 → 1.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 (64) hide show
  1. package/dist/handlers/index.d.ts +7 -0
  2. package/dist/handlers/index.d.ts.map +1 -0
  3. package/dist/handlers/index.js +7 -0
  4. package/dist/handlers/index.js.map +1 -0
  5. package/dist/handlers/xcstrings-sync-handler.d.ts +72 -0
  6. package/dist/handlers/xcstrings-sync-handler.d.ts.map +1 -0
  7. package/dist/handlers/xcstrings-sync-handler.js +126 -0
  8. package/dist/handlers/xcstrings-sync-handler.js.map +1 -0
  9. package/dist/locale-detection/index.d.ts.map +1 -1
  10. package/dist/locale-detection/index.js +80 -7
  11. package/dist/locale-detection/index.js.map +1 -1
  12. package/dist/locale-detection/patterns.d.ts.map +1 -1
  13. package/dist/locale-detection/patterns.js +45 -0
  14. package/dist/locale-detection/patterns.js.map +1 -1
  15. package/dist/tools/get-translation-status.d.ts.map +1 -1
  16. package/dist/tools/get-translation-status.js +34 -6
  17. package/dist/tools/get-translation-status.js.map +1 -1
  18. package/dist/tools/list-local-locales.d.ts +1 -1
  19. package/dist/tools/list-local-locales.js +2 -2
  20. package/dist/tools/list-local-locales.js.map +1 -1
  21. package/dist/tools/sync-translations.d.ts.map +1 -1
  22. package/dist/tools/sync-translations.js +228 -42
  23. package/dist/tools/sync-translations.js.map +1 -1
  24. package/dist/utils/apple-common.d.ts +63 -0
  25. package/dist/utils/apple-common.d.ts.map +1 -0
  26. package/dist/utils/apple-common.js +118 -0
  27. package/dist/utils/apple-common.js.map +1 -0
  28. package/dist/utils/apple-common.test.d.ts +2 -0
  29. package/dist/utils/apple-common.test.d.ts.map +1 -0
  30. package/dist/utils/apple-common.test.js +108 -0
  31. package/dist/utils/apple-common.test.js.map +1 -0
  32. package/dist/utils/arb-parser.d.ts +77 -0
  33. package/dist/utils/arb-parser.d.ts.map +1 -0
  34. package/dist/utils/arb-parser.js +142 -0
  35. package/dist/utils/arb-parser.js.map +1 -0
  36. package/dist/utils/arb-parser.test.d.ts +2 -0
  37. package/dist/utils/arb-parser.test.d.ts.map +1 -0
  38. package/dist/utils/arb-parser.test.js +243 -0
  39. package/dist/utils/arb-parser.test.js.map +1 -0
  40. package/dist/utils/strings-parser.d.ts +62 -0
  41. package/dist/utils/strings-parser.d.ts.map +1 -0
  42. package/dist/utils/strings-parser.js +276 -0
  43. package/dist/utils/strings-parser.js.map +1 -0
  44. package/dist/utils/strings-parser.test.d.ts +2 -0
  45. package/dist/utils/strings-parser.test.d.ts.map +1 -0
  46. package/dist/utils/strings-parser.test.js +143 -0
  47. package/dist/utils/strings-parser.test.js.map +1 -0
  48. package/dist/utils/stringsdict-parser.d.ts +106 -0
  49. package/dist/utils/stringsdict-parser.d.ts.map +1 -0
  50. package/dist/utils/stringsdict-parser.js +473 -0
  51. package/dist/utils/stringsdict-parser.js.map +1 -0
  52. package/dist/utils/stringsdict-parser.test.d.ts +2 -0
  53. package/dist/utils/stringsdict-parser.test.d.ts.map +1 -0
  54. package/dist/utils/stringsdict-parser.test.js +244 -0
  55. package/dist/utils/stringsdict-parser.test.js.map +1 -0
  56. package/dist/utils/xcstrings-parser.d.ts +128 -0
  57. package/dist/utils/xcstrings-parser.d.ts.map +1 -0
  58. package/dist/utils/xcstrings-parser.js +216 -0
  59. package/dist/utils/xcstrings-parser.js.map +1 -0
  60. package/dist/utils/xcstrings-parser.test.d.ts +2 -0
  61. package/dist/utils/xcstrings-parser.test.d.ts.map +1 -0
  62. package/dist/utils/xcstrings-parser.test.js +157 -0
  63. package/dist/utils/xcstrings-parser.test.js.map +1 -0
  64. package/package.json +1 -1
@@ -0,0 +1,243 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { isArbFile, isArbMetadataKey, isArbLocaleKey, parseArbContent, reconstructArbContent, mergeArbContent, getLocaleFileExtension, } from "./arb-parser.js";
3
+ describe("ARB Parser", () => {
4
+ describe("isArbFile", () => {
5
+ it("should return true for .arb files", () => {
6
+ expect(isArbFile("lib/l10n/app_en.arb")).toBe(true);
7
+ expect(isArbFile("/absolute/path/to/intl_de.arb")).toBe(true);
8
+ expect(isArbFile("simple.arb")).toBe(true);
9
+ });
10
+ it("should return false for non-ARB files", () => {
11
+ expect(isArbFile("locales/en.json")).toBe(false);
12
+ expect(isArbFile("file.arb.json")).toBe(false);
13
+ expect(isArbFile("file.txt")).toBe(false);
14
+ });
15
+ it("should be case-insensitive", () => {
16
+ expect(isArbFile("file.ARB")).toBe(true);
17
+ expect(isArbFile("file.Arb")).toBe(true);
18
+ expect(isArbFile("FILE.ARB")).toBe(true);
19
+ });
20
+ });
21
+ describe("getLocaleFileExtension", () => {
22
+ it("should return .arb for ARB files", () => {
23
+ expect(getLocaleFileExtension("app_en.arb")).toBe(".arb");
24
+ expect(getLocaleFileExtension("file.ARB")).toBe(".arb");
25
+ });
26
+ it("should return .json for non-ARB files", () => {
27
+ expect(getLocaleFileExtension("en.json")).toBe(".json");
28
+ expect(getLocaleFileExtension("file.txt")).toBe(".json");
29
+ expect(getLocaleFileExtension("noextension")).toBe(".json");
30
+ });
31
+ });
32
+ describe("isArbMetadataKey", () => {
33
+ it("should return true for @ prefixed keys", () => {
34
+ expect(isArbMetadataKey("@greeting")).toBe(true);
35
+ expect(isArbMetadataKey("@@locale")).toBe(true);
36
+ expect(isArbMetadataKey("@")).toBe(true);
37
+ });
38
+ it("should return false for regular keys", () => {
39
+ expect(isArbMetadataKey("greeting")).toBe(false);
40
+ expect(isArbMetadataKey("hello_world")).toBe(false);
41
+ expect(isArbMetadataKey("")).toBe(false);
42
+ });
43
+ });
44
+ describe("isArbLocaleKey", () => {
45
+ it("should return true for @@locale", () => {
46
+ expect(isArbLocaleKey("@@locale")).toBe(true);
47
+ });
48
+ it("should return false for other keys", () => {
49
+ expect(isArbLocaleKey("@locale")).toBe(false);
50
+ expect(isArbLocaleKey("@@other")).toBe(false);
51
+ expect(isArbLocaleKey("locale")).toBe(false);
52
+ });
53
+ });
54
+ describe("parseArbContent", () => {
55
+ it("should separate translatable keys from metadata", () => {
56
+ const arbData = {
57
+ "@@locale": "en",
58
+ greeting: "Hello, {name}!",
59
+ "@greeting": {
60
+ description: "A greeting message",
61
+ placeholders: {
62
+ name: { type: "String" },
63
+ },
64
+ },
65
+ farewell: "Goodbye!",
66
+ };
67
+ const result = parseArbContent(arbData);
68
+ expect(result.locale).toBe("en");
69
+ expect(result.translatableKeys).toEqual([
70
+ { key: "greeting", value: "Hello, {name}!" },
71
+ { key: "farewell", value: "Goodbye!" },
72
+ ]);
73
+ expect(result.metadata["@@locale"]).toBe("en");
74
+ expect(result.metadata["@greeting"]).toEqual({
75
+ description: "A greeting message",
76
+ placeholders: {
77
+ name: { type: "String" },
78
+ },
79
+ });
80
+ });
81
+ it("should handle ARB files without metadata", () => {
82
+ const arbData = {
83
+ "@@locale": "en",
84
+ hello: "Hello",
85
+ world: "World",
86
+ };
87
+ const result = parseArbContent(arbData);
88
+ expect(result.locale).toBe("en");
89
+ expect(result.translatableKeys).toHaveLength(2);
90
+ expect(Object.keys(result.metadata)).toEqual(["@@locale"]);
91
+ });
92
+ it("should handle ARB files without @@locale", () => {
93
+ const arbData = {
94
+ greeting: "Hello",
95
+ "@greeting": { description: "A greeting" },
96
+ };
97
+ const result = parseArbContent(arbData);
98
+ expect(result.locale).toBeNull();
99
+ expect(result.translatableKeys).toEqual([{ key: "greeting", value: "Hello" }]);
100
+ expect(result.metadata["@greeting"]).toEqual({ description: "A greeting" });
101
+ });
102
+ it("should skip non-string translatable values", () => {
103
+ const arbData = {
104
+ "@@locale": "en",
105
+ validString: "Hello",
106
+ invalidNumber: 123,
107
+ invalidObject: { nested: "value" },
108
+ "@validString": { description: "Valid" },
109
+ };
110
+ const result = parseArbContent(arbData);
111
+ expect(result.translatableKeys).toEqual([{ key: "validString", value: "Hello" }]);
112
+ });
113
+ });
114
+ describe("reconstructArbContent", () => {
115
+ it("should reconstruct ARB with updated locale and preserved metadata", () => {
116
+ const translations = [
117
+ { key: "greeting", value: "Hallo, {name}!" },
118
+ { key: "farewell", value: "Auf Wiedersehen!" },
119
+ ];
120
+ const metadata = {
121
+ "@@locale": "en",
122
+ "@greeting": { description: "A greeting message" },
123
+ };
124
+ const result = reconstructArbContent(translations, metadata, "de");
125
+ expect(result["@@locale"]).toBe("de");
126
+ expect(result["greeting"]).toBe("Hallo, {name}!");
127
+ expect(result["@greeting"]).toEqual({ description: "A greeting message" });
128
+ expect(result["farewell"]).toBe("Auf Wiedersehen!");
129
+ // farewell has no metadata, so @farewell should not exist
130
+ expect(result["@farewell"]).toBeUndefined();
131
+ });
132
+ it("should maintain key order: @@locale first, then key-metadata pairs", () => {
133
+ const translations = [
134
+ { key: "alpha", value: "Alpha" },
135
+ { key: "beta", value: "Beta" },
136
+ ];
137
+ const metadata = {
138
+ "@@locale": "en",
139
+ "@alpha": { description: "First" },
140
+ "@beta": { description: "Second" },
141
+ };
142
+ const result = reconstructArbContent(translations, metadata, "fr");
143
+ const keys = Object.keys(result);
144
+ expect(keys[0]).toBe("@@locale");
145
+ expect(keys[1]).toBe("alpha");
146
+ expect(keys[2]).toBe("@alpha");
147
+ expect(keys[3]).toBe("beta");
148
+ expect(keys[4]).toBe("@beta");
149
+ });
150
+ it("should handle empty metadata", () => {
151
+ const translations = [{ key: "hello", value: "Bonjour" }];
152
+ const metadata = { "@@locale": "en" };
153
+ const result = reconstructArbContent(translations, metadata, "fr");
154
+ expect(result["@@locale"]).toBe("fr");
155
+ expect(result["hello"]).toBe("Bonjour");
156
+ expect(Object.keys(result)).toHaveLength(2);
157
+ });
158
+ });
159
+ describe("mergeArbContent", () => {
160
+ it("should merge new translations with existing content", () => {
161
+ const existingContent = {
162
+ "@@locale": "de",
163
+ greeting: "Hallo",
164
+ "@greeting": { description: "A greeting" },
165
+ farewell: "Tschüss",
166
+ };
167
+ const newTranslations = [
168
+ { key: "greeting", value: "Hallo, {name}!" }, // Updated
169
+ ];
170
+ const sourceMetadata = {
171
+ "@@locale": "en",
172
+ "@greeting": { description: "A greeting message" },
173
+ "@farewell": { description: "A farewell" },
174
+ };
175
+ const sourceKeys = new Set(["greeting", "farewell"]);
176
+ const result = mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, "de");
177
+ expect(result["@@locale"]).toBe("de");
178
+ expect(result["greeting"]).toBe("Hallo, {name}!"); // New translation
179
+ expect(result["farewell"]).toBe("Tschüss"); // Preserved existing
180
+ expect(result["@greeting"]).toEqual({ description: "A greeting message" }); // Source metadata
181
+ expect(result["@farewell"]).toEqual({ description: "A farewell" }); // Source metadata
182
+ });
183
+ it("should remove keys not in source", () => {
184
+ const existingContent = {
185
+ "@@locale": "de",
186
+ greeting: "Hallo",
187
+ oldKey: "Should be removed",
188
+ };
189
+ const newTranslations = [];
190
+ const sourceMetadata = { "@@locale": "en" };
191
+ const sourceKeys = new Set(["greeting"]); // oldKey not in source
192
+ const result = mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, "de");
193
+ expect(result["greeting"]).toBe("Hallo");
194
+ expect(result["oldKey"]).toBeUndefined(); // Removed
195
+ });
196
+ it("should handle empty existing content", () => {
197
+ const existingContent = {};
198
+ const newTranslations = [
199
+ { key: "hello", value: "Bonjour" },
200
+ { key: "world", value: "Monde" },
201
+ ];
202
+ const sourceMetadata = {
203
+ "@@locale": "en",
204
+ "@hello": { description: "Hello message" },
205
+ };
206
+ const sourceKeys = new Set(["hello", "world"]);
207
+ const result = mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, "fr");
208
+ expect(result["@@locale"]).toBe("fr");
209
+ expect(result["hello"]).toBe("Bonjour");
210
+ expect(result["world"]).toBe("Monde");
211
+ expect(result["@hello"]).toEqual({ description: "Hello message" });
212
+ });
213
+ it("should preserve existing translations when no new translations provided", () => {
214
+ const existingContent = {
215
+ "@@locale": "de",
216
+ greeting: "Hallo",
217
+ farewell: "Tschüss",
218
+ };
219
+ const newTranslations = [];
220
+ const sourceMetadata = { "@@locale": "en" };
221
+ const sourceKeys = new Set(["greeting", "farewell"]);
222
+ const result = mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, "de");
223
+ expect(result["greeting"]).toBe("Hallo");
224
+ expect(result["farewell"]).toBe("Tschüss");
225
+ });
226
+ it("should use source metadata instead of existing metadata", () => {
227
+ const existingContent = {
228
+ "@@locale": "de",
229
+ greeting: "Hallo",
230
+ "@greeting": { description: "Old description" },
231
+ };
232
+ const newTranslations = [];
233
+ const sourceMetadata = {
234
+ "@@locale": "en",
235
+ "@greeting": { description: "New description from source" },
236
+ };
237
+ const sourceKeys = new Set(["greeting"]);
238
+ const result = mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, "de");
239
+ expect(result["@greeting"]).toEqual({ description: "New description from source" });
240
+ });
241
+ });
242
+ });
243
+ //# sourceMappingURL=arb-parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arb-parser.test.js","sourceRoot":"","sources":["../../src/utils/arb-parser.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Localizable.strings file parser
3
+ *
4
+ * Handles Apple's traditional localization format:
5
+ * - Key-value pairs: "key" = "value";
6
+ * - Comments: block and line comments
7
+ * - Escaped characters: quotes, newlines, tabs, backslashes, Unicode
8
+ */
9
+ import type { KeyValue } from "../api/types.js";
10
+ /**
11
+ * Parsed content from a .strings file
12
+ */
13
+ export interface StringsContent {
14
+ /** Key-value translation pairs */
15
+ entries: KeyValue[];
16
+ /** Comments associated with keys (key -> comment text) */
17
+ comments: Map<string, string>;
18
+ /** File-level header comment (at top of file) */
19
+ headerComment: string | null;
20
+ }
21
+ /**
22
+ * Parse a Localizable.strings file content
23
+ *
24
+ * Handles:
25
+ * - Basic key-value pairs: "key" = "value";
26
+ * - Block comments above entries for descriptions
27
+ * - Escaped quotes and special characters
28
+ * - Multi-line values
29
+ *
30
+ * @param content Raw file content
31
+ * @returns Parsed content with entries, comments, and header
32
+ */
33
+ export declare function parseStringsContent(content: string): StringsContent;
34
+ /**
35
+ * Escape a string for use in a .strings file
36
+ */
37
+ export declare function escapeStringsValue(value: string): string;
38
+ /**
39
+ * Reconstruct a .strings file from parsed content
40
+ *
41
+ * @param entries Translation key-value pairs
42
+ * @param comments Comments to associate with keys
43
+ * @param headerComment Optional file-level header comment
44
+ * @returns Formatted .strings file content
45
+ */
46
+ export declare function reconstructStringsContent(entries: KeyValue[], comments: Map<string, string>, headerComment: string | null): string;
47
+ /**
48
+ * Merge new translations into existing .strings content
49
+ *
50
+ * - Preserves existing translations not in newTranslations
51
+ * - Updates with new translations
52
+ * - Removes keys not in sourceKeys
53
+ * - Preserves comments from source
54
+ *
55
+ * @param existingContent Existing target file content (raw string)
56
+ * @param newTranslations New/updated translations from API
57
+ * @param sourceComments Comments from source file
58
+ * @param sourceKeys Set of all keys in source file
59
+ * @returns Merged .strings file content
60
+ */
61
+ export declare function mergeStringsContent(existingContent: string, newTranslations: KeyValue[], sourceComments: Map<string, string>, sourceKeys: Set<string>): string;
62
+ //# sourceMappingURL=strings-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strings-parser.d.ts","sourceRoot":"","sources":["../../src/utils/strings-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,kCAAkC;IAClC,OAAO,EAAE,QAAQ,EAAE,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,iDAAiD;IACjD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmInE;AA2ED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,QAAQ,EAAE,EACnB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,GAC3B,MAAM,CAoBR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,EAAE,MAAM,EACvB,eAAe,EAAE,QAAQ,EAAE,EAC3B,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,GACtB,MAAM,CA8BR"}
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Localizable.strings file parser
3
+ *
4
+ * Handles Apple's traditional localization format:
5
+ * - Key-value pairs: "key" = "value";
6
+ * - Comments: block and line comments
7
+ * - Escaped characters: quotes, newlines, tabs, backslashes, Unicode
8
+ */
9
+ /**
10
+ * Parse a Localizable.strings file content
11
+ *
12
+ * Handles:
13
+ * - Basic key-value pairs: "key" = "value";
14
+ * - Block comments above entries for descriptions
15
+ * - Escaped quotes and special characters
16
+ * - Multi-line values
17
+ *
18
+ * @param content Raw file content
19
+ * @returns Parsed content with entries, comments, and header
20
+ */
21
+ export function parseStringsContent(content) {
22
+ const entries = [];
23
+ const comments = new Map();
24
+ let headerComment = null;
25
+ // Track current position
26
+ let pos = 0;
27
+ let pendingComment = null;
28
+ let isFirstEntry = true;
29
+ while (pos < content.length) {
30
+ // Skip whitespace
31
+ while (pos < content.length && /\s/.test(content[pos])) {
32
+ pos++;
33
+ }
34
+ if (pos >= content.length)
35
+ break;
36
+ // Check for block comment /* ... */
37
+ if (content.slice(pos, pos + 2) === "/*") {
38
+ const commentEnd = content.indexOf("*/", pos + 2);
39
+ if (commentEnd === -1) {
40
+ // Unterminated comment, skip to end
41
+ break;
42
+ }
43
+ const commentText = content.slice(pos + 2, commentEnd).trim();
44
+ pendingComment = commentText;
45
+ pos = commentEnd + 2;
46
+ continue;
47
+ }
48
+ // Check for line comment // ...
49
+ if (content.slice(pos, pos + 2) === "//") {
50
+ const lineEnd = content.indexOf("\n", pos);
51
+ const commentText = lineEnd === -1
52
+ ? content.slice(pos + 2).trim()
53
+ : content.slice(pos + 2, lineEnd).trim();
54
+ pendingComment = commentText;
55
+ pos = lineEnd === -1 ? content.length : lineEnd + 1;
56
+ continue;
57
+ }
58
+ // Check for quoted key
59
+ if (content[pos] === '"') {
60
+ const keyResult = parseQuotedString(content, pos);
61
+ if (!keyResult) {
62
+ // Malformed, skip character
63
+ pos++;
64
+ continue;
65
+ }
66
+ const key = keyResult.value;
67
+ pos = keyResult.end;
68
+ // Skip whitespace
69
+ while (pos < content.length && /\s/.test(content[pos])) {
70
+ pos++;
71
+ }
72
+ // Expect '='
73
+ if (content[pos] !== "=") {
74
+ // Malformed, continue
75
+ continue;
76
+ }
77
+ pos++;
78
+ // Skip whitespace
79
+ while (pos < content.length && /\s/.test(content[pos])) {
80
+ pos++;
81
+ }
82
+ // Expect quoted value
83
+ if (content[pos] !== '"') {
84
+ // Malformed, continue
85
+ continue;
86
+ }
87
+ const valueResult = parseQuotedString(content, pos);
88
+ if (!valueResult) {
89
+ // Malformed, skip
90
+ pos++;
91
+ continue;
92
+ }
93
+ const value = valueResult.value;
94
+ pos = valueResult.end;
95
+ // Skip whitespace
96
+ while (pos < content.length && /\s/.test(content[pos])) {
97
+ pos++;
98
+ }
99
+ // Expect ';'
100
+ if (content[pos] === ";") {
101
+ pos++;
102
+ }
103
+ // Store entry
104
+ entries.push({ key, value });
105
+ // Handle comment
106
+ if (pendingComment !== null) {
107
+ if (isFirstEntry && entries.length === 1) {
108
+ // Check if this looks like a file header comment (contains copyright, license, etc.)
109
+ if (/copyright|license|generated|created by/i.test(pendingComment)) {
110
+ headerComment = pendingComment;
111
+ }
112
+ else {
113
+ comments.set(key, pendingComment);
114
+ }
115
+ }
116
+ else {
117
+ comments.set(key, pendingComment);
118
+ }
119
+ pendingComment = null;
120
+ }
121
+ isFirstEntry = false;
122
+ }
123
+ else {
124
+ // Skip unknown character
125
+ pos++;
126
+ }
127
+ }
128
+ // If there's a pending comment at the end with no entry, it might be a header
129
+ if (pendingComment !== null && entries.length === 0) {
130
+ headerComment = pendingComment;
131
+ }
132
+ return { entries, comments, headerComment };
133
+ }
134
+ /**
135
+ * Parse a quoted string starting at the given position
136
+ *
137
+ * @returns Object with unescaped value and end position, or null if malformed
138
+ */
139
+ function parseQuotedString(content, start) {
140
+ if (content[start] !== '"')
141
+ return null;
142
+ let pos = start + 1;
143
+ let value = "";
144
+ while (pos < content.length) {
145
+ const char = content[pos];
146
+ if (char === '"') {
147
+ // End of string
148
+ return { value, end: pos + 1 };
149
+ }
150
+ if (char === "\\") {
151
+ // Escape sequence
152
+ pos++;
153
+ if (pos >= content.length)
154
+ break;
155
+ const escaped = content[pos];
156
+ switch (escaped) {
157
+ case "n":
158
+ value += "\n";
159
+ break;
160
+ case "t":
161
+ value += "\t";
162
+ break;
163
+ case "r":
164
+ value += "\r";
165
+ break;
166
+ case '"':
167
+ value += '"';
168
+ break;
169
+ case "\\":
170
+ value += "\\";
171
+ break;
172
+ case "U":
173
+ case "u":
174
+ // Unicode escape: \U0000 or \u0000
175
+ const hexLength = escaped === "U" ? 4 : 4;
176
+ const hex = content.slice(pos + 1, pos + 1 + hexLength);
177
+ if (/^[0-9A-Fa-f]+$/.test(hex)) {
178
+ const codePoint = parseInt(hex, 16);
179
+ value += String.fromCodePoint(codePoint);
180
+ pos += hexLength;
181
+ }
182
+ else {
183
+ // Invalid unicode, keep as-is
184
+ value += "\\" + escaped;
185
+ }
186
+ break;
187
+ default:
188
+ // Unknown escape, keep both characters
189
+ value += "\\" + escaped;
190
+ }
191
+ pos++;
192
+ }
193
+ else {
194
+ value += char;
195
+ pos++;
196
+ }
197
+ }
198
+ // Unterminated string
199
+ return null;
200
+ }
201
+ /**
202
+ * Escape a string for use in a .strings file
203
+ */
204
+ export function escapeStringsValue(value) {
205
+ return value
206
+ .replace(/\\/g, "\\\\")
207
+ .replace(/"/g, '\\"')
208
+ .replace(/\n/g, "\\n")
209
+ .replace(/\r/g, "\\r")
210
+ .replace(/\t/g, "\\t");
211
+ }
212
+ /**
213
+ * Reconstruct a .strings file from parsed content
214
+ *
215
+ * @param entries Translation key-value pairs
216
+ * @param comments Comments to associate with keys
217
+ * @param headerComment Optional file-level header comment
218
+ * @returns Formatted .strings file content
219
+ */
220
+ export function reconstructStringsContent(entries, comments, headerComment) {
221
+ const lines = [];
222
+ // Add header comment if present
223
+ if (headerComment) {
224
+ lines.push(`/* ${headerComment} */`);
225
+ lines.push("");
226
+ }
227
+ // Add each entry with its comment
228
+ for (const { key, value } of entries) {
229
+ const comment = comments.get(key);
230
+ if (comment) {
231
+ lines.push(`/* ${comment} */`);
232
+ }
233
+ lines.push(`"${escapeStringsValue(key)}" = "${escapeStringsValue(value)}";`);
234
+ }
235
+ // Ensure trailing newline
236
+ return lines.join("\n") + "\n";
237
+ }
238
+ /**
239
+ * Merge new translations into existing .strings content
240
+ *
241
+ * - Preserves existing translations not in newTranslations
242
+ * - Updates with new translations
243
+ * - Removes keys not in sourceKeys
244
+ * - Preserves comments from source
245
+ *
246
+ * @param existingContent Existing target file content (raw string)
247
+ * @param newTranslations New/updated translations from API
248
+ * @param sourceComments Comments from source file
249
+ * @param sourceKeys Set of all keys in source file
250
+ * @returns Merged .strings file content
251
+ */
252
+ export function mergeStringsContent(existingContent, newTranslations, sourceComments, sourceKeys) {
253
+ // Parse existing content
254
+ const existing = parseStringsContent(existingContent);
255
+ const existingMap = new Map();
256
+ for (const { key, value } of existing.entries) {
257
+ existingMap.set(key, value);
258
+ }
259
+ // Create map of new translations
260
+ const newMap = new Map();
261
+ for (const { key, value } of newTranslations) {
262
+ newMap.set(key, value);
263
+ }
264
+ // Build merged entries - only include keys from source
265
+ const mergedEntries = [];
266
+ for (const key of sourceKeys) {
267
+ // Prefer new translation, then existing, skip if neither
268
+ const value = newMap.get(key) ?? existingMap.get(key);
269
+ if (value !== undefined) {
270
+ mergedEntries.push({ key, value });
271
+ }
272
+ }
273
+ // Use source comments, preserve existing header
274
+ return reconstructStringsContent(mergedEntries, sourceComments, existing.headerComment);
275
+ }
276
+ //# sourceMappingURL=strings-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strings-parser.js","sourceRoot":"","sources":["../../src/utils/strings-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=strings-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strings-parser.test.d.ts","sourceRoot":"","sources":["../../src/utils/strings-parser.test.ts"],"names":[],"mappings":""}