diff --git a/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts b/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts index e2bb28fe1eaf..4286f33a1b41 100644 --- a/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts +++ b/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts @@ -6,8 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ -import remapping, { type DecodedSourceMap, type SourceMapInput } from '@ampproject/remapping'; -import type { ɵParsedTranslation } from '@angular/localize'; +import { type DecodedSourceMap, type SourceMapInput } from '@ampproject/remapping'; +import { + type ɵParsedTranslation, + ɵisMissingTranslationError, + ɵmakeTemplateObject, + ɵtranslate, +} from '@angular/localize'; import { MagicString } from 'magic-string'; import { deserialize } from 'node:v8'; import { parseSync } from 'oxc-parser'; @@ -151,6 +156,8 @@ const deserializedTranslations = new Map, + messageParts: TemplateStringsArray, + substitutions: readonly number[], + missingTranslation: 'error' | 'warning' | 'ignore', +): [TemplateStringsArray, readonly number[]] { + try { + return ɵtranslate(translations, messageParts, substitutions) as [ + TemplateStringsArray, + readonly number[], + ]; + } catch (error) { + if (ɵisMissingTranslationError(error)) { + if (missingTranslation !== 'ignore') { + diagnostics.push({ type: missingTranslation, message: error.message }); + } -/** - * Attempts to load the `@angular/localize/tools` module containing the functionality to - * perform the file translations. - * This module must be dynamically loaded as it is an ESM module and this file is CommonJS. - */ -async function loadLocalizeTools(): Promise { - // Load ESM `@angular/localize/tools` using the TypeScript dynamic import workaround. - // Once TypeScript provides support for keeping the dynamic import this workaround can be - // changed to a direct dynamic import. - localizeToolsModule ??= await import('@angular/localize/tools'); + return [ + ɵmakeTemplateObject(error.parsedMessage.messageParts, error.parsedMessage.messageParts), + substitutions, + ]; + } else { + const message = error instanceof Error ? error.message : String(error); + diagnostics.push({ type: 'error', message }); - return localizeToolsModule; + return [messageParts, substitutions]; + } + } } /** @@ -464,21 +483,20 @@ async function inlineLocalize( missingTranslation: 'error' | 'warning' | 'ignore' = 'warning', ) { const magicString = new MagicString(code); - const { Diagnostics, translate } = await loadLocalizeTools(); - const diagnostics = new Diagnostics(); + const diagnostics: DiagnosticMessage[] = []; if (metadata.diagnostics) { for (const message of metadata.diagnostics) { - diagnostics.error(message); + diagnostics.push({ type: 'error', message }); } } if (metadata.localeInsertSites.length > 0) { const localeData = await loadLocaleData(locale); if (localeData.error) { - diagnostics.error(localeData.error); + diagnostics.push({ type: 'error', message: localeData.error }); } else if (localeData.warning) { - diagnostics.warn(localeData.warning); + diagnostics.push({ type: 'warning', message: localeData.warning }); } let injected = false; for (const site of metadata.localeInsertSites) { @@ -492,7 +510,7 @@ async function inlineLocalize( } for (const callSite of metadata.callSites) { - const [translatedParts, translatedSubstitutions] = translate( + const [translatedParts, translatedSubstitutions] = translateMessage( diagnostics, translation || {}, callSite.messageParts, @@ -541,6 +559,7 @@ async function inlineLocalize( includeContent: true, hires: 'boundary', }); + remapping ??= (await import('@ampproject/remapping')).default; outputMap = remapping([{ ...rawMap, version: 3 } satisfies DecodedSourceMap, map], () => null); }