n42translation 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ce8725b1932a987f51bba7099ce82a312bb9b821
4
+ data.tar.gz: 51a8089a033c2df52d87572f35562d87cfb9add8
5
+ SHA512:
6
+ metadata.gz: bba10b71bb89113f79ec7c4b247083c758fa73cb7ae50a828a19533ec76c908d8afce7ffd11490029508dd279668ecc28fe3c9c02d3a2a40564a13aff85a1ea4
7
+ data.tar.gz: 67fe97b54058148d71d9226808de9c9239ba4ff8816c398d4c246cf46d3186bdd29afa6b2308c63f7fbcd58b5876ffd1f028e04e2edfdf4ca0d4bc7f77856e42
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ locales/Android
3
+ locales/csv
4
+ locales/iOS
5
+ locales/Rails
6
+ locales/builds
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+
4
+ # Specify your gem's dependencies in gem_name.gemspec
5
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,39 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ n42translation (0.0.9)
5
+ activesupport
6
+ builder
7
+ thor
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.2.1)
13
+ i18n (~> 0.7)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.3, >= 0.3.4)
17
+ tzinfo (~> 1.1)
18
+ builder (3.2.2)
19
+ i18n (0.7.0)
20
+ json (1.8.2)
21
+ minitest (5.5.1)
22
+ rubyzip (1.1.7)
23
+ thor (0.19.1)
24
+ thread_safe (0.3.5)
25
+ tzinfo (1.2.2)
26
+ thread_safe (~> 0.1)
27
+ write_xlsx (0.83.0)
28
+ rubyzip (>= 1.0.0)
29
+ zip-zip
30
+ zip-zip (0.3)
31
+ rubyzip (>= 1.0.0)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (>= 1.0.0)
38
+ n42translation!
39
+ write_xlsx
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Number42
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/Readme.md ADDED
@@ -0,0 +1,31 @@
1
+ # n42translation Tool
2
+ Creates locale files for iOS, Android and Rails from common source files.
3
+
4
+ ## Source Files
5
+ ### Config File
6
+
7
+ * n42translation-config.yml defines the default outputpaths for Android, iOS and Rails
8
+
9
+ ### Language Files
10
+
11
+ * <fileprefix>.<lang>.yml (e.g. horsch.en.yml) contains keys for all platforms
12
+ * <fileprefix>.<lang>.<target>.yml (e.g. horsch.en.android.yml) contains keys for only this target platform
13
+
14
+ ## Examples
15
+
16
+ * bundle exec n42translation build android horsch: builds the locales for android from the sources to the default folder given in the config file
17
+ * bundle exec n42translation build android horsch myAndroid: builds the locales for android from the sources to the myAndroid folder
18
+ * bundle exec n42translation build all horsch: build ios, android and rails to the default folders
19
+ * bundle exec n42translation add all horsch "path.to.my_message" "my new message": adds the key path.to.my_message with value "my new message" to the horsch.<lang>.yml files
20
+ * bundle exec n42translation add all ios "path.to.my_message" "my new message": adds the key path.to.my_message with value "my new message" to the horsch.<lang>.ios.yml files
21
+
22
+
23
+ ### HOW TO INITILAIZE A PROJECT
24
+
25
+ * you first have to run the initilizer script:
26
+ bundle exec n42translation init <project_name> <target> <languages>
27
+
28
+ * so lets say if you wanted to initialize a project with name stack:
29
+ bundle exec n42translation init stacks all "en,de"
30
+
31
+ * this creates a config file, and the necessary target-yaml files for the languages en and de
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'n42translation/cli'
3
+ N42translation::CLI.start
@@ -0,0 +1,3 @@
1
+ module N42translation
2
+ # Your code goes here...
3
+ end
@@ -0,0 +1,266 @@
1
+ require 'thor'
2
+ require 'yaml'
3
+ require 'active_support'
4
+ require 'n42translation/xml'
5
+ require 'n42translation/strings'
6
+ require 'n42translation/csv_convert'
7
+ require 'n42translation/xlsx'
8
+ require 'n42translation/config'
9
+ require 'fileutils'
10
+ require 'csv'
11
+
12
+ module N42translation
13
+ class CLI < Thor
14
+
15
+ desc "init <project-name> <target> <languages>", "init"
16
+ def init(project_name, target = nil, languages = nil)
17
+ config = load_config_file(project_name)
18
+
19
+ build_path = config["build_path"]
20
+ source_path = config["source_path"]
21
+
22
+ project_name = config["project_name"] if project_name.nil?
23
+
24
+ targets = []
25
+ targets = [target] if target != nil && target != ""
26
+ targets = config["targets"]["all"].split(',').map(&:lstrip).map(&:rstrip) if target === "all"
27
+ targets = config["targets"]["mobile"].split(',').map(&:lstrip).map(&:rstrip) if target === "mobile"
28
+ targets << "" # we want a platform named "all", to build from 'platform' + 'all' files
29
+
30
+ raise Thor::Error, "no build path found" if build_path.nil?
31
+ raise Thor::Error, "no locale path found" if source_path.nil?
32
+ raise Thor::Error, "no project name was specified" if project_name.nil?
33
+ raise Thor::Error, "no targets specified" if targets.nil?
34
+
35
+ # write config File
36
+ puts config.to_yaml
37
+ File.open("config.#{project_name}.yml", 'w'){|file| file.write config.to_yaml} unless File.exists?("config.#{project_name}.yml")
38
+
39
+ languages = config["languages"] if languages.nil?
40
+ langs = languages.split(',').map(&:lstrip).map(&:rstrip)
41
+ langs = (langs + get_languages(project_name)).uniq
42
+
43
+ targets.each do |_target|
44
+ build(_target, project_name, build_path) unless _target == "all"
45
+
46
+ langs.each do |lang|
47
+ filename = File.join(source_path, "#{project_name}.#{lang}.yml") if _target ===""
48
+ filename = File.join(source_path, "#{project_name}.#{lang}.#{_target}.yml") unless _target === ""
49
+ File.open(filename, 'w') { |file| file.write("---") } unless File.exists?(filename)
50
+ end
51
+ end
52
+ end
53
+
54
+ desc "build <target> <project_name> <outputfile_path> <default-language>", "builds the files for the target (all, ios, android, rails) for the project_name (e.g. horsch) to the outputfile_path (if given, they are taken from the config file otherwise)"
55
+ def build(target, project_name, outputfile_path=nil, default_language="en")
56
+ config = load_config_file(project_name)
57
+
58
+ outputfile_path = config["build_path"] if outputfile_path.nil?
59
+ raise Thor::Error, "no output path specified" if outputfile_path.nil?
60
+
61
+ source_path = config["source_path"]
62
+ raise Thor::Error, "no source path specified" if source_path.nil?
63
+
64
+ path_name = config["target_build_path_names"][target.to_s].to_s
65
+ target_build_path = File.join(outputfile_path, path_name)
66
+
67
+ case target.to_sym
68
+ when :all
69
+ self.build(:android, project_name, outputfile_path)
70
+ self.build(:ios, project_name, outputfile_path)
71
+ self.build(:rails, project_name, outputfile_path)
72
+ self.build(:csv, project_name, outputfile_path)
73
+ self.build(:xlsx, project_name, outputfile_path)
74
+ when :android
75
+ raise Thor::Error, "no build path specified for your target: #{target}" if target_build_path.nil?
76
+ build_platform(source_path, project_name, target_build_path, target, :xml)
77
+ when :ios
78
+ raise Thor::Error, "no build path specified for your target: #{target}" if target_build_path.nil?
79
+ build_platform(source_path, project_name, target_build_path, target, :strings)
80
+ when :rails
81
+ raise Thor::Error, "no build path specified for your target: #{target}" if target_build_path.nil?
82
+ build_platform(source_path, project_name, target_build_path, target, :yml)
83
+ when :csv
84
+ raise Thor::Error, "no build path specified for your target: #{target}" if target_build_path.nil?
85
+ build_csv(source_path, project_name, target_build_path, [:all, :ios, :android, :rails], default_language)
86
+ when :xlsx
87
+ raise Thor::Error, "no build path specified for your target: #{target}" if target_build_path.nil?
88
+ build_xlsx(source_path, project_name, target_build_path, [:all, :ios, :android, :rails], default_language)
89
+ when "".to_sym
90
+ # ignore the “” case
91
+ else
92
+ raise Thor::Error, "unknown target: #{target}"
93
+ end
94
+ end
95
+
96
+ desc "add <target> <project_name> <key> <value> <language>", "creates or updates the specified <value> for the <key> in the <target> in the project <project_name> for the specified <language>(default: 'en') will add a 'TODO: <value>(<language>)' to all other languages"
97
+ def add(target, project_name, key, value, language = "en")
98
+ # puts "#{target}, #{project_name}, #{key}, #{value}, #{language}"
99
+ insert_string(target, project_name, key, value, language, :override)
100
+ end
101
+
102
+ desc "update <target> <project_name> <key> <value> <language>", "updates or creates the specified <value> for the <key> in the <target> for the project <project_name>. Optional is language(default: 'en') will add to specific <language>"
103
+ def update(target, project_name, key, new_value, language="en")
104
+ insert_string(target, project_name, key, new_value, language, :update)
105
+ end
106
+
107
+
108
+
109
+ private
110
+ ## Helper
111
+
112
+ # inserts string into languages
113
+ def insert_string(target, project_name, key, value, language = "en", mode)
114
+ config = load_config_file(project_name)
115
+ source_path = config["source_path"]
116
+
117
+ get_languages(project_name).each do |lang|
118
+ if lang === language
119
+ val = value
120
+ insert_string_into_language(key, value, lang, source_path, project_name, target)
121
+ else
122
+ next if mode === :update
123
+ val = "TODO: #{value}(#{language.upcase})"
124
+ insert_string_into_language(key, val, lang, source_path, project_name, target) if mode === :override
125
+ end
126
+ end
127
+ end
128
+
129
+ def insert_string_into_language(key, value, language, source_path, project_name, target)
130
+ # hash is a string here
131
+ hash = value
132
+
133
+ key.split(".").reverse.each do |keypart|
134
+ # hash is a hash after calling this the first time
135
+ hash = {keypart => hash}
136
+ end
137
+
138
+ fileContent = yaml_for_platform_and_lang(source_path, project_name, target.to_sym, language).deep_merge(hash).to_yaml
139
+ save_with_target(fileContent, language, project_name, target)
140
+ end
141
+
142
+ def load_config_file(project_name = "default")
143
+ default_yml = N42translation::Config.default_yml
144
+ project_path = "./config.#{project_name}.yml"
145
+ project_yml = load_yaml(project_path)
146
+
147
+ return default_yml if ( project_yml.nil? || project_yml == false || project_yml.empty?)
148
+ default_yml.deep_merge(project_yml)
149
+ end
150
+
151
+ # returns the language part of filenames as array following <project_name>.<lang>.yml
152
+ def get_languages(project_name)
153
+ config = load_config_file(project_name)
154
+
155
+ source_path = config["source_path"]
156
+ raise Thor::Error, "no locale path found" if source_path.nil?
157
+
158
+ langs = Dir.glob("#{source_path}/#{project_name}.*.yml").map{|f| File.basename(f,".yml").split(".")[1]}
159
+ langs.uniq
160
+ end
161
+
162
+ def save_with_filename(content, lang, project_name, outputfile_path, method)
163
+ filename = ""
164
+ case method
165
+ when :xml
166
+ filename = "#{outputfile_path}/values-#{lang}/strings-generated.xml"
167
+ when :strings
168
+ filename = "#{outputfile_path}/#{lang}.lproj/Localizable.strings"
169
+ when :yml
170
+ filename = "#{outputfile_path}/#{project_name}.#{lang}.yml"
171
+ end
172
+
173
+ FileUtils.mkdir_p(File.dirname(filename))
174
+ File.open(filename, 'w') { |file| file.write(content) }
175
+ end
176
+
177
+ def save_with_target(content, lang, project_name, target)
178
+ filename = "#{project_name}.#{lang}.yml" if target.eql? "all"
179
+ filename = "#{project_name}.#{lang}.#{target}.yml" unless target.eql? "all"
180
+
181
+ FileUtils.mkdir_p(File.dirname(filename))
182
+ File.open(filename, 'w') { |file| file.write(content) }
183
+ end
184
+
185
+ def load_yaml(file_path)
186
+ if File.exist?(file_path)
187
+ YAML.load_file(file_path) || {}
188
+ else
189
+ return YAML.load("---")
190
+ end
191
+ end
192
+
193
+ def load_merged_yaml_for_platform(source_path, project_name, language, platform)
194
+ yaml = load_yaml(File.join(source_path,"#{project_name}.#{language}.yml"))
195
+ target_yaml = load_yaml(File.join(source_path,"#{project_name}.#{language}.#{platform.to_s}.yml"))
196
+ return yaml.deep_merge(target_yaml) if !target_yaml.nil? || target_yaml === false
197
+ yaml
198
+ end
199
+
200
+ ## Builder
201
+ def build_platform(source_path, project_name, outputfile_path, platform, method)
202
+ get_languages(project_name).each do |lang|
203
+ yaml = load_merged_yaml_for_platform(source_path, project_name, lang, platform)
204
+ fileContent = ""
205
+ fileContent = N42translation::XML.createXML(join_hash_keys(yaml, "_")).target! if method == :xml
206
+ fileContent = N42translation::Strings.createStrings(join_hash_keys(yaml, ".")).join("\n") if method == :strings
207
+ fileContent = yaml.to_yaml if method == :yml
208
+ save_with_filename(fileContent, lang, project_name, outputfile_path, method)
209
+ end
210
+ end
211
+
212
+ def build_csv(source_path, project_name, outputfile_path, platforms, default_language)
213
+ languages = get_languages(project_name)
214
+ language_yamls = {}
215
+ languages.each do |language|
216
+ language_yamls["#{language}"] = platforms.map {|platform| yaml_for_platform_and_lang(source_path, project_name, platform, language) }.reduce({}, :merge)
217
+ end
218
+
219
+ csv_data = N42translation::CSVConvert.createCSV(language_yamls.values.map{|yml| join_hash_keys(yml,'.')}, languages, join_hash_keys(language_yamls[default_language],'.'), default_language)
220
+
221
+ filename = File.join(outputfile_path,'csv',"#{project_name}.csv")
222
+ FileUtils.mkpath(File.dirname(filename))
223
+ File.open(filename, "w") {|f| f.write(csv_data.inject([]) { |csv, row| csv << CSV.generate_line(row) }.join(""))}
224
+ end
225
+
226
+ def build_xlsx(source_path, project_name, outputfile_path, platforms, default_language)
227
+ languages = get_languages(project_name)
228
+ language_yamls = {}
229
+ languages.each do |language|
230
+ language_yamls["#{language}"] = platforms.map {|platform| yaml_for_platform_and_lang(source_path, project_name, platform, language) }.reduce({}, :merge)
231
+ end
232
+
233
+ csv_data = N42translation::CSVConvert.createCSV(language_yamls.values.map{|yml| join_hash_keys(yml,'.')}, languages, join_hash_keys(language_yamls[default_language],'.'), default_language)
234
+
235
+ filename = File.join(outputfile_path,'xlsx',"#{project_name}.xlsx")
236
+ FileUtils.mkpath(File.dirname(filename))
237
+
238
+ N42translation::XLSX.create(csv_data, filename, project_name)
239
+ end
240
+
241
+
242
+ def yaml_for_platform_and_lang(source_path, project_name, platform, language)
243
+ if platform == :all
244
+ load_yaml(File.join(source_path,"#{project_name}.#{language}.yml")).to_h
245
+ else
246
+ load_yaml(File.join(source_path,"#{project_name}.#{language}.#{platform.to_s}.yml")).to_h
247
+ end
248
+ end
249
+
250
+
251
+ ## Hash Helper
252
+
253
+ # flatten the hash, ["a" => ["b" => "c"]] becomes [["a", "b"]=>"c"]
254
+ def flat_hash(h,f=[],g={})
255
+ return g.update({ f=>h }) unless h.is_a? Hash
256
+ h.each { |k,r| flat_hash(r,f+[k],g) }
257
+ g
258
+ end
259
+
260
+ # flatten and jon the hash keys, [["a", "b"]=>"c"] becomes ["a.b"=>"c"]
261
+ def join_hash_keys(hash, joiner)
262
+ Hash[flat_hash(hash).map {|k, v| [k.join(joiner), v] }]
263
+ end
264
+
265
+ end
266
+ end
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+
3
+ module N42translation
4
+ class Config
5
+ def self.default_yml
6
+ YAML.load('
7
+ build_path: ./builds
8
+ source_path: ./
9
+ project_name: n42translation_project
10
+ languages: de,en
11
+ targets:
12
+ all: ios,android,rails,csv,xlsx
13
+ mobile: ios,android
14
+ target_build_path_names:
15
+ android: Android
16
+ ios: iOS
17
+ rails: Rails
18
+ csv: CSV
19
+ xlsx: EXCEL
20
+ ')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ require 'yaml'
2
+
3
+ module N42translation
4
+ class CSVConvert
5
+ def self.createCSV(ymls, langs, default_yml, default_language)
6
+
7
+ keys = ymls.map{|yml| yml.keys }.flatten.uniq
8
+
9
+ rows = []
10
+ rows << ["key",langs].flatten
11
+
12
+ keys.each do |key|
13
+ rows << [key, get_values_from_key(ymls, key, default_yml, default_language)].flatten
14
+ end
15
+
16
+ rows
17
+ end
18
+
19
+ private
20
+ def self.get_values_from_key(ymls, key, default_yml, default_language)
21
+ ymls.map do |yml|
22
+ val = yml[key.to_s]
23
+ if yml[key.to_s].nil?
24
+ default_val = default_yml[key.to_s]
25
+ if default_val.nil?
26
+ "TODO: "
27
+ else
28
+ "TODO: #{default_val}(#{default_language.to_s.upcase})"
29
+ end
30
+ else
31
+ val
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end