Options

edit

Slug.Create(text, options) takes a SlugOptions. null uses the default options.

OptionDefaultDescription
MaximumLength80 (SlugOptions.DefaultMaximumLength)Maximum length of the slug. 0 or a negative value means no limit
Separator"-" (SlugOptions.DefaultSeparator)Text written in place of the characters that are not allowed. It cannot be null, but it can be empty
CanEndWithSeparatorfalseKeeps a separator at the end of the slug
CasingTransformationPreserveCasePreserveCase, ToLowerCase or ToUpperCase
CulturenullCulture of the case transformation. null uses the invariant casing rules
AllowedRangesa-z, A-Z, 0-9Unicode ranges of the characters kept in the slug

Limit the length

var options = new SlugOptions
{
    MaximumLength = 10,
    CasingTransformation = CasingTransformation.ToLowerCase,
};

// "the-quick-" is cut to 10 characters, then the trailing separator is removed
Assert.Equal("the-quick", Slug.Create("The quick brown fox", options));

// The slug is cut between characters, not between words
options.MaximumLength = 12;
Assert.Equal("the-quick-br", Slug.Create("The quick brown fox", options));

The limit applies to the returned slug and is never exceeded. The slug is cut between characters, so it never ends with half of a surrogate pair, half of a multi-character separator, or a letter without its accents; it can therefore be a little shorter than the limit. The slug is not cut between words.

Change the separator

var options = new SlugOptions
{
    Separator = "_",
    CasingTransformation = CasingTransformation.ToUpperCase,
};
Assert.Equal("RELEASE_NOTES_V2", Slug.Create("Release notes: v2!", options));

options.CanEndWithSeparator = true;
Assert.Equal("RELEASE_NOTES_V2_", Slug.Create("Release notes: v2!", options));

Use a separator made of characters that are not allowed. When the text itself contains an allowed separator character, it is kept as content, so a trailing one stays even when CanEndWithSeparator is false.

Allow more characters

AllowedRanges is a list of UnicodeRange (from System.Text.Unicode). Add ranges to keep more characters:

var options = new SlugOptions { CasingTransformation = CasingTransformation.ToLowerCase };

// Keep Cyrillic letters in addition to a-z, A-Z and 0-9
options.AllowedRanges.Add(UnicodeRanges.Cyrillic);
Assert.Equal("привет-мир", Slug.Create("Привет, мир!", options));

The list has no setter. To replace the default ranges, clear it, then add yours:

var options = new SlugOptions
{
    MaximumLength = 20,
    Separator = "-",
    CanEndWithSeparator = false,
    CasingTransformation = CasingTransformation.ToLowerCase,
};

// AllowedRanges is read-only, so change the list in place instead of assigning a new one.
// It already contains a-z, A-Z and 0-9, so clear it first to replace that set.
options.AllowedRanges.Clear();
options.AllowedRanges.Add(UnicodeRange.Create('a', 'z'));
options.AllowedRanges.Add(UnicodeRange.Create('A', 'Z'));
options.AllowedRanges.Add(UnicodeRange.Create('0', '9'));

var result = Slug.Create("This is a text", options);
Assert.Equal("this-is-a-text", result);

An empty list allows every character instead of none: clearing it without adding a range returns the text unfiltered, with its spaces, punctuation and diacritics, and only the case transformation applied.

Replace characters

SlugOptions has two virtual methods that run for each character of the text:

  • IsAllowed(Rune) tells whether the character is kept. The default implementation checks AllowedRanges.
  • Replace(Rune) returns the text written for a kept character. The default implementation applies CasingTransformation.

Override them to write a character differently:

private sealed class AmpersandSlugOptions : SlugOptions
{
    // Keep '&' instead of turning it into a separator...
    public override bool IsAllowed(Rune character) => character.Value is '&' || base.IsAllowed(character);

    // ...and write it as "and"
    public override string Replace(Rune rune) => rune.Value is '&' ? "and" : base.Replace(rune);
}
Assert.Equal("Tom-and-Jerry", Slug.Create("Tom & Jerry", new AmpersandSlugOptions()));

The text is decomposed before these methods run, so a letter and its accents are separate characters: é is e followed by a combining accent. IsAllowed receives the character before Replace and the case transformation. An accent that is not allowed is dropped without writing a separator. A replacement is never cut by MaximumLength: it is kept whole or dropped.