phrase 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ tmp
19
19
  .rvmrc
20
20
  .DS_Store
21
21
  /phrase
22
+ .rspec
@@ -1,6 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  require 'active_support/all'
4
+ require 'colorize'
4
5
 
5
6
  module Phrase
6
7
  autoload :Config, 'phrase/config'
@@ -22,11 +22,9 @@ class Phrase::Api::Client
22
22
 
23
23
  def fetch_locales
24
24
  result = perform_api_request("/locales", :get)
25
- locales = []
26
25
  parsed(result).map do |locale|
27
- locales << locale['name']
26
+ {id: locale['id'], name: locale['name'], code: locale['code'], is_default: locale['is_default']}
28
27
  end
29
- locales
30
28
  end
31
29
 
32
30
  def fetch_blacklisted_keys
@@ -74,7 +72,7 @@ class Phrase::Api::Client
74
72
  true
75
73
  end
76
74
 
77
- def download_translations_for_locale(name, format=Phrase::Tool::DEFAULT_DOWNLOAD_FORMAT)
75
+ def download_translations_for_locale(name, format)
78
76
  raise "You must specify a name" if name.nil? or name.blank?
79
77
  raise "You must specify a format" if format.nil? or format.blank?
80
78
 
@@ -7,290 +7,33 @@ require 'phrase'
7
7
  class Phrase::Tool
8
8
  autoload :Config, 'phrase/tool/config'
9
9
  autoload :Options, 'phrase/tool/options'
10
+ autoload :Formats, 'phrase/tool/formats'
11
+ autoload :Commands, 'phrase/tool/commands'
10
12
  autoload :TagValidator, 'phrase/tool/tag_validator'
11
-
12
- ALLOWED_FILE_TYPES = %w(yml pot po xml strings json resx ts qph ini plist properties xlf)
13
- ALLOWED_DOWNLOAD_FORMATS = %w(yml po xml strings json resx ts qph ini plist properties xlf)
14
- FORMATS_CONTAINING_LOCALE = %q(po yml qph ts xlf)
15
- DEFAULT_DOWNLOAD_FORMAT = "yml"
16
- DEFAULT_TARGET_FOLDER = "phrase/locales/"
17
-
18
- attr_accessor :config, :options
13
+ autoload :Locale, 'phrase/tool/locale'
19
14
 
20
15
  def initialize(argv)
21
16
  @args = argv
22
17
  end
23
18
 
24
19
  def run
25
- subcommand = @args.first
20
+ command_name = @args.first
21
+ @options = Phrase::Tool::Options.new(@args, command_name)
26
22
 
27
- @config = Phrase::Tool::Config.new
28
- @options = Phrase::Tool::Options.new(@args, subcommand)
29
-
30
- case subcommand
23
+ command = case command_name
31
24
  when /init/
32
- init
25
+ Phrase::Tool::Commands::Init.new(@options, @args)
33
26
  when /push/
34
- push
27
+ Phrase::Tool::Commands::Push.new(@options, @args)
35
28
  when /pull/
36
- pull
29
+ Phrase::Tool::Commands::Pull.new(@options, @args)
37
30
  else
38
31
  if @options.get(:version)
39
- print_version
32
+ Phrase::Tool::Commands::ShowVersion.new(@options, @args)
40
33
  else
41
- print_usage
34
+ Phrase::Tool::Commands::ShowHelp.new(@options, @args)
42
35
  end
43
36
  end
