ENTRY
1094

Wordpress Without Borders

Wordpress Without Borders
Wednesday, March 26, 2008
/ Programming, Technology, Wordpress

Yesterday, while browsing the Google blog, I stumbled across a recent entry they did regarding the release of their new AJAX language API. While reading it, I had a couple of crazy ideas what it could potentially be used for. One of those ideas was to dynamically translate content on my blog.

So after grabbing a bite to eat on my way home tonight, I decided to take a stab at creating a Wordpress plugin that would automatically translate content on the fly. My basic game-plan was to grab the HTTP header associated with the user’s language, intercept a few Wordpress locations, and do some dynamic translating of the content. I just wanted to do the titles at first, thinking that would be enough. But to be honest, I was so excited after getting that working that I decided to take a stab at the rest of the content on my blog, including posts, excerpts, categories, and tags.

It wasn’t easy. One of the major problems I had was the failure of the Google API to translate any content with HTML tags in it (which obviously makes sense, but makes it difficult to translate dynamic content littered with it). So what I ended up having to do is decompose the content into text fragments, translate those, and then recompose it back together with the appropriate markup.

The result, after about three hours, is a blog that translates itself to whatever language you are trying to view it in (assuming of course Google supports that language). If you want to test it out, you should be able to use Firefox. From the preferences panel, choose “Advanced”, “Languages”, and pick a new language to view the blog in (making sure to move it to the top of your list). If you stick with a well known language and refresh on this blog, you should see most of the dynamic content on this blog magically transform into the language of your choice.

My Blog, Dynamically Translated To Japanese

If you don’t have Firefox, you can head on over to my Flickr set to view a few photos.

I think this plugin is actually pretty elegant. If you consider how it works, it’s basically dynamic content driving server side scripts which in term drive the creation of dynamic client-side Javascript that ultimately performs the translation. Dynamically.

And while that by itself is really exciting to me, there’s another benefit that’s subtle but has the potential to really change how blogging is done. If you leave out the source translation language identifier in the API call for the translation, Google will automatically try to detect it for you. That feature is fairly useless if I write all the content on my blog myself and always stick to my primary language (since auto-detection will always come up with that language), which is the typical case for most people. However, if you couple a blog that can translate itself for every user and add the ability to auto-detect what language they commented in, you can then allow users to comment in their native language, and automatically translate it for everyone else. Quite simply, it allows for language independent information sharing.

A Reader Leaves A Comment In Chinese

Comment Translated To English When Viewed In English

There are obviously a few bugs, and I have to give some major thought to the big picture. But as a proof of concept I think it’s pretty exciting. I posted a few comments below in various languages. If all is well, they should be translated to english for you (assuming you are viewing the blog in english). Obviously there are some subtle translation differences, but for the most part the meaning still comes across. If you don’t believe it, view the HTML source — you should see the comments in their original languages prior to the AJAX translation.

Ultimately this plugin will end up over on BraveNewCode, a place where Dale Mugford and myself are going to throw all the really cool things we’re working on in our spare time.

* Update - head over to the official Wordpress Without Borders page to download an alpha version of the plugin.

note: multibyte strings should work as well, but there appears to be a bug in Wordpress 2.5 that’s limiting my use of them below — this works in Wordpress 2.3.3 though

32 comments
Krista
Posted on Wednesday, March 26th, 2008 at 10:24 pm

A “byte” you say? I guess it deserves a chuckle.
I like the Russian even though I can’t read a word of it, i think it’s the way the font is.

Duane Storey
Posted on Wednesday, March 26th, 2008 at 10:32 pm

Ceci est un commentaire en français. Je pense vraiment que la langue de traduction dynamique est étonnante.

Duane Storey
Posted on Wednesday, March 26th, 2008 at 10:33 pm

Este es un comentario en español. Realmente creo dinámico de la traducción de la lengua es increíble.

Duane Storey
Posted on Wednesday, March 26th, 2008 at 10:35 pm

Questo è un commento in italiano. Penso proprio dinamico linguaggio traduzione è sorprendente.

Duane Storey
Posted on Wednesday, March 26th, 2008 at 10:38 pm

Dies ist ein Kommentar in Deutsch. Ich glaube wirklich, dynamische Sprache Übersetzung ist erstaunlich.

Duane Storey
Posted on Wednesday, March 26th, 2008 at 10:41 pm

Este é um comentário em português. Creio sinceramente dinâmica tradução é espantoso.

Ciavarro
Posted on Wednesday, March 26th, 2008 at 11:08 pm

Pretty cool man.

Lazy
Posted on Thursday, March 27th, 2008 at 12:10 am

i recognized the new api last week and gave it a test, too.. it`s really working great.. nice try.. i`m the same opinion like you, it could be a really great thing and make a lot of things very easy..

wünsche dir einen schönen tag ;)

liebe grüße

Chris

Duane Storey
Posted on Thursday, March 27th, 2008 at 12:13 am

^^ I want to point out that Lazy’s comment was actually written in two languages.

Raul
Posted on Thursday, March 27th, 2008 at 2:22 am

Well, I could leave my comments in seven different languages but I don’t have the keyboard to leave it in Japanese :)

Andrea
Posted on Thursday, March 27th, 2008 at 4:43 am

WHOA. this is flippin’ sweeet. I was just loking at a WP translation plugin yesterday but this? Oh man, the languages translated before my eyes…

If you need a tester, pick me! pick me! :D

Dan
Posted on Thursday, March 27th, 2008 at 6:28 am

Very impressive!

And what’s more, seeing this here, I wonder why practically every website doesn’t do this (newspapers, forums)? And how long until they do?

Good innovating man!

Lazy
Posted on Thursday, March 27th, 2008 at 7:50 am

