Plans for Basic Bilingual [en]

[fr] Projets de développement pour le plugin WordPress Basic Bilingual, qui rend ce blog bilingue.

Here are the next improvements I want to make to the WordPress plugin Basic Bilingual. Considering my coding skills, they will happen slowly, so feel free to lend a hand if you think you can.

  1. Move the language definition to the admin screen. There’s already a screen and an option there, so it’s a simple case of copying and modifying code around to create options for language 1 and language 2, and create a simple function to retrieve the values at the beginning of the plugin.
  2. Allow WordPress search to access the other-excerpt field. The Keyword Search in Plugin Table example in Codex is close enough to what I’d like, only it would need to search in the postmeta table instead of a custom plugin table.
  3. Here’s the big one. Append a language code to any WordPress URL (except permalinks) to filter out posts from the other language. Ideally, would display posts in the language and also the other-excerpts of posts in the other language, with different formatting (smaller title font to distinguish them from full posts written in the desired language). Am reading up on wp-rewrite, permalinks for custom archives, WordPress queries and custom queries. I feel I’m onto something, but I also feel just a little bit out of my depth.

Similar Posts:

Two Plugin Updates: Basic Bilingual 0.32 and Language Linker 0.2 [en]

[fr] Je me suis levée à l'aube pour aller faire la nouille sur RSR1 et prendre un p'tit déj improvisé chez une ancienne copine d'uni. Ensuite, j'ai passé la journée les mains dans le PHP, ce qui veut dire que je n'ai beaucoup blogué, mais que j'ai mis à jour deux plugins: Basic Bilingual, qui permet de tenir sans peine un blog "bilingue" comme celui-ci (c'est ce qui me permet de rédiger et d'afficher ce petit extrait en français) et Bunny's Language Linker, très utile pour afficher des liens entre pages correspondantes des différentes traductions d'un site.

