zlocalize 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,248 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ # Memory representation of a list of translation strings in a given language
4
+ # Reads and writes each entry with a list of references to occurences in source files.
5
+
6
+ require 'yaml'
7
+
8
+ module ZLocalize
9
+
10
+ def self.escape_ruby_string(s)
11
+ s2 = s.to_s.dup
12
+ # s2.gsub!("'","\\\\'") # no need to escape single quotes, since we will be writing only double-quoted strings
13
+ s2.gsub!("\"","\\\"")
14
+ s2.gsub!("\r\n","\n")
15
+ s2.gsub!("\n\r","\n")
16
+ s2.gsub!("\n","\\n")
17
+ s2
18
+ end
19
+
20
+ def self.unescape_ruby_string(s)
21
+ s2 = s.to_s
22
+ s2.gsub!("\\'","'")
23
+ s2.gsub!('\\"','"')
24
+ s2.gsub!("\\n","\n")
25
+ s2.gsub("\\","")
26
+ s2
27
+ end
28
+
29
+ def self.clean_ruby_string(s)
30
+ s2 = s.to_s
31
+ if s2.size > 1
32
+ # remove single or double quotes
33
+ if (s2[0] == '"' && s2[s2.size-1] == '"') || (s2[0] == "'" && s2[s2.size-1] == "'")
34
+ s2 = s2[1..s2.size-2].to_s
35
+ end
36
+ end
37
+ unescape_ruby_string(s2)
38
+ end
39
+
40
+ class TranslationFileError < StandardError; end
41
+
42
+ class TranslationEntry #:nodoc: all
43
+ attr_accessor :plural
44
+ attr_accessor :source
45
+ attr_accessor :translation
46
+ attr_accessor :references
47
+ attr_accessor :id
48
+ attr_accessor :ignore
49
+
50
+ def initialize(opts = {})
51
+ # opts = HashWithIndifferentAccess.new(opts)
52
+ @plural = opts['plural']
53
+ @source = opts['source']
54
+ @translation = opts['translation']
55
+ @references = opts['references']
56
+ @id = opts['id']
57
+ @ignore = opts['ignore']
58
+ end
59
+
60
+ def add_reference(ref)
61
+ @references << ref unless @references.include?(ref)
62
+ end
63
+
64
+ def synchronize_references(entry)
65
+ # first, remove references that are not present for other entry
66
+ @references.delete_if { |ref| !entry.references.include?(ref) }
67
+ # add all references from other entry (duplicates are avoided)
68
+ entry.references.each { |ref| add_reference(ref) }
69
+ end
70
+
71
+ def to_yaml
72
+ # write YAML ourselves, to allow UTF-8 strings as they are
73
+ out = "entry_#{sprintf('%0.6d',id.to_i)}:\n" +
74
+ " id: #{@id}\n" +
75
+ " ignore: #{@ignore ? 'true' : 'false'}\n" +
76
+ " plural: #{@plural ? 'true' : 'false'}\n" +
77
+ " references:\n" +
78
+ @references.map { |r| " - #{r}" }.join("\n") + "\n" +
79
+ " source: #{data_to_yaml(@source)}\n" +
80
+ " translation: #{data_to_yaml(@translation)}\n"
81
+ out.force_encoding("UTF-8")
82
+ end
83
+
84
+ def data_to_yaml(data)
85
+ if data.nil?
86
+ nil
87
+ elsif data.is_a?(Array)
88
+ "\n" + data.map { |el| " - \"#{ZLocalize.escape_ruby_string(el)}\"" }.join("\n")
89
+ else
90
+ "\"#{ZLocalize.escape_ruby_string(data)}\""
91
+ end
92
+ end
93
+
94
+ end
95
+
96
+
97
+ class TranslationEntryCollection < Hash #:nodoc: all
98
+
99
+ def add_entry(key,ref)
100
+ if entry = self[key]
101
+ entry.add_reference(ref)
102
+ else
103
+ entry = TranslationEntry.new(:source => key, :references => [ref], :translation => nil)
104
+ self[key] = entry
105
+ end
106
+ entry
107
+ end
108
+
109
+ def synchronize_with(collection, purge = false)
110
+ if purge
111
+ # first, remove our entries that are not present in the other collection
112
+ self.delete_if { |key,entry| !collection.key?(key) }
113
+ end
114
+ # add entries from the other collection that are not already in this collection
115
+ collection.each do |key,entry|
116
+ if self.key?(key)
117
+ self[key].synchronize_references(entry)
118
+ else
119
+ self[key] = entry
120
+ end
121
+ end
122
+ end
123
+
124
+ # return an Array of entries, sorted by id
125
+ def sort_by_id
126
+ # Hash#sort will convert each item to an Array of 2 elements [key, value]
127
+ sort { |entry1,entry2| entry1[1].id.to_i <=> entry2[1].id.to_i }.collect { |el| el[1] }
128
+ end
129
+
130
+ end
131
+
132
+
133
+ class TranslationFile #:nodoc: all
134
+
135
+ attr_accessor :entries
136
+
137
+ # regex to match Revision line
138
+ REGEX_REVISION = /^#\s*Revision:\s+([0-9]+)/i
139
+
140
+ # regex to match an entry delimiter
141
+ REGEX_ENTRY = /^#\s*entry\s+([0-9]+)/
142
+
143
+ # regex to match file and line number references for an entry
144
+ REGEX_FILE_REF = /^#\s*file:\s+(.*?)\s*,\s*line\s+([0-9]{1,6})/
145
+
146
+ # regex to match an optional # IGNORE line indicating that entry should be ignored
147
+ REGEX_IGNORE_ENTRY = /^#\s*IGNORE/
148
+
149
+ def self.load(file_name)
150
+ f = new
151
+ f.load(file_name)
152
+ f
153
+ end
154
+
155
+ def initialize
156
+ @entries = TranslationEntryCollection.new
157
+ end
158
+
159
+ def revision
160
+ @revision.to_i > 0 ? @revision + 1 : 1
161
+ end
162
+
163
+ def list_entries
164
+ @entries.each do |e|
165
+ puts e.inspect
166
+ puts "\n"
167
+ end
168
+ end
169
+
170
+ def clear_entries
171
+ @entries.clear
172
+ end
173
+
174
+ def add_entry(key,ref)
175
+ @entries.add_entry(key,ref)
176
+ end
177
+
178
+ def synchronize_with(translation_file)
179
+ @entries.synchronize_with(translation_file.entries)
180
+ end
181
+
182
+ def get_max_entry_id
183
+ max_id = 0
184
+ @entries.values.each do |e|
185
+ max_id = e.id.to_i if e.id.to_i > max_id
186
+ end
187
+ max_id
188
+ end
189
+
190
+ def ensure_entry_ids
191
+ next_id = get_max_entry_id + 1
192
+ @entries.values.each do |e|
193
+ unless e.id.to_i > 0
194
+ e.id = next_id
195
+ next_id += 1
196
+ end
197
+ end
198
+ end
199
+
200
+ def load(filename)
201
+ if File.exists?(filename)
202
+ content = File.open(filename,"r") { |f| f.read }
203
+ read_yaml_header(content)
204
+ entries = YAML::load(content)
205
+ if entries && !entries.is_a?(Hash)
206
+ raise TranslationFileError.new("Invalid YAML translation file #{filename}\n\n#{entries.inspect}")
207
+ end
208
+ else
209
+ raise TranslationFileError.new("Specified translation file does not exist: #{filename}")
210
+ end
211
+ entries ||= {}
212
+ @entries.clear
213
+ entries.each_pair do |k,entry|
214
+ @entries[entry['source']] = TranslationEntry.new(entry)
215
+ end
216
+ end
217
+
218
+ def read_yaml_header(content)
219
+ re = /#\s*Revision:\s+([0-9]+)/i
220
+ if m = re.match(content)
221
+ @revision = m[1].to_i
222
+ else
223
+ @revision = 0
224
+ end
225
+ end
226
+
227
+ def output_header
228
+ out = "# Generated on: #{Time.now.strftime('%Y/%m/%d %H:%M')}\n"
229
+ out << "# Revision: #{revision}\n\n"
230
+ out
231
+ end
232
+
233
+ def to_yaml
234
+ ensure_entry_ids
235
+ out = output_header
236
+ @entries.sort_by_id.each do |e|
237
+ out << e.to_yaml
238
+ out << "\n"
239
+ end
240
+ out
241
+ end
242
+
243
+ def non_translated_entries
244
+ @entries.sort_by_id.collect { |e| e.translation.to_s.strip == "" }
245
+ end
246
+
247
+ end # class TranlationFile
248
+ end # module ZLocalize
data/lib/zlocalize.rb ADDED
@@ -0,0 +1,9 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ zlocalize_path = File.expand_path(File.dirname(__FILE__))
4
+ $:.unshift(zlocalize_path) if File.directory?(zlocalize_path) && !$:.include?(zlocalize_path)
5
+
6
+ require 'active_support/core_ext/string/output_safety'
7
+ require 'zlocalize/rails/railtie' # hook into Rails framework
8
+ require 'zlocalize/backend'
9
+
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zlocalize
3
+ version: !ruby/object:Gem::Version
4
+ version: 4.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Charles Bedard
8
+ - Stephane Volet
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-03-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: 4.1.0
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: 4.1.0
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: 4.1.0
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: 4.1.0
42
+ - !ruby/object:Gem::Dependency
43
+ name: actionpack
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: 4.1.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 4.1.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: i18n
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: 0.5.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.5.0
70
+ description:
71
+ email: zzeligg@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - lib/zlocalize/backend.rb
77
+ - lib/zlocalize/config.rb
78
+ - lib/zlocalize/harvester.rb
79
+ - lib/zlocalize/rails/active_record.rb
80
+ - lib/zlocalize/rails/attached_translations.rb
81
+ - lib/zlocalize/rails/decimal_attributes.rb
82
+ - lib/zlocalize/rails/generators/initializer.rb
83
+ - lib/zlocalize/rails/generators/templates/initializer_template.rb
84
+ - lib/zlocalize/rails/generators/templates/translations_migration_template.rb
85
+ - lib/zlocalize/rails/generators/translations_migration.rb
86
+ - lib/zlocalize/rails/railtie.rb
87
+ - lib/zlocalize/rails/tasks/harvest.rake
88
+ - lib/zlocalize/rails/translated_columns.rb
89
+ - lib/zlocalize/rails/translation.rb
90
+ - lib/zlocalize/rails/translation_validator.rb
91
+ - lib/zlocalize/source_parser.rb
92
+ - lib/zlocalize/translation_file.rb
93
+ - lib/zlocalize.rb
94
+ homepage:
95
+ licenses: []
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - '>='
104
+ - !ruby/object:Gem::Version
105
+ version: 1.9.3
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.0.14
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Translation engine for Rails applications
117
+ test_files: []