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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +89 -0
- data/Rakefile +1 -0
- data/bin/localize +5 -0
- data/lib/localio.rb +52 -0
- data/lib/localio/formatter.rb +20 -0
- data/lib/localio/localizable_writer.rb +21 -0
- data/lib/localio/locfile.rb +49 -0
- data/lib/localio/module.rb +15 -0
- data/lib/localio/processor.rb +19 -0
- data/lib/localio/processors/google_drive_processor.rb +103 -0
- data/lib/localio/processors/xls_processor.rb +80 -0
- data/lib/localio/processors/xlsx_processor.rb +78 -0
- data/lib/localio/segment.rb +14 -0
- data/lib/localio/segments_list_holder.rb +12 -0
- data/lib/localio/string_helper.rb +50 -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/json_localizable.erb +16 -0
- data/lib/localio/templates/rails_localizable.erb +13 -0
- data/lib/localio/term.rb +14 -0
- data/lib/localio/version.rb +3 -0
- data/lib/localio/writers/android_writer.rb +37 -0
- data/lib/localio/writers/ios_writer.rb +48 -0
- data/lib/localio/writers/json_writer.rb +34 -0
- data/lib/localio/writers/rails_writer.rb +34 -0
- data/localio.gemspec +34 -0
- metadata +177 -0
checksums.yaml
ADDED
@@ -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
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/localize
ADDED
data/lib/localio.rb
ADDED
@@ -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,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,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
|
+
%>
|
data/lib/localio/term.rb
ADDED
@@ -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
|
data/localio.gemspec
ADDED
@@ -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: []
|