// Copyright 2013 by Dobrosław Żybort. All rights reserved.// This Source Code Form is subject to the terms of the Mozilla Public// License, v. 2.0. If a copy of the MPL was not distributed with this// file, You can obtain one at http://mozilla.org/MPL/2.0/.package slugimport ()var (// CustomSub stores custom substitution mapCustomSubmap[string]string// CustomRuneSub stores custom rune substitution mapCustomRuneSubmap[rune]string// MaxLength stores maximum slug length. // By default slugs aren't shortened. // If MaxLength is smaller than length of the first word, then returned // slug will contain only substring from the first word truncated // after MaxLength.MaxLengthint// EnableSmartTruncate defines if cutting with MaxLength is smart. // Smart algorithm will cat slug after full word. // Default is true.EnableSmartTruncate = true// Lowercase defines if the resulting slug is transformed to lowercase. // Default is true.Lowercase = true regexpNonAuthorizedChars = regexp.MustCompile("[^a-zA-Z0-9-_]") regexpMultipleDashes = regexp.MustCompile("-+"))//=============================================================================// Make returns slug generated from provided string. Will use "en" as language// substitution.func ( string) ( string) {returnMakeLang(, "en")}// MakeLang returns slug generated from provided string and will use provided// language for chars substitution.func ( string, string) ( string) { = strings.TrimSpace()// Custom substitutions // Always substitute runes first = SubstituteRune(, CustomRuneSub) = Substitute(, CustomSub)// Process string with selected substitution language. // Catch ISO 3166-1, ISO 639-1:2002 and ISO 639-3:2007.switchstrings.ToLower() {case"bg", "bgr": = SubstituteRune(, bgSub)case"cs", "ces": = SubstituteRune(, csSub)case"de", "deu": = SubstituteRune(, deSub)case"en", "eng": = SubstituteRune(, enSub)case"es", "spa": = SubstituteRune(, esSub)case"fi", "fin": = SubstituteRune(, fiSub)case"fr", "fra": = SubstituteRune(, frSub)case"gr", "el", "ell": = SubstituteRune(, grSub)case"hu", "hun": = SubstituteRune(, huSub)case"id", "idn", "ind": = SubstituteRune(, idSub)case"it", "ita": = SubstituteRune(, itSub)case"kz", "kk", "kaz": = SubstituteRune(, kkSub)case"nb", "nob": = SubstituteRune(, nbSub)case"nl", "nld": = SubstituteRune(, nlSub)case"nn", "nno": = SubstituteRune(, nnSub)case"pl", "pol": = SubstituteRune(, plSub)case"ro", "rou": = SubstituteRune(, roSub)case"sl", "slv": = SubstituteRune(, slSub)case"sv", "swe": = SubstituteRune(, svSub)case"tr", "tur": = SubstituteRune(, trSub)default: // fallback to "en" if lang not found = SubstituteRune(, enSub) }// Process all non ASCII symbols = unidecode.Unidecode()ifLowercase { = strings.ToLower() }if !EnableSmartTruncate && len() >= MaxLength { = [:MaxLength] }// Process all remaining symbols = regexpNonAuthorizedChars.ReplaceAllString(, "-") = regexpMultipleDashes.ReplaceAllString(, "-") = strings.Trim(, "-_")ifMaxLength > 0 && EnableSmartTruncate { = smartTruncate() }return}// Substitute returns string with superseded all substrings from// provided substitution map. Substitution map will be applied in alphabetic// order. Many passes, on one substitution another one could apply.func ( string, map[string]string) ( string) { = var []stringfor := range { = append(, ) }sort.Strings()for , := range { = strings.Replace(, , [], -1) }return}// SubstituteRune substitutes string chars with provided rune// substitution map. One pass.func ( string, map[rune]string) string {varbytes.Bufferfor , := range {if , := []; { .WriteString() } else { .WriteRune() } }return .String()}func smartTruncate( string) string {iflen() <= MaxLength {return }// If slug is too long, we need to find the last '-' before MaxLength, and // we cut there. // If we don't find any, we have only one word, and we cut at MaxLength.for := MaxLength; >= 0; -- {if [] == '-' {return [:] } }return [:MaxLength]}// IsSlug returns True if provided text does not contain white characters,// punctuation, all letters are lower case and only from ASCII range.// It could contain `-` and `_` but not at the beginning or end of the text.// It should be in range of the MaxLength var if specified.// All output from slug.Make(text) should pass this test.func ( string) bool {if == "" || (MaxLength > 0 && len() > MaxLength) || [0] == '-' || [0] == '_' || [len()-1] == '-' || [len()-1] == '_' {returnfalse }for , := range {if ( < 'a' || > 'z') && != '-' && != '_' && ( < '0' || > '9') {returnfalse } }returntrue}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.