Changing front

This commit is contained in:
2023-01-16 17:49:38 +01:00
parent 0b8a93b256
commit 4fe4be7730
48586 changed files with 4725790 additions and 17464 deletions
+4
View File
@@ -0,0 +1,4 @@
### Disclaimer
The localize tools are consumed via the Angular CLI. The programmatic APIs are not considered officially
supported though and are not subject to the breaking change guarantees of SemVer.
+678
View File
@@ -0,0 +1,678 @@
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
import {
Diagnostics,
buildCodeFrameError,
buildLocalizeReplacement,
isBabelParseError,
isLocalize,
translate,
types,
unwrapMessagePartsFromLocalizeCall,
unwrapMessagePartsFromTemplateLiteral,
unwrapSubstitutionsFromLocalizeCall
} from "./chunk-EE2T5UCZ.js";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/source_files/es2015_translate_plugin.mjs
import { getFileSystem } from "@angular/compiler-cli/private/localize";
function makeEs2015TranslatePlugin(diagnostics, translations, { missingTranslation = "error", localizeName = "$localize" } = {}, fs = getFileSystem()) {
return {
visitor: {
TaggedTemplateExpression(path) {
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, e);
} else {
throw e;
}
}
}
}
};
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/source_files/es5_translate_plugin.mjs
import { getFileSystem as getFileSystem2 } from "@angular/compiler-cli/private/localize";
function makeEs5TranslatePlugin(diagnostics, translations, { missingTranslation = "error", localizeName = "$localize" } = {}, fs = getFileSystem2()) {
return {
visitor: {
CallExpression(callPath) {
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, e));
} else {
throw e;
}
}
}
}
};
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/source_files/locale_plugin.mjs
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(types.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" });
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/arb_translation_parser.mjs
import { \u0275parseTranslation } from "@angular/localize";
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] = \u0275parseTranslation(targetMessage);
}
return bundle;
}
tryParseArbFormat(contents) {
const json = JSON.parse(contents);
if (typeof json["@@locale"] !== "string") {
throw new Error("Missing @@locale property.");
}
return json;
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser.mjs
import { \u0275parseTranslation as \u0275parseTranslation2 } from "@angular/localize";
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] = \u0275parseTranslation2(targetMessage);
}
return { locale: parsedLocale, translations: parsedTranslations, diagnostics: new Diagnostics() };
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.mjs
import { ParseErrorLevel as ParseErrorLevel3, visitAll as visitAll2 } from "@angular/compiler";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/base_visitor.mjs
var BaseVisitor = class {
visitElement(_element, _context) {
}
visitAttribute(_attribute, _context) {
}
visitText(_text, _context) {
}
visitComment(_comment, _context) {
}
visitExpansion(_expansion, _context) {
}
visitExpansionCase(_expansionCase, _context) {
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/message_serialization/message_serializer.mjs
import { Element as Element2, visitAll } from "@angular/compiler";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/translation_parse_error.mjs
import { ParseErrorLevel } from "@angular/compiler";
var TranslationParseError = class extends Error {
constructor(span, msg, level = ParseErrorLevel.ERROR) {
super(contextualMessage(span, msg, level));
this.span = span;
this.msg = msg;
this.level = level;
}
};
function contextualMessage(span, msg, level) {
const ctx = span.start.getContext(100, 2);
msg += `
At ${span.start}${span.details ? `, ${span.details}` : ""}:
`;
if (ctx) {
msg += `...${ctx.before}[${ParseErrorLevel[level]} ->]${ctx.after}...
`;
}
return msg;
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/translation_utils.mjs
import { Element, ParseError, ParseErrorLevel as ParseErrorLevel2, XmlParser } from "@angular/compiler";
function getAttrOrThrow(element, attrName) {
const attrValue = getAttribute(element, attrName);
if (attrValue === void 0) {
throw new TranslationParseError(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 === ParseErrorLevel2.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]}".`, ParseErrorLevel2.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.", ParseErrorLevel2.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 === ParseErrorLevel2.ERROR) {
diagnostics.error(parseError.toString());
} else {
diagnostics.warn(parseError.toString());
}
}
function addErrorsToBundle(bundle, errors) {
for (const error of errors) {
addParseError(bundle.diagnostics, error);
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/message_serialization/message_serializer.mjs
var MessageSerializer = class extends BaseVisitor {
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 TranslationParseError(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;
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/message_serialization/target_message_renderer.mjs
import { \u0275makeParsedTranslation } from "@angular/localize";
var TargetMessageRenderer = class {
constructor() {
this.current = { messageParts: [], placeholderNames: [], text: "" };
this.icuDepth = 0;
}
get message() {
const { messageParts, placeholderNames } = this.current;
return \u0275makeParsedTranslation(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 = "";
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/serialize_translation_message.mjs
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] };
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser.mjs
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", ParseErrorLevel3.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>", 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: 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}"`, ParseErrorLevel3.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.`, ParseErrorLevel3.ERROR);
return;
}
if (bundle.translations[id] !== void 0) {
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${id}"`, 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: ["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);
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser.mjs
import { Element as Element3, ParseErrorLevel as ParseErrorLevel4, 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>", ParseErrorLevel4.WARNING);
} else if (files.length > 1) {
addParseDiagnostic(diagnostics, files[1].sourceSpan, "More than one <file> element found in <xliff>", ParseErrorLevel4.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.`, ParseErrorLevel4.ERROR);
return;
}
if (bundle.translations[externalId] !== void 0) {
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${externalId}"`, ParseErrorLevel4.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.", ParseErrorLevel4.ERROR);
return;
}
let targetMessage = element.children.find(isNamedElement("target"));
if (targetMessage === void 0) {
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, "Missing <target> element", ParseErrorLevel4.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", ParseErrorLevel4.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";
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser.mjs
import { ParseErrorLevel as ParseErrorLevel5, 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.`, ParseErrorLevel5.ERROR);
return;
}
if (bundle.translations[id] !== void 0) {
addParseDiagnostic(bundle.diagnostics, element.sourceSpan, `Duplicated translations for message "${id}"`, ParseErrorLevel5.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.`, ParseErrorLevel5.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.io/license
*/
//# sourceMappingURL=chunk-52W4JLVC.js.map
File diff suppressed because one or more lines are too long
+344
View File
@@ -0,0 +1,344 @@
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/diagnostics.mjs
var Diagnostics = class {
constructor() {
this.messages = [];
}
get hasErrors() {
return this.messages.some((m) => m.type === "error");
}
add(type, message) {
if (type !== "ignore") {
this.messages.push({ type, message });
}
}
warn(message) {
this.messages.push({ type: "warning", message });
}
error(message) {
this.messages.push({ type: "error", message });
}
merge(other) {
this.messages.push(...other.messages);
}
formatDiagnostics(message) {
const errors = this.messages.filter((d) => d.type === "error").map((d) => " - " + d.message);
const warnings = this.messages.filter((d) => d.type === "warning").map((d) => " - " + d.message);
if (errors.length) {
message += "\nERRORS:\n" + errors.join("\n");
}
if (warnings.length) {
message += "\nWARNINGS:\n" + warnings.join("\n");
}
return message;
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/source_file_utils.mjs
import { getFileSystem } from "@angular/compiler-cli/private/localize";
import { \u0275isMissingTranslationError, \u0275makeTemplateObject, \u0275translate } from "@angular/localize";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/babel_core.mjs
import * as _babelNamespace from "@babel/core";
import _babelDefault from "@babel/core";
var _a;
var babel = (_a = _babelDefault) != null ? _a : _babelNamespace;
var _typesNamespace = _babelNamespace.types;
if (_babelDefault !== void 0) {
_typesNamespace = _babelDefault.types;
}
var types2 = _typesNamespace;
var NodePath = babel.NodePath;
var transformSync = babel.transformSync;
var parseSync = babel.parseSync;
var transformFromAstSync = babel.transformFromAstSync;
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/source_file_utils.mjs
function isLocalize(expression, localizeName) {
return isNamedIdentifier(expression, localizeName) && isGlobalIdentifier(expression);
}
function isNamedIdentifier(expression, name) {
return expression.isIdentifier() && expression.node.name === name;
}
function isGlobalIdentifier(identifier) {
return !identifier.scope || !identifier.scope.hasBinding(identifier.node.name);
}
function buildLocalizeReplacement(messageParts, substitutions) {
let mappedString = types2.stringLiteral(messageParts[0]);
for (let i = 1; i < messageParts.length; i++) {
mappedString = types2.binaryExpression("+", mappedString, wrapInParensIfNecessary(substitutions[i - 1]));
mappedString = types2.binaryExpression("+", mappedString, types2.stringLiteral(messageParts[i]));
}
return mappedString;
}
function unwrapMessagePartsFromLocalizeCall(call, fs = getFileSystem()) {
let cooked = call.get("arguments")[0];
if (cooked === void 0) {
throw new BabelParseError(call.node, "`$localize` called without any arguments.");
}
if (!cooked.isExpression()) {
throw new BabelParseError(cooked.node, "Unexpected argument to `$localize` (expected an array).");
}
let raw = cooked;
if (cooked.isLogicalExpression() && cooked.node.operator === "||" && cooked.get("left").isIdentifier()) {
const right = cooked.get("right");
if (right.isAssignmentExpression()) {
cooked = right.get("right");
if (!cooked.isExpression()) {
throw new BabelParseError(cooked.node, 'Unexpected "makeTemplateObject()" function (expected an expression).');
}
} else if (right.isSequenceExpression()) {
const expressions = right.get("expressions");
if (expressions.length > 2) {
const [first, second] = expressions;
if (first.isAssignmentExpression()) {
cooked = first.get("right");
if (!cooked.isExpression()) {
throw new BabelParseError(first.node, "Unexpected cooked value, expected an expression.");
}
if (second.isAssignmentExpression()) {
raw = second.get("right");
if (!raw.isExpression()) {
throw new BabelParseError(second.node, "Unexpected raw value, expected an expression.");
}
} else {
raw = cooked;
}
}
}
}
}
if (cooked.isCallExpression()) {
let call2 = cooked;
if (call2.get("arguments").length === 0) {
call2 = unwrapLazyLoadHelperCall(call2);
}
cooked = call2.get("arguments")[0];
if (!cooked.isExpression()) {
throw new BabelParseError(cooked.node, 'Unexpected `cooked` argument to the "makeTemplateObject()" function (expected an expression).');
}
const arg2 = call2.get("arguments")[1];
if (arg2 && !arg2.isExpression()) {
throw new BabelParseError(arg2.node, 'Unexpected `raw` argument to the "makeTemplateObject()" function (expected an expression).');
}
raw = arg2 !== void 0 ? arg2 : cooked;
}
const [cookedStrings] = unwrapStringLiteralArray(cooked, fs);
const [rawStrings, rawLocations] = unwrapStringLiteralArray(raw, fs);
return [\u0275makeTemplateObject(cookedStrings, rawStrings), rawLocations];
}
function unwrapSubstitutionsFromLocalizeCall(call, fs = getFileSystem()) {
const expressions = call.get("arguments").splice(1);
if (!isArrayOfExpressions(expressions)) {
const badExpression = expressions.find((expression) => !expression.isExpression());
throw new BabelParseError(badExpression.node, "Invalid substitutions for `$localize` (expected all substitution arguments to be expressions).");
}
return [
expressions.map((path) => path.node),
expressions.map((expression) => getLocation(fs, expression))
];
}
function unwrapMessagePartsFromTemplateLiteral(elements, fs = getFileSystem()) {
const cooked = elements.map((q) => {
if (q.node.value.cooked === void 0) {
throw new BabelParseError(q.node, `Unexpected undefined message part in "${elements.map((q2) => q2.node.value.cooked)}"`);
}
return q.node.value.cooked;
});
const raw = elements.map((q) => q.node.value.raw);
const locations = elements.map((q) => getLocation(fs, q));
return [\u0275makeTemplateObject(cooked, raw), locations];
}
function unwrapExpressionsFromTemplateLiteral(quasi, fs = getFileSystem()) {
return [
quasi.node.expressions,
quasi.get("expressions").map((e) => getLocation(fs, e))
];
}
function wrapInParensIfNecessary(expression) {
if (types2.isBinaryExpression(expression)) {
return types2.parenthesizedExpression(expression);
} else {
return expression;
}
}
function unwrapStringLiteralArray(array, fs = getFileSystem()) {
if (!isStringLiteralArray(array.node)) {
throw new BabelParseError(array.node, "Unexpected messageParts for `$localize` (expected an array of strings).");
}
const elements = array.get("elements");
return [elements.map((str) => str.node.value), elements.map((str) => getLocation(fs, str))];
}
function unwrapLazyLoadHelperCall(call) {
const callee = call.get("callee");
if (!callee.isIdentifier()) {
throw new BabelParseError(callee.node, "Unexpected lazy-load helper call (expected a call of the form `_templateObject()`).");
}
const lazyLoadBinding = call.scope.getBinding(callee.node.name);
if (!lazyLoadBinding) {
throw new BabelParseError(callee.node, "Missing declaration for lazy-load helper function");
}
const lazyLoadFn = lazyLoadBinding.path;
if (!lazyLoadFn.isFunctionDeclaration()) {
throw new BabelParseError(lazyLoadFn.node, "Unexpected expression (expected a function declaration");
}
const returnedNode = getReturnedExpression(lazyLoadFn);
if (returnedNode.isCallExpression()) {
return returnedNode;
}
if (returnedNode.isIdentifier()) {
const identifierName = returnedNode.node.name;
const declaration = returnedNode.scope.getBinding(identifierName);
if (declaration === void 0) {
throw new BabelParseError(returnedNode.node, "Missing declaration for return value from helper.");
}
if (!declaration.path.isVariableDeclarator()) {
throw new BabelParseError(declaration.path.node, "Unexpected helper return value declaration (expected a variable declaration).");
}
const initializer = declaration.path.get("init");
if (!initializer.isCallExpression()) {
throw new BabelParseError(declaration.path.node, "Unexpected return value from helper (expected a call expression).");
}
if (lazyLoadBinding.references === 1) {
lazyLoadFn.remove();
}
return initializer;
}
return call;
}
function getReturnedExpression(fn) {
const bodyStatements = fn.get("body").get("body");
for (const statement of bodyStatements) {
if (statement.isReturnStatement()) {
const argument = statement.get("argument");
if (argument.isSequenceExpression()) {
const expressions = argument.get("expressions");
return Array.isArray(expressions) ? expressions[expressions.length - 1] : expressions;
} else if (argument.isExpression()) {
return argument;
} else {
throw new BabelParseError(statement.node, "Invalid return argument in helper function (expected an expression).");
}
}
}
throw new BabelParseError(fn.node, "Missing return statement in helper function.");
}
function isStringLiteralArray(node) {
return types2.isArrayExpression(node) && node.elements.every((element) => types2.isStringLiteral(element));
}
function isArrayOfExpressions(paths) {
return paths.every((element) => element.isExpression());
}
function translate(diagnostics, translations, messageParts, substitutions, missingTranslation) {
try {
return \u0275translate(translations, messageParts, substitutions);
} catch (e) {
if (\u0275isMissingTranslationError(e)) {
diagnostics.add(missingTranslation, e.message);
return [
\u0275makeTemplateObject(e.parsedMessage.messageParts, e.parsedMessage.messageParts),
substitutions
];
} else {
diagnostics.error(e.message);
return [messageParts, substitutions];
}
}
}
var BabelParseError = class extends Error {
constructor(node, message) {
super(message);
this.node = node;
this.type = "BabelParseError";
}
};
function isBabelParseError(e) {
return e.type === "BabelParseError";
}
function buildCodeFrameError(fs, path, e) {
let filename = path.hub.file.opts.filename;
if (filename) {
filename = fs.resolve(filename);
let cwd = path.hub.file.opts.cwd;
if (cwd) {
cwd = fs.resolve(cwd);
filename = fs.relative(cwd, filename);
}
} else {
filename = "(unknown file)";
}
const message = path.hub.file.buildCodeFrameError(e.node, e.message).message;
return `${filename}: ${message}`;
}
function getLocation(fs, startPath, endPath) {
const startLocation = startPath.node.loc;
const file = getFileFromPath(fs, startPath);
if (!startLocation || !file) {
return void 0;
}
const endLocation = endPath && getFileFromPath(fs, endPath) === file && endPath.node.loc || startLocation;
return {
start: getLineAndColumn(startLocation.start),
end: getLineAndColumn(endLocation.end),
file,
text: getText(startPath)
};
}
function serializeLocationPosition(location) {
const endLineString = location.end !== void 0 && location.end.line !== location.start.line ? `,${location.end.line + 1}` : "";
return `${location.start.line + 1}${endLineString}`;
}
function getFileFromPath(fs, path) {
var _a2, _b;
const opts = path == null ? void 0 : path.hub.file.opts;
const filename = opts == null ? void 0 : opts.filename;
if (!filename || !opts.cwd) {
return null;
}
const relativePath = fs.relative(opts.cwd, filename);
const root = (_b = (_a2 = opts.generatorOpts) == null ? void 0 : _a2.sourceRoot) != null ? _b : opts.cwd;
const absPath = fs.resolve(root, relativePath);
return absPath;
}
function getLineAndColumn(loc) {
return { line: loc.line - 1, column: loc.column };
}
function getText(path) {
if (path.node.start == null || path.node.end == null) {
return void 0;
}
return path.hub.file.code.substring(path.node.start, path.node.end);
}
export {
Diagnostics,
types2 as types,
transformSync,
parseSync,
transformFromAstSync,
isLocalize,
isNamedIdentifier,
isGlobalIdentifier,
buildLocalizeReplacement,
unwrapMessagePartsFromLocalizeCall,
unwrapSubstitutionsFromLocalizeCall,
unwrapMessagePartsFromTemplateLiteral,
unwrapExpressionsFromTemplateLiteral,
translate,
isBabelParseError,
buildCodeFrameError,
getLocation,
serializeLocationPosition
};
/**
* @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.io/license
*/
//# sourceMappingURL=chunk-EE2T5UCZ.js.map
File diff suppressed because one or more lines are too long
+902
View File
@@ -0,0 +1,902 @@
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
import {
Diagnostics,
buildCodeFrameError,
getLocation,
isBabelParseError,
isGlobalIdentifier,
isNamedIdentifier,
serializeLocationPosition,
transformSync,
unwrapExpressionsFromTemplateLiteral,
unwrapMessagePartsFromLocalizeCall,
unwrapMessagePartsFromTemplateLiteral,
unwrapSubstitutionsFromLocalizeCall
} from "./chunk-EE2T5UCZ.js";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/duplicates.mjs
function checkDuplicateMessages(fs, messages, duplicateMessageHandling, basePath) {
const diagnostics = new Diagnostics();
if (duplicateMessageHandling === "ignore")
return diagnostics;
const messageMap = /* @__PURE__ */ new Map();
for (const message of messages) {
if (messageMap.has(message.id)) {
messageMap.get(message.id).push(message);
} else {
messageMap.set(message.id, [message]);
}
}
for (const duplicates of messageMap.values()) {
if (duplicates.length <= 1)
continue;
if (duplicates.every((message) => message.text === duplicates[0].text))
continue;
const diagnosticMessage = `Duplicate messages with id "${duplicates[0].id}":
` + duplicates.map((message) => serializeMessage(fs, basePath, message)).join("\n");
diagnostics.add(duplicateMessageHandling, diagnosticMessage);
}
return diagnostics;
}
function serializeMessage(fs, basePath, message) {
if (message.location === void 0) {
return ` - "${message.text}"`;
} else {
const locationFile = fs.relative(basePath, message.location.file);
const locationPosition = serializeLocationPosition(message.location);
return ` - "${message.text}" : ${locationFile}:${locationPosition}`;
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/extraction.mjs
import { SourceFileLoader } from "@angular/compiler-cli/private/localize";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/source_files/es2015_extract_plugin.mjs
import { \u0275parseMessage } from "@angular/localize";
function makeEs2015ExtractPlugin(fs, messages, localizeName = "$localize") {
return {
visitor: {
TaggedTemplateExpression(path) {
const tag = path.get("tag");
if (isNamedIdentifier(tag, localizeName) && isGlobalIdentifier(tag)) {
const quasiPath = path.get("quasi");
const [messageParts, messagePartLocations] = unwrapMessagePartsFromTemplateLiteral(quasiPath.get("quasis"), fs);
const [expressions, expressionLocations] = unwrapExpressionsFromTemplateLiteral(quasiPath, fs);
const location = getLocation(fs, quasiPath);
const message = \u0275parseMessage(messageParts, expressions, location, messagePartLocations, expressionLocations);
messages.push(message);
}
}
}
};
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/source_files/es5_extract_plugin.mjs
import { \u0275parseMessage as \u0275parseMessage2 } from "@angular/localize";
function makeEs5ExtractPlugin(fs, messages, localizeName = "$localize") {
return {
visitor: {
CallExpression(callPath) {
try {
const calleePath = callPath.get("callee");
if (isNamedIdentifier(calleePath, localizeName) && isGlobalIdentifier(calleePath)) {
const [messageParts, messagePartLocations] = unwrapMessagePartsFromLocalizeCall(callPath, fs);
const [expressions, expressionLocations] = unwrapSubstitutionsFromLocalizeCall(callPath, fs);
const [messagePartsArg, expressionsArg] = callPath.get("arguments");
const location = getLocation(fs, messagePartsArg, expressionsArg);
const message = \u0275parseMessage2(messageParts, expressions, location, messagePartLocations, expressionLocations);
messages.push(message);
}
} catch (e) {
if (isBabelParseError(e)) {
throw buildCodeFrameError(fs, callPath, e);
} else {
throw e;
}
}
}
}
};
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/extraction.mjs
var MessageExtractor = class {
constructor(fs, logger, { basePath, useSourceMaps = true, localizeName = "$localize" }) {
this.fs = fs;
this.logger = logger;
this.basePath = basePath;
this.useSourceMaps = useSourceMaps;
this.localizeName = localizeName;
this.loader = new SourceFileLoader(this.fs, this.logger, { webpack: basePath });
}
extractMessages(filename) {
const messages = [];
const sourceCode = this.fs.readFile(this.fs.resolve(this.basePath, filename));
if (sourceCode.includes(this.localizeName)) {
transformSync(sourceCode, {
sourceRoot: this.basePath,
filename,
plugins: [
makeEs2015ExtractPlugin(this.fs, messages, this.localizeName),
makeEs5ExtractPlugin(this.fs, messages, this.localizeName)
],
code: false,
ast: false
});
if (this.useSourceMaps && messages.length > 0) {
this.updateSourceLocations(filename, sourceCode, messages);
}
}
return messages;
}
updateSourceLocations(filename, contents, messages) {
const sourceFile = this.loader.loadSourceFile(this.fs.resolve(this.basePath, filename), contents);
if (sourceFile === null) {
return;
}
for (const message of messages) {
if (message.location !== void 0) {
message.location = this.getOriginalLocation(sourceFile, message.location);
if (message.messagePartLocations) {
message.messagePartLocations = message.messagePartLocations.map((location) => location && this.getOriginalLocation(sourceFile, location));
}
if (message.substitutionLocations) {
const placeholderNames = Object.keys(message.substitutionLocations);
for (const placeholderName of placeholderNames) {
const location = message.substitutionLocations[placeholderName];
message.substitutionLocations[placeholderName] = location && this.getOriginalLocation(sourceFile, location);
}
}
}
}
}
getOriginalLocation(sourceFile, location) {
const originalStart = sourceFile.getOriginalLocation(location.start.line, location.start.column);
if (originalStart === null) {
return location;
}
const originalEnd = sourceFile.getOriginalLocation(location.end.line, location.end.column);
const start = { line: originalStart.line, column: originalStart.column };
const end = originalEnd !== null && originalEnd.file === originalStart.file ? { line: originalEnd.line, column: originalEnd.column } : start;
const originalSourceFile = sourceFile.sources.find((sf) => (sf == null ? void 0 : sf.sourcePath) === originalStart.file);
const startPos = originalSourceFile.startOfLinePositions[start.line] + start.column;
const endPos = originalSourceFile.startOfLinePositions[end.line] + end.column;
const text = originalSourceFile.contents.substring(startPos, endPos).trim();
return { file: originalStart.file, start, end, text };
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/utils.mjs
function consolidateMessages(messages, getMessageId2) {
const messageGroups = /* @__PURE__ */ new Map();
for (const message of messages) {
const id = getMessageId2(message);
if (!messageGroups.has(id)) {
messageGroups.set(id, [message]);
} else {
messageGroups.get(id).push(message);
}
}
for (const messages2 of messageGroups.values()) {
messages2.sort(compareLocations);
}
return Array.from(messageGroups.values()).sort((a1, a2) => compareLocations(a1[0], a2[0]));
}
function hasLocation(message) {
return message.location !== void 0;
}
function compareLocations({ location: location1 }, { location: location2 }) {
if (location1 === location2) {
return 0;
}
if (location1 === void 0) {
return -1;
}
if (location2 === void 0) {
return 1;
}
if (location1.file !== location2.file) {
return location1.file < location2.file ? -1 : 1;
}
if (location1.start.line !== location2.start.line) {
return location1.start.line < location2.start.line ? -1 : 1;
}
if (location1.start.column !== location2.start.column) {
return location1.start.column < location2.start.column ? -1 : 1;
}
return 0;
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/arb_translation_serializer.mjs
var ArbTranslationSerializer = class {
constructor(sourceLocale, basePath, fs) {
this.sourceLocale = sourceLocale;
this.basePath = basePath;
this.fs = fs;
}
serialize(messages) {
const messageGroups = consolidateMessages(messages, (message) => getMessageId(message));
let output = `{
"@@locale": ${JSON.stringify(this.sourceLocale)}`;
for (const duplicateMessages of messageGroups) {
const message = duplicateMessages[0];
const id = getMessageId(message);
output += this.serializeMessage(id, message);
output += this.serializeMeta(id, message.description, message.meaning, duplicateMessages.filter(hasLocation).map((m) => m.location));
}
output += "\n}";
return output;
}
serializeMessage(id, message) {
return `,
${JSON.stringify(id)}: ${JSON.stringify(message.text)}`;
}
serializeMeta(id, description, meaning, locations) {
const meta = [];
if (description) {
meta.push(`
"description": ${JSON.stringify(description)}`);
}
if (meaning) {
meta.push(`
"x-meaning": ${JSON.stringify(meaning)}`);
}
if (locations.length > 0) {
let locationStr = `
"x-locations": [`;
for (let i = 0; i < locations.length; i++) {
locationStr += (i > 0 ? ",\n" : "\n") + this.serializeLocation(locations[i]);
}
locationStr += "\n ]";
meta.push(locationStr);
}
return meta.length > 0 ? `,
${JSON.stringify("@" + id)}: {${meta.join(",")}
}` : "";
}
serializeLocation({ file, start, end }) {
return [
` {`,
` "file": ${JSON.stringify(this.fs.relative(this.basePath, file))},`,
` "start": { "line": "${start.line}", "column": "${start.column}" },`,
` "end": { "line": "${end.line}", "column": "${end.column}" }`,
` }`
].join("\n");
}
};
function getMessageId(message) {
return message.customId || message.id;
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/json_translation_serializer.mjs
var SimpleJsonTranslationSerializer = class {
constructor(sourceLocale) {
this.sourceLocale = sourceLocale;
}
serialize(messages) {
const fileObj = { locale: this.sourceLocale, translations: {} };
for (const [message] of consolidateMessages(messages, (message2) => message2.id)) {
fileObj.translations[message.id] = message.text;
}
return JSON.stringify(fileObj, null, 2);
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/legacy_message_id_migration_serializer.mjs
var LegacyMessageIdMigrationSerializer = class {
constructor(_diagnostics) {
this._diagnostics = _diagnostics;
}
serialize(messages) {
let hasMessages = false;
const mapping = messages.reduce((output, message) => {
if (shouldMigrate(message)) {
for (const legacyId of message.legacyIds) {
if (output.hasOwnProperty(legacyId)) {
this._diagnostics.warn(`Detected duplicate legacy ID ${legacyId}.`);
}
output[legacyId] = message.id;
hasMessages = true;
}
}
return output;
}, {});
if (!hasMessages) {
this._diagnostics.warn("Could not find any legacy message IDs in source files while generating the legacy message migration file.");
}
return JSON.stringify(mapping, null, 2);
}
};
function shouldMigrate(message) {
return !message.customId && !!message.legacyIds && message.legacyIds.length > 0;
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/format_options.mjs
function validateOptions(name, validOptions, options) {
const validOptionsMap = new Map(validOptions);
for (const option in options) {
if (!validOptionsMap.has(option)) {
throw new Error(`Invalid format option for ${name}: "${option}".
Allowed options are ${JSON.stringify(Array.from(validOptionsMap.keys()))}.`);
}
const validOptionValues = validOptionsMap.get(option);
const optionValue = options[option];
if (!validOptionValues.includes(optionValue)) {
throw new Error(`Invalid format option value for ${name}: "${option}".
Allowed option values are ${JSON.stringify(validOptionValues)} but received "${optionValue}".`);
}
}
}
function parseFormatOptions(optionString = "{}") {
return JSON.parse(optionString);
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/xliff1_translation_serializer.mjs
import { getFileSystem } from "@angular/compiler-cli/private/localize";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/icu_parsing.mjs
function extractIcuPlaceholders(text) {
const state = new StateStack();
const pieces = new IcuPieces();
const braces = /[{}]/g;
let lastPos = 0;
let match;
while (match = braces.exec(text)) {
if (match[0] == "{") {
state.enterBlock();
} else {
state.leaveBlock();
}
if (state.getCurrent() === "placeholder") {
const name = tryParsePlaceholder(text, braces.lastIndex);
if (name) {
pieces.addText(text.substring(lastPos, braces.lastIndex - 1));
pieces.addPlaceholder(name);
braces.lastIndex += name.length + 1;
state.leaveBlock();
} else {
pieces.addText(text.substring(lastPos, braces.lastIndex));
state.nestedIcu();
}
} else {
pieces.addText(text.substring(lastPos, braces.lastIndex));
}
lastPos = braces.lastIndex;
}
pieces.addText(text.substring(lastPos));
return pieces.toArray();
}
var IcuPieces = class {
constructor() {
this.pieces = [""];
}
addText(text) {
this.pieces[this.pieces.length - 1] += text;
}
addPlaceholder(name) {
this.pieces.push(name);
this.pieces.push("");
}
toArray() {
return this.pieces;
}
};
var StateStack = class {
constructor() {
this.stack = [];
}
enterBlock() {
const current = this.getCurrent();
switch (current) {
case "icu":
this.stack.push("case");
break;
case "case":
this.stack.push("placeholder");
break;
case "placeholder":
this.stack.push("case");
break;
default:
this.stack.push("icu");
break;
}
}
leaveBlock() {
return this.stack.pop();
}
nestedIcu() {
const current = this.stack.pop();
assert(current === "placeholder", "A nested ICU must replace a placeholder but got " + current);
this.stack.push("icu");
}
getCurrent() {
return this.stack[this.stack.length - 1];
}
};
function tryParsePlaceholder(text, start) {
for (let i = start; i < text.length; i++) {
if (text[i] === ",") {
break;
}
if (text[i] === "}") {
return text.substring(start, i);
}
}
return null;
}
function assert(test, message) {
if (!test) {
throw new Error("Assertion failure: " + message);
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/xml_file.mjs
var XmlFile = class {
constructor() {
this.output = '<?xml version="1.0" encoding="UTF-8" ?>\n';
this.indent = "";
this.elements = [];
this.preservingWhitespace = false;
}
toString() {
return this.output;
}
startTag(name, attributes = {}, { selfClosing = false, preserveWhitespace } = {}) {
if (!this.preservingWhitespace) {
this.output += this.indent;
}
this.output += `<${name}`;
for (const [attrName, attrValue] of Object.entries(attributes)) {
if (attrValue) {
this.output += ` ${attrName}="${escapeXml(attrValue)}"`;
}
}
if (selfClosing) {
this.output += "/>";
} else {
this.output += ">";
this.elements.push(name);
this.incIndent();
}
if (preserveWhitespace !== void 0) {
this.preservingWhitespace = preserveWhitespace;
}
if (!this.preservingWhitespace) {
this.output += `
`;
}
return this;
}
endTag(name, { preserveWhitespace } = {}) {
const expectedTag = this.elements.pop();
if (expectedTag !== name) {
throw new Error(`Unexpected closing tag: "${name}", expected: "${expectedTag}"`);
}
this.decIndent();
if (!this.preservingWhitespace) {
this.output += this.indent;
}
this.output += `</${name}>`;
if (preserveWhitespace !== void 0) {
this.preservingWhitespace = preserveWhitespace;
}
if (!this.preservingWhitespace) {
this.output += `
`;
}
return this;
}
text(str) {
this.output += escapeXml(str);
return this;
}
rawText(str) {
this.output += str;
return this;
}
incIndent() {
this.indent = this.indent + " ";
}
decIndent() {
this.indent = this.indent.slice(0, -2);
}
};
var _ESCAPED_CHARS = [
[/&/g, "&amp;"],
[/"/g, "&quot;"],
[/'/g, "&apos;"],
[/</g, "&lt;"],
[/>/g, "&gt;"]
];
function escapeXml(text) {
return _ESCAPED_CHARS.reduce((text2, entry) => text2.replace(entry[0], entry[1]), text);
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/xliff1_translation_serializer.mjs
var LEGACY_XLIFF_MESSAGE_LENGTH = 40;
var Xliff1TranslationSerializer = class {
constructor(sourceLocale, basePath, useLegacyIds, formatOptions = {}, fs = getFileSystem()) {
this.sourceLocale = sourceLocale;
this.basePath = basePath;
this.useLegacyIds = useLegacyIds;
this.formatOptions = formatOptions;
this.fs = fs;
validateOptions("Xliff1TranslationSerializer", [["xml:space", ["preserve"]]], formatOptions);
}
serialize(messages) {
const messageGroups = consolidateMessages(messages, (message) => this.getMessageId(message));
const xml = new XmlFile();
xml.startTag("xliff", { "version": "1.2", "xmlns": "urn:oasis:names:tc:xliff:document:1.2" });
xml.startTag("file", {
"source-language": this.sourceLocale,
"datatype": "plaintext",
"original": "ng2.template",
...this.formatOptions
});
xml.startTag("body");
for (const duplicateMessages of messageGroups) {
const message = duplicateMessages[0];
const id = this.getMessageId(message);
xml.startTag("trans-unit", { id, datatype: "html" });
xml.startTag("source", {}, { preserveWhitespace: true });
this.serializeMessage(xml, message);
xml.endTag("source", { preserveWhitespace: false });
for (const { location } of duplicateMessages.filter(hasLocation)) {
this.serializeLocation(xml, location);
}
if (message.description) {
this.serializeNote(xml, "description", message.description);
}
if (message.meaning) {
this.serializeNote(xml, "meaning", message.meaning);
}
xml.endTag("trans-unit");
}
xml.endTag("body");
xml.endTag("file");
xml.endTag("xliff");
return xml.toString();
}
serializeMessage(xml, message) {
var _a;
const length = message.messageParts.length - 1;
for (let i = 0; i < length; i++) {
this.serializeTextPart(xml, message.messageParts[i]);
const name = message.placeholderNames[i];
const location = (_a = message.substitutionLocations) == null ? void 0 : _a[name];
const associatedMessageId = message.associatedMessageIds && message.associatedMessageIds[name];
this.serializePlaceholder(xml, name, location == null ? void 0 : location.text, associatedMessageId);
}
this.serializeTextPart(xml, message.messageParts[length]);
}
serializeTextPart(xml, text) {
const pieces = extractIcuPlaceholders(text);
const length = pieces.length - 1;
for (let i = 0; i < length; i += 2) {
xml.text(pieces[i]);
this.serializePlaceholder(xml, pieces[i + 1], void 0, void 0);
}
xml.text(pieces[length]);
}
serializePlaceholder(xml, id, text, associatedId) {
const attrs = { id };
const ctype = getCtypeForPlaceholder(id);
if (ctype !== null) {
attrs.ctype = ctype;
}
if (text !== void 0) {
attrs["equiv-text"] = text;
}
if (associatedId !== void 0) {
attrs["xid"] = associatedId;
}
xml.startTag("x", attrs, { selfClosing: true });
}
serializeNote(xml, name, value) {
xml.startTag("note", { priority: "1", from: name }, { preserveWhitespace: true });
xml.text(value);
xml.endTag("note", { preserveWhitespace: false });
}
serializeLocation(xml, location) {
xml.startTag("context-group", { purpose: "location" });
this.renderContext(xml, "sourcefile", this.fs.relative(this.basePath, location.file));
const endLineString = location.end !== void 0 && location.end.line !== location.start.line ? `,${location.end.line + 1}` : "";
this.renderContext(xml, "linenumber", `${location.start.line + 1}${endLineString}`);
xml.endTag("context-group");
}
renderContext(xml, type, value) {
xml.startTag("context", { "context-type": type }, { preserveWhitespace: true });
xml.text(value);
xml.endTag("context", { preserveWhitespace: false });
}
getMessageId(message) {
return message.customId || this.useLegacyIds && message.legacyIds !== void 0 && message.legacyIds.find((id) => id.length === LEGACY_XLIFF_MESSAGE_LENGTH) || message.id;
}
};
function getCtypeForPlaceholder(placeholder) {
const tag = placeholder.replace(/^(START_|CLOSE_)/, "");
switch (tag) {
case "LINE_BREAK":
return "lb";
case "TAG_IMG":
return "image";
default:
const element = tag.startsWith("TAG_") ? tag.replace(/^TAG_(.+)/, (_, tagName) => tagName.toLowerCase()) : TAG_MAP[tag];
if (element === void 0) {
return null;
}
return `x-${element}`;
}
}
var TAG_MAP = {
"LINK": "a",
"BOLD_TEXT": "b",
"EMPHASISED_TEXT": "em",
"HEADING_LEVEL1": "h1",
"HEADING_LEVEL2": "h2",
"HEADING_LEVEL3": "h3",
"HEADING_LEVEL4": "h4",
"HEADING_LEVEL5": "h5",
"HEADING_LEVEL6": "h6",
"HORIZONTAL_RULE": "hr",
"ITALIC_TEXT": "i",
"LIST_ITEM": "li",
"MEDIA_LINK": "link",
"ORDERED_LIST": "ol",
"PARAGRAPH": "p",
"QUOTATION": "q",
"STRIKETHROUGH_TEXT": "s",
"SMALL_TEXT": "small",
"SUBSTRIPT": "sub",
"SUPERSCRIPT": "sup",
"TABLE_BODY": "tbody",
"TABLE_CELL": "td",
"TABLE_FOOTER": "tfoot",
"TABLE_HEADER_CELL": "th",
"TABLE_HEADER": "thead",
"TABLE_ROW": "tr",
"MONOSPACED_TEXT": "tt",
"UNDERLINED_TEXT": "u",
"UNORDERED_LIST": "ul"
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/xliff2_translation_serializer.mjs
import { getFileSystem as getFileSystem2 } from "@angular/compiler-cli/private/localize";
var MAX_LEGACY_XLIFF_2_MESSAGE_LENGTH = 20;
var Xliff2TranslationSerializer = class {
constructor(sourceLocale, basePath, useLegacyIds, formatOptions = {}, fs = getFileSystem2()) {
this.sourceLocale = sourceLocale;
this.basePath = basePath;
this.useLegacyIds = useLegacyIds;
this.formatOptions = formatOptions;
this.fs = fs;
this.currentPlaceholderId = 0;
validateOptions("Xliff1TranslationSerializer", [["xml:space", ["preserve"]]], formatOptions);
}
serialize(messages) {
const messageGroups = consolidateMessages(messages, (message) => this.getMessageId(message));
const xml = new XmlFile();
xml.startTag("xliff", {
"version": "2.0",
"xmlns": "urn:oasis:names:tc:xliff:document:2.0",
"srcLang": this.sourceLocale
});
xml.startTag("file", { "id": "ngi18n", "original": "ng.template", ...this.formatOptions });
for (const duplicateMessages of messageGroups) {
const message = duplicateMessages[0];
const id = this.getMessageId(message);
xml.startTag("unit", { id });
const messagesWithLocations = duplicateMessages.filter(hasLocation);
if (message.meaning || message.description || messagesWithLocations.length) {
xml.startTag("notes");
for (const { location: { file, start, end } } of messagesWithLocations) {
const endLineString = end !== void 0 && end.line !== start.line ? `,${end.line + 1}` : "";
this.serializeNote(xml, "location", `${this.fs.relative(this.basePath, file)}:${start.line + 1}${endLineString}`);
}
if (message.description) {
this.serializeNote(xml, "description", message.description);
}
if (message.meaning) {
this.serializeNote(xml, "meaning", message.meaning);
}
xml.endTag("notes");
}
xml.startTag("segment");
xml.startTag("source", {}, { preserveWhitespace: true });
this.serializeMessage(xml, message);
xml.endTag("source", { preserveWhitespace: false });
xml.endTag("segment");
xml.endTag("unit");
}
xml.endTag("file");
xml.endTag("xliff");
return xml.toString();
}
serializeMessage(xml, message) {
this.currentPlaceholderId = 0;
const length = message.messageParts.length - 1;
for (let i = 0; i < length; i++) {
this.serializeTextPart(xml, message.messageParts[i]);
const name = message.placeholderNames[i];
const associatedMessageId = message.associatedMessageIds && message.associatedMessageIds[name];
this.serializePlaceholder(xml, name, message.substitutionLocations, associatedMessageId);
}
this.serializeTextPart(xml, message.messageParts[length]);
}
serializeTextPart(xml, text) {
const pieces = extractIcuPlaceholders(text);
const length = pieces.length - 1;
for (let i = 0; i < length; i += 2) {
xml.text(pieces[i]);
this.serializePlaceholder(xml, pieces[i + 1], void 0, void 0);
}
xml.text(pieces[length]);
}
serializePlaceholder(xml, placeholderName, substitutionLocations, associatedMessageId) {
var _a, _b;
const text = (_a = substitutionLocations == null ? void 0 : substitutionLocations[placeholderName]) == null ? void 0 : _a.text;
if (placeholderName.startsWith("START_")) {
const closingPlaceholderName = placeholderName.replace(/^START/, "CLOSE").replace(/_\d+$/, "");
const closingText = (_b = substitutionLocations == null ? void 0 : substitutionLocations[closingPlaceholderName]) == null ? void 0 : _b.text;
const attrs = {
id: `${this.currentPlaceholderId++}`,
equivStart: placeholderName,
equivEnd: closingPlaceholderName
};
const type = getTypeForPlaceholder(placeholderName);
if (type !== null) {
attrs.type = type;
}
if (text !== void 0) {
attrs.dispStart = text;
}
if (closingText !== void 0) {
attrs.dispEnd = closingText;
}
xml.startTag("pc", attrs);
} else if (placeholderName.startsWith("CLOSE_")) {
xml.endTag("pc");
} else {
const attrs = {
id: `${this.currentPlaceholderId++}`,
equiv: placeholderName
};
const type = getTypeForPlaceholder(placeholderName);
if (type !== null) {
attrs.type = type;
}
if (text !== void 0) {
attrs.disp = text;
}
if (associatedMessageId !== void 0) {
attrs["subFlows"] = associatedMessageId;
}
xml.startTag("ph", attrs, { selfClosing: true });
}
}
serializeNote(xml, name, value) {
xml.startTag("note", { category: name }, { preserveWhitespace: true });
xml.text(value);
xml.endTag("note", { preserveWhitespace: false });
}
getMessageId(message) {
return message.customId || this.useLegacyIds && message.legacyIds !== void 0 && message.legacyIds.find((id) => id.length <= MAX_LEGACY_XLIFF_2_MESSAGE_LENGTH && !/[^0-9]/.test(id)) || message.id;
}
};
function getTypeForPlaceholder(placeholder) {
const tag = placeholder.replace(/^(START_|CLOSE_)/, "").replace(/_\d+$/, "");
switch (tag) {
case "BOLD_TEXT":
case "EMPHASISED_TEXT":
case "ITALIC_TEXT":
case "LINE_BREAK":
case "STRIKETHROUGH_TEXT":
case "UNDERLINED_TEXT":
return "fmt";
case "TAG_IMG":
return "image";
case "LINK":
return "link";
default:
return /^(START_|CLOSE_)/.test(placeholder) ? "other" : null;
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/translation_files/xmb_translation_serializer.mjs
import { getFileSystem as getFileSystem3 } from "@angular/compiler-cli/private/localize";
var XmbTranslationSerializer = class {
constructor(basePath, useLegacyIds, fs = getFileSystem3()) {
this.basePath = basePath;
this.useLegacyIds = useLegacyIds;
this.fs = fs;
}
serialize(messages) {
const messageGroups = consolidateMessages(messages, (message) => this.getMessageId(message));
const xml = new XmlFile();
xml.rawText(`<!DOCTYPE messagebundle [
<!ELEMENT messagebundle (msg)*>
<!ATTLIST messagebundle class CDATA #IMPLIED>
<!ELEMENT msg (#PCDATA|ph|source)*>
<!ATTLIST msg id CDATA #IMPLIED>
<!ATTLIST msg seq CDATA #IMPLIED>
<!ATTLIST msg name CDATA #IMPLIED>
<!ATTLIST msg desc CDATA #IMPLIED>
<!ATTLIST msg meaning CDATA #IMPLIED>
<!ATTLIST msg obsolete (obsolete) #IMPLIED>
<!ATTLIST msg xml:space (default|preserve) "default">
<!ATTLIST msg is_hidden CDATA #IMPLIED>
<!ELEMENT source (#PCDATA)>
<!ELEMENT ph (#PCDATA|ex)*>
<!ATTLIST ph name CDATA #REQUIRED>
<!ELEMENT ex (#PCDATA)>
]>
`);
xml.startTag("messagebundle");
for (const duplicateMessages of messageGroups) {
const message = duplicateMessages[0];
const id = this.getMessageId(message);
xml.startTag("msg", { id, desc: message.description, meaning: message.meaning }, { preserveWhitespace: true });
if (message.location) {
this.serializeLocation(xml, message.location);
}
this.serializeMessage(xml, message);
xml.endTag("msg", { preserveWhitespace: false });
}
xml.endTag("messagebundle");
return xml.toString();
}
serializeLocation(xml, location) {
xml.startTag("source");
const endLineString = location.end !== void 0 && location.end.line !== location.start.line ? `,${location.end.line + 1}` : "";
xml.text(`${this.fs.relative(this.basePath, location.file)}:${location.start.line}${endLineString}`);
xml.endTag("source");
}
serializeMessage(xml, message) {
const length = message.messageParts.length - 1;
for (let i = 0; i < length; i++) {
this.serializeTextPart(xml, message.messageParts[i]);
xml.startTag("ph", { name: message.placeholderNames[i] }, { selfClosing: true });
}
this.serializeTextPart(xml, message.messageParts[length]);
}
serializeTextPart(xml, text) {
const pieces = extractIcuPlaceholders(text);
const length = pieces.length - 1;
for (let i = 0; i < length; i += 2) {
xml.text(pieces[i]);
xml.startTag("ph", { name: pieces[i + 1] }, { selfClosing: true });
}
xml.text(pieces[length]);
}
getMessageId(message) {
return message.customId || this.useLegacyIds && message.legacyIds !== void 0 && message.legacyIds.find((id) => id.length <= 20 && !/[^0-9]/.test(id)) || message.id;
}
};
export {
checkDuplicateMessages,
MessageExtractor,
ArbTranslationSerializer,
SimpleJsonTranslationSerializer,
LegacyMessageIdMigrationSerializer,
parseFormatOptions,
Xliff1TranslationSerializer,
Xliff2TranslationSerializer,
XmbTranslationSerializer
};
/**
* @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.io/license
*/
//# sourceMappingURL=chunk-ELQQ25UB.js.map
File diff suppressed because one or more lines are too long
+72
View File
@@ -0,0 +1,72 @@
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
import {
ArbTranslationSerializer,
LegacyMessageIdMigrationSerializer,
MessageExtractor,
SimpleJsonTranslationSerializer,
Xliff1TranslationSerializer,
Xliff2TranslationSerializer,
XmbTranslationSerializer,
checkDuplicateMessages
} from "./chunk-ELQQ25UB.js";
import {
ArbTranslationParser,
SimpleJsonTranslationParser,
Xliff1TranslationParser,
Xliff2TranslationParser,
XtbTranslationParser,
makeEs2015TranslatePlugin,
makeEs5TranslatePlugin,
makeLocalePlugin
} from "./chunk-52W4JLVC.js";
import {
Diagnostics,
buildLocalizeReplacement,
isGlobalIdentifier,
translate,
unwrapExpressionsFromTemplateLiteral,
unwrapMessagePartsFromLocalizeCall,
unwrapMessagePartsFromTemplateLiteral,
unwrapSubstitutionsFromLocalizeCall
} from "./chunk-EE2T5UCZ.js";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/index.mjs
import { NodeJSFileSystem, setFileSystem } from "@angular/compiler-cli/private/localize";
setFileSystem(new NodeJSFileSystem());
export {
ArbTranslationParser,
ArbTranslationSerializer,
Diagnostics,
LegacyMessageIdMigrationSerializer,
MessageExtractor,
SimpleJsonTranslationParser,
SimpleJsonTranslationSerializer,
Xliff1TranslationParser,
Xliff1TranslationSerializer,
Xliff2TranslationParser,
Xliff2TranslationSerializer,
XmbTranslationSerializer,
XtbTranslationParser,
buildLocalizeReplacement,
checkDuplicateMessages,
isGlobalIdentifier,
makeEs2015TranslatePlugin,
makeEs5TranslatePlugin,
makeLocalePlugin,
translate,
unwrapExpressionsFromTemplateLiteral,
unwrapMessagePartsFromLocalizeCall,
unwrapMessagePartsFromTemplateLiteral,
unwrapSubstitutionsFromLocalizeCall
};
/**
* @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.io/license
*/
//# sourceMappingURL=index.js.map
+6
View File
@@ -0,0 +1,6 @@
{
"version": 3,
"sources": ["../../../../../../../packages/localize/tools/index.ts"],
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAWA,SAAQ,kBAAkB,qBAAoB;AAC9C,cAAc,IAAI,iBAAgB,CAAE;",
"names": []
}
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env node
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
import {
ArbTranslationSerializer,
LegacyMessageIdMigrationSerializer,
MessageExtractor,
SimpleJsonTranslationSerializer,
Xliff1TranslationSerializer,
Xliff2TranslationSerializer,
XmbTranslationSerializer,
checkDuplicateMessages,
parseFormatOptions
} from "../../chunk-ELQQ25UB.js";
import "../../chunk-EE2T5UCZ.js";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/cli.mjs
import { ConsoleLogger, LogLevel, NodeJSFileSystem, setFileSystem } from "@angular/compiler-cli/private/localize";
import glob from "glob";
import yargs from "yargs";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/index.mjs
function extractTranslations({ rootPath: rootPath2, sourceFilePaths: sourceFilePaths2, sourceLocale, format: format2, outputPath: output, logger: logger2, useSourceMaps, useLegacyIds, duplicateMessageHandling: duplicateMessageHandling2, formatOptions: formatOptions2 = {}, fileSystem: fs }) {
const basePath = fs.resolve(rootPath2);
const extractor = new MessageExtractor(fs, logger2, { basePath, useSourceMaps });
const messages = [];
for (const file of sourceFilePaths2) {
messages.push(...extractor.extractMessages(file));
}
const diagnostics = checkDuplicateMessages(fs, messages, duplicateMessageHandling2, basePath);
if (diagnostics.hasErrors) {
throw new Error(diagnostics.formatDiagnostics("Failed to extract messages"));
}
const outputPath = fs.resolve(rootPath2, output);
const serializer = getSerializer(format2, sourceLocale, fs.dirname(outputPath), useLegacyIds, formatOptions2, fs, diagnostics);
const translationFile = serializer.serialize(messages);
fs.ensureDir(fs.dirname(outputPath));
fs.writeFile(outputPath, translationFile);
if (diagnostics.messages.length) {
logger2.warn(diagnostics.formatDiagnostics("Messages extracted with warnings"));
}
}
function getSerializer(format2, sourceLocale, rootPath2, useLegacyIds, formatOptions2 = {}, fs, diagnostics) {
switch (format2) {
case "xlf":
case "xlif":
case "xliff":
return new Xliff1TranslationSerializer(sourceLocale, rootPath2, useLegacyIds, formatOptions2, fs);
case "xlf2":
case "xlif2":
case "xliff2":
return new Xliff2TranslationSerializer(sourceLocale, rootPath2, useLegacyIds, formatOptions2, fs);
case "xmb":
return new XmbTranslationSerializer(rootPath2, useLegacyIds, fs);
case "json":
return new SimpleJsonTranslationSerializer(sourceLocale);
case "arb":
return new ArbTranslationSerializer(sourceLocale, rootPath2, fs);
case "legacy-migrate":
return new LegacyMessageIdMigrationSerializer(diagnostics);
}
throw new Error(`No translation serializer can handle the provided format: ${format2}`);
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/extract/cli.mjs
process.title = "Angular Localization Message Extractor (localize-extract)";
var args = process.argv.slice(2);
var options = yargs(args).option("l", {
alias: "locale",
describe: "The locale of the source being processed",
default: "en",
type: "string"
}).option("r", {
alias: "root",
default: ".",
describe: "The root path for other paths provided in these options.\nThis should either be absolute or relative to the current working directory.",
type: "string"
}).option("s", {
alias: "source",
required: true,
describe: "A glob pattern indicating what files to search for translations, e.g. `./dist/**/*.js`.\nThis should be relative to the root path.",
type: "string"
}).option("f", {
alias: "format",
required: true,
choices: ["xmb", "xlf", "xlif", "xliff", "xlf2", "xlif2", "xliff2", "json", "legacy-migrate"],
describe: "The format of the translation file.",
type: "string"
}).option("formatOptions", {
describe: 'Additional options to pass to the translation file serializer, in the form of JSON formatted key-value string pairs:\nFor example: `--formatOptions {"xml:space":"preserve"}.\nThe meaning of the options is specific to the format being serialized.',
type: "string"
}).option("o", {
alias: "outputPath",
required: true,
describe: "A path to where the translation file will be written. This should be relative to the root path.",
type: "string"
}).option("loglevel", {
describe: "The lowest severity logging message that should be output.",
choices: ["debug", "info", "warn", "error"],
type: "string"
}).option("useSourceMaps", {
type: "boolean",
default: true,
describe: "Whether to generate source information in the output files by following source-map mappings found in the source files"
}).option("useLegacyIds", {
type: "boolean",
default: true,
describe: "Whether to use the legacy id format for messages that were extracted from Angular templates."
}).option("d", {
alias: "duplicateMessageHandling",
describe: "How to handle messages with the same id but different text.",
choices: ["error", "warning", "ignore"],
default: "warning",
type: "string"
}).strict().help().parseSync();
var fileSystem = new NodeJSFileSystem();
setFileSystem(fileSystem);
var rootPath = options.r;
var sourceFilePaths = glob.sync(options.s, { cwd: rootPath, nodir: true });
var logLevel = options.loglevel;
var logger = new ConsoleLogger(logLevel ? LogLevel[logLevel] : LogLevel.warn);
var duplicateMessageHandling = options.d;
var formatOptions = parseFormatOptions(options.formatOptions);
var format = options.f;
extractTranslations({
rootPath,
sourceFilePaths,
sourceLocale: options.l,
format,
outputPath: options.o,
logger,
useSourceMaps: options.useSourceMaps,
useLegacyIds: format === "legacy-migrate" || options.useLegacyIds,
duplicateMessageHandling,
formatOptions,
fileSystem
});
/**
* @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.io/license
*/
//# sourceMappingURL=cli.js.map
+6
View File
@@ -0,0 +1,6 @@
{
"version": 3,
"sources": ["../../../../../../../../../packages/localize/tools/src/extract/cli.ts", "../../../../../../../../../packages/localize/tools/src/extract/index.ts"],
"mappings": ";;;;;;;;;;;;;;;;;;;AASA,SAAQ,eAAe,UAAU,kBAAkB,qBAAoB;AACvE,OAAO,UAAU;AACjB,OAAO,WAAW;;;AC+DZ,SAAU,oBAAoB,EAClC,UAAAA,WACA,iBAAAC,kBACA,cACA,QAAAC,SACA,YAAY,QACZ,QAAAC,SACA,eACA,cACA,0BAAAC,2BACA,eAAAC,iBAAgB,CAAA,GAChB,YAAY,GAAE,GACa;AAC3B,QAAM,WAAW,GAAG,QAAQL,SAAQ;AACpC,QAAM,YAAY,IAAI,iBAAiB,IAAIG,SAAQ,EAAC,UAAU,cAAa,CAAC;AAE5E,QAAM,WAA6B,CAAA;AACnC,aAAW,QAAQF,kBAAiB;AAClC,aAAS,KAAK,GAAG,UAAU,gBAAgB,IAAI,CAAC;;AAGlD,QAAM,cAAc,uBAAuB,IAAI,UAAUG,2BAA0B,QAAQ;AAC3F,MAAI,YAAY,WAAW;AACzB,UAAM,IAAI,MAAM,YAAY,kBAAkB,4BAA4B,CAAC;;AAG7E,QAAM,aAAa,GAAG,QAAQJ,WAAU,MAAM;AAC9C,QAAM,aAAa,cACfE,SAAQ,cAAc,GAAG,QAAQ,UAAU,GAAG,cAAcG,gBAAe,IAAI,WAAW;AAC9F,QAAM,kBAAkB,WAAW,UAAU,QAAQ;AACrD,KAAG,UAAU,GAAG,QAAQ,UAAU,CAAC;AACnC,KAAG,UAAU,YAAY,eAAe;AAExC,MAAI,YAAY,SAAS,QAAQ;AAC/B,IAAAF,QAAO,KAAK,YAAY,kBAAkB,kCAAkC,CAAC;;AAEjF;AAEA,SAAS,cACLD,SAAgB,cAAsBF,WAA0B,cAChEK,iBAA+B,CAAA,GAAI,IACnC,aAAwB;AAC1B,UAAQH,SAAQ;IACd,KAAK;IACL,KAAK;IACL,KAAK;AACH,aAAO,IAAI,4BACP,cAAcF,WAAU,cAAcK,gBAAe,EAAE;IAC7D,KAAK;IACL,KAAK;IACL,KAAK;AACH,aAAO,IAAI,4BACP,cAAcL,WAAU,cAAcK,gBAAe,EAAE;IAC7D,KAAK;AACH,aAAO,IAAI,yBAAyBL,WAAU,cAAc,EAAE;IAChE,KAAK;AACH,aAAO,IAAI,gCAAgC,YAAY;IACzD,KAAK;AACH,aAAO,IAAI,yBAAyB,cAAcA,WAAU,EAAE;IAChE,KAAK;AACH,aAAO,IAAI,mCAAmC,WAAW;;AAE7D,QAAM,IAAI,MAAM,6DAA6DE,SAAQ;AACvF;;;ADxHA,QAAQ,QAAQ;AAChB,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UACF,MAAM,IAAI,EACL,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,SAAS;EACT,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,SAAS;EACT,UAAU;EAEV,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,UACI;EAEJ,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,SACI,CAAC,OAAO,OAAO,QAAQ,SAAS,QAAQ,SAAS,UAAU,QAAQ,gBAAgB;EACvF,UAAU;EACV,MAAM;CACP,EACA,OAAO,iBAAiB;EACvB,UACI;EAGJ,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,UACI;EACJ,MAAM;CACP,EACA,OAAO,YAAY;EAClB,UAAU;EACV,SAAS,CAAC,SAAS,QAAQ,QAAQ,OAAO;EAC1C,MAAM;CACP,EACA,OAAO,iBAAiB;EACvB,MAAM;EACN,SAAS;EACT,UACI;CACL,EACA,OAAO,gBAAgB;EACtB,MAAM;EACN,SAAS;EACT,UACI;CACL,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,SAAS,CAAC,SAAS,WAAW,QAAQ;EACtC,SAAS;EACT,MAAM;CACP,EACA,OAAM,EACN,KAAI,EACJ,UAAS;AAElB,IAAM,aAAa,IAAI,iBAAgB;AACvC,cAAc,UAAU;AAExB,IAAM,WAAW,QAAQ;AACzB,IAAM,kBAAkB,KAAK,KAAK,QAAQ,GAAG,EAAC,KAAK,UAAU,OAAO,KAAI,CAAC;AACzE,IAAM,WAAW,QAAQ;AACzB,IAAM,SAAS,IAAI,cAAc,WAAW,SAAS,YAAY,SAAS,IAAI;AAC9E,IAAM,2BAA2B,QAAQ;AACzC,IAAM,gBAAgB,mBAAmB,QAAQ,aAAa;AAC9D,IAAM,SAAS,QAAQ;AAEvB,oBAAoB;EAClB;EACA;EACA,cAAc,QAAQ;EACtB;EACA,YAAY,QAAQ;EACpB;EACA,eAAe,QAAQ;EACvB,cAAc,WAAW,oBAAoB,QAAQ;EACrD;EACA;EACA;CACD;",
"names": ["rootPath", "sourceFilePaths", "format", "logger", "duplicateMessageHandling", "formatOptions"]
}
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env node
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/migrate/cli.mjs
import { ConsoleLogger, LogLevel, NodeJSFileSystem, setFileSystem } from "@angular/compiler-cli/private/localize";
import glob from "glob";
import yargs from "yargs";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/migrate/index.mjs
import { getFileSystem } from "@angular/compiler-cli/private/localize";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/migrate/migrate.mjs
function migrateFile(sourceCode, mapping) {
const legacyIds = Object.keys(mapping);
for (const legacyId of legacyIds) {
const canonicalId = mapping[legacyId];
const pattern = new RegExp(escapeRegExp(legacyId), "g");
sourceCode = sourceCode.replace(pattern, canonicalId);
}
return sourceCode;
}
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1");
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/migrate/index.mjs
function migrateFiles({ rootPath: rootPath2, translationFilePaths: translationFilePaths2, mappingFilePath, logger: logger2 }) {
const fs2 = getFileSystem();
const absoluteMappingPath = fs2.resolve(rootPath2, mappingFilePath);
const mapping = JSON.parse(fs2.readFile(absoluteMappingPath));
if (Object.keys(mapping).length === 0) {
logger2.warn(`Mapping file at ${absoluteMappingPath} is empty. Either there are no messages that need to be migrated, or the extraction step failed to find them.`);
} else {
translationFilePaths2.forEach((path) => {
const absolutePath = fs2.resolve(rootPath2, path);
const sourceCode = fs2.readFile(absolutePath);
fs2.writeFile(absolutePath, migrateFile(sourceCode, mapping));
});
}
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/migrate/cli.mjs
var args = process.argv.slice(2);
var options = yargs(args).option("r", {
alias: "root",
default: ".",
describe: "The root path for other paths provided in these options.\nThis should either be absolute or relative to the current working directory.",
type: "string"
}).option("f", {
alias: "files",
required: true,
describe: "A glob pattern indicating what files to migrate. This should be relative to the root path",
type: "string"
}).option("m", {
alias: "mapFile",
required: true,
describe: "Path to the migration mapping file generated by `localize-extract`. This should be relative to the root path.",
type: "string"
}).strict().help().parseSync();
var fs = new NodeJSFileSystem();
setFileSystem(fs);
var rootPath = options.r;
var translationFilePaths = glob.sync(options.f, { cwd: rootPath, nodir: true });
var logger = new ConsoleLogger(LogLevel.warn);
migrateFiles({ rootPath, translationFilePaths, mappingFilePath: options.m, logger });
process.exit(0);
/**
* @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.io/license
*/
//# sourceMappingURL=cli.js.map
+6
View File
@@ -0,0 +1,6 @@
{
"version": 3,
"sources": ["../../../../../../../../../packages/localize/tools/src/migrate/cli.ts", "../../../../../../../../../packages/localize/tools/src/migrate/index.ts", "../../../../../../../../../packages/localize/tools/src/migrate/migrate.ts"],
"mappings": ";;;;;;;AASA,SAAQ,eAAe,UAAU,kBAAkB,qBAAoB;AACvE,OAAO,UAAU;AACjB,OAAO,WAAW;;;ACHlB,SAAQ,qBAA4B;;;ACM9B,SAAU,YAAY,YAAoB,SAAyB;AACvE,QAAM,YAAY,OAAO,KAAK,OAAO;AAErC,aAAW,YAAY,WAAW;AAChC,UAAM,cAAc,QAAQ;AAC5B,UAAM,UAAU,IAAI,OAAO,aAAa,QAAQ,GAAG,GAAG;AACtD,iBAAa,WAAW,QAAQ,SAAS,WAAW;;AAGtD,SAAO;AACT;AAGA,SAAS,aAAa,KAAW;AAC/B,SAAO,IAAI,QAAQ,8BAA8B,MAAM;AACzD;;;ADAM,SAAU,aAAa,EAC3B,UAAAA,WACA,sBAAAC,uBACA,iBACA,QAAAC,QAAM,GACc;AACpB,QAAMC,MAAK,cAAa;AACxB,QAAM,sBAAsBA,IAAG,QAAQH,WAAU,eAAe;AAChE,QAAM,UAAU,KAAK,MAAMG,IAAG,SAAS,mBAAmB,CAAC;AAE3D,MAAI,OAAO,KAAK,OAAO,EAAE,WAAW,GAAG;AACrC,IAAAD,QAAO,KACH,mBAAmB,kIACoD;SACtE;AACL,IAAAD,sBAAqB,QAAQ,UAAO;AAClC,YAAM,eAAeE,IAAG,QAAQH,WAAU,IAAI;AAC9C,YAAM,aAAaG,IAAG,SAAS,YAAY;AAC3C,MAAAA,IAAG,UAAU,cAAc,YAAY,YAAY,OAAO,CAAC;IAC7D,CAAC;;AAEL;;;ADpCA,IAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,IAAM,UACF,MAAM,IAAI,EACL,OAAO,KAAK;EACX,OAAO;EACP,SAAS;EACT,UAAU;EAEV,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,UACI;EACJ,MAAM;CACP,EACA,OAAO,KAAK;EACX,OAAO;EACP,UAAU;EACV,UACI;EACJ,MAAM;CACP,EACA,OAAM,EACN,KAAI,EACJ,UAAS;AAElB,IAAM,KAAK,IAAI,iBAAgB;AAC/B,cAAc,EAAE;AAEhB,IAAM,WAAW,QAAQ;AACzB,IAAM,uBAAuB,KAAK,KAAK,QAAQ,GAAG,EAAC,KAAK,UAAU,OAAO,KAAI,CAAC;AAC9E,IAAM,SAAS,IAAI,cAAc,SAAS,IAAI;AAE9C,aAAa,EAAC,UAAU,sBAAsB,iBAAiB,QAAQ,GAAG,OAAM,CAAC;AACjF,QAAQ,KAAK,CAAC;",
"names": ["rootPath", "translationFilePaths", "logger", "fs"]
}
+322
View File
@@ -0,0 +1,322 @@
#!/usr/bin/env node
import {createRequire as __cjsCompatRequire} from 'module';
const require = __cjsCompatRequire(import.meta.url);
import {
ArbTranslationParser,
SimpleJsonTranslationParser,
Xliff1TranslationParser,
Xliff2TranslationParser,
XtbTranslationParser,
makeEs2015TranslatePlugin,
makeEs5TranslatePlugin,
makeLocalePlugin
} from "../../chunk-52W4JLVC.js";
import {
Diagnostics,
parseSync,
transformFromAstSync
} from "../../chunk-EE2T5UCZ.js";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/cli.mjs
import { NodeJSFileSystem, setFileSystem } from "@angular/compiler-cli/private/localize";
import glob from "glob";
import yargs from "yargs";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/output_path.mjs
function getOutputPathFn(fs2, outputFolder) {
const [pre, post] = outputFolder.split("{{LOCALE}}");
return post === void 0 ? (_locale, relativePath) => fs2.join(pre, relativePath) : (locale, relativePath) => fs2.join(pre + locale + post, relativePath);
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/index.mjs
import { getFileSystem, relativeFrom } from "@angular/compiler-cli/private/localize";
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/asset_files/asset_translation_handler.mjs
import { absoluteFrom } from "@angular/compiler-cli/private/localize";
var AssetTranslationHandler = class {
constructor(fs2) {
this.fs = fs2;
}
canTranslate(_relativeFilePath, _contents) {
return true;
}
translate(diagnostics2, _sourceRoot, relativeFilePath, contents, outputPathFn2, translations, sourceLocale2) {
for (const translation of translations) {
this.writeAssetFile(diagnostics2, outputPathFn2, translation.locale, relativeFilePath, contents);
}
if (sourceLocale2 !== void 0) {
this.writeAssetFile(diagnostics2, outputPathFn2, sourceLocale2, relativeFilePath, contents);
}
}
writeAssetFile(diagnostics2, outputPathFn2, locale, relativeFilePath, contents) {
try {
const outputPath = absoluteFrom(outputPathFn2(locale, relativeFilePath));
this.fs.ensureDir(this.fs.dirname(outputPath));
this.fs.writeFile(outputPath, contents);
} catch (e) {
diagnostics2.error(e.message);
}
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/source_files/source_file_translation_handler.mjs
import { absoluteFrom as absoluteFrom2 } from "@angular/compiler-cli/private/localize";
var SourceFileTranslationHandler = class {
constructor(fs2, translationOptions = {}) {
this.fs = fs2;
this.translationOptions = translationOptions;
this.sourceLocaleOptions = { ...this.translationOptions, missingTranslation: "ignore" };
}
canTranslate(relativeFilePath, _contents) {
return this.fs.extname(relativeFilePath) === ".js";
}
translate(diagnostics2, sourceRoot, relativeFilePath, contents, outputPathFn2, translations, sourceLocale2) {
const sourceCode = Buffer.from(contents).toString("utf8");
if (!sourceCode.includes("$localize")) {
for (const translation of translations) {
this.writeSourceFile(diagnostics2, outputPathFn2, translation.locale, relativeFilePath, contents);
}
if (sourceLocale2 !== void 0) {
this.writeSourceFile(diagnostics2, outputPathFn2, sourceLocale2, relativeFilePath, contents);
}
} else {
const ast = parseSync(sourceCode, { sourceRoot, filename: relativeFilePath });
if (!ast) {
diagnostics2.error(`Unable to parse source file: ${this.fs.join(sourceRoot, relativeFilePath)}`);
return;
}
for (const translationBundle of translations) {
this.translateFile(diagnostics2, ast, translationBundle, sourceRoot, relativeFilePath, outputPathFn2, this.translationOptions);
}
if (sourceLocale2 !== void 0) {
this.translateFile(diagnostics2, ast, { locale: sourceLocale2, translations: {} }, sourceRoot, relativeFilePath, outputPathFn2, this.sourceLocaleOptions);
}
}
}
translateFile(diagnostics2, ast, translationBundle, sourceRoot, filename, outputPathFn2, options2) {
const translated = transformFromAstSync(ast, void 0, {
compact: true,
generatorOpts: { minified: true },
plugins: [
makeLocalePlugin(translationBundle.locale),
makeEs2015TranslatePlugin(diagnostics2, translationBundle.translations, options2, this.fs),
makeEs5TranslatePlugin(diagnostics2, translationBundle.translations, options2, this.fs)
],
cwd: sourceRoot,
filename
});
if (translated && translated.code) {
this.writeSourceFile(diagnostics2, outputPathFn2, translationBundle.locale, filename, translated.code);
const outputPath = absoluteFrom2(outputPathFn2(translationBundle.locale, filename));
this.fs.ensureDir(this.fs.dirname(outputPath));
this.fs.writeFile(outputPath, translated.code);
} else {
diagnostics2.error(`Unable to translate source file: ${this.fs.join(sourceRoot, filename)}`);
return;
}
}
writeSourceFile(diagnostics2, outputPathFn2, locale, relativeFilePath, contents) {
try {
const outputPath = absoluteFrom2(outputPathFn2(locale, relativeFilePath));
this.fs.ensureDir(this.fs.dirname(outputPath));
this.fs.writeFile(outputPath, contents);
} catch (e) {
diagnostics2.error(e.message);
}
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translation_files/translation_loader.mjs
var TranslationLoader = class {
constructor(fs2, translationParsers, duplicateTranslation2, diagnostics2) {
this.fs = fs2;
this.translationParsers = translationParsers;
this.duplicateTranslation = duplicateTranslation2;
this.diagnostics = diagnostics2;
}
loadBundles(translationFilePaths2, translationFileLocales2) {
return translationFilePaths2.map((filePaths, index) => {
const providedLocale = translationFileLocales2[index];
return this.mergeBundles(filePaths, providedLocale);
});
}
loadBundle(filePath, providedLocale) {
const fileContents = this.fs.readFile(filePath);
const unusedParsers = /* @__PURE__ */ new Map();
for (const translationParser of this.translationParsers) {
const result = translationParser.analyze(filePath, fileContents);
if (!result.canParse) {
unusedParsers.set(translationParser, result);
continue;
}
const { locale: parsedLocale, translations, diagnostics: diagnostics2 } = translationParser.parse(filePath, fileContents, result.hint);
if (diagnostics2.hasErrors) {
throw new Error(diagnostics2.formatDiagnostics(`The translation file "${filePath}" could not be parsed.`));
}
const locale = providedLocale || parsedLocale;
if (locale === void 0) {
throw new Error(`The translation file "${filePath}" does not contain a target locale and no explicit locale was provided for this file.`);
}
if (parsedLocale !== void 0 && providedLocale !== void 0 && parsedLocale !== providedLocale) {
diagnostics2.warn(`The provided locale "${providedLocale}" does not match the target locale "${parsedLocale}" found in the translation file "${filePath}".`);
}
if (this.diagnostics) {
this.diagnostics.merge(diagnostics2);
}
return { locale, translations, diagnostics: diagnostics2 };
}
const diagnosticsMessages = [];
for (const [parser, result] of unusedParsers.entries()) {
diagnosticsMessages.push(result.diagnostics.formatDiagnostics(`
${parser.constructor.name} cannot parse translation file.`));
}
throw new Error(`There is no "TranslationParser" that can parse this translation file: ${filePath}.` + diagnosticsMessages.join("\n"));
}
mergeBundles(filePaths, providedLocale) {
const bundles = filePaths.map((filePath) => this.loadBundle(filePath, providedLocale));
const bundle = bundles[0];
for (let i = 1; i < bundles.length; i++) {
const nextBundle = bundles[i];
if (nextBundle.locale !== bundle.locale) {
if (this.diagnostics) {
const previousFiles = filePaths.slice(0, i).map((f) => `"${f}"`).join(", ");
this.diagnostics.warn(`When merging multiple translation files, the target locale "${nextBundle.locale}" found in "${filePaths[i]}" does not match the target locale "${bundle.locale}" found in earlier files [${previousFiles}].`);
}
}
Object.keys(nextBundle.translations).forEach((messageId) => {
var _a;
if (bundle.translations[messageId] !== void 0) {
(_a = this.diagnostics) == null ? void 0 : _a.add(this.duplicateTranslation, `Duplicate translations for message "${messageId}" when merging "${filePaths[i]}".`);
} else {
bundle.translations[messageId] = nextBundle.translations[messageId];
}
});
}
return bundle;
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/translator.mjs
var Translator = class {
constructor(fs2, resourceHandlers, diagnostics2) {
this.fs = fs2;
this.resourceHandlers = resourceHandlers;
this.diagnostics = diagnostics2;
}
translateFiles(inputPaths, rootPath, outputPathFn2, translations, sourceLocale2) {
inputPaths.forEach((inputPath) => {
const absInputPath = this.fs.resolve(rootPath, inputPath);
const contents = this.fs.readFileBuffer(absInputPath);
const relativePath = this.fs.relative(rootPath, absInputPath);
for (const resourceHandler of this.resourceHandlers) {
if (resourceHandler.canTranslate(relativePath, contents)) {
return resourceHandler.translate(this.diagnostics, rootPath, relativePath, contents, outputPathFn2, translations, sourceLocale2);
}
}
this.diagnostics.error(`Unable to handle resource file: ${inputPath}`);
});
}
};
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/index.mjs
function translateFiles({ sourceRootPath: sourceRootPath2, sourceFilePaths: sourceFilePaths2, translationFilePaths: translationFilePaths2, translationFileLocales: translationFileLocales2, outputPathFn: outputPathFn2, diagnostics: diagnostics2, missingTranslation: missingTranslation2, duplicateTranslation: duplicateTranslation2, sourceLocale: sourceLocale2 }) {
const fs2 = getFileSystem();
const translationLoader = new TranslationLoader(fs2, [
new Xliff2TranslationParser(),
new Xliff1TranslationParser(),
new XtbTranslationParser(),
new SimpleJsonTranslationParser(),
new ArbTranslationParser()
], duplicateTranslation2, diagnostics2);
const resourceProcessor = new Translator(fs2, [
new SourceFileTranslationHandler(fs2, { missingTranslation: missingTranslation2 }),
new AssetTranslationHandler(fs2)
], diagnostics2);
const translationFilePathsArrays = translationFilePaths2.map((filePaths) => Array.isArray(filePaths) ? filePaths.map((p) => fs2.resolve(p)) : [fs2.resolve(filePaths)]);
const translations = translationLoader.loadBundles(translationFilePathsArrays, translationFileLocales2);
sourceRootPath2 = fs2.resolve(sourceRootPath2);
resourceProcessor.translateFiles(sourceFilePaths2.map(relativeFrom), fs2.resolve(sourceRootPath2), outputPathFn2, translations, sourceLocale2);
}
// bazel-out/k8-fastbuild/bin/packages/localize/tools/src/translate/cli.mjs
process.title = "Angular Localization Message Translator (localize-translate)";
var args = process.argv.slice(2);
var options = yargs(args).option("r", {
alias: "root",
required: true,
describe: "The root path of the files to translate, either absolute or relative to the current working directory. E.g. `dist/en`.",
type: "string"
}).option("s", {
alias: "source",
required: true,
describe: "A glob pattern indicating what files to translate, relative to the `root` path. E.g. `bundles/**/*`.",
type: "string"
}).option("l", {
alias: "source-locale",
describe: "The source locale of the application. If this is provided then a copy of the application will be created with no translation but just the `$localize` calls stripped out.",
type: "string"
}).option("t", {
alias: "translations",
required: true,
array: true,
describe: 'A list of paths to the translation files to load, either absolute or relative to the current working directory.\nE.g. `-t src/locale/messages.en.xlf src/locale/messages.fr.xlf src/locale/messages.de.xlf`.\nIf you want to merge multiple translation files for each locale, then provide the list of files in an array.\nNote that the arrays must be in double quotes if you include any whitespace within the array.\nE.g. `-t "[src/locale/messages.en.xlf, src/locale/messages-2.en.xlf]" [src/locale/messages.fr.xlf,src/locale/messages-2.fr.xlf]`',
type: "string"
}).option("target-locales", {
array: true,
describe: 'A list of target locales for the translation files, which will override any target locale parsed from the translation file.\nE.g. "-t en fr de".',
type: "string"
}).option("o", {
alias: "outputPath",
required: true,
describe: "A output path pattern to where the translated files will be written.\nThe path must be either absolute or relative to the current working directory.\nThe marker `{{LOCALE}}` will be replaced with the target locale. E.g. `dist/{{LOCALE}}`.",
type: "string"
}).option("m", {
alias: "missingTranslation",
describe: "How to handle missing translations.",
choices: ["error", "warning", "ignore"],
default: "warning",
type: "string"
}).option("d", {
alias: "duplicateTranslation",
describe: "How to handle duplicate translations.",
choices: ["error", "warning", "ignore"],
default: "warning",
type: "string"
}).strict().help().parseSync();
var fs = new NodeJSFileSystem();
setFileSystem(fs);
var sourceRootPath = options.r;
var sourceFilePaths = glob.sync(options.s, { cwd: sourceRootPath, nodir: true });
var translationFilePaths = convertArraysFromArgs(options.t);
var outputPathFn = getOutputPathFn(fs, fs.resolve(options.o));
var diagnostics = new Diagnostics();
var missingTranslation = options.m;
var duplicateTranslation = options.d;
var sourceLocale = options.l;
var translationFileLocales = options["target-locales"] || [];
translateFiles({
sourceRootPath,
sourceFilePaths,
translationFilePaths,
translationFileLocales,
outputPathFn,
diagnostics,
missingTranslation,
duplicateTranslation,
sourceLocale
});
diagnostics.messages.forEach((m) => console.warn(`${m.type}: ${m.message}`));
process.exit(diagnostics.hasErrors ? 1 : 0);
function convertArraysFromArgs(args2) {
return args2.map((arg) => arg.startsWith("[") && arg.endsWith("]") ? arg.slice(1, -1).split(",").map((arg2) => arg2.trim()) : arg);
}
/**
* @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.io/license
*/
//# sourceMappingURL=cli.js.map
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+26
View File
@@ -0,0 +1,26 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools" />
export { DiagnosticHandlingStrategy, Diagnostics } from './src/diagnostics';
export { checkDuplicateMessages } from './src/extract/duplicates';
export { MessageExtractor } from './src/extract/extraction';
export { ArbTranslationSerializer } from './src/extract/translation_files/arb_translation_serializer';
export { SimpleJsonTranslationSerializer } from './src/extract/translation_files/json_translation_serializer';
export { LegacyMessageIdMigrationSerializer } from './src/extract/translation_files/legacy_message_id_migration_serializer';
export { Xliff1TranslationSerializer } from './src/extract/translation_files/xliff1_translation_serializer';
export { Xliff2TranslationSerializer } from './src/extract/translation_files/xliff2_translation_serializer';
export { XmbTranslationSerializer } from './src/extract/translation_files/xmb_translation_serializer';
export { buildLocalizeReplacement, isGlobalIdentifier, translate, unwrapExpressionsFromTemplateLiteral, unwrapMessagePartsFromLocalizeCall, unwrapMessagePartsFromTemplateLiteral, unwrapSubstitutionsFromLocalizeCall } from './src/source_file_utils';
export { makeEs2015TranslatePlugin } from './src/translate/source_files/es2015_translate_plugin';
export { makeEs5TranslatePlugin } from './src/translate/source_files/es5_translate_plugin';
export { makeLocalePlugin } from './src/translate/source_files/locale_plugin';
export { ArbTranslationParser } from './src/translate/translation_files/translation_parsers/arb_translation_parser';
export { SimpleJsonTranslationParser } from './src/translate/translation_files/translation_parsers/simple_json_translation_parser';
export { Xliff1TranslationParser } from './src/translate/translation_files/translation_parsers/xliff1_translation_parser';
export { Xliff2TranslationParser } from './src/translate/translation_files/translation_parsers/xliff2_translation_parser';
export { XtbTranslationParser } from './src/translate/translation_files/translation_parsers/xtb_translation_parser';
+38
View File
@@ -0,0 +1,38 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/babel_core" />
/**
* This is an interop file allowing for `@babel/core` to be imported in both CommonJS or
* ES module files. The `@babel/core` package needs some special treatment because:
*
* Using a default import does not with CommonJS because the `@babel/core` package does not
* expose a `default` export at runtime (because it sets the `_esModule` property that causes
* TS to not create the necessary interop `default` export). On the other side, when loaded
* as part of an ESM, NodeJS will make all of the exports available as default export.
*
* Using named import bindings (i.e. namespace import or actual named bindings) is not
* working well for ESM because as said before, NodeJS will make all of the exports available
* as the `default` export. Hence ESM that imports CJS, always should use the default import.
*
* There is no solution that would work for both CJS and ESM, so we need to use a custom interop
* that switches between the named exports or the default exports depending on what is available.
* This allows the code to run in both ESM (for production) and CJS (for development).
*
* TODO(devversion): remove this once devmode uses ESM as well.
*/
import * as _babelNamespace from '@babel/core';
import _typesNamespace = _babelNamespace.types;
export import types = _typesNamespace;
export declare type PluginObj = _babelNamespace.PluginObj;
export declare type ConfigAPI = _babelNamespace.ConfigAPI;
export declare type NodePath<T = _babelNamespace.Node> = _babelNamespace.NodePath<T>;
export declare type TransformOptions = _babelNamespace.TransformOptions;
export declare const NodePath: typeof _babelNamespace.NodePath;
export declare const transformSync: typeof _babelNamespace.transformSync;
export declare const parseSync: typeof _babelNamespace.parseSync;
export declare const transformFromAstSync: typeof _babelNamespace.transformFromAstSync;
+30
View File
@@ -0,0 +1,30 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/diagnostics" />
/**
* How to handle potential diagnostics.
*/
export declare type DiagnosticHandlingStrategy = 'error' | 'warning' | 'ignore';
/**
* This class is used to collect and then report warnings and errors that occur during the execution
* of the tools.
*
* @publicApi used by CLI
*/
export declare class Diagnostics {
readonly messages: {
type: 'warning' | 'error';
message: string;
}[];
get hasErrors(): boolean;
add(type: DiagnosticHandlingStrategy, message: string): void;
warn(message: string): void;
error(message: string): void;
merge(other: Diagnostics): void;
formatDiagnostics(message: string): string;
}
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env node
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/extract/cli" />
export {};
+17
View File
@@ -0,0 +1,17 @@
/// <amd-module name="@angular/localize/tools/src/extract/duplicates" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { DiagnosticHandlingStrategy, Diagnostics } from '../diagnostics';
/**
* Check each of the given `messages` to find those that have the same id but different message
* text. Add diagnostics messages for each of these duplicate messages to the given `diagnostics`
* object (as necessary).
*/
export declare function checkDuplicateMessages(fs: PathManipulation, messages: ɵParsedMessage[], duplicateMessageHandling: DiagnosticHandlingStrategy, basePath: AbsoluteFsPath): Diagnostics;
+46
View File
@@ -0,0 +1,46 @@
/// <amd-module name="@angular/localize/tools/src/extract/extraction" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, Logger, ReadonlyFileSystem } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
export interface ExtractionOptions {
basePath: AbsoluteFsPath;
useSourceMaps?: boolean;
localizeName?: string;
}
/**
* Extracts parsed messages from file contents, by parsing the contents as JavaScript
* and looking for occurrences of `$localize` in the source code.
*
* @publicApi used by CLI
*/
export declare class MessageExtractor {
private fs;
private logger;
private basePath;
private useSourceMaps;
private localizeName;
private loader;
constructor(fs: ReadonlyFileSystem, logger: Logger, { basePath, useSourceMaps, localizeName }: ExtractionOptions);
extractMessages(filename: string): ɵParsedMessage[];
/**
* Update the location of each message to point to the source-mapped original source location, if
* available.
*/
private updateSourceLocations;
/**
* Find the original location using source-maps if available.
*
* @param sourceFile The generated `sourceFile` that contains the `location`.
* @param location The location within the generated `sourceFile` that needs mapping.
*
* @returns A new location that refers to the original source location mapped from the given
* `location` in the generated `sourceFile`.
*/
private getOriginalLocation;
}
+61
View File
@@ -0,0 +1,61 @@
/// <amd-module name="@angular/localize/tools/src/extract/index" />
/**
* @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.io/license
*/
import { FileSystem, Logger } from '@angular/compiler-cli/private/localize';
import { DiagnosticHandlingStrategy } from '../diagnostics';
import { FormatOptions } from './translation_files/format_options';
export interface ExtractTranslationsOptions {
/**
* The locale of the source being processed.
*/
sourceLocale: string;
/**
* The base path for other paths provided in these options.
* This should either be absolute or relative to the current working directory.
*/
rootPath: string;
/**
* An array of paths to files to search for translations. These should be relative to the
* rootPath.
*/
sourceFilePaths: string[];
/**
* The format of the translation file.
*/
format: string;
/**
* A path to where the translation file will be written. This should be relative to the rootPath.
*/
outputPath: string;
/**
* The logger to use for diagnostic messages.
*/
logger: Logger;
/**
* Whether to generate source information in the output files by following source-map mappings
* found in the source file.
*/
useSourceMaps: boolean;
/**
* Whether to use the legacy id format for messages that were extracted from Angular templates
*/
useLegacyIds: boolean;
/**
* How to handle messages with the same id but not the same text.
*/
duplicateMessageHandling: DiagnosticHandlingStrategy;
/**
* A collection of formatting options to pass to the translation file serializer.
*/
formatOptions?: FormatOptions;
/**
* The file-system abstraction to use.
*/
fileSystem: FileSystem;
}
export declare function extractTranslations({ rootPath, sourceFilePaths, sourceLocale, format, outputPath: output, logger, useSourceMaps, useLegacyIds, duplicateMessageHandling, formatOptions, fileSystem: fs, }: ExtractTranslationsOptions): void;
@@ -0,0 +1,12 @@
/// <amd-module name="@angular/localize/tools/src/extract/source_files/es2015_extract_plugin" />
/**
* @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.io/license
*/
import { PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { PluginObj } from '../../babel_core';
export declare function makeEs2015ExtractPlugin(fs: PathManipulation, messages: ɵParsedMessage[], localizeName?: string): PluginObj;
@@ -0,0 +1,12 @@
/// <amd-module name="@angular/localize/tools/src/extract/source_files/es5_extract_plugin" />
/**
* @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.io/license
*/
import { PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { PluginObj } from '../../babel_core';
export declare function makeEs5ExtractPlugin(fs: PathManipulation, messages: ɵParsedMessage[], localizeName?: string): PluginObj;
@@ -0,0 +1,46 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/arb_translation_serializer" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { TranslationSerializer } from './translation_serializer';
/**
* A translation serializer that can render JSON formatted as an Application Resource Bundle (ARB).
*
* See https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification
*
* ```
* {
* "@@locale": "en-US",
* "message-id": "Target message string",
* "@message-id": {
* "type": "text",
* "description": "Some description text",
* "x-locations": [
* {
* "start": {"line": 23, "column": 145},
* "end": {"line": 24, "column": 53},
* "file": "some/file.ts"
* },
* ...
* ]
* },
* ...
* }
* ```
*/
export declare class ArbTranslationSerializer implements TranslationSerializer {
private sourceLocale;
private basePath;
private fs;
constructor(sourceLocale: string, basePath: AbsoluteFsPath, fs: PathManipulation);
serialize(messages: ɵParsedMessage[]): string;
private serializeMessage;
private serializeMeta;
private serializeLocation;
}
@@ -0,0 +1,23 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/format_options" />
export declare type FormatOptions = Record<string, string>;
export declare type ValidOption = [key: string, values: string[]];
export declare type ValidOptions = ValidOption[];
/**
* Check that the given `options` are allowed based on the given `validOptions`.
* @param name The name of the serializer that is receiving the options.
* @param validOptions An array of valid options and their allowed values.
* @param options The options to be validated.
*/
export declare function validateOptions(name: string, validOptions: ValidOptions, options: FormatOptions): void;
/**
* Parse the given `optionString` into a collection of `FormatOptions`.
* @param optionString The string to parse.
*/
export declare function parseFormatOptions(optionString?: string): FormatOptions;
@@ -0,0 +1,52 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/icu_parsing" />
/**
* Split the given `text` into an array of "static strings" and ICU "placeholder names".
*
* This is required because ICU expressions in `$localize` tagged messages may contain "dynamic"
* piece (e.g. interpolations or element markers). These markers need to be translated to
* placeholders in extracted translation files. So we must parse ICUs to identify them and separate
* them out so that the translation serializers can render them appropriately.
*
* An example of an ICU with interpolations:
*
* ```
* {VAR_PLURAL, plural, one {{INTERPOLATION}} other {{INTERPOLATION_1} post}}
* ```
*
* In this ICU, `INTERPOLATION` and `INTERPOLATION_1` are actually placeholders that will be
* replaced with dynamic content at runtime.
*
* Such placeholders are identifiable as text wrapped in curly braces, within an ICU case
* expression.
*
* To complicate matters, it is possible for ICUs to be nested indefinitely within each other. In
* such cases, the nested ICU expression appears enclosed in a set of curly braces in the same way
* as a placeholder. The nested ICU expressions can be differentiated from placeholders as they
* contain a comma `,`, which separates the ICU value from the ICU type.
*
* Furthermore, nested ICUs can have placeholders of their own, which need to be extracted.
*
* An example of a nested ICU containing its own placeholders:
*
* ```
* {VAR_SELECT_1, select,
* invoice {Invoice for {INTERPOLATION}}
* payment {{VAR_SELECT, select,
* processor {Payment gateway}
* other {{INTERPOLATION_1}}
* }}
* ```
*
* @param text Text to be broken.
* @returns an array of strings, where
* - even values are static strings (e.g. 0, 2, 4, etc)
* - odd values are placeholder names (e.g. 1, 3, 5, etc)
*/
export declare function extractIcuPlaceholders(text: string): string[];
@@ -0,0 +1,21 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/json_translation_serializer" />
/**
* @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.io/license
*/
import { ɵParsedMessage } from '@angular/localize';
import { TranslationSerializer } from './translation_serializer';
/**
* This is a semi-public bespoke serialization format that is used for testing and sometimes as a
* format for storing translations that will be inlined at runtime.
*
* @see SimpleJsonTranslationParser
*/
export declare class SimpleJsonTranslationSerializer implements TranslationSerializer {
private sourceLocale;
constructor(sourceLocale: string);
serialize(messages: ɵParsedMessage[]): string;
}
@@ -0,0 +1,21 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/legacy_message_id_migration_serializer" />
/**
* @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.io/license
*/
import { ɵParsedMessage as ParsedMessage } from '@angular/localize';
import { Diagnostics } from '../../diagnostics';
import { TranslationSerializer } from './translation_serializer';
/**
* A translation serializer that generates the mapping file for the legacy message ID migration.
* The file is used by the `localize-migrate` script to migrate existing translation files from
* the legacy message IDs to the canonical ones.
*/
export declare class LegacyMessageIdMigrationSerializer implements TranslationSerializer {
private _diagnostics;
constructor(_diagnostics: Diagnostics);
serialize(messages: ParsedMessage[]): string;
}
@@ -0,0 +1,21 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/translation_serializer" />
/**
* @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.io/license
*/
import { ɵParsedMessage } from '@angular/localize';
/**
* Implement this interface to provide a class that can render messages into a translation file.
*/
export interface TranslationSerializer {
/**
* Serialize the contents of a translation file containing the given `messages`.
*
* @param messages The messages to render to the file.
* @returns The contents of the serialized file.
*/
serialize(messages: ɵParsedMessage[]): string;
}
@@ -0,0 +1,32 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/utils" />
/**
* @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.io/license
*/
import { ɵParsedMessage, ɵSourceLocation } from '@angular/localize';
/**
* Consolidate messages into groups that have the same id.
*
* Messages with the same id are grouped together so that we can quickly deduplicate messages when
* rendering into translation files.
*
* To ensure that messages are rendered in a deterministic order:
* - the messages within a group are sorted by location (file path, then start position)
* - the groups are sorted by the location of the first message in the group
*
* @param messages the messages to consolidate.
* @param getMessageId a function that will compute the message id of a message.
* @returns an array of message groups, where each group is an array of messages that have the same
* id.
*/
export declare function consolidateMessages(messages: ɵParsedMessage[], getMessageId: (message: ɵParsedMessage) => string): ɵParsedMessage[][];
/**
* Does the given message have a location property?
*/
export declare function hasLocation(message: ɵParsedMessage): message is ɵParsedMessage & {
location: ɵSourceLocation;
};
export declare function compareLocations({ location: location1 }: ɵParsedMessage, { location: location2 }: ɵParsedMessage): number;
@@ -0,0 +1,50 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/xliff1_translation_serializer" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { FormatOptions } from './format_options';
import { TranslationSerializer } from './translation_serializer';
/**
* A translation serializer that can write XLIFF 1.2 formatted files.
*
* https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
* https://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
*
* @see Xliff1TranslationParser
* @publicApi used by CLI
*/
export declare class Xliff1TranslationSerializer implements TranslationSerializer {
private sourceLocale;
private basePath;
private useLegacyIds;
private formatOptions;
private fs;
constructor(sourceLocale: string, basePath: AbsoluteFsPath, useLegacyIds: boolean, formatOptions?: FormatOptions, fs?: PathManipulation);
serialize(messages: ɵParsedMessage[]): string;
private serializeMessage;
private serializeTextPart;
private serializePlaceholder;
private serializeNote;
private serializeLocation;
private renderContext;
/**
* Get the id for the given `message`.
*
* If there was a custom id provided, use that.
*
* If we have requested legacy message ids, then try to return the appropriate id
* from the list of legacy ids that were extracted.
*
* Otherwise return the canonical message id.
*
* An Xliff 1.2 legacy message id is a hex encoded SHA-1 string, which is 40 characters long. See
* https://csrc.nist.gov/csrc/media/publications/fips/180/4/final/documents/fips180-4-draft-aug2014.pdf
*/
private getMessageId;
}
@@ -0,0 +1,49 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/xliff2_translation_serializer" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { FormatOptions } from './format_options';
import { TranslationSerializer } from './translation_serializer';
/**
* A translation serializer that can write translations in XLIFF 2 format.
*
* https://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html
*
* @see Xliff2TranslationParser
* @publicApi used by CLI
*/
export declare class Xliff2TranslationSerializer implements TranslationSerializer {
private sourceLocale;
private basePath;
private useLegacyIds;
private formatOptions;
private fs;
private currentPlaceholderId;
constructor(sourceLocale: string, basePath: AbsoluteFsPath, useLegacyIds: boolean, formatOptions?: FormatOptions, fs?: PathManipulation);
serialize(messages: ɵParsedMessage[]): string;
private serializeMessage;
private serializeTextPart;
private serializePlaceholder;
private serializeNote;
/**
* Get the id for the given `message`.
*
* If there was a custom id provided, use that.
*
* If we have requested legacy message ids, then try to return the appropriate id
* from the list of legacy ids that were extracted.
*
* Otherwise return the canonical message id.
*
* An Xliff 2.0 legacy message id is a 64 bit number encoded as a decimal string, which will have
* at most 20 digits, since 2^65-1 = 36,893,488,147,419,103,231. This digest is based on:
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
*/
private getMessageId;
}
@@ -0,0 +1,44 @@
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/xmb_translation_serializer" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedMessage } from '@angular/localize';
import { TranslationSerializer } from './translation_serializer';
/**
* A translation serializer that can write files in XMB format.
*
* http://cldr.unicode.org/development/development-process/design-proposals/xmb
*
* @see XmbTranslationParser
* @publicApi used by CLI
*/
export declare class XmbTranslationSerializer implements TranslationSerializer {
private basePath;
private useLegacyIds;
private fs;
constructor(basePath: AbsoluteFsPath, useLegacyIds: boolean, fs?: PathManipulation);
serialize(messages: ɵParsedMessage[]): string;
private serializeLocation;
private serializeMessage;
private serializeTextPart;
/**
* Get the id for the given `message`.
*
* If there was a custom id provided, use that.
*
* If we have requested legacy message ids, then try to return the appropriate id
* from the list of legacy ids that were extracted.
*
* Otherwise return the canonical message id.
*
* An XMB legacy message id is a 64 bit number encoded as a decimal string, which will have
* at most 20 digits, since 2^65-1 = 36,893,488,147,419,103,231. This digest is based on:
* https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/GoogleJsMessageIdGenerator.java
*/
private getMessageId;
}
@@ -0,0 +1,26 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/extract/translation_files/xml_file" />
interface Options {
selfClosing?: boolean;
preserveWhitespace?: boolean;
}
export declare class XmlFile {
private output;
private indent;
private elements;
private preservingWhitespace;
toString(): string;
startTag(name: string, attributes?: Record<string, string | undefined>, { selfClosing, preserveWhitespace }?: Options): this;
endTag(name: string, { preserveWhitespace }?: Options): this;
text(str: string): this;
rawText(str: string): this;
private incIndent;
private decIndent;
}
export {};
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env node
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/migrate/cli" />
export {};
+24
View File
@@ -0,0 +1,24 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/migrate/index" />
import { Logger } from '@angular/compiler-cli/private/localize';
export interface MigrateFilesOptions {
/**
* The base path for other paths provided in these options.
* This should either be absolute or relative to the current working directory.
*/
rootPath: string;
/** Paths to the files that should be migrated. Should be relative to the `rootPath`. */
translationFilePaths: string[];
/** Path to the file containing the message ID mappings. Should be relative to the `rootPath`. */
mappingFilePath: string;
/** Logger to use for diagnostic messages. */
logger: Logger;
}
/** Migrates the legacy message IDs based on the passed in configuration. */
export declare function migrateFiles({ rootPath, translationFilePaths, mappingFilePath, logger, }: MigrateFilesOptions): void;
+14
View File
@@ -0,0 +1,14 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/migrate/migrate" />
/** Mapping between legacy message IDs and their canonical counterparts. */
export declare type MigrationMapping = {
[legacyId: string]: string;
};
/** Migrates the legacy message IDs within a single file. */
export declare function migrateFile(sourceCode: string, mapping: MigrationMapping): string;
+151
View File
@@ -0,0 +1,151 @@
/// <amd-module name="@angular/localize/tools/src/source_file_utils" />
/**
* @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.io/license
*/
import { PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedTranslation, ɵSourceLocation } from '@angular/localize';
import { NodePath } from '@babel/traverse';
import { types as t } from './babel_core';
import { DiagnosticHandlingStrategy, Diagnostics } from './diagnostics';
/**
* Is the given `expression` the global `$localize` identifier?
*
* @param expression The expression to check.
* @param localizeName The configured name of `$localize`.
*/
export declare function isLocalize(expression: NodePath, localizeName: string): expression is NodePath<t.Identifier>;
/**
* Is the given `expression` an identifier with the correct `name`?
*
* @param expression The expression to check.
* @param name The name of the identifier we are looking for.
*/
export declare function isNamedIdentifier(expression: NodePath, name: string): expression is NodePath<t.Identifier>;
/**
* Is the given `identifier` declared globally.
*
* @param identifier The identifier to check.
* @publicApi used by CLI
*/
export declare function isGlobalIdentifier(identifier: NodePath<t.Identifier>): boolean;
/**
* Build a translated expression to replace the call to `$localize`.
* @param messageParts The static parts of the message.
* @param substitutions The expressions to substitute into the message.
* @publicApi used by CLI
*/
export declare function buildLocalizeReplacement(messageParts: TemplateStringsArray, substitutions: readonly t.Expression[]): t.Expression;
/**
* Extract the message parts from the given `call` (to `$localize`).
*
* The message parts will either by the first argument to the `call` or it will be wrapped in call
* to a helper function like `__makeTemplateObject`.
*
* @param call The AST node of the call to process.
* @param fs The file system to use when computing source-map paths. If not provided then it uses
* the "current" FileSystem.
* @publicApi used by CLI
*/
export declare function unwrapMessagePartsFromLocalizeCall(call: NodePath<t.CallExpression>, fs?: PathManipulation): [TemplateStringsArray, (ɵSourceLocation | undefined)[]];
/**
* Parse the localize call expression to extract the arguments that hold the substitution
* expressions.
*
* @param call The AST node of the call to process.
* @param fs The file system to use when computing source-map paths. If not provided then it uses
* the "current" FileSystem.
* @publicApi used by CLI
*/
export declare function unwrapSubstitutionsFromLocalizeCall(call: NodePath<t.CallExpression>, fs?: PathManipulation): [t.Expression[], (ɵSourceLocation | undefined)[]];
/**
* Parse the tagged template literal to extract the message parts.
*
* @param elements The elements of the template literal to process.
* @param fs The file system to use when computing source-map paths. If not provided then it uses
* the "current" FileSystem.
* @publicApi used by CLI
*/
export declare function unwrapMessagePartsFromTemplateLiteral(elements: NodePath<t.TemplateElement>[], fs?: PathManipulation): [
TemplateStringsArray,
(ɵSourceLocation | undefined)[]
];
/**
* Parse the tagged template literal to extract the interpolation expressions.
*
* @param quasi The AST node of the template literal to process.
* @param fs The file system to use when computing source-map paths. If not provided then it uses
* the "current" FileSystem.
* @publicApi used by CLI
*/
export declare function unwrapExpressionsFromTemplateLiteral(quasi: NodePath<t.TemplateLiteral>, fs?: PathManipulation): [t.Expression[], (ɵSourceLocation | undefined)[]];
/**
* Wrap the given `expression` in parentheses if it is a binary expression.
*
* This ensures that this expression is evaluated correctly if it is embedded in another expression.
*
* @param expression The expression to potentially wrap.
*/
export declare function wrapInParensIfNecessary(expression: t.Expression): t.Expression;
/**
* Extract the string values from an `array` of string literals.
*
* @param array The array to unwrap.
* @param fs The file system to use when computing source-map paths. If not provided then it uses
* the "current" FileSystem.
*/
export declare function unwrapStringLiteralArray(array: NodePath<t.Expression>, fs?: PathManipulation): [string[], (ɵSourceLocation | undefined)[]];
/**
* This expression is believed to be a call to a "lazy-load" template object helper function.
* This is expected to be of the form:
*
* ```ts
* function _templateObject() {
* var e = _taggedTemplateLiteral(['cooked string', 'raw string']);
* return _templateObject = function() { return e }, e
* }
* ```
*
* We unwrap this to return the call to `_taggedTemplateLiteral()`.
*
* @param call the call expression to unwrap
* @returns the call expression
*/
export declare function unwrapLazyLoadHelperCall(call: NodePath<t.CallExpression>): NodePath<t.CallExpression>;
/**
* Is the given `node` an array of literal strings?
*
* @param node The node to test.
*/
export declare function isStringLiteralArray(node: t.Node): node is t.Expression & {
elements: t.StringLiteral[];
};
/**
* Are all the given `nodes` expressions?
* @param nodes The nodes to test.
*/
export declare function isArrayOfExpressions(paths: NodePath<t.Node>[]): paths is NodePath<t.Expression>[];
/** Options that affect how the `makeEsXXXTranslatePlugin()` functions work. */
export interface TranslatePluginOptions {
missingTranslation?: DiagnosticHandlingStrategy;
localizeName?: string;
}
/**
* Translate the text of the given message, using the given translations.
*
* Logs as warning if the translation is not available
* @publicApi used by CLI
*/
export declare function translate(diagnostics: Diagnostics, translations: Record<string, ɵParsedTranslation>, messageParts: TemplateStringsArray, substitutions: readonly any[], missingTranslation: DiagnosticHandlingStrategy): [TemplateStringsArray, readonly any[]];
export declare class BabelParseError extends Error {
node: t.Node;
private readonly type;
constructor(node: t.Node, message: string);
}
export declare function isBabelParseError(e: any): e is BabelParseError;
export declare function buildCodeFrameError(fs: PathManipulation, path: NodePath, e: BabelParseError): string;
export declare function getLocation(fs: PathManipulation, startPath: NodePath, endPath?: NodePath): ɵSourceLocation | undefined;
export declare function serializeLocationPosition(location: ɵSourceLocation): string;
@@ -0,0 +1,22 @@
/// <amd-module name="@angular/localize/tools/src/translate/asset_files/asset_translation_handler" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, FileSystem, PathSegment } from '@angular/compiler-cli/private/localize';
import { Diagnostics } from '../../diagnostics';
import { OutputPathFn } from '../output_path';
import { TranslationBundle, TranslationHandler } from '../translator';
/**
* Translate an asset file by simply copying it to the appropriate translation output paths.
*/
export declare class AssetTranslationHandler implements TranslationHandler {
private fs;
constructor(fs: FileSystem);
canTranslate(_relativeFilePath: PathSegment | AbsoluteFsPath, _contents: Uint8Array): boolean;
translate(diagnostics: Diagnostics, _sourceRoot: AbsoluteFsPath, relativeFilePath: PathSegment | AbsoluteFsPath, contents: Uint8Array, outputPathFn: OutputPathFn, translations: TranslationBundle[], sourceLocale?: string): void;
private writeAssetFile;
}
+3
View File
@@ -0,0 +1,3 @@
#!/usr/bin/env node
/// <amd-module name="@angular/localize/tools/src/translate/cli" />
export {};
+67
View File
@@ -0,0 +1,67 @@
/// <amd-module name="@angular/localize/tools/src/translate/index" />
import { DiagnosticHandlingStrategy, Diagnostics } from '../diagnostics';
import { OutputPathFn } from './output_path';
export interface TranslateFilesOptions {
/**
* The root path of the files to translate, either absolute or relative to the current working
* directory. E.g. `dist/en`
*/
sourceRootPath: string;
/**
* The files to translate, relative to the `root` path.
*/
sourceFilePaths: string[];
/**
* An array of paths to the translation files to load, either absolute or relative to the current
* working directory.
*
* For each locale to be translated, there should be an element in `translationFilePaths`.
* Each element is either an absolute path to the translation file, or an array of absolute paths
* to translation files, for that locale.
*
* If the element contains more than one translation file, then the translations are merged.
*
* If allowed by the `duplicateTranslation` property, when more than one translation has the same
* message id, the message from the earlier translation file in the array is used.
*
* For example, if the files are `[app.xlf, lib-1.xlf, lib-2.xlif]` then a message that appears in
* `app.xlf` will override the same message in `lib-1.xlf` or `lib-2.xlf`.
*/
translationFilePaths: (string | string[])[];
/**
* A collection of the target locales for the translation files.
*
* If there is a locale provided in `translationFileLocales` then this is used rather than a
* locale extracted from the file itself.
* If there is neither a provided locale nor a locale parsed from the file, then an error is
* thrown.
* If there are both a provided locale and a locale parsed from the file, and they are not the
* same, then a warning is reported.
*/
translationFileLocales: (string | undefined)[];
/**
* A function that computes the output path of where the translated files will be
* written. The marker `{{LOCALE}}` will be replaced with the target locale. E.g.
* `dist/{{LOCALE}}`.
*/
outputPathFn: OutputPathFn;
/**
* An object that will receive any diagnostics messages due to the processing.
*/
diagnostics: Diagnostics;
/**
* How to handle missing translations.
*/
missingTranslation: DiagnosticHandlingStrategy;
/**
* How to handle duplicate translations.
*/
duplicateTranslation: DiagnosticHandlingStrategy;
/**
* The locale of the source files.
* If this is provided then a copy of the application will be created with no translation but just
* the `$localize` calls stripped out.
*/
sourceLocale?: string;
}
export declare function translateFiles({ sourceRootPath, sourceFilePaths, translationFilePaths, translationFileLocales, outputPathFn, diagnostics, missingTranslation, duplicateTranslation, sourceLocale }: TranslateFilesOptions): void;
+24
View File
@@ -0,0 +1,24 @@
/// <amd-module name="@angular/localize/tools/src/translate/output_path" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathManipulation } from '@angular/compiler-cli/private/localize';
/**
* A function that will return an absolute path to where a file is to be written, given a locale and
* a relative path.
*/
export interface OutputPathFn {
(locale: string, relativePath: string): string;
}
/**
* Create a function that will compute the absolute path to where a translated file should be
* written.
*
* The special `{{LOCALE}}` marker will be replaced with the locale code of the current translation.
* @param outputFolder An absolute path to the folder containing this set of translations.
*/
export declare function getOutputPathFn(fs: PathManipulation, outputFolder: AbsoluteFsPath): OutputPathFn;
@@ -0,0 +1,20 @@
/// <amd-module name="@angular/localize/tools/src/translate/source_files/es2015_translate_plugin" />
/**
* @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.io/license
*/
import { PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedTranslation } from '@angular/localize';
import { PluginObj } from '../../babel_core';
import { Diagnostics } from '../../diagnostics';
import { TranslatePluginOptions } from '../../source_file_utils';
/**
* Create a Babel plugin that can be used to do compile-time translation of `$localize` tagged
* messages.
*
* @publicApi used by CLI
*/
export declare function makeEs2015TranslatePlugin(diagnostics: Diagnostics, translations: Record<string, ɵParsedTranslation>, { missingTranslation, localizeName }?: TranslatePluginOptions, fs?: PathManipulation): PluginObj;
@@ -0,0 +1,20 @@
/// <amd-module name="@angular/localize/tools/src/translate/source_files/es5_translate_plugin" />
/**
* @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.io/license
*/
import { PathManipulation } from '@angular/compiler-cli/private/localize';
import { ɵParsedTranslation } from '@angular/localize';
import { PluginObj } from '../../babel_core';
import { Diagnostics } from '../../diagnostics';
import { TranslatePluginOptions } from '../../source_file_utils';
/**
* Create a Babel plugin that can be used to do compile-time translation of `$localize` tagged
* messages.
*
* @publicApi used by CLI
*/
export declare function makeEs5TranslatePlugin(diagnostics: Diagnostics, translations: Record<string, ɵParsedTranslation>, { missingTranslation, localizeName }?: TranslatePluginOptions, fs?: PathManipulation): PluginObj;
@@ -0,0 +1,24 @@
/// <amd-module name="@angular/localize/tools/src/translate/source_files/locale_plugin" />
/**
* @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.io/license
*/
import { PluginObj } from '../../babel_core';
import { TranslatePluginOptions } from '../../source_file_utils';
/**
* This Babel plugin will replace the following code forms with a string literal containing the
* given `locale`.
*
* * `$localize.locale` -> `"locale"`
* * `typeof $localize !== "undefined" && $localize.locale` -> `"locale"`
* * `xxx && typeof $localize !== "undefined" && $localize.locale` -> `"xxx && locale"`
* * `$localize.locale || default` -> `"locale" || default`
*
* @param locale The name of the locale to inline into the code.
* @param options Additional options including the name of the `$localize` function.
* @publicApi used by CLI
*/
export declare function makeLocalePlugin(locale: string, { localizeName }?: TranslatePluginOptions): PluginObj;
@@ -0,0 +1,27 @@
/// <amd-module name="@angular/localize/tools/src/translate/source_files/source_file_translation_handler" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, FileSystem, PathSegment } from '@angular/compiler-cli/private/localize';
import { Diagnostics } from '../../diagnostics';
import { TranslatePluginOptions } from '../../source_file_utils';
import { OutputPathFn } from '../output_path';
import { TranslationBundle, TranslationHandler } from '../translator';
/**
* Translate a file by inlining all messages tagged by `$localize` with the appropriate translated
* message.
*/
export declare class SourceFileTranslationHandler implements TranslationHandler {
private fs;
private translationOptions;
private sourceLocaleOptions;
constructor(fs: FileSystem, translationOptions?: TranslatePluginOptions);
canTranslate(relativeFilePath: PathSegment | AbsoluteFsPath, _contents: Uint8Array): boolean;
translate(diagnostics: Diagnostics, sourceRoot: AbsoluteFsPath, relativeFilePath: PathSegment, contents: Uint8Array, outputPathFn: OutputPathFn, translations: TranslationBundle[], sourceLocale?: string): void;
private translateFile;
private writeSourceFile;
}
@@ -0,0 +1,22 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/base_visitor" />
/**
* @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.io/license
*/
import { Attribute, Comment, Element, Expansion, ExpansionCase, Text, Visitor } from '@angular/compiler';
/**
* A simple base class for the `Visitor` interface, which is a noop for every method.
*
* Sub-classes only need to override the methods that they care about.
*/
export declare class BaseVisitor implements Visitor {
visitElement(_element: Element, _context: any): any;
visitAttribute(_attribute: Attribute, _context: any): any;
visitText(_text: Text, _context: any): any;
visitComment(_comment: Comment, _context: any): any;
visitExpansion(_expansion: Expansion, _context: any): any;
visitExpansionCase(_expansionCase: ExpansionCase, _context: any): any;
}
@@ -0,0 +1,21 @@
/**
* @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.io/license
*/
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/message_serialization/message_renderer" />
export interface MessageRenderer<T> {
message: T;
startRender(): void;
endRender(): void;
text(text: string): void;
placeholder(name: string, body: string | undefined): void;
startPlaceholder(name: string): void;
closePlaceholder(name: string): void;
startContainer(): void;
closeContainer(): void;
startIcu(): void;
endIcu(): void;
}
@@ -0,0 +1,43 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/message_serialization/message_serializer" />
/**
* @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.io/license
*/
import { Element, Expansion, ExpansionCase, Node, Text } from '@angular/compiler';
import { BaseVisitor } from '../base_visitor';
import { MessageRenderer } from './message_renderer';
export interface MessageSerializerConfig {
inlineElements: string[];
placeholder?: {
elementName: string;
nameAttribute: string;
bodyAttribute?: string;
};
placeholderContainer?: {
elementName: string;
startAttribute: string;
endAttribute: string;
};
}
/**
* This visitor will walk over a set of XML nodes, which represent an i18n message, and serialize
* them into a message object of type `T`.
* The type of the serialized message is controlled by the
*/
export declare class MessageSerializer<T> extends BaseVisitor {
private renderer;
private config;
constructor(renderer: MessageRenderer<T>, config: MessageSerializerConfig);
serialize(nodes: Node[]): T;
visitElement(element: Element): void;
visitText(text: Text): void;
visitExpansion(expansion: Expansion): void;
visitExpansionCase(expansionCase: ExpansionCase): void;
visitContainedNodes(nodes: Node[]): void;
visitPlaceholder(name: string, body: string | undefined): void;
visitPlaceholderContainer(startName: string, children: Node[], closeName: string): void;
private isPlaceholderContainer;
}
@@ -0,0 +1,31 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/message_serialization/target_message_renderer" />
/**
* @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.io/license
*/
import { ɵParsedTranslation } from '@angular/localize';
import { MessageRenderer } from './message_renderer';
/**
* A message renderer that outputs `ɵParsedTranslation` objects.
*/
export declare class TargetMessageRenderer implements MessageRenderer<ɵParsedTranslation> {
private current;
private icuDepth;
get message(): ɵParsedTranslation;
startRender(): void;
endRender(): void;
text(text: string): void;
placeholder(name: string, body: string | undefined): void;
startPlaceholder(name: string): void;
closePlaceholder(name: string): void;
startContainer(): void;
closeContainer(): void;
startIcu(): void;
endIcu(): void;
private normalizePlaceholderName;
private renderPlaceholder;
private storeMessagePart;
}
@@ -0,0 +1,55 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_loader" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, ReadonlyFileSystem } from '@angular/compiler-cli/private/localize';
import { DiagnosticHandlingStrategy, Diagnostics } from '../../diagnostics';
import { TranslationBundle } from '../translator';
import { TranslationParser } from './translation_parsers/translation_parser';
/**
* Use this class to load a collection of translation files from disk.
*/
export declare class TranslationLoader {
private fs;
private translationParsers;
private duplicateTranslation;
/** @deprecated */ private diagnostics?;
constructor(fs: ReadonlyFileSystem, translationParsers: TranslationParser<any>[], duplicateTranslation: DiagnosticHandlingStrategy,
/** @deprecated */ diagnostics?: Diagnostics | undefined);
/**
* Load and parse the translation files into a collection of `TranslationBundles`.
*
* @param translationFilePaths An array, per locale, of absolute paths to translation files.
*
* For each locale to be translated, there is an element in `translationFilePaths`. Each element
* is an array of absolute paths to translation files for that locale.
* If the array contains more than one translation file, then the translations are merged.
* If allowed by the `duplicateTranslation` property, when more than one translation has the same
* message id, the message from the earlier translation file in the array is used.
* For example, if the files are `[app.xlf, lib-1.xlf, lib-2.xlif]` then a message that appears in
* `app.xlf` will override the same message in `lib-1.xlf` or `lib-2.xlf`.
*
* @param translationFileLocales An array of locales for each of the translation files.
*
* If there is a locale provided in `translationFileLocales` then this is used rather than a
* locale extracted from the file itself.
* If there is neither a provided locale nor a locale parsed from the file, then an error is
* thrown.
* If there are both a provided locale and a locale parsed from the file, and they are not the
* same, then a warning is reported.
*/
loadBundles(translationFilePaths: AbsoluteFsPath[][], translationFileLocales: (string | undefined)[]): TranslationBundle[];
/**
* Load all the translations from the file at the given `filePath`.
*/
private loadBundle;
/**
* There is more than one `filePath` for this locale, so load each as a bundle and then merge
* them all together.
*/
private mergeBundles;
}
@@ -0,0 +1,59 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/arb_translation_parser" />
/**
* @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.io/license
*/
import { MessageId, ɵSourceMessage } from '@angular/localize';
import { ParseAnalysis, ParsedTranslationBundle, TranslationParser } from './translation_parser';
export interface ArbJsonObject extends Record<MessageId, ɵSourceMessage | ArbMetadata> {
'@@locale': string;
}
export interface ArbMetadata {
type?: 'text' | 'image' | 'css';
description?: string;
['x-locations']?: ArbLocation[];
}
export interface ArbLocation {
start: {
line: number;
column: number;
};
end: {
line: number;
column: number;
};
file: string;
}
/**
* A translation parser that can parse JSON formatted as an Application Resource Bundle (ARB).
*
* See https://github.com/google/app-resource-bundle/wiki/ApplicationResourceBundleSpecification
*
* ```
* {
* "@@locale": "en-US",
* "message-id": "Target message string",
* "@message-id": {
* "type": "text",
* "description": "Some description text",
* "x-locations": [
* {
* "start": {"line": 23, "column": 145},
* "end": {"line": 24, "column": 53},
* "file": "some/file.ts"
* },
* ...
* ]
* },
* ...
* }
* ```
*/
export declare class ArbTranslationParser implements TranslationParser<ArbJsonObject> {
analyze(_filePath: string, contents: string): ParseAnalysis<ArbJsonObject>;
parse(_filePath: string, contents: string, arb?: ArbJsonObject): ParsedTranslationBundle;
private tryParseArbFormat;
}
@@ -0,0 +1,19 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/serialize_translation_message" />
/**
* @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.io/license
*/
import { Element, ParseError } from '@angular/compiler';
import { ɵParsedTranslation } from '@angular/localize';
import { MessageSerializerConfig } from '../message_serialization/message_serializer';
/**
* Serialize the given `element` into a parsed translation using the given `serializer`.
*/
export declare function serializeTranslationMessage(element: Element, config: MessageSerializerConfig): {
translation: ɵParsedTranslation | null;
parseErrors: ParseError[];
serializeErrors: ParseError[];
};
@@ -0,0 +1,29 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/simple_json_translation_parser" />
import { ParseAnalysis, ParsedTranslationBundle, TranslationParser } from './translation_parser';
interface SimpleJsonFile {
locale: string;
translations: {
[messageId: string]: string;
};
}
/**
* A translation parser that can parse JSON that has the form:
*
* ```
* {
* "locale": "...",
* "translations": {
* "message-id": "Target message string",
* ...
* }
* }
* ```
*
* @see SimpleJsonTranslationSerializer
* @publicApi used by CLI
*/
export declare class SimpleJsonTranslationParser implements TranslationParser<SimpleJsonFile> {
analyze(filePath: string, contents: string): ParseAnalysis<SimpleJsonFile>;
parse(_filePath: string, contents: string, json?: SimpleJsonFile): ParsedTranslationBundle;
}
export {};
@@ -0,0 +1,18 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/translation_parse_error" />
/**
* @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.io/license
*/
import { ParseErrorLevel, ParseSourceSpan } from '@angular/compiler';
/**
* This error is thrown when there is a problem parsing a translation file.
*/
export declare class TranslationParseError extends Error {
span: ParseSourceSpan;
msg: string;
level: ParseErrorLevel;
constructor(span: ParseSourceSpan, msg: string, level?: ParseErrorLevel);
}
@@ -0,0 +1,80 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/translation_parser" />
/**
* @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.io/license
*/
import { MessageId, ɵParsedTranslation } from '@angular/localize';
import { Diagnostics } from '../../../diagnostics';
/**
* Indicates that a parser can parse a given file, with a hint that can be used to speed up actual
* parsing.
*/
export interface CanParseAnalysis<Hint> {
canParse: true;
diagnostics: Diagnostics;
hint: Hint;
}
/**
* Indicates that a parser cannot parse a given file with diagnostics as why this is.
* */
export interface CannotParseAnalysis {
canParse: false;
diagnostics: Diagnostics;
}
/**
* Information about whether a `TranslationParser` can parse a given file.
*/
export declare type ParseAnalysis<Hint> = CanParseAnalysis<Hint> | CannotParseAnalysis;
/**
* An object that holds translations that have been parsed from a translation file.
*/
export interface ParsedTranslationBundle {
locale: string | undefined;
translations: Record<MessageId, ɵParsedTranslation>;
diagnostics: Diagnostics;
}
/**
* Implement this interface to provide a class that can parse the contents of a translation file.
*
* The `analyze()` method can return a hint that can be used by the `parse()` method to speed
* up parsing. This allows the parser to do significant work to determine if the file can be parsed
* without duplicating the work when it comes to actually parsing the file.
*
* Example usage:
*
* ```
* const parser: TranslationParser = getParser();
* const analysis = parser.analyze(filePath, content);
* if (analysis.canParse) {
* return parser.parse(filePath, content, analysis.hint);
* }
* ```
*/
export interface TranslationParser<Hint = true> {
/**
* Analyze the file to see if this parser can parse the given file.
*
* @param filePath The absolute path to the translation file.
* @param contents The contents of the translation file.
* @returns Information indicating whether the file can be parsed by this parser.
*/
analyze(filePath: string, contents: string): ParseAnalysis<Hint>;
/**
* Parses the given file, extracting the target locale and translations.
*
* Note that this method should not throw an error. Check the `bundle.diagnostics` property for
* potential parsing errors and warnings.
*
* @param filePath The absolute path to the translation file.
* @param contents The contents of the translation file.
* @param hint A value that can be used by the parser to speed up parsing of the file. This will
* have been provided as the return result from calling `analyze()`.
* @returns The translation bundle parsed from the file.
* @throws No errors. If there was a problem with parsing the bundle will contain errors
* in the `diagnostics` property.
*/
parse(filePath: string, contents: string, hint: Hint): ParsedTranslationBundle;
}
@@ -0,0 +1,65 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/translation_utils" />
/**
* @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.io/license
*/
import { Element, Node, ParseError, ParseErrorLevel, ParseSourceSpan, ParseTreeResult } from '@angular/compiler';
import { Diagnostics } from '../../../diagnostics';
import { ParseAnalysis, ParsedTranslationBundle } from './translation_parser';
export declare function getAttrOrThrow(element: Element, attrName: string): string;
export declare function getAttribute(element: Element, attrName: string): string | undefined;
/**
* Parse the "contents" of an XML element.
*
* This would be equivalent to parsing the `innerHTML` string of an HTML document.
*
* @param element The element whose inner range we want to parse.
* @returns a collection of XML `Node` objects and any errors that were parsed from the element's
* contents.
*/
export declare function parseInnerRange(element: Element): ParseTreeResult;
/**
* This "hint" object is used to pass information from `analyze()` to `parse()` for
* `TranslationParser`s that expect XML contents.
*
* This saves the `parse()` method from having to re-parse the XML.
*/
export interface XmlTranslationParserHint {
element: Element;
errors: ParseError[];
}
/**
* Can this XML be parsed for translations, given the expected `rootNodeName` and expected root node
* `attributes` that should appear in the file.
*
* @param filePath The path to the file being checked.
* @param contents The contents of the file being checked.
* @param rootNodeName The expected name of an XML root node that should exist.
* @param attributes The attributes (and their values) that should appear on the root node.
* @returns The `XmlTranslationParserHint` object for use by `TranslationParser.parse()` if the XML
* document has the expected format.
*/
export declare function canParseXml(filePath: string, contents: string, rootNodeName: string, attributes: Record<string, string>): ParseAnalysis<XmlTranslationParserHint>;
/**
* Create a predicate, which can be used by things like `Array.filter()`, that will match a named
* XML Element from a collection of XML Nodes.
*
* @param name The expected name of the element to match.
*/
export declare function isNamedElement(name: string): (node: Node) => node is Element;
/**
* Add an XML parser related message to the given `diagnostics` object.
*/
export declare function addParseDiagnostic(diagnostics: Diagnostics, sourceSpan: ParseSourceSpan, message: string, level: ParseErrorLevel): void;
/**
* Copy the formatted error message from the given `parseError` object into the given `diagnostics`
* object.
*/
export declare function addParseError(diagnostics: Diagnostics, parseError: ParseError): void;
/**
* Add the provided `errors` to the `bundle` diagnostics.
*/
export declare function addErrorsToBundle(bundle: ParsedTranslationBundle, errors: ParseError[]): void;
@@ -0,0 +1,17 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/xliff1_translation_parser" />
import { ParseAnalysis, ParsedTranslationBundle, TranslationParser } from './translation_parser';
import { XmlTranslationParserHint } from './translation_utils';
/**
* A translation parser that can load XLIFF 1.2 files.
*
* https://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
* https://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
*
* @see Xliff1TranslationSerializer
* @publicApi used by CLI
*/
export declare class Xliff1TranslationParser implements TranslationParser<XmlTranslationParserHint> {
analyze(filePath: string, contents: string): ParseAnalysis<XmlTranslationParserHint>;
parse(filePath: string, contents: string, hint: XmlTranslationParserHint): ParsedTranslationBundle;
private extractBundle;
}
@@ -0,0 +1,16 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/xliff2_translation_parser" />
import { ParseAnalysis, ParsedTranslationBundle, TranslationParser } from './translation_parser';
import { XmlTranslationParserHint } from './translation_utils';
/**
* A translation parser that can load translations from XLIFF 2 files.
*
* https://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html
*
* @see Xliff2TranslationSerializer
* @publicApi used by CLI
*/
export declare class Xliff2TranslationParser implements TranslationParser<XmlTranslationParserHint> {
analyze(filePath: string, contents: string): ParseAnalysis<XmlTranslationParserHint>;
parse(filePath: string, contents: string, hint: XmlTranslationParserHint): ParsedTranslationBundle;
private extractBundle;
}
@@ -0,0 +1,16 @@
/// <amd-module name="@angular/localize/tools/src/translate/translation_files/translation_parsers/xtb_translation_parser" />
import { ParseAnalysis, ParsedTranslationBundle, TranslationParser } from './translation_parser';
import { XmlTranslationParserHint } from './translation_utils';
/**
* A translation parser that can load XTB files.
*
* http://cldr.unicode.org/development/development-process/design-proposals/xmb
*
* @see XmbTranslationSerializer
* @publicApi used by CLI
*/
export declare class XtbTranslationParser implements TranslationParser<XmlTranslationParserHint> {
analyze(filePath: string, contents: string): ParseAnalysis<XmlTranslationParserHint>;
parse(filePath: string, contents: string, hint: XmlTranslationParserHint): ParsedTranslationBundle;
private extractBundle;
}
+63
View File
@@ -0,0 +1,63 @@
/// <amd-module name="@angular/localize/tools/src/translate/translator" />
/**
* @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.io/license
*/
import { AbsoluteFsPath, PathSegment, ReadonlyFileSystem } from '@angular/compiler-cli/private/localize';
import { MessageId, ɵParsedTranslation } from '@angular/localize';
import { Diagnostics } from '../diagnostics';
import { OutputPathFn } from './output_path';
/**
* An object that holds information to be used to translate files.
*/
export interface TranslationBundle {
locale: string;
translations: Record<MessageId, ɵParsedTranslation>;
diagnostics?: Diagnostics;
}
/**
* Implement this interface to provide a class that can handle translation for the given resource in
* an appropriate manner.
*
* For example, source code files will need to be transformed if they contain `$localize` tagged
* template strings, while most static assets will just need to be copied.
*/
export interface TranslationHandler {
/**
* Returns true if the given file can be translated by this handler.
*
* @param relativeFilePath A relative path from the sourceRoot to the resource file to handle.
* @param contents The contents of the file to handle.
*/
canTranslate(relativeFilePath: PathSegment | AbsoluteFsPath, contents: Uint8Array): boolean;
/**
* Translate the file at `relativeFilePath` containing `contents`, using the given `translations`,
* and write the translated content to the path computed by calling `outputPathFn()`.
*
* @param diagnostics An object for collecting translation diagnostic messages.
* @param sourceRoot An absolute path to the root of the files being translated.
* @param relativeFilePath A relative path from the sourceRoot to the file to translate.
* @param contents The contents of the file to translate.
* @param outputPathFn A function that returns an absolute path where the output file should be
* written.
* @param translations A collection of translations to apply to this file.
* @param sourceLocale The locale of the original application source. If provided then an
* additional copy of the application is created under this locale just with the `$localize` calls
* stripped out.
*/
translate(diagnostics: Diagnostics, sourceRoot: AbsoluteFsPath, relativeFilePath: PathSegment | AbsoluteFsPath, contents: Uint8Array, outputPathFn: OutputPathFn, translations: TranslationBundle[], sourceLocale?: string): void;
}
/**
* Translate each file (e.g. source file or static asset) using the given `TranslationHandler`s.
* The file will be translated by the first handler that returns true for `canTranslate()`.
*/
export declare class Translator {
private fs;
private resourceHandlers;
private diagnostics;
constructor(fs: ReadonlyFileSystem, resourceHandlers: TranslationHandler[], diagnostics: Diagnostics);
translateFiles(inputPaths: PathSegment[], rootPath: AbsoluteFsPath, outputPathFn: OutputPathFn, translations: TranslationBundle[], sourceLocale?: string): void;
}