views:

19

answers:

1

I'm trying to create a multibyte safe title => url string converter, however I've run into the problem of not knowing how to allow legal asian (and other) characters in the url when removing others. This is the function set at the moment.

public static function convertAccentedCharacters($string)
{                            
    $table = array(
            'Œ'=>'CE', 'œ'=>'ce', '¥'=>'Y', 'Ÿ'=>'Y', 'µ'=>'u', 'ü'=>'u', 
            'Š'=>'S', 'š'=>'s', 'Đ'=>'Dj', 'đ'=>'dj', 'Ž'=>'Z', 'ž'=>'z', 'Č'=>'C', 'č'=>'c', 'Ć'=>'C', 'ć'=>'c',
            'À'=>'A', 'Á'=>'A', 'Â'=>'A', 'Ã'=>'A', 'Ä'=>'A', 'Å'=>'A', 'Æ'=>'A', 'Ç'=>'C', 'È'=>'E', 'É'=>'E',
            'Ê'=>'E', 'Ë'=>'E', 'Ì'=>'I', 'Í'=>'I', 'Î'=>'I', 'Ï'=>'I', 'Ñ'=>'N', 'Ò'=>'O', 'Ó'=>'O', 'Ô'=>'O',
            'Õ'=>'O', 'Ö'=>'O', 'Ø'=>'O', 'Ù'=>'U', 'Ú'=>'U', 'Û'=>'U', 'Ü'=>'U', 'Ý'=>'Y', 'Þ'=>'B', 'ß'=>'Ss',
            'à'=>'a', 'á'=>'a', 'â'=>'a', 'ã'=>'a', 'ä'=>'a', 'å'=>'a', 'æ'=>'a', 'ç'=>'c', 'è'=>'e', 'é'=>'e',
            'ê'=>'e', 'ë'=>'e', 'ì'=>'i', 'í'=>'i', 'î'=>'i', 'ï'=>'i', 'ð'=>'o', 'ñ'=>'n', 'ò'=>'o', 'ó'=>'o',
            'ô'=>'o', 'õ'=>'o', 'ö'=>'o', 'ø'=>'o', 'ù'=>'u', 'ú'=>'u', 'û'=>'u', 'ý'=>'y', 'ý'=>'y', 'þ'=>'b',
            'ÿ'=>'y', 'Ŕ'=>'R', 'ŕ'=>'r',
        );

    return str_replace(array_keys($table), array_values($table), $string); 
}

public static function convertStringToSafeCharacters($string)
{                 
    $string = self::convertAccentedCharacters($string);
    if (function_exists('mb_convert_encoding') === true)
    {
        $string = mb_convert_encoding($string, 'UTF-8', 'auto'); 
    }
    else if(function_exists('iconv') === true && ($iconvstr = @iconv('', 'UTF-8', $string)) !== false)
    {
        $string = $iconvstr;
    }
    else
    {
        $string = utf8_decode($string);
    }
    return strip_tags(trim($string));
}

public static function convertToUrlsafe($string, $options=array())
{    
    if(isset($options['separator']) === false || $options['separator'] === false)
    {
        global $_SITE;
        if(strpos($_SITE->urlsafe_format, 'underscore') !== false)
        {
            $options['separator'] = '_';
        }
        else
        {
            $options['separator'] = '-';
        }
    }
    if(isset($options['case']) === false || $options['case'] === false)
    {
        global $_SITE;
        $format = substr($_SITE->urlsafe_format, 0, 5);
        if($format === 'lower')
        {
            $options['case'] = 'lower';
        }
        else if($format === 'upper')  
        {
            $options['case'] = 'upper';
        }
        else
        {
            $options['case'] = 'mixed';
        }
    } 

    $string = self::convertStringToSafeCharacters($string);

    $separator_in_use = $options['separator'];
    $separtor_convert = $options['separator'] == '-' ? '_' : '-';
    $preg_changes = array(
//              convert other seperators into the seperator being used
        '/\\'.$separtor_convert.'/i'            => $separator_in_use, 
//              remove any none legal chars
        '/[^\-\_a-zA-Z0-9&\s]/i'                => '',
        '/\&\#\d+?\;/'                          => '', 
        '/\&\S+?\;/'                            => '',
//              replace spaces with separator
        '/\s+/'                                 => $separator_in_use,
//              replace amersands with and
        '/\&/'                                  => 'and',
//              now finally remove any last chars
        '/[^A-Za-z0-9\/'.$separator_in_use.']/' => '',
//              remove any double paddinging separators, ie ----
        '/'.$separator_in_use.'+/'              => $separator_in_use, 
//              remove separator padding from start and end of string
        '/'.$separator_in_use.'$/'              => '',
        '/^'.$separator_in_use.'/'              => ''
    );
    $string = preg_replace(array_keys($preg_changes), array_values($preg_changes), $string);
    switch($options['case'])
    {
        case 'lower' :
            $string = strtolower($string);
            break;
        case 'upper' :
            $string = strtoupper($string);
            break;
        case 'mixed' :
            // do nothing
    }
    return $string;
}

Please bear in mind that there is some CMS related functionality in there and that the functions are actually part of a class. The main function call would be

convertToUrlsafe($string);

And I'm pretty sure the problem lies with the regex replaces in that function itself but I'm not quite sure what to change without breaking some existing functionality.

I want to be able to take the following titles and have them return urlsafe titles.

"The Very First Blog!" => "The-Very-First-Blog"

"スーザンは本日、グラスゴーのベラヒューストン・パークでローマ法王の" (no idea what this says) but it needs to be converted to urlsafe and at the moment it just returns an empty string.

+1  A: 

There are no "legal asian" characters in URLs: URLs can contain only ASCII characters.

Western characters you can normalize using your method (or e.g. iconv(); here is another alternative). For everything else, use percent encoding (i.e. urlencode). Modern browsers will automatically display the proper characters instead where appropriate.

Background: Unicode characters in URLs

Pekka
Hmm ok. I think I was confused because I checked asian wiki pages and they have the correct characters, but reading your further posts it seems thats just the browser decoding the url encoded format. My next question I suppose would be how to detect no latin based characters in the text.
buggedcom
@buggedcom check out the links in the answer I link to, one answer works with a replacement table; the one below that with the `Normalizer` PECL package. Both should work
Pekka
@Pekka. I think you've misunderstood. I want to detect if there is a non latin character in the text ie "ラ" and if there is then run a different set of processing functions across the string compared to a string with just latin characters. I thought strlen() vs mb_strlen() may work but it doesn't, it returns the same number of characters for the sample string above.
buggedcom
Found the answer, I didn't know regex had sub patterns. http://stackoverflow.com/questions/3411566
buggedcom
@buggedcom but why? Won't non-latin characters simply be skipped? (although true, only the character-by-character replacement strategy will work in that case.)
Pekka
yes they would be skipped by the wordpress code, however two things stop me from using that code. The first is that using my post filters on the url would still string the non latin characters. And secondly this is a commercially distributed cms and as such don't won't to re-use their code. Checking and matching non latin characters first allows me to urlencode these characters and then with the additional allow of '%' in the post filters creates a functioning urlsafe title.
buggedcom
@buggedcom ah, fair enough.
Pekka