crowdin-cli 0.0.2

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.
data/README.rdoc ADDED
@@ -0,0 +1,21 @@
1
+ = crowdin-cli
2
+
3
+ Describe your project here
4
+
5
+ == Configuration
6
+ Create a `.crowdin` YAML file in your root project directory with the following structure:
7
+
8
+ ```yaml
9
+ ---
10
+ project_id: ''
11
+ api_key: ''
12
+ server: 'http://api.crowdin.net'
13
+
14
+ files:
15
+ -
16
+ source-file-path: locale/en/LC_MESSAGES/messages.po
17
+ translations: locale/<two-letters-code>/LC_MESSAGES/<original-file-name>
18
+ ```
19
+
20
+ :include:crowdin-cli.rdoc
21
+
data/bin/crowdin-cli ADDED
@@ -0,0 +1,337 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+ require 'json'
5
+ require 'gli'
6
+ require 'zip/zip'
7
+ require 'crowdin-api'
8
+ require 'crowdin-cli'
9
+
10
+ #begin # XXX: Remove this begin/rescue before distributing your app
11
+ #require 'crowdin-cli'
12
+ #rescue LoadError
13
+ # STDERR.puts "In development, you need to use `bundle exec bin/crowdin-cli` to run your app"
14
+ # STDERR.puts "At install-time, RubyGems will make sure lib, etc. are in the load path"
15
+ # STDERR.puts "Feel free to remove this message from bin/crowdin-cli now"
16
+ # exit 64
17
+ #end
18
+
19
+ # Ця хуїта повертає структуру директорій на сервері
20
+ #
21
+ def walk_remote_tree(files, root = '/', result = { dirs: [], files: [] })
22
+ files.each do |node|
23
+ case node['node_type']
24
+ when 'directory'
25
+ result[:dirs] << "#{root}#{node['name']}"
26
+ walk_remote_tree(node['files'], root + node['name'] + '/', result)
27
+ when 'file'
28
+ result[:files] << "#{root}#{node['name']}"
29
+ end
30
+ end
31
+
32
+ return result
33
+ end
34
+
35
+
36
+ # Хуйня... хулі
37
+ # Повертає локальну структуру директорій
38
+ # На вході масив файлів, наприклад:
39
+ # ['/path/to/admin/en.xml', '/path/to/user/settings/strings.xml']
40
+ # Результат хеш { dirs:[послідовний масив директорій для створення], files: [] }
41
+ #
42
+ def walk_local_tree(files, result = { dirs: [], files: [] })
43
+ result[:files] = files
44
+
45
+ files = files.inject([]){ |res, a|
46
+ res << a.split('/').drop(1).inject([]){ |res, s|
47
+ res << res.last.to_s + '/' + s
48
+ }
49
+ }
50
+ # Ex: files = [["/path", "/path/to", "/path/to/admin", "/path/to/admin/en.xml"], ... ]
51
+
52
+ files.map(&:pop) # delete last element from each array
53
+ result[:dirs] = files.flatten.uniq
54
+
55
+ return result
56
+ end
57
+
58
+ def unzip_file(file, dest)
59
+ # overwrite files if they already exist inside of the extracted path
60
+ Zip.options[:on_exists_proc] = true
61
+
62
+ Zip::ZipFile.open(file) do |zip_file|
63
+ zip_file.each do |f|
64
+ f_path = File.join(dest, f.name)
65
+ FileUtils.mkdir_p(File.dirname(f_path))
66
+ puts "Download: #{f}"
67
+ zip_file.extract(f, f_path)
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ ###
74
+ include GLI::App
75
+
76
+ program_desc 'A CLI to sync locale files with crowdin.net'
77
+
78
+ version Crowdin::CLI::VERSION
79
+
80
+ desc 'Be verbose'
81
+ switch [:v, :verbose]
82
+
83
+ desc 'Path to config file'
84
+ default_value File.join(Dir.pwd, 'crowdin.yaml')
85
+ arg_name '<s>'
86
+ flag [:c, :config]
87
+
88
+
89
+ desc 'Upload existing translations to Crowdin project'
90
+ #arg_name 'Describe arguments to upload here'
91
+ command :upload do |c|
92
+ # Command 'upload' requires a subcommand
93
+ #
94
+ #c.action do |global_options, options, args|
95
+ # puts "upload command ran"
96
+ #end
97
+
98
+ #c.default_command :all
99
+
100
+ #c.desc 'Upload source and translation files'
101
+ #c.command :all do |c|
102
+ # c.action do |global_options, options, args|
103
+ # puts options
104
+ # puts "`upload all` command ran"
105
+ # end
106
+ #end
107
+
108
+ # TODO: change variables names!
109
+ c.desc 'Upload source files'
110
+ c.command :sources do |c|
111
+ c.action do |global_options, options, args|
112
+
113
+ c.desc 'defines whether to add files if there is the same file previously added'
114
+ c.switch [:upload_duplicates]
115
+
116
+ project_info = @crowdin.project_info(:json)
117
+ project_info = JSON.parse(project_info)
118
+
119
+ remote_project_tree = walk_remote_tree(project_info['files'])
120
+
121
+ local_files = []
122
+ @config['files'].each do |file|
123
+ local_file = "#{@local_path}#{@sources_root}#{file['source']}"
124
+
125
+ if File.exist?(local_file)
126
+ local_files << { dest: file['source'], source: local_file, export_pattern: file['translation'] }
127
+ else
128
+ Dir.glob(local_file).each do |f|
129
+ local_files << { dest: f.sub("#{@local_path}#{@sources_root}", ''), source: f, export_pattern: file['translation'] }
130
+ end
131
+ end
132
+ end
133
+
134
+ local_project_tree = walk_local_tree(local_files.collect{ |h| h[:dest] })
135
+
136
+ #=begin
137
+ # Create directory tree
138
+ #
139
+ create_dirs = local_project_tree[:dirs] - remote_project_tree[:dirs]
140
+ create_dirs.each do |dir|
141
+ puts "Create directory `#{dir}`"
142
+ @crowdin.add_directory(dir)
143
+ end
144
+
145
+ # Update existing files in Crowdin project
146
+ #
147
+ # array containing elements common to the two arrays
148
+ if options[:upload_dublicates]
149
+ update_files = local_project_tree[:files] & remote_project_tree[:files]
150
+ files_for_upload = local_files.select{ |file| update_files.include?(file[:dest]) }
151
+ files_for_upload.map{ |file| file.keep_if{ |k, v| [:source, :dest].include?(k) } }
152
+ files_for_upload.each do |file|
153
+ puts "Update file `#{file[:dest]}`"
154
+ @crowdin.update_file([] << file)
155
+ end
156
+ end
157
+
158
+ # Add new files to Crowdin project
159
+ #
160
+ add_files = local_project_tree[:files] - remote_project_tree[:files]
161
+ files_for_add = local_files.select{ |file| add_files.include?(file[:dest]) }
162
+ files_for_add.each do |file|
163
+ puts "Add new file `#{file[:dest]}`"
164
+ @crowdin.add_file([] << file)
165
+ end
166
+ #=end
167
+
168
+ end # action
169
+ end # command
170
+
171
+
172
+ c.desc 'Upload translation files'
173
+ c.command :translations do |c|
174
+
175
+ c.desc 'the language of translation you need'
176
+ c.default_value 'all'
177
+ c.arg_name 'language_code'
178
+ c.flag [:l, :language]
179
+
180
+ c.desc 'defines whether to add translation if there is the same translation previously added'
181
+ c.switch [:import_duplicates]
182
+
183
+ c.desc 'defines whether to add translation if it is equal to source string at Crowdin'
184
+ c.switch [:import_eq_suggestions]
185
+
186
+ c.desc 'mark uploaded translations as approved'
187
+ c.switch [:auto_approve_imported]
188
+
189
+
190
+ c.action do |global_options, options, args|
191
+ language = options[:language]
192
+
193
+ project_info = @crowdin.project_info(:json)
194
+ project_info = JSON.parse(project_info)
195
+
196
+ # Array of project languages
197
+ if language == 'all'
198
+ project_languages = project_info['languages'].collect{ |h| h['code'] }
199
+ else
200
+ project_languages = [] << language
201
+ end
202
+
203
+ # Crowdin supported languages list
204
+ supported_languages = @crowdin.supported_languages(:json)
205
+ supported_languages = JSON.parse(supported_languages)
206
+ supported_languages.select!{ |lang| project_languages.include?(lang['crowdin_code']) }
207
+
208
+ translated_files = Hash.new{ |hash, key| hash[key] = Array.new }
209
+
210
+ # Тут робиться багато припіздатєнької роботи :)
211
+ # TODO тут тре всьо нахуй переписати!!! НЕНАВИСТЬ
212
+ @config['files'].each do |file|
213
+ translation = file['translation']
214
+ source = file['source'] # relative path to source file in Crowdin project
215
+
216
+ sources = []
217
+ if File.exists?("#{@local_path}#{@sources_root}#{source}")
218
+ sources << source
219
+ else
220
+ Dir.glob("#{@local_path}#{@sources_root}#{source}").each do |f|
221
+ sources << f.sub("#{@local_path}#{@sources_root}", '')
222
+ end
223
+ end
224
+
225
+ sources.each do |source|
226
+ original_path = source.split('/')[1...-1].join('/')
227
+ original_file_name = source.split('/').last
228
+ file_extension = original_file_name.split('.').last
229
+ file_name = original_file_name.split('.').shift
230
+
231
+ supported_languages.each do |lang|
232
+ file = translation.gsub(/%.+?%/, {
233
+ '%language%' => lang['name'],
234
+ '%two_letter_code%' => lang['iso_639_1'],
235
+ '%tree_letter_code%' => lang['iso_639_3'],
236
+ '%locale%' => lang['locale'],
237
+ '%locale_with_underscore%' => lang['locale'].gsub('-', '_'),
238
+ '%original_file_name%' => original_file_name,
239
+ '%android_code%' => 'values-',
240
+ '%original_path%' => original_path,
241
+ '%file_extension%' => file_extension,
242
+ '%file_name%' => file_extension,
243
+ })
244
+ translated_files[lang['crowdin_code']] << { source: @local_path + file, dest: source }
245
+ end
246
+
247
+ end
248
+
249
+ end
250
+
251
+ params = {}
252
+ params[:import_duplicates] = options[:import_dublicates] ? 1 : 0
253
+ params[:import_eq_suggestions] = options[:import_eq_suggestions] ? 1 : 0
254
+ params[:auto_approve_imported] = options[:auto_approve_imported] ? 1 : 0
255
+
256
+ translated_files.each do |language, files|
257
+ files.each do |file|
258
+ if File.exist?(file[:source])
259
+ puts "Uploading #{file[:source]}"
260
+ @crowdin.upload_translation([] << file, language, params)
261
+ else
262
+ puts "Local file #{file[:source]} not exists"
263
+ end
264
+ end
265
+ end
266
+
267
+ end # action
268
+ end # command
269
+
270
+ end
271
+
272
+
273
+ desc 'Download existing translations'
274
+ #arg_name 'Describe arguments to download here'
275
+ command :download do |c|
276
+
277
+ c.desc 'the language of translation you need'
278
+ c.arg_name 'language_code'
279
+ c.flag :l, :language, :default_value => 'all'
280
+
281
+ c.action do |global_options ,options, args|
282
+ language = options[:language]
283
+
284
+ file = Tempfile.new(language)
285
+ begin
286
+ @crowdin.download_translation(language, :output => file)
287
+ unzip_file(file, @local_path)
288
+ ensure
289
+ file.close
290
+ file.unlink # delete the temp file
291
+ end
292
+ end
293
+
294
+ end
295
+
296
+ pre do |global ,command, options, args|
297
+ # Pre logic here
298
+ # Return true to proceed; false to abourt and not call the
299
+ # chosen command
300
+ # Use skips_pre before a command to skip this block
301
+ # on that command only
302
+
303
+ @config = YAML.load_file(global[:config])
304
+
305
+ #@local_path = @config['local_path'] || Dir.pwd
306
+ if @config['local_path']
307
+ if @config['local_path'].start_with?('/')
308
+ @local_path = @config['local_path']
309
+ else
310
+ @local_path = Dir.pwd + '/' + @config['local_path']
311
+ end
312
+ else
313
+ @local_path = Dir.pwd
314
+ end
315
+
316
+ @sources_root = @config['sources_root']
317
+
318
+ @crowdin = Crowdin::API.new(api_key: @config['api_key'], project_id: @config['project_id'], base_url: @config['base_url'])
319
+
320
+ puts "Executing #{command.name}" if global[:v]
321
+ true
322
+ end
323
+
324
+ post do |global, command, options, args|
325
+ # Post logic here
326
+ # Use skips_post before a command to skip this
327
+ # block on that command only
328
+ puts "Executed #{command.name}" if global[:v]
329
+ end
330
+
331
+ on_error do |exception|
332
+ # Error logic here
333
+ # return false to skip default error handling
334
+ true
335
+ end
336
+
337
+ exit run(ARGV)
data/crowdin-cli.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = crowdin-cli
2
+
3
+ Generate this with
4
+ crowdin-cli rdoc
5
+ After you have described your command line interface
@@ -0,0 +1,6 @@
1
+ require 'crowdin-cli/version.rb'
2
+
3
+ # Add requires for other files you add to your project here, so
4
+ # you just need to require this one file in your bin file
5
+
6
+ require 'yaml'
@@ -0,0 +1,5 @@
1
+ module Crowdin
2
+ module CLI
3
+ VERSION = '0.0.2'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crowdin-cli
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Anton Maminov
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rdoc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: aruba
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: gli
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 2.1.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 2.1.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: rubyzip
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.9.9
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.9.9
94
+ - !ruby/object:Gem::Dependency
95
+ name: crowdin-api
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 0.0.2
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 0.0.2
110
+ description:
111
+ email: anton.linux@gmail.com
112
+ executables:
113
+ - crowdin-cli
114
+ extensions: []
115
+ extra_rdoc_files:
116
+ - README.rdoc
117
+ - crowdin-cli.rdoc
118
+ files:
119
+ - bin/crowdin-cli
120
+ - lib/crowdin-cli/version.rb
121
+ - lib/crowdin-cli.rb
122
+ - README.rdoc
123
+ - crowdin-cli.rdoc
124
+ homepage: https://github.com/mamantoha/crowdin-cli
125
+ licenses: []
126
+ post_install_message:
127
+ rdoc_options:
128
+ - --title
129
+ - crowdin-cli
130
+ - --main
131
+ - README.rdoc
132
+ - -ri
133
+ require_paths:
134
+ - lib
135
+ - lib
136
+ required_ruby_version: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ! '>='
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubyforge_project:
150
+ rubygems_version: 1.8.24
151
+ signing_key:
152
+ specification_version: 3
153
+ summary: A description of your project
154
+ test_files: []