fastlane-plugin-google_sheet_localize 0.1.6 → 0.1.20
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: 3e981832345e489a2346adcc3901d5fae7700b379b1e02009cca1e55e8ab5fc6
|
4
|
+
data.tar.gz: df90ee04b2d43aac2229738b8b06f85426331771730acbbbbd83b036e313b70e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 293d36b523297fd849f035d85709610f9c8760774b64604d47b30480d117a236243c0b0cbe1d78f578af6045989d3857ea6e63fba07717b30e73b24957581577
|
7
|
+
data.tar.gz: 1b493e47d984de4e389fefad74eca3692dea3bd9d82eb4b25fb1d6834321a2fb4a8ed8fbe65f8838b0592e57e4d1c49b76b668d67c57102870f1fcc8a014dc67
|
data/README.md
CHANGED
@@ -14,13 +14,30 @@ fastlane add_plugin google_sheet_localize
|
|
14
14
|
|
15
15
|
Creates .strings files for iOS and strings.xml files for Android
|
16
16
|
|
17
|
-
|
17
|
+
to use our plugin you have to duplicate this google sheet: https://docs.google.com/spreadsheets/d/1fwRj1ZFPu2XlrDqkaqmIpJulqR5OVFEZnN35a9v37yc/edit?usp=sharing
|
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)
|
18
25
|
|
19
26
|
## Example
|
20
27
|
|
21
|
-
|
28
|
+
```ruby
|
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
|
+
```
|
22
40
|
|
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)
|
24
41
|
|
25
42
|
## Run tests for this plugin
|
26
43
|
|
@@ -15,6 +15,7 @@ module Fastlane
|
|
15
15
|
path = params[:localization_path]
|
16
16
|
language_titles = params[:language_titles]
|
17
17
|
default_language = params[:default_language]
|
18
|
+
base_language = params[:base_language]
|
18
19
|
|
19
20
|
spreadsheet = session.spreadsheet_by_url(spreadsheet_id)
|
20
21
|
worksheet = spreadsheet.worksheets.first
|
@@ -48,7 +49,7 @@ module Fastlane
|
|
48
49
|
result.push(language)
|
49
50
|
end
|
50
51
|
end
|
51
|
-
self.createFiles(result, platform, path, default_language)
|
52
|
+
self.createFiles(result, platform, path, default_language, base_language)
|
52
53
|
end
|
53
54
|
|
54
55
|
def self.generateJSONObject(contentRows, index)
|
@@ -82,15 +83,15 @@ module Fastlane
|
|
82
83
|
|
83
84
|
end
|
84
85
|
|
85
|
-
def filterUnusedRows(items, identifier)
|
86
|
+
def self.filterUnusedRows(items, identifier)
|
86
87
|
return items.select { |item|
|
87
|
-
|
88
|
-
|
88
|
+
currentIdentifier = item[identifier]
|
89
|
+
currentIdentifier != "NR" && currentIdentifier != "" && currentIdentifier != "TBD"
|
89
90
|
}
|
90
91
|
end
|
91
92
|
|
92
|
-
def self.createFiles(languages, platform, destinationPath, defaultLanguage)
|
93
|
-
self.createFilesForLanguages(languages, platform, destinationPath, defaultLanguage)
|
93
|
+
def self.createFiles(languages, platform, destinationPath, defaultLanguage, base_language)
|
94
|
+
self.createFilesForLanguages(languages, platform, destinationPath, defaultLanguage, base_language)
|
94
95
|
|
95
96
|
if platform == "ios"
|
96
97
|
|
@@ -99,27 +100,37 @@ module Fastlane
|
|
99
100
|
|
100
101
|
filteredItems = languages[0]["items"].select { |item|
|
101
102
|
iosIdentifier = item['identifierIos']
|
102
|
-
iosIdentifier != "NR" && iosIdentifier != "" && !iosIdentifier.include?('//')
|
103
|
+
iosIdentifier != "NR" && iosIdentifier != "" && !iosIdentifier.include?('//') && iosIdentifier != "TBD"
|
103
104
|
}
|
104
105
|
|
105
106
|
File.open(swiftFilepath, "w") do |f|
|
106
|
-
f.write("import Foundation\n\n\npublic struct Localization {\n")
|
107
|
+
f.write("import Foundation\n\n// swiftlint:disable all\npublic struct Localization {\n")
|
107
108
|
filteredItems.each { |item|
|
108
109
|
|
109
110
|
identifier = item['identifierIos']
|
110
111
|
|
111
|
-
values = identifier.dup.
|
112
|
+
values = identifier.dup.split(".")
|
112
113
|
|
113
114
|
constantName = ""
|
114
115
|
|
115
116
|
values.each_with_index do |item, index|
|
116
117
|
if index == 0
|
117
|
-
|
118
|
+
item[0] = item[0].downcase
|
119
|
+
constantName += item
|
118
120
|
else
|
119
|
-
|
121
|
+
item[0] = item[0].upcase
|
122
|
+
constantName += item
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
126
|
+
if constantName == "continue"
|
127
|
+
constantName = "`continue`"
|
128
|
+
end
|
129
|
+
|
130
|
+
if constantName == "switch"
|
131
|
+
constantName = "`switch`"
|
132
|
+
end
|
133
|
+
|
123
134
|
text = self.mapInvalidPlaceholder(item['text'])
|
124
135
|
|
125
136
|
arguments = self.findArgumentsInText(text)
|
@@ -137,7 +148,7 @@ module Fastlane
|
|
137
148
|
end
|
138
149
|
end
|
139
150
|
|
140
|
-
def self.createFilesForLanguages(languages, platform, destinationPath, defaultLanguage)
|
151
|
+
def self.createFilesForLanguages(languages, platform, destinationPath, defaultLanguage, base_language)
|
141
152
|
|
142
153
|
languages.each { |language|
|
143
154
|
|
@@ -145,10 +156,20 @@ module Fastlane
|
|
145
156
|
|
146
157
|
filteredItems = self.filterUnusedRows(language["items"],'identifierIos')
|
147
158
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
159
|
+
stringFileName = "Localizable.strings"
|
160
|
+
pluralsFileName = "Plurals.stringsdict"
|
161
|
+
|
162
|
+
languageName = language['language']
|
163
|
+
|
164
|
+
if languageName == base_language
|
165
|
+
languageName = "Base"
|
166
|
+
end
|
167
|
+
|
168
|
+
stringFilepath = "#{destinationPath}/#{languageName}.lproj/#{stringFileName}"
|
169
|
+
pluralsFilepath = "#{destinationPath}/#{languageName}.lproj/#{pluralsFileName}"
|
170
|
+
FileUtils.mkdir_p "#{destinationPath}/#{languageName}.lproj"
|
171
|
+
|
172
|
+
File.open(stringFilepath, "w") do |f|
|
152
173
|
filteredItems.each_with_index { |item, index|
|
153
174
|
|
154
175
|
text = self.mapInvalidPlaceholder(item['text'])
|
@@ -160,32 +181,86 @@ module Fastlane
|
|
160
181
|
line = "\n\n#{identifier}\n"
|
161
182
|
else
|
162
183
|
|
184
|
+
if !text.include?("one|")
|
185
|
+
|
186
|
+
if text == "" || text == "TBD"
|
187
|
+
default_language_object = languages.select { |languageItem| languageItem['language'] == defaultLanguage }.first["items"]
|
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
|
202
|
+
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|")
|
163
220
|
if text == "" || text == "TBD"
|
164
221
|
default_language_object = languages.select { |languageItem| languageItem['language'] == defaultLanguage }.first["items"]
|
165
222
|
default_language_object = self.filterUnusedRows(default_language_object,'identifierIos')
|
166
223
|
|
167
224
|
defaultLanguageText = default_language_object[index]['text']
|
168
|
-
puts "found empty text for
|
225
|
+
puts "found empty text for:\n\tidentifier: #{identifier}\n\tlanguage:#{language['language']}\n\treplacing it with: #{defaultLanguageText}"
|
169
226
|
text = self.mapInvalidPlaceholder(defaultLanguageText)
|
170
227
|
end
|
171
228
|
|
172
|
-
|
173
|
-
if !comment.to_s.empty?
|
174
|
-
line = line + " //#{comment}\n"
|
175
|
-
else
|
176
|
-
line = line + "\n"
|
177
|
-
end
|
178
|
-
end
|
229
|
+
text = text.gsub("\n", "|")
|
179
230
|
|
180
|
-
|
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
|
+
end
|
181
254
|
}
|
255
|
+
f.write("</dict>\n")
|
256
|
+
f.write("</plist>\n")
|
182
257
|
end
|
183
258
|
end
|
184
259
|
|
185
260
|
if platform == "android"
|
186
261
|
languageDir = language['language']
|
187
262
|
|
188
|
-
if languageDir ==
|
263
|
+
if languageDir == base_language
|
189
264
|
languageDir = "values"
|
190
265
|
else
|
191
266
|
languageDir = "values-#{languageDir}"
|
@@ -195,23 +270,54 @@ module Fastlane
|
|
195
270
|
File.open("#{destinationPath}/#{languageDir}/strings.xml", "w") do |f|
|
196
271
|
f.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n")
|
197
272
|
f.write("<resources>\n")
|
198
|
-
|
273
|
+
|
274
|
+
filteredItems = self.filterUnusedRows(language["items"],'identifierAndroid')
|
275
|
+
|
276
|
+
filteredItems.each_with_index { |item, index|
|
199
277
|
|
200
278
|
comment = item['comment']
|
201
279
|
identifier = item['identifierAndroid']
|
202
280
|
text = item['text']
|
203
281
|
|
204
|
-
|
205
|
-
line = ""
|
282
|
+
line = ""
|
206
283
|
|
207
|
-
|
208
|
-
|
209
|
-
|
284
|
+
if !comment.to_s.empty?
|
285
|
+
line = line + "\t<!--#{comment}-->\n"
|
286
|
+
end
|
210
287
|
|
211
|
-
|
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')
|
212
291
|
|
213
|
-
|
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
|
214
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)
|
215
321
|
}
|
216
322
|
f.write("</resources>\n")
|
217
323
|
end
|
@@ -220,14 +326,13 @@ module Fastlane
|
|
220
326
|
end
|
221
327
|
|
222
328
|
def self.createiOSFileEndString()
|
223
|
-
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.
|
329
|
+
return "\n\nprivate class LocalizationHelper { }\n\nextension Localization {\n\tprivate static func localized(identifier key: String, _ args: CVarArg...) -> String {\n\t\tlet format = NSLocalizedString(key, tableName: nil, bundle: Bundle(for: LocalizationHelper.self), comment: \"\")\n\n\t\tguard !args.isEmpty else { return format }\n\n\t\treturn String(format: format, locale: .current, arguments: args)\n\t}\n}"
|
224
330
|
end
|
225
331
|
|
226
332
|
def self.createiOSFunction(constantName, identifier, arguments, comment)
|
227
333
|
functionTitle = "\n\t///Sheet comment: #{comment}\n\tpublic static func #{constantName}("
|
228
334
|
|
229
335
|
arguments.each_with_index do |item, index|
|
230
|
-
puts item
|
231
336
|
functionTitle = functionTitle + "_ arg#{index}: #{item[:type]}"
|
232
337
|
if index < arguments.count - 1
|
233
338
|
functionTitle = functionTitle + ", "
|
@@ -249,6 +354,11 @@ module Fastlane
|
|
249
354
|
end
|
250
355
|
|
251
356
|
def self.findArgumentsInText(text)
|
357
|
+
|
358
|
+
if text.include?("one|")
|
359
|
+
text = text.dup.split("|")[1]
|
360
|
+
end
|
361
|
+
|
252
362
|
result = Array.new
|
253
363
|
filtered = self.mapInvalidPlaceholder(text)
|
254
364
|
|
@@ -319,7 +429,8 @@ module Fastlane
|
|
319
429
|
FastlaneCore::ConfigItem.new(key: :tabs,
|
320
430
|
env_name: "TABS",
|
321
431
|
description: "Array of all Google Sheet Tabs",
|
322
|
-
|
432
|
+
default_value: [],
|
433
|
+
optional: true,
|
323
434
|
type: Array),
|
324
435
|
FastlaneCore::ConfigItem.new(key: :language_titles,
|
325
436
|
env_name: "LANGUAGE_TITLES",
|
@@ -331,6 +442,11 @@ module Fastlane
|
|
331
442
|
description: "Default Language",
|
332
443
|
optional: false,
|
333
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),
|
334
450
|
FastlaneCore::ConfigItem.new(key: :localization_path,
|
335
451
|
env_name: "LOCALIZATION_PATH",
|
336
452
|
description: "Output path",
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
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.20
|
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-08 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'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: pry
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|