localeapp 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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