i18n-polyfill
i18n-polyfill copied to clipboard
Angular 9 - Unhandled Promise rejection: this._input.charCodeAt is not a function
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!
I have the same. No idea how to do in-code translations so far...
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.
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.
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
You're right :( See: https://github.com/angular/angular-cli/issues/16375 Seems like it'll be fixed in 9.1.
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 Thanks for the reaction! Will have a look at Locl CLI.
@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.
@ocombe You could include short migration guide:
$localize:`meaning|description@@id:message`
- Replace all occurrences with the new syntax:
- You can use this regex in VSCode:
Search:
Replace:(.*)this\.i18n\(\{\n[\s\/]*meaning:[\s\n]*'(.*)',\n[\s\/]*description:[\s\n]*'(.*)',\n[\s\/]*id:[\s\n]*'(.*)',\n[\s\/]*value:[\s\n]*['`](.*)['`]\n[\s\/]*\}\)
$1$localize`:$5|$3@@$4:$2`
- You can use this regex in VSCode:
Search:
- 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
andid
can no longer contain:
-symbol - be aware that
message
should not contain ```
@DanielHabenicht any chance that you could make a PR for that? 😅
Whizz, Whizz, here it is: #90
@ocombe You could include short migration guide:
$localize:`meaning|description@@id:message`
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`
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
andid
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 You can add it to the front page, just make a PR. :)
@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?
Maybe share it here in a first step. Maybe @ocombe can integrate it into the update logic.
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); } }); }
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.
Maybe make gist from it, it's easier to manage and you can update it.
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
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 For HTML it is {node.supervisors.length, plural, =0 { No Supervisor } =1 { Supervisor: } other { Supervisors: }}
I don't know how that translates to code.
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.
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 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.
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