package slug import ( "regexp" "strconv" "strings" "unicode/utf8" ) const MaxRunes = 100 var pattern = regexp.MustCompile(`^[a-z0-9]+(-[a-z0-9]+)*$`) var nonSlugRun = regexp.MustCompile(`[^a-z0-9]+`) var diacriticFolder = strings.NewReplacer( "à", "a", "á", "a", "â", "a", "ã", "a", "ä", "a", "å", "a", "ā", "a", "ă", "a", "ą", "a", "æ", "ae", "ç", "c", "ć", "c", "č", "c", "ĉ", "c", "ď", "d", "đ", "d", "è", "e", "é", "e", "ê", "e", "ë", "e", "ē", "e", "ė", "e", "ę", "e", "ě", "e", "ğ", "g", "ì", "i", "í", "i", "î", "i", "ï", "i", "ī", "i", "į", "i", "ı", "i", "ł", "l", "ľ", "l", "ñ", "n", "ń", "n", "ň", "n", "ò", "o", "ó", "o", "ô", "o", "õ", "o", "ö", "o", "ø", "o", "ō", "o", "ő", "o", "œ", "oe", "ř", "r", "ś", "s", "š", "s", "ş", "s", "ș", "s", "ß", "ss", "ť", "t", "ț", "t", "ţ", "t", "ù", "u", "ú", "u", "û", "u", "ü", "u", "ū", "u", "ů", "u", "ű", "u", "ų", "u", "ý", "y", "ÿ", "y", "ź", "z", "ż", "z", "ž", "z", ) func IsValid(candidate string) bool { length := utf8.RuneCountInString(candidate) return length >= 1 && length <= MaxRunes && pattern.MatchString(candidate) } func Generate(text string, fallback string) string { folded := diacriticFolder.Replace(strings.ToLower(text)) generated := strings.Trim(nonSlugRun.ReplaceAllString(folded, "-"), "-") generated = truncate(generated, MaxRunes) if generated == "" { return fallback } return generated } func WithSuffix(base string, attempt int) string { if attempt <= 1 { return base } suffix := "-" + strconv.Itoa(attempt) return truncate(base, MaxRunes-len(suffix)) + suffix } func truncate(candidate string, limit int) string { if len(candidate) <= limit { return candidate } return strings.TrimRight(candidate[:limit], "-") }