angular-cli icon indicating copy to clipboard operation
angular-cli copied to clipboard

New build system application missing i18n strings from templates

Open Xriuk opened this issue 3 months ago • 4 comments

Which @angular/* package(s) are the source of the bug?

compiler-cli

Is this a regression?

No

Description

I have opted in to the new build system, this is my angular.json after the upgrade:

{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "my-lib": {
            "projectType": "application",
            "schematics": {
                "@schematics/angular:component": {
                    "style": "scss"
                },
                "@schematics/angular:application": {
                    "strict": true
                }
            },
            "root": "",
            "sourceRoot": "src",
            "prefix": "app",
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:application",
                    "options": {
                        "outputPath": {
                          "base": "dist/my-lib"
                        },
						"i18nMissingTranslation": "ignore",
                        "deleteOutputPath": true,
                        "index": "src/index.html",
                        "polyfills": [
							"@angular/localize/init",
                          	"src/polyfills.ts"
                        ],
                        "tsConfig": "tsconfig.app.json",
                        "inlineStyleLanguage": "scss",
                        "assets": [
                            "src/favicon.ico",
                            "src/assets"
                        ],
                        "styles": [
                            "src/styles.scss"
                        ],
                        "scripts": [
                            "./node_modules/quill/dist/quill.min.js"
                        ],
                        "stylePreprocessorOptions": {
                            "includePaths": [
                                "node_modules",
                                "src/app"
                            ]
                        },
                        "allowedCommonJsDependencies": [
                            "@uppy/core",
                            "@uppy/tus",
                            "url-parse",
                            "lodash",
                            "namespace-emitter",
                            "classnames",
                            "is-shallow-equal",
                            "cropperjs",
                            "quill",
                            "ts-deepmerge"
                        ],
                        "browser": "src/main.ts"
                    },
                    "configurations": {
                        "production": {
                            "fileReplacements": [
                                {
                                    "replace": "src/environments/environment.ts",
                                    "with": "src/environments/environment.prod.ts"
                                }
                            ],
                            "outputHashing": "all"
                        },
                        "development": {
                            "aot": false,
                            "optimization": false,
                            "extractLicenses": false,
                            "sourceMap": true,
                            "namedChunks": true
                        }
                    },
                    "defaultConfiguration": "development"
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "configurations": {
                        "production": {
                          "buildTarget": "my-lib:build:production"
                        },
                        "development": {
                          "buildTarget": "my-lib:build:development"
                        }
                    },
                    "defaultConfiguration": "development",
                    "options": {
                        "sslCert": "./node_modules/browser-sync/certs/server.crt",
                        "sslKey": "./node_modules/browser-sync/certs/server.key",
                        "ssl": true,
                        "liveReload": false
                    }
                },
                "extract-i18n": {
                    "builder": "@angular-devkit/build-angular:extract-i18n",
                    "options": {
                      "buildTarget": "my-lib:build"
                    }
                },
                "test": {
                    "builder": "@angular-devkit/build-angular:karma",
                    "options": {
                        "main": "src/test/test.ts",
                        "tsConfig": "tsconfig.spec.json",
                        "karmaConfig": "karma.conf.js",
                        "styles": [
                            "src/styles.scss"
                        ],
                        "stylePreprocessorOptions": {
                            "includePaths": [
                                "node_modules",
                                "src/app"
                            ]
                        }
                    }
                }
            }
        }
    },
    "cli": {
        "analytics": false
    }
}

The project is serving and building but when I try to extract the translation strings with:

ng extract-i18n --format=json

At the end I get:

Extraction Complete. (Messages: 713)

In fact with my previous setup:

{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "version": 1,
    "newProjectRoot": "projects",
    "projects": {
        "my-lib": {
            "projectType": "application",
            "schematics": {
                "@schematics/angular:component": {
                    "style": "scss"
                },
                "@schematics/angular:application": {
                    "strict": true
                }
            },
            "root": "",
            "sourceRoot": "src",
            "prefix": "app",
            "architect": {
                "build": {
                    "builder": "@angular-devkit/build-angular:browser",
                    "options": {
                        "outputPath": "dist/my-lib",
						"i18nMissingTranslation": "ignore",
                        "deleteOutputPath": true,
                        "index": "src/index.html",
                        "main": "src/main.ts",
                        "polyfills": "src/polyfills.ts",
                        "tsConfig": "tsconfig.app.json",
                        "inlineStyleLanguage": "scss",
                        "assets": [
                            "src/favicon.ico",
                            "src/assets"
                        ],
                        "styles": [
                            "src/styles.scss"
                        ],
                        "scripts": [
                            "./node_modules/quill/dist/quill.min.js"
                        ],
                        "stylePreprocessorOptions": {
                            "includePaths": [
                                "node_modules",
                                "src/app"
                            ]
                        },
                        "allowedCommonJsDependencies": [
                            "@uppy/core",
                            "@uppy/tus",
                            "url-parse",
                            "lodash",
                            "namespace-emitter",
                            "classnames",
                            "is-shallow-equal",
                            "cropperjs",
                            "quill",
                            "ts-deepmerge"
                        ]
                    },
                    "configurations": {
                        "production": {
                            "fileReplacements": [
                                {
                                    "replace": "src/environments/environment.ts",
                                    "with": "src/environments/environment.prod.ts"
                                }
                            ],
                            "outputHashing": "all"
                        },
                        "development": {
                            "aot": false,
							"buildOptimizer": false,
                            "optimization": false,
							"vendorChunk": true,
                            "extractLicenses": false,
                            "sourceMap": true,
                            "namedChunks": true
                        }
                    },
                    "defaultConfiguration": "development"
                },
                "serve": {
                    "builder": "@angular-devkit/build-angular:dev-server",
                    "configurations": {
                        "production": {
                          "buildTarget": "my-lib:build:production"
                        },
                        "development": {
                          "buildTarget": "my-lib:build:development"
                        }
                    },
                    "defaultConfiguration": "development",
                    "options": {
                        "sslCert": "./node_modules/browser-sync/certs/server.crt",
                        "sslKey": "./node_modules/browser-sync/certs/server.key",
                        "ssl": true,
                        "liveReload": false
                    }
                },
                "extract-i18n": {
                    "builder": "@angular-devkit/build-angular:extract-i18n",
                    "options": {
                      "buildTarget": "my-lib:build"
                    }
                },
                "test": {
                    "builder": "@angular-devkit/build-angular:karma",
                    "options": {
                        "main": "src/test/test.ts",
                        "tsConfig": "tsconfig.spec.json",
                        "karmaConfig": "karma.conf.js",
                        "styles": [
                            "src/styles.scss"
                        ],
                        "stylePreprocessorOptions": {
                            "includePaths": [
                                "node_modules",
                                "src/app"
                            ]
                        }
                    }
                }
            }
        }
    },
    "cli": {
        "analytics": false
    }
}

The same i18n extraction command gets me:

Extraction Complete. (Messages: 1602)

I notices that with the application builder translations are missing from the component html templates, while $localization used inside TS files are recognized.

Please provide a link to a minimal reproduction of the bug

No response

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 20.3.1
Node: 20.19.5

Anything else?

No response

Xriuk avatar Sep 16 '25 22:09 Xriuk

This seems like a bug but we'll need to look at a reproduction to find and fix the problem. Can you setup a minimal repro please?

You can read here why this is needed. A good way to make a minimal repro is to create a new app via ng new repro-app and adding the minimum possible code to show the problem. Then you can push this repository to github and link it here.

This might be related to your directory structure so its really important to get an accurate repro to diagnose this.

alan-agius4 avatar Sep 17 '25 06:09 alan-agius4

Here's the repro: https://github.com/Xriuk/repro-31227 I found out that the culprit is architect.build.configurations.development.aot which when set to false gets treated differently by the two build systems, the old and the new one. If I set it to true it works in the new build system too, but when false it just seems to ignore HTML template translation strings.

Xriuk avatar Sep 17 '25 11:09 Xriuk

Also I am noticing that with aot = true it's still behaving a bit differently in my original project, as I get:

Extraction Complete. (Messages: 1569)

Looking at the strings left out it seems that they are localizations which are imported but not used, so it seems some optimization of the AOT compiler, so it shouldn't be a big deal, but just to let you know.

Xriuk avatar Sep 17 '25 12:09 Xriuk

The later is expected behavior. Only strings used within an application are extracted for translation. Translation costs can be significant for large applications and translating strings that are not actually part of the shipped application can unduly increase those costs.

clydin avatar Sep 17 '25 13:09 clydin