aboutsummaryrefslogtreecommitdiffstats
path: root/src/IOL.Helpers/SlugGenerator.cs
diff options
context:
space:
mode:
authorivarlovlie <git@ivarlovlie.no>2021-04-01 22:36:59 +0200
committerivarlovlie <git@ivarlovlie.no>2021-04-01 22:36:59 +0200
commit9e0fbac6d3cb4e11efdb33c339ccf0adbca0bc6c (patch)
tree8fd1e8710abc3b17f0bc7f960c89931efd83c944 /src/IOL.Helpers/SlugGenerator.cs
downloaddotnet-helpers-9e0fbac6d3cb4e11efdb33c339ccf0adbca0bc6c.tar.xz
dotnet-helpers-9e0fbac6d3cb4e11efdb33c339ccf0adbca0bc6c.zip
Initial commit
Diffstat (limited to 'src/IOL.Helpers/SlugGenerator.cs')
-rw-r--r--src/IOL.Helpers/SlugGenerator.cs108
1 files changed, 108 insertions, 0 deletions
diff --git a/src/IOL.Helpers/SlugGenerator.cs b/src/IOL.Helpers/SlugGenerator.cs
new file mode 100644
index 0000000..3f53dd6
--- /dev/null
+++ b/src/IOL.Helpers/SlugGenerator.cs
@@ -0,0 +1,108 @@
+using System.Text;
+
+namespace IOL.Helpers
+{
+ public static class Slug
+ {
+ public static string Generate(bool toLower, params string[] values) {
+ return Create(string.Join("-", values), toLower);
+ }
+
+ /// <summary>
+ /// Creates a slug.
+ /// References:
+ /// http://www.unicode.org/reports/tr15/tr15-34.html
+ /// https://meta.stackexchange.com/questions/7435/non-us-ascii-characters-dropped-from-full-profile-url/7696#7696
+ /// https://stackoverflow.com/questions/25259/how-do-you-include-a-webpage-title-as-part-of-a-webpage-url/25486#25486
+ /// https://stackoverflow.com/questions/3769457/how-can-i-remove-accents-on-a-string
+ /// </summary>
+ /// <param name="value"></param>
+ /// <param name="toLower"></param>
+ /// <returns>Slugified string</returns>
+ public static string Create(string value, bool toLower) {
+ if (string.IsNullOrWhiteSpace(value))
+ return value;
+
+ var normalised = value.Normalize(NormalizationForm.FormKD);
+
+ const int MAXLEN = 80;
+ var len = normalised.Length;
+ var prevDash = false;
+ var sb = new StringBuilder(len);
+
+ for (var i = 0; i < len; i++) {
+ var c = normalised[i];
+ switch (c) {
+ case >= 'a' and <= 'z':
+ case >= '0' and <= '9': {
+ if (prevDash) {
+ sb.Append('-');
+ prevDash = false;
+ }
+
+ sb.Append(c);
+ break;
+ }
+ case >= 'A' and <= 'Z': {
+ if (prevDash) {
+ sb.Append('-');
+ prevDash = false;
+ }
+
+ // Tricky way to convert to lowercase
+ if (toLower)
+ sb.Append((char) (c | 32));
+ else
+ sb.Append(c);
+ break;
+ }
+ case ' ':
+ case ',':
+ case '.':
+ case '/':
+ case '\\':
+ case '-':
+ case '_':
+ case '=': {
+ if (!prevDash && sb.Length > 0) {
+ prevDash = true;
+ }
+
+ break;
+ }
+ default: {
+ var swap = ConvertEdgeCases(c, toLower);
+ if (swap != null) {
+ if (prevDash) {
+ sb.Append('-');
+ prevDash = false;
+ }
+
+ sb.Append(swap);
+ }
+
+ break;
+ }
+ }
+
+ if (sb.Length == MAXLEN)
+ break;
+ }
+
+ return sb.ToString();
+ }
+
+ private static string ConvertEdgeCases(char c, bool toLower) => c switch {
+ 'ı' => "i",
+ 'ł' => "l",
+ 'Ł' => toLower ? "l" : "L",
+ 'đ' => "d",
+ 'ß' => "ss",
+ 'ø' => "o",
+ 'å' => "aa",
+ 'æ' => "ae",
+ 'Þ' => "th",
+ _ => null
+ };
+ }
+} \ No newline at end of file