UNPKG

@angular/localize

Version:

Angular - library for localizing messages

625 lines (614 loc) 21.9 kB
import {createRequire as __cjsCompatRequire} from 'module'; const require = __cjsCompatRequire(import.meta.url); // bazel-out/k8-fastbuild/bin/packages/localize/tools/src/diagnostics.js var Diagnostics = class { 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.js import { getFileSystem } from "@angular/compiler-cli/private/localize"; // bazel-out/k8-fastbuild/bin/packages/localize/src/utils/src/constants.js var BLOCK_MARKER = ":"; var MEANING_SEPARATOR = "|"; var ID_SEPARATOR = "@@"; var LEGACY_ID_INDICATOR = "\u241F"; // bazel-out/k8-fastbuild/bin/packages/compiler/src/i18n/digest.js var textEncoder; var _SerializerVisitor = class { visitText(text, context) { return text.value; } visitContainer(container, context) { return `[${container.children.map((child) => child.visit(this)).join(", ")}]`; } visitIcu(icu, context) { const strCases = Object.keys(icu.cases).map((k) => `${k} {${icu.cases[k].visit(this)}}`); return `{${icu.expression}, ${icu.type}, ${strCases.join(", ")}}`; } visitTagPlaceholder(ph, context) { return ph.isVoid ? `<ph tag name="${ph.startName}"/>` : `<ph tag name="${ph.startName}">${ph.children.map((child) => child.visit(this)).join(", ")}</ph name="${ph.closeName}">`; } visitPlaceholder(ph, context) { return ph.value ? `<ph name="${ph.name}">${ph.value}</ph>` : `<ph name="${ph.name}"/>`; } visitIcuPlaceholder(ph, context) { return `<ph icu name="${ph.name}">${ph.value.visit(this)}</ph>`; } visitBlockPlaceholder(ph, context) { return `<ph block name="${ph.startName}">${ph.children.map((child) => child.visit(this)).join(", ")}</ph name="${ph.closeName}">`; } }; var serializerVisitor = new _SerializerVisitor(); function fingerprint(str) { textEncoder ??= new TextEncoder(); const utf8 = textEncoder.encode(str); const view = new DataView(utf8.buffer, utf8.byteOffset, utf8.byteLength); let hi = hash32(view, utf8.length, 0); let lo = hash32(view, utf8.length, 102072); if (hi == 0 && (lo == 0 || lo == 1)) { hi = hi ^ 319790063; lo = lo ^ -1801410264; } return BigInt.asUintN(32, BigInt(hi)) << BigInt(32) | BigInt.asUintN(32, BigInt(lo)); } function computeMsgId(msg, meaning = "") { let msgFingerprint = fingerprint(msg); if (meaning) { msgFingerprint = BigInt.asUintN(64, msgFingerprint << BigInt(1)) | msgFingerprint >> BigInt(63) & BigInt(1); msgFingerprint += fingerprint(meaning); } return BigInt.asUintN(63, msgFingerprint).toString(); } function hash32(view, length, c) { let a = 2654435769, b = 2654435769; let index = 0; const end = length - 12; for (; index <= end; index += 12) { a += view.getUint32(index, true); b += view.getUint32(index + 4, true); c += view.getUint32(index + 8, true); const res = mix(a, b, c); a = res[0], b = res[1], c = res[2]; } const remainder = length - index; c += length; if (remainder >= 4) { a += view.getUint32(index, true); index += 4; if (remainder >= 8) { b += view.getUint32(index, true); index += 4; if (remainder >= 9) { c += view.getUint8(index++) << 8; } if (remainder >= 10) { c += view.getUint8(index++) << 16; } if (remainder === 11) { c += view.getUint8(index++) << 24; } } else { if (remainder >= 5) { b += view.getUint8(index++); } if (remainder >= 6) { b += view.getUint8(index++) << 8; } if (remainder === 7) { b += view.getUint8(index++) << 16; } } } else { if (remainder >= 1) { a += view.getUint8(index++); } if (remainder >= 2) { a += view.getUint8(index++) << 8; } if (remainder === 3) { a += view.getUint8(index++) << 16; } } return mix(a, b, c)[2]; } function mix(a, b, c) { a -= b; a -= c; a ^= c >>> 13; b -= c; b -= a; b ^= a << 8; c -= a; c -= b; c ^= b >>> 13; a -= b; a -= c; a ^= c >>> 12; b -= c; b -= a; b ^= a << 16; c -= a; c -= b; c ^= b >>> 5; a -= b; a -= c; a ^= c >>> 3; b -= c; b -= a; b ^= a << 10; c -= a; c -= b; c ^= b >>> 15; return [a, b, c]; } var Endian; (function(Endian2) { Endian2[Endian2["Little"] = 0] = "Little"; Endian2[Endian2["Big"] = 1] = "Big"; })(Endian || (Endian = {})); // bazel-out/k8-fastbuild/bin/packages/localize/src/utils/src/messages.js function parseMessage(messageParts, expressions, location, messagePartLocations, expressionLocations = []) { const substitutions = {}; const substitutionLocations = {}; const associatedMessageIds = {}; const metadata = parseMetadata(messageParts[0], messageParts.raw[0]); const cleanedMessageParts = [metadata.text]; const placeholderNames = []; let messageString = metadata.text; for (let i = 1; i < messageParts.length; i++) { const { messagePart, placeholderName = computePlaceholderName(i), associatedMessageId } = parsePlaceholder(messageParts[i], messageParts.raw[i]); messageString += `{$${placeholderName}}${messagePart}`; if (expressions !== void 0) { substitutions[placeholderName] = expressions[i - 1]; substitutionLocations[placeholderName] = expressionLocations[i - 1]; } placeholderNames.push(placeholderName); if (associatedMessageId !== void 0) { associatedMessageIds[placeholderName] = associatedMessageId; } cleanedMessageParts.push(messagePart); } const messageId = metadata.customId || computeMsgId(messageString, metadata.meaning || ""); const legacyIds = metadata.legacyIds ? metadata.legacyIds.filter((id) => id !== messageId) : []; return { id: messageId, legacyIds, substitutions, substitutionLocations, text: messageString, customId: metadata.customId, meaning: metadata.meaning || "", description: metadata.description || "", messageParts: cleanedMessageParts, messagePartLocations, placeholderNames, associatedMessageIds, location }; } function parseMetadata(cooked, raw) { const { text: messageString, block } = splitBlock(cooked, raw); if (block === void 0) { return { text: messageString }; } else { const [meaningDescAndId, ...legacyIds] = block.split(LEGACY_ID_INDICATOR); const [meaningAndDesc, customId] = meaningDescAndId.split(ID_SEPARATOR, 2); let [meaning, description] = meaningAndDesc.split(MEANING_SEPARATOR, 2); if (description === void 0) { description = meaning; meaning = void 0; } if (description === "") { description = void 0; } return { text: messageString, meaning, description, customId, legacyIds }; } } function parsePlaceholder(cooked, raw) { const { text: messagePart, block } = splitBlock(cooked, raw); if (block === void 0) { return { messagePart }; } else { const [placeholderName, associatedMessageId] = block.split(ID_SEPARATOR); return { messagePart, placeholderName, associatedMessageId }; } } function splitBlock(cooked, raw) { if (raw.charAt(0) !== BLOCK_MARKER) { return { text: cooked }; } else { const endOfBlock = findEndOfBlock(cooked, raw); return { block: cooked.substring(1, endOfBlock), text: cooked.substring(endOfBlock + 1) }; } } function computePlaceholderName(index) { return index === 1 ? "PH" : `PH_${index - 1}`; } function findEndOfBlock(cooked, raw) { for (let cookedIndex = 1, rawIndex = 1; cookedIndex < cooked.length; cookedIndex++, rawIndex++) { if (raw[rawIndex] === "\\") { rawIndex++; } else if (cooked[cookedIndex] === BLOCK_MARKER) { return cookedIndex; } } throw new Error(`Unterminated $localize metadata block in "${raw}".`); } // bazel-out/k8-fastbuild/bin/packages/localize/src/utils/src/translations.js var MissingTranslationError = class extends Error { parsedMessage; type = "MissingTranslationError"; constructor(parsedMessage) { super(`No translation found for ${describeMessage(parsedMessage)}.`); this.parsedMessage = parsedMessage; } }; function isMissingTranslationError(e) { return e.type === "MissingTranslationError"; } function translate(translations, messageParts, substitutions) { const message = parseMessage(messageParts, substitutions); let translation = translations[message.id]; if (message.legacyIds !== void 0) { for (let i = 0; i < message.legacyIds.length && translation === void 0; i++) { translation = translations[message.legacyIds[i]]; } } if (translation === void 0) { throw new MissingTranslationError(message); } return [ translation.messageParts, translation.placeholderNames.map((placeholder) => { if (message.substitutions.hasOwnProperty(placeholder)) { return message.substitutions[placeholder]; } else { throw new Error(`There is a placeholder name mismatch with the translation provided for the message ${describeMessage(message)}. The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`); } }) ]; } function parseTranslation(messageString) { const parts = messageString.split(/{\$([^}]*)}/); const messageParts = [parts[0]]; const placeholderNames = []; for (let i = 1; i < parts.length - 1; i += 2) { placeholderNames.push(parts[i]); messageParts.push(`${parts[i + 1]}`); } const rawMessageParts = messageParts.map((part) => part.charAt(0) === BLOCK_MARKER ? "\\" + part : part); return { text: messageString, messageParts: makeTemplateObject(messageParts, rawMessageParts), placeholderNames }; } function makeParsedTranslation(messageParts, placeholderNames = []) { let messageString = messageParts[0]; for (let i = 0; i < placeholderNames.length; i++) { messageString += `{$${placeholderNames[i]}}${messageParts[i + 1]}`; } return { text: messageString, messageParts: makeTemplateObject(messageParts, messageParts), placeholderNames }; } function makeTemplateObject(cooked, raw) { Object.defineProperty(cooked, "raw", { value: raw }); return cooked; } function describeMessage(message) { const meaningString = message.meaning && ` - "${message.meaning}"`; const legacy = message.legacyIds && message.legacyIds.length > 0 ? ` [${message.legacyIds.map((l) => `"${l}"`).join(", ")}]` : ""; return `"${message.id}"${legacy} ("${message.text}"${meaningString})`; } // bazel-out/k8-fastbuild/bin/packages/localize/tools/src/source_file_utils.js import { types as t } from "@babel/core"; 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 = t.stringLiteral(messageParts[0]); for (let i = 1; i < messageParts.length; i++) { mappedString = t.binaryExpression("+", mappedString, wrapInParensIfNecessary(substitutions[i - 1])); mappedString = t.binaryExpression("+", mappedString, t.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 [makeTemplateObject(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 [makeTemplateObject(cooked, raw), locations]; } function unwrapExpressionsFromTemplateLiteral(quasi, fs = getFileSystem()) { return [ quasi.node.expressions, quasi.get("expressions").map((e) => getLocation(fs, e)) ]; } function wrapInParensIfNecessary(expression) { if (t.isBinaryExpression(expression)) { return t.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 t.isArrayExpression(node) && node.elements.every((element) => t.isStringLiteral(element)); } function isArrayOfExpressions(paths) { return paths.every((element) => element.isExpression()); } function translate2(diagnostics, translations, messageParts, substitutions, missingTranslation) { try { return translate(translations, messageParts, substitutions); } catch (e) { if (isMissingTranslationError(e)) { diagnostics.add(missingTranslation, e.message); return [ makeTemplateObject(e.parsedMessage.messageParts, e.parsedMessage.messageParts), substitutions ]; } else { diagnostics.error(e.message); return [messageParts, substitutions]; } } } var BabelParseError = class extends Error { node; type = "BabelParseError"; constructor(node, message) { super(message); this.node = node; } }; function isBabelParseError(e) { return e.type === "BabelParseError"; } function buildCodeFrameError(fs, path, file, e) { let filename = file.opts.filename; if (filename) { filename = fs.resolve(filename); let cwd = file.opts.cwd; if (cwd) { cwd = fs.resolve(cwd); filename = fs.relative(cwd, filename); } } else { filename = "(unknown file)"; } const { message } = file.hub.buildError(e.node, e.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: startPath.getSource() || void 0 }; } 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) { const opts = (path?.hub).file?.opts; const filename = opts?.filename; if (!filename || !opts.cwd) { return null; } const relativePath = fs.relative(opts.cwd, filename); const root = opts.generatorOpts?.sourceRoot ?? opts.cwd; const absPath = fs.resolve(root, relativePath); return absPath; } function getLineAndColumn(loc) { return { line: loc.line - 1, column: loc.column }; } export { Diagnostics, parseMessage, parseTranslation, makeParsedTranslation, isLocalize, isNamedIdentifier, isGlobalIdentifier, buildLocalizeReplacement, unwrapMessagePartsFromLocalizeCall, unwrapSubstitutionsFromLocalizeCall, unwrapMessagePartsFromTemplateLiteral, unwrapExpressionsFromTemplateLiteral, translate2 as 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.dev/license */ //# sourceMappingURL=chunk-ARKHNZ5Y.js.map