phrase 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +4 -1
  2. data/Guardfile +29 -0
  3. data/README.md +1 -1
  4. data/lib/phrase.rb +8 -9
  5. data/lib/phrase/adapters/fast_gettext.rb +22 -0
  6. data/lib/phrase/adapters/i18n.rb +11 -0
  7. data/lib/phrase/api/client.rb +1 -1
  8. data/lib/phrase/backend/phrase_service.rb +2 -2
  9. data/lib/phrase/delegate.rb +13 -165
  10. data/lib/phrase/delegate/fast_gettext.rb +27 -0
  11. data/lib/phrase/delegate/i18n.rb +159 -0
  12. data/lib/phrase/tool.rb +1 -1
  13. data/lib/phrase/tool/commands/base.rb +1 -1
  14. data/lib/phrase/tool/commands/init.rb +13 -5
  15. data/lib/phrase/tool/commands/pull.rb +11 -8
  16. data/lib/phrase/tool/commands/push.rb +11 -2
  17. data/lib/phrase/tool/config.rb +55 -1
  18. data/lib/phrase/tool/encoding_detector.rb +4 -2
  19. data/lib/phrase/tool/formats.rb +33 -4
  20. data/lib/phrase/tool/formats/base.rb +23 -1
  21. data/lib/phrase/tool/formats/custom.rb +38 -0
  22. data/lib/phrase/tool/formats/gettext.rb +14 -2
  23. data/lib/phrase/tool/formats/gettext_pot.rb +11 -0
  24. data/lib/phrase/tool/formats/ini.rb +4 -0
  25. data/lib/phrase/tool/formats/json.rb +4 -0
  26. data/lib/phrase/tool/formats/plist.rb +5 -1
  27. data/lib/phrase/tool/formats/properties.rb +4 -0
  28. data/lib/phrase/tool/formats/qt_phrase_book.rb +4 -0
  29. data/lib/phrase/tool/formats/qt_translation_source.rb +4 -0
  30. data/lib/phrase/tool/formats/resx.rb +4 -0
  31. data/lib/phrase/tool/formats/strings.rb +9 -1
  32. data/lib/phrase/tool/formats/xliff.rb +4 -0
  33. data/lib/phrase/tool/formats/xml.rb +7 -4
  34. data/lib/phrase/tool/formats/yaml.rb +4 -0
  35. data/lib/phrase/tool/options.rb +13 -52
  36. data/lib/phrase/tool/options_factory.rb +77 -0
  37. data/lib/phrase/version.rb +1 -1
  38. metadata +74 -21
data/lib/phrase/tool.rb CHANGED
@@ -37,4 +37,4 @@ class Phrase::Tool
37
37
  end
38
38
  command.execute!
39
39
  end
40
- end
40
+ end
@@ -70,4 +70,4 @@ module Phrase::Tool::Commands
70
70
  self.class.exit_command
71
71
  end
72
72
  end
73
- end
73
+ end
@@ -6,13 +6,18 @@ class Phrase::Tool::Commands::Init < Phrase::Tool::Commands::Base
6
6
  end
7
7
 
8
8
  def execute!
9
- secret = options.get(:secret)
9
+ secret = get_option(:secret)
10
10
  if secret.present?
11
11
  config.secret = secret
12
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)
13
+ config.default_locale = get_option(:default_locale)
14
+ create_locale(config.default_locale)
15
+ make_locale_default(config.default_locale)
16
+ config.format = get_option(:format)
17
+ config.target_directory = get_option(:target_directory)
18
+ config.domain = get_option(:domain)
19
+ config.locale_directory = get_option(:locale_directory)
20
+ config.locale_filename = get_option(:locale_filename)
16
21
  else
17
22
  print_auth_token_error
18
23
  exit_command
@@ -20,6 +25,9 @@ class Phrase::Tool::Commands::Init < Phrase::Tool::Commands::Base
20
25
  end
21
26
 
22
27
  private
28
+ def get_option(symbol)
29
+ options.get symbol
30
+ end
23
31
 
24
32
  def create_locale(name)
25
33
  begin
@@ -44,4 +52,4 @@ private
44
52
  print_error "No auth token was given"
45
53
  print_error "Please provide the --secret=YOUR_SECRET parameter."
46
54
  end
47
- end
55
+ end
@@ -1,10 +1,8 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  class Phrase::Tool::Commands::Pull < Phrase::Tool::Commands::Base
4
-
5
4
  ALLOWED_DOWNLOAD_FORMATS = %w(yml po xml strings json resx ts qph ini plist properties xlf)
6
5
  DEFAULT_DOWNLOAD_FORMAT = "yml"
7
- DEFAULT_TARGET_FOLDER = "phrase/locales/"
8
6
 
