<?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>Internationalisation Tips &#187; i18n</title>
	<atom:link href="http://internationalisationtips.com/tag/i18n/feed/" rel="self" type="application/rss+xml" />
	<link>http://internationalisationtips.com</link>
	<description>practical tips on building an international presence</description>
	<lastBuildDate>Wed, 24 Aug 2011 14:20:24 +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>The dynamic sentence creation anti-pattern</title>
		<link>http://internationalisationtips.com/2010/03/05/the-dynamic-sentence-creation-anti-pattern/</link>
		<comments>http://internationalisationtips.com/2010/03/05/the-dynamic-sentence-creation-anti-pattern/#comments</comments>
		<pubDate>Fri, 05 Mar 2010 21:42:43 +0000</pubDate>
		<dc:creator>Isofarro</dc:creator>
				<category><![CDATA[Translations]]></category>
		<category><![CDATA[antipattern]]></category>
		<category><![CDATA[dynamic]]></category>
		<category><![CDATA[gotcha]]></category>
		<category><![CDATA[grammar]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalisation]]></category>
		<category><![CDATA[text]]></category>
		<category><![CDATA[translation]]></category>

		<guid isPermaLink="false">http://internationalisationtips.com/?p=18</guid>
		<description><![CDATA[The natural internationalisation stumbling block, particularly for technical people, is that localisation isn&#8217;t just about translating static text strings. Surprisingly, many developers and programmers fail to consider that sentences in one natural language cannot be simply translated one word at a time to another language. Differences in grammar Every human language has its own grammatical [...]]]></description>
			<content:encoded><![CDATA[<p>The natural internationalisation stumbling block, particularly for technical people, is that localisation isn&#8217;t just about translating static text strings. Surprisingly, many developers and programmers fail to consider that sentences in one natural language cannot be simply translated one word at a time to another language.</p>
<h3>Differences in grammar</h3>
<p>Every human language has its own grammatical rules and style. The chances of a grammatical structure being the same across a range of natural languages is extremely low. So code written around a specific grammatical construct in one human language presents an impossible internationalisation barrier when needing to be translated into another language.</p>
<p>There&#8217;s only two approaches that could work here:</p>
<ol>
<li>Write a custom replacement function per language</li>
<li>Throw away the code and try again</li>
</ol>
<h3>Drawbacks of language dependent code</h3>
<p>Unfortunately option 1 &#8211; writing a custom replacement function per language &#8211; would require a developer to be involved every time a new language needs to be supported. And that developer needs to know this new language well enough to make the necessary changes or additions to get his function returning the grammatically correct output each time.</p>
<p>For every natural language that needs to be supported, the developer has to supply a new function. That function most likely already contains business logic. Adding a new language means duplicating, or reimplementing existing business logic to meet the grammatical structures of the new language.</p>
<p>So what happens when the business logic needs to be updated? Yep, the developer now has to update every single copy of the function. Each time checking the natural language syntax is correct. That means that the developer maintaining this piece of code has to be broadly familiar with every language his code supports. And that is totally unrealistic.</p>
<p>Developer zugzwang.</p>
<h3>A real-world case</h3>
<p>I ran into a piece of dynamic sentence generation code about two years ago when I was tasked with localising some &#8220;global-ready&#8221; JavaScript for use in Europe. I was assured all that needed to be done is to replace the static strings in the code with a reference to a JavaScript translations lookup.</p>
<p>Here is a simplified JavaScript pseudo-code version of the code I uncovered. (Simplified so we can focus on the internationalisation issues without getting bogged down in convoluted business logic.)</p>
<pre><code>
function getMarketStatus(market) {
	var message = market.name;
	var now     = new Date().getTime();

	if (market.open) {

		message += &quot; open&quot;

		if (market.open.early) {
			message += &quot; early&quot;

			if (market.reason) {
				message += &quot; for &quot; + market.reason;
			}

		} elseif (market.open.late) {
			message += &quot; late &quot;

			if (market.reason) {
				message += &quot; for &quot; + market.reason;
			}
		}

		if (now &lt; market.open.time) {
			message += &quot; in &quot; +
				formatTimeLeft(now, market.open.time);
		}

	} elseif (market.close) {

		message += &quot; close&quot;;

		if (market.close.early) {
			message += &quot; early&quot;

			if (market.reason) {
				message += &quot; for &quot; + market.reason;
			}

		} elseif (market.close.late) {
			message += &quot; late &quot;

			if (market.reason) {
				message += &quot; for &quot; + market.reason;
			}
		}

		if (now &lt; market.close.time) {
			message += &quot; in &quot; +
				formatTimeLeft(now, market.close.time);
		}

	} else {
		message += &quot; closed&quot;;
	}

 	return message;
}
</code></pre>
<p>This piece of code generates one sentence of text summarising the market status. Either the market is open or closed, opening soon or closing soon, maybe earlier or later than normal (perhaps for a specified reason).</p>
<h3>Identifying the possibilities</h3>
<p>The function returns one of the following patterns (variable data identified with curly braced place-holders):</p>
<ul>
<li><samp>{market} open</samp></li>
<li><samp>{market} open in {timePeriod}</samp></li>
<li><samp>{market} open early</samp></li>
<li><samp>{market} open early in {timePeriod}</samp></li>
<li><samp>{market} open early for {reason}</samp></li>
<li><samp>{market} open early for {reason} in {timePeriod}</samp></li>
<li><samp>{market} open late</samp></li>
<li><samp>{market} open late in {timePeriod}</samp></li>
<li><samp>{market} open late for {reason}</samp></li>
<li><samp>{market} open late for {reason} in {timePeriod}</samp></li>
<li><samp>{market} close</samp></li>
<li><samp>{market} close in {timePeriod}</samp></li>
<li><samp>{market} close early</samp></li>
<li><samp>{market} close early in {timePeriod}</samp></li>
<li><samp>{market} close early for {reason}</samp></li>
<li><samp>{market} close early for {reason} in {timePeriod}</samp></li>
<li><samp>{market} close late</samp></li>
<li><samp>{market} close late in {timePeriod}</samp></li>
<li><samp>{market} close late for {reason}</samp></li>
<li><samp>{market} close late for {reason} in {timePeriod}</samp></li>
<li><samp>{market} closed</samp></li>
</ul>
<p>Where:</p>
<ul>
<li><var>{market}</var> is the name of the market under scrutiny, e.g. <samp>UK markets</samp></li>
<li><var>{timePeriod}</var> is the number of minutes and hours before the market opens or closes</li>
<li><var>{reason}</var> is the stated reason why a marked opened or closed early or late.</li>
</ul>
<h3>The simple difficulty</h3>
<p>That&#8217;s 21 different text strings. The likelihood of the word order remaining the same across different languages is close zero.</p>
<p>The simple case is that the order of atomic elements works in English, but unlikely to consistently work in other languages. And for this piece of logic to be fit for internationalisation, this order cannot be assumed to work. A translator needs to be able to use the most appropriate and correct order of the targeted language and culture. </p>
<p>Moreover, the difficulties don&#8217;t end there. </p>
<h3>The disguised change of meaning</h3>
<p>Perhaps the most insidious feature of the above code is that adding in an extra word significantly changes the meaning of previous words. Take for example these two generated sentences:</p>
<ol>
<li>UK markets open</li>
<li>UK markets open in 20 minutes</li>
</ol>
<p>The first sentence is a declaration that the market is currently open. The second, however, does not; it states that the market <em>will</em> open after a defined period of time. So the sentence has changed from a present tense declarative, to a future tense expectation.</p>
<p>The English grammar barely holds together in this change of tense, and it&#8217;s unlikely that more regular and refined languages could pull off this form of grammatical gymnastics.</p>
<h3>Factor out the natural language</h3>
<p>So how do we fix this? Rather simply, by avoiding constructing sentences fragments at a time. Figure out which pieces of information are needed, and then look up the most appropriate translatable sentence that matches the information.</p>
<p>We refactor the code above in a two step process:</p>
<ol>
<li>Replace the sentence appending logic with something that keeps track of which bits of information needs to be conveyed, and pick the most appropriate sentence.</li>
<li>Add the dynamic data into the sentence by means of token or place-holder replacement</li>
</ol>
<p>Step 1 requires a rethink of the business logic implementation. We have to keep track of what pieces of information we need to display, and from that pick the most appropriate sentence. An obvious way of doing this is to keep a translations hash with all the possible combinations, and keying those in a calculatable way. </p>
<p>I&#8217;ve done this the same way as the original code builds up a sentence, except I&#8217;m building up a lookup key. And the lookup key then maps to a full sentence. This level of abstraction divorces the actual sentence grammar from the business logic rather neatly. (This approach is analogous to bitwise logic; something familiar to most C developers)</p>
<p>After that, it&#8217;s a simple case of retrieving the correct sentence, and replacing any data place-holders with the actual information.</p>
<p>After these refactoring steps the code looks like this:</p>
<pre><code>
function getMarketStatus(market) {
	var status;
	var now     = new Date().getTime();

	// Collect the pertinent pieces of information
	// So we can pick the right translation string
	var message = {
		market:   market.name,
		sentence: &quot;&quot;
	};

	if (market.open) {
		status = market.open;
		message.sentence = &quot;O&quot;;

	} elseif (market.close) {
		status = market.close;
		message.sentence = &quot;C&quot;;

	} else {
		message.sentence = &quot;X&quot;;

	}

	if (status) {

		// Make a note of early/late status
		if (status.early) {
			message.sentence += &quot;E&quot;;

		} elseif (status.late) {
			message.sentence += &quot;L&quot;;

		}

		// Make a note of any reason
		if (market.reason) {
			message.reason    = market.reason;
			message.sentence += &quot;R&quot;;

		}

		// Make a note of any time period
		if (status.time) {
			message.timePeriod =
				formatTimeLeft(now, market.open.time);
			message.sentence += &quot;T&quot;;

		}
	}

	// Pick the right sentence to display
	var sentence = TRANSLATIONS[message.sentence];

	// Replace dynamic data
	return YAHOO.lang.substitute(
		sentence, message
	);
}

// Mapping each combination into a sentence
TRANSLATIONS = {
	O:    &quot;{market} open&quot;,
	OT:   &quot;{market} open in {timePeriod}&quot;,
	OE:   &quot;{market} open early&quot;,
	OET:  &quot;{market} open early in {timePeriod}&quot;,
	OER:  &quot;{market} open early for {reason}&quot;,
	OERT: &quot;{market} open early for {reason} in {timePeriod}&quot;,
	OL:   &quot;{market} open late&quot;,
	OLT:  &quot;{market} open late in {timePeriod}&quot;,
	OLR:  &quot;{market} open late for {reason}&quot;,
	OLRT: &quot;{market} open late for {reason} in {timePeriod}&quot;,
	C:    &quot;{market} close&quot;,
	CT:   &quot;{market} close in {timePeriod}&quot;,
	CE:   &quot;{market} close early&quot;,
	CET:  &quot;{market} close early in {timePeriod}&quot;,
	CER:  &quot;{market} close early for {reason}&quot;,
	CERT: &quot;{market} close early for {reason} in {timePeriod}&quot;,
	CL:   &quot;{market} close late&quot;,
	CLT:  &quot;{market} close late in {timePeriod}&quot;,
	CLR:  &quot;{market} close late for {reason}&quot;,
	CLRT: &quot;{market} close late for {reason} in {timePeriod}&quot;,
	X:    &quot;{market} closed&quot;
};
</code></pre>
<p>Once we move the <var>TRANSLATIONS</var> object to a language-specific file we can then allow translators to translate the sentences to the targeted language.</p>
<p>This technique is flexible enough even to tackle the irregular grammar when the <var>market.close</var> object exists but with no useful data, thus <q>{market} close</q> could easily be corrected to <q>{market} will close soon</q>.</p>
<p>Also the flexibility will make handling the <var>{reason}</var> wildcard a little easier, if not perfectly.</p>
<p>So we avoid the need to create dynamic sentences on the fly, and instead focus on what pieces of information we need to share. And then offer the translator sufficient flexibility, through the use of replaceable tokens, to set the most appropriate translation for the targeted language.</p>
<h3>Back in the real world&hellip;</h3>
<p>My refactored solution didn&#8217;t go live. Instead, the feature it powered was descoped in Europe. That was the cost of using this particular anti-pattern.</p>
]]></content:encoded>
			<wfw:commentRss>http://internationalisationtips.com/2010/03/05/the-dynamic-sentence-creation-anti-pattern/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Avoid text image buttons</title>
		<link>http://internationalisationtips.com/2009/01/27/avoid-text-image-buttons/</link>
		<comments>http://internationalisationtips.com/2009/01/27/avoid-text-image-buttons/#comments</comments>
		<pubDate>Tue, 27 Jan 2009 16:20:50 +0000</pubDate>
		<dc:creator>Isofarro</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[buttons]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[fonts]]></category>
		<category><![CDATA[forms]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[images]]></category>
		<category><![CDATA[sliding doors]]></category>
		<category><![CDATA[style]]></category>
		<category><![CDATA[text]]></category>
		<category><![CDATA[Translations]]></category>

		<guid isPermaLink="false">http://www.internationalisationtips.com/?p=3</guid>
		<description><![CDATA[Localising sites should not be about recreating the same images over and over with different text and then spending days fixing layout bugs as each locale needs a different widths to comfortably fit in each text label.]]></description>
			<content:encoded><![CDATA[<p>Styling buttons are a nightmare for web developers, thanks to the inconsistent cross-browser and cross-platform handling. So we take the easy way out and cut out the button image from the design mocks, save it in an image, and replace the default button with our new image.</p>
<p>Using images for buttons is nothing new. Accessibility wise, all you really need is some way of specifying a text-equivalent to the image, and you are essentially done.</p>
<p>The two main techniques of using imaged buttons are using the input type of image, or a button element styled with image replacement.</p>
<p>Typically the size of the image button comes directly from the design specification, which means that the text on the button has actually been a design decision, and the page design has taken that into consideration.</p>
<p>The result is a button that cannot be internationalised, for the following reasons:</p>
<ul>
<li>translating the button text means creating a new image for each translation</li>
<li>the size of each image now has to change to accommodate the translated text.</li>
</ul>
<p>There are two sane approaches to dealing with this situation, and it depends on the willingness of your designers to compromise in favour of international-friendly designs. But both solutions involve reimplementing the button markup, and if there are JavaScript event-handlers registered, there&#8217;s a strong possibility that these would need to be updated to take into account the changed markup.</p>
<p>The first solution is to get rid of the button image entirely, and use a proper submit input button. Then use CSS to style the button away from the default set into something closer to the original design, compromising the exacting details of text-shadows, rounded corners.</p>
<p>Designs that require a particular font, with specific kerning and other font metrics are unfit for internationalising websites, unless you can get your designer to guarantee that they will create and supply all the button images you need for every localisation from now on.</p>
<p>The second solution is to get the design team to create a tile-able button background big enough to cater for any reasonable width of text. Then using a sliding doors technique, a button element (or a cleverly marked-up submit button) can be styled to produce an elastic button background. All that&#8217;s left is to style the font of the real text as closely as reasonably possible to the original design specification.</p>
<p>By making the actual text back into real text your existing ways of translating text can be used, and localising forms becomes a matter of translations and other unique locale-specific requirements, not laboriously dealing with image manipulation.</p>
<p>Text inside images cause headaches in localising sites. It is far better to spend more time upfront coming up with a reasonable compromise that is based on text being real text, not an image.</p>
]]></content:encoded>
			<wfw:commentRss>http://internationalisationtips.com/2009/01/27/avoid-text-image-buttons/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

