socialcast-i18n-js 4.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +13 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +2 -0
  4. data/README.md +324 -0
  5. data/README.rdoc +320 -0
  6. data/Rakefile +13 -0
  7. data/app/assets/javascripts/i18n/translations.js.erb +6 -0
  8. data/app/assets/javascripts/i18n.js +661 -0
  9. data/config/i18n-js.yml +22 -0
  10. data/i18n-js.gemspec +28 -0
  11. data/lib/i18n/js/engine.rb +6 -0
  12. data/lib/i18n/js/file_dependency_processor.rb +17 -0
  13. data/lib/i18n/js/railtie.rb +17 -0
  14. data/lib/i18n/js/translator.rb +56 -0
  15. data/lib/i18n/js/version.rb +10 -0
  16. data/lib/i18n-js/engine.rb +63 -0
  17. data/lib/i18n-js/middleware.rb +59 -0
  18. data/lib/i18n-js/railtie.rb +13 -0
  19. data/lib/i18n-js/rake.rb +16 -0
  20. data/lib/i18n-js/version.rb +10 -0
  21. data/lib/i18n-js.rb +22 -0
  22. data/lib/tasks/i18n-js.rake +17 -0
  23. data/spec/file_dependency_processor_spec.rb +32 -0
  24. data/spec/fixtures/custom_path.yml +2 -0
  25. data/spec/fixtures/default.yml +2 -0
  26. data/spec/fixtures/js_file_per_locale.yml +1 -0
  27. data/spec/fixtures/locales.yml +76 -0
  28. data/spec/fixtures/multiple_conditions.yml +3 -0
  29. data/spec/fixtures/no_config.yml +2 -0
  30. data/spec/fixtures/simple_scope.yml +2 -0
  31. data/spec/i18n_js_spec.rb +11 -0
  32. data/spec/i18n_spec.js +820 -0
  33. data/spec/i18n_spec.rb +205 -0
  34. data/spec/js/currency.spec.js +60 -0
  35. data/spec/js/current_locale.spec.js +19 -0
  36. data/spec/js/dates.spec.js +218 -0
  37. data/spec/js/defaults.spec.js +23 -0
  38. data/spec/js/interpolation.spec.js +28 -0
  39. data/spec/js/jasmine/MIT.LICENSE +20 -0
  40. data/spec/js/jasmine/jasmine-html.js +190 -0
  41. data/spec/js/jasmine/jasmine.css +166 -0
  42. data/spec/js/jasmine/jasmine.js +2476 -0
  43. data/spec/js/jasmine/jasmine_favicon.png +0 -0
  44. data/spec/js/localization.spec.js +41 -0
  45. data/spec/js/numbers.spec.js +124 -0
  46. data/spec/js/placeholder.spec.js +24 -0
  47. data/spec/js/pluralization.spec.js +105 -0
  48. data/spec/js/prepare_options.spec.js +41 -0
  49. data/spec/js/specs.html +46 -0
  50. data/spec/js/translate.spec.js +119 -0
  51. data/spec/js/translations.js +115 -0
  52. data/spec/resources/custom_path.yml +4 -0
  53. data/spec/resources/default.yml +4 -0
  54. data/spec/resources/js_file_per_locale.yml +3 -0
  55. data/spec/resources/locales.yml +76 -0
  56. data/spec/resources/multiple_conditions.yml +6 -0
  57. data/spec/resources/multiple_files.yml +6 -0
  58. data/spec/resources/no_config.yml +2 -0
  59. data/spec/resources/no_scope.yml +3 -0
  60. data/spec/resources/simple_scope.yml +4 -0
  61. data/spec/spec_helper.rb +40 -0
  62. data/spec/translator_spec.rb +45 -0
  63. data/vendor/assets/javascripts/i18n/translations.js.erb +9 -0
  64. data/vendor/assets/javascripts/i18n.js +531 -0
  65. data/vendor/assets/javascripts/underscore.js +1059 -0
  66. metadata +240 -0
