localio 0.0.1

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a31389582a8885e9540e3503e028a6a6dfceceb4
4
+ data.tar.gz: 4da6e1a9f66fdb864ff104f87671aeecc6321a45
5
+ SHA512:
6
+ metadata.gz: 96bc17d25ee2216f4be4e7083ce2da2a04baee4502ed27a06a5baa64397777eef14695ced346a111ba7b32984933bb013bf10a4760b9ead1de23fc63a7d9a0d3
7
+ data.tar.gz: 38071736bc776050cff64d4507f431719467fdc0f6855dfd94ba78f2467235fcb8303be16658561e657532c8f1d85e7869eb03dd949029454d7393c4a63ade7b
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .idea/
19
+ testing/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in localio.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Nacho L.
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,89 @@
1
+ # Localio
2
+
3
+ Localio generates automatically localizable files for many platforms like Rails, Android, iOS, etc., using a centralized spreadsheet as source. The spreadsheet can be from Google Drive or a simple Excel file.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'localio'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install localio
18
+
19
+ ## Usage
20
+
21
+ You have to create a custom file, Locfile, similar to Rakefile or Gemfile, with some information for this to work. Also you must have some spreadsheet with a particular format, either in Google Drive or in XLS format.
22
+
23
+ In your Locfile directory you can then execute
24
+
25
+ ````
26
+ localize
27
+ ````
28
+
29
+ and your localizable files will be created with the parameters specified in the Locfile.
30
+
31
+ ### The Spreadsheet
32
+
33
+ You will need a little spreadsheet with all the localization literals and their intended keys for internal use while coding.
34
+
35
+ There is a basic example in this Google Drive link: [https://docs.google.com/spreadsheet/ccc?key=0AmX_w4-5HkOgdFFoZ19iSUlRSERnQTJ4NVZiblo2UXc&usp=sharing](https://docs.google.com/spreadsheet/ccc?key=0AmX_w4-5HkOgdFFoZ19iSUlRSERnQTJ4NVZiblo2UXc&usp=sharing). You just have to duplicate and save to your account, or download and save it as XLS file.
36
+
37
+ ### Locfile
38
+
39
+ A minimal `Locfile` example could be:
40
+
41
+ ````ruby
42
+ platform :ios
43
+
44
+ output_path 'out/'
45
+
46
+ source :google_drive,
47
+ :spreadsheet => '[Localizables] My Project!',
48
+ :login => 'your_email@gmail.com',
49
+ :password => 'your_password'
50
+ ````
51
+
52
+ This would connect localio to your Google Drive and process the spreadsheet with title "[Localizables] My Project!".
53
+
54
+ The list of possible commands is this.
55
+
56
+ Option | Description | Default
57
+ ----------------------------|------------------------------------------------------------------|--------
58
+ `platform` | (Req.) Target platform for the localizable files. | ---
59
+ `source` | (Req.) Information on where to find the spreadsheet w/ the info | ---
60
+ `output_path` | (Req.) Target directory for the localizables. | `out/`
61
+ `formatting` | The formatter that will be used for key processing. | `smart`
62
+
63
+
64
+ #### Supported platforms
65
+
66
+ * `:android` for Android string.xml files.
67
+ * `:ios` for iOS Localizable.strings files.
68
+ * `:rails` for Rails YAML files.
69
+ * `:json` for an easy JSON format for localizables.
70
+
71
+ #### Supported sources
72
+
73
+ * `:google_drive` will connect to Google Drive.
74
+ * `:xls` will use a local XLS file.
75
+
76
+ #### Key formatters
77
+
78
+ * `:none` for no formatting.
79
+ * `:snake_case` for snake case formatting (ie "this_kind_of_keys").
80
+ * `:camel_case` for camel case formatting (ie "thisKindOfKeys").
81
+ * `:smart` use a different formatting depending on the platform.
82
+
83
+ ## Contributing
84
+
85
+ 1. Fork it
86
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
87
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
88
+ 4. Push to the branch (`git push origin my-new-feature`)
89
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'localio'
4
+
5
+ Localio.from_cmdline(ARGV)
@@ -0,0 +1,52 @@
1
+ require 'localio/version'
2
+ require 'localio/locfile'
3
+ require 'localio/processor'
4
+ require 'localio/localizable_writer'
5
+
6
+ module Localio
7
+
8
+ def self.from_cmdline(args)
9
+ if ARGV.empty?
10
+ if File.exist? 'Locfile'
11
+ process_locfile('Locfile')
12
+ else
13
+ abort 'Locfile not found in current directory, and no compatible file supplied in arguments.'
14
+ end
15
+ else
16
+ process_locfile(ARGV.shift)
17
+ end
18
+ end
19
+
20
+ def self.from_configuration(configuration)
21
+ @configuration = configuration
22
+ generate_localizables
23
+ end
24
+
25
+ private
26
+
27
+ def self.process_locfile(path)
28
+ @configuration = Locfile.load(path)
29
+ generate_localizables
30
+ end
31
+
32
+ def self.generate_localizables
33
+ process_to_memory
34
+ build_localizables
35
+ end
36
+
37
+ def self.process_to_memory
38
+ @localizables = Processor.load_localizables @configuration.source_service,
39
+ @configuration.source_options
40
+ end
41
+
42
+ def self.build_localizables
43
+ LocalizableWriter.write @configuration.platform,
44
+ @localizables[:languages],
45
+ @localizables[:segments],
46
+ @configuration.output_path,
47
+ @configuration.formatting,
48
+ :default_language => @localizables[:default_language]
49
+ puts 'Done!'.green
50
+ end
51
+
52
+ end
@@ -0,0 +1,20 @@
1
+ require 'localio/string_helper'
2
+
3
+ module Formatter
4
+ def self.format(key, formatter, callback)
5
+ case formatter
6
+ when :smart
7
+ # Smart formatting is given by the processor.
8
+ # I don't like this very much but creating more classes seemed overkill.
9
+ callback.call(key)
10
+ when :none
11
+ key
12
+ when :camel_case
13
+ key.camel_case
14
+ when :snake_case
15
+ key.space_to_underscore.strip_tag.downcase
16
+ else
17
+ abort 'Unknown formatting used. Must use :smart, :none, :camel_case or :snake_case'
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'localio/writers/android_writer'
2
+ require 'localio/writers/ios_writer'
3
+ require 'localio/writers/json_writer'
4
+ require 'localio/writers/rails_writer'
5
+
6
+ module LocalizableWriter
7
+ def self.write(platform, languages, terms, path, formatter, options)
8
+ case platform
9
+ when :android
10
+ AndroidWriter.write languages, terms, path, formatter, options
11
+ when :ios
12
+ IosWriter.write languages, terms, path, formatter, options
13
+ when :json
14
+ JsonWriter.write languages, terms, path, formatter, options
15
+ when :rails
16
+ RailsWriter.write languages, terms, path, formatter, options
17
+ else
18
+ abort 'Platform not supported! Current possibilities are :android, :ios, :json, :rails'
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,49 @@
1
+ require 'localio/module'
2
+
3
+ class Locfile
4
+
5
+ # Specify the target platform for the localizables
6
+ #
7
+ # possible values
8
+ # :android, :ios, :php, :json, :yml
9
+ dsl_accessor :platform
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
+ # Defined using 'source' ideally
23
+ dsl_accessor :source_service, :source_options
24
+
25
+ def initialize
26
+ @platform = nil
27
+ @source_service = :google_drive
28
+ @source_path = nil
29
+ @source_options = nil
30
+ @output_path = './out/'
31
+ @formatting = :smart
32
+ end
33
+
34
+ # Defines the service storing the translations
35
+ #
36
+ # service : can be :google_drive, :xls
37
+ # options : hash with extra options, view documentation for the different services
38
+ def source(service, options = {})
39
+ @source_service = service
40
+ @source_options = options
41
+ end
42
+
43
+ def self.load(filename)
44
+ dsl = new
45
+ dsl.instance_eval(File.read(filename), filename)
46
+ dsl
47
+ end
48
+
49
+ end
@@ -0,0 +1,15 @@
1
+ class Module
2
+ def dsl_accessor(*symbols)
3
+ symbols.each { |sym|
4
+ class_eval %{
5
+ def #{sym}(*val)
6
+ if val.empty?
7
+ @#{sym}
8
+ else
9
+ @#{sym} = val.size == 1 ? val[0] : val
10
+ end
11
+ end
12
+ }
13
+ }
14
+ end
15
+ end
@@ -0,0 +1,19 @@
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(service, options)
7
+ case service
8
+ when :google_drive
9
+ GoogleDriveProcessor.load_localizables options
10
+ when :xls
11
+ XlsProcessor.load_localizables options
12
+ when :xlsx
13
+ raise 'Temporarily disabled due to rubyzip problems. Sorry!'
14
+ # XlsxProcessor.load_localizables options
15
+ else
16
+ abort 'Unsupported service! Try with :google_drive, :xlsx or :xls in the source argument'
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,103 @@
1
+ require 'google_drive'
2
+ require 'localio/term'
3
+
4
+ class GoogleDriveProcessor
5
+
6
+ def self.load_localizables(options)
7
+
8
+ # Parameter validations
9
+ spreadsheet = options[:spreadsheet]
10
+ abort ':spreadsheet required for Google Drive source!' if spreadsheet.nil?
11
+ login = options[:login]
12
+ abort ':login required for Google Drive source!' if login.nil?
13
+ password = options[:password]
14
+ abort ':password required for Google Drive source!' if password.nil?
15
+
16
+ # Log in and get spreadsheet
17
+ puts 'Logging in to Google Drive...'
18
+ begin
19
+ session = GoogleDrive.login(login, password)
20
+ rescue
21
+ abort 'Couldn\'t access Google Drive. Check your credentials in :login and :password'
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
+ end
29
+
30
+ case matching_spreadsheets.count
31
+ when 1
32
+ puts 'Spreadsheet found. Analyzing...'
33
+ when 0
34
+ abort "Unable to find any spreadsheet matching your criteria: #{spreadsheet}"
35
+ else
36
+ abort 'More than one match found. You have to be more specific!'
37
+ end
38
+
39
+
40
+ # TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
41
+ worksheet = matching_spreadsheets[0].worksheets[0]
42
+ abort 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
43
+
44
+ # At this point we have the worksheet, so we want to store all the key / values
45
+ first_valid_row_index = nil
46
+ last_valid_row_index = nil
47
+
48
+ for row in 1..worksheet.max_rows
49
+ first_valid_row_index = row if worksheet[row, 1].downcase == '[key]'
50
+ last_valid_row_index = row if worksheet[row, 1].downcase == '[end]'
51
+ end
52
+
53
+ abort 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
54
+ abort 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
55
+ abort 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
56
+
57
+ languages = Hash.new('languages')
58
+ default_language = nil
59
+
60
+ for column in 2..worksheet.max_cols
61
+ col_all = worksheet[first_valid_row_index, column]
62
+ col_all.each_line(' ') do |col_text|
63
+ default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
64
+ languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
65
+ end
66
+ end
67
+
68
+ abort 'There are no language columns in the worksheet' if languages.count == 0
69
+
70
+ default_language = languages[0] if default_language.to_s == ''
71
+
72
+ puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
73
+
74
+ puts 'Building terminology in memory...'
75
+
76
+ terms = []
77
+ first_term_row = first_valid_row_index+1
78
+ last_term_row = last_valid_row_index-1
79
+
80
+ for row in first_term_row..last_term_row
81
+ key = worksheet[row, 1]
82
+ unless key.to_s == ''
83
+ term = Term.new(key)
84
+ languages.each do |lang, column_index|
85
+ term_text = worksheet[row, column_index]
86
+ term.values.store lang, term_text
87
+ end
88
+ terms << term
89
+ end
90
+ end
91
+
92
+ puts 'Loaded!'
93
+
94
+ # Return the array of terms, languages and default language
95
+ res = Hash.new
96
+ res[:segments] = terms
97
+ res[:languages] = languages
98
+ res[:default_language] = default_language
99
+
100
+ res
101
+ end
102
+
103
+ end
@@ -0,0 +1,80 @@
1
+ require 'spreadsheet'
2
+ require 'localio/term'
3
+
4
+ class XlsProcessor
5
+
6
+ def self.load_localizables(options)
7
+
8
+ # Parameter validations
9
+ path = options[:path]
10
+ abort ':path attribute is missing from the source, and it is required for xls spreadsheets' if path.nil?
11
+
12
+ Spreadsheet.client_encoding = 'UTF-8'
13
+
14
+ book = Spreadsheet.open path
15
+
16
+ # TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
17
+ worksheet = book.worksheet 0
18
+ abort 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
19
+
20
+ # At this point we have the worksheet, so we want to store all the key / values
21
+ first_valid_row_index = nil
22
+ last_valid_row_index = nil
23
+
24
+ for row in 0..worksheet.row_count
25
+ first_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[key]'
26
+ last_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[end]'
27
+ end
28
+
29
+ abort 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
30
+ abort 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
31
+ abort 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
32
+
33
+ languages = Hash.new('languages')
34
+ default_language = nil
35
+
36
+ for column in 1..worksheet.column_count
37
+ col_all = worksheet[first_valid_row_index, column].to_s
38
+ col_all.each_line(' ') do |col_text|
39
+ default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
40
+ languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
41
+ end
42
+ end
43
+
44
+ abort 'There are no language columns in the worksheet' if languages.count == 0
45
+
46
+ default_language = languages[0] if default_language.to_s == ''
47
+
48
+ puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
49
+
50
+ puts 'Building terminology in memory...'
51
+
52
+ terms = []
53
+ first_term_row = first_valid_row_index+1
54
+ last_term_row = last_valid_row_index-1
55
+
56
+ for row in first_term_row..last_term_row
57
+ key = worksheet[row, 0]
58
+ unless key.to_s == ''
59
+ term = Term.new(key)
60
+ languages.each do |lang, column_index|
61
+ term_text = worksheet[row, column_index]
62
+ term.values.store lang, term_text
63
+ end
64
+ terms << term
65
+ end
66
+ end
67
+
68
+ puts 'Loaded!'
69
+
70
+ # Return the array of terms, languages and default language
71
+ res = Hash.new
72
+ res[:segments] = terms
73
+ res[:languages] = languages
74
+ res[:default_language] = default_language
75
+
76
+ res
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,78 @@
1
+ require 'simple_xlsx_reader'
2
+ require 'localio/term'
3
+
4
+ class XlsxProcessor
5
+
6
+ def self.load_localizables(options)
7
+
8
+ # Parameter validations
9
+ path = options[:path]
10
+ abort ':path attribute is missing from the source, and it is required for xlsx spreadsheets' if path.nil?
11
+
12
+ book = SimpleXlsxReader.open path
13
+
14
+ # TODO we could pass a :page_index in the options hash and get that worksheet instead, defaulting to zero?
15
+ worksheet = book.sheets.first
16
+ abort 'Unable to retrieve the first worksheet from the spreadsheet. Are there any pages?' if worksheet.nil?
17
+
18
+ # At this point we have the worksheet, so we want to store all the key / values
19
+ first_valid_row_index = nil
20
+ last_valid_row_index = nil
21
+
22
+ for row in 0..worksheet.rows.count
23
+ first_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[key]'
24
+ last_valid_row_index = row if worksheet[row, 0].to_s.downcase == '[end]'
25
+ end
26
+
27
+ abort 'Invalid format: Could not find any [key] keyword in the A column of the worksheet' if first_valid_row_index.nil?
28
+ abort 'Invalid format: Could not find any [end] keyword in the A column of the worksheet' if last_valid_row_index.nil?
29
+ abort 'Invalid format: [end] must not be before [key] in the A column' if first_valid_row_index > last_valid_row_index
30
+
31
+ languages = Hash.new('languages')
32
+ default_language = nil
33
+
34
+ for column in 1..worksheet.column_count
35
+ col_all = worksheet[first_valid_row_index, column].to_s
36
+ col_all.each_line(' ') do |col_text|
37
+ default_language = col_text.downcase.gsub('*','') if col_text.include? '*'
38
+ languages.store col_text.downcase.gsub('*',''), column unless col_text.to_s == ''
39
+ end
40
+ end
41
+
42
+ abort 'There are no language columns in the worksheet' if languages.count == 0
43
+
44
+ default_language = languages[0] if default_language.to_s == ''
45
+
46
+ puts "Languages detected: #{languages.keys.join(', ')} -- using #{default_language} as default."
47
+
48
+ puts 'Building terminology in memory...'
49
+
50
+ terms = []
51
+ first_term_row = first_valid_row_index+1
52
+ last_term_row = last_valid_row_index-1
53
+
54
+ for row in first_term_row..last_term_row
55
+ key = worksheet[row, 0]
56
+ unless key.to_s == ''
57
+ term = Term.new(key)
58
+ languages.each do |lang, column_index|
59
+ term_text = worksheet[row, column_index]
60
+ term.values.store lang, term_text
61
+ end
62
+ terms << term
63
+ end
64
+ end
65
+
66
+ puts 'Loaded!'
67
+
68
+ # Return the array of terms, languages and default language
69
+ res = Hash.new
70
+ res[:segments] = terms
71
+ res[:languages] = languages
72
+ res[:default_language] = default_language
73
+
74
+ res
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,14 @@
1
+ class Segment
2
+
3
+ attr_accessor :key, :translation, :language
4
+
5
+ def initialize(key, translation, language)
6
+ @key = key
7
+ @translation = translation
8
+ @language = language
9
+ end
10
+
11
+ def is_comment?
12
+ @key == nil
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ class SegmentsListHolder
2
+ attr_accessor :segments, :language
3
+
4
+ def initialize(language)
5
+ @segments = []
6
+ @language = language
7
+ end
8
+
9
+ def get_binding
10
+ binding
11
+ end
12
+ end
@@ -0,0 +1,50 @@
1
+ class String
2
+ def self.colorize(text, color_code)
3
+ "\e[#{color_code}m#{text}\e[0m"
4
+ end
5
+
6
+ def cyan
7
+ self.class.colorize(self, 36)
8
+ end
9
+
10
+ def green
11
+ self.class.colorize(self, 32)
12
+ end
13
+
14
+ def yellow
15
+ self.class.colorize(self, 33)
16
+ end
17
+
18
+ def red
19
+ self.class.colorize(self, 31)
20
+ end
21
+
22
+ def underscore
23
+ self.gsub(/::/, '/').
24
+ gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
25
+ gsub(/([a-z\d])([A-Z])/, '\1_\2').
26
+ tr("-", "_").
27
+ downcase
28
+ end
29
+
30
+ def strip_tag
31
+ self.gsub(/^[\[][a-z][\]]/, '')
32
+ end
33
+
34
+ def space_to_underscore
35
+ self.gsub(' ', '_')
36
+ end
37
+
38
+ def camel_case
39
+ return self if self !~ /_/ && self =~ /[A-Z]+.*/
40
+ split('_').map{|e| e.capitalize}.join
41
+ end
42
+
43
+ def uncapitalize
44
+ self[0, 1].downcase + self[1..-1]
45
+ end
46
+
47
+ def blank?
48
+ respond_to?(:empty?) ? empty? : !self
49
+ end
50
+ end
@@ -0,0 +1,21 @@
1
+ require 'erb'
2
+ require 'localio/segments_list_holder'
3
+
4
+ class TemplateHandler
5
+ def self.process_template(template_name, target_directory, generated_file_name, segments)
6
+ full_template_path = File.join(File.dirname(File.expand_path(__FILE__)), "templates/#{template_name}")
7
+ input_file = File.open(full_template_path, 'rb')
8
+ template = input_file.read
9
+ input_file.close
10
+ renderer = ERB.new(template)
11
+ output = renderer.result(segments.get_binding)
12
+ output_file = File.new(generated_file_name, 'w')
13
+ output_file.write(output)
14
+ output_file.close
15
+
16
+ destination_path = File.join(target_directory, generated_file_name)
17
+ FileUtils.mkdir_p(File.dirname(destination_path))
18
+ FileUtils.cp(generated_file_name, destination_path)
19
+ FileUtils.rm(generated_file_name)
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ <!-- Localizable created with localio on (<%= Time.now.localtime %>). DO NOT MODIFY. -->
2
+ <resources>
3
+ <%
4
+ @segments.each do |term|
5
+ if term.is_comment? %>
6
+ <!-- <%= term.translation %> -->
7
+ <% else %> <string name="<%= term.key %>"><%= term.translation %></string>
8
+ <% end
9
+ end
10
+ %>
11
+ </resources>
@@ -0,0 +1,11 @@
1
+ /*
2
+ LocalizableConstants.h
3
+
4
+ GENERATED - DO NOT MODIFY - use localio instead.
5
+
6
+ Created by localio on <%= Time.now.localtime %>.
7
+ */
8
+
9
+ <% @segments.each do |term| %>
10
+ #define <%= term.key %> NSLocalizedString(@"<%= term.translation %>",nil)<%
11
+ end %>
@@ -0,0 +1,17 @@
1
+ /*
2
+ Localizable.strings
3
+
4
+ GENERATED - DO NOT MODIFY - use the localio gem instead.
5
+
6
+ Created by localio on <%= Time.now.localtime %>.
7
+ */
8
+
9
+ <% @segments.each do |term| %>
10
+ <%
11
+ if term.is_comment?
12
+ %>
13
+ // <%= term.translation %>
14
+ <% else %>"<%= term.key %>" = "<%= term.translation %>";<%
15
+ end
16
+ end
17
+ %>
@@ -0,0 +1,16 @@
1
+ {
2
+ "meta" : {
3
+ "info": "Localizable created with localio. DO NOT MODIFY.",
4
+ "last_modification": "<%= Time.now.localtime %>",
5
+ "language": "<%= @language %>"
6
+ },
7
+ "translations": {
8
+ <% @segments.each do |term|
9
+ term_value = term.translation
10
+ term_key = term.key
11
+
12
+ term_key = '___comment___' if term.is_comment?
13
+ %> "<%= term_key %>": "<%= term_value %>"<% unless term == @segments.last %>,<% end %>
14
+ <% end %>
15
+ }
16
+ }
@@ -0,0 +1,13 @@
1
+ # Localizable created with localio on (<%= Time.now.localtime %>). DO NOT MODIFY.
2
+ #
3
+ # Language: <%= @language %>
4
+
5
+ <%= @language %>:
6
+ <%
7
+ @segments.each do |term|
8
+ if term.is_comment? %>
9
+ # <%= term.translation %>
10
+ <% else %> <%= term.key %>: "<%= term.translation %>"
11
+ <% end
12
+ end
13
+ %>
@@ -0,0 +1,14 @@
1
+ class Term
2
+
3
+ attr_accessor :values, :keyword
4
+
5
+ def initialize(keyword)
6
+ @keyword = keyword
7
+ @values = Hash.new
8
+ end
9
+
10
+ def is_comment?
11
+ @keyword.downcase == '[comment]'
12
+ end
13
+
14
+ end
@@ -0,0 +1,3 @@
1
+ module Localio
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,37 @@
1
+ require 'localio/template_handler'
2
+ require 'localio/segments_list_holder'
3
+ require 'localio/segment'
4
+ require 'localio/formatter'
5
+
6
+ class AndroidWriter
7
+ def self.write(languages, terms, path, formatter, options)
8
+ puts 'Writing Android translations...'
9
+ default_language = options[:default_language]
10
+
11
+ languages.keys.each do |lang|
12
+ output_path = File.join(path,"values-#{lang}/")
13
+ output_path = File.join(path,'values/') if default_language == lang
14
+
15
+ # We have now to iterate all the terms for the current language, extract them, and store them into a new array
16
+
17
+ segments = SegmentsListHolder.new lang
18
+ terms.each do |term|
19
+ key = Formatter.format(term.keyword, formatter, method(:android_key_formatter))
20
+ translation = term.values[lang]
21
+ segment = Segment.new(key, translation, lang)
22
+ segment.key = nil if term.is_comment?
23
+ segments.segments << segment
24
+ end
25
+
26
+ TemplateHandler.process_template 'android_localizable.erb', output_path, 'strings.xml', segments
27
+ puts " > #{lang.yellow}"
28
+ end
29
+
30
+ end
31
+
32
+ private
33
+
34
+ def self.android_key_formatter(key)
35
+ key.space_to_underscore.strip_tag.downcase
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ require 'localio/template_handler'
2
+ require 'localio/segments_list_holder'
3
+ require 'localio/segment'
4
+ require 'localio/formatter'
5
+
6
+ class IosWriter
7
+ def self.write(languages, terms, path, formatter, options)
8
+ puts 'Writing iOS translations...'
9
+
10
+ constant_segments = nil
11
+ languages.keys.each do |lang|
12
+ output_path = File.join(path, "#{lang}.lproj/")
13
+
14
+ # We have now to iterate all the terms for the current language, extract them, and store them into a new array
15
+
16
+ segments = SegmentsListHolder.new lang
17
+ constant_segments = SegmentsListHolder.new lang
18
+ terms.each do |term|
19
+ key = Formatter.format(term.keyword, formatter, method(:ios_key_formatter))
20
+ translation = term.values[lang]
21
+ segment = Segment.new(key, translation, lang)
22
+ segment.key = nil if term.is_comment?
23
+ segments.segments << segment
24
+
25
+ unless term.is_comment?
26
+ constant_key = 'kLocale' + Formatter.format(term.keyword, :camel_case, nil)
27
+ constant_value = key
28
+ constant_segment = Segment.new(constant_key, constant_value, lang)
29
+ constant_segments.segments << constant_segment
30
+ end
31
+ end
32
+
33
+ TemplateHandler.process_template 'ios_localizable.erb', output_path, 'Localizable.strings', segments
34
+ puts " > #{lang.yellow}"
35
+ end
36
+
37
+ unless constant_segments.nil?
38
+ TemplateHandler.process_template 'ios_constant_localizable.erb', path, 'LocalizableConstants.h', constant_segments
39
+ puts ' > ' + 'LocalizableConstants.h'.yellow
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def self.ios_key_formatter(key)
46
+ '_'+key.space_to_underscore.strip_tag.capitalize
47
+ end
48
+ end
@@ -0,0 +1,34 @@
1
+ require 'localio/template_handler'
2
+ require 'localio/segments_list_holder'
3
+ require 'localio/segment'
4
+ require 'localio/formatter'
5
+
6
+ class JsonWriter
7
+ def self.write(languages, terms, path, formatter, options)
8
+ puts 'Writing JSON translations...'
9
+
10
+ languages.keys.each do |lang|
11
+ output_path = path
12
+
13
+ # We have now to iterate all the terms for the current language, extract them, and store them into a new array
14
+
15
+ segments = SegmentsListHolder.new lang
16
+ terms.each do |term|
17
+ key = Formatter.format(term.keyword, formatter, method(:json_key_formatter))
18
+ translation = term.values[lang]
19
+ segment = Segment.new(key, translation, lang)
20
+ segment.key = nil if term.is_comment?
21
+ segments.segments << segment
22
+ end
23
+
24
+ TemplateHandler.process_template 'json_localizable.erb', output_path, "strings-#{lang}.json", segments
25
+ puts " > #{lang.yellow}"
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def self.json_key_formatter(key)
32
+ key.space_to_underscore.strip_tag.downcase
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'localio/template_handler'
2
+ require 'localio/segments_list_holder'
3
+ require 'localio/segment'
4
+ require 'localio/formatter'
5
+
6
+ class RailsWriter
7
+ def self.write(languages, terms, path, formatter, options)
8
+ puts 'Writing Rails YAML translations...'
9
+
10
+ languages.keys.each do |lang|
11
+ output_path = path
12
+
13
+ # We have now to iterate all the terms for the current language, extract them, and store them into a new array
14
+
15
+ segments = SegmentsListHolder.new lang
16
+ terms.each do |term|
17
+ key = Formatter.format(term.keyword, formatter, method(:rails_key_formatter))
18
+ translation = term.values[lang]
19
+ segment = Segment.new(key, translation, lang)
20
+ segment.key = nil if term.is_comment?
21
+ segments.segments << segment
22
+ end
23
+
24
+ TemplateHandler.process_template 'rails_localizable.erb', output_path, "#{lang}.yml", segments
25
+ puts " > #{lang.yellow}"
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def self.rails_key_formatter(key)
32
+ key.space_to_underscore.strip_tag.downcase
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'localio/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "localio"
8
+ spec.version = Localio::VERSION
9
+ spec.authors = ["Nacho Lopez"]
10
+ spec.email = ["nacho@nlopez.io"]
11
+ spec.description = %q{Automatic Localizable file generation for multiple platforms (Rails, Android, iOS, JSON)}
12
+ spec.summary = %q{Automatic Localizable file generation for multiple type of files, like Android string.xml, Xcode Localizable.strings, JSON files, Rails YAML files, etc. reading from Google Drive and Excel spreadsheets as base.}
13
+ spec.homepage = "http://nlopez.io"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.executables << "localize"
22
+
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.required_ruby_version = ">= 1.9.2"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+
30
+ spec.add_dependency "micro-optparse", "~> 1.1.5"
31
+ spec.add_dependency "google_drive", "~> 0.3.6"
32
+ spec.add_dependency "spreadsheet", "~> 0.8.9"
33
+ spec.add_dependency "simple_xlsx_reader", "~> 0.9.7"
34
+ end
metadata ADDED
@@ -0,0 +1,177 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: localio
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Nacho Lopez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: micro-optparse
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 1.1.5
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 1.1.5
69
+ - !ruby/object:Gem::Dependency
70
+ name: google_drive
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: 0.3.6
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ~>
81
+ - !ruby/object:Gem::Version
82
+ version: 0.3.6
83
+ - !ruby/object:Gem::Dependency
84
+ name: spreadsheet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: 0.8.9
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: 0.8.9
97
+ - !ruby/object:Gem::Dependency
98
+ name: simple_xlsx_reader
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ~>
102
+ - !ruby/object:Gem::Version
103
+ version: 0.9.7
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 0.9.7
111
+ description: Automatic Localizable file generation for multiple platforms (Rails,
112
+ Android, iOS, JSON)
113
+ email:
114
+ - nacho@nlopez.io
115
+ executables:
116
+ - localize
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - .gitignore
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - bin/localize
126
+ - lib/localio.rb
127
+ - lib/localio/formatter.rb
128
+ - lib/localio/localizable_writer.rb
129
+ - lib/localio/locfile.rb
130
+ - lib/localio/module.rb
131
+ - lib/localio/processor.rb
132
+ - lib/localio/processors/google_drive_processor.rb
133
+ - lib/localio/processors/xls_processor.rb
134
+ - lib/localio/processors/xlsx_processor.rb
135
+ - lib/localio/segment.rb
136
+ - lib/localio/segments_list_holder.rb
137
+ - lib/localio/string_helper.rb
138
+ - lib/localio/template_handler.rb
139
+ - lib/localio/templates/android_localizable.erb
140
+ - lib/localio/templates/ios_constant_localizable.erb
141
+ - lib/localio/templates/ios_localizable.erb
142
+ - lib/localio/templates/json_localizable.erb
143
+ - lib/localio/templates/rails_localizable.erb
144
+ - lib/localio/term.rb
145
+ - lib/localio/version.rb
146
+ - lib/localio/writers/android_writer.rb
147
+ - lib/localio/writers/ios_writer.rb
148
+ - lib/localio/writers/json_writer.rb
149
+ - lib/localio/writers/rails_writer.rb
150
+ - localio.gemspec
151
+ homepage: http://nlopez.io
152
+ licenses:
153
+ - MIT
154
+ metadata: {}
155
+ post_install_message:
156
+ rdoc_options: []
157
+ require_paths:
158
+ - lib
159
+ required_ruby_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - '>='
162
+ - !ruby/object:Gem::Version
163
+ version: 1.9.2
164
+ required_rubygems_version: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ requirements: []
170
+ rubyforge_project:
171
+ rubygems_version: 2.0.6
172
+ signing_key:
173
+ specification_version: 4
174
+ summary: Automatic Localizable file generation for multiple type of files, like Android
175
+ string.xml, Xcode Localizable.strings, JSON files, Rails YAML files, etc. reading
176
+ from Google Drive and Excel spreadsheets as base.
177
+ test_files: []