44
- end
45
-
46
- protected
47
-
48
- def init
49
- secret = @options.get(:secret)
50
- unless secret.present?
51
- print_error "Need a secret to init, but found none."
52
- print_error "Please provide the --secret=YOUR_SECRET parameter."
53
- exit(41)
54
- end
55
-
56
- @config.secret = secret
57
- print_message "Wrote secret to config file .phrase"
58
-
59
- default_locale_name = @options.get(:default_locale)
60
- create_locale(default_locale_name)
61
- make_locale_default(default_locale_name)
62
- end
63
-
64
- def push
65
- check_config_available
66
- tags = @options.get(:tags)
67
- locale = @options.get(:locale)
68
-
69
- unless tags.empty? or valid_tags_are_given?(tags)
70
- print_error "Invalid tags: Only letters, numbers, underscores and dashes are allowed"
71
- exit(43)
72
- end
73
-
74
- files = choose_files_to_upload
75
- if files.empty?
76
- print_message "Could not find any files to upload :("
77
- exit(43)
78
- end
79
-
80
- upload_files(files, tags, locale)
81
- end
82
-
83
- def pull
84
- check_config_available
85
-
86
- create_locales_folder!
87
-
88
- locale = args[1]
89
- locales = []
90
- if locale && locale.strip != ''
91
- locales = [locale]
92
- else
93
- locales = fetch_locales
94
- end
95
-
96
- format = @options.get(:format) || DEFAULT_DOWNLOAD_FORMAT
97
- target = @options.get(:target) || DEFAULT_TARGET_FOLDER
98
-
99
- unless ALLOWED_DOWNLOAD_FORMATS.include?(format)
100
- print_error "Invalid format: #{format}"
101
- exit(43)
102
- end
103
-
104
- locales.each do |locale_name|
105
- print "Downloading phrase.#{locale_name}.#{format}..."
106
- fetch_translations_for_locale(locale_name, format, target)
107
- end
108
- end
109
-
110
- def print_usage
111
- $stderr.puts <<USAGE
112
- usage: phrase <command> [<args>]
113
-
114
- phrase init --secret=<YOUR SECRET> --default-locale=<DEFAULT LOCALE>
115
-
116
- phrase push FILE [--tags=<tags>] [--locale=<locale>]
117
- phrase push DIRECTORY [--tags=<tags>] [--locale=<locale>]
118
-
119
- phrase pull [LOCALE] [--target=<target-folder>] [--format=<format>]
120
-
121
- phrase --version
122
- USAGE
123
- end
124
-
125
- def print_version
126
- print_message "phrase version #{Phrase::VERSION}"
127
- end
128
-
129
- private
130
- def choose_files_to_upload
131
- file_name = args[1]
132
- recursive = @options.get(:recursive)
133
-
134
- unless file_name
135
- if self.class.rails_default_locale_folder_is_available
136
- file_name = self.class.rails_default_locale_folder
137
- print_message "No file or directory specified, using #{self.class.rails_default_locale_folder}"
138
- else
139
- print_error "Need either a file or directory:"
140
- print_error "phrase push FILE"
141
- print_error "phrase push DIRECTORY"
142
- exit(46)
143
- end
144
- end
145
-
146
- unless File.exist?(file_name)
147
- print_error "The file #{file_name} could not be found."
148
- exit(42)
149
- end
150
-
151
- if File.directory?(file_name)
152
- pattern = recursive ? "#{File.expand_path(file_name)}/**/*" : "#{File.expand_path(file_name)}/**"
153
- files = Dir.glob(pattern)
154
- else
155
- files = [file_name]
156
- end
157
- end
158
-
159
- def upload_files(files, tags=[], locale=nil)
160
- files.each do |file|
161
- upload_file(file, tags, locale)
162
- end
163
- end
164
-
165
- def upload_file(file, tags=[], locale=nil)
166
- valid = true
167
-
168
- if File.directory?(file)
169
- valid = false
170
- end
171
-
172
- unless file_valid?(file)
173
- valid = false
174
- print_error "Notice: Could not upload #{file} (type not supported)"
175
- end
176
-
177
- if valid
178
- begin
179
- tagged = " (tagged: #{tags.join(", ")})" if tags.size > 0
180
- print_message "Uploading #{file}#{tagged}..."
181
- api_client.upload(file, File.read(file), tags, locale)
182
- print_message "OK"
183
- rescue Exception => e
184
- print_message "Failed"
185
- print_server_error(e.message, file)
186
- end
187
- end
188
- end
189
-
190
- def fetch_translations_for_locale(name, format=DEFAULT_DOWNLOAD_FORMAT, target=DEFAULT_TARGET_FOLDER)
191
- begin
192
- content = api_client.download_translations_for_locale(name, format)
193
- print_message "OK"
194
- store_translations_file(name, content, format, target)
195
- rescue Exception => e
196
- print_error "Failed"
197
- print_server_error(e.message)
198
- end
199
- end
200
-
201
- def store_translations_file(name, content, format=DEFAULT_DOWNLOAD_FORMAT, target=DEFAULT_TARGET_FOLDER)
202
- directory = target
203
- directory << "/" unless directory.end_with?("/")
204
-
205
- if File.directory?(directory)
206
- File.open("#{directory}phrase.#{name}.#{format}", "w") do |file|
207
- file.write(content)
208
- end
209
- else
210
- print_error("Cannot write file to target folder (#{directory})")
211
- exit(101)
212
- end
213
- end
214
-
215
- def fetch_locales
216
- begin
217
- locales = api_client.fetch_locales
218
- print_message "Fetched all locales"
219
- return locales
220
- rescue Exception => e
221
- print_message "Failed"
222
- print_server_error(e.message)
223
- exit(47)
224
- end
225
- end
226
-
227
- def create_locale(name)
228
- begin
229
- api_client.create_locale(name)
230
- print_message "Created locale \"#{name}\""
231
- rescue Exception => e
232
- print_message "Notice: Locale \"#{name}\" could not be created (maybe it already exists)"
233
- end
234
- end
235
-
236
- def make_locale_default(name)
237
- begin
238
- api_client.make_locale_default(name)
239
- print_message "Locale \"#{name}\" is now the default locale"
240
- rescue Exception => e
241
- print_message "Notice: Locale \"#{name}\" could not be made the default locale"
242
- print_server_error(e.message)
243
- end
244
- end
245
-
246
- def api_client
247
- Phrase::Api::Client.new(@config.secret)
248
- end
249
-
250
- def print_server_error(message, filename=nil)
251
- print_error "#{message} (#{filename})"
252
- end
253
-
254
- def print_message(message)
255
- puts message
256
- end
257
-
258
- def print_error(message)
259
- $stderr.puts message
260
- end
261
-
262
- def args
263
- @args
264
- end
265
-
266
- def file_valid?(filepath)
267
- extension = filepath.split('.').last
268
- ALLOWED_FILE_TYPES.include?(extension)
269
- end
270
-
271
- def create_locales_folder!
272
- ::FileUtils.mkdir_p("phrase/locales")
273
- end
274
-
275
- def check_config_available
276
- if !@config.secret || @config.secret.empty?
277
- print_error "No config present. You need to initialize phrase first."
278
- exit(43)
279
- end
280
- end
281
-
282
- def valid_tags_are_given?(tags)
283
- tags.each do |tag|
284
- return false unless TagValidator.valid?(tag)
285
- end
286
- true
287
- end
288
-
289
- def self.rails_default_locale_folder
290
- "./config/locales/"
291
- end
292
-
293
- def self.rails_default_locale_folder_is_available
294
- File.exist?(rails_default_locale_folder) && File.directory?(rails_default_locale_folder)
37
+ command.execute!
295
38
  end
