i18n-polyfill icon indicating copy to clipboard operation
i18n-polyfill copied to clipboard

AOT bundle files

Open rwlogel opened this issue 6 years ago • 11 comments

We have been using the angular-cli ng xi18n for our project with much success. I just setup our system to use the runtime I18n service and was able to get it working in both JIT for development server and the AOT for our released code.

On thing I ran into was that I noticed the main bundle size grew dramatically. After looking at it I found that our entire xlf file was embedded in the javascript adding over 500K. Currently our project only has a couple of runtime translations so 99% of the embedded file is only used at compile time.

I realize that this project is a temporary solution until this feature is incorporated into the existing translation stuff, but I had a few questions:

  1. Is there any plans to optimize the AOT compile so that it only extracts the translations required at runtime and stores them in the bundle.
  2. Will the storage format of the AOT translations be changed to something smaller like a Object or Map so the xlf file only needs to be processed during the build.

rwlogel avatar Jun 07 '18 20:06 rwlogel

Hello, I'm not planning on adding new features or optimizations to this library, because like you said it's only a temporary solution. That being said, the same issues will arise with the official Angular i18n solution. How come your xlf file is 500k?!! That seems huge! I guess that uglify cannot compress xml like it can compress js... I'm been trying to convince our team to support some kind of json format for a long time now, it would indeed fix the issue of having to ship a lot of serializer code to decode the xml. It would also probably reduce the size a bit, json is less verbose than xml. You could probably try to switch to xmb/xtb, it's a lot less verbose than xliff (xml) too, maybe that would help? As for the first point it wouldn't be possible for Angular i18n because both the service and the template translations will be handled at runtime.

ocombe avatar Jun 08 '18 08:06 ocombe

Our project has around 300 translated strings for english and french the raw xlf files are around 250K. After investigating the bundled code further it appears that it is saving the english and french file in the bundle even though we are building the bundle once for english and once for french as described in the documentation.

One way to reduce the size is during the load operation it could remove the context-group tag before embedding the files in the in the bundle.

rwlogel avatar Jun 08 '18 13:06 rwlogel

I've been looking into this some more and have made a few observations, I wonder if you could comment on if we are doing something wrong with our setup.

In our current setup we make no reference to TRANSLATIONS in our main.ts nor our app.module.ts . When we build our release versions we do: ng build --prod --base-href /en --deploy-url /en --i18n-file=src/i18n/messages.en.xlf --i18n-format=xlf --locale=en" for each language we support. The resulting bundle files do not contain the xliff file but all the templates are translated correctly. Passing the similar arguments to the ng serve command doesn't do any translations, which we were willing to accept. If we add TRANSLATIONS to the main.ts then translations also work with ng serve, but doing this will embed the xliff file in the bundles.

Now trying to use the i18n-polyfill we have had to setup TRANSLATIONS in the app.module.ts so that the I18n service would work. Doings this also causes the xlf files to be embedded in the bundles.

So to summerize it appears that:

  1. For JIT build, template translations require the TRANSLATIONS are provided in the main.ts
  2. For JIT and AOT build, I18n service requires the TRANSLATIONS are provided in the app.module.ts
  3. If TRANSLATIONS are provided in main.ts or app.module.ts then the xliff file is embeded in the AOT bundle.

rwlogel avatar Jun 11 '18 17:06 rwlogel

Yes that's correct. The reason is that this is an external library, not part of the framework itself. When Angular is built, it will replace texts in the templates, but not in the service (since it's just another external lib for the compiler).

When runtime i18n lands in the framework for Angular v7, then the built-in service will use the same translations for code and templates.

ocombe avatar Jun 11 '18 17:06 ocombe

Thank you for all the information, I had one last confirmation question.

So when v7 releases will it be possible to do ng build --prod --i18n-file=... and have it work without specifying any TRANSLATIONS provider or embedding the entire XLF file in the main bundle.

rwlogel avatar Jun 11 '18 19:06 rwlogel

Came here to ask the same thing. Actually it's much worse for us.

The XLIFF 1.2 is 340kb and our bundle grew from 1.8mb to 3.6mb (doubled). Thought there was something wrong when I saw the XLIFF actually embedded into my bundle but see that it's expected :(

Created 2x bundles (english and french) and I can see both messages(en).xlf and messages(fr).xlf are both embedded in both bundles.

Our build command looks like:

./node_modules/.bin/ng "build" "--app=mysite" "--environment=prod" "--i18n-file=src/mysite/locale/messages(en).xlf" "--locale=en" "--missing-translation=ignore" "--output-path=dist/mysite/en" "--prod" "--base-href=/en/" "--deploy-url=/en/"

Bundle looks like:

webpackJsonp(["main"],{"+1m/":function(t,e){t.exports='<?xml version="1.0"?>\n<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">\n  <file source-language="af" datatype="plaintext" original="ng2.template" target-language="en-US">\n    <body>\n      <trans-unit id="5e6548eb9cbb0467c307b4079bc989cd21f44355" datatype="html">\n        <source>This field is required</source><target>This field is required</target>\n        <context-group purpose="location">\n          <context context-type="sourcefile">../app/shared/form-errors/form-errors.component.ts</context>\n          <context context-type="linenumber">4</context>\n        </context-group>\n      </trans-unit>\n      <trans-unit id="bab5e4d5a5641055b05160f8cdc95de7a2289140" datatype="html">\n        <source>This field must be an email</source>

Was expecting a JSON map like:

{
  "5e6548eb9cbb0467c307b4079bc989cd21f44355": "This field is required"
}

Later down the line I see this in my bundle as well. Maybe these could be directly replaced with i18n being a NOOP?

this.required=this.i18n("This field is required")

So I guess a couple of things:

  • Is it normal that my single-language bundle includes 2 languages?
  • I guess we don't need to store context-group or note and extra metadata like equiv-text. We'll clean those manually but is there any idea to automate it? (created a script to remove context-group tags and XLF went from 340 to 170kb)
  • If trans-unit has a unique ID can I also delete the source tags (drops from 170 -> 119kb. Minify also drops it further to 94kb)
  • 99% of our translations are template driven and there's probably like... 20 strings which are translated in TS. Could we loop through our XML and leave only those dynamically translated?
  • Wouldn't XML parsing in JavaScript be really heavy and unperformant?
  • JSON would be a billion times smaller than XML :)

Maybe after all of the post-processing, 94kb extra within the bundle would be an acceptable trade-off for all the savings generated from reducing HTML

intellix avatar Aug 01 '18 16:08 intellix

@intellix about a script to remove <context-group>, do you have it? Is it available on this repo? My XLF file has about 300 strings, and removing them manually will be a big pain

rodrigozietlow avatar Jan 31 '19 12:01 rodrigozietlow

in jetbrain ide's it's quite simple to replace all context-groups with a regex search => "[\s\S]*?</context-group>"

dyingangel666 avatar Oct 28 '19 12:10 dyingangel666

This is the regex for the vscode, in case someone is interested in reducing the bundle size: <context-group[\n\s\S]*?</context-group> You can remove other tags by replacing context-group with the tag name

cngarkar avatar Feb 21 '20 04:02 cngarkar

@cngarkar Basically the same as i already commented. But also to mention, with angular 9 use the newly written i18n tools and it's no longer necessary to use the i18n polyfill

dyingangel666 avatar Feb 21 '20 12:02 dyingangel666

Thanks @dyingangel666, that was helpful. For others on how to use $localize to use translation in ts, this post can be useful: https://blog.ninja-squad.com/2019/12/10/angular-localize/

cngarkar avatar Feb 22 '20 22:02 cngarkar