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

Angular 9 - Unhandled Promise rejection: this._input.charCodeAt is not a function

Open kpicavet opened this issue 5 years ago • 25 comments

Hi guys, Today we tried upgrading to Angular 9 and It required us to install @angular/localize ( not sure if this is related)

However, after running ng update @angular/core @angular/cli, we don’t seem to hit any errors except for one that seems to be coming from this polyfill.

The full error is:

zone.js:699 Unhandled Promise rejection: this._input.charCodeAt is not a function ; Zone: <root> ; Task: Promise.then ; Value: TypeError: this._input.charCodeAt is not a function
    at Tokenizer.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.Tokenizer._advance (ngx-translate-i18n-polyfill.js:1370)
    at new Tokenizer (ngx-translate-i18n-polyfill.js:1184)
    at tokenize (ngx-translate-i18n-polyfill.js:1136)
    at Parser.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.Parser.parse (ngx-translate-i18n-polyfill.js:2160)
    at XliffParser.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.XliffParser.parse (ngx-translate-i18n-polyfill.js:3651)
    at xliffLoadToI18n (ngx-translate-i18n-polyfill.js:3622)
    at Function.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.TranslationBundle.load (ngx-translate-i18n-polyfill.js:7748)
    at new I18n (ngx-translate-i18n-polyfill.js:8478)
    at Function.I18n_Factory [as ɵfac] (ngx-translate-i18n-polyfill.js:8504)
    at Object.factory (ngx-translate-i18n-polyfill.js:8505) TypeError: this._input.charCodeAt is not a function
    at Tokenizer.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.Tokenizer._advance (http://localhost:4000/vendor.js:86129:71)
    at new Tokenizer (http://localhost:4000/vendor.js:85943:14)
    at tokenize (http://localhost:4000/vendor.js:85895:12)
    at Parser.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.Parser.parse (http://localhost:4000/vendor.js:86919:48)
    at XliffParser.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.XliffParser.parse (http://localhost:4000/vendor.js:88410:71)
    at xliffLoadToI18n (http://localhost:4000/vendor.js:88381:26)
    at Function.push../node_modules/@ngx-translate/i18n-polyfill/__ivy_ngcc__/fesm5/ngx-translate-i18n-polyfill.js.TranslationBundle.load (http://localhost:4000/vendor.js:92507:49)
    at new I18n (http://localhost:4000/vendor.js:93237:69)
    at Function.I18n_Factory [as ɵfac] (http://localhost:4000/vendor.js:93263:47)
    at Object.factory (http://localhost:4000/vendor.js:93264:130)

Did anyone stumble on this same issue perhaps, while updating to Angular 9 - and has a solution for it?

Or does this require an update on the polyfill from your side, @ocombe

Big thanks in advance!

kpicavet avatar Feb 12 '20 11:02 kpicavet

I have the same. No idea how to do in-code translations so far...

obiwan007 avatar Feb 14 '20 06:02 obiwan007

Maybe this helps. Do this:

$localize`::@@some-id:source text`

instead of

this.i18n({ value: 'source text', id: 'some-id' });

No imports needed. $localize is global. You can remove @ngx-translate/i18n-polyfill from your dependencies. Be sure to remove I18n imports everywhere.

HeroBart avatar Feb 14 '20 14:02 HeroBart

Nice but unfortunately this seems not to be documented yet. Any idea on how to autogenerate the xlf files from the *.ts files? As far as I know they are not yet parsing them.

obiwan007 avatar Feb 14 '20 15:02 obiwan007

Nice but unfortunately this seems not to be documented yet. Any idea on how to autogenerate the xlf files from the *.ts files? As far as I know they are not yet parsing them.

I think you are correct. We are having the same problems here

StevenSerrien avatar Feb 17 '20 11:02 StevenSerrien

You're right :( See: https://github.com/angular/angular-cli/issues/16375 Seems like it'll be fixed in 9.1.

HeroBart avatar Feb 17 '20 12:02 HeroBart

I don't think there's a point in making i18n polyfill work with Angular v9 since @angular/localize is the official replacement. I'll take a PR to fix the bug, but I don't really have the time to work on this lib now...

If you need to extract from code, you can use https://github.com/loclapp/locl/tree/master/libs/cli until the official Angular cli implements it

ocombe avatar Feb 17 '20 14:02 ocombe

@ocombe Thanks for the reaction! Will have a look at Locl CLI.

kpicavet avatar Feb 17 '20 14:02 kpicavet

@ocombe We loved the polyfill but are migrating to angular 9. In that regard it be super helpful for others if you could update the polyfill documentation to point out that:

  • angular 9 now supports code based localization via $localize`::@@some-id:source text`
  • and that you have created a tool that performs the extraction (since angular CLI does not yet)

While the information is available, it took us a while to assemble it all together.

ntziolis avatar Mar 02 '20 15:03 ntziolis

@ocombe You could include short migration guide:

$localize:`meaning|description@@id:message`
  1. Replace all occurrences with the new syntax:
    • You can use this regex in VSCode: Search:
      (.*)this\.i18n\(\{\n[\s\/]*meaning:[\s\n]*'(.*)',\n[\s\/]*description:[\s\n]*'(.*)',\n[\s\/]*id:[\s\n]*'(.*)',\n[\s\/]*value:[\s\n]*['`](.*)['`]\n[\s\/]*\}\)
      
      Replace:
      $1$localize`:$5|$3@@$4:$2`
      
  2. Remove all i18n usings such as:
import { I18n } from '@ngx-translate/i18n-polyfill';
private i18n: I18n

Some Caveats:

  • be aware of existing tranlation functions that are named differently or whose attributes are in a different order
  • be aware that meaning, description and id can no longer contain :-symbol
  • be aware that message should not contain ```

DanielHabenicht avatar Mar 03 '20 22:03 DanielHabenicht

@DanielHabenicht any chance that you could make a PR for that? 😅

ocombe avatar Mar 05 '20 14:03 ocombe

Whizz, Whizz, here it is: #90

DanielHabenicht avatar Mar 05 '20 15:03 DanielHabenicht

@ocombe You could include short migration guide:

$localize:`meaning|description@@id:message`
  1. Replace all occurrences with the new syntax:

    • You can use this regex in VSCode: Search:

      (.*)this\.i18n\(\{\n[\s\/]*meaning:[\s\n]*'(.*)',\n[\s\/]*description:[\s\n]*'(.*)',\n[\s\/]*id:[\s\n]*'(.*)',\n[\s\/]*value:[\s\n]*['`](.*)['`]\n[\s\/]*\}\)
      

      Replace:

      $1$localize`:$5|$3@@$4:$2`
      
  2. Remove all i18n usings such as:

import { I18n } from '@ngx-translate/i18n-polyfill';
private i18n: I18n

Some Caveats:

  • be aware of existing tranlation functions that are named differently or whose attributes are in a different order
  • be aware that meaning, description and id can no longer contain :-symbol
  • be aware that message should not contain ```

Here is mine version without meaning and description. FYI you need to add /r to get yours work if you use line breaks in your translations. (.*)this\.i18n\([\s\r]*\{[\s\r\/]*value:[\s\r]*['](.*)['],[\s\r]*id:[\s\r]*'(.*)'[\s\r]*\}[\s\r]*\) replaced by $1$localize:@@$3:$2``

JanneHarju avatar Mar 19 '20 11:03 JanneHarju

@JanneHarju You can add it to the front page, just make a PR. :)

DanielHabenicht avatar Mar 19 '20 13:03 DanielHabenicht

@DanielHabenicht and @ocombe I have made node script which handle most of the cases to convert every ts file in specific folder to new format. It removes I18n import It removes I18n from component construction parameter It converts this.i18n() to new format (we only used id and value so you need extend to handle it with description and meaning if you will) It can handle i18n data interpolation syntax change (from {{variable}} to ${variable})

Still there is probably several cases which I haven't figured yet but this is good start.

It uses this tool https://www.npmjs.com/package/replace-in-file

Best scenario is that you add something similar to update schema or add schema of @angular/localize package.

I haven't done much PR:s in github and I have no idea where to but that script for you in folder hierarchy of this project. So if you want it can you help little bit?

JanneHarju avatar Mar 23 '20 21:03 JanneHarju

Maybe share it here in a first step. Maybe @ocombe can integrate it into the update logic.

DanielHabenicht avatar Mar 23 '20 21:03 DanielHabenicht

Code block didn't work because there is backticks in code. So sorry for bad format. And On more assumption for this is that interpolated variable has same name as variable in code.

const replace = require('replace-in-file'); // example command node .\localization_migration.js projects/KmsFrontend/**/.ts const path = process.argv.slice(2)[0]; console.log(path); const options1 = { //Glob(s) files: [path], //Replacement to make (string or regex) //this.i18n({value:'asd', id:'adssad'}); from: /(.)this.i18n([\s\r]{[\s\r]value:[\s\r]['](.*)['],[\s\r]id:[\s\r]'(.)'[\s\r]}([\s\r],[\s\r]{.})[\s\r])/g, to: "$1$localize:@@$3:$2" };

replace(options1, (error, changedFiles1) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles1.filter(x => x.hasChanged).length, this.i18n({value:'asd', id:'adssad'}); changed); //this.i18n({id:'adssad', value:'asd'}); options1.from = /(.)this.i18n([\s\r]{[\s\r]id:[\s\r]'(.)'[\s\r],[\s\r]value:[\s\r]['](.*)['][\s\r]}([\s\r],[\s\r]{.})[\s\r])/g; options1.to = "$1$localize:@@$2:$3"; replace(options1, (error, changedFiles2) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles2.filter(x => x.hasChanged).length, this.i18n({id:'adssad', value:'asd'}); changed); //i18n({value:'asd', id:'adssad'}); options1.from = /(.)i18n([\s\r]{[\s\r]value:[\s\r]['](.*)['],[\s\r]id:[\s\r]'(.)'[\s\r]}([\s\r],[\s\r]{.})[\s\r])/g; options1.to = "$1$localize:@@$3:$2"; replace(options1, (error, changedFiles3) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles3.filter(x => x.hasChanged).length, i18n({value:'asd', id:'adssad'}); changed); //i18n({id:'adssad', value:'asd'}); options1.from = /(.)i18n([\s\r]{[\s\r]id:[\s\r]'(.)'[\s\r],[\s\r]value:[\s\r]['](.*)['][\s\r]}([\s\r],[\s\r]{.})[\s\r])/g; options1.to = "$1$localize:@@$2:$3"; replace(options1, (error, changedFiles4) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles4.filter(x => x.hasChanged).length, i18n({id:'adssad', value:'asd'}); changed); //remove imports options1.from = "import { I18n } from '@ngx-translate/i18n-polyfill';"; options1.to = ''; replace(options1, (error, changedFiles5) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles5.filter(x => x.hasChanged).length, import { I18n } from '@ngx-translate/i18n-polyfill'; removed); //remove component construction parameters options1.from = /(,[\s\r]private i18n: I18n[\s\r]|[\s\r]private i18n: I18n,[\s\r]|private i18n: I18n)/g; options1.to = ''; replace(options1, (error, changedFiles6) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles6.filter(x => x.hasChanged).length, omponent construction parameters removed); //remove from test initialization and modules options1.from = /[\s\r]+I18n[,]/g; options1.to = ''; replace(options1, (error, changedFiles7) => { if (error) { return console.error('Error occurred:', error); } console.log(changedFiles7.filter(x => x.hasChanged).length, remove from module imports); // replace interpolations from {{asdasd}} to ${asdasd} options1.from = /(.*$localize)(.*)(\{\{)(.*)(\}\})(.*)/g; options1.to = "$1$2${$4}$6`"; replaceInterpolations(replace, options1); }); }); }); }); }); }); });

function replaceInterpolations(replace, options1) { replace(options1, (error, changedFiles8) => { if (error) { return console.error('Error occurred:', error); } const filesChanged = changedFiles8.filter(x => x.hasChanged).length; console.log(filesChanged, "replace interpolations from {{asdasd}} to ${asdasd}"); if (filesChanged > 0) { replaceInterpolations(replace, options1); } }); }

JanneHarju avatar Mar 24 '20 12:03 JanneHarju

Sorry there was one bug in replace part of every this.i18n part. I was missing : character. I will update it. And FYI github changed my `` in my answer to it own syntax. so thats it, the whole script is unusable. sorry for that.

JanneHarju avatar Mar 26 '20 15:03 JanneHarju

Maybe make gist from it, it's easier to manage and you can update it.

DanielHabenicht avatar Mar 26 '20 16:03 DanielHabenicht

That was new feature for me. Thanks. Here it is. It assume that you have named I18n by i18n. In my case some has used i18 for some reason. ANd also some has used public instead of private. https://gist.github.com/JanneHarju/fd17609a81d50ae34f6b32c3c5d8284a

JanneHarju avatar Mar 27 '20 07:03 JanneHarju

Hi guys, Please provide a short example, how can I use $localize for plurals? How would XLF file look like with plurals? For this example: $localize':@@text.users:You have ${users.length}:count: (user/users ???)';

andrewmakar77 avatar Mar 30 '20 23:03 andrewmakar77

@andrewmakar77 For HTML it is {node.supervisors.length, plural, =0 { No Supervisor } =1 { Supervisor: } other { Supervisors: }} I don't know how that translates to code.

DanielHabenicht avatar Mar 31 '20 06:03 DanielHabenicht

Maybe if you but that plural syntax what @DanielHabenicht suggest to html and then build application with --localize=false I think every i18n html translations are converted to use $localize in build process if I remember correct. Then find from dist folder that translation key and then tell us what you see.

JanneHarju avatar Mar 31 '20 07:03 JanneHarju

Is there any updates ? I use angular with webpack ...

If I use template application is compiled, but if I use templateUrl it provides the same error:

Unhandled Promise rejection: this.input.charCodeAt is not a function ; Zone: <root> ; Task: Promise.then ; Value: TypeError: this.input.charCodeAt is not a function

Is there a way to workaround it ?

redradist avatar Mar 03 '22 15:03 redradist

@redradist I have no idea what are you talking about. But just info you that this is no more maintained https://github.com/ngx-translate/i18n-polyfill#heavy_exclamation_mark-out-of-date-you-should-use-angularlocalize Are you which Angular version. If above 9 then use $localize in your ts-side translations and remove I18n-injections from everywhere in TypeScript. Template(html)-side will stay as is.

JanneHarju avatar Mar 07 '22 07:03 JanneHarju

Hi @redradist I am facing exact same issue, after upgrading Webpack and angular. I am sure it is not related to i18, but a loader issue. Can you please let me know if you had a fix for this? I have raised a question in StackOverflow for the same. https://stackoverflow.com/questions/76771169/unable-to-mount-single-spa-angular-application-after-angular-upgrade

madhavansu avatar Jul 30 '23 17:07 madhavansu