zlocalize 4.1.0
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 +7 -0
- data/lib/zlocalize/backend.rb +246 -0
- data/lib/zlocalize/config.rb +62 -0
- data/lib/zlocalize/harvester.rb +123 -0
- data/lib/zlocalize/rails/active_record.rb +11 -0
- data/lib/zlocalize/rails/attached_translations.rb +122 -0
- data/lib/zlocalize/rails/decimal_attributes.rb +76 -0
- data/lib/zlocalize/rails/generators/initializer.rb +21 -0
- data/lib/zlocalize/rails/generators/templates/initializer_template.rb +53 -0
- data/lib/zlocalize/rails/generators/templates/translations_migration_template.rb +21 -0
- data/lib/zlocalize/rails/generators/translations_migration.rb +27 -0
- data/lib/zlocalize/rails/railtie.rb +28 -0
- data/lib/zlocalize/rails/tasks/harvest.rake +43 -0
- data/lib/zlocalize/rails/translated_columns.rb +73 -0
- data/lib/zlocalize/rails/translation.rb +10 -0
- data/lib/zlocalize/rails/translation_validator.rb +44 -0
- data/lib/zlocalize/source_parser.rb +756 -0
- data/lib/zlocalize/translation_file.rb +248 -0
- data/lib/zlocalize.rb +9 -0
- metadata +117 -0
@@ -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: []
|