polint 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.
data/.gitignore ADDED
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in polint.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ polint (0.0.1)
5
+ term-ansicolor
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.0)
11
+ method_source (0.8.2)
12
+ pry (0.10.1)
13
+ coderay (~> 1.1.0)
14
+ method_source (~> 0.8.1)
15
+ slop (~> 3.4)
16
+ rake (10.4.2)
17
+ slop (3.6.0)
18
+ term-ansicolor (1.3.0)
19
+ tins (~> 1.0)
20
+ tins (1.3.3)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler (~> 1.7)
27
+ polint!
28
+ pry
29
+ rake (~> 10.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Julien Letessier
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,37 @@
1
+ # Polint
2
+
3
+ A linter for Uniforum PO files.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'polint'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install polint
20
+
21
+ ## Usage
22
+
23
+ Run:
24
+
25
+ $ polint file.po ...
26
+
27
+ Exit status will be non-zero if any issues are encountered.
28
+
29
+ Details about the issues will be listed on standard output.
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it ( https://github.com/mezis/polint/fork )
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/polint ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Sanity checks on PO files
4
+ #
5
+ require 'rubygems'
6
+ require 'term/ansicolor'
7
+ require 'polint'
8
+
9
+ ARGV.each do |arg|
10
+ Polint::Checker.new(arg).run
11
+ end
@@ -0,0 +1,3 @@
1
+ module Polint
2
+ VERSION = "0.0.1"
3
+ end
data/lib/polint.rb ADDED
@@ -0,0 +1,161 @@
1
+ # coding: utf-8
2
+ require 'polint/version'
3
+ require 'singleton'
4
+
5
+ module Polint
6
+ class BlackHole
7
+ include Singleton
8
+
9
+ def method_missing(*args)
10
+ nil
11
+ end
12
+ end
13
+
14
+ class Checker
15
+ AttributeRe = /%\{[^\}]+\}/
16
+ TA = Term::ANSIColor
17
+
18
+ def initialize(pofile)
19
+ @pofile = pofile
20
+ end
21
+
22
+ def run
23
+ @verbose = !!ENV["VERBOSE"]
24
+ @errors = 0
25
+ @fuzzies = 0
26
+
27
+ lint_po
28
+ parse_data annotate_and_load_po
29
+
30
+ total_errors = @errors + @fuzzies
31
+ $stderr.puts "#{@errors} warnings, #{@fuzzies} fuzzies." if total_errors > 0
32
+ return total_errors
33
+ end
34
+
35
+ private
36
+
37
+ def term
38
+ @term ||= ENV['NOCOLOR'] ? BlackHole.instance : Term::ANSIColor
39
+ end
40
+
41
+ def success
42
+ $? == 0
43
+ end
44
+
45
+ def die(message)
46
+ raise StandardError.new(message)
47
+ end
48
+
49
+ # Check the PO file for syntactical correctness
50
+ def lint_po
51
+ system %Q{msgcat "#{@pofile}" > /dev/null}
52
+ success or die "The PO file '#{@pofile}' is broken, aborting."
53
+ end
54
+
55
+ # Load the PO file, annotate keys with original line numbers, and unwrap lines.
56
+ def annotate_and_load_po
57
+ data = []
58
+ header_seen = false
59
+ File.open(@pofile).each_with_index do |line,index|
60
+ if line =~ /^msgid\s+"(.*)"$/
61
+ if header_seen
62
+ data << %Q{msgid "#{index+1}:#{$1}"\n}
63
+ else
64
+ data << line
65
+ header_seen = true
66
+ end
67
+ else
68
+ data << line
69
+ end
70
+ end
71
+
72
+ IO.popen("msgcat --no-wrap -",'r+') do |io|
73
+ io.write data.join
74
+ io.flush
75
+ io.close_write
76
+ data = io.read.split(/\n/)
77
+ end
78
+ success and return data
79
+ die "Error while loading the PO file '#{@pofile}', aborting."
80
+ end
81
+
82
+ def parse_data(lines)
83
+ current = []
84
+ context = []
85
+ next_is_fuzzy = false
86
+
87
+ lines.each do |line|
88
+ case line
89
+
90
+ when /^#, fuzzy/
91
+ next_is_fuzzy = true
92
+
93
+ when /^#: (.*)/
94
+ context << $1
95
+
96
+ when /^(msgid|msgid_plural)\s+"(.*)"$/
97
+ current << $2
98
+
99
+ when /^(msgstr(?:\[([01])\])?)\s+"(.*)"$/
100
+ index = $2.to_i # $2 will be nil for non-plural, so index will be 0
101
+ check_pair(current[index], $3, context, next_is_fuzzy)
102
+
103
+ else
104
+ current = []
105
+ context = []
106
+ next_is_fuzzy = false
107
+ end
108
+ end
109
+ end
110
+
111
+ # Check for errors in a key/translation pair.
112
+ def check_pair(key, val, contexts, is_fuzzy)
113
+ return if key.nil? || val.nil?
114
+
115
+ is_empty = key.strip.length > 0 && val.strip.length == 0
116
+
117
+ attrs_key = key.scan(AttributeRe).sort.uniq
118
+ attrs_val = val.scan(AttributeRe).sort.uniq
119
+
120
+ not_in_key = attrs_val - attrs_key
121
+ not_in_val = attrs_key - attrs_val
122
+
123
+ return unless not_in_key.any? || not_in_val.any? || is_empty || is_fuzzy
124
+
125
+ @errors += 1 if not_in_val.any? || not_in_key.any? || is_empty
126
+ @fuzzies += 1 if is_fuzzy
127
+
128
+ if key =~ /(\d+):(.*)/
129
+ lineno = $1
130
+ file_and_line = "#{@pofile}:#{$1}:"
131
+ key = $2
132
+ else
133
+ lineno = nil
134
+ file_and_line = "#{@pofile}:"
135
+ end
136
+
137
+ if is_empty
138
+ puts "#{file_and_line}#{term.red} Error: translated string empty.#{term.clear}"
139
+ elsif not_in_key.any?
140
+ not_in_key.each do |name|
141
+ puts "#{file_and_line}#{term.red} Error: #{name} absent from reference string.#{term.clear}"
142
+ end
143
+ elsif not_in_val.any?
144
+ not_in_val.each do |name|
145
+ puts "#{file_and_line}#{term.yellow} Warning: #{name} absent from translated string.#{term.clear}"
146
+ end
147
+ elsif is_fuzzy
148
+ puts "#{file_and_line}#{term.yellow} Warning: translation is fuzzy.#{term.clear}"
149
+ end
150
+
151
+ if @verbose
152
+ contexts.each do |context|
153
+ puts "#{term.blue}CONTEXT:#{term.clear} #{context}"
154
+ end
155
+ puts "#{term.blue}KEY:#{term.clear} #{key}"
156
+ puts "#{term.blue}TRN:#{term.clear} #{val}"
157
+ puts "–" * 80
158
+ end
159
+ end
160
+ end
161
+ end
data/polint.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'polint/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "polint"
8
+ spec.version = Polint::VERSION
9
+ spec.authors = ["Julien Letessier"]
10
+ spec.email = ["julien.letessier@gmail.com"]
11
+ spec.summary = %q{A linter for Uniforum PO files.}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.7"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ spec.add_development_dependency "pry"
23
+
24
+ spec.add_dependency "term-ansicolor"
25
+ end
metadata ADDED
@@ -0,0 +1,121 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: polint
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Julien Letessier
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-03-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.7'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.7'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: '10.0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '10.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: pry
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: term-ansicolor
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description:
79
+ email:
80
+ - julien.letessier@gmail.com
81
+ executables:
82
+ - polint
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - Gemfile.lock
89
+ - LICENSE.txt
90
+ - README.md
91
+ - Rakefile
92
+ - bin/polint
93
+ - lib/polint.rb
94
+ - lib/polint/version.rb
95
+ - polint.gemspec
96
+ homepage: ''
97
+ licenses:
98
+ - MIT
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ required_rubygems_version: !ruby/object:Gem::Requirement
110
+ none: false
111
+ requirements:
112
+ - - ! '>='
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.23.2
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: A linter for Uniforum PO files.
121
+ test_files: []