<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" xml:lang="en-US">
  <id>tag:patricklenz.com,2005:/feed</id>
  <link rel="alternate" type="text/html" href="http://patricklenz.com" />
  
  <title>Patrick Lenz</title>
  <updated>2012-02-23T12:24:01Z</updated>
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/patricklenz" /><feedburner:info uri="patricklenz" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <id>tag:patricklenz.com,2005:Post/526</id>
    <published>2012-02-23T12:13:07Z</published>
    <updated>2012-02-23T12:24:01Z</updated>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patricklenz/~3/2OEibpNUmJE/deploying-nesta-cms-blog-heroku-cedar-pygments-syntax-highlighting" />
    <title>Deploying a Nesta CMS Blog with Pygments Syntax Highlighting</title>
    <content type="html">&lt;p&gt;Ryan Daigle relaunched &lt;a href="http://ryandaigle.com/"&gt;his website&lt;/a&gt; using a new set of tools:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Having wrestled with quite a few blogging engines in the past I had several requirements of a new setup. Firstly it had to support a workflow that lets me write on my local machine using the tools I prefer, namely markdown formatted articles composed with IA Writer or a basic text editor.&lt;/p&gt;

&lt;p&gt;Second, it had to support a git-based workflow. My content is going to live in git and there’s no reason the publishing platform shouldn’t build on top of that as well. This also plays well with Heroku deployments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;He&amp;rsquo;s using the Sinatra-based &lt;a href="http://nestacms.com/"&gt;Nesta CMS&lt;/a&gt; in combination with hosting on Heroku. The post I&amp;rsquo;m linking to contains an in-depth walk-through of his setup and template, including Pygments-based syntax-highlighting, which is a topic &lt;a href="http://patricklenz.com/posts/syntax-highlighting-for-a-rails-blog"&gt;I just wrote about on this very blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ryan&amp;rsquo;s setup differs from mine insofar that I decided to build something from scratch&lt;sup id="ref-footnote-deploying-a-nesta-cms-blog-with-pygments-syntax-highlighting-1"&gt;&lt;a href="#footnote-deploying-a-nesta-cms-blog-with-pygments-syntax-highlighting-1"&gt;1&lt;/a&gt;&lt;/sup&gt; and not build it as a static site generator. I currently use a combination of &lt;a href="http://www.iawriter.com/"&gt;iA Writer&lt;/a&gt; for article drafting and an online posting form for the actual posting of articles. This way, I can also post from my iOS devices and without access to the computer that runs the static site generator. But Ryan&amp;rsquo;s approach has definitely picked my interest and I&amp;rsquo;ll surely play around with what Nesta has to offer.&lt;/p&gt;

&lt;ol class="footnotes"&gt;&lt;li id="footnote-deploying-a-nesta-cms-blog-with-pygments-syntax-highlighting-1"&gt;
&lt;p&gt;cf. &lt;a href="http://en.wikipedia.org/wiki/Not_invented_here"&gt;NIH-syndrome&lt;/a&gt; &lt;a href="#ref-footnote-deploying-a-nesta-cms-blog-with-pygments-syntax-highlighting-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;&lt;/ol&gt;

&lt;p&gt;&lt;a href="http://patricklenz.com/posts/deploying-a-nesta-cms-blog-with-pygments-syntax-highlighting" title="Permalink"&gt;#&lt;/a&gt;&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/patricklenz/~4/2OEibpNUmJE" height="1" width="1"/&gt;</content>
    <category term="linked" />
    <author>
      <name>Patrick Lenz</name>
    </author>
  <feedburner:origLink>http://ryandaigle.com/a/deploying-nesta-cms-blog-heroku-cedar-pygments-syntax-highlighting</feedburner:origLink></entry>
  <entry>
    <id>tag:patricklenz.com,2005:Post/525</id>
    <published>2012-02-21T11:11:08Z</published>
    <updated>2012-02-21T16:37:52Z</updated>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patricklenz/~3/-awc086Mkk8/the-little-launch-center-that-could-ve-been" />
    <title>The Little Launch Center That Could've Been</title>
    <content type="html">&lt;p&gt;App Cubby&amp;rsquo;s &lt;a href="http://appcubby.com/launch-center/"&gt;Launch Center&lt;/a&gt; is piece of art. It&amp;rsquo;s beautifully crafted, delightful to look at, and incorporates innovative features.&lt;/p&gt;

&lt;p&gt;And yet, earlier today, I deleted it from my iPhone.&lt;/p&gt;

&lt;p&gt;Launch Center&amp;rsquo;s basic premise, taken from their website, is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Launch apps and trigger actions from a quick, easy to use app! Call a friend, start an email, send a text, post to Twitter, turn on a flashlight — get to where you&amp;rsquo;re going faster than ever before.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Turns out, I was able to get to where I was going faster using other means after all.&lt;/p&gt;

&lt;h3&gt;My Setup&lt;/h3&gt;

&lt;p&gt;When Launch Center 1.2 debuted it &lt;a href="http://512pixels.net/app-recommendation-launch-center/"&gt;received&lt;/a&gt; &lt;a href="http://shawnblanc.net/2012/02/launch-center/"&gt;rave&lt;/a&gt; &lt;a href="http://www.readwriteweb.com/archives/launch_centers_curious_quest_to_fix_the_iphone.php"&gt;reviews&lt;/a&gt; &lt;a href="www.wired.com/gadgetlab/2012/01/launch-center-automates-iphone-apps-using-notification-center/"&gt;all&lt;/a&gt; &lt;a href="http://www.macstories.net/reviews/launch-center-1-2-gets-all-new-app-detection/"&gt;over&lt;/a&gt; the place. And whenever something gets so much attention, the nerd in me wants in on the action and give it a spin as well.&lt;/p&gt;

&lt;p&gt;The much toted headline feature of the 1.2 update was an easier way to add shortcuts to apps supporting URL schemes for some of the deeper nested screens, without having to go through the regular menu structure. Think &lt;a href="http://instagram.com/"&gt;Instagram&lt;/a&gt;&amp;rsquo;s camera or &lt;a href="http://omnigroup.com/omnifocus"&gt;OmniFocus&lt;/a&gt;&amp;lsquo; &amp;ldquo;Add Inbox Item&amp;rdquo; screen. So I started adding actions launching a few frequently used apps, calling my wife, and, obviously, the flash light, among others.&lt;/p&gt;

&lt;p&gt;I ended up with a screenful of shortcuts like this:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://f.cl.ly/items/220A1o1703412e2d0w30/launch-center-screen.jpg" alt="My Launch Center Shortcuts"&gt;&lt;div class="caption"&gt;My Launch Center Shortcuts&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;I added Launch Center to my iPhone&amp;rsquo;s first home screen and forced myself to go through it whenever I wanted to accomplish the task that could&amp;rsquo;ve been accomplished with one the shortcuts shown above.&lt;/p&gt;

&lt;p&gt;So much for the theoretical part. Let&amp;rsquo;s go through the list of shortcuts to discuss my results in practice.&lt;/p&gt;

&lt;h3&gt;Phonecalls and Messages&lt;/h3&gt;

&lt;p&gt;Calling my significant other or sending her a message is a common task for me during the day. Using Launch Center shortcuts to accomplish those didn&amp;rsquo;t really give me a boost in getting where I wanted faster though. I keep the &amp;ldquo;Phone&amp;rdquo; icon in my iPhone&amp;rsquo;s dock and have my wife&amp;rsquo;s phone numbers added to my list of &amp;ldquo;Favorites&amp;rdquo; so they&amp;rsquo;re never more than one or two taps away.&lt;/p&gt;

&lt;p&gt;For messages, I never close or delete conversations in the &amp;ldquo;Messages&amp;rdquo; app and since most recent conversations are located at the top neither were those very much out of reach.&lt;/p&gt;

&lt;p&gt;Furthermore, while out and about I tend to use Siri to make calls and send messages, which tends to be much faster still than either the &amp;ldquo;tappy&amp;rdquo; way or Launch Center.&lt;/p&gt;

&lt;h3&gt;Todos and Tweets&lt;/h3&gt;

&lt;p&gt;Launch Center supposedly strives with apps that support a &lt;a href="http://appcubby.com/urls/"&gt;deeper URL scheme&lt;/a&gt; and both OmniFocus (my task manager of choice) and &lt;a href="http://tapbots.com/software/tweetbot/"&gt;Tweetbot&lt;/a&gt; (my iOS Twitter client of  choice) are such brethren. &lt;/p&gt;

&lt;p&gt;As shown above, I added shortcuts for launching OmniFocus straight to the &amp;ldquo;Add Item to Inbox&amp;rdquo; screen and to launch Tweetbot straight to the &amp;ldquo;New Tweet&amp;rdquo; screen.&lt;/p&gt;

&lt;p&gt;In practice, both OmniFocus and Tweetbot have pretty much omnipresent buttons in their UI to accomplish the same task. You have to be navigated pretty deeply into Tweetbot&amp;rsquo;s view hierarchy to not be able to locate its bright blue &amp;ldquo;New Tweet&amp;rdquo; icon in the upper right corner. OmniFocus&amp;rsquo; icon bar at the bottom of the screen includes an &amp;ldquo;Add to Inbox&amp;rdquo; icon that even works while the app is syncing with the server (when pretty much everything else doesn&amp;rsquo;t work).&lt;/p&gt;

&lt;h3&gt;Launching Apps&lt;/h3&gt;

