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