Take a look at this slug function to sanitize URLs, developed by Sean Murphy at https://gist.github.com/sgmurphy/3095196
/**
* Create a web friendly URL slug from a string.
*
* Requires XRegExp (http://xregexp.com) with unicode add-ons for UTF-8 support.
*
* Although supported, transliteration is discouraged because
* 1) most web browsers support UTF-8 characters in URLs
* 2) transliteration causes a loss of information
*
* @author Sean Murphy <[email protected]>
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
* @license http://creativecommons.org/publicdomain/zero/1.0/
*
* @param string s
* @param object opt
* @return string
*/
function url_slug(s, opt) {
s = String(s);
opt = Object(opt);
var defaults = {
'delimiter': '-',
'limit': undefined,
'lowercase': true,
'replacements': {},
'transliterate': (typeof(XRegExp) === 'undefined') ? true : false
};
// Merge options
for (var k in defaults) {
if (!opt.hasOwnProperty(k)) {
opt[k] = defaults[k];
}
}
var char_map = {
// Latin
'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 'C',
'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I',
'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', '?': 'O',
'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', '?': 'U', 'Ý': 'Y', 'Þ': 'TH',
'ß': 'ss',
'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c',
'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i',
'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', '?': 'o',
'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', '?': 'u', 'ý': 'y', 'þ': 'th',
'ÿ': 'y',
// Latin symbols
'©': '(c)',
// Greek
'?': 'A', '?': 'B', '?': 'G', '?': 'D', '?': 'E', '?': 'Z', '?': 'H', '?': '8',
'?': 'I', '?': 'K', '?': 'L', '?': 'M', '?': 'N', '?': '3', '?': 'O', '?': 'P',
'?': 'R', '?': 'S', '?': 'T', '?': 'Y', '?': 'F', '?': 'X', '?': 'PS', '?': 'W',
'?': 'A', '?': 'E', '?': 'I', '?': 'O', '?': 'Y', '?': 'H', '?': 'W', '?': 'I',
'?': 'Y',
'?': 'a', '?': 'b', '?': 'g', '?': 'd', '?': 'e', '?': 'z', '?': 'h', '?': '8',
'?': 'i', '?': 'k', '?': 'l', '?': 'm', '?': 'n', '?': '3', '?': 'o', '?': 'p',
'?': 'r', '?': 's', '?': 't', '?': 'y', '?': 'f', '?': 'x', '?': 'ps', '?': 'w',
'?': 'a', '?': 'e', '?': 'i', '?': 'o', '?': 'y', '?': 'h', '?': 'w', '?': 's',
'?': 'i', '?': 'y', '?': 'y', '?': 'i',
// Turkish
'?': 'S', '?': 'I', 'Ç': 'C', 'Ü': 'U', 'Ö': 'O', '?': 'G',
'?': 's', '?': 'i', 'ç': 'c', 'ü': 'u', 'ö': 'o', '?': 'g',
// Russian
'?': 'A', '?': 'B', '?': 'V', '?': 'G', '?': 'D', '?': 'E', '?': 'Yo', '?': 'Zh',
'?': 'Z', '?': 'I', '?': 'J', '?': 'K', '?': 'L', '?': 'M', '?': 'N', '?': 'O',
'?': 'P', '?': 'R', '?': 'S', '?': 'T', '?': 'U', '?': 'F', '?': 'H', '?': 'C',
'?': 'Ch', '?': 'Sh', '?': 'Sh', '?': '', '?': 'Y', '?': '', '?': 'E', '?': 'Yu',
'?': 'Ya',
'?': 'a', '?': 'b', '?': 'v', '?': 'g', '?': 'd', '?': 'e', '?': 'yo', '?': 'zh',
'?': 'z', '?': 'i', '?': 'j', '?': 'k', '?': 'l', '?': 'm', '?': 'n', '?': 'o',
'?': 'p', '?': 'r', '?': 's', '?': 't', '?': 'u', '?': 'f', '?': 'h', '?': 'c',
'?': 'ch', '?': 'sh', '?': 'sh', '?': '', '?': 'y', '?': '', '?': 'e', '?': 'yu',
'?': 'ya',
// Ukrainian
'?': 'Ye', '?': 'I', '?': 'Yi', '?': 'G',
'?': 'ye', '?': 'i', '?': 'yi', '?': 'g',
// Czech
'?': 'C', '?': 'D', '?': 'E', '?': 'N', '?': 'R', 'Š': 'S', '?': 'T', '?': 'U',
'Ž': 'Z',
'?': 'c', '?': 'd', '?': 'e', '?': 'n', '?': 'r', 'š': 's', '?': 't', '?': 'u',
'ž': 'z',
// Polish
'?': 'A', '?': 'C', '?': 'e', '?': 'L', '?': 'N', 'Ó': 'o', '?': 'S', '?': 'Z',
'?': 'Z',
'?': 'a', '?': 'c', '?': 'e', '?': 'l', '?': 'n', 'ó': 'o', '?': 's', '?': 'z',
'?': 'z',
// Latvian
'?': 'A', '?': 'C', '?': 'E', '?': 'G', '?': 'i', '?': 'k', '?': 'L', '?': 'N',
'Š': 'S', '?': 'u', 'Ž': 'Z',
'?': 'a', '?': 'c', '?': 'e', '?': 'g', '?': 'i', '?': 'k', '?': 'l', '?': 'n',
'š': 's', '?': 'u', 'ž': 'z'
};
// Make custom replacements
for (var k in opt.replacements) {
s = s.replace(RegExp(k, 'g'), opt.replacements[k]);
}
// Transliterate characters to ASCII
if (opt.transliterate) {
for (var k in char_map) {
s = s.replace(RegExp(k, 'g'), char_map[k]);
}
}
// Replace non-alphanumeric characters with our delimiter
var alnum = (typeof(XRegExp) === 'undefined') ? RegExp('[^a-z0-9]+', 'ig') : XRegExp('[^\\p{L}\\p{N}]+', 'ig');
s = s.replace(alnum, opt.delimiter);
// Remove duplicate delimiters
s = s.replace(RegExp('[' + opt.delimiter + ']{2,}', 'g'), opt.delimiter);
// Truncate slug to max. characters
s = s.substring(0, opt.limit);
// Remove delimiter from ends
s = s.replace(RegExp('(^' + opt.delimiter + '|' + opt.delimiter + '$)', 'g'), '');
return opt.lowercase ? s.toLowerCase() : s;
}