iLib-js / iLib

A library of i18n routines written in Javascript.
Apache License 2.0
36 stars 4 forks source link

NumFmt Constructor error #213

Closed delcon closed 1 year ago

delcon commented 5 years ago

iLib 14.4.0

I have installed iLib through npm and try to use NumFmt with the following example:

import NumFmt from "ilib/lib/NumFmt";
const fmt = new NumFmt({
    locale: "de-DE"
});
const str = fmt.format(1234567.89);

This fails with error:

Uncaught TypeError: Cannot read property 'negativenumFmt' of undefined at LocaleInfo.getNegativeNumberFormat (LocaleInfo.js?85d0:2)

I also tried to do the same with ilib-es6, but with no success.

ehoogerbeets commented 5 years ago

Hey there, we're going to need a little more context here. I tried the above out as es6 using babel7 and node 11, and it worked for me in a clean directory:

Lisbon:/tmp/test> cat y.js
import NumFmt from "ilib/lib/NumFmt";
const fmt = new NumFmt({
    locale: "de-DE"
});
const str = fmt.format(1234567.89);
console.log(str);
Lisbon:/tmp/test> npm install ilib @babel/cli @babel/core @babel/preset-env
[ ... lots of output deleted for brevity ]
add ilib    14.4.0  node_modules/ilib       
add js-levenshtein  1.1.6   node_modules/js-levenshtein     
add output-file-sync    2.0.1   node_modules/output-file-sync       
add @babel/cli  7.6.4   node_modules/@babel/cli     
add resolve 1.12.0  node_modules/resolve        
add semver  5.7.1   node_modules/semver     
add @babel/core 7.6.4   node_modules/@babel/core        
add @babel/preset-env   7.6.3   node_modules/@babel/preset-env      
Lisbon:/tmp/test> node_modules/.bin/babel --presets=@babel/preset-env y.js -o z.js
Lisbon:/tmp/test> node z.js
1.234.567,89
Lisbon:/tmp/test> cat x.js
var NumFmt = require("ilib/lib/NumFmt.js");
var fmt = new NumFmt({locale: "de-DE"});
console.log(fmt.format(1234567.89));

Lisbon:/tmp/test> node x.js
1.234.567,89

I tried with es6 and es5 and both seem to work.

What versions of node, babel, etc. are you using? Is this running inside of a webpack'ed react app?

delcon commented 5 years ago

Hi

First of all, awesome work, this library. I am trying to use it in vue.

I am not sure what versions vue uses.
I have my example available in CodeSandBox (very slow startup): https://codesandbox.io/s/vue-ilib-qhtgo
Or in git: https://github.com/delcon/vue_iLib git clone, npm install, npm run serve for local testing and hot reload

ehoogerbeets commented 5 years ago

Thanks for the compliment!

I have never used vue before, but as far as I can tell from the documentation, it looks like vue uses webpack to do its bundling of modules. In this case, the ilib locale data json files will not be included in the files that webpack generates. The reason is that ilib's locale data is 139 megabytes for all locales and data types, so it is just too large to put all of it into a webpacked application! So, ilib does not depend explicitly on any of it.

Instead, what you'll need to do is use the ilib-webpack-loader and ilib-webpack-plugin to include only the appropriate data files into the webpack output. Basically, this loader and plugin keep track of all of the ilib classes that are used by your application and then at the end, includes only the ilib locale data that those classes need, and only for the locales that you specify. This usually creates a much more reasonably sized chunk of locale data. (Less than 1 megabyte typically if you don't go nuts with the number locales you want to support.) It can even put that locale data in a separate webpack chunk per locale and load those chunks dynamically. That way, you'll only ever have one set of locale data in memory and in use at a time.

The documentation on how to use the loader and plugin is in with the loader documentation, so click on that link above to read more.

If you are not using webpack, I bet there is some other bundler with the same problem -- that the code files are included, but the locale data files are not.

Let me know what you find!

delcon commented 5 years ago

Thanks for the explanation.

I understand the idea behind the Webpack loader, but Vue is based on and build with components and to reconfigure Webpack ist not recommended.

So how do other libraries like FontAwesome load a subset of the whole library data? Translated to iLib it would look like this:

import loacaleData from "ilib/localesDataManager"  
import { en-US, de-DE, fr-FR } form "ilib/locales"  //these are references, not strings. reference the one that should be included in webpack
localeData.use( en-US, de-DE, fr-FR )  //make the available in the manager

Now we would be able to async load additional locales like:

localesData.load( ...locales/it-IT //CDN or self hosted)

In vue it would be possible to handle those cases as well.

ehoogerbeets commented 5 years ago

If you put all of the locale data into files named for the locale, and then statically import them, then webpack will include all of the locale data for all locales into the same chunk. In this case, you may as well just create your own version of ilib with the locale data statically included in it already. You can use the ilib-scanner tool to do this. It will generate the webpack configuration needed to put the custom version of ilib.js together, and then you run webpack, et voila. This works nicely if you have only a few locales and/or you only use a few ilib classes and therefore do not need too much locale data.

Alternately, if you use System.import() (or just import() in webpack 4) then you can dynamically load the locale data files (en-US.js, de-DE.js, etc.) directly and asynchronously and webpack will not put them all in the same chunk. The only problem is that there is currently no tool available that will create these locale data files. The basic logic for what should go into those files already exists in ilib-webpack-plugin, but that package is, of course, very webpack-specific. That logic would need to be isolated into a separate package that both ilib-webpack-plugin and another tool (perhaps maybe ilib-scanner?) would both be able to call.

A better idea would be to use ilib-scanner to create a dynamic version of ilib.js that will dynamically load the locale data. Then, when you include ilib.js in your application, it should only load the locale data on demand. The only caveat is that you have to use all the ilib classes asynchronously by giving an onLoad callback function. (Or with ilib-es6, use it with promises.)

ehoogerbeets commented 1 year ago

Closing the issue as resolved