Today we launched the new version of NakedTranslations.com, a website for my friend’s translation business. The site is entirely bilingual, including a blog, and raised some technical challenges. Here’s how I did it.
We wanted to be able to do the following:
- give the user a clean experience in their language of of choice, so they can navigate the site entirely in English or French, but switch between the two at will
- have a distinct url for each entry, with the url relevant to the language used (for example, the related posts “New Design” on the English site has the url
while the French entry about the same thing is called “Y a du changement dans l’air” and has the url
- keep track of comments for each entry, split by language (so, French and English comments don’t get mixed up)
- link between blog entries of different languages
Choice of platform – Movable Type
The site uses Movable Type, which is certainly not my blogging software of choice. This is because MT allows you to have multiple blogs under the same login, and reference between them through Custom Fields. It also allows us to set up the language for one of the blogs to be French, while the other is English, making it easier to get blog entry dates to automatically use the right language and so forth. This allows us to have the proper urls for the language used, and link our blog entries between languages. MT has a lot of things going for it, but good documentation, a friendly user community, and a slick user interface are all missing from the mix. A fair amount of the documentation is out of date or missing a key element, and I was shocked by some of the defensive rudeness in the forums from people who said RTFM without pointing to the relevant bit of the FM or writing anything useful in the FM. Anyway…
But what about WordPress?
There are a lot of very nice looking WordPress plugins and tweaks that do similar things, but none that met our requirements entirely. The other thing that made me nervous about using WordPress was the degree of modification and tweaking required to get it working and I was concerned about maintanance with WordPress upgrades in future.
These are my notes about the plugins I found. Please keep in mind this was a few months ago and our bilingual definition is not the same as other people’s.
Works and is maintained. Allows easy switching between posts in different languages. Will probably work well with WordPress languages.
However, it does not let you specify different slugs for different languages, which will emphasise one language over the other. It requires you to set a primary language and then just enter translation for the other. This is neat, but doesn’t use WP’s post architecture. Again, neat, but I’m concerned about long term mainantance issues.
Not as well supported as ZDMultilang.
Allows for category translation
Again, not possible to change the slug.
It appears to not be actively maintained and it doesn’t work robustly enough. Nice idea, though.
Looks like it would be good, but not stable at the moment
Doesn’t quite work with the latest WP.
Doesn’t link posts in different languages.
Does nicely allow for posts to all live in the posts directory properly, which of course means you can edit the slugs etc.
Does not allow for different pages properly
Doesn’t maintain the sense of proper bilingualism
Switches text on the page. not what we want.
This is tricky – it holds all translations of the text in the standard fields, and filters output accordingly. Does not allow for different slugs, though.
All in the same entry, which seems nasty to me. Where do the comments live? Etc.
And the others?
I looked briefly, but seriously, at Textpattern, and suspect this would have done the job as well, if not better, than Movable Type. It seems to have plugins designed exactly for this purpose. I didn’t pursue this further because when we build the first version of this site a few years ago, MT was the software of choice and I didn’t want to add learning a new system into the mix, especially as my first investigations with Textpattern revealed quite a few areas of minor frustration that I would have had to address. Better the devil you know. In retrospect, and with the hassles I’ve had with MT, I’m not sure that was the right decision, but there we go.
One of my key concerns was ensuring the templating system was as easy to keep track of as possible. I elected to use one set of templates for both blogs, referencing the files at the same location in MT’s template setup. This removed the risk of making a design change on one side but not the other. I then created a language template which gets included in every page, which sets PHP variables for all the static text on the page, with a version for French and a version for English. As the site gets published to static HTML (in the main), I was not too concerned about adding a lot of PHP language processing overhead for the template, since it only needs to do the work once.
The language file has entries like this:
$en["recententries"] = "Recent blog entries";
$fr["recententries"] = "articles récents";
And the template then uses these like this:
To get the linking between blog posts, I installed the LinkedEntryCustomFields plugin (if
you go this route and use this plugin, you need to use the Pro version of MT, which is free to individual pro bloggers). This is set up to create a linked entry field for each individual entry on both blogs, and Céline has to select the related entry when she enters her new entries.
Here is more detailed implementation info, since it took me a while to gather all this, and it might help someone:
Use the Linked Entries plugin to allow you to create links between entries in different blogs.
Set up a French Version custom field in the English blog and an English Version field for the French blog.
If your Linked Entry is called english version, then you will probably want code like this on the French template.
Best to call your linking tags something like English Version and French Version, rather than using ‘entry’ keyword. This will make it easier when you have to code into templates and use ‘entry’ as a special term to access the entry info.
NOTE: The var name needs ‘entry’ at the end to access the correct link and text. In this example: entrydataenglish_versionentry.
NOTE 2: Some of the spaces are displaying a bit odd in the code snippet. Check the spaces are as they should be if you use this.
I automated the links between the old entries by making guesses based on date of publication and this SQL. If you are in this situation:
Use this sql to extract the blog ids
SELECT date( a.entry_created_on ) , a.entry_id, a.entry_title, b.entry_id, b.entry_title
FROM mt_entry a, mt_entry b
WHERE date( a.entry_created_on ) = date( b.entry_created_on )
AND a.entry_blog_id != b.entry_blog_id
AND a.entry_blog_id =1
AND a.entry_status =2
AND b.entry_status =2
ORDER BY a.entry_created_on
Remove entries where there’s more than one on a date and deal with those separately (keep a list). Create one list for insert for English and one list for insert for French. Insert direct into database in mt_entry_meta table.
set the first id (the english entry id) as entry_meta_entry_id, the french entry id should have heading entry_meta_vchar_idx
add a heading entry_meta_type and put: “field.french_entry” on each line.
When this is tidy, copy the data below the existing data. Swap the ids with each other, and put field.english_entry in the entry_meta_type column.
Then delete the date and title fields and import into the database.
Bells and whistles
The look of the site was nothing to do with me. Andy at origin8 creative did all that and was a pleasure to work with in the implementation phase. The front end coding of the HTML and CSS was also nothing to do with me, with Sarah making that work well for all platforms and thinking about print stylesheets and the suchlike.
The fancy font work is sIFR, which works nicely when it does, but drove us all a bit crazy in terms of documentation and versions of sIFR and so on.
In all, I’m pretty pleased with the end result and will be tidying up the loose ends over the next few days.