<?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>rwec.co.uk &#187; postresql</title>
	<atom:link href="http://rwec.co.uk/blog/tag/postresql/feed/" rel="self" type="application/rss+xml" />
	<link>http://rwec.co.uk/blog</link>
	<description>Rowan&#039;s World, Et Cetera</description>
	<lastBuildDate>Sun, 27 Nov 2011 19:13:17 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Atomic Translations &#8211; Part 2 &#8211; Translated Text as a Data Type in PostgreSQL</title>
		<link>http://rwec.co.uk/blog/2009/12/atomic-translations-part-2/</link>
		<comments>http://rwec.co.uk/blog/2009/12/atomic-translations-part-2/#comments</comments>
		<pubDate>Sat, 12 Dec 2009 23:42:37 +0000</pubDate>
		<dc:creator>Rowan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[atomicity]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[library]]></category>
		<category><![CDATA[postgres]]></category>
		<category><![CDATA[postresql]]></category>
		<category><![CDATA[projects]]></category>
		<category><![CDATA[prototype]]></category>

		<guid isPermaLink="false">http://rwec.co.uk/blog/?p=60</guid>
		<description><![CDATA[[Part 1: Database i18n as a Data Type Problem &#124; Prototype Postgres Library] In my last post (longer ago than I intended), I discussed the approaches I've come across to adding i18n to a database schema, and outlined my theory that the whole problem could be recast as one of data types. In order to [...]]]></description>
			<content:encoded><![CDATA[<p>[<a href="http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/">Part 1: Database i18n as a Data Type Problem</a> | <a href="http://rwec.co.uk/pg-atom/i18n.html">Prototype Postgres Library</a>]</p>
<p>In <a href="http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/">my last post</a> (longer ago than I intended), I discussed the approaches I've come across to adding i18n to a database schema, and outlined my theory that the whole problem could be recast as one of data types. In order to explore how this would work in practice, I've put together <a href="http://rwec.co.uk/pg-atom/i18n.html">a prototype "library"</a> using only a standard install of <a href="http://www.postgresql.org/">PostgreSQL</a> that shows how simple it could be, and why it makes sense.</p>
<p><span id="more-60"></span></p>
<h2>Views and Functions</h2>
<p>One of the things I haven't really touched on with my examples so far is that you will not normally interact directly with all the tables or data created by any i18n solution. You don't want to write double the code to perform the same query, just because it contains translated items, so you will generally try to wrap it up in a set of functions and/or views that provide a high level of abstraction.</p>
<p>What these functions look like will be closely related to the structure you've given your data, and some solutions lend themselves to simpler wrappers than others:</p>
<ul>
<li>If you have a separate table for every translated column, you may end up with dozens of functions like "get_country_name( <em>language_code</em>, <em>country_id</em> )"</li>
<li>If you have a big table of text but reference items by category you might instead call "get_translated_item( 'database', 'country', 'name', <em>country_id</em>, <em>language_code )</em>"</li>
<li>If you instead store the text_id as a foreign key reference, you might be able to simply say "Select translate( name_text_id, 'de' ) From country"</li>
</ul>
<p>Another thing that will commonly be handled by functions or views is the fact that you won't have a full set of translations of every item in every language, so your code needs to do something other than break when a value is missing. The general approach is to have a "fall-back" language - code somewhere that says "if there's no French available, English will have to do". For some languages, there might be multiple levels of fall-back, e.g. if BrazilianPortuguese is unavailable, try European Portuguese, then English as a last resort.</p>
<p>If you store your fall-back rules in the database itself, any of the above functions could be handling all this logic behind the scenes. You will rarely need the ability to prioritise languages differently for different users.</p>
<h2>Modification</h2>
<p>Most solutions lend themselves to some variation of the above for simplifying the <strong>retrieval</strong> of translated content, but it is not always easy to simplify the <strong>modification</strong> of that content, with so many tables involved.</p>
<p>In the "atomic i18n" model, all your data is stored inside the original tables, so the abstraction functions don't need to know anything about the underlying data structure - they can just manipulate the values directly. So the function<em> translate(item, language_code)</em> just takes an item of type <em>translated_text</em> and looks for the appropriate language - the item might well be a particular table column, but it could just as easily be something calculated on the fly.</p>
<p>More significantly, the function <em>set_translation(item, language_code, translation)</em> can take a <em>translated_text</em> value, and return a new <em>translated_text</em> value, without updating any underlying tables. So to modify your data, you can just use INSERT and UPDATE queries, just as you would if it was a normal string:</p>
<ul>
<li>To create a new value, use an INSERT statement, with an appropriate representation of the set of translations to be added:<em><br />
Insert Into country ( name, currency ) Values ( i18n.translation_pair('en', 'Austria'), 'EUR'</em> );</li>
<li>To update a value, use an UPDATE statement:<br />
<em>Update country Set name = translation_set(name,  'de', 'Österreich') Where id = 4</em></li>
<li>To delete a value, use a DELETE statement - no need to look for dependencies, or set up cascade triggers.</li>
</ul>
<h2>Environment Variables</h2>
<p>One of the things that the above retrieval functions all need - for obvious reasons - is which language you are translating <em>into</em>. But when you come to using that in an application, that becomes a bit of a pain - every time you mention a translation function, you have to insert a placeholder or a variable from the calling application to say what the current user's language is.</p>
<p>But what would be really nice is if you didn't have to pass any languages at all - I just want to say "the current user speaks German; now go away and do this query, but in German please". Or, more technically, to set an environment variable at the top of the query, and let the rest of the query silently refer to it.</p>
<h3>Using Environment Variables in PL/pgSQL</h3>
<p>When I started looking into this, I was working with Postgresql, and discovered three things:</p>
<ol>
<li>some of the <a href="http://www.postgresql.org/docs/8.4/static/xplang.html">procedural languages</a> available for Postgresql, such as <a href="http://www.postgresql.org/docs/8.4/static/plperl.html">PL/Perl</a>, have support for session-wide global variables</li>
<li>the most common, <a href="http://www.postgresql.org/docs/8.4/static/plpgsql.html">PL/pgSQL</a>, doesn't</li>
<li>my shared host only had PL/pgSQL installed.</li>
</ol>
<p>So I've written a simple pair of functions - "env.set(item, value)" and "env.get(item)" - implemented using only PL/pgSQL. By wrapping them like this, I figure they could easily be replaced by completely equivalent - and much simpler - functions in PL/Perl, or whatever other language you may have to hand.</p>
<p>The implementation itself takes advantage of the way PostgreSQL handles temporary tables - namely, that they last for the entire "session" of queries, wherever they were created. So you can run one function that creates a temporary table, and another that accesses it, and they'll "see" the same table - but if you do that twice in separate "sessions", each session will have its own copy. <em>Voilá</em>, somewhere to put your environment variables.</p>
<h2>Operators</h2>
<p>The other thing you'll notice about the examples so far, is that they are quite wordy - there are various functions involved, and in the middle of a complex query they could get quite confusing. Luckily, Postgres also supports custom <em>operators</em>, so you can replace some of that wordiness with punctuation.</p>
<p>Whether using operators rather than functions makes things more or less readable will depend on the context, but the simplest operation, requesting a translation, now becomes as simple as using the <strong>-&gt;</strong> (translate) operator:</p>
<blockquote><p><em>Select id, name-&gt;'en' as name, currency From country;</em></p></blockquote>
<p>Or using the <em>environment variable</em> approach, using the <strong>-&gt;!</strong> operator, which means "translate to currently set language":</p>
<blockquote><p><em>Select i18n.set_lang('en');</em></p>
<p><em>Select id, name-&gt;! as name, currency From country;</em></p></blockquote>
<p>And, of course, operators can also make our updates simpler; this example uses <strong>|| </strong>(combine) and <strong>==&gt;</strong> (create translation pair):</p>
<blockquote><p><em>Update country Set name = name || ('fr' ==&gt; 'Autriche') Where id = 4</em></p></blockquote>
<h2>Full Implementation</h2>
<p>The best way to see how it all works is to play around with a working implementation, so I've put a fully documented "library" online as <a href="http://rwec.co.uk/pg-atom/i18n.html">pg-atom: i18n</a>. The implementation consists of the following parts:</p>
<ul>
<li>an <strong><em>env</em> </strong><strong>schema</strong>, consisting of <em>env.set</em> and <em>env.get</em> functions, currently implemented in Pl/Pgsql, using temporary tables</li>
<li>an <strong><em>i18n</em> </strong><strong>schema</strong>, containing:
<ul>
<li>a <strong><em>language</em> </strong><strong>table</strong>, which defines the mapping from codes to numeric IDs and fall-back logic</li>
<li>a <strong><em>translated_text</em> </strong><strong>type</strong>, which is implemented as a standard Postgres text[] array, using the numeric language IDs as keys and the translation strings as values</li>
<li>a set of <strong>functions</strong> which implement the functionality for this new type</li>
</ul>
</li>
<li>a set of <strong>operators</strong> for the more i18n functions; I haven't declared these as inside the <em>i18n </em>schema, because that would make them usable only if you set your<a href="http://www.postgresql.org/docs/8.4/static/ddl-schemas.html#DDL-SCHEMAS-PATH"> search_path variable</a> first.</li>
</ul>
<h2>Limitations and Future Possibilities</h2>
<p>It's important to note that I haven't as yet had a chance to test this in anything like a production environment, so it may still be a bit ropy. In particular, there may be performance issues, as I'm not sure how the functions and operators will interact with query plans, indexes, etc.</p>
<p>The other thing to point out is that while the abstraction is quite elegant, the underlying implementation is not ideal: Postgres arrays have strictly numeric, ordered, indexes, and cannot contain nulls, whereas I am effectively using them to emulate an associative array (or perhaps some kind of tuple). In fact, the only way to add something with a particular key to an array (in this case, the language ID is used as the key) is to repeatedly "pad" the array with dummy values until it is long enough; for this reason, the current implementation <em>treats empty strings as though they were null</em>s.</p>
<p>There are other ways to simulate an associative array, but more tantalisingly, there is a module distributed with Postgres called <a href="http://www.postgresql.org/docs/8.4/static/hstore.html">hstore</a> which implements true associative arrays. A better <em>translated_text</em> type could probably be built "on top of" that, and typecasts between <em>translated_text</em> and <em>hstore</em> values would be pretty handy.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 0px; width: 1px; height: 1px;">&lt;a href="http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/"&gt;Part 1: Database i18n as a Data Type Problem&lt;/a&gt;</div>
]]></content:encoded>
			<wfw:commentRss>http://rwec.co.uk/blog/2009/12/atomic-translations-part-2/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Atomic Translations &#8211; Part 1 &#8211; Database i18n as a Data Type Problem</title>
		<link>http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/</link>
		<comments>http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/#comments</comments>
		<pubDate>Mon, 23 Nov 2009 00:10:18 +0000</pubDate>
		<dc:creator>Rowan</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[atomicity]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[postgres]]></category>
		<category><![CDATA[postresql]]></category>

		<guid isPermaLink="false">http://rwec.co.uk/blog/?p=50</guid>
		<description><![CDATA[[Part 2: Translated Text as a Data Type in PostgreSQL &#124; Prototype Postgres Library] The most obvious task when making any application multi-lingual is to make the UI translatable; there are absolutely tons of options for this, libraries in all sorts of languages, recipes, discussions, etc. But for many applications there's another task, just as [...]]]></description>
			<content:encoded><![CDATA[<p>[<a href="http://rwec.co.uk/blog/2009/12/atomic-translations-part-2/">Part 2: Translated Text as a Data Type in PostgreSQL</a> | <a href="http://rwec.co.uk/pg-atom/i18n.html">Prototype Postgres Library</a>]</p>
<p>The most obvious task when making any application multi-lingual is to make the <abbr title="user interface">UI</abbr> translatable; there are absolutely <strong>tons</strong> of options for this, libraries in all sorts of languages, recipes, discussions, etc. But for many applications there's another task, just as important, which is taking a database, and allowing someone to translate the data inside that.</p>
<p>There are various approaches to "database <abbr title="internationalization">i18n</abbr>" too - generally involving re-designing your schema in such a way that the translations are to some extent part of the "normalised" data. But these tend to make the schema somewhat unwieldy, and writing ad hoc queries and reports becomes tricky. So it occurred to me that if you treat i18n not as a <strong>schema</strong> problem, but as a <strong>data type</strong> problem, you could save some of that pain.</p>
<p><span id="more-50"></span></p>
<h2>The Problem</h2>
<p>Just to be clear, let's have an example we can work through: if I have a table of countries in my database, and one of the columns is the name of the country, that name isn't going to be the same in all the languages of my new super-duper internationalised system. So let's say we start with this:</p>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="3">Table "country"</th>
</tr>
<tr>
<th>id</th>
<th>name</th>
<th>currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>United Kingdom</td>
<td>GBP</td>
</tr>
<tr>
<td>2</td>
<td>Germany</td>
<td>EUR</td>
</tr>
<tr>
<td>3</td>
<td>France</td>
<td>EUR</td>
</tr>
</tbody>
</table>
<h2>Existing Solutions</h2>
<p>I've come upon various approaches to the database <abbr title="internationalization">i18n</abbr> problem, but they all group down to two basic ideas: using <strong>one big table</strong>, and treating <strong><abbr title="internationalization">i18n</abbr> as a problem of normalization</strong>.</p>
<h3>One Big Table</h3>
<p>The basic idea of the "one big table" solution is to extract all the text that needs translating, and put it in one big list, ready for translation. This is, after all, how <abbr title="user interface">UI</abbr> <abbr title="internationalization">i18n</abbr> usually works, so why not just treat your data as "some more translation entries"? As an example, this appears to be (I haven't actually experimented with it) what the <a href="http://book.cakephp.org/view/92/Translate">built-in i18n system in CakePHP</a> will do for you.</p>
<p>So, for our example, we create a new "text" table which contains all the text we want to translate. The primary key on this table will be a combination of some kind of unique ID, plus a language code; something like this:</p>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="3">Table "text"</th>
</tr>
<tr>
<th>id</th>
<th>language_code</th>
<th>content</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>42</td>
<td>en</td>
<td>United Kingdom</td>
</tr>
<tr>
<td>42</td>
<td>fr</td>
<td>Royaume Uni</td>
</tr>
<tr>
<td>42</td>
<td>de</td>
<td>Deutschland</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>Now probably the simplest way to use this is to replace the "name" column in our original "country" table with a reference to this new table - giving us something like this:</p>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="3">Table "country"</th>
</tr>
<tr>
<th>id</th>
<th>name_text_id</th>
<th>currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>42</td>
<td>GBP</td>
</tr>
<tr>
<td>2</td>
<td>43</td>
<td>EUR</td>
</tr>
<tr>
<td>3</td>
<td>44</td>
<td>EUR</td>
</tr>
</tbody>
</table>
<p>This looks like nicely normalised data - you can set up a foreign key from the "name_text_id" to the "text" table, and so forth. But what kind of a table is "text"? It has nothing to do with your schema - it's not really <em>representing anything</em>; if you think about it, you could just as well have a table called "numbers" as well, and reference it whenever you want a numeric value...</p>
<p>A slightly neater variant of this is to replace the "id" in the "text" column with some reference <em>back</em> to the table and column being translated. Maybe we already have a table holding all our UI messages, appropriately categorised. So, with a bit of extra normalisation, we might end up with this:</p>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="2">Table "country"</th>
</tr>
<tr>
<th>id</th>
<th>currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>GBP</td>
</tr>
<tr>
<td>2</td>
<td>EUR</td>
</tr>
<tr>
<td>3</td>
<td>EUR</td>
</tr>
</tbody>
</table>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="5">Table "text_item"</th>
</tr>
<tr>
<th>id</th>
<th>type</th>
<th>category</th>
<th>section</th>
<th>item</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>42</td>
<td>database</td>
<td>country</td>
<td>name</td>
<td>1</td>
</tr>
<tr>
<td>43</td>
<td>database</td>
<td>country</td>
<td>name</td>
<td>2</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="3">Table "text_translation"</th>
</tr>
<tr>
<th>text_item_id</th>
<th>language_code</th>
<th>content</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>42</td>
<td>en</td>
<td>United Kingdom</td>
</tr>
<tr>
<td>42</td>
<td>fr</td>
<td>Royaume Uni</td>
</tr>
<tr>
<td>42</td>
<td>de</td>
<td>Deutschland</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>This is quite attractive - all your translations are in one place, so writing an interface to keep them up to date should be quite easy - but as well as the meaninglessness of the "text" table, we now have another oddity, which I call the "<strong>anorexic table problem</strong>".</p>
<p>The problem is that the "country" table has given away most of its data to the "text" table - without joining through, you can only learn the currency. And this is a mild case: you could start off with a table with 5 columns, decide all of them except the primary key need translating, and be left with a glorified sequence - a table with nothing but a primary key.</p>
<p>This isn't unique to i18n - indeed many extreme forms of normalisation pretty much require you to create such tables - but in practice, it's a nuisance, and a downside of a lot of i18n schemas.</p>
<h3>I18n as N11n</h3>
<p>The other set of i18n schemas I've come across can be broadly called "normalisation-based solutions" - they take the existing schema, add multiple languages to it, and apply the rules of normalisation to the result.</p>
<p>There are various ways this can end up looking, but the idea is to treat the translations on each table as you would any 1-to-many relationship: with a new table. So for our example, we might create this:</p>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="2">Table "country"</th>
</tr>
<tr>
<th>id</th>
<th>currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>GBP</td>
</tr>
<tr>
<td>2</td>
<td>EUR</td>
</tr>
<tr>
<td>3</td>
<td>EUR</td>
</tr>
</tbody>
</table>
<table border="1">
<thead>
<tr>
<th style="align: center;" colspan="3">Table "country_names"</th>
</tr>
<tr>
<th>country_id</th>
<th>language_code</th>
<th>content</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>42</td>
<td>en</td>
<td>United Kingdom</td>
</tr>
<tr>
<td>42</td>
<td>fr</td>
<td>Royaume Uni</td>
</tr>
<tr>
<td>42</td>
<td>de</td>
<td>Deutschland</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
<p>Now, this example only has one column to translate, but I've called my new table "country_name", implying that if there were multiple columns to translate, I would have created multiple new tables. So now not only do I have an "anorexic" <em>country</em> table, I have a huge number of new tables cluttering up my schema.</p>
<p>You don't have to go this far - you could instead create a "country_i18n" table, with columns for each of the bits of country information you need to store, which would at least keep this explosion down a bit. This seems to be (again, I'm afraid I haven't experimented) <a href="http://www.symfony-project.org/book/1_0/13-I18n-and-L10n">what the Symfony framework offers you</a>.</p>
<p>There are a few other variations on this, as well - I came upon <a href="http://scratchpad.wikia.com/wiki/Multilingual_Data_Structure">this article with various examples</a> (demonstrated, oddly, in terms of XML, not a DBMS). One worth mentioning is the idea of <strong>base + translations</strong>, which avoids the "anorexic table problem" by leaving the base language in the original table, and only the translations in a new structure. This is certainly an interesting compromise - but it requires you to formally pick a single "base" language, and I suspect the inconsistency of where data is stored would make writing maintenance code trickier.</p>
<h2>Translations as Sub-Atomic Particles</h2>
<p>Both of the approaches I've mentioned, and all the variations within them, frame the underlying <em>problem</em> in the same terms: translations are additional pieces of data, and your schema needs to accommodate this data. But there is actually a different way of looking at the problem, and that is that the translations are all <em>representations</em> of the same <em>value</em> - and the act of translating them is about selecting a <em>representation</em>, not filtering and combining <em>additional data</em>.</p>
<p>In other words, <strong>translated text is a data type</strong>.</p>
<p>Now, one of the principles often cited in relation to databases is that of <em>atomicity</em> - values in the database should be <em>atomic</em>, with structure defined at a relational level, not within each value. But as many people have pointed out - see <a href="http://thoughts.j-davis.com/2009/09/30/choosing-data-types/">this blog post</a> for example - it is quite hard to pin down just what <em>atomic</em> means.</p>
<p>If you're not allowed <em>any</em> structure, you shouldn't really be storing strings - they are, after all, an ordered list of characters. But, crucially, that has no bearing on the data you are trying to <em>represent </em>- you might want to model a "country", but you don't want to <em>model </em>a "name", you just want to <em>store </em>and <em>use </em>it. And - in my view - having multiple translations of that name needn't change that fact. As far as your schema is concerned, all the translations of a particular name form a single atom, and the individual strings are just a part of that value.</p>
<p>So just as you don't use the <strong>schema</strong> to link together the letters 'G', 'e', 'r', 'm', 'a', 'n', and 'y', you shouldn't use it to link together the translations 'Germany', 'Allemagne', and 'Deutschland'. Instead, you select a <strong>data type</strong> that represents the kind of data you are storing.</p>
<h2>The Atomic I18n Solution</h2>
<p>So if we go back to our original example, and look at what it would mean to treat i18n as a problem of data types, not schema design, what would it look like? Obviously, the table still has to change, but not by much - we just change the type of the "name" column to our new "translated text" type. Once we've stored our extra translations, we end up with something a bit like this:</p>
<table border="1">
<thead>
<tr>
<th colspan="3">Table "country"</th>
</tr>
<tr>
<th>id</th>
<th>name</th>
<th>currency</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>{'en':'United Kingdom'; 'fr':'Royaume Uni'; 'de': 'Vereinigte Königreich'}</td>
<td>GBP</td>
</tr>
<tr>
<td>2</td>
<td>{'en':'Germany'; 'fr':'Allemagne'; 'de':'Deutschland'}</td>
<td>EUR</td>
</tr>
<tr>
<td>3</td>
<td>{'en':'France'; 'de':'Frankreich'}</td>
<td>EUR</td>
</tr>
</tbody>
</table>
<p>Note that we don't need to create any new tables - our schema is effectively unchanged - and we've avoided the "anorexic table problem". We still have to update any existing queries to select the name in a particular language, rather than all of them at once, but we don't have to pull more tables into the query just to see what we're doing.</p>
<p>Of course, this relies on a "translated text" type being available in our DBMS, along with all the functions we'll need to interact with it.</p>
<p><em>This post has grown somewhat longer than I expected, so I have split it into two parts; in the next part, I will go into the details of my prototype implementation, which aims to create a user-friendly translated_text type from standard features of PostgreSQL.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://rwec.co.uk/blog/2009/11/atomic-translations-part-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

