tolk 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }