socialcast-i18n-js 4.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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