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; }
}