applocale 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,69 @@
1
+ require 'google/apis/drive_v3'
2
+ require 'googleauth'
3
+ require 'googleauth/stores/file_token_store'
4
+ require 'google/apis/sheets_v4'
5
+ require 'fileutils'
6
+ require File.expand_path('../../Util/file_util.rb', __FILE__)
7
+ require File.expand_path('../../Util/color_util.rb', __FILE__)
8
+ require File.expand_path('../../Util/error_util.rb', __FILE__)
9
+
10
+ module Applocale
11
+
12
+ class GoogleHelper
13
+ OOB_URI = 'urn:ietf:wg:oauth:2.0:oob'
14
+ APPLICATION_NAME = 'AppLocale'
15
+ CLIENT_SECRETS_PATH = 'client_secret.json'
16
+ CREDENTIALS_PATH = File.join(Dir.home, '.applan_credentials',
17
+ "drive-ruby-applocale.yaml")
18
+ SCOPE = [Google::Apis::DriveV3::AUTH_DRIVE_METADATA_READONLY, Google::Apis::DriveV3::AUTH_DRIVE, Google::Apis::DriveV3::AUTH_DRIVE_FILE]
19
+
20
+ def self.isGoogleLink(link)
21
+ if !link.nil? && link.length > 0
22
+ if link.match(/https:\/\/docs.google.com\/spreadsheets\/d\/([^\/]*)/i)
23
+ if $1.length > 0
24
+ return $1
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.downloadSpreadSheet(spreadSheetId, filename)
31
+ puts "Start download from google, fileId: #{spreadSheetId} ...".green
32
+ service = Google::Apis::DriveV3::DriveService.new
33
+ service.client_options.application_name = APPLICATION_NAME
34
+ service.authorization = self.authorize
35
+ content = service.export_file(spreadSheetId,
36
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
37
+ download_dest: filename)
38
+ if File.exist? filename
39
+ puts "Download from google finished".green
40
+ else
41
+ ErrorUtil::DownloadXlsxError.new("Cannot download from google").raise
42
+ end
43
+ end
44
+
45
+ private
46
+ def self.authorize
47
+
48
+ FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
49
+ client_id = Google::Auth::ClientId.from_file(File.expand_path(CLIENT_SECRETS_PATH, File.dirname(__FILE__)))
50
+ token_store = Google::Auth::Stores::FileTokenStore.new(file: CREDENTIALS_PATH)
51
+ authorizer = Google::Auth::UserAuthorizer.new(
52
+ client_id, SCOPE, token_store)
53
+ user_id = 'default'
54
+ credentials = authorizer.get_credentials(user_id)
55
+ if credentials.nil?
56
+ url = authorizer.get_authorization_url(
57
+ base_url: OOB_URI)
58
+ puts "!!! Open the following URL in the browser and enter the ".red +
59
+ "resulting code after authorization:".red
60
+ puts url.blue.on_white
61
+ code = STDIN.gets.chomp
62
+ credentials = authorizer.get_and_store_credentials_from_code(
63
+ user_id: user_id, code: code, base_url: OOB_URI)
64
+ end
65
+ credentials
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../setting.rb', __FILE__)
2
+ require File.expand_path('../google_helper.rb', __FILE__)
3
+ require File.expand_path('../parse_xlsx', __FILE__)
4
+ require File.expand_path('../convert_to_str_file', __FILE__)
5
+
6
+ module Applocale
7
+ def self.start(is_localupdate, setting)
8
+ if !is_localupdate
9
+ google_file_id = GoogleHelper.isGoogleLink(setting.link)
10
+ if !google_file_id.nil?
11
+ GoogleHelper.downloadSpreadSheet(google_file_id,setting.xlsxpath)
12
+ end
13
+ end
14
+
15
+ parseXlsx = ParseXLSX.new()
16
+ ConvertToStrFile.convert(parseXlsx.result)
17
+ end
18
+
19
+ end
@@ -0,0 +1,166 @@
1
+ require File.expand_path('../setting.rb', __FILE__)
2
+ require File.expand_path('../parse_xlsx_module.rb', __FILE__)
3
+ require File.expand_path('../../Util/error_util.rb', __FILE__)
4
+ require File.expand_path('../../Util/color_util.rb', __FILE__)
5
+ require File.expand_path('../../Util/regex_util.rb', __FILE__)
6
+
7
+ require 'rubyXL'
8
+
9
+
10
+ module Applocale
11
+ class ParseXLSX
12
+
13
+ @xlsx = nil
14
+ @sheetcontent_list = nil
15
+ @allError = nil
16
+ @setting = Setting
17
+
18
+ def initialize()
19
+ @setting = Setting
20
+ puts "Start to Parse XLSX: \"#{@setting.xlsxpath}\" ...".green
21
+ # @xlsx = Roo::Spreadsheet.open(@@setting.xlsxpath)
22
+ @sheetcontent_list = Array.new
23
+ @allKeyDict = {}
24
+ @allError = Array.new
25
+
26
+ self.parse()
27
+ end
28
+
29
+ def result
30
+ return @sheetcontent_list
31
+ end
32
+
33
+ def parse()
34
+
35
+ workbook = RubyXL::Parser.parse(@setting.xlsxpath)
36
+ workbook.worksheets.each do |worksheet|
37
+ sheetName = worksheet.sheet_name
38
+ sheetcontent = ParseXLSXModule::SheetContent.new(sheetName)
39
+
40
+ rowno = -1
41
+ worksheet.each {|row|
42
+ rowno += 1
43
+ # colno = 0
44
+ next if row.nil?
45
+ cells = row && row.cells
46
+ if sheetcontent.header_rowno.nil?
47
+ headerinfo = findHeader(cells)
48
+ if !headerinfo.nil?
49
+ sheetcontent.header_rowno = rowno
50
+ sheetcontent.keyStrWithColNo = headerinfo[:keystr_colno]
51
+ sheetcontent.langWithColNo_list = headerinfo[:lang_colno]
52
+ end
53
+ else
54
+ begin
55
+ rowcontent = parseRow(sheetName, rowno, worksheet.sheet_data[rowno], sheetcontent.keyStrWithColNo, sheetcontent.langWithColNo_list)
56
+ if !rowcontent.nil?
57
+ prev_rowcontent = @allKeyDict[rowcontent.key_str.downcase]
58
+ if prev_rowcontent.nil?
59
+ @allKeyDict[rowcontent.key_str.downcase] = rowcontent
60
+ sheetcontent.rowinfo_list.push(rowcontent)
61
+ else
62
+ error = ErrorUtil::ParseXlsxError::ErrorDuplicateKey.new(rowcontent, "duplicate with sheet '#{prev_rowcontent.sheetname}' row '#{prev_rowcontent.rowno}'")
63
+ @allError.push(error)
64
+ end
65
+ end
66
+ rescue ErrorUtil::ParseXlsxError::ParseError => e
67
+ @allError.push(e)
68
+
69
+ end
70
+ end
71
+ }
72
+ if sheetcontent.header_rowno.nil?
73
+ ErrorUtil::ParseXlsxError::HeadeNotFoundError.new("Header not found in sheet: #{sheetName}").to_warn
74
+ end
75
+ @sheetcontent_list.push(sheetcontent)
76
+ end
77
+ if @allError.length > 0
78
+ ErrorUtil::ParseXlsxError::ParseError.raiseArr(@allError)
79
+ end
80
+
81
+ end
82
+
83
+ def parseRow(sheetname, rowno, cells, keyStrWithColNo, langWithColNo_list)
84
+ begin
85
+ cell = cells[keyStrWithColNo.colno]
86
+ val = cell && cell.value
87
+ keystr = toValueKey(val)
88
+ rescue ErrorUtil::ParseXlsxError::ErrorInValidKey => e
89
+ e.rowinfo.sheetname = sheetname
90
+ e.rowinfo.rowno = rowno
91
+ raise e
92
+ end
93
+
94
+ if !keystr.nil?
95
+ rowinfo = ParseXLSXModule::RowInfo.new(sheetname, rowno, keystr)
96
+ for k in 0..langWithColNo_list.length-1
97
+ langWithColNo = langWithColNo_list[k]
98
+ cell = cells[langWithColNo.colno]
99
+ val = cell && cell.value
100
+ cell_value = val.to_s
101
+ lang_name = langWithColNo.lang
102
+ rowinfo.content_dict[lang_name] = convertContect(cell_value)
103
+ end
104
+ return rowinfo
105
+ end
106
+ return nil
107
+ end
108
+
109
+ def toValueKey(value)
110
+ if !value.nil? && value != ""
111
+ new_value = value.to_s
112
+ if ValidKey.isValidKey(@setting.platform, new_value)
113
+ return new_value
114
+ else
115
+ rowinfo = ParseXLSXModule::RowInfo.new(nil, nil, value)
116
+ raise ErrorUtil::ParseXlsxError::ErrorInValidKey.new(rowinfo, "Invaild Key: #{value}")
117
+ end
118
+ end
119
+ return nil
120
+ end
121
+
122
+ def convertContect(cell_value)
123
+ if cell_value.nil?
124
+ return ""
125
+ else
126
+ return ContentUtil.addEscapedDoubleQuote(cell_value)
127
+ end
128
+ end
129
+
130
+ def findHeader(cells)
131
+ keyStrWithColNo = nil
132
+ langWithColNo_list = Array.new()
133
+ k_header_lang_dict = []
134
+ colno = 0
135
+ cells.each{ |cell|
136
+ value = cell && cell.value
137
+ if !value.nil?
138
+ if value == @setting.keystr && keyStrWithColNo.nil?
139
+ keyStrWithColNo = ParseXLSXModule::KeyStrWithColNo.new(value, colno)
140
+ else
141
+ @setting.langlist.each do |lang, info|
142
+ if value == info[:xlsheader] && k_header_lang_dict.index(lang).nil?
143
+ langWithColNo_list.push(ParseXLSXModule::LangWithColNo.new(info[:xlsheader], lang, colno))
144
+ k_header_lang_dict.push(lang)
145
+ end
146
+ end
147
+ end
148
+ end
149
+ colno += 1
150
+ }
151
+
152
+ allPass = true
153
+ for i in 0..langWithColNo_list.length-1
154
+ if langWithColNo_list[i].nil?
155
+ allPass = false
156
+ break
157
+ end
158
+ end
159
+ if !keyStrWithColNo.nil? && langWithColNo_list.length == @setting.langlist.length
160
+ return {:keystr_colno => keyStrWithColNo, :lang_colno => langWithColNo_list}
161
+ end
162
+ return nil
163
+ end
164
+
165
+ end
166
+ end
@@ -0,0 +1,91 @@
1
+ module Applocale
2
+
3
+ module ParseXLSXModule
4
+ class SheetContent
5
+ attr_accessor :sheetname, :header_rowno, :keyStrWithColNo, :langWithColNo_list, :rowinfo_list, :comment
6
+
7
+ def initialize(sheetname)
8
+ self.sheetname = sheetname
9
+ self.rowinfo_list = Array.new()
10
+ self.langWithColNo_list = Array.new()
11
+ self.comment = sheetname
12
+ end
13
+
14
+ def getRowInfoSortByKey()
15
+ return self.rowinfo_list.sort_by { |obj| obj.key_str.to_s }
16
+ end
17
+
18
+ def getRowInfoSortByRowNo()
19
+ return self.rowinfo_list.sort_by { |obj| obj.rowno.to_i }
20
+ end
21
+
22
+ def to_s
23
+ str_keyStrWithColNo = ""
24
+ if !keyStrWithColNo.nil?
25
+ str_keyStrWithColNo = "\n\t#{keyStrWithColNo.to_s}"
26
+ end
27
+ str_langWithColNo_list = ""
28
+ self.langWithColNo_list.each do |langWithColNo|
29
+ str_langWithColNo_list += "\n\t#{langWithColNo.to_s}"
30
+ end
31
+ str_contentlist = "\n"
32
+ self.getRowInfoSortByRowNo().each do |value|
33
+ str_contentlist += "\t #{value.to_s}\n"
34
+ end
35
+
36
+ "sheetname = #{sheetname}\n" +
37
+ "header_rowno = #{header_rowno}\n" +
38
+ "keyStrWithColNo = #{str_keyStrWithColNo}\n" +
39
+ "langWithColNo_list = #{str_langWithColNo_list}\n" +
40
+ "rowinfo_list = #{str_contentlist}"
41
+ end
42
+ end
43
+
44
+ class RowInfo
45
+
46
+ attr_accessor :sheetname, :rowno, :key_str, :content_dict
47
+
48
+ def initialize(sheetname = nil, rowno = nil, key_str = nil)
49
+ self.sheetname = sheetname
50
+ self.rowno = rowno
51
+ self.key_str = key_str
52
+ self.content_dict = {}
53
+ end
54
+
55
+ def to_s
56
+ "sheetname = #{sheetname}, rowno = #{rowno}, key_str = #{key_str}, content_dict = #{content_dict}"
57
+ end
58
+
59
+ end
60
+
61
+ class KeyStrWithColNo
62
+
63
+ attr_accessor :header_str, :colno
64
+
65
+ def initialize(header_str, colno)
66
+ self.header_str = header_str
67
+ self.colno = colno
68
+ end
69
+
70
+ def to_s
71
+ "{header_str => #{header_str}, colno => #{colno}}"
72
+ end
73
+
74
+ end
75
+
76
+ class LangWithColNo
77
+ attr_accessor :header_str, :lang, :colno
78
+
79
+ def initialize(header_str, lang, colno)
80
+ self.header_str = header_str
81
+ self.lang = lang
82
+ self.colno = colno
83
+ end
84
+
85
+ def to_s
86
+ "{header_str => #{header_str}, lang => #{lang}, colno => #{colno}}"
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -0,0 +1,18 @@
1
+ require File.expand_path('../../Util/color_util.rb', __FILE__)
2
+
3
+ module Applocale
4
+ class Setting
5
+ class <<self
6
+ attr_accessor :link, :platform, :keystr, :langlist, :xlsxpath
7
+ end
8
+
9
+ def self.printlog
10
+ puts " In Setting"
11
+ puts " link = #{self.link}"
12
+ puts " platform = #{self.platform}"
13
+ puts " keystr = #{self.keystr}"
14
+ puts " langlist = #{self.langlist}"
15
+ puts " xlsxpath = #{self.xlsxpath}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ require 'colorize'
2
+
3
+
4
+ # class String
5
+ # def black; "\e[30m#{self}\e[0m" end
6
+ # def red; "\e[31m#{self}\e[0m" end
7
+ # def green; "\e[32m#{self}\e[0m" end
8
+ # def brown; "\e[33m#{self}\e[0m" end
9
+ # def blue; "\e[34m#{self}\e[0m" end
10
+ # def magenta; "\e[35m#{self}\e[0m" end
11
+ # def cyan; "\e[36m#{self}\e[0m" end
12
+ # def gray; "\e[37m#{self}\e[0m" end
13
+ #
14
+ # def bg_black; "\e[40m#{self}\e[0m" end
15
+ # def bg_red; "\e[41m#{self}\e[0m" end
16
+ # def bg_green; "\e[42m#{self}\e[0m" end
17
+ # def bg_brown; "\e[43m#{self}\e[0m" end
18
+ # def bg_blue; "\e[44m#{self}\e[0m" end
19
+ # def bg_magenta; "\e[45m#{self}\e[0m" end
20
+ # def bg_cyan; "\e[46m#{self}\e[0m" end
21
+ # def bg_gray; "\e[47m#{self}\e[0m" end
22
+ #
23
+ # def bold; "\e[1m#{self}\e[22m" end
24
+ # def italic; "\e[3m#{self}\e[23m" end
25
+ # def underline; "\e[4m#{self}\e[24m" end
26
+ # def blink; "\e[5m#{self}\e[25m" end
27
+ # def reverse_color; "\e[7m#{self}\e[27m" end
28
+ # end
@@ -0,0 +1,169 @@
1
+ require 'yaml'
2
+ require File.expand_path('../file_util.rb', __FILE__)
3
+ require File.expand_path('../error_util.rb', __FILE__)
4
+ require File.expand_path('../platform.rb', __FILE__)
5
+ require File.expand_path('../../Core/setting.rb', __FILE__)
6
+
7
+ require 'pathname'
8
+
9
+ module Applocale
10
+ class ConfigUtil
11
+ def self.createConfigFileIfNeed(platform)
12
+ pathstr = FileUtil.configFilePathStr
13
+ self.createConfigFile(platform, pathstr) unless File.exist?(pathstr)
14
+ end
15
+
16
+ def self.createConfigFile(platform, configfile_pathstr)
17
+ src_pathstr = File.expand_path("../../#{FileUtil.filename_config}", __FILE__)
18
+
19
+ File.open(src_pathstr, "r") do |form|
20
+ File.open(configfile_pathstr, "w") do |to|
21
+ form.each_line do |line|
22
+ newline = line.gsub("\#{platform}", "#{platform.to_s}")
23
+ newline = newline.gsub("\#{path_zh_TW}", FileUtil.defaultLocaleFileRelativePathStr(platform, Locale::ZH_TW))
24
+ newline = newline.gsub("\#{path_zh_CN}", FileUtil.defaultLocaleFileRelativePathStr(platform, Locale::ZH_CN))
25
+ newline = newline.gsub("\#{path_en_US}", FileUtil.defaultLocaleFileRelativePathStr(platform, Locale::EN_US))
26
+ newline = newline.gsub("\#{xlsxpath}", FileUtil.defaultXlsxRelativePathStr())
27
+ to.puts(newline)
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.loadAndValidateForXlsxToStringFile(is_local_update)
35
+ config_yaml = self.load_config
36
+ self.validateForXlsxToStringFile(config_yaml, is_local_update)
37
+ end
38
+
39
+ def self.loadAndValidateForStringFileToXlsx()
40
+ config_yaml = self.load_config
41
+ self.validateForStringFileToXlsx(config_yaml)
42
+ end
43
+
44
+ # private
45
+ def self.load_config
46
+ configfile_path = FileUtil.configFilePathStr
47
+ unless File.exist?(configfile_path)
48
+ ErrorUtil::MissingConfigFileError.new("Missing ConfigFile").raise
49
+ end
50
+ config_yaml = YAML.load_file configfile_path
51
+ return config_yaml
52
+ end
53
+
54
+ def self.validateForXlsxToStringFile(config_yaml, is_local_update)
55
+ error_list = self.validateCommon(config_yaml)
56
+ if is_local_update
57
+ if !File.exist? Setting.xlsxpath
58
+ error = ErrorUtil::ConfigFileValidError.new("#{Setting.xlsxpath} do not exist")
59
+ error_list.push(error)
60
+ end
61
+ else
62
+ if Setting.link.nil?
63
+ error = ErrorUtil::ConfigFileValidError.new("[link] should not be empty")
64
+ error_list.push(error)
65
+ end
66
+ end
67
+ ErrorUtil::ConfigFileValidError.raiseArr(error_list)
68
+ end
69
+
70
+ def self.validateForStringFileToXlsx(config_yaml)
71
+ error_list = self.validateCommon(config_yaml)
72
+ Setting.langlist.each do |lang, langinfo|
73
+ if !File.exist? langinfo[:path]
74
+ error = ErrorUtil::ConfigFileValidError.new("#{langinfo[:path]} do not exist")
75
+ error_list.push(error)
76
+ end
77
+ end
78
+ ErrorUtil::ConfigFileValidError.raiseArr(error_list)
79
+ end
80
+
81
+ def self.validateCommon(config_yaml)
82
+ error_list = Array.new
83
+ link = config_yaml["link"].to_s
84
+ platform = config_yaml["platform"].to_s
85
+ keystr = config_yaml["keystr"].to_s
86
+ langlist = config_yaml["langlist"]
87
+ xlsxpath = config_yaml["xlsxpath"].to_s
88
+
89
+ newlink = nil
90
+ newplatform = nil
91
+ newkeystr = nil
92
+ newlanglist = Hash.new
93
+ newxlsxpath = nil
94
+
95
+ if !(link.nil? || link.length == 0)
96
+ if (link =~ /^https/).nil? && (link =~ /^http/).nil?
97
+ error = ErrorUtil::ConfigFileValidError.new("Invalid link for [link] : #{link}")
98
+ error_list.push(error)
99
+ else
100
+ newlink = link
101
+ end
102
+ end
103
+
104
+ if !(xlsxpath.nil? || xlsxpath.length == 0)
105
+ if !(Pathname.new xlsxpath).absolute?
106
+ newxlsxpath = File.expand_path(xlsxpath, File.dirname(FileUtil.configFilePathStr))
107
+ else
108
+ newxlsxpath = xlsxpath
109
+ end
110
+ else
111
+ error = ErrorUtil::ConfigFileValidError.new("[xlsxpath] should not be empty or missing")
112
+ error_list.push(error)
113
+ end
114
+
115
+ if platform.nil? || platform.length == 0
116
+ error = ErrorUtil::ConfigFileValidError.new("[platform] should not be empty")
117
+ error_list.push(error)
118
+ else
119
+ if Platform.init(platform).nil?
120
+ error = ErrorUtil::ConfigFileValidError.new("[platform] can only be 'ios' or 'android' ")
121
+ error_list.push(error)
122
+ else
123
+ newplatform = Platform.init(platform)
124
+ end
125
+ end
126
+
127
+ if keystr.nil? || keystr.length == 0
128
+ error = ErrorUtil::ConfigFileValidError.new("[keystr] should not be empty")
129
+ error_list.push(error)
130
+ else
131
+ newkeystr = keystr
132
+ end
133
+
134
+ if langlist.nil?
135
+ error = ErrorUtil::ConfigFileValidError.new("[langlist] should not be empty or missing")
136
+ error_list.push(error)
137
+ elsif !(langlist.is_a? Hash)
138
+ error = ErrorUtil::ConfigFileValidError.new("[langlist] wrong format")
139
+ error_list.push(error)
140
+ else
141
+ if langlist.length <= 0
142
+ error = ErrorUtil::ConfigFileValidError.new("[langlist] should not be empty ")
143
+ error_list.push(error)
144
+ end
145
+ langlist.each do |lang, arr|
146
+ if arr.length != 2
147
+ error = ErrorUtil::ConfigFileValidError.new("[langlist] wrong format")
148
+ error_list.push(error)
149
+ else
150
+ path = arr[1]
151
+ if !(Pathname.new path).absolute?
152
+ path = File.expand_path(path, File.dirname(FileUtil.configFilePathStr))
153
+ end
154
+ newlanglist[lang] = {:xlsheader => arr[0], :path => path}
155
+ end
156
+ end
157
+ end
158
+
159
+ Setting.link = newlink
160
+ Setting.platform = newplatform
161
+ Setting.keystr = newkeystr
162
+ Setting.langlist = newlanglist
163
+ Setting.xlsxpath = newxlsxpath
164
+
165
+ return error_list
166
+ end
167
+
168
+ end
169
+ end