-
Notifications
You must be signed in to change notification settings - Fork 1
/
static-translations.html
executable file
·297 lines (249 loc) · 12.8 KB
/
static-translations.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><![endif]-->
<meta name="viewport" content="width=1024, user-scalable=no">
<title>Building International Sites: Managing Static Translations</title>
<!-- Required stylesheet -->
<link rel="stylesheet" href="deck.js/core/deck.core.css">
<link rel="stylesheet" href="deck.js/extensions/goto/deck.goto.css">
<link rel="stylesheet" href="deck.js/extensions/menu/deck.menu.css">
<link rel="stylesheet" href="deck.js/extensions/navigation/deck.navigation.css">
<link rel="stylesheet" href="deck.js/extensions/status/deck.status.css">
<link rel="stylesheet" href="deck.js/extensions/hash/deck.hash.css">
<link rel="stylesheet" href="deck.js/themes/style/swiss.css">
<link rel="stylesheet" href="custom.css">
<script src="deck.js/modernizr.custom.js"></script>
</head>
<body class="deck-container">
<section class="slide">
<h1>Managing Static Translations</h1>
</section>
<section class="slide">
<h2>The Basics</h2>
<h3>An aggressive summary of the <a href="https://docs.djangoproject.com/en/1.4/topics/i18n/translation/">Django translation documentation</a></h3>
<p class="slide">
Essentially what we're doing is changing every place where our
code contains text which is displayed to the user (e.g.
<samp>Hello, USER</samp>) to instead ask a translation system
for the current language equivalent. Django uses the very common
<a href="http://www.gnu.org/software/gettext/">GNU gettext</a>
system.
</p>
<ol>
<li class="slide">
Change our source code to ask gettext for each string
<code class="block">
from django.utils.translation import ugettext as _<br>
print <em>_("Welcome to our site")</em><br>
</code>
<code class="block">
{% load i18n %}<br>
<h1><em>{% trans "Welcome to our site" %}<</em>/h1><br>
</code>
</li>
<li class="slide">
Run <kbd>django-admin.py makemessages -a</kbd>
</li>
<li class="slide">
Translate the files which makemessages created in your
projects' locale directory
</li>
<li class="slide">
Run <kbd>django-admin.py compilemessages</kbd>
</li>
</ol>
</section>
<section class="slide">
<h2>The Not-So-Basics</h2>
<h3>Templates: trans vs. blocktrans</h3>
<p>
The <code>trans</code> templatetag is shorter but limited to
simple string values or entire variables. If you need to
include variables inside a string or process your input in any
way, use <code>blocktrans</code>. Since the format strings are
part of the text sent to translators having obvious variable
names can avoid potential confusion.
</p>
<code class="block">
{% blocktrans with username=request.user.username %}You're logged in as {{ username }}{% endblocktrans %}
</code>
<code class="block">
{% blocktrans with modification_date=object.updated|date %}Last updated: {{ modification_date }}{% endblocktrans %}
</code>
</section>
<section class="slide">
<h2>The Not-So-Basics</h2>
<h3>Pluralization</h3>
<p>
Proper usage of the language often requires the translator to
specify multiple translations based on the number of items
being referred to. This is even useful in English for avoiding
sloppy forms like <samp>follower(s)</samp>:
</p>
<code class="block">
{% blocktrans count followers=user.followers.count %}<br>
You have only one follower.<br>
{% plural %}<br>
You have {{ followers }} followers.<br>
{% endblocktrans %}<br>
</code>
<p>
Gettext has support for translators adding more than one or two
translations based on a particular language's rules for
<a href="http://www.gnu.org/savannah-checkouts/gnu/gettext/manual/html_node/Translating-plural-forms.html#Translating-plural-forms">plural forms</a>.
The syntax is unfriendly but it allows you to correctly handle
most languages
</p>
</section>
<section class="slide">
<h2>The Not-So-Basics</h2>
<h3>Context</h3>
<p>
A translator may not be able to tell what a word means without
additional context (is <samp>June</samp> a month or a name? Is
<samp>object</samp> a complaint or a programming term?). Django
exposes the underlying gettext framework support:
</p>
<code class="block">
{% trans "Print" context "Type of artwork" %}
</code>
<code class="block">
{% trans "Print" context "Word processor command" %}
</code>
</section>
<section id="locales" class="slide">
<h2>Format Localization</h2>
<p>
Django's <a href="https://docs.djangoproject.com/en/1.4/topics/i18n/formatting/">format localization</a>
support allows you to adjust how dates and numbers are
displayed. Enabling <code>L10N = True</code> in your
settings will activate this globally but sometimes localization
isn't appropriate - perhaps you need to format a number for
another program and want to tell Django not to use a thousands
separator:
</p>
<code class="block">
{% load l10n %}<br>
{% localize off %}<br>
<div data-id="{{ obj.pk }}"><br>
{% endlocalize %}<br>
<div data-id="{{ obj.pk|unlocalize }}"><br>
</code>
<p>
In other cases, you might want to
<a href="https://docs.djangoproject.com/en/1.4/topics/i18n/formatting/#creating-custom-format-files">override Django's
default format rules</a>. In one example, we needed to change
the way full month names were formatted in Portuguese from
<samp lang="pt">20 de Janeiro de 2012</samp> to
<samp lang="pt">20 de janeiro de 2012</samp>.
</p>
<p>
Django's date formatting codes include both <code>F</code>
(long month name, e.g. <samp lang="pt">Janeiro</samp>) and
<code>E</code> (
<q cite="https://docs.djangoproject.com/en/1.4/ref/templates/builtins/#date">
locale specific alternative representation usually used for
long date representation
</q>, e.g. <samp lang="pt">janeiro</samp>) so we simply need to
tell Django to use a different <code>DATE_FORMAT</code> for Portuguese.
This required creating a Python module structure under my
project so <samp>locale/pt/formats.py</samp> could set
<code>DATE_FORMAT = 'j \d\e E \d\e Y'</code>.
</p>
</section>
<section class="slide">
<h2>Dealing with translation services</h2>
<p>
By now, your project has a bunch of gettext .po files containing
English text which need to be translated. There are a number of
commercial translation firms and some community-oriented ones
such as <a href="http://transifex.com">Transifex</a> and
<a href="http://translate.google.com/toolkit/">Google Translator Toolkit</a>.
The right choice will depend on your project, team and budget.
</p>
<p>
No matter what you pick, you're effectively sending part of your
source code to non-programmers. You'll need to decide how to
handle reviews, version control and how to prevent hand-editing
mistakes or buggy software from destabilizing your translations.
Since gettext will return the original language by default if
there is no available translation, you particularly need to
regularly review the other supported languages.
</p>
</section>
<section id="gettext-utilities" class="slide">
<h2>Translation file utilities</h2>
<p>
Gettext provides a number of
<a href="http://www.gnu.org/software/gettext/manual/html_node/Manipulating.html">handy utilities for working with .po files</a>
without editing them by hand.
In particular,
<a href="http://www.gnu.org/software/gettext/manual/html_node/msgcat-Invocation.html">msgcat</a>,
<a href="http://www.gnu.org/software/gettext/manual/html_node/msgfmt-Invocation.html">msgfmt</a> (consistently formats files)
and
<a href="http://www.gnu.org/software/gettext/manual/html_node/msggrep-Invocation.html">msggrep</a> (structure-aware search)
all have cumbersome UIs but will save you hassle in the long run.
</p>
<p>
By default, .po files include a comment with the source
locations for each msgid and string. This is helpful context in
some cases but it has the disadvantage of causing your .po files
to be different every time you run <code>makemessages</code>
after an unrelated source change which changes the line number
of a translated string. Git's textconv feature allows me to
run an external program when diff-ing a .po file and the msgcat
utility allows me to sort the file and strip the location
comments so I can focus on changes to the actual translation
strings.
</p>
<p>
<a href="https://github.com/acdha/unix_tools/blob/master/etc/gitattributes"><code>~/.gitattributes</code></a>:
<code>*.po diff=msgcat</code>
</p>
<p>
<a href="https://github.com/acdha/unix_tools/blob/master/etc/gitconfig#L53-54"><code>~/.gitconfig</code></a>:
<code>
[diff "msgcat"]<br>
textconv = msgcat --no-location --sort-output
</code>
</p>
</section>
<section class="slide exit">
<nav>
<a href="index.html#agenda">Back to agenda</a>
</nav>
</section>
<a href="#" class="deck-prev-link" title="Previous">←</a>
<a href="#" class="deck-next-link" title="Next">→</a>
<!-- deck.status snippet -->
<p class="deck-status">
<span class="deck-status-current"></span>
/
<span class="deck-status-total"></span>
</p>
<!-- deck.goto snippet -->
<form action="." method="get" class="goto-form">
<label for="goto-slide">Go to slide:</label>
<input type="text" name="slidenum" id="goto-slide" list="goto-datalist">
<datalist id="goto-datalist"></datalist>
<input type="submit" value="Go">
</form>
<!-- deck.hash snippet -->
<a href="." title="Permalink to this slide" class="deck-permalink">#</a>
<script src="deck.js/jquery-1.7.2.min.js"></script>
<script src="deck.js/core/deck.core.js"></script>
<script src="deck.js/core/deck.core.js"></script>
<script src="deck.js/extensions/hash/deck.hash.js"></script>
<script src="deck.js/extensions/menu/deck.menu.js"></script>
<script src="deck.js/extensions/goto/deck.goto.js"></script>
<script src="deck.js/extensions/status/deck.status.js"></script>
<script src="deck.js/extensions/navigation/deck.navigation.js"></script>
<script>
$(function() {
$.deck('.slide');
});
</script>
</body>
</html>