translation 0.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +50 -0
  3. data/lib/translation.rb +75 -0
  4. data/lib/translation/client.rb +27 -0
  5. data/lib/translation/client/base_operation.rb +49 -0
  6. data/lib/translation/client/base_operation/create_new_mo_files_step.rb +22 -0
  7. data/lib/translation/client/base_operation/dump_haml_gettext_keys_step.rb +44 -0
  8. data/lib/translation/client/base_operation/dump_slim_gettext_keys_step.rb +43 -0
  9. data/lib/translation/client/base_operation/save_new_po_files_step.rb +31 -0
  10. data/lib/translation/client/base_operation/save_new_yaml_files_step.rb +37 -0
  11. data/lib/translation/client/base_operation/save_special_yaml_files_step.rb +53 -0
  12. data/lib/translation/client/init_operation.rb +42 -0
  13. data/lib/translation/client/init_operation/cleanup_yaml_files_step.rb +52 -0
  14. data/lib/translation/client/init_operation/create_yaml_po_files_step.rb +53 -0
  15. data/lib/translation/client/init_operation/update_and_collect_po_files_step.rb +33 -0
  16. data/lib/translation/client/init_operation/update_pot_file_step.rb +18 -0
  17. data/lib/translation/client/purge_operation.rb +13 -0
  18. data/lib/translation/client/sync_operation.rb +41 -0
  19. data/lib/translation/client/sync_operation/create_yaml_pot_file_step.rb +46 -0
  20. data/lib/translation/client/sync_operation/update_and_collect_pot_file_step.rb +18 -0
  21. data/lib/translation/config.rb +20 -0
  22. data/lib/translation/controller.rb +18 -0
  23. data/lib/translation/flat_hash.rb +112 -0
  24. data/lib/translation/railtie.rb +37 -0
  25. data/lib/translation/tasks.rb +31 -0
  26. data/lib/translation/yaml_conversion.rb +81 -0
  27. metadata +104 -11
  28. data/README.rdoc +0 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d107d21e5d34e6c3196473d241e89ce9dfdc6068
4
- data.tar.gz: 24850c42bf206998b93f066cd1a7ed182df94521
3
+ metadata.gz: 8d9ba5b1f146ab9c345be6542b3c2b02a7645855
4
+ data.tar.gz: ecb4386329547f2b0894146b69374c650f2dcd10
5
5
  SHA512:
6
- metadata.gz: b2474e725b88ccba46604c91b470710ad28aae86ce2219dd57620ccae1baa984268f4d59fc02f527485610b8595eecd996d93b0ed794a4359fb902dcd45932f1
7
- data.tar.gz: 7e08ab1177dbb8a667caea4ca4334f14bcb2ce5215519228ad30a6c6c3b7feb3403f3926ecb98095db10180a92cafbfe570b6a09bd914af6b20c97d93b041cc7
6
+ metadata.gz: 4f1af0d393bee32f48cd04b5a453436d7e353e83ca0c26fcbb7133997301a9f296b31305c3a2d76600b9ce909c87b3076a5ab25aa8c88ad5706122a69a9f8240
7
+ data.tar.gz: 952f4b1cbf1bdbe7083d8997aaad7f41b25bd9865bbb1cb76d02e96ec8d14dcef74206803224f1cc7b1efd2cfee55c24d56cd8553c4b8dbf9cda1259ff1996b8
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # gem for [Rails Translation](http://rails.translation.io)
2
+
3
+ ## Description
4
+
5
+ Add this gem to your [Rails](http://rubyonrails.org) app to translate it with [Rails Translation](http://rails.translation.io).
6
+
7
+ ## Installation
8
+
9
+ Add the gem to your project's Gemfile :
10
+
11
+ gem 'translation'
12
+
13
+ Then :
14
+
15
+ * Create a translation project from the UI.
16
+ * Copy the initializer into your Rails app (`config/initializers/translation.rb`)
17
+
18
+ And finish by inititalizing your translation project with :
19
+
20
+ bundle exec rake translation:init
21
+
22
+ ## Synchronization
23
+
24
+ To get new translations from Rails Translation and to send new translatable strings, simply run :
25
+
26
+ bundle exec rake translation:sync
27
+
28
+ ## Purge
29
+
30
+ If you need to remove unused keys taking the current branch as reference :
31
+
32
+ bundle exec rake translation:purge
33
+
34
+ Note that this operation will also perform a sync at the same time.
35
+
36
+ Warning : all keys that are not present in the current branch will be **permanently deleted both on Rails Translation and in your app**.
37
+
38
+ ## Tests
39
+
40
+ To run the specs :
41
+
42
+ bundle
43
+ bundle exec rspec
44
+
45
+ ## Credits
46
+
47
+ The [translation gem](https://rubygems.org/gems/translation) in released under MIT license by [Aurélien Malisart](http://aurelien.malisart.be) and [Michaël Hoste](http://80limit.com) (see MIT-LICENSE
48
+ file).
49
+
50
+ (c) http://rails.translation.io
data/lib/translation.rb CHANGED
@@ -1,2 +1,77 @@
1
+ require 'net/http'
2
+
3
+ require 'gettext'
4
+ require 'gettext/po'
5
+ require 'gettext/po_parser'
6
+ require 'gettext/tools'
7
+ require 'gettext/text_domain_manager'
8
+
1
9
  module Translation
10
+ GETTEXT_METHODS = [
11
+ :_, :n_, :p_, :s_, :np_, :ns_, :N_, :Nn,
12
+ :gettext, :sgettext, :ngettext, :nsgettext, :pgettext, :npgettext
13
+ ]
14
+
15
+ TEXT_DOMAIN = 'app'
16
+ SOURCE_FILES_PATTERN = '**/*.{rb,erb,html.erb,xml.erb}'
17
+ end
18
+
19
+ require 'translation/config'
20
+ require 'translation/railtie'
21
+ require 'translation/client'
22
+ require 'translation/flat_hash'
23
+ require 'translation/yaml_conversion'
24
+
25
+ require 'translation/controller'
26
+
27
+ module Translation
28
+ module Proxy
29
+ include GetText
30
+ end
31
+
32
+ class << self
33
+ attr_reader :config, :client
34
+
35
+ def configure(&block)
36
+ if Rails.env.development?
37
+ GetText::TextDomainManager.cached = false
38
+ end
39
+
40
+ @config ||= Config.new
41
+
42
+ yield @config
43
+
44
+ Proxy.bindtextdomain(TEXT_DOMAIN, {
45
+ :path => @config.locales_path,
46
+ :charset => 'utf-8'
47
+ })
48
+
49
+ Proxy.textdomain(TEXT_DOMAIN)
50
+ Object.delegate *GETTEXT_METHODS, :to => Proxy
51
+
52
+ @client = Client.new(@config.api_key, @config.endpoint)
53
+
54
+ return true
55
+ end
56
+
57
+ def pot_path
58
+ File.join(Translation.config.locales_path, "#{TEXT_DOMAIN}.pot")
59
+ end
60
+
61
+ def info(message, level = 0, verbose_level = 0)
62
+ verbose = @config.try(:verbose) || 0
63
+ if verbose >= verbose_level
64
+ indent = (1..level).to_a.collect { " " }.join('')
65
+ puts "#{indent}* #{message}"
66
+ end
67
+ end
68
+
69
+ def normalize_path(relative_or_absolute_path)
70
+ File.expand_path(relative_or_absolute_path).gsub("#{Dir.pwd}/", '')
71
+ end
72
+
73
+ def version
74
+ Gem::Specification::find_by_name('translation').version.to_s
75
+ end
76
+ end
2
77
  end
@@ -0,0 +1,27 @@
1
+ require 'translation/client/base_operation'
2
+ require 'translation/client/init_operation'
3
+ require 'translation/client/sync_operation'
4
+ require 'translation/client/purge_operation'
5
+
6
+ module Translation
7
+ class Client
8
+ attr_reader :api_key, :endpoint
9
+
10
+ def initialize(api_key, endpoint)
11
+ @api_key = api_key
12
+ @endpoint = endpoint
13
+ end
14
+
15
+ def init
16
+ Translation::Client::InitOperation.new(self).run
17
+ end
18
+
19
+ def sync
20
+ Translation::Client::SyncOperation.new(self).run
21
+ end
22
+
23
+ def purge
24
+ Translation::Client::SyncOperation.new(self).run(true)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require 'translation/client/base_operation/save_new_po_files_step'
2
+ require 'translation/client/base_operation/create_new_mo_files_step'
3
+ require 'translation/client/base_operation/save_new_yaml_files_step'
4
+ require 'translation/client/base_operation/save_special_yaml_files_step'
5
+ require 'translation/client/base_operation/dump_haml_gettext_keys_step'
6
+ require 'translation/client/base_operation/dump_slim_gettext_keys_step'
7
+
8
+ module Translation
9
+ class Client
10
+ class BaseOperation
11
+ attr_accessor :client, :params
12
+
13
+ GETTEXT_ENTRY_RE = Regexp.new('(?:' + Translation::GETTEXT_METHODS.join('|') + ')\([^)]+\)?\)')
14
+
15
+ def initialize(client, perform_real_requests = true)
16
+ @client = client
17
+ @params = {
18
+ 'gem_version' => Translation.version,
19
+ 'target_languages[]' => Translation.config.target_locales.map(&:to_s)
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def perform_request(uri, params)
26
+ begin
27
+ http = Net::HTTP.new(uri.host, uri.port)
28
+ http.read_timeout = 500
29
+
30
+ request = Net::HTTP::Post.new(uri.request_uri)
31
+ request.set_form_data(params)
32
+
33
+ response = http.request(request)
34
+ parsed_response = JSON.parse(response.body)
35
+
36
+ if response.code.to_i == 200
37
+ return parsed_response
38
+ elsif response.code.to_i == 400 && parsed_response.has_key?('error')
39
+ $stderr.puts "[Error] #{parsed_response['error']}"
40
+ else
41
+ $stderr.puts "[Error] Unknown error."
42
+ end
43
+ rescue Errno::ECONNREFUSED
44
+ $stderr.puts "[Error] Server not responding."
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class CreateNewMoFilesStep
5
+ def initialize(locales_path)
6
+ @locales_path = locales_path
7
+ end
8
+
9
+ def run
10
+ Translation.info "Creating new MO files."
11
+
12
+ Dir["#{@locales_path}/*/#{TEXT_DOMAIN}.po"].each do |po_path|
13
+ mo_path = "#{File.dirname(po_path)}/LC_MESSAGES/app.mo"
14
+ Translation.info mo_path, 2, 2
15
+ FileUtils.mkdir_p(File.dirname(mo_path))
16
+ GetText::Tools::MsgFmt.run(po_path, '-o', mo_path)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,44 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class DumpHamlGettextKeysStep
5
+ def initialize(haml_source_files)
6
+ @haml_source_files = haml_source_files
7
+ end
8
+
9
+ def run
10
+ if @haml_source_files.any? && defined?(Haml)
11
+ Translation.info "Extracting Gettext entries from HAML files."
12
+
13
+ File.open(File.join('tmp', 'translation-haml-gettext.rb'), 'w') do |file|
14
+ extracted_gettext_entries.each do |entry|
15
+ file.puts "#{entry}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def extracted_gettext_entries
24
+ entries = []
25
+
26
+ @haml_source_files.each do |haml_file_path|
27
+ Translation.info haml_file_path, 2, 2
28
+
29
+ haml_data = File.read(haml_file_path)
30
+ ruby_data = Haml::Engine.new(haml_data).precompiled
31
+
32
+ ruby_data.scan(GETTEXT_ENTRY_RE).each do |entry|
33
+ entries << entry
34
+ end
35
+ end
36
+
37
+ Translation.info "#{entries.size} entries found", 2, 2
38
+
39
+ entries
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,43 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class DumpSlimGettextKeysStep
5
+ def initialize(slim_source_files)
6
+ @slim_source_files = slim_source_files
7
+ end
8
+
9
+ def run
10
+ if @slim_source_files.any? && defined?(Slim)
11
+ Translation.info "Extracting Gettext entries from SLIM files."
12
+
13
+ File.open(File.join('tmp', 'translation-slim-gettext.rb'), 'w') do |file|
14
+ extracted_gettext_entries.each do |entry|
15
+ file.puts "#{entry}"
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def extracted_gettext_entries
24
+ entries = []
25
+
26
+ @slim_source_files.each do |slim_file_path|
27
+ Translation.info slim_file_path, 2, 2
28
+
29
+ ruby_data = Slim::Template.new(slim_file_path, {}).precompiled_template
30
+
31
+ ruby_data.scan(GETTEXT_ENTRY_RE).each do |entry|
32
+ entries << entry
33
+ end
34
+ end
35
+
36
+ Translation.info "#{entries.size} entries found", 2, 2
37
+
38
+ entries
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class SaveNewPoFilesStep
5
+ def initialize(target_locales, locales_path, parsed_response)
6
+ @target_locales = target_locales
7
+ @locales_path = locales_path
8
+ @parsed_response = parsed_response
9
+ end
10
+
11
+ def run
12
+ Translation.info "Saving new PO files."
13
+
14
+ @target_locales.each do |target_locale|
15
+ if @parsed_response.has_key?("po_data_#{target_locale}")
16
+ po_path = File.join(@locales_path, target_locale.to_s.gsub('-', '_'), "#{TEXT_DOMAIN}.po")
17
+ FileUtils.mkdir_p(File.dirname(po_path))
18
+ Translation.info po_path, 2, 2
19
+
20
+ File.open(po_path, 'wb') do |file|
21
+ file.write(@parsed_response["po_data_#{target_locale}"])
22
+ end
23
+ end
24
+ end
25
+
26
+ return self
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class SaveNewYamlFilesStep
5
+ def initialize(target_locales, yaml_locales_path, parsed_response)
6
+ @target_locales = target_locales
7
+ @yaml_locales_path = yaml_locales_path
8
+ @parsed_response = parsed_response
9
+ end
10
+
11
+ def run
12
+ Translation.info "Saving new translation YAML files."
13
+
14
+ @target_locales.each do |target_locale|
15
+ if @parsed_response.has_key?("yaml_po_data_#{target_locale}")
16
+ FileUtils.mkdir_p(@yaml_locales_path)
17
+ yaml_path = File.join(@yaml_locales_path, "translation.#{target_locale}.yml")
18
+ Translation.info yaml_path, 2, 2
19
+ yaml_data = YAMLConversion.get_yaml_data_from_po_data(@parsed_response["yaml_po_data_#{target_locale}"], target_locale)
20
+
21
+ top_comment = <<EOS
22
+ # WARNING. THIS FILE WAS AUTO-GENERATED BY THE TRANSLATION GEM.
23
+ # IF YOU UPDATE IT, YOUR CHANGES WILL BE LOST AT THE NEXT SYNC.
24
+ EOS
25
+
26
+ File.open(yaml_path, 'wb') do |file|
27
+ file.write("---\n")
28
+ file.write(top_comment)
29
+ file.write(yaml_data.split("---\n", 2).last)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,53 @@
1
+ module Translation
2
+ class Client
3
+ class BaseOperation
4
+ class SaveSpecialYamlFilesStep
5
+
6
+ def initialize(source_locale, target_locales, yaml_locales_path, yaml_file_paths)
7
+ @source_locale = source_locale
8
+ @target_locales = target_locales
9
+ @yaml_locales_path = yaml_locales_path
10
+ @yaml_file_paths = yaml_file_paths
11
+ end
12
+
13
+ def run
14
+ Translation.info "Saving new localization YAML files (with non-string values)."
15
+ all_flat_translations = {}
16
+
17
+ @yaml_file_paths.each do |file_path|
18
+ all_flat_translations.merge!(
19
+ YAMLConversion.get_flat_translations_for_yaml_file(file_path)
20
+ )
21
+ end
22
+
23
+ all_flat_special_translations = all_flat_translations.select do |key, value|
24
+ !value.is_a?(String) && !value.nil?
25
+ end
26
+
27
+ source_flat_special_translations = all_flat_special_translations.select do |key|
28
+ key.start_with?("#{@source_locale}.")
29
+ end
30
+
31
+ @target_locales.each do |target_locale|
32
+ yaml_path = File.join(@yaml_locales_path, "localization.#{target_locale}.yml")
33
+
34
+ Translation.info yaml_path, 2, 2
35
+ flat_translations = {}
36
+
37
+ source_flat_special_translations.each_pair do |key, value|
38
+ target_key = key.gsub(/\A#{@source_locale}\./, "#{target_locale}.")
39
+ flat_translations[target_key] = all_flat_special_translations[target_key]
40
+ end
41
+
42
+ yaml_data = YAMLConversion.get_yaml_data_from_flat_translations(flat_translations)
43
+
44
+ File.open(yaml_path, 'wb') do |file|
45
+ file.write(yaml_data)
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ require 'translation/client/init_operation/update_pot_file_step'
2
+ require 'translation/client/init_operation/update_and_collect_po_files_step'
3
+ require 'translation/client/init_operation/create_yaml_po_files_step'
4
+ require 'translation/client/init_operation/cleanup_yaml_files_step'
5
+
6
+ module Translation
7
+ class Client
8
+ class InitOperation < BaseOperation
9
+ def run
10
+ haml_source_files = Dir['**/*.{haml}']
11
+ slim_source_files = Dir['**/*.{slim}']
12
+
13
+ BaseOperation::DumpHamlGettextKeysStep.new(haml_source_files).run
14
+ BaseOperation::DumpSlimGettextKeysStep.new(slim_source_files).run
15
+
16
+ source_files = Dir[SOURCE_FILES_PATTERN]
17
+ pot_path = Translation.pot_path
18
+ source_locale = Translation.config.source_locale
19
+ target_locales = Translation.config.target_locales
20
+ locales_path = Translation.config.locales_path
21
+ yaml_locales_path = 'config/locales'
22
+ yaml_file_paths = I18n.load_path
23
+
24
+ UpdatePotFileStep.new(pot_path, source_files).run
25
+ UpdateAndCollectPoFilesStep.new(target_locales, pot_path, locales_path).run(params)
26
+ CreateYamlPoFilesStep.new(source_locale, target_locales, yaml_file_paths).run(params)
27
+
28
+ Translation.info "Sending data to server"
29
+ uri = URI("http://#{client.endpoint}/projects/#{client.api_key}/init")
30
+ parsed_response = perform_request(uri, params)
31
+
32
+ unless parsed_response.nil?
33
+ BaseOperation::SaveNewPoFilesStep.new(target_locales, locales_path, parsed_response).run
34
+ BaseOperation::SaveNewYamlFilesStep.new(target_locales, yaml_locales_path, parsed_response).run
35
+ BaseOperation::SaveSpecialYamlFilesStep.new(source_locale, target_locales, yaml_locales_path, yaml_file_paths).run
36
+ CleanupYamlFilesStep.new(source_locale, target_locales, yaml_file_paths, yaml_locales_path).run
37
+ BaseOperation::CreateNewMoFilesStep.new(locales_path).run
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,52 @@
1
+ module Translation
2
+ class Client
3
+ class InitOperation < BaseOperation
4
+ class CleanupYamlFilesStep
5
+ def initialize(source_locale, target_locales, yaml_file_paths, yaml_locales_path)
6
+ @source_locale = source_locale
7
+ @target_locales = target_locales
8
+ @yaml_file_paths = yaml_file_paths
9
+ @yaml_locales_path = yaml_locales_path
10
+ end
11
+
12
+ def run
13
+ @yaml_file_paths.each do |locale_file_path|
14
+ in_project = locale_file_path_in_project?(locale_file_path)
15
+
16
+ protected_file = @target_locales.any? do |target_locale|
17
+ paths = [
18
+ File.join(@yaml_locales_path, "translation.#{target_locale}.yml").to_s,
19
+ File.join(@yaml_locales_path, "localization.#{target_locale}.yml").to_s
20
+ ]
21
+
22
+ paths.include?(locale_file_path)
23
+ end
24
+
25
+ if in_project && !protected_file
26
+ content_hash = YAML::load(File.read(locale_file_path))
27
+ new_content_hash = content_hash.keep_if { |k| k.to_s == @source_locale.to_s }
28
+
29
+ if new_content_hash.keys.any?
30
+ Translation.info "Rewriting #{locale_file_path}", 2, 2
31
+ File.open(locale_file_path, 'wb') do |file|
32
+ file.write(new_content_hash.to_yaml)
33
+ end
34
+ else
35
+ Translation.info "Removing #{locale_file_path}", 2, 2
36
+ FileUtils.rm(locale_file_path)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def locale_file_path_in_project?(locale_file_path)
45
+ Translation.normalize_path(locale_file_path).start_with?(
46
+ Translation.normalize_path(@yaml_locales_path)
47
+ )
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ module Translation
2
+ class Client
3
+ class InitOperation < BaseOperation
4
+ class CreateYamlPoFilesStep
5
+ def initialize(source_locale, target_locales, yaml_file_paths)
6
+ @source_locale = source_locale
7
+ @target_locales = target_locales
8
+ @yaml_file_paths = yaml_file_paths
9
+ end
10
+
11
+ def run(params)
12
+ Translation.info "Importing translations from YAML files."
13
+ all_flat_translations = {}
14
+
15
+ @yaml_file_paths.each do |file_path|
16
+ Translation.info file_path, 2, 2
17
+ all_flat_translations.merge!(YAMLConversion.get_flat_translations_for_yaml_file(file_path))
18
+ end
19
+
20
+ all_flat_string_translations = all_flat_translations.select do |key, value|
21
+ key.present? && value.is_a?(String) && !value.nil?
22
+ end
23
+
24
+ source_flat_string_tanslations = all_flat_string_translations.select do |key|
25
+ key.start_with?("#{@source_locale}.") && !key.start_with?("#{@source_locale}.faker.")
26
+ end
27
+
28
+ @target_locales.each do |target_locale|
29
+ po_representation = GetText::PO.new
30
+
31
+ source_flat_string_tanslations.each_pair do |key, value|
32
+ target_key = key.gsub(/\A#{Translation.config.source_locale}\./, "#{target_locale}.")
33
+ msgid = value
34
+ msgstr = all_flat_string_translations[target_key]
35
+
36
+ unless msgid.blank?
37
+ po_entry = GetText::POEntry.new(:msgctxt)
38
+ po_entry.msgid = msgid
39
+ po_entry.msgstr = msgstr
40
+ po_entry.msgctxt = key.split('.', 2).last
41
+ #po_entry.references = [ value[:locale_file_path] ]
42
+
43
+ po_representation[po_entry.msgctxt, po_entry.msgid] = po_entry
44
+ end
45
+ end
46
+
47
+ params["yaml_po_data_#{target_locale}"] = po_representation.to_s
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,33 @@
1
+ module Translation
2
+ class Client
3
+ class InitOperation < BaseOperation
4
+ class UpdateAndCollectPoFilesStep
5
+ def initialize(target_locales, pot_path, locales_path)
6
+ @target_locales = target_locales
7
+ @pot_path = pot_path
8
+ @locales_path = locales_path
9
+ end
10
+
11
+ def run(params)
12
+ Translation.info "Updating PO files."
13
+
14
+ @target_locales.each do |target_locale|
15
+ po_path = "#{@locales_path}/#{target_locale.gsub('-', '_')}/#{TEXT_DOMAIN}.po"
16
+ Translation.info po_path, 2, 2
17
+
18
+ if File.exist?(po_path)
19
+ GetText::Tools::MsgMerge.run(po_path, @pot_path, '-o', po_path, '--no-fuzzy-matching', '--no-obsolete-entries')
20
+ else
21
+ FileUtils.mkdir_p(File.dirname(po_path))
22
+ GetText::Tools::MsgInit.run('-i', @pot_path, '-o', po_path, '-l', target_locale, '--no-translator')
23
+ end
24
+
25
+ params["po_data_#{target_locale}"] = File.read(po_path)
26
+ end
27
+
28
+ return self
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Translation
2
+ class Client
3
+ class InitOperation < BaseOperation
4
+ class UpdatePotFileStep
5
+ def initialize(pot_path, source_files)
6
+ @pot_path = pot_path
7
+ @source_files = source_files
8
+ end
9
+
10
+ def run
11
+ Translation.info "Updating POT file."
12
+ FileUtils.mkdir_p(File.dirname(@pot_path))
13
+ GetText::Tools::XGetText.run(*@source_files, '-o', @pot_path, '--msgid-bugs-address', 'contact@translation.io',)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ # NOT USED
2
+
3
+ module Translation
4
+ class Client
5
+ class PurgeOperation < BaseOperation
6
+
7
+ def run
8
+
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,41 @@
1
+ require 'translation/client/sync_operation/update_and_collect_pot_file_step'
2
+ require 'translation/client/sync_operation/create_yaml_pot_file_step'
3
+
4
+ module Translation
5
+ class Client
6
+ class SyncOperation < BaseOperation
7
+ def run(purge = false)
8
+ haml_source_files = Dir['**/*.{haml}']
9
+ slim_source_files = Dir['**/*.{slim}']
10
+
11
+ BaseOperation::DumpHamlGettextKeysStep.new(haml_source_files).run
12
+ BaseOperation::DumpSlimGettextKeysStep.new(slim_source_files).run
13
+
14
+ source_files = Dir[SOURCE_FILES_PATTERN]
15
+ pot_path = Translation.pot_path
16
+ source_locale = Translation.config.source_locale
17
+ target_locales = Translation.config.target_locales
18
+ locales_path = Translation.config.locales_path
19
+ yaml_locales_path = 'config/locales'
20
+ yaml_file_paths = I18n.load_path
21
+
22
+ UpdateAndCollectPotFileStep.new(pot_path, source_files).run(params)
23
+ CreateYamlPotFileStep.new(source_locale, yaml_file_paths).run(params)
24
+
25
+ if purge
26
+ params['purge'] = 'true'
27
+ end
28
+
29
+ uri = URI("http://#{client.endpoint}/projects/#{client.api_key}/sync")
30
+ parsed_response = perform_request(uri, params)
31
+
32
+ unless parsed_response.nil?
33
+ BaseOperation::SaveNewPoFilesStep.new(target_locales, locales_path, parsed_response).run
34
+ BaseOperation::CreateNewMoFilesStep.new(locales_path).run
35
+ BaseOperation::SaveNewYamlFilesStep.new(target_locales, yaml_locales_path, parsed_response).run
36
+ BaseOperation::SaveSpecialYamlFilesStep.new(source_locale, target_locales, yaml_locales_path, yaml_file_paths).run
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,46 @@
1
+ module Translation
2
+ class Client
3
+ class SyncOperation < BaseOperation
4
+ class CreateYamlPotFileStep
5
+ def initialize(source_locale, yaml_file_paths)
6
+ @source_locale = source_locale
7
+ @yaml_file_paths = yaml_file_paths
8
+ end
9
+
10
+ def run(params)
11
+ Translation.info "Generating POT file from YAML files."
12
+ all_flat_translations = {}
13
+
14
+ @yaml_file_paths.each do |file_path|
15
+ Translation.info file_path, 2, 2
16
+ all_flat_translations.merge!(
17
+ YAMLConversion.get_flat_translations_for_yaml_file(file_path)
18
+ )
19
+ end
20
+
21
+ source_flat_string_tanslations = all_flat_translations.select do |key, value|
22
+ value.is_a?(String) && key.start_with?("#{@source_locale}.") && !key.start_with?("#{@source_locale}.faker.")
23
+ end
24
+
25
+ pot_representation = GetText::PO.new
26
+
27
+ source_flat_string_tanslations.each_pair do |key, value|
28
+ msgid = value
29
+
30
+ unless msgid.blank?
31
+ pot_entry = GetText::POEntry.new(:msgctxt)
32
+ pot_entry.msgid = msgid
33
+ pot_entry.msgstr = ''
34
+ pot_entry.msgctxt = key.split('.', 2).last
35
+ #pot_entry.references = [ value[:locale_file_path] ]
36
+
37
+ pot_representation[pot_entry.msgctxt, pot_entry.msgid] = pot_entry
38
+ end
39
+ end
40
+
41
+ params['yaml_pot_data'] = pot_representation.to_s
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ module Translation
2
+ class Client
3
+ class SyncOperation < BaseOperation
4
+ class UpdateAndCollectPotFileStep
5
+ def initialize(pot_path, source_files)
6
+ @pot_path = pot_path
7
+ @source_files = source_files
8
+ end
9
+
10
+ def run(params)
11
+ Translation.info "Updating POT file."
12
+ GetText::Tools::XGetText.run(*@source_files, '-o', @pot_path)
13
+ params['pot_data'] = File.read(@pot_path)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Translation
2
+ class Config
3
+ attr_accessor :api_key, :locales_path
4
+ attr_accessor :source_locale, :target_locales
5
+ attr_accessor :endpoint
6
+ attr_accessor :verbose
7
+
8
+ def initialize
9
+ self.locales_path = File.join('config', 'locales', 'gettext')
10
+ self.source_locale = :en
11
+ self.target_locales = []
12
+ self.endpoint = 'api.translation.io/api'
13
+ self.verbose = 1
14
+ end
15
+
16
+ def to_s
17
+ "#{api_key} — #{source_locale} => #{target_locales.join(' + ')}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ module Translation
2
+ module Controller
3
+ def set_locale
4
+ requested_locale = params[:locale] ||
5
+ session[:locale] ||
6
+ cookies[:locale] ||
7
+ request.env['HTTP_ACCEPT_LANGUAGE'] ||
8
+ I18n.default_locale
9
+
10
+ if requested_locale.to_sym.in?(I18n.available_locales)
11
+ session[:locale] = requested_locale
12
+ I18n.locale = requested_locale
13
+ else
14
+ redirect_to :root, :locale => I18n.default_locale
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,112 @@
1
+ module Translation
2
+ module FlatHash
3
+ class << self
4
+ def to_flat_hash(hash)
5
+ get_flat_hash_for_level(hash)
6
+ end
7
+
8
+ def to_hash(flat_hash)
9
+ hash = {}
10
+
11
+ flat_hash.each_pair do |key, value|
12
+ build_hash_with_flat(hash, key, value)
13
+ end
14
+
15
+ hash
16
+ end
17
+
18
+ private
19
+
20
+ def build_hash_with_flat(hash, key_string, value)
21
+ current_object = hash
22
+ splitted = key_string.split(/\.|\[/, 2)
23
+ current_key = splitted[0] # first is always a hash
24
+ key_string = splitted[1]
25
+
26
+ if key_string.blank? # if only one key like { 'en' => 'salut' }
27
+ current_object[current_key] = value
28
+ else
29
+ # Put back '[' if needed
30
+ if key_string.count(']') > key_string.count('[')
31
+ key_string = '[' + key_string
32
+ end
33
+
34
+ while key_string != ''
35
+ # Next is array
36
+ if key_string[0] == '['
37
+ array_pos = key_string.split(']', 2)[0]
38
+ array_pos = array_pos.split('[', 2)[1].to_i
39
+
40
+ key_string = key_string.split(']', 2).count == 2 ? key_string.split(']', 2)[1] : ""
41
+ key_string = key_string[1..-1] if key_string[0] == '.'
42
+
43
+ if no_key?(current_object, current_key)
44
+ current_object[current_key] = []
45
+ end
46
+
47
+ current_object = current_object[current_key]
48
+ current_key = array_pos
49
+
50
+ # array is terminal
51
+ if key_string == ''
52
+ current_object[array_pos] = value
53
+ end
54
+ # next is hash
55
+ elsif key_string[0] != '[' && (key_string.include?('.') or key_string.include?('['))
56
+ splitted = key_string.split(/\.|\[/, 2)
57
+ new_key = splitted[0]
58
+ key_string = splitted[1]
59
+
60
+ # Put back '[' if needed
61
+ if key_string.count(']') > key_string.count('[')
62
+ key_string = '[' + key_string
63
+ end
64
+
65
+ if no_key?(current_object, current_key)
66
+ current_object[current_key] = {}
67
+ end
68
+ current_object = current_object[current_key]
69
+ current_key = new_key
70
+ # next (last) is value
71
+ else
72
+ new_key = key_string
73
+
74
+ if no_key?(current_object, current_key)
75
+ current_object[current_key] = {}
76
+ end
77
+ current_object = current_object[current_key]
78
+ current_object[new_key] = value
79
+
80
+ key_string = ''
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def no_key?(array_or_hash, key)
87
+ (array_or_hash.is_a?(Hash) && !array_or_hash.has_key?(key)) || !array_or_hash[key]
88
+ end
89
+
90
+ def get_flat_hash_for_level(value, parent_key = nil)
91
+ flat_hash = {}
92
+
93
+ if value.is_a? Hash
94
+ value.each_pair do |key, value|
95
+ current_level_key = [ parent_key, key ].reject(&:blank?).join('.')
96
+ flat_hash.merge!(get_flat_hash_for_level(value, current_level_key))
97
+ end
98
+ elsif value.is_a? Array
99
+ value.each_with_index do |item, index|
100
+ flat_hash.merge!(get_flat_hash_for_level(item, "#{parent_key}[#{index}]"))
101
+ end
102
+ else
103
+ flat_hash[parent_key] = value
104
+ end
105
+
106
+ flat_hash
107
+ end
108
+
109
+ end
110
+ end
111
+ end
112
+
@@ -0,0 +1,37 @@
1
+ require 'i18n'
2
+ require 'i18n/config'
3
+
4
+ require 'gettext'
5
+ require 'gettext/po'
6
+ require 'gettext/po_parser'
7
+
8
+ module Translation
9
+ class Railtie < Rails::Railtie
10
+ rake_tasks do
11
+ require 'translation/tasks'
12
+ end
13
+
14
+ initializer 'translation.rails_extensions' do
15
+ ActionController::Base.send(:include, Translation::Controller)
16
+ end
17
+ end
18
+ end
19
+
20
+ module I18n
21
+ class Config
22
+ def locale=(locale)
23
+ I18n.enforce_available_locales!(locale)
24
+ @locale = locale.to_sym rescue nil
25
+ GetText.locale = locale.to_s.gsub('-', '_').to_sym
26
+ end
27
+ end
28
+ end
29
+
30
+ module GetText
31
+ class POParser < Racc::Parser
32
+ def initialize
33
+ @ignore_fuzzy = true
34
+ @report_warning = false
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ require 'gettext/tools'
2
+
3
+ namespace :translation do
4
+ task :config => :environment do
5
+ puts Translation.config
6
+ end
7
+
8
+ task :init => :environment do
9
+ if Translation.client
10
+ Translation.client.init
11
+ else
12
+ Translation.info "[Error] Client cannot be built. Did you set up the initializer?"
13
+ end
14
+ end
15
+
16
+ task :sync => :environment do
17
+ if Translation.client
18
+ Translation.client.sync
19
+ else
20
+ Translation.info "[Error] Client cannot be built. Did you set up the initializer?"
21
+ end
22
+ end
23
+
24
+ task :purge => :environment do
25
+ if Translation.client
26
+ Translation.client.purge
27
+ else
28
+ Translation.info "[Error] Client cannot be built. Did you set up the initializer?"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,81 @@
1
+ module Translation
2
+ module YAMLConversion
3
+ class << self
4
+
5
+ def get_pot_data_from_yaml(source_locale = nil, yaml_file_paths = nil)
6
+ source_locale = Translation.config.source_locale if source_locale.blank?
7
+ yaml_file_paths = I18n.load_path if yaml_file_paths.nil?
8
+
9
+ source_translations = YAMLConversion.get_flat_translations_for_locale(source_locale, yaml_file_paths)
10
+ pot_representation = GetText::PO.new
11
+
12
+ source_translations.each_pair do |key, value|
13
+ msgid = value
14
+
15
+ unless msgid.blank?
16
+ pot_entry = GetText::POEntry.new(:msgctxt)
17
+ pot_entry.msgid = msgid
18
+ pot_entry.msgctxt = key.split('.', 2).last
19
+ #pot_entry.references = [ value[:locale_file_path] ]
20
+
21
+ pot_representation[ pot_entry.msgctxt, pot_entry.msgid ] = pot_entry
22
+ end
23
+ end
24
+
25
+ pot_representation.to_s
26
+ end
27
+
28
+ def get_yaml_data_from_po_data(po_data, target_locale)
29
+ parser = GetText::POParser.new
30
+ po_representation = GetText::PO.new
31
+ flat_translations = {}
32
+
33
+ parser.parse(po_data, po_representation)
34
+
35
+ po_representation.each do |po_entry|
36
+ flat_translations["#{target_locale}.#{po_entry.msgctxt}"] = po_entry.msgstr
37
+ end
38
+
39
+ translations = YAMLConversion.get_yaml_data_from_flat_translations(flat_translations)
40
+
41
+ return translations
42
+ end
43
+
44
+ # Shortcut methods
45
+
46
+ def get_flat_translations_for_locale(locale, yaml_file_paths = nil)
47
+ yaml_file_paths = I18n.load_path if yaml_file_paths.blank?
48
+
49
+ all_flat_translations = {}
50
+
51
+ yaml_file_paths.each do |yaml_file_path|
52
+ content = File.read(yaml_file_path)
53
+ translations = YAML::load(content)
54
+
55
+ if translations.has_key?(locale.to_s)
56
+ flat_translations = FlatHash.to_flat_hash(translations)
57
+ all_flat_translations.merge!(flat_translations)
58
+ end
59
+ end
60
+
61
+ return all_flat_translations
62
+ end
63
+
64
+ def get_flat_translations_for_yaml_file(file_path)
65
+ yaml_data = File.read(file_path)
66
+ return get_flat_translations_for_yaml_data(yaml_data)
67
+ end
68
+
69
+ def get_flat_translations_for_yaml_data(yaml_data)
70
+ translations = YAML::load(yaml_data)
71
+ return FlatHash.to_flat_hash(translations)
72
+ end
73
+
74
+ def get_yaml_data_from_flat_translations(flat_translations)
75
+ translations = FlatHash.to_hash(flat_translations)
76
+ return translations.to_yaml
77
+ end
78
+
79
+ end
80
+ end
81
+ end
metadata CHANGED
@@ -1,39 +1,131 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: translation
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aurelien Malisart
8
- - Michaël Hoste
8
+ - Michael Hoste
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-03-28 00:00:00.000000000 Z
12
+ date: 2014-07-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: gettext
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - '>'
18
+ - - '>='
19
19
  - !ruby/object:Gem::Version
20
- version: 3.1.1
20
+ version: 3.1.2
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>'
25
+ - - '>='
26
26
  - !ruby/object:Gem::Version
27
- version: 3.1.1
27
+ version: 3.1.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rails
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: haml
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: slim
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
28
98
  description: translation.io connector
29
99
  email: contact@translation.io
30
100
  executables: []
31
101
  extensions: []
32
- extra_rdoc_files:
33
- - README.rdoc
102
+ extra_rdoc_files: []
34
103
  files:
104
+ - lib/translation/client/base_operation/create_new_mo_files_step.rb
105
+ - lib/translation/client/base_operation/dump_haml_gettext_keys_step.rb
106
+ - lib/translation/client/base_operation/dump_slim_gettext_keys_step.rb
107
+ - lib/translation/client/base_operation/save_new_po_files_step.rb
108
+ - lib/translation/client/base_operation/save_new_yaml_files_step.rb
109
+ - lib/translation/client/base_operation/save_special_yaml_files_step.rb
110
+ - lib/translation/client/base_operation.rb
111
+ - lib/translation/client/init_operation/cleanup_yaml_files_step.rb
112
+ - lib/translation/client/init_operation/create_yaml_po_files_step.rb
113
+ - lib/translation/client/init_operation/update_and_collect_po_files_step.rb
114
+ - lib/translation/client/init_operation/update_pot_file_step.rb
115
+ - lib/translation/client/init_operation.rb
116
+ - lib/translation/client/purge_operation.rb
117
+ - lib/translation/client/sync_operation/create_yaml_pot_file_step.rb
118
+ - lib/translation/client/sync_operation/update_and_collect_pot_file_step.rb
119
+ - lib/translation/client/sync_operation.rb
120
+ - lib/translation/client.rb
121
+ - lib/translation/config.rb
122
+ - lib/translation/controller.rb
123
+ - lib/translation/flat_hash.rb
124
+ - lib/translation/railtie.rb
125
+ - lib/translation/tasks.rb
126
+ - lib/translation/yaml_conversion.rb
35
127
  - lib/translation.rb
36
- - README.rdoc
128
+ - README.md
37
129
  homepage: http://rails.translation.io
38
130
  licenses:
39
131
  - MIT
@@ -54,8 +146,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
146
  version: '0'
55
147
  requirements: []
56
148
  rubyforge_project:
57
- rubygems_version: 2.1.5
149
+ rubygems_version: 2.0.0
58
150
  signing_key:
59
151
  specification_version: 4
60
152
  summary: translation.io connector
61
153
  test_files: []
154
+ has_rdoc: false
data/README.rdoc DELETED
@@ -1,20 +0,0 @@
1
- = Description
2
-
3
- ...
4
-
5
- = Basic Usage
6
-
7
- ...
8
-
9
- = Installation
10
-
11
- In your project's Gemfile :
12
-
13
- gem 'translation'
14
-
15
- = Credits
16
-
17
- This plugin in released under MIT license by Aurélien Malisart and Michaël Hoste (see MIT-LICENSE
18
- file).
19
-
20
- (c) http://rails.translation.io