curl bugs

157 views
Skip to first unread message

Gehan Gonsalkorale

unread,
Aug 10, 2013, 5:37:09 PM8/10/13
to cuj...@googlegroups.com
Hey guys,

The other thread was getting massive so thought I'd make a new one

I have found a few things:

Module excludes
If you are excluding a module that does not exist then it throws an exception. You might wonder why you'd want to do this, but it's for defining modules programatically. I made a patch here to illustrate where it happens - https://github.com/cujojs/cram/pull/23/files

Commonjs loader
Modules defined with moduleLoader: curl/loader/cjsm11 don't cram. I get this problem

cram failed:  ENOENT, open '/home/vagrant/conversocial/static/lib/backbone/backbone' curl/loader/cjsm11!backbone/backbone app/main/Router wire/wire!app/spec
Error: ENOENT, open '/home/vagrant/conversocial/static/lib/backbone/backbone' curl/loader/cjsm11!backbone/backbone app/main/Router wire/wire!app/spec

js! modules crammed in wrong place?
I included a file called jstorage.js as ['js!js/lib/jstorage.js'].

When it was crammed then the file source came before the module define..! It was also minified onto one line for some reason. i.e.

<< js/lib/jstorage.js src >>
define('curl/plugin/js!js/lib/jstorage.js', function () {
    return void 0;
});

I would have thought it should do this:

define('curl/plugin/js!js/lib/jstorage.js', function () {
    << js/lib/jstorage.js src >>
    return void 0;
});

Any fixes would be very welcome as we still can't use cram on our build!!

Cheers,
Gehan

Gehan Gonsalkorale

unread,
Aug 11, 2013, 6:34:25 AM8/11/13
to
fyi with commonjs loader the file that it is having a problem opening does exist

/home/vagrant/conversocial/static/lib/backbone/backbone.js

Perhaps it's looking for the file but missing the .js extension?

** EDIT **

I'm not sure if it's cram or curl at fault but the above appears to be the problem. Here's a patch that makes it work to illustrate:

Gehan Gonsalkorale

unread,
Aug 11, 2013, 6:29:21 PM8/11/13
to cuj...@googlegroups.com
Further to my above js/cjsm11 woes I don't quite understand why jsEncode is run on the text of the module in the cram loaders? The build file doesn't load in the browser as it finds an unexpected identifier.

The function seems to take a file and then escape it so that you can include it in a javascript variable, ie it converts newlines to \n and replaces " with \". I can see why it would be used for the css! and text! loaders.

However for defining a module loaded vis the js!/cjsm11! loaders this seems a bit wrong as instead of:

define('backbone/backbone', ['require', 'exports', 'module'], function (require, exports, module) {

(function(){
  var root = this;
  var previousBackbone = root.Backbone;

Cram is outputting:

define('backbone/backbone', ['require', 'exports', 'module'], function (require, exports, module) {(function(){\n\n  var root = this;\nvar previousBackbone = root.Backbone;

This seems to be from the code in the cram.js parsers for curl.js.

e.g. in js.js we have this

moduleText = jsEncode(text) + ';\n'
    + 'define("' + absId + '", function () {\n';

moduleText += exports
    ? '\treturn ' + exports
    : '\treturn void 0';

moduleText += ';\n});\n';

Should that call to jsEncode be there..?

Also in cjsm11.js we have:

io.read(resId, function (text) {
    io.write(jsEncode(wrapCjsm11(text, moduleId)));
}, io.error);

I assume it's safe to just remove that call? 

Also for trying to load the below file via cjsm11 loader, the variable text in the above call gets truncated before the end of the file..!  It gets the file 65,536 character of knockback.js and then truncates the rest.


Finally, if I emulate what I think is the correct behaviour and load backbone into the browser like so:

define('backbone/backbone', ['require', 'exports', 'module'], function (require, exports, module) {

(function(){
  var root = this;
  var previousBackbone = root.Backbone;
  ....

Then when I load the file in the way that is declared in the build file, i.e.:

curl(["curl/loader/cjsm11!backbone/backbone"])

it actually fetches the file again from the server and ignores the defined module..! And so naturally you then get a duplicate define error: Uncaught Error: duplicate define: backbone/backbone

Sorry to keep bringing up problems! :)

Cheers,
Gehan

On Saturday, 10 August 2013 22:39:25 UTC+1, Gehan Gonsalkorale wrote:
fyi with commonjs loader the file that it is having a problem opening does exist

/home/vagrant/conversocial/static/lib/backbone/backbone.js

Perhaps it's looking for the file but missing the .js extension?

** EDIT **

I'm not sure if it's cram or curl at fault but the above appears to be the problem. Here's a patch that makes it work to illustrate:


On Saturday, 10 August 2013 22:37:09 UTC+1, Gehan Gonsalkorale wrote:

Gehan Gonsalkorale

unread,
Aug 12, 2013, 6:05:52 AM8/12/13
to cuj...@googlegroups.com
Sorry, having more problems!

1 - Forgot to mention that I tried altering the cram build.js file to define backbone like this:

define('curl/loader/cjsm11!backbone/backbone')

But I get these errors:

  1. Uncaught TypeError: Cannot use 'in' operator to search for 'normalize' in undefined curl.js:317
    1. core.getDefUrlcurl.js:444
    2. core.getCjsModulecurl.js:433
    3. core.getDepscurl.js:869
    4. core.defineResourcecurl.js:835
    5. (anonymous function)

2 - When I try to cram lodash.js, it has this define inside it which cram seems to miss:

    define(function() {
      return _;
    });

i.e. should be 

    define('lodash/lodash.compat', function() {
      return _;
    });


3 - When I use cram on my run.js, it puts the actual run.js code below at the top of the file instead of the bottom, which then means the modules aren't defined and so it seems to just curls then all in again! Kinda loses the point of doing a cram :)

(function(curl){

success =  function (ctx) {};
fail = function (error) {};
curl(config, ['wire!app/spec']).then(success, fail);

})(curl);

///... crammed modules are then appended here..

Gehan Gonsalkorale

unread,
Aug 12, 2013, 9:02:22 AM8/12/13
to cuj...@googlegroups.com
Found one more problem, maybe fixed another

1 - With the cjsm11 problem, I think its the fact it's a loader not a plugin so it should be defined as

define('backbone/backbone')

But then also included as

define('somemodule', ['backbone/backbone'], function(Backbone){})

At the moment cram treats it as a plugin and so updated the other module definitions to say this:

define('somemodule', ['curl/loader/cjsm11!backbone/backbone'], function(Backbone){})

When I updated the built file and removed the plugin prefix then it worked fine

2 - Problem with css!

I think it's a problem with jsEncode but if you have this in the css

content: "\e8fe";

It comes out as this:

content: "e8fe";

And so all my font icons are broken :)

unscriptable

unread,
Aug 12, 2013, 9:06:17 AM8/12/13
to cuj...@googlegroups.com
Hey Gehan,

Thanks for all of these!  Let's tackle all of these asap, but one at a time.  Which shall we tackle first?

-- John

Gehan Gonsalkorale

unread,
Aug 12, 2013, 9:21:16 AM8/12/13
to cuj...@googlegroups.com
Yeah getting a bit carried away here! I seem to have found a fair few little bugs though

Perhaps this order?


This is an issue with cram. When excluding a module then if the file doesn't exist it has an error


I see you commented on this! I'll rebase shall I?

3 - Missing amd def

In lodash library we have this:

    define(function() {
      return _;
    });

But it gets missed by cram! So we get an anonymous define error!

i.e. should be 

    define('lodash/lodash.compat', function() {
      return _;
    });


4 - cjsm11 define problem

The module id should be below for updating dependencies in the def

define('somemodule', ['backbone/backbone'], function(Backbone){})

Rather than this

define('somemodule', ['curl/loader/cjsm11!backbone/backbone'], function(Backbone){})

5 - regex bas is jsEncode.ks

The \ gets lost here!

jsEncode('content: "\e738"')
>"content: \"e738\""

jsEncode('content: "e738"')
>"content: \"e738\""

Those two strings are different but eval to the same

6 - run.js problem

I was talking about the run.js being at the top when it should be at the bottom. Actually that's not a problem is it? Me being thick :)

unscriptable

unread,
Aug 12, 2013, 10:03:08 AM8/12/13
to cuj...@googlegroups.com
This sounds great.  I am going to create github issues for all of these that aren't already PRs.  Some notes here:

3- Lodash: lodash does some crazy stuff internally, including stringifying and re-creating functions.  cram.js can't handle this.  I have started working on a fix that will make cram more robust with crazy code like this.


4- cjsm module ids

Interesting.  Plugins always prefix their compiled resources with the plugin id.  This is so that a dev can request the same resource at run-time (from outside the bundle, for instance), and it will match.

On the other hand, CJS modules are supposed to be loaded transparently (no explicit module id) so the plugin id should be left out.  I guess I hadn't thought this through, yet.  This makes sense to me. 

Also: ES6 module loaders will allow cross-compiled ("transpiled") modules and will likely work similarly (the module loaders won't have to be explicit).



6 - run.js does two things: configures and bootstraps.  Config stuff needs to be at the top so the `defines()` know what to do.  This is because curl.js eagerly figures out its dependency graph.  When loading files individually, this is a speed advantage.  However, it causes problems (such as needing to config at the top).  The bootstrapping needs to happen after all of the `defines()` are executed.  curl.js currently handles this by running the "main" or any other `curl(id)` calls in a future event loop turn (setTimeout).  

FWIW, the next curl (in the works!) will do more things just-in-time, rather than be so eager.  This will make several things (features and internals) much easier to implement.

I'm not sure if this is a bug.  As far I know, it works correctly.  Let me know if you think otherwise.

I'll start tackling these asap.

Regards,

-- John

Gehan Gonsalkorale

unread,
Aug 12, 2013, 12:11:15 PM8/12/13
to cuj...@googlegroups.com
Ok all sounds great!

3 - Lodash

I assume I can just exclude lodash for now and it'll still build?

6 - run.js

Yes it works fine for me, not sure why I thought otherwise. Since the module defs were below I thought it was going to do something else. I suppose since javascript is single-threaded and the curl call is asynchronous, it actually runs all the defines at the bottom before actually executing the curl('wire!) bit?

unscriptable

unread,
Aug 12, 2013, 12:48:10 PM8/12/13
to cuj...@googlegroups.com
3 - Lodash

I assume I can just exclude lodash for now and it'll still build?

Yes, thanks to your patch! :)

 

6 - run.js

Yes it works fine for me, not sure why I thought otherwise. Since the module defs were below I thought it was going to do something else. I suppose since javascript is single-threaded and the curl call is asynchronous, it actually runs all the defines at the bottom before actually executing the curl('wire!) bit?

Yah, that's how it works.  Feels a bit loose, atm.  Can't wait until curl is doing more stuff just-in-time.  That'll allow more ways to build these bundles without having to worry about timing.

-- J

Gehan Gonsalkorale

unread,
Aug 14, 2013, 6:11:12 AM8/14/13
to cuj...@googlegroups.com
Ok so looks like a lot of this is patched now :)


However I've found another two! 

1 - I'm not even sure how to describe this but something's interfering with knockout, similar to what happens when i have the wire debug plugin on with {trace: true}. I assume it's something to do with wire, rather than some oddity in the cram process.

The box should render like the left one, but for some reason it renders like the right one.

 


What's happening is the knockout bindings in the template are like this 

<span data-bind="text: replyTypeText"></span>

replyTypeText is actually a observable (so a function that is run to return a value).

Knockout should do this

<span data-bind="text: replyTypeText">@Reply</span>

but instead is doing this

<span data-bind="text: replyTypeText">function m(){if(0&lt;arguments.length){if("function"===typeof r)r.apply(c,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");return this}l||e();a.q.bb(m);return k}</span>

i.e. actually putting the function text in there.

I tried excluding the html template from the build, and the knockout library but it still does it. I'm not quite sure what's going on! 

Is there anything different about how wire runs after being cram'ed?

2 - i18n string replacement pluing - say I have this in my wire spec

    layoutView:
        render:
            template:
                module: 'text!app/conversations/views/layout.html'
            replace:
                module: 'i18n!app/conversations/strings'

And these files:

app/conversations/strings.js
app/conversations/strings/en.js
app/conversations/strings/en-us.js

Cram will only define the first file and not search for any others

define('curl/plugin/i18n!app/main/strings', function () {
    return {};
});

Which would mean extra requests, but would still work you'd think. However the i18n plugin doesn't search for the en-us or en files and so just reads the main empty strings file! So all of our replaced strings our empty now :(

Event if I do this:

 ;define('curl/plugin/i18n!app/conversations/strings/en-us', function () {
     return {
         'menuInbox': 'Inbox'
     };
 });
 ;define('curl/plugin/i18n!app/conversations/strings/en', function () {
     return {
         'menuInbox': 'Inbox'
     };
 });
 ;define('curl/plugin/i18n!app/conversations/strings', function () {
     return {};
 }); 

Then the en and en-us files are still not read. I'm guessing that the i18n plugin handles defined modules to anonymous ones or something.

I'm not sure what the correct behaviour should be, however either it should still request the local files, or the i18n cram plugin should append all the locale files in the strings/ directory

I can exclude the i18n files from the build for the moment.

Gehan Gonsalkorale

unread,
Aug 14, 2013, 6:35:40 AM8/14/13
to cuj...@googlegroups.com
Ok I've fixed #1 now - it wasn't a bug with wire or cram

I had knockout setup as 2 packages: ko and knockout, and module were including one or the other

For some reason having knockout defined two and crammed twice under different aliases was causing it to mess up. I just have the one 'knockout' package and it's working fine now!

So it's just the i18n issue :)

unscriptable

unread,
Aug 14, 2013, 10:24:19 PM8/14/13
to cuj...@googlegroups.com
Heh, I figured that most devs would put *something* in their default strings module. :)

Fwiw, you're probably doing it "the right way", just not how I thought most devs would do it.

So here's what I think is happening:

1. cram's i18n plugin uses curl's i18n plugin to run its logic to decide what to load.
2. curl's i18n plugin looks for config information to determine which locale to use.
3. Absent any config information, curl's i18n plugin sniffs the browser's locale.
4. If it is unable to sniff the locale, it just uses the default bundle.

In cram, there is no browser to sniff, of course.  Therefore, if you haven't specified any configuration, it will just use the default bundle.

So, am I right if I assume you're not providing any locale config to cram.js?


Hmmm.... I just remembered that you can only specify one locale per build, currently.  I want to change this, but it would require that some of the browser-sniffing logic inside the plugin be put into the bundle.  We could just include the entire i18n plugin, I guess.  Or maybe we should extract that bit of logic into a separate module.  Hmm......  

Do you need multiple locales per bundle asap or not?

-- John

Gehan Gonsalkorale

unread,
Aug 15, 2013, 3:21:46 AM8/15/13
to cuj...@googlegroups.com
I wouldn't expect cram to sniff for locale at all. 

Yep we're not passing a locale to cram, but I don't think that's a good idea anyway and kinda against the point of internationalisation? Passing an array of available locales would be much better and would solve all the problems I think. e.g.

plugins: {
    i18n: {
        locales: ['en', 'en-us', 'de', 'es']
    }
}

I would expect really that:

1 - At the very least cram includes the default strings file (like it does now) - but the i18n plugin still look for the locale specific files when running in the browser! 

At the moment when the default strings module is defined - the i18n plugin doesn't bother looking for the specific en-us and us bundles. This is the main problem and means that I have to exclude *all* string files otherwise it will just use the default strings file and ignore whatever locale you are actually in. Kinda renders the whole internationalisation thing useless!

2 - A better behaviour would be to include all available locale files in the bundle as I mentioned above. This would be quite easy as you would just need to include all files in the directory

e.g. for the strings module app/strings, you would just need to include:

app/strings.js
app/strings/*.js

This means no extra requests!

3 - Perhaps the best case would be to have an option to specify which locales are available as I mentioned above! At the moment the plugin fires off 3 requests - for strings, strings/en and strings/en-us. This a bit nasty in my opinion as if any of the specific files aren't available then you get 404 errors!! I would rather specify what's available to avoid throwing errors in the browser. Server-side this would be fine but I don't think throwing errors in the browser is a good idea :)

Gehan Gonsalkorale

unread,
Aug 15, 2013, 3:25:38 AM8/15/13
to cuj...@googlegroups.com
For options actually I'd like to be able to specific

1 - All available locales - so i18n doesn't cause 404s by looking for non-existent files. Cram would also read this option.

2 - To also specific the current locale, in case the user sets their locale in settings or something. Cram would *not* read this option. This would be important for showing en-gb vs en-us files since the browsers generally just report en-us even if you are in the UK :)

Gehan Gonsalkorale

unread,
Aug 15, 2013, 4:34:49 AM8/15/13
to cuj...@googlegroups.com
Ok I see the problem with the interaction between cram/curl/i18n.

I didn't realise how plugins worked. So the i18n plugin, including all the locale logic, is run by cram as you outlined above in the same way it is in the browser. I didn't realise then that this meant the module is now defined and the i18n plugin isn't used any more! This means once the locale is set in cram then that's it - your app will just get those strings as the logic to look for other locales has already been run.

Perhaps then it would be worth doing something like you do for the css! plugin - where the css file is defined but then a separate injector module is included and the sheet is passed into it, if I understand it correctly. Perhaps then for i18m cram should do something like this:


// Define all the locale modules as maps
define('curl/plugin/i18n!app/strings/de', {
    'someString': 'someGermanString'
});

define('curl/plugin/i18n!app/strings/en-us', {
  'someString': 'someEnUSString'
});

define('curl/plugin/i18n!app/strings/en', {
  someString': 'someEnString'
});

// For the actual strings module, define the defaults and then call some other
// module to determine the locale and try to load the appropriate language file.
define('curl/plugin/i18n!app/strings', ['curl/plugin/i18nRunner'], function(i18n) {
  var defaults = {
    'someString': 'someDefaultString'
  };
  i18n(defaults);
});

Would something like that work? I don't mind having a stab at it.

I also might play around with passing in an 'availableLocales' array too :D

unscriptable

unread,
Aug 15, 2013, 9:40:13 AM8/15/13
to cuj...@googlegroups.com
Great ideas!  There's a plan forming in my head:

1. Extract the getLocale() function into a separate module, and move the config-related logic into it so it can also check for run-time overrides of the locale.  So now we have a module that returns getLocale(config) --> string|boolean.  This can be used by the curl i18n plugin (run-time plugin) and injected into the build by the cram i18n plugin (build plugin).

2. Make a new build plugin config option, `availableLocales` -- or just `locales`, perhaps?  The build plugin should attempt to load the modules for each locale using the run-time plugin, keeping a hashmap of all of the modules that are successfully loaded.  All of these modules will be output to the bundle.  At run-time, the list of locales included in the build must be available so we know if getLocale() returns something we have or not.  I am thinking that we should not force the dev to specify this list again at run-time: we have to "bake" it into the bundle.

3. Make a new curl / run-time option to help decide whether to attempt to fetch locales that aren't in the bundle.  Actually, I think we already have this option: `locale: false`.  If the locale bundle is missing and `locale == false`, the default bundle is used.  Otherwise, it is fetched. (Side note: the i18n plugin will have to be fetched, too, if it is not already loaded.)

4. (Optional) Make a new build plugin option, `merge`.  If `merge` is false (default), the behavior in (2) will be used.  If it is true, a procedure much like the existing code will be used.  The existing code merges all specificities of the i18n bundles into one bundle (default, en, en-gb, for instance).  I'm not sure how useful this would be in the real world, but the code i already there, so I just wonder if some people might prefer to pre-merge the bundles.

5. Change the build plugin so it outputs something that encompasses all of the above.  I'll have to put some more thought into exactly what this means, but it seems like it would follow from implementing the previous parts of the plan.  At a minimum, we'll need to output the locale bundles, the list of locale bundles included in the build, some code to determine whether to fetch a locale bundle that is not already loaded, and some code to merge bundles at run-time (`merge: false`).


Problems:

1. In order to avoid configuration scope issues, users will have to specify the `locale` run-time config option at the top-level.  Using `{ plugins: { i18n: { locale: 'fr' } } }` will limit the scope of `locale` to just the i18n plugin which may have been eliminated at build time.  I just wrote some code docs about this.


What do you think?

-- John

Gehan Gonsalkorale

unread,
Aug 15, 2013, 10:52:53 AM8/15/13
to cuj...@googlegroups.com
1. Extract the getLocale() function into a separate module, and move the config-related logic into it so it can also check for run-time overrides of the locale.  So now we have a module that returns getLocale(config) --> string|boolean.  This can be used by the curl i18n plugin (run-time plugin) and injected into the build by the cram i18n plugin (build plugin).

2. Make a new build plugin config option, `availableLocales` -- or just `locales`, perhaps?  The build plugin should attempt to load the modules for each locale using the run-time plugin, keeping a hashmap of all of the modules that are successfully loaded.  All of these modules will be output to the bundle.  At run-time, the list of locales included in the build must be available so we know if getLocale() returns something we have or not.  I am thinking that we should not force the dev to specify this list again at run-time: we have to "bake" it into the bundle.

Yes that sounds ideal! Could you just specific the list of available locales in the curl plugin, and so at compile and run-time the same list is used? This would mean the run-time list and baked-in list are the same.

{ plugins: { i18n: { locale: 'fr', locales: ['en', 'en-us', 'fr', 'gr'] } } }`

Although in this case the locale option should be ignored by cram, as the locales array gives the available options.

The locale string I think should be used at run time to override curls locale detection. At least that's what I'd use it for!

In theory you might want to bundle the common locales, but have a larger list for the uncommon ones to use at runtime, which would then get requested. However that's sounds a bit excessive and wouldn't really save that many bytes, so may as well have one common list I think.
 
3. Make a new curl / run-time option to help decide whether to attempt to fetch locales that aren't in the bundle.  Actually, I think we already have this option: `locale: false`.  If the locale bundle is missing and `locale == false`, the default bundle is used.  Otherwise, it is fetched. (Side note: the i18n plugin will have to be fetched, too, if it is not already loaded.)

I think having a common list solves this problem? In production it wouldn't need to fetch anything, and in development it would.
 
4. (Optional) Make a new build plugin option, `merge`.  If `merge` is false (default), the behavior in (2) will be used.  If it is true, a procedure much like the existing code will be used.  The existing code merges all specificities of the i18n bundles into one bundle (default, en, en-gb, for instance).  I'm not sure how useful this would be in the real world, but the code i already there, so I just wonder if some people might prefer to pre-merge the bundles.

5. Change the build plugin so it outputs something that encompasses all of the above.  I'll have to put some more thought into exactly what this means, but it seems like it would follow from implementing the previous parts of the plan.  At a minimum, we'll need to output the locale bundles, the list of locale bundles included in the build, some code to determine whether to fetch a locale bundle that is not already loaded, and some code to merge bundles at run-time (`merge: false`).

By merge you mean merge en-gb -> en -> default? I guess if all the info is in the bundle then it doesn't really matter. It would be slightly more efficient but then make the bundle slightly bigger. Probably doesn't matter too much!
 
Problems:

1. In order to avoid configuration scope issues, users will have to specify the `locale` run-time config option at the top-level.  Using `{ plugins: { i18n: { locale: 'fr' } } }` will limit the scope of `locale` to just the i18n plugin which may have been eliminated at build time.  I just wrote some code docs about this.

That sounds sane, I can't imagine the locale changing within the different parts of an app.
 
What do you think?

Sounds a great generally!
 

--
You received this message because you are subscribed to a topic in the Google Groups "cujojs" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/cujojs/Gt1Fof6tjOk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to cujojs+un...@googlegroups.com.
To post to this group, send email to cuj...@googlegroups.com.
Visit this group at http://groups.google.com/group/cujojs.
To view this discussion on the web visit https://groups.google.com/d/msgid/cujojs/36bbb735-b1ef-4401-be65-f13504c9d48e%40googlegroups.com.

For more options, visit https://groups.google.com/groups/opt_out.

unscriptable

unread,
Aug 15, 2013, 3:18:46 PM8/15/13
to cuj...@googlegroups.com
Prototyping something, atm. :)

Gehan Gonsalkorale

unread,
Aug 15, 2013, 4:05:11 PM8/15/13
to cuj...@googlegroups.com
Nice - looking forward to seeing it!

At the moment our build is 11 files, it will be 2 after the i18n update and then when the lodash thing is fixed it'll be the magic 1!

Gehan Gonsalkorale

unread,
Aug 15, 2013, 4:06:45 PM8/15/13
to cuj...@googlegroups.com

unscriptable

unread,
Aug 16, 2013, 9:52:40 AM8/16/13
to cuj...@googlegroups.com
Ok, so the plan was fine up until step 5. ;)

Here's what I ended up creating.  It's pretty close:

1. `locale` config option is the same:

`true` (default) means sniff the browser's locale, fetch-and-merge a bundle if it isn't already loaded.
`false` means sniff the browser's locale, but don't fetch-and-merge a bundle if it isn't already loaded. Use the default bundle instead.
A string means override the browser's locale, fetch-and-merge a bundle if it isn't already loaded.
A function may be used to return any of the values above.  (config, moduleId) --> localeOrBoolean

2. The getLocale function-cum-module grew a bit.  It's now a tiny locale! plugin.  

The local! plugin's job is to perform the logic for the `locale` config option (above).  If the correct locale bundle isn't loaded, it fetches it via the i18n! plugin.  (If the i18n! plugin isn't already loaded, curl will try to load it, of course.)

I didn't want to use a plugin in the bundle.  However, there is no other way in *standard* AMD without a plugin.  To allow for a potential async fetch-and-merge operation at run time, you need a plugin.

I guess we could add another build-time option for `locale`.  Something that means "I don't care what the run-time value of `locale` is, just use the default bundle if the correct one is missing".  This option could still use the locale! plugin's logic, but not use it as a plugin.  This would allow limited AMD environments (like almond.js) to work with the bundle.

3. I updated the i18n build-time plugin to iterate through the `locales` config option and output a "locale!id/locale" module for each locale.  Then it outputs a "i18n!id" module that just uses the locale! plugin to find the correct "locale!id/locale" bundle.

I pushed the code to the multiple-locales branch, if you want to peek when github is back up. :/

Gonna spend some time testing...

-- John

unscriptable

unread,
Aug 16, 2013, 10:20:00 AM8/16/13
to cuj...@googlegroups.com

Gehan Gonsalkorale

unread,
Aug 16, 2013, 10:34:42 AM8/16/13
to cuj...@googlegroups.com
Nice! I'll have a play around, works without a bundle but wanna play with options

However I ran cram and I get this in my app now :(

    1. Uncaught TypeError: Cannot use 'in' operator to search for 'normalize' in undefined curl.js:317
      1. localRequirecurl.js:347
      2. (anonymous function)curl.js:1096
      3. core.fetchDepcurl.js:1054
      4. core.getDepscurl.js:888
      1. core.defineResourcecurl.js:835
      2. (anonymous function)

    Gehan Gonsalkorale

    unread,
    Aug 16, 2013, 11:05:33 AM8/16/13
    to cuj...@googlegroups.com
    I think the cram build plugins are passed a different config to what you are expecting:


    Also when iterating through the locales config you weren't appending the locale :)

    Gehan Gonsalkorale

    unread,
    Aug 16, 2013, 11:30:48 AM8/16/13
    to cuj...@googlegroups.com
    You'll only want to add the default locale once too:


    I was getting multiple defines before ;)

    unscriptable

    unread,
    Aug 16, 2013, 11:46:42 AM8/16/13
    to cuj...@googlegroups.com
    I'm still setting up a test app to try out the bundle.  You're way ahead of me. :)

    Gehan Gonsalkorale

    unread,
    Aug 16, 2013, 11:55:35 AM8/16/13
    to cuj...@googlegroups.com
    Yep, guess I have one ready to go!

    Looks great though, and awesome to be able to bundle multiple locales together. The bundle doesn't run at the moment as I said but that's probably just a small bug :D

    John Hann

    unread,
    Aug 16, 2013, 12:01:12 PM8/16/13
    to cuj...@googlegroups.com
    If you send me a bundle (john _at_ unscriptable _dot_ com), I'll take a peek.  Otherwise, you'll have to wait until I create my test setup. :)


    --
    You received this message because you are subscribed to the Google Groups "cujojs" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to cujojs+un...@googlegroups.com.

    To post to this group, send email to cuj...@googlegroups.com.
    Visit this group at http://groups.google.com/group/cujojs.

    Johan Chouquet

    unread,
    Sep 10, 2014, 5:43:17 AM9/10/14
    to cuj...@googlegroups.com
    Hi there,

    Very interesting talk. Indeed, I try to follow up, with the possibilities of the i18n plugin.

    Here is my case :
    - I'm not using cram
    - I'm using curl + wire on a small POC

    My wire spec looks like :
    define ({ 
         header: {
            render: {
                template : { module: 'text!app/header.html'},
                replace: { module: 'i18n!app/strings' }
            },
            insert: {
                at: {
                    $ref: 'dom!cntHeader'
                }
            }
        },
        ...
    });

    The thing is that I have a default app/strings.js file. And I also have 2 other files :
    app/strings/fr.js
    app/strings/en.js

    What I see is that my page loads : app/strings.js + app/strings/fr.js
    What do I need to put in app/strings.js file ? I read that it could be empty, is that so ?

    And one last thing : how can I test the another version of the locale ? Is there an easy way of doing that ?

    Thanks by advance guys.
    Johan

    unscriptable

    unread,
    Oct 15, 2014, 1:42:38 PM10/15/14
    to cuj...@googlegroups.com
    Hey Johan,

    Sorry for the late reply.  

    We like to include a default strings.js file that has your favorite locale strings in it.  In your case, that means the strings/fr.js file will be empty.  This allows you to set `locale:false` in your development curl config, avoiding possible 404s.  Otherwise, yes, the default strings.js can be empty.

    You  can typically change your locale through the browser settings.  Alternatively, you can set the locale via curl config: `locale: "fr-FR"`, for instance.

    Hope that helps.

    -- John
    Reply all
    Reply to author
    Forward
    0 new messages