&lt;p&gt;The next type of shortcut was to launch apps that didn&amp;rsquo;t support any special URL scheme. For me, this included &lt;a href="http://reederapp.com/2/"&gt;Reeder&lt;/a&gt; and &lt;a href="https://agilebits.com/onepassword/iphone"&gt;1Password&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ll make it short. Even on an iPhone 4S swiping to the second home screen was faster than going through the overhead of launching Launch Center, locating the app to launch in the scrollable list, and then sitting through &lt;em&gt;its&lt;/em&gt; launch time.&lt;/p&gt;

&lt;p&gt;(The way my brain is wired up, I know exactly where a frequently used icon sits on any given home screen, so there&amp;rsquo;s no &amp;ldquo;hunting for the spot to tap&amp;rdquo;.)&lt;/p&gt;

&lt;h3&gt;System Settings&lt;/h3&gt;

&lt;p&gt;On iOS 5, the built-in Settings app also has some of these URL scheme shortcuts available for Launch Center to use. I added shortcuts to the VPN pane (there&amp;rsquo;s no way to specifically launch one VPN connection straight through a shortcut as far as I can tell) and to toggle &amp;ldquo;Airplane Mode&amp;rdquo; (which isn&amp;rsquo;t really a toggle after all, since it&amp;rsquo;ll only ever switch it on, never off).&lt;/p&gt;

&lt;p&gt;Those settings shortcuts worked, but they&amp;rsquo;re going the way of the dodo if you &lt;a href="http://www.appleinsider.com/articles/11/11/30/settings_shortcuts_disabled_by_apple_in_ios_5_1_beta.html"&gt;believe the rumors&lt;/a&gt; to be true of what changes are coming in iOS 5.1.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;Admittedly, I may just be using the wrong apps to get something out of Launch Center. Or my workflow may just not be appropriate  for it to fit in. (As opposed to &lt;a href="http://patricklenz.com/posts/where-clear-fits-in"&gt;Clear&lt;/a&gt;, which I did manage to fit in.)&lt;/p&gt;

&lt;p&gt;Also, I didn&amp;rsquo;t really get the appeal of Launch Center&amp;rsquo;s &amp;ldquo;scheduling&amp;rdquo; feature, which &lt;a href="http://www.macstories.net/news/launch-center-1-1-adds-scheduled-launch-actions/"&gt;debuted in version 1.1&lt;/a&gt;. It uses a &amp;ldquo;Notification Center&amp;rdquo;-based approach to push shortcuts you have defined and scheduled to your lock screen for easy access at a given time.&lt;/p&gt;

&lt;p&gt;One could argue that you can use it as a gentle reminder for daily sweeps of your OmniFocus inbox for example, but due items in OmniFocus also show up on your lock screen and swiping them brings you directly to that respective view.&lt;/p&gt;

&lt;p&gt;All in all, &lt;em&gt;for me personally&lt;/em&gt;, Launch Center seems to be more trouble than it&amp;rsquo;s worth. I applaud App Cubby for their efforts and I&amp;rsquo;m sure the majority of my criticism isn&amp;rsquo;t to be blamed on them in the first place. Things like Settings losing its URL scheme for example is totally not their fault.&lt;/p&gt;

&lt;p&gt;But, as of this morning, my home screen looks like this again and I have one less thing to worry about.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://f.cl.ly/items/220A1o1703412e2d0w30/homescreen.jpg" alt="My back-to-the-roots Homescreen"&gt;&lt;div class="caption"&gt;My back-to-the-roots Homescreen&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; App Cubby&amp;rsquo;s David Barnard &lt;a href="http://twitter.com/drbarnard/status/171991716218404864"&gt;responded on Twitter&lt;/a&gt; (mere &lt;em&gt;seconds&lt;/em&gt; after I&amp;rsquo;ve posted this) with the reassuring information that they&amp;rsquo;re aware of some of the issues and working on solutions. I&amp;rsquo;m certainly keeping an eye out on their future updates.&lt;/p&gt;&lt;img src="http://feeds.feedburner.com/~r/patricklenz/~4/-awc086Mkk8" height="1" width="1"/&gt;</content>
    <author>
      <name>Patrick Lenz</name>
    </author>
  <feedburner:origLink>http://patricklenz.com/posts/the-little-launch-center-that-could-ve-been</feedburner:origLink></entry>
  <entry>
    <id>tag:patricklenz.com,2005:Post/524</id>
    <published>2012-02-19T16:49:20Z</published>
    <updated>2012-02-19T17:21:12Z</updated>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patricklenz/~3/2jttE1IO5vU/where-clear-fits-in" />
    <title>Where Clear Fits In</title>
    <content type="html">&lt;p&gt;The web was abuzz when Realmac Software’s &lt;a href="http://www.realmacsoftware.com/clear/"&gt;Clear for iPhone&lt;/a&gt; was released. The buzz was comprised of both ends of the spectrum: High praise for its innovative, chrome-less, gesture-based UI on one end, criticism for its lack of features and being limited to just the iPhone app without a desktop counterpart on the other. &lt;/p&gt;

