xlocalize 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/.gitignore +14 -0
- data/lib/xlocalize/executor.rb +119 -42
- data/lib/xlocalize/version.rb +1 -1
- data/lib/xlocalize/webtranslateit.rb +55 -12
- data/xlocalize.gemspec +9 -6
- metadata +54 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 792155074986084e3e7e83a6eb58336bb00c5534
|
4
|
+
data.tar.gz: 7a2d092e9300a12d26c88493813ea5dee23268c3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 736409b41ba7b868a91c953c8d12e1aa663c9b973a16ba13e165911659533df17d277f0556687ce13d8acd78f7152429a3bee59e309b822ddbe70e1cb9601441
|
7
|
+
data.tar.gz: 34b303e10d3990a19101b1da6d4fa8561bf00a7f64b62fa4f4f1a45c6136e1539fb2ca468c00b938d36d4260645a200b7d24af56d46371d1d6babf18a03d0910
|
data/.gitignore
CHANGED
@@ -8,3 +8,17 @@
|
|
8
8
|
/pkg/
|
9
9
|
/spec/reports/
|
10
10
|
/tmp/
|
11
|
+
|
12
|
+
# iOS specific ignores, used in sample projects inside the repo
|
13
|
+
*.pbxuser
|
14
|
+
!default.pbxuser
|
15
|
+
*.mode1v3
|
16
|
+
!default.mode1v3
|
17
|
+
*.mode2v3
|
18
|
+
!default.mode2v3
|
19
|
+
*.perspectivev3
|
20
|
+
!default.perspectivev3
|
21
|
+
xcuserdata/
|
22
|
+
*.moved-aside
|
23
|
+
*.xccheckout
|
24
|
+
*.xcscmblueprint
|
data/lib/xlocalize/executor.rb
CHANGED
@@ -1,10 +1,17 @@
|
|
1
1
|
require 'xlocalize/webtranslateit'
|
2
2
|
require 'colorize'
|
3
3
|
require 'nokogiri'
|
4
|
+
require 'plist'
|
5
|
+
require 'yaml'
|
6
|
+
require 'pathname'
|
4
7
|
|
5
8
|
module Xlocalize
|
6
9
|
class Executor
|
7
10
|
|
11
|
+
def plurals_file_name(locale)
|
12
|
+
return locale_file_name(locale) << '_plurals.yml'
|
13
|
+
end
|
14
|
+
|
8
15
|
def locale_file_name(locale)
|
9
16
|
return "#{locale}.xliff"
|
10
17
|
end
|
@@ -12,22 +19,34 @@ module Xlocalize
|
|
12
19
|
def export_master(wti, project, target, excl_prefix, master_lang)
|
13
20
|
master_file_name = locale_file_name(master_lang)
|
14
21
|
|
15
|
-
|
16
|
-
|
22
|
+
# hacky way to finish xcodebuild -exportLocalizations script, because
|
23
|
+
# since Xcode7.3 & OS X Sierra script hangs even though it produces
|
24
|
+
# xliff output
|
25
|
+
# http://www.openradar.me/25857436
|
26
|
+
File.delete(master_file_name) if File.exist?(master_file_name)
|
27
|
+
system "xcodebuild -exportLocalizations -localizationPath ./ -project #{project} & sleep 0"
|
28
|
+
while !File.exist?(master_file_name) do
|
29
|
+
sleep(1)
|
30
|
+
end
|
31
|
+
|
32
|
+
purelyze(master_lang, target, excl_prefix, project)
|
17
33
|
|
18
34
|
# Pushing master file to WebtranslateIt
|
19
35
|
begin
|
20
36
|
puts "Uploading master file to WebtranslateIt"
|
21
|
-
File.open(master_file_name,
|
22
|
-
|
23
|
-
|
24
|
-
|
37
|
+
file = File.open(master_file_name, 'r')
|
38
|
+
plurals_file = File.open(plurals_file_name(master_lang), 'r')
|
39
|
+
wti.push_master(file, plurals_file)
|
40
|
+
puts "Done.".green
|
25
41
|
rescue => err
|
26
42
|
puts err.to_s.red
|
43
|
+
ensure
|
44
|
+
file.close unless file.nil?
|
27
45
|
end if !wti.nil?
|
28
46
|
end
|
29
47
|
|
30
|
-
def purelyze(
|
48
|
+
def purelyze(locale, target, excl_prefix, project)
|
49
|
+
locale_file_name = locale_file_name(locale)
|
31
50
|
target_prefix = "#{target}/"
|
32
51
|
doc = Nokogiri::XML(open(locale_file_name))
|
33
52
|
|
@@ -42,62 +61,120 @@ module Xlocalize
|
|
42
61
|
node.parent.remove if node.content.start_with?(excl_prefix)
|
43
62
|
}
|
44
63
|
|
64
|
+
puts "Filtering plurals"
|
65
|
+
plurals = {}
|
66
|
+
doc.xpath("//xmlns:file").each { |node|
|
67
|
+
fname = node["original"]
|
68
|
+
next if !fname.end_with?(".strings")
|
69
|
+
fname_stringsdict = fname << 'dict'
|
70
|
+
file_full_path = Pathname.new(project).split.first.to_s << '/' << fname_stringsdict
|
71
|
+
next if !File.exist?(file_full_path)
|
72
|
+
|
73
|
+
Plist::parse_xml(file_full_path).each do |key, val|
|
74
|
+
values = val["value"]
|
75
|
+
transl = values.select { |k, v| ['zero', 'one', 'few', 'other'].include?(k) }
|
76
|
+
plurals[fname_stringsdict] = {key => transl}
|
77
|
+
sel = 'body > trans-unit[id="' << key << '"]'
|
78
|
+
node.css(sel).remove
|
79
|
+
end
|
80
|
+
}
|
81
|
+
|
45
82
|
puts "Removing all files having no trans-unit elements after removal"
|
46
83
|
doc.xpath("//xmlns:body").each { |node|
|
47
84
|
node.parent.remove if node.elements.count == 0
|
48
85
|
}
|
49
86
|
|
50
87
|
puts "Writing modified XLIFF file to #{locale_file_name}"
|
51
|
-
File.open(locale_file_name,
|
88
|
+
File.open(locale_file_name, 'w') { |f| f.write(doc.to_xml) }
|
89
|
+
|
90
|
+
if !plurals.empty?
|
91
|
+
puts "Writing plurals to plurals YAML file"
|
92
|
+
File.open(plurals_file_name(locale), 'w') { |f| f.write({locale => plurals}.to_yaml) }
|
93
|
+
end
|
52
94
|
end
|
53
95
|
|
54
96
|
def download(wti, locales)
|
55
97
|
begin
|
56
98
|
locales.each do |locale|
|
57
|
-
puts "Downloading
|
99
|
+
puts "Downloading translations for #{locale}"
|
100
|
+
translations = wti.pull(locale)
|
101
|
+
|
58
102
|
File.open("#{locale}.xliff", "w") {|file|
|
59
|
-
|
60
|
-
puts "Done.".green
|
103
|
+
file.write(translations['xliff'])
|
104
|
+
puts "Done saving xliff.".green
|
61
105
|
}
|
106
|
+
|
107
|
+
if !translations['plurals'].nil?
|
108
|
+
File.open("#{locale}_plurals.yaml", "w") {|file|
|
109
|
+
file.write(translations['plurals'])
|
110
|
+
puts "Done saving plurals.".green
|
111
|
+
}
|
112
|
+
end
|
62
113
|
end
|
63
114
|
rescue => err
|
64
115
|
puts err.to_s.red
|
65
116
|
end
|
66
117
|
end
|
67
118
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
119
|
+
def import_xliff(locale)
|
120
|
+
doc = Nokogiri::XML(open("#{locale}.xliff"))
|
121
|
+
|
122
|
+
doc.xpath("//xmlns:file").each do |node|
|
123
|
+
file_name = node["original"]
|
124
|
+
parts = file_name.split('/')
|
125
|
+
name = ""
|
126
|
+
parts.each_with_index do |part, idx|
|
127
|
+
name += "/" if idx > 0
|
128
|
+
if part.end_with?(".lproj")
|
129
|
+
name += "#{locale}.lproj"
|
130
|
+
elsif idx+1 == parts.count
|
131
|
+
# TODO: join all parts till the last '.'
|
132
|
+
name += "#{part.split('.')[0]}.strings"
|
133
|
+
else
|
134
|
+
name += part
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
File.open(name, "w") do |file|
|
139
|
+
(node > "body > trans-unit").each do |trans_unit|
|
140
|
+
key = trans_unit["id"]
|
141
|
+
target = (trans_unit > "target").text
|
142
|
+
note = (trans_unit > "note").text
|
143
|
+
note = "(No Commment)" if note.length <= 0
|
144
|
+
|
145
|
+
file.write "/* #{note} */\n"
|
146
|
+
file.write "\"#{key}\" = #{target.inspect};\n\n"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def import_plurals_if_needed(locale)
|
154
|
+
plurals_fname = "#{locale}_plurals.yaml"
|
155
|
+
return if !File.exist?(plurals_fname)
|
156
|
+
plurals_yml = YAML.load_file(plurals_fname)
|
157
|
+
plurals_yml[locale].each do |fname, trans_units|
|
158
|
+
content = {}
|
159
|
+
trans_units.each do |key, vals|
|
160
|
+
content[key] = {
|
161
|
+
"NSStringLocalizedFormatKey" => "%\#@value@",
|
162
|
+
"value" => vals.merge({
|
163
|
+
"NSStringFormatSpecTypeKey" => "NSStringPluralRuleType",
|
164
|
+
"NSStringFormatValueTypeKey" => "d"
|
165
|
+
})
|
98
166
|
}
|
167
|
+
end
|
168
|
+
File.open(fname, 'w') { |f| f.write content.to_plist }
|
169
|
+
end
|
170
|
+
end
|
99
171
|
|
100
|
-
|
172
|
+
def import(locales)
|
173
|
+
puts 'Importing translations'
|
174
|
+
locales.each do |locale|
|
175
|
+
import_xliff(locale)
|
176
|
+
import_plurals_if_needed(locale)
|
177
|
+
puts "Done #{locale}".green
|
101
178
|
end
|
102
179
|
end
|
103
180
|
end
|
data/lib/xlocalize/version.rb
CHANGED
@@ -6,7 +6,10 @@ module Xlocalize
|
|
6
6
|
class WebtranslateIt
|
7
7
|
|
8
8
|
attr_reader :http
|
9
|
-
attr_reader :key, :source_locale
|
9
|
+
attr_reader :key, :source_locale
|
10
|
+
|
11
|
+
attr_reader :xliff_file_id
|
12
|
+
attr_reader :plurals_file_id
|
10
13
|
|
11
14
|
def initialize(key)
|
12
15
|
@key = key
|
@@ -18,17 +21,18 @@ module Xlocalize
|
|
18
21
|
project = JSON.parse(response.body)["project"]
|
19
22
|
@source_locale = project["source_locale"]["code"]
|
20
23
|
project["project_files"].each {|file|
|
21
|
-
if file["locale_code"]
|
22
|
-
|
23
|
-
|
24
|
-
end
|
24
|
+
next if file["locale_code"] != @source_locale
|
25
|
+
@xliff_file_id = file["id"] if file['name'].end_with? '.xliff'
|
26
|
+
@plurals_file_id = file["id"] if file['name'] == 'plurals.yaml'
|
25
27
|
}
|
26
|
-
raise "Could not find master file for source locale #{@source_locale}" if @
|
28
|
+
raise "Could not find master xliff file for source locale #{@source_locale}" if @xliff_file_id.nil?
|
27
29
|
}
|
28
30
|
end
|
29
31
|
|
30
|
-
def push_master(file, override = true)
|
31
|
-
|
32
|
+
def push_master(file, plurals_file, override = true)
|
33
|
+
puts 'Updating xliff file'
|
34
|
+
# uploding master xliff file
|
35
|
+
request = Net::HTTP::Put::Multipart.new("/api/projects/#{@key}/files/#{@xliff_file_id}/locales/#{@source_locale}", {
|
32
36
|
"file" => UploadIO.new(file, "text/plain", file.path),
|
33
37
|
"merge" => !override,
|
34
38
|
"ignore_missing" => true,
|
@@ -40,12 +44,51 @@ module Xlocalize
|
|
40
44
|
raise JSON.parse(res.body)["error"]
|
41
45
|
end
|
42
46
|
}
|
47
|
+
|
48
|
+
if @plurals_file_id.nil?
|
49
|
+
puts 'Creating plurals file'
|
50
|
+
# /api/projects/:project_token/files [POST]
|
51
|
+
request = Net::HTTP::Post::Multipart.new("/api/projects/#{@key}/files", {
|
52
|
+
"file" => UploadIO.new(plurals_file, "text/plain", plurals_file.path),
|
53
|
+
"name" => "plurals.yaml",
|
54
|
+
"low_priority" => false
|
55
|
+
})
|
56
|
+
@http.request(request) { |res|
|
57
|
+
if !res.code.to_i.between?(200, 300)
|
58
|
+
raise JSON.parse(res.body)["error"]
|
59
|
+
end
|
60
|
+
}
|
61
|
+
else
|
62
|
+
puts 'Updating plurals file'
|
63
|
+
# /api/projects/:project_token/files/:master_project_file_id/locales/:locale_code [PUT]
|
64
|
+
request = Net::HTTP::Put::Multipart.new("/api/projects/#{@key}/files/#{@plurals_file_id}/locales/#{@source_locale}", {
|
65
|
+
"file" => UploadIO.new(plurals_file, "text/plain", plurals_file.path),
|
66
|
+
"merge" => !override,
|
67
|
+
"ignore_missing" => true,
|
68
|
+
"label" => "",
|
69
|
+
"low_priority" => false
|
70
|
+
})
|
71
|
+
@http.request(request) { |res|
|
72
|
+
if !res.code.to_i.between?(200, 300)
|
73
|
+
raise JSON.parse(res.body)["error"]
|
74
|
+
end
|
75
|
+
}
|
76
|
+
end
|
43
77
|
end
|
44
78
|
|
45
|
-
def pull(
|
46
|
-
|
47
|
-
|
48
|
-
}
|
79
|
+
def pull(locale)
|
80
|
+
# downloading master xliff file
|
81
|
+
data = {}
|
82
|
+
res = http.request(Net::HTTP::Get.new("/api/projects/#{@key}/files/#{@xliff_file_id}/locales/#{locale}"))
|
83
|
+
raise JSON.parse(res.body)["error"] if !res.code.to_i.between?(200, 300)
|
84
|
+
data['xliff'] = res.body
|
85
|
+
# downloading master plurals file
|
86
|
+
if !@plurals_file_id.nil?
|
87
|
+
res = http.request(Net::HTTP::Get.new("/api/projects/#{@key}/files/#{@plurals_file_id}/locales/#{locale}"))
|
88
|
+
raise JSON.parse(res.body)["error"] if !res.code.to_i.between?(200, 300)
|
89
|
+
data['plurals'] = res.body
|
90
|
+
end
|
91
|
+
return data
|
49
92
|
end
|
50
93
|
|
51
94
|
end
|
data/xlocalize.gemspec
CHANGED
@@ -19,10 +19,13 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_runtime_dependency 'nokogiri'
|
23
|
-
spec.add_runtime_dependency 'commander'
|
24
|
-
spec.add_runtime_dependency 'colorize', '~> 0'
|
25
|
-
|
26
|
-
spec.
|
27
|
-
|
22
|
+
spec.add_runtime_dependency 'nokogiri', '~> 1.6'
|
23
|
+
spec.add_runtime_dependency 'commander', '~> 4.4'
|
24
|
+
spec.add_runtime_dependency 'colorize', '~> 0.8'
|
25
|
+
spec.add_runtime_dependency 'plist', '~> 3.2'
|
26
|
+
spec.add_runtime_dependency 'multipart-post', '~> 2.0'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.10'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
28
31
|
end
|
metadata
CHANGED
@@ -1,57 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xlocalize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Viktoras Laukevičius
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.6'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: commander
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '4.4'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '4.4'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: colorize
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
47
|
+
version: '0.8'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
54
|
+
version: '0.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: plist
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: multipart-post
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: bundler
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +108,20 @@ dependencies:
|
|
80
108
|
- - "~>"
|
81
109
|
- !ruby/object:Gem::Version
|
82
110
|
version: '10.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.5'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '3.5'
|
83
125
|
description:
|
84
126
|
email:
|
85
127
|
- viktoras.laukevicius@yahoo.com
|