question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Extracted translations include unused translations

See original GitHub issue

Command

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 generated messages.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:closed
  • Created a year ago
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

4reactions
alan-agius4commented, Oct 27, 2022

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.

2reactions
dgp1130commented, Dec 20, 2022

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. So ng extract-i18n is probably doing a full compilation and generating $localize calls.

If we switch tsconfig.lib.prod.json to use compilationMode: 'full' we can see the actual output being processed:

import { Component } from '@angular/core'; // Carried over from user-space.
import { $localize } from '@angular/localize/init'; // Carried over from user-space.
import * as i0 from "@angular/core"; // Added by compiler.

// Carried over from user-space.
export class LibraryWithLocalizeComponent {
    constructor() {
        this.name = $localize `my name`;
    }
}

// ...

// Added by compiler (simplified).
LibraryWithLocalizeComponent.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({
  // ...
  consts: () => {
    const = $localize `unused library template with localize`;
    return [i18n_0];
  },
});

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 use import '@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.

  1. Users really shouldn’t import @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.
  2. We should ban users shadowing built-in symbols like $localize or i0 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 like window.$localize = () => { /* ... */ }).
  3. We should remove the $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?
  4. If we move forward with 3., we should also consider deleting (or at least stop documenting) @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. 😁

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found