rubustrings 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbbadb6f88015382d88dbce31f356de8db8f8730
4
+ data.tar.gz: 8b3bd9483ee694595ad3c047e66a2890d468d95c
5
+ SHA512:
6
+ metadata.gz: 6d8eeac3b005bfaf6d7133aed1c574fd97cb652bd51619b37a48663e330a53aeb92804cec8955fce07b48b3817788e0d8ee691c4a21532391df5d9eef2d154f9
7
+ data.tar.gz: d76e61a91ea79bba6924838422b7ebfcce9c1f120d072ddde0a1db048f9eb7298b18cab34bd959157b1a304aa6bb6cc1fa78d1ea2907bf795f0ce22c05f16cf3
data/bin/rubustrings ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubustrings'
4
+ Rubustrings.validate(ARGV)
@@ -0,0 +1,11 @@
1
+
2
+ require 'rubustrings/action'
3
+
4
+ # The main Rubustrings driver
5
+ module Rubustrings
6
+ class << self
7
+ def validate(filenames)
8
+ Action.new.validate(filenames)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,147 @@
1
+
2
+ require 'colored'
3
+
4
+ module Rubustrings
5
+ class Action
6
+
7
+ def validate(filenames)
8
+ abort 'No strings file provided' unless filenames
9
+ filenames.each do |file_name|
10
+ log_output(:info, '', 0, "Processing file: \"#{file_name}\"\n")
11
+ result = validate_localizable_string_file file_name
12
+
13
+ if result
14
+ log_output(:result_success, file_name, 0, 'Strings file validated succesfully')
15
+ exit 0
16
+ else
17
+ log_output(:result_error, file_name, 0, 'Some errors detected')
18
+ exit 1
19
+ end
20
+ end
21
+ end
22
+
23
+ # Possible levels are :error, :result_error, :warning, :result_success, :info
24
+ def log_output(level, file_name, line_number, message)
25
+ message = message.chomp
26
+ case level
27
+ when :error
28
+ puts "#{file_name}:#{line_number}: error: #{message}"
29
+ when :warning
30
+ puts "#{file_name}:#{line_number}: warning: #{message}"
31
+ when :result_success
32
+ puts "\nResult: ✓ #{message}".bold.green
33
+ when :result_error
34
+ puts "\nResult: ✘ #{message}".bold.red
35
+ when :info
36
+ puts message.to_s.blue
37
+ end
38
+ end
39
+
40
+ def validate_localizable_string_file(file_name)
41
+ file_data = open_and_read_file file_name
42
+ cleaned_strings = remove_comments_and_empty_lines file_data
43
+
44
+ return log_output(:error, file_name, 0, "no translations found in file: #{file_name}") if cleaned_strings.empty?
45
+
46
+ validation_result = true
47
+ cleaned_strings.each_line do |line|
48
+ validation_result &= validate_translation_line file_name, line
49
+ end
50
+ validation_result
51
+ end
52
+
53
+ def add_line_numbers(file_data)
54
+ line_num = 0
55
+ result = ''
56
+ file_data.each_line do |line|
57
+ line_num += 1
58
+ result += "#{line_num} #{line}"
59
+ end
60
+ result
61
+ end
62
+
63
+ def open_and_read_file(file_name)
64
+ return nil unless File.exist?(file_name)
65
+
66
+ begin
67
+ File.open(file_name, 'rb:utf-16:utf-8').read
68
+ rescue
69
+ File.open(file_name, 'rb:utf-8:utf-8').read
70
+ end
71
+ end
72
+
73
+ def remove_comments_and_empty_lines(file_data)
74
+ multiline_comments_regex = %r{/\*.*?\*/}m
75
+ empty_lines_regex = /^[1-9]\d* $\n/
76
+
77
+ file_data_with_lines = add_line_numbers file_data
78
+ file_data_with_lines.gsub(multiline_comments_regex, '').gsub(empty_lines_regex, '') if file_data
79
+ end
80
+
81
+ def validate_format(line)
82
+ localizable_strings_format_regex = /^\"((?:\\.|[^\\"])*?)\"\s=\s\"((?:\\.|[^\\"])*?)\";/
83
+ localizable_strings_format_regex.match line
84
+ end
85
+
86
+ def validate_special_characters(translation_key, translation_value)
87
+ variables_regex = /%[hlqLztj]?[@%dDuUxXoOfeEgGcCsSpaAF]/
88
+ key_variables = translation_key.scan(variables_regex)
89
+ value_variables = translation_value.scan(variables_regex)
90
+
91
+ key_variables.sort == value_variables.sort
92
+ end
93
+
94
+ def validate_special_beginning(translation_key, translation_value)
95
+ beginning_regex = /^(?:\s|\n|\r)/
96
+
97
+ return true unless translation_key =~ beginning_regex || translation_value =~ beginning_regex
98
+ translation_key.chars.first == translation_value.chars.first
99
+ end
100
+
101
+ def validate_special_ending(translation_key, translation_value)
102
+ ending_regex = /(?:\s|\n|\r)$/
103
+
104
+ return true unless translation_key =~ ending_regex || translation_value =~ ending_regex
105
+ translation_key.chars.last == translation_value.chars.last
106
+ end
107
+
108
+ def check_translation_length(translation_key, translation_value)
109
+ translation_value.length / translation_key.length < 3
110
+ end
111
+
112
+ def validate_translation_line(file_name, line)
113
+ line_number = 0
114
+
115
+ empty_regex = /^\d+\s*\n?$/
116
+ return true if empty_regex.match line
117
+
118
+ numbered_line_regex = /^(\d+) (.*)/
119
+ numbered_line_match = numbered_line_regex.match line
120
+
121
+ return log_output(:error, file_name, line_number, 'internal error') unless numbered_line_match
122
+ line_number = numbered_line_match[1]
123
+ line = numbered_line_match[2]
124
+
125
+ match = validate_format line
126
+ return log_output(:error, file_name, line_number, "invalid format: #{line}") unless match
127
+
128
+ match_key = match[1]
129
+ match_value = match[2]
130
+
131
+ log_output(:warning, file_name, line_number, "no translated string: #{line}") if match_value.empty?
132
+
133
+ log_output(:warning, file_name, line_number, "translation significantly large: #{line}") unless check_translation_length match_key, match_value
134
+
135
+ validation_special_characters = validate_special_characters match_key, match_value
136
+ log_output(:error, file_name, line_number, "number of variables mismatch: #{line}") unless validation_special_characters
137
+
138
+ validation_special_beginning = validate_special_beginning match_key, match_value
139
+ log_output(:error, file_name, line_number, "beginning mismatch: #{line}") unless validation_special_beginning
140
+
141
+ validation_special_ending = validate_special_ending match_key, match_value
142
+ log_output(:error, file_name, line_number, "ending mismatch: #{line}") unless validation_special_ending
143
+
144
+ validation_special_characters && validation_special_beginning && validation_special_ending
145
+ end
146
+ end
147
+ end
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubustrings
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - David Cordero
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-12-03 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Check the format and consistency of the Localizable.strings files of
14
+ iOS Apps with multi-language support
15
+ email: dcorderoramirez@gmail.com
16
+ executables:
17
+ - rubustrings
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - bin/rubustrings
22
+ - lib/rubustrings.rb
23
+ - lib/rubustrings/action.rb
24
+ homepage: https://github.com/dcordero/Rubustrings
25
+ licenses:
26
+ - MIT
27
+ metadata: {}
28
+ post_install_message:
29
+ rdoc_options: []
30
+ require_paths:
31
+ - lib
32
+ required_ruby_version: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ requirements: []
43
+ rubyforge_project:
44
+ rubygems_version: 2.6.11
45
+ signing_key:
46
+ specification_version: 4
47
+ summary: Check Localizable.strings files of iOS Apps
48
+ test_files: []
49
+ has_rdoc: