phrase 0.0.0 → 0.0.1
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 +2 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +26 -0
- data/README.md +15 -0
- data/bin/phrase +4 -0
- data/lib/phrase.rb +33 -4
- data/lib/phrase/backend.rb +134 -0
- data/lib/phrase/backend/base.rb +120 -0
- data/lib/phrase/backend/phrase_service.rb +14 -0
- data/lib/phrase/config.rb +34 -0
- data/lib/phrase/engine.rb +18 -0
- data/lib/phrase/extensions.rb +7 -0
- data/lib/phrase/extensions/base.rb +11 -0
- data/lib/phrase/extensions/hash.rb +33 -0
- data/lib/phrase/extensions/string.rb +14 -0
- data/lib/phrase/tool.rb +166 -0
- data/lib/phrase/tool_config.rb +41 -0
- data/phrase.gemspec +20 -0
- metadata +42 -43
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
addressable (2.2.7)
|
5
|
+
crack (0.3.1)
|
6
|
+
diff-lcs (1.1.3)
|
7
|
+
rspec (2.8.0)
|
8
|
+
rspec-core (~> 2.8.0)
|
9
|
+
rspec-expectations (~> 2.8.0)
|
10
|
+
rspec-mocks (~> 2.8.0)
|
11
|
+
rspec-core (2.8.0)
|
12
|
+
rspec-expectations (2.8.0)
|
13
|
+
diff-lcs (~> 1.1.2)
|
14
|
+
rspec-mocks (2.8.0)
|
15
|
+
vcr (1.11.3)
|
16
|
+
webmock (1.7.10)
|
17
|
+
addressable (~> 2.2, > 2.2.5)
|
18
|
+
crack (>= 0.1.7)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
rspec
|
25
|
+
vcr
|
26
|
+
webmock (~> 1.7.0)
|
data/README.md
ADDED
data/bin/phrase
ADDED
data/lib/phrase.rb
CHANGED
@@ -1,5 +1,34 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module Phrase
|
4
|
+
autoload :Config, 'phrase/config'
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def config
|
8
|
+
Thread.current[:phrase_config] ||= Phrase::Config.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def config=(value)
|
12
|
+
Thread.current[:phrase_config] = value
|
13
|
+
end
|
14
|
+
|
15
|
+
%w(backend default_locale available_locales).each do |method|
|
16
|
+
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
|
17
|
+
def #{method}
|
18
|
+
config.#{method}
|
19
|
+
end
|
20
|
+
DELEGATORS
|
21
|
+
|
22
|
+
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
|
23
|
+
def #{method}=(value)
|
24
|
+
config.#{method} = (value)
|
25
|
+
end
|
26
|
+
DELEGATORS
|
27
|
+
end
|
4
28
|
end
|
5
|
-
|
29
|
+
|
30
|
+
autoload :Extensions, 'phrase/extensions'
|
31
|
+
autoload :Helpers, 'phrase/helpers'
|
32
|
+
require 'phrase/engine'
|
33
|
+
require 'phrase/backend'
|
34
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module Phrase::Backend
|
4
|
+
# require_relative 'backend/base'
|
5
|
+
module Phrase::Backend::Base
|
6
|
+
PREFIX = "{{__phrase_"
|
7
|
+
SUFFIX = "__}}"
|
8
|
+
|
9
|
+
# attr_accessor :config
|
10
|
+
|
11
|
+
def translate(*args)
|
12
|
+
key = lookup_normalized_key(*args)
|
13
|
+
case key[:translation]
|
14
|
+
when String, nil, "" then decorate_translation(key[:key])
|
15
|
+
else key[:translation]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# def load_translations(*args)
|
20
|
+
# raise NotImplementedError
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def raw_lookup(locale, key, scope=[], options={})
|
24
|
+
# I18n.backend.load_translations unless I18n.backend.initialized?
|
25
|
+
# keys = I18n.normalize_keys(locale, key, scope, options[:separator])
|
26
|
+
# keys.inject(I18n.backend.send(:translations)) do |result, _key|
|
27
|
+
# _key = _key.to_sym
|
28
|
+
# return nil unless result.is_a?(Hash) && result.has_key?(_key)
|
29
|
+
# result[_key]
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
|
33
|
+
protected
|
34
|
+
def decorate_translation(key=nil)
|
35
|
+
return nil unless key.presence
|
36
|
+
"#{PREFIX}#{key}#{SUFFIX}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def translation_presence(*args)
|
40
|
+
I18n.translate!(*args)
|
41
|
+
rescue I18n::MissingTranslationData => e
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def lookup_normalized_key(*args)
|
46
|
+
translation = translation_presence(*args)
|
47
|
+
caller = identify_caller
|
48
|
+
|
49
|
+
if (translation.nil? || translation.is_a?(Hash)) && caller && args.first =~ /^\./
|
50
|
+
args = transform_args_for_caller(caller, *args)
|
51
|
+
translation = translation_presence(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
new_args = split_args(*args)
|
55
|
+
|
56
|
+
normalized_key = I18n::Backend::Flatten.normalize_flat_keys(*new_args)
|
57
|
+
normalized_key.gsub!("..", ".")
|
58
|
+
|
59
|
+
{:key => normalized_key, :translation => translation}
|
60
|
+
end
|
61
|
+
|
62
|
+
def identify_caller
|
63
|
+
caller = nil
|
64
|
+
send(:caller)[0..6].each { |string| caller = match_caller(string) unless caller }
|
65
|
+
caller.present? ? find_lookup_scope(caller) : nil
|
66
|
+
end
|
67
|
+
|
68
|
+
def match_caller(string)
|
69
|
+
string.match(/(views)(\/.+)(?>:[0-9]+:in)/)
|
70
|
+
end
|
71
|
+
|
72
|
+
# def extract_required_vars(*args)
|
73
|
+
# excluded = ["scope", "locale"]
|
74
|
+
# required_vars = args.last.is_a?(Hash) ? args.pop.keys.sort { |a, b| a.to_s <=> b.to_s } : []
|
75
|
+
# required_vars.delete_if { |var| excluded.include?(var.to_s) }
|
76
|
+
# end
|
77
|
+
|
78
|
+
def split_args(*args)
|
79
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
80
|
+
key ||= args.shift
|
81
|
+
locale = options.delete(:locale) || I18n.locale
|
82
|
+
return [locale, key, options[:scope], nil]
|
83
|
+
end
|
84
|
+
|
85
|
+
def transform_args_for_caller(caller, *args)
|
86
|
+
_scope = caller
|
87
|
+
|
88
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
89
|
+
|
90
|
+
if !options[:scope].presence && _scope.presence
|
91
|
+
options[:scope] = _scope
|
92
|
+
end
|
93
|
+
|
94
|
+
args.push(options)
|
95
|
+
parts = args.first.to_s.split(".").select { |e| !e.blank? }
|
96
|
+
args[0] = parts[0] if parts.size == 1
|
97
|
+
|
98
|
+
return args
|
99
|
+
end
|
100
|
+
|
101
|
+
def find_lookup_scope(caller)
|
102
|
+
split_path = caller[2][1..-1].split(".")[0].split("/")
|
103
|
+
|
104
|
+
template_or_partial = remove_underscore_form_partial(split_path[-1])
|
105
|
+
split_path[-1] = template_or_partial
|
106
|
+
|
107
|
+
split_path.map!(&:to_sym)
|
108
|
+
end
|
109
|
+
|
110
|
+
def remove_underscore_form_partial(template_or_partial)
|
111
|
+
if template_or_partial.to_s[0,1] == "_"
|
112
|
+
template_or_partial.to_s[1..-1]
|
113
|
+
else
|
114
|
+
template_or_partial.to_s
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# require_relative 'backend/phrase_service'
|
120
|
+
module Phrase::Backend
|
121
|
+
class PhraseService
|
122
|
+
include Base
|
123
|
+
|
124
|
+
def initialize(args = {})
|
125
|
+
# self.config = { "locales" => {}, "strings" => {} }
|
126
|
+
# Phrase.available_locales = self.config["locales"].keys.map(&:to_sym)
|
127
|
+
self
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
132
|
+
# LOCALE in keys mit schreiben, hilft der GUI!? -> manuel
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module Phrase::Backend::Base
|
4
|
+
PREFIX = "{{__phrase_"
|
5
|
+
SUFFIX = "__}}"
|
6
|
+
|
7
|
+
attr_accessor :config
|
8
|
+
|
9
|
+
def translate(*args)
|
10
|
+
key = lookup_normalized_key(*args)
|
11
|
+
case key[:translation]
|
12
|
+
when String, nil, "" then decorate_translation(key[:key])
|
13
|
+
else key[:translation]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_translations(*args)
|
18
|
+
raise NotImplementedError
|
19
|
+
end
|
20
|
+
|
21
|
+
def store_translation(locale, key, translation, options = {})
|
22
|
+
I18n.backend.load_translations unless I18n.backend.initialized?
|
23
|
+
I18n.backend.store_translations(locale, key.expand_to_hash(translation))
|
24
|
+
end
|
25
|
+
|
26
|
+
def raw_lookup(locale, key, scope=[], options={})
|
27
|
+
I18n.backend.load_translations unless I18n.backend.initialized?
|
28
|
+
keys = I18n.normalize_keys(locale, key, scope, options[:separator])
|
29
|
+
keys.inject(I18n.backend.send(:translations)) do |result, _key|
|
30
|
+
_key = _key.to_sym
|
31
|
+
return nil unless result.is_a?(Hash) && result.has_key?(_key)
|
32
|
+
result[_key]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
def decorate_translation(key=nil)
|
38
|
+
return nil unless key.presence
|
39
|
+
"#{PREFIX}#{key}#{SUFFIX}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def translation_presence(*args)
|
43
|
+
I18n.translate!(*args)
|
44
|
+
rescue I18n::MissingTranslationData => e
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
|
48
|
+
def lookup_normalized_key(*args)
|
49
|
+
translation = translation_presence(*args)
|
50
|
+
caller = identify_caller
|
51
|
+
|
52
|
+
if (translation.nil? || translation.is_a?(Hash)) && caller && args.first =~ /^\./
|
53
|
+
args = transform_args_for_caller(caller, *args)
|
54
|
+
translation = translation_presence(*args)
|
55
|
+
end
|
56
|
+
|
57
|
+
new_args = split_args(*args)
|
58
|
+
|
59
|
+
normalized_key = I18n::Backend::Flatten.normalize_flat_keys(*new_args)
|
60
|
+
normalized_key.gsub!("..", ".")
|
61
|
+
|
62
|
+
{:key => normalized_key, :translation => translation}
|
63
|
+
end
|
64
|
+
|
65
|
+
def identify_caller
|
66
|
+
caller = nil
|
67
|
+
send(:caller)[0..6].each { |string| caller = match_caller(string) unless caller }
|
68
|
+
caller.present? ? find_lookup_scope(caller) : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def match_caller(string)
|
72
|
+
string.match(/(views)(\/.+)(?>:[0-9]+:in)/)
|
73
|
+
end
|
74
|
+
|
75
|
+
def extract_required_vars(*args)
|
76
|
+
excluded = ["scope", "locale"]
|
77
|
+
required_vars = args.last.is_a?(Hash) ? args.pop.keys.sort { |a, b| a.to_s <=> b.to_s } : []
|
78
|
+
required_vars.delete_if { |var| excluded.include?(var.to_s) }
|
79
|
+
end
|
80
|
+
|
81
|
+
def split_args(*args)
|
82
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
83
|
+
key ||= args.shift
|
84
|
+
locale = options.delete(:locale) || I18n.locale
|
85
|
+
return [locale, key, options[:scope], nil]
|
86
|
+
end
|
87
|
+
|
88
|
+
def transform_args_for_caller(caller, *args)
|
89
|
+
_scope = caller
|
90
|
+
|
91
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
92
|
+
|
93
|
+
if !options[:scope].presence && _scope.presence
|
94
|
+
options[:scope] = _scope
|
95
|
+
end
|
96
|
+
|
97
|
+
args.push(options)
|
98
|
+
parts = args.first.to_s.split(".").select { |e| !e.blank? }
|
99
|
+
args[0] = parts[0] if parts.size == 1
|
100
|
+
|
101
|
+
return args
|
102
|
+
end
|
103
|
+
|
104
|
+
def find_lookup_scope(caller)
|
105
|
+
split_path = caller[2][1..-1].split(".")[0].split("/")
|
106
|
+
|
107
|
+
template_or_partial = remove_underscore_form_partial(split_path[-1])
|
108
|
+
split_path[-1] = template_or_partial
|
109
|
+
|
110
|
+
split_path.map!(&:to_sym)
|
111
|
+
end
|
112
|
+
|
113
|
+
def remove_underscore_form_partial(template_or_partial)
|
114
|
+
if template_or_partial.to_s[0,1] == "_"
|
115
|
+
template_or_partial.to_s[1..-1]
|
116
|
+
else
|
117
|
+
template_or_partial.to_s
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module Phrase::Backend
|
4
|
+
class PhraseService
|
5
|
+
include Base
|
6
|
+
|
7
|
+
def initialize(args = {})
|
8
|
+
self.config = { "locales" => {}, "strings" => {} }
|
9
|
+
Phrase.available_locales = self.config["locales"].keys.map(&:to_sym)
|
10
|
+
self
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Phrase
|
3
|
+
class Config
|
4
|
+
def enabled?
|
5
|
+
ENV['TRANSLATABLE'] == "true" || (defined?(Rails) == true && Rails.env == "staging")
|
6
|
+
end
|
7
|
+
|
8
|
+
def locale
|
9
|
+
I18n.locale
|
10
|
+
end
|
11
|
+
|
12
|
+
def default_locale
|
13
|
+
@@default_locale ||= I18n.default_locale
|
14
|
+
end
|
15
|
+
|
16
|
+
def backend
|
17
|
+
@@backend ||= Backend::PhraseService.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def backend=(backend)
|
21
|
+
@@backend = backend
|
22
|
+
end
|
23
|
+
|
24
|
+
def available_locales
|
25
|
+
@@available_locales ||= nil
|
26
|
+
@@available_locales ||= I18n.available_locales
|
27
|
+
end
|
28
|
+
|
29
|
+
def available_locales=(locales)
|
30
|
+
@@available_locales = Array(locales).map { |locale| locale.to_sym }
|
31
|
+
@@available_locales = nil if @@available_locales.empty?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
require 'phrase'
|
3
|
+
require 'rails'
|
4
|
+
|
5
|
+
module Phrase
|
6
|
+
class Engine < Rails::Engine
|
7
|
+
|
8
|
+
initializer 'phrase' do |app|
|
9
|
+
ActiveSupport.on_load(:action_view) do
|
10
|
+
::ActionView::Base.send :include, Phrase::Extensions::Base
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load(:action_controller) do
|
14
|
+
::ActionController::Base.send :include, Phrase::Extensions::Base
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Phrase::Extensions::Hash
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
def to_shallow_hash
|
7
|
+
self.inject({}) do |shallow_hash, (key, value)|
|
8
|
+
if value.is_a?(Hash)
|
9
|
+
value.to_shallow_hash.each do |sub_key, sub_value|
|
10
|
+
shallow_hash[[key, sub_key].join(".")] = sub_value
|
11
|
+
end
|
12
|
+
else
|
13
|
+
shallow_hash[key.to_s] = value
|
14
|
+
end
|
15
|
+
shallow_hash
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def deep_stringify_keys
|
20
|
+
inject({}) { |result, (key, value)|
|
21
|
+
value = value.deep_stringify_keys if value.is_a?(Hash)
|
22
|
+
unless value.is_a? Proc
|
23
|
+
result[(key.to_s rescue key) || key] = value
|
24
|
+
else
|
25
|
+
result[(key.to_s rescue key) || key] = nil
|
26
|
+
end
|
27
|
+
result
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
Hash.send(:include, Phrase::Extensions::Hash)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
module Phrase::Extensions::String
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module InstanceMethods
|
6
|
+
def expand_to_hash(value=nil)
|
7
|
+
self.split('.').reverse.inject(value) do |hash, item|
|
8
|
+
{item => hash}
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
String.send(:include, Phrase::Extensions::String)
|
data/lib/phrase/tool.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'phrase/tool_config'
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'net/http'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module PhraseGem
|
8
|
+
class Tool
|
9
|
+
|
10
|
+
def initialize(argv)
|
11
|
+
@args = argv
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
config = ToolConfig.new
|
16
|
+
command = args.first
|
17
|
+
|
18
|
+
case command
|
19
|
+
when /init/
|
20
|
+
init(config)
|
21
|
+
when /push/
|
22
|
+
push(config)
|
23
|
+
when /pull/
|
24
|
+
pull(config)
|
25
|
+
else
|
26
|
+
print_usage
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def init(config)
|
32
|
+
secret_param = args.find{ |arg| arg =~ /secret=/ }
|
33
|
+
unless secret_param.to_s.match(/secret=.+/)
|
34
|
+
$stderr.puts "Need a secret to init, but found none."
|
35
|
+
$stderr.puts "Please provide the --secret=YOUR_SECRET parameter."
|
36
|
+
exit(41)
|
37
|
+
end
|
38
|
+
|
39
|
+
secret = secret_param.split("=", 2).last
|
40
|
+
config.secret = secret
|
41
|
+
puts "Wrote secret to config file .phrase"
|
42
|
+
end
|
43
|
+
|
44
|
+
def push(config)
|
45
|
+
files = choose_files_to_upload
|
46
|
+
if files.empty?
|
47
|
+
puts "Could not find any files to upload :("
|
48
|
+
exit(43)
|
49
|
+
end
|
50
|
+
|
51
|
+
if !config.secret || config.secret.empty?
|
52
|
+
puts "No config present. Please initialize phrase before pushing or pulling."
|
53
|
+
exit(43)
|
54
|
+
end
|
55
|
+
|
56
|
+
files.each do |file|
|
57
|
+
if file.split('.').last != 'yml'
|
58
|
+
$stderr.puts "Currently only .yml files are supported."
|
59
|
+
exit(43)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
upload_files(files, config)
|
64
|
+
end
|
65
|
+
|
66
|
+
def choose_files_to_upload
|
67
|
+
file_name = args[1]
|
68
|
+
unless file_name
|
69
|
+
$stderr.puts "Need either a file or directory:"
|
70
|
+
$stderr.puts " phrase push FILE"
|
71
|
+
$stderr.puts " phrase push DIRECTORY"
|
72
|
+
exit(46)
|
73
|
+
end
|
74
|
+
|
75
|
+
unless File.exist?(file_name)
|
76
|
+
$stderr.puts "The file #{file_name} could not be found."
|
77
|
+
exit(42)
|
78
|
+
end
|
79
|
+
|
80
|
+
if File.directory?(file_name)
|
81
|
+
files = Dir.glob("#{File.expand_path(file_name)}/**")
|
82
|
+
else
|
83
|
+
files = [file_name]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def upload_files(files, config)
|
88
|
+
files.each do |file|
|
89
|
+
puts "Uploading #{file}..."
|
90
|
+
params = {
|
91
|
+
'auth_token'=>config.secret,
|
92
|
+
'filename'=> file,
|
93
|
+
'file_content' => File.read(file)
|
94
|
+
}
|
95
|
+
res = Net::HTTP.post_form(URI.parse("#{config.api_endpoint}/translation_keys/upload"),params)
|
96
|
+
|
97
|
+
dump_summary_to_file(res, file)
|
98
|
+
unless res.code.to_s =~ /^[23]/
|
99
|
+
print_server_error(res, file)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def pull(config)
|
105
|
+
locale = args[1]
|
106
|
+
uri = "#{config.api_endpoint}/translations/download?auth_token=#{config.secret}&locale=#{locale}"
|
107
|
+
print "Downloading phrase.#{locale}.yml..."
|
108
|
+
::FileUtils.mkdir_p("phrase/locales")
|
109
|
+
res = Net::HTTP.get_response(URI.parse(uri))
|
110
|
+
if res.code.to_s =~ /200/
|
111
|
+
puts " OK"
|
112
|
+
File.open("phrase/locales/phrase.#{locale}.yml", "w") do |file|
|
113
|
+
file.write(res.body)
|
114
|
+
end
|
115
|
+
else
|
116
|
+
puts " Failed"
|
117
|
+
print_server_error(res)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def print_server_error(res, filename=nil)
|
122
|
+
error_message = server_error_message(res.body)
|
123
|
+
$stderr.puts "Server error: #{res.code} - #{error_message} (#{filename})"
|
124
|
+
end
|
125
|
+
|
126
|
+
def server_error_message(body)
|
127
|
+
begin
|
128
|
+
JSON.parse(body)["error"]
|
129
|
+
rescue JSON::ParserError
|
130
|
+
"Unkown error"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def print_usage
|
135
|
+
$stderr.puts <<USAGE
|
136
|
+
Welcome to phrase!
|
137
|
+
phrase Prints usage
|
138
|
+
|
139
|
+
phrase init --secret=<YOUR SECRET>
|
140
|
+
|
141
|
+
phrase push FILE
|
142
|
+
phrase push DIRECTORY
|
143
|
+
USAGE
|
144
|
+
end
|
145
|
+
|
146
|
+
def args
|
147
|
+
@args
|
148
|
+
end
|
149
|
+
|
150
|
+
def puts_debug_info
|
151
|
+
puts "ARGS: #{args.join(",")}"
|
152
|
+
puts "Dir.pwd: #{Dir.pwd}"
|
153
|
+
end
|
154
|
+
|
155
|
+
def dump_summary_to_file(res, upload_file)
|
156
|
+
::FileUtils.mkdir_p("phrase/uploads/")
|
157
|
+
timestamp = Time.now.strftime("%Y%m%m-%H%M%S")
|
158
|
+
file_name = "#{File.basename(upload_file)}-#{timestamp}"
|
159
|
+
summary_file = "phrase/uploads/#{file_name}.json"
|
160
|
+
File.open(summary_file, "w") do |file|
|
161
|
+
file.write(res.body)
|
162
|
+
end
|
163
|
+
puts "Summary saved in #{summary_file}"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PhraseGem
|
4
|
+
class ToolConfig
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
if File.exist?(".phrase")
|
8
|
+
begin
|
9
|
+
contents = File.read(".phrase")
|
10
|
+
@config = JSON.parse(contents)
|
11
|
+
rescue JSON::ParserError => err
|
12
|
+
$stderr.puts "Could not parse config file: #{err}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def secret
|
18
|
+
config["secret"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def secret=(new_secret)
|
22
|
+
config["secret"] = new_secret
|
23
|
+
save_config!
|
24
|
+
end
|
25
|
+
|
26
|
+
def api_endpoint
|
27
|
+
ENV.fetch("PHRASE_API_ENDPOINT", "https://phraseapp.com/api/v1")
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def config
|
32
|
+
@config ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_config!
|
36
|
+
File.open(".phrase", "w+") do |file|
|
37
|
+
file.write(JSON.pretty_generate(config))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/phrase.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$:.unshift lib unless $:.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "phrase"
|
7
|
+
s.version = "0.0.1"
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Dynport GmbH"]
|
10
|
+
s.email = ["info@phraseapp.com"]
|
11
|
+
s.homepage = "http://phraseapp.com"
|
12
|
+
s.summary = %q{The best way to manage i18n.}
|
13
|
+
s.description = s.summary
|
14
|
+
s.required_rubygems_version = ">= 1.3.6"
|
15
|
+
s.rubyforge_project = "phrase"
|
16
|
+
git_files = `git ls-files | grep -v spec/`.split("\n") rescue ''
|
17
|
+
s.files = git_files
|
18
|
+
s.executables = %w(phrase)
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
end
|
metadata
CHANGED
@@ -1,65 +1,64 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: phrase
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
- 0
|
10
|
-
version: 0.0.0
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
13
|
-
-
|
7
|
+
authors:
|
8
|
+
- Dynport GmbH
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2012-03-12 00:00:00 Z
|
12
|
+
date: 2012-03-13 00:00:00.000000000Z
|
19
13
|
dependencies: []
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
executables:
|
24
|
-
|
14
|
+
description: The best way to manage i18n.
|
15
|
+
email:
|
16
|
+
- info@phraseapp.com
|
17
|
+
executables:
|
18
|
+
- phrase
|
25
19
|
extensions: []
|
26
|
-
|
27
20
|
extra_rdoc_files: []
|
28
|
-
|
29
|
-
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- Gemfile
|
24
|
+
- Gemfile.lock
|
25
|
+
- README.md
|
26
|
+
- bin/phrase
|
30
27
|
- lib/phrase.rb
|
28
|
+
- lib/phrase/backend.rb
|
29
|
+
- lib/phrase/backend/base.rb
|
30
|
+
- lib/phrase/backend/phrase_service.rb
|
31
|
+
- lib/phrase/config.rb
|
32
|
+
- lib/phrase/engine.rb
|
33
|
+
- lib/phrase/extensions.rb
|
34
|
+
- lib/phrase/extensions/base.rb
|
35
|
+
- lib/phrase/extensions/hash.rb
|
36
|
+
- lib/phrase/extensions/string.rb
|
37
|
+
- lib/phrase/tool.rb
|
38
|
+
- lib/phrase/tool_config.rb
|
39
|
+
- phrase.gemspec
|
31
40
|
homepage: http://phraseapp.com
|
32
41
|
licenses: []
|
33
|
-
|
34
42
|
post_install_message:
|
35
43
|
rdoc_options: []
|
36
|
-
|
37
|
-
require_paths:
|
44
|
+
require_paths:
|
38
45
|
- lib
|
39
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
47
|
none: false
|
41
|
-
requirements:
|
42
|
-
- -
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
|
45
|
-
|
46
|
-
- 0
|
47
|
-
version: "0"
|
48
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
53
|
none: false
|
50
|
-
requirements:
|
51
|
-
- -
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
|
54
|
-
segments:
|
55
|
-
- 0
|
56
|
-
version: "0"
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: 1.3.6
|
57
58
|
requirements: []
|
58
|
-
|
59
|
-
rubyforge_project:
|
59
|
+
rubyforge_project: phrase
|
60
60
|
rubygems_version: 1.8.10
|
61
61
|
signing_key:
|
62
62
|
specification_version: 3
|
63
|
-
summary:
|
63
|
+
summary: The best way to manage i18n.
|
64
64
|
test_files: []
|
65
|
-
|