you`re right, Duane Storey :) of course.. es macht aber auch viel spaß und auf deutsch geschrieben klingt mein deutsch auch etwas normaler und nicht so naja bisschen seltsam ;) aber es ist wirklich innovativ.. ich seh schon foren plugins kommen, blog plugins, das übliche.. vielleicht wirds ein trend werden, wer weiß.. ;)

John
Posted on Thursday, March 27th, 2008 at 9:03 am

Very cool dude…this will be huge.

Tawcan
Posted on Thursday, March 27th, 2008 at 10:15 am

wow this is cool.

Samuel Mikel Bowles
Posted on Thursday, March 27th, 2008 at 5:13 pm

It would be cool if translated comments could be marked somehow (flag icon?). And if the end-user could choose to view a comment in it’s original language on the site. For example I speak both English and Spanish and would prefer to read both of those languages without translation even though I have my browser set to English. It would be nice to click an icon to remove translation on a given comment especially if I could quickly identify it’s original language.

Even simply marking that comments have been translated would clarify communication. I could imagine for example a pedantic commenter correcting someone’s translated grammar or worse confusion stemming from the assumption that a given commenter meant exactly what they wrote even though we are not reading exactly what they wrote.

Duane Storey
Posted on Thursday, March 27th, 2008 at 5:17 pm

Yah, I absolutely agree. This was just a few hours of work. The main problem is I really don’t want to make it so that the theme has to be modified. I want everyone to be able to download it and simply turn it on. So with that constraint it’s tricky to do what you are proposing, although I agree it has to be done.

Right now I’m leaning towards having a small 20 pixel tall semi-transparent bar at the top that indicates the content has been translated. By clicking on it, you can go back to the original untranslated-version. Hopefully that will work for most people.

Duane Storey
Posted on Thursday, March 27th, 2008 at 5:18 pm

Do you have spanish chosen as your language backup in your list? I could make it so that all languages in the list are not translated possibly.

Raul
Posted on Friday, March 28th, 2008 at 12:26 am

Por ejemplo, este comentario esta en espanol.

And the rest is in English.

Brian
Posted on Friday, March 28th, 2008 at 1:13 am

Wow! I changed to Spanish and seeing it translate inline was really, really cool. Great job with this. Are you going to be open sourcing the code?

nortypig » Blog Archive » Wordpress Without Borders
Posted on Friday, March 28th, 2008 at 1:28 am

[...] Duane Story has put together a proof of concept on a self-translating blog using the new Google Ajax Language API - Wordpress without Borders. [...]

Jonathan Lumb
Posted on Friday, March 28th, 2008 at 9:42 am

Wow, loving this plugin! I’ve tested it out and it works nice in French, Spanish and Chinese (I have a firefox plugin that switches the user agent around). Translations are slightly dodgy but as you said the meaning is still there! Having said that there are some interesting chinese comments going on here - my girlfriend (who is chinese) was a bit perplexed!

The one thing I would be careful about is forcing the translation on a user - sometimes people would probably just prefer to read the article in english (even if it isn’t their native language).

Look forward to seeing this being released!

Duane Storey
Posted on Friday, March 28th, 2008 at 10:02 am

Yah, it will be optional, or at least the option will be there to view it natively as well. Thanks for the feedback.

Steven Wittens
Posted on Friday, March 28th, 2008 at 11:54 am

So… how does one turn this *off*? Just because my system is set to Dutch doesn’t mean I can’t read English… and I’d rather not read your blog as a series of badly mangled pseudo-Dutch phrases or be forced to change my entire system language.

Machine translation as a last resort? Sure. But machine translation by default? Horrible idea.

It also seems rather silly that the code for this repeats the same code snippet over and over again with only a single ID changed, instead of just looping over an array of content to translate. It adds 36KB to this page.

Duane Storey
Posted on Friday, March 28th, 2008 at 11:57 am

As I pointed out up above in the comments, and in my blog entry, this is just a proof of concept to get some ideas. I totally agree that it shouldn’t be all or nothing, and I already pointed out some alternate approaches up above as well.

In terms of the code, there are about a million ways to make it better. But like I said, I only put in a few hours of work.

Dynamic Translation Of Wordpress Content » the duane storey
Posted on Friday, March 28th, 2008 at 8:17 pm

[...] Without BordersTitle Tags And WordpressWordpress 2.5 BetaMedia Burner Beta Wordpress Without BordersTitle Tags And WordpressWordpress 2.5 BetaMedia Burner [...]

jon
Posted on Saturday, March 29th, 2008 at 4:08 pm

chupa mi pinga carajo

Duane Storey
Posted on Saturday, March 29th, 2008 at 4:12 pm

It’s not so good with slang.

WordPress Localization » random process | charlie 2.0
Posted on Wednesday, April 2nd, 2008 at 5:57 pm

[...] This use of Google’s new AJAX language API with WordPress is absolutely fantastic. I can’t wait to try my hands at it. [...]

Jai
Posted on Thursday, April 3rd, 2008 at 7:49 am

Does it… does it work in Klingon?

Will
Posted on Thursday, April 3rd, 2008 at 10:12 am

Cool idea… I’ll be interested to see how long it takes for private newspapers and online journals to implement something like it.

By the way, you’ve got a bunch of code showing at the bottom of the page :P

Rogan McGillis
Posted on Thursday, April 3rd, 2008 at 12:30 pm

I have to say this is by far the the coolest plugin I have seen! I can’t wait to see this one built. I’ve been looking for a translator plugin for a while and haven’t found any I like that actually function properly. This is going to be really cool… can’t wait to see it finished.

Back to the Top

google.load("language", "1"); var curstate = 0; var hasloaded = 0; function bnc_show_translated() { if (hasloaded == 0) { bnc_lang_callback(); hasloaded = 1; } for (i = 0; i < 33; i++) { var elem = $("bnc_original_" + i); if (elem) { if (curstate) { elem.show(); } else { elem.hide(); } } } for (i = 0; i < 33; i++) { var elem = $("bnc_trans_" + i); if (elem) { if (curstate) { elem.hide(); } else { elem.show(); } } } if (curstate) { $("bnc_trans_state1").show(); $("bnc_trans_state2").hide(); curstate = 0; } else { $("bnc_trans_state1").hide(); $("bnc_trans_state2").show(); curstate = 1; } } function bnc_detect_div(div_id) { var text = document.getElementById(div_id); if (text) { text = text.innerHTML; if (text.length > 0) { google.language.detect(text, function(result) { if (!result.error) { if (result.language != "en") { if (result.confidence > 0.25) { $("bnc_translating").show(); bnc_xlate_div(result.language, div_id, "en"); } } } } ); } } } function bnc_xlate_div(src_lang,div_id,o_lang) { var text = document.getElementById(div_id); if (text) { text = text.innerHTML; google.language.translate(text, src_lang, o_lang, function(result) { var translated = document.getElementById(div_id); if (result.translation) { translated.innerHTML = result.translation; } }); } } function bnc_lang_callback() { bnc_xlate_div("en", "bnc_lang_i_0_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_1_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_2_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_3_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_4_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_5_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_6_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_7_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_8_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_9_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_10_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_11_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_12_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_13_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_14_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_15_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_16_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_17_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_18_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_19_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_20_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_21_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_22_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_23_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_24_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_25_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_26_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_27_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_28_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_29_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_30_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_31_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_32_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_33_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_34_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_35_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_36_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_37_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_38_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_39_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_40_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_41_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_42_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_43_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_44_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_45_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_46_1094", "en"); bnc_xlate_div("en", "bnc_lang_i_47_1094", "en"); bnc_xlate_div("en", "bnc_lang_a_0_0", "en"); bnc_xlate_div("en", "bnc_lang_a_1_0", "en"); bnc_xlate_div("en", "bnc_lang_a_2_0", "en"); bnc_xlate_div("en", "bnc_lang_a_3_0", "en"); bnc_xlate_div("en", "bnc_lang_a_4_0", "en"); bnc_xlate_div("en", "bnc_lang_a_5_0", "en"); bnc_xlate_div("en", "bnc_lang_a_6_0", "en"); } function bnc_startup() { bnc_xlate_div("en", "bnc_translate_info", "en"); bnc_xlate_div("en", "bnc_translate_info2", "en"); bnc_detect_div("bnc_lang_i_0_1094");bnc_detect_div("bnc_lang_i_1_1094");bnc_detect_div("bnc_lang_i_2_1094");bnc_detect_div("bnc_lang_i_3_1094");bnc_detect_div("bnc_lang_i_4_1094");bnc_detect_div("bnc_lang_i_5_1094");bnc_detect_div("bnc_lang_i_6_1094");bnc_detect_div("bnc_lang_i_7_1094");bnc_detect_div("bnc_lang_i_8_1094");bnc_detect_div("bnc_lang_i_9_1094");bnc_detect_div("bnc_lang_i_10_1094");bnc_detect_div("bnc_lang_i_11_1094");bnc_detect_div("bnc_lang_i_12_1094");bnc_detect_div("bnc_lang_i_13_1094");bnc_detect_div("bnc_lang_i_14_1094");bnc_detect_div("bnc_lang_i_15_1094");bnc_detect_div("bnc_lang_i_16_1094");bnc_detect_div("bnc_lang_i_17_1094");bnc_detect_div("bnc_lang_i_18_1094");bnc_detect_div("bnc_lang_i_19_1094");bnc_detect_div("bnc_lang_i_20_1094");bnc_detect_div("bnc_lang_i_21_1094");bnc_detect_div("bnc_lang_i_22_1094");bnc_detect_div("bnc_lang_i_23_1094");bnc_detect_div("bnc_lang_i_24_1094");bnc_detect_div("bnc_lang_i_25_1094");bnc_detect_div("bnc_lang_i_26_1094");bnc_detect_div("bnc_lang_i_27_1094");bnc_detect_div("bnc_lang_i_28_1094");bnc_detect_div("bnc_lang_i_29_1094");bnc_detect_div("bnc_lang_i_30_1094");bnc_detect_div("bnc_lang_i_31_1094");bnc_detect_div("bnc_lang_i_32_1094");bnc_detect_div("bnc_lang_i_33_1094");bnc_detect_div("bnc_lang_i_34_1094");bnc_detect_div("bnc_lang_i_35_1094");bnc_detect_div("bnc_lang_i_36_1094");bnc_detect_div("bnc_lang_i_37_1094");bnc_detect_div("bnc_lang_i_38_1094");bnc_detect_div("bnc_lang_i_39_1094");bnc_detect_div("bnc_lang_i_40_1094");bnc_detect_div("bnc_lang_i_41_1094");bnc_detect_div("bnc_lang_i_42_1094");bnc_detect_div("bnc_lang_i_43_1094");bnc_detect_div("bnc_lang_i_44_1094");bnc_detect_div("bnc_lang_i_45_1094");bnc_detect_div("bnc_lang_i_46_1094");bnc_detect_div("bnc_lang_i_47_1094"); } google.setOnLoadCallback(bnc_startup);