Skip to content

Commit

Permalink
Merge pull request #1 from fantakim/dev
Browse files Browse the repository at this point in the history
Release
  • Loading branch information
fantakim authored Dec 31, 2021
2 parents 93b21ca + 9b82887 commit 0c69538
Show file tree
Hide file tree
Showing 8 changed files with 1,125 additions and 2 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
53 changes: 51 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,51 @@
# Slugify
Slugifies a string
# SlugifyNet
> Converts a string into a slug.
[![Nuget](https://img.shields.io/nuget/v/SlugifyNet)](https://www.nuget.org/packages/SlugifyNet)
[![.NET Standard](https://img.shields.io/badge/.NET%20Standard-%3E%3D%202.0-red.svg)](#)

## Description
Created by referring to https://github.com/simov/slugify

## Prerequisites
- .NET Standard 2.0

## Installation
Using the [.NET Core command-line interface (CLI) tools]

```sh
dotnet add package SlugifyNet
```

Using the [NuGet Command Line Interface (CLI)]

```sh
nuget install SlugifyNet
```

Using the [Package Manager Console]

```powershell
Install-Package SlugifyNet
```

## Usage
```c#
"Elon Musk considers move to Mars".GenerateSlug(); // elon-musk-considers-move-to-mars
"Fintech startups raised $34B in 2019".GenerateSlug(); // fintech-startups-raised-dollar34b-in-2019
```
## Slugify Examples
| Web Page Title | SEO-friendly URL Slug |
|--|--|
| Best Practices and Considerations for Multilingual Sites | best-practices-and-considerations-for-multilingual-sites |
| Meilleures pratiques et considérations relatives aux sites multilingues | meilleures-pratiques-et-considerations-relatives-aux-sites-multilingues |
| Bewährte Vorgehensweisen und Überlegungen für mehrsprachige Sites | bewahrte-vorgehensweisen-und-uberlegungen-fur-mehrsprachige-sites |
| Considerazioni e procedure consigliate per i siti multilingue | considerazioni-e-procedure-consigliate-per-i-siti-multilingue |
| マルチ言語サイトのベストプラクティスと考慮事項 | マルチ言語サイトのベストプラクティスと考慮事項 |
| Prácticas recomendadas y consideraciones para sitios multilingüe | practicas-recomendadas-y-consideraciones-para-sitios-multilingue |
| 多语言站点的最佳实践和注意事项 | 多语言站点的最佳实践和注意事项 |
| 다국어 사이트에 대한 모범 사례 및 고려 사항 | 다국어-사이트에-대한-모범-사례-및-고려-사항 |
| Советы и рекомендации для многоязычных сайтов | sovety-i-rekomendacii-dlya-mnogoyazychnyh-sajtov |
| Melhores práticas e considerações para sites multilíngues | melhores-praticas-e-consideracoes-para-sites-multilingues |
| Các phương pháp hay nhất và cân nhắc cho các trang web đa ngôn ngữ | cac-phuong-phap-hay-nhat-va-can-nhac-cho-cac-trang-web-dja-ngon-ngu |
| แนวปฏิบัติที่ดีที่สุดและข้อควรพิจารณาสำหรับไซต์หลายภาษา | แนวปฏิบัติที่ดีที่สุดและข้อควรพิจารณาสำหรับไซต์หลายภาษา |
32 changes: 32 additions & 0 deletions SlugifyNet.Tests/SlugifyNet.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="1.3.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SlugifyNet\SlugifyNet.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="xunit.runner.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
135 changes: 135 additions & 0 deletions SlugifyNet.Tests/SlugifyNetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace SlugifyNet.Tests
{
public class SlugifyTests
{
[Fact]
public void replace_whitespaces_with_replacement()
{
Assert.Equal("foo-bar-baz", "foo bar baz".GenerateSlug());
Assert.Equal("foo_bar_baz", "foo bar baz".GenerateSlug(replacement: "_"));
}

[Fact]
public void remove_duplicates_of_the_replacement_character()
{
Assert.Equal("foo-bar", "foo , bar".GenerateSlug());
}

[Fact]
public void remove_trailing_space_if_any()
{
Assert.Equal("foo-bar-baz", " foo bar baz ".GenerateSlug());
}

[Fact]
public void remove_not_allowed_chars()
{
Assert.Equal("foo-bar-baz", "foo, bar baz".GenerateSlug());
Assert.Equal("foo-bar-baz", "foo- bar baz".GenerateSlug());
Assert.Equal("foo-bar-baz", "foo] bar baz".GenerateSlug());
Assert.Equal("foo-bar-baz", "foo bar--baz".GenerateSlug());
}

[Fact]
public void leave_allowed_chars()
{
var allowed = new char[] { '*', '+', '~', '.', '(', ')', '\'', '"', '!', ':', '@' };
allowed.ToList().ForEach(symbol =>
{
var text = "foo " + symbol + " bar baz";
var actual = text.GenerateSlug();
var expected = "foo-" + symbol + "-bar-baz";
Assert.Equal(expected, actual);
});
}

[Fact]
public void options_replacement()
{
Assert.Equal("foo_bar_baz", "foo bar baz".GenerateSlug(replacement: "_"));
}

[Fact]
public void options_replacement_empty_string()
{
Assert.Equal("foobarbaz", "foo bar baz".GenerateSlug(replacement: ""));
}

[Fact]
public void options_lower()
{
Assert.Equal("Foo-bAr-baZ", "Foo bAr baZ".GenerateSlug(lower: false));
}

[Fact]
public void replace_latin_chars()
{
var characters = new Dictionary<string, string>
{
{ "À", "A" }, { "Á", "A" }, { "Â", "A" }, { "Ã", "A" }, { "Ä", "A" }, { "Å", "A" }, { "Æ", "AE" },
{ "Ç", "C" }, { "È", "E" }, { "É", "E" }, { "Ê", "E" }, { "Ë", "E" }, { "Ì", "I" }, { "Í", "I" },
{ "Î", "I" }, { "Ï", "I" }, { "Ð", "D" }, { "Ñ", "N" }, { "Ò", "O" }, { "Ó", "O" }, { "Ô", "O" },
{ "Õ", "O" }, { "Ö", "O" }, { "Ő", "O" }, { "Ø", "O" }, { "Ù", "U" }, { "Ú", "U" }, { "Û", "U" },
{ "Ü", "U" }, { "Ű", "U" }, { "Ý", "Y" }, { "Þ", "TH" }, { "ß", "ss" }, { "à", "a" }, { "á", "a" },
{ "â", "a" }, { "ã", "a" }, { "ä", "a" }, { "å", "a" }, { "æ", "ae" }, { "ç", "c" }, { "è", "e" },
{ "é", "e" }, { "ê", "e" }, { "ë", "e" }, { "ì", "i" }, { "í", "i" }, { "î", "i" }, { "ï", "i" },
{ "ð", "d" }, { "ñ", "n" }, { "ò", "o" }, { "ó", "o" }, { "ô", "o" }, { "õ", "o" }, { "ö", "o" },
{ "ő", "o" }, { "ø", "o" }, { "ù", "u" }, { "ú", "u" }, { "û", "u" }, { "ü", "u" }, { "ű", "u" },
{ "ý", "y" }, { "þ", "th" }, { "ÿ", "y" }, { "", "SS" }
};

foreach (var c in characters)
{
var text = "foo " + c.Key + " bar baz";
var actual = text.GenerateSlug(lower: false);
var expected = "foo-" + c.Value + "-bar-baz";

Assert.Equal(expected, actual);
}
}

[Fact]
public void replaces_leading_and_trailing_replacement_chars()
{
Assert.Equal("Come-on-fhqwhgads", "-Come on, fhqwhgads-".GenerateSlug(lower: false));
}

[Theory]
[InlineData("Elon Musk considers move to Mars", "elon-musk-considers-move-to-mars")]
[InlineData("Fintech startups raised $34B in 2019", "fintech-startups-raised-dollar34b-in-2019")]
[InlineData("Shopify joins Facebook’s cryptocurrency Libra Association", "shopify-joins-facebook's-cryptocurrency-libra-association")]
[InlineData("What is a slug and how to optimize it?", "what-is-a-slug-and-how-to-optimize-it")]
[InlineData("Bitcoin soars past $33,000, its highest ever", "bitcoin-soars-past-dollar33000-its-highest-ever")]
public void slugify_examples(string input, string expected)
{
var actual = input.GenerateSlug();

Assert.Equal(expected, actual);
}

[Theory]
[InlineData("Best Practices and Considerations for Multilingual Sites", "best-practices-and-considerations-for-multilingual-sites")]
[InlineData("Meilleures pratiques et considérations relatives aux sites multilingues", "meilleures-pratiques-et-considerations-relatives-aux-sites-multilingues")]
[InlineData("Bewährte Vorgehensweisen und Überlegungen für mehrsprachige Sites", "bewahrte-vorgehensweisen-und-uberlegungen-fur-mehrsprachige-sites")]
[InlineData("Considerazioni e procedure consigliate per i siti multilingue", "considerazioni-e-procedure-consigliate-per-i-siti-multilingue")]
[InlineData("マルチ言語サイトのベストプラクティスと考慮事項", "マルチ言語サイトのベストプラクティスと考慮事項")]
[InlineData("Prácticas recomendadas y consideraciones para sitios multilingüe", "practicas-recomendadas-y-consideraciones-para-sitios-multilingue")]
[InlineData("多语言站点的最佳实践和注意事项", "多语言站点的最佳实践和注意事项")]
[InlineData("다국어 사이트에 대한 모범 사례 및 고려 사항", "다국어-사이트에-대한-모범-사례-및-고려-사항")]
[InlineData("Советы и рекомендации для многоязычных сайтов", "sovety-i-rekomendacii-dlya-mnogoyazychnyh-sajtov")]
[InlineData("Melhores práticas e considerações para sites multilíngues", "melhores-praticas-e-consideracoes-para-sites-multilingues")]
[InlineData("Các phương pháp hay nhất và cân nhắc cho các trang web đa ngôn ngữ", "cac-phuong-phap-hay-nhat-va-can-nhac-cho-cac-trang-web-dja-ngon-ngu")]
[InlineData("แนวปฏิบัติที่ดีที่สุดและข้อควรพิจารณาสำหรับไซต์หลายภาษา", "แนวปฏิบัติที่ดีที่สุดและข้อควรพิจารณาสำหรับไซต์หลายภาษา")]
public void slugify_multilingual_examples(string input, string expected)
{
var actual = input.GenerateSlug();

Assert.Equal(expected, actual);
}
}
}
4 changes: 4 additions & 0 deletions SlugifyNet.Tests/xunit.runner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"parallelizeAssembly": false,
"parallelizeTestCollections": false
}
31 changes: 31 additions & 0 deletions SlugifyNet.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlugifyNet", "SlugifyNet\SlugifyNet.csproj", "{760C2556-66E4-46C2-8294-24B01C3D5A0A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SlugifyNet.Tests", "SlugifyNet.Tests\SlugifyNet.Tests.csproj", "{5705BCB3-8D99-416F-90FB-FE3C311EF998}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{760C2556-66E4-46C2-8294-24B01C3D5A0A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{760C2556-66E4-46C2-8294-24B01C3D5A0A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{760C2556-66E4-46C2-8294-24B01C3D5A0A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{760C2556-66E4-46C2-8294-24B01C3D5A0A}.Release|Any CPU.Build.0 = Release|Any CPU
{5705BCB3-8D99-416F-90FB-FE3C311EF998}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5705BCB3-8D99-416F-90FB-FE3C311EF998}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5705BCB3-8D99-416F-90FB-FE3C311EF998}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5705BCB3-8D99-416F-90FB-FE3C311EF998}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {B9EA1A98-4B2E-473F-A12B-6FB07160523D}
EndGlobalSection
EndGlobal
Loading

0 comments on commit 0c69538

Please sign in to comment.