Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 191 lines (143 sloc) 7.06 kb
63d1531 @mikewest Initial commit.
authored
1 Static `gettext`
2 ===============
3
4 Django's internationalization framework is a relatively clean integration
5 of [`gettext`][gettext]'s approach to the tough problem of localizing
6 dynamic applications. The process is straightforward:
7
8 1. Text in an application's code and templates is marked for future
9 translation, generally through `_('text')` function calls in code,
10 and `{% trans ... %}` or `{% blocktrans %}...{% endblocktrans %}`
11 tags in templates.
12
13 2. Translatable strings are extracted from the application's source
14 into _message files_ in a standard format. These files contain
15 each string (almost always in English) in the application , and
16 allow translators to easily map them to their target-language
17 counterparts. One file is generated per language.
18
19 3. Fully translated message files are then compiled into a binary
20 format for quick lookup, and loaded into the application, where
21 they can be used to dynamically generate responses in a language
22 of the user's choice.
23
24 This is a good workflow, one which I'd like to replicate for static
25 documents and websites. As you might have guessed, that's where this
26 repository comes in. `static_gettext.py` wraps the `gettext` framework,
27 allowing static documents to be used as translation templates, generating
28 one copy of a set of documents for each language available. From
29 `index.html`, you can produce `en_US/index.html`, `de_DE/index.html`, and
30 so on, enabling (among other things) multi-language static websites.
31
32
05e3fef @mikewest Crosslinking README.
authored
33 [gettext]: http://www.gnu.org/software/gettext/
34
63d1531 @mikewest Initial commit.
authored
35 Usage
36 -----
37
38 The first step is to mark up the documents you'd like to translate.
39 `static_gettext.py` supports the following markup formats out of the
40 box:
41
42 * `{% blocktrans %}TRANSLATABLE STRING{% endblocktrans %}`
43 * `<blocktrans>TRANSLATABLE STRING</blocktrans>`
44 * `<!-- blocktrans -->TRANSLATABLE STRING<!-- /blocktrans -->`
45 * `/* blocktrans */TRANSLATABLE STRING/* /blocktrans */`
46
47 I prefer the former, as it makes "upgrading" to a real Django project
48 easy, but the others might be worthwhile, depending on your needs.
49
50 Let's take the following HTML document as an example:
51
52 <!doctype html>
53 <html lang="en">
54 <head>
55 <title>Hello, world!</title>
56 </head>
57 <body>
58 <p>This is a document!</p>
59 </body>
60 </html>
61
62 Three strings would need translation if I wanted to render this page in German,
05e3fef @mikewest Crosslinking README.
authored
63 so let's demarcate them as translatable (see [`./example/src/index.html`][example_index]):
63d1531 @mikewest Initial commit.
authored
64
65 <!doctype html>
66 <html lang="{% blocktrans %}en{% endblocktrans %}">>
67 <head>
68 <title>{% blocktrans %}Hello, world!{% endblocktrans %}</title>
69 </head>
70 <body>
71 <p>{% blocktrans %}This is a document!{% endblocktrans %}</p>
72 </body>
73 </html>
74
05e3fef @mikewest Crosslinking README.
authored
75 [example_index]: http://github.com/mikewest/static_gettext/blob/master/example/src/index.html
76
63d1531 @mikewest Initial commit.
authored
77 With those markers in place, I can generate message files:
78
79 static_gettext.py --input ./example/src --locale ./example/locale --languages en_US,de_DE
80
05e3fef @mikewest Crosslinking README.
authored
81 This command generates [`./example/locale/en_US/LC_MESSAGES/messages.po`][example_po_en]
82 and [`./example/locale/de_DE/LC_MESSAGES/messages.po`][example_po_de], which both look
63d1531 @mikewest Initial commit.
authored
83 like:
84
85 # SOME DESCRIPTIVE TITLE.
86 # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
87 # This file is distributed under the same license as the PACKAGE package.
88 # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
89 #
90 #, fuzzy
91 msgid ""
92 msgstr ""
93 "Project-Id-Version: PACKAGE VERSION\n"
94 "Report-Msgid-Bugs-To: \n"
95 "POT-Creation-Date: 2010-09-12 16:34+0200\n"
96 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
97 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
98 "Language-Team: LANGUAGE <LL@li.org>\n"
99 "Language: \n"
100 "MIME-Version: 1.0\n"
101 "Content-Type: text/plain; charset=UTF-8\n"
102 "Content-Transfer-Encoding: 8bit\n"
103
104 #: example/src/index.html:2
105 msgid "en"
106 msgstr ""
107
108 #: example/src/index.html:4
109 msgid "Hello, world!"
110 msgstr ""
111
112 #: example/src/index.html:7
113 msgid "This is a document!"
114 msgstr ""
115
05e3fef @mikewest Crosslinking README.
authored
116 [example_po_de]: http://github.com/mikewest/static_gettext/blob/master/example/locale/de_DE/LC_MESSAGES/messages.po
117 [example_po_en]: http://github.com/mikewest/static_gettext/blob/master/example/locale/en_US/LC_MESSAGES/messages.po
118
63d1531 @mikewest Initial commit.
authored
119 You should edit the header information of each `.po` file to reflect who's
120 actually responsible for each, then hand the file to that person for
121 translation. That's as straightforward as you'd expect:
122
123 ...
124
125 #: example/src/index.html:2
126 msgid "en"
127 msgstr "de"
128
129 #: example/src/index.html:4
130 msgid "Hello, world!"
131 msgstr "Hallo Welt!"
132
133 #: example/src/index.html:7
134 msgid "This is a document!"
135 msgstr "Dies ist ein Dokument!"
136
137 ...
138
139 `gettext` wants a compiled message file, so, run:
140
141 static_gettext.py --compile --input ./example/src --locale ./example/locale --languages en_US,de_DE
142
143 That generates binary `.mo` files for both `en_US` and `de_DE`, which can be used
98713ce @mikewest Cleanup.
authored
144 to generate translated versions of the file (see
145 [`./example/build/en_US/index.html`][example_build_en] and
146 [`./example/build/de_DE/index.html`][example_build_de])
63d1531 @mikewest Initial commit.
authored
147
148 static_gettext.py --render --input ./example/src --locale ./example/locale --output ./example/build --languages en_US,de_DE
149
05e3fef @mikewest Crosslinking README.
authored
150 [example_build_de]: http://github.com/mikewest/static_gettext/blob/master/example/build/de_DE/index.html
151 [example_build_en]: http://github.com/mikewest/static_gettext/blob/master/example/build/en_US/index.html
152
63d1531 @mikewest Initial commit.
authored
153 In a nutshell:
154
155 static_gettext.py --input ./path/to/input/root --locale ./path/to/locale/root --languages LANG1,LANG2,...
156 # translate the message files in `./path/to/locale/root/[LANG]/LC_MESSAGES/`
157 static_gettext.py --input ./path/to/input/root --locale ./path/to/locale/root --languages LANG1,LANG2,... --compile
158 static_gettext.py --input ./path/to/input/root --locale ./path/to/locale/root --output ./path/to/build/root --languages LANG1,LANG2,... --render
159
160
161 Notes
162 -----
163
164 * By default, only files with `.html`, `.js`, `.css`, `.txt`, and
165 `.markdown` extensions are processed for translation. Files with other
166 extensions are simply copied over to the build directories. This means
167 you'll end up with multiple copies of images. So far, I see this as an
168 acceptable trade-off...
169
98713ce @mikewest Cleanup.
authored
170 * The defaults are sensible, so, if you run the script from the root of a project
171 laid out as follows, you can leave off the script's `input`, `locale`, and
172 `output` arguments:
173
174 - project root
175 - src
176 - locale
177 - build
178
179 Running `static_gettext.py --language en_US,de_DE` would be enough. The `example`
180 directory in this project is, unsurprisingly, just such an example. For extra details,
181 see the [Makefile][make] in that directory.
182
183 [make]: http://github.com/mikewest/static_gettext/blob/master/example/Makefile
e7a9430 @mikewest Fixing a small bug in message generation, adding BSD license info.
authored
184
185 License
186 -------
187
188 [BSD licensed][license]
189
190 [license]: http://github.com/mikewest/static_gettext/blob/master/LICENSE.markdown
Something went wrong with that request. Please try again.