Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to load only locales i want? #2517

Closed
schovi opened this issue Jul 27, 2015 · 27 comments
Closed

How to load only locales i want? #2517

schovi opened this issue Jul 27, 2015 · 27 comments

Comments

@schovi
Copy link

schovi commented Jul 27, 2015

I am using Webpack and your npm package. Unfortunately it require all locales at once.
Webpack knows they are optional

[252] ./~/moment/locale/bs.js 4.59 kB {0} {1} [optional] [built]
[253] ./~/moment/locale/ca.js 2.85 kB {0} {1} [optional] [built]
[254] ./~/moment/locale/cs.js 5.79 kB {0} {1} [optional] [built]

but for some reason they are not droped. Is there any way how to require just core with selected locales?

@mattjohnsonpint
Copy link
Contributor

You can always take individual locale files from here.

The docs describe how to load them

But I think what you're asking is how to load them individually via npm? I'm not sure if that's possible without having a separate npm package for each locale.

@schovi
Copy link
Author

schovi commented Jul 28, 2015

I described my problem badly. Another try :)

In my app I installed momentjs with npm install moment and require it var moment = require('moment')
Then it automatically require all locales for some reason. In Webpack log it is described

[239] ./~/moment/moment.js 104 kB {0} {1} [built]
[240] ./~/moment/locale ^\.\/.*$ 2.06 kB {0} {1} [optional] [built]
[241] ./~/moment/locale/af.js 2.58 kB {0} {1} [optional] [built]
[242] ./~/moment/locale/ar.js 4.52 kB {0} {1} [optional] [built]

For some reason and I cant find in momentjs source why it require all locales with this ./~/moment/locale ^\.\/.*$

What i want is to require only moment and separatly moment/locale/cs and moment/locale/en-gb

Any suggestion?

@ichernev
Copy link
Contributor

@schovi I'm not familiar with webpack, but you can do the following experiment.

  1. Put a console.log into one of the locale files.
--- /home/iskren/src/moment/locale/fr.js    2015-07-27 21:37:05.000000000 -0700
+++ locale/fr.js    2015-07-30 21:53:41.833619994 -0700
@@ -8,6 +8,8 @@
    factory(global.moment)
 }(this, function (moment) { 'use strict';

+    console.log("hello world");
+

     var fr = moment.defineLocale('fr', {
         months : 'janvier_février_mars_avril_mai_juin_juillet_août_septembre_octobre_novembre_décembre'.split('_'),
  1. Require moment.js (via node command line) and verify it is NOT printed
$ node
> require("./moment")
// prints moment properties, but no "hello world"
  1. Optionally require the locale to see that it IS printed
$ node
> require("./locale/fr.js")
hello world
{}

So this means Webpack is at fault and not the way we package moment. jspm and spm sections in package.json list locale as a thing, maybe it is related. Check Webpack source code, ask in their bug trackers ... etc :)

@ichernev
Copy link
Contributor

@mj1856 just to be clear, the locale files are shipped with npm, so he should find them in his node_modules/moment/locale directory at best, not in the git repo (they are also in the git repo, but for npm users that is irrelevant).

@schovi
Copy link
Author

schovi commented Jul 31, 2015

Thanks for answer, will try to dig in webpack.

@rattrayalex
Copy link

FWIW, the size of the "moment" package shrinks from 364.36kB to 101.97kB (second-largest to fourth-largest package, going from twice-as-large to half-as-big as our company codebase itself) when I don't include the locales (done hackily by commenting out require('./locale/' + name); in moment.js). We don't use this feature at our company, so I'd love a supported way not to include these in our webpack bundle.

@mrberggg
Copy link

I suggest reopening this as it's not fixed. For anyone else looking for a fix, I found one here. In your webpack config, add the following to your plugins array:

new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/)

This is pretty hacky, dependent on that particular include not changing. An official method would be ideal. Could you perhaps add a moment section to the package.json file for config options, with an array of desired locales?

@mattjohnsonpint
Copy link
Contributor

Reopening per @mrberggg's feedback. @ichernev, please take a look. Thanks.

@mattjohnsonpint
Copy link
Contributor

Actually, closing this as dup of #1435. Didn't see this before somehow.

@dmytro-y-dev
Copy link

dmytro-y-dev commented Jun 16, 2017

Actually, closing this as dup of #1435. Didn't see this before somehow.

They are relevant, but absolutely different issues. The problem that people face here is not documenting moment.js behavior, but how to load only required locales and not all at once (how it is done in moment.js by default).

I still don't see not hacky way to do it.

@balexandre
Copy link

jut to make a reference to an old topic about this exact issue on in webpack project

webpack/webpack#87

@pbrain19
Copy link

The solution proposed looks great if you only use english. but what about the scenario you want to support all the moment js but only want to load the one you need to the browsers. The Dynamic loading works but it is inefficient in that it bundles all the locales to one bundle. When the bundle is requested it is essentially downloading the entire file. Has anyone found a good way such that that we only dynamically load the one you need? The only solution I see is making a locales.js file and that being a giant if else statement with one branch for each language file. tedious but would solve the issue of only loading the file I need.

@exoer
Copy link

exoer commented Oct 8, 2017

import "moment/locale/fr"
import "moment/locale/de"

@pbrain19
Copy link

pbrain19 commented Nov 1, 2017

For anyone that cares in the future. The solution which resulted to be the least hacky was to use the suggested new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en/) to only load my default language and then have my builder copy the moment files into a public folder. My browser app then requests the one it before it loads.

@mikegleasonjr
Copy link

@pbrain19 see this answer to include languages other than english:
#1435 (comment)

@pbrain19
Copy link

pbrain19 commented Nov 3, 2017

From a browsers perspective it will still bundle and load it to the browser. This is not the ideal behavior we want in the browser. The answer there only explains how to bundle the locales you want. What I was looking for was an ability to support all locales without loading them all to the browser.

@Mat-thieu
Copy link

Moment uses dynamic requires, Webpack can't know which locales you want to require during compile time so it loads the entire directory just in case it's required during runtime (where locale usage is defined).

// moment.js:1837
function loadLocale(name) {
    var oldLocale = null;
    // TODO: Find a better way to register and load all the locales in Node
    if (!locales[name] && (typeof module !== 'undefined') &&
            module && module.exports) {
        try {
            oldLocale = globalLocale._abbr;
            var aliasedRequire = require;
            aliasedRequire('./locale/' + name);
            getSetGlobalLocale(oldLocale);
        } catch (e) {}
    }
    return locales[name];
}

@eigood
Copy link

eigood commented Dec 22, 2017

The following snippet keeps the default en.js, en-*.js, and moment source in the primary bundles/entry points, while mapping all other locale files into a separate async loaded bundle.

To ensure that the locales you need are available, you just need to import them into your es6-compatible file. If you don't know what those are ahead of time, then there are existing runtime delay load things you can use; we use react-loadable.

Before:

         common-lazy-main.0.86c95acc47616cc81c89.bundle.js.gz     102 kB

After:

   locale-not-en-main.1.8f31af252992c69da121.bundle.js.gz    46.8 kB
     common-lazy-main.0.603eebddcd2bcf32c0d3.bundle.js.gz    54.5 kB
10:51:22 -0600 adam@tooz(MLO-63|REBASE-i):XXXX/react-player-prototype$ git diff HEAD^ HEAD
diff --git a/webpack.config.js b/webpack.config.js
index a11d0ed..395b7b3 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -79,9 +79,22 @@ let plugins = [
     'analyzer',
     'srcReplacement',
 ]
+const momentNotEnLocales = /node_modules\/moment\/locale\/(?!en-?).*\.js$/
 const splittingPlugins = [
     new webpack.optimize.AggressiveMergingPlugin(),
     new webpack.optimize.CommonsChunkPlugin({
+        async: 'locale-not-en',
+        minChunks(module) {
+            const {context, resource} = module
+            if (resource) {
+                if (resource.match(momentNotEnLocales)) {
+                    return true
+                }
+            }
+            return false
+        },
+    }),
+    new webpack.optimize.CommonsChunkPlugin({
         name: 'vendor',
         minChunks(module) {
             return module.context && module.context.indexOf("node_modules") !== -1

jmporchet referenced this issue in annacollins85/padel-bot Jan 30, 2018
@JamesPlayer
Copy link

JamesPlayer commented May 31, 2018

The solution here looks a little cleaner: https://medium.com/@michalozogan/how-to-split-moment-js-locales-to-chunks-with-webpack-de9e25caccea

# webpack config
...
plugins: [
    new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/),
],
...

Then you can just import the ones you want

import 'moment/locale/cs.js';
import 'moment/locale/es.js';

@peterpeterparker
Copy link

It's me or the webpack "solution" like new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /a^/) isn't applicable anymore with Angular v6 (since webpack can't be ejected)?

@jvanderbiest
Copy link

@peterpeterparker for Angular-CLI see: angular/angular-cli#6137 (comment)

@peterpeterparker
Copy link

@jvanderbiest cool I may give a try. In the meantime I just remove all the unnecessary locales (the one I don't use) straight in node_modules after each install. Ugly but does the job

@kievsash
Copy link

kievsash commented Dec 4, 2018

In my project - Angular cli 6 + moment-timezone - this article worked well: https://medium.jonasbandi.net/angular-cli-and-moment-js-a-recipe-for-disaster-and-how-to-fix-it-163a79180173

@pnowy
Copy link

pnowy commented Aug 18, 2019

See that: https://github.com/jmblog/how-to-optimize-momentjs-with-webpack

@arcataroger
Copy link

For anyone still dealing with this issue in 2020, there is now a webpack plugin that lets you define which locales you need (defaults to "en" only) and removes the rest:
https://www.npmjs.com/package/moment-locales-webpack-plugin

It has a sister package to strip out unnecessary timezones from moment-timezone:
https://www.npmjs.com/package/moment-timezone-data-webpack-plugin

Between the two, we shaved off 800 kB of unnecessary size.

@brendon
Copy link

brendon commented Jul 14, 2022

Just wondering if there is anything equivalent to moment-locales-webpack-plugin in Vite land?

@phil294
Copy link

phil294 commented Mar 25, 2024

Regarding Vite:

I'm not sure why, but this seems to simply not be necessary. Nothing is bundled in the output module, unless you explicitly import it. #5926

import moment from 'moment'
import 'moment/dist/locale/fr'

console.log(moment('2012 juillet', 'YYYY MMM', 'fr').format())

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests