crowdin-cli 0.0.2

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