phrase 0.3.1 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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