c11n 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: 9d6ddb9f33eac9ad392124d3d42bc094e5b0f2c4
4
+ data.tar.gz: fca0db5616d9ff41a3d8bbbfd873974293831179
5
+ SHA512:
6
+ metadata.gz: fc1eceaa98d8982ce77098aa0010cb735d1905f5c3fd5fe234914013eaed133bd15ab95cb00a09f2461a8852b345a5753fa428eefc9edf7809d7d79051320e80
7
+ data.tar.gz: 3bded82ae6a10c8faa53ffef5f56981a168087f568884c73c2cb7905d5cd15d702f8afc89d0b21181e89d9b3f0682e8a277b6eb306b4d45b8931e2d4a61a62c4
@@ -0,0 +1,17 @@
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
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in c11n.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Inbeom Hwang
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,52 @@
1
+ # C11n
2
+
3
+ C11n enables you to collaborate on i18n for your apps easily using Google
4
+ Drive. It can upload locales in your project to Google Drive Spreadsheet and
5
+ download contents from Google Drive Spreadsheet and modify your source files
6
+ up to date.
7
+
8
+ Default conventions for Rails/Android/iOS application projects are supported.
9
+
10
+ ## Installation
11
+
12
+ C11n runs from command line. Install it via Rubygems as:
13
+
14
+ $ gem install c11n
15
+
16
+ You may need to install additional libraries c11n depends on:
17
+
18
+ $ gem install nokogiri # for parsing XML files in Android projects
19
+
20
+ ## Usage
21
+
22
+ You need to make your own configuration file for your projects. Example:
23
+
24
+ ---
25
+ :external:
26
+ :google_drive:
27
+ :email: [EMAIL ADDRESS OF YOUR GOOGLE ACCOUNT]
28
+ :password: [PASSWORD FOR YOUR GOOGLE ACCOUNT]
29
+ :spreadsheet_key: [KEY OF SPREADSHEET CONTAINS LOCALES FOR YOUR APPLICATION]
30
+ :worksheet_number: [0-BASED INDEX OF YOUR WORKSHEET]
31
+ :type: [TYPE OF YOUR PROJECT: android, ios, rails]
32
+ :root: [ROOT DIRECTORY OF YOUR PROJECT]
33
+ :default_locale: [DEFAULT LOCALE FOR YOUR PROJECT - ONLY FOR android]
34
+
35
+ After writing configuration file, you can set up a spreadsheet for your project
36
+ with `setup` task. You may skip this step if you have a spreadsheet already.
37
+
38
+ $ c11n -c [PATH TO CONFIG FILE] setup
39
+
40
+ After setting up the spreadsheet, you can move your i18n dat to/from google
41
+ drive using these commands:
42
+
43
+ $ c11n -c [PATH TO CONFIG FILE] push
44
+ $ c11n -c [PATH TO CONFIG FILE] pull
45
+
46
+ ## Contributing
47
+
48
+ 1. Fork it
49
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
50
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
51
+ 4. Push to the branch (`git push origin my-new-feature`)
52
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'optparse'
6
+
7
+ require 'c11n'
8
+ require 'c11n/executable/runner'
9
+
10
+ options = {}
11
+
12
+ OptionParser.new do |opts|
13
+ opts.banner = 'Usage: c11n [options]'
14
+
15
+ opts.on('-c', '--config [CONFIG_FILE]', 'Configuration file') do |config_file_path|
16
+ options[:config_file_path] = config_file_path
17
+ end
18
+ end.parse!
19
+
20
+ options[:task] = ARGV.shift
21
+
22
+ C11n::Executable::Runner.new(options).run
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'c11n/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'c11n'
8
+ gem.version = C11n::VERSION
9
+ gem.authors = ['Inbeom Hwang']
10
+ gem.email = ['hwanginbeom@gmail.com']
11
+ gem.description = %q{Imports/exports i18n translations from/to Google Drive Spreadsheet for easier collaboration.}
12
+ gem.summary = %q{Manage i18n for your projects using Google Drive.}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency 'google_drive'
21
+
22
+ gem.add_development_dependency 'rspec'
23
+ gem.add_development_dependency 'rake'
24
+ gem.add_development_dependency 'nokogiri'
25
+ end
@@ -0,0 +1,8 @@
1
+ require 'c11n/version'
2
+ require 'c11n/configuration'
3
+
4
+ module C11n
5
+ def self.configure
6
+ yield C11n::Configuration.instance
7
+ end
8
+ end
@@ -0,0 +1,72 @@
1
+ module C11n
2
+ class Configuration
3
+ attr_reader :configurations, :externals
4
+
5
+ class NotConfigured < StandardError; end
6
+
7
+ def self.instance
8
+ @instance ||= new
9
+ end
10
+
11
+ def initialize
12
+ @configurations = {}
13
+ @externals = {}
14
+ end
15
+
16
+ def external(klass)
17
+ if block_given?
18
+ yield(@externals[klass] ||= Configuration.new)
19
+
20
+ @externals[klass]
21
+ else
22
+ @externals[klass] || raise(NotConfigured.new)
23
+ end
24
+ end
25
+
26
+ def [](key)
27
+ @configurations[key]
28
+ end
29
+
30
+ def []=(key, value)
31
+ @configurations[key.to_sym] = value
32
+ end
33
+
34
+ def to_hash
35
+ @configurations
36
+ end
37
+
38
+ def method_missing(method_name, *args, &block)
39
+ if method_name =~ /(.+)=$/
40
+ @configurations[$1.to_sym] = args.first
41
+ elsif value = @configurations[method_name.to_sym]
42
+ value
43
+ else
44
+ super
45
+ end
46
+ end
47
+
48
+ def configure_with_hash(configurations)
49
+ configurations.each do |config_key, config_value|
50
+ @configurations[config_key.to_sym] = config_value
51
+ end
52
+ end
53
+
54
+ def load_from_hash(configurations)
55
+ load_external_configurations(configurations.delete(:external) || {})
56
+
57
+ configurations.each do |entry, configuration|
58
+ self[entry] = configuration
59
+ end
60
+ end
61
+
62
+ def load_external_configurations(externals)
63
+ externals.each do |external_entry, nested_configurations|
64
+ external(external_entry) do |external_config|
65
+ nested_configurations.each do |entry, configuration|
66
+ external_config[entry] = configuration
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,37 @@
1
+ require 'c11n/translations'
2
+
3
+ module C11n
4
+ module Conversion
5
+ # Deserializes translations in tabular form into Hash.
6
+ class ComposedKeyDeserializer
7
+ attr_accessor :table
8
+ attr_reader :translations
9
+
10
+ def initialize(table)
11
+ @table = table
12
+ @translations = {}
13
+ end
14
+
15
+ def deserialize
16
+ @table.each do |composed_key, value|
17
+ path = path_for(composed_key)
18
+ create_or_get_leaf_node_for(path)[path.last] = value
19
+ end
20
+
21
+ @translations
22
+ end
23
+
24
+ def create_or_get_leaf_node_for(path)
25
+ path[0..-2].inject(@translations) do |node, path_component|
26
+ node[path_component] ||= {}
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def path_for(key)
33
+ key.split('.').map(&:to_sym)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ module C11n
2
+ module Conversion
3
+ class PlainDeserializer
4
+ attr_accessor :table
5
+ attr_reader :translations
6
+
7
+ def initialize(table)
8
+ @table = table
9
+ @translations = {}
10
+ end
11
+
12
+ def deserialize
13
+ @table.each do |key, value|
14
+ @translations[key] = value
15
+ end
16
+
17
+ @translations
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ module C11n
2
+ module Conversion
3
+ class Serializer
4
+
5
+ class Pair
6
+ attr_accessor :key, :value
7
+
8
+ def initialize(key, value)
9
+ @key = key
10
+ @value = value
11
+ end
12
+
13
+ def to_a
14
+ [@key, @value]
15
+ end
16
+
17
+ def ==(another)
18
+ another.key == @key && another.value == @value
19
+ end
20
+ end
21
+
22
+ attr_accessor :translations
23
+ attr_reader :table
24
+
25
+ def initialize(translations)
26
+ @translations = translations
27
+ @table = nil
28
+ end
29
+
30
+ def serialize
31
+ @table ||= Hash[serialized_translations_for('', @translations).map(&:to_a)]
32
+ end
33
+
34
+ private
35
+
36
+ def serialized_translations_for(prefix, translations)
37
+ translations.map do |key, value|
38
+ if value.is_a?(Hash)
39
+ serialized_translations_for(child_prefix_for(prefix, key), value)
40
+ else
41
+ Pair.new(child_prefix_for(prefix, key), value)
42
+ end
43
+ end.flatten
44
+ end
45
+
46
+ def child_prefix_for(prefix, key)
47
+ prefix.empty? ? key : "#{prefix}.#{key}"
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ require 'c11n/translations'
2
+ require 'c11n/synchronizer'
3
+
4
+ module C11n
5
+ module Executable
6
+ class Runner
7
+ def initialize(options = {})
8
+ @options = options
9
+ end
10
+
11
+ def run
12
+ load_config_file
13
+
14
+ case task
15
+ when 'push'
16
+ synchronizer.upload
17
+ when 'pull'
18
+ synchronizer.download
19
+ when 'setup'
20
+ synchronizer.setup
21
+ end
22
+ end
23
+
24
+ def task
25
+ @options[:task]
26
+ end
27
+
28
+ def config_hash
29
+ if @options[:config_file_path]
30
+ @config_hash ||= YAML.load(File.open(@options[:config_file_path]).read)
31
+ end
32
+ end
33
+
34
+ def load_config_file
35
+ C11n::Configuration.instance.load_from_hash(config_hash) if config_hash
36
+ end
37
+
38
+ def synchronizer
39
+ C11n::Synchronizer.new(configuration: C11n::Configuration.instance)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,41 @@
1
+ require 'nokogiri'
2
+ require 'c11n/types'
3
+
4
+ module C11n
5
+ module Exporter
6
+ class Android
7
+ def initialize(options = {})
8
+ @path = options[:path]
9
+ end
10
+
11
+ def file
12
+ @file ||= File.new(@path, 'w')
13
+ end
14
+
15
+ def export(translations, locale)
16
+ builder = Nokogiri::XML::Builder.new(encoding: 'utf-8')
17
+ types = translations.types
18
+
19
+ root_node = builder.resources do |xml|
20
+ translations.translations_for(locale).each do |key, translation|
21
+ case types[key]
22
+ when C11n::Types::ARRAY
23
+ xml.send(:'string-array') do
24
+ translation.each do |item|
25
+ xml.item item
26
+ end
27
+ end
28
+ when C11n::Types::CDATA
29
+ xml.string { xml.cdata translation }
30
+ else
31
+ xml.string translation
32
+ end['name'] = key
33
+ end
34
+ end
35
+
36
+ file.print builder.to_xml
37
+ file.close
38
+ end
39
+ end
40
+ end
41
+ end