fastlane-plugin-google_sheet_localize 0.1.20 → 0.1.51
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
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
|