&lt;p&gt;&lt;a href="http://brooksreview.net/2012/02/clear/"&gt;Like Ben Brooks&lt;/a&gt;, I am a long-time &lt;a href="http://omnigroup.com/omnifocus"&gt;OmniFocus&lt;/a&gt; user on my Mac(s), iPhone, and iPad – Clear ain&amp;rsquo;t nowhere near replacing that. &lt;/p&gt;

&lt;p&gt;What I found, however, is that because of the sheer speed of using Clear it&amp;rsquo;s perfectly usable as a pure list-making tool to complement the full-blown OmniFocus experience. &lt;/p&gt;

&lt;p&gt;One obvious use case is maintaining a grocery shopping list in Clear. I have tried to do that in OmniFocus once. And boy have I tried to like it. But it has way too much friction to be even remotely useful for such a simple task. &lt;/p&gt;

&lt;p&gt;In a way, OmniFocus is just overqualified for that. And I mean that in an entirely positive fashion. It&amp;rsquo;s just impossible to excel at &lt;em&gt;any and all&lt;/em&gt; potential situations you could use a todo list for, so it’s unreasonable to expect that. &lt;/p&gt;

&lt;p&gt;Another scenario that I found myself preferring Clear over its bigger, more fully-featured brother is meeting notes, both as preparation and note taking during a meeting, like Scrum standups and one-on-one meetings. &lt;/p&gt;

&lt;p&gt;It was also more effective to jot down the things I wanted to remember to talk about during the meetings in Clear than in, say, a plain-text (or Markdown-flavored) note-taking tool such as &lt;a href="http://www.secondgearsoftware.com/elements/"&gt;Elements&lt;/a&gt; – not only due to the fact that Clear &lt;em&gt;only&lt;/em&gt; supports lists, but also because it artificially limits you to 30 characters per list item&lt;sup id="ref-footnote-where-clear-fits-in-1"&gt;&lt;a href="#footnote-where-clear-fits-in-1"&gt;1&lt;/a&gt;&lt;/sup&gt;, encouraging clear (pun!) and concise bullet points. &lt;/p&gt;

&lt;p&gt;In general, Clear is great at simple lists of non-repeating tasks that also don’t require a lot of context-switching (and, hence, context specification per task) to work through. &lt;/p&gt;

&lt;p&gt;What&amp;rsquo;s your use-case for Clear?&lt;/p&gt;

&lt;p&gt;PS: Some of the tweets Clear&amp;rsquo;s &lt;a href="http://twitter.com/useclear"&gt;@useclear&lt;/a&gt; Twitter account retweeted recently &lt;a href="https://twitter.com/klavr/statuses/170973570283995137"&gt;are&lt;/a&gt; &lt;a href="https://twitter.com/iXenus/statuses/170954133589864448"&gt;quite&lt;/a&gt; &lt;a href="https://twitter.com/darkrock/statuses/170954089830682624"&gt;hilarious&lt;/a&gt;. &lt;/p&gt;

&lt;ol class="footnotes"&gt;&lt;li id="footnote-where-clear-fits-in-1"&gt;
&lt;p&gt;Except when you use Siri-dictation, where this limit &lt;a href="https://twitter.com/patricklenz/statuses/170402882909253632"&gt;apparently does not apply&lt;/a&gt;. &lt;a href="#ref-footnote-where-clear-fits-in-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/patricklenz/~4/2jttE1IO5vU" height="1" width="1"/&gt;</content>
    <author>
      <name>Patrick Lenz</name>
    </author>
  <feedburner:origLink>http://patricklenz.com/posts/where-clear-fits-in</feedburner:origLink></entry>
  <entry>
    <id>tag:patricklenz.com,2005:Post/523</id>
    <published>2012-02-16T07:51:41Z</published>
    <updated>2012-02-16T14:04:48Z</updated>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patricklenz/~3/wejHjXpwCRI/syntax-highlighting-for-a-rails-blog" />
    <title>Syntax Highlighting for a Rails Blog</title>
    <content type="html">&lt;p&gt;Sparing you the boring details of the holy language war between Python and Ruby, the Python world has one great thing going for it: Proper &lt;a href="http://en.wikipedia.org/wiki/Syntax_highlighting"&gt;syntax highlighting&lt;/a&gt; libraries. While there are notable Ruby implementations like &lt;a href="http://coderay.rubychan.de/"&gt;Coderay&lt;/a&gt;, even popular Ruby-based shops like GitHub rely on the elephant in the room: &lt;a href="http://pygments.org/"&gt;Pygments&lt;/a&gt; – which is, if you couldn&amp;rsquo;t tell from the name, based on Python.&lt;/p&gt;