296
39
  end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Phrase::Tool::Commands
4
+ end
5
+
6
+ require "phrase/tool/commands/base"
7
+ require "phrase/tool/commands/init"
8
+ require "phrase/tool/commands/push"
9
+ require "phrase/tool/commands/pull"
10
+ require "phrase/tool/commands/show_version"
11
+ require "phrase/tool/commands/show_help"
@@ -0,0 +1,73 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Phrase::Tool::Commands
4
+ class Base
5
+ def initialize(options, args=[])
6
+ @options = options
7
+ @args = args
8
+ end
9
+
10
+ def execute!
11
+ raise "not implemented"
12
+ end
13
+
14
+ def require_auth_token!
15
+ unless config.secret and config.secret.present?
16
+ print_error "No auth token present. You need to initialize phrase first."
17
+ exit_command
18
+ end
19
+ end
20
+
21
+ def self.print_error(msg)
22
+ $stderr.puts msg.red
23
+ end
24
+
25
+ def self.print_server_error(msg, location=nil)
26
+ error_message = "#{msg}"
27
+ error_message << " (#{location})" unless location.nil?
28
+ print_error error_message
29
+ end
30
+
31
+ def self.print_message(msg)
32
+ $stdout.puts msg
33
+ end
34
+
35
+ def self.exit_command
36
+ exit()
37
+ end
38
+
39
+ protected
40
+ def api_client
41
+ Phrase::Api::Client.new(config.secret)
42
+ end
43
+
44
+ def config
45
+ @config ||= get_config
46
+ end
47
+
48
+ def get_config
49
+ config = Phrase::Tool::Config.new
50
+ config.load
51
+ end
52
+
53
+ def options
54
+ @options
55
+ end
56
+
57
+ def print_error(msg)
58
+ self.class.print_error(msg)
59
+ end
60
+
61
+ def print_server_error(message, location=nil)
62
+ self.class.print_server_error(message, location)
63
+ end
64
+
65
+ def print_message(msg)
66
+ self.class.print_message(msg)
67
+ end
68
+
69
+ def exit_command
70
+ self.class.exit_command
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Commands::Init < Phrase::Tool::Commands::Base
4
+ def initialize(options, args)
5
+ super(options, args)
6
+ end
7
+
8
+ def execute!
9
+ secret = options.get(:secret)
10
+ if secret.present?
11
+ config.secret = secret
12
+ print_message "Wrote secret to config file .phrase"
13
+ default_locale_name = options.get(:default_locale)
14
+ create_locale(default_locale_name)
15
+ make_locale_default(default_locale_name)
16
+ else
17
+ print_auth_token_error
18
+ exit_command
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def create_locale(name)
25
+ begin
26
+ api_client.create_locale(name)
27
+ print_message "Created locale \"#{name}\"".green
28
+ rescue Exception => e
29
+ print_message "Notice: Locale \"#{name}\" could not be created (maybe it already exists)".light_red
30
+ end
31
+ end
32
+
33
+ def make_locale_default(name)
34
+ begin
35
+ api_client.make_locale_default(name)
36
+ print_message "Locale \"#{name}\" is now the default locale".green
37
+ rescue Exception => e
38
+ print_message "Notice: Locale \"#{name}\" could not be made the default locale".light_red
39
+ print_server_error(e.message)
40
+ end
41
+ end
42
+
43
+ def print_auth_token_error
44
+ print_error "No auth token was given"
45
+ print_error "Please provide the --secret=YOUR_SECRET parameter."
46
+ end
47
+ end
@@ -0,0 +1,99 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Commands::Pull < Phrase::Tool::Commands::Base
4
+
5
+ ALLOWED_DOWNLOAD_FORMATS = %w(yml po xml strings json resx ts qph ini plist properties xlf)
6
+ DEFAULT_DOWNLOAD_FORMAT = "yml"
7
+ DEFAULT_TARGET_FOLDER = "phrase/locales/"
8
+
9
+ def initialize(options, args)
10
+ super(options, args)
11
+ require_auth_token!
12
+
13
+ @locale = @args[1]
14
+
15
+ @format = @options.get(:format) || DEFAULT_DOWNLOAD_FORMAT
16
+ @target = @options.get(:target) || DEFAULT_TARGET_FOLDER
17
+ end
18
+
19
+ def execute!
20
+ (print_error("Invalid format: #{@format}") and exit_command) unless format_valid?(@format)
21
+ locales_to_download.compact.each do |locale|
22
+ print_message "Downloading #{locale.name}..."
23
+ fetch_translations_for_locale(locale, @format)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def fetch_translations_for_locale(locale, format)
30
+ begin
31
+ content = api_client.download_translations_for_locale(locale.name, format)
32
+ store_content_in_locale_file(locale, content)
33
+ rescue Exception => e
34
+ print_error "Failed"
35
+ print_server_error(e.message)
36
+ end
37
+ end
38
+
39
+ def store_content_in_locale_file(locale, content)
40
+ directory = Phrase::Tool::Formats.directory_for_locale_in_format(locale, @format)
41
+ filename = Phrase::Tool::Formats.filename_for_locale_in_format(locale, @format)
42
+ path = File.join(base_directory, directory)
43
+ target = File.join(path, filename)
44
+ begin
45
+ FileUtils.mkpath(path)
46
+ File.open(target, "w") do |file|
47
+ file.write(content)
48
+ end
49
+ print_message "Saved to #{target}".green
50
+ rescue
51
+ print_error("Cannot write file to target folder (#{path})")
52
+ exit_command
53
+ end
54
+ end
55
+
56
+ def fetch_locales
57
+ begin
58
+ Phrase::Tool::Locale.all
59
+ rescue Exception => e
60
+ print_error "Could not fetch locales from server"
61
+ print_server_error e.message
62
+ exit_command
63
+ end
64
+ end
65
+
66
+ def format_valid?(format)
67
+ ALLOWED_DOWNLOAD_FORMATS.include?(format)
68
+ end
69
+
70
+ def base_directory
71
+ directory = @target
72
+ end
73
+
74
+ # TODO: test
75
+ def locales_to_download
76
+ if user_specified_a_locale?
77
+ [specified_locale]
78
+ else
79
+ all_locales
80
+ end
81
+ end
82
+
83
+ # TODO: test
84
+ def specified_locale
85
+ locale = all_locales.select { |locale| locale.name == @locale }.first
86
+ (print_error("Locale #{@locale} does not exist") and exit_command) if locale.nil?
87
+ locale
88
+ end
89
+
90
+ # TODO: test
91
+ def all_locales
92
+ @all_locales_from_server ||= fetch_locales
93
+ end
94
+
95
+ # TODO: test
96
+ def user_specified_a_locale?
97
+ @locale and @locale.strip != ''
98
+ end
99
+ end
@@ -0,0 +1,106 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Commands::Push < Phrase::Tool::Commands::Base
4
+ ALLOWED_FILE_TYPES = %w(yml pot po xml strings json resx ts qph ini plist properties xlf)
5
+ FORMATS_CONTAINING_LOCALE = %q(po yml qph ts xlf)
6
+ RAILS_DEFAULT_FOLDER = "./config/locales/"
7
+
8
+ def initialize(options, args)
9
+ super(options, args)
10
+ require_auth_token!
11
+
12
+ @file_name = @args[1]
13
+ @locale = @options.get(:locale)
14
+ @tags = @options.get(:tags)
15
+ @recursive = @options.get(:recursive)
16
+ end
17
+
18
+ def execute!
19
+ unless @tags.empty? or valid_tags_are_given?(@tags)
20
+ print_error "Invalid tags: Only letters, numbers, underscores and dashes are allowed"
21
+ exit_command
22
+ end
23
+
24
+ files = choose_files_to_upload
25
+ if files.empty?
26
+ print_message "Could not find any files to upload".light_red
27
+ exit_command
28
+ else
29
+ upload_files(files)
30
+ end
31
+ end
32
+
33
+ private
34
+ def choose_files_to_upload
35
+ if @file_name.blank?
36
+ if rails_default_locale_folder_available?
37
+ @file_name = RAILS_DEFAULT_FOLDER
38
+ print_message "No file or directory specified, using #{RAILS_DEFAULT_FOLDER}"
39
+ else
40
+ print_error "Need either a file or directory:"
41
+ print_error "phrase push FILE"
42
+ print_error "phrase push DIRECTORY"
43
+ exit_command
44
+ end
45
+ end
46
+
47
+ unless File.exist?(@file_name)
48
+ print_error "The file #{@file_name} could not be found."
49
+ exit_command
50
+ end
51
+
52
+ if File.directory?(@file_name)
53
+ pattern = @recursive ? "#{File.expand_path(@file_name)}/**/*" : "#{File.expand_path(@file_name)}/**"
54
+ files = Dir.glob(pattern)
55
+ else
56
+ files = [@file_name]
57
+ end
58
+ end
59
+
60
+ def upload_files(files)
61
+ files.each { |file| upload_file(file) }
62
+ end
63
+
64
+ def upload_file(file)
65
+ valid = true
66
+
67
+ if File.directory?(file)
68
+ valid = false
69
+ else
70
+ unless file_valid?(file)
71
+ valid = false
72
+ print_error "Notice: Could not upload #{file} (type not supported)"
73
+ end
74
+ end
75
+
76
+ if valid
77
+ begin
78
+ tagged = " (tagged: #{@tags.join(", ")})" if @tags.size > 0
79
+ print_message "Uploading #{file}#{tagged}..."
80
+ locale = @locale || detect_locale_name_from_file_path(file)
81
+ api_client.upload(file, File.read(file), @tags, locale)
82
+ print_message "OK".green
83
+ rescue Exception => e
84
+ print_error "Failed"
85
+ print_server_error(e.message)
86
+ end
87
+ end
88
+ end
89
+
90
+ def file_valid?(filepath)
91
+ extension = filepath.split('.').last
92
+ ALLOWED_FILE_TYPES.include?(extension)
93
+ end
94
+
95
+ def valid_tags_are_given?(tags)
96
+ tags.all? { |tag| Phrase::Tool::TagValidator.valid?(tag) }
97
+ end
98
+
99
+ def rails_default_locale_folder_available?
100
+ File.exist?(RAILS_DEFAULT_FOLDER) && File.directory?(RAILS_DEFAULT_FOLDER)
101
+ end
102
+
103
+ def detect_locale_name_from_file_path(file_path)
104
+ Phrase::Tool::Formats.detect_locale_name_from_file_path(file_path)
105
+ end
106
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Commands::ShowHelp < Phrase::Tool::Commands::Base
4
+ def initialize(options, args)
5
+ super(options, args)
6
+ end
7
+
8
+ def execute!
9
+ show_help
10
+ end
11
+
12
+ private
13
+
14
+ def show_help
15
+ print_message <<USAGE
16
+ usage: phrase <command> [<args>]
17
+
18
+ phrase init --secret=<YOUR SECRET> --default-locale=<DEFAULT LOCALE>
19
+
20
+ phrase push FILE [--tags=<tags>] [--locale=<locale>]
21
+ phrase push DIRECTORY [--tags=<tags>] [--locale=<locale>]
22
+
23
+ phrase pull [LOCALE] [--target=<target-folder>] [--format=<format>]
24
+
25
+ phrase --version
26
+ USAGE
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Commands::ShowVersion < Phrase::Tool::Commands::Base
4
+ def initialize(options, args)
5
+ super(options, args)
6
+ end
7
+
8
+ def execute!
9
+ show_version
10
+ end
11
+
12
+ private
13
+
14
+ def show_version
15
+ print_message "phrase version #{Phrase::VERSION}"
16
+ end
17
+ end
@@ -3,7 +3,7 @@
3
3
  require 'json'
