i18n-js 3.0.0.rc5 → 3.0.0.rc6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a8e90f40cb73ad416912220f5a1636794964356d
4
- data.tar.gz: e912fa9f82f3c76d26fffeedfc4850fe7553c838
3
+ metadata.gz: 37f6e669f9c581fd9b82bf2141dc6829b1a32d21
4
+ data.tar.gz: 5203d02a3ce1445054f036930c7cca0bf2103809
5
5
  SHA512:
6
- metadata.gz: dbea11c2d0729c00da1c121d146c0f5325e133ab6c07989fae0e922650e8f7f5503bc5f4107d46a4d3d4012f0c03d88d39ce4a87d12e05295c59d29974b6b4a8
7
- data.tar.gz: a56605c1ab472757cfd446a9c0e895a47c2c9ce6ffa00a01796efb6a8f3acae793370be272c253b906778b352d199876025e67f8cf2473021acdbd0f9f26125d
6
+ metadata.gz: 65ee86cbca52f2b511e9f39511308ae0640d0c1fb3099f685687eabe9d2cad4b4f28e08a389fd1b8c70bd3f71c5b475f4d1d8fa28ca07e4a4820fdfc8a496bfb
7
+ data.tar.gz: a0f657c665ddc48f6c49b25321dd3ed15fa66407a9baa24ee91386604f98bd1fbf1bc40fdab23460f2c4246e12f8a63512ea6dd12b172c5f256651264a118aaf
data/.gitignore CHANGED
@@ -1,2 +1,5 @@
1
1
  pkg
2
2
  node_modules
