tr8n 3.1.6 → 3.1.7
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.
- data/Gemfile.lock +1 -1
- data/{local/tr8n_server/app/assets/stylesheets/admin.css → app/assets/stylesheets/tr8n/admin.css.scss} +0 -0
- data/app/assets/stylesheets/tr8n/components.css.scss +211 -0
- data/app/assets/stylesheets/tr8n/layout.css.scss +143 -0
- data/app/models/tr8n/language_rule.rb +31 -29
- data/app/models/tr8n/sync_log.rb +114 -19
- data/app/models/tr8n/translation.rb +43 -40
- data/app/models/tr8n/translation_key.rb +48 -18
- data/config/routes.rb +1 -1
- data/lib/generators/tr8n/templates/config/tr8n/config.yml +2 -2
- data/lib/generators/tr8n/templates/layouts/tr8n.html.erb +4 -1
- data/lib/generators/tr8n/templates/layouts/tr8n_admin.html.erb +5 -3
- data/lib/generators/tr8n/tr8n_generator.rb +1 -1
- data/lib/tasks/tr8n.rake +4 -2
- data/lib/tr8n/config.rb +1 -1
- data/lib/tr8n/version.rb +1 -1
- data/local/tr8n_server/app/assets/stylesheets/admin.css.scss +200 -0
- data/local/tr8n_server/app/assets/stylesheets/{application.css → application.css.scss} +0 -0
- metadata +43 -42
- data/app/controllers/tr8n/api/v1/sync_controller.rb +0 -30
- data/app/controllers/tr8n/api/v1/translator_controller.rb +0 -34
data/Gemfile.lock
CHANGED
File without changes
|
@@ -0,0 +1,211 @@
|
|
1
|
+
/* General */
|
2
|
+
|
3
|
+
.flt_r {float:right}
|
4
|
+
.quiet {color:#888 !important;}
|
5
|
+
.strong {font-weight:bold}
|
6
|
+
.em {font-style:italic}
|
7
|
+
.small {font-size:11px}
|
8
|
+
.xsmall {font-size:10px}
|
9
|
+
.sub_heading {border-bottom:solid 1px #ddd;margin-bottom:3px;}
|
10
|
+
.notice_msg {color:darkorange;background:#feffef;padding:3px 5px;margin:0 0 3px 0}
|
11
|
+
.warning_msg {color:darkred;background:pink;padding:3px 5px;margin:0 0 3px 0}
|
12
|
+
.focus {background-color:#f1f5ff}
|
13
|
+
.highlight {background-color:#fefff1}
|
14
|
+
.highlight_hover:hover {background:#fefff1}
|
15
|
+
.nowrap {white-space:nowrap}
|
16
|
+
.underscore {border-bottom:solid 1px #eee}
|
17
|
+
|
18
|
+
.light_bevel_hr {height:0px;border:solid 1px #e1e5e8;border-bottom-color:#fff;}
|
19
|
+
|
20
|
+
.padding_0 {padding:0px !important;}
|
21
|
+
.padding_5 {padding:5px !important;}
|
22
|
+
.padding_10 {padding:10px !important;}
|
23
|
+
.padding_15 {padding:15px !important;}
|
24
|
+
|
25
|
+
/* Module Skins */
|
26
|
+
|
27
|
+
.basic .inner {padding:10px;}
|
28
|
+
|
29
|
+
.outset .inner {border:1px solid #D7D7D7;border-color:#e9e9e9 #cdcdcd #a4a4a4;border-width:1px 1px 2px;padding:10px;}
|
30
|
+
.outset b {background-image:url('rounded_corners_sprite.gif');}
|
31
|
+
.outset .tl {background-position: 0 -70px;}
|
32
|
+
.outset .tr {background-position: -10px -70px;}
|
33
|
+
.outset .bl {background-position: 0 -80px;}
|
34
|
+
.outset .br {background-position: -10px -80px;}
|
35
|
+
|
36
|
+
.inset .inner {border:1px solid #e1e5e8;border-color:#b1b3b7 #e1e5e8 #e1e5e8;border-width:2px 1px 1px;padding:10px;}
|
37
|
+
.inset b {background-image:url('rounded_corners_sprite.gif');}
|
38
|
+
.inset .tl {background-position: 0 -93px;}
|
39
|
+
.inset .tr {background-position: -10px -93px;}
|
40
|
+
.inset .bl {background-position: 0 -103px;}
|
41
|
+
.inset .br {background-position: -10px -103px;}
|
42
|
+
|
43
|
+
.rounded .inner {padding:10px;text-align:left;}
|
44
|
+
.rounded b {background-image:url('rounded_corners_sprite.gif');}
|
45
|
+
.rounded .tl {background-position: 0 -116px;}
|
46
|
+
.rounded .tr {background-position: -10px -116px;}
|
47
|
+
.rounded .bl {background-position: 0 -126px;}
|
48
|
+
.rounded .br {background-position: -10px -126px;}
|
49
|
+
|
50
|
+
|
51
|
+
/* Module Backgrounds */
|
52
|
+
|
53
|
+
.highlight_bevel {background:url('/images/highlight_bkgd.gif') repeat-x 0 bottom #fefff1}
|
54
|
+
.focus_bevel {background:url('/images/focus_bkgd.gif') repeat-x 0 bottom #edf2f8}
|
55
|
+
.success_bevel {background:url('/images/success_bkgd.gif') repeat-x 0 bottom #eefce7}
|
56
|
+
|
57
|
+
|
58
|
+
/* Module Headers */
|
59
|
+
.pagination_hd {border-bottom:solid 1px #ddd;padding:5px 0}
|
60
|
+
|
61
|
+
/* Module Body */
|
62
|
+
|
63
|
+
|
64
|
+
/* Module Footers */
|
65
|
+
.action_ft {background:#fbfbfb;border-top:solid 1px #eee;padding:15px 20px;}
|
66
|
+
.pagination_ft {border-top:solid 1px #ddd;padding:5px 0;margin-top:-1px;}
|
67
|
+
|
68
|
+
|
69
|
+
/* Forms */
|
70
|
+
|
71
|
+
.field {clear:both;padding:5px 0 7px 0;overflow:hidden;}
|
72
|
+
.field_hd {float:left;width:145px;text-align:right;color:#666;}
|
73
|
+
.field_bd {margin-left:165px;clear:right}
|
74
|
+
.field_section {float:left;margin-right:5px;color:#999;font-size:10px;}
|
75
|
+
.field_note {margin-top:5px;font-size:10px;color:#666;clear:both;}
|
76
|
+
.field_hint {float:right;font-size:10px;color:#666;width:170px;line-height:11px;font-weight:normal}
|
77
|
+
|
78
|
+
.xshort_input {width:50px}
|
79
|
+
.short_input {width:100px}
|
80
|
+
.medium_input {width:150px}
|
81
|
+
.long_input {width:200px}
|
82
|
+
.xlong_input {width:250px}
|
83
|
+
.full_width_input {width:98%}
|
84
|
+
|
85
|
+
|
86
|
+
/* Lists */
|
87
|
+
|
88
|
+
ol.simple_list li {list-style-type: decimal; margin-left:40px;}
|
89
|
+
ul.simple_list li {list-style-type:disc; margin-left:0px;padding:4px 0}
|
90
|
+
|
91
|
+
.nav_list,
|
92
|
+
.horiz_list,
|
93
|
+
.right_horiz_list {overflow:hidden;text-align:left}
|
94
|
+
|
95
|
+
.nav_list li {margin-top:-1px;border-top:solid 1px #ced2e4;padding:3px 5px;zoom:1}
|
96
|
+
.nav_list li:hover {background-color:#ebeef1}
|
97
|
+
.nav_list li a {text-decoration:none;}
|
98
|
+
|
99
|
+
.horiz_list li {margin-left:-11px;border-left:solid 1px #ccc;padding:0px 10px;margin-right:10px;float:left;}
|
100
|
+
.right_horiz_list li {margin-right:-11px;border-right:solid 1px #ccc;padding:0px 10px;margin-left:10px;float:left;}
|
101
|
+
|
102
|
+
.checklist li {overflow:hidden;padding:0px 0}
|
103
|
+
.checklist input {float:left;}
|
104
|
+
.checklist label {margin:0 0 5px 25px;display:block}
|
105
|
+
|
106
|
+
.segmented_list {overflow:hidden;}
|
107
|
+
.segmented_list li {padding:7px 0;border-bottom:solid 1px #eee;margin-bottom:-1px}
|
108
|
+
|
109
|
+
.block_list li {padding:3px 0}
|
110
|
+
|
111
|
+
|
112
|
+
/* Pagination */
|
113
|
+
|
114
|
+
.pagination {overflow:hidden;zoom:1}
|
115
|
+
.pagination li {padding:0 3px;margin-right:2px;float:left;font-weight:bold;}
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
/* Buttons */
|
120
|
+
|
121
|
+
.btn {position:relative;border:0;padding:0;margin:0 2px 0 -2px;cursor:pointer;overflow:visible;background:transparent url('/images/site_sprite.gif') no-repeat right -100px;font-size:11px;font-weight:bold;text-align:center;font-family:Arial}
|
122
|
+
.btn span {position:relative;display:block;white-space:nowrap;background:transparent url('/images/site_sprite.gif') no-repeat left top;}
|
123
|
+
button::-moz-focus-inner {border:none;}
|
124
|
+
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
125
|
+
.btn span {margin-top:-1px;}
|
126
|
+
}
|
127
|
+
a.btn {display:block;display:-moz-inline-box;display:inline-block;vertical-align:middle}
|
128
|
+
a.btn span {display:block;margin-top:0px}
|
129
|
+
|
130
|
+
/* Button Skins */
|
131
|
+
|
132
|
+
.button {-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;-webkit-box-shadow: rgba(0, 0, 0, 0.246094) 0px 1px 3px;-moz-box-shadow: rgba(0, 0, 0, 0.246094) 0px 1px 3px;background-color: #222;border-bottom: 1px solid rgba(0, 0, 0, 0.246094);color: white !important;cursor: pointer;display: inline-block;font-size: 13px;font-weight: bold;line-height: 1;overflow: visible;padding: 5px 15px 6px;position: relative;text-decoration: none !important;width: auto;}
|
133
|
+
|
134
|
+
.super.button {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAeCAYAAADtlXTHAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAABYSURBVHjaZM27EcAgDANQSzZZgP0XpKLh20BM7mjS6J7kwrrWEuScgZQS2HsXjjHEdaqytfZ4kLXWU81lruAK9wqWUuBVvni3n+4Pc9nRnFOUZFQAcQswAI/sUZSGPd0tAAAAAElFTkSuQmCC) repeat-x 0 bottom;background-position:0 bottom;border: 1px solid rgba(0, 0, 0, 0.246094);font-size: 12px;padding: 0px;}
|
135
|
+
.super.button span {border-top: 1px solid rgba(255, 255, 255, 0.199219);display: block;line-height: 1;padding: 4px 15px 6px;}
|
136
|
+
|
137
|
+
.green.button {background-color: #91BD09;}
|
138
|
+
.green.button:hover {background-color:#749a02}
|
139
|
+
|
140
|
+
.blue.button {background-color:#5689c0}
|
141
|
+
.blue.button:hover {background-color:#4a77a8}
|
142
|
+
|
143
|
+
.large.super.button span {padding: 7px 20px 9px;font-size:13px;}
|
144
|
+
.xlarge.super.button span {padding: 8px 20px 10px;font-size:16px;}
|
145
|
+
|
146
|
+
/* Icons */
|
147
|
+
|
148
|
+
.icn {background:url(/images/icon_sprite.gif) no-repeat 0 0;}
|
149
|
+
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
/* Tabs */
|
154
|
+
|
155
|
+
.tab_nav {list-style-type:none;overflow:auto;margin-bottom:0px !important;position:relative}
|
156
|
+
.tab_nav li {float:left;margin-right:2px;cursor:pointer;}
|
157
|
+
.tab_nav li a {display:block;padding:6px 10px 6px 10px;}
|
158
|
+
|
159
|
+
/* Tables */
|
160
|
+
|
161
|
+
.data_table {padding:0;width:auto;margin:0}
|
162
|
+
.data_table td, .data_table th {vertical-align:top;padding:1px 0px 2px 0px}
|
163
|
+
.data_table th {width:115px;color:#777;font-weight:normal;padding-right:6px}
|
164
|
+
|
165
|
+
.data_table.small th {width:70px}
|
166
|
+
|
167
|
+
.segmented_table {width:100%;}
|
168
|
+
.segmented_table td {border-bottom:solid 1px #eee;padding:10px 5px}
|
169
|
+
.segmented_table .last td {border:none}
|
170
|
+
|
171
|
+
.skinny_cells td {padding:5px}
|
172
|
+
.fat_cells td {padding:10px 5px}
|
173
|
+
|
174
|
+
.txt_c {text-align:center;}
|
175
|
+
.txt_l {text-align:left;}
|
176
|
+
.txt_r {text-align:right;}
|
177
|
+
.txt_t {vertical-align:top;}
|
178
|
+
.txt_b {vertical-align:bottom;}
|
179
|
+
.txt_m {vertical-align:middle;}
|
180
|
+
|
181
|
+
.min_cell_width {width:1%}
|
182
|
+
.max_cell_width {width:99%}
|
183
|
+
|
184
|
+
.mrgn_v {margin:10px 0}
|
185
|
+
.mrgn_h {margin:0 10px}
|
186
|
+
|
187
|
+
/* Media Block */
|
188
|
+
|
189
|
+
.media {overflow:hidden;_overflow:visible; zoom:1;}
|
190
|
+
.media .img {float:left;margin-right: 10px;}
|
191
|
+
.media .img img {display:block;}
|
192
|
+
.media .imgExt {float:right; margin-left: 10px;}
|
193
|
+
|
194
|
+
|
195
|
+
|
196
|
+
|
197
|
+
/* Home */
|
198
|
+
|
199
|
+
.home_banner {background:url('/images/bubbles_bkgd.jpg');-webkit-border-radius:5px 5px 0 0;-moz-border-radius:5px 5px 0 0;border-radius:5px 5px 0 0;padding:50px 25px 0px 25px;height:220px;border-bottom:solid 10px #c2d6eb}
|
200
|
+
.home_banner h1 {font:normal bold 45px Arial, Helvetica, sans-serif;color:#fff;text-shadow:0 -1px 1px #000;letter-spacing:-2px}
|
201
|
+
.home_banner h2 {font:normal normal 24px Arial, Helvetica, sans-serif;color:#ccc;text-shadow:0 -1px 1px #000;width:520px;}
|
202
|
+
|
203
|
+
|
204
|
+
.benefit_details {line-height:20px;height:70px;}
|
205
|
+
.page_form {margin:0 50px}
|
206
|
+
|
207
|
+
.tr8n {
|
208
|
+
width:auto !important;
|
209
|
+
overflow:hidden !important
|
210
|
+
}
|
211
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
@mixin shadow($info) {
|
2
|
+
text-shadow:$info;
|
3
|
+
-moz-text-shadow:$info;
|
4
|
+
-webkit-text-shadow:$info;
|
5
|
+
}
|
6
|
+
|
7
|
+
@mixin rounded-corners($radius) {
|
8
|
+
border-radius: $radius;
|
9
|
+
-moz-border-radius: $radius;
|
10
|
+
-webkit-border-radius: $radius;
|
11
|
+
}
|
12
|
+
|
13
|
+
body {margin:0;padding:0;font:normal normal 12px Arial, Helvetica, sans-serif;text-align:left;background:#f5f5f5;color:#444;}
|
14
|
+
|
15
|
+
h1,h2,h3,h4,h5,h6 {margin:0 0 5px 0;padding:0;line-height: 1em;}
|
16
|
+
h1,.h1 {font:normal normal 35px Georgia, serif;color:#424c56;letter-spacing:-1px;}
|
17
|
+
h2,.h2 {font: normal normal normal 24px/normal Arial, Helvetica, sans-serif;color:#666;margin:0 0 8px 0;letter-spacing:-1px;}
|
18
|
+
h3,.h3 {font: normal normal bold 18px/normal Arial, Helvetica, sans-serif;color:#555;margin:0 0 8px 0;padding-bottom:5px;}
|
19
|
+
h4,.h4 {font:normal normal 14px Arial, Helvetica, sans-serif;color:#444;padding:6px;}
|
20
|
+
h5,.h5 {font:normal bold 11px Arial, Helvetica, sans-serif;color:#999}
|
21
|
+
h6,.h6 {font:normal bold 11px Arial, Helvetica, sans-serif;color:#444}
|
22
|
+
|
23
|
+
|
24
|
+
a {color:#3B5998;text-decoration: none;outline:none}
|
25
|
+
a:focus, a:hover {text-decoration: underline }
|
26
|
+
a:visited {color:#3B5998;}
|
27
|
+
|
28
|
+
p {margin:0 0 10px 0;}
|
29
|
+
table {border-collapse:collapse;border-spacing:0}
|
30
|
+
table,ul,ol,dt,dl,dd {padding:0;margin:0;list-style-type:none}
|
31
|
+
td,th {vertical-align:top}
|
32
|
+
img {vertical-align:middle;border:none}
|
33
|
+
input, select, textarea {vertical-align:middle;border:solid 1px #eee;border-color:#bbb #eee #eee #bbb;border-width:1px;font:normal normal 11px Arial, Helvetica, sans-serif;color:#444;outline:none;padding:3px;}
|
34
|
+
select {padding:2px;}
|
35
|
+
input[type=checkbox],
|
36
|
+
input[type=radio],
|
37
|
+
input[type=file],
|
38
|
+
checkbox,
|
39
|
+
radio {border:none;padding:0;}
|
40
|
+
|
41
|
+
|
42
|
+
/* Page Template */
|
43
|
+
|
44
|
+
.page_fixed {width:1000px;margin:0 auto;}
|
45
|
+
.page_elastic {width:auto;margin:0;}
|
46
|
+
.page_fluid {max-width:1100px;min-width:925px;_width:960px;margin:0 auto;padding:0 25px;}
|
47
|
+
|
48
|
+
.page_body,
|
49
|
+
.page_body .main_col {overflow:hidden;_overflow:visible;_zoom:1;}
|
50
|
+
.page_body .left_col {float:left; width:250px;_margin-right:-3px;}
|
51
|
+
.page_body .right_col {float:right; width: 300px;_margin-left:-3px;}
|
52
|
+
|
53
|
+
|
54
|
+
/* Header */
|
55
|
+
|
56
|
+
.page_head {background:#fff;border-bottom: 1px solid #DDD;border-top: 4px solid #6699cc;margin-bottom: 15px;padding: 5px 15px 2px;}
|
57
|
+
.logo a {
|
58
|
+
font-family:Arial;
|
59
|
+
color:#424c56;
|
60
|
+
text-decoration:none;
|
61
|
+
@include shadow(2px 3px 3px 2px rgba(0,0,0,0.6));
|
62
|
+
}
|
63
|
+
.logo a:hover {color:#000}
|
64
|
+
.top_nav {padding-top:13px;}
|
65
|
+
|
66
|
+
/* Grids */
|
67
|
+
|
68
|
+
.line, .last_unit {overflow:hidden;_overflow:visible;_zoom:1; }
|
69
|
+
.unit {float:left;_zoom:1;}
|
70
|
+
.size_1of1 {float:none;}
|
71
|
+
.size_1of2 {width:50%;}
|
72
|
+
.size_1of3 {width:33.33333%;}
|
73
|
+
.size_2of3 {width:66.66666%;}
|
74
|
+
.size_1of4 {width:25%;}
|
75
|
+
.size_3of4 {width:75%;}
|
76
|
+
.size_1of5 {width:20%;}
|
77
|
+
.size_2of5 {width:40%;}
|
78
|
+
.size_3of5 {width:60%;}
|
79
|
+
.size_4of5 {width:80%;}
|
80
|
+
.last_unit {float:none;_position:relative;_left:-3px;_margin-right:-3px;width:auto;}
|
81
|
+
|
82
|
+
|
83
|
+
/* Page Content */
|
84
|
+
|
85
|
+
.content_module {margin:0 10px 10px;}
|
86
|
+
.content_inner {
|
87
|
+
border:1px solid #D7D7D7;
|
88
|
+
border-color:#e9e9e9 #cdcdcd #a4a4a4;
|
89
|
+
border-width:1px 1px 2px;
|
90
|
+
background:#fff;
|
91
|
+
@include rounded-corners(10px);
|
92
|
+
}
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
.content_hd,
|
97
|
+
.content_bd,
|
98
|
+
.content_ft {overflow:hidden;zoom:1;padding:25px}
|
99
|
+
|
100
|
+
|
101
|
+
|
102
|
+
/* Module */
|
103
|
+
|
104
|
+
.module {margin:10px;}
|
105
|
+
.hd,.bd,.ft {overflow:hidden;_overflow:visible;_zoom:1;}
|
106
|
+
.inner {position:relative;}
|
107
|
+
|
108
|
+
|
109
|
+
/* Rounded Corners */
|
110
|
+
|
111
|
+
.tl, .tr, .bl, .br {height:10px; width:10px;position:absolute;}
|
112
|
+
.tl {background-position: left top;left:0}
|
113
|
+
.tr {background-position: right top;}
|
114
|
+
.bl {background-position: left bottom;left:0}
|
115
|
+
.br {background-position: right bottom;}
|
116
|
+
.br,.tr {right:0}
|
117
|
+
.tr,.tl {overflow:hidden;margin-bottom:-32000px;}
|
118
|
+
.bl,.br {bottom:0}
|
119
|
+
|
120
|
+
|
121
|
+
/* Complex Module */
|
122
|
+
|
123
|
+
.complex {overflow:visible;margin: 10px 20px 20px 10px; background-position:left top;}
|
124
|
+
.complex .inner {right:-10px; bottom:-10px; background-position:right bottom;padding:0 10px 10px 0;}
|
125
|
+
.complex .tl, .complex .br {display:none;}
|
126
|
+
.complex .bl {bottom:-10px;}
|
127
|
+
.complex .tr {right:-10px;}
|
128
|
+
|
129
|
+
|
130
|
+
/* Media Block */
|
131
|
+
|
132
|
+
.media {overflow:auto;zoom:1}
|
133
|
+
.media .thumb {width:100px;float:left;}
|
134
|
+
.media .desc {margin-left:110px;}
|
135
|
+
|
136
|
+
|
137
|
+
/* Clear Floats */
|
138
|
+
.clearfix:after {visibility: hidden;display: block;font-size: 0;content: " ";clear: both;height: 0;}
|
139
|
+
.clearfix {display: inline-block; }
|
140
|
+
/* start commented backslash hack \*/
|
141
|
+
* html .clearfix {height: 1%;}
|
142
|
+
.clearfix {display:block;}
|
143
|
+
/* close commented backslash hack */
|
@@ -61,28 +61,6 @@ class Tr8n::LanguageRule < ActiveRecord::Base
|
|
61
61
|
dependency
|
62
62
|
end
|
63
63
|
|
64
|
-
# {"locale"=>"ru", "label"=>"{count} сообщения", "rank"=>1, "rules"=>[
|
65
|
-
# {"token"=>"count", "type"=>"number", "definition"=>
|
66
|
-
# {"multipart"=>true, "part1"=>"ends_in", "value1"=>"2,3,4", "operator"=>"and", "part2"=>"does_not_end_in", "value2"=>"12,13,14"}
|
67
|
-
# }
|
68
|
-
# ]
|
69
|
-
# }
|
70
|
-
|
71
|
-
def self.for_definition(lang, translator, type, definition, opts = {})
|
72
|
-
opts[:force_create] ||= false
|
73
|
-
|
74
|
-
rule_class = Tr8n::Config.language_rule_dependencies[type]
|
75
|
-
return if rule_class == nil # unsupported rule type, skip this completely
|
76
|
-
|
77
|
-
rule_class.for(lang).each do |rule|
|
78
|
-
return rule if rule.definition == definition
|
79
|
-
end
|
80
|
-
|
81
|
-
if opts[:force_create]
|
82
|
-
rule_class.create(:language => lang, :translator => translator, :definition => definition)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
64
|
# TDOD: switch to using keyword
|
87
65
|
def self.dependency
|
88
66
|
raise Tr8n::Exception.new("This method must be implemented in the extending rule")
|
@@ -102,13 +80,6 @@ class Tr8n::LanguageRule < ActiveRecord::Base
|
|
102
80
|
sanitize_values(values).join(", ")
|
103
81
|
end
|
104
82
|
|
105
|
-
def to_api_hash
|
106
|
-
{
|
107
|
-
:type => self.class.keyword,
|
108
|
-
:definition => definition
|
109
|
-
}
|
110
|
-
end
|
111
|
-
|
112
83
|
def evaluate(token_value)
|
113
84
|
raise Tr8n::Exception.new("This method must be implemented in the extending rule")
|
114
85
|
end
|
@@ -149,4 +120,35 @@ class Tr8n::LanguageRule < ActiveRecord::Base
|
|
149
120
|
Tr8n::Cache.delete("language_rule_#{id}")
|
150
121
|
end
|
151
122
|
|
123
|
+
###############################################################
|
124
|
+
## Synchronization Methods
|
125
|
+
###############################################################
|
126
|
+
def to_sync_hash(token)
|
127
|
+
{
|
128
|
+
"token" => token,
|
129
|
+
"type" => self.class.keyword,
|
130
|
+
"definition" => definition
|
131
|
+
}
|
132
|
+
end
|
133
|
+
|
134
|
+
# {"locale"=>"ru", "label"=>"{count} сообщения", "rank"=>1, "rules"=>[
|
135
|
+
# {"token"=>"count", "type"=>"number", "definition"=>
|
136
|
+
# {"multipart"=>true, "part1"=>"ends_in", "value1"=>"2,3,4", "operator"=>"and", "part2"=>"does_not_end_in", "value2"=>"12,13,14"}
|
137
|
+
# }
|
138
|
+
# ]
|
139
|
+
# }
|
140
|
+
|
141
|
+
def self.create_from_sync_hash(lang, translator, rule_hash, opts = {})
|
142
|
+
return unless rule_hash["token"] and rule_hash["type"] and rule_hash["definition"]
|
143
|
+
|
144
|
+
rule_class = Tr8n::Config.language_rule_dependencies[rule_hash["type"]]
|
145
|
+
return unless rule_class # unsupported rule type, skip this completely
|
146
|
+
|
147
|
+
rule_class.for(lang).each do |rule|
|
148
|
+
return rule if rule.definition == rule_hash["definition"]
|
149
|
+
end
|
150
|
+
|
151
|
+
rule_class.create(:language => lang, :translator => translator, :definition => rule_hash["definition"])
|
152
|
+
end
|
153
|
+
|
152
154
|
end
|
data/app/models/tr8n/sync_log.rb
CHANGED
@@ -23,25 +23,73 @@
|
|
23
23
|
|
24
24
|
class Tr8n::SyncLog < ActiveRecord::Base
|
25
25
|
|
26
|
-
def self.sync
|
27
|
-
|
28
|
-
|
26
|
+
def self.sync(opts = {})
|
27
|
+
sync_log = Tr8n::SyncLog.create(:started_at => Time.now)
|
28
|
+
|
29
29
|
translation_count = 0
|
30
30
|
payload = []
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
batch_count = 0
|
32
|
+
total_key_count = 0
|
33
|
+
|
34
|
+
log("Begin synchronization process...")
|
35
|
+
log("Registering keys...")
|
36
|
+
|
37
|
+
conditions = "synced_at is null or updated_at > synced_at"
|
38
|
+
conditions = nil if opts[:force]
|
39
|
+
|
40
|
+
# STDOUT.sync = true
|
41
|
+
|
42
|
+
Tr8n::TranslationKey.find_each(:conditions => conditions, :batch_size => Tr8n::Config.synchronization_batch_size) do |key|
|
43
|
+
total_key_count += 1
|
44
|
+
sync_hash = key.to_sync_hash
|
45
|
+
payload << sync_hash
|
35
46
|
|
36
|
-
|
47
|
+
key.mark_as_synced!
|
37
48
|
|
38
|
-
if
|
49
|
+
# if sync_hash["label"] == "you have {count||message}"
|
50
|
+
# payload << sync_hash
|
51
|
+
# end
|
52
|
+
|
53
|
+
if payload.size == Tr8n::Config.synchronization_batch_size
|
54
|
+
# pp "Sending #{batch_count+1} batch of #{Tr8n::Config.synchronization_batch_size} keys..."
|
55
|
+
batch_count += 1
|
39
56
|
exchange(payload)
|
57
|
+
payload = []
|
40
58
|
end
|
41
59
|
end
|
42
|
-
|
60
|
+
|
61
|
+
if payload.size > 0
|
62
|
+
# pp "Sending final batch..."
|
63
|
+
batch_count += 1
|
64
|
+
exchange(payload, opts)
|
65
|
+
end
|
66
|
+
|
67
|
+
sync_log.keys_sent = total_key_count
|
68
|
+
|
69
|
+
log("Sent #{total_key_count} keys in #{batch_count} batches.")
|
70
|
+
|
71
|
+
batch_count = 0
|
72
|
+
total_key_count = 0
|
73
|
+
|
74
|
+
unless opts[:force]
|
75
|
+
log("Downloading translations...")
|
76
|
+
|
77
|
+
key_count = download
|
78
|
+
while key_count > 0
|
79
|
+
batch_count += 1
|
80
|
+
total_key_count += key_count
|
81
|
+
key_count = download(opts)
|
82
|
+
end
|
83
|
+
log("Downloaded #{total_key_count} keys in #{batch_count} batches.")
|
84
|
+
end
|
85
|
+
|
86
|
+
sync_log.keys_received = total_key_count
|
87
|
+
sync_log.finished_at = Time.now
|
88
|
+
sync_log.save
|
89
|
+
|
43
90
|
rescue Exception => ex
|
44
91
|
pp ex.message
|
92
|
+
pp ex.backtrace
|
45
93
|
end
|
46
94
|
|
47
95
|
def self.access_token
|
@@ -49,28 +97,75 @@ class Tr8n::SyncLog < ActiveRecord::Base
|
|
49
97
|
uri = URI.parse("#{Tr8n::Config.synchronization_server}/platform/oauth/request_token?client_id=#{Tr8n::Config.synchronization_key}&client_secret=#{Tr8n::Config.synchronization_secret}&grant_type=client_credentials")
|
50
98
|
response = Net::HTTP.get_response(uri)
|
51
99
|
data = JSON.parse(response.body)
|
52
|
-
raise Tr8n::Exception.new("Failed to get access token") unless data["
|
100
|
+
raise Tr8n::Exception.new("Failed to get access token") unless data["access_token"]
|
53
101
|
data["access_token"]
|
54
102
|
end
|
55
103
|
end
|
56
104
|
|
57
|
-
def self.exchange(payload)
|
58
|
-
uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/sync")
|
105
|
+
def self.exchange(payload, opts = {})
|
106
|
+
uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
|
107
|
+
params = {:method => "register", :batch_size => Tr8n::Config.synchronization_batch_size, :translation_keys => payload}
|
59
108
|
|
60
109
|
req = Net::HTTP::Post.new(uri.path)
|
61
|
-
req.body = JSON.generate({:translation_keys => payload})
|
62
110
|
req["Content-Type"] = "application/json"
|
63
111
|
req["Authorization"] = "Bearer #{access_token}"
|
112
|
+
req.body = params.to_json
|
64
113
|
|
65
|
-
|
66
|
-
response = http.start {|htt| htt.request(req)}
|
67
|
-
raise Tr8n::Exception.new("Synchronization failed") unless response.status == 200
|
114
|
+
# pp payload
|
68
115
|
|
69
|
-
|
116
|
+
response = Net::HTTP.start(uri.host, uri.port) do |http|
|
117
|
+
http.request(req)
|
118
|
+
end
|
119
|
+
|
120
|
+
if response.is_a?(Net::HTTPInternalServerError)
|
121
|
+
raise Exception.new("Failed to synchronize keys: #{response.body}")
|
122
|
+
end
|
123
|
+
|
124
|
+
raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
|
125
|
+
|
126
|
+
data = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
127
|
+
# pp data
|
128
|
+
|
129
|
+
data[:translation_keys].each do |tkey_hash|
|
130
|
+
# pp tkey_hash
|
131
|
+
Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, Tr8n::Config.system_translator)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.download(opts = {})
|
136
|
+
uri = URI.parse("#{Tr8n::Config.synchronization_server}/api/application/sync")
|
137
|
+
params = {:method=>"download", :batch_size => Tr8n::Config.synchronization_batch_size}
|
138
|
+
params[:force] = true if opts[:force]
|
139
|
+
|
140
|
+
req = Net::HTTP::Post.new(uri.path)
|
141
|
+
req["Content-Type"] = "application/json"
|
142
|
+
req["Authorization"] = "Bearer #{access_token}"
|
143
|
+
req.body = params.to_json
|
144
|
+
|
145
|
+
response = Net::HTTP.start(uri.host, uri.port) do |http|
|
146
|
+
http.request(req)
|
147
|
+
end
|
148
|
+
|
149
|
+
if response.is_a?(Net::HTTPInternalServerError)
|
150
|
+
raise Exception.new("Failed to download translations: #{response.body}")
|
151
|
+
end
|
152
|
+
|
153
|
+
raise Tr8n::Exception.new("Synchronization failed") unless response.is_a?(Net::HTTPOK)
|
154
|
+
|
155
|
+
data = HashWithIndifferentAccess.new(JSON.parse(response.body))
|
156
|
+
# pp data
|
70
157
|
|
71
158
|
data[:translation_keys].each do |tkey_hash|
|
72
|
-
|
159
|
+
# pp tkey_hash
|
160
|
+
tkey, translations = Tr8n::TranslationKey.create_from_sync_hash(tkey_hash, Tr8n::Config.system_translator)
|
161
|
+
tkey.mark_as_synced!
|
73
162
|
end
|
163
|
+
|
164
|
+
data[:translation_keys].size
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.log(msg)
|
168
|
+
pp "#{Time.now}: #{msg}"
|
74
169
|
end
|
75
170
|
|
76
171
|
end
|