After waking up at an ungodly hour this New Year’s Day (for a [live radio appearance](http://info.rsr.ch/fr/rsr.html?programId=110451&bcItemName=le_grand_8&rubricId=8888&contentDisplay=last_five&siteSect=1000#mardi) and impromptu breakfast at a uni friend’s home nearby) I spent the rest of my day elbows deep in PHP code. As a result, I haven’t written the half-dozen of posts that have been sitting in my drafts list over Christmas, but I have updated two plugins — an old one, and a new-born.

**[Basic Bilingual](http://wordpress.org/extend/plugins/basic-bilingual/) 0.32**

[Download](/code/basic-bilingual.tgz) | [zip](/code/basic-bilingual.zip) | [.phps](/code/basic-bilingual.phps)

This release fixes the [disappearing excerpts problem](http://climbtothestars.org/archives/2007/12/18/nasty-problem-with-basic-bilingual-plugin/) (was fixed in 0.31 actually, but I never announced it) and replaces the ugly “language box” floating somewhere near the top of the post admin page by a pretty DBX (let me know what it stands for) box in the sidebar:

Basic Bilingual got a dbx box for the new year!

**[Bunny’s Language Linker](http://wordpress.org/extend/plugins/bunnys-language-linker/) 0.2**

[Download](/code/language-linker.tgz) | [zip](/code/language-linker.zip) | [.phps](/code/language-linker.phps)

(I always want to call it “Language Links”, which was the initial name I chose — still not sure I was right to change.) Anyway, this version is pretty exciting, as it does something I’ve been thinking of for a while: it puts the link to the other localized versions of the page you’re viewing in the menu bar if you’re using a Sandbox-based theme:

Language Linker link in the menu bar!

Otherwise, it puts it at the end of the page in its own div (you can style it the way you wish). I’m not saying this is the best, final solution, but I think it’s headed in the right direction.

Similar Posts:

Browser Language Detection and Redirection [en]

[fr] Une explication de la méthode que j'ai suivie pour que http://stephanie-booth.com redirige le visiteur soit vers la version anglaise du site, soit la française, en fonction des préférences linguistiques définies dans son navigateur.

**Update, 29.12.2007: scroll to the bottom of this post for a more straightforward solution, using Multiviews.**

I’ve been working on [stephanie-booth.com](http://stephanie-booth.com) today. One of my objectives is the add an [English version](http://stephanie-booth.com/en/) to the previously [French-only site](http://stephanie-booth.com/fr/).

I’m doing this by using two separate installations of WordPress. The content in both languages should be roughly equivalent, and I’ll write a WordPress plugin which allows to “automate” the process of linking back and forth from equivalent content in different languages.

What I did today is solve a problem I’ve been wanting to attack for some time now: use people’s browser settings to direct them to the “correct” language for the site. Here is what I learnt in the process, and how I did it. It’s certainly not the most elegant way to do things, so let me know if you have a better solution by using the comments below.

First, what I needed to know was that the browser language preferences are sent in the HTTP_ACCEPT_LANGUAGE header (HTTP header). First, I thought of [capturing the information through PHP](http://www.webdeveloper.com/forum/showpost.php?s=d989341270ceae8820a3bc1c6273dc9e&p=217863&postcount=2), but I discovered that Apache (logical, if you think of it) could handle it directly.

[This tutorial was useful in getting me started](http://www.ibm.com/developerworks/library/wa-apac.html), though I think it references an older version of Apache. Out of the horses mouth, [Apache content negotiation](http://httpd.apache.org/docs/2.0/content-negotiation.html) had the final information I needed.

I’ll first explain the brief attempt I did with Multiviews (because it can come in handy) before going through the setup I currently have.

### Multiviews

In this example, you request a file, e.g. test.html which doesn’t physically exist, and Apache uses either test.html.en or test.html.fr depending on your language preferences. You’ll still see test.html in your browser bar, though.

To do this, add the line:

Options +Multiviews

to your .htaccess file. Create the files test.html.en and test.html.fr with sample text (“English” and “French” will do if you’re just trying it out).

Then, request the file test.html in your browser. You should see the test content of the file corresponding to your language settings appear. Change your browser language prefs and reload to see what happens.

This is pretty neat, but it forces you to open a file — and I wanted / to redirect either to /en/ or to /fr/.

It’s explained pretty well in [this tutorial I already linked to](http://www.ibm.com/developerworks/library/wa-apac.html), and [this page has some useful information](http://unix.org.ua/orelly/linux/apache/ch06_03.htm) too.

### Type maps

I used a type map and some PHP redirection magic to achieve my aim. A type map is not limited to languages, but this is what we’re going to use it for here. It’s a text file which you can name menu.var for example. In that case, you need to add the following line to your .htaccess so that the file is dealth with as a type map:

AddHandler type-map .var

Here is the content of my type-map, which I named menu.var:

URI: en.php
Content-Type: text/html
Content-Language: en, en-us, en-gb

URI: fr.php
Content-Type: text/html
Content-Language: fr, fr-ch, fr-qc

Based on my tests, I concluded that the value for URI in the type map cannot be a directory, so I used a little workaround. This means that if you load menu.var in the browser, Apache will serve either en.php or fr.php depending on the content-language the browser accepts, and these two PHP files redirect to the correct URL of the localized sites. Here is what en.php looks like:

And fr.php, logically:

Just in case somebody came by with a browser providing neither English nor French in the HTTP_ACCEPT_LANGUAGE header, I added this line to my .htaccess to catch [any 406 errors (“not acceptable”)](http://www.checkupdown.com/status/E406.html):

ErrorDocument 406 /en.php

So, if something goes wrong, we’re redirected to the English version of the site.

The last thing that needs to be done is to have menu.var (the type map) load automatically when we go to stephanie-booth.com. I first tried by adding a DirectoryIndex directive to .htaccess, but that messed up the use of index.php as the normal index file. Here’s the line for safe-keeping, if you ever need it in other circumstances, or if you want to try:

DirectoryIndex menu.var

Anyway, I used another PHP workaround. I created an index.php file with the following content:

And there we are!

### Accepted language priority and regional flavours

In my browser settings, I’ve used en-GB and fr-CH to indicate that I prefer British English and Swiss French. Unfortunately, the header matching is strict. So if the order of your languages is “en-GB, fr-CH, fr, en” you will be shown the French page (en-GB and fr-CH are ignored, and fr comes before en). It’s all explained in the Apache documentation:

> The server will also attempt to match language-subsets when no other match can be found. For example, if a client requests documents with the language en-GB for British English, the server is not normally allowed by the HTTP/1.1 standard to match that against a document that is marked as simply en. (Note that it is almost surely a configuration error to include en-GB and not en in the Accept-Language header, since it is very unlikely that a reader understands British English, but doesn’t understand English in general. Unfortunately, many current clients have default configurations that resemble this.) However, if no other language match is possible and the server is about to return a “No Acceptable Variants” error or fallback to the LanguagePriority, the server will ignore the subset specification and match en-GB against en documents. Implicitly, Apache will add the parent language to the client’s acceptable language list with a very low quality value. But note that if the client requests “en-GB; q=0.9, fr; q=0.8”, and the server has documents designated “en” and “fr”, then the “fr” document will be returned. This is necessary to maintain compliance with the HTTP/1.1 specification and to work effectively with properly configured clients.

Apache, Content Negotiation

This means that I added regional language codes to the type map (“fr, fr-ch, fr-qc”) and also that I changed the order of my language preferences in Firefox, making sure that all variations of one language were grouped together, in the order in which I prefer them:

Language Prefs in Firefox

### Catching old (now invalid) URLs

There are lots of incoming links to pages of the French site, where it used to live — at the web root. For example, the contact page address used to be http://stephanie-booth.com/contact, but it is now http://stephanie-booth.com/fr/contact. I could write a whole list of permanent redirects in my .htaccess file, but this is simpler. I just copied and modified the rewrite rules that WordPress provides, to make sure that the correct blog installation does something useful with those old URLs (**bold** is my modification):

# BEGIN WordPress

RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . **/fr**/index.php [L]

# END WordPress

In this way, as you can check, [http://stephanie-booth.com/contact](http://stephanie-booth.com/contact) is not broken.

### Next steps

My next mission is to write a small plugin which I will install on both WordPress sites (I’ve got to write it for a client too, so double benefit). This plugin will do the following:

– add a field to the write/edit post field in which to type the post slug of the correponding page/post in the other language *(e.g. “particuliers” in French will be “individuals” in English)
– add a link to each post pointing to the equivalent page in the other language

It’s pretty basic, but it beats manual links, and remains very simple. (I like simple.)

As I said, if you have a better (simpler!) way of doing all this, please send it my way.

### A simpler solution **[Added 29.12.2007]**

For each language, create a file named index.php.lg where “lg” is the language code. For French, you would create index.php.fr with the following content:

Repeat for each language available.

**Do not** put an index.php file in your root directory, just the index.php.lg files.

Add the two following lines to your .htaccess:

Options +Multiviews
ErrorDocument 406 /fr/

…assuming French is the default language you want your site to show up in if your visitor’s browser doesn’t accept any of the languages you provide your site in.

You’re done!

Similar Posts:

Invalid argument supplied for foreach() in wp-capabilities.php: Case Cracked! [en]

[fr] Le problème avec wp-capabilities.php qui fait qu'on peut se retrouver "exfermé" (enfermé dehors) de son blog WordPress (typiquement en cas de changement de serveur) semble avoir sa source dans le contenu du champ wp_user_roles dans la table wp_options. En particulier, pour la version française, "Abonné" est un rôle d'utilisateur, et en cas de problèmes d'encodage MySQL, le caractère accentué sera corrompu, causant ainsi l'erreur.

Il suffit de remplacer le caractère fautif dans PhpMyAdmin, et on retrouve l'accès à son blog. Bon, reste ensuite à régler les questions d'encodage... mais c'est déjà ça!

Finally. At last. Endlich. Enfin.

Once more, while trying to transfer a WordPress installation from one server to another, I found myself facing the dreaded problem which locks me out of my WordPress install with a rather cryptic message:

Warning: Invalid argument supplied for foreach() in /home/user/wp/wp-includes/capabilities.php on line 31

(Your lineage may vary.)

What happens is that WordPress cannot read user roles, and therefore, even though your password is accepted, you get a message telling you that you’re not welcome in the wp-admin section:

Vous n’avez pas les droits suffisants pour accéder à cette page.

Or, in English:

You do not have sufficient permissions to access this page.

A quick search on the WordPress forums told me that I was [not alone in my fight with wp-capabilities.php](http://wordpress.org/search/wp-capabilities.php?forums=1), but that many problems had not been resolved, and more importantly, that suggested solutions often did not work for everyone.

I’ve bumped into this problem a couple of times before, and I knew that it was linked to encoding problems in the database. (I’ve had my share of encoding problems: [once](http://climbtothestars.org/archives/2004/07/18/converting-mysql-database-contents-to-utf-8/), [twice](http://climbtothestars.org/archives/2005/02/19/problemes-dencodage-mysql/), [thrice](http://climbtothestars.org/archives/2006/07/12/finally-out-of-mysql-encoding-hell/) — “once” being on of the most-visited posts on this blog, by the way, proof if needed that [I’m not alone with mysql encoding issues](http://wordpress.org/search/mysql+encoding?forums=1) either.)

I’ll leave the detailed resolution of how to avoid/cure the MySQL problems later (adding
mysql_query("SET NAMES 'utf8'");
to wp-db.php as detailed in [this thread](http://wordpress.org/support/topic/55282?replies=7#post-311649), and as [zedrdave](http://unknowngenius.com/blog/) had already previously told me to do — should have listened! — should prevent them). So anyway, adding that line to my *working* WordPress install showed me that the problem was not so much in the database dumping process than in the way WordPress itself interacted with the database, because the dreaded wp-capabilities.php problem suddenly appeared on the original blog.

Now, this is where I got lucky. Browsing quickly through the first dozen or so of [forum threads about wp-capability.php problems](http://wordpress.org/search/wp-capabilities.php?forums=1), [this response](http://wordpress.org/support/topic/67796?replies=18#post-392812) caught my eye. It indicated that the source of the problem was the content of the wp_user_roles field (your prefix may vary). In this case, it had been split on more than one line.

I headed for the database, looked at the field, and didn’t see anything abnormal about it at first. All on one line, no weird characters… just before giving up, I moved the horizontal scrollbar to the end of the line, and there — **Eurêka!** I saw it.

Abonné

“Contributor”, in French, is “abonné”, with an accent. Accent which got horribly mangled by the MySQL problems which I’ll strive to resolve shorty. Mangled character which caused the foreach() loop to break in wp-capabilities.php, which caused the capabilities to not be loaded, which caused me to be locked out of my blog.

So, in summary: if you’re locked out of your blog and get a warning/error about wp-capabilities and some invalid foreach() loop thingy, head for PhpMyAdmin, and look carefully through the wp_user_roles field in the wp_options table. If it’s split over two or more lines, or contains funky characters, you have probably found the source of your problem.

Good luck!

Similar Posts:

Michael Hampton is My Hero of the Day [en]

[fr] En principe, les problèmes de serveur sont résolus. Retour à la normale aussi vite que j'arrive à transférer les données avec la connection wifi très approximative que nous avons ici.

[Michael Hampton, also known as io_error](http://www.homelandstupidity.us/) just saved my life today by solving the [encoding problem on my new hosting](http://climbtothestars.org/archives/2006/12/11/wordpress-dreamhost-wifi-arghl/). It seems something went wrong when I imported my SQL dumps into the new database. Solving the encoding issue seems to have solved the “can log into admin but can’t do anything” WordPress issue (if someone can explain why, I’d be interested).

And [danah](http://www.zephoria.org/thoughts/) is my heroine of the day, because after a morning of [politicians](http://www.onemanandhisblog.com/archives/2006/12/le_web_3_hijack.html) and [WiFi fighting](http://annedominique.wordpress.com/2006/12/12/leweb3-day-1-joies-et-frustration/), it was nice to hear an [interesting talk](http://steph.wordpress.com/2006/12/12/le-web-3-danah-boyd/).

Similar Posts:

Project With Technorati API [en]

[fr] J'ai un projet de répertoire de blogs suisses construit sur Technorati. Je cherche un développeur pour la partie PHP/MySQL/TechnoratiAPI et un designer/graphiste pour l'interface web. C'est un projet non lucratif mais qui peut rapporter visibilité et célébrité à  ceux qui y prendront part!

You might have heard that [blog.ch is for sale](http://blog.ch/blog/archives/2006/03/04/blogch-zu-verkaufen/ “Announcement, in German.”). As we all could suspect, Matt unfortunately and understandably doesn’t have time for [SwissBlogs](http://swissblogs.com/ “The oldest Swiss blog directory.”) and handed it back to me a few weeks ago.

I have plans for SwissBlogs, which involve a complete make-over and using “what’s out there” as much as possible. I think it’s silly to spend energy re-inventing the wheel. I won’t go into details here, but my idea involves building on all the data already available through Technorati. [I mentioned this idea on the bloggerbosse list](http://groups.yahoo.com/group/bloggerbosse/message/117) but got the feeling everybody was just interested in building something from the ground up.

I have the ideas, but not the time (and my limited skills would make the necessary time long) to implement it. I’m looking for a developer who could do the PHP, MySQL and Technorati API stuff, and a designer to take care of the web part. The directory I have in mind would be a public service, and would be **strictly non-profit**. I want people who are in the directory to not worry about their data being sold or distributed as part of a list for marketing purposes.

If you’re interested in participating, and have **time and energy** to spend on the project, drop me a line or leave a comment. Credit will be given, of course, which means fame awaits you if everything goes as planned.

Similar Posts:

PHP Dev Wanted [en]

[fr] Blog.ch (et Swissblogs -- plus de détails suivront) cherche un programmeur PHP.

Want a bit of fame in the Swiss blogosphere? Head over to Matt’s announcement on blog.ch for the details.

Considering the last discussions Matt and I have been having, consider it an announcement for Swissblogs.com too 😉

Similar Posts:

Plugin Idea: Weighted Tags by Category [en]

Another plugin added to my wishlist: one which would display a weighted list of “related” tags on each category page.

[fr] Encore une idée pour un plugin WordPress, qui permettrait d'afficher sur chaque page de "catégorie" une liste des tags courants utilisés pour étiqueter les billets de cette catégorie. Il y a des volontaires?

I’ve been doing some cleaning up around here — mainly the sidebar, which now tries to provide links which make sense depending on which page we are looking at. I’ve also added navigation between monthly archive pages, and I’m working on my archive index a little.

I thought Weighted Categories looked like a nice plugin, particularly as I have (too) many categories. You can now see it in action for Climb to the Stars.

These last weeks, I’ve been getting into quite a few conversations about tags and categories (and keywords). “Will tags replace categories?” seems to be the big question. I don’t think they will. They do not serve the same purpose. I’m less sure about keywords: since I’ve been tagging all my posts, I’ve stopped choosing keywords for them too. But I’m clearly aware that I do not choose my tags in the same way as I chose my keywords — so it would probably make sense to keep them both (if keywords are actually useful, and I keep getting contradictory information about that).

So, to my plugin idea: for each category page, I’d love to be able to display a list of “tags used for posts in this category”. This list could be weighted, or not. It would be pretty simple to retrieve from the database. Off the top of my head and probably with syntax errors, something like: select * from post_meta where key=’tags’ and post_id in (select post_id from table_categories where cat_id = ‘our current category’). Whee, that must be the ugliest wannabe-SQL ever written — but you get the idea, don’t you? Then, the list of tags would be parsed and weighted as are categories in this plugin.

Anybody want to take up the challenge? Actually, this might be best as an extra feature for Bunny’s Technorati Tags, as it wouldn’t be much use without tags. Let me know if you’d like to do it (credit, of course, will be given) — otherwise I’ll probably end up doing it myself at some point.

Thanks for listening to me think out loud, and my apologies to my non-techie readers — I know my weblog hasn’t been very interesting for you to read lately!

Similar Posts:

Basic Bilingual Plugin [en]

This is a simple plugin which wraps together my bilingual hacks to make day-to-day posting less of a hassle.

[fr] Ce plugin pour WordPress regroupe les hacks que j'utilise depuis un moment déjà pour gérer le bilinguisme de mon weblog. Il permet d'afficher un sommaire de chaque billet dans "l'autre langue" et d'appliquer un formattage par langue via l'attribut lang.

Plus de détails sur la page officielle du plugin.

**Update 01.02.2007:** This plugin broke badly with WordPress 2.1, but [has now been (hopefully) updated](http://climbtothestars.org/archives/2007/02/01/basic-bilingual-and-bunnys-technorati-tags-plugins-updated-for-wordpress-21/). The wiki page on wp-plugins.org is frozen and may not be up-to-date anymore. [Download here.](/code/basic-bilingual.zip)

This post is the test run for my Basic Bilingual plugin.

It doesn’t add much functionality to what I already have through my hacks, but it’s cleaner from a code point of view, and it’s portable — you can use it too if you wish.

Be patient if the wiki page isn’t exactly up-to-date. It will be shortly — and the plugin will be available through the Plugin Manager as soon as I’ve made sure it’s functional enough (ie, when I press publish and hell doesn’t break loose).

This plugin basically allows you to do what you can see on this weblog: add lang attributes to your posts, excerpts in “the other language”, and localize the date. It also creates permanent fields in the admin pages for entering the language and “other language excerpt” easier.

I’d like to emphasize that this plugin is very simple. It is in no way a replacement of any sort for the larger-scale multilingual efforts going on these days. I wanted to get my code cleaned up and my hacks back in the admin interface (I lost them when I upgraded WP), and I’m making the result public.

Similar Posts:

Simple Technorati Tags Plugin for WordPress [en]

A simple plugin for WordPress which lets you display tags (or keywords) in your posts. Tags are stored in a Custom Field (no database modification required).

[fr] Un plugin simple pour WordPress qui vous permet de garnir vos billets de 'tags' (étiquettes) qui seront compris et interprétés par Technorati. Ce plugin ne modifie pas la base de données, et permet l'affichage de tags stockés dans un Custom Field nommé "tags", sous forme de liste de mots séparés par des espaces. Il est aussi possible d'afficher ainsi des mots-clés se trouvant dans un Custom Field nommé "keywords".

Update 31.01.07: The wp-plugins.org wiki isn’t allowing edits anymore. The plugin has been [updated to version 0.6](http://climbtothestars.org/archives/2007/02/01/basic-bilingual-and-bunnys-technorati-tags-plugins-updated-for-wordpress-21/), compatible with WordPress 2.1, by [Sudar](http://sudarmuthu.com/blog/2007/01/31/wordpress-21-and-custom-field-plugin-gotcha.html). Get [Bunny Tags](/code/bunny-tags.zip).

Update 21.01.05: Please see Bunny’s Technorati Tags on the wp-plugins.org wiki for information about this plugin!

As wp-plugins.org isn’t letting me in yet, here is a little post to announce my first real live plugin for WordPress (one can’t exactly say that Batch Categories is a proper plugin).

Bunny’s Technorati Tags (zip, phps) provides you with a template function that you can use to easily display Technorati tags for your posts. The tags are stored in a Custom Field, so this plugin does not require any modifications of the database structure. The plugin has not been thoroughly tested on different versions, though it works fine on my recent 1.5 nightly. As it is really very basic, I don’t believe you’ll run into any major compatibility issues on other versions (do keep me informed, though).

Once you’ve installed and activated the plugin (manually or with the Plugin Manager, enter a space-separated list of tags in a Custom Field named “tags”, and place the following code in your template where you want the list of tags to appear:

<php the_bunny_tags(); ?>

If you’re like me and you’ve been painfully entering comma-separated keywords for your posts using a Custom Field of same name, you can use the following line to use these existing keywords as tags:

<php the_bunny_keyword_tags(); ?>

Update 21.01.05: by setting $bunny_strict to false (see SETTINGS at the top of the plugin code) you will display keywords as tags for posts which do not have tags.

This might not be the greatest idea, as I believe tagging and choosing keywords is a different process, but it makes me happy for the moment, so I thought I’d share this possibility with you too.

Future development of the plugin includes adding a text input to the “Create New Post” form for easy tagging. (Coming as soon as I can figure out how to do it.)Done!

Plugin available on wp-plugins.net and wp-plugins.org.

Update 21:40 Who’dathunkit? Version 0.2 is out already, with a nice little “Tags” field in the post editing form. the_bunny_keyword_tags() has been revised to display keywords only when no tags are present. All the tagging pleasure is yours to take!

Update 21.01.05 Version 0.3 is out. Upgrade strongly recommended, or your tags won’t be indexed correctly by Technorati.

Similar Posts: