tolk 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.
@@ -0,0 +1,6 @@
1
+ require 'will_paginate'
2
+ require 'ya2yaml'
3
+ require 'tolk/engine'
4
+
5
+ module Tolk
6
+ end
@@ -0,0 +1,7 @@
1
+ require 'rails'
2
+
3
+ module Tolk
4
+ class Engine < Rails::Engine
5
+ engine_name :tolk
6
+ end
7
+ end
@@ -0,0 +1,47 @@
1
+ module Tolk
2
+ module Import
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+
9
+ def import_secondary_locales
10
+ locales = Dir.entries(self.locales_config_path)
11
+ locales = locales.reject {|l| ['.', '..'].include?(l) || !l.ends_with?('.yml') }.map {|x| x.split('.').first } - [Tolk::Locale.primary_locale.name]
12
+
13
+ locales.each {|l| import_locale(l) }
14
+ end
15
+
16
+ def import_locale(locale_name)
17
+ locale = Tolk::Locale.find_or_create_by_name(locale_name)
18
+ data = locale.read_locale_file
19
+
20
+ phrases = Tolk::Phrase.all
21
+ count = 0
22
+
23
+ data.each do |key, value|
24
+ phrase = phrases.detect {|p| p.key == key}
25
+
26
+ if phrase
27
+ translation = locale.translations.new(:text => value, :phrase => phrase)
28
+ count = count + 1 if translation.save
29
+ else
30
+ puts "[ERROR] Key '#{key}' was found in #{locale_name}.yml but #{Tolk::Locale.primary_language_name} translation is missing"
31
+ end
32
+ end
33
+
34
+ puts "[INFO] Imported #{count} keys from #{locale_name}.yml"
35
+ end
36
+
37
+ end
38
+
39
+ def read_locale_file
40
+ locale_file = "#{self.locales_config_path}/#{self.name}.yml"
41
+ raise "Locale file #{locale_file} does not exists" unless File.exists?(locale_file)
42
+
43
+ self.class.flat_hash(YAML::load(IO.read(locale_file))[self.name])
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,74 @@
1
+ module Tolk
2
+ module Sync
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def sync!
9
+ sync_phrases(load_translations)
10
+ end
11
+
12
+ def load_translations
13
+ I18n.available_locales # force load
14
+ translations = flat_hash(I18n.backend.send(:translations)[primary_locale.name.to_sym])
15
+ filter_out_i18n_keys(translations.merge(read_primary_locale_file))
16
+ end
17
+
18
+ def read_primary_locale_file
19
+ primary_file = "#{self.locales_config_path}/#{self.primary_locale_name}.yml"
20
+ File.exists?(primary_file) ? flat_hash(YAML::load(IO.read(primary_file))[self.primary_locale_name]) : {}
21
+ end
22
+
23
+ def flat_hash(data, prefix = '', result = {})
24
+ data.each do |key, value|
25
+ current_prefix = prefix.present? ? "#{prefix}.#{key}" : key
26
+
27
+ if !value.is_a?(Hash) || Tolk::Locale.pluralization_data?(value)
28
+ result[current_prefix] = value.respond_to?(:stringify_keys) ? value.stringify_keys : value
29
+ else
30
+ flat_hash(value, current_prefix, result)
31
+ end
32
+ end
33
+
34
+ result.stringify_keys
35
+ end
36
+
37
+ private
38
+
39
+ def sync_phrases(translations)
40
+ primary_locale = self.primary_locale
41
+ secondary_locales = self.secondary_locales
42
+
43
+ # Handle deleted phrases
44
+ translations.present? ? Tolk::Phrase.destroy_all(["tolk_phrases.key NOT IN (?)", translations.keys]) : Tolk::Phrase.destroy_all
45
+
46
+ phrases = Tolk::Phrase.all
47
+
48
+ translations.each do |key, value|
49
+ # Create phrase and primary translation if missing
50
+ existing_phrase = phrases.detect {|p| p.key == key} || Tolk::Phrase.create!(:key => key)
51
+ translation = existing_phrase.translations.primary || primary_locale.translations.build(:phrase_id => existing_phrase.id)
52
+ translation.text = value
53
+
54
+ if translation.changed? && !translation.new_record?
55
+ # Set the primary updated flag if the primary translation has changed and it is not a new record.
56
+ secondary_locales.each do |locale|
57
+ if existing_translation = existing_phrase.translations.detect {|t| t.locale_id == locale.id }
58
+ existing_translation.force_set_primary_update = true
59
+ existing_translation.save!
60
+ end
61
+ end
62
+ end
63
+
64
+ translation.primary = true
65
+ translation.save!
66
+ end
67
+ end
68
+
69
+ def filter_out_i18n_keys(flat_hash)
70
+ flat_hash.reject { |key, value| key.starts_with? "i18n" }
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,30 @@
1
+ /*-------------------------------------------------
2
+ RESET
3
+ -------------------------------------------------*/
4
+
5
+ body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td {
6
+ margin:0;
7
+ padding:0;
8
+ }
9
+ table {
10
+ border-collapse:collapse;
11
+ border-spacing:0;
12
+ }
13
+ fieldset,img {border:0;}
14
+ address,caption,cite,code,dfn,th,var {
15
+ font-style:normal;
16
+ font-weight:normal;
17
+ }
18
+
19
+ h1,h2,h3,h4,h5,h6 {
20
+ font-size:100%;
21
+ font-weight:normal;
22
+ }
23
+
24
+ ol,ul {list-style:none;}
25
+ caption,th {text-align:left;}
26
+ q:before,q:after {content:'';}
27
+ abbr,acronym {border:0;}
28
+
29
+ img {border: none;}
30
+ em em {font-style: normal;}
@@ -0,0 +1,336 @@
1
+ /*-------------------------------------------------
2
+ Defaults
3
+ -------------------------------------------------*/
4
+
5
+ acronym, abbr {
6
+ font-variant: small-caps;
7
+ text-transform: lowercase;
8
+ font-weight: bold;
9
+ }
10
+
11
+ .center {text-align: center;}
12
+ .large {font-size: larger;}
13
+ .small {font-size: smaller;}
14
+ strong {font-weight: bold;}
15
+ em {font-style: italic;}
16
+
17
+ .clear {clear: both;}
18
+ .clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
19
+ .clearfix { display: inline-block; }
20
+ .clearfix{ display: block; }
21
+
22
+ a {color: #888;}
23
+ a:hover {color: #000;}
24
+
25
+ /*-------------------------------------------------
26
+ Layout
27
+ -------------------------------------------------*/
28
+
29
+ body {
30
+ font-family: "Lucida Sans", "Lucida Grande", "Lucida Sans Unicode", sans-serif;
31
+ background: #e5e5e5;
32
+ color: #333;
33
+ margin: 0;
34
+ padding: 0;
35
+ font-size: 14px;
36
+ line-height: 21px;
37
+ text-align: left;
38
+ }
39
+
40
+ div#container {
41
+ margin: 2% 4%;
42
+ padding: 25px;
43
+ background: #fff;
44
+ -webkit-border-radius: 20px;
45
+ -moz-border-radius: 20px;
46
+ border-radius: 20px;
47
+ box-shadow: 0px 0px 15px rgba(00,00,00,0.5);
48
+ -moz-box-shadow: 0px 0px 15px rgba(00,00,00,0.5);
49
+ -webkit-box-shadow: 0px 0px 15px rgba(00,00,00,0.5);
50
+ }
51
+
52
+ div#head {
53
+ margin: -25px -25px 0;
54
+ background: #111;
55
+ color: #999;
56
+ padding: 25px 25px 20px 15px;
57
+ -webkit-border-top-left-radius: 20px;
58
+ -webkit-border-top-right-radius: 20px;
59
+ -moz-border-radius-topleft: 20px;
60
+ -moz-border-radius-topright: 20px;
61
+ border-top-left-radius: 20px;
62
+ border-top-right-radius: 20px;
63
+ font-size: 18px;
64
+ }
65
+
66
+ div#head a {
67
+ color: #2fadcf;
68
+ font-weight: bold;
69
+ text-decoration: none;
70
+ }
71
+
72
+ div#head span.home {
73
+ -webkit-border-top-left-radius: 10px;
74
+ -webkit-border-bottom-left-radius: 10px;
75
+ -moz-border-radius-topleft: 10px;
76
+ -moz-border-radius-bottomleft: 10px;
77
+ border-top-left-radius: 10px;
78
+ border-bottom-left-radius: 10px;
79
+ background: #333;
80
+ padding: 8px 6px 8px 12px;
81
+ margin-right: 1px;
82
+ }
83
+
84
+ div#head span.locale {
85
+ color: #fff;
86
+ font-weight: bold;
87
+ background: #444;
88
+ padding: 8px 12px 8px 6px;
89
+ -webkit-border-top-right-radius: 10px;
90
+ -webkit-border-bottom-right-radius: 10px;
91
+ -moz-border-radius-topright: 10px;
92
+ -moz-border-radius-bottomright: 10px;
93
+ border-top-right-radius: 10px;
94
+ border-bottom-right-radius: 10px;
95
+ }
96
+
97
+ div#head span.locale.empty {
98
+ background: #333;
99
+ padding-left: 0;
100
+ margin-left: -6px;
101
+ }
102
+
103
+ div#head span.locale em {
104
+ font-style: normal;
105
+ font-weight: normal;
106
+ }
107
+
108
+ div#head span.note {
109
+ color: #777;
110
+ font-size: 13px;
111
+ font-weight: normal;
112
+ margin-left: 10px;
113
+ }
114
+
115
+ h2,
116
+ h3 {
117
+ font-size: 18px;
118
+ color: #2fadcf;
119
+ margin: 25px 0 10px;
120
+ }
121
+
122
+ h2 span,
123
+ h3 span {
124
+ font-size: 13px;
125
+ color: #888;
126
+ }
127
+
128
+ ul.locales {
129
+ margin: 25px 0;
130
+ }
131
+
132
+ ul.locales li {
133
+ /*width: 230px;*/
134
+ width: 150px;
135
+ display: block;
136
+ float: left;
137
+ margin: 0 10px 10px 0;
138
+ /* background: #fff;
139
+ -webkit-border-radius: 5px;
140
+ -moz-border-radius: 5px;
141
+ border-radius: 5px;
142
+ border: 1px solid #e5e5e5;
143
+ padding: 5px 10px;*/
144
+ }
145
+
146
+ ul.locales a {
147
+ font-weight: bold;
148
+ text-decoration: underline;
149
+ color: #2fadcf;
150
+ }
151
+
152
+ /*ul.locales li:hover {
153
+ background: #f5f5f5;
154
+ border-color: #ccc;
155
+ }*/
156
+
157
+ ul.locales span.missing_translations {
158
+ color: #fff;
159
+ font-weight: bold;
160
+ background: #c00;
161
+ font-size: 9px;
162
+ padding: 3px;
163
+ -webkit-border-radius: 3px;
164
+ -moz-border-radius: 3px;
165
+ border-radius: 3px;
166
+ line-height: 9px;
167
+ vertical-align: top;
168
+ display: inline-block;
169
+ }
170
+
171
+ ul.locales span {
172
+ font-size: 13px;
173
+ color: #666;
174
+ }
175
+
176
+ div.submit {
177
+ background: #f5f5f5;
178
+ margin: 25px -25px -25px;
179
+ padding: 15px 25px;
180
+ border-top: 1px dashed #ccc;
181
+ -webkit-border-bottom-right-radius: 20px;
182
+ -webkit-border-bottom-left-radius: 20px;
183
+ -moz-border-radius-bottomright: 20px;
184
+ -moz-border-radius-bottomleft: 20px;
185
+ border-bottom-right-radius: 20px;
186
+ border-bottom-left-radius: 20px;
187
+ }
188
+
189
+ span.updated {
190
+ font-size: 11px;
191
+ padding: 1px;
192
+ background: #ffc;
193
+ color: #777;
194
+ margin-bottom: 10px;
195
+ float: right;
196
+ }
197
+
198
+ div.table_submit {
199
+ background: #f5f5f5;
200
+ margin: -25px 0 0;
201
+ padding: 12px 15px 15px;
202
+ text-align: left;
203
+ }
204
+
205
+ div.translations {
206
+ width: 96%;
207
+ text-align: center;
208
+ }
209
+
210
+ span.notice {
211
+ background: #ffc;
212
+ color: #666;
213
+ font-size: 12px;
214
+ padding: 2px 5px;
215
+ margin: -5px 0 15px;
216
+ display: inline-block;
217
+ }
218
+
219
+ div.original {
220
+ color: #999;
221
+ margin: 5px 0;
222
+ padding: 1px 8px 4px;
223
+ }
224
+
225
+ div.updated {
226
+ background: #ffc;
227
+ padding: 1px 8px 4px;
228
+ }
229
+
230
+ table.translations div.original span.key {
231
+ margin: 0 0 -2px;
232
+ padding: 0;
233
+ }
234
+
235
+ table.translations div.updated span.key {
236
+ margin: 0 0 -2px;
237
+ color: orange !important;
238
+ padding: 0;
239
+ }
240
+
241
+ /*-------------------------------------------------
242
+ Translation tables
243
+ -------------------------------------------------*/
244
+
245
+ table.translations {
246
+ margin: 0 0 25px;
247
+ width: 100%;
248
+ text-align: left;
249
+ }
250
+
251
+ table.translations td,
252
+ table.translations th {
253
+ font-size: 14px;
254
+ color: #222;
255
+ padding: 12px 8px;
256
+ border-bottom: 1px solid #e5e5e5;
257
+ vertical-align: top;
258
+ width: 50%;
259
+ }
260
+
261
+ table.translations th {
262
+ border-bottom-color: #bbb;
263
+ font-size: 11px;
264
+ font-weight: bold;
265
+ text-transform: uppercase;
266
+ color: #999;
267
+ padding-bottom: 2px;
268
+ }
269
+
270
+ table.translations textarea.locale {
271
+ font-family: "Lucida Sans", "Lucida Grande", "Lucida Sans Unicode", sans-serif;
272
+ font-size: 14px;
273
+ line-height: 21px;
274
+ color: #222;
275
+ padding: 1px;
276
+ width: 100%;
277
+ height: 42px;
278
+ }
279
+
280
+ table.translations span.key {
281
+ color: #aaa;
282
+ font-size: 9px;
283
+ display: block;
284
+ }
285
+
286
+ table.translations td.translation {
287
+ padding: 10px 8px;
288
+ }
289
+
290
+ table.translations tr.active td {
291
+ background: #edf9fe;
292
+ }
293
+
294
+ table.translations .highlight {
295
+ background-color: yellow;
296
+ }
297
+
298
+ /*-------------------------------------------------
299
+ Pagination
300
+ -------------------------------------------------*/
301
+
302
+ div.paginate {
303
+ margin: 15px auto 20px;
304
+ font-size: 12px;
305
+ color: #777;
306
+ }
307
+
308
+ div.paginate a,
309
+ div.paginate span {
310
+ padding: 2px 6px;
311
+ text-decoration: none;
312
+ }
313
+
314
+ div.paginate a:hover,
315
+ div.paginate span.current {
316
+ -webkit-border-radius: 5px;
317
+ -moz-border-radius: 5px;
318
+ border-radius: 5px;
319
+ background: #eee;
320
+ color: #333;
321
+ }
322
+
323
+ div.paginate .next_page,
324
+ div.paginate .prev_page {
325
+ margin: 0 15px;
326
+ -webkit-border-radius: 5px;
327
+ -moz-border-radius: 5px;
328
+ border-radius: 5px;
329
+ border: 1px solid #bbb;
330
+ padding: 4px 8px;
331
+ }
332
+
333
+ div.paginate .disabled {
334
+ color: #ccc;
335
+ border-color: #eee;
336
+ }