@langapi/mcp-server 1.0.7 → 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.
- package/dist/handlers/index.d.ts +7 -0
- package/dist/handlers/index.d.ts.map +1 -0
- package/dist/handlers/index.js +7 -0
- package/dist/handlers/index.js.map +1 -0
- package/dist/handlers/xcstrings-sync-handler.d.ts +72 -0
- package/dist/handlers/xcstrings-sync-handler.d.ts.map +1 -0
- package/dist/handlers/xcstrings-sync-handler.js +126 -0
- package/dist/handlers/xcstrings-sync-handler.js.map +1 -0
- package/dist/locale-detection/index.d.ts.map +1 -1
- package/dist/locale-detection/index.js +68 -4
- package/dist/locale-detection/index.js.map +1 -1
- package/dist/locale-detection/patterns.d.ts.map +1 -1
- package/dist/locale-detection/patterns.js +30 -0
- package/dist/locale-detection/patterns.js.map +1 -1
- package/dist/tools/get-translation-status.d.ts.map +1 -1
- package/dist/tools/get-translation-status.js +34 -6
- package/dist/tools/get-translation-status.js.map +1 -1
- package/dist/tools/list-local-locales.d.ts +1 -1
- package/dist/tools/list-local-locales.js +2 -2
- package/dist/tools/list-local-locales.js.map +1 -1
- package/dist/tools/sync-translations.d.ts.map +1 -1
- package/dist/tools/sync-translations.js +199 -40
- package/dist/tools/sync-translations.js.map +1 -1
- package/dist/utils/apple-common.d.ts +63 -0
- package/dist/utils/apple-common.d.ts.map +1 -0
- package/dist/utils/apple-common.js +118 -0
- package/dist/utils/apple-common.js.map +1 -0
- package/dist/utils/apple-common.test.d.ts +2 -0
- package/dist/utils/apple-common.test.d.ts.map +1 -0
- package/dist/utils/apple-common.test.js +108 -0
- package/dist/utils/apple-common.test.js.map +1 -0
- package/dist/utils/strings-parser.d.ts +62 -0
- package/dist/utils/strings-parser.d.ts.map +1 -0
- package/dist/utils/strings-parser.js +276 -0
- package/dist/utils/strings-parser.js.map +1 -0
- package/dist/utils/strings-parser.test.d.ts +2 -0
- package/dist/utils/strings-parser.test.d.ts.map +1 -0
- package/dist/utils/strings-parser.test.js +143 -0
- package/dist/utils/strings-parser.test.js.map +1 -0
- package/dist/utils/stringsdict-parser.d.ts +106 -0
- package/dist/utils/stringsdict-parser.d.ts.map +1 -0
- package/dist/utils/stringsdict-parser.js +473 -0
- package/dist/utils/stringsdict-parser.js.map +1 -0
- package/dist/utils/stringsdict-parser.test.d.ts +2 -0
- package/dist/utils/stringsdict-parser.test.d.ts.map +1 -0
- package/dist/utils/stringsdict-parser.test.js +244 -0
- package/dist/utils/stringsdict-parser.test.js.map +1 -0
- package/dist/utils/xcstrings-parser.d.ts +128 -0
- package/dist/utils/xcstrings-parser.d.ts.map +1 -0
- package/dist/utils/xcstrings-parser.js +216 -0
- package/dist/utils/xcstrings-parser.js.map +1 -0
- package/dist/utils/xcstrings-parser.test.d.ts +2 -0
- package/dist/utils/xcstrings-parser.test.d.ts.map +1 -0
- package/dist/utils/xcstrings-parser.test.js +157 -0
- package/dist/utils/xcstrings-parser.test.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { isStringsFile, isXCStringsFile, isStringsDictFile, detectAppleFileType, isAppleLocalizationFile, extractLanguageFromLproj, computeAppleLprojTargetPath, } from "./apple-common.js";
|
|
3
|
+
describe("Apple Common Utilities", () => {
|
|
4
|
+
describe("isStringsFile", () => {
|
|
5
|
+
it("should return true for .strings files", () => {
|
|
6
|
+
expect(isStringsFile("Localizable.strings")).toBe(true);
|
|
7
|
+
expect(isStringsFile("/path/to/en.lproj/Localizable.strings")).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
it("should be case-insensitive", () => {
|
|
10
|
+
expect(isStringsFile("Localizable.STRINGS")).toBe(true);
|
|
11
|
+
expect(isStringsFile("file.Strings")).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
it("should return false for other files", () => {
|
|
14
|
+
expect(isStringsFile("file.json")).toBe(false);
|
|
15
|
+
expect(isStringsFile("file.xcstrings")).toBe(false);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
describe("isXCStringsFile", () => {
|
|
19
|
+
it("should return true for .xcstrings files", () => {
|
|
20
|
+
expect(isXCStringsFile("Localizable.xcstrings")).toBe(true);
|
|
21
|
+
expect(isXCStringsFile("/path/to/Localizable.xcstrings")).toBe(true);
|
|
22
|
+
});
|
|
23
|
+
it("should be case-insensitive", () => {
|
|
24
|
+
expect(isXCStringsFile("File.XCSTRINGS")).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it("should return false for other files", () => {
|
|
27
|
+
expect(isXCStringsFile("file.strings")).toBe(false);
|
|
28
|
+
expect(isXCStringsFile("file.json")).toBe(false);
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
describe("isStringsDictFile", () => {
|
|
32
|
+
it("should return true for .stringsdict files", () => {
|
|
33
|
+
expect(isStringsDictFile("Localizable.stringsdict")).toBe(true);
|
|
34
|
+
expect(isStringsDictFile("/path/to/en.lproj/Localizable.stringsdict")).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it("should be case-insensitive", () => {
|
|
37
|
+
expect(isStringsDictFile("File.STRINGSDICT")).toBe(true);
|
|
38
|
+
});
|
|
39
|
+
it("should return false for other files", () => {
|
|
40
|
+
expect(isStringsDictFile("file.strings")).toBe(false);
|
|
41
|
+
expect(isStringsDictFile("file.json")).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("detectAppleFileType", () => {
|
|
45
|
+
it("should detect .strings files", () => {
|
|
46
|
+
expect(detectAppleFileType("Localizable.strings")).toBe("strings");
|
|
47
|
+
});
|
|
48
|
+
it("should detect .xcstrings files", () => {
|
|
49
|
+
expect(detectAppleFileType("Localizable.xcstrings")).toBe("xcstrings");
|
|
50
|
+
});
|
|
51
|
+
it("should detect .stringsdict files", () => {
|
|
52
|
+
expect(detectAppleFileType("Localizable.stringsdict")).toBe("stringsdict");
|
|
53
|
+
});
|
|
54
|
+
it("should return null for non-Apple files", () => {
|
|
55
|
+
expect(detectAppleFileType("file.json")).toBeNull();
|
|
56
|
+
expect(detectAppleFileType("file.arb")).toBeNull();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe("isAppleLocalizationFile", () => {
|
|
60
|
+
it("should return true for Apple files", () => {
|
|
61
|
+
expect(isAppleLocalizationFile("file.strings")).toBe(true);
|
|
62
|
+
expect(isAppleLocalizationFile("file.xcstrings")).toBe(true);
|
|
63
|
+
expect(isAppleLocalizationFile("file.stringsdict")).toBe(true);
|
|
64
|
+
});
|
|
65
|
+
it("should return false for non-Apple files", () => {
|
|
66
|
+
expect(isAppleLocalizationFile("file.json")).toBe(false);
|
|
67
|
+
expect(isAppleLocalizationFile("file.arb")).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("extractLanguageFromLproj", () => {
|
|
71
|
+
it("should extract language from en.lproj path", () => {
|
|
72
|
+
expect(extractLanguageFromLproj("/Project/en.lproj/Localizable.strings")).toBe("en");
|
|
73
|
+
});
|
|
74
|
+
it("should handle regional codes like pt-BR.lproj", () => {
|
|
75
|
+
expect(extractLanguageFromLproj("/Project/pt-BR.lproj/Localizable.strings")).toBe("pt-BR");
|
|
76
|
+
expect(extractLanguageFromLproj("/Project/zh-Hans.lproj/Main.strings")).toBe("zh-Hans");
|
|
77
|
+
});
|
|
78
|
+
it("should return null for Base.lproj", () => {
|
|
79
|
+
expect(extractLanguageFromLproj("/Project/Base.lproj/Localizable.strings")).toBeNull();
|
|
80
|
+
});
|
|
81
|
+
it("should return null for non-lproj paths", () => {
|
|
82
|
+
expect(extractLanguageFromLproj("/Project/locales/en/file.json")).toBeNull();
|
|
83
|
+
expect(extractLanguageFromLproj("/Project/en/file.strings")).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
it("should handle case-insensitive matching", () => {
|
|
86
|
+
expect(extractLanguageFromLproj("/Project/EN.lproj/file.strings")).toBe("EN");
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
describe("computeAppleLprojTargetPath", () => {
|
|
90
|
+
it("should compute correct target for .lproj structure", () => {
|
|
91
|
+
const result = computeAppleLprojTargetPath("/Project/en.lproj/Localizable.strings", "en", "de");
|
|
92
|
+
expect(result).toBe("/Project/de.lproj/Localizable.strings");
|
|
93
|
+
});
|
|
94
|
+
it("should handle nested paths", () => {
|
|
95
|
+
const result = computeAppleLprojTargetPath("/Project/Resources/en.lproj/Main.strings", "en", "fr");
|
|
96
|
+
expect(result).toBe("/Project/Resources/fr.lproj/Main.strings");
|
|
97
|
+
});
|
|
98
|
+
it("should handle regional codes", () => {
|
|
99
|
+
const result = computeAppleLprojTargetPath("/Project/pt-BR.lproj/Localizable.strings", "pt-BR", "es-MX");
|
|
100
|
+
expect(result).toBe("/Project/es-MX.lproj/Localizable.strings");
|
|
101
|
+
});
|
|
102
|
+
it("should return null for non-lproj paths", () => {
|
|
103
|
+
const result = computeAppleLprojTargetPath("/Project/locales/en/file.json", "en", "de");
|
|
104
|
+
expect(result).toBeNull();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=apple-common.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-common.test.js","sourceRoot":"","sources":["../../src/utils/apple-common.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 @@
|
|
|
1
|
+
{"version":3,"file":"strings-parser.test.d.ts","sourceRoot":"","sources":["../../src/utils/strings-parser.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { parseStringsContent, reconstructStringsContent, mergeStringsContent, escapeStringsValue, } from "./strings-parser.js";
|
|
3
|
+
describe("Strings Parser", () => {
|
|
4
|
+
describe("parseStringsContent", () => {
|
|
5
|
+
it("should parse simple key-value pairs", () => {
|
|
6
|
+
const content = `"greeting" = "Hello";
|
|
7
|
+
"farewell" = "Goodbye";`;
|
|
8
|
+
const result = parseStringsContent(content);
|
|
9
|
+
expect(result.entries).toEqual([
|
|
10
|
+
{ key: "greeting", value: "Hello" },
|
|
11
|
+
{ key: "farewell", value: "Goodbye" },
|
|
12
|
+
]);
|
|
13
|
+
});
|
|
14
|
+
it("should handle escaped quotes", () => {
|
|
15
|
+
const content = `"message" = "He said \\"Hello\\"";`;
|
|
16
|
+
const result = parseStringsContent(content);
|
|
17
|
+
expect(result.entries).toEqual([
|
|
18
|
+
{ key: "message", value: 'He said "Hello"' },
|
|
19
|
+
]);
|
|
20
|
+
});
|
|
21
|
+
it("should handle escaped special characters", () => {
|
|
22
|
+
const content = `"message" = "Line1\\nLine2\\tTabbed";`;
|
|
23
|
+
const result = parseStringsContent(content);
|
|
24
|
+
expect(result.entries).toEqual([
|
|
25
|
+
{ key: "message", value: "Line1\nLine2\tTabbed" },
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
it("should preserve comments above entries", () => {
|
|
29
|
+
const content = `/* User greeting */
|
|
30
|
+
"greeting" = "Hello";
|
|
31
|
+
/* User farewell */
|
|
32
|
+
"farewell" = "Goodbye";`;
|
|
33
|
+
const result = parseStringsContent(content);
|
|
34
|
+
expect(result.entries).toHaveLength(2);
|
|
35
|
+
expect(result.comments.get("greeting")).toBe("User greeting");
|
|
36
|
+
expect(result.comments.get("farewell")).toBe("User farewell");
|
|
37
|
+
});
|
|
38
|
+
it("should handle empty file", () => {
|
|
39
|
+
const result = parseStringsContent("");
|
|
40
|
+
expect(result.entries).toEqual([]);
|
|
41
|
+
expect(result.comments.size).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
it("should handle file-level header comment", () => {
|
|
44
|
+
const content = `/* Copyright 2024 Company */
|
|
45
|
+
"greeting" = "Hello";`;
|
|
46
|
+
const result = parseStringsContent(content);
|
|
47
|
+
expect(result.headerComment).toBe("Copyright 2024 Company");
|
|
48
|
+
expect(result.entries).toHaveLength(1);
|
|
49
|
+
});
|
|
50
|
+
it("should handle line comments", () => {
|
|
51
|
+
const content = `// This is a greeting
|
|
52
|
+
"greeting" = "Hello";`;
|
|
53
|
+
const result = parseStringsContent(content);
|
|
54
|
+
expect(result.comments.get("greeting")).toBe("This is a greeting");
|
|
55
|
+
});
|
|
56
|
+
it("should parse Unicode escape sequences", () => {
|
|
57
|
+
const content = `"char" = "caf\\U00E9";`;
|
|
58
|
+
const result = parseStringsContent(content);
|
|
59
|
+
expect(result.entries).toEqual([
|
|
60
|
+
{ key: "char", value: "café" },
|
|
61
|
+
]);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
describe("escapeStringsValue", () => {
|
|
65
|
+
it("should escape quotes", () => {
|
|
66
|
+
expect(escapeStringsValue('Say "Hello"')).toBe('Say \\"Hello\\"');
|
|
67
|
+
});
|
|
68
|
+
it("should escape newlines and tabs", () => {
|
|
69
|
+
expect(escapeStringsValue("Line1\nLine2")).toBe("Line1\\nLine2");
|
|
70
|
+
expect(escapeStringsValue("Col1\tCol2")).toBe("Col1\\tCol2");
|
|
71
|
+
});
|
|
72
|
+
it("should escape backslashes", () => {
|
|
73
|
+
expect(escapeStringsValue("path\\to\\file")).toBe("path\\\\to\\\\file");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("reconstructStringsContent", () => {
|
|
77
|
+
it("should rebuild with proper formatting", () => {
|
|
78
|
+
const entries = [
|
|
79
|
+
{ key: "greeting", value: "Hello" },
|
|
80
|
+
{ key: "farewell", value: "Goodbye" },
|
|
81
|
+
];
|
|
82
|
+
const comments = new Map();
|
|
83
|
+
const result = reconstructStringsContent(entries, comments, null);
|
|
84
|
+
expect(result).toBe('"greeting" = "Hello";\n"farewell" = "Goodbye";\n');
|
|
85
|
+
});
|
|
86
|
+
it("should preserve comments", () => {
|
|
87
|
+
const entries = [{ key: "greeting", value: "Hello" }];
|
|
88
|
+
const comments = new Map([["greeting", "User greeting"]]);
|
|
89
|
+
const result = reconstructStringsContent(entries, comments, null);
|
|
90
|
+
expect(result).toBe('/* User greeting */\n"greeting" = "Hello";\n');
|
|
91
|
+
});
|
|
92
|
+
it("should add header comment", () => {
|
|
93
|
+
const entries = [{ key: "key", value: "value" }];
|
|
94
|
+
const comments = new Map();
|
|
95
|
+
const result = reconstructStringsContent(entries, comments, "Header");
|
|
96
|
+
expect(result).toContain("/* Header */");
|
|
97
|
+
});
|
|
98
|
+
it("should escape special characters in output", () => {
|
|
99
|
+
const entries = [{ key: "msg", value: 'Say "Hi"\nOK' }];
|
|
100
|
+
const comments = new Map();
|
|
101
|
+
const result = reconstructStringsContent(entries, comments, null);
|
|
102
|
+
expect(result).toBe('"msg" = "Say \\"Hi\\"\\nOK";\n');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe("mergeStringsContent", () => {
|
|
106
|
+
it("should merge new translations", () => {
|
|
107
|
+
const existing = '"greeting" = "Hallo";';
|
|
108
|
+
const newTranslations = [{ key: "farewell", value: "Auf Wiedersehen" }];
|
|
109
|
+
const sourceComments = new Map();
|
|
110
|
+
const sourceKeys = new Set(["greeting", "farewell"]);
|
|
111
|
+
const result = mergeStringsContent(existing, newTranslations, sourceComments, sourceKeys);
|
|
112
|
+
expect(result).toContain('"greeting" = "Hallo"');
|
|
113
|
+
expect(result).toContain('"farewell" = "Auf Wiedersehen"');
|
|
114
|
+
});
|
|
115
|
+
it("should preserve existing translations not updated", () => {
|
|
116
|
+
const existing = '"greeting" = "Hallo";\n"farewell" = "Tschüss";';
|
|
117
|
+
const newTranslations = [{ key: "greeting", value: "Guten Tag" }];
|
|
118
|
+
const sourceComments = new Map();
|
|
119
|
+
const sourceKeys = new Set(["greeting", "farewell"]);
|
|
120
|
+
const result = mergeStringsContent(existing, newTranslations, sourceComments, sourceKeys);
|
|
121
|
+
expect(result).toContain('"greeting" = "Guten Tag"');
|
|
122
|
+
expect(result).toContain('"farewell" = "Tschüss"');
|
|
123
|
+
});
|
|
124
|
+
it("should remove keys not in source", () => {
|
|
125
|
+
const existing = '"greeting" = "Hallo";\n"deleted" = "To be removed";';
|
|
126
|
+
const newTranslations = [];
|
|
127
|
+
const sourceComments = new Map();
|
|
128
|
+
const sourceKeys = new Set(["greeting"]);
|
|
129
|
+
const result = mergeStringsContent(existing, newTranslations, sourceComments, sourceKeys);
|
|
130
|
+
expect(result).toContain('"greeting" = "Hallo"');
|
|
131
|
+
expect(result).not.toContain("deleted");
|
|
132
|
+
});
|
|
133
|
+
it("should preserve comment associations from source", () => {
|
|
134
|
+
const existing = '"greeting" = "Hallo";';
|
|
135
|
+
const newTranslations = [];
|
|
136
|
+
const sourceComments = new Map([["greeting", "User greeting"]]);
|
|
137
|
+
const sourceKeys = new Set(["greeting"]);
|
|
138
|
+
const result = mergeStringsContent(existing, newTranslations, sourceComments, sourceKeys);
|
|
139
|
+
expect(result).toContain("/* User greeting */");
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=strings-parser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strings-parser.test.js","sourceRoot":"","sources":["../../src/utils/strings-parser.test.ts"],"names":[],"mappings":"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"}
|