localeapp 0.0.7

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 (60) hide show
  1. data/.autotest +4 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +2 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +7 -0
  6. data/Gemfile +2 -0
  7. data/README.textile +147 -0
  8. data/Rakefile +11 -0
  9. data/bin/localeapp +61 -0
  10. data/cucumber.yml +8 -0
  11. data/features/localeapp_binary.feature +116 -0
  12. data/features/step_definitions/cli_steps.rb +27 -0
  13. data/features/support/env.rb +21 -0
  14. data/features/support/hooks.rb +3 -0
  15. data/init.rb +1 -0
  16. data/lib/locale_app/api_call.rb +9 -0
  17. data/lib/locale_app/api_caller.rb +77 -0
  18. data/lib/locale_app/cli/install.rb +41 -0
  19. data/lib/locale_app/cli/pull.rb +34 -0
  20. data/lib/locale_app/cli/push.rb +49 -0
  21. data/lib/locale_app/cli/update.rb +19 -0
  22. data/lib/locale_app/configuration.rb +93 -0
  23. data/lib/locale_app/exception_handler.rb +21 -0
  24. data/lib/locale_app/key_checker.rb +43 -0
  25. data/lib/locale_app/missing_translations.rb +36 -0
  26. data/lib/locale_app/poller.rb +61 -0
  27. data/lib/locale_app/rails/2_3_translation_helper_monkeypatch.rb +36 -0
  28. data/lib/locale_app/rails/controller.rb +34 -0
  29. data/lib/locale_app/rails/flatten.rb +113 -0
  30. data/lib/locale_app/rails.rb +53 -0
  31. data/lib/locale_app/routes.rb +80 -0
  32. data/lib/locale_app/sender.rb +49 -0
  33. data/lib/locale_app/tasks/locale_app.rake +20 -0
  34. data/lib/locale_app/updater.rb +63 -0
  35. data/lib/locale_app/version.rb +3 -0
  36. data/lib/locale_app.rb +98 -0
  37. data/lib/localeapp.rb +1 -0
  38. data/localeapp.gemspec +35 -0
  39. data/run_ci +5 -0
  40. data/spec/fixtures/en.yml +6 -0
  41. data/spec/fixtures/es.yml +6 -0
  42. data/spec/locale_app/api_call_spec.rb +15 -0
  43. data/spec/locale_app/api_caller_spec.rb +157 -0
  44. data/spec/locale_app/cli/install_spec.rb +42 -0
  45. data/spec/locale_app/cli/pull_spec.rb +45 -0
  46. data/spec/locale_app/cli/push_spec.rb +30 -0
  47. data/spec/locale_app/cli/update_spec.rb +18 -0
  48. data/spec/locale_app/configuration_spec.rb +119 -0
  49. data/spec/locale_app/exception_handler_spec.rb +21 -0
  50. data/spec/locale_app/key_checker_spec.rb +19 -0
  51. data/spec/locale_app/missing_translations_spec.rb +28 -0
  52. data/spec/locale_app/poller_spec.rb +61 -0
  53. data/spec/locale_app/rails/controller_spec.rb +117 -0
  54. data/spec/locale_app/routes_spec.rb +134 -0
  55. data/spec/locale_app/sender_spec.rb +49 -0
  56. data/spec/locale_app/updater_spec.rb +89 -0
  57. data/spec/spec_helper.rb +24 -0
  58. data/spec/support/locale_app_integration_data.rb +33 -0
  59. data/spec/support/locale_app_synchronization_data.rb +21 -0
  60. metadata +300 -0
