5
\$\begingroup\$

I am using this script to detect the most prefered language by the user.

function Get_Client_Prefered_Language ($getSortedList = false, $acceptedLanguages = false)
{

    if (empty($acceptedLanguages))
        $acceptedLanguages = $_SERVER["HTTP_ACCEPT_LANGUAGE"];

    preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})*)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $acceptedLanguages, $lang_parse);
    $langs = $lang_parse[1];
    $ranks = $lang_parse[4];

        // (create an associative array 'language' => 'preference')
    $lang2pref = array();
    for($i=0; $i<count($langs); $i++)
        $lang2pref[$langs[$i]] = (float) (!empty($ranks[$i]) ? $ranks[$i] : 1);

        // (comparison function for uksort)
    $cmpLangs = function ($a, $b) use ($lang2pref) {
        if ($lang2pref[$a] > $lang2pref[$b])
            return -1;
        elseif ($lang2pref[$a] < $lang2pref[$b])
            return 1;
        elseif (strlen($a) > strlen($b))
            return -1;
        elseif (strlen($a) < strlen($b))
            return 1;
        else
            return 0;
    };

        // sort the languages by prefered language and by the most specific region
    uksort($lang2pref, $cmpLangs);

    if ($getSortedList)
        return $lang2pref;

        // return the first value's key
    reset($lang2pref);
    return key($lang2pref);
}

Example:

print_r(Get_Client_Prefered_Language(true, 'en,en-US,en-AU;q=0.8,fr;q=0.6,en-GB;q=0.4'));

Output:

Array
    (
        [en-US] => 1
        [en] => 1
        [en-AU] => 0.8
        [fr] => 0.6
        [en-GB] => 0.4
    )

Is this reliable? How about performance, this script will get called many times per seconds.

\$\endgroup\$
1
\$\begingroup\$

The following is imho easier to read and should give you best performance.

<?php

/**
 * Get a sorted list of client accepted languages.
 *
 * @staticvar array $examined
 *   Used to cache the sorted list for <var>$accept_languages</var> strings.
 * @param boolean $list [optional]
 *   Whether to get a sorted list or the language with the highest preference, defaults to <code>FALSE</code> and
 *   the language with the highest preference will be returned.
 * @param string $accept_languages [optional]
 *   The client submitted accept languages string in the format defined in RFC 2616.
 * @param string $default [optional]
 *   The default ISO 639-1 alpha-2 language code to use if no other language could be determined, defaults to
 *   <code>"en"</code>.
 * @return array|string
 *   Associative array where the key is the language code or locale and the value the preference if <var>$list</var>
 *   is set to <code>TRUE</code>, otherwise the ISO 639-1 alpha-2 code or locale of the language with the highest
 *   preference.
 */
function get_client_preferred_language($list = false, $accept_languages = null, $default = "en") {
  static $examined = array();

  // Either use the supplied string or use the string from server input, this construct ensures highest performance
  // because a positive check is performed.
  $accept_languages || ($accept_languages = filter_input(INPUT_SERVER, "HTTP_ACCEPT_LANGUAGE", FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH));

  // Only examine this string if we haven't done so in the past.
  if (empty($examined[$accept_languages])) {
    // Prepare default fallback in our cache for this string.
    $examined[$accept_languages] = array($default => 1.0);

    // Only continue if we have a string to examine.
    if ($accept_languages) {
      // Prepare array for collecting the preferred languages.
      $languages = array();

      // Extract all languages from the accept languages string, the format is defined in RFC 2616.
      preg_match_all("/([a-z]+(?:-[a-z]+)?)\s*(?:;\s*q\s*=\s*(1|0?\.[0-9]+))?/i", $accept_languages, $matches);

      // Only go through all extracted languages if we have any.
      if (!empty($matches[1])) {
        foreach ($matches[1] as $delta => $code) {
          // Empty means highest preference.
          $languages[$code] = empty($matches[2][$delta]) ? 1.0 : (float) $matches[2][$delta];
        }

        // Sort the accepted languages by preference, if sorting fails use default.
        if (arsort($languages, SORT_NUMERIC) === true) {
          $examined[$accept_languages] = $languages;
        }
      }
    }
  }

  return $list ? $examined[$accept_languages] : array_values($examined[$accept_languages])[0];
}


// ----------------------------------------------------------------------------------------------------------------- Test


function get_client_preferred_language_test() {
  assert(array(
    "en-US" => 1.0,
    "en"    => 1.0,
    "en-AU" => 0.8,
    "fr"    => 0.6,
    "en-GB" => 0.4,
  ) === get_client_preferred_language(true, "en,en-US,en-AU;q=0.8,fr;q=0.6,en-GB;q=0.4"));
}

get_client_preferred_language_test();
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.