Sorry about making this so lengthy. I just want to explain everything clearly and walk you through what I have tried so far to solve this problem.
There is an English and a Spanish version of the website, which hopefully can be toggled between. I have a session variable $_SESSION['language_code'] that I am trying to use to dynamically set the locale and use a different language file.
I should note first off that the session is working properly. I have $_SESSION['language_code']='es_US' hard set for testing, which is echoed at the top of the < body > just so I can see that it is working. I also know that the language files are working, because I can set the defined variable in wp-config define('WPLANG', 'es_US'); and I can see that translations work from my .po/.mo files.
Instead of changing WPLANG, I can also use a filter to get this to work, like so:
function spark_change_language( $locale ){
return 'es_US';
}
add_filter( 'locale', 'spark_change_language' );
or:
function spark_change_language( $locale ){
$locale = 'es_US';
return $locale;
}
add_filter( 'locale', 'spark_change_language' );
I can do all this and see the translations work properly on the website. The problems arise when I try to change the locale dynamically. I can set the $_SESSION variable to a different language code and try to change the locale using that, but for whatever reason the filter function gives some very odd behavior. I'm trying to do this like Setting WPLANG from a plugin or Change locale at runtime?
Here is what I tried first:
function spark_change_language( $locale ){
return $_SESSION['language_code'];
}
add_filter( 'locale', 'spark_change_language' );
That should do it right? Especially if $_SESSION['language_code']='es_US'. Well, in this example it just generates an error: Notice: Undefined variable: _SESSION ...
Alright, so $_SESSION clearly isn't created by the time the filter runs for 'locale'. OR IS IT?
If I add some conditionals, it seems to act like $_SESSION is totally available:
function spark_change_language( $locale ){
if( isset($_SESSION['language_code']) ){
if( $_SESSION['language_code']=='es_US' ){
$locale = 'es_US';
}
}
return $locale;
}
add_filter( 'locale', 'spark_change_language' );
No errors here, but 'es_US' is still not being returned as the language code, or at least the website is not being translated. Now I can already hear you, it's because isset() is returning false since I got that "Undefined variable" notice, so it just skips that part and goes straight to return $locale;
, right? RIGHT?
Well, I added a way to test how far it gets into the conditionals, and the conditionals are returning TRUE. In my header.php, right after the opening < body> tag I have <?php do_action('spark_body_start'); ?>
and I created a function like so:
function test_alert(){ ?>
<script>alert('ohai');</script>
<?php
}
... and I used add_action() in the locale function, like so:
function spark_change_language( $locale ){
if( isset($_SESSION['language_code']) ){
if( $_SESSION['language_code']=='es_US' ){
$locale = 'es_US';
add_action('spark_body_start','test_alert');
}
}
return $locale;
}
add_filter( 'locale', 'spark_change_language' );
Now upon refreshing the page, I get a javascript alert that says "ohai". But wait, that's within the conditionals that check if $_SESSION['language_code'] exists and if it's set to 'es_US'. So since the javascript alert appeared, I know that the add_action() ran, which means $locale = 'es_US';
, which means both of the conditionals were TRUE. So $_SESSION does exist by the time this function runs, even though the previous example gave an undefined variable notice? And the page still doesn't get translated into Spanish.
Alright, well maybe I just can't return a variable, maybe it has to be a string. That's fine, I can write the function like this:
function spark_change_language( $locale ){
if( isset($_SESSION['language_code']) ){
if( $_SESSION['language_code']=='es_US' ){
add_action('spark_body_start','test_alert');
return 'es_US';
}
}
return $locale;
}
add_filter( 'locale', 'spark_change_language' );
Now I refresh the page and the 'ohai' alert appears, so it must get to the return 'es_US';
line. However, the page still isn't translated.
What, then, is actually getting returned by this filter?! Why is $_SESSION defined in conditionals, but not outside of them? Why does the filter work when I set a return value outside of conditionals, but it doesn't work if the return value is set within conditionals?
And for my main problem, is there actually a way to get the language to change dynamically, using the .po/.mo files? We don't want to use Google Translate due to accuracy concerns. A dynamic solution is also preferable since we will end up having over 200 languages.
UPDATE
Alright, I think I figured this out.
I think my problem was that I was using the “standard” way of starting a session in my functions.php:
function myStartSession() {
if(!session_id()) {
session_start();
}
}
add_action('init', 'myStartSession', 1);
I don’t think it made session available “soon enough”.
I followed another example (http://codeblow.com/questions/how-you-can-alter-the-value-wplang-in-wordpress-by-visiting-a/) and reworked it into my wp-config.php file:
if(!session_id()) {
session_start();
}
//First check if the $_GET value is there. This occurs when someone clicks the link to change the language...
if ( isset($_GET['lang']) ){
// Check that it's either 'es_US' (USA Spanish/Espanol) or 'en_US' (USA English), and set the $_SESSION['WPLANG'] accordingly.
if( $_GET['lang']=='es_US' ){
$_SESSION['WPLANG'] = 'es_US';
} elseif( $_GET['lang']=='en_US' ){
$_SESSION['WPLANG'] = 'en_US';
} else {
//Fall back to the default if it's not es_US or en_US.
$_SESSION['WPLANG'] = '';
}
// Once the $_SESSION['WPLANG'] is set, define it's value to WPLANG.
define ('WPLANG', $_SESSION['WPLANG']);
//... otherwise if $_GET wasn't set, check if $_SESSION['WPLANG'] was already set previously...
} elseif( isset($_SESSION['WPLANG']) ){
// If it's es_US or en_US, define WPLANG as such.
if( $_SESSION['WPLANG']=='es_US' || $_SESSION['WPLANG']=='en_US' ){
define ('WPLANG', $_SESSION['WPLANG']);
} else {
//Fall back to the default if it's not es_US or en_US.
define ('WPLANG', '');
}
//... and finally just set WPLANG to a blank string (default value).
} else {
define ('WPLANG', '');
}
So it’s not as nice as using the filters/actions, but it works.