Extracted translations include unused translations
See original GitHub issueCommand
extract-i18n
Is this a regression?
- Yes, this behavior used to work in the previous version
The previous version in which this bug was not present was
No response
Description
The extracted translations from the extract-i18n
builder include trans-units from unused templates/components unless these components import and use the $localize
function. This is the case for both app components as well as library components.
Minimal Reproduction
Reproduction repository: https://github.com/fischeversenker/ng-decorator-tree-shaking
(don’t be fooled by the name of the repository. I re-used an existing repro-repo.)
In essence:
- Import a library module that exports components with translatable strings but where the components don’t use the
$localize
function - don’t use any of these components in your app
- run
extract-i18n
on your app and verify that the generatedmessages.xlf
file contains unused translations from these components
Exception or Error
No response
Your Environment
Angular CLI: 14.2.6
Node: 16.16.0
Package Manager: yarn 1.22.19
OS: linux x64
Angular: 14.2.7
... animations, common, compiler, compiler-cli, core, forms
... localize, platform-browser, platform-browser-dynamic, router
Package Version
---------------------------------------------------------
@angular-devkit/architect 0.1402.6
@angular-devkit/build-angular 14.2.6
@angular-devkit/core 14.2.6
@angular-devkit/schematics 14.2.6
@angular/cli 14.2.6
@schematics/angular 14.2.6
ng-packagr 14.2.2
rxjs 7.5.7
typescript 4.7.4
Anything else relevant?
No response
Issue Analytics
- State:
- Created a year ago
- Comments:9 (2 by maintainers)
Top Results From Across the Web
i18next check for unused translation - Stack Overflow
List unused translations keys. There is i18-unused library which prints list of all unused keys for all json files.
Read more >Unused state of some translations which are really used #29085
Strange but I found above unused states, that translations with unused state I removed, now has state missing, if I return them, they...
Read more >Extracting translations - i18next documentation
Extracting translations · 1) Adding new strings manually · 2) Using an extraction tool · 3) Runtime extraction.
Read more >[Resolved] Removing unused translations - WPML
I have set the option to remove unused translations if a post is deleted, but I still have a lot of translated strings...
Read more >How to Find Missing or Unused I18n Translations - Thoughtbot
% i18n-tasks unused. and · % i18n-tasks missing. Want the test suite to fail when there are missing or unused tasks? Add i18n-tasks...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
This is currently working as intended as since tree-shaking and dead-code elimination are not done during extraction.
Let me bring this up with the rest of the team.
Ah sorry @fischeversenker, didn’t see the difference with
$localize
. That’s definitely weird given that I’m pretty sure<div i18n>Hello, World</div>
just generates a$localize
call under the hood, so technically both components should use$localize
, regardless of how you import it. Chatting with @AndrewKushnir and looking at the compiled output for these libraries, they don’t actually generate$localize
, but I think that’s because they use partial compilation, while i18n only applies to full compilations. Song extract-i18n
is probably doing a full compilation and generating$localize
calls.If we switch
tsconfig.lib.prod.json
to usecompilationMode: 'full'
we can see the actual output being processed:Both of these use the imported
$localize
, however$localize
is actually a global definition with special semantics to the Angular compiler. So I think this shadowing is actually confusing the compiler. If we useimport '@angular/localize';
instead and rely on the global declaration, these two messages extract correctly. That’s probably the workaround you need @fischeversenker.Beyond that immediate problem, this does bring up a few rough edges we can hopefully improve.
@angular/localize
or@angular/localize/init
. The compiler should do this for you, though it’s necessary right now to pass type checking. @alan-agius4 already has a PR for that, so hopefully you should be able to drop the@angular/localize
import altogether.$localize
ori0
since it can lead to situations like this. One nuance is that some users may want to define the$localize
implementation at runtime. I have no idea if anyone actually does this, but even then I think you could probably do it without shadowing$localize
(something likewindow.$localize = () => { /* ... */ }
).$localize
export from@angular/localize/init
, given that it’s just misleading for users to import and then get confused when it doesn’t extract. The global declaration should be the only way to use this symbol. Maybe we should remove it from@angular/localize
as well?@angular/localize
and@angular/localize/init
given that exposing$localize
is their whole purpose. Since that’s really an implementation detail of the compiler. The package might need to exist, but it should at least be internal to Angular. There were some discussions some time ago about possibly supporting$localize
for non-Angular usages, not sure whatever happened to that or if anyone does it today.I’ll file some issues in the main repository for these and see if we can improve this particular footgun. Thanks for pointing this out, lots of interesting tidbits here. 😁