Golang : Format strings to SEO friendly URL example
Problem:
You want to take a string and format it to SEO friendly URL. SEO friendly means no uppercase, underscore and characters deemed not suitable for search engine crawlers. How to do that?
NOTES: This example can be used in sanitazing and cleaning up input strings as well.
Solution:
Ported this PHP function to Golang
function SEOfriendlyURL($string)
{
$string = strtolower($string);
$string = str_replace(" ", "-", $string);
$string = str_replace("/", "-", $string);
$string = preg_replace('/\s+/', '-', $string);
$string = preg_replace("`\[.*\]`U", "", $string);
$string = preg_replace('`&(amp;)?#?[a-z0-9]+;`i', '-', $string);
$string = preg_replace("/[^\x9\xA\xD\x20-\x7F]/", "", $string);
$string = htmlentities($string, ENT_COMPAT, 'utf-8');
$string = preg_replace("`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig|quot|rsquo);`i", "\\1", $string);
$string = preg_replace(array("`[^a-z0-9]`i", "`[-]+`"), "-", $string);
return strtolower(trim($string, '-'));
}
and added some improvements to normalize unicode strings and remove all diacritical/accents marks.
Here you go!
package main
import (
"fmt"
"golang.org/x/text/transform"
"golang.org/x/text/unicode/norm"
"regexp"
"strings"
"unicode"
)
func isMn(r rune) bool {
return unicode.Is(unicode.Mn, r) // Mn: nonspacing marks
}
func SEOURL(s string) string {
seoStr := strings.ToLower(s)
//seoStr = strings.Replace(seoStr, "/", "-", -1)
//regE := regexp.MustCompile("/s+/")
//seoStrByte := regE.ReplaceAll([]byte(seoStr), []byte("-"))
//seoStr = string(seoStrByte) // convert []byte to string
// convert all spaces to dash
regE := regexp.MustCompile("[[:space:]]")
seoStrByte := regE.ReplaceAll([]byte(seoStr), []byte("-"))
seoStr = string(seoStrByte) // convert []byte to string
// remove all blanks such as tab
regE = regexp.MustCompile("[[:blank:]]")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte(""))
seoStr = string(seoStrByte) // convert []byte to string
// remove all punctuations with the exception of dash
//regE = regexp.MustCompile("[[:punct:]]")
regE = regexp.MustCompile("[!/:-@[-`{-~]")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte(""))
seoStr = string(seoStrByte) // convert []byte to string
// \x9\xA\xD will cause non-hex character in escape sequence error
// regE = regexp.MustCompile("/[^\x9\xA\xD\x20-\x7F]/")
//regE = regexp.MustCompile("[[:xdigit:]]") -- will remove some alphabet. Bug?
regE = regexp.MustCompile("/[^\x20-\x7F]/")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte(""))
seoStr = string(seoStrByte) // convert []byte to string
regE = regexp.MustCompile("`&(amp;)?#?[a-z0-9]+;`i")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte("-"))
seoStr = string(seoStrByte) // convert []byte to string
regE = regexp.MustCompile("`&([a-z])(acute|uml|circ|grave|ring|cedil|slash|tilde|caron|lig|quot|rsquo);`i")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte("\\1"))
seoStr = string(seoStrByte) // convert []byte to string
regE = regexp.MustCompile("`[^a-z0-9]`i")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte("-"))
seoStr = string(seoStrByte) // convert []byte to string
regE = regexp.MustCompile("`[-]+`")
seoStrByte = regE.ReplaceAll([]byte(seoStr), []byte("-"))
seoStr = string(seoStrByte) // convert []byte to string
// normalize unicode strings and remove all diacritical/accents marks
// see https://www.socketloop.com/tutorials/golang-normalize-unicode-strings-for-comparison-purpose
t := transform.Chain(norm.NFD, transform.RemoveFunc(isMn), norm.NFC)
seoStr, _, _ = transform.String(t, seoStr)
return strings.TrimSpace(seoStr)
}
func main() {
NonSEOString := "@<ElNi\u00f1o coming? > #% sooner this year!"
fmt.Println("BEFORE : ", NonSEOString)
SEOedString := SEOURL(NonSEOString)
fmt.Println("AFTER : ", SEOedString)
}
Output:
BEFORE : @<ElNiño coming? > #% sooner this year!
AFTER : elnino-coming--#%-sooner-this-year
NOTES: This example is not perfect as regular expression is not exactly my forte. Also, the conversion from byte to string can be optimized instead of converting in and out. Will leave it as an exercise for you. ;-)
References:
https://golang.org/pkg/regexp/#Regexp.ReplaceAll
https://golang.org/pkg/regexp/syntax/#pkg-overview
https://www.socketloop.com/tutorials/golang-normalize-unicode-strings-for-comparison-purpose
https://www.socketloop.com/tutorials/trim-white-spaces-string-golang
See also : Golang : Normalize unicode strings for comparison purpose
By Adam Ng
IF you gain some knowledge or the information here solved your programming problem. Please consider donating to the less fortunate or some charities that you like. Apart from donation, planting trees, volunteering or reducing your carbon footprint will be great too.
Advertisement
Tutorials
+11.2k Golang : Handle API query by curl with Gorilla Queries example
+11.1k Golang : Byte format example
+16.3k Golang : Merge video(OpenCV) and audio(PortAudio) into a mp4 file
+18.8k Golang : Populate dropdown with html/template example
+13.9k Golang : Check if a file exist or not
+77.6k Golang : How to return HTTP status code?
+18.8k Golang : Clearing slice
+10.2k Golang : Resolve domain name to IP4 and IP6 addresses.
+7.3k Gogland : Single File versus Go Application Run Configurations
+47.2k Golang : Convert int to byte array([]byte)
+22.4k Golang : untar or extract tar ball archive example
+17.3k How to enable MariaDB/MySQL logs ?