@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.
- 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 +80 -7
- 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 +45 -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 +228 -42
- 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/arb-parser.d.ts +77 -0
- package/dist/utils/arb-parser.d.ts.map +1 -0
- package/dist/utils/arb-parser.js +142 -0
- package/dist/utils/arb-parser.js.map +1 -0
- package/dist/utils/arb-parser.test.d.ts +2 -0
- package/dist/utils/arb-parser.test.d.ts.map +1 -0
- package/dist/utils/arb-parser.test.js +243 -0
- package/dist/utils/arb-parser.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,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common utilities for iOS/macOS localization file handling
|
|
3
|
+
*
|
|
4
|
+
* Apple localization uses several file formats:
|
|
5
|
+
* - Localizable.strings: Traditional key-value format ("key" = "value";)
|
|
6
|
+
* - String Catalogs (.xcstrings): Modern JSON-based format (Xcode 15+)
|
|
7
|
+
* - Localizable.stringsdict: XML plist for plurals/gender
|
|
8
|
+
*
|
|
9
|
+
* Files are typically organized in .lproj directories:
|
|
10
|
+
* - en.lproj/Localizable.strings
|
|
11
|
+
* - de.lproj/Localizable.strings
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Check if a file is an Apple .strings file
|
|
15
|
+
*/
|
|
16
|
+
export function isStringsFile(filePath) {
|
|
17
|
+
return filePath.toLowerCase().endsWith(".strings");
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a file is an Apple String Catalog (.xcstrings)
|
|
21
|
+
*/
|
|
22
|
+
export function isXCStringsFile(filePath) {
|
|
23
|
+
return filePath.toLowerCase().endsWith(".xcstrings");
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Check if a file is an Apple stringsdict file
|
|
27
|
+
*/
|
|
28
|
+
export function isStringsDictFile(filePath) {
|
|
29
|
+
return filePath.toLowerCase().endsWith(".stringsdict");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Detect Apple file type from file path
|
|
33
|
+
* @returns The file type or null if not an Apple localization file
|
|
34
|
+
*/
|
|
35
|
+
export function detectAppleFileType(filePath) {
|
|
36
|
+
if (isStringsFile(filePath))
|
|
37
|
+
return "strings";
|
|
38
|
+
if (isXCStringsFile(filePath))
|
|
39
|
+
return "xcstrings";
|
|
40
|
+
if (isStringsDictFile(filePath))
|
|
41
|
+
return "stringsdict";
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Check if a file path is any Apple localization file type
|
|
46
|
+
*/
|
|
47
|
+
export function isAppleLocalizationFile(filePath) {
|
|
48
|
+
return detectAppleFileType(filePath) !== null;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Extract language code from .lproj directory path
|
|
52
|
+
*
|
|
53
|
+
* Apple organizes locale files in language-specific directories:
|
|
54
|
+
* - en.lproj/Localizable.strings -> "en"
|
|
55
|
+
* - pt-BR.lproj/Main.strings -> "pt-BR"
|
|
56
|
+
* - Base.lproj/Main.strings -> null (Base is special, not a language)
|
|
57
|
+
*
|
|
58
|
+
* @param filePath Path containing .lproj directory
|
|
59
|
+
* @returns Language code or null if not found or is Base.lproj
|
|
60
|
+
*/
|
|
61
|
+
export function extractLanguageFromLproj(filePath) {
|
|
62
|
+
// Match language code before .lproj
|
|
63
|
+
// Supports: en.lproj, de.lproj, pt-BR.lproj, zh-Hans.lproj
|
|
64
|
+
const match = filePath.match(/\/([a-zA-Z]{2,3}(?:-[a-zA-Z]{2,4})?)\.lproj\//i);
|
|
65
|
+
if (!match) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
const lang = match[1];
|
|
69
|
+
// Skip Base.lproj - it's a special case for base internationalization
|
|
70
|
+
if (lang.toLowerCase() === "base") {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return lang;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Compute target file path for .lproj directory structure
|
|
77
|
+
*
|
|
78
|
+
* Replaces the source language directory with the target language:
|
|
79
|
+
* - /Project/en.lproj/Localizable.strings -> /Project/de.lproj/Localizable.strings
|
|
80
|
+
*
|
|
81
|
+
* @param sourcePath Original file path with source language .lproj
|
|
82
|
+
* @param sourceLang Source language code
|
|
83
|
+
* @param targetLang Target language code
|
|
84
|
+
* @returns Target file path or null if no .lproj pattern found
|
|
85
|
+
*/
|
|
86
|
+
export function computeAppleLprojTargetPath(sourcePath, sourceLang, targetLang) {
|
|
87
|
+
// Create regex that matches the source language .lproj directory
|
|
88
|
+
// Use case-insensitive matching for the language code
|
|
89
|
+
const lprojPattern = new RegExp(`(/)${escapeRegExp(sourceLang)}\\.lproj(/)`, "i");
|
|
90
|
+
if (!lprojPattern.test(sourcePath)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
// Replace source language with target language in the .lproj directory name
|
|
94
|
+
return sourcePath.replace(lprojPattern, `$1${targetLang}.lproj$2`);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the file extension for Apple localization files
|
|
98
|
+
*/
|
|
99
|
+
export function getAppleFileExtension(filePath) {
|
|
100
|
+
const fileType = detectAppleFileType(filePath);
|
|
101
|
+
switch (fileType) {
|
|
102
|
+
case "strings":
|
|
103
|
+
return ".strings";
|
|
104
|
+
case "xcstrings":
|
|
105
|
+
return ".xcstrings";
|
|
106
|
+
case "stringsdict":
|
|
107
|
+
return ".stringsdict";
|
|
108
|
+
default:
|
|
109
|
+
return "";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Escape special regex characters in a string
|
|
114
|
+
*/
|
|
115
|
+
function escapeRegExp(str) {
|
|
116
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=apple-common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-common.js","sourceRoot":"","sources":["../../src/utils/apple-common.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,QAAgB;IAC5C,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AACvD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AACzD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,IAAI,aAAa,CAAC,QAAQ,CAAC;QAAE,OAAO,SAAS,CAAC;IAC9C,IAAI,eAAe,CAAC,QAAQ,CAAC;QAAE,OAAO,WAAW,CAAC;IAClD,IAAI,iBAAiB,CAAC,QAAQ,CAAC;QAAE,OAAO,aAAa,CAAC;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,QAAgB;IACtD,OAAO,mBAAmB,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;AAChD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CAAC,QAAgB;IACvD,oCAAoC;IACpC,2DAA2D;IAC3D,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,gDAAgD,CAAC,CAAC;IAE/E,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAEtB,sEAAsE;IACtE,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,MAAM,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CACzC,UAAkB,EAClB,UAAkB,EAClB,UAAkB;IAElB,iEAAiE;IACjE,sDAAsD;IACtD,MAAM,YAAY,GAAG,IAAI,MAAM,CAC7B,MAAM,YAAY,CAAC,UAAU,CAAC,aAAa,EAC3C,GAAG,CACJ,CAAC;IAEF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,4EAA4E;IAC5E,OAAO,UAAU,CAAC,OAAO,CAAC,YAAY,EAAE,KAAK,UAAU,UAAU,CAAC,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC;IAC/C,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,SAAS;YACZ,OAAO,UAAU,CAAC;QACpB,KAAK,WAAW;YACd,OAAO,YAAY,CAAC;QACtB,KAAK,aAAa;YAChB,OAAO,cAAc,CAAC;QACxB;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,GAAW;IAC/B,OAAO,GAAG,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apple-common.test.d.ts","sourceRoot":"","sources":["../../src/utils/apple-common.test.ts"],"names":[],"mappings":""}
|
|
@@ -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,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARB (Application Resource Bundle) parsing utilities
|
|
3
|
+
*
|
|
4
|
+
* ARB files are JSON-based localization files used by Flutter.
|
|
5
|
+
* They contain:
|
|
6
|
+
* - @@locale: the locale identifier (e.g., "en", "de")
|
|
7
|
+
* - Regular keys: translatable strings (e.g., "greeting": "Hello")
|
|
8
|
+
* - Metadata keys: start with @ (e.g., "@greeting": { description: "..." })
|
|
9
|
+
*
|
|
10
|
+
* Metadata should be preserved but NOT translated.
|
|
11
|
+
*/
|
|
12
|
+
import type { KeyValue } from "../api/types.js";
|
|
13
|
+
/**
|
|
14
|
+
* Check if a file is an ARB file based on extension (case-insensitive)
|
|
15
|
+
*/
|
|
16
|
+
export declare function isArbFile(filePath: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Get the file extension for locale files (.json or .arb)
|
|
19
|
+
*/
|
|
20
|
+
export declare function getLocaleFileExtension(filePath: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Check if a key is ARB metadata (starts with @)
|
|
23
|
+
*/
|
|
24
|
+
export declare function isArbMetadataKey(key: string): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Check if a key is the ARB locale identifier (@@locale)
|
|
27
|
+
*/
|
|
28
|
+
export declare function isArbLocaleKey(key: string): boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Parsed ARB content with translatable strings separated from metadata
|
|
31
|
+
*/
|
|
32
|
+
export interface ArbContent {
|
|
33
|
+
/** Locale identifier from @@locale, if present */
|
|
34
|
+
locale: string | null;
|
|
35
|
+
/** Keys to translate (non-@ prefixed string values) */
|
|
36
|
+
translatableKeys: KeyValue[];
|
|
37
|
+
/** Metadata entries (@ prefixed keys with their full values preserved) */
|
|
38
|
+
metadata: Record<string, unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Separate ARB content into translatable strings and metadata
|
|
42
|
+
*
|
|
43
|
+
* @param obj - Parsed ARB JSON object
|
|
44
|
+
* @returns ArbContent with separated translatable keys and preserved metadata
|
|
45
|
+
*/
|
|
46
|
+
export declare function parseArbContent(obj: Record<string, unknown>): ArbContent;
|
|
47
|
+
/**
|
|
48
|
+
* Reconstruct ARB file content from translations and preserved metadata
|
|
49
|
+
*
|
|
50
|
+
* The output maintains ARB conventions:
|
|
51
|
+
* - @@locale is set to the target locale
|
|
52
|
+
* - Each translatable key is followed by its @metadata if it exists
|
|
53
|
+
*
|
|
54
|
+
* @param translations - Translated key-value pairs
|
|
55
|
+
* @param metadata - Original metadata from source ARB file
|
|
56
|
+
* @param targetLocale - Target language code
|
|
57
|
+
* @returns Reconstructed ARB object ready to be serialized
|
|
58
|
+
*/
|
|
59
|
+
export declare function reconstructArbContent(translations: KeyValue[], metadata: Record<string, unknown>, targetLocale: string): Record<string, unknown>;
|
|
60
|
+
/**
|
|
61
|
+
* Merge new translations into existing ARB content (incremental update)
|
|
62
|
+
*
|
|
63
|
+
* This function supports partial syncs by:
|
|
64
|
+
* - Preserving existing translations that weren't updated
|
|
65
|
+
* - Overriding with new translations from API
|
|
66
|
+
* - Using source metadata for all keys (source is authoritative for metadata)
|
|
67
|
+
* - Removing keys that no longer exist in source
|
|
68
|
+
*
|
|
69
|
+
* @param existingContent - Existing target ARB file content (parsed JSON)
|
|
70
|
+
* @param newTranslations - New/updated translations from API
|
|
71
|
+
* @param sourceMetadata - Metadata from source ARB file (authoritative)
|
|
72
|
+
* @param sourceKeys - Set of all keys in source file (to detect removed keys)
|
|
73
|
+
* @param targetLocale - Target language code
|
|
74
|
+
* @returns Merged ARB object ready to be serialized
|
|
75
|
+
*/
|
|
76
|
+
export declare function mergeArbContent(existingContent: Record<string, unknown>, newTranslations: KeyValue[], sourceMetadata: Record<string, unknown>, sourceKeys: Set<string>, targetLocale: string): Record<string, unknown>;
|
|
77
|
+
//# sourceMappingURL=arb-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arb-parser.d.ts","sourceRoot":"","sources":["../../src/utils/arb-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,kDAAkD;IAClD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uDAAuD;IACvD,gBAAgB,EAAE,QAAQ,EAAE,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,UAAU,CAuBxE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,QAAQ,EAAE,EACxB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,YAAY,EAAE,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAiBzB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAC7B,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxC,eAAe,EAAE,QAAQ,EAAE,EAC3B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvC,UAAU,EAAE,GAAG,CAAC,MAAM,CAAC,EACvB,YAAY,EAAE,MAAM,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAmCzB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ARB (Application Resource Bundle) parsing utilities
|
|
3
|
+
*
|
|
4
|
+
* ARB files are JSON-based localization files used by Flutter.
|
|
5
|
+
* They contain:
|
|
6
|
+
* - @@locale: the locale identifier (e.g., "en", "de")
|
|
7
|
+
* - Regular keys: translatable strings (e.g., "greeting": "Hello")
|
|
8
|
+
* - Metadata keys: start with @ (e.g., "@greeting": { description: "..." })
|
|
9
|
+
*
|
|
10
|
+
* Metadata should be preserved but NOT translated.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Check if a file is an ARB file based on extension (case-insensitive)
|
|
14
|
+
*/
|
|
15
|
+
export function isArbFile(filePath) {
|
|
16
|
+
return filePath.toLowerCase().endsWith(".arb");
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Get the file extension for locale files (.json or .arb)
|
|
20
|
+
*/
|
|
21
|
+
export function getLocaleFileExtension(filePath) {
|
|
22
|
+
if (isArbFile(filePath))
|
|
23
|
+
return ".arb";
|
|
24
|
+
return ".json";
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check if a key is ARB metadata (starts with @)
|
|
28
|
+
*/
|
|
29
|
+
export function isArbMetadataKey(key) {
|
|
30
|
+
return key.startsWith("@");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if a key is the ARB locale identifier (@@locale)
|
|
34
|
+
*/
|
|
35
|
+
export function isArbLocaleKey(key) {
|
|
36
|
+
return key === "@@locale";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Separate ARB content into translatable strings and metadata
|
|
40
|
+
*
|
|
41
|
+
* @param obj - Parsed ARB JSON object
|
|
42
|
+
* @returns ArbContent with separated translatable keys and preserved metadata
|
|
43
|
+
*/
|
|
44
|
+
export function parseArbContent(obj) {
|
|
45
|
+
const translatableKeys = [];
|
|
46
|
+
const metadata = {};
|
|
47
|
+
let locale = null;
|
|
48
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
49
|
+
if (key === "@@locale") {
|
|
50
|
+
// Extract locale identifier
|
|
51
|
+
locale = String(value);
|
|
52
|
+
metadata[key] = value; // Also preserve in metadata for reconstruction
|
|
53
|
+
}
|
|
54
|
+
else if (key.startsWith("@")) {
|
|
55
|
+
// Metadata key - preserve as-is (including nested objects)
|
|
56
|
+
metadata[key] = value;
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
// Translatable key - must be a string
|
|
60
|
+
if (typeof value === "string") {
|
|
61
|
+
translatableKeys.push({ key, value });
|
|
62
|
+
}
|
|
63
|
+
// Skip non-string values (shouldn't happen in valid ARB files)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { locale, translatableKeys, metadata };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reconstruct ARB file content from translations and preserved metadata
|
|
70
|
+
*
|
|
71
|
+
* The output maintains ARB conventions:
|
|
72
|
+
* - @@locale is set to the target locale
|
|
73
|
+
* - Each translatable key is followed by its @metadata if it exists
|
|
74
|
+
*
|
|
75
|
+
* @param translations - Translated key-value pairs
|
|
76
|
+
* @param metadata - Original metadata from source ARB file
|
|
77
|
+
* @param targetLocale - Target language code
|
|
78
|
+
* @returns Reconstructed ARB object ready to be serialized
|
|
79
|
+
*/
|
|
80
|
+
export function reconstructArbContent(translations, metadata, targetLocale) {
|
|
81
|
+
const result = {};
|
|
82
|
+
// Add @@locale first (update to target locale)
|
|
83
|
+
result["@@locale"] = targetLocale;
|
|
84
|
+
// Interleave translations with their metadata
|
|
85
|
+
for (const { key, value } of translations) {
|
|
86
|
+
result[key] = value;
|
|
87
|
+
// Add corresponding metadata if it exists
|
|
88
|
+
const metaKey = `@${key}`;
|
|
89
|
+
if (metaKey in metadata) {
|
|
90
|
+
result[metaKey] = metadata[metaKey];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Merge new translations into existing ARB content (incremental update)
|
|
97
|
+
*
|
|
98
|
+
* This function supports partial syncs by:
|
|
99
|
+
* - Preserving existing translations that weren't updated
|
|
100
|
+
* - Overriding with new translations from API
|
|
101
|
+
* - Using source metadata for all keys (source is authoritative for metadata)
|
|
102
|
+
* - Removing keys that no longer exist in source
|
|
103
|
+
*
|
|
104
|
+
* @param existingContent - Existing target ARB file content (parsed JSON)
|
|
105
|
+
* @param newTranslations - New/updated translations from API
|
|
106
|
+
* @param sourceMetadata - Metadata from source ARB file (authoritative)
|
|
107
|
+
* @param sourceKeys - Set of all keys in source file (to detect removed keys)
|
|
108
|
+
* @param targetLocale - Target language code
|
|
109
|
+
* @returns Merged ARB object ready to be serialized
|
|
110
|
+
*/
|
|
111
|
+
export function mergeArbContent(existingContent, newTranslations, sourceMetadata, sourceKeys, targetLocale) {
|
|
112
|
+
const result = {};
|
|
113
|
+
// Add @@locale first (update to target locale)
|
|
114
|
+
result["@@locale"] = targetLocale;
|
|
115
|
+
// Parse existing content to get current translations
|
|
116
|
+
const existingArb = parseArbContent(existingContent);
|
|
117
|
+
const existingTranslationsMap = new Map();
|
|
118
|
+
for (const { key, value } of existingArb.translatableKeys) {
|
|
119
|
+
existingTranslationsMap.set(key, value);
|
|
120
|
+
}
|
|
121
|
+
// Create map of new translations for quick lookup
|
|
122
|
+
const newTranslationsMap = new Map();
|
|
123
|
+
for (const { key, value } of newTranslations) {
|
|
124
|
+
newTranslationsMap.set(key, value);
|
|
125
|
+
}
|
|
126
|
+
// Process all source keys in order
|
|
127
|
+
// This ensures we only include keys that exist in source (removes deleted keys)
|
|
128
|
+
for (const key of sourceKeys) {
|
|
129
|
+
// Use new translation if available, otherwise keep existing, otherwise skip
|
|
130
|
+
const value = newTranslationsMap.get(key) ?? existingTranslationsMap.get(key);
|
|
131
|
+
if (value !== undefined) {
|
|
132
|
+
result[key] = value;
|
|
133
|
+
// Add metadata from source (source metadata is authoritative)
|
|
134
|
+
const metaKey = `@${key}`;
|
|
135
|
+
if (metaKey in sourceMetadata) {
|
|
136
|
+
result[metaKey] = sourceMetadata[metaKey];
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=arb-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arb-parser.js","sourceRoot":"","sources":["../../src/utils/arb-parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"arb-parser.test.d.ts","sourceRoot":"","sources":["../../src/utils/arb-parser.test.ts"],"names":[],"mappings":""}
|