Closed mmonteiroc closed 2 years ago
Are the translations in templates or are you using $localize
yourself in code?
Are the components where this problem appears local or imported as libraries?
When this happens do "all" $localize
instances remain, or only some of them?
@petebacondarwin
1st: seems to happen when we use $localize in the typescript. HTML templates translations looks correct
2nd: So local ones, even though, it does not happen in specific components as seems random
3rd: Not all, as part of our app remains working. But sometimes seems to happen in ALL as we get full white page and nothing on our app works
Remark: We have seen that in our xliff files, in the equiv-text for the translations this $localize appears in some translations.
Is this something to worry about ? Should we try to delete all the content of the equiv-text ? or this should not affect at all ?
The equiv-text
attributes are purely for translators to see where the original message came from. So they should not be deleted and should not be relevant to your problem here.
As we thought.
Okei, something that comes to your mind that we can try then ? :)
How are you building the production build? Is it using Nx or just normal Angular CLI? Are you doing any special steps of your own?
Do you have lazy loaded chunks? Are the missing transformed $localize
calls always confined to one or more of these chunk files?
To build: angular cli, we have this NPM script:
"build-i18n": "ng build --configuration=production --localize",
We use lazy loading for all our application modules. Often when we can see the issue, we find it in a page of a module, but we don't find it in other pages of same module.
I don't have any other ideas without seeing the code. Is it possible to share the repo with me (or @alan-agius4) privately?
Will have to comment it with my colleagues / bosses and let you know. Will answer you shortly ;) Thanks
@alan-agius4 @petebacondarwin Could we talk privately ? I have talked with my superiors, and we cannot share the code and give direct access, but we could schedule a meeting(teams, zoom....) and share with you in it, all code you would need to find the issue and solve it.
@petebacondarwin reached out via email.
We also have this issue in our Nx-based app: every once in a while, the app builds successfully and most of the routes work, but just one "random" lazy loaded module fails to load with ReferenceError: $localize is not defined
. Rebuilding the app fixes the issue, but it is really hard to catch (most of our e2e tests run on dev builds which don't have this issue). I'm sure it didn't happen before updating to v12 of angular/localize
Just today we ran into a case where even rebuilding didn't help. Reverting @angular/localize
to ~11.2.4
(the version we previously used, even with Angular 12) fixed it immediately.
@madve2
For us, re-running the build would solve the issue, but as it's random, we might have to re-run it 1 time, or maybe even 10 times until it solves the issue.
Maybe that's why today you "ruan into a case where even re-building didn't help" ??
In our side, we didn't try to revert to a version, as it is random, we might have had this issue in previous versions without us noticing, so we don't know in our side what might be the last known working version
@mmonteiroc well I only re-ran it 2 or 3 times, so yes, you are probably right that rerunning it a few more times might have helped eventually.
Thankfully we are reasonably confident about the last known working version. Even though our e2e jobs slipped over this issue, our QA team did catch it every time, and we know that they didn't observe it at all before we upgraded @angular/localize
from 11.2 to 12.1. Of course we can't stay on the old version forever, so I'm still hoping that a proper resolution will pop up eventually :)
@madve2 - just to be clear. Downgrading only the localize package (while keeping all the other packages, and the CLI at the same versions) has solved the problem for you?
@petebacondarwin yes, exactly. We "discovered" this setup by accident: when we upgraded from Angular 11 to 12, for some reason localize
stayed at 11. Few weeks later we noticed this, and upgraded localize
to 12.x as well, and that's when the problems started appearing.
I ask because this is the only meaningful commit that has been added to $localize
since v11 - https://github.com/angular/angular/commit/992c70df590151522bfeee7567a89ac9c4a108f1 - and I don't believe this can be related.
Fair point! In the same commit where we upgraded $localize
to 12.1 (from 11.2), we also updated the rest of the angular stuff from 12.0 to 12.1. Could that be the cause? Anyway, I'll let you know if I experience this issue again and then I'll experiment further.
Seems in our side, to downgrade only the package of localize, does not fix the issue.
We are currently trying to revert some packages of angular as well to 12.0.5 to see if fixes the issue and will post here the feedback of it :)
[Update] In our side, switching ONLY devkit/angular-build to version 12.0.5 seems to fix the issue. 15 build and no issue so far. We still try to narrow in which version works, and in which one stop working. Will post our feedback asap we have it
Can you try to verify if the problem is reproducibly using the below versions?
@angular-devkit/angular-build@12.1.3
@angular-devkit/angular-build@12.1.4
Yes, we are running some CI now with different versions, 15 times per version, and we are checking those versions & the RC of 12.2.0 :)
So we are trying to narrow down in which one is failing and which one isnt.
Will let you know asap
[Update] In our side, we found that the issue appeared between the versions 12.2.0-next.0 (no problem) and the 12.2.0-next.1 (Has problem). So if someone has the same issue as us, while waiting for a fix, can downgrade to versions older than 12.2.0-next.0.
From the discussions offline we were unable to get to the bottom of this.
We tried to use snapshot SHAs from https://github.com/angular/angular-devkit-build-angular-builds to determine which changed introduced the bug. Unfortunately we weren't able to track this down as in some cases older SHA's produced incorrect results, while newer SHA's produced the expected results.
At this point, there is nothing actionable from our end here, unless a reproduction is provided. I'll leave the issue open for a couple of days to give time for someone to come up with a reproduction.
We have exactly the same problem. Sometimes the translations are translated, sometimes $localize remains random in some lazy loaded file if we build the app "production" with aot. Downgrade the @angular-devkit/build-angular to 12.2.0-next.0 fixed it for us as well as @mmonteiroc mentioned above.
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 12.2.6
Node: 14.17.6
Package Manager: npm 7.21.0
OS: linux x64
Angular: 12.2.6
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, language-service, localize, platform-browser
... platform-browser-dynamic, platform-server, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1202.6
@angular-devkit/build-angular 12.2.6
@angular-devkit/core 12.2.6
@angular-devkit/schematics 12.2.6
@schematics/angular 12.2.6
rxjs 7.3.0
typescript 4.3.5
For the lazy loaded file that still contained $localize
, were all usages of $localize
kept in place or only some of them?
@cwaespi - would you be able to share a reproduction that we can play with?
For the lazy loaded file that still contained
$localize
, were all usages of$localize
kept in place or only some of them?
@clydin It looks like all usages of $localize
kept in place.
@cwaespi - would you be able to share a reproduction that we can play with?
@petebacondarwin Unfortunately, I am not allowed to share our project. But I am trying to create a reproduction.
@cwaespi out of curiosity. Your issue happens mainly in CI (pipelines) ?? or you can aslo manage to reproduce locally ??
In our side it's mainly reproducible in CI, but almost can't reproduce locally
@mmonteiroc Just tried to reproduce the problem locally but had no success. Everything indicates that the problem only occurs in our CI pipeline.
The curious thing is that locally and in the CI pipeline i use the same Dockerfile for the build. We use Gitlab AutoDevops for CI/CD.
Our Dockerfile
ARG CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX
# Builder
FROM ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}node:16-alpine as builder
WORKDIR /app
COPY package.json package-lock.json .npmrc /app/
RUN npm ci
COPY . /app
RUN mkdir /app/dist
RUN npm run build -- easy --configuration production
# Runtime
FROM ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}nginx:1.21-alpine
COPY ./.nginx/default.conf.template /etc/nginx/conf.d/default.conf.template
COPY --from=builder /app/dist/ /usr/share/nginx/html
EXPOSE 5000
CMD ["/bin/sh", "-c", "envsubst < /etc/nginx/conf.d/default.conf.template | sed -e 's/§/$/g' > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
@cwaespi In our side we use Azure Devops pipelines without docker. So I assume, the issue isn't related to the CI service not the fact of using / not using docker
We have this error as well. It appears randomly and only in our CI pipeline. Only happens for lazy loaded routes, but also completely random which one it happens to.
It is as if the i18n-variable substitution does not happen during build. I have looked in the build logs but can't see anything suspicious, although I am note sure what I am looking for.
See samples below:
Sample how the code looks when broken:
return e = $localize:@@admin-clear-user-data␟84b9e941cbfed42244d77ad3fe91c45102351270␟5698175515008817140:Clear user data
, t = $localize:@@admin-clear-user-data-description-v2␟dbaf9468af65ea407ed21b2408d62a2d3e7e28ae␟7967954382951777750:Use this dashboard to clear all Hej! data for a specific user. While the user is pending, deletion, you can see it in a table below.
, n = $localize:@@label-privacy-dashboard-add-yourself␟66b6e520d2535ef081b09d0b5a6d0a2c045342c5␟8720367929285043226:Delete for yourself
, i = $localize:@@label-queue-for-deletion␟0bd9e01e687e3b46bd102deecd9bee571b27be6c␟3854011180104367841:Queue users for deletion
, a = $localize:@@admin-clear-user-pending-deletion␟b38f0673b58973cb6c435c9485893191f0656622␟81765222213306383:User pending deletion
, r = $localize:@@label-email␟244aae9346da82b0922506c2d2581373a15641cc␟4768749765465246664:Email
, [[1, "elevated", "p-4"], [1, "mb-3"], e, [1, "text-muted", "mb-3"], t, [1, "d-block", "mb-3", 3, "defaultValue", "onValueChanged"], [1, "d-lg-flex"], [1, "mr-3", 3, "disabled", "click"], n, [3, "disabled", "click"], i, [4, "ngIf"], [4, "ngIf", "ngIfElse"], ["loading", ""], ["class", "w-100 mt-4", 4, "ngIf"], [1, "w-100", "mt-4"], [1, "pb-3"], a, r, [4, "ngFor", "ngForOf"], ["class", "border-top", 4, "ngIf"], [1, "border-top"], [1, "pt-2", "pb-2"]]
Sample how the same code chunk looks when working properly. return e = "Clear user data", t = "Use this dashboard to clear all data for a specific user. While the user is pending, deletion, you can see it in a table below.", n = "Delete for yourself", i = "Queue users for deletion", a = "User pending deletion", r = "Email", [[1, "elevated", "p-4"], [1, "mb-3"], e, [1, "text-muted", "mb-3"], t, [1, "d-block", "mb-3", 3, "defaultValue", "onValueChanged"], [1, "d-lg-flex"], [1, "mr-3", 3, "disabled", "click"], n, [3, "disabled", "click"], i, [4, "ngIf"], [4, "ngIf", "ngIfElse"], ["loading", ""], ["class", "w-100 mt-4", 4, "ngIf"], [1, "w-100", "mt-4"], [1, "pb-3"], a, r, [4, "ngFor", "ngForOf"], ["class", "border-top", 4, "ngIf"], [1, "border-top"], [1, "pt-2", "pb-2"]]
Could this be caused if the CI server has not enough memory maybe? Does angular do parallel builds or lock files etc? Can we force it to run in single file mode to see if that helps?
I am unable to reproduce it locally, maybe someone else has better luck. Here is a script that runs a build and then looks for the word $localize
in a file that includes "579" in all language folders which is one of our routes that has failed.
Will probably need some modification for other peoples setup. I will leave mine on overnight and see if it comes up with anything.
const cp = require("child_process");
const fs = require('fs');
const path = require("path");
const distFolder = path.join(__dirname, "dist/localize");
for (let i = 1; i < 10000; i++) {
console.log(`Build: ${i}`);
cp.execSync("npm run build");
// check all built languages
const languages: string[] = fs.readdirSync(distFolder);
languages.forEach(language => {
// get all files for language
// I know the lazy files has 579 in the name
const files: string[] = fs.readdirSync(path.join(distFolder, language));
let lazyFiles = files.filter(f => f.includes("579"))
lazyFiles.forEach(lazyFile => {
// check file contents for bad string
const file = fs.readFileSync(path.join(distFolder, language, lazyFile), "utf8");
const isBroken = file.includes("$localize");
if (isBroken) {
console.log(`FOUND AFTER ${i} BUILDS\n`);
console.log(file + "\n");
throw new Error("ABORT");
}
})
})
}
Unfortunately, without a reproduction, even shared privately we'll be unable to investigate this issue.
I will see if I can get our main project to fail locally; only tried it for a minimal version so far. Let me get back to you tomorrow latest.
I have also enabled verbose logging in our production builds: next time it happens I will share those logs as well (if it helps).
Logs for a failing build. The chunk that is not processed is: 8721-es2015.013e569803a1121ce5df.js
Chunk is missing from the section where it logs: Localized "FILENAMEjs" [n translation(s)].
Does anyone know where this is in the client code so I can check how that list is generated? Maybe it failed to detect that it should be processed and then does not swap as intended. Is this a dependency maybe, I can't find it in angular/cli when searching.
@submarines-and, I suggest you debug/add logs to the below to determine why $localize
is not being replaced for the mentioned file. https://github.com/angular/angular-cli/blob/821a1b5a949d53f2e82f734062b711a166d42e24/packages/angular_devkit/build_angular/src/utils/process-bundle.ts#L476
Does the same problem occur if USE_LOCALIZE_PLUGINS
is changed to true
? https://github.com/angular/angular-cli/blob/821a1b5a949d53f2e82f734062b711a166d42e24/packages/angular_devkit/build_angular/src/utils/process-bundle.ts#L403
I will check and get back to you.
I can replicate it locally now - it just takes a long time!
I have appended logging to our prod builds as well and will update here as soon as it breaks. If anyone else wants to add it as well, here is a script that adds logging to the function mentioned above. Run it after you have restored dependencies.
sed -i '323s/^/console.log(options, i18n)\n/' node_modules/@angular-devkit/build-angular/src/utils/process-bundle.js
Github action:
- name: Add logging to angular compiler
run: |
sed -i '323s/^/console.log(options, i18n)\n/' node_modules/@angular-devkit/build-angular/src/utils/process-bundle.js
(It is on line 323 for version: "@angular-devkit/build-angular": "12.2.7").
Circuit breaker script (if it helps anyone) We also added a circuit breaker script that runs after building to protect us from bad deploys: Run it as a step after compiling and then just retry the build if it fails. You need to change the paths to match your setup.
import { readdirSync, readFileSync } from 'fs';
import { join } from 'path';
const { log } = console;
log('\nWill check angular output for potential localization errors...');
// get all language folders in output dir
const distFolder = join(__dirname, '..', '..', 'angular', 'PROJECTNAME', 'dist', 'browser');
const folders = readdirSync(distFolder, { withFileTypes: true }).filter(f => f.isDirectory()).map(f => f.name);
folders.forEach(folder => {
// check all javascript files
const scriptFiles = readdirSync(join(distFolder, folder)).filter(f => f.includes('.js') && !f.includes('main'));
log(`Checking: ${folder} (${scriptFiles.length} files)`);
scriptFiles.forEach(script => {
const filePath = join(distFolder, folder, script);
const file = readFileSync(filePath, 'utf8');
// check if i18n variable substituion has failed
const isBroken = file.includes('$localize');
if (isBroken) {
log(`${file}\n`);
log(`Localize error in file: ${filePath}`);
throw new Error('ABORT');
}
});
});
log('\nNo localization errors detected!');
Ok I can confirm that for us, the chunk that does not gets $localize
replaced never passes through inlineLocales
. (which makes sense). To replicate, I re-ran the same build until it failed and then compared the broken logs with the working logs.
I will trace it backwards and see where inlineLocales
gets called. Seems to be file: i18n-inlining.js
. It sends a bunch of lists with paths and I bet that the chunk is missing from one of them.
Update:
Success run
START inlineLocales projectName_src_app_components_social_organization-chart_organization-chart_component_ts.js
then a bit later:
START inlineLocales projectName_src_app_components_social_organization-chart_organization-chart_component_ts-es2015.156ed53de83054543436.js
START inlineLocales projectName_src_app_components_social_organization-chart_organization-chart_component_ts-es5.156ed53de83054543436.js
Failure run:
START inlineLocales projectName_src_app_components_social_organization-chart_organization-chart_component_ts.js
Nothing later, so the es5 and es2015 files never make it to inlineLocales
.
Two more observations:
es2015
file that has the problem (this could of course be a coincidence if it is truly random)es5
version from the Lazy Chunk Files
section of the logs. All others come in pairs. See example of this difference below. If this is the source, then it would indeed have to do with lazy chunks, which at least makes sense for us, I think I will pursue this, and start adding logging to whatever file is responsible for deciding which files are lazy. If anyone knows which file this is, please advise :)GOOD: 2021-09-28T16:53:35.2119318Z projectName_src_app_components_feeds_old-home_old-home_component_ts-node_modules_date-fns_esm-eb2e59-es5.9d47594c5e93f305a62b.js | - | 151.62 kB 2021-09-28T16:53:35.2121905Z projectName_src_app_components_feeds_old-home_old-home_component_ts-node_modules_date-fns_esm-eb2e59-es2015.9d47594c5e93f305a62b.js | - | 139.21 kB
BAD 2021-09-28T17:05:47.5006639Z projectName_src_app_components_feeds_old-home_old-home_component_ts-node_modules_date-fns_esm-eb2e59-es2015.9d47594c5e93f305a62b.js | - | 139.32 kB
Update to point 1 above. Turns out I am stupid, it is actually both files, but my detector script stopped after the first one. It happens in both es5 and es2015 but the same file will fail for every language.
Updated post about inlineLocales
above after more logging. I guess it is called twice, once for the regular .ts file and once for the es5 es2015 files, the second of which gets skipped. I think I will abandon the lazy load track and look at this instead, i.e. where the browser specific bundles get generated.
All of this might also just be GitHub eating my logs, if so then at least I learned something about the compiler steps!
If I set USE_LOCALIZE_PLUGINS
to true
and add the following code (which it does not run without)
const ngFsUtils = require('@angular/compiler-cli/src/ngtsc/file_system');
ngFsUtils.setFileSystem(new ngFsUtils.NodeJSFileSystem());
It then crashes with Strict mode code may not include a with statement
. I guess something is imported far down the chain that breaks a rule :(
Edit: Actually, I misread it as javascript "strict mode". Found this: https://github.com/angular/angular/issues/34970#issuecomment-581838896 workaround which I will try. We do use universal, but I just want it to compile for now.
Edit2: fails with same message and with workaround so I guess something else is breaking. At this point, I feel I am getting derailed and investigating unrelated stuff. Iwill go out and scream for a while now, but will keep testing tomorrow!
Here is a "minimal" reproduction @alan-agius4, @petebacondarwin (sorry for tagging if you are busy / the wrong people for this)
https://github.com/submarines-and/angular-localize-bug
To replicate run yarn r
which will loop build the project in a docker image and then check all js-files for the word $localize
(which only main should have). I did this to try to emulate our CI system. I hope you will forgive for not having a super slim image, I basically added things until it crashed but it still takes 5-10 runs for me before it does so.
I will try to make it smaller tomorrow. Today I need to catch up on some sprint work (because my manager hates fun taks :D )
If anyone knows a good place to put logging in the angular devkit code, send me the function name or such I will add it to the project above and then post the logs here from when it crashes.
My best (but unqualified) guess right now is that it has to do with: 1) Something with docker/linux 2) Build cache (I THINK I can only replicate it when I have just reset node_modules)
Both these makes sense if everyone is only seeing it in CI systems
@submarines-and - Awesome. This should really help us get to the bottom of it. I might not have time today but will try to reproduce locally before the weekend.
🐞 Bug report
Command (mark with an
x
)Is this a regression?
Possible, just if it is, we don't know in which version was working 100% sure and in which one stopped working
Description
App with AOT compilation. When building the production application, sometimes (randomly) the build seems to not replace properly the
$localize
occurrences with the translations. Therefore seems to cause an issue in runtime, as it search for$localize
which is not defined.We migrated to use
$localize
with thecli
commandng add @angular/localize
. We thought would be a module / component issue, but we find this issue in random modules / components. And the worst part is that with the same code (no changes at all) if you build it 2 times, you can get a perfect build, or this error in random modules.🔬 Minimal Reproduction
We cannot know how to reproduce it (as we manage to reproduce it - by building app several times - but we don't know how to reproduce it in another project). (This is in production builds of angular with AOT) Its totally random and we don't know what depends on. If you need anything of code, we could provide it, but would be by private as, we are not allowed to post it publicly by confidentiality limitations.
🔥 Exception or Error
🌍 Your Environment
Anything else relevant?
This is our packages.json versions
This is our polyfills.ts file: