@angular/localize
Version:
Angular - library for localizing messages
1 lines • 14.4 kB
Source Map (JSON)
{"version":3,"file":"localize.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/packages/localize/src/utils/src/translations.ts","../../../../../darwin_arm64-fastbuild-ST-fdfa778d11ba/bin/packages/localize/src/translate.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport {BLOCK_MARKER} from './constants';\nimport {MessageId, MessageMetadata, ParsedMessage, parseMessage, TargetMessage} from './messages';\n\n/**\n * A translation message that has been processed to extract the message parts and placeholders.\n */\nexport interface ParsedTranslation extends MessageMetadata {\n messageParts: TemplateStringsArray;\n placeholderNames: string[];\n}\n\n/**\n * The internal structure used by the runtime localization to translate messages.\n */\nexport type ParsedTranslations = Record<MessageId, ParsedTranslation>;\n\nexport class MissingTranslationError extends Error {\n private readonly type = 'MissingTranslationError';\n constructor(readonly parsedMessage: ParsedMessage) {\n super(`No translation found for ${describeMessage(parsedMessage)}.`);\n }\n}\n\nexport function isMissingTranslationError(e: any): e is MissingTranslationError {\n return e.type === 'MissingTranslationError';\n}\n\n/**\n * Translate the text of the `$localize` tagged-string (i.e. `messageParts` and\n * `substitutions`) using the given `translations`.\n *\n * The tagged-string is parsed to extract its `messageId` which is used to find an appropriate\n * `ParsedTranslation`. If this doesn't match and there are legacy ids then try matching a\n * translation using those.\n *\n * If one is found then it is used to translate the message into a new set of `messageParts` and\n * `substitutions`.\n * The translation may reorder (or remove) substitutions as appropriate.\n *\n * If there is no translation with a matching message id then an error is thrown.\n * If a translation contains a placeholder that is not found in the message being translated then an\n * error is thrown.\n */\nexport function translate(\n translations: Record<string, ParsedTranslation>,\n messageParts: TemplateStringsArray,\n substitutions: readonly any[],\n): [TemplateStringsArray, readonly any[]] {\n const message = parseMessage(messageParts, substitutions);\n // Look up the translation using the messageId, and then the legacyId if available.\n let translation = translations[message.id];\n // If the messageId did not match a translation, try matching the legacy ids instead\n if (message.legacyIds !== undefined) {\n for (let i = 0; i < message.legacyIds.length && translation === undefined; i++) {\n translation = translations[message.legacyIds[i]];\n }\n }\n if (translation === undefined) {\n throw new MissingTranslationError(message);\n }\n return [\n translation.messageParts,\n translation.placeholderNames.map((placeholder) => {\n if (message.substitutions.hasOwnProperty(placeholder)) {\n return message.substitutions[placeholder];\n } else {\n throw new Error(\n `There is a placeholder name mismatch with the translation provided for the message ${describeMessage(\n message,\n )}.\\n` +\n `The translation contains a placeholder with name ${placeholder}, which does not exist in the message.`,\n );\n }\n }),\n ];\n}\n\n/**\n * Parse the `messageParts` and `placeholderNames` out of a target `message`.\n *\n * Used by `loadTranslations()` to convert target message strings into a structure that is more\n * appropriate for doing translation.\n *\n * @param message the message to be parsed.\n */\nexport function parseTranslation(messageString: TargetMessage): ParsedTranslation {\n const parts = messageString.split(/{\\$([^}]*)}/);\n const messageParts = [parts[0]];\n const placeholderNames: string[] = [];\n for (let i = 1; i < parts.length - 1; i += 2) {\n placeholderNames.push(parts[i]);\n messageParts.push(`${parts[i + 1]}`);\n }\n const rawMessageParts = messageParts.map((part) =>\n part.charAt(0) === BLOCK_MARKER ? '\\\\' + part : part,\n );\n return {\n text: messageString,\n messageParts: makeTemplateObject(messageParts, rawMessageParts),\n placeholderNames,\n };\n}\n\n/**\n * Create a `ParsedTranslation` from a set of `messageParts` and `placeholderNames`.\n *\n * @param messageParts The message parts to appear in the ParsedTranslation.\n * @param placeholderNames The names of the placeholders to intersperse between the `messageParts`.\n */\nexport function makeParsedTranslation(\n messageParts: string[],\n placeholderNames: string[] = [],\n): ParsedTranslation {\n let messageString = messageParts[0];\n for (let i = 0; i < placeholderNames.length; i++) {\n messageString += `{$${placeholderNames[i]}}${messageParts[i + 1]}`;\n }\n return {\n text: messageString,\n messageParts: makeTemplateObject(messageParts, messageParts),\n placeholderNames,\n };\n}\n\n/**\n * Create the specialized array that is passed to tagged-string tag functions.\n *\n * @param cooked The message parts with their escape codes processed.\n * @param raw The message parts with their escaped codes as-is.\n */\nexport function makeTemplateObject(cooked: string[], raw: string[]): TemplateStringsArray {\n Object.defineProperty(cooked, 'raw', {value: raw});\n return cooked as any;\n}\n\nfunction describeMessage(message: ParsedMessage): string {\n const meaningString = message.meaning && ` - \"${message.meaning}\"`;\n const legacy =\n message.legacyIds && message.legacyIds.length > 0\n ? ` [${message.legacyIds.map((l) => `\"${l}\"`).join(', ')}]`\n : '';\n return `\"${message.id}\"${legacy} (\"${message.text}\"${meaningString})`;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\nimport {LocalizeFn} from './localize';\nimport {\n MessageId,\n ParsedTranslation,\n parseTranslation,\n TargetMessage,\n translate as _translate,\n} from './utils';\n\n/**\n * We augment the `$localize` object to also store the translations.\n *\n * Note that because the TRANSLATIONS are attached to a global object, they will be shared between\n * all applications that are running in a single page of the browser.\n */\ndeclare const $localize: LocalizeFn & {TRANSLATIONS: Record<MessageId, ParsedTranslation>};\n\n/**\n * Load translations for use by `$localize`, if doing runtime translation.\n *\n * If the `$localize` tagged strings are not going to be replaced at compiled time, it is possible\n * to load a set of translations that will be applied to the `$localize` tagged strings at runtime,\n * in the browser.\n *\n * Loading a new translation will overwrite a previous translation if it has the same `MessageId`.\n *\n * Note that `$localize` messages are only processed once, when the tagged string is first\n * encountered, and does not provide dynamic language changing without refreshing the browser.\n * Loading new translations later in the application life-cycle will not change the translated text\n * of messages that have already been translated.\n *\n * The message IDs and translations are in the same format as that rendered to \"simple JSON\"\n * translation files when extracting messages. In particular, placeholders in messages are rendered\n * using the `{$PLACEHOLDER_NAME}` syntax. For example the message from the following template:\n *\n * ```html\n * <div i18n>pre<span>inner-pre<b>bold</b>inner-post</span>post</div>\n * ```\n *\n * would have the following form in the `translations` map:\n *\n * ```ts\n * {\n * \"2932901491976224757\":\n * \"pre{$START_TAG_SPAN}inner-pre{$START_BOLD_TEXT}bold{$CLOSE_BOLD_TEXT}inner-post{$CLOSE_TAG_SPAN}post\"\n * }\n * ```\n *\n * @param translations A map from message ID to translated message.\n *\n * These messages are processed and added to a lookup based on their `MessageId`.\n *\n * @see {@link clearTranslations} for removing translations loaded using this function.\n * @see {@link /api/localize/init/$localize $localize} for tagging messages as needing to be translated.\n * @publicApi\n */\nexport function loadTranslations(translations: Record<MessageId, TargetMessage>) {\n // Ensure the translate function exists\n if (!$localize.translate) {\n $localize.translate = translate;\n }\n if (!$localize.TRANSLATIONS) {\n $localize.TRANSLATIONS = {};\n }\n Object.keys(translations).forEach((key) => {\n $localize.TRANSLATIONS[key] = parseTranslation(translations[key]);\n });\n}\n\n/**\n * Remove all translations for `$localize`, if doing runtime translation.\n *\n * All translations that had been loading into memory using `loadTranslations()` will be removed.\n *\n * @see {@link loadTranslations} for loading translations at runtime.\n * @see {@link /api/localize/init/$localize $localize} for tagging messages as needing to be translated.\n *\n * @publicApi\n */\nexport function clearTranslations() {\n $localize.translate = undefined;\n $localize.TRANSLATIONS = {};\n}\n\n/**\n * Translate the text of the given message, using the loaded translations.\n *\n * This function may reorder (or remove) substitutions as indicated in the matching translation.\n */\nexport function translate(\n messageParts: TemplateStringsArray,\n substitutions: readonly any[],\n): [TemplateStringsArray, readonly any[]] {\n try {\n return _translate($localize.TRANSLATIONS, messageParts, substitutions);\n } catch (e) {\n console.warn((e as Error).message);\n return [messageParts, substitutions];\n }\n}\n"],"names":["MissingTranslationError","Error","parsedMessage","type","constructor","describeMessage","isMissingTranslationError","e","translate","translations","messageParts","substitutions","message","parseMessage","translation","id","legacyIds","undefined","i","length","placeholderNames","map","placeholder","hasOwnProperty","parseTranslation","messageString","parts","split","push","rawMessageParts","part","charAt","BLOCK_MARKER","text","makeTemplateObject","makeParsedTranslation","cooked","raw","Object","defineProperty","value","meaningString","meaning","legacy","l","join","loadTranslations","$localize","TRANSLATIONS","keys","forEach","key","clearTranslations","_translate","console","warn"],"mappings":"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"}