rescodegen 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ae7c311ad8954c71748d8e536bc203fd451c7012
4
+ data.tar.gz: 1411a5c914b880c6ace167e40a56a118f9275ea4
5
+ SHA512:
6
+ metadata.gz: 5ace15b6e958b8ac72fc89654dccacf2436e98abb52f6e9fce6cdd59a381600fc2b2d0995781524fd3d1fc5786abfe18af747ce9467cc949c425d3c60de41b5f
7
+ data.tar.gz: af62dbcb88d7180a24ff74cd9c61a793f0925a09f05e5c3d87bef3f352fd66f07d550d711962d5e25dc6bec55a077d70ee1b90a19a30730e9c52d076a367bc6e
data/.gitignore ADDED
@@ -0,0 +1,36 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ ## Specific to RubyMotion:
14
+ .dat*
15
+ .repl_history
16
+ build/
17
+
18
+ ## Documentation cache and generated files:
19
+ /.yardoc/
20
+ /_yardoc/
21
+ /doc/
22
+ /rdoc/
23
+
24
+ ## Environment normalization:
25
+ /.bundle/
26
+ /vendor/bundle
27
+ /lib/bundler/man/
28
+
29
+ # for a library or gem, you might want to ignore these files since the code is
30
+ # intended to run in multiple environments; otherwise, check them in:
31
+ # Gemfile.lock
32
+ # .ruby-version
33
+ # .ruby-gemset
34
+
35
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
36
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rescodegen.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Sean Henry
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # rescodegen
2
+ A command line tool for creating Swift and Objective-C code from localizable strings.
3
+ ## Installation
4
+ `$ sudo gem install rescodegen`
5
+
6
+ ## Usage
7
+ ### Generate Code
8
+ `$ rescodegen -l swift -o output_folder Localizable.strings`
9
+ ### Options
10
+
11
+ |Option|Value|Description |
12
+ |---|---|---|
13
+ |-l|swift\|objc|The language of the generated code|
14
+ |-o|directory|Where to generate the files|
15
+ ## Example
16
+ ### Localizable.strings
17
+
18
+ ```
19
+ /* The Ok button label displayed when an error occurs on the home screen. */
20
+ "HomeScreen.Alert.OkButtonLabel" = "Ok";
21
+
22
+ /* The error message displayed by an alert when content could not be loaded. */
23
+ "HomeScreen.Alert.LoadErrorMessage" = "There was an error loading your content.";
24
+ ```
25
+ ### Generate Code
26
+
27
+ ```
28
+ $ rescodegen -l swift -o . Localizable.strings
29
+ $ rescodegen -l objc -o . Localizable.strings
30
+ ```
31
+ ### Xcode
32
+ Drag the generated files into Xcode and add them to your target.
33
+ ### Swift
34
+ Access localised strings using the `localizedString` property.
35
+ `Strings.Singular.homeScreen_alert_loadErrorMessage.localizedString`
36
+ ### Objective-C
37
+ Access localised strings using the `LocalizedSingularString` function.
38
+ `LocalizedSingularString(SingularStringHomeScreen_alert_loadErrorMessage);`
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_tests.rb']
8
+ end
9
+
10
+ task :default => :test
data/bin/rescodegen ADDED
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if $PROGRAM_NAME == __FILE__
4
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__)
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
8
+ end
9
+
10
+ require 'optparse'
11
+ require 'rescodegen/code_generator/swift_strings_generator'
12
+ require 'rescodegen/code_generator/objc_header_strings_generator'
13
+ require 'rescodegen/code_generator/objc_main_strings_generator'
14
+ require 'rescodegen/key_generator/strings_key_generator'
15
+ require 'rescodegen/code_formatter/swift_code_formatter'
16
+ require 'rescodegen/code_formatter/objc_code_formatter'
17
+
18
+ options = { output: ".", language: "swift" }
19
+
20
+ parser = OptionParser.new do |opts|
21
+ opts.banner = "Usage: rescodegen [options] input_file"
22
+ opts.separator "Generates some strings"
23
+ opts.separator "Options:"
24
+ opts.on("-l", "--language=swift|objc", "swift or objc") do |l|
25
+ options[:language] = l
26
+ end
27
+ opts.on("-o", "--output=directory", "Directory to create file.") do |o|
28
+ options[:output] = o
29
+ end
30
+ end
31
+ parser.parse!
32
+
33
+ abort "Invalid -l argument. Expects swift or objc." if !options[:language].match("swift|objc")
34
+ input_file = ARGV.last
35
+ abort "Missing input_file.\n\n#{parser.help}" if input_file.nil?
36
+ output_file = options[:output] + "/Strings"
37
+
38
+ def generate_swift_file(code_safe_keys, keys, output_file)
39
+ formatter = Rescodegen::SwiftCodeFormatter.new
40
+ code_safe_keys = code_safe_keys.map { |k| formatter.format_string(k) }
41
+ File.write(output_file + ".swift", Rescodegen::SwiftStringsGenerator.new.generate(code_safe_keys, keys))
42
+ end
43
+
44
+ def generate_objc_files(code_safe_keys, keys, output_file)
45
+ formatter = Rescodegen::ObjcCodeFormatter.new
46
+ code_safe_keys = code_safe_keys.map { |k| formatter.format_string(k) }
47
+ File.write(output_file + ".h", Rescodegen::ObjcHeaderStringsGenerator.new.generate(code_safe_keys, keys))
48
+ File.write(output_file + ".m", Rescodegen::ObjcMainStringsGenerator.new.generate(code_safe_keys, keys))
49
+ end
50
+
51
+ generator = Rescodegen::StringsKeyGenerator.new(File.readlines(input_file))
52
+ keys = generator.keys
53
+ code_safe_keys = generator.code_safe_keys
54
+ generate_swift_file(code_safe_keys, keys, output_file) if options[:language] == "swift"
55
+ generate_objc_files(code_safe_keys, keys, output_file) if options[:language] == "objc"
@@ -0,0 +1,11 @@
1
+
2
+ module Rescodegen
3
+ class CodeFormatter
4
+
5
+ protected
6
+ def downcase_string_unless_acronym(string)
7
+ string.downcase! unless string.tr("_", "").size > 1
8
+ string
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'code_formatter'
2
+
3
+ module Rescodegen
4
+ class ObjcCodeFormatter < CodeFormatter
5
+
6
+ def format_string(string)
7
+ string.sub(/^[a-z]/, &:upcase)
8
+ .gsub(/_[A-Z]+/) { |s| downcase_string_unless_acronym(s) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'code_formatter'
2
+
3
+ module Rescodegen
4
+ class SwiftCodeFormatter < CodeFormatter
5
+
6
+ def format_string(string)
7
+ string.sub(/^[A-Z]+/) { |s| downcase_string_unless_acronym(s) }
8
+ .gsub(/_[A-Z]+/) { |s| downcase_string_unless_acronym(s) }
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,55 @@
1
+ require_relative 'strings_generator'
2
+
3
+ module Rescodegen
4
+ class ObjcHeaderStringsGenerator < StringsGenerator
5
+
6
+ def generate(keys, values)
7
+ super(keys, values)
8
+ import_module("Foundation")
9
+ .start_enum("SingularString", "NSInteger")
10
+ .add_cases("SingularString", keys)
11
+ .finish_enum
12
+ .add_c_method("NSString*", "LocalizedSingularString", "SingularString", "singularString")
13
+ .newline
14
+ @output
15
+ end
16
+
17
+ protected
18
+
19
+ def import_module(name)
20
+ @output += "@import #{name};"
21
+ newline.newline
22
+ self
23
+ end
24
+
25
+ def start_enum(name, type)
26
+ indent
27
+ @output += "typedef NS_ENUM(#{type}, #{name})"
28
+ open_brackets
29
+ self
30
+ end
31
+
32
+ def add_c_method(return_type, name, parameter_type, parameter_name)
33
+ newline
34
+ @output += "#{return_type} #{name}(#{parameter_type} #{parameter_name});"
35
+ self
36
+ end
37
+
38
+ def add_cases(enum_name, keys)
39
+ keys.each do |key|
40
+ indent
41
+ @output += "#{enum_name}#{key},"
42
+ newline
43
+ end
44
+ self
45
+ end
46
+
47
+ def finish_enum
48
+ decrement_indent_level
49
+ indent
50
+ @output += "};"
51
+ newline
52
+ self
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,88 @@
1
+ require_relative 'strings_generator'
2
+
3
+ module Rescodegen
4
+ class ObjcMainStringsGenerator < StringsGenerator
5
+
6
+ def generate(keys, values)
7
+ super(keys, values)
8
+ newline
9
+ .import_header("Strings.h")
10
+ .newline
11
+ .start_c_method("NSString*", "LocalizedSingularString", "SingularString", "singularString")
12
+ .start_switch("singularString")
13
+ .add_cases(keys.map { |k| "SingularString" + k }, values)
14
+ .close_brackets
15
+ .close_brackets
16
+ .newline
17
+ @output
18
+ end
19
+
20
+ def import_header(name)
21
+ @output += "#import \"#{name}\""
22
+ self
23
+ end
24
+
25
+ def start_c_method(return_type, name, parameter_type, parameter_name)
26
+ newline
27
+ @output += "#{return_type} #{name}(#{parameter_type} #{parameter_name})"
28
+ open_brackets
29
+ self
30
+ end
31
+
32
+ def start_switch(value)
33
+ indent
34
+ @output += "switch (#{value})"
35
+ open_brackets
36
+ self
37
+ end
38
+
39
+ def add_cases(keys, values)
40
+ i = 0
41
+ until i == keys.size
42
+ start_case(keys[i], values[i])
43
+ .return_value(localized_string(values[i]))
44
+ .finish_case
45
+ i += 1
46
+ end
47
+ add_default_case
48
+ self
49
+ end
50
+
51
+ def add_default_case
52
+ start_default_case
53
+ .return_value("@\"\"")
54
+ .finish_case
55
+ end
56
+
57
+ def start_case(key, value)
58
+ indent
59
+ @output += "case #{key}:"
60
+ newline
61
+ increment_indent_level
62
+ end
63
+
64
+ def start_default_case
65
+ indent
66
+ @output += "default:"
67
+ newline
68
+ increment_indent_level
69
+ self
70
+ end
71
+
72
+ def finish_case
73
+ decrement_indent_level
74
+ self
75
+ end
76
+
77
+ def return_value(value)
78
+ indent
79
+ @output += "return #{value};"
80
+ newline
81
+ self
82
+ end
83
+
84
+ def localized_string(value)
85
+ return "NSLocalizedString(@\"#{value}\", @\"\")"
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,52 @@
1
+
2
+ module Rescodegen
3
+ class StringsGenerator
4
+
5
+ def generate(keys, values)
6
+ raise "Expects keys and values of equal sizes" if keys.size != values.size
7
+ @output = ""
8
+ @tab_level = 0
9
+ end
10
+
11
+ protected
12
+
13
+ def newline
14
+ @output += "\n"
15
+ self
16
+ end
17
+
18
+ def open_brackets
19
+ @output += " {"
20
+ newline
21
+ increment_indent_level
22
+ self
23
+ end
24
+
25
+ def close_brackets
26
+ decrement_indent_level
27
+ indent
28
+ @output += "}"
29
+ newline
30
+ self
31
+ end
32
+
33
+ def increment_indent_level
34
+ @tab_level += 1
35
+ self
36
+ end
37
+
38
+ def decrement_indent_level
39
+ @tab_level -= 1
40
+ self
41
+ end
42
+
43
+ def indent
44
+ @output += (1..@tab_level).reduce("") { |a, b| a + tab }
45
+ self
46
+ end
47
+
48
+ def tab
49
+ " "
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,72 @@
1
+ require_relative 'strings_generator'
2
+
3
+ module Rescodegen
4
+ class SwiftStringsGenerator < StringsGenerator
5
+
6
+ def generate(keys, values)
7
+ super(keys, values)
8
+ import_header("Foundation")
9
+ start_struct("Strings")
10
+ .start_enum("Singular", "String")
11
+ .add_cases(keys, values)
12
+ .start_computed_property("localizedString", "String")
13
+ .return_localized_string
14
+ .close_brackets
15
+ .close_brackets
16
+ .close_brackets
17
+ @output
18
+ end
19
+
20
+ protected
21
+
22
+ def newline
23
+ @output += "\n"
24
+ self
25
+ end
26
+
27
+ def import_header(name)
28
+ @output += "import #{name}"
29
+ newline.newline
30
+ self
31
+ end
32
+
33
+ def start_struct(name)
34
+ @output += "struct #{name}"
35
+ open_brackets
36
+ newline
37
+ self
38
+ end
39
+
40
+ def start_enum(name, type)
41
+ indent
42
+ @output += "enum #{name}: #{type}"
43
+ open_brackets
44
+ self
45
+ end
46
+
47
+ def add_cases(keys, values)
48
+ ### a.zip(b) -> [ [ a[0], b[0] ], [ a[n], b[n] ] ]
49
+ keys.zip(values).each do |i|
50
+ indent
51
+ @output += "case #{i[0]} = \"#{i[1]}\""
52
+ newline
53
+ end
54
+ newline
55
+ self
56
+ end
57
+
58
+ def start_computed_property(name, type)
59
+ indent
60
+ @output += "var #{name}: #{type}"
61
+ open_brackets
62
+ self
63
+ end
64
+
65
+ def return_localized_string
66
+ indent
67
+ @output += "return NSLocalizedString(rawValue, comment: \"\")"
68
+ newline
69
+ self
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module Rescodegen
3
+ class StringsKeyGenerator
4
+
5
+ def self.create_from_file(input_file)
6
+ initialize_from_lines File.readlines(input_file)
7
+ end
8
+
9
+ def initialize(lines)
10
+ @keys = lines.map { |l| l.encode("UTF-16be", :invalid=>:replace, :replace=>"?").encode('UTF-8') }
11
+ .reject { |l| l.strip == "" }
12
+ .select { |l| l[0] == "\"" }
13
+ .map do |line|
14
+ line.gsub(/\n$/, "")
15
+ .gsub(/(")(.*)(" = ".*)/, "\\2") # extracts key from "key" = "description"; format
16
+ end
17
+ end
18
+
19
+ def keys
20
+ @keys
21
+ end
22
+
23
+ def code_safe_keys
24
+ @keys.map do |key|
25
+ key.gsub(/%\.[0-9]f|%[a-zA-Z@]+/, "_") # replace %d, %ld, %.2f etc
26
+ .gsub(/[^a-zA-Z0-9]/, "_") # replace unsupported characters
27
+ .gsub(/(\s|_)+/, "_") # replaces 1 or more occurance of whitespace and/or '_' with single '_'
28
+ .gsub(/^_|_$/, "") # remove _ from beginning and end
29
+ .gsub(/(^[0-9])/, "_\\1") # add underscore at beginning if starts with number
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module Rescodegen
2
+ VERSION = "0.1.0"
3
+ end
data/lib/rescodegen.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "rescodegen/version"
2
+
3
+ module Rescodegen
4
+ # Your code goes here...
5
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rescodegen/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rescodegen"
8
+ spec.version = Rescodegen::VERSION
9
+ spec.authors = ["Sean Henry"]
10
+ spec.email = ["hello@seanhenry.codes"]
11
+
12
+ spec.summary = %q{Converts localised string files into Objective-C and Swift code.}
13
+ spec.homepage = "https://github.com/seanhenry/rescodegen"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "bin"
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.11"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rescodegen
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sean Henry
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-03-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description:
56
+ email:
57
+ - hello@seanhenry.codes
58
+ executables:
59
+ - rescodegen
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/rescodegen
69
+ - lib/rescodegen.rb
70
+ - lib/rescodegen/code_formatter/code_formatter.rb
71
+ - lib/rescodegen/code_formatter/objc_code_formatter.rb
72
+ - lib/rescodegen/code_formatter/swift_code_formatter.rb
73
+ - lib/rescodegen/code_generator/objc_header_strings_generator.rb
74
+ - lib/rescodegen/code_generator/objc_main_strings_generator.rb
75
+ - lib/rescodegen/code_generator/strings_generator.rb
76
+ - lib/rescodegen/code_generator/swift_strings_generator.rb
77
+ - lib/rescodegen/key_generator/strings_key_generator.rb
78
+ - lib/rescodegen/version.rb
79
+ - rescodegen.gemspec
80
+ homepage: https://github.com/seanhenry/rescodegen
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.4.5
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: Converts localised string files into Objective-C and Swift code.
104
+ test_files: []