&lt;p&gt;Now, my blog is written in Ruby on Rails and runs on &lt;a href="http://heroku.com/"&gt;Heroku&lt;/a&gt; and, let&amp;rsquo;s put it this way, Heroku&amp;rsquo;s Ruby VMs and Python aren&amp;rsquo;t exactly sending each other Valentine&amp;rsquo;s cards.&lt;/p&gt;

&lt;p&gt;However, there&amp;rsquo;s an &lt;a href="http://pygments.appspot.com/"&gt;unofficial API for Pygments&lt;/a&gt;, accessible over HTTP, that you can use from your Rails application to do the syntax highlighting for you. You just &lt;code&gt;POST&lt;/code&gt; a blob of text to it and get back a blob of text with syntax highlighting classes applied.&lt;/p&gt;

&lt;p&gt;With that in mind, you can totally go down the route of passing your blog post&amp;rsquo;s body to that service when somebody views a page on your blog, just like &lt;a href="http://iamaust.in/posts/3"&gt;Austin Vance&lt;/a&gt; blogged about&lt;sup id="ref-footnote-syntax-highlighting-for-a-rails-blog-1"&gt;&lt;a href="#footnote-syntax-highlighting-for-a-rails-blog-1"&gt;1&lt;/a&gt;&lt;/sup&gt;. And while I adore the moving clouds, it&amp;rsquo;s not something I would advocate for performance and scalability reasons. Plus, as &lt;a href="https://twitter.com/#!/hmans/status/170030800195170304"&gt;@hmans put it&lt;/a&gt;, you don&amp;rsquo;t really want to be directly dependent on a third-party service to render a page on your site&lt;sup id="ref-footnote-syntax-highlighting-for-a-rails-blog-2"&gt;&lt;a href="#footnote-syntax-highlighting-for-a-rails-blog-2"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;So I&amp;rsquo;ve done things in a slightly different way around here.&lt;/p&gt;

&lt;h3&gt;The Background&lt;/h3&gt;

&lt;p&gt;My posts are written in &lt;a href="http://daringfireball.net/projects/markdown/"&gt;Markdown&lt;/a&gt;, rendered to HTML through &lt;a href="https://github.com/tanoku/redcarpet"&gt;Redcarpet 2&lt;/a&gt; in a &lt;code&gt;before_save&lt;/code&gt; callback whenever a post is created or updated. The syntax highlighting through the aforementioned unofficial Pygments API happens right after my Markdown prose has been transformed into HTML and stored in a separate database column along with the original, unhighlighted Markdown.&lt;/p&gt;

&lt;p&gt;That way, I&amp;rsquo;m killing multiple birds with one stone.&lt;/p&gt;

&lt;p&gt;First of all, displaying a page on my blog displays pre-generated HTML. It doesn&amp;rsquo;t slow down the request with calling third-party services or executing expensive regular expressions. While there&amp;rsquo;s a way to counteract many of these side-effects with caching, this approach also makes sure that future changes in APIs or syntax won&amp;rsquo;t change any of my old posts without me noticing. I try to be very attentive with my posts and old posts being a potential moving target isn&amp;rsquo;t an option. &lt;/p&gt;

&lt;p&gt;Lastly, doing all the syntax highlighting server-side saves clients the execution of expensive syntax highlighting in JavaScript for example, using plugins like &lt;a href="http://www.oriontransfer.co.nz/projects/jquery-syntax/index.en"&gt;jquery-syntax&lt;/a&gt;, improving rendering times of your pages noticeably.&lt;/p&gt;

&lt;h3&gt;The Implementation&lt;/h3&gt;

&lt;p&gt;As I mentioned before, the transformation from Markdown to syntax-highlighted HTML happens in a &lt;code&gt;before_save&lt;/code&gt; callback&lt;sup id="ref-footnote-syntax-highlighting-for-a-rails-blog-3"&gt;&lt;a href="#footnote-syntax-highlighting-for-a-rails-blog-3"&gt;3&lt;/a&gt;&lt;/sup&gt; in my &lt;code&gt;Post&lt;/code&gt; model.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;
  &lt;span class="c1"&gt;# [..]&lt;/span&gt;
  &lt;span class="n"&gt;before_save&lt;/span&gt; &lt;span class="ss"&gt;:generate_body_html&lt;/span&gt;

  &lt;span class="kp"&gt;protected&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_body_html&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;blank?&lt;/span&gt;
    &lt;span class="n"&gt;markdown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redcarpet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Markdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="no"&gt;Redcarpet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Render&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:hard_wrap&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;:no_intra_emphasis&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:autolink&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;:fenced_code_blocks&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body_html&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Redcarpet&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Render&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;SmartyPants&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="no"&gt;SyntaxHighlighter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;markdown&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Basically, a new renderer is instantiated with a variety of my preferred options (&lt;code&gt;fenced_code_blocks&lt;/code&gt; being the most important, more on that in a bit). The contents of the post (stored in the &lt;code&gt;body&lt;/code&gt; attribute) are then rendered to HTML, passed to the to-be-disclosed &lt;code&gt;SyntaxHighlighter&lt;/code&gt; class, and, lastly, the result of that gets the &lt;a href="http://daringfireball.net/projects/smartypants/"&gt;SmartyPants&lt;/a&gt; rules applied.&lt;/p&gt;

