babelyoda 1.6.0 → 2.0.0
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 +5 -0
- data/CHANGELOG +1 -0
- data/Gemfile +6 -0
- data/LICENSE +1 -1
- data/README.rdoc +2 -2
- data/Rakefile +1 -0
- data/babelyoda.gemspec +28 -0
- data/lib/babelyoda/file.rb +20 -0
- data/lib/babelyoda/genstrings.rb +27 -0
- data/lib/babelyoda/git.rb +99 -0
- data/lib/babelyoda/git_versions.rb +39 -0
- data/lib/babelyoda/ibtool.rb +51 -0
- data/lib/babelyoda/keyset.rb +58 -0
- data/lib/babelyoda/localization_key.rb +56 -0
- data/lib/babelyoda/localization_value.rb +26 -0
- data/lib/babelyoda/logger.rb +16 -0
- data/lib/babelyoda/rake.rb +10 -0
- data/lib/babelyoda/specification.rb +40 -0
- data/lib/babelyoda/specification_loader.rb +35 -0
- data/lib/babelyoda/strings.rb +64 -8
- data/lib/babelyoda/strings_lexer.rb +12 -0
- data/lib/babelyoda/strings_parser.rb +72 -0
- data/lib/babelyoda/tanker.rb +190 -0
- data/lib/babelyoda/version.rb +3 -0
- data/lib/babelyoda/xib.rb +94 -0
- data/lib/babelyoda.rb +207 -0
- data/templates/Babelfile.erb +15 -0
- metadata +162 -70
- data/VERSION +0 -1
- data/bin/babelyoda +0 -399
- data/data/empty.strings +0 -1
- data/lib/babelyoda/options.rb +0 -42
@@ -0,0 +1,72 @@
|
|
1
|
+
require_relative 'localization_key'
|
2
|
+
require_relative 'localization_value'
|
3
|
+
|
4
|
+
module Babelyoda
|
5
|
+
class StringsParser
|
6
|
+
Bit = Struct.new(:token, :value)
|
7
|
+
|
8
|
+
def initialize(lexer, language)
|
9
|
+
@lexer, @language = lexer, language
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(str, &block)
|
13
|
+
@block = block
|
14
|
+
bitstream = []
|
15
|
+
@lexer.lex(str) do | token, value |
|
16
|
+
bitstream << Bit.new(token, value)
|
17
|
+
end
|
18
|
+
while bitstream.size > 0
|
19
|
+
record = produce(bitstream)
|
20
|
+
@block.call(record) if record
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def produce(bs)
|
25
|
+
match_bs(bs, :multiline_comment, :string, :equal_sign, :string, :semicolon) do |bits|
|
26
|
+
localization_key = LocalizationKey.new(cleanup_string(bits[1]), cleanup_comment(bits[0]))
|
27
|
+
localization_value = LocalizationValue.new(@language, cleanup_string(bits[3]))
|
28
|
+
localization_key << localization_value
|
29
|
+
return localization_key
|
30
|
+
end
|
31
|
+
match_bs(bs, :singleline_comment, :string, :equal_sign, :string, :semicolon) do |bits|
|
32
|
+
localization_key = LocalizationKey.new(cleanup_string(bits[1]), cleanup_comment(bits[0]))
|
33
|
+
localization_value = LocalizationValue.new(@language, cleanup_string(bits[3]))
|
34
|
+
localization_key << localization_value
|
35
|
+
return localization_key
|
36
|
+
end
|
37
|
+
match_bs(bs, :string, :equal_sign, :string, :semicolon) do |bits|
|
38
|
+
localization_key = LocalizationKey.new(cleanup_string(bits[0]), nil)
|
39
|
+
localization_value = LocalizationValue.new(@language, cleanup_string(bits[2]))
|
40
|
+
localization_key << localization_value
|
41
|
+
return localization_key
|
42
|
+
end
|
43
|
+
match_bs(bs, :singleline_comment) do |bits|
|
44
|
+
return nil
|
45
|
+
end
|
46
|
+
match_bs(bs, :multiline_comment) do |bits|
|
47
|
+
return nil
|
48
|
+
end
|
49
|
+
raise "Syntax error: #{bs.shift(5).inspect}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def match_bs(bs, *tokens)
|
53
|
+
return unless bs.size >= tokens.size
|
54
|
+
tokens.each_with_index do |token, idx|
|
55
|
+
return unless bs[idx][:token] == token
|
56
|
+
end
|
57
|
+
yield bs.shift(tokens.size).map { |bit| bit[:value] }
|
58
|
+
end
|
59
|
+
|
60
|
+
def cleanup_comment(str)
|
61
|
+
if str.match(/^\/\/\s*/)
|
62
|
+
str.sub(/^\/\/\s*/, '')
|
63
|
+
else
|
64
|
+
str.sub(/^\/\*\s*/, '').sub(/\s*\*\/$/, '')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def cleanup_string(str)
|
69
|
+
str.sub(/^\"/, '').sub(/\"$/, '')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'net/http'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
require 'babelyoda/specification_loader'
|
7
|
+
|
8
|
+
module Babelyoda
|
9
|
+
class Keyset
|
10
|
+
def to_xml(xml, language = nil)
|
11
|
+
xml.keyset(:id => name) do
|
12
|
+
keys.each_value do |key|
|
13
|
+
xml.key(:id => key.id, :is_plural => 'False') do
|
14
|
+
xml.context(key.context)
|
15
|
+
key.values.each_value do |value|
|
16
|
+
next if language && (value.language.to_s != language.to_s)
|
17
|
+
xml.value(value.text, :language => value.language, :status => value.status)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_xml(node)
|
25
|
+
result = self.new(node[:id])
|
26
|
+
node.css('key').each do |key_node|
|
27
|
+
result.merge_key!(Babelyoda::LocalizationKey.parse_xml(key_node))
|
28
|
+
end
|
29
|
+
result
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class LocalizationKey
|
34
|
+
def self.parse_xml(node)
|
35
|
+
context = node.css('context').first
|
36
|
+
context &&= context.text
|
37
|
+
result = self.new(node[:id], context)
|
38
|
+
node.css('value').each do |value_node|
|
39
|
+
result << Babelyoda::LocalizationValue.parse_xml(value_node)
|
40
|
+
end
|
41
|
+
result
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class LocalizationValue
|
46
|
+
def self.parse_xml(node)
|
47
|
+
self.new(node[:language], node.text, node[:status])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Tanker
|
52
|
+
include Babelyoda::SpecificationLoader
|
53
|
+
|
54
|
+
class FileNameInvalidError < RuntimeError ; end
|
55
|
+
|
56
|
+
attr_accessor :endpoint
|
57
|
+
attr_accessor :token
|
58
|
+
attr_accessor :project_id
|
59
|
+
|
60
|
+
def replace(keyset, language = nil)
|
61
|
+
doc = project_xml do |xml|
|
62
|
+
keyset.to_xml(xml, language)
|
63
|
+
end
|
64
|
+
payload = {
|
65
|
+
:file => StringIO.new(doc),
|
66
|
+
'project-id' => project_id,
|
67
|
+
'keyset-id' => keyset.name,
|
68
|
+
:format => 'xml'
|
69
|
+
}
|
70
|
+
payload.merge!({:language => language}) if language
|
71
|
+
post('/keysets/replace/', payload)
|
72
|
+
end
|
73
|
+
|
74
|
+
def list
|
75
|
+
get('/keysets/', { 'project-id' => project_id }).css('keyset').map { |keyset| keyset['id'] }
|
76
|
+
end
|
77
|
+
|
78
|
+
def create(keyset_name)
|
79
|
+
post('/keysets/create/', { 'project-id' => project_id, 'keyset-id' => keyset_name })
|
80
|
+
end
|
81
|
+
|
82
|
+
def export(keyset_name = nil, languages = nil, status = nil, safe = false)
|
83
|
+
payload = { 'project-id' => project_id }
|
84
|
+
payload.merge!({ 'keyset-id' => keyset_name }) if keyset_name
|
85
|
+
if languages
|
86
|
+
value = languages
|
87
|
+
value = languages.join(',') if languages.respond_to?(:join)
|
88
|
+
payload.merge!({ 'language' => value })
|
89
|
+
end
|
90
|
+
payload.merge!({ 'status' => status.to_s }) if status
|
91
|
+
payload.merge!({ 'safe' => safe }) if safe
|
92
|
+
get('/projects/export/xml/', payload)
|
93
|
+
end
|
94
|
+
|
95
|
+
def load_keyset(keyset_name, languages = nil, status = nil, safe = false)
|
96
|
+
doc = export(keyset_name, languages, status, safe)
|
97
|
+
doc.css("keyset[@id='#{keyset_name}']").each do |keyset_node|
|
98
|
+
keyset = Babelyoda::Keyset.parse_xml(keyset_node)
|
99
|
+
return keyset if keyset.name == keyset_name
|
100
|
+
end
|
101
|
+
Babelyoda::Keyset.new(keyset_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
MULTIPART_BOUNDARY = '114YANDEXTANKERCLIENTBNDR';
|
107
|
+
|
108
|
+
def multipart_content_type
|
109
|
+
"multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
|
110
|
+
end
|
111
|
+
|
112
|
+
def method(name)
|
113
|
+
"#{endpoint}#{name}"
|
114
|
+
end
|
115
|
+
|
116
|
+
def project_xml(&block)
|
117
|
+
xml = Builder::XmlMarkup.new
|
118
|
+
xml.instruct!(:xml, :encoding => "UTF-8")
|
119
|
+
xml.tanker do
|
120
|
+
xml.project(:id => project_id) do
|
121
|
+
yield xml
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def multipart_data(payload = {}, boundary = MULTIPART_BOUNDARY)
|
127
|
+
payload.keys.map { |k|
|
128
|
+
"--#{boundary}\r\n" + multipart_field(k, payload[k])
|
129
|
+
}.join('') + "--#{boundary}--\r\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
def multipart_field(k, v)
|
133
|
+
if v.respond_to?(:read)
|
134
|
+
"Content-Disposition: form-data; name=\"#{k}\"; filename=\"#{k}.xml\"\r\n" +
|
135
|
+
"Content-Type: application/octet-stream\r\n" +
|
136
|
+
"Content-Transfer-Encoding: binary\r\n\r\n" +
|
137
|
+
"#{v.read}\r\n"
|
138
|
+
else
|
139
|
+
"Content-Disposition: form-data; name=\"#{k}\"\r\n\r\n" +
|
140
|
+
"#{v}\r\n"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def post(method_name, payload)
|
145
|
+
uri = URI(method(method_name))
|
146
|
+
req = Net::HTTP::Post.new(uri.path)
|
147
|
+
req['AUTHORIZATION'] = token
|
148
|
+
req.content_type = multipart_content_type
|
149
|
+
req.body = multipart_data(payload)
|
150
|
+
|
151
|
+
# puts "POST URI: #{uri}"
|
152
|
+
# puts "POST BODY: #{req.body}"
|
153
|
+
|
154
|
+
res = Net::HTTP.start(uri.host, uri.port) do |http|
|
155
|
+
http.request(req)
|
156
|
+
end
|
157
|
+
|
158
|
+
case res
|
159
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
160
|
+
Nokogiri::XML.parse(res.body)
|
161
|
+
else
|
162
|
+
doc = Nokogiri::XML.parse(res.body)
|
163
|
+
error = doc.css('result error')[0].content
|
164
|
+
raise RuntimeError.new(error)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def get(method_name, payload = nil)
|
169
|
+
uri = URI(method(method_name))
|
170
|
+
uri.query = URI.encode_www_form(payload) if payload
|
171
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
172
|
+
req['AUTHORIZATION'] = token
|
173
|
+
|
174
|
+
# puts "GET URI: #{uri}"
|
175
|
+
|
176
|
+
res = Net::HTTP.start(uri.host, uri.port) do |http|
|
177
|
+
http.request(req)
|
178
|
+
end
|
179
|
+
|
180
|
+
case res
|
181
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
182
|
+
Nokogiri::XML.parse(res.body)
|
183
|
+
else
|
184
|
+
doc = Nokogiri::XML.parse(res.body)
|
185
|
+
error = doc.css('result error')[0].content
|
186
|
+
raise Error.new(error)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
require_relative 'file'
|
4
|
+
require_relative 'ibtool'
|
5
|
+
|
6
|
+
module Babelyoda
|
7
|
+
class Xib
|
8
|
+
attr_reader :filename
|
9
|
+
attr_reader :language
|
10
|
+
|
11
|
+
def initialize(filename, language)
|
12
|
+
@filename, @language = filename, language
|
13
|
+
end
|
14
|
+
|
15
|
+
def extractable?(development_language)
|
16
|
+
lproj_part = File.lproj_part(@filename)
|
17
|
+
(!lproj_part.nil?) && lproj_part == "#{development_language}.lproj"
|
18
|
+
end
|
19
|
+
|
20
|
+
def dirname
|
21
|
+
File.dirname(@filename)
|
22
|
+
end
|
23
|
+
|
24
|
+
def basename
|
25
|
+
File.basename(File.split(@filename)[1], '.xib')
|
26
|
+
end
|
27
|
+
|
28
|
+
def resourced?
|
29
|
+
!File.lproj_part(@filename).nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def resource!
|
33
|
+
raise "The XIB is already in a resource folder: #{@filename}" unless File.lproj_part(@filename).nil?
|
34
|
+
mv(File.localized(@filename, @language))
|
35
|
+
end
|
36
|
+
|
37
|
+
def mv(new_filename)
|
38
|
+
FileUtils.mkdir_p(File.dirname(new_filename))
|
39
|
+
FileUtils.mv(@filename, new_filename)
|
40
|
+
@filename = new_filename
|
41
|
+
end
|
42
|
+
|
43
|
+
def strings?
|
44
|
+
!strings.empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def strings
|
48
|
+
Babelyoda::Ibtool.extract_strings(@filename, @language)
|
49
|
+
end
|
50
|
+
|
51
|
+
def localize(language, scm)
|
52
|
+
puts "Localizing #{filename} => #{File.localized(filename, language)}..."
|
53
|
+
assert_localization_target(language)
|
54
|
+
strings_fn = strings_filename(language)
|
55
|
+
$logger.error "No strings file found: #{strings_fn}" unless File.exist?(strings_fn)
|
56
|
+
Babelyoda::Ibtool.localize(filename, File.localized(filename, language), strings_fn)
|
57
|
+
end
|
58
|
+
|
59
|
+
def localize_incremental(language, scm)
|
60
|
+
assert_localization_target(language)
|
61
|
+
unless has_incremental_resources?(language, scm)
|
62
|
+
localize(language, scm)
|
63
|
+
else
|
64
|
+
puts "Incrementally localizing #{filename} => #{File.localized(filename, language)}..."
|
65
|
+
strings_fn = strings_filename(language)
|
66
|
+
$logger.error "No strings file found: #{strings_fn}" unless File.exist?(strings_fn)
|
67
|
+
|
68
|
+
scm.fetch_versions!(filename, File.localized(filename, language)) do |filenames|
|
69
|
+
Babelyoda::Ibtool.localize_incrementally(filename, File.localized(filename, language), strings_fn, filenames[0], filenames[1])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def strings_filename(language = nil)
|
75
|
+
language ? File.localized(File.join(dirname, "#{basename}.strings"), language) : File.join(dirname, "#{basename}.strings")
|
76
|
+
end
|
77
|
+
|
78
|
+
def import_strings(scm)
|
79
|
+
Babelyoda::Ibtool.import_strings(filename, strings_filename)
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def has_incremental_resources?(language, scm)
|
85
|
+
scm.version_exist?(filename) && scm.version_exist?(File.localized(filename, language))
|
86
|
+
end
|
87
|
+
|
88
|
+
def assert_localization_target(language)
|
89
|
+
raise "Can't localize a XIB file that has not been put into an .lproj folder: #{filename}" unless resourced?
|
90
|
+
raise "Can't localize #{@language} to #{language} for: #{filename}" if @language == language
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
data/lib/babelyoda.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
BABELYODA_PATH = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
2
|
+
|
3
|
+
require 'awesome_print'
|
4
|
+
|
5
|
+
require_relative 'babelyoda/genstrings'
|
6
|
+
require_relative 'babelyoda/git'
|
7
|
+
require_relative 'babelyoda/ibtool'
|
8
|
+
require_relative 'babelyoda/keyset'
|
9
|
+
require_relative 'babelyoda/localization_key'
|
10
|
+
require_relative 'babelyoda/localization_value'
|
11
|
+
require_relative 'babelyoda/rake'
|
12
|
+
require_relative 'babelyoda/specification'
|
13
|
+
require_relative 'babelyoda/tanker'
|
14
|
+
require_relative 'babelyoda/xib'
|
15
|
+
|
16
|
+
desc "Do a full localization cycle: push new strings, get translations and merge them"
|
17
|
+
task :babelyoda => ['babelyoda:push', 'babelyoda:pull'] do
|
18
|
+
end
|
19
|
+
|
20
|
+
namespace :babelyoda do
|
21
|
+
|
22
|
+
file 'Babelfile' do
|
23
|
+
Babelyoda::Specification.generate_default_babelfile
|
24
|
+
end
|
25
|
+
|
26
|
+
desc "Create a basic bootstrap Babelfile"
|
27
|
+
task :init => 'Babelfile' do
|
28
|
+
end
|
29
|
+
|
30
|
+
Babelyoda::Rake.spec do |spec|
|
31
|
+
|
32
|
+
desc "Extract strings from sources"
|
33
|
+
task :extract_strings do
|
34
|
+
puts "Extracting strings from sources..."
|
35
|
+
dev_lang = spec.development_language
|
36
|
+
|
37
|
+
spec.scm.transaction("Extract strings from sources") do
|
38
|
+
Babelyoda::Genstrings.run(spec.source_files, dev_lang) do |keyset|
|
39
|
+
old_strings_filename = strings_filename(keyset.name, dev_lang)
|
40
|
+
old_strings = Babelyoda::Strings.new(old_strings_filename, dev_lang).read
|
41
|
+
old_strings.merge!(keyset)
|
42
|
+
old_strings.save!
|
43
|
+
puts " #{old_strings_filename}: #{old_strings.keys.size} keys"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
desc "Extract strings from XIBs"
|
49
|
+
task :extract_xib_strings do
|
50
|
+
puts "Extracting .strings from XIBs..."
|
51
|
+
spec.scm.transaction("Extract strings from XIBs") do
|
52
|
+
spec.xib_files.each do |xib_filename|
|
53
|
+
xib = Babelyoda::Xib.new(xib_filename, spec.development_language)
|
54
|
+
next unless xib.extractable?(spec.development_language)
|
55
|
+
keyset = xib.strings
|
56
|
+
puts " #{xib_filename} => #{xib.strings_filename}"
|
57
|
+
Babelyoda::Strings.save_keyset(keyset, xib.strings_filename, spec.development_language)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
desc "Extracts localizable strings into the corresponding .strings files"
|
63
|
+
task :extract => [:extract_strings, :extract_xib_strings] do
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "Create remote keysets for local keysets"
|
67
|
+
task :create_keysets => :extract do
|
68
|
+
# Create remote keysets for each local keyset if they don't exist.
|
69
|
+
puts "Creating remote keysets for local keysets..."
|
70
|
+
remote_keyset_names = spec.engine.list
|
71
|
+
spec.strings_files.each do |filename|
|
72
|
+
keyset_name = Babelyoda::Keyset.keyset_name(filename)
|
73
|
+
if remote_keyset_names.include?(keyset_name)
|
74
|
+
puts " Tanker: An existing keyset found: #{keyset_name}"
|
75
|
+
next
|
76
|
+
end
|
77
|
+
spec.engine.create(keyset_name)
|
78
|
+
puts " Tanker: Created NEW keyset: #{keyset_name}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "Drops remote keys not found in local keysets"
|
83
|
+
task :drop_orphan_keys => :create_keysets do
|
84
|
+
puts "Dropping orphan keys..."
|
85
|
+
spec.strings_files.each do |filename|
|
86
|
+
strings = Babelyoda::Strings.new(filename, spec.development_language).read!
|
87
|
+
puts " Processing keyset: #{strings.name}"
|
88
|
+
remote_keyset = spec.engine.load_keyset(strings.name)
|
89
|
+
keys_to_drop = []
|
90
|
+
remote_keyset.keys.each_value do |key|
|
91
|
+
unless strings.keys.has_key?(key.id)
|
92
|
+
keys_to_drop << key.id
|
93
|
+
puts " Found orphan key: #{key.id}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
keys_to_drop.each do |key|
|
97
|
+
remote_keyset.keys.delete(key)
|
98
|
+
end
|
99
|
+
spec.engine.replace(remote_keyset)
|
100
|
+
puts " Dropped keys: #{keys_to_drop.size}"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
desc "Pushes resources to the translators"
|
105
|
+
task :push => :drop_orphan_keys do
|
106
|
+
puts "Pushing local keys to the remote..."
|
107
|
+
spec.strings_files.each do |filename|
|
108
|
+
strings = Babelyoda::Strings.new(filename, spec.development_language).read!
|
109
|
+
puts " Processing keyset: #{strings.name}"
|
110
|
+
remote_keyset = spec.engine.load_keyset(strings.name, nil, :unapproved)
|
111
|
+
result = remote_keyset.merge!(strings, preserve: true)
|
112
|
+
remote_keyset.ensure_languages!(spec.all_languages)
|
113
|
+
spec.engine.replace(remote_keyset)
|
114
|
+
puts " New keys: #{result[:new]} Updated keys: #{result[:updated]}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
desc "Fetches remote strings and merges them down into local .string files"
|
119
|
+
task :fetch_strings do
|
120
|
+
puts "Fetching remote translations..."
|
121
|
+
spec.scm.transaction("Merge in remote translations") do
|
122
|
+
spec.strings_files.each do |filename|
|
123
|
+
keyset_name = Babelyoda::Keyset.keyset_name(filename)
|
124
|
+
remote_keyset = spec.engine.load_keyset(keyset_name, nil, :unapproved, true)
|
125
|
+
remote_keyset.drop_empty!
|
126
|
+
spec.all_languages.each do |language|
|
127
|
+
keyset_filename = strings_filename(keyset_name, language)
|
128
|
+
Babelyoda::Strings.save_keyset(remote_keyset, keyset_filename, language)
|
129
|
+
puts " #{keyset_filename}"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
desc "Incrementally localizes XIB files"
|
136
|
+
task :localize_xibs do
|
137
|
+
puts "Translating XIB files..."
|
138
|
+
|
139
|
+
spec.scm.transaction("Localize XIB files") do
|
140
|
+
spec.xib_files.each do |filename|
|
141
|
+
xib = Babelyoda::Xib.new(filename, spec.development_language)
|
142
|
+
|
143
|
+
xib.import_strings(spec.scm)
|
144
|
+
spec.localization_languages.each do |language|
|
145
|
+
xib.localize_incremental(language, spec.scm)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
spec.scm.transaction("Update XIB SHA1 version refs") do
|
151
|
+
spec.xib_files.each do |filename|
|
152
|
+
spec.scm.store_version!(filename)
|
153
|
+
spec.localization_languages.each do |language|
|
154
|
+
spec.scm.store_version!(File.localized(filename, language))
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
desc "Pull remote translations"
|
161
|
+
task :pull => [:fetch_strings, :localize_xibs] do
|
162
|
+
end
|
163
|
+
|
164
|
+
namespace :remote do
|
165
|
+
|
166
|
+
desc "List remote keysets"
|
167
|
+
task :list do
|
168
|
+
ap spec.engine.list
|
169
|
+
end
|
170
|
+
|
171
|
+
desc "Drop remote keysets in KEYSETS"
|
172
|
+
task :drop_keysets do
|
173
|
+
if ENV['KEYSETS']
|
174
|
+
keysets = ENV['KEYSETS'].split(',')
|
175
|
+
if keysets.include?('*')
|
176
|
+
keysets = spec.engine.list
|
177
|
+
puts "Dropping ALL keysets: #{keysets}"
|
178
|
+
else
|
179
|
+
puts "Dropping keysets: #{keysets}"
|
180
|
+
end
|
181
|
+
keysets.each do |keyset_name|
|
182
|
+
puts " Dropping: #{keyset_name}"
|
183
|
+
keyset = Babelyoda::Keyset.new(keyset_name)
|
184
|
+
key = Babelyoda::LocalizationKey.new("Dummy", "Dummy")
|
185
|
+
value = Babelyoda::LocalizationValue.new(:en, "Dummy")
|
186
|
+
key << value
|
187
|
+
keyset.merge_key!(key)
|
188
|
+
spec.engine.replace(keyset)
|
189
|
+
end
|
190
|
+
puts "All done!"
|
191
|
+
else
|
192
|
+
puts "Please provide keyset names to drop in the KEYSET environment variable. Separate by commas. Use * for ALL."
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def strings_filename(keyset_name, lang)
|
202
|
+
if keyset_name.match(/\//)
|
203
|
+
File.join(File.dirname(keyset_name), "#{lang}.lproj", "#{File.basename(keyset_name)}.strings")
|
204
|
+
else
|
205
|
+
File.join("#{lang}.lproj", "#{keyset_name}.strings")
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Babelyoda::Specification.new do |s|
|
2
|
+
s.name = 'YOUR PROJECT NAME HERE'
|
3
|
+
s.development_language = :en
|
4
|
+
s.localization_languages = [:ru, :uk, :tr]
|
5
|
+
s.engine = Babelyoda::Tanker.new do |t|
|
6
|
+
t.token = 'FIX: TANKER TOKEN HERE'
|
7
|
+
t.project_id = 'FIX: TANKER PROJECT ID HERE'
|
8
|
+
t.endpoint = 'FIX: TANKER END POINT HERE'
|
9
|
+
end
|
10
|
+
s.scm = Babelyoda::Git.new
|
11
|
+
s.source_files = FileList['Classes/**/*.{m,mm,h}']
|
12
|
+
s.resources_folder = 'Resources'
|
13
|
+
s.xib_files = FileList['{Classes,Resources}/**/en.lproj/*.{xib}']
|
14
|
+
s.strings_files = FileList['{Classes,Resources}/**/en.lproj/*.{strings}']
|
15
|
+
end
|