@@ -0,0 +1,6 @@
1
+ module I18n
2
+ module Js
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ #encoding: utf-8
2
+ require 'sprockets'
3
+ module I18n
4
+ module Js
5
+ class FileDependencyProcessor < Sprockets::Processor
6
+ def self.asset_path_regexp
7
+ @@asset_path_regexp ||= %r{#{I18n::Js.config[:asset_path]}}
8
+ end
9
+ def evaluate(context, locals)
10
+ return data unless context.logical_path =~ self.class.asset_path_regexp
11
+ ::I18n.load_path.each {|path| context.depend_on(path)}
12
+ context.depend_on(I18n::Js.config_path)
13
+ data
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ require 'i18n/js/engine'
2
+
3
+ module I18n
4
+ module Js
5
+ autoload :Translator, "i18n/js/translator"
6
+ autoload :FileDependencyProcessor, "i18n/js/file_dependency_processor"
7
+
8
+ class Railtie < ::Rails::Railtie
9
+ rake_tasks do
10
+ load "tasks/i18n-js.rake"
11
+ end
12
+ initializer "i18n-js.assetpipeline.environment", :after => "sprockets.environment" do |app|
13
+ app.assets.register_preprocessor("application/javascript", FileDependencyProcessor) if app.config.assets.enabled
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ module I18n
2
+ module Js
3
+ class Translator
4
+ attr_accessor :scopes, :translations
5
+ def initialize(scopes)
6
+ @scopes = scopes
7
+ @translations = self.class.segment_for_scope(@scopes)
8
+ end
9
+
10
+ def self.segment_for_scope(scope)
11
+ if scope == "*"
12
+ translations
13
+ else
14
+ scoped_translations(scope)
15
+ end
16
+ end
17
+
18
+ def self.scoped_translations(scopes) # :nodoc:
19
+ result = {}
20
+
21
+ [scopes].flatten.each do |scope|
22
+ result.deep_merge!(filter(translations, scope))
23
+ end
24
+
25
+ result
26
+ end
27
+
28
+ # Filter translations according to the specified scope.
29
+ def self.filter(filter_translations, scopes)
30
+ scopes = scopes.split(".") if scopes.is_a?(String)
31
+ scopes = scopes.clone
32
+ scope = scopes.shift
33
+
34
+ if scope == "*"
35
+ results = {}
36
+ filter_translations.each do |filter_scope, filter_translation|
37
+ tmp = scopes.empty? ? filter_translation : filter(filter_translation, scopes)
38
+ results[filter_scope.to_sym] = tmp unless tmp.nil?
39
+ end
40
+ return results
41
+ elsif filter_translations.has_key?(scope.to_sym)
42
+ return {scope.to_sym => scopes.empty? ? filter_translations[scope.to_sym] : filter(filter_translations[scope.to_sym], scopes)}
43
+ end
44
+ nil
45
+ end
46
+
47
+ # Initialize and return translations
48
+ def self.translations
49
+ ::I18n.backend.instance_eval do
50
+ init_translations unless initialized?
51
+ translations
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,10 @@
1
+ module I18n
2
+ module Js
3
+ module Version
4
+ MAJOR = 4
5
+ MINOR = 0
6
+ PATCH = 0
7
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}.rc1"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,63 @@
1
+ module SimplesIdeias
2
+ module I18n
3
+ class Engine < ::Rails::Engine
4
+ I18N_TRANSLATIONS_ASSET = "i18n/translations"
5
+
6
+ initializer "i18n-js.asset_dependencies", :after => "sprockets.environment",
7
+ :before => "i18n-js.initialize" do
8
+ next unless SimplesIdeias::I18n.has_asset_pipeline?
9
+
10
+ config = I18n.config_file
11
+ cache_file = I18n::Engine.load_path_hash_cache
12
+
13
+ Rails.application.assets.register_preprocessor "application/javascript", :"i18n-js_dependencies" do |context, data|
14
+ if context.logical_path == I18N_TRANSLATIONS_ASSET
15
+ context.depend_on(config) if I18n.config?
16
+ # also set up dependencies on every locale file
17
+ ::I18n.load_path.each {|path| context.depend_on(path)}
18
+
19
+ # Set up a dependency on the contents of the load path
20
+ # itself. In some situations it is possible to get here
21
+ # before the path hash cache file has been written; in
22
+ # this situation, write it now.
23
+ I18n::Engine.write_hash! unless File.exists?(cache_file)
24
+ context.depend_on(cache_file)
25
+ end
26
+
27
+ data
28
+ end
29
+ end
30
+
31
+ # rewrite path cache hash at startup and before each request in development
32
+ config.to_prepare do
33
+ next unless SimplesIdeias::I18n.has_asset_pipeline?
34
+ SimplesIdeias::I18n::Engine.write_hash_if_changed unless Rails.env.production?
35
+ end
36
+
37
+ def self.load_path_hash_cache
38
+ @load_path_hash_cache ||= Rails.root.join("tmp/i18n-js.cache")
39
+ end
40
+
41
+ def self.write_hash_if_changed
42
+ load_path_hash = ::I18n.load_path.hash
43
+
44
+ if load_path_hash != cached_load_path_hash
45
+ self.cached_load_path_hash = load_path_hash
46
+ write_hash!
47
+ end
48
+ end
49
+
50
+ def self.write_hash!
51
+ FileUtils.mkdir_p Rails.root.join("tmp")
52
+
53
+ File.open(load_path_hash_cache, "w+") do |f|
54
+ f.write(cached_load_path_hash)
55
+ end
56
+ end
57
+
58
+ class << self
59
+ attr_accessor :cached_load_path_hash
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,59 @@
1
+ module SimplesIdeias
2
+ module I18n
3
+ class Middleware
4
+ def initialize(app)
5
+ @app = app
6
+ end
7
+
8
+ def call(env)
9
+ @cache = nil
10
+ verify_locale_files!
11
+ @app.call(env)
12
+ end
13
+
14
+ private
15
+ def cache_path
16
+ @cache_path ||= Rails.root.join("tmp/cache/i18n-js.yml")
17
+ end
18
+
19
+ def cache
20
+ @cache ||= begin
21
+ if cache_path.exist?
22
+ YAML.load_file(cache_path) || {}
23
+ else
24
+ {}
25
+ end
26
+ end
27
+ end
28
+
29
+ # Check if translations should be regenerated.
30
+ # ONLY REGENERATE when these conditions are met:
31
+ #
32
+ # # Cache file doesn't exist
33
+ # # Translations and cache size are different (files were removed/added)
34
+ # # Translation file has been updated
35
+ #
36
+ def verify_locale_files!
37
+ valid_cache = []
38
+ new_cache = {}
39
+
40
+ valid_cache.push cache_path.exist?
41
+ valid_cache.push ::I18n.load_path.uniq.size == cache.size
42
+
43
+ ::I18n.load_path.each do |path|
44
+ changed_at = File.mtime(path).to_i
45
+ valid_cache.push changed_at == cache[path]
46
+ new_cache[path] = changed_at
47
+ end
48
+
49
+ unless valid_cache.all?
50
+ File.open(cache_path, "w+") do |file|
51
+ file << new_cache.to_yaml
52
+ end
53
+
54
+ SimplesIdeias::I18n.export!
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,13 @@
1
+ module SimplesIdeias
2
+ module I18n
3
+ class Railtie < Rails::Railtie
4
+ rake_tasks do
5
+ require "i18n-js/rake"
6
+ end
7
+
8
+ initializer "i18n-js.initialize" do |app|
9
+ app.config.middleware.use(Middleware) if Rails.env.development? && !SimplesIdeias::I18n.has_asset_pipeline?
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ namespace "i18n:js" do
2
+ desc "Copy i18n.js and configuration file"
3
+ task :setup => :environment do
4
+ SimplesIdeias::I18n.setup!
5
+ end
6
+
7
+ desc "Export the messages files"
8
+ task :export => :environment do
9
+ SimplesIdeias::I18n.export!
10
+ end
11
+
12
+ desc "Update the JavaScript library"
13
+ task :update => :environment do
14
+ SimplesIdeias::I18n.update!
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ module SimplesIdeias
2
+ module I18n
3
+ module Version
4
+ MAJOR = 2
5
+ MINOR = 1
6
+ PATCH = 2
7
+ STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
+ end
9
+ end
10
+ end
data/lib/i18n-js.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ require 'i18n'
4
+ require 'i18n/js/railtie' if defined?(Rails)
5
+
6
+ module I18n
7
+ module Js
8
+ def self.config_path
9
+ Rails.root.join("config", "i18n-js.yml")
10
+ end
11
+ def self.config?
12
+ File.exists?(config_path)
13
+ end
14
+ def self.config
15
+ @@config ||= if config?
16
+ YAML.load_file(config_path).with_indifferent_access
17
+ else
18
+ {}.with_indifferent_access
19
+ end.reverse_merge!(:asset_path => "i18n/translation", :only => "*")
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ namespace :i18n do
2
+ namespace :js do
3
+ namespace :export do
4
+ task :assets => [:environment] do
5
+ require 'fileutils'
6
+ Dir.glob(Rails.application.assets.paths.map { |path| File.join(path, "#{I18n::Js.config[:asset_path]}*") }) do |asset_path|
7
+ asset = Rails.application.assets[asset_path]
8
+ export_path = Rails.root.join('public', asset.logical_path)
9
+ FileUtils.mkdir_p File.dirname(export_path)
10
+ File.open(export_path, 'w') do |file|
11
+ file.write(asset.to_a.map { |x| x.body }.join("\n"))
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+ require 'i18n/js/file_dependency_processor'
3
+
4
+ describe I18n::Js::FileDependencyProcessor do
5
+ context "#evaluate" do
6
+ let(:dependency_processor) { I18n::Js::FileDependencyProcessor.new { @js } }
7
+ it "sets dependencies on the I18n load_paths and the I18n::Js config file" do
8
+ ::I18n.stub(:load_path => ["/path/to/en.yml", "/path/to/es.yml"])
9
+ I18n::Js.stub(:config_path => "/path/to/i18n/js/config.yml")
10
+ context = double("context")
11
+ context.should_receive(:logical_path).and_return("i18n/translation-en.js")
12
+ context.should_receive(:depend_on).with("/path/to/en.yml")
13
+ context.should_receive(:depend_on).with("/path/to/es.yml")
14
+ context.should_receive(:depend_on).with("/path/to/i18n/js/config.yml")
15
+ locals = double("locals")
16
+ dependency_processor.evaluate(context, locals)
17
+ end
18
+ it "skips setting dependencies when the logical path doesn't match the asset_path_regexp" do
19
+ I18n::Js.stub(:config_path => "/path/to/i18n/js/config.yml")
20
+ context = double("context")
21
+ context.should_receive(:logical_path).and_return("not_i18n_translation.js")
22
+ context.should_not_receive(:depend_on)
23
+ locals = double("locals")
24
+ dependency_processor.evaluate(context, locals)
25
+ end
26
+ end
27
+ context "#asset_path_regexp" do
28
+ it "returns a regex of asset_path" do
29
+ I18n::Js::FileDependencyProcessor.asset_path_regexp.should eql(%r{i18n/translation})
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ # Find more details about this configuration file at http://github.com/fnando/i18n-js
2
+ only: "*"
@@ -0,0 +1,2 @@
1
+ # Find more details about this configuration file at http://github.com/fnando/i18n-js
2
+ only: "*"
@@ -0,0 +1 @@
1
+ only: '*'
@@ -0,0 +1,76 @@
1
+ en:
2
+ number:
3
+ format:
4
+ separator: "."
5
+ delimiter: ","
6
+ precision: 3
7
+ currency:
8
+ format:
9
+ format: "%u%n"
10
+ unit: "$"
11
+ separator: "."
12
+ delimiter: ","
13
+ precision: 2
14
+ date:
15
+ formats:
16
+ default: "%Y-%m-%d"
17
+ short: "%b %d"
18
+ long: "%B %d, %Y"
19
+ day_names: [Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday]
20
+ abbr_day_names: [Sun, Mon, Tue, Wed, Thu, Fri, Sat]
21
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
22
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
23
+ # order: [ :year, :month, :day ]
24
+ time:
25
+ formats:
26
+ default: "%a, %d %b %Y %H:%M:%S %z"
27
+ short: "%d %b %H:%M"
28
+ long: "%B %d, %Y %H:%M"
29
+ am: "am"
30
+ pm: "pm"
31
+ admin:
32
+ show:
33
+ title: "Show"
34
+ note: "more details"
35
+ edit:
36
+ title: "Edit"
37
+
38
+ fr:
39
+ date:
40
+ formats:
41
+ default: "%d/%m/%Y"
42
+ short: "%e %b"
43
+ long: "%e %B %Y"
44
+ long_ordinal: "%e %B %Y"
45
+ only_day: "%e"
46
+ day_names: [dimanche, lundi, mardi, mercredi, jeudi, vendredi, samedi]
47
+ abbr_day_names: [dim, lun, mar, mer, jeu, ven, sam]
48
+ month_names: [~, janvier, février, mars, avril, mai, juin, juillet, août, septembre, octobre, novembre, décembre]
49
+ abbr_month_names: [~, jan., fév., mar., avr., mai, juin, juil., août, sept., oct., nov., déc.]
50
+ # order: [ :day, :month, :year ]
51
+ time:
52
+ formats:
53
+ default: "%d %B %Y %H:%M"
54
+ time: "%H:%M"
55
+ short: "%d %b %H:%M"
56
+ long: "%A %d %B %Y %H:%M:%S %Z"
57
+ long_ordinal: "%A %d %B %Y %H:%M:%S %Z"
58
+ only_second: "%S"
59
+ am: 'am'
60
+ pm: 'pm'
61
+ number:
62
+ format:
63
+ precision: 3
64
+ separator: ','
65
+ delimiter: ' '
66
+ currency:
67
+ format:
68
+ unit: '€'
69
+ precision: 2
70
+ format: '%n %u'
71
+ admin:
72
+ show:
73
+ title: "Visualiser"
74
+ note: "plus de détails"
75
+ edit:
76
+ title: "Editer"
@@ -0,0 +1,3 @@
1
+ only:
2
+ - "*.date.formats"
3
+ - "*.number.currency"
@@ -0,0 +1,2 @@
1
+ #This file has no config
2
+
@@ -0,0 +1,2 @@
1
+ # Find more details about this configuration file at http://github.com/fnando/i18n-js
2
+ only: "*.date.formats"
@@ -0,0 +1,11 @@
1
+ require "spec_helper"
2
+
3
+ describe I18n::Js do
4
+ context "general" do
5
+ it "sets empty hash as configuration when no file is found" do
6
+ I18n::Js.stub(:config_path => "/path/to/config/config.yml")
7
+ I18n::Js.config?.should be_false
8
+ I18n::Js.config.should eql({"asset_path" => "i18n/translation", "only" => "*"})
9
+ end
10
+ end
11
+ end