lingui-po-translate 1.0.0 → 1.0.2
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/core/core-definitions.js +21 -0
- package/dist/core/core-util.js +65 -0
- package/dist/core/invoke-translation-service.js +82 -0
- package/dist/core/translate-cli.js +113 -0
- package/dist/core/translate-core.js +230 -0
- package/dist/core/tset-ops.js +110 -0
- package/dist/file-formats/common/format-cache.js +72 -0
- package/dist/file-formats/common/managed-json.js +37 -0
- package/dist/file-formats/common/managed-utf8.js +69 -0
- package/dist/file-formats/common/parse-utils.js +14 -0
- package/dist/file-formats/csv/csv.js +64 -0
- package/dist/file-formats/file-format-definitions.js +43 -0
- package/dist/file-formats/flat-json/flat-json.js +29 -0
- package/dist/file-formats/flutter-arb/flutter-arb.js +62 -0
- package/dist/file-formats/ios-strings/ios-read.js +72 -0
- package/dist/file-formats/ios-strings/ios-strings.js +22 -0
- package/dist/file-formats/ios-strings/ios-write.js +37 -0
- package/dist/file-formats/nested-json/nested-json.js +66 -0
- package/dist/file-formats/po/comment-parser.js +68 -0
- package/dist/file-formats/po/po-files.js +78 -0
- package/dist/file-formats/po/po-ops.js +113 -0
- package/dist/file-formats/xml/xml-generic.js +81 -0
- package/dist/file-formats/xml/xml-read.js +66 -0
- package/dist/file-formats/xml/xml-traverse.js +160 -0
- package/dist/file-formats/xml/xml-write.js +62 -0
- package/dist/file-formats/yaml/yaml-generic.js +113 -0
- package/dist/file-formats/yaml/yaml-manipulation.js +132 -0
- package/dist/file-formats/yaml/yaml-parse.js +38 -0
- package/dist/index.js +66 -0
- package/dist/matchers/i18next.js +11 -0
- package/dist/matchers/icu.js +23 -0
- package/dist/matchers/matcher-definitions.js +46 -0
- package/dist/matchers/sprintf.js +11 -0
- package/dist/services/azure-translator.js +52 -0
- package/dist/services/google-translate.js +55 -0
- package/dist/services/key-as-translation.js +18 -0
- package/dist/services/manual.js +29 -0
- package/dist/services/openai-translate.js +91 -0
- package/dist/services/service-definitions.js +57 -0
- package/dist/services/sync-without-translate.js +18 -0
- package/dist/services/typechat.js +214 -0
- package/dist/util/extract-version.js +20 -0
- package/dist/util/flatten.js +40 -0
- package/dist/util/util.js +87 -0
- package/package.json +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FormatCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* We use format-caches to hold format-specific structures.
|
|
6
|
+
* A general pattern is:
|
|
7
|
+
* Prefer "same-file-caches" over other caches, and prefer newer caches over older caches.
|
|
8
|
+
*/
|
|
9
|
+
class FormatCache {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.fileCaches = [];
|
|
12
|
+
}
|
|
13
|
+
findFileCache(path) {
|
|
14
|
+
var _a;
|
|
15
|
+
return (_a = this.fileCaches.find((fileCache) => fileCache.path === path)) !== null && _a !== void 0 ? _a : null;
|
|
16
|
+
}
|
|
17
|
+
lookupFileCache(path) {
|
|
18
|
+
return this.findFileCache(path);
|
|
19
|
+
}
|
|
20
|
+
insertFileCache(fileCache) {
|
|
21
|
+
this.fileCaches.push(fileCache);
|
|
22
|
+
}
|
|
23
|
+
insert(args) {
|
|
24
|
+
let fileCache = this.findFileCache(args.path);
|
|
25
|
+
if (!fileCache) {
|
|
26
|
+
fileCache = { path: args.path, entries: new Map(), auxData: null };
|
|
27
|
+
this.insertFileCache(fileCache);
|
|
28
|
+
}
|
|
29
|
+
fileCache.entries.set(args.key, args.entry);
|
|
30
|
+
}
|
|
31
|
+
lookup(args) {
|
|
32
|
+
const sameFileCache = this.findFileCache(args.path);
|
|
33
|
+
if (sameFileCache) {
|
|
34
|
+
const sameFileHit = sameFileCache.entries.get(args.key);
|
|
35
|
+
if (sameFileHit !== undefined) {
|
|
36
|
+
return sameFileHit;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (let idx = this.fileCaches.length - 1; idx >= 0; idx--) {
|
|
40
|
+
const fileCache = this.fileCaches[idx];
|
|
41
|
+
const hit = fileCache.entries.get(args.key);
|
|
42
|
+
if (hit !== undefined) {
|
|
43
|
+
return hit;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
lookupSameFileAuxdata(args) {
|
|
49
|
+
var _a, _b;
|
|
50
|
+
return (_b = (_a = this.findFileCache(args.path)) === null || _a === void 0 ? void 0 : _a.auxData) !== null && _b !== void 0 ? _b : null;
|
|
51
|
+
}
|
|
52
|
+
lookupAuxdata(args) {
|
|
53
|
+
const sameFileAuxdata = this.lookupSameFileAuxdata(args);
|
|
54
|
+
if (sameFileAuxdata) {
|
|
55
|
+
return sameFileAuxdata;
|
|
56
|
+
}
|
|
57
|
+
if (this.fileCaches.length) {
|
|
58
|
+
return this.fileCaches[this.fileCaches.length - 1].auxData;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
getOldestAuxdata() {
|
|
63
|
+
if (!this.fileCaches.length) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return this.fileCaches[0].auxData;
|
|
67
|
+
}
|
|
68
|
+
purge() {
|
|
69
|
+
this.fileCaches = [];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
exports.FormatCache = FormatCache;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readRawJson = exports.readManagedJson = exports.writeRawJson = exports.writeManagedJson = void 0;
|
|
4
|
+
const util_1 = require("../../util/util");
|
|
5
|
+
const managed_utf8_1 = require("./managed-utf8");
|
|
6
|
+
function stringifyJson(obj) {
|
|
7
|
+
return JSON.stringify(obj, null, 2);
|
|
8
|
+
}
|
|
9
|
+
function writeManagedJson(args) {
|
|
10
|
+
const jsonString = stringifyJson(args.object);
|
|
11
|
+
return (0, managed_utf8_1.writeManagedUtf8)({
|
|
12
|
+
path: args.path,
|
|
13
|
+
utf8: jsonString,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
exports.writeManagedJson = writeManagedJson;
|
|
17
|
+
function writeRawJson(args) {
|
|
18
|
+
(0, util_1.writeUtf8File)(args.path, stringifyJson(args.object) + "\n");
|
|
19
|
+
}
|
|
20
|
+
exports.writeRawJson = writeRawJson;
|
|
21
|
+
function readManagedJson(path) {
|
|
22
|
+
const { object, jsonString } = readRawJson(path);
|
|
23
|
+
(0, managed_utf8_1.insertUtf8Cache)({ path, utf8: jsonString });
|
|
24
|
+
return object;
|
|
25
|
+
}
|
|
26
|
+
exports.readManagedJson = readManagedJson;
|
|
27
|
+
function readRawJson(path) {
|
|
28
|
+
try {
|
|
29
|
+
const jsonString = (0, util_1.readUtf8File)(path);
|
|
30
|
+
return { object: JSON.parse(jsonString), jsonString };
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
console.error(e);
|
|
34
|
+
(0, util_1.logFatal)(`Failed to parse ${(0, util_1.getDebugPath)(path)}.`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.readRawJson = readRawJson;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readManagedUtf8 = exports.insertUtf8Cache = exports.writeManagedUtf8 = void 0;
|
|
4
|
+
const format_cache_1 = require("./format-cache");
|
|
5
|
+
const util_1 = require("../../util/util");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
/**
|
|
8
|
+
* Divergent line-endings can be painful in IDEs like WebStorm.
|
|
9
|
+
* Therefore, we remember line-endings that were previously present.
|
|
10
|
+
*/
|
|
11
|
+
const utf8Cache = new format_cache_1.FormatCache();
|
|
12
|
+
const possibleEndings = ["\n", "\r", os_1.EOL];
|
|
13
|
+
function writeManagedUtf8(args) {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const endings = (_b = (_a = utf8Cache.lookupAuxdata({ path: args.path })) === null || _a === void 0 ? void 0 : _a.endings) !== null && _b !== void 0 ? _b : null;
|
|
16
|
+
const expandedContent = endings !== null
|
|
17
|
+
? replaceLineEndings({ str: args.utf8, endings })
|
|
18
|
+
: args.utf8;
|
|
19
|
+
(0, util_1.writeUtf8File)(args.path, expandedContent);
|
|
20
|
+
return expandedContent;
|
|
21
|
+
}
|
|
22
|
+
exports.writeManagedUtf8 = writeManagedUtf8;
|
|
23
|
+
function insertUtf8Cache(args) {
|
|
24
|
+
const endings = extractLineEndings(args.utf8);
|
|
25
|
+
utf8Cache.insertFileCache({
|
|
26
|
+
path: args.path,
|
|
27
|
+
entries: new Map(),
|
|
28
|
+
auxData: { endings },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
exports.insertUtf8Cache = insertUtf8Cache;
|
|
32
|
+
function readManagedUtf8(path) {
|
|
33
|
+
const utf8 = (0, util_1.readUtf8File)(path);
|
|
34
|
+
insertUtf8Cache({ path, utf8 });
|
|
35
|
+
return utf8;
|
|
36
|
+
}
|
|
37
|
+
exports.readManagedUtf8 = readManagedUtf8;
|
|
38
|
+
function replaceLineEndings(args) {
|
|
39
|
+
const beginOfEnd = beginOfEndIndex(args.str);
|
|
40
|
+
if (beginOfEnd === null) {
|
|
41
|
+
return `${args.str}${args.endings}`;
|
|
42
|
+
}
|
|
43
|
+
const shorterStr = args.str.slice(0, beginOfEnd);
|
|
44
|
+
return `${shorterStr}${args.endings}`;
|
|
45
|
+
}
|
|
46
|
+
function extractLineEndings(str) {
|
|
47
|
+
const beginOfEnd = beginOfEndIndex(str);
|
|
48
|
+
if (beginOfEnd !== null) {
|
|
49
|
+
return str.slice(beginOfEnd);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
return "";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function beginOfEndIndex(str) {
|
|
56
|
+
if (!str.length) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
let beginOfEnd = null;
|
|
60
|
+
for (let idx = str.length - 1; idx >= 0; idx--) {
|
|
61
|
+
if (possibleEndings.includes(str[idx])) {
|
|
62
|
+
beginOfEnd = idx;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return beginOfEnd;
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logParseWarning = exports.logParseError = void 0;
|
|
4
|
+
const util_1 = require("../../util/util");
|
|
5
|
+
function logParseError(rawMsg, args) {
|
|
6
|
+
const msg = `Failed to parse ${(0, util_1.getDebugPath)(args.path)} with expected format '${args.format}': ${rawMsg}`;
|
|
7
|
+
(0, util_1.logFatal)(msg);
|
|
8
|
+
}
|
|
9
|
+
exports.logParseError = logParseError;
|
|
10
|
+
function logParseWarning(rawMsg, args) {
|
|
11
|
+
const msg = `Warning: Parsing '${args.path}': ${rawMsg}`;
|
|
12
|
+
console.log(msg);
|
|
13
|
+
}
|
|
14
|
+
exports.logParseWarning = logParseWarning;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SimpleCsv = void 0;
|
|
4
|
+
const parse_utils_1 = require("../common/parse-utils");
|
|
5
|
+
const managed_utf8_1 = require("../common/managed-utf8");
|
|
6
|
+
// We might make this configurable if the need arises.
|
|
7
|
+
const CSV_SEPARATOR = ",";
|
|
8
|
+
function parseCsvStruct(args) {
|
|
9
|
+
const lines = args.utf8.split("\n");
|
|
10
|
+
if (!lines || lines.length < 2) {
|
|
11
|
+
(0, parse_utils_1.logParseError)("Expected at least 2 CSV lines (header + content)", args.args);
|
|
12
|
+
}
|
|
13
|
+
const rawHeader = lines[0];
|
|
14
|
+
const header = rawHeader.split(CSV_SEPARATOR);
|
|
15
|
+
if (!header || header.length < 2) {
|
|
16
|
+
(0, parse_utils_1.logParseError)(`Expected at least 2 columns in CSV header with separator '${CSV_SEPARATOR}'`, args.args);
|
|
17
|
+
}
|
|
18
|
+
const languageCodes = header.slice(1);
|
|
19
|
+
const languageIndex = 1 + languageCodes.findIndex((value) => value.trim() === args.args.lng);
|
|
20
|
+
if (languageIndex <= 0) {
|
|
21
|
+
(0, parse_utils_1.logParseError)(`Did not find language '${args.args.lng}' in CSV header '${rawHeader}'`, args.args);
|
|
22
|
+
}
|
|
23
|
+
const contentLines = lines.slice(1);
|
|
24
|
+
contentLines.forEach((line, index) => {
|
|
25
|
+
contentLines[index] = line.replace("\r", "");
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
rawHeader,
|
|
29
|
+
languageIndex,
|
|
30
|
+
contentLines,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
class SimpleCsv {
|
|
34
|
+
readTFile(args) {
|
|
35
|
+
const utf8 = (0, managed_utf8_1.readManagedUtf8)(args.path);
|
|
36
|
+
const csvStruct = parseCsvStruct({ utf8, args });
|
|
37
|
+
const tSet = new Map();
|
|
38
|
+
csvStruct.contentLines.forEach((line) => {
|
|
39
|
+
const tokens = line.split(CSV_SEPARATOR);
|
|
40
|
+
if (tokens.length <= csvStruct.languageIndex) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const key = tokens[0];
|
|
44
|
+
const value = tokens[csvStruct.languageIndex];
|
|
45
|
+
if (tSet.has(key)) {
|
|
46
|
+
(0, parse_utils_1.logParseError)(`duplicate key '${key}' -> Currently, the usage of duplicate translation-keys is discouraged.`, args);
|
|
47
|
+
}
|
|
48
|
+
tSet.set(key, value);
|
|
49
|
+
});
|
|
50
|
+
return Promise.resolve(tSet);
|
|
51
|
+
}
|
|
52
|
+
writeTFile(args) {
|
|
53
|
+
console.log("Warning: Currently, 'lingui-po-translate' may overwrite pre-existing CSV-content. This might change in future versions.");
|
|
54
|
+
const lines = [];
|
|
55
|
+
const header = ["keys", args.lng].join(CSV_SEPARATOR);
|
|
56
|
+
lines.push(header);
|
|
57
|
+
args.tSet.forEach((value, key) => {
|
|
58
|
+
lines.push([key, value].join(CSV_SEPARATOR));
|
|
59
|
+
});
|
|
60
|
+
const csv = lines.join("\n");
|
|
61
|
+
(0, managed_utf8_1.writeManagedUtf8)({ path: args.path, utf8: csv });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.SimpleCsv = SimpleCsv;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instantiateTFileFormat = exports.getTFileFormatList = void 0;
|
|
4
|
+
const flat_json_1 = require("./flat-json/flat-json");
|
|
5
|
+
const nested_json_1 = require("./nested-json/nested-json");
|
|
6
|
+
function getTFileFormatList() {
|
|
7
|
+
return Object.keys(fileFormatMap);
|
|
8
|
+
}
|
|
9
|
+
exports.getTFileFormatList = getTFileFormatList;
|
|
10
|
+
const fileFormatMap = {
|
|
11
|
+
"flat-json": null,
|
|
12
|
+
"nested-json": null,
|
|
13
|
+
yaml: null,
|
|
14
|
+
po: null,
|
|
15
|
+
xml: null,
|
|
16
|
+
"ios-strings": null,
|
|
17
|
+
arb: null,
|
|
18
|
+
csv: null,
|
|
19
|
+
};
|
|
20
|
+
async function instantiateTFileFormat(fileFormat) {
|
|
21
|
+
/**
|
|
22
|
+
* To improve launch-performance, we import file-formats dynamically.
|
|
23
|
+
*/
|
|
24
|
+
switch (fileFormat) {
|
|
25
|
+
case "flat-json":
|
|
26
|
+
return new flat_json_1.FlatJson();
|
|
27
|
+
case "nested-json":
|
|
28
|
+
return new nested_json_1.NestedJson();
|
|
29
|
+
case "yaml":
|
|
30
|
+
return new (await Promise.resolve().then(() => __importStar(require("./yaml/yaml-generic")))).YamlGeneric();
|
|
31
|
+
case "po":
|
|
32
|
+
return new (await Promise.resolve().then(() => __importStar(require("./po/po-files")))).PoFile();
|
|
33
|
+
case "arb":
|
|
34
|
+
return new (await Promise.resolve().then(() => __importStar(require("./flutter-arb/flutter-arb")))).FlutterArb();
|
|
35
|
+
case "xml":
|
|
36
|
+
return new (await Promise.resolve().then(() => __importStar(require("./xml/xml-generic")))).XmlGeneric();
|
|
37
|
+
case "ios-strings":
|
|
38
|
+
return new (await Promise.resolve().then(() => __importStar(require("./ios-strings/ios-strings")))).IosStrings();
|
|
39
|
+
case "csv":
|
|
40
|
+
return new (await Promise.resolve().then(() => __importStar(require("./csv/csv")))).SimpleCsv();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.instantiateTFileFormat = instantiateTFileFormat;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlatJson = void 0;
|
|
4
|
+
const parse_utils_1 = require("../common/parse-utils");
|
|
5
|
+
const managed_json_1 = require("../common/managed-json");
|
|
6
|
+
class FlatJson {
|
|
7
|
+
readTFile(args) {
|
|
8
|
+
const json = (0, managed_json_1.readManagedJson)(args.path);
|
|
9
|
+
const tMap = new Map();
|
|
10
|
+
for (const key of Object.keys(json)) {
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
12
|
+
// @ts-ignore
|
|
13
|
+
const value = json[key];
|
|
14
|
+
if (typeof value !== "string" && value !== null) {
|
|
15
|
+
(0, parse_utils_1.logParseError)(`Property '${key}' is not a string or null`, args);
|
|
16
|
+
}
|
|
17
|
+
tMap.set(key, value);
|
|
18
|
+
}
|
|
19
|
+
return Promise.resolve(tMap);
|
|
20
|
+
}
|
|
21
|
+
writeTFile(args) {
|
|
22
|
+
const flatJson = {};
|
|
23
|
+
args.tSet.forEach((value, key) => {
|
|
24
|
+
flatJson[key] = value;
|
|
25
|
+
});
|
|
26
|
+
(0, managed_json_1.writeManagedJson)({ path: args.path, object: flatJson });
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.FlatJson = FlatJson;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FlutterArb = void 0;
|
|
4
|
+
const util_1 = require("../../util/util");
|
|
5
|
+
const format_cache_1 = require("../common/format-cache");
|
|
6
|
+
const managed_json_1 = require("../common/managed-json");
|
|
7
|
+
const attributeCache = new format_cache_1.FormatCache();
|
|
8
|
+
class FlutterArb {
|
|
9
|
+
readTFile(args) {
|
|
10
|
+
const json = (0, managed_json_1.readManagedJson)(args.path);
|
|
11
|
+
const tMap = new Map();
|
|
12
|
+
const globalAttributes = {};
|
|
13
|
+
for (const key of Object.keys(json)) {
|
|
14
|
+
const value = json[key];
|
|
15
|
+
if (key.startsWith("@@")) {
|
|
16
|
+
globalAttributes[key] = value;
|
|
17
|
+
}
|
|
18
|
+
else if (key.startsWith("@") && value && typeof value === "object") {
|
|
19
|
+
attributeCache.insert({
|
|
20
|
+
path: args.path,
|
|
21
|
+
key,
|
|
22
|
+
entry: value,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
else if (typeof value === "string") {
|
|
26
|
+
tMap.set(key, value);
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
console.info(`Warning: '${key}-${value}' in ${(0, util_1.getDebugPath)(args.path)} is unexpected`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const fileCache = attributeCache.findFileCache(args.path);
|
|
33
|
+
if (fileCache) {
|
|
34
|
+
fileCache.auxData = { globalAttributes };
|
|
35
|
+
}
|
|
36
|
+
return Promise.resolve(tMap);
|
|
37
|
+
}
|
|
38
|
+
writeTFile(args) {
|
|
39
|
+
const json = {};
|
|
40
|
+
args.tSet.forEach((value, key) => {
|
|
41
|
+
json[key] = value;
|
|
42
|
+
const attributeKey = `@${key}`;
|
|
43
|
+
const cachedAttributes = attributeCache.lookup({
|
|
44
|
+
path: args.path,
|
|
45
|
+
key: attributeKey,
|
|
46
|
+
});
|
|
47
|
+
if (cachedAttributes) {
|
|
48
|
+
const mergedAttributes = {
|
|
49
|
+
...cachedAttributes,
|
|
50
|
+
};
|
|
51
|
+
json[attributeKey] = mergedAttributes;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const auxData = attributeCache.lookupAuxdata({ path: args.path });
|
|
55
|
+
const mergedJson = {
|
|
56
|
+
...auxData === null || auxData === void 0 ? void 0 : auxData.globalAttributes,
|
|
57
|
+
...json,
|
|
58
|
+
};
|
|
59
|
+
(0, managed_json_1.writeManagedJson)({ path: args.path, object: mergedJson });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.FlutterArb = FlutterArb;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseiOSFile = exports.VALUE_INDEX = void 0;
|
|
4
|
+
const parse_utils_1 = require("../common/parse-utils");
|
|
5
|
+
const managed_utf8_1 = require("../common/managed-utf8");
|
|
6
|
+
const KEY_INDEX = 1;
|
|
7
|
+
exports.VALUE_INDEX = 3;
|
|
8
|
+
function parseiOSFile(args) {
|
|
9
|
+
const rawString = (0, managed_utf8_1.readManagedUtf8)(args.path);
|
|
10
|
+
const lines = rawString.split("\n");
|
|
11
|
+
if (!lines.length) {
|
|
12
|
+
(0, parse_utils_1.logParseError)("Empty file", args);
|
|
13
|
+
}
|
|
14
|
+
const iosFile = {
|
|
15
|
+
path: args.path,
|
|
16
|
+
entries: new Map(),
|
|
17
|
+
auxData: [],
|
|
18
|
+
};
|
|
19
|
+
let currentChunk = [];
|
|
20
|
+
lines.forEach((line) => {
|
|
21
|
+
const keyValue = parseiOSLine(args, line);
|
|
22
|
+
currentChunk.push(line);
|
|
23
|
+
if (keyValue) {
|
|
24
|
+
const key = keyValue.key;
|
|
25
|
+
const value = keyValue.value;
|
|
26
|
+
const lineChunk = {
|
|
27
|
+
value,
|
|
28
|
+
lines: currentChunk,
|
|
29
|
+
};
|
|
30
|
+
if (iosFile.entries.has(key)) {
|
|
31
|
+
(0, parse_utils_1.logParseError)(`duplicate key '${key}' -> Currently, the usage of duplicate translation-keys is discouraged.`, args);
|
|
32
|
+
}
|
|
33
|
+
iosFile.entries.set(key, lineChunk);
|
|
34
|
+
currentChunk = [];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
if (!iosFile.entries.size) {
|
|
38
|
+
(0, parse_utils_1.logParseError)("Did not find any Strings in the expected format", args);
|
|
39
|
+
}
|
|
40
|
+
return iosFile;
|
|
41
|
+
}
|
|
42
|
+
exports.parseiOSFile = parseiOSFile;
|
|
43
|
+
function parseiOSLine(args, line) {
|
|
44
|
+
if (!line.trim().length) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (isComment(line)) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const token = line.split('"');
|
|
51
|
+
if (token.length < 5) {
|
|
52
|
+
(0, parse_utils_1.logParseWarning)(`Line '${line}' seems to be unexpected`, args);
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const key = token[KEY_INDEX];
|
|
56
|
+
if (!key || !key.trim().length) {
|
|
57
|
+
(0, parse_utils_1.logParseWarning)(`Did not find a key in '${line}'`, args);
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
const value = token[exports.VALUE_INDEX];
|
|
61
|
+
return {
|
|
62
|
+
key,
|
|
63
|
+
value,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function isComment(line) {
|
|
67
|
+
const trimLine = line.trim();
|
|
68
|
+
if (trimLine.startsWith("//")) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return trimLine.startsWith("/*") || trimLine.endsWith("*/");
|
|
72
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IosStrings = void 0;
|
|
4
|
+
const ios_read_1 = require("./ios-read");
|
|
5
|
+
const ios_write_1 = require("./ios-write");
|
|
6
|
+
const format_cache_1 = require("../common/format-cache");
|
|
7
|
+
const iOSCache = new format_cache_1.FormatCache();
|
|
8
|
+
class IosStrings {
|
|
9
|
+
readTFile(args) {
|
|
10
|
+
const iosFile = (0, ios_read_1.parseiOSFile)(args);
|
|
11
|
+
iOSCache.insertFileCache(iosFile);
|
|
12
|
+
const result = new Map();
|
|
13
|
+
iosFile.entries.forEach((value, key) => {
|
|
14
|
+
result.set(key, value.value);
|
|
15
|
+
});
|
|
16
|
+
return Promise.resolve(result);
|
|
17
|
+
}
|
|
18
|
+
writeTFile(args) {
|
|
19
|
+
(0, ios_write_1.writeiOSFile)(args, iOSCache);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.IosStrings = IosStrings;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.writeiOSFile = void 0;
|
|
4
|
+
const ios_read_1 = require("./ios-read");
|
|
5
|
+
const managed_utf8_1 = require("../common/managed-utf8");
|
|
6
|
+
function writeiOSFile(args, cache) {
|
|
7
|
+
const outLines = [];
|
|
8
|
+
args.tSet.forEach((value, key) => {
|
|
9
|
+
const oldChunk = cache.lookup({ path: args.path, key });
|
|
10
|
+
let newChunk;
|
|
11
|
+
if (oldChunk) {
|
|
12
|
+
newChunk = convertOldChunkIntoNewChunk(oldChunk, value);
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
newChunk = createNewChunk(key, value);
|
|
16
|
+
}
|
|
17
|
+
outLines.push(...newChunk.lines);
|
|
18
|
+
});
|
|
19
|
+
outLines.push("\n");
|
|
20
|
+
const output = outLines.join("\n");
|
|
21
|
+
(0, managed_utf8_1.writeManagedUtf8)({ path: args.path, utf8: output });
|
|
22
|
+
}
|
|
23
|
+
exports.writeiOSFile = writeiOSFile;
|
|
24
|
+
function createNewChunk(key, newValue) {
|
|
25
|
+
return {
|
|
26
|
+
value: newValue,
|
|
27
|
+
lines: ["", "", `"${key}" = "${newValue}";`],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function convertOldChunkIntoNewChunk(oldChunk, newValue) {
|
|
31
|
+
oldChunk.value = newValue;
|
|
32
|
+
const valueLine = oldChunk.lines[oldChunk.lines.length - 1];
|
|
33
|
+
const token = valueLine.split('"');
|
|
34
|
+
token[ios_read_1.VALUE_INDEX] = newValue !== null && newValue !== void 0 ? newValue : "";
|
|
35
|
+
oldChunk.lines[oldChunk.lines.length - 1] = token.join('"');
|
|
36
|
+
return oldChunk;
|
|
37
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NestedJson = void 0;
|
|
4
|
+
const flatten_1 = require("../../util/flatten");
|
|
5
|
+
const managed_json_1 = require("../common/managed-json");
|
|
6
|
+
const format_cache_1 = require("../common/format-cache");
|
|
7
|
+
const jsonCache = new format_cache_1.FormatCache();
|
|
8
|
+
class NestedJson {
|
|
9
|
+
readTFile(args) {
|
|
10
|
+
const tMap = new Map();
|
|
11
|
+
const json = (0, managed_json_1.readManagedJson)(args.path);
|
|
12
|
+
traverseJson("", json, (entry) => {
|
|
13
|
+
tMap.set(entry[0], entry[1]);
|
|
14
|
+
return null;
|
|
15
|
+
});
|
|
16
|
+
jsonCache.insertFileCache({
|
|
17
|
+
path: args.path,
|
|
18
|
+
entries: new Map(),
|
|
19
|
+
auxData: json,
|
|
20
|
+
});
|
|
21
|
+
return Promise.resolve(tMap);
|
|
22
|
+
}
|
|
23
|
+
writeTFile(args) {
|
|
24
|
+
const sourceJson = jsonCache.getOldestAuxdata();
|
|
25
|
+
let json;
|
|
26
|
+
if (sourceJson) {
|
|
27
|
+
json = sourceJson;
|
|
28
|
+
replaceTranslatableProperties(args, json);
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
json = constructNestedJsonFromFlatMap(args);
|
|
32
|
+
}
|
|
33
|
+
(0, managed_json_1.writeManagedJson)({ path: args.path, object: json });
|
|
34
|
+
jsonCache.purge();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.NestedJson = NestedJson;
|
|
38
|
+
function traverseJson(path, node, operation) {
|
|
39
|
+
for (const key of Object.keys(node)) {
|
|
40
|
+
const value = node[key];
|
|
41
|
+
const newPath = path ? path + "." + key : key;
|
|
42
|
+
if (typeof value === "string") {
|
|
43
|
+
const newValue = operation([newPath, value]);
|
|
44
|
+
if (newValue) {
|
|
45
|
+
node[key] = newValue;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else if (value && typeof value === "object") {
|
|
49
|
+
traverseJson(newPath, value, operation);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function replaceTranslatableProperties(args, json) {
|
|
54
|
+
traverseJson("", json, (entry) => {
|
|
55
|
+
var _a;
|
|
56
|
+
const path = entry[0];
|
|
57
|
+
return (_a = args.tSet.get(path)) !== null && _a !== void 0 ? _a : null;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function constructNestedJsonFromFlatMap(args) {
|
|
61
|
+
const flatJson = {};
|
|
62
|
+
args.tSet.forEach((value, key) => {
|
|
63
|
+
flatJson[key] = value;
|
|
64
|
+
});
|
|
65
|
+
return (0, flatten_1.unflatten)(flatJson);
|
|
66
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.hasManualMarking = exports.shouldSkipForManual = exports.parseExtractedComment = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Parse extracted comments from PO file
|
|
6
|
+
*
|
|
7
|
+
* Supports:
|
|
8
|
+
* - @manual:zh-Hans,zh-Hant - languages requiring manual translation
|
|
9
|
+
* - @context:text - context for AI translation
|
|
10
|
+
* - Plain text (without @) - also treated as context
|
|
11
|
+
*
|
|
12
|
+
* Example:
|
|
13
|
+
* #. @manual:zh-Hans
|
|
14
|
+
* #. @context:This is a save button
|
|
15
|
+
* #. Some additional context
|
|
16
|
+
*
|
|
17
|
+
* @param extracted - The extracted comment string from PO file
|
|
18
|
+
* @returns ParsedComment with manual languages and context
|
|
19
|
+
*/
|
|
20
|
+
function parseExtractedComment(extracted) {
|
|
21
|
+
const result = {
|
|
22
|
+
manual: [],
|
|
23
|
+
context: "",
|
|
24
|
+
};
|
|
25
|
+
if (!extracted) {
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
const lines = extracted.split("\n");
|
|
29
|
+
const contextParts = [];
|
|
30
|
+
for (const line of lines) {
|
|
31
|
+
const trimmedLine = line.trim();
|
|
32
|
+
if (trimmedLine.startsWith("@manual:")) {
|
|
33
|
+
// Parse @manual:zh-Hans,zh-Hant
|
|
34
|
+
const langList = trimmedLine.substring("@manual:".length).trim();
|
|
35
|
+
if (langList) {
|
|
36
|
+
result.manual = langList.split(",").map((lang) => lang.trim()).filter(Boolean);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else if (trimmedLine.startsWith("@context:")) {
|
|
40
|
+
// Parse @context:text
|
|
41
|
+
const contextText = trimmedLine.substring("@context:".length).trim();
|
|
42
|
+
if (contextText) {
|
|
43
|
+
contextParts.push(contextText);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (trimmedLine && !trimmedLine.startsWith("@")) {
|
|
47
|
+
// Plain text without @ prefix is also context
|
|
48
|
+
contextParts.push(trimmedLine);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
result.context = contextParts.join("\n");
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
exports.parseExtractedComment = parseExtractedComment;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a target language should be skipped (manual translation required)
|
|
57
|
+
*/
|
|
58
|
+
function shouldSkipForManual(parsedComment, targetLng) {
|
|
59
|
+
return parsedComment.manual.includes(targetLng);
|
|
60
|
+
}
|
|
61
|
+
exports.shouldSkipForManual = shouldSkipForManual;
|
|
62
|
+
/**
|
|
63
|
+
* Check if this entry has any @manual marking
|
|
64
|
+
*/
|
|
65
|
+
function hasManualMarking(parsedComment) {
|
|
66
|
+
return parsedComment.manual.length > 0;
|
|
67
|
+
}
|
|
68
|
+
exports.hasManualMarking = hasManualMarking;
|