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.
- data/.gitignore +4 -1
- data/Guardfile +29 -0
- data/README.md +1 -1
- data/lib/phrase.rb +8 -9
- data/lib/phrase/adapters/fast_gettext.rb +22 -0
- data/lib/phrase/adapters/i18n.rb +11 -0
- data/lib/phrase/api/client.rb +1 -1
- data/lib/phrase/backend/phrase_service.rb +2 -2
- data/lib/phrase/delegate.rb +13 -165
- data/lib/phrase/delegate/fast_gettext.rb +27 -0
- data/lib/phrase/delegate/i18n.rb +159 -0
- data/lib/phrase/tool.rb +1 -1
- data/lib/phrase/tool/commands/base.rb +1 -1
- data/lib/phrase/tool/commands/init.rb +13 -5
- data/lib/phrase/tool/commands/pull.rb +11 -8
- data/lib/phrase/tool/commands/push.rb +11 -2
- data/lib/phrase/tool/config.rb +55 -1
- data/lib/phrase/tool/encoding_detector.rb +4 -2
- data/lib/phrase/tool/formats.rb +33 -4
- data/lib/phrase/tool/formats/base.rb +23 -1
- data/lib/phrase/tool/formats/custom.rb +38 -0
- data/lib/phrase/tool/formats/gettext.rb +14 -2
- data/lib/phrase/tool/formats/gettext_pot.rb +11 -0
- data/lib/phrase/tool/formats/ini.rb +4 -0
- data/lib/phrase/tool/formats/json.rb +4 -0
- data/lib/phrase/tool/formats/plist.rb +5 -1
- data/lib/phrase/tool/formats/properties.rb +4 -0
- data/lib/phrase/tool/formats/qt_phrase_book.rb +4 -0
- data/lib/phrase/tool/formats/qt_translation_source.rb +4 -0
- data/lib/phrase/tool/formats/resx.rb +4 -0
- data/lib/phrase/tool/formats/strings.rb +9 -1
- data/lib/phrase/tool/formats/xliff.rb +4 -0
- data/lib/phrase/tool/formats/xml.rb +7 -4
- data/lib/phrase/tool/formats/yaml.rb +4 -0
- data/lib/phrase/tool/options.rb +13 -52
- data/lib/phrase/tool/options_factory.rb +77 -0
- data/lib/phrase/version.rb +1 -1
- metadata +74 -21
data/lib/phrase/tool.rb
CHANGED
@@ -6,13 +6,18 @@ class Phrase::Tool::Commands::Init < Phrase::Tool::Commands::Base
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def execute!
|
9
|
-
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
|
-
|
14
|
-
create_locale(
|
15
|
-
make_locale_default(
|
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
|
-
|
16
|
-
@
|
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
|
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
|
-
|
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
|
data/lib/phrase/tool/config.rb
CHANGED
@@ -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)
|
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)
|
14
|
+
input = IO.read(file, 2)
|
15
|
+
input and input.bytes.to_a == [0xFF, 0xFE]
|
14
16
|
end
|
15
17
|
end
|
data/lib/phrase/tool/formats.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
"
|
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
|