@@ -0,0 +1,93 @@
1
+ module LocaleApp
2
+ class Configuration
3
+
4
+ # The API key for your project, found on the project edit form
5
+ attr_accessor :api_key
6
+
7
+ # The host to connect to (defaults to api.localeapp.com)
8
+ attr_accessor :host
9
+
10
+ # The port to connect to (defaults to 80)
11
+ attr_accessor :port
12
+
13
+ attr_accessor :http_auth_username
14
+ attr_accessor :http_auth_password
15
+
16
+ # The name of the environment the application is running in
17
+ attr_accessor :environment_name
18
+
19
+ # The path to the project in which the translation occurred, such as the
20
+ # RAILS_ROOT
21
+ attr_accessor :project_root
22
+
23
+ # The names of environments where notifications aren't sent (defaults to
24
+ # 'test', 'cucumber', 'production')
25
+ attr_accessor :disabled_sending_environments
26
+
27
+ # The names of environments where I18n.reload isn't called for each request
28
+ # (defaults to 'test', 'cucumber', 'production')
29
+ attr_accessor :disabled_reloading_environments
30
+
31
+ # The names of environments where updates aren't pulled (defaults to
32
+ # 'test', 'cucumber', 'production')
33
+ attr_accessor :disabled_polling_environments
34
+
35
+ # The logger used by LocaleApp
36
+ attr_accessor :logger
37
+
38
+ # The number of seconds to wait before asking the service for new
39
+ # translations (defaults to 0 - every request).
40
+ attr_accessor :poll_interval
41
+
42
+ # The complete path to the data file where we store synchronization
43
+ # information (defaults to ./locale_app.yml) local_app/rails overwrites
44
+ # this to RAILS_ROOT/log/locale_app.yml
45
+ attr_accessor :synchronization_data_file
46
+
47
+ # The complete path to the directory where translations are stored
48
+ attr_accessor :translation_data_directory
49
+
50
+ def initialize
51
+ @host = 'api.localeapp.com'
52
+ @port = 80
53
+ @disabled_sending_environments = %w(test cucumber production)
54
+ @disabled_reloading_environments = %w(test cucumber production)
55
+ @disabled_polling_environments = %w(test cucumber production)
56
+ @poll_interval = 0
57
+ @synchronization_data_file = File.join('log', 'locale_app.yml')
58
+ @translation_data_directory = File.join('config', 'locales')
59
+ if ENV['DEBUG']
60
+ require 'logger'
61
+ @logger = Logger.new(STDOUT)
62
+ end
63
+ end
64
+
65
+ def polling_disabled?
66
+ disabled_polling_environments.include?(environment_name)
67
+ end
68
+
69
+ def reloading_disabled?
70
+ disabled_reloading_environments.include?(environment_name)
71
+ end
72
+
73
+ def sending_disabled?
74
+ disabled_sending_environments.include?(environment_name)
75
+ end
76
+
77
+ def write_initial(path)
78
+ dir = File.dirname(path)
79
+ FileUtils.mkdir_p(dir)
80
+ File.open(path, 'w+') do |file|
81
+ file.write <<-CONTENT
82
+ require 'locale_app/rails'
83
+
84
+ LocaleApp.configure do |config|
85
+ config.api_key = '#{@api_key}'
86
+ config.host = '#{@host}'
87
+ config.port = #{@port}
88
+ end
89
+ CONTENT
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,21 @@
1
+ module LocaleApp
2
+ class ExceptionHandler
3
+ def self.call(exception, locale, key, options)
4
+ LocaleApp.log(exception.message)
5
+ if I18n::MissingTranslationData === exception
6
+ LocaleApp.log("Detected missing translation for key(s) #{key.inspect}")
7
+
8
+ [*key].each do |key|
9
+ LocaleApp.missing_translations.add(locale, key, options)
10
+ end
11
+
12
+ [locale, key].join(', ')
13
+ else
14
+ LocaleApp.log('Raising exception')
15
+ raise
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ I18n.exception_handler = LocaleApp::ExceptionHandler
@@ -0,0 +1,43 @@
1
+ require 'yaml'
2
+ require 'rest-client'
3
+ require 'time'
4
+
5
+ module LocaleApp
6
+ class KeyChecker
7
+ include ::LocaleApp::ApiCall
8
+
9
+ def check(key)
10
+ if LocaleApp.configuration.nil? # no config file yet
11
+ LocaleApp.configuration = LocaleApp::Configuration.new
12
+ LocaleApp.configuration.host = ENV['LA_TEST_HOST'] if ENV['LA_TEST_HOST']
13
+ end
14
+ LocaleApp.configuration.api_key = key
15
+ api_call :project,
16
+ :success => :handle_success,
17
+ :failure => :handle_failure,
18
+ :max_connection_attempts => 1
19
+
20
+ if @checked
21
+ [@ok, @data]
22
+ else
23
+ [false, "Error communicating with server"]
24
+ end
25
+ end
26
+
27
+ def handle_success(response)
28
+ @checked = true
29
+ @ok = true
30
+ @data = JSON.parse(response)
31
+ end
32
+
33
+ def handle_failure(response)
34
+ if response.code.to_i == 404
35
+ @checked = true
36
+ @ok = false
37
+ @data = {}
38
+ else
39
+ @checked = false
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ module LocaleApp
2
+ MissingTranslationRecord = Struct.new(:key, :locale, :options)
3
+
4
+ class MissingTranslations
5
+ def initialize
6
+ @translations = Hash.new { |h, k| h[k] = {} }
7
+ end
8
+
9
+ def add(locale, key, options = {})
10
+ record = MissingTranslationRecord.new(key, locale, options)
11
+ @translations[locale][key] = record
12
+ end
13
+
14
+ def [](locale)
15
+ @translations[locale]
16
+ end
17
+
18
+ # This method will get cleverer so we don't resend keys we've
19
+ # already sent, or send multiple times for the same locale etc.
20
+ # For now it's pretty dumb
21
+ def to_send
22
+ data = []
23
+ # need the sort to make specs work under 1.8
24
+ @translations.sort { |a, b| a.to_s <=> b.to_s }.each do |locale, records|
25
+ records.each do |key, record|
26
+ missing_data = {}
27
+ missing_data[:key] = key
28
+ missing_data[:locale] = locale
29
+ missing_data[:options] = record.options
30
+ data << missing_data
31
+ end
32
+ end
33
+ data
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,61 @@
1
+ require 'yaml'
2
+ require 'rest-client'
3
+ require 'time'
4
+
5
+ module LocaleApp
6
+ class Poller
7
+ include ::LocaleApp::ApiCall
8
+
9
+ # when we last asked the service for updates
10
+ attr_accessor :polled_at
11
+
12
+ # the last time the service had updates for us
13
+ attr_accessor :updated_at
14
+
15
+ def initialize
16
+ @polled_at = synchronization_data[:polled_at] || 0
17
+ @updated_at = synchronization_data[:updated_at] || 0
18
+ end
19
+
20
+ def synchronization_data
21
+ if File.exists?(LocaleApp.configuration.synchronization_data_file)
22
+ YAML.load_file(LocaleApp.configuration.synchronization_data_file)
23
+ else
24
+ {}
25
+ end
26
+ end
27
+
28
+ def write_synchronization_data!(polled_at, updated_at)
29
+ File.open(LocaleApp.configuration.synchronization_data_file, 'w+') do |f|
30
+ f.write({:polled_at => polled_at, :updated_at => updated_at}.to_yaml)
31
+ end
32
+ end
33
+
34
+ def needs_polling?
35
+ synchronization_data[:polled_at] < (Time.now.to_i - LocaleApp.configuration.poll_interval)
36
+ end
37
+
38
+ def needs_reloading?
39
+ synchronization_data[:updated_at] != @updated_at
40
+ end
41
+
42
+ def poll!
43
+ api_call :translations,
44
+ :url_options => { :query => { :updated_at => updated_at }},
45
+ :success => :handle_success,
46
+ :failure => :handle_failure,
47
+ :max_connection_attempts => 1
48
+ @success
49
+ end
50
+
51
+ def handle_success(response)
52
+ @success = true
53
+ LocaleApp.updater.update(JSON.parse(response))
54
+ write_synchronization_data!(Time.now.to_i, Time.parse(response.headers[:date]).to_i)
55
+ end
56
+
57
+ def handle_failure(response)
58
+ @success = false
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ # We're replacing the original method with one that doesn't set the :raise option.
2
+ # This means the exception handler will be called and missing translations get sent to
3
+ # localeapp. It's ugly but there's no other way to do it :(
4
+
5
+ module LocaleApp::TranslationHelperMonkeyPatch
6
+ # Delegates to I18n#translate but also performs two additional functions. First, it'll catch MissingTranslationData exceptions
7
+ # and turn them into inline spans that contains the missing key, such that you can see in a view what is missing where.
8
+ #
9
+ # Second, it'll scope the key by the current partial if the key starts with a period. So if you call translate(".foo") from the
10
+ # people/index.html.erb template, you'll actually be calling I18n.translate("people.index.foo"). This makes it less repetitive
11
+ # to translate many keys within the same partials and gives you a simple framework for scoping them consistently. If you don't
12
+ # prepend the key with a period, nothing is converted.
13
+ def translate(keys, options = {})
14
+ if multiple_keys = keys.is_a?(Array)
15
+ ActiveSupport::Deprecation.warn "Giving an array to translate is deprecated, please give a symbol or a string instead", caller
16
+ end
17
+
18
+ keys = scope_keys_by_partial(keys)
19
+
20
+ translations = I18n.translate(keys, options)
21
+ translations = [translations] if !multiple_keys && translations.size > 1
22
+ translations = html_safe_translation_keys(keys, translations)
23
+
24
+ if multiple_keys || translations.size > 1
25
+ translations
26
+ else
27
+ translations.first
28
+ end
29
+ rescue I18n::MissingTranslationData => e
30
+ keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
31
+ content_tag('span', keys.join(', '), :class => 'translation_missing')
32
+ end
33
+ alias :t :translate
34
+ end
35
+
36
+ ActionView::Base.send(:include, ::LocaleApp::TranslationHelperMonkeyPatch)
@@ -0,0 +1,34 @@
1
+ module LocaleApp
2
+ module Rails
3
+ module Controller
4
+ def self.included(base)
5
+ base.before_filter :handle_translation_updates
6
+ base.after_filter :send_missing_translations
7
+ end
8
+
9
+ def handle_translation_updates
10
+ unless ::LocaleApp.configuration.polling_disabled?
11
+ ::LocaleApp.log Time.now.to_i.to_s << '-- Handling translation updates'
12
+ if ::LocaleApp.poller.needs_polling?
13
+ ::LocaleApp.log Time.now.to_i.to_s << ' - polling'
14
+ ::LocaleApp.poller.poll!
15
+ end
16
+ end
17
+
18
+ unless ::LocaleApp.configuration.reloading_disabled?
19
+ if ::LocaleApp.poller.needs_reloading?
20
+ ::LocaleApp.log Time.now.to_i.to_s << '- reloading I18n'
21
+ I18n.reload!
22
+ ::LocaleApp.poller.updated_at = ::LocaleApp.poller.synchronization_data[:updated_at]
23
+ end
24
+ end
25
+ end
26
+
27
+ def send_missing_translations
28
+ return if ::LocaleApp.configuration.sending_disabled?
29
+
30
+ ::LocaleApp.sender.post_missing_translations
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,113 @@
1
+ module I18n
2
+ module Backend
3
+ # This module contains several helpers to assist flattening translations.
4
+ # You may want to flatten translations for:
5
+ #
6
+ # 1) speed up lookups, as in the Memoize backend;
7
+ # 2) In case you want to store translations in a data store, as in ActiveRecord backend;
8
+ #
9
+ # You can check both backends above for some examples.
10
+ # This module also keeps all links in a hash so they can be properly resolved when flattened.
11
+ module Flatten
12
+ SEPARATOR_ESCAPE_CHAR = "\001"
13
+ FLATTEN_SEPARATOR = "."
14
+
15
+ # normalize_keys the flatten way. This method is significantly faster
16
+ # and creates way less objects than the one at I18n.normalize_keys.
17
+ # It also handles escaping the translation keys.
18
+ def self.normalize_flat_keys(locale, key, scope, separator)
19
+ keys = [scope, key].flatten.compact
20
+ separator ||= I18n.default_separator
21
+
22
+ if separator != FLATTEN_SEPARATOR
23
+ keys.map! do |k|
24
+ k.to_s.tr("#{FLATTEN_SEPARATOR}#{separator}",
25
+ "#{SEPARATOR_ESCAPE_CHAR}#{FLATTEN_SEPARATOR}")
26
+ end
27
+ end
28
+
29
+ keys.join(".")
30
+ end
31
+
32
+ # Receives a string and escape the default separator.
33
+ def self.escape_default_separator(key) #:nodoc:
34
+ key.to_s.tr(FLATTEN_SEPARATOR, SEPARATOR_ESCAPE_CHAR)
35
+ end
36
+
37
+ # Shortcut to I18n::Backend::Flatten.normalize_flat_keys
38
+ # and then resolve_links.
39
+ def normalize_flat_keys(locale, key, scope, separator)
40
+ key = I18n::Backend::Flatten.normalize_flat_keys(locale, key, scope, separator)
41
+ resolve_link(locale, key)
42
+ end
43
+
44
+ # Store flattened links.
45
+ def links
46
+ @links ||= Hash.new { |h,k| h[k] = {} }
47
+ end
48
+
49
+ # Flatten keys for nested Hashes by chaining up keys:
50
+ #
51
+ # >> { "a" => { "b" => { "c" => "d", "e" => "f" }, "g" => "h" }, "i" => "j"}.wind
52
+ # => { "a.b.c" => "d", "a.b.e" => "f", "a.g" => "h", "i" => "j" }
53
+ #
54
+ def flatten_keys(hash, escape, prev_key=nil, &block)
55
+ hash.each_pair do |key, value|
56
+ key = escape_default_separator(key) if escape
57
+ curr_key = [prev_key, key].compact.join(FLATTEN_SEPARATOR).to_sym
58
+ yield curr_key, value
59
+ flatten_keys(value, escape, curr_key, &block) if value.is_a?(Hash)
60
+ end
61
+ end
62
+
63
+ # Receives a hash of translations (where the key is a locale and
64
+ # the value is another hash) and return a hash with all
65
+ # translations flattened.
66
+ #
67
+ # Nested hashes are included in the flattened hash just if subtree
68
+ # is true and Symbols are automatically stored as links.
69
+ def flatten_translations(locale, data, escape, subtree)
70
+ hash = {}
71
+ flatten_keys(data, escape) do |key, value|
72
+ if value.is_a?(Hash)
73
+ hash[key] = value if subtree
74
+ else
75
+ store_link(locale, key, value) if value.is_a?(Symbol)
76
+ hash[key] = value
77
+ end
78
+ end
79
+ hash
80
+ end
81
+
82
+ protected
83
+
84
+ def store_link(locale, key, link)
85
+ links[locale.to_sym][key.to_s] = link.to_s
86
+ end
87
+
88
+ def resolve_link(locale, key)
89
+ key, locale = key.to_s, locale.to_sym
90
+ links = self.links[locale]
91
+
92
+ if links.key?(key)
93
+ links[key]
94
+ elsif link = find_link(locale, key)
95
+ store_link(locale, key, key.gsub(*link))
96
+ else
97
+ key
98
+ end
99
+ end
100
+
101
+ def find_link(locale, key) #:nodoc:
102
+ links[locale].each do |from, to|
103
+ return [from, to] if key[0, from.length] == from
104
+ end && nil
105
+ end
106
+
107
+ def escape_default_separator(key) #:nodoc:
108
+ I18n::Backend::Flatten.escape_default_separator(key)
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,53 @@
1
+ module LocaleApp
2
+ module Rails
3
+ def self.initialize
4
+ if defined?(::Rails.logger)
5
+ rails_logger = ::Rails.logger
6
+ elsif defined?(RAILS_DEFAULT_LOGGER)
7
+ rails_logger = RAILS_DEFAULT_LOGGER
8
+ end
9
+
10
+ if defined?(::Rails.env)
11
+ rails_env = ::Rails.env
12
+ elsif defined?(RAILS_ENV)
13
+ rails_env = RAILS_ENV
14
+ end
15
+
16
+ if defined?(::Rails.root)
17
+ rails_root = ::Rails.root
18
+ elsif defined?(RAILS_ROOT)
19
+ rails_root = RAILS_ROOT
20
+ end
21
+
22
+ ActionController::Base.send(:include, LocaleApp::Rails::Controller)
23
+
24
+ if ::Rails::VERSION::MAJOR == 2 && ::Rails::VERSION::MINOR >= 3 # TODO: Check previous rails versions if required
25
+ require 'locale_app/rails/2_3_translation_helper_monkeypatch'
26
+ end
27
+
28
+ LocaleApp.configure do |config|
29
+ config.logger = rails_logger
30
+ config.environment_name = rails_env
31
+ config.project_root = rails_root
32
+ config.synchronization_data_file = File.join([rails_root, 'log', 'locale_app.yml'])
33
+ config.translation_data_directory = File.join([rails_root, 'config', 'locales'])
34
+ end
35
+ initialize_synchronization_data_file
36
+ end
37
+
38
+ def self.initialize_synchronization_data_file
39
+ if !File.exists?(LocaleApp.configuration.synchronization_data_file)
40
+ File.open(LocaleApp.configuration.synchronization_data_file, 'w') do |f|
41
+ f.write({:polled_at => Time.now.to_i, :updated_at => Time.now.to_i}.to_yaml)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ if defined?(Rails)
49
+ require 'locale_app/rails/controller'
50
+ require 'locale_app/exception_handler'
51
+ LocaleApp::Rails.initialize
52
+ LocaleApp.log('Loaded locale_app/rails')
53
+ end
@@ -0,0 +1,80 @@
1
+ module LocaleApp
2
+ module Routes
3
+ VERSION = 'v1'
4
+
5
+ def project_endpoint(options = {})
6
+ [:get, project_url(options)]
7
+ end
8
+
9
+ def project_url(options = {})
10
+ options[:format] ||= 'json'
11
+ URI::HTTP.build(base_options.merge(:path => project_path(options[:format]))).to_s
12
+ end
13
+
14
+ def translations_url(options={})
15
+ options[:format] ||= 'json'
16
+ url = URI::HTTP.build(base_options.merge(:path => translations_path(options[:format])))
17
+ url.query = options[:query].map { |k,v| "#{k}=#{v}" }.join('&') if options[:query]
18
+ url.to_s
19
+ end
20
+
21
+ def translations_endpoint(options = {})
22
+ [:get, translations_url(options)]
23
+ end
24
+
25
+ def create_translation_endpoint(options = {})
26
+ [:post, translations_url(options)]
27
+ end
28
+
29
+ def missing_translations_endpoint(options = {})
30
+ [:post, missing_translations_url(options)]
31
+ end
32
+
33
+ def missing_translations_url(options={})
34
+ options[:format] ||= 'json'
35
+ url = URI::HTTP.build(base_options.merge(:path => missing_translations_path(options[:format])))
36
+ url.query = options[:query].map { |k,v| "#{k}=#{v}" }.join('&') if options[:query]
37
+ url.to_s
38
+ end
39
+
40
+ def import_endpoint(options = {})
41
+ [:post, import_url(options)]
42
+ end
43
+
44
+ def import_url(options={})
45
+ URI::HTTP.build(base_options.merge(:path => import_path)).to_s
46
+ end
47
+
48
+ private
49
+
50
+ def base_options
51
+ options = {:host => LocaleApp.configuration.host, :port => LocaleApp.configuration.port}
52
+ if LocaleApp.configuration.http_auth_username
53
+ options[:userinfo] = "#{LocaleApp.configuration.http_auth_username}:#{LocaleApp.configuration.http_auth_password}"
54
+ end
55
+ options
56
+ end
57
+
58
+ def project_path(format = nil)
59
+ path = "/#{VERSION}/projects/#{LocaleApp.configuration.api_key}"
60
+ path << ".#{format}" if format
61
+ path
62
+ end
63
+
64
+ def translations_path(format = nil)
65
+ path = project_path << '/translations'
66
+ path << ".#{format}" if format
67
+ path
68
+ end
69
+
70
+ def missing_translations_path(format = nil)
71
+ path = project_path << '/translations/missing'
72
+ path << ".#{format}" if format
73
+ path
74
+ end
75
+
76
+ def import_path(format = nil)
77
+ project_path << '/import/'
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ require 'rest-client'
2
+ require 'json'
3
+
4
+ module LocaleApp
5
+ class Sender
6
+ include ::LocaleApp::ApiCall
7
+ include ::LocaleApp::Routes
8
+
9
+ def post_translation(locale, key, options, value = nil)
10
+ options ||= {}
11
+ translation = { :key => key, :locale => locale, :substitutions => options.keys, :description => value}
12
+ @data = { :translation => translation }
13
+ api_call :create_translation,
14
+ :payload => @data.to_json,
15
+ :request_options => { :content_type => :json },
16
+ :success => :handle_single_translation_success,
17
+ :failure => :handle_single_translation_failure,
18
+ :max_connection_attempts => 1
19
+ end
20
+
21
+ def handle_single_translation_success(response)
22
+ LocaleApp.log([translations_url, response.code, @data.inspect].join(' - '))
23
+ end
24
+
25
+ def handle_single_translation_failure(response)
26
+ LocaleApp.log([translations_url, response.code, @data.inspect].join(' - '))
27
+ end
28
+
29
+ def post_missing_translations
30
+ to_send = LocaleApp.missing_translations.to_send
31
+ return if to_send.empty?
32
+ @data = { :translations => to_send }
33
+ api_call :missing_translations,
34
+ :payload => @data.to_json,
35
+ :request_options => { :content_type => :json },
36
+ :success => :handle_missing_translation_success,
37
+ :failure => :handle_missing_translation_failure,
38
+ :max_connection_attempts => 1
39
+ end
40
+
41
+ def handle_missing_translation_success(response)
42
+ LocaleApp.log([translations_url, response.code, @data.inspect].join(' - '))
43
+ end
44
+
45
+ def handle_missing_translation_failure(response)
46
+ LocaleApp.log([translations_url, response.code, @data.inspect].join(' - '))
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ namespace :locale_app do
2
+ desc 'Imports the en.yml file to the LocaleServer'
3
+ task :import => :environment do
4
+ require 'flatten'
5
+ include I18n::Backend::Flatten
6
+ yml = YAML.load_file(ENV['LOCALE_FILE'])
7
+
8
+ yml.each do |locale, translations|
9
+ flatten_translations(
10
+ locale,
11
+ translations,
12
+ I18n::Backend::Flatten::SEPARATOR_ESCAPE_CHAR,
13
+ nil
14
+ ).each do |key, value|
15
+ puts "#{key} => #{value}"
16
+ LocaleApp.sender.post_translation(locale, key, {}, value)
17
+ end
18
+ end
19
+ end
20
+ end