mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
671 lines
26 KiB
671 lines
26 KiB
|
|
import {createRequire as __cjsCompatRequire} from 'module';
|
|
const require = __cjsCompatRequire(import.meta.url);
|
|
|
|
import {
|
|
Diagnostics,
|
|
buildCodeFrameError,
|
|
buildLocalizeReplacement,
|
|
isBabelParseError,
|
|
isLocalize,
|
|
makeParsedTranslation,
|
|
parseTranslation,
|
|
translate,
|
|
unwrapMessagePartsFromLocalizeCall,
|
|
unwrapMessagePartsFromTemplateLiteral,
|
|
unwrapSubstitutionsFromLocalizeCall
|
|
} from "./chunk-HR5KPXEW.js";
|
|
|
|
// packages/localize/tools/src/translate/source_files/es2015_translate_plugin.js
|
|
import { getFileSystem } from "@angular/compiler-cli/private/localize";
|
|
function makeEs2015TranslatePlugin(diagnostics, translations, { missingTranslation = "error", localizeName = "$localize" } = {}, fs = getFileSystem()) {
|
|
return {
|
|
visitor: {
|
|
TaggedTemplateExpression(path, state) {
|
|
try {
|
|
const tag = path.get("tag");
|
|
if (isLocalize(tag, localizeName)) {
|
|
const [messageParts] = unwrapMessagePartsFromTemplateLiteral(path.get("quasi").get("quasis"), fs);
|
|
const translated = translate(diagnostics, translations, messageParts, path.node.quasi.expressions, missingTranslation);
|
|
path.replaceWith(buildLocalizeReplacement(translated[0], translated[1]));
|
|
}
|
|
} catch (e) {
|
|
if (isBabelParseError(e)) {
|
|
throw buildCodeFrameError(fs, path, state.file, e);
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/source_files/es5_translate_plugin.js
|
|
import { getFileSystem as getFileSystem2 } from "@angular/compiler-cli/private/localize";
|
|
function makeEs5TranslatePlugin(diagnostics, translations, { missingTranslation = "error", localizeName = "$localize" } = {}, fs = getFileSystem2()) {
|
|
return {
|
|
visitor: {
|
|
CallExpression(callPath, state) {
|
|
try {
|
|
const calleePath = callPath.get("callee");
|
|
if (isLocalize(calleePath, localizeName)) {
|
|
const [messageParts] = unwrapMessagePartsFromLocalizeCall(callPath, fs);
|
|
const [expressions] = unwrapSubstitutionsFromLocalizeCall(callPath, fs);
|
|
const translated = translate(diagnostics, translations, messageParts, expressions, missingTranslation);
|
|
callPath.replaceWith(buildLocalizeReplacement(translated[0], translated[1]));
|
|
}
|
|
} catch (e) {
|
|
if (isBabelParseError(e)) {
|
|
diagnostics.error(buildCodeFrameError(fs, callPath, state.file, e));
|
|
} else {
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/source_files/locale_plugin.js
|
|
import { types as t } from "@babel/core";
|
|
function makeLocalePlugin(locale, { localizeName = "$localize" } = {}) {
|
|
return {
|
|
visitor: {
|
|
MemberExpression(expression) {
|
|
const obj = expression.get("object");
|
|
if (!isLocalize(obj, localizeName)) {
|
|
return;
|
|
}
|
|
const property = expression.get("property");
|
|
if (!property.isIdentifier({ name: "locale" })) {
|
|
return;
|
|
}
|
|
if (expression.parentPath.isAssignmentExpression() && expression.parentPath.get("left") === expression) {
|
|
return;
|
|
}
|
|
const parent = expression.parentPath;
|
|
if (parent.isLogicalExpression({ operator: "&&" }) && parent.get("right") === expression) {
|
|
const left = parent.get("left");
|
|
if (isLocalizeGuard(left, localizeName)) {
|
|
parent.replaceWith(expression);
|
|
} else if (left.isLogicalExpression({ operator: "&&" }) && isLocalizeGuard(left.get("right"), localizeName)) {
|
|
left.replaceWith(left.get("left"));
|
|
}
|
|
}
|
|
expression.replaceWith(t.stringLiteral(locale));
|
|
}
|
|
}
|
|
};
|
|
}
|
|
function isLocalizeGuard(expression, localizeName) {
|
|
if (!expression.isBinaryExpression() || !(expression.node.operator === "!==" || expression.node.operator === "!=")) {
|
|
return false;
|
|
}
|
|
const left = expression.get("left");
|
|
const right = expression.get("right");
|
|
return left.isUnaryExpression({ operator: "typeof" }) && isLocalize(left.get("argument"), localizeName) && right.isStringLiteral({ value: "undefined" }) || right.isUnaryExpression({ operator: "typeof" }) && isLocalize(right.get("argument"), localizeName) && left.isStringLiteral({ value: "undefined" });
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/arb_translation_parser.js
|
|
var ArbTranslationParser = class {
|
|
analyze(_filePath, contents) {
|
|
const diagnostics = new Diagnostics();
|
|
if (!contents.includes('"@@locale"')) {
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
try {
|
|
return { canParse: true, diagnostics, hint: this.tryParseArbFormat(contents) };
|
|
} catch {
|
|
diagnostics.warn("File is not valid JSON.");
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
}
|
|
parse(_filePath, contents, arb = this.tryParseArbFormat(contents)) {
|
|
const bundle = {
|
|
locale: arb["@@locale"],
|
|
translations: {},
|
|
diagnostics: new Diagnostics()
|
|
};
|
|
for (const messageId of Object.keys(arb)) {
|
|
if (messageId.startsWith("@")) {
|
|
continue;
|
|
}
|
|
const targetMessage = arb[messageId];
|
|
bundle.translations[messageId] = parseTranslation(targetMessage);
|
|
}
|
|
return bundle;
|
|
}
|
|
tryParseArbFormat(contents) {
|
|
const json = JSON.parse(contents);
|
|
if (typeof json["@@locale"] !== "string") {
|
|
throw new Error("Missing @@locale property.");
|
|
}
|
|
return json;
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.js
|
|
import { extname } from "path";
|
|
var SimpleJsonTranslationParser = class {
|
|
analyze(filePath, contents) {
|
|
const diagnostics = new Diagnostics();
|
|
if (extname(filePath) !== ".json" || !(contents.includes('"locale"') && contents.includes('"translations"'))) {
|
|
diagnostics.warn("File does not have .json extension.");
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
try {
|
|
const json = JSON.parse(contents);
|
|
if (json.locale === void 0) {
|
|
diagnostics.warn('Required "locale" property missing.');
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
if (typeof json.locale !== "string") {
|
|
diagnostics.warn('The "locale" property is not a string.');
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
if (json.translations === void 0) {
|
|
diagnostics.warn('Required "translations" property missing.');
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
if (typeof json.translations !== "object") {
|
|
diagnostics.warn('The "translations" is not an object.');
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
return { canParse: true, diagnostics, hint: json };
|
|
} catch (e) {
|
|
diagnostics.warn("File is not valid JSON.");
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
}
|
|
parse(_filePath, contents, json) {
|
|
const { locale: parsedLocale, translations } = json || JSON.parse(contents);
|
|
const parsedTranslations = {};
|
|
for (const messageId in translations) {
|
|
const targetMessage = translations[messageId];
|
|
parsedTranslations[messageId] = parseTranslation(targetMessage);
|
|
}
|
|
return { locale: parsedLocale, translations: parsedTranslations, diagnostics: new Diagnostics() };
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.js
|
|
import { ParseErrorLevel as ParseErrorLevel2, visitAll as visitAll2 } from "@angular/compiler";
|
|
|
|
// packages/localize/tools/src/translate/translation_files/base_visitor.js
|
|
var BaseVisitor = class {
|
|
visitElement(_element, _context) {
|
|
}
|
|
visitAttribute(_attribute, _context) {
|
|
}
|
|
visitText(_text, _context) {
|
|
}
|
|
visitComment(_comment, _context) {
|
|
}
|
|
visitExpansion(_expansion, _context) {
|
|
}
|
|
visitExpansionCase(_expansionCase, _context) {
|
|
}
|
|
visitBlock(_block, _context) {
|
|
}
|
|
visitBlockParameter(_parameter, _context) {
|
|
}
|
|
visitLetDeclaration(_decl, _context) {
|
|
}
|
|
visitComponent(_component, _context) {
|
|
}
|
|
visitDirective(_directive, _context) {
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/message_serialization/message_serializer.js
|
|
import { Element as Element2, ParseError as ParseError2, visitAll } from "@angular/compiler";
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/translation_utils.js
|
|
import { Element, ParseError, ParseErrorLevel, XmlParser } from "@angular/compiler";
|
|
function getAttrOrThrow(element, attrName) {
|
|
const attrValue = getAttribute(element, attrName);
|
|
if (attrValue === void 0) {
|
|
throw new ParseError(element.sourceSpan, `Missing required "${attrName}" attribute:`);
|
|
}
|
|
return attrValue;
|
|
}
|
|
function getAttribute(element, attrName) {
|
|
const attr = element.attrs.find((a) => a.name === attrName);
|
|
return attr !== void 0 ? attr.value : void 0;
|
|
}
|
|
function parseInnerRange(element) {
|
|
const xmlParser = new XmlParser();
|
|
const xml = xmlParser.parse(element.sourceSpan.start.file.content, element.sourceSpan.start.file.url, { tokenizeExpansionForms: true, range: getInnerRange(element) });
|
|
return xml;
|
|
}
|
|
function getInnerRange(element) {
|
|
const start = element.startSourceSpan.end;
|
|
const end = element.endSourceSpan.start;
|
|
return {
|
|
startPos: start.offset,
|
|
startLine: start.line,
|
|
startCol: start.col,
|
|
endPos: end.offset
|
|
};
|
|
}
|
|
function canParseXml(filePath, contents, rootNodeName, attributes) {
|
|
const diagnostics = new Diagnostics();
|
|
const xmlParser = new XmlParser();
|
|
const xml = xmlParser.parse(contents, filePath);
|
|
if (xml.rootNodes.length === 0 || xml.errors.some((error) => error.level === ParseErrorLevel.ERROR)) {
|
|
xml.errors.forEach((e) => addParseError(diagnostics, e));
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
const rootElements = xml.rootNodes.filter(isNamedElement(rootNodeName));
|
|
const rootElement = rootElements[0];
|
|
if (rootElement === void 0) {
|
|
diagnostics.warn(`The XML file does not contain a <${rootNodeName}> root node.`);
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
for (const attrKey of Object.keys(attributes)) {
|
|
const attr = rootElement.attrs.find((attr2) => attr2.name === attrKey);
|
|
if (attr === void 0 || attr.value !== attributes[attrKey]) {
|
|
addParseDiagnostic(diagnostics, rootElement.sourceSpan, `The <${rootNodeName}> node does not have the required attribute: ${attrKey}="${attributes[attrKey]}".`, ParseErrorLevel.WARNING);
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
}
|
|
if (rootElements.length > 1) {
|
|
xml.errors.push(new ParseError(xml.rootNodes[1].sourceSpan, "Unexpected root node. XLIFF 1.2 files should only have a single <xliff> root node.", ParseErrorLevel.WARNING));
|
|
}
|
|
return { canParse: true, diagnostics, hint: { element: rootElement, errors: xml.errors } };
|
|
}
|
|
function isNamedElement(name) {
|
|
function predicate(node) {
|
|
return node instanceof Element && node.name === name;
|
|
}
|
|
return predicate;
|
|
}
|
|
function addParseDiagnostic(diagnostics, sourceSpan, message, level) {
|
|
addParseError(diagnostics, new ParseError(sourceSpan, message, level));
|
|
}
|
|
function addParseError(diagnostics, parseError) {
|
|
if (parseError.level === ParseErrorLevel.ERROR) {
|
|
diagnostics.error(parseError.toString());
|
|
} else {
|
|
diagnostics.warn(parseError.toString());
|
|
}
|
|
}
|
|
function addErrorsToBundle(bundle, errors) {
|
|
for (const error of errors) {
|
|
addParseError(bundle.diagnostics, error);
|
|
}
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/translation_files/message_serialization/message_serializer.js
|
|
var MessageSerializer = class extends BaseVisitor {
|
|
renderer;
|
|
config;
|
|
constructor(renderer, config) {
|
|
super();
|
|
this.renderer = renderer;
|
|
this.config = config;
|
|
}
|
|
serialize(nodes) {
|
|
this.renderer.startRender();
|
|
visitAll(this, nodes);
|
|
this.renderer.endRender();
|
|
return this.renderer.message;
|
|
}
|
|
visitElement(element) {
|
|
if (this.config.placeholder && element.name === this.config.placeholder.elementName) {
|
|
const name = getAttrOrThrow(element, this.config.placeholder.nameAttribute);
|
|
const body = this.config.placeholder.bodyAttribute && getAttribute(element, this.config.placeholder.bodyAttribute);
|
|
this.visitPlaceholder(name, body);
|
|
} else if (this.config.placeholderContainer && element.name === this.config.placeholderContainer.elementName) {
|
|
const start = getAttrOrThrow(element, this.config.placeholderContainer.startAttribute);
|
|
const end = getAttrOrThrow(element, this.config.placeholderContainer.endAttribute);
|
|
this.visitPlaceholderContainer(start, element.children, end);
|
|
} else if (this.config.inlineElements.indexOf(element.name) !== -1) {
|
|
visitAll(this, element.children);
|
|
} else {
|
|
throw new ParseError2(element.sourceSpan, `Invalid element found in message.`);
|
|
}
|
|
}
|
|
visitText(text) {
|
|
this.renderer.text(text.value);
|
|
}
|
|
visitExpansion(expansion) {
|
|
this.renderer.startIcu();
|
|
this.renderer.text(`${expansion.switchValue}, ${expansion.type},`);
|
|
visitAll(this, expansion.cases);
|
|
this.renderer.endIcu();
|
|
}
|
|
visitExpansionCase(expansionCase) {
|
|
this.renderer.text(` ${expansionCase.value} {`);
|
|
this.renderer.startContainer();
|
|
visitAll(this, expansionCase.expression);
|
|
this.renderer.closeContainer();
|
|
this.renderer.text(`}`);
|
|
}
|
|
visitContainedNodes(nodes) {
|
|
this.renderer.startContainer();
|
|
visitAll(this, nodes);
|
|
this.renderer.closeContainer();
|
|
}
|
|
visitPlaceholder(name, body) {
|
|
this.renderer.placeholder(name, body);
|
|
}
|
|
visitPlaceholderContainer(startName, children, closeName) {
|
|
this.renderer.startPlaceholder(startName);
|
|
this.visitContainedNodes(children);
|
|
this.renderer.closePlaceholder(closeName);
|
|
}
|
|
isPlaceholderContainer(node) {
|
|
return node instanceof Element2 && node.name === this.config.placeholderContainer.elementName;
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/message_serialization/target_message_renderer.js
|
|
var TargetMessageRenderer = class {
|
|
current = { messageParts: [], placeholderNames: [], text: "" };
|
|
icuDepth = 0;
|
|
get message() {
|
|
const { messageParts, placeholderNames } = this.current;
|
|
return makeParsedTranslation(messageParts, placeholderNames);
|
|
}
|
|
startRender() {
|
|
}
|
|
endRender() {
|
|
this.storeMessagePart();
|
|
}
|
|
text(text) {
|
|
this.current.text += text;
|
|
}
|
|
placeholder(name, body) {
|
|
this.renderPlaceholder(name);
|
|
}
|
|
startPlaceholder(name) {
|
|
this.renderPlaceholder(name);
|
|
}
|
|
closePlaceholder(name) {
|
|
this.renderPlaceholder(name);
|
|
}
|
|
startContainer() {
|
|
}
|
|
closeContainer() {
|
|
}
|
|
startIcu() {
|
|
this.icuDepth++;
|
|
this.text("{");
|
|
}
|
|
endIcu() {
|
|
this.icuDepth--;
|
|
this.text("}");
|
|
}
|
|
normalizePlaceholderName(name) {
|
|
return name.replace(/-/g, "_");
|
|
}
|
|
renderPlaceholder(name) {
|
|
name = this.normalizePlaceholderName(name);
|
|
if (this.icuDepth > 0) {
|
|
this.text(`{${name}}`);
|
|
} else {
|
|
this.storeMessagePart();
|
|
this.current.placeholderNames.push(name);
|
|
}
|
|
}
|
|
storeMessagePart() {
|
|
this.current.messageParts.push(this.current.text);
|
|
this.current.text = "";
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.js
|
|
function serializeTranslationMessage(element, config) {
|
|
const { rootNodes, errors: parseErrors } = parseInnerRange(element);
|
|
try {
|
|
const serializer = new MessageSerializer(new TargetMessageRenderer(), config);
|
|
const translation = serializer.serialize(rootNodes);
|
|
return { translation, parseErrors, serializeErrors: [] };
|
|
} catch (e) {
|
|
return { translation: null, parseErrors, serializeErrors: [e] };
|
|
}
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.js
|
|
var Xliff1TranslationParser = class {
|
|
analyze(filePath, contents) {
|
|
return canParseXml(filePath, contents, "xliff", { version: "1.2" });
|
|
}
|
|
parse(filePath, contents, hint) {
|
|
return this.extractBundle(hint);
|
|
}
|
|
extractBundle({ element, errors }) {
|
|
const diagnostics = new Diagnostics();
|
|
errors.forEach((e) => addParseError(diagnostics, e));
|
|
if (element.children.length === 0) {
|
|
addParseDiagnostic(diagnostics, element.sourceSpan, "Missing expected <file> element", ParseErrorLevel2.WARNING);
|
|
return { locale: void 0, translations: {}, diagnostics };
|
|
}
|
|
const files = element.children.filter(isNamedElement("file"));
|
|
if (files.length === 0) {
|
|
addParseDiagnostic(diagnostics, element.sourceSpan, "No <file> elements found in <xliff>", ParseErrorLevel2.WARNING);
|
|
} else if (files.length > 1) {
|
|
addParseDiagnostic(diagnostics, files[1].sourceSpan, "More than one <file> element found in <xliff>", ParseErrorLevel2.WARNING);
|
|
}
|
|
const bundle = { locale: void 0, translations: {}, diagnostics };
|
|
const translationVisitor = new XliffTranslationVisitor();
|
|
const localesFound = /* @__PURE__ */ new Set();
|
|
for (const file of files) {
|
|
const locale = getAttribute(file, "target-language");
|
|
if (locale !== void 0) {
|
|
localesFound.add(locale);
|
|
bundle.locale = locale;
|
|
}
|
|
visitAll2(translationVisitor, file.children, bundle);
|
|
}
|
|
if (localesFound.size > 1) {
|
|
addParseDiagnostic(diagnostics, element.sourceSpan, `More than one locale found in translation file: ${JSON.stringify(Array.from(localesFound))}. Using "${bundle.locale}"`, ParseErrorLevel2.WARNING);
|
|
}
|
|
return bundle;
|
|
}
|
|
};
|
|
var XliffTranslationVisitor = class extends BaseVisitor {
|
|
visitElement(element, bundle) {
|
|
if (element.name === "trans-unit") {
|
|
this.visitTransUnitElement(element, bundle);
|
|
} else {
|
|
visitAll2(this, element.children, bundle);
|
|
}
|
|
}
|
|
visitTransUnitElement(element, bundle) {
|
|
const id = getAttribute(element, "id");
|
|
if (id === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Missing required "id" attribute on <trans-unit> element.`, ParseErrorLevel2.ERROR);
|
|
return;
|
|
}
|
|
if (bundle.translations[id] !== void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${id}"`, ParseErrorLevel2.ERROR);
|
|
return;
|
|
}
|
|
let targetMessage = element.children.find(isNamedElement("target"));
|
|
if (targetMessage === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Missing <target> element", ParseErrorLevel2.WARNING);
|
|
targetMessage = element.children.find(isNamedElement("source"));
|
|
if (targetMessage === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Missing required element: one of <target> or <source> is required", ParseErrorLevel2.ERROR);
|
|
return;
|
|
}
|
|
}
|
|
const { translation, parseErrors, serializeErrors } = serializeTranslationMessage(targetMessage, {
|
|
inlineElements: ["g", "bx", "ex", "bpt", "ept", "ph", "it", "mrk"],
|
|
placeholder: { elementName: "x", nameAttribute: "id" }
|
|
});
|
|
if (translation !== null) {
|
|
bundle.translations[id] = translation;
|
|
}
|
|
addErrorsToBundle(bundle, parseErrors);
|
|
addErrorsToBundle(bundle, serializeErrors);
|
|
}
|
|
};
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.js
|
|
import { Element as Element3, ParseErrorLevel as ParseErrorLevel3, visitAll as visitAll3 } from "@angular/compiler";
|
|
var Xliff2TranslationParser = class {
|
|
analyze(filePath, contents) {
|
|
return canParseXml(filePath, contents, "xliff", { version: "2.0" });
|
|
}
|
|
parse(filePath, contents, hint) {
|
|
return this.extractBundle(hint);
|
|
}
|
|
extractBundle({ element, errors }) {
|
|
const diagnostics = new Diagnostics();
|
|
errors.forEach((e) => addParseError(diagnostics, e));
|
|
const locale = getAttribute(element, "trgLang");
|
|
const files = element.children.filter(isFileElement);
|
|
if (files.length === 0) {
|
|
addParseDiagnostic(diagnostics, element.sourceSpan, "No <file> elements found in <xliff>", ParseErrorLevel3.WARNING);
|
|
} else if (files.length > 1) {
|
|
addParseDiagnostic(diagnostics, files[1].sourceSpan, "More than one <file> element found in <xliff>", ParseErrorLevel3.WARNING);
|
|
}
|
|
const bundle = { locale, translations: {}, diagnostics };
|
|
const translationVisitor = new Xliff2TranslationVisitor();
|
|
for (const file of files) {
|
|
visitAll3(translationVisitor, file.children, { bundle });
|
|
}
|
|
return bundle;
|
|
}
|
|
};
|
|
var Xliff2TranslationVisitor = class extends BaseVisitor {
|
|
visitElement(element, { bundle, unit }) {
|
|
if (element.name === "unit") {
|
|
this.visitUnitElement(element, bundle);
|
|
} else if (element.name === "segment") {
|
|
this.visitSegmentElement(element, bundle, unit);
|
|
} else {
|
|
visitAll3(this, element.children, { bundle, unit });
|
|
}
|
|
}
|
|
visitUnitElement(element, bundle) {
|
|
const externalId = getAttribute(element, "id");
|
|
if (externalId === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Missing required "id" attribute on <trans-unit> element.`, ParseErrorLevel3.ERROR);
|
|
return;
|
|
}
|
|
if (bundle.translations[externalId] !== void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${externalId}"`, ParseErrorLevel3.ERROR);
|
|
return;
|
|
}
|
|
visitAll3(this, element.children, { bundle, unit: externalId });
|
|
}
|
|
visitSegmentElement(element, bundle, unit) {
|
|
if (unit === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Invalid <segment> element: should be a child of a <unit> element.", ParseErrorLevel3.ERROR);
|
|
return;
|
|
}
|
|
let targetMessage = element.children.find(isNamedElement("target"));
|
|
if (targetMessage === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Missing <target> element", ParseErrorLevel3.WARNING);
|
|
targetMessage = element.children.find(isNamedElement("source"));
|
|
if (targetMessage === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Missing required element: one of <target> or <source> is required", ParseErrorLevel3.ERROR);
|
|
return;
|
|
}
|
|
}
|
|
const { translation, parseErrors, serializeErrors } = serializeTranslationMessage(targetMessage, {
|
|
inlineElements: ["cp", "sc", "ec", "mrk", "sm", "em"],
|
|
placeholder: { elementName: "ph", nameAttribute: "equiv", bodyAttribute: "disp" },
|
|
placeholderContainer: {
|
|
elementName: "pc",
|
|
startAttribute: "equivStart",
|
|
endAttribute: "equivEnd"
|
|
}
|
|
});
|
|
if (translation !== null) {
|
|
bundle.translations[unit] = translation;
|
|
}
|
|
addErrorsToBundle(bundle, parseErrors);
|
|
addErrorsToBundle(bundle, serializeErrors);
|
|
}
|
|
};
|
|
function isFileElement(node) {
|
|
return node instanceof Element3 && node.name === "file";
|
|
}
|
|
|
|
// packages/localize/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.js
|
|
import { ParseErrorLevel as ParseErrorLevel4, visitAll as visitAll4 } from "@angular/compiler";
|
|
import { extname as extname2 } from "path";
|
|
var XtbTranslationParser = class {
|
|
analyze(filePath, contents) {
|
|
const extension = extname2(filePath);
|
|
if (extension !== ".xtb" && extension !== ".xmb") {
|
|
const diagnostics = new Diagnostics();
|
|
diagnostics.warn("Must have xtb or xmb extension.");
|
|
return { canParse: false, diagnostics };
|
|
}
|
|
return canParseXml(filePath, contents, "translationbundle", {});
|
|
}
|
|
parse(filePath, contents, hint) {
|
|
return this.extractBundle(hint);
|
|
}
|
|
extractBundle({ element, errors }) {
|
|
const langAttr = element.attrs.find((attr) => attr.name === "lang");
|
|
const bundle = {
|
|
locale: langAttr && langAttr.value,
|
|
translations: {},
|
|
diagnostics: new Diagnostics()
|
|
};
|
|
errors.forEach((e) => addParseError(bundle.diagnostics, e));
|
|
const bundleVisitor = new XtbVisitor();
|
|
visitAll4(bundleVisitor, element.children, bundle);
|
|
return bundle;
|
|
}
|
|
};
|
|
var XtbVisitor = class extends BaseVisitor {
|
|
visitElement(element, bundle) {
|
|
switch (element.name) {
|
|
case "translation":
|
|
const id = getAttribute(element, "id");
|
|
if (id === void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Missing required "id" attribute on <translation> element.`, ParseErrorLevel4.ERROR);
|
|
return;
|
|
}
|
|
if (bundle.translations[id] !== void 0) {
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${id}"`, ParseErrorLevel4.ERROR);
|
|
return;
|
|
}
|
|
const { translation, parseErrors, serializeErrors } = serializeTranslationMessage(element, {
|
|
inlineElements: [],
|
|
placeholder: { elementName: "ph", nameAttribute: "name" }
|
|
});
|
|
if (parseErrors.length) {
|
|
bundle.diagnostics.warn(computeParseWarning(id, parseErrors));
|
|
} else if (translation !== null) {
|
|
bundle.translations[id] = translation;
|
|
}
|
|
addErrorsToBundle(bundle, serializeErrors);
|
|
break;
|
|
default:
|
|
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Unexpected <${element.name}> tag.`, ParseErrorLevel4.ERROR);
|
|
}
|
|
}
|
|
};
|
|
function computeParseWarning(id, errors) {
|
|
const msg = errors.map((e) => e.toString()).join("\n");
|
|
return `Could not parse message with id "${id}" - perhaps it has an unrecognised ICU format?
|
|
` + msg;
|
|
}
|
|
|
|
export {
|
|
makeEs2015TranslatePlugin,
|
|
makeEs5TranslatePlugin,
|
|
makeLocalePlugin,
|
|
ArbTranslationParser,
|
|
SimpleJsonTranslationParser,
|
|
Xliff1TranslationParser,
|
|
Xliff2TranslationParser,
|
|
XtbTranslationParser
|
|
};
|
|
/**
|
|
* @license
|
|
* Copyright Google LLC All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.dev/license
|
|
*/
|
|
|