4
4
 
5
5
  class Phrase::Tool::Config
6
- def initialize
6
+ def load
7
7
  if File.exist?(".phrase")
8
8
  begin
9
9
  contents = File.read(".phrase")
@@ -12,6 +12,7 @@ class Phrase::Tool::Config
12
12
  $stderr.puts "Could not parse config file: #{err}"
13
13
  end
14
14
  end
15
+ self
15
16
  end
16
17
 
17
18
  def secret
@@ -0,0 +1,58 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module Phrase::Tool::Formats
4
+ autoload :Base, 'phrase/tool/formats/base'
5
+ autoload :Yaml, 'phrase/tool/formats/yaml'
6
+ autoload :Gettext, 'phrase/tool/formats/gettext'
7
+ autoload :Xml, 'phrase/tool/formats/xml'
8
+ autoload :Strings, 'phrase/tool/formats/strings'
9
+ autoload :Xliff, 'phrase/tool/formats/xliff'
10
+ autoload :QtPhraseBook, 'phrase/tool/formats/qt_phrase_book'
11
+ autoload :QtTranslationSource, 'phrase/tool/formats/qt_translation_source'
12
+ autoload :Json, 'phrase/tool/formats/json'
13
+ autoload :Resx, 'phrase/tool/formats/resx'
14
+ autoload :Ini, 'phrase/tool/formats/ini'
15
+ autoload :Properties, 'phrase/tool/formats/properties'
16
+ autoload :Plist, 'phrase/tool/formats/plist'
17
+
18
+ SUPPORTED_FORMATS = {
19
+ yml: Phrase::Tool::Formats::Yaml,
20
+ po: Phrase::Tool::Formats::Gettext,
21
+ xml: Phrase::Tool::Formats::Xml,
22
+ strings: Phrase::Tool::Formats::Strings,
23
+ xlf: Phrase::Tool::Formats::Xliff,
24
+ qph: Phrase::Tool::Formats::QtPhraseBook,
25
+ ts: Phrase::Tool::Formats::QtTranslationSource,
26
+ json: Phrase::Tool::Formats::Json,
27
+ resx: Phrase::Tool::Formats::Resx,
28
+ ini: Phrase::Tool::Formats::Ini,
29
+ properties: Phrase::Tool::Formats::Properties,
30
+ plist: Phrase::Tool::Formats::Plist
31
+ }
32
+
33
+ def self.directory_for_locale_in_format(locale, format_name)
34
+ handler = handler_class_for_format(format_name)
35
+ handler.directory_for_locale(locale)
36
+ end
37
+
38
+ def self.filename_for_locale_in_format(locale, format_name)
39
+ handler = handler_class_for_format(format_name)
40
+ handler.filename_for_locale(locale)
41
+ end
42
+
43
+ def self.detect_locale_name_from_file_path(file_path)
44
+ format = guess_possible_file_format_from_file_path(file_path)
45
+ format.nil? ? nil : handler_class_for_format(format).extract_locale_name_from_file_path(file_path)
46
+ end
47
+
48
+ def self.handler_class_for_format(format_name)
49
+ SUPPORTED_FORMATS.fetch(format_name.to_sym)
50
+ end
51
+ private_class_method :handler_class_for_format
52
+
53
+ def self.guess_possible_file_format_from_file_path(file_path)
54
+ extension = file_path.split('.').last.downcase
55
+ return SUPPORTED_FORMATS.has_key?(extension.to_sym) ? extension.to_sym : nil
56
+ end
57
+ private_class_method :guess_possible_file_format_from_file_path
58
+ end
@@ -0,0 +1,15 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Base
4
+ def self.directory_for_locale(locale)
5
+ "./"
6
+ end
7
+
8
+ def self.filename_for_locale(locale)
9
+ raise "not implemented"
10
+ end
11
+
12
+ def self.extract_locale_name_from_file_path(file_path)
13
+ nil
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Gettext < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.po"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Ini < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.ini"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Json < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.json"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Plist < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.plist"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Properties < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.properties"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::QtPhraseBook < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.qph"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::QtTranslationSource < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.ts"
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Resx < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.resx"
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Strings < Phrase::Tool::Formats::Base
4
+ def self.directory_for_locale(locale)
5
+ name = locale.code || locale.name
6
+ "#{formatted(name)}.lproj"
7
+ end
8
+
9
+ def self.filename_for_locale(locale)
10
+ "Localizable.strings"
11
+ end
12
+
13
+ def self.extract_locale_name_from_file_path(file_path)
14
+ file_path.scan(/\/([a-zA-Z\-_]*).lproj\//i).first.try(:first)
15
+ end
16
+
17
+ def self.formatted(name)
18
+ return name unless name.include?("-")
19
+ parts = name.split("-")
20
+ "#{parts.first}_#{parts.last.upcase}"
21
+ end
22
+ private_class_method :formatted
23
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Xliff < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.xlf"
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Xml < Phrase::Tool::Formats::Base
4
+ def self.directory_for_locale(locale)
5
+ if locale.default?
6
+ "values"
7
+ else
8
+ name = locale.code || locale.name
9
+ "values-#{formatted(name)}"
10
+ end
11
+ end
12
+
13
+ def self.filename_for_locale(locale)
14
+ "strings.xml"
15
+ end
16
+
17
+ def self.extract_locale_name_from_file_path(file_path)
18
+ return default_locale_name if file_path.scan(/\/values\/strings.xml/i).first
19
+ locale_part = file_path.scan(/\/values-([a-zA-Z\-_]*)\/strings.xml/i).first.try(:first)
20
+ if locale_part and locale_part.include?("-r")
21
+ "#{locale_part.split("-r").first}-#{locale_part.split("-r").last}"
22
+ else
23
+ locale_part
24
+ end
25
+ end
26
+
27
+ def self.default_locale_name
28
+ Phrase::Tool::Locale.find_default_locale.try(:name)
29
+ end
30
+ private_class_method :default_locale_name
31
+
32
+ def self.formatted(name)
33
+ return name unless name.include?("-")
34
+ parts = name.split("-")
35
+ "#{parts.first}-r#{parts.last.upcase}"
36
+ end
37
+ private_class_method :formatted
38
+ end
@@ -0,0 +1,7 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::Yaml < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.#{locale.name}.yml"
6
+ end
7
+ end
@@ -0,0 +1,42 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Locale
4
+ attr_accessor :id, :name, :code, :is_default
5
+
6
+ def initialize(attrs={})
7
+ attrs.each do |key,value|
8
+ self.send("#{key}=", value)
9
+ end
10
+ end
11
+
12
+ def default?
13
+ self.is_default == true
14
+ end
15
+
16
+ def ==(object)
17
+ object.id == self.id
18
+ end
19
+
20
+ def self.find_default_locale
21
+ all.select { |locale| locale.default? }.try(:first)
22
+ end
23
+
24
+ def self.all
25
+ locales = []
26
+ api_client.fetch_locales.each do |locale|
27
+ locales << Phrase::Tool::Locale.new(id: locale[:id], name: locale[:name], code: locale[:code], is_default: locale[:is_default])
28
+ end
29
+ locales
30
+ end
31
+
32
+ def self.config
33
+ config = Phrase::Tool::Config.new
34
+ config.load
35
+ end
36
+ private_class_method :config
37
+
38
+ def self.api_client
39
+ Phrase::Api::Client.new(config.secret)
40
+ end
41
+ private_class_method :api_client
42
+ end
@@ -64,7 +64,7 @@ private
64
64
  end
65
65
  when :pull
66
66
  OptionParser.new do |opts|
67
- opts.on("--format=yml", String, "Allowed formats: #{Phrase::Tool::ALLOWED_DOWNLOAD_FORMATS.join(", ")}") do |format|
67
+ opts.on("--format=yml", String, "See documentation for list of allowed locales") do |format|
68
68
  @data[command_name][:format] = format
69
69
  end
70
70
 
@@ -1,3 +1,3 @@
1
1
  module Phrase
2
- VERSION = "0.2.5"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.require_paths = ["lib"]
23
23
  s.add_dependency('activesupport', '~> 3.0')
24
24
  s.add_dependency('addressable', '~> 2.2.8')
25
+ s.add_dependency('colorize')
25
26
  s.add_dependency('json')
26
27
  s.add_development_dependency('rspec')
27
28
  s.add_development_dependency('i18n')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phrase
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-19 00:00:00.000000000 Z
12
+ date: 2012-10-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
16
- requirement: &70191400504960 !ruby/object:Gem::Requirement
16
+ requirement: &70212288992600 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70191400504960
24
+ version_requirements: *70212288992600
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: addressable
27
- requirement: &70191400504500 !ruby/object:Gem::Requirement
27
+ requirement: &70212288992140 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,21 @@ dependencies:
32
32
  version: 2.2.8
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70191400504500
35
+ version_requirements: *70212288992140
36
+ - !ruby/object:Gem::Dependency
37
+ name: colorize
38
+ requirement: &70212288991760 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70212288991760
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: json
38
- requirement: &70191400504120 !ruby/object:Gem::Requirement
49
+ requirement: &70212288991300 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ! '>='
@@ -43,10 +54,10 @@ dependencies:
43
54
  version: '0'
44
55
  type: :runtime
45
56
  prerelease: false
46
- version_requirements: *70191400504120
57
+ version_requirements: *70212288991300
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: rspec
49
- requirement: &70191400503660 !ruby/object:Gem::Requirement
60
+ requirement: &70212288990880 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ! '>='
@@ -54,10 +65,10 @@ dependencies:
54
65
  version: '0'
55
66
  type: :development
56
67
  prerelease: false
57
- version_requirements: *70191400503660
68
+ version_requirements: *70212288990880
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: i18n
60
- requirement: &70191400503240 !ruby/object:Gem::Requirement
71
+ requirement: &70212288990460 !ruby/object:Gem::Requirement
61
72
  none: false
62
73
  requirements:
63
74
  - - ! '>='
@@ -65,10 +76,10 @@ dependencies:
65
76
  version: '0'
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *70191400503240
79
+ version_requirements: *70212288990460
69
80
  - !ruby/object:Gem::Dependency
70
81
  name: webmock
71
- requirement: &70191400502820 !ruby/object:Gem::Requirement
82
+ requirement: &70212288990040 !ruby/object:Gem::Requirement
72
83
  none: false
73
84
  requirements:
74
85
  - - ! '>='
@@ -76,10 +87,10 @@ dependencies:
76
87
  version: '0'
77
88
  type: :development
78
89
  prerelease: false
79
- version_requirements: *70191400502820
90
+ version_requirements: *70212288990040
80
91
  - !ruby/object:Gem::Dependency
81
92
  name: vcr
82
- requirement: &70191400502400 !ruby/object:Gem::Requirement
93
+ requirement: &70212288989620 !ruby/object:Gem::Requirement
83
94
  none: false
84
95
  requirements:
85
96
  - - ! '>='
@@ -87,10 +98,10 @@ dependencies:
87
98
  version: '0'
88
99
  type: :development
89
100
  prerelease: false
90
- version_requirements: *70191400502400
101
+ version_requirements: *70212288989620
91
102
  - !ruby/object:Gem::Dependency
92
103
  name: timecop
93
- requirement: &70191400501980 !ruby/object:Gem::Requirement
104
+ requirement: &70212288989200 !ruby/object:Gem::Requirement
94
105
  none: false
95
106
  requirements:
96
107
  - - ! '>='
@@ -98,7 +109,7 @@ dependencies:
98
109
  version: '0'
99
110
  type: :development
100
111
  prerelease: false
101
- version_requirements: *70191400501980
112
+ version_requirements: *70212288989200
102
113
  description: phrase allows you to edit translations in-place on the page itself. More
103
114
  information at phraseapp.com
104
115
  email:
@@ -130,7 +141,29 @@ files:
130
141
  - lib/phrase/engine.rb
131
142
  - lib/phrase/hash_flattener.rb
132
143
  - lib/phrase/tool.rb
144
+ - lib/phrase/tool/commands.rb
145
+ - lib/phrase/tool/commands/base.rb
146
+ - lib/phrase/tool/commands/init.rb
147
+ - lib/phrase/tool/commands/pull.rb
148
+ - lib/phrase/tool/commands/push.rb
149
+ - lib/phrase/tool/commands/show_help.rb
150
+ - lib/phrase/tool/commands/show_version.rb
133
151
  - lib/phrase/tool/config.rb
152
+ - lib/phrase/tool/formats.rb
153
+ - lib/phrase/tool/formats/base.rb
154
+ - lib/phrase/tool/formats/gettext.rb
155
+ - lib/phrase/tool/formats/ini.rb
156
+ - lib/phrase/tool/formats/json.rb
157
+ - lib/phrase/tool/formats/plist.rb
158
+ - lib/phrase/tool/formats/properties.rb
159
+ - lib/phrase/tool/formats/qt_phrase_book.rb
160
+ - lib/phrase/tool/formats/qt_translation_source.rb
161
+ - lib/phrase/tool/formats/resx.rb
162
+ - lib/phrase/tool/formats/strings.rb
163
+ - lib/phrase/tool/formats/xliff.rb
164
+ - lib/phrase/tool/formats/xml.rb
165
+ - lib/phrase/tool/formats/yaml.rb
166
+ - lib/phrase/tool/locale.rb
134
167
  - lib/phrase/tool/options.rb
135
168
  - lib/phrase/tool/tag_validator.rb
136
169
  - lib/phrase/version.rb