3
+ Gemfile.lock
4
+ .idea/
5
+ gemfiles/*.gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.2
6
+ script: "bundle exec rake spec"
7
+ before_install: # Need to install npm to test js
8
+ - sudo apt-get update
9
+ - sudo apt-get install npm
10
+ - npm install jasmine-node@1.14.2
data/Appraisals ADDED
@@ -0,0 +1,4 @@
1
+
2
+ appraise "i18n-0-6" do
3
+ gem 'i18n', '0.6.9'
4
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ ## 3.0.0.rc6
2
+
3
+ ### enhancements
4
+
5
+ - You can now assign `I18n.locale` & `I18n.default_locale` before loading `i18n.js` in `application.html.*`
6
+ (merged to `i18n-js-pika` already)
7
+ - You can include ERB in `config/i18n-js.yml`(https://github.com/fnando/i18n-js/pull/224)
8
+ - Add support for +00:00 style time zone designator (https://github.com/fnando/i18n-js/pull/167)
9
+ - Add back rake task for export (`rake i18n:js:export`)
10
+ - Not overriding translation when manually run `I18n::JS.export` (https://github.com/fnando/i18n-js/pull/171)
11
+ - Move missing placeholder text generation into its own function (for easier debugging) (https://github.com/fnando/i18n-js/pull/169)
12
+ - Add support for milliseconds (`lll` in `yyyy-mm-ddThh:mm:ss.lllZ`) (https://github.com/fnando/i18n-js/pull/192)
13
+ - Add back i18n-js.yml config file generator : `rails generate i18n:js:config` (https://github.com/fnando/i18n-js/pull/225)
14
+
15
+ ### bug fixes
16
+
17
+ - `I18n::JS.export` no longer exports locales other than those in `I18n.available_locales`, if `I18n.available_locales` is set
18
+ - I18.t supports the base scope through the options argument
19
+ - I18.t accepts an array as the scope
20
+ - Fix regression: asset not being reloaded in development when translation changed
21
+ - Requires `i18n` to be `~> 0.6`, `0.5` does not work at all
22
+ - Fix using multi-star scope with top-level translation key (https://github.com/fnando/i18n-js/pull/221)
23
+
24
+
25
+ ## Before 3.0.0.rc5
26
+
27
+ - Things happened.
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # I18n.js
2
2
 
3
- It's a small library to provide the Rails I18n translations on the Javascript.
3
+ [![Build Status](https://travis-ci.org/fnando/i18n-js.svg?branch=master)](https://travis-ci.org/fnando/i18n-js)
4
+ [![Code Climate](https://codeclimate.com/github/fnando/i18n-js.png)](https://codeclimate.com/github/fnando/i18n-js)
5
+
6
+ It's a small library to provide the Rails I18n translations on the JavaScript.
4
7
 
5
8
  Features:
6
9
 
@@ -19,23 +22,32 @@ Features:
19
22
 
20
23
  Add the gem to your Gemfile.
21
24
 
22
- source :rubygems
25
+ source "https://rubygems.org"
23
26
  gem "rails", "3.2.3"
24
27
  gem "i18n-js"
25
28
 
29
+ #### Rails app with [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
30
+
26
31
  If you're using the [asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html),
27
32
  then you must add the following line to your `app/assets/javascripts/application.js`.
28
33
 
29
- //= require i18n/translations
34
+ ```javascript
35
+ //= require i18n/translations
36
+ ```
37
+
38
+ #### Rails app without [Asset Pipeline](http://guides.rubyonrails.org/asset_pipeline.html)
30
39
 
31
40
  If you're not using the asset pipeline, download the JavaScript file at
32
41
  <https://github.com/fnando/i18n-js/tree/master/lib/i18n.js> and load it on your page.
33
42
  Also load the `translations.js` file.
34
43
 
35
- <%= javascript_include_tag "i18n", "translations" %>
44
+ ```erb
45
+ <%= javascript_include_tag "i18n", "translations" %>
46
+ ```
36
47
 
37
48
  This `translations.js` file can be automatically generated by the `I18n::JS::Middleware`.
38
49
  Just add it to your `config/application.rb` file.
50
+ Don't add this middleware if you are using [asset pipeline](http://guides.rubyonrails.org/asset_pipeline.html)!
39
51
 
40
52
  config.middleware.use I18n::JS::Middleware
41
53
 
@@ -44,10 +56,53 @@ it by running the following command. Move the middleware line to your
44
56
  `config/environments/development.rb` file and run the following command before
45
57
  deploying.
46
58
 
47
- $ rails runner I18n::JS.export
59
+ $ rake i18n:js:export
48
60
 
49
61
  This will export all translation files, including the custom scopes you may have
50
- defined on `config/i18n-js.yml`.
62
+ defined on `config/i18n-js.yml`. If `I18n.available_locales` is set (e.g. in your
63
+ Rails `config/application.rb` file) then only the specified locales will be exported.
64
+
65
+ #### Export Configuration
66
+
67
+ Exported translation files generated by `I18n::JS::Middleware` or `rake i18n:js:export` can be customized with config file `config/i18n-js.yml` (use `rails generate i18n:js:config` to create it). You can even get more files generated to different folders and with different translations to best suit your needs. But this does not affect anything if you use Asset Pipeline.
68
+
69
+ Examples:
70
+ ```yaml
71
+ translations:
72
+ - file: 'public/javascripts/path-to-your-messages-file.js'
73
+ only: '*.date.formats'
74
+ - file: 'public/javascripts/path-to-your-second-file.js'
75
+ only: ['*.activerecord', '*.admin.*.title']
76
+ ```
77
+
78
+ If `only` is omitted all the translations will be saved. Also, make sure you add that initial `*`; it specifies that all languages will be exported. If you want to export only one language, you can do something like this:
79
+ ```yaml
80
+ translations:
81
+ - file: 'public/javascripts/en.js'
82
+ only: 'en.*'
83
+ - file: 'public/javascripts/pt-BR.js'
84
+ only: 'pt-BR.*'
85
+ ```
86
+
87
+ Optionally, you can auto generate a translation file per available locale if you specify the `%{locale}` placeholder.
88
+ ```yaml
89
+ translations:
90
+ - file: "public/javascripts/i18n/%{locale}.js"
91
+ only: '*'
92
+ - file: "public/javascripts/frontend/i18n/%{locale}.js"
93
+ only: ['frontend', 'users']
94
+ ```
95
+
96
+ You can also include ERB in your config file.
97
+ ```yaml
98
+ translations:
99
+ <% Widgets.each do |widget| %>
100
+ - file: <%= "'#{widget.file}'" %>
101
+ only: <%= "'#{widget.only}'" %>
102
+ <% end %>
103
+ ```
104
+
105
+ To find more examples on how to use the configuration file please refer to the tests.
51
106
 
52
107
  #### Vanilla JavaScript
53
108
 
@@ -59,13 +114,24 @@ by hand or using your favorite programming language. More info below.
59
114
  You **don't** need to set up a thing. The default settings will work just okay. But if you want to split translations into several files or specify specific contexts, you can follow the rest of this setting up section.
60
115
 
61
116
  Set your locale is easy as
117
+ ```javascript
118
+ I18n.defaultLocale = "pt-BR";
119
+ I18n.locale = "pt-BR";
120
+ I18n.currentLocale();
121
+ // pt-BR
122
+ ```
62
123
 
63
- I18n.defaultLocale = "pt-BR";
64
- I18n.locale = "pt-BR";
65
- I18n.currentLocale();
66
- // pt-BR
124
+ **NOTE:** You can now apply your configuration **before I18n** is loaded like this:
125
+ ```javascript
126
+ I18n = {} // You must define this object in top namespace, which should be `window`
127
+ I18n.defaultLocale = "pt-BR";
128
+ I18n.locale = "pt-BR";
67
129
 
68
- **NOTE:** Just make sure you apply your configuration **after i18n.js** is loaded. Otherwise, your settings will be ignored.
130
+ // Load I18n from `i18n.js`, `application.js` or whatever
131
+
132
+ I18n.currentLocale();
133
+ // pt-BR
134
+ ```
69
135
 
70
136
  In practice, you'll have something like the following in your `application.html.erb`:
71
137
 
@@ -77,7 +143,7 @@ In practice, you'll have something like the following in your `application.html.
77
143
  You can use translate your messages:
78
144
 
79
145
  I18n.t("some.scoped.translation");
80
- // or translate with explicite setting of locale
146
+ // or translate with explicit setting of locale
81
147
  I18n.t("some.scoped.translation", {locale: "fr"});
82
148
 
83
149
  You can also interpolate values:
@@ -92,6 +158,21 @@ You can set default values for missing scopes:
92
158
  // with interpolation
93
159
  I18n.t("noun", {defaultValue: "I'm a {{noun}}", noun: "Mac"});
94
160
 
161
+ You can also provide a list of default fallbacks for missing scopes:
162
+
163
+ // As a scope
164
+ I18n.t("some.missing.scope", {defaults: [{scope: "some.existing.scope"}]});
165
+
166
+ // As a simple translation
167
+ I18n.t("some.missing.scope", {defaults: [{message: "some.existing.scope"}]});
168
+
169
+ Default values must be provided as an array of hashs where the key is the
170
+ type of translation desired, a `scope` or a `message`. The translation returned
171
+ will be either the first scope recognized, or the first message defined.
172
+
173
+ The translation will fallback to the `defaultValue` translation if no scope
174
+ in `defaults` matches and if no default of type `message` is found.
175
+
95
176
  Translation fallback can be enabled by enabling the `I18n.fallbacks` option:
96
177
 
97
178
  <script type="text/javascript">
@@ -114,7 +195,7 @@ are three different ways of doing it so:
114
195
  I18n.locales.no = "nb";
115
196
  I18n.locales.no = function(locale){ return ["nb"]; };
116
197
 
117
- Pluralization is possible as well and by default provides english rules:
198
+ Pluralization is possible as well and by default provides English rules:
118
199
 
119
200
  I18n.t("inbox.counting", {count: 10}); // You have 10 messages
120
201
 
@@ -127,7 +208,7 @@ The sample above expects the following translation:
127
208
  other: You have {{count}} new messages
128
209
  zero: You have no messages
129
210
 
130
- **NOTE:** Rais I18n recognizes the `zero` option.
211
+ **NOTE:** Rails I18n recognizes the `zero` option.
131
212
 
132
213
  If you need special rules just define them for your language, for example Russian, just add a new pluralizer:
133
214
 
@@ -232,7 +313,6 @@ The accepted formats are:
232
313
  %A - The full weekday name (Sunday)
233
314
  %b - The abbreviated month name (Jan)
234
315
  %B - The full month name (January)
235
- %c - The preferred local date and time representation
236
316
  %d - Day of the month (01..31)
237
317
  %-d - Day of the month (1..31)
238
318
  %H - Hour of the day, 24-hour clock (00..23)
@@ -256,7 +336,7 @@ Check out `spec/*.spec.js` files for more examples!
256
336
 
257
337
  ## Using I18n.js with other languages (Python, PHP, ...)
258
338
 
259
- The JavaScript library is language agnostic; so you can use it with PHP, Python, [you favorite language here].
339
+ The JavaScript library is language agnostic; so you can use it with PHP, Python, [your favorite language here].
260
340
  The only requirement is that you need to set the `translations` attribute like following:
261
341
 
262
342
  I18n.translations = {};
@@ -278,10 +358,11 @@ The only requirement is that you need to set the `translations` attribute like f
278
358
  Once you've made your great commits:
279
359
 
280
360
  1. [Fork](http://help.github.com/forking/) I18n.js
281
- 2. Create a topic branch - `git checkout -b my_branch`
282
- 3. Push to your branch - `git push origin my_branch`
283
- 4. [Create an Issue](http://github.com/fnando/i18n-js/issues) with a link to your branch
284
- 5. That's it!
361
+ 2. Create a branch with a clear name
362
+ 3. Make your changes (Please also add/change spec, README and CHANGELOG if applicable)
363
+ 4. Push changes to the created branch
364
+ 5. [Create an Pull Request](http://github.com/fnando/i18n-js/pulls)
365
+ 6. That's it!
285
366
 
286
367
  Please respect the indentation rules and code style.
287
368
  And use 2 spaces, not tabs. And don't touch the versioning thing.
data/Rakefile CHANGED
@@ -1,7 +1,10 @@
1
+ require "appraisal"
2
+ require "rubygems"
1
3
  require "bundler"
4
+ require "rspec/core/rake_task"
5
+
2
6
  Bundler::GemHelper.install_tasks
3
7
 
4
- require "rspec/core/rake_task"
5
8
  RSpec::Core::RakeTask.new(:"spec:ruby")
6
9
 
7
10
  desc "Run JavaScript specs"
@@ -11,3 +14,11 @@ end
11
14
 
12
15
  desc "Run all specs"
13
16
  task :spec => ["spec:ruby", "spec:js"]
17
+
18
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
19
+ task :default do
20
+ sh "appraisal install && rake appraisal spec"
21
+ end
22
+ else
23
+ task :default => :spec
24
+ end
@@ -23,11 +23,12 @@
23
23
  };
24
24
 
25
25
  // Set default days/months translations.
26
- var DAYS_AND_MONTHS = {
26
+ var DATE = {
27
27
  day_names: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
28
28
  , abbr_day_names: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
29
29
  , month_names: [null, "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
30
30
  , abbr_month_names: [null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
31
+ , meridian: ["AM", "PM"]
31
32
  };
32
33
 
33
34
  // Set default number format.
@@ -57,30 +58,59 @@
57
58
  // Set default size units.
58
59
  var SIZE_UNITS = [null, "kb", "mb", "gb", "tb"];
59
60
 
60
- // Set meridian.
61
- var MERIDIAN = ["AM", "PM"];
61
+ // Other default options
62
+ var DEFAULT_OPTIONS = {
63
+ defaultLocale: "en",
64
+ locale: "en",
65
+ defaultSeparator: ".",
66
+ placeholder: /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm,
67
+ fallbacks: false,
68
+ translations: {},
69
+ };
62
70
 
63
71
  I18n.reset = function() {
64
72
  // Set default locale. This locale will be used when fallback is enabled and
65
73
  // the translation doesn't exist in a particular locale.
66
- this.defaultLocale = "en";
74
+ this.defaultLocale = DEFAULT_OPTIONS.defaultLocale;
67
75
 
68
76
  // Set the current locale to `en`.
69
- this.locale = "en";
77
+ this.locale = DEFAULT_OPTIONS.locale;
70
78
 
71
79
  // Set the translation key separator.
72
- this.defaultSeparator = ".";
80
+ this.defaultSeparator = DEFAULT_OPTIONS.defaultSeparator;
73
81
 
74
82
  // Set the placeholder format. Accepts `{{placeholder}}` and `%{placeholder}`.
75
- this.placeholder = /(?:\{\{|%\{)(.*?)(?:\}\}?)/gm;
83
+ this.placeholder = DEFAULT_OPTIONS.placeholder;
76
84
 
77
85
  // Set if engine should fallback to the default locale when a translation
78
86
  // is missing.
79
- this.fallbacks = false;
87
+ this.fallbacks = DEFAULT_OPTIONS.fallbacks;
80
88
 
81
89
  // Set the default translation object.
82
- this.translations = {};
90
+ this.translations = DEFAULT_OPTIONS.translations;
91
+ };
92
+
93
+ // Much like `reset`, but only assign options if not already assigned
94
+ I18n.initializeOptions = function() {
95
+ if (typeof(this.defaultLocale) === "undefined" && this.defaultLocale !== null)
96
+ this.defaultLocale = DEFAULT_OPTIONS.defaultLocale;
97
+
98
+ if (typeof(this.locale) === "undefined" && this.locale !== null)
99
+ this.locale = DEFAULT_OPTIONS.locale;
100
+
101
+ if (typeof(this.defaultSeparator) === "undefined" && this.defaultSeparator !== null)
102
+ this.defaultSeparator = DEFAULT_OPTIONS.defaultSeparator;
103
+
104
+ if (typeof(this.placeholder) === "undefined" && this.placeholder !== null)
105
+ this.placeholder = DEFAULT_OPTIONS.placeholder;
106
+
107
+ if (typeof(this.fallbacks) === "undefined" && this.fallbacks !== null)
108
+ this.fallbacks = DEFAULT_OPTIONS.fallbacks;
109
+
110
+ if (typeof(this.translations) === "undefined" && this.translations !== null)
111
+ this.translations = DEFAULT_OPTIONS.translations;
83
112
  };
113
+ I18n.initializeOptions();
84
114
 
85
115
  // Return a list of all locales that must be tried before returning the
86
116
  // missing translation message. By default, this will consider the inline option,
@@ -181,10 +211,6 @@
181
211
  }
182
212
  };
183
213
 
184
- // Reset all default attributes. This is specially useful
185
- // while running tests.
186
- I18n.reset();
187
-
188
214
  // Return current locale. If no locale has been set, then
189
215
  // the current locale will be the default locale.
190
216
  I18n.currentLocale = function() {
@@ -209,6 +235,19 @@
209
235
  , translations
210
236
  ;
211
237
 
238
+ // Deal with the scope as an array.
239
+ if (scope.constructor === Array) {
240
+ scope = scope.join(this.defaultSeparator);
241
+ }
242
+
243
+ // Deal with the scope option provided through the second argument.
244
+ //
245
+ // I18n.t('hello', {scope: 'greetings'});
246
+ //
247
+ if (options.scope) {
248
+ scope = [options.scope, scope].join(this.defaultSeparator);
249
+ }
250
+
212
251
  while (locales.length) {
213
252
  locale = locales.shift();
214
253
  scopes = scope.split(this.defaultSeparator);
@@ -236,6 +275,24 @@
236
275
  }
237
276
  };
238
277
 
278
+ // Rails changed the way the meridian is stored.
279
+ // It started with `date.meridian` returning an array,
280
+ // then it switched to `time.am` and `time.pm`.
281
+ // This function abstracts this difference and returns
282
+ // the correct meridian or the default value when none is provided.
283
+ I18n.meridian = function() {
284
+ var time = this.lookup("time");
285
+ var date = this.lookup("date");
286
+
287
+ if (time && time.am && time.pm) {
288
+ return [time.am, time.pm];
289
+ } else if (date && date.meridian) {
290
+ return date.meridian;
291
+ } else {
292
+ return DATE.meridian;
293
+ }
294
+ };
295
+
239
296
  // Merge serveral hash options, checking if value is set before
240
297
  // overwriting any value. The precedence is from left to right.
241
298
  //
@@ -271,12 +328,51 @@
271
328
  return options;
272
329
  };
273
330
 
331
+ // Generate a list of translation options for default fallbacks.
332
+ // `defaultValue` is also deleted from options as it is returned as part of
333
+ // the translationOptions array.
334
+ I18n.createTranslationOptions = function(scope, options) {
335
+ var translationOptions = [{scope: scope}];
336
+
337
+ // Defaults should be an array of hashes containing either
338
+ // fallback scopes or messages
339
+ if (this.isSet(options.defaults)) {
340
+ translationOptions = translationOptions.concat(options.defaults);
341
+ }
342
+
343
+ // Maintain support for defaultValue. Since it is always a message
344
+ // insert it in to the translation options as such.
345
+ if (this.isSet(options.defaultValue)) {
346
+ translationOptions.push({ message: options.defaultValue });
347
+ delete options.defaultValue;
348
+ }
349
+
350
+ return translationOptions;
351
+ };
352
+
274
353
  // Translate the given scope with the provided options.
275
354
  I18n.translate = function(scope, options) {
276
355
  options = this.prepareOptions(options);
277
- var translation = this.lookup(scope, options);
278
356
 
279
- if (translation === undefined || translation === null) {
357
+ var translationOptions = this.createTranslationOptions(scope, options);
358
+
359
+ var translation;
360
+ // Iterate through the translation options until a translation
361
+ // or message is found.
362
+ var translationFound =
363
+ translationOptions.some(function(translationOption) {
364
+ if (this.isSet(translationOption.scope)) {
365
+ translation = this.lookup(translationOption.scope, options);
366
+ } else if (this.isSet(translationOption.message)) {
367
+ translation = translationOption.message;
368
+ }
369
+
370
+ if (translation !== undefined && translation !== null) {
371
+ return true;
372
+ }
373
+ }, this);
374
+
375
+ if (!translationFound) {
280
376
  return this.missingTranslation(scope);
281
377
  }
282
378
 
@@ -306,17 +402,17 @@
306
402
  while (matches.length) {
307
403
  placeholder = matches.shift();
308
404
  name = placeholder.replace(this.placeholder, "$1");
309
- value = options[name];
405
+ value = options[name].toString().replace(/\$/gm, "_#$#_");
310
406
 
311
407
  if (!this.isSet(options[name])) {
312
- value = "[missing " + placeholder + " value]";
408
+ value = this.missingPlaceholder(placeholder, message);
313
409
  }
314
410
 
315
411
  regex = new RegExp(placeholder.replace(/\{/gm, "\\{").replace(/\}/gm, "\\}"));
316
412
  message = message.replace(regex, value);
317
413
  }
318
414
 
319
- return message;
415
+ return message.replace("_#$#_", "$");
320
416
  };
321
417
 
322
418
  // Pluralize the given scope using the `count` value.
@@ -363,6 +459,11 @@
363
459
  return message;
364
460
  };
365
461
 
462
+ // Return a missing placeholder message for given parameters
463
+ I18n.missingPlaceholder = function(placeholder, message) {
464
+ return "[missing " + placeholder + " value]";
465
+ };
466
+
366
467
  // Format number using localization rules.
367
468
  // The options will be retrieved from the `number.format` scope.
368
469
  // If this isn't present, then the following options will be used:
@@ -481,16 +582,17 @@
481
582
  // yyyy-mm-dd[ T]hh:mm::ss
482
583
  // yyyy-mm-dd[ T]hh:mm::ssZ
483
584
  // yyyy-mm-dd[ T]hh:mm::ss+0000
585
+ // yyyy-mm-dd[ T]hh:mm::ss+00:00
586
+ // yyyy-mm-dd[ T]hh:mm::ss.123Z
484
587
  //
485
588
  I18n.parseDate = function(date) {
486
- var matches, convertedDate;
487
-
589
+ var matches, convertedDate, fraction;
488
590
  // we have a date, so just return it.
489
591
  if (typeof(date) == "object") {
490
592
  return date;
491
593
  };
492
594
 
493
- matches = date.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2}))?(Z|\+0000)?/);
595
+ matches = date.toString().match(/(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2}):(\d{2})([\.,]\d{1,3})?)?(Z|\+00:?00)?/);
494
596
 
495
597
  if (matches) {
496
598
  for (var i = 1; i <= 6; i++) {
@@ -500,19 +602,28 @@
500
602
  // month starts on 0
501
603
  matches[2] -= 1;
502
604
 
503
- if (matches[7]) {
504
- convertedDate = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]));
605
+ fraction = matches[7] ? 1000 * ("0" + matches[7]) : null;
606
+
607
+ if (matches[8]) {
608
+ convertedDate = new Date(Date.UTC(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction));
505
609
  } else {
506
- convertedDate = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6]);
610
+ convertedDate = new Date(matches[1], matches[2], matches[3], matches[4], matches[5], matches[6], fraction);
507
611
  }
508
612
  } else if (typeof(date) == "number") {
509
613
  // UNIX timestamp
510
614
  convertedDate = new Date();
511
615
  convertedDate.setTime(date);
616
+ } else if (date.match(/([A-Z][a-z]{2}) ([A-Z][a-z]{2}) (\d+) (\d+:\d+:\d+) ([+-]\d+) (\d+)/)) {
617
+ // This format `Wed Jul 20 13:03:39 +0000 2011` is parsed by
618
+ // webkit/firefox, but not by IE, so we must parse it manually.
619
+ convertedDate = new Date();
620
+ convertedDate.setTime(Date.parse([
621
+ RegExp.$1, RegExp.$2, RegExp.$3, RegExp.$6, RegExp.$4, RegExp.$5
622
+ ].join(" ")));
512
623
  } else if (date.match(/\d+ \d+:\d+:\d+ [+-]\d+ \d+/)) {
513
624
  // a valid javascript format with timezone info
514
625
  convertedDate = new Date();
515
- convertedDate.setTime(Date.parse(date))
626
+ convertedDate.setTime(Date.parse(date));
516
627
  } else {
517
628
  // an arbitrary javascript string
518
629
  convertedDate = new Date();
@@ -553,15 +664,15 @@
553
664
  // %z - Timezone offset (+0545)
554
665
  //
555
666
  I18n.strftime = function(date, format) {
556
- var options = this.lookup("date");
667
+ var options = this.lookup("date")
668
+ , meridianOptions = I18n.meridian()
669
+ ;
557
670
 
558
671
  if (!options) {
559
- options = DAYS_AND_MONTHS;
672
+ options = {};
560
673
  }
561
674
 
562
- if (!options.meridian) {
563
- options.meridian = MERIDIAN;
564
- }
675
+ options = this.prepareOptions(options, DATE);
565
676
 
566
677
  var weekDay = date.getDay()
567
678
  , day = date.getDate()
@@ -575,7 +686,9 @@
575
686
  , offset = date.getTimezoneOffset()
576
687
  , absOffsetHours = Math.floor(Math.abs(offset / 60))
577
688
  , absOffsetMinutes = Math.abs(offset) - (absOffsetHours * 60)
578
- , timezoneoffset = (offset > 0 ? "-" : "+") + (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) + (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes)
689
+ , timezoneoffset = (offset > 0 ? "-" : "+") +
690
+ (absOffsetHours.toString().length < 2 ? "0" + absOffsetHours : absOffsetHours) +
691
+ (absOffsetMinutes.toString().length < 2 ? "0" + absOffsetMinutes : absOffsetMinutes)
579
692
  ;
580
693
 
581
694
  if (hour12 > 12) {
@@ -599,7 +712,7 @@
599
712
  format = format.replace("%-m", month);
600
713
  format = format.replace("%M", padding(mins));
601
714
  format = format.replace("%-M", mins);
602
- format = format.replace("%p", options.meridian[meridian]);
715
+ format = format.replace("%p", meridianOptions[meridian]);
603
716
  format = format.replace("%S", padding(secs));
604
717
  format = format.replace("%-S", secs);
605
718
  format = format.replace("%w", weekDay);
@@ -681,4 +794,4 @@
681
794
  I18n.t = I18n.translate;
682
795
  I18n.l = I18n.localize;
683
796
  I18n.p = I18n.pluralize;
684
- })(typeof(exports) === "undefined" ? (this.I18n = {}) : exports);
797
+ })(typeof(exports) === "undefined" ? (this.I18n || (this.I18n = {})) : exports);