&lt;p&gt;The whole shebang finally gets assigned to the &lt;code&gt;body_html&lt;/code&gt; attribute, which will henceforth contain the fully-baked, ready to display HTML code of the post body.&lt;/p&gt;

&lt;p&gt;Now, for the meat of it, let&amp;rsquo;s take a look at the &lt;code&gt;SyntaxHighlighter&lt;/code&gt; class.&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'net/http'&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'uri'&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SyntaxHighlighter&lt;/span&gt;
  &lt;span class="no"&gt;PYGMENTS_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'http://pygments.appspot.com/'&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Nokogiri&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;html&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"pre &amp;gt; code[class]"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Net&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post_form&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PYGMENTS_URI&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
                  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s1"&gt;'lang'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:class&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'code'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rstrip&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;replace&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;to_s&lt;/span&gt;
    &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"//body"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;I&amp;rsquo;m using &lt;a href="http://nokogiri.org/"&gt;Nokogiri&lt;/a&gt; (in my book still the de-facto standard in HTML parsing) to disassemble the post&amp;rsquo;s HTML structure (remember that we&amp;rsquo;re passing in a copy of the body that has already been transformed from Markdown into HTML) to find blocks of code wrapped in &lt;code&gt;&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;&lt;/code&gt; tag pairs, which also have a value assigned to the &lt;code&gt;class&lt;/code&gt; attribute of the &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; tag.&lt;/p&gt;

&lt;p&gt;And this is where the &lt;code&gt;fenced_code_blocks&lt;/code&gt; option to Redcarpet comes in. With this enabled, you can write your code-blocks like this in Markdown:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;``` ruby
class Foo
end
```
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That means, no more 4-spaces indentation for every line &lt;em&gt;and&lt;/em&gt;, more importantly, you can pass along a value for the class attribute to assign to the &lt;code&gt;&amp;lt;code&amp;gt;&lt;/code&gt; tag it&amp;rsquo;s wrapped in, specifying the programming language of the code to highlight.&lt;/p&gt;

&lt;p&gt;But back to the code.&lt;/p&gt;

&lt;p&gt;Once those code blocks have been located and extracted along with their classes, they&amp;rsquo;re posted to &lt;code&gt;pygments.appspot.com&lt;/code&gt; individually (because you could have multiple code blocks in a post, each in a different programming language) and the result is inserted back into the document structure, replacing the previously extracted code block.&lt;/p&gt;

&lt;p&gt;Lastly, the &lt;code&gt;to_s&lt;/code&gt; method simply returns the document converted back to a string, which, as you recall, then gets saved in the &lt;code&gt;body_html&lt;/code&gt; attribute for later use when displaying a post.&lt;/p&gt;

&lt;p&gt;Not to beat a dead horse here, but all of this only ever happens when you create or update a post. It never happens when a post is merely viewed.&lt;/p&gt;

&lt;p&gt;For styling, all the default &lt;a href="http://pygments.org/docs/styles/"&gt;Pygments styles&lt;/a&gt; rules apply. If you&amp;rsquo;re happy with the defaults, Trevor supplies a &lt;a href="https://github.com/trevorturk/pygments/blob/master/default.css"&gt;default.css&lt;/a&gt; in his pygments repository. In addition to that, more styles are available &lt;a href="http://www.google.com/search?q=pygments%20styles"&gt;all over the web&lt;/a&gt;. Also, Favio Manriquez Leon has &lt;a href="http://blog.favrik.com/2011/02/22/preview-all-pygments-styles-for-your-code-highlighting-needs/"&gt;a way to preview the built-in styles&lt;/a&gt; on his blog.&lt;/p&gt;

&lt;h3&gt;Conclusion&lt;/h3&gt;

&lt;p&gt;So there you have it. A way to re-use Python&amp;rsquo;s great syntax highlighting for your Heroku-powered Rails blog in a way that will survive getting &lt;a href="http://fireballed.org/"&gt;fireballed&lt;/a&gt;.&lt;/p&gt;

&lt;ol class="footnotes"&gt;
&lt;li id="footnote-syntax-highlighting-for-a-rails-blog-1"&gt;
&lt;p&gt;Sadly, I couldn&amp;rsquo;t really tell when, as his post doesn&amp;rsquo;t seem to have a date. But it must&amp;rsquo;ve been some time after July 2011, since he references a Railscasts episode from that time.
 &lt;a href="#ref-footnote-syntax-highlighting-for-a-rails-blog-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="footnote-syntax-highlighting-for-a-rails-blog-2"&gt;
