Further to my issue http://drupal.org/node/1261034 requesting a hook_locale_alter() and Gábor's suggestion in response, I've had a go at working out how to do what he suggests.
After much head-scratching, this is what I and a colleague have come up with. It is complex because the array that goes in $conf['locale_custom_strings_$langcode'] is a multi-dimensional array. I haven't had a chance to test it in D7 yet, but with some test code instead of the call to locale() it worked. I would expect to add a bootstrap function that adds a locale_custom_strings entry to $conf for each language on the system that has an allocated fallback language.
A function like this is heavily dependent on internal implementations and so I had to weigh up whether to replicate the whole of locale() (and have to watch for bug/security updates to that) or whether it was OK to make reference to drupal_static('locale') to see if it is set to TRUE, meaning 'untranslated'. I think fewer kittens die if I choose the latter approach, as shown below. What I want to know is: do others agree, or is this really really bad? Is there a better way to do this?
class localeWithFallback implements ArrayAccess {
private $langcode;
private $cache = array();
public function __construct($langcode) {
$this->langcode = $langcode;
$this->cache = array();
}
public function offsetExists($offset)
{
return (isset($this->cache[$offset]));
}
public function &offsetGet($offset)
{
if (!isset($this->cache[$offset])) {
$this->cache[$offset] = new localeContextWithFallback($this->langcode, $offset);
}
return $this->cache[$offset];
}
public function offsetSet($offset, $value) { return; }
public function offsetUnset($offset) { return; }
}
class localeContextWthFallback implements ArrayAccess {
private $first_language = '';
private $second_language = '';
private $context = '';
public function __construct($langcode, $context) {
$this->first_language = $langcode;
// Insert function to find fallback language here.
$this->second_language = fallback_for($langcode);
$this->context = $context;
}
public function offsetGet($offset) {
$locale_t = &drupal_static('locale');
$translation = locale($offset, $this->context, $this->first_language);
// Check whether string returned was a translation or the untranslated original.
// Have to check locale()'s static variable, because just comparing the strings
// might be misleading. (Stupid example: (en) village == (fr) village.)
if ($locale_t[$this->first_language, $this->context, $offset] === TRUE) {
$translation = locale($offset, $this->context, $this->second_language);
}
return $translation;
}
public function offsetExists($offset) {
$locale_t = &drupal_static('locale');
$translation = locale($offset, $this->context, $this->first_language);
if ($locale_t[$this->first_language, $this->context, $offset] !== TRUE) {
return TRUE;
}
else {
$translation = locale($offset, $this->context, $this->second_language);
return ($locale_t[$this->second_language, $this->context, $offset] !== TRUE);
}
}
public function offsetSet($offset, $value) { return; }
public function offsetUnset($offset) { return; }
}