<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Pablo's Development Blog</title>
	
	<link>http://mel.melaxis.com/devblog</link>
	<description />
	<lastBuildDate>Mon, 10 Apr 2006 12:30:41 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/PablosDevelopmentBlog" /><feedburner:info uri="pablosdevelopmentblog" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId>PablosDevelopmentBlog</feedburner:emailServiceId><feedburner:feedburnerHostname>http://feedburner.google.com</feedburner:feedburnerHostname><feedburner:browserFriendly>This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site, subject to copyright and fair use.</feedburner:browserFriendly><item>
		<title>Benchmarking PHP Localization – Is gettext fast enough?</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/_qDsf7EHNEY/</link>
		<comments>http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/#comments</comments>
		<pubDate>Mon, 10 Apr 2006 12:11:54 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/</guid>
		<description><![CDATA[Last year, I wrote a post about using gettext to localize PHP web pages. Gettext makes it easy to maintain the translations and always provides a fallback locale. But is it fast? I created a simple web page to compare the performance of various localization methods for PHP. It only contains 3 localized strings and [...]]]></description>
				<content:encoded><![CDATA[<p>Last year, I wrote a post about <a href="http://mel.melaxis.com/devblog/2005/08/06/localizing-php-web-sites-using-gettext/">using gettext to localize PHP web pages</a>. Gettext makes it easy to maintain the translations and always provides a fallback locale. But is it fast?</p>
<p>I created a simple web page to compare the performance of various localization methods for PHP. It only contains 3 localized strings and does not use advanced features of gettext (e.g. plurals). I wrote a version using the gettext PHP extension (&#8220;gettext Ext.&#8221;), one using PHP-gettext (&#8220;gettext PHP&#8221;, a gettext implementation written in pure PHP) and a version that does not use gettext at all, instead it uses an array that contains all the translations (&#8220;String ID&#8221;).</p>
<p><span id="more-13"></span></p>
<p>I put all three pages on a Debian machine with the latest Apache 2.0.55 and PHP 5.1.2 and used the <a href="http://httpd.apache.org/docs/2.0/programs/ab.html">Apache HTTP server benchmarking tool</a> to measure the performance of the different methods. I always made two tests &#8211; using the default locale (English) and a translation (German), because gettext does not have to use a locale file for the default language (it&#8217;s embedded in the page).</p>
<h4>The results</h4>
<p>Here are the results (requests per second, more is better, I used &#8220;ab -n 5000 URL&#8221;):</p>
<p><img src="http://mel.melaxis.com/devblog/wp-content/uploads/2006/04/php-gettext-rps.png" alt="Requests per Second" class="centered" /></p>
<p>As you can see, the version using <em>the PHP gettext extension is the fastest solution</em>. It is only marginally slower when using a language file, because the extension caches the translations (the downside is that you have to restart the webserver when you change a locale file). The String ID version is equally fast for either locale, because it always has to lookup the text in the locale array. The pure PHP gettext implementation is the slowest solution, and even slower when it needs to use a locale file. This is understandable because it always has to read the whole file for every request.</p>
<p>You can <a href="http://mel.melaxis.com/devblog/wp-content/uploads/2006/04/localization-benchmark.zip">download the test files</a> if you&#8217;re interested.</p>
<h4>The gettext extension</h4>
<p>When using the gettext extension on Linux, make sure all used locales are installed on the system. For example, in Debian (and probably other distributions, too), add the required locales to /etc/locale.gen and run locale-gen. For this test, I added &#8220;de_DE.UTF-8 UTF-8&#8243; for the German UTF-8 version.</p>
<h4>Conclusion</h4>
<p>The native gettext extension for PHP performed best in the benchmarks. Using the gettext Extension allows you to create clean code by only wrapping strings in _() and including a file that loads the localizations. As I already wrote in my previous post, gettext also allows painless updates of the localizations by automatically finding new or changed strings. The only downside is that the webserver must be restarted to load the new language files. This can be a problem for users on shared hosts. In these cases, a pure PHP implementation can be used (which is not very fast though).</p>
<p>If you can restart the webserver, I recommend using gettext. It is very useful especially for larger projects or projects where not all translations can be updated simultaneously, because it always provides a fallback language. It also makes the source code much easier to read, because the default texts are directly in the source code.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=_qDsf7EHNEY:rsAHooGi7aU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=_qDsf7EHNEY:rsAHooGi7aU:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/_qDsf7EHNEY" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/feed/</wfw:commentRss>
		<slash:comments>14</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/</feedburner:origLink></item>
		<item>
		<title>Improved Ruby syntax highlighting for Notepad2</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/7l1zfGjbgoo/</link>
		<comments>http://mel.melaxis.com/devblog/2005/09/03/improved-ruby-syntax-highlighting-for-notepad2/#comments</comments>
		<pubDate>Sat, 03 Sep 2005 17:04:56 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/devblog/?p=11</guid>
		<description><![CDATA[In my previous post about Notepad2 (Notepad2 with Syntax highlighting for Ruby, YAML, Bash and Conf), I posted a modified version of the Notepad2 editor which included syntax highlighting for Ruby and some other languages. I have now extended Scintilla&#8217;s Ruby lexer and Notepad2 to support seperate syntax highlighting for symbols, instance variables, class variables [...]]]></description>
				<content:encoded><![CDATA[<p><img src="http://mel.melaxis.com/webdev-blog/wp-content/images/devblog/notepad2-ruby-1.png" width="222" height="170" alt="Notepad2 Screenshot" title="Notepad2 Screenshot" class="alignright" />In my previous post about Notepad2 (<a href="http://mel.melaxis.com/devblog/2005/08/14/notepad2-with-syntax-highlighting-for-ruby-yaml-bash-and-conf/">Notepad2 with Syntax highlighting for Ruby, YAML, Bash and Conf</a>), I posted a modified version of the <a href="http://www.flos-freeware.ch/notepad2.html">Notepad2</a> editor which included syntax highlighting for Ruby and some other languages.</p>
<p>I have now extended Scintilla&#8217;s Ruby lexer and Notepad2 to support seperate syntax highlighting for symbols, instance variables, class variables and global variables. I also added the keywords &#8220;private&#8221;, &#8220;protected&#8221; and &#8220;public&#8221; to Notepad2&#8242;s keyword list.</p>
<p><a href="http://www.melaxis.com/download/notepad2mod/notepad2mod.zip">Download the new, modified Notepad2</a>. This version includes English and German binaries and the complete source code including Scintilla.</p>
<p><strong>Update:</strong> I have made some more small modifications to the Ruby lexer which fix some bugs. A single &#8216;:&#8217; character is now no longer highlighted as a symbol, and expressions such as &#8220;@test.x&#8221; are now highlighted correctly (@test as a local variable, . as an operator and x as an identifier). Download the fixed version from above.<br />
<br style="clear: both" /></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=7l1zfGjbgoo:V34QIDySxkQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=7l1zfGjbgoo:V34QIDySxkQ:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/7l1zfGjbgoo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/09/03/improved-ruby-syntax-highlighting-for-notepad2/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/09/03/improved-ruby-syntax-highlighting-for-notepad2/</feedburner:origLink></item>
		<item>
		<title>Notepad2 with Syntax highlighting for Ruby, YAML, Bash and Conf</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/P5kt5a4Smz0/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/14/notepad2-with-syntax-highlighting-for-ruby-yaml-bash-and-conf/#comments</comments>
		<pubDate>Sun, 14 Aug 2005 16:43:42 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/devblog/?p=10</guid>
		<description><![CDATA[The free, open source Notepad2 is my favorite text editor. I use Notepad2 to edit HTML, PHP, JavaScript etc. Unfortunately, Notepad2 does not have syntax highlighting for all file types supported by Scintilla. Wesner Moise offers a version of Notepad2 with Ruby support. I have created a modified version of Notepad2 that is based on [...]]]></description>
				<content:encoded><![CDATA[<p>The free, open source <a href="http://www.flos-freeware.ch/notepad2.html">Notepad2</a> is my favorite text editor. I use Notepad2 to edit HTML, PHP, JavaScript etc. Unfortunately, Notepad2 does not have syntax highlighting for all file types supported by Scintilla. Wesner Moise offers a version of <a href="http://wesnerm.blogs.com/net_undocumented/2005/07/notepad2_with_r.html">Notepad2 with Ruby support</a>. I have created a modified version of Notepad2 that is based on Wesner Moise&#8217;s changes to Ruby and Makefile support and Scintilla 1.65 and added Syntax highlighting for YAML, Bash shell scripts and Apache configuration files. I have compiled an English and German version (I have extracted the German strings from the official German Notepad2 build using <a href="http://www.angusj.com/resourcehacker/">Resource Hacker</a>). I also added the extensions .rhtml (for Ruby on Rails), .php4 and .php5 to the HTML lexer (if you have previously used Notepad2, you may have to add them manually under View -&gt; Customize Schemes). It does however not highlight the included Ruby code in .rhtml files, because the <a href="http://www.scintilla.org/">Scintilla</a> HTML Lexer does not yet support included Ruby in HTML. It should be possible to include that feature in LexHTML.cxx somehow, but that Lexer is quite complex. I don&#8217;t know if someone is working on that. If you find something, please let me know.</p>
<p><a href="http://www.melaxis.com/download/notepad2mod/notepad2mod.zip">Download modified Notepad2</a> (includes English and German binaries + source code)</p>
<p>This version comes with syntax highlighting support for HTML, XML, CSS, JavaScript, VBScript, ASP, PHP, CSS, Perl, C, C++, C#, Java, Visual Basic, Pascal, Assembler, SQL, Python, NSIS, INI, REG, INF, BAT, DIFF, Ruby, YAML, Bash and Apache configuration files.</p>
<p><strong>Update:</strong> I have replaced the download with a newer version which includes an improved Ruby lexer. See my post <a href="http://mel.melaxis.com/devblog/2005/09/03/improved-ruby-syntax-highlighting-for-notepad2/">Improved Ruby syntax highlighting for Notepad2</a>. If you need the old version, <a href="http://www.melaxis.com/download/notepad2mod/notepad2mod-old.zip">here it is</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=P5kt5a4Smz0:vaJrejS-Dhk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=P5kt5a4Smz0:vaJrejS-Dhk:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/P5kt5a4Smz0" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/14/notepad2-with-syntax-highlighting-for-ruby-yaml-bash-and-conf/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/14/notepad2-with-syntax-highlighting-for-ruby-yaml-bash-and-conf/</feedburner:origLink></item>
		<item>
		<title>A flexible caching system for .NET: MelCache</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/0IxomaG6CzI/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/07/a-flexible-caching-system-for-net-melcache/#comments</comments>
		<pubDate>Sun, 07 Aug 2005 16:49:10 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[.NET]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/devblog/?p=9</guid>
		<description><![CDATA[MelCache is a caching system for .NET I wrote some time ago in C#. It is very easy to use and allows you to cache arbitrary data (byte[]) with a key (string) and an expiry date. You can also compress the cached data using GZip or BZip2 (uses the #ziplib library). Cached data can be [...]]]></description>
				<content:encoded><![CDATA[<p>MelCache is a caching system for .NET I wrote some time ago in C#. It is very easy to use and allows you to cache arbitrary data (byte[]) with a key (string) and an expiry date. You can also compress the cached data using GZip or BZip2 (uses the <a href="http://www.icsharpcode.com/OpenSource/SharpZipLib/Default.aspx">#ziplib</a> library). Cached data can be retrieved later if it has not yet expired. The caching system creates binary files which are used to store the cached data (by default 5 MB) and an XML index file. New cache files are created if needed. This is done to reduce hard drive fragmentation when caching small files. Cached files can also be deleted from the cache at any time. It is especially useful to cache files your application downloads from the Internet. <em>Note: MelCache has nothing to do with ASP.NET or ASP.NET&#8217;s caching capabilities</em> (however, it should work with ASP.NET, too).</p>
<p>The MelCache DLL (Melaxis.MelCache.dll) is signed with a strong name and can be installed in the <acronym title="Global Assembly Cache">GAC</acronym>. It requires <a href="http://www.icsharpcode.com/OpenSource/SharpZipLib/Default.aspx">ICSharpCode.SharpZipLib.dll</a> (included in the ZIP file).</p>
<p><a href="http://www.melaxis.com/download/melcache/melcache.zip">Download MelCache</a> (Documentation included)</p>
<p>You can use MelCache in freeware and open source projects without restriction. Please contact me if you want to use it otherwise. If you find any bugs in MelCache or if you have an idea how it could be improved, please leave a comment.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=0IxomaG6CzI:VaXxPc650-Y:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=0IxomaG6CzI:VaXxPc650-Y:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/0IxomaG6CzI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/07/a-flexible-caching-system-for-net-melcache/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/07/a-flexible-caching-system-for-net-melcache/</feedburner:origLink></item>
		<item>
		<title>MultiWP 1.1</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/Ji62F5MjLis/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/06/multiwp-11/#comments</comments>
		<pubDate>Sat, 06 Aug 2005 10:22:33 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/devblog/2005/08/06/multiwp-11/</guid>
		<description><![CDATA[I have just installed some WordPress plugins that try to create files in the blog directory. They use the get_home_path function, which returns the path of the WordPress installation. Therefore, the files are created in the wrong directory. To fix this problem, I have released an updated version of MultiWP. Unfortunately, this time, you have [...]]]></description>
				<content:encoded><![CDATA[<p>I have just installed some WordPress plugins that try to create files in the blog directory. They use the get_home_path function, which returns the path of the WordPress installation. Therefore, the files are created in the wrong directory. To fix this problem, I have released an updated version of MultiWP. Unfortunately, this time, you have to modify one line in admin-functions.php to make it work. Detailed installation instructions are included. If you update from MultiWP 1.0, be sure to also update the multiwp.php plugin file. I have also improved the wordpress/wp-config.php file. I encourage you to update that file as well (be sure to adjust the base path in line 11 if needed (see included howto)), but it should work with the old one as well.</p>
<p><a href="http://mel.melaxis.com/files/multiwp.zip">Download MultiWP 1.1</a></p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=Ji62F5MjLis:pv0fOnELXJk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=Ji62F5MjLis:pv0fOnELXJk:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/Ji62F5MjLis" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/06/multiwp-11/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/06/multiwp-11/</feedburner:origLink></item>
		<item>
		<title>Localizing PHP web sites using gettext</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/Cvc_IIyYvO4/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/06/localizing-php-web-sites-using-gettext/#comments</comments>
		<pubDate>Sat, 06 Aug 2005 09:36:53 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/webdev-blog/?p=7</guid>
		<description><![CDATA[Developing multi language web sites using PHP is actually very easy. A common approach is having an include file for every supported language which contains an array that maps string ids to localized text (for example "WelcomeText" => "Welcome to our homepage." would be included using something like &#60;?= $strings["WelcomeText"] &#62;). However there are several [...]]]></description>
				<content:encoded><![CDATA[<p>Developing multi language web sites using PHP is actually very easy. A common approach is having an include file for every supported language which contains an array that maps string ids to localized text (for example "WelcomeText" => "Welcome to our homepage." would be included using something like &lt;?= $strings["WelcomeText"] &gt;). However there are several problems with this approach. First of all, when the application is updated and additional strings are added, there is no way to determine which new strings were added and if they are present in every language (unless you write a script for it). What happens if a newly added string is not yet translated into a specific language?</p>
<h4>Using gettext with PHP</h4>
<p>A widely used framework for internationalization is <a href="http://www.gnu.org/software/gettext/gettext.html">gettext</a>. It can be used with a variety of programming languages, including PHP. There are basically two ways to use gettext in your PHP applications. You can use the native <a href="http://www.php.net/gettext">gettext PHP extension</a> or you can use a library implemented in PHP that does not need any extension, such as <a href="http://savannah.nongnu.org/projects/php-gettext/">php-gettext</a>. I will use the native PHP extension, but once you have read the post you should be able to use the php-gettext-library, too (have a look at the included example).</p>
<p><span id="more-7"></span></p>
<h4>Using gettext in your application</h4>
<p>Using gettext to get translated strings couldn't be easier. Just call gettext("Text to be translated") and you get a localized version of "Text to be translated" if available, or "Text to be translated" otherwise. If you're lazy, you can use _() instead of gettext().<br />
Let's try this out. Create a new PHP file (we'll call it test.php), and insert the following code:</p>
<div class="syntax_hilite">
<div id="php-4">
<div class="php"><span style="color:#000000; font-weight:bold;">&lt;?php</span><br />
<a href="http://www.php.net/echo"><span style="color:#000066;">echo</span></a> <a href="http://www.php.net/_"><span style="color:#000066;">_</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#FF0000;">"Hello World!"</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<span style="color:#000000; font-weight:bold;">?&gt;</span></div>
</div>
</div>
<p></p>
<p>When you open that page in your browser, you will see "Hello World!".</p>
<h4>Localizing your application</h4>
<p>Now that you have created a first version of your script, you may want to create localized versions for different languages. In order to do that, you either need the <a href="http://www.gnu.org/software/gettext/">gettext utilities</a> (<a href="http://gnuwin32.sourceforge.net/packages/gettext.htm">windows version</a>) or the graphical editor <a href="http://www.poedit.org/">poEdit</a>, which we will be using. So install and launch poEdit. Create a new catalog (File -&gt; new catalog). Choose the language you want to translate the application to. I'm going to use German, GERMANY and iso-8859-1 for both character sets. The last option (plural forms) is an advanced feature of gettext which I'm going to explain later, so for now, just leave it blank. On the paths tab, set the base path to the directory containing your test.php file, and add "." as a path. On the keywords tab you can add names of additional functions that call gettext. You may want to use this if you're using php-gettext. Now click OK to open the save dialog. Now create a sub folder called "locale" in your script directory. Create a subdirectory inside locale for every language you support. We'll create one for "de_DE" (the first part is the language, the second part is the country). Inside that folder create another one called "LC_MESSAGES". You should now have a directory structure like locale/de_DE/LC_MESSAGES/. Save the file inside that directory as "messages.po". poEdit will now automatically scan all source files inside the path you specified earlier and extract all strings that are passed to gettext() or _() (or any other methods you may have added in the keywords tab). Click OK. Now that's cool! poEdit just extracted all strings you want to be localized.</p>
<p>In the upper half of the poEdit window, you have a list of strings (the original string on the left and your translation on the right). Select the first string in the list. In the lower left hand corner, you have 2 text boxes. The first one contains the original string, and the second one is still empty. Type your translation into that box. I'll enter "Hallo Welt!". If you had more entries in your file, you could navigate between them by pressing ctrl-up and ctrl-down. Save the file. poEdit automatically created "messages.mo" in the same directory as "messages.po". This is the compiled version that will later be used by PHP.</p>
<p><script type="text/javascript"><!--
google_ad_client = "pub-6722431982008737";
google_ad_width = 468;
google_ad_height = 60;
google_ad_format = "468x60_as";
google_ad_type = "text_image";
google_ad_channel ="0963628652";
google_color_border = "FFFFFF";
google_color_bg = "FFFFFF";
google_color_link = "000000";
google_color_url = "000000";
google_color_text = "6F6F6F";
//--></script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script></p>
<h4>Initializing the gettext library</h4>
<p>Let's see if our script can now display the localized string. The first thing you have to do is telling the gettext library which <a href="http://en.wikipedia.org/wiki/Locale">locale</a> you want to use and where the language files are stored. Let's create a new file called "localization.php" that will handle all the gettext initialization. Copy the following code into localization.php (<em>Note: this code is for the php gettext extension, php libraries such as php-gettext may require different initialization</em>):</p>
<div class="syntax_hilite">
<div id="php-5">
<div class="php"><span style="color:#000000; font-weight:bold;">&lt;?php</span><br />
<span style="color:#0000FF;">$locale</span> = <span style="color:#FF0000;">"de_DE"</span>;<br />
<span style="color:#616100;">if</span> <span style="color:#006600; font-weight:bold;">&#40;</span><a href="http://www.php.net/isset"><span style="color:#000066;">isSet</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF;">$_GET</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#FF0000;">"locale"</span><span style="color:#006600; font-weight:bold;">&#93;</span><span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#0000FF;">$locale</span> = <span style="color:#0000FF;">$_GET</span><span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#FF0000;">"locale"</span><span style="color:#006600; font-weight:bold;">&#93;</span>;<br />
<a href="http://www.php.net/putenv"><span style="color:#000066;">putenv</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#FF0000;">"LC_ALL=$locale"</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<a href="http://www.php.net/setlocale"><span style="color:#000066;">setlocale</span></a><span style="color:#006600; font-weight:bold;">&#40;</span>LC_ALL, <span style="color:#0000FF;">$locale</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<a href="http://www.php.net/bindtextdomain"><span style="color:#000066;">bindtextdomain</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#FF0000;">"messages"</span>, <span style="color:#FF0000;">"./locale"</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<a href="http://www.php.net/textdomain"><span style="color:#000066;">textdomain</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#FF0000;">"messages"</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<span style="color:#000000; font-weight:bold;">?&gt;</span></div>
</div>
</div>
<p></p>
<p>In the first line, we set the default locale to "de_DE". The second line allows you to override this by appending ?locale=... to the URL. You should replace this by real code to select the locale (perhaps by looking at the Accept-Language header), but it works for our tutorial. The next two lines actually set the current locale to that value and the bindtextdomain call creates a text domain which will use our messages.mo file in the locale directory. The textdomain function selects that domain as the default domain.</p>
<p>Include that file in your test.php by adding a require_once("localization.php"); line to the top of the file. Now reload test.php in your browser and it should now say "Hallo Welt!". Now try test.php?locale=en_US and it should say "Hello World!". You have just created a multi language PHP script!</p>
<h4>Updating your application</h4>
<p>Let's add another string to the script. I'll add <code>echo _("Welcome to my test page");</code>. When you reload the page with the German locale, you will see that Hello World is localized, while the second string is not. Let's change that. In poEdit (reopen the .po file if you have closed it), click the update button (that's the one with the wheel). A new dialog should show one new string. Click OK, translate it (in German it would be "Willkommen auf meiner Testseite") and save the file. Reload test.php in your browser. Nothing has changed? <em>When using the PHP gettext extension, .mo files are cached by the gettext library. The only way I have found to clear the cache is to restart the web server.</em> Once you have done that, both strings should be translated.</p>
<h4>Using plurals</h4>
<p>One last thing I want to discuss is gettext's plural support. This may seem a bit complicated at first, but is actually very powerful. Sometimes you need a string that supports plural forms. A typical example would be "0 comments", "1 comment", "2 comments", "3 comments"... In English you can just add an 's' if the number n != 1. However, in other languages it's not that easy. gettext supports all these cases. In your scripts you use something like this:</p>
<div class="syntax_hilite">
<div id="php-6">
<div class="php"><span style="color:#000000; font-weight:bold;">&lt;?php</span><br />
<span style="color:#0000FF;">$n</span> = <span style="color:#CC66CC;">3</span>;<br />
<a href="http://www.php.net/printf"><span style="color:#000066;">printf</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><a href="http://www.php.net/ngettext"><span style="color:#000066;">ngettext</span></a><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#FF0000;">"%d comment"</span>, <span style="color:#FF0000;">"%d comments"</span>, <span style="color:#0000FF;">$n</span><span style="color:#006600; font-weight:bold;">&#41;</span>, <span style="color:#0000FF;">$n</span><span style="color:#006600; font-weight:bold;">&#41;</span>;<br />
<span style="color:#000000; font-weight:bold;">?&gt;</span></div>
</div>
</div>
<p></p>
<p>If you have never used (s)printf before, have a look at it's <a href="http://www.php.net/manual/en/function.sprintf.php">documentation</a>. The interesting part is of course the <a href="http://www.php.net/ngettext">ngettext</a> call. ngettext takes three arguments. The first one is the singular string, the second one the plural string and the last one is the number. While that was quite easy, localizing these strings is a bit harder. Add the two lines to your test.php script.</p>
<p>Before you update your language file in poEdit, open the catalog options window (catalog -&gt; options). Now let's fill in that last field. For our German locale, set the plural forms field to "nplurals=2; plural=(n != 1);". Essentially this specifies that there are two plural forms and the plural form is determined by the expression (n != 1). This evaluates to 0 if n == 1 and 1 if n != 1. For more information about this field, including samples for various languages, please see the <a href="http://www.gnu.org/software/gettext/manual/html_mono/gettext.html#SEC150">gettext documentation</a>. Now, let's press the update button again and select the new string. You will notice that the lower left hand corner of the window has changed. It now displays the original singular and plural forms and the translation box has been replaced by two (depends on the number of plural forms the language has). Enter "%d Kommentar" in the first tab and "%d Kommentare" in the second tab. Save the file, reload the web server if needed and your page should show the correct translation (you may want to change the value of $n to see the effect of the ngettext function).</p>
<h4>Improving localization in your scripts</h4>
<p>This concludes my introduction to localizing PHP web sites using gettext. Here are a few ideas to improve localization in your applications:</p>
<ul>
<li>During development, you may want to use a php library instead of the PHP gettext extension because they do not require you to restart the web server every time you modify the language files. You can create a wrapper function (e.g. __()) that calls either the library function or the extension, depending on which is available or selected). You can add the name of the wrapper function as a keyword in poEdit, so that it is recognized.</li>
<li>You may want to select a default locale according to the browser's Accept-Language header, so that the user does not have to select his language first.</li>
</ul>
<h4>The performance of gettext</h4>
<p>See my follow-up post "<a href="http://mel.melaxis.com/devblog/2006/04/10/benchmarking-php-localization-is-gettext-fast-enough/">Benchmarking PHP Localization - Is gettext fast enough?</a>" for Benchmarks. In general, the gettext Extension is faster than using a String-Array. The pure PHP implementation of gettext is slower and not recommended if you can use the PHP Extension.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=Cvc_IIyYvO4:fa98ONIKx8Q:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=Cvc_IIyYvO4:fa98ONIKx8Q:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/Cvc_IIyYvO4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/06/localizing-php-web-sites-using-gettext/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/06/localizing-php-web-sites-using-gettext/</feedburner:origLink></item>
		<item>
		<title>Learning Ruby on Rails</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/dEhecMbc5q4/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/05/learning-ruby-on-rails/#comments</comments>
		<pubDate>Fri, 05 Aug 2005 17:48:39 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[Ruby on Rails]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/webdev-blog/?p=6</guid>
		<description><![CDATA[Ruby on Rails is an open source framework for web applications written in Ruby. It has become very popular recently because it allows you to write web applications much faster than with most other frameworks. You have to write very little code to do the most common operations and its clean MVC architecture ensures that [...]]]></description>
				<content:encoded><![CDATA[<p>Ruby on Rails is an open source framework for web applications written in Ruby. It has become very popular recently because it allows you to write web applications much faster than with most other frameworks. You have to write very little code to do the <a href="http://en.wikipedia.org/wiki/CRUD_%28acronym%29">most common operations</a> and its clean <a href="http://en.wikipedia.org/wiki/Model-View-Controller"><acronym title="Model-View-Controller">MVC</acronym></a> architecture ensures that your application is clearly structured. Ruby on Rails comes with several generator scripts that automatically created the required files.</p>
<p>So far, I created all my dynamic web sites using PHP. Writing a simple application such as a guest book requires quite a lot of code in PHP to connect to the database, retrieve the entries, add new entries and so on. With Ruby on Rails that's much easier. You simple create a table, run a generator script to create a model and a controller. A cool feature called scaffolding automatically creates an interface to list, add, edit and remove entries. This is very useful to add some entries to a new table before you have created the real interface. But even that is really simple.</p>
<p>For example, by adding a line like "@entries = Entry.find_all" to a controller action, Ruby on Rails fetches all rows of the entries table (that's another cool feature: table names are lowercase and plural, model names uppercase and singular - Ruby on Rails can convert them automatically) and saves them in a variable called entries. In a template with the same name as the action, you can loop trough all entries using "&lt;% @entries.each do |entry| %&gt;" (Ruby code is embedded in &lt;% %&gt;-Blocks inside the HTML template) and then print them easily using "&lt;%= entry.message %&gt;" etc.</p>
<p>Ruby on Rails is really cool. In order to understand Rails, I decided to learn Ruby first. So far, I can recommend two links for learning Ruby. The first one is the <a href="http://www.rubyist.net/~slagell/ruby/index.html">Ruby user's guide</a> by matz, the creator of the language, which provides a rather short introduction to Ruby. The other one is <a href="http://poignantguide.net/ruby/">Why's (Poignant) Guide to Ruby</a>, which is quite entertaining.<br />
O'Reilly has a tutorial for Ruby on Rails: <a href="http://www.onlamp.com/lpt/a/5546">Rolling with Ruby on Rails</a> (this includes installation instructions for Ruby, Ruby on Rails and MySQL) and <a href="http://www.onlamp.com/lpt/a/5641">Rolling with Ruby on Rails, Part 2</a>. More tutorials and API references can be found on the <a href="http://documentation.rubyonrails.com/">Ruby on Rails Homepage</a>.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=dEhecMbc5q4:DPRSFVYFwpU:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=dEhecMbc5q4:DPRSFVYFwpU:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/dEhecMbc5q4" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/05/learning-ruby-on-rails/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/05/learning-ruby-on-rails/</feedburner:origLink></item>
		<item>
		<title>Multiple blogs with a single WordPress installation: MultiWP</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/6cYksneyKI8/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/05/multiple-blogs-with-a-single-wordpress-installation-multiwp/#comments</comments>
		<pubDate>Fri, 05 Aug 2005 16:39:09 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/webdev-blog/?p=5</guid>
		<description><![CDATA[As I wrote in my previous post, I'm going to start a few blogs based on WordPress. Unfortunately, WordPress does not support multiple blogs by default. Normally you need a separate WordPress installation for every blog. While this may not seem like a big problem at first, what do you do when an updated WordPress [...]]]></description>
				<content:encoded><![CDATA[<p>As I wrote in my previous post, I'm going to start a few blogs based on WordPress. Unfortunately, WordPress does not support multiple blogs by default. Normally you need a separate WordPress installation for every blog. While this may not seem like a big problem at first, what do you do when an updated WordPress version is released? I didn't want to update every single blog, so I wrote a little script called <a href="http://mel.melaxis.com/multiwp.php" title="German description of MultiWP">MultiWP</a>. MultiWP allows you to have several blogs with just a single WordPress install. When you update WordPress, all blogs are immediately updated to the new version. MultiWP also does not modify any WordPress files, so you can just follow the standard update procedure. Sounds good? <a href="http://mel.melaxis.com/files/multiwp.zip">Download MultiWP</a>. A detailled installation howto is included in both English and German.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=6cYksneyKI8:IMq0FC14Bow:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=6cYksneyKI8:IMq0FC14Bow:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/6cYksneyKI8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/05/multiple-blogs-with-a-single-wordpress-installation-multiwp/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/05/multiple-blogs-with-a-single-wordpress-installation-multiwp/</feedburner:origLink></item>
		<item>
		<title>Welcome to my web development blog!</title>
		<link>http://feedproxy.google.com/~r/PablosDevelopmentBlog/~3/1pAFuWhJkZs/</link>
		<comments>http://mel.melaxis.com/devblog/2005/08/05/welcome-to-my-web-development-blog/#comments</comments>
		<pubDate>Fri, 05 Aug 2005 16:22:48 +0000</pubDate>
		<dc:creator>Pablo Hoch</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://mel.melaxis.com/webdev-blog/?p=4</guid>
		<description><![CDATA[Welcome to my new weblog. This blog is about web development and web design and is the first of a number of WordPress based blogs I plan to launch soon.]]></description>
				<content:encoded><![CDATA[<p>Welcome to my new weblog. This blog is about web development and web design and is the first of a number of WordPress based blogs I plan to launch soon.</p>
<div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=1pAFuWhJkZs:eufE4jpCnwA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?a=1pAFuWhJkZs:eufE4jpCnwA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/PablosDevelopmentBlog?d=7Q72WNTAKBA" border="0"></img></a>
</div><img src="http://feeds.feedburner.com/~r/PablosDevelopmentBlog/~4/1pAFuWhJkZs" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://mel.melaxis.com/devblog/2005/08/05/welcome-to-my-web-development-blog/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://mel.melaxis.com/devblog/2005/08/05/welcome-to-my-web-development-blog/</feedburner:origLink></item>
	</channel>
</rss>
