Repository navigation
feat(i18n): inline the source locale and refresh integrity hashes after translation - #173
Conversation
bc2a5e9 to
f82b3c6
Compare
Aukevanoost
left a comment
There was a problem hiding this comment.
Thanks! One thing outside the diff: builder.ts only calls translateFederationArtifacts when i18n.locales is non-empty, so the source-locale-only case (i18n: { sourceLocale: 'en' }, no locales, localize: true) is never reached from the builder. The federation bundles still keep raw $localize calls there. The hasLocales guard needs adjusting too.
| expect(infoSpy).not.toHaveBeenCalled(); | ||
| }); | ||
|
|
||
| describe('source locale', () => { |
There was a problem hiding this comment.
Since #176, callers pass base/browser and localePath no longer adds browser. These dist/browser/<locale> expectations (and the ones in the integration spec) will fail after a rebase on main.
| } | ||
|
|
||
| // Translating changes the bundles, so the integrity hashes in the copied remoteEntry.json are stale | ||
| refreshIntegrity(translatedPaths); |
There was a problem hiding this comment.
Core also writes these hashes to importmap.json next to remoteEntry.json. That file keeps the old hashes, so a host that uses it gets an SRI mismatch on the shared bundles.
| const inlineSourceLocale = | ||
| localize === true || (Array.isArray(localize) && localize.includes(sourceLocale)); | ||
|
|
||
| if (locales.length === 0 && !inlineSourceLocale) { |
There was a problem hiding this comment.
With localize: ['en'] this used to return early. Now every watch/serve rebuild starts localize-translate and re-translates all shared bundles, which adds noticeable time to each rebuild.
| const locales = Object.keys(i18n.locales).filter(locale => neededLocales.includes(locale)); | ||
|
|
||
| if (locales.length === 0) { | ||
| // Angular inlines $localize in the source locale whenever it builds it, but the federation output is |
There was a problem hiding this comment.
Nit: could this be one line? Also, "a run of its own" reads like a second run, but it's the same run without --target-locales.
| } | ||
| } | ||
|
|
||
| // remoteEntry.json lists a subresource integrity hash per file when the integrityHashes feature is on |
There was a problem hiding this comment.
Nit: this comment only restates what the field is, so it can go.
f82b3c6 to
60879d4
Compare
|
@Aukevanoost - thanks again for the review; I've rebased on main and pushed these updates:
One question on the rebuild cost: with |
|
Thanks for the updates! #194 was merged just after your push, and it changes the API this PR builds on, so could you rebase on main again?
On the rebuild cost: let's keep it as is. Skipping the source-only run would bring back the
|
60879d4 to
69e61fa
Compare
… integrity hashes after translation localize-translate already writes an inlined source locale copy when it is given -l, which Angular's own build does too. That only happened when another locale was being translated, though: with localize listing just the source locale (or no other locale configured) translateFederationArtifacts returned early and the federation output kept its raw $localize calls. Run it for that case as well; no translation file is needed, a bare -t is accepted. remoteEntry.json was copied to the locale folders before translating, so its integrity hashes no longer matched the translated files (nor the inlined source locale). Recompute them after translation, keeping the existing algorithm. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
69e61fa to
270fad6
Compare
|
I've pulled in the latest changes:
|
Aukevanoost
left a comment
There was a problem hiding this comment.
Thanks again for the contribution!
Backports 22.2.3..22.2.4 onto the Angular 21 layout. Incoming: - #167/#169: the dev server keeps federation externals out of the stylesheet build; Angular's ssr/prerender corrections reach serveWithVite. - #166/#168: context tsconfigs pass `types` resolved instead of widening typeRoots. - #173/#176/#186/#200: i18n translates every federation output, honours outputPath.browser, stays under the Windows command line limit, inlines the source locale only when requested and refreshes integrity hashes. - #187/#190: optional-peer entry points are skipped only when the peer is missing; @angular/localize is an optional peer. - #189/#195/#196: federation build results carry their entry; a path written twice is reported once. - #194: @softarc/native-federation ~4.9.0, orchestrator ^4.9.0. Angular 21 specific adaptations: - Checked the #167/#169 assumptions against @angular/build 21.2.24: serveWithVite reads externalDependencies and ssr back from the options it passed, and the stylesheet bundler uses externalDependencies, as on 22. - No // upstream: tags change (still angular-cli v21.2.19, 1a728258d6). - Skipped #188/#198 (remote builder): this branch has no remote builder. - Kept the internal builder without instrumentForCoverage; dropped it from the incoming spec. - Moved output-entries and the i18n integration spec into src/utils. - Skipped the `types: ["*"]` spec below TypeScript 6 (branch is on 5.9). - @angular/localize optional peer/devDependency at >=21.2.0/^21.2.0. - Kept the -v4 name, 21.2.16 version, ng21 pins and this branch's global pnpm overrides; main's vite 8 override doesn't apply to 21.2's vite 7. - pnpm-lock.yaml regenerated rather than merged.
Fixes #170.
Problem
1. A source-only run leaves the raw
$localizecalls. Withlocalize: true, Angular inlines$localizein everything it builds, including the source locale, and removes the@angular/localize/initpolyfill because nothing needs the runtime any more (this is deliberate in@angular/build). The federation output (shared packages, mappings, exposed modules and their chunks) is built outside Angular, sotranslateFederationArtifactstranslates it itself.localize-translatewrites an inlined copy of the source locale whenever it is given-l, and that already happens when another locale is translated. But when the source locale is the only locale to process (localizelists just the source locale, or no other locale is configured), the function returned early. The source locale folder then keeps the raw calls, and nothing defines the global$localizeat runtime unless the app brings its own copy of the polyfill. The app fails with$localize is not a functionin the source locale only.The builder had the same limitation: it only called
translateFederationArtifactswheni18n.localeswas non-empty, soi18n: { sourceLocale: 'en' }withlocalize: truenever reached it.2. Stale integrity hashes (#170).
remoteEntry.jsonis copied to the locale folders beforelocalize-translateruns. Translation changes the bundles, so withfeatures.integrityHasheson, the hashes in each locale'sremoteEntry.jsonno longer match the files. The same hashes are written toimportmap.json, which had the same problem.Change
localize: true, or an array that contains the source locale code). The same singlelocalize-translaterun inlines it, so there is no second pass and no extra translation file:-tis passed without values and--target-localesis left out when there is no other locale.i18n.localesto calltranslateFederationArtifacts, andI18nConfig.localesis optional, so a config with only asourceLocaleis handled.integrityentries inremoteEntry.jsonandimportmap.jsonfor every locale folder that was written, the source locale included. The existing algorithm prefix of each entry is kept, entries for files that are not in that folder are left alone, and a file is only rewritten when it has anintegritymap.Apps that do not use
localizeare unaffected (this code only runs when translation does).Tests
--target-locales, noremoteEntry.jsoncopy), no configured locales, an i18n config withoutlocales, and the integrity refresh (all written locales,remoteEntry.jsonandimportmap.json, algorithm kept, missing file kept, nointegritymap left untouched).i18n.integration.spec.tsthat run the reallocalize-translateCLI: one checks that both the source and a translated locale have no$localizeleft and matching integrity hashes, the other covers the source-only run.i18n.spec.tstests fail with and without this change because they assert POSIX paths.Open questions
localize: ['en'](source locale only), watch/serve rebuilds now runlocalize-translateover the shared bundles, which they skipped before. That is the same cost a single translated locale such as['de']already has on each rebuild. Is that acceptable, or should source-only runs be skipped or reduced on rebuilds?$localizecalls left, so translating it again does not change it. I did not test multi-locale watch mode.