localio 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []