localio-simonz 0.0.21.pre.2
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 +7 -0
- data/.gitignore +20 -0
- data/CONTRIBUTING.md +8 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +197 -0
- data/Rakefile +9 -0
- data/bin/localize +11 -0
- data/lib/localio.rb +61 -0
- data/lib/localio/filter.rb +43 -0
- data/lib/localio/formatter.rb +20 -0
- data/lib/localio/localizable_writer.rb +28 -0
- data/lib/localio/locfile.rb +65 -0
- data/lib/localio/module.rb +15 -0
- data/lib/localio/processor.rb +18 -0
- data/lib/localio/processors/google_drive_processor.rb +105 -0
- data/lib/localio/processors/xls_processor.rb +84 -0
- data/lib/localio/processors/xlsx_processor.rb +82 -0
- data/lib/localio/segment.rb +14 -0
- data/lib/localio/segments_list_holder.rb +12 -0
- data/lib/localio/string_helper.rb +64 -0
- data/lib/localio/template_handler.rb +21 -0
- data/lib/localio/templates/android_localizable.erb +11 -0
- data/lib/localio/templates/ios_constant_localizable.erb +11 -0
- data/lib/localio/templates/ios_localizable.erb +17 -0
- data/lib/localio/templates/java_properties_localizable.erb +10 -0
- data/lib/localio/templates/json_localizable.erb +16 -0
- data/lib/localio/templates/rails_localizable.erb +13 -0
- data/lib/localio/templates/swift_constant_localizable.erb +11 -0
- data/lib/localio/term.rb +14 -0
- data/lib/localio/version.rb +3 -0
- data/lib/localio/writers/android_writer.rb +42 -0
- data/lib/localio/writers/ios_writer.rb +56 -0
- data/lib/localio/writers/java_properties_writer.rb +37 -0
- data/lib/localio/writers/json_writer.rb +38 -0
- data/lib/localio/writers/rails_writer.rb +38 -0
- data/lib/localio/writers/swift_writer.rb +56 -0
- data/localio.gemspec +35 -0
- metadata +197 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'localio/module'
|
2
|
+
|
3
|
+
class Locfile
|
4
|
+
|
5
|
+
# Specify the target platform for the localizables
|
6
|
+
#
|
7
|
+
# possible values
|
8
|
+
# :android, :ios, :rails, :json
|
9
|
+
dsl_accessor :platform_name, :platform_options
|
10
|
+
|
11
|
+
# Specifies the filesystem path where the generated files will be
|
12
|
+
dsl_accessor :output_path
|
13
|
+
|
14
|
+
# Specifies the format for the keys in the localizable file
|
15
|
+
#
|
16
|
+
# :smart - choose the formatting depending on the platform's best practices. This is the best option for multiplatform apps.
|
17
|
+
# :camel_case - camel case formatting (ie thisKindOfKeys)
|
18
|
+
# :snake_case - snake case formatting (ie this_kind_of_keys)
|
19
|
+
# :none - no formatting done, the keys will be used as
|
20
|
+
dsl_accessor :formatting
|
21
|
+
|
22
|
+
# Specify a filter that we can use for keys. It would work as "put everything except what matches with this key"
|
23
|
+
dsl_accessor :except
|
24
|
+
|
25
|
+
# Specify a filter that we can use for keys. It would work as "put only what matches with this key"
|
26
|
+
dsl_accessor :only
|
27
|
+
|
28
|
+
# Defined using 'source' ideally
|
29
|
+
dsl_accessor :source_service, :source_options
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@platform_name = nil
|
33
|
+
@platform_options = nil
|
34
|
+
@source_service = :google_drive
|
35
|
+
@source_path = nil
|
36
|
+
@source_options = nil
|
37
|
+
@output_path = './out/'
|
38
|
+
@formatting = :smart
|
39
|
+
end
|
40
|
+
|
41
|
+
# Defines the platform
|
42
|
+
#
|
43
|
+
# service : any of the supported ones (see above)
|
44
|
+
# options : hash with extra options, view documentation for the different services
|
45
|
+
def platform(name, options = {})
|
46
|
+
@platform_name = name
|
47
|
+
@platform_options = options
|
48
|
+
end
|
49
|
+
|
50
|
+
# Defines the service storing the translations
|
51
|
+
#
|
52
|
+
# service : can be :google_drive, :xls
|
53
|
+
# options : hash with extra options, view documentation for the different services
|
54
|
+
def source(service, options = {})
|
55
|
+
@source_service = service
|
56
|
+
@source_options = options
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.load(filename)
|
60
|
+
dsl = new
|
61
|
+
dsl.instance_eval(File.read(filename), filename)
|
62
|
+
dsl
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'localio/processors/google_drive_processor'
|
2
|
+
require 'localio/processors/xls_processor'
|
3
|
+
require 'localio/processors/xlsx_processor'
|
4
|
+
|
5
|
+
module Processor
|
6
|
+
def self.load_localizables(platform_options, service, options)
|
7
|
+
case service
|
8
|
+
when :google_drive
|
9
|
+
GoogleDriveProcessor.load_localizables platform_options, options
|
10
|
+
when :xls
|
11
|
+
XlsProcessor.load_localizables platform_options, options
|
12
|
+
when :xlsx
|
13
|
+
XlsxProcessor.load_localizables platform_options, options
|
14
|
+
else
|
15
|
+
raise ArgumentError, 'Unsupported service! Try with :google_drive, :xlsx or :xls in the source argument'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'google_drive'
|
2
|
+
require 'localio/term'
|
3
|
+
|
4
|
+
class GoogleDriveProcessor
|
5
|
+
|
6
|
+
def self.load_localizables(platform_options, options)
|
7
|
+
|
8
|
+
# Parameter validations
|
9
|
+
spreadsheet = options[:spreadsheet]
|
10
|
+
raise ArgumentError, ':spreadsheet required for Google Drive source!' if spreadsheet.nil?
|
11
|
+
|
12
|
+
override_default = nil
|
13
|
+
override_default = platform_options[:override_default] unless platform_options.nil? or platform_options[:override_default].nil?
|
14
|
+
|
15
|
+
# Log in and get spreadsheet
|
16
|
+
puts 'Logging in to Google Drive...'
|
17
|
+
begin
|
18
|
+
token_file = options[:token_file] || '.localio_google_drive_token.json'
|
19
|
+
session = GoogleDrive.saved_session(token_file, nil, options[:client_id], options[:client_secret])
|
20
|
+
rescue => e
|
21
|
+
raise 'Couldn\'t access Google Drive. Check your client id and secret'
|
22
|
+
end
|
23
|
+
puts 'Logged in!'
|
24
|
+
|
25
|
+
matching_spreadsheets = []
|
26
|
+
session.spreadsheets.each do |s|
|
27
|
+
matching_spreadsheets << s if s.title.include? spreadsheet
|
28
|
+
matching_spreadsheets << s if s.key == spreadsheet
|
29
|
+
end
|
30
|
+
|
31
|
+
case matching_spreadsheets.count
|
32
|
+
when 1
|
33
|
+
puts 'Spreadsheet found. Analyzing...'
|
34
|
+
when 0
|
35
|
+
abort "Unable to find any spreadsheet matching your criteria: #{spreadsheet}"
|
36
|
+
else
|
37
|
+
abort 'More than one match found. You have to be more specific!'
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
# TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
|
42
|
+
worksheet = matching_spreadsheets[0].worksheets[0]
|
43
|
+
raise 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
|
44
|
+
|
45
|
+
# At this point we have the worksheet, so we want to store all the key / values
|
46
|
+
first_valid_row_index = nil
|
47
|
+
last_valid_row_index = nil
|
48
|
+
|
49
|
+
for row in 1..worksheet.max_rows
|
50
|
+
first_valid_row_index = row if worksheet[row, 1].downcase == '[key]'
|
51
|
+
last_valid_row_index = row if worksheet[row, 1].downcase == '[end]'
|
52
|
+
end
|
53
|
+
|
54
|
+
raise IndexError, 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
|
55
|
+
raise IndexError, 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
|
56
|
+
raise IndexError, 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
|
57
|
+
|
58
|
+
languages = Hash.new('languages')
|
59
|
+
default_language = nil
|
60
|
+
|
61
|
+
for column in 2..worksheet.max_cols
|
62
|
+
col_all = worksheet[first_valid_row_index, column]
|
63
|
+
col_all.each_line(' ') do |col_text|
|
64
|
+
default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
|
65
|
+
languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
abort 'There are no language columns in the worksheet' if languages.count == 0
|
70
|
+
|
71
|
+
default_language = languages[0] if default_language.to_s == ''
|
72
|
+
default_language = override_default unless override_default.nil?
|
73
|
+
|
74
|
+
puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
|
75
|
+
|
76
|
+
puts 'Building terminology in memory...'
|
77
|
+
|
78
|
+
terms = []
|
79
|
+
first_term_row = first_valid_row_index+1
|
80
|
+
last_term_row = last_valid_row_index-1
|
81
|
+
|
82
|
+
for row in first_term_row..last_term_row
|
83
|
+
key = worksheet[row, 1]
|
84
|
+
unless key.to_s == ''
|
85
|
+
term = Term.new(key)
|
86
|
+
languages.each do |lang, column_index|
|
87
|
+
term_text = worksheet[row, column_index]
|
88
|
+
term.values.store lang, term_text
|
89
|
+
end
|
90
|
+
terms << term
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
puts 'Loaded!'
|
95
|
+
|
96
|
+
# Return the array of terms, languages and default language
|
97
|
+
res = Hash.new
|
98
|
+
res[:segments] = terms
|
99
|
+
res[:languages] = languages
|
100
|
+
res[:default_language] = default_language
|
101
|
+
|
102
|
+
res
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spreadsheet'
|
2
|
+
require 'localio/term'
|
3
|
+
|
4
|
+
class XlsProcessor
|
5
|
+
|
6
|
+
def self.load_localizables(platform_options, options)
|
7
|
+
|
8
|
+
# Parameter validations
|
9
|
+
path = options[:path]
|
10
|
+
raise ArgumentError, ':path attribute is missing from the source, and it is required for xls spreadsheets' if path.nil?
|
11
|
+
|
12
|
+
override_default = nil
|
13
|
+
override_default = platform_options[:override_default] unless platform_options.nil? or platform_options[:override_default].nil?
|
14
|
+
|
15
|
+
Spreadsheet.client_encoding = 'UTF-8'
|
16
|
+
|
17
|
+
book = Spreadsheet.open path
|
18
|
+
|
19
|
+
# TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
|
20
|
+
worksheet = book.worksheet 0
|
21
|
+
raise 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
|
22
|
+
|
23
|
+
# At this point we have the worksheet, so we want to store all the key / values
|
24
|
+
first_valid_row_index = nil
|
25
|
+
last_valid_row_index = nil
|
26
|
+
|
27
|
+
for row in 0..worksheet.row_count
|
28
|
+
first_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[key]'
|
29
|
+
last_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[end]'
|
30
|
+
end
|
31
|
+
|
32
|
+
raise IndexError, 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
|
33
|
+
raise IndexError, 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
|
34
|
+
raise IndexError, 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
|
35
|
+
|
36
|
+
languages = Hash.new('languages')
|
37
|
+
default_language = nil
|
38
|
+
|
39
|
+
for column in 1..worksheet.column_count
|
40
|
+
col_all = worksheet[first_valid_row_index, column].to_s
|
41
|
+
col_all.each_line(' ') do |col_text|
|
42
|
+
default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
|
43
|
+
languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
raise 'There are no language columns in the worksheet' if languages.count == 0
|
48
|
+
|
49
|
+
default_language = languages[0] if default_language.to_s == ''
|
50
|
+
default_language = override_default unless override_default.nil?
|
51
|
+
|
52
|
+
puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
|
53
|
+
|
54
|
+
puts 'Building terminology in memory...'
|
55
|
+
|
56
|
+
terms = []
|
57
|
+
first_term_row = first_valid_row_index+1
|
58
|
+
last_term_row = last_valid_row_index-1
|
59
|
+
|
60
|
+
for row in first_term_row..last_term_row
|
61
|
+
key = worksheet[row, 0]
|
62
|
+
unless key.to_s == ''
|
63
|
+
term = Term.new(key)
|
64
|
+
languages.each do |lang, column_index|
|
65
|
+
term_text = worksheet[row, column_index]
|
66
|
+
term.values.store lang, term_text
|
67
|
+
end
|
68
|
+
terms << term
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
puts 'Loaded!'
|
73
|
+
|
74
|
+
# Return the array of terms, languages and default language
|
75
|
+
res = Hash.new
|
76
|
+
res[:segments] = terms
|
77
|
+
res[:languages] = languages
|
78
|
+
res[:default_language] = default_language
|
79
|
+
|
80
|
+
res
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'simple_xlsx_reader'
|
2
|
+
require 'localio/term'
|
3
|
+
|
4
|
+
class XlsxProcessor
|
5
|
+
|
6
|
+
def self.load_localizables(platform_options, options)
|
7
|
+
|
8
|
+
# Parameter validations
|
9
|
+
path = options[:path]
|
10
|
+
raise ArgumentError, ':path attribute is missing from the source, and it is required for xlsx spreadsheets' if path.nil?
|
11
|
+
|
12
|
+
override_default = nil
|
13
|
+
override_default = platform_options[:override_default] unless platform_options.nil? or platform_options[:override_default].nil?
|
14
|
+
|
15
|
+
book = SimpleXlsxReader.open path
|
16
|
+
|
17
|
+
# TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
|
18
|
+
worksheet = book.sheets.first
|
19
|
+
raise 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
|
20
|
+
|
21
|
+
# At this point we have the worksheet, so we want to store all the key / values
|
22
|
+
first_valid_row_index = nil
|
23
|
+
last_valid_row_index = nil
|
24
|
+
|
25
|
+
for row in 1..worksheet.rows.count-1
|
26
|
+
first_valid_row_index = row if worksheet.rows[row][0].to_s.downcase == '[key]'
|
27
|
+
last_valid_row_index = row if worksheet.rows[row][0].to_s.downcase == '[end]'
|
28
|
+
end
|
29
|
+
|
30
|
+
raise IndexError, 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
|
31
|
+
raise IndexError, 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
|
32
|
+
raise IndexError, 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
|
33
|
+
|
34
|
+
languages = Hash.new('languages')
|
35
|
+
default_language = nil
|
36
|
+
|
37
|
+
for column in 1..worksheet.rows[first_valid_row_index].count-1
|
38
|
+
col_all = worksheet.rows[first_valid_row_index][column].to_s
|
39
|
+
col_all.each_line(' ') do |col_text|
|
40
|
+
default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
|
41
|
+
languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
raise 'There are no language columns in the worksheet' if languages.count == 0
|
46
|
+
|
47
|
+
default_language = languages[0] if default_language.to_s == ''
|
48
|
+
default_language = override_default unless override_default.nil?
|
49
|
+
|
50
|
+
puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
|
51
|
+
|
52
|
+
puts 'Building terminology in memory...'
|
53
|
+
|
54
|
+
terms = []
|
55
|
+
first_term_row = first_valid_row_index+1
|
56
|
+
last_term_row = last_valid_row_index-1
|
57
|
+
|
58
|
+
for row in first_term_row..last_term_row
|
59
|
+
key = worksheet.rows[row][0]
|
60
|
+
unless key.to_s == ''
|
61
|
+
term = Term.new(key)
|
62
|
+
languages.each do |lang, column_index|
|
63
|
+
term_text = worksheet.rows[row][column_index]
|
64
|
+
term.values.store lang, term_text
|
65
|
+
end
|
66
|
+
terms << term
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
puts 'Loaded!'
|
71
|
+
|
72
|
+
# Return the array of terms, languages and default language
|
73
|
+
res = Hash.new
|
74
|
+
res[:segments] = terms
|
75
|
+
res[:languages] = languages
|
76
|
+
res[:default_language] = default_language
|
77
|
+
|
78
|
+
res
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rexml/text'
|
2
|
+
|
3
|
+
class String
|
4
|
+
def self.colorize(text, color_code)
|
5
|
+
"\e[#{color_code}m#{text}\e[0m"
|
6
|
+
end
|
7
|
+
|
8
|
+
def cyan
|
9
|
+
self.class.colorize(self, 36)
|
10
|
+
end
|
11
|
+
|
12
|
+
def green
|
13
|
+
self.class.colorize(self, 32)
|
14
|
+
end
|
15
|
+
|
16
|
+
def yellow
|
17
|
+
self.class.colorize(self, 33)
|
18
|
+
end
|
19
|
+
|
20
|
+
def red
|
21
|
+
self.class.colorize(self, 31)
|
22
|
+
end
|
23
|
+
|
24
|
+
def underscore
|
25
|
+
self.gsub(/::/, '/').
|
26
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
27
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
28
|
+
tr("-", "_").
|
29
|
+
downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
def strip_tag
|
33
|
+
self.gsub(/^[\[][a-z][\]]/, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
def space_to_underscore
|
37
|
+
self.gsub(' ', '_')
|
38
|
+
end
|
39
|
+
|
40
|
+
def replace_escaped
|
41
|
+
self.gsub("`+", "+").gsub("`=","=").gsub("\\+", "+").gsub("\\=","=")
|
42
|
+
end
|
43
|
+
|
44
|
+
def camel_case
|
45
|
+
return self if self !~ /_/ && self =~ /[A-Z]+.*/
|
46
|
+
split('_').map{|e| e.capitalize}.join
|
47
|
+
end
|
48
|
+
|
49
|
+
def uncapitalize
|
50
|
+
self[0, 1].downcase + self[1..-1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def blank?
|
54
|
+
respond_to?(:empty?) ? empty? : !self
|
55
|
+
end
|
56
|
+
|
57
|
+
def escape_xml
|
58
|
+
REXML::Text.normalize(self)
|
59
|
+
end
|
60
|
+
|
61
|
+
def escape_newline_and_tab
|
62
|
+
self.gsub(/\n/, '\n').gsub(/\t/, '\t')
|
63
|
+
end
|
64
|
+
end
|