Merge lp:~adiroiban/launchpad/bug-359180-take-2 into lp:launchpad
- bug-359180-take-2
- Merge into devel
Status: | Superseded | ||||||||
---|---|---|---|---|---|---|---|---|---|
Proposed branch: | lp:~adiroiban/launchpad/bug-359180-take-2 | ||||||||
Merge into: | lp:launchpad | ||||||||
Diff against target: |
802 lines (+446/-68) 11 files modified
lib/canonical/launchpad/javascript/translations/pofile.js (+306/-2) lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt (+4/-0) lib/lp/translations/browser/pofile.py (+20/-2) lib/lp/translations/browser/tests/pofile-views.txt (+20/-2) lib/lp/translations/browser/translationmessage.py (+24/-0) lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt (+2/-2) lib/lp/translations/stories/standalone/xx-pofile-translate.txt (+33/-5) lib/lp/translations/templates/currenttranslationmessage-translate-one.pt (+8/-11) lib/lp/translations/templates/pofile-translate.pt (+10/-43) lib/lp/translations/templates/translationmessage-translate.pt (+7/-0) lib/lp/translations/templates/translations-macros.pt (+12/-1) |
||||||||
To merge this branch: | bzr merge lp:~adiroiban/launchpad/bug-359180-take-2 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer (community) | code | Approve | |
Review via email: mp+17785@code.launchpad.net |
This proposal has been superseded by a proposal from 2010-02-25.
Commit message
Add keybindings on translating pofiles or translation messages.
Description of the change
= Bug 35180 =
Would be useful if the First - Previous - Next - Last link in the translation interface had a keyboard shortcut for fast accessing them. Don't need to be visible, but at least documented somewhere.
Maybe even the Save & Continue button.
== Proposed Fix ==
Add the following keybindings on +translate page for pofile and translaitonmessage
First field is autofocused.
Shift+Alt+b - Focus first translation field
Shift+Alt+a - First page
Shift+Alt+n - Next page
Shift+Alt+p - Previous page
Shift+Alt+l - Last page
Shift+Alt+s - Save and continue
Shift+Alt+Down - Next field
Shift+Alt+Up - Previous field
Shift+Alt+j - Next field
Shift+Alt+k - Previous field
Shift+Alt+C - Copy original text (both singular and plural)
Shift+Alt+0 - Mark current translation
Shift+Alt+NUMBER - Mark suggestion NUMBER
Shift+Alt+d - Dismiss all suggestions
Shift+Alt+r - Tick "Someone should review this translation"
== Pre-implementation notes ==
I talked with Danilo about keybindng implementation using comments for bug 35180
Previous MPs:
https:/
https:/
Part of this branch was already approved in previous MPs, but I have refactored most of those functions.
== Implementation notes ==
The legacy JS code from canonical/
Those functions from lp.js was rewritten using YUI3. Maybe we can no delete them from lp.js.
I have added a test for Bug #513625, since that bug was produces by some changes in this branch.
== Tests ==
I was not able to produce a reasonable windmill test since it is not trivial to find the current focused node or to see if a node is focused.
This requires adding onFocus and onBlur trigger for all DOM nodes.
./bin/test -t pofile-views
== Demo and QA ==
Log in as admin or as a person with rights on adding translations to a pofile.
Go to a pofile translate page:
https:/
The first field should have focus and you can start translating right away.
Adding a new translation should automatically select the radio button in front of it.
Try the keybindings described in the "Proposed fix" section.
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files:
lib/canonical
== JSLint notices ==
No handlers could be found for logger "bzr"
jslint: No problem found in '/home/
jslint: 1 file to lint.
Adi Roiban (adiroiban) wrote : | # |
Paul Hummer (rockstar) wrote : | # |
Please change line 140 of this diff so that the page tests conform with the 80 character line rule. Other than that, thanks for looking into this.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/javascript/translations/pofile.js' | |||
2 | --- lib/canonical/launchpad/javascript/translations/pofile.js 2010-01-27 07:38:02 +0000 | |||
3 | +++ lib/canonical/launchpad/javascript/translations/pofile.js 2010-02-25 13:00:44 +0000 | |||
4 | @@ -1,7 +1,7 @@ | |||
5 | 1 | /** Copyright (c) 2009, Canonical Ltd. All rights reserved. | 1 | /** Copyright (c) 2009, Canonical Ltd. All rights reserved. |
6 | 2 | * | 2 | * |
7 | 3 | * @module lp.pofile | 3 | * @module lp.pofile |
9 | 4 | * @requires event, node | 4 | * @requires anim, cookie, event-key, event, node |
10 | 5 | */ | 5 | */ |
11 | 6 | 6 | ||
12 | 7 | YUI.add('lp.pofile', function(Y) { | 7 | YUI.add('lp.pofile', function(Y) { |
13 | @@ -41,4 +41,308 @@ | |||
14 | 41 | } | 41 | } |
15 | 42 | }; | 42 | }; |
16 | 43 | 43 | ||
18 | 44 | }, "0.1", {"requires": ["event", "node"]}); | 44 | |
19 | 45 | var hide_notification = function(node) { | ||
20 | 46 | var hide_anim = new Y.Anim({ | ||
21 | 47 | node: node, | ||
22 | 48 | to: { height: 0, | ||
23 | 49 | marginTop: 0, marginBottom: 0, | ||
24 | 50 | paddingTop: 0, paddingBottom: 0 } | ||
25 | 51 | }); | ||
26 | 52 | node.setStyle('border', 'none'); | ||
27 | 53 | hide_anim.set('duration', 0.4); | ||
28 | 54 | hide_anim.on('end', function(e) { | ||
29 | 55 | node.setStyle('display', 'none'); | ||
30 | 56 | }); | ||
31 | 57 | hide_anim.run(); | ||
32 | 58 | } | ||
33 | 59 | |||
34 | 60 | self.updateNotificationBox = function(e) { | ||
35 | 61 | var notice = Y.one('.important-notice-container'); | ||
36 | 62 | var balloon = notice.one('.important-notice-balloon'); | ||
37 | 63 | var dismiss_notice_cookie = ('translation-docs-for-' + | ||
38 | 64 | documentation_cookie); | ||
39 | 65 | |||
40 | 66 | // Check the cookie to see if the user has already dismissed | ||
41 | 67 | // the notification box for this session. | ||
42 | 68 | var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean); | ||
43 | 69 | if (already_seen) { | ||
44 | 70 | notice.setStyle('display', 'none'); | ||
45 | 71 | } | ||
46 | 72 | |||
47 | 73 | var cancel_button = notice.one( | ||
48 | 74 | '.important-notice-cancel-button'); | ||
49 | 75 | // Cancel button starts out hidden. If user has JavaScript, | ||
50 | 76 | // then we want to show it. | ||
51 | 77 | cancel_button.setStyle('visibility', 'visible'); | ||
52 | 78 | cancel_button.on('click', function(e) { | ||
53 | 79 | e.halt(); | ||
54 | 80 | hide_notification(balloon); | ||
55 | 81 | Y.Cookie.set(dismiss_notice_cookie, true); | ||
56 | 82 | }); | ||
57 | 83 | }; | ||
58 | 84 | |||
59 | 85 | |||
60 | 86 | self.setFocus = function(field) { | ||
61 | 87 | // if there is nofield, do nothing | ||
62 | 88 | if (Y.one('#' + field)) { | ||
63 | 89 | Y.one('#' + field).focus(); | ||
64 | 90 | } | ||
65 | 91 | }; | ||
66 | 92 | |||
67 | 93 | |||
68 | 94 | var setNextFocus = function(e, field) { | ||
69 | 95 | self.setFocus(field); | ||
70 | 96 | // stopPropagation() and preventDefault() | ||
71 | 97 | e.halt(); | ||
72 | 98 | }; | ||
73 | 99 | |||
74 | 100 | |||
75 | 101 | var setPreviousFocus = function(e, field, original) { | ||
76 | 102 | |||
77 | 103 | // Original singular test is focused first to make sure | ||
78 | 104 | // it is visible when scrolling up | ||
79 | 105 | self.setFocus(original); | ||
80 | 106 | self.setFocus(field); | ||
81 | 107 | // stopPropagation() and preventDefault() | ||
82 | 108 | e.halt(); | ||
83 | 109 | }; | ||
84 | 110 | |||
85 | 111 | |||
86 | 112 | var copyOriginalTextOne = function(from_id, to_id, select_id) { | ||
87 | 113 | var from = Y.one('#' + from_id); | ||
88 | 114 | var to = Y.one('#' + to_id); | ||
89 | 115 | // The replacement regex strips all tags from the html. | ||
90 | 116 | to.set('value', unescapeHTML( | ||
91 | 117 | from.get('innerHTML').replace(/<\/?[^>]+>/gi, ""))); | ||
92 | 118 | selectWidgetByID(select_id); | ||
93 | 119 | }; | ||
94 | 120 | |||
95 | 121 | |||
96 | 122 | var copyOriginalTextPlural = function (from_id, | ||
97 | 123 | to_id_pattern, nplurals) { | ||
98 | 124 | // skip when x is 0, as that is the singular | ||
99 | 125 | for (var x = 1; x < nplurals; x++) { | ||
100 | 126 | var to_id = to_id_pattern + x + "_new"; | ||
101 | 127 | var to_select = to_id_pattern + x + "_new_select"; | ||
102 | 128 | copyOriginalTextOne(from_id, to_id, to_select); | ||
103 | 129 | } | ||
104 | 130 | }; | ||
105 | 131 | |||
106 | 132 | |||
107 | 133 | var copyOriginalTextAll = function(e, original_stem, translation_stem) { | ||
108 | 134 | |||
109 | 135 | var original_singular = original_stem + '_singular'; | ||
110 | 136 | var original_plural = original_stem + '_plural'; | ||
111 | 137 | var singular_select = translation_stem + '_translation_0_new_select'; | ||
112 | 138 | var translation_singular = translation_stem + '_translation_0_new'; | ||
113 | 139 | var translation_plural = translation_stem + '_translation_'; | ||
114 | 140 | // Copy singular text | ||
115 | 141 | copyOriginalTextOne( | ||
116 | 142 | original_singular, translation_singular, singular_select); | ||
117 | 143 | |||
118 | 144 | // Copy plural text if needed | ||
119 | 145 | if (Y.one('#' + translation_plural + '1')) { | ||
120 | 146 | copyOriginalTextPlural( | ||
121 | 147 | original_plural, translation_plural, plural_forms); | ||
122 | 148 | } | ||
123 | 149 | // stopPropagation() and preventDefault() | ||
124 | 150 | e.halt(); | ||
125 | 151 | }; | ||
126 | 152 | |||
127 | 153 | |||
128 | 154 | var selectWidgetByID = function(widget) { | ||
129 | 155 | var node = Y.one('#' + widget); | ||
130 | 156 | if (node) { | ||
131 | 157 | node.set('checked', true); | ||
132 | 158 | } | ||
133 | 159 | }; | ||
134 | 160 | |||
135 | 161 | |||
136 | 162 | var toggleWidget = function(widget) { | ||
137 | 163 | var node = Y.one('#' + widget); | ||
138 | 164 | if (node) { | ||
139 | 165 | if (node.get('checked')) { | ||
140 | 166 | node.set('checked', false); | ||
141 | 167 | } else { | ||
142 | 168 | node.set('checked', true); | ||
143 | 169 | } | ||
144 | 170 | } | ||
145 | 171 | }; | ||
146 | 172 | |||
147 | 173 | |||
148 | 174 | var selectTranslation = function(e, widget) { | ||
149 | 175 | // Don't select when tabbing, navigating and simply pressing | ||
150 | 176 | // enter to submit the form. | ||
151 | 177 | // Looks like this is not needed for Epiphany and Chromium | ||
152 | 178 | if (e.keyCode == 9 || e.keyCode == 13 || | ||
153 | 179 | e.keyCode == 38 || e.keyCode == 40 || | ||
154 | 180 | (e.shiftKey && e.altKey && e.keyCode == 74) || | ||
155 | 181 | (e.shiftKey && e.altKey && e.keyCode == 75)) { | ||
156 | 182 | return; | ||
157 | 183 | } | ||
158 | 184 | selectWidgetByID(widget); | ||
159 | 185 | }; | ||
160 | 186 | |||
161 | 187 | |||
162 | 188 | var initializeGlobalKeyBindings = function(fields) { | ||
163 | 189 | |||
164 | 190 | Y.get('document').on("keyup", function(e) { | ||
165 | 191 | // Shift+Alt+s - Save form | ||
166 | 192 | if (e.shiftKey && e.altKey && e.keyCode == 83) { | ||
167 | 193 | Y.one('#save_and_continue_button').invoke('click'); | ||
168 | 194 | } | ||
169 | 195 | // Shift+Alt+f - Go to search field | ||
170 | 196 | if (e.shiftKey && e.altKey && e.keyCode == 70) { | ||
171 | 197 | self.setFocus('search_box'); | ||
172 | 198 | } | ||
173 | 199 | // Shift+Alt+b - Go to first translation field | ||
174 | 200 | if (e.shiftKey && e.altKey && e.keyCode == 66) { | ||
175 | 201 | self.setFocus(fields[0]); | ||
176 | 202 | } | ||
177 | 203 | // Shift+Alt+n - Go to next page in batch | ||
178 | 204 | if (e.shiftKey && e.altKey && e.keyCode == 78) { | ||
179 | 205 | if (link = Y.one('#batchnav_next')){ | ||
180 | 206 | window.location.assign(link.get('href')) | ||
181 | 207 | } | ||
182 | 208 | } | ||
183 | 209 | // Shift+Alt+p - Go to previous page in batch | ||
184 | 210 | if (e.shiftKey && e.altKey && e.keyCode == 80) { | ||
185 | 211 | if (link = Y.one('#batchnav_previous')){ | ||
186 | 212 | window.location.assign(link.get('href')) | ||
187 | 213 | } | ||
188 | 214 | } | ||
189 | 215 | // Shift+Alt+a - Go to first page in batch | ||
190 | 216 | if (e.shiftKey && e.altKey && e.keyCode == 65) { | ||
191 | 217 | if (link = Y.one('#batchnav_first')){ | ||
192 | 218 | window.location.assign(link.get('href')) | ||
193 | 219 | } | ||
194 | 220 | } | ||
195 | 221 | // Shift+Alt+l - Go to last page in batch | ||
196 | 222 | if (e.shiftKey && e.altKey && e.keyCode == 76) { | ||
197 | 223 | if (link = Y.one('#batchnav_last')){ | ||
198 | 224 | window.location.assign(link.get('href')) | ||
199 | 225 | } | ||
200 | 226 | } | ||
201 | 227 | }); | ||
202 | 228 | } | ||
203 | 229 | |||
204 | 230 | |||
205 | 231 | var initializeSuggestionsKeyBindings = function(stem) { | ||
206 | 232 | |||
207 | 233 | suggestions = Y.all('.' + stem.replace(/_new/,"") + ' input'); | ||
208 | 234 | suggestions.each(function(node) { | ||
209 | 235 | // Only add keybinding for the first 9 suggestions | ||
210 | 236 | var index = suggestions.indexOf(node); | ||
211 | 237 | if (index < 10) { | ||
212 | 238 | // Shift+Alt+NUMBER - Mark suggestion NUMBER | ||
213 | 239 | Y.on('key', function(e, id) { | ||
214 | 240 | selectWidgetByID(id); | ||
215 | 241 | }, | ||
216 | 242 | '#' + stem, 'down:' + Number(index+49) + '+shift+alt', | ||
217 | 243 | Y, node.get('id')); | ||
218 | 244 | } | ||
219 | 245 | }); | ||
220 | 246 | } | ||
221 | 247 | |||
222 | 248 | |||
223 | 249 | var initializeFieldsKeyBindings = function (fields) { | ||
224 | 250 | for (var key = 0; key < fields.length; key++) { | ||
225 | 251 | var next = key + 1; | ||
226 | 252 | var previous = key - 1; | ||
227 | 253 | |||
228 | 254 | var html_parts = fields[key].split('_'); | ||
229 | 255 | var original_stem = html_parts[0] + '_' + html_parts[1]; | ||
230 | 256 | var translation_stem = fields[key].replace(/_translation_(\d)+_new/,""); | ||
231 | 257 | var select_widget = ( | ||
232 | 258 | translation_stem + '_' + html_parts[3] + '_' + | ||
233 | 259 | html_parts[4] + '_new_select'); | ||
234 | 260 | |||
235 | 261 | Y.on( | ||
236 | 262 | 'change', selectTranslation, | ||
237 | 263 | '#' + fields[key], Y, select_widget); | ||
238 | 264 | Y.on( | ||
239 | 265 | 'keypress', selectTranslation, | ||
240 | 266 | '#' + fields[key], Y, select_widget); | ||
241 | 267 | |||
242 | 268 | // Set next field and copy text for all but last field | ||
243 | 269 | // (last is Save & Continue button) | ||
244 | 270 | if (key < fields.length - 1) { | ||
245 | 271 | // Shift+Alt+j - Go to next translation | ||
246 | 272 | Y.on( | ||
247 | 273 | 'key', setNextFocus, '#' + fields[key], | ||
248 | 274 | 'down:74+shift+alt', Y, fields[next]); | ||
249 | 275 | // Shift+Alt+KEY_DOWN - Go to next translation | ||
250 | 276 | Y.on( | ||
251 | 277 | 'key', setNextFocus, '#' + fields[key], | ||
252 | 278 | 'down:40+shift+alt', Y, fields[next]); | ||
253 | 279 | // Shift+Alt+c - Copy original text | ||
254 | 280 | Y.on( | ||
255 | 281 | 'key', copyOriginalTextAll, '#' + fields[key], | ||
256 | 282 | 'down:67+shift+alt', Y, original_stem, translation_stem); | ||
257 | 283 | |||
258 | 284 | // Shift+Alt+r - Toggle someone should review | ||
259 | 285 | Y.on( | ||
260 | 286 | 'key', | ||
261 | 287 | function(e, stem) { | ||
262 | 288 | toggleWidget(stem + '_force_suggestion'); | ||
263 | 289 | }, | ||
264 | 290 | '#' + fields[key], 'down:82+shift+alt', Y, original_stem); | ||
265 | 291 | |||
266 | 292 | // Shift+Alt+d - Toggle dismiss all translations | ||
267 | 293 | Y.on( | ||
268 | 294 | 'key', function(e, stem) { | ||
269 | 295 | toggleWidget(stem + '_dismiss'); | ||
270 | 296 | }, '#' + fields[key], 'down:68+shift+alt', Y, original_stem); | ||
271 | 297 | |||
272 | 298 | // Shift+Alt+0 - Mark current translation | ||
273 | 299 | Y.on( | ||
274 | 300 | 'key', function(e, key) { | ||
275 | 301 | selectWidgetByID(key.replace(/_new/, "_radiobutton")); | ||
276 | 302 | }, '#' + fields[key], 'down:48+shift+alt', Y, fields[key]); | ||
277 | 303 | |||
278 | 304 | initializeSuggestionsKeyBindings(fields[key]); | ||
279 | 305 | } | ||
280 | 306 | |||
281 | 307 | // Set previous field for all but first field | ||
282 | 308 | if (key > 0) { | ||
283 | 309 | var parts = fields[previous].split('_'); | ||
284 | 310 | var singular_copy_text = ( | ||
285 | 311 | parts[0] + '_' + parts[1] + '_singular_copy_text'); | ||
286 | 312 | // Shift+Alt+k - Go to previous translation | ||
287 | 313 | Y.on( | ||
288 | 314 | 'key', setPreviousFocus, '#' + fields[key], | ||
289 | 315 | 'down:75+shift+alt', Y, fields[previous], | ||
290 | 316 | singular_copy_text); | ||
291 | 317 | // Shift+Alt+KEY_UP - Go to previous translation | ||
292 | 318 | Y.on( | ||
293 | 319 | 'key', setPreviousFocus, '#' + fields[key], | ||
294 | 320 | 'down:38+shift+alt', Y, fields[previous], | ||
295 | 321 | singular_copy_text); | ||
296 | 322 | } | ||
297 | 323 | } | ||
298 | 324 | } | ||
299 | 325 | |||
300 | 326 | |||
301 | 327 | /** | ||
302 | 328 | * Initialize event-key bindings such as moving to the next or previous | ||
303 | 329 | * field, or copying original text | ||
304 | 330 | */ | ||
305 | 331 | self.initializeKeyBindings = function(e) { | ||
306 | 332 | |||
307 | 333 | if (translations_order.length < 1) { | ||
308 | 334 | // If no translations fiels are displayed on the page | ||
309 | 335 | // don't initialize the translations order | ||
310 | 336 | return; | ||
311 | 337 | } | ||
312 | 338 | |||
313 | 339 | var fields = translations_order.split(' '); | ||
314 | 340 | // The last field is Save & Continue button | ||
315 | 341 | fields.push('save_and_continue_button'); | ||
316 | 342 | |||
317 | 343 | initializeGlobalKeyBindings(fields); | ||
318 | 344 | initializeFieldsKeyBindings(fields); | ||
319 | 345 | }; | ||
320 | 346 | |||
321 | 347 | }, "0.1", {"requires": ["event", "event-key", "node", "cookie", "anim"]}); | ||
322 | 348 | |||
323 | 45 | 349 | ||
324 | === modified file 'lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt' | |||
325 | --- lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2010-01-27 07:38:02 +0000 | |||
326 | +++ lib/canonical/launchpad/templates/batchnavigator-navigation-links.pt 2010-02-25 13:00:44 +0000 | |||
327 | @@ -29,6 +29,7 @@ | |||
328 | 29 | tal:condition="first_page_url" | 29 | tal:condition="first_page_url" |
329 | 30 | tal:attributes="href first_page_url" | 30 | tal:attributes="href first_page_url" |
330 | 31 | class="first" | 31 | class="first" |
331 | 32 | id="batchnav_first" | ||
332 | 32 | rel="first" | 33 | rel="first" |
333 | 33 | >First</a> | 34 | >First</a> |
334 | 34 | <span tal:condition="not:first_page_url" class="first inactive" | 35 | <span tal:condition="not:first_page_url" class="first inactive" |
335 | @@ -38,6 +39,7 @@ | |||
336 | 38 | tal:condition="prev_page_url" | 39 | tal:condition="prev_page_url" |
337 | 39 | tal:attributes="href prev_page_url" | 40 | tal:attributes="href prev_page_url" |
338 | 40 | class="previous" | 41 | class="previous" |
339 | 42 | id="batchnav_previous" | ||
340 | 41 | rel="previous" | 43 | rel="previous" |
341 | 42 | >Previous</a> | 44 | >Previous</a> |
342 | 43 | <span tal:condition="not:prev_page_url" class="previous inactive" | 45 | <span tal:condition="not:prev_page_url" class="previous inactive" |
343 | @@ -46,6 +48,7 @@ | |||
344 | 46 | <a | 48 | <a |
345 | 47 | tal:condition="next_page_url" | 49 | tal:condition="next_page_url" |
346 | 48 | tal:attributes="href next_page_url" | 50 | tal:attributes="href next_page_url" |
347 | 51 | id="batchnav_next" | ||
348 | 49 | class="next" | 52 | class="next" |
349 | 50 | rel="next" | 53 | rel="next" |
350 | 51 | ><strong>Next</strong></a> | 54 | ><strong>Next</strong></a> |
351 | @@ -57,6 +60,7 @@ | |||
352 | 57 | tal:condition="last_page_url" | 60 | tal:condition="last_page_url" |
353 | 58 | tal:attributes="href last_page_url" | 61 | tal:attributes="href last_page_url" |
354 | 59 | class="last" | 62 | class="last" |
355 | 63 | id="batchnav_last" | ||
356 | 60 | rel="last" | 64 | rel="last" |
357 | 61 | >Last</a> | 65 | >Last</a> |
358 | 62 | <span tal:condition="not:last_page_url" class="last inactive" | 66 | <span tal:condition="not:last_page_url" class="last inactive" |
359 | 63 | 67 | ||
360 | === modified file 'lib/lp/translations/browser/pofile.py' | |||
361 | --- lib/lp/translations/browser/pofile.py 2010-01-27 07:38:02 +0000 | |||
362 | +++ lib/lp/translations/browser/pofile.py 2010-02-25 13:00:44 +0000 | |||
363 | @@ -503,8 +503,6 @@ | |||
364 | 503 | 503 | ||
365 | 504 | DEFAULT_BATCH_SIZE = 50 | 504 | DEFAULT_BATCH_SIZE = 50 |
366 | 505 | 505 | ||
367 | 506 | page_title = "Contributions" | ||
368 | 507 | |||
369 | 508 | @property | 506 | @property |
370 | 509 | def _person_name(self): | 507 | def _person_name(self): |
371 | 510 | """Person's display name. Graceful about unknown persons.""" | 508 | """Person's display name. Graceful about unknown persons.""" |
372 | @@ -930,6 +928,26 @@ | |||
373 | 930 | def completeness(self): | 928 | def completeness(self): |
374 | 931 | return '%.0f%%' % self.context.translatedPercentage() | 929 | return '%.0f%%' % self.context.translatedPercentage() |
375 | 932 | 930 | ||
376 | 931 | def _messages_html_id(self): | ||
377 | 932 | order = [] | ||
378 | 933 | for message in self.translationmessage_views: | ||
379 | 934 | if (message.form_is_writeable): | ||
380 | 935 | for dictionary in message.translation_dictionaries: | ||
381 | 936 | order.append( | ||
382 | 937 | dictionary['html_id_translation'] + '_new') | ||
383 | 938 | return order | ||
384 | 939 | |||
385 | 940 | @property | ||
386 | 941 | def autofocus_html_id(self): | ||
387 | 942 | if (len(self._messages_html_id()) > 0): | ||
388 | 943 | return self._messages_html_id()[0] | ||
389 | 944 | else: | ||
390 | 945 | return "" | ||
391 | 946 | |||
392 | 947 | @property | ||
393 | 948 | def translations_order(self): | ||
394 | 949 | return ' '.join(self._messages_html_id()) | ||
395 | 950 | |||
396 | 933 | 951 | ||
397 | 934 | class POExportView(BaseExportView): | 952 | class POExportView(BaseExportView): |
398 | 935 | 953 | ||
399 | 936 | 954 | ||
400 | === modified file 'lib/lp/translations/browser/tests/pofile-views.txt' | |||
401 | --- lib/lp/translations/browser/tests/pofile-views.txt 2009-09-17 16:22:32 +0000 | |||
402 | +++ lib/lp/translations/browser/tests/pofile-views.txt 2010-02-25 13:00:44 +0000 | |||
403 | @@ -145,6 +145,23 @@ | |||
404 | 145 | >>> translationmessage_view.context.translations | 145 | >>> translationmessage_view.context.translations |
405 | 146 | [u'libreta de direcciones de Evolution'] | 146 | [u'libreta de direcciones de Evolution'] |
406 | 147 | 147 | ||
407 | 148 | To help the JavaScript key navigation the view is exposing the autofocus | ||
408 | 149 | field and a list of all translation fields ordered by the way they are | ||
409 | 150 | listed in the page. | ||
410 | 151 | |||
411 | 152 | >>> for translationmessage_view in ( | ||
412 | 153 | ... pofile_view.translationmessage_views): | ||
413 | 154 | ... translationmessage_view.initialize() | ||
414 | 155 | >>> print pofile_view.autofocus_html_id | ||
415 | 156 | msgset_130_es_translation_0_new | ||
416 | 157 | >>> print pofile_view.translations_order | ||
417 | 158 | msgset_130_es_translation_0_new msgset_131_es_translation_0_new | ||
418 | 159 | msgset_132_es_translation_0_new msgset_133_es_translation_0_new | ||
419 | 160 | msgset_134_es_translation_0_new msgset_135_es_translation_0_new | ||
420 | 161 | msgset_136_es_translation_0_new msgset_137_es_translation_0_new | ||
421 | 162 | msgset_138_es_translation_0_new msgset_139_es_translation_0_new | ||
422 | 163 | msgset_130_es_translation_0_new | ||
423 | 164 | |||
424 | 148 | It's time to check the submission of translations and the IPOFile statistics | 165 | It's time to check the submission of translations and the IPOFile statistics |
425 | 149 | update. | 166 | update. |
426 | 150 | 167 | ||
427 | @@ -403,7 +420,8 @@ | |||
428 | 403 | 420 | ||
429 | 404 | = POFileNavigation = | 421 | = POFileNavigation = |
430 | 405 | 422 | ||
432 | 406 | This class is used to traverse from IPOFile objects to ITranslationMessage ones. | 423 | This class is used to traverse from IPOFile objects to ITranslationMessage |
433 | 424 | ones. | ||
434 | 407 | 425 | ||
435 | 408 | >>> from zope.security.proxy import isinstance | 426 | >>> from zope.security.proxy import isinstance |
436 | 409 | >>> from lp.translations.browser.pofile import POFileNavigation | 427 | >>> from lp.translations.browser.pofile import POFileNavigation |
437 | @@ -483,4 +501,4 @@ | |||
438 | 483 | And we are redirected to the index page, as expected: | 501 | And we are redirected to the index page, as expected: |
439 | 484 | 502 | ||
440 | 485 | >>> print pofile_view.request.response.getHeader('Location') | 503 | >>> print pofile_view.request.response.getHeader('Location') |
442 | 486 | http://translations.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es | 504 | http://trans.../ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es |
443 | 487 | 505 | ||
444 | === modified file 'lib/lp/translations/browser/translationmessage.py' | |||
445 | --- lib/lp/translations/browser/translationmessage.py 2010-01-27 07:38:02 +0000 | |||
446 | +++ lib/lp/translations/browser/translationmessage.py 2010-02-25 13:00:44 +0000 | |||
447 | @@ -829,6 +829,30 @@ | |||
448 | 829 | self._redirectToNextPage() | 829 | self._redirectToNextPage() |
449 | 830 | return True | 830 | return True |
450 | 831 | 831 | ||
451 | 832 | def _messages_html_id(self): | ||
452 | 833 | order = [] | ||
453 | 834 | message = self.translationmessage_view | ||
454 | 835 | # If we don't know about plural forms, or there are some other | ||
455 | 836 | # reason that prevent translations, translationmessage_view is | ||
456 | 837 | # not created | ||
457 | 838 | if ((message is not None) and (message.form_is_writeable)): | ||
458 | 839 | for dictionary in message.translation_dictionaries: | ||
459 | 840 | order.append( | ||
460 | 841 | dictionary['html_id_translation'] + '_new') | ||
461 | 842 | return order | ||
462 | 843 | |||
463 | 844 | @property | ||
464 | 845 | def autofocus_html_id(self): | ||
465 | 846 | if (len(self._messages_html_id()) > 0): | ||
466 | 847 | return self._messages_html_id()[0] | ||
467 | 848 | else: | ||
468 | 849 | return "" | ||
469 | 850 | |||
470 | 851 | @property | ||
471 | 852 | def translations_order(self): | ||
472 | 853 | return ' '.join(self._messages_html_id()) | ||
473 | 854 | |||
474 | 855 | |||
475 | 832 | class CurrentTranslationMessageView(LaunchpadView): | 856 | class CurrentTranslationMessageView(LaunchpadView): |
476 | 833 | """Holds all data needed to show an ITranslationMessage. | 857 | """Holds all data needed to show an ITranslationMessage. |
477 | 834 | 858 | ||
478 | 835 | 859 | ||
479 | === modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt' | |||
480 | --- lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2010-01-27 07:38:02 +0000 | |||
481 | +++ lib/lp/translations/stories/standalone/xx-pofile-translate-newlines-check.txt 2010-02-25 13:00:44 +0000 | |||
482 | @@ -43,7 +43,7 @@ | |||
483 | 43 | >>> print browser.url | 43 | >>> print browser.url |
484 | 44 | http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1 | 44 | http://translations.launchpad.dev/ubuntu/hoary/+source/evolution/+pots/evolution-2.2/es/+translate?start=19&batch=1 |
485 | 45 | >>> print find_tag_by_id(browser.contents, 'msgset_149_es_translation_0_new') | 45 | >>> print find_tag_by_id(browser.contents, 'msgset_149_es_translation_0_new') |
487 | 46 | <textarea ... name="msgset_149_es_translation_0_new" ...> | 46 | <textarea ... name="msgset_149_es_translation_0_new"...> |
488 | 47 | 47 | ||
489 | 48 | foo | 48 | foo |
490 | 49 | 49 | ||
491 | @@ -63,7 +63,7 @@ | |||
492 | 63 | >>> print find_tag_by_id( | 63 | >>> print find_tag_by_id( |
493 | 64 | ... browser.contents, | 64 | ... browser.contents, |
494 | 65 | ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE | 65 | ... 'msgset_149_es_translation_0_new') #doctest: -NORMALIZE_WHITESPACE |
496 | 66 | <textarea ... name="msgset_149_es_translation_0_new" ...> | 66 | <textarea ... name="msgset_149_es_translation_0_new"...> |
497 | 67 | foo</textarea> | 67 | foo</textarea> |
498 | 68 | 68 | ||
499 | 69 | Now, Check that even though the user forgot the trailing new line char, | 69 | Now, Check that even though the user forgot the trailing new line char, |
500 | 70 | 70 | ||
501 | === modified file 'lib/lp/translations/stories/standalone/xx-pofile-translate.txt' | |||
502 | --- lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2010-01-27 07:38:02 +0000 | |||
503 | +++ lib/lp/translations/stories/standalone/xx-pofile-translate.txt 2010-02-25 13:00:44 +0000 | |||
504 | @@ -30,7 +30,8 @@ | |||
505 | 30 | 30 | ||
506 | 31 | The page is rendered in read-only mode, without any textareas for input. | 31 | The page is rendered in read-only mode, without any textareas for input. |
507 | 32 | 32 | ||
509 | 33 | >>> main_content = find_tag_by_id(browser.contents, 'messages_to_translate') | 33 | >>> main_content = find_tag_by_id( |
510 | 34 | ... browser.contents, 'messages_to_translate') | ||
511 | 34 | >>> for textarea in main_content.findAll('textarea'): | 35 | >>> for textarea in main_content.findAll('textarea'): |
512 | 35 | ... print 'Found textarea:\n%s' % textarea | 36 | ... print 'Found textarea:\n%s' % textarea |
513 | 36 | 37 | ||
514 | @@ -145,8 +146,8 @@ | |||
515 | 145 | 146 | ||
516 | 146 | >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test') | 147 | >>> browser = setupBrowser(auth='Basic carlos@canonical.com:test') |
517 | 147 | >>> browser.open("http://translations.launchpad.dev/" | 148 | >>> browser.open("http://translations.launchpad.dev/" |
520 | 148 | ... "ubuntu/hoary/+source/evolution/+pots/evolution-2.2/en_AU/" | 149 | ... "ubuntu/hoary/+source/evolution/+pots/evolution-2.2" |
521 | 149 | ... "+translate?field.alternative_language=es") | 150 | ... "/en_AU/+translate?field.alternative_language=es") |
522 | 150 | 151 | ||
523 | 151 | Elements related 1:1 to a translatable message on this form have names and | 152 | Elements related 1:1 to a translatable message on this form have names and |
524 | 152 | identifiers constructed as "msgset_<id>," where <id> is the unpadded decimal | 153 | identifiers constructed as "msgset_<id>," where <id> is the unpadded decimal |
525 | @@ -158,7 +159,7 @@ | |||
526 | 158 | ... print id | 159 | ... print id |
527 | 159 | msgset_130 | 160 | msgset_130 |
528 | 160 | ... | 161 | ... |
530 | 161 | msgset_130_singular | 162 | msgset_130_singular... |
531 | 162 | 163 | ||
532 | 163 | HTML element identifiers for suggestions and translations on this form are | 164 | HTML element identifiers for suggestions and translations on this form are |
533 | 164 | constructed as an underscore-separated sequence of: | 165 | constructed as an underscore-separated sequence of: |
534 | @@ -180,7 +181,9 @@ | |||
535 | 180 | msgset_130_es_suggestion_562_0 | 181 | msgset_130_es_suggestion_562_0 |
536 | 181 | msgset_130_es_suggestion_562_0_origin | 182 | msgset_130_es_suggestion_562_0_origin |
537 | 182 | msgset_130_es_suggestion_562_0_radiobutton | 183 | msgset_130_es_suggestion_562_0_radiobutton |
538 | 184 | msgset_130_force_suggestion | ||
539 | 183 | msgset_130_singular | 185 | msgset_130_singular |
540 | 186 | msgset_130_singular_copy_text | ||
541 | 184 | 187 | ||
542 | 185 | Radio buttons are grouped by their name attribute. The translate page shows | 188 | Radio buttons are grouped by their name attribute. The translate page shows |
543 | 186 | each translatable message with one radiobutton to select the existing | 189 | each translatable message with one radiobutton to select the existing |
544 | @@ -190,7 +193,9 @@ | |||
545 | 190 | Here we see an example where three suggestions are offered, making for five | 193 | Here we see an example where three suggestions are offered, making for five |
546 | 191 | identically-named radio buttons and sundry other HTML tags. | 194 | identically-named radio buttons and sundry other HTML tags. |
547 | 192 | 195 | ||
549 | 193 | >>> browser.open('http://translations.launchpad.dev/alsa-utils/trunk/+pots/alsa-utils/es/+translate') | 196 | >>> browser.open( |
550 | 197 | ... 'http://translations.launchpad.dev/alsa-utils/trunk/' | ||
551 | 198 | ... '+pots/alsa-utils/es/+translate') | ||
552 | 194 | >>> msgset_198 = get_tags(browser, 'name', 'msgset_198') | 199 | >>> msgset_198 = get_tags(browser, 'name', 'msgset_198') |
553 | 195 | >>> for name in msgset_198: | 200 | >>> for name in msgset_198: |
554 | 196 | ... print name | 201 | ... print name |
555 | @@ -213,3 +218,26 @@ | |||
556 | 213 | ... browser.contents, 'msgset_134_es_suggestion_694_0')) | 218 | ... browser.contents, 'msgset_134_es_suggestion_694_0')) |
557 | 214 | tarjetas | 219 | tarjetas |
558 | 215 | 220 | ||
559 | 221 | |||
560 | 222 | Missing plural forms information | ||
561 | 223 | -------------------------------- | ||
562 | 224 | |||
563 | 225 | If the plural forms are not known for a language, users can not add | ||
564 | 226 | new translations and are asked to help Launchpad Translations by providing | ||
565 | 227 | the plural form informations. | ||
566 | 228 | |||
567 | 229 | This notice is display when doing batch translations or translating a | ||
568 | 230 | single message. | ||
569 | 231 | |||
570 | 232 | >>> browser.open('http://translations.launchpad.dev/ubuntu/hoary/' | ||
571 | 233 | ... '+source/evolution/+pots/evolution-2.2/ab/+translate') | ||
572 | 234 | >>> print extract_text(find_tag_by_id( | ||
573 | 235 | ... browser.contents, 'maincontent')) | ||
574 | 236 | Launchpad can’t handle the plural items ... | ||
575 | 237 | |||
576 | 238 | >>> browser.open('http://translations.launchpad.dev/ubuntu/hoary/' | ||
577 | 239 | ... '+source/evolution/+pots/evolution-2.2/ab/5/+translate') | ||
578 | 240 | >>> print extract_text(find_tag_by_id( | ||
579 | 241 | ... browser.contents, 'maincontent')) | ||
580 | 242 | Launchpad can’t handle the plural items ... | ||
581 | 243 | |||
582 | 216 | 244 | ||
583 | === modified file 'lib/lp/translations/templates/currenttranslationmessage-translate-one.pt' | |||
584 | --- lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2010-01-27 07:38:02 +0000 | |||
585 | +++ lib/lp/translations/templates/currenttranslationmessage-translate-one.pt 2010-02-25 13:00:44 +0000 | |||
586 | @@ -42,6 +42,7 @@ | |||
587 | 42 | <a href="" | 42 | <a href="" |
588 | 43 | tal:condition="not:context/potmsgset/is_translation_credit" | 43 | tal:condition="not:context/potmsgset/is_translation_credit" |
589 | 44 | tal:attributes=" | 44 | tal:attributes=" |
590 | 45 | id string:${view/html_id}_singular_copy_text; | ||
591 | 45 | onClick string: | 46 | onClick string: |
592 | 46 | javascript:copyInnerHTMLById( | 47 | javascript:copyInnerHTMLById( |
593 | 47 | '${view/html_id}_singular', | 48 | '${view/html_id}_singular', |
594 | @@ -172,7 +173,7 @@ | |||
595 | 172 | <tal:translation-dictionaries | 173 | <tal:translation-dictionaries |
596 | 173 | repeat="translation_dictionary view/translation_dictionaries"> | 174 | repeat="translation_dictionary view/translation_dictionaries"> |
597 | 174 | <tal:plural-form define="plural_index translation_dictionary/plural_index"> | 175 | <tal:plural-form define="plural_index translation_dictionary/plural_index"> |
599 | 175 | <tr class="secondary translation"> | 176 | <tr tal:attributes="class string:secondary translation ${view/html_id}"> |
600 | 176 | <th colspan="3"> | 177 | <th colspan="3"> |
601 | 177 | <label class="language-code">Current | 178 | <label class="language-code">Current |
602 | 178 | <span tal:replace="context/pofile/language/englishname"> | 179 | <span tal:replace="context/pofile/language/englishname"> |
603 | @@ -321,9 +322,10 @@ | |||
604 | 321 | </td> | 322 | </td> |
605 | 322 | </tal:locked> | 323 | </tal:locked> |
606 | 323 | </tr> | 324 | </tr> |
610 | 324 | <tr class="secondary confirm_and_dismiss" | 325 | <tr tal:define="name_id string:${view/html_id}_dismiss" |
611 | 325 | tal:define="name_id string:${view/html_id}_dismiss" | 326 | tal:condition="view/can_confirm_and_dismiss" |
612 | 326 | tal:condition="view/can_confirm_and_dismiss"> | 327 | tal:attributes=" |
613 | 328 | class string:secondary confirm_and_dismiss ${view/html_id}"> | ||
614 | 327 | <td colspan="4"></td> | 329 | <td colspan="4"></td> |
615 | 328 | <td> | 330 | <td> |
616 | 329 | <label class="fuzzy-checkbox" tal:attributes="for name_id"> | 331 | <label class="fuzzy-checkbox" tal:attributes="for name_id"> |
617 | @@ -504,8 +506,6 @@ | |||
618 | 504 | lang context/pofile/language/dashedcode; | 506 | lang context/pofile/language/dashedcode; |
619 | 505 | dir context/pofile/language/abbreviated_text_dir; | 507 | dir context/pofile/language/abbreviated_text_dir; |
620 | 506 | value translation_dictionary/submitted_translation; | 508 | value translation_dictionary/submitted_translation; |
621 | 507 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
622 | 508 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
623 | 509 | " | 509 | " |
624 | 510 | class="translate expandable" | 510 | class="translate expandable" |
625 | 511 | /> | 511 | /> |
626 | @@ -528,8 +528,6 @@ | |||
627 | 528 | name string:${translation_dictionary/html_id_translation}_new; | 528 | name string:${translation_dictionary/html_id_translation}_new; |
628 | 529 | lang context/pofile/language/dashedcode; | 529 | lang context/pofile/language/dashedcode; |
629 | 530 | dir context/pofile/language/abbreviated_text_dir; | 530 | dir context/pofile/language/abbreviated_text_dir; |
630 | 531 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
631 | 532 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
632 | 533 | "> | 531 | "> |
633 | 534 | <tal:content replace="translation_dictionary/submitted_translation" /></textarea> | 532 | <tal:content replace="translation_dictionary/submitted_translation" /></textarea> |
634 | 535 | </tal:with-content> | 533 | </tal:with-content> |
635 | @@ -548,8 +546,6 @@ | |||
636 | 548 | name string:${translation_dictionary/html_id_translation}_new; | 546 | name string:${translation_dictionary/html_id_translation}_new; |
637 | 549 | lang context/pofile/language/dashedcode; | 547 | lang context/pofile/language/dashedcode; |
638 | 550 | dir context/pofile/language/abbreviated_text_dir; | 548 | dir context/pofile/language/abbreviated_text_dir; |
639 | 551 | onKeyPress string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
640 | 552 | onChange string:javascript:selectWidget('${translation_dictionary/html_id_translation}_new_select', event); | ||
641 | 553 | "></textarea> | 549 | "></textarea> |
642 | 554 | </tal:without-content> | 550 | </tal:without-content> |
643 | 555 | </tal:multi-line> | 551 | </tal:multi-line> |
644 | @@ -595,7 +591,8 @@ | |||
645 | 595 | value="force_suggestion" | 591 | value="force_suggestion" |
646 | 596 | tal:attributes=" | 592 | tal:attributes=" |
647 | 597 | checked view/force_suggestion; | 593 | checked view/force_suggestion; |
649 | 598 | name string:${view/html_id}_${language_code}_needsreview" | 594 | name string:${view/html_id}_${language_code}_needsreview; |
650 | 595 | id string:${view/html_id}_force_suggestion;" | ||
651 | 599 | /> | 596 | /> |
652 | 600 | <tal:block condition="not:view/is_plural"> | 597 | <tal:block condition="not:view/is_plural"> |
653 | 601 | Someone should review this translation | 598 | Someone should review this translation |
654 | 602 | 599 | ||
655 | === modified file 'lib/lp/translations/templates/pofile-translate.pt' | |||
656 | --- lib/lp/translations/templates/pofile-translate.pt 2010-01-27 07:38:02 +0000 | |||
657 | +++ lib/lp/translations/templates/pofile-translate.pt 2010-02-25 13:00:44 +0000 | |||
658 | @@ -13,6 +13,7 @@ | |||
659 | 13 | color: lightgray; | 13 | color: lightgray; |
660 | 14 | } | 14 | } |
661 | 15 | </style> | 15 | </style> |
662 | 16 | |||
663 | 16 | <script type="text/javascript" | 17 | <script type="text/javascript" |
664 | 17 | tal:condition="devmode" | 18 | tal:condition="devmode" |
665 | 18 | tal:define="lp_js string:${icingroot}/build" | 19 | tal:define="lp_js string:${icingroot}/build" |
666 | @@ -20,50 +21,13 @@ | |||
667 | 20 | <script type="text/javascript"> | 21 | <script type="text/javascript"> |
668 | 21 | registerLaunchpadFunction(insertAllExpansionButtons); | 22 | registerLaunchpadFunction(insertAllExpansionButtons); |
669 | 22 | 23 | ||
712 | 23 | LPS.use('node', 'cookie', 'anim', 'lp.pofile', function(Y) { | 24 | LPS.use('lp.pofile', function(Y) { |
671 | 24 | |||
672 | 25 | var hide_notification = function(node) { | ||
673 | 26 | var hide_anim = new Y.Anim({ | ||
674 | 27 | node: node, | ||
675 | 28 | to: { height: 0, | ||
676 | 29 | marginTop: 0, marginBottom: 0, | ||
677 | 30 | paddingTop: 0, paddingBottom: 0 } | ||
678 | 31 | }); | ||
679 | 32 | node.setStyle('border', 'none'); | ||
680 | 33 | hide_anim.set('duration', 0.4); | ||
681 | 34 | hide_anim.on('end', function(e) { | ||
682 | 35 | node.setStyle('display', 'none'); | ||
683 | 36 | }); | ||
684 | 37 | hide_anim.run(); | ||
685 | 38 | } | ||
686 | 39 | |||
687 | 40 | var updateNotificationBox = function(e) { | ||
688 | 41 | var notice = Y.one('.important-notice-container'); | ||
689 | 42 | var balloon = notice.one('.important-notice-balloon'); | ||
690 | 43 | var dismiss_notice_cookie = ('translation-docs-for-' + | ||
691 | 44 | documentation_cookie); | ||
692 | 45 | |||
693 | 46 | // Check the cookie to see if the user has already dismissed | ||
694 | 47 | // the notification box for this session. | ||
695 | 48 | var already_seen = Y.Cookie.get(dismiss_notice_cookie, Boolean); | ||
696 | 49 | if (already_seen) { | ||
697 | 50 | notice.setStyle('display', 'none'); | ||
698 | 51 | } | ||
699 | 52 | |||
700 | 53 | var cancel_button = notice.one( | ||
701 | 54 | '.important-notice-cancel-button'); | ||
702 | 55 | // Cancel button starts out hidden. If user has JavaScript, | ||
703 | 56 | // then we want to show it. | ||
704 | 57 | cancel_button.setStyle('visibility', 'visible'); | ||
705 | 58 | cancel_button.on('click', function(e) { | ||
706 | 59 | e.halt(); | ||
707 | 60 | hide_notification(balloon); | ||
708 | 61 | Y.Cookie.set(dismiss_notice_cookie, true); | ||
709 | 62 | }); | ||
710 | 63 | }; | ||
711 | 64 | |||
713 | 65 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); | 25 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); |
715 | 66 | Y.on('domready', updateNotificationBox); | 26 | Y.on('domready', Y.lp.pofile.updateNotificationBox); |
716 | 27 | Y.on('domready', Y.lp.pofile.initializeKeyBindings); | ||
717 | 28 | Y.on('domready', function(e) { | ||
718 | 29 | Y.lp.pofile.setFocus(autofocus_field); | ||
719 | 30 | }); | ||
720 | 67 | }); | 31 | }); |
721 | 68 | </script> | 32 | </script> |
722 | 69 | </div> | 33 | </div> |
723 | @@ -262,6 +226,7 @@ | |||
724 | 262 | <td style="text-align: right;"> | 226 | <td style="text-align: right;"> |
725 | 263 | <input type="submit" | 227 | <input type="submit" |
726 | 264 | name="submit_translations" | 228 | name="submit_translations" |
727 | 229 | id="save_and_continue_button" | ||
728 | 265 | value="Save & Continue" | 230 | value="Save & Continue" |
729 | 266 | /> | 231 | /> |
730 | 267 | </td> | 232 | </td> |
731 | @@ -277,6 +242,8 @@ | |||
732 | 277 | <tal:status replace="structure context/@@+access" /> | 242 | <tal:status replace="structure context/@@+access" /> |
733 | 278 | <tal:contributors replace="structure context/@@+contributors" /> | 243 | <tal:contributors replace="structure context/@@+contributors" /> |
734 | 279 | </tal:havepluralforms> | 244 | </tal:havepluralforms> |
735 | 245 | <metal:pofile-js-footer | ||
736 | 246 | use-macro="context/@@+translations-macros/pofile-js-footer" /> | ||
737 | 280 | </div> | 247 | </div> |
738 | 281 | </body> | 248 | </body> |
739 | 282 | </html> | 249 | </html> |
740 | 283 | 250 | ||
741 | === modified file 'lib/lp/translations/templates/translationmessage-translate.pt' | |||
742 | --- lib/lp/translations/templates/translationmessage-translate.pt 2010-01-27 07:38:02 +0000 | |||
743 | +++ lib/lp/translations/templates/translationmessage-translate.pt 2010-02-25 13:00:44 +0000 | |||
744 | @@ -20,6 +20,10 @@ | |||
745 | 20 | <script type="text/javascript"> | 20 | <script type="text/javascript"> |
746 | 21 | LPS.use('node', 'lp.pofile', function(Y) { | 21 | LPS.use('node', 'lp.pofile', function(Y) { |
747 | 22 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); | 22 | Y.on('domready', Y.lp.pofile.setupSuggestionDismissal); |
748 | 23 | Y.on('domready', Y.lp.pofile.initializeKeyBindings); | ||
749 | 24 | Y.on('domready', function(e) { | ||
750 | 25 | Y.lp.pofile.setFocus(autofocus_field); | ||
751 | 26 | }); | ||
752 | 23 | }); | 27 | }); |
753 | 24 | </script> | 28 | </script> |
754 | 25 | </div> | 29 | </div> |
755 | @@ -76,6 +80,7 @@ | |||
756 | 76 | <th colspan="5" style="text-align: right;"> | 80 | <th colspan="5" style="text-align: right;"> |
757 | 77 | <input type="submit" | 81 | <input type="submit" |
758 | 78 | name="submit_translations" | 82 | name="submit_translations" |
759 | 83 | id="save_and_continue_button" | ||
760 | 79 | value="Save & Continue" /> | 84 | value="Save & Continue" /> |
761 | 80 | </th> | 85 | </th> |
762 | 81 | </tr> | 86 | </tr> |
763 | @@ -89,6 +94,8 @@ | |||
764 | 89 | replace="structure view/batchnav/@@+navigation-links-lower" /> | 94 | replace="structure view/batchnav/@@+navigation-links-lower" /> |
765 | 90 | <tal:status replace="structure context/pofile/@@+access" /> | 95 | <tal:status replace="structure context/pofile/@@+access" /> |
766 | 91 | </tal:havepluralforms> | 96 | </tal:havepluralforms> |
767 | 97 | <metal:pofile-js-footer | ||
768 | 98 | use-macro="context/@@+translations-macros/pofile-js-footer" /> | ||
769 | 92 | </div> | 99 | </div> |
770 | 93 | 100 | ||
771 | 94 | </body> | 101 | </body> |
772 | 95 | 102 | ||
773 | === modified file 'lib/lp/translations/templates/translations-macros.pt' | |||
774 | --- lib/lp/translations/templates/translations-macros.pt 2010-01-27 07:38:02 +0000 | |||
775 | +++ lib/lp/translations/templates/translations-macros.pt 2010-02-25 13:00:44 +0000 | |||
776 | @@ -6,7 +6,7 @@ | |||
777 | 6 | <metal:render-suggestion define-macro="render-suggestion"> | 6 | <metal:render-suggestion define-macro="render-suggestion"> |
778 | 7 | <tal:submission condition="submission"> | 7 | <tal:submission condition="submission"> |
779 | 8 | <tal:not-empty condition="not:submission/is_empty"> | 8 | <tal:not-empty condition="not:submission/is_empty"> |
781 | 9 | <tr tal:attributes="class string:secondary ${dismissable}; | 9 | <tr tal:attributes="class string:secondary ${dismissable} ${submission/translation_html_id}; |
782 | 10 | id submission/row_html_id"> | 10 | id submission/row_html_id"> |
783 | 11 | <th colspan="3" tal:content="section_title"> | 11 | <th colspan="3" tal:content="section_title"> |
784 | 12 | Packaged: | 12 | Packaged: |
785 | @@ -130,6 +130,17 @@ | |||
786 | 130 | </metal:nav-pofile-subpages> | 130 | </metal:nav-pofile-subpages> |
787 | 131 | 131 | ||
788 | 132 | 132 | ||
789 | 133 | <metal:pofile-js-footer define-macro="pofile-js-footer"> | ||
790 | 134 | <script type="text/javascript" | ||
791 | 135 | tal:content=" | ||
792 | 136 | structure string:<!-- | ||
793 | 137 | var autofocus_field = '${view/autofocus_html_id}'; | ||
794 | 138 | var translations_order = '${view/translations_order}'; | ||
795 | 139 | var plural_forms = ${context/plural_forms}; | ||
796 | 140 | // -->" /> | ||
797 | 141 | </metal:pofile-js-footer> | ||
798 | 142 | |||
799 | 143 | |||
800 | 133 | <metal:translations-js define-macro="translations-js"> | 144 | <metal:translations-js define-macro="translations-js"> |
801 | 134 | <script | 145 | <script |
802 | 135 | type="text/javascript" | 146 | type="text/javascript" |
= Bug 359180 =
This is the follow up for the current commited branch for bug 359180 as it was discovered that Shift+Alt+up and Shift+Alt+Down are used by.
== Proposed fix ==
Use Shift+Alt+j and Shift+Alt+k for navigation.
Add fields in the translations_order only if the user can edit(add, change or suggest) the translation.
== Pre-implementation notes == /code.edge. launchpad. net/~adiroiban/ launchpad/ bug-359180/ +merge/ 16422
Notes can be found on the previous MP:
https:/
== Implementation details ==
There was a small refactorization for getting translations_order and autofocus_html_id.
== Tests ==
I was not able to produce a reasonable windmill test since it is not trivial to find the current focused node or to see if a node is focused.
This requires adding onFocus and onBlur trigger for all DOM nodes.
The test for the view is here:
./bin/test -t pofile-views
== Demo and Q/A == /code.edge. launchpad. net/~adiroiban/ launchpad/ bug-359180/ +merge/ 16422
Demo and Q/A can be found on the previous MP
https:/
Instead of UP and DOWN, use j and k
= Launchpad lint =
Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.
Linting changed files: /launchpad/ javascript/ translations/ pofile. js
lib/canonical
== JSLint notices == adi/launchpad/ lp-branches/bug-359180-take-2/ lib/canonical/ launchpad/ javascript/ translations/ pofile. js'.
No handlers could be found for logger "bzr"
jslint: No problem found in '/home/
jslint: 1 file to lint.