9
7
  def initialize(options, args)
10
8
  super(options, args)
@@ -12,8 +10,10 @@ class Phrase::Tool::Commands::Pull < Phrase::Tool::Commands::Base
12
10
 
13
11
  @locale = @args[1]
14
12
 
15
- @format = @options.get(:format) || DEFAULT_DOWNLOAD_FORMAT
16
- @target = @options.get(:target) || DEFAULT_TARGET_FOLDER
13
+ # TODO: remove DEFAULT_DOWNLOAD_FORMAT when phrase app has been updated
14
+ @format = @options.get(:format) || config.format || DEFAULT_DOWNLOAD_FORMAT
15
+ @target = @options.get(:target)
16
+ @target ||= Phrase::Tool::Formats.target_directory(@format) if format_valid?(@format)
17
17
  end
18
18
 
19
19
  def execute!
@@ -25,7 +25,6 @@ class Phrase::Tool::Commands::Pull < Phrase::Tool::Commands::Base
25
25
  end
26
26
 
27
27
  private
28
-
29
28
  def fetch_translations_for_locale(locale, format)
30
29
  begin
31
30
  content = api_client.download_translations_for_locale(locale.name, format)
@@ -46,12 +45,16 @@ private
46
45
  File.open(target, "w") do |file|
47
46
  file.write(content)
48
47
  end
49
- print_message "Saved to #{target}".green
48
+ print_message "Saved to #{clean_path target}".green
50
49
  rescue
51
50
  print_error("Cannot write file to target folder (#{path})")
52
51
  exit_command
53
52
  end
54
53
  end
54
+
55
+ def clean_path(str)
56
+ str.gsub("/./", "/")
57
+ end
55
58
 
56
59
  def fetch_locales
57
60
  begin
@@ -64,7 +67,7 @@ private
64
67
  end
65
68
 
66
69
  def format_valid?(format)
67
- ALLOWED_DOWNLOAD_FORMATS.include?(format)
70
+ format.nil? or ALLOWED_DOWNLOAD_FORMATS.include?(format)
68
71
  end
69
72
 
70
73
  def base_directory
@@ -96,4 +99,4 @@ private
96
99
  def user_specified_a_locale?
97
100
  @locale and @locale.strip != ''
98
101
  end
99
- end
102
+ end
@@ -1,7 +1,7 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
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)
4
+ ALLOWED_FILE_TYPES = %w(yml po pot xml strings json resx ts qph ini plist properties xlf)
5
5
  FORMATS_CONTAINING_LOCALE = %q(po yml qph ts xlf)
6
6
  RAILS_DEFAULT_FOLDER = "./config/locales/"
7
7
 
@@ -77,7 +77,12 @@ private
77
77
  begin
78
78
  tagged = " (tagged: #{@tags.join(", ")})" if @tags.size > 0
79
79
  print_message "Uploading #{file}#{tagged}..."
80
- locale = @locale || detect_locale_name_from_file_path(file)
80
+ unless force_use_of_default_locale?(file)
81
+ locale = detect_locale_name_from_file_path(file)
82
+ else
83
+ locale = Phrase::Tool::Locale.find_default_locale.try(:name)
84
+ end
85
+ locale = @locale if @locale
81
86
  api_client.upload(file, file_content(file), @tags, locale)
82
87
  print_message "OK".green
83
88
  rescue Exception => e
@@ -93,6 +98,10 @@ private
93
98
  content
94
99
  end
95
100
 
101
+ def force_use_of_default_locale?(file_path)
102
+ not Phrase::Tool::Formats.file_format_exposes_locale?(file_path)
103
+ end
104
+
96
105
  def utf16_to_utf8(string)
97
106
  string.encode("UTF-8", "UTF-16")
98
107
  end
@@ -23,7 +23,61 @@ class Phrase::Tool::Config
23
23
  config["secret"] = new_secret
24
24
  save_config!
25
25
  end
26
+
27
+ def default_locale
28
+ config["default_locale"]
29
+ end
30
+
31
+ def default_locale=(new_default_locale)
32
+ config["default_locale"] = new_default_locale
33
+ save_config!
34
+ end
35
+
36
+ def domain
37
+ config["domain"] || 'phrase'
38
+ end
39
+
40
+ def domain=(new_domain)
41
+ config["domain"] = new_domain
42
+ save_config!
43
+ end
44
+
45
+ def format
46
+ config["format"]
47
+ end
48
+
49
+ def format=(new_domain)
50
+ config["format"] = new_domain
51
+ save_config!
52
+ end
26
53
 
