phrase 0.2.5 → 0.3.0

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/.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