<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Ceres Logic :: Blog &#187; l10n</title>
	<atom:link href="http://www.cereslogic.com/pages/tag/l10n/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.cereslogic.com/pages</link>
	<description>Mobile and Web App Development</description>
	<lastBuildDate>Tue, 23 Aug 2011 14:45:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>L10N with Python in Avant Window Navigator Applets</title>
		<link>http://www.cereslogic.com/pages/2007/10/30/l10n-with-python-in-avant-window/</link>
		<comments>http://www.cereslogic.com/pages/2007/10/30/l10n-with-python-in-avant-window/#comments</comments>
		<pubDate>Tue, 30 Oct 2007 17:23:00 +0000</pubDate>
		<dc:creator>mdesjardins</dc:creator>
				<category><![CDATA[blog]]></category>
		<category><![CDATA[awn]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[l10n]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://mikedesjardins.us/wordpress/2007/10/l10n-with-python-in-avant-window-navigator-applets/</guid>
		<description><![CDATA[It&#8217;s been a while since I blogged about my latest &#8220;hobby&#8221; (oh, how my wife would cringe), writing Python-based applets for the Avant Window Navigator. I&#8217;ve been spending a lot of time working on my clock/calendar applet lately, but today I&#8217;m going to go back to my weather applet because it&#8217;s more interesting to write [...]]]></description>
			<content:encoded><![CDATA[<p><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.dragonflymarsh.com/blog/uploaded_images/file-788164.png"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://www.dragonflymarsh.com/blog/uploaded_images/file-788161.png" alt="" border="0" /></a>It&#8217;s been a while since I blogged about my latest &#8220;hobby&#8221; (oh, how my wife would cringe), writing Python-based applets for the <a href="http://wiki.awn-project.org/">Avant Window Navigator</a>.  I&#8217;ve been spending a lot of time working on my <a href="http://wiki.awn-project.org/index.php?title=Clock/Calendar_Applet">clock/calendar applet</a> lately, but today I&#8217;m going to go back to my <a href="http://wiki.awn-project.org/index.php?title=Weather_Applet">weather applet</a> because it&#8217;s more interesting to write about.</p>
<p>A while ago, I decided it was time to add some alternate (that is, non-English) language support to the applet.  The de-facto standard tool to accomplish this is the Python variant of GNU&#8217;s <a href="http://docs.python.org/lib/module-gettext.html">gettext</a>.  I found a couple of resources that helped guide me through this.  The first was the <a href="http://wiki.laptop.org/go/Python_i18n">wiki</a> for the &#8220;one laptop per child&#8221; project, which does a lot of L10N in Python.    The second was an excellent <a href="http://http//www.learningpython.com/2006/12/03/translating-your-pythonpygtk-application/">post</a> in the &#8220;Learning Python&#8221; blog (the author doesn&#8217;t give his/her name in the &#8220;<a href="http://www.learningpython.com/who-am-i/">Who am I</a>&#8221; section, otherwise I&#8217;d give props).  And of course, there&#8217;s always the official Python <a href="http://docs.python.org/lib/node732.html">library documentation</a>, too.</p>
<p>The first step is to import the gettext libraries into your Python code, and do some setup work, thusly:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;">APP=<span style="color: #483d8b;">&quot;awn-weather-applet&quot;</span>
DIR=<span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">dirname</span> <span style="color: black;">&#40;</span>__file__<span style="color: black;">&#41;</span> + <span style="color: #483d8b;">'/locale'</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">locale</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">gettext</span>
<span style="color: #808080; font-style: italic;">#locale.setlocale(locale.LC_ALL, '')</span>
<span style="color: #dc143c;">gettext</span>.<span style="color: black;">bindtextdomain</span><span style="color: black;">&#40;</span>APP, DIR<span style="color: black;">&#41;</span>
<span style="color: #dc143c;">gettext</span>.<span style="color: black;">textdomain</span><span style="color: black;">&#40;</span>APP<span style="color: black;">&#41;</span>
_ = <span style="color: #dc143c;">gettext</span>.<span style="color: #dc143c;">gettext</span></pre></div></div>

<p>(Note: Made an edit to the above source code on Nov 16 2007 &#8211; evidently, the DIR parameter needs the full path to work properly)</p>
<p>Now, here&#8217;s what we&#8217;re actually doing.  The call to <span style="font-family:courier new;">bindtextdomain</span> is <span style="font-style: italic;">binding</span> the <span style="font-weight: bold;">awn-weather-applet</span> domain to the <span style="font-weight: bold;">locale</span> subdirectory. When the applet is deployed, it has a locale directory right underneath the main script, weather.py.  The directory argument of <span style="font-family:courier new;">bindtextdomain</span> is relative, so we&#8217;re pointing gettext at that directory.  We&#8217;re basically telling gettext to look in the locale subdirectory for files named <span class="file"><var>language</var>/LC_MESSAGES/<var></var>awn-weather-applet.mo, where language is the two-letter language code defined by the environment (more on that later).</p>
<p>It should be noted that, customarily, locale files are stored in a default location which is a more global place in the filesystem, e.g. /usr/share/locale/<span style="font-style: italic;">language</span>/LC_MESSAGES.  If you don&#8217;t supply a directory to the <span style="font-family:courier new;">bindtextdomain</span> method call, gettext will use the default directory for the system.  I opted <span style="font-style: italic;">not</span> to use the default filesystem location because I wanted non-superusers to be able to easily use language files that I supply with the applet, using the standard AWN applet installation mechanism.  Requiring the user to put .mo files in the /usr/share directory tree isn&#8217;t an option.</p>
<p>The call to <span style="font-family:courier new;">textdomain</span> sets the global domain to awn-weather-applet.  Essentially we&#8217;re telling gettext that all future calls into gettext should use the </span><span class="file">awn-weather-applet domain as its source for translations.</p>
<p>Lastly, the line that reads <span style="font-family:courier new;">_ = gettext.gettext()</span> defines a convenient alias that we will use to identify strings in our code that need translation.  If you&#8217;re familiar with i18n of C applications, this will look quite familiar; in the C implementation, an identically named macro is used for the same purpose.</p>
<p>The next step is to read through the code to identify the literal strings that will be translated.  Generally, any UI element that a user will see is a candidate for translation.  Things like log and console output are not as important.  When we find a candidate string, we wrap it in _( ).  For example, this:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #008000;">self</span>.<span style="color: black;">dialog</span>.<span style="color: black;">set_title</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Forecast&quot;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>becomes<br /></span></p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #008000;">self</span>.<span style="color: black;">dialog</span>.<span style="color: black;">set_title</span><span style="color: black;">&#40;</span>_<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Forecast&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Because of the alias at the top of the source file, what we&#8217;re <span style="font-style: italic;">really</span> doing is this:</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #008000;">self</span>.<span style="color: black;">dialog</span>.<span style="color: black;">set_title</span><span style="color: black;">&#40;</span><span style="color: #dc143c;">gettext</span>.<span style="color: #dc143c;">gettext</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;Forecast&quot;</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>It&#8217;s a good thing we have the shorthand version!</p>
<p>As I alluded to earlier, gettext does its magic by reading files that ends in an .mo extension.  At runtime, it reads the string that&#8217;s passed into it (in the above example, &#8220;Forecast&#8221;), then looks up that string in the proper .mo file to see if a translation is available.  If it finds an entry, it returns the translated string, otherwise it just returns the string that was passed into it.  So our next task is to create the .mo files.</p>
<p>Creating .mo files is a three step process.  First, we need to run a tool that extracts all of the strings to be translated into a usable text file that a translator can edit.  The tool to do this is called xgettext.  The command line to run looks like this:<br /><span style="font-family:monospace;"><br /></span></p>
<pre>
xgettext --language=Python --keyword=_ --output=awn-weather-applet.pot *.py</pre>
<p>This command generates an output file named awn-weather-applet.pot which acts as a template for translators to do their translation.  The file has a bunch of lines that look like this:</p>
<pre>
#: weather.py:127
msgid "Forecast"
msgstr ""
</pre>
<p>So, let&#8217;s say we want to make a Spanish translation of the weather applet.  First, we&#8217;d make a copy of this file, and name it something sensible like awn-weather-applet.po (note, you can also use the msginit tool, which does a few other housekeeping things for you like fill in the e-mail address).  We&#8217;d find all the lines that start with msgid, translate it, and put the result in the following line&#8217;s msgstr, like this:</p>
<pre>
#: weather.py:127
msgid "Forecast"
msgstr "Pronóstico"
</pre>
<p>Next, we need to &#8220;compile&#8221; the awn-weather-applet.po file into an awn-weather-applet.mo file, so that it&#8217;s usable by gettext at runtime.  This is done using the msgfmt command, like this:</p>
<p>msgfmt awn-weather-applet.po -o awn-weather-applet.mo</p>
<p>That&#8217;s it!  Now all you need to do is ensure that the awn-weather-applet.mo file ends up in the (base of weather applet)/locale/es/LC_MESSAGES directory, and Spanish translations work!<br /><span class="file"><span style="font-size:85%;"><span style="font-family:courier new;"></span></span></span></p>
]]></content:encoded>
			<wfw:commentRss>http://www.cereslogic.com/pages/2007/10/30/l10n-with-python-in-avant-window/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

