-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@angular-devkit/build-angular): support i18n inlining with esbui…
…ld-based builder When using the esbuild-based application build system through either the `application` or `browser-esbuild` builder, the `localize` option will now allow inlining project defined localizations. The process to configure and enable the i18n system is the same as with the Webpack-based `browser` builder. The implementation uses a similar approach to the `browser` builder in which the application is built once and then post-processed for each active locale. In addition to inlining translations, the locale identifier is injected and the locale specific data is added to the applications. Currently, this implementation adds all the locale specific data to each application during the initial building. While this may cause a small increase in the polyfills bundle (locale data is very small in size), it has a benefit of faster builds and a significantly less complicated build process. Additional size optimizations to the data itself are also being considered to even further reduce impact. Also, with the eventual shift towards the standard `Intl` web APIs, the need for the locale data will become obsolete in addition to the build time code necessary to add it to the application. While build capabilities are functional, there are several areas which have not yet been fully implemented but will be in future changes. These include console progress information, efficient watch support, and app-shell/service worker support.
- Loading branch information
1 parent
11449b1
commit c5f3ec7
Showing
25 changed files
with
858 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
packages/angular_devkit/build_angular/src/builders/application/i18n.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
/** | ||
* @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.io/license | ||
*/ | ||
|
||
import { BuilderContext } from '@angular-devkit/architect'; | ||
import { join } from 'node:path'; | ||
import { InitialFileRecord } from '../../tools/esbuild/bundler-context'; | ||
import { ExecutionResult } from '../../tools/esbuild/bundler-execution-result'; | ||
import { I18nInliner } from '../../tools/esbuild/i18n-inliner'; | ||
import { generateIndexHtml } from '../../tools/esbuild/index-html-generator'; | ||
import { createOutputFileFromText } from '../../tools/esbuild/utils'; | ||
import { maxWorkers } from '../../utils/environment-options'; | ||
import { loadTranslations } from '../../utils/i18n-options'; | ||
import { createTranslationLoader } from '../../utils/load-translations'; | ||
import { urlJoin } from '../../utils/url'; | ||
import { NormalizedApplicationBuildOptions } from './options'; | ||
|
||
/** | ||
* Inlines all active locales as specified by the application build options into all | ||
* application JavaScript files created during the build. | ||
* @param options The normalized application builder options used to create the build. | ||
* @param executionResult The result of an executed build. | ||
* @param initialFiles A map containing initial file information for the executed build. | ||
*/ | ||
export async function inlineI18n( | ||
options: NormalizedApplicationBuildOptions, | ||
executionResult: ExecutionResult, | ||
initialFiles: Map<string, InitialFileRecord>, | ||
): Promise<void> { | ||
// Create the multi-threaded inliner with common options and the files generated from the build. | ||
const inliner = new I18nInliner( | ||
{ | ||
missingTranslation: options.i18nOptions.missingTranslationBehavior ?? 'warning', | ||
outputFiles: executionResult.outputFiles, | ||
shouldOptimize: options.optimizationOptions.scripts, | ||
}, | ||
maxWorkers, | ||
); | ||
|
||
// For each active locale, use the inliner to process the output files of the build. | ||
const updatedOutputFiles = []; | ||
const updatedAssetFiles = []; | ||
try { | ||
for (const locale of options.i18nOptions.inlineLocales) { | ||
// A locale specific set of files is returned from the inliner. | ||
const localeOutputFiles = await inliner.inlineForLocale( | ||
locale, | ||
options.i18nOptions.locales[locale].translation, | ||
); | ||
|
||
// Generate locale specific index HTML files | ||
if (options.indexHtmlOptions) { | ||
const { content, errors, warnings } = await generateIndexHtml( | ||
initialFiles, | ||
localeOutputFiles, | ||
{ | ||
...options, | ||
baseHref: | ||
getLocaleBaseHref(options.baseHref, options.i18nOptions, locale) ?? options.baseHref, | ||
}, | ||
locale, | ||
); | ||
|
||
localeOutputFiles.push(createOutputFileFromText(options.indexHtmlOptions.output, content)); | ||
} | ||
|
||
// Update directory with locale base | ||
if (options.i18nOptions.flatOutput !== true) { | ||
localeOutputFiles.forEach((file) => { | ||
file.path = join(locale, file.path); | ||
}); | ||
|
||
for (const assetFile of executionResult.assetFiles) { | ||
updatedAssetFiles.push({ | ||
source: assetFile.source, | ||
destination: join(locale, assetFile.destination), | ||
}); | ||
} | ||
} | ||
|
||
updatedOutputFiles.push(...localeOutputFiles); | ||
} | ||
} finally { | ||
await inliner.close(); | ||
} | ||
|
||
// Update the result with all localized files | ||
executionResult.outputFiles = updatedOutputFiles; | ||
|
||
// Assets are only changed if not using the flat output option | ||
if (options.i18nOptions.flatOutput !== true) { | ||
executionResult.assetFiles = updatedAssetFiles; | ||
} | ||
} | ||
|
||
function getLocaleBaseHref( | ||
baseHref: string | undefined, | ||
i18n: NormalizedApplicationBuildOptions['i18nOptions'], | ||
locale: string, | ||
): string | undefined { | ||
if (i18n.flatOutput) { | ||
return undefined; | ||
} | ||
|
||
if (i18n.locales[locale] && i18n.locales[locale].baseHref !== '') { | ||
return urlJoin(baseHref || '', i18n.locales[locale].baseHref ?? `/${locale}/`); | ||
} | ||
|
||
return undefined; | ||
} | ||
|
||
/** | ||
* Loads all active translations using the translation loaders from the `@angular/localize` package. | ||
* @param context The architect builder context for the current build. | ||
* @param i18n The normalized i18n options to use. | ||
*/ | ||
export async function loadActiveTranslations( | ||
context: BuilderContext, | ||
i18n: NormalizedApplicationBuildOptions['i18nOptions'], | ||
) { | ||
// Load locale data and translations (if present) | ||
let loader; | ||
for (const [locale, desc] of Object.entries(i18n.locales)) { | ||
if (!i18n.inlineLocales.has(locale) && locale !== i18n.sourceLocale) { | ||
continue; | ||
} | ||
|
||
if (!desc.files.length) { | ||
continue; | ||
} | ||
|
||
loader ??= await createTranslationLoader(); | ||
|
||
loadTranslations( | ||
locale, | ||
desc, | ||
context.workspaceRoot, | ||
loader, | ||
{ | ||
warn(message) { | ||
context.logger.warn(message); | ||
}, | ||
error(message) { | ||
throw new Error(message); | ||
}, | ||
}, | ||
undefined, | ||
i18n.duplicateTranslationBehavior, | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.