&lt;p&gt;Even when it&amp;rsquo;s being run by the commendable &lt;a href="https://github.com/trevorturk"&gt;Trevor Turk&lt;/a&gt;.
 &lt;a href="#ref-footnote-syntax-highlighting-for-a-rails-blog-2"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="footnote-syntax-highlighting-for-a-rails-blog-3"&gt;
&lt;p&gt;Brush up your callbacks knowledge in Rails with &lt;a href="http://guides.rubyonrails.org/active_record_validations_callbacks.html"&gt;this guide&lt;/a&gt; &lt;a href="#ref-footnote-syntax-highlighting-for-a-rails-blog-3"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/patricklenz/~4/wejHjXpwCRI" height="1" width="1"/&gt;</content>
    <author>
      <name>Patrick Lenz</name>
    </author>
  <feedburner:origLink>http://patricklenz.com/posts/syntax-highlighting-for-a-rails-blog</feedburner:origLink></entry>
  <entry>
    <id>tag:patricklenz.com,2005:Post/522</id>
    <published>2012-02-14T14:03:23Z</published>
    <updated>2012-02-14T20:36:53Z</updated>
    <link rel="alternate" type="text/html" href="http://feedproxy.google.com/~r/patricklenz/~3/VEttPPRI8KU/proper-language-detection-on-the-web" />
    <title>Proper Language Detection on the Web</title>
    <content type="html">&lt;p&gt;A couple of days ago I quipped:&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;
&lt;p&gt;Why can&amp;rsquo;t we agree that polling the preferred language from browsers trumps GeoIP six ways to Sunday? Travelers of the world will thank you.&lt;/p&gt;— Patrick Lenz (@patricklenz) &lt;a href="https://twitter.com/patricklenz/status/168085187111030784" data-datetime="2012-02-10T21:33:26+00:00"&gt;February 10, 2012&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;Which, apparently, struck some sort of nerve as it got favorited and retweeted above my usual average. And that prompted me to explore this topic a little more from a technical standpoint.&lt;/p&gt;

&lt;p&gt;But, first, a little bit of history.&lt;/p&gt;

&lt;p&gt;What originally angered me was the fact that some websites insist on offering me a German language experience of their site, despite the fact that I deliberately switch all of my computers, phones, televisions, and vacuum cleaners to use the English language, because, well, that’s how I roll.&lt;/p&gt;

&lt;h2&gt;The Difference Between Shopping and Information&lt;/h2&gt;

&lt;p&gt;Shopping sites like Amazon have every right in the world to send me to their local offerings if they detect that I’m coming from a certain country where they have a local presence in. (More on that detection in a bit.) &lt;/p&gt;

&lt;p&gt;&lt;img src="http://f.cl.ly/items/2n0y1J1p2d1V2k0P1a0n/Amazon%20Header.png" alt="Friendly reminder to shop locally"&gt;&lt;div class="caption"&gt;Friendly reminder to shop locally&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;Yet, they don’t. They simply offer a friendly banner at the top of &lt;a href="http://amazon.com/"&gt;Amazon.com&lt;/a&gt; that informs me of a local offering I might be interested in. (And I am.)&lt;/p&gt;

&lt;p&gt;Compare and contrast that with sites like &lt;a href="http://softonic.com/"&gt;Softonic&lt;/a&gt;, which slap me in the face with a modal dialog – requiring an immediate decision on my part – suggesting I use their site in German instead of the version that I chose to access and which matches the language of my browser.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://f.cl.ly/items/1B3u1z2V2f2h3M0w0O04/Softonic%20Language%20Dialog.png" alt="Modal language dialog at Softonic"&gt;&lt;div class="caption"&gt;Modal language dialog at Softonic&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;While I appreciate the fact that they may be employing a CDN&lt;sup id="ref-footnote-proper-language-detection-on-the-web-1"&gt;&lt;a href="#footnote-proper-language-detection-on-the-web-1"&gt;1&lt;/a&gt;&lt;/sup&gt; for speedy downloads from a local server, this is an implementation detail independent from the actual language used on their site, which I don’t really happen to care about to be in the language of my geographic location (as opposed to the language of my browser and operating system).&lt;/p&gt;

&lt;p&gt;(And I’m not trying to single out Softonic here. This forced language detection based on geographic location instead of browser language seems to be common practice. Softonic just happened to be the site that prompted the tweet embedded above.)&lt;/p&gt;

&lt;h2&gt;Odd Birds and Travelers&lt;/h2&gt;

&lt;p&gt;I may well be the odd bird in this case in that my geographical location does not match the preferred language set in my browser.&lt;/p&gt;

&lt;p&gt;But what about travelers?&lt;/p&gt;

&lt;p&gt;Everyone traveling abroad is probably in the same boat as I am. Potentially even worse, given that their foreign language skills may not be as fluent and a foreign language version of a site (that is not a shopping site) may be of even less interest.&lt;/p&gt;

&lt;h2&gt;Technical Hurdles&lt;/h2&gt;

&lt;p&gt;Detecting the geographic location of a visitor to your site is a non-trivial task. It usually involves some kind of third-party service doing a &lt;a href="http://en.wikipedia.org/wiki/Geolocation"&gt;geolocation&lt;/a&gt; lookup, which means a round-trip to at least one other server before you can even render the first page view to your visitor, delaying the often crucial first impression the visitor gets by a couple hundred milliseconds.&lt;/p&gt;

&lt;p&gt;While this is a necessary evil for DRM-plagued services such as &lt;a href="http://netflix.com/"&gt;Netflix&lt;/a&gt; and targeted online advertising to use &lt;a href="http://en.wikipedia.org/wiki/Geotargeting"&gt;geotargeting&lt;/a&gt; as part of their offering, using a geolocation for the display language of your website is just plain overkill.&lt;/p&gt;

&lt;p&gt;Why? Because every modern browser sends its language preference (or, more appropriately, that of its user) along with every HTTP request being made in the form of the &lt;code&gt;Accept-Language&lt;/code&gt; header that is part of the &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html"&gt;HTTP/1.1 RFC&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Accept-Language request-header field is similar to Accept, but restricts the set of natural languages that are preferred as a response to the request. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s even possible to supply multiple languages with appropriate weightings:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Each language-range MAY be given an associated quality value which represents an estimate of the user&amp;rsquo;s preference for the languages specified by that range. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So without round-tripping to (potentially non-free) third-party services in order to find out where your visitor is coming from, you can simply use the &lt;code&gt;Accept-Language&lt;/code&gt; header supplied by the visitor in most situations to make an even more informed decision what their language preference may be.&lt;/p&gt;

&lt;h2&gt;Lead by example&lt;/h2&gt;

&lt;p&gt;In Rails, the value of the &lt;code&gt;Accept-Language&lt;/code&gt; can be found in &lt;code&gt;request.headers[‘HTTP_ACCEPT_LANGUAGE’]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is, for example, the value supplied by Chrome 17 when set to the English language:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Accept-Language: en-US,en;q=0.8
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If you switch it to German, it’ll send:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Basically, this list specifies German as the most desired language, US-English as the middle ground, and, if all else fails, any dialect of English will do.&lt;/p&gt;

&lt;p&gt;Other browsers like Firefox and Safari supply similar values for &lt;code&gt;Accept-Language&lt;/code&gt;, albeit not as detailed.&lt;/p&gt;

&lt;p&gt;Great further reading on the topic of internationalizing a Rails application is the &lt;a href="http://guides.rubyonrails.org/i18n.html"&gt;Guide on the Rails I18n API&lt;/a&gt; and Iain Hecker&amp;rsquo;s &lt;a href="https://github.com/iain/http_accept_language"&gt;http_accept_language&lt;/a&gt; gem.&lt;/p&gt;

&lt;p&gt;If you’d like to use this header in JavaScript and don’t have a way to pass it to the Javascript layer from your app (I heard those cases exist), there’s a helpful jQuery plugin from Dan Singerman called &lt;a href="https://github.com/dansingerman/jQuery-Browser-Language"&gt;jQuery Browser Language&lt;/a&gt;, which gives you access to this header otherwise unavailable to JavaScript.&lt;/p&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;

&lt;p&gt;Don’t force people to use your site or web application in a language they didn’t request just because they happen to sit in a spot on this planet where people &lt;em&gt;usually&lt;/em&gt; speak it. Don&amp;rsquo;t use geotargeting for language detection just because you can. Instead, make use of established practices such as the &lt;code&gt;Accept-Language&lt;/code&gt; header to deliver a much better user experience and much more likely matching their expectations.&lt;/p&gt;

&lt;ol class="footnotes"&gt;&lt;li id="footnote-proper-language-detection-on-the-web-1"&gt;
&lt;p&gt;A Content Delivery Network, supposedly serving files from a geographically close location with better latency to your neck of the woods. &lt;a href="#ref-footnote-proper-language-detection-on-the-web-1"&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;&lt;/ol&gt;&lt;img src="http://feeds.feedburner.com/~r/patricklenz/~4/VEttPPRI8KU" height="1" width="1"/&gt;</content>
    <author>
      <name>Patrick Lenz</name>
    </author>
  <feedburner:origLink>http://patricklenz.com/posts/proper-language-detection-on-the-web</feedburner:origLink></entry>
</feed>