54
+ def target_directory
55
+ config["target_directory"]
56
+ end
57
+
58
+ def target_directory=(new_domain)
59
+ config["target_directory"] = new_domain
60
+ save_config!
61
+ end
62
+
63
+ def locale_directory
64
+ config["locale_directory"]
65
+ end
66
+
67
+ def locale_directory=(new_domain)
68
+ config["locale_directory"] = new_domain
69
+ save_config!
70
+ end
71
+
72
+ def locale_filename
73
+ config["locale_filename"]
74
+ end
75
+
76
+ def locale_filename=(new_domain)
77
+ config["locale_filename"] = new_domain
78
+ save_config!
79
+ end
80
+
27
81
  private
28
82
  def config
29
83
  @config ||= {}
@@ -34,4 +88,4 @@ private
34
88
  file.write(JSON.pretty_generate(config))
35
89
  end
36
90
  end
37
- end
91
+ end
@@ -6,10 +6,12 @@ class Phrase::Tool::EncodingDetector
6
6
  end
7
7
 
8
8
  def self.file_seems_to_be_utf16_be?(file)
9
- IO.read(file, 2).bytes.to_a == [0xFE, 0xFF]
9
+ input = IO.read(file, 2)
10
+ input and input.bytes.to_a == [0xFE, 0xFF]
10
11
  end
11
12
 
12
13
  def self.file_seems_to_be_utf16_le?(file)
13
- IO.read(file, 2).bytes.to_a == [0xFF, 0xFE]
14
+ input = IO.read(file, 2)
15
+ input and input.bytes.to_a == [0xFF, 0xFE]
14
16
  end
15
17
  end
@@ -4,6 +4,7 @@ module Phrase::Tool::Formats
4
4
  autoload :Base, 'phrase/tool/formats/base'
5
5
  autoload :Yaml, 'phrase/tool/formats/yaml'
6
6
  autoload :Gettext, 'phrase/tool/formats/gettext'
7
+ autoload :GettextPot, 'phrase/tool/formats/gettext_pot'
7
8
  autoload :Xml, 'phrase/tool/formats/xml'
8
9
  autoload :Strings, 'phrase/tool/formats/strings'
9
10
  autoload :Xliff, 'phrase/tool/formats/xliff'
@@ -14,10 +15,13 @@ module Phrase::Tool::Formats
14
15
  autoload :Ini, 'phrase/tool/formats/ini'
15
16
  autoload :Properties, 'phrase/tool/formats/properties'
16
17
  autoload :Plist, 'phrase/tool/formats/plist'
18
+ autoload :Custom, 'phrase/tool/formats/custom'
17
19
 
18
20
  SUPPORTED_FORMATS = {
21
+ custom: Phrase::Tool::Formats::Custom,
19
22
  yml: Phrase::Tool::Formats::Yaml,
20
23
  po: Phrase::Tool::Formats::Gettext,
24
+ pot: Phrase::Tool::Formats::GettextPot,
21
25
  xml: Phrase::Tool::Formats::Xml,
22
26
  strings: Phrase::Tool::Formats::Strings,
23
27
  xlf: Phrase::Tool::Formats::Xliff,
@@ -27,17 +31,42 @@ module Phrase::Tool::Formats
27
31
  resx: Phrase::Tool::Formats::Resx,
28
32
  ini: Phrase::Tool::Formats::Ini,
29
33
  properties: Phrase::Tool::Formats::Properties,
30
- plist: Phrase::Tool::Formats::Plist
34
+ plist: Phrase::Tool::Formats::Plist,
31
35
  }
36
+
37
+ def self.config
38
+ @config ||= get_config
39
+ end
40
+
41
+ def self.get_config
42
+ config = Phrase::Tool::Config.new
43
+ config.load
44
+ end
45
+
46
+ def self.custom_handler
47
+ handler_class_for_format(:custom)
48
+ end
49
+
50
+ def self.target_directory(format_name)
51
+ handler = handler_class_for_format(format_name)
52
+ custom_handler.target_directory || handler.target_directory
53
+ end
32
54
 
33
55
  def self.directory_for_locale_in_format(locale, format_name)
34
56
  handler = handler_class_for_format(format_name)
35
- handler.directory_for_locale(locale)
57
+ custom_directory = custom_handler.directory_for_locale(locale, format_name)
58
+ custom_directory || handler.directory_for_locale(locale)
36
59
  end
37
60
 
38
61
  def self.filename_for_locale_in_format(locale, format_name)
39
62
  handler = handler_class_for_format(format_name)
40
- handler.filename_for_locale(locale)
63
+ custom_filename = custom_handler.filename_for_locale(locale, format_name)
64
+ custom_filename || handler.filename_for_locale(locale)
65
+ end
66
+
67
+ def self.file_format_exposes_locale?(file_path)
68
+ format = guess_possible_file_format_from_file_path(file_path)
69
+ format.nil? ? false : handler_class_for_format(format).locale_aware?
41
70
  end
42
71
 
43
72
  def self.detect_locale_name_from_file_path(file_path)
@@ -55,4 +84,4 @@ module Phrase::Tool::Formats
55
84
  return SUPPORTED_FORMATS.has_key?(extension.to_sym) ? extension.to_sym : nil
56
85
  end
57
86
  private_class_method :guess_possible_file_format_from_file_path
58
- end
87
+ end
@@ -12,4 +12,26 @@ class Phrase::Tool::Formats::Base
12
12
  def self.extract_locale_name_from_file_path(file_path)
13
13
  nil
14
14
  end
15
- end
15
+
16
+ def self.default_locale_name
17
+ Phrase::Tool::Locale.find_default_locale.try(:name)
18
+ end
19
+
20
+ def self.locale_aware?
21
+ false
22
+ end
23
+
24
+ def self.target_directory
25
+ "phrase/locales/"
26
+ end
27
+
28
+ def self.config
29
+ @config ||= get_config
30
+ end
31
+
32
+ def self.get_config
33
+ config = Phrase::Tool::Config.new
34
+ config.load
35
+ end
36
+ private_class_method :config
37
+ end
@@ -0,0 +1,38 @@
1
+ class Phrase::Tool::Formats::Custom < Phrase::Tool::Formats::Base
2
+ def self.directory_for_locale(locale, format)
3
+ setting = config.locale_directory
4
+ return unless setting
5
+ parse(setting, locale, format)
6
+ end
7
+
8
+ def self.filename_for_locale(locale, format)
9
+ setting = config.locale_filename
10
+ return unless setting
11
+ parse(setting, locale, format)
12
+ end
13
+
14
+ def self.target_directory
15
+ config.target_directory
16
+ end
17
+
18
+ def self.parse(str, locale, format)
19
+ str.gsub(/#{regexp(locale, format)}/) do |match|
20
+ replacements(locale, format)[match]
21
+ end
22
+ end
23
+ private_class_method :parse
24
+
25
+ def self.regexp(locale, format)
26
+ "(#{replacements(locale, format).keys.join('|')})"
27
+ end
28
+
29
+ def self.replacements(locale, format)
30
+ {
31
+ '<domain>' => config.domain,
32
+ '<format>' => format,
33
+ '<locale.name>' => locale.name,
34
+ '<locale.code>' => locale.code,
35
+ '<locale>' => locale.name
36
+ }
37
+ end
38
+ end
@@ -1,7 +1,19 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
 
3
3
  class Phrase::Tool::Formats::Gettext < Phrase::Tool::Formats::Base
4
+ def self.directory_for_locale(locale)
5
+ "./#{locale.name}/"
6
+ end
7
+
4
8
  def self.filename_for_locale(locale)
5
- "phrase.#{locale.name}.po"
9
+ "#{config.domain}.po"
10
+ end
11
+
12
+ def self.target_directory
13
+ "locales/"
14
+ end
15
+
16
+ def self.locale_aware?
17
+ true
6
18
  end
7
- end
19
+ end
@@ -0,0 +1,11 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ class Phrase::Tool::Formats::GettextPot < Phrase::Tool::Formats::Base
4
+ def self.filename_for_locale(locale)
5
+ "phrase.pot"
6
+ end
7
+
8
+ def self.locale_aware?
9
+ false
10
+ end
11
+ end
@@ -4,4 +4,8 @@ class Phrase::Tool::Formats::Ini < Phrase::Tool::Formats::Base
4
4
  def self.filename_for_locale(locale)
5
5
  "phrase.#{locale.name}.ini"
6
6
  end
7
+
8
+ def self.locale_aware?
9
+ true
10
+ end
7
11
  end
@@ -4,4 +4,8 @@ class Phrase::Tool::Formats::Json < Phrase::Tool::Formats::Base
4
4
  def self.filename_for_locale(locale)
5
5
  "phrase.#{locale.name}.json"
6
6
  end
7
+
8
+ def self.locale_aware?
9
+ true
10
+ end
7
11
  end
@@ -4,4 +4,8 @@ class Phrase::Tool::Formats::Plist < Phrase::Tool::Formats::Base
4
4
  def self.filename_for_locale(locale)
5
5
  "phrase.#{locale.name}.plist"
6
6
  end
7
- end
7
+
8
+ def self.locale_aware?
9
+ true
10
+ end
11
+ end
@@ -4,4 +4,8 @@ class Phrase::Tool::Formats::Properties < Phrase::Tool::Formats::Base
4
4
  def self.filename_for_locale(locale)
5
5
  "phrase.#{locale.name}.properties"
6
6
  end
7
+
8
+ def self.locale_aware?
9
+ true
10
+ end
7
11
  end