Skip to content

Commit 3ad028b

Browse files
clydinalan-agius4
authored andcommitted
fix(@angular-devkit/build-angular): ensure localize polyfill and locale specifier are injected when not inlining
When using the esbuild-based builders (`application`/`browser-esbuild`), the localize polyfill and the locale specifier for the configured source locale will now be injected into the application when available and the application is not configured to inline translations (`localize` option disabled or otherwise not used). This is useful for when developing a localized application with the development server or when the application is not translated but has a customized source locale.
1 parent 2c33f09 commit 3ad028b

File tree

1 file changed

+47
-3
lines changed

1 file changed

+47
-3
lines changed

packages/angular_devkit/build_angular/src/tools/esbuild/application-code-bundle.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { BuildOptions } from 'esbuild';
1010
import assert from 'node:assert';
1111
import { createHash } from 'node:crypto';
1212
import { readFile } from 'node:fs/promises';
13+
import { createRequire } from 'node:module';
1314
import { extname, join, relative } from 'node:path';
1415
import type { NormalizedApplicationBuildOptions } from '../../builders/application/options';
1516
import { allowMangle } from '../../utils/environment-options';
@@ -114,8 +115,11 @@ export function createBrowserCodeBundleOptions(
114115
createVirtualModulePlugin({
115116
namespace,
116117
loadContent: async (_, build) => {
118+
let hasLocalizePolyfill = false;
117119
const polyfillPaths = await Promise.all(
118120
polyfills.map(async (path) => {
121+
hasLocalizePolyfill ||= path.startsWith('@angular/localize');
122+
119123
if (path.startsWith('zone.js') || !extname(path)) {
120124
return path;
121125
}
@@ -130,10 +134,29 @@ export function createBrowserCodeBundleOptions(
130134
}),
131135
);
132136

137+
if (!options.i18nOptions.shouldInline && !hasLocalizePolyfill) {
138+
// Cannot use `build.resolve` here since it does not allow overriding the external options
139+
// and the actual presence of the `@angular/localize` package needs to be checked here.
140+
const workspaceRequire = createRequire(workspaceRoot + '/');
141+
try {
142+
workspaceRequire.resolve('@angular/localize');
143+
// The resolve call above will throw if not found
144+
polyfillPaths.push('@angular/localize/init');
145+
} catch {}
146+
}
147+
148+
// Generate module contents with an import statement per defined polyfill
149+
let contents = polyfillPaths
150+
.map((file) => `import '${file.replace(/\\/g, '/')}';`)
151+
.join('\n');
152+
153+
// If not inlining translations and source locale is defined, inject the locale specifier
154+
if (!options.i18nOptions.shouldInline && options.i18nOptions.hasDefinedSourceLocale) {
155+
contents += `(globalThis.$localize ??= {}).locale = "${options.i18nOptions.sourceLocale}";\n`;
156+
}
157+
133158
return {
134-
contents: polyfillPaths
135-
.map((file) => `import '${file.replace(/\\/g, '/')}';`)
136-
.join('\n'),
159+
contents,
137160
loader: 'js',
138161
resolveDir: workspaceRoot,
139162
};
@@ -258,6 +281,27 @@ export function createServerCodeBundleOptions(
258281
contents.push(`export { ɵresetCompiledComponents } from '@angular/core';`);
259282
}
260283

284+
if (!options.i18nOptions.shouldInline) {
285+
// Cannot use `build.resolve` here since it does not allow overriding the external options
286+
// and the actual presence of the `@angular/localize` package needs to be checked here.
287+
const workspaceRequire = createRequire(workspaceRoot + '/');
288+
try {
289+
workspaceRequire.resolve('@angular/localize');
290+
// The resolve call above will throw if not found
291+
contents.push(`import '@angular/localize/init';`);
292+
} catch {}
293+
}
294+
295+
if (options.i18nOptions.shouldInline) {
296+
// When inlining, a placeholder is used to allow the post-processing step to inject the $localize locale identifier
297+
contents.push('(globalThis.$localize ??= {}).locale = "___NG_LOCALE_INSERT___";');
298+
} else if (options.i18nOptions.hasDefinedSourceLocale) {
299+
// If not inlining translations and source locale is defined, inject the locale specifier
300+
contents.push(
301+
`(globalThis.$localize ??= {}).locale = "${options.i18nOptions.sourceLocale}";`,
302+
);
303+
}
304+
261305
if (prerenderOptions?.discoverRoutes) {
262306
// We do not import it directly so that node.js modules are resolved using the correct context.
263307
const routesExtractorCode = await readFile(

0 commit comments

Comments
 (0)