Skip to content

Commit 4765fe1

Browse files
committed
✨ Add slugify filter
1 parent ba60910 commit 4765fe1

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

Docs/Templating.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ This is mostly standard [Stencil](https://stencil.fuller.li/en/latest/) templati
3434
You have access to
3535
- an `events` array
3636
- a custom `format` filter for date/time formatting (see cheatsheet [here](https://www.advancedswift.com/date-formatter-cheatsheet-formulas-swift/))
37+
- a custom `slugify` filter to create a url friendly version of it
3738

3839
It assumes:
3940
- that your calendar names are without spaces

LICENSING.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,8 @@ This project uses parts of the Apache licensed [MeetingBar](https://github.com/l
1212
This project has copies/variations of the [regular expressions](https://github.com/leits/MeetingBar/blob/b4af1de5d0d490c2a69c29024843995211865977/MeetingBar/MeetingServices.swift#L264) to identify services.
1313

1414
See their license [here](https://github.com/leits/MeetingBar/blob/master/LICENSE)
15+
## dirtyhenry/swift-blocks
16+
17+
This project uses parts of the MIT licensed [dirtyhenry/swift-blocks](https://github.com/dirtyhenry/swift-blocks) project.
18+
19+
In particular the method to slugify a string, see [here](about)

Sources/IO/String.swift

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import Foundation
2+
3+
// From
4+
// https://github.com/dirtyhenry/swift-blocks/blob/4e6b2fb079edb0ab0951a8fce061ee308c9d3503/Sources/Blocks/Extensions/StringProtocol.swift
5+
6+
public extension StringProtocol {
7+
/// Converts the string into a URL-friendly slug.
8+
///
9+
/// This method transforms the string to lowercase, removes accents and combining marks, replaces spaces and
10+
/// non-alphanumeric characters with hyphens, and trims leading and trailing hyphens.
11+
///
12+
/// - Returns: A URL-friendly slug representation of the string.
13+
///
14+
/// # Usage Example #
15+
/// ```
16+
/// let exampleString = "Hello, World! This is an example string with accents: é, è, ê, ñ."
17+
/// let slugifiedString = exampleString.slugify()
18+
/// print(slugifiedString) // Output: "hello-world-this-is-an-example-string-with-accents-e-e-e-n"
19+
/// ```
20+
func slugify() -> String {
21+
var slug = lowercased()
22+
23+
// Remove accents and combining marks
24+
slug = slug.applyingTransform(.toLatin, reverse: false) ?? slug
25+
slug = slug.applyingTransform(.stripDiacritics, reverse: false) ?? slug
26+
slug = slug.applyingTransform(.stripCombiningMarks, reverse: false) ?? slug
27+
28+
// Replace spaces and non-alphanumeric characters with hyphens
29+
slug = slug.replacingOccurrences(
30+
of: "[\\+]+", with: "+plus+", options: .regularExpression
31+
)
32+
slug = slug.replacingOccurrences(
33+
of: "[^a-z0-9]+", with: "-", options: .regularExpression
34+
)
35+
36+
// Trim hyphens from the start and end
37+
slug = slug.trimmingCharacters(in: CharacterSet(charactersIn: "-"))
38+
39+
if !isEmpty, slug.isEmpty {
40+
if let extendedSelf = applyingTransform(.toUnicodeName, reverse: false)?
41+
.replacingOccurrences(of: "\\N", with: ""), self != extendedSelf
42+
// swiftlint:disable:next opening_brace
43+
{
44+
return extendedSelf.slugify()
45+
}
46+
}
47+
48+
return slug
49+
}
50+
}

Sources/IO/Template.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,16 @@ class Template {
3434
return value
3535
}
3636

37+
// extension `<String>|slugify`
38+
// transforms a string into a slug
39+
ext.registerFilter("slugify") { (value: Any?, _: [Any?]) in
40+
if let value = value as? String {
41+
return value.slugify()
42+
}
43+
44+
return value
45+
}
46+
3747
let context = ["events": events]
3848
let environment = Environment(
3949
loader: DictionaryLoader(templates: ["default": template]),

0 commit comments

Comments
 (0)