fastlane-plugin-google_sheet_localize 0.1.20 → 0.1.51
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d17878398029568feb9ee6bd27af8636db4f8210f52c2a08dd07c90a4892586b
|
4
|
+
data.tar.gz: 1dc64c0e36953f70154bb5c191695ea2fafa918d45947ff3bafd57f28e575bbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4e683cd4c804e51b9b8617368afeeed0d2c19373d7c02fd7d659691d9193a11500632043b7e1077dd7b1ef4c6736262c58d996b35466cfd815c67e1410d22289
|
7
|
+
data.tar.gz: 616c2f44afe6521c56900e861594767e906965ed7e7cf676210ef5a57d03fc0e5b346494df3c1001f40dc422aceb85495bac686ef3f60ed88d996f2365d58f75
|
data/README.md
CHANGED
@@ -14,30 +14,13 @@ fastlane add_plugin google_sheet_localize
|
|
14
14
|
|
15
15
|
Creates .strings files for iOS and strings.xml files for Android
|
16
16
|
|
17
|
-
to
|
18
|
-
|
19
|
-
Google Drive access token:
|
20
|
-
https://medium.com/@osanda.deshan/getting-google-oauth-access-token-using-google-apis-18b2ba11a11a
|
21
|
-
|
22
|
-
* The language_titles (the columns which should be exported)
|
23
|
-
* The default_language (If a string is not present in a specific language, this is the fallback)
|
24
|
-
* The base_language (The language which is placed in the base values folder)
|
17
|
+
**Note to author:** Add a more detailed description about this plugin here. If your plugin contains multiple actions, make sure to mention them here.
|
25
18
|
|
26
19
|
## Example
|
27
20
|
|
28
|
-
|
29
|
-
lane :localize do
|
30
|
-
google_sheet_localize(service_account_path: "./fastlane/google_drive_credentials.json",
|
31
|
-
sheet_id: "sheet id",
|
32
|
-
platform: "ios",
|
33
|
-
tabs: ["3TV"], #array of tab titles in google sheet
|
34
|
-
localization_path: "./Kit/TVKit",
|
35
|
-
language_titles: ["de", "en"], #language titles in google sheet
|
36
|
-
default_language: "de", #default language for google sheet
|
37
|
-
base_language: "en") #ios: Base.lproj android: values
|
38
|
-
end
|
39
|
-
```
|
21
|
+
Check out the [example `Fastfile`](fastlane/Fastfile) to see how to use this plugin. Try it by cloning the repo, running `fastlane install_plugins` and `bundle exec fastlane test`.
|
40
22
|
|
23
|
+
**Note to author:** Please set up a sample project to make it easy for users to explore what your plugin does. Provide everything that is necessary to try out the plugin in this project (including a sample Xcode/Android project if necessary)
|
41
24
|
|
42
25
|
## Run tests for this plugin
|
43
26
|
|
@@ -13,43 +13,41 @@ module Fastlane
|
|
13
13
|
tabs = params[:tabs]
|
14
14
|
platform = params[:platform]
|
15
15
|
path = params[:localization_path]
|
16
|
-
language_titles = params[:language_titles]
|
17
|
-
default_language = params[:default_language]
|
18
|
-
base_language = params[:base_language]
|
19
16
|
|
20
17
|
spreadsheet = session.spreadsheet_by_url(spreadsheet_id)
|
18
|
+
|
19
|
+
# Get the first worksheet
|
21
20
|
worksheet = spreadsheet.worksheets.first
|
22
21
|
|
23
|
-
|
22
|
+
languages = self.numberOfLanguages(worksheet)
|
24
23
|
|
25
|
-
|
24
|
+
result = []
|
26
25
|
|
26
|
+
for i in 2..1+languages
|
27
27
|
title = worksheet.rows[0][i]
|
28
28
|
|
29
|
-
|
29
|
+
language = {
|
30
|
+
'language' => title,
|
31
|
+
'items' => []
|
32
|
+
}
|
30
33
|
|
31
|
-
|
32
|
-
'language' => title,
|
33
|
-
'items' => []
|
34
|
-
}
|
34
|
+
filterdWorksheets = []
|
35
35
|
|
36
|
-
|
36
|
+
if tabs.count == 0
|
37
|
+
filterdWorksheets = spreadsheet.worksheets
|
38
|
+
else
|
39
|
+
filterdWorksheets = spreadsheet.worksheets.select { |item| tabs.include?(item.title) }
|
40
|
+
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
42
|
+
filterdWorksheets.each { |worksheet|
|
43
|
+
contentRows = worksheet.rows.drop(1)
|
44
|
+
language['items'].concat(self.generateJSONObject(contentRows, i))
|
45
|
+
}
|
43
46
|
|
44
|
-
|
45
|
-
contentRows = worksheet.rows.drop(1)
|
46
|
-
language['items'].concat(self.generateJSONObject(contentRows, i))
|
47
|
-
}
|
47
|
+
result.push(language)
|
48
48
|
|
49
|
-
result.push(language)
|
50
|
-
end
|
51
49
|
end
|
52
|
-
self.createFiles(result, platform, path
|
50
|
+
self.createFiles(result, platform, path)
|
53
51
|
end
|
54
52
|
|
55
53
|
def self.generateJSONObject(contentRows, index)
|
@@ -66,6 +64,12 @@ module Fastlane
|
|
66
64
|
|
67
65
|
end
|
68
66
|
|
67
|
+
def self.writeToJSONFile(languages)
|
68
|
+
File.open("output.json","w") do |f|
|
69
|
+
f.write(JSON.pretty_generate(languages))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
69
73
|
def self.generateSingleObject(row, column)
|
70
74
|
identifierIos = row[0]
|
71
75
|
identifierAndroid = row[1]
|
@@ -83,15 +87,8 @@ module Fastlane
|
|
83
87
|
|
84
88
|
end
|
85
89
|
|
86
|
-
def self.
|
87
|
-
|
88
|
-
currentIdentifier = item[identifier]
|
89
|
-
currentIdentifier != "NR" && currentIdentifier != "" && currentIdentifier != "TBD"
|
90
|
-
}
|
91
|
-
end
|
92
|
-
|
93
|
-
def self.createFiles(languages, platform, destinationPath, defaultLanguage, base_language)
|
94
|
-
self.createFilesForLanguages(languages, platform, destinationPath, defaultLanguage, base_language)
|
90
|
+
def self.createFiles(languages, platform, destinationPath)
|
91
|
+
languages.each { |language| self.createFileForLanguage(language, platform, destinationPath) }
|
95
92
|
|
96
93
|
if platform == "ios"
|
97
94
|
|
@@ -100,37 +97,27 @@ module Fastlane
|
|
100
97
|
|
101
98
|
filteredItems = languages[0]["items"].select { |item|
|
102
99
|
iosIdentifier = item['identifierIos']
|
103
|
-
iosIdentifier != "NR" && iosIdentifier != "" && !iosIdentifier.include?('//')
|
100
|
+
iosIdentifier != "NR" && iosIdentifier != "" && !iosIdentifier.include?('//')
|
104
101
|
}
|
105
102
|
|
106
103
|
File.open(swiftFilepath, "w") do |f|
|
107
|
-
f.write("import Foundation\n\n
|
104
|
+
f.write("import Foundation\n\n\npublic struct Localization {\n")
|
108
105
|
filteredItems.each { |item|
|
109
106
|
|
110
107
|
identifier = item['identifierIos']
|
111
108
|
|
112
|
-
values = identifier.dup.split("
|
109
|
+
values = identifier.dup.gsub('.', ' ').split(" ")
|
113
110
|
|
114
111
|
constantName = ""
|
115
112
|
|
116
113
|
values.each_with_index do |item, index|
|
117
114
|
if index == 0
|
118
|
-
|
119
|
-
constantName += item
|
115
|
+
constantName += item.downcase
|
120
116
|
else
|
121
|
-
|
122
|
-
constantName += item
|
117
|
+
constantName += item.capitalize
|
123
118
|
end
|
124
119
|
end
|
125
120
|
|
126
|
-
if constantName == "continue"
|
127
|
-
constantName = "`continue`"
|
128
|
-
end
|
129
|
-
|
130
|
-
if constantName == "switch"
|
131
|
-
constantName = "`switch`"
|
132
|
-
end
|
133
|
-
|
134
121
|
text = self.mapInvalidPlaceholder(item['text'])
|
135
122
|
|
136
123
|
arguments = self.findArgumentsInText(text)
|
@@ -148,191 +135,67 @@ module Fastlane
|
|
148
135
|
end
|
149
136
|
end
|
150
137
|
|
151
|
-
def self.
|
152
|
-
|
153
|
-
languages.each { |language|
|
154
|
-
|
138
|
+
def self.createFileForLanguage(language, platform, destinationPath)
|
155
139
|
if platform == "ios"
|
156
140
|
|
157
|
-
|
141
|
+
swiftFilename = "Localization.swift"
|
142
|
+
swiftFilepath = "#{destinationPath}/#{swiftFilename}"
|
158
143
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
if languageName == base_language
|
165
|
-
languageName = "Base"
|
166
|
-
end
|
144
|
+
filteredItems = language["items"].select { |item|
|
145
|
+
iosIdentifier = item['identifierIos']
|
146
|
+
iosIdentifier != "NR" && iosIdentifier != ""
|
147
|
+
}
|
167
148
|
|
168
|
-
|
169
|
-
|
170
|
-
FileUtils.mkdir_p
|
171
|
-
|
172
|
-
|
173
|
-
filteredItems.each_with_index { |item, index|
|
149
|
+
filename = "Localizable.strings"
|
150
|
+
filepath = "#{destinationPath}/#{language['language']}.lproj/#{filename}"
|
151
|
+
FileUtils.mkdir_p language['language']
|
152
|
+
File.open(filepath, "w") do |f|
|
153
|
+
filteredItems.each { |item|
|
174
154
|
|
175
155
|
text = self.mapInvalidPlaceholder(item['text'])
|
176
156
|
comment = item['comment']
|
177
157
|
identifier = item['identifierIos']
|
178
158
|
|
179
159
|
line = ""
|
160
|
+
|
180
161
|
if identifier.include?('//')
|
181
162
|
line = "\n\n#{identifier}\n"
|
182
163
|
else
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
default_language_object = self.filterUnusedRows(default_language_object,'identifierIos')
|
189
|
-
|
190
|
-
defaultLanguageText = default_language_object[index]['text']
|
191
|
-
puts "found empty text for:\n\tidentifier: #{identifier}\n\tlanguage:#{language['language']}\n\treplacing it with: #{defaultLanguageText}"
|
192
|
-
text = self.mapInvalidPlaceholder(defaultLanguageText)
|
193
|
-
end
|
194
|
-
|
195
|
-
line = "\"#{identifier}\" = \"#{text}\";"
|
196
|
-
if !comment.to_s.empty?
|
197
|
-
line = line + " //#{comment}\n"
|
198
|
-
else
|
199
|
-
line = line + "\n"
|
200
|
-
end
|
201
|
-
end
|
164
|
+
line = "\"#{identifier}\" = \"#{text}\";"
|
165
|
+
if !comment.to_s.empty?
|
166
|
+
line = line + " //#{comment}\n"
|
167
|
+
else
|
168
|
+
line = line + "\n"
|
202
169
|
end
|
203
|
-
f.write(line)
|
204
|
-
}
|
205
|
-
end
|
206
|
-
|
207
|
-
File.open(pluralsFilepath, "w") do |f|
|
208
|
-
|
209
|
-
f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
|
210
|
-
f.write("<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n")
|
211
|
-
f.write("<plist version=\"1.0\">\n")
|
212
|
-
f.write("<dict>\n")
|
213
|
-
|
214
|
-
filteredItems.each_with_index { |item, index|
|
215
|
-
|
216
|
-
text = self.mapInvalidPlaceholder(item['text'])
|
217
|
-
identifier = item['identifierIos']
|
218
|
-
|
219
|
-
if !identifier.include?('//') && text.include?("one|")
|
220
|
-
if text == "" || text == "TBD"
|
221
|
-
default_language_object = languages.select { |languageItem| languageItem['language'] == defaultLanguage }.first["items"]
|
222
|
-
default_language_object = self.filterUnusedRows(default_language_object,'identifierIos')
|
223
|
-
|
224
|
-
defaultLanguageText = default_language_object[index]['text']
|
225
|
-
puts "found empty text for:\n\tidentifier: #{identifier}\n\tlanguage:#{language['language']}\n\treplacing it with: #{defaultLanguageText}"
|
226
|
-
text = self.mapInvalidPlaceholder(defaultLanguageText)
|
227
|
-
end
|
228
|
-
|
229
|
-
text = text.gsub("\n", "|")
|
230
|
-
|
231
|
-
formatIdentifier = identifier.gsub(".", "")
|
232
|
-
|
233
|
-
f.write("\t\t<key>#{identifier}</key>\n")
|
234
|
-
f.write("\t\t<dict>\n")
|
235
|
-
f.write("\t\t\t<key>NSStringLocalizedFormatKey</key>\n")
|
236
|
-
f.write("\t\t\t<string>%#@#{formatIdentifier}@</string>\n")
|
237
|
-
f.write("\t\t\t<key>#{formatIdentifier}</key>\n")
|
238
|
-
f.write("\t\t\t<dict>\n")
|
239
|
-
f.write("\t\t\t\t<key>NSStringFormatSpecTypeKey</key>\n")
|
240
|
-
f.write("\t\t\t\t<string>NSStringPluralRuleType</string>\n")
|
241
|
-
f.write("\t\t\t\t<key>NSStringFormatValueTypeKey</key>\n")
|
242
|
-
f.write("\t\t\t\t<string>d</string>\n")
|
243
|
-
|
244
|
-
text.split("|").each_with_index { |word, wordIndex|
|
245
|
-
if wordIndex % 2 == 0
|
246
|
-
f.write("\t\t\t\t<key>#{word}</key>\n")
|
247
|
-
else
|
248
|
-
f.write("\t\t\t\t<string>#{word}</string>\n")
|
249
|
-
end
|
250
|
-
}
|
251
|
-
f.write("\t\t\t</dict>\n")
|
252
|
-
f.write("\t\t</dict>\n")
|
253
170
|
end
|
171
|
+
|
172
|
+
f.write(line)
|
254
173
|
}
|
255
|
-
f.write("</dict>\n")
|
256
|
-
f.write("</plist>\n")
|
257
174
|
end
|
258
175
|
end
|
259
176
|
|
260
177
|
if platform == "android"
|
261
|
-
|
262
|
-
|
263
|
-
if languageDir == base_language
|
264
|
-
languageDir = "values"
|
265
|
-
else
|
266
|
-
languageDir = "values-#{languageDir}"
|
267
|
-
end
|
268
|
-
|
269
|
-
FileUtils.mkdir_p "#{destinationPath}/#{languageDir}"
|
270
|
-
File.open("#{destinationPath}/#{languageDir}/strings.xml", "w") do |f|
|
178
|
+
FileUtils.mkdir_p "values-#{language['language']}"
|
179
|
+
File.open("values-#{language['language']}/strings.xml", "w") do |f|
|
271
180
|
f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
|
272
181
|
f.write("<resources>\n")
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
filteredItems.each_with_index { |item, index|
|
277
|
-
|
278
|
-
comment = item['comment']
|
279
|
-
identifier = item['identifierAndroid']
|
280
|
-
text = item['text']
|
281
|
-
|
282
|
-
line = ""
|
283
|
-
|
284
|
-
if !comment.to_s.empty?
|
285
|
-
line = line + "\t<!--#{comment}-->\n"
|
286
|
-
end
|
287
|
-
|
288
|
-
if text == "" || text == "TBD"
|
289
|
-
default_language_object = languages.select { |languageItem| languageItem['language'] == defaultLanguage }.first["items"]
|
290
|
-
default_language_object = self.filterUnusedRows(default_language_object,'identifierAndroid')
|
291
|
-
|
292
|
-
defaultLanguageText = default_language_object[index]['text']
|
293
|
-
puts "found empty text for:\n\tidentifier: #{identifier}\n\tlanguage:#{language['language']}\n\treplacing it with: #{defaultLanguageText}"
|
294
|
-
text = defaultLanguageText
|
295
|
-
end
|
296
|
-
|
297
|
-
text = text.gsub(/\\?'/, "\\\\'")
|
298
|
-
|
299
|
-
if text.include?("one|")
|
300
|
-
|
301
|
-
text = text.gsub("\n", "|")
|
302
|
-
|
303
|
-
line = line + "\t<plurals name=\"#{identifier}\">\n"
|
304
|
-
|
305
|
-
plural = ""
|
306
|
-
|
307
|
-
text.split("|").each_with_index { |word, wordIndex|
|
308
|
-
if wordIndex % 2 == 0
|
309
|
-
plural = "\t\t<item quantity=\"#{word}\">"
|
310
|
-
else
|
311
|
-
plural = plural + "<![CDATA[\"#{word}\"]]></item>\n"
|
312
|
-
line = line + plural
|
313
|
-
end
|
314
|
-
}
|
315
|
-
line = line + "\t</plurals>\n"
|
316
|
-
else
|
317
|
-
line = line + "\t<string name=\"#{identifier}\"><![CDATA[#{text}]]></string>\n"
|
318
|
-
end
|
319
|
-
|
320
|
-
f.write(line)
|
182
|
+
language["items"].each { |item|
|
183
|
+
f.write("\t<!--#{item['comment']}-->\n\t<string name=\"#{item['identifierAndroid']}\"><![CDATA[#{item['text']}]]></string>\n")
|
321
184
|
}
|
322
185
|
f.write("</resources>\n")
|
323
186
|
end
|
324
187
|
end
|
325
|
-
}
|
326
188
|
end
|
327
189
|
|
328
190
|
def self.createiOSFileEndString()
|
329
|
-
return "\n\
|
191
|
+
return "\n\nextension Localization {\n\tprivate static func localized(identifier key: String, _ args: CVarArg...) -> String {\n\t\tlet format = NSLocalizedString(key, tableName: nil, bundle: Bundle.main, comment: \"\")\n\n\t\tguard !args.isEmpty else { return format }\n\n\t\treturn String(format: format, locale: Locale.current, arguments: args)\n\t}\n}"
|
330
192
|
end
|
331
193
|
|
332
194
|
def self.createiOSFunction(constantName, identifier, arguments, comment)
|
333
195
|
functionTitle = "\n\t///Sheet comment: #{comment}\n\tpublic static func #{constantName}("
|
334
196
|
|
335
197
|
arguments.each_with_index do |item, index|
|
198
|
+
puts item
|
336
199
|
functionTitle = functionTitle + "_ arg#{index}: #{item[:type]}"
|
337
200
|
if index < arguments.count - 1
|
338
201
|
functionTitle = functionTitle + ", "
|
@@ -354,11 +217,6 @@ module Fastlane
|
|
354
217
|
end
|
355
218
|
|
356
219
|
def self.findArgumentsInText(text)
|
357
|
-
|
358
|
-
if text.include?("one|")
|
359
|
-
text = text.dup.split("|")[1]
|
360
|
-
end
|
361
|
-
|
362
220
|
result = Array.new
|
363
221
|
filtered = self.mapInvalidPlaceholder(text)
|
364
222
|
|
@@ -387,10 +245,15 @@ module Fastlane
|
|
387
245
|
end
|
388
246
|
|
389
247
|
def self.mapInvalidPlaceholder(text)
|
390
|
-
filtered = text.gsub('%s', '%@')
|
248
|
+
filtered = text.gsub('%s', '%@')
|
391
249
|
return filtered
|
392
250
|
end
|
393
251
|
|
252
|
+
def self.numberOfLanguages(worksheet)
|
253
|
+
i = worksheet.num_cols
|
254
|
+
return i - 3
|
255
|
+
end
|
256
|
+
|
394
257
|
def self.description
|
395
258
|
"Creates .strings files for iOS and strings.xml files for Android"
|
396
259
|
end
|
@@ -429,24 +292,8 @@ module Fastlane
|
|
429
292
|
FastlaneCore::ConfigItem.new(key: :tabs,
|
430
293
|
env_name: "TABS",
|
431
294
|
description: "Array of all Google Sheet Tabs",
|
432
|
-
default_value: [],
|
433
|
-
optional: true,
|
434
|
-
type: Array),
|
435
|
-
FastlaneCore::ConfigItem.new(key: :language_titles,
|
436
|
-
env_name: "LANGUAGE_TITLES",
|
437
|
-
description: "Alle language titles",
|
438
295
|
optional: false,
|
439
296
|
type: Array),
|
440
|
-
FastlaneCore::ConfigItem.new(key: :default_language,
|
441
|
-
env_name: "DEFAULT_LANGUAGE",
|
442
|
-
description: "Default Language",
|
443
|
-
optional: false,
|
444
|
-
type: String),
|
445
|
-
FastlaneCore::ConfigItem.new(key: :base_language,
|
446
|
-
env_name: "BASE_LANGUAGE",
|
447
|
-
description: "Base language for Xcode projects",
|
448
|
-
optional: true,
|
449
|
-
type: String),
|
450
297
|
FastlaneCore::ConfigItem.new(key: :localization_path,
|
451
298
|
env_name: "LOCALIZATION_PATH",
|
452
299
|
description: "Output path",
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastlane-plugin-google_sheet_localize
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.51
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mario Hahn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: google_drive
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: pry
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|