<?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/"
	xmlns:creativeCommons="http://backend.userland.com/creativeCommonsRssModule"
>

<channel>
	<title>Martin Normark&#039;s blog &#187; Internationalization</title>
	<atom:link href="http://martinnormark.com/tag/internationalization/feed" rel="self" type="application/rss+xml" />
	<link>http://martinnormark.com</link>
	<description></description>
	<lastBuildDate>Tue, 15 May 2012 21:06:09 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
<xhtml:meta xmlns:xhtml="http://www.w3.org/1999/xhtml" name="robots" content="noindex" />
		<item>
		<title>Making your ASP.NET Global Resource files work in JavaScript. IntelliSense included!</title>
		<link>http://martinnormark.com/making-your-asp-net-global-resource-files-work-in-javascript-intellisense-included?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link>
		<comments>http://martinnormark.com/making-your-asp-net-global-resource-files-work-in-javascript-intellisense-included#comments</comments>
		<pubDate>Mon, 15 Nov 2010 15:22:20 +0000</pubDate>
		<dc:creator>Martin Normark</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[ASP.NET]]></category>
		<category><![CDATA[ASP.NET MVC]]></category>

		<guid isPermaLink="false">http://martinnormark.com/?p=56</guid>
		<description><![CDATA[Any modern web application needs localization! You simply can't ignore the huge amounts of people who doesn't speak your language, or whose native language is different from yours. You're probably using resource files (.resx) in .NET, but how do you &#8230; <p><a class="btn small" href="http://martinnormark.com/making-your-asp-net-global-resource-files-work-in-javascript-intellisense-included">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
			<content:encoded><![CDATA[<p>Any modern web application needs localization! You simply can't ignore the huge amounts of people who doesn't speak your language, or whose native language is different from yours.</p>
<p>You're probably using resource files (.resx) in .NET, but how do you go about getting values from your resource files in JavaScript?</p>
<p><a href="http://www.west-wind.com/Weblog/default.aspx">Rick Strahl</a> wrote a great <a href="http://www.west-wind.com/Weblog/posts/698097.aspx">blog post about an HttpHandler</a> that serves the content of your resource files in JavaScript. You basically add a script tag to your page that points to the HttpHandler, and the HttpHandler will produce the resources in JavaScript as an object with properties on it, like this:</p>
<blockquote><p>
[code lang="js"]var localRes = {<br />
    AreYouSureYouWantToRemoveValue: &quot;Sind Sie sicher dass Sie diesen Wert l\u00F6schen wollen?&quot;,<br />
    BackupComplete: &quot;Der Backup f\u00FChrte erfolgreich durch&quot;,<br />
    BackupFailed: &quot;Der Backup konnte nicht durchgef\u00FChrt werden&quot;,<br />
    BackupNotification: &quot;Diese Operation macht einen Backup von der Lokalisationtabelle. \nM\u00F6chten Sie fortfahren?&quot;,<br />
    Close: &quot;Schliessen&quot;,<br />
    FeatureDisabled: &quot;Diese Funktion ist nicht vorhanden im on-line Demo &quot;,<br />
    InvalidFileUploaded: &quot;Unzul\u00E4ssige Akten Format hochgeladen.&quot;,<br />
    InvalidResourceId: &quot;Unzul\u00E4ssige ResourceId&quot;,<br />
    Loading: &quot;Laden&quot;,<br />
    LocalizationTableCreated: &quot;Lokalisations Akte wurde erfolgreich erstellt.&quot;,<br />
    LocalizationTableNotCreated: &quot;Die Localizations Akte konnte nicht erstellt werden.&quot;<br />
};[/code]</p></blockquote>
<p>This is awesome! Now you can access your resource files from JavaScript. Though, I see a single improvement to be made. The thing I don't like about the HttpHandler approach, is that you don't get IntelliSense support in Visual Studio. So you have to browse your resource file and copy &amp; paste the resource key to your JavaScript files in order for this to work.</p>
<h2>Generate static JavaScript files on Post-build</h2>
<p>Instead of generating dynamic files at runtime, I prefer to generate static JavaScript files and then dynamically include the file of the current language on my pages. I do this by calling a Console Application I’ve written on the Post-build event of my ASP.NET (MVC) project, which you can set in the property pages of your project (By the way, did you know that you can open property pages of a project by double clicking the default Properties folder?):</p>
<p><a href="http://martinnormark.com/wp-content/uploads/2010/11/image1.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://martinnormark.com/wp-content/uploads/2010/11/image_thumb1.png" border="0" alt="image" width="634" height="490" /></a></p>
<p>What my JavascriptResxGenerator App does, is quite messy. But the essence of the App is, of course, to take a single (or several) .resx files and do the following:</p>
<p>1. Loop through all keys.</p>
<p>2. Make sure the key is not a <a href="http://javascript.about.com/library/blreserved.htm">JavaScript reserved word</a>.</p>
<p>3. Add the key and value to a dictionary (in JavaScript).</p>
<p>4. Write the file.</p>
<p>And for the default culture I generate a –vsdoc file, that I can use for Visual Studio IntelliSense.</p>
<h2>Using the JavaScript Resx Generator App</h2>
<p>My App supports a single file approach, and directory approach.</p>
<p><strong>Single file: </strong>JavascriptResxGenerator.exe C:\Folder\Text.resx C:\Output C:\Output\VsDoc MyApp.Namespace.Resources</p>
<p><strong>Directory: </strong>JavascriptResxGenerator.exe C:\Folder C:\Output C:\Output\VsDoc MyApp.Namespace.Resources</p>
<p>The MyApp.Namespace.Resources value, is the namespace your resource dictionary will get enclosed in.</p>
<p><a href="http://martinnormark.com/wp-content/uploads/2010/11/image2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://martinnormark.com/wp-content/uploads/2010/11/image_thumb2.png" border="0" alt="image" width="634" height="294" /></a></p>
<h2>Embedding the file on your pages</h2>
<p>To include the JavaScript resource file in your page, you simply add a script include tag that points to the correct language. The App will respect the region token used in your Resx files. So if you have a file called Text.da.resx, the JavaScript file generated will include .da.resx at the end. It’s then up to you to add the correct logic to keep hold of the current language and specify the correct region token in order to include the correct JavaScript resource file.</p>
<h2>Download</h2>
<p>You can <a href="http://cid-146a3db7b364e824.office.live.com/self.aspx/Public/JavascriptResxGenerator.zip">download the App here</a>, as a ZIP file.</p>
]]></content:encoded>
			<wfw:commentRss>http://martinnormark.com/making-your-asp-net-global-resource-files-work-in-javascript-intellisense-included/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
	</item>
		<item>
		<title>Translate text in C#, using Google Translate, revisited</title>
		<link>http://martinnormark.com/translate-text-in-c-using-google-translate-revisited?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link>
		<comments>http://martinnormark.com/translate-text-in-c-using-google-translate-revisited#comments</comments>
		<pubDate>Mon, 25 Oct 2010 15:31:49 +0000</pubDate>
		<dc:creator>Martin Normark</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[Google]]></category>

		<guid isPermaLink="false">http://martinnormark.com/?p=131</guid>
		<description><![CDATA[A long time ago, I wrote a blog post about how to translate text in C# using Google Translate. Since then, an official AJAX API has been released, which is a much better solution. Reading the comments of the blog &#8230; <p><a class="btn small" href="http://martinnormark.com/translate-text-in-c-using-google-translate-revisited">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
			<content:encoded><![CDATA[<p>A long time ago, I wrote a blog post about how to <a href="http://martinnormark.com/2009/03/12/translate-text-in-c-using-google-translate">translate text in C# using Google Translate</a>. Since then, an <strong>official AJAX API</strong> has been released, which is a much better solution. Reading the comments of the blog post indicates that the code was broken quite quickly!</p>
<p><a href="http://code.google.com/apis/ajaxlanguage/">Google’s AJAX Language API</a>, also includes functionality to detect language from a given string. All you can do on <a href="http://translate.google.com/">translate.google.com</a> you can do via the API. Reading the <a href="http://code.google.com/apis/ajaxlanguage/documentation/reference.html">class reference for the Translation API</a>, for <a href="http://code.google.com/apis/ajaxlanguage/documentation/reference.html#_intro_fonje">Flash and other Non-Javascript environments</a> gives us the information we need to perform <strong>translations from C#</strong>. Most important is the URL and the parameters required and accepted.</p>
<p>Another <strong>very important thing</strong> we can read in the documentation is this:</p>
<blockquote><p>Applications MUST always include a valid and accurate <a href="http://en.wikipedia.org/wiki/HTTP_referer">http referer header</a> in their requests</p></blockquote>
<p><strong>We must remember to add a valid referrer when making the actual call!</strong></p>
<h2>The response object</h2>
<p>Google’s AJAX Language APIs uses JSON, and returns a JSON object containing 3 properties:</p>
<ul>
<li>responseData</li>
<li>responseDetails</li>
<li>responseStatus</li>
</ul>
<p>This means we need to have some C# objects that we can use to deserialize the JSON returned from Google. I’m using a generic class called GoogleAjaxResponse&lt;T&gt;, with T being the specific class I want the JSON to be deserialized to.</p>
<pre class="csharp">using System.Runtime.Serialization;

namespace Milkshake.Integration.Google
{
	/// Defines a response from one of Google's AJAX APIs.
	/// The type of object, being returned.
	[DataContract]
	public class GoogleAjaxResponse&lt;T&gt;
	{
		///
		/// Gets or sets the response data.
		///
		///
		/// The responseData from Google AJAX API Call
		///
		/// The response data.
		[DataMember(Name = "responseData", Order = 0)]
		public T ResponseData { get; set; }
	}
}</pre>
<p>To deserialize the Translation specific JSON, I use the TranslationResponse class:</p>
<pre class="csharp">using System.Net;
using System.Runtime.Serialization;

namespace Milkshake.Integration.Google.Translate
{
	/// Defines a response from the Google AJAX Translate API.
	[DataContract]
	public class TranslationResponse
	{
		/// Initializes a new instance of the  class.
		public TranslationResponse()
		{
			this.ResponseStatus = HttpStatusCode.OK;
		}

		/// Gets or sets the translated text.
		/// The translated text.
		[DataMember(Name = "translatedText", Order = 0)]
		public string TranslatedText { get; set; }

		/// Gets or sets the response details.
		/// The response details.
		[DataMember(Name = "responseDetails", Order = 1)]
		public object ResponseDetails { get; set; }

		/// Gets or sets the response status.
		/// The response status.
		[DataMember(Name = "responseStatus", Order = 2)]
		public HttpStatusCode ResponseStatus { get; set; }
	}
}</pre>
<h2>Calling the Google AJAX Translate API</h2>
<p>To make the actual call, we can use the <a href="http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.aspx">HttpWebRequest</a> and <a href="http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.aspx">HttpWebResponse</a> classes.</p>
<pre class="csharp">using System;
using System.Globalization;
using System.IO;
using System.Net;
using System.Web;
using System.Web.Script.Serialization;

namespace Milkshake.Integration.Google.Translate
{
	/// An API Client for the Google AJAX Translate API.
	public class TranslateApi
	{
		/// The JavaScript serializer
		private JavaScriptSerializer _Serializer = new JavaScriptSerializer();

		/// Translates the text.
<pre>		public string TranslateText(string inputText, string sourceLanguage, string destinationLanguage, string referrer, string apiKey)
		{
			string requestUrl = string.Format("http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&amp;q={0}&amp;langpair={1}|{2}&amp;key={3}", HttpUtility.UrlEncode(inputText), sourceLanguage.ToLowerInvariant(), destinationLanguage.ToLowerInvariant(), apiKey);

			try
			{
				using (HttpClient http = new HttpClient(requestUrl))
				{
					http.AddReferrer(referrer);
					http.ExecuteRequest();

					string responseJson = http.GetResponseString();

					GoogleAjaxResponse&lt;TranslationResponse&gt; translation = this._Serializer.Deserialize&lt;GoogleAjaxResponse&lt;TranslationResponse&gt;&gt;(responseJson);

					if (translation != null &amp;&amp; translation.ResponseData != null &amp;&amp; translation.ResponseData.ResponseStatus == HttpStatusCode.OK)
					{
						return translation.ResponseData.TranslatedText;
					}
					else
					{
						return String.Empty;
					}
				}
			}
			catch
			{
				return String.Empty;
			}
		}
<pre>	}
}</pre>
</pre>
</pre>
<h2>The response JSON and translated text</h2>
<p>Doing a simple test, I translate “Hello world” from English to Danish, and I can see that it works like a charm.</p>
<p><a href="http://martinnormark.com/wp-content/uploads/2010/10/image.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://martinnormark.com/wp-content/uploads/2010/10/image_thumb.png" border="0" alt="image" width="628" height="273" /></a></p>
<p>
<strong>Professional translations - even via API!</strong><br />
<a href="http://www.onehourtranslation.com/affiliate/martinnormark"><img src="http://www.onehourtranslation.com/public/images/banners/set2/translation-services-468x49.jpg" alt="translation agency" border="0"></a></p>
]]></content:encoded>
			<wfw:commentRss>http://martinnormark.com/translate-text-in-c-using-google-translate-revisited/feed</wfw:commentRss>
		<slash:comments>10</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
	</item>
		<item>
		<title>Unit test for verifying references from DataAnnotation validation to the ErrorMessageResourceName value</title>
		<link>http://martinnormark.com/unit-test-for-verifying-references-from-dataannotation-validation-to-the-errormessageresourcename-value?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link>
		<comments>http://martinnormark.com/unit-test-for-verifying-references-from-dataannotation-validation-to-the-errormessageresourcename-value#comments</comments>
		<pubDate>Thu, 04 Mar 2010 17:52:00 +0000</pubDate>
		<dc:creator>Martin Normark</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Internationalization]]></category>
		<category><![CDATA[Unit testing]]></category>

		<guid isPermaLink="false">/post/Unit-test-for-verifying-references-from-DataAnnotation-validation-to-the-ErrorMessageResourceName-value.aspx</guid>
		<description><![CDATA[I love the new model validation features in System.ComponentModel.DataAnnotations. One thing I don’t like though, is that the ErrorMessageResourceName is loosely typed. The ErrorMessageResourceType, however, is a System.Type which will be strongly typed by assigning its value using the typeof(Namespace.ResourceSetType) &#8230; <p><a class="btn small" href="http://martinnormark.com/unit-test-for-verifying-references-from-dataannotation-validation-to-the-errormessageresourcename-value">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
			<content:encoded><![CDATA[<p>I love the new model validation features in System.ComponentModel.DataAnnotations. One thing I don’t like though, is that the ErrorMessageResourceName is loosely typed. The ErrorMessageResourceType, however, is a System.Type which will be strongly typed by assigning its value using the typeof(Namespace.ResourceSetType) method.</p>
<p>Since there’s no build-breaking reference between a resource file and the value of the ErrorMessageResourceName on all classes where you use it, I thought it would be cool to have a unit test that verifies the existence of all referenced resource keys.</p>
<p>Remember to add a reference to System.ComponentModel.DataAnnotations.</p>
<h2>Code</h2>
<pre class="csharpcode"><span class="rem">/// &lt;summary&gt;</span>
<span class="rem">/// Verifies that all properties that are decorated with validation data-annotations, refers to </span>
<span class="rem">/// an existing resource. This will make sure, that missing resources are not referenced.</span>
<span class="rem">/// &lt;/summary&gt;</span>
[TestMethod]
<span class="kwrd">public</span> <span class="kwrd">void</span> All_Properties_With_Validation_Annotations_Must_Refer_To_Existing_Resource()
{
    Assembly assembly = Assembly.Load(<span class="kwrd">new</span> AssemblyName(<span class="str">"MyApp.Model.Namespace"</span>));
    var types = assembly.GetTypes().Where&lt;Type&gt;(t =&gt; t.IsClass &amp;&amp; !t.IsAbstract);

    <span class="kwrd">foreach</span> (var type <span class="kwrd">in</span> types)
    {
        var properties = type.GetProperties();

        <span class="kwrd">foreach</span> (PropertyInfo property <span class="kwrd">in</span> properties)
        {
            var attributes = property.GetCustomAttributes(<span class="kwrd">true</span>);

            <span class="kwrd">foreach</span> (var item <span class="kwrd">in</span> attributes)
            {
                <span class="kwrd">if</span> (item <span class="kwrd">is</span> ValidationAttribute)
                {
                    ValidationAttribute val = item <span class="kwrd">as</span> ValidationAttribute;

                    Assert.IsNotNull(val);

                    <span class="kwrd">if</span> (val.ErrorMessageResourceType != <span class="kwrd">null</span>)
                    {
                        Assert.AreNotEqual(String.Empty, val.ErrorMessageResourceName,
                            String.Format(<span class="str">@"Validation Error Resource specified on property:
                        {0}.{1} is empty!"</span>, type.ToString(), property.Name));

                        <span class="kwrd">try</span>
                        {
                            ResourceManager rm = <span class="kwrd">new</span> ResourceManager(val.ErrorMessageResourceType);
                            <span class="kwrd">string</span> resourceValue = rm.GetString(val.ErrorMessageResourceName);
                            Assert.IsFalse(String.IsNullOrEmpty(resourceValue),
                                String.Format(<span class="str">@"The value of the Validation Error Resource specified on property:
                            {0}.{1} is empty!"</span>, type.ToString(), property.Name));
                        }
                        <span class="kwrd">catch</span> (MissingManifestResourceException)
                        {
                            Assert.Fail(String.Format(<span class="str">@"Validation Error Resource specified on property:
                            {0}.{1} could not be found!"</span>, type.ToString(), property.Name));
                        }
                    }
                }
            }
        }
    }
}</pre>
<p><!-- .csharpcode, .csharpcode pre { 	font-size: small; 	color: black; 	font-family: consolas, "Courier New", courier, monospace; 	background-color: #ffffff; 	/*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt  { 	background-color: #f4f4f4; 	width: 100%; 	margin: 0em; } .csharpcode .lnum { color: #606060; } --></p>
]]></content:encoded>
			<wfw:commentRss>http://martinnormark.com/unit-test-for-verifying-references-from-dataannotation-validation-to-the-errormessageresourcename-value/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
	</item>
		<item>
		<title>Automatically translate Global and Local Resource (resx) files</title>
		<link>http://martinnormark.com/automatically-translate-global-and-local-resource-resx-files?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link>
		<comments>http://martinnormark.com/automatically-translate-global-and-local-resource-resx-files#comments</comments>
		<pubDate>Fri, 13 Mar 2009 22:31:14 +0000</pubDate>
		<dc:creator>Martin Normark</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Internationalization]]></category>

		<guid isPermaLink="false">/post/Automatically-translate-Global-and-Local-Resource-(resx)-files.aspx</guid>
		<description><![CDATA[Yesterday, I blogged about how you can use Google Translate to translate a string in C#. To make it more useful than just a simple translator, and because I need to translate some Global Resource files for an E-commerce website &#8230; <p><a class="btn small" href="http://martinnormark.com/automatically-translate-global-and-local-resource-resx-files">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
			<content:encoded><![CDATA[<p>Yesterday, I blogged about how you can <a href="http://martinnormark.com/post/Translate-text-in-C-using-Google-Translate.aspx">use Google Translate to translate a string in C#</a>. To make it more useful than just a simple translator, and because I need to translate some Global Resource files for an E-commerce website that I’m working on, I wanted to create a small Windows Application in C# that could read a Global Resource file (.resx) and translate it into a selected language using the <a href="http://martinnormark.com/post/Translate-text-in-C-using-Google-Translate.aspx">method for translating a word in C#</a> that i blogged about yesterday.</p>
<p><a href="http://www.martinnormark.com/image.axd?picture=image.png"><img style="display: inline; border-width: 0px;" title="image" src="http://www.martinnormark.com/image.axd?picture=image_thumb.png" border="0" alt="image" width="451" height="166" /></a></p>
<p>This is how it looks so far. You simply select the resource file you want to translate. Select the current language of the resource file in the middle box, and select the language you want to translate it to in the last box. Click <strong>Translate</strong> at it should work. The new resource file will be saved in the same location as the application itself.</p>
]]></content:encoded>
			<wfw:commentRss>http://martinnormark.com/automatically-translate-global-and-local-resource-resx-files/feed</wfw:commentRss>
		<slash:comments>5</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
	</item>
		<item>
		<title>Designing for Internationalization</title>
		<link>http://martinnormark.com/designing-for-internationalization?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=rss</link>
		<comments>http://martinnormark.com/designing-for-internationalization#comments</comments>
		<pubDate>Tue, 10 Jun 2008 20:17:05 +0000</pubDate>
		<dc:creator>Martin Normark</dc:creator>
				<category><![CDATA[C#]]></category>
		<category><![CDATA[Internationalization]]></category>

		<guid isPermaLink="false">/post/Designing-for-Internationalization.aspx</guid>
		<description><![CDATA[Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace &#8230; <p><a class="btn small" href="http://martinnormark.com/designing-for-internationalization">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
			<content:encoded><![CDATA[<p>Web sites and Web Applications today, are very often exposed beyond the borders of your home country, and therefore users speak different languages, and has a different currency, date- and time formats etc. ASP.NET provides you with an entire namespace for handling things like this. That is the <a href="http://msdn.microsoft.com/en-us/library/system.globalization.aspx" target="_blank">System.Globalization</a> namespace, where you will find a lot of classes for handling your every day globalization tasks. I'm not going to cover anything in this namespace now, if you want to get your hands dirty take a look at this video: <a title="http://asp.net/learn/videos/video-40.aspx" href="http://asp.net/learn/videos/video-40.aspx">http://asp.net/learn/videos/video-40.aspx</a> where you will see how to use local and global resources for your application.</p>
<p>Using a global resource file for a place to store display text on buttons, labels, validation controls etc. is fine. But if you have an e-commerce site, selling products in multiple regions with different languages, you need an extra level. That level is a way to globalize e.g. the name and description of your product. When a user changes language, the name and description of your "display product details page" should change accordingly.</p>
<p>For me, the ideal solution should not result in extra database columns like name_us, name_da, name_es. This would be a very static solution, as you would have to change your database whenever a new language is added to your application. Nor should it require extra tables, so you need to join like hell, when you need to select a product.</p>
<p>I've decided to store e.g. the Name values as XML in the database, and parse that XML into a Dictionary&lt;string, string&gt; property on my Product object, with the key of the Dictionary being the language code (en-US for US English). To me this seems to work just fine. My database design is not getting more complex, and I can get and set values quite easily.</p>
<p>The XML string that goes into my ProductName column in the database table, looks like this:</p>
<p>&lt;cultures&gt;&lt;culture code="en-us"&gt;Logitech SmartCam 124&lt;/culture&gt;&lt;/cultures&gt;</p>
<p>On my Product object, the Name property is a Dictionary:</p>
<div>
<pre style="font-size: 8pt; margin: 0em; overflow: visible; width: 100%; color: black; line-height: 12pt; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4; border-style: none; padding: 0px;"><span style="color: #0000ff;">private</span> Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt; _Name;

<span style="color: #0000ff;">public</span> Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt; Name
{
  get { <span style="color: #0000ff;">return</span> _Name; }
  set
  {
    <span style="color: #0000ff;">if</span> (_Name != <span style="color: #0000ff;">value</span>) MarkDirty(<span style="color: #006080;">"Name"</span>);
    _Name = <span style="color: #0000ff;">value</span>;
  }
}</pre>
</div>
<p>When I need to get the US English value of the Product.Name property, I call:</p>
<div>
<pre style="font-size: 8pt; margin: 0em; overflow: visible; width: 100%; color: black; line-height: 12pt; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4; border-style: none; padding: 0px;">p.Name[<span style="color: #006080;">"en-us"</span>]</pre>
</div>
<p>To get a Dictionary from my XML, I use this helper method:</p>
<div>
<pre style="font-size: 8pt; margin: 0em; overflow: visible; width: 100%; color: black; line-height: 12pt; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4; border-style: none; padding: 0px;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt; GetDictionary(<span style="color: #0000ff;">string</span> xml)
{
  <span style="color: #0000ff;">if</span> (String.IsNullOrEmpty(xml))
    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">new</span> Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt;(owner);

  XmlDocument doc = <span style="color: #0000ff;">new</span> XmlDocument();
  doc.LoadXml(xml);

  Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt; dic = <span style="color: #0000ff;">new</span> Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt;();

  <span style="color: #0000ff;">foreach</span> (XmlNode node <span style="color: #0000ff;">in</span> doc.DocumentElement)
  {
    dic.Add(node.Attributes[<span style="color: #006080;">"code"</span>].Value, node.InnerText);
  }

  <span style="color: #0000ff;">return</span> dic;
}</pre>
</div>
<p>And when I want to update my database, after I change the Name of the Product, I convert to Dictionary to an XML string using this helper method:</p>
<div>
<pre style="font-size: 8pt; margin: 0em; overflow: visible; width: 100%; color: black; line-height: 12pt; font-family: consolas, 'Courier New', courier, monospace; background-color: #f4f4f4; border-style: none; padding: 0px;"><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">string</span> GetXmlDocument(Dictionary&lt;<span style="color: #0000ff;">string</span>, <span style="color: #0000ff;">string</span>&gt; dic)
{
  XmlDocument doc = <span style="color: #0000ff;">new</span> XmlDocument();
  XmlNode docElement = doc.CreateNode(XmlNodeType.Element, <span style="color: #006080;">"cultures"</span>, <span style="color: #006080;">""</span>);

  <span style="color: #0000ff;">foreach</span> (<span style="color: #0000ff;">string</span> key <span style="color: #0000ff;">in</span> dic.Keys)
  {
    XmlNode node = doc.CreateNode(XmlNodeType.Element, <span style="color: #006080;">"culture"</span>, <span style="color: #006080;">""</span>);
    XmlAttribute att = doc.CreateAttribute(<span style="color: #006080;">"code"</span>);
    att.Value = key;
    node.Attributes.Append(att);

    node.InnerText = dic[key];

    docElement.AppendChild(node);
  }

  doc.AppendChild(docElement);

  <span style="color: #0000ff;">return</span> doc.OuterXml;
}</pre>
</div>
<p>If you need more information on this huge topic, take a look at the ASP.NET Wiki: <a title="http://wiki.asp.net/page.aspx/55/internationalization/" href="http://wiki.asp.net/page.aspx/55/internationalization/">http://wiki.asp.net/page.aspx/55/internationalization/</a></p>
]]></content:encoded>
			<wfw:commentRss>http://martinnormark.com/designing-for-internationalization/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
	<creativeCommons:license>http://creativecommons.org/licenses/by/3.0/</creativeCommons:license>
	</item>
	</channel>
</rss>

