I’ve been testing several multilingual wordpress plugins (xLanguage, qTranslate and Gengo), before I stopped on zdmultilang.
This fit what I was looking for: the ability to manage translated pages / articles with a special relationship with the original. Moreover, in order to best meet the plugin spirit, I was looking for something that leaves the platform directly operational in the event of cancellation.
For me, it only lacked a multilingual widget management. So I tried to add this functionality as I describe here.
The standardization of widgets
WordPress always uses the same pattern to store widgets data: it is a parameter name / value hash array that is stored in the wp_option
table through the get_option
and update_option
functions. Also, in the first instance, I modified the zdmultilang language switcher widget to comply with this standard.
On the road to options by language
Zdmultilang already manages language options: blogname
and blogdescription
in the wp_zd_ml_langs
table. However, if we want to manage widgets by language we will have to change the architecture. My choice was to create the wp_zd_ml_options
table whose structure is based on the wp_options
table where I added the LanguageID
field. This table contains all the options I wanted to keep in a specific language. So, I put there blogname
and blogdescription
as this place now seemed legitimate.
How to build dynamic hooks for our widgets?
As said before, the parameters of the widgets are stored in the wp_options
table. So, in order to stay as closed as possible to the WordPress architecture, it seemed appropriate the use the pre_option_...
and pre_update_option_...
hooks (where ...
is the name of the widget) provided by the get_option
and update_option
functions. The use of the “pre” hook is nice as it exits the initial get_option
or update_option
functions almost immediately. On the other hand, this requires to completly rewrite the initiale functions inside the plugin. For consistency, most of the functions regarding options have been rewritten: get_option, update_option, add_option, load_alloptions
. It always has been done using the initial WordPress functions to take advantage of caching tools as alloption
and notoption
avoiding to much queries to the database. This is especially useful in our case. Indeed, widgets do not always have parameters and there is no way we can get this information. So we’re going to register as much shortcut filters as widgets based on the only information we have: the $wp_registered_widgets
array. Thus, if a widget does not have a parameter, we should exit very quickly thanks to notoption
. That way the database shouldn’t be stressed.
Then, we still have to register our new zd_get_option
and zd_update_option
functions to the hooks identified above. In order to accomplish this, we get inside the $wp_registered_widgets
hash array and for each widget we build a dynamic function that we register to the hook. A sample of code :
foreach($wp_registered_widgets as $widget_name => $widget) {
$funcname = 'zd_multilang_get_option_'.$widget['classname'];
$function = "function $funcname".'() {
$option=str_replace("zd_multilang_get_option_","",__FUNCTION__);
return zd_multilang_get_option($option);
}');
eval($function);
add_filter('pre_option_'.$widget['classname'], $funcname);
}
Here is an exemple with the widget-text :
Imagine get_option('widget_text');
is called. Its first action is $pre = apply_filters( 'pre_option_widget_text';
. As widget-text is a widget we did add_filter('pre_option_widget_text', 'zd_multilang_get_option_widget_text');
where zd_multilang_get_option_widget_text
return zd_multilang_get_option('widget_text');
. So call to get_option('widget_text')
will lead to zd_multilang_get_option('widget_text')
.
Once all those shortcut filters are installed, we can recall the wp_widgets_init()
which resets all widgets. But this time, it will look for the parameters in our wp_zd_ml_options
table through our zd_multilang_get_option
function. That sounds good: it was our goal.
How to store sidebars by language?
The sidebars are registered by the theme. Here also, we have no idea of their number. So we are going to keep an additional option equivalent to sidebars_widgets
option that contains information on the content of sidebars. Our option will contain nb of sidebars in the theme x nb of languages dealt by zd_multilangue elements. Then we will rebuild on the fly the result WordPress expects to find based on the context (simple blog reading or administering widgets through the admin screen).
How users will administer widgets in each language?
In the beginning I wanted to make a setup screen for widgets a bit like zdmultilang manages categories, tags … However, the management of widgets by WordPress (wp-adminwidgets.php) is not obvious ( work in case of javascript deactivation, compute modified data if ever before displaying anything …). Also, I finally resigned myself to add a language selector to the initial widgets admin page. Again, the hooks offered by WordPress don’t help so much and make the code non trivial. But, at last, it works.
How to test those changes to the plugin?
You can access my code by following this link: zd_multilang with support for multilingual widgets but it comes with the status: it works for me. In addition, you may lose your blogname
and blogdescription
data by language if you decide to revert to an earlier version.
Don’t forget to leave your comments in case of test.