n42translation 0.1.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.
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