phrase 0.1.4 → 0.2.0.beta.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +2 -2
- data/Gemfile.lock +6 -6
- data/bin/phrase +1 -1
- data/lib/phrase/api/client.rb +168 -0
- data/lib/phrase/api/config.rb +22 -0
- data/lib/phrase/api/exceptions.rb +9 -0
- data/lib/phrase/api.rb +6 -0
- data/lib/phrase/backend/base.rb +5 -3
- data/lib/phrase/config.rb +64 -62
- data/lib/phrase/delegate.rb +55 -0
- data/lib/phrase/engine.rb +3 -5
- data/lib/phrase/tool.rb +198 -235
- data/lib/phrase/tool_config.rb +26 -45
- data/lib/phrase.rb +1 -0
- data/phrase.gemspec +1 -1
- metadata +16 -6
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -2,7 +2,7 @@ GEM
|
|
2
2
|
remote: http://rubygems.org/
|
3
3
|
specs:
|
4
4
|
activesupport (3.0.0)
|
5
|
-
addressable (2.2.
|
5
|
+
addressable (2.2.8)
|
6
6
|
crack (0.3.1)
|
7
7
|
diff-lcs (1.1.3)
|
8
8
|
i18n (0.6.0)
|
@@ -15,9 +15,9 @@ GEM
|
|
15
15
|
rspec-expectations (2.8.0)
|
16
16
|
diff-lcs (~> 1.1.2)
|
17
17
|
rspec-mocks (2.8.0)
|
18
|
-
vcr (
|
19
|
-
webmock (1.7
|
20
|
-
addressable (
|
18
|
+
vcr (2.2.1)
|
19
|
+
webmock (1.8.7)
|
20
|
+
addressable (>= 2.2.7)
|
21
21
|
crack (>= 0.1.7)
|
22
22
|
|
23
23
|
PLATFORMS
|
@@ -28,5 +28,5 @@ DEPENDENCIES
|
|
28
28
|
i18n
|
29
29
|
json
|
30
30
|
rspec
|
31
|
-
vcr
|
32
|
-
webmock
|
31
|
+
vcr
|
32
|
+
webmock
|
data/bin/phrase
CHANGED
@@ -0,0 +1,168 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'net/https'
|
5
|
+
require 'phrase'
|
6
|
+
require 'phrase/api'
|
7
|
+
|
8
|
+
class Phrase::Api::Client
|
9
|
+
|
10
|
+
METHOD_GET = :get
|
11
|
+
METHOD_POST = :post
|
12
|
+
METHOD_PUT = :put
|
13
|
+
|
14
|
+
attr_reader :auth_token
|
15
|
+
|
16
|
+
def initialize(auth_token)
|
17
|
+
raise "No auth token specified!" if (auth_token.nil? or auth_token.blank?)
|
18
|
+
@auth_token = auth_token
|
19
|
+
end
|
20
|
+
|
21
|
+
def fetch_locales
|
22
|
+
result = perform_api_request("/locales", :get)
|
23
|
+
locales = []
|
24
|
+
JSON.parse(result).map do |locale|
|
25
|
+
locales << locale['name']
|
26
|
+
end
|
27
|
+
locales
|
28
|
+
end
|
29
|
+
|
30
|
+
def translate(key)
|
31
|
+
raise "You must specify a key" if key.nil? or key.blank?
|
32
|
+
keys = {}
|
33
|
+
result = JSON.parse(perform_api_request("/translation_keys/translate", :get, {:key => key}))
|
34
|
+
if result["translate"]
|
35
|
+
keys = result["translate"]
|
36
|
+
end
|
37
|
+
keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_locale(name)
|
41
|
+
raise "You must specify a name" if name.nil? or name.blank?
|
42
|
+
|
43
|
+
begin
|
44
|
+
perform_api_request("/locales", :post, {
|
45
|
+
"locale[name]" => name
|
46
|
+
})
|
47
|
+
rescue Phrase::Api::Exceptions::ServerError => e
|
48
|
+
raise "Locale #{name} could not be created (Maybe it already exists)"
|
49
|
+
end
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def make_locale_default(name)
|
54
|
+
raise "You must specify a name" if name.nil? or name.blank?
|
55
|
+
|
56
|
+
begin
|
57
|
+
perform_api_request("/locales/#{name}/make_default", :put)
|
58
|
+
rescue Phrase::Api::Exceptions::ServerError => e
|
59
|
+
raise "Locale #{name} could not be made the default locale"
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def download_translations_for_locale(name)
|
65
|
+
raise "You must specify a name" if name.nil? or name.blank?
|
66
|
+
|
67
|
+
begin
|
68
|
+
content = perform_api_request("/translations/download", :get, {'locale' => name})
|
69
|
+
return content
|
70
|
+
rescue Phrase::Api::Exceptions::ServerError => e
|
71
|
+
raise "Translations #{name} could not be downloaded"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def upload(filename, file_content)
|
76
|
+
begin
|
77
|
+
perform_api_request("/translation_keys/upload", :post, {
|
78
|
+
"filename" => filename,
|
79
|
+
"file_content" => file_content,
|
80
|
+
})
|
81
|
+
rescue Phrase::Api::Exceptions::ServerError => e
|
82
|
+
raise "File #{filename} could not be uploaded"
|
83
|
+
end
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def display_api_error(response)
|
89
|
+
error_message = api_error_message(response)
|
90
|
+
$stderr.puts error_message
|
91
|
+
end
|
92
|
+
|
93
|
+
def api_error_message(response)
|
94
|
+
message = ""
|
95
|
+
begin
|
96
|
+
error = JSON.parse(response.body)["error"]
|
97
|
+
if error.class == String
|
98
|
+
message = error
|
99
|
+
else
|
100
|
+
message = JSON.parse(response.body)["message"]
|
101
|
+
end
|
102
|
+
rescue JSON::ParserError
|
103
|
+
end
|
104
|
+
message = "Unknown Error" if message.blank?
|
105
|
+
"Error: #{message} (#{response.code})"
|
106
|
+
end
|
107
|
+
|
108
|
+
def perform_api_request(endpoint, method=METHOD_GET, params={})
|
109
|
+
request = case method
|
110
|
+
when METHOD_GET
|
111
|
+
get_request(endpoint, params)
|
112
|
+
when METHOD_POST
|
113
|
+
post_request(endpoint, params)
|
114
|
+
when METHOD_PUT
|
115
|
+
put_request(endpoint, params)
|
116
|
+
else
|
117
|
+
raise "Invalid Request Method: #{method}"
|
118
|
+
end
|
119
|
+
response = http_client.request(request)
|
120
|
+
code = response.code.to_i
|
121
|
+
if (code == 200)
|
122
|
+
return response.body
|
123
|
+
else
|
124
|
+
error_message = api_error_message(response)
|
125
|
+
raise Phrase::Api::Exceptions::Unauthorized.new(error_message) if (code == 401)
|
126
|
+
raise Phrase::Api::Exceptions::ServerError.new(error_message)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def get_request(endpoint, params={})
|
131
|
+
query_string = "?auth_token=#{@auth_token}"
|
132
|
+
params.each do |key, value|
|
133
|
+
query_string << "&#{key}=#{value}"
|
134
|
+
end unless params.empty?
|
135
|
+
request = Net::HTTP::Get.new("#{api_path_for(endpoint)}#{query_string}")
|
136
|
+
request
|
137
|
+
end
|
138
|
+
|
139
|
+
def post_request(endpoint, params={})
|
140
|
+
request = Net::HTTP::Post.new("#{api_path_for(endpoint)}")
|
141
|
+
params.merge!({
|
142
|
+
'auth_token' => @auth_token
|
143
|
+
})
|
144
|
+
request.set_form_data(params)
|
145
|
+
request
|
146
|
+
end
|
147
|
+
|
148
|
+
def put_request(endpoint, params={})
|
149
|
+
request = Net::HTTP::Put.new("#{api_path_for(endpoint)}")
|
150
|
+
params.merge!({
|
151
|
+
'auth_token' => @auth_token
|
152
|
+
})
|
153
|
+
request.set_form_data(params)
|
154
|
+
request
|
155
|
+
end
|
156
|
+
|
157
|
+
def api_path_for(endpoint)
|
158
|
+
"#{Phrase::Api::Config.api_path_prefix}#{endpoint}"
|
159
|
+
end
|
160
|
+
|
161
|
+
def http_client
|
162
|
+
client = Net::HTTP.new(Phrase::Api::Config.api_host, Phrase::Api::Config.api_port)
|
163
|
+
client.use_ssl = true if Phrase::Api::Config.api_use_ssl?
|
164
|
+
client.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
165
|
+
client.ca_file = File.join(File.dirname(__FILE__), "..", "..", "cacert.pem")
|
166
|
+
client
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'phrase'
|
4
|
+
require 'phrase/api'
|
5
|
+
|
6
|
+
class Phrase::Api::Config
|
7
|
+
def self.api_port
|
8
|
+
ENV.fetch("PHRASE_API_PORT", "443")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.api_host
|
12
|
+
ENV.fetch("PHRASE_API_HOST", "phraseapp.com")
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.api_path_prefix
|
16
|
+
"/api/#{Phrase::API_VERSION}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.api_use_ssl?
|
20
|
+
(ENV.fetch("PHRASE_API_USE_SSL", "1") == "1")
|
21
|
+
end
|
22
|
+
end
|
data/lib/phrase/api.rb
ADDED
data/lib/phrase/backend/base.rb
CHANGED
@@ -1,20 +1,22 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
+
require 'phrase/delegate'
|
4
|
+
|
3
5
|
module Phrase::Backend::Base
|
4
6
|
|
5
7
|
def translate(*args)
|
6
8
|
if Phrase.enabled?
|
7
9
|
key = lookup_normalized_key(*args)
|
8
|
-
|
10
|
+
phrase_delegate_for(key[:key])
|
9
11
|
else
|
10
12
|
I18n.translate_without_phrase(*args)
|
11
13
|
end
|
12
14
|
end
|
13
15
|
|
14
16
|
protected
|
15
|
-
def
|
17
|
+
def phrase_delegate_for(key=nil)
|
16
18
|
return nil unless key.presence
|
17
|
-
|
19
|
+
Phrase::Delegate.new(key)
|
18
20
|
end
|
19
21
|
|
20
22
|
def lookup_normalized_key(*args)
|
data/lib/phrase/config.rb
CHANGED
@@ -1,68 +1,70 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
end
|
3
|
+
class Phrase::Config
|
4
|
+
def client_version
|
5
|
+
Phrase::CLIENT_VERSION
|
6
|
+
end
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
def backend=(backend)
|
32
|
-
@@backend = backend
|
33
|
-
end
|
34
|
-
|
35
|
-
def prefix
|
36
|
-
@@prefix ||= "{{__"
|
37
|
-
end
|
38
|
-
|
39
|
-
def prefix=(prefix)
|
40
|
-
@@prefix = prefix
|
41
|
-
end
|
42
|
-
|
43
|
-
def suffix
|
44
|
-
@@suffix ||= "__}}"
|
45
|
-
end
|
46
|
-
|
47
|
-
def suffix=(suffix)
|
48
|
-
@@suffix = suffix
|
49
|
-
end
|
50
|
-
|
51
|
-
def js_host
|
52
|
-
@@js_host ||= 'phraseapp.com'
|
53
|
-
end
|
54
|
-
|
55
|
-
def js_host=(js_host)
|
56
|
-
@@js_host = js_host
|
57
|
-
end
|
58
|
-
|
59
|
-
def js_use_ssl
|
60
|
-
@@js_use_ssl = true if !defined? @@js_use_ssl or @@js_use_ssl.nil?
|
61
|
-
@@js_use_ssl
|
62
|
-
end
|
8
|
+
def api_version
|
9
|
+
Phrase::API_VERSION
|
10
|
+
end
|
11
|
+
|
12
|
+
def auth_token
|
13
|
+
@@auth_token = "" if !defined? @@auth_token or @@auth_token.nil?
|
14
|
+
@@auth_token
|
15
|
+
end
|
16
|
+
|
17
|
+
def auth_token=(auth_token)
|
18
|
+
@@auth_token = auth_token
|
19
|
+
end
|
20
|
+
|
21
|
+
def enabled
|
22
|
+
@@enabled = true if !defined? @@enabled or @@enabled.nil?
|
23
|
+
@@enabled
|
24
|
+
end
|
25
|
+
|
26
|
+
def enabled=(enabled)
|
27
|
+
@@enabled = enabled
|
28
|
+
end
|
63
29
|
|
64
|
-
|
65
|
-
|
66
|
-
|
30
|
+
def backend
|
31
|
+
@@backend ||= Phrase::Backend::PhraseService.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def backend=(backend)
|
35
|
+
@@backend = backend
|
36
|
+
end
|
37
|
+
|
38
|
+
def prefix
|
39
|
+
@@prefix ||= "{{__"
|
40
|
+
end
|
41
|
+
|
42
|
+
def prefix=(prefix)
|
43
|
+
@@prefix = prefix
|
44
|
+
end
|
45
|
+
|
46
|
+
def suffix
|
47
|
+
@@suffix ||= "__}}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def suffix=(suffix)
|
51
|
+
@@suffix = suffix
|
52
|
+
end
|
53
|
+
|
54
|
+
def js_host
|
55
|
+
@@js_host ||= 'phraseapp.com'
|
56
|
+
end
|
57
|
+
|
58
|
+
def js_host=(js_host)
|
59
|
+
@@js_host = js_host
|
60
|
+
end
|
61
|
+
|
62
|
+
def js_use_ssl
|
63
|
+
@@js_use_ssl = true if !defined? @@js_use_ssl or @@js_use_ssl.nil?
|
64
|
+
@@js_use_ssl
|
65
|
+
end
|
66
|
+
|
67
|
+
def js_use_ssl=(js_use_ssl)
|
68
|
+
@@js_use_ssl = js_use_ssl
|
67
69
|
end
|
68
70
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'phrase/api'
|
2
|
+
|
3
|
+
class Phrase::Delegate
|
4
|
+
attr_accessor :key, :api_client
|
5
|
+
|
6
|
+
def initialize(key)
|
7
|
+
@key = key
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{decorated_key_name}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(*args, &block)
|
15
|
+
if @key.respond_to?(args.first)
|
16
|
+
to_s.send(*args)
|
17
|
+
else
|
18
|
+
data = translation_or_subkeys
|
19
|
+
if data.is_a?(String)
|
20
|
+
to_s
|
21
|
+
elsif data.respond_to?(args.first)
|
22
|
+
data.send(*args, &block)
|
23
|
+
else
|
24
|
+
log "You are trying to execute the method ##{args.first} on a translation key which is not supported. Please make sure you treat your translations as strings only."
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def decorated_key_name
|
32
|
+
"#{Phrase.prefix}phrase_#{@key}#{Phrase.suffix}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def translation_or_subkeys
|
36
|
+
begin
|
37
|
+
api_client.translate(@key)
|
38
|
+
rescue Exception => e
|
39
|
+
log "Server Error: #{e.message}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def api_client
|
44
|
+
@api_client ||= Phrase::Api::Client.new(Phrase.auth_token)
|
45
|
+
end
|
46
|
+
|
47
|
+
def log(message)
|
48
|
+
message = "phrase: #{message}"
|
49
|
+
if defined?(Rails)
|
50
|
+
Rails.logger.warn(message)
|
51
|
+
else
|
52
|
+
$stderr.puts message
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/phrase/engine.rb
CHANGED
@@ -4,11 +4,9 @@ require 'phrase'
|
|
4
4
|
require 'i18n'
|
5
5
|
|
6
6
|
if defined? Rails
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
ActionView::Base.send :include, Phrase::ViewHelpers
|
11
|
-
end
|
7
|
+
class Phrase::Engine < Rails::Engine
|
8
|
+
initializer 'phrase' do |app|
|
9
|
+
ActionView::Base.send :include, Phrase::ViewHelpers
|
12
10
|
end
|
13
11
|
end
|
14
12
|
end
|
data/lib/phrase/tool.rb
CHANGED
@@ -1,277 +1,240 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
-
require 'phrase/tool_config'
|
4
|
-
|
5
3
|
require 'optparse'
|
6
4
|
require 'net/http'
|
7
5
|
require 'net/https'
|
8
6
|
require 'fileutils'
|
7
|
+
require 'phrase/tool_config'
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
class Phrase::Tool
|
10
|
+
|
11
|
+
attr_accessor :config
|
12
|
+
|
13
|
+
def initialize(argv)
|
14
|
+
@args = argv
|
15
|
+
end
|
12
16
|
|
13
|
-
|
14
|
-
|
17
|
+
def run
|
18
|
+
@config = Phrase::ToolConfig.new
|
19
|
+
command = args.first
|
20
|
+
|
21
|
+
case command
|
22
|
+
when /init/
|
23
|
+
init
|
24
|
+
when /push/
|
25
|
+
push
|
26
|
+
when /pull/
|
27
|
+
pull
|
28
|
+
else
|
29
|
+
print_usage
|
15
30
|
end
|
31
|
+
end
|
16
32
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
when /push/
|
25
|
-
push(config)
|
26
|
-
when /pull/
|
27
|
-
pull(config)
|
28
|
-
else
|
29
|
-
print_usage
|
30
|
-
end
|
33
|
+
protected
|
34
|
+
def init
|
35
|
+
secret_param = args.find{ |arg| arg =~ /secret=/ }
|
36
|
+
unless secret_param.to_s.match(/secret=.+/)
|
37
|
+
$stderr.puts "Need a secret to init, but found none."
|
38
|
+
$stderr.puts "Please provide the --secret=YOUR_SECRET parameter."
|
39
|
+
exit(41)
|
31
40
|
end
|
32
41
|
|
33
|
-
|
42
|
+
secret = secret_param.split("=", 2).last
|
43
|
+
@config.secret = secret
|
44
|
+
puts "Wrote secret to config file .phrase"
|
34
45
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
46
|
+
default_locale_param = args.find{ |arg| arg =~ /default-locale=/ }
|
47
|
+
if default_locale_param.to_s.match(/default-locale=.+/)
|
48
|
+
locale_name = default_locale_param.split("=", 2).last
|
49
|
+
else
|
50
|
+
locale_name = "en"
|
51
|
+
end
|
52
|
+
create_locale(locale_name)
|
53
|
+
make_locale_default(locale_name)
|
54
|
+
end
|
42
55
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
locale_name = "en"
|
52
|
-
end
|
53
|
-
create_locale(locale_name, config)
|
54
|
-
make_locale_default(locale_name, config)
|
56
|
+
def push
|
57
|
+
check_config_available
|
58
|
+
|
59
|
+
files = choose_files_to_upload
|
60
|
+
if files.empty?
|
61
|
+
puts "Could not find any files to upload :("
|
62
|
+
exit(43)
|
55
63
|
end
|
64
|
+
upload_files(files)
|
65
|
+
end
|
56
66
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
upload_files(files, config)
|
67
|
+
def pull
|
68
|
+
check_config_available
|
69
|
+
|
70
|
+
create_locales_folder!
|
71
|
+
|
72
|
+
locale = args[1]
|
73
|
+
locales = []
|
74
|
+
if locale && locale.strip != ''
|
75
|
+
locales = [locale]
|
76
|
+
else
|
77
|
+
locales = fetch_locales
|
70
78
|
end
|
79
|
+
|
80
|
+
locales.each do |locale_name|
|
81
|
+
print "Downloading phrase.#{locale_name}.yml..."
|
82
|
+
fetch_translations_for_locale(locale_name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def print_usage
|
87
|
+
$stderr.puts <<USAGE
|
88
|
+
Welcome to phrase!
|
71
89
|
|
72
|
-
|
73
|
-
file_name = args[1]
|
74
|
-
|
75
|
-
unless file_name
|
76
|
-
if self.class.rails_default_locale_folder_is_available
|
77
|
-
file_name = self.class.rails_default_locale_folder
|
78
|
-
puts "No file or directory specified, using #{self.class.rails_default_locale_folder}"
|
79
|
-
else
|
80
|
-
$stderr.puts "Need either a file or directory:"
|
81
|
-
$stderr.puts "phrase push FILE"
|
82
|
-
$stderr.puts "phrase push DIRECTORY"
|
83
|
-
exit(46)
|
84
|
-
end
|
85
|
-
end
|
90
|
+
phrase Prints usage
|
86
91
|
|
87
|
-
|
88
|
-
$stderr.puts "The file #{file_name} could not be found."
|
89
|
-
exit(42)
|
90
|
-
end
|
92
|
+
phrase init --secret=<YOUR SECRET> --default-locale=<YOUR DEFAULT LOCALE>
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
94
|
+
phrase push FILE
|
95
|
+
phrase push DIRECTORY
|
96
|
+
USAGE
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def choose_files_to_upload
|
101
|
+
file_name = args[1]
|
102
|
+
|
103
|
+
unless file_name
|
104
|
+
if self.class.rails_default_locale_folder_is_available
|
105
|
+
file_name = self.class.rails_default_locale_folder
|
106
|
+
puts "No file or directory specified, using #{self.class.rails_default_locale_folder}"
|
107
|
+
else
|
108
|
+
$stderr.puts "Need either a file or directory:"
|
109
|
+
$stderr.puts "phrase push FILE"
|
110
|
+
$stderr.puts "phrase push DIRECTORY"
|
111
|
+
exit(46)
|
96
112
|
end
|
97
113
|
end
|
98
114
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
if File.directory?(file)
|
104
|
-
proceed_with_upload = false
|
105
|
-
end
|
106
|
-
|
107
|
-
if is_yaml_file(file)
|
108
|
-
proceed_with_upload = false
|
109
|
-
$stderr.puts "Notice: Could not upload #{file} (extension not supported - see http://phraseapp.com/help for more information)"
|
110
|
-
end
|
111
|
-
|
112
|
-
if proceed_with_upload
|
113
|
-
puts "Uploading #{file}..."
|
114
|
-
params = {
|
115
|
-
'auth_token' => config.secret,
|
116
|
-
'filename' => file,
|
117
|
-
'file_content' => File.read(file)
|
118
|
-
}
|
119
|
-
|
120
|
-
http = http_client(config)
|
121
|
-
|
122
|
-
request = Net::HTTP::Post.new("#{config.api_path_prefix}/translation_keys/upload")
|
123
|
-
request.set_form_data(params)
|
124
|
-
res = http.request(request)
|
125
|
-
|
126
|
-
unless res.code.to_s =~ /^[23]/
|
127
|
-
print_server_error(res, file)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
115
|
+
unless File.exist?(file_name)
|
116
|
+
$stderr.puts "The file #{file_name} could not be found."
|
117
|
+
exit(42)
|
131
118
|
end
|
132
119
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
if locale && locale.strip != ''
|
138
|
-
locales = [locale]
|
139
|
-
else
|
140
|
-
locales = fetch_locales(config)
|
141
|
-
end
|
142
|
-
|
143
|
-
locales.each do |locale|
|
144
|
-
fetch_translation_for_locale(config, locale)
|
145
|
-
end
|
120
|
+
if File.directory?(file_name)
|
121
|
+
files = Dir.glob("#{File.expand_path(file_name)}/**")
|
122
|
+
else
|
123
|
+
files = [file_name]
|
146
124
|
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def upload_files(files)
|
128
|
+
files.each do |file|
|
129
|
+
proceed_with_upload = true
|
147
130
|
|
148
|
-
|
149
|
-
|
150
|
-
::FileUtils.mkdir_p("phrase/locales")
|
151
|
-
|
152
|
-
http = http_client(config)
|
153
|
-
|
154
|
-
request = Net::HTTP::Get.new("#{config.api_path_prefix}/translations/download?auth_token=#{config.secret}&locale=#{locale}")
|
155
|
-
res = http.request(request)
|
156
|
-
|
157
|
-
if res.code.to_s =~ /200/
|
158
|
-
puts "OK"
|
159
|
-
File.open("phrase/locales/phrase.#{locale}.yml", "w") do |file|
|
160
|
-
file.write(res.body)
|
161
|
-
end
|
162
|
-
else
|
163
|
-
puts "Failed"
|
164
|
-
print_server_error(res)
|
131
|
+
if File.directory?(file)
|
132
|
+
proceed_with_upload = false
|
165
133
|
end
|
166
|
-
end
|
167
134
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
request = Net::HTTP::Get.new("#{config.api_path_prefix}/locales?auth_token=#{config.secret}")
|
172
|
-
res = http.request(request)
|
173
|
-
|
174
|
-
if res.code.to_s =~ /200/
|
175
|
-
puts "Fetched all locales"
|
176
|
-
return JSON.parse(res.body).map { |locale| locale['name'] }
|
177
|
-
else
|
178
|
-
puts "Failed"
|
179
|
-
print_server_error(res)
|
180
|
-
exit(47)
|
135
|
+
if is_yaml_file(file)
|
136
|
+
proceed_with_upload = false
|
137
|
+
$stderr.puts "Notice: Could not upload #{file} (extension not supported - see http://phraseapp.com/help for more information)"
|
181
138
|
end
|
182
|
-
end
|
183
139
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
request.set_form_data(params)
|
194
|
-
res = http.request(request)
|
195
|
-
|
196
|
-
if res.code.to_s =~ /200/
|
197
|
-
puts "Created locale \"#{locale_name}\""
|
198
|
-
else
|
199
|
-
puts "Notice: Locale \"#{locale_name}\" could not be created (maybe it already exists)"
|
140
|
+
if proceed_with_upload
|
141
|
+
begin
|
142
|
+
puts "Uploading #{file}..."
|
143
|
+
api_client.upload(file, File.read(file))
|
144
|
+
puts "OK"
|
145
|
+
rescue Exception => e
|
146
|
+
puts "Failed"
|
147
|
+
print_server_error(e.message, file)
|
148
|
+
end
|
200
149
|
end
|
201
150
|
end
|
202
|
-
|
203
|
-
def make_locale_default(locale_name, config)
|
204
|
-
params = {
|
205
|
-
'auth_token' => config.secret,
|
206
|
-
}
|
151
|
+
end
|
207
152
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
else
|
217
|
-
puts "Notice: Locale \"#{locale_name}\" could not be made the default locale"
|
218
|
-
print_server_error(res)
|
219
|
-
end
|
153
|
+
def fetch_translations_for_locale(name)
|
154
|
+
begin
|
155
|
+
content = api_client.download_translations_for_locale(name)
|
156
|
+
puts "OK"
|
157
|
+
store_translations_file(name, content)
|
158
|
+
rescue Exception => e
|
159
|
+
puts "Failed"
|
160
|
+
print_server_error(e.message)
|
220
161
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
162
|
+
end
|
163
|
+
|
164
|
+
def store_translations_file(name, content)
|
165
|
+
File.open("phrase/locales/phrase.#{name}.yml", "w") do |file|
|
166
|
+
file.write(content)
|
225
167
|
end
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
168
|
+
end
|
169
|
+
|
170
|
+
def fetch_locales
|
171
|
+
begin
|
172
|
+
locales = api_client.fetch_locales
|
173
|
+
puts "Fetched all locales"
|
174
|
+
return locales
|
175
|
+
rescue Exception => e
|
176
|
+
puts "Failed"
|
177
|
+
print_server_error(e.message)
|
178
|
+
exit(47)
|
233
179
|
end
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
phrase push FILE
|
244
|
-
phrase push DIRECTORY
|
245
|
-
USAGE
|
180
|
+
end
|
181
|
+
|
182
|
+
def create_locale(name)
|
183
|
+
begin
|
184
|
+
api_client.create_locale(name)
|
185
|
+
puts "Created locale \"#{name}\""
|
186
|
+
rescue Exception => e
|
187
|
+
puts "Notice: Locale \"#{name}\" could not be created (maybe it already exists)"
|
246
188
|
end
|
247
|
-
|
248
|
-
|
249
|
-
|
189
|
+
end
|
190
|
+
|
191
|
+
def make_locale_default(name)
|
192
|
+
begin
|
193
|
+
api_client.make_locale_default(name)
|
194
|
+
puts "Locale \"#{name}\" is now the default locale"
|
195
|
+
rescue Exception => e
|
196
|
+
puts "Notice: Locale \"#{name}\" could not be made the default locale"
|
197
|
+
print_server_error(e.message)
|
250
198
|
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def api_client
|
202
|
+
Phrase::Api::Client.new(@config.secret)
|
203
|
+
end
|
251
204
|
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
205
|
+
def print_server_error(message, filename=nil)
|
206
|
+
$stderr.puts "#{message} (#{filename})"
|
207
|
+
end
|
208
|
+
|
209
|
+
def args
|
210
|
+
@args
|
211
|
+
end
|
212
|
+
|
213
|
+
def puts_debug_info
|
214
|
+
puts "ARGS: #{args.join(",")}"
|
215
|
+
puts "Dir.pwd: #{Dir.pwd}"
|
216
|
+
end
|
217
|
+
|
218
|
+
def is_yaml_file(filepath)
|
219
|
+
!File.directory?(filepath) && filepath.split('.').last != 'yml'
|
220
|
+
end
|
221
|
+
|
222
|
+
def create_locales_folder!
|
223
|
+
::FileUtils.mkdir_p("phrase/locales")
|
224
|
+
end
|
225
|
+
|
226
|
+
def check_config_available
|
227
|
+
if !@config.secret || @config.secret.empty?
|
228
|
+
$stderr.puts "No config present. You need to initialize phrase first."
|
229
|
+
exit(43)
|
275
230
|
end
|
276
231
|
end
|
277
|
-
|
232
|
+
|
233
|
+
def self.rails_default_locale_folder
|
234
|
+
"./config/locales/"
|
235
|
+
end
|
236
|
+
|
237
|
+
def self.rails_default_locale_folder_is_available
|
238
|
+
File.exist?(rails_default_locale_folder) && File.directory?(rails_default_locale_folder)
|
239
|
+
end
|
240
|
+
end
|
data/lib/phrase/tool_config.rb
CHANGED
@@ -1,56 +1,37 @@
|
|
1
1
|
# -*- encoding : utf-8 -*-
|
2
2
|
|
3
|
+
require 'phrase'
|
3
4
|
require 'json'
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
rescue JSON::ParserError => err
|
14
|
-
$stderr.puts "Could not parse config file: #{err}"
|
15
|
-
end
|
6
|
+
class Phrase::ToolConfig
|
7
|
+
def initialize
|
8
|
+
if File.exist?(".phrase")
|
9
|
+
begin
|
10
|
+
contents = File.read(".phrase")
|
11
|
+
@config = JSON.parse(contents)
|
12
|
+
rescue JSON::ParserError => err
|
13
|
+
$stderr.puts "Could not parse config file: #{err}"
|
16
14
|
end
|
17
15
|
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def secret
|
19
|
+
config["secret"]
|
20
|
+
end
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
def api_port
|
29
|
-
ENV.fetch("PHRASE_API_PORT", "443")
|
30
|
-
end
|
31
|
-
|
32
|
-
def api_host
|
33
|
-
ENV.fetch("PHRASE_API_HOST", "phraseapp.com")
|
34
|
-
end
|
35
|
-
|
36
|
-
def api_path_prefix
|
37
|
-
"/api/v1"
|
38
|
-
end
|
39
|
-
|
40
|
-
def api_use_ssl
|
41
|
-
ENV.fetch("PHRASE_API_USE_SSL", "1")
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def config
|
47
|
-
@config ||= {}
|
48
|
-
end
|
22
|
+
def secret=(new_secret)
|
23
|
+
config["secret"] = new_secret
|
24
|
+
save_config!
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def config
|
29
|
+
@config ||= {}
|
30
|
+
end
|
49
31
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
32
|
+
def save_config!
|
33
|
+
File.open(".phrase", "w+") do |file|
|
34
|
+
file.write(JSON.pretty_generate(config))
|
54
35
|
end
|
55
36
|
end
|
56
37
|
end
|
data/lib/phrase.rb
CHANGED
data/phrase.gemspec
CHANGED
metadata
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: phrase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.0.beta.2
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Dynport GmbH
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-06-
|
12
|
+
date: 2012-06-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
|
-
requirement:
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,7 +21,12 @@ dependencies:
|
|
21
21
|
version: '3.0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements:
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.0'
|
25
30
|
description: phrase allows you to edit translations inline, on the page itself. More
|
26
31
|
information at phraseapp.com
|
27
32
|
email:
|
@@ -38,10 +43,15 @@ files:
|
|
38
43
|
- bin/phrase
|
39
44
|
- cacert.pem
|
40
45
|
- lib/phrase.rb
|
46
|
+
- lib/phrase/api.rb
|
47
|
+
- lib/phrase/api/client.rb
|
48
|
+
- lib/phrase/api/config.rb
|
49
|
+
- lib/phrase/api/exceptions.rb
|
41
50
|
- lib/phrase/backend.rb
|
42
51
|
- lib/phrase/backend/base.rb
|
43
52
|
- lib/phrase/backend/phrase_service.rb
|
44
53
|
- lib/phrase/config.rb
|
54
|
+
- lib/phrase/delegate.rb
|
45
55
|
- lib/phrase/engine.rb
|
46
56
|
- lib/phrase/tool.rb
|
47
57
|
- lib/phrase/tool_config.rb
|
@@ -67,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
77
|
version: 1.3.6
|
68
78
|
requirements: []
|
69
79
|
rubyforge_project: phrase
|
70
|
-
rubygems_version: 1.8.
|
80
|
+
rubygems_version: 1.8.24
|
71
81
|
signing_key:
|
72
82
|
specification_version: 3
|
73
83
|
summary: The best way to manage i18n.
|