From af5be9004f6697037d20f433bd2a78a2f7f1a7e5 Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 28 Sep 2026 16:17:37 -0400 Subject: [PATCH 1/2] refactor(@angular/build): avoid loading localize tools and babel in i18n inliner worker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, `i18n-inliner-worker.ts` dynamically loaded `@angular/localize/tools` to invoke its `translate()` function and `Diagnostics` class. Importing `@angular/localize/tools` eagerly pulls in `@babel/core`, `@angular/compiler-cli`, and `@angular/compiler`, consuming ~180 ms of CPU latency and ~32 MB of heap per worker thread upon first translation. Since the inliner only performs string translation replacements using AST locations already discovered via OXC, the full Babel transformation and compiler tooling bundle is unnecessary. This updates `i18n-inliner-worker.ts` to directly import low-level runtime functions (`ɵtranslate`, `ɵisMissingTranslationError`, `ɵmakeTemplateObject`) from `@angular/localize` and manage diagnostics via a direct message array. This completely avoids loading `@angular/localize/tools` and its heavy transitive dependencies. --- .../src/tools/i18n/i18n-inliner-worker.ts | 69 +++++++++++-------- 1 file changed, 42 insertions(+), 27 deletions(-) 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 6a7e84b308b7..8a666aff67ad 100644 --- a/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts +++ b/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts @@ -7,7 +7,12 @@ */ import remapping, { type DecodedSourceMap, type SourceMapInput } from '@ampproject/remapping'; -import type { ɵParsedTranslation } from '@angular/localize'; +import { + type ɵParsedTranslation, + ɵisMissingTranslationError, + ɵmakeTemplateObject, + ɵtranslate, +} from '@angular/localize'; import { MagicString } from 'magic-string'; import { deserialize } from 'node:v8'; import { parseSync } from 'oxc-parser'; @@ -285,7 +290,7 @@ export async function inlineFileBatch( locale, code: result.code, map: result.map, - messages: result.diagnostics.messages, + messages: result.diagnostics, }; }), ); @@ -317,33 +322,44 @@ export async function inlineCode(request: InlineCodeRequest): Promise, + 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 { + diagnostics.push({ type: 'error', message: (error as Error).message }); - return localizeToolsModule; + return [messageParts, substitutions]; + } + } } /** @@ -465,21 +481,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) { @@ -493,7 +508,7 @@ async function inlineLocalize( } for (const callSite of metadata.callSites) { - const [translatedParts, translatedSubstitutions] = translate( + const [translatedParts, translatedSubstitutions] = translateMessage( diagnostics, translation || {}, callSite.messageParts, From d6ce6043fa3d6be14c1560f71519791e8e19b4dc Mon Sep 17 00:00:00 2001 From: Charles Lyding <19598772+clydin@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:00:42 -0400 Subject: [PATCH 2/2] fixup! refactor(@angular/build): avoid loading localize tools and babel in i18n inliner worker --- packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 8a666aff67ad..f0bd270dc56c 100644 --- a/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts +++ b/packages/angular/build/src/tools/i18n/i18n-inliner-worker.ts @@ -355,7 +355,8 @@ function translateMessage( substitutions, ]; } else { - diagnostics.push({ type: 'error', message: (error as Error).message }); + const message = error instanceof Error ? error.message : String(error); + diagnostics.push({ type: 'error', message }); return [messageParts, substitutions]; }