SmartFormat.NET 3.0.0-alpha.4

The ID prefix of this package has been reserved for one of the owners of this package by Prefix Reserved
This is a prerelease version of SmartFormat.NET.
There is a newer version of this package available.
See the version list below for details.
dotnet add package SmartFormat.NET --version 3.0.0-alpha.4
NuGet\Install-Package SmartFormat.NET -Version 3.0.0-alpha.4
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="SmartFormat.NET" Version="3.0.0-alpha.4" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add SmartFormat.NET --version 3.0.0-alpha.4
#r "nuget: SmartFormat.NET, 3.0.0-alpha.4"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install SmartFormat.NET as a Cake Addin
#addin nuget:?package=SmartFormat.NET&version=3.0.0-alpha.4&prerelease

// Install SmartFormat.NET as a Cake Tool
#tool nuget:?package=SmartFormat.NET&version=3.0.0-alpha.4&prerelease

What's new in v3.0.0-alpha.4

(compared to v2.7.1)

1. Significant boost in performance

After implementing a zero allocation ValueStringBuilder based on ZString with #193:

  • Parsing is 10% faster with 50-80% less GC and memory allocation
  • Formatting is up to 40% faster with 50% less GC and memory allocation

More optimizations:

  1. ReflectionSource
  • Added a type cache which increases speed by factor 4. Thanks to @karljj1. (#155).
  • Type caching can be disabled (#217)
  • Dictionary for type cache changed to ConcurrentDictionary (#217)
  • TypeCache is accessible from a derived class (#217)
  1. DictionarySource
  • Speed increased by 10% with less GC pressure (#189)

2. Exact control of whitespace text output

This was an issue in v2 and was going back to combining string.Format compatibility with Smart.Format features. This is resolved by setting the desired mode with SmartSettings.StringFormatCompatibility (defaults to false). (#172)

3. Literals may contain any Unicode characters (#166)

Add unicode escape characters like "\u1234". Thanks to @karljj1.

4. Separate modes for "Smart.Format features" and "string.Format compatibility"

The mode can be set with SmartSettings.StringFormatCompatibility. By default, SmartSettings.StringFormatCompatibility is false. (#173, #175)

a) Smart.Format features mode

  • Brings the full set of features implemented in Smart.Format
  • Curly braces are escaped the Smart.Format way with \{ and \}.
  • As long as special characters (){}:\ are escaped with \, any character is allowed anywhere. Note: This applies also for the colon.

b) string.Format compatibility mode

  • SmartFormat acts as a drop-in replacement
  • On top, it allows for named placeholders besides indexed placeholders.
  • The Parser will not include the formatter name or formatting options. Like with string.Format, everything after the Selector separator (colon) is considered as format specifier.
  • Curly braces are escaped the string.Format way with {{ and }}. This is the reason for all limitations in string.Format compatibility mode
  • DefaultFormatter is the only formatter which will be invoked. null will be output as string.Empty.

5. Unlimited characters in formatter options (#164, #165)

This was a big limitation in v2. In v3, the Parser can parse any character as part of formatter options. This means e.g. no limitations for RegEx expressions used in IsMatchFormatter. Note: Special characters like (){}:\ must be escaped with \.

Say we want to include in the output, whether an email is valid (simplified):

var emailRegEx = "^((\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*)\s*[;]{0,1}\s*)+$";
// ask a little helper to escape the RegEx
var escaped = EscapedLiteral.EscapeCharLiterals('\\', emailRegEx, 0, emailRegEx.Length, true);
// insert the escaped literal from the helper
var result = Smart.Format("Email {0:ismatch("^\(\(\\w+\([-+.]\\w+\)*@\\w+\([-.]\\w+\)*\\.\\w+\([-.]\\w+\)*\)\\s*[;]\{0,1\}\\s*\)+$"):{} is valid|{} NOT valid}", "");
// returns "Email is valid"

6. Simplified caching of parser result (#183)

"Parse once, format often" is simplified: The Parser's Format result can be directly used as a parameter of the SmartFormatter.

var temperatures = new[] {-20, -10, -15};
// parse once
var parsedFormat = new Parser().ParseFormat("Temperature is {Temp}°.");
// one SmartFormatter instance
var formatter = Smart.CreateDefaultSmartFormat();
foreach (var current in temperatures)
    var result = formatter.Format(parsedFormat, new {Temp = current});

This pattern is much faster than calling

// combined parsing and formatting
var result = Smart.Format("Temperature at North Pole is {Temp}°.", new {Temp = current});

in the foreach loop.

Class FormatCache has been removed in v3.

7. Global support for Alignment (#174)

In v2, Alignment of output values was limited to the DefaultFormatter. It's about the equivalent to e.g. string.Format("{0,10}").

  • Alignment is now supported by all IFormatters.
  • Introduced FormatterSettings.AlignmentFillCharacter, to customize the the fill character. Default is space (0x20), like with string.Format.
  • Modified ListFormatter so that items can be aligned (but the spacers stay untouched).

8. Added StringSource as another ISource (#178, #216)

The StringSource takes over functionality, which have been implemented in ReflectionSource in v2. Compared to reflection caching, speed is 20% better at 25% less memory allocation.

StringSource brings the following built-in methods (as selector names):

  • Length
  • ToUpper
  • ToUpperInvariant
  • ToLower
  • ToLowerInvariant
  • Trim
  • TrimStart
  • TrimEnd
  • ToCharArray
  • Capitalize
  • CapitalizeWords
  • FromBase64
  • ToBase64

All these selector names may be linked. Example with an indexed placeholder:

Smart.Format("{0.ToLower.TrimStart.TrimEnd.ToBase64}", " ABCDE ");
// result: "YWJjZGU="

9. Introduced Nullable Notation (#176)

C# like nullable notation allows to display Nullable<T> types.

The Smart.Format notation is "{SomeNullable?.Property}". If SomeNullable is null, the expression is evaluated as string.Empty.

The nullable operator can also be used for evaluating a list index. E.g.: Smart.Format("{TheList?[1]}") will output string.Empty, if the list variable is null.

Note: Trying to evaluate null without the nullable operator will result in a formatting exception. This is the same behavior as in v2.

All Format() methods accept nullable args (#196). Opposed to string.Format null(able) arguments are allowed.

10. Added NullFormatter (#176, #199)

In the context of Nullable Notation, the NullFormatter has been added. It outputs a custom string literal, if the variable is null, else another literal (default is string.Empty) or Placeholder.


Smart.Format("{TheValue:isnull:This value is null}", new {TheValue = null});
// Result: "This value is null"
Smart.Format("{TheValue:isnull:The value is null|The value is {}}", new {TheValue = 1234});
// Result: "The value is 1234"

11. Added LocalizationFormatter (#176

  • Added LocalizationFormatter to localize literals and placeholders
  • Added ILocalizationProvider and a standard implemention as LocalizationProvider, which handles resx resource files. A fallback culture can be set. It will be used, in case no item for a certain culture could be found in any of the resources. LocalizationProvider can search an unlimited number of defined resoures.
  • SmartSettings were exended with category Localization. That way, custom IFormatters can also make use of localization, if needed.
  • Added LocalizationFormattingException, which is derived from FormattingException to easily identify this kind of issues

Culture-specific results shown here are included in embedded resource files, which are omitted for brevity.

  1. Localize pure literals into Spanish:
// culture supplied as a format option
_ = Smart.Format(culture, "{:L(en):WeTranslateText}");
// culture supplied as an argument to the formatter
var culture = CultureInfo.GetCultureInfo("es");
_ = Smart.Format(culture, "{:L:WeTranslateText}");
// result for both: "Traducimos el texto"
  1. Localized strings may contain placeholders
_ = Smart.Format("{0} {1:L(es):has {:#,#} inhabitants}", "X-City", 8900000);
// result: "X-City tiene 8.900.000 habitantes"
_ = Smart.Format("{0} {1:L(es):has {:#,#} inhabitants}", "X-City", 8900000);
// result: "X-City has 8,900,000 inhabitants"
  1. Localization can be used together with other formatters
_ = Smart.Format("{0:plural:{:L(en):{} item}|{:L(en):{} items}}", 0;
// result for English: 0 items
_ = Smart.Format("{0:plural:{:L(fr):{} item}|{:L(fr):{} items}}", 0;
// result for French: 0 élément
_ = Smart.Format("{0:plural:{:L(fr):{} item}|{:L(fr):{} items}}", 200;
// result for French: 200 éléments

12. Improved custom ISource and IFormatter implementations (#180)

Any custom exensions can implement IInitializer. Then, the SmartFormatter will call Initialize(SmartFormatter smartFormatter) of the extension, before adding it to the extension list.

13. IFormatters have one single, unique name (#185)

In v2, IFormatters could have an unlimited number of names. To improve performance, in v3, this is limited to one single, unique name.

14. JSON support (#177, #201)

Separation of JsonSource into 2 ISource extensions:

  • NewtonSoftJsonSource
  • SystemTextJsonSource

Fix: NewtonSoftJsonSource handles null values correctly (#201)

15. SmartFormatter takes IList<object> parameters

Added support for IList<object> parameters to the SmartFormatter (thanks to @karljj1) (#154)

16. SmartObjects have been removed

  • Removed obsolete SmartObjects (which have been replaced by ValueTuple) (092b7b1)

17. Improved parsing of HTML input (#203)

Introduced experimental bool ParserSettings.ParseInputAsHtml. The default is false.

If true, theParser will parse all content inside <script> and <style> tags as LiteralText. All other places may still contain Placeholders.

This is because <script> and <style> tags may contain curly or square braces, that interfere with the SmartFormat {Placeholder}.

Best results can only be expected with clean HTML: balanced opening and closing tags, single and double quotes. Also, do not use angle brackets, single and double quotes in script or style comments.

SmartFormat is not a fully-fledged HTML parser. If this is required, use AngleSharp or HtmlAgilityPack.

18. Refactored PluralLocalizationFormatter (#209)

  • Constructor with string argument for default language is obsolete.
  • Property DefaultTwoLetterISOLanguageName is obsolete.
  • Culture is now determined in this sequence (same as with LocalizationFormatter):<br/>
    1. Get the culture from the FormattingInfo.FormatterOptions.<br/>
    2. Get the culture from the IFormatProvider argument (which may be a CultureInfo) to SmartFormatter.Format(IFormatProvider, string, object?[])<br/>
    3. The CultureInfo.CurrentUICulture<br/>

19. Refactored TimeFormatter (#220, #221)

  • Constructor with string argument for default language is obsolete.
  • Property DefaultTwoLetterISOLanguageName is obsolete.
  • Culture is now determined in this sequence (same as with LocalizationFormatter and PluralLocalizationFormatter):<br/>
    1. Get the culture from the FormattingInfo.FormatterOptions.<br/>
    2. Get the culture from the IFormatProvider argument (which may be a CultureInfo) to SmartFormatter.Format(IFormatProvider, string, object?[])<br/>
    3. The CultureInfo.CurrentUICulture<br/>
  • New: With the extended CommonLanguagesTimeTextInfo, TimeFormatter includes French, Spanish, Portuguese, Italian and German as new languages besides English out-of-the-box.
  • New: With e.g. TimeFormatter.FallbackLanguage = "en";, this fallback language will be used, if no supported language could be found.
  • New: Custom languages can now easily be added to CommonLanguagesTimeTextInfo. Custom languages override built-in definitions.
    var language = "nl"; // dummy - it's English, not Dutch ;-)
    TimeTextInfo custom = new(
        pluralRule: PluralRules.GetPluralRule(language),
        week: new[] { "{0} week", "{0} weeks" },
        day: new[] { "{0} day", "{0} days" },
        hour: new[] { "{0} hour", "{0} hours" },
        minute: new[] { "{0} minute", "{0} minutes" },
        second: new[] { "{0} second", "{0} seconds" },
        millisecond: new[] { "{0} millisecond", "{0} milliseconds" },
        w: new[] { "{0}w" },
        d: new[] { "{0}d" },
        h: new[] { "{0}h" },
        m: new[] { "{0}m" },
        s: new[] { "{0}s" },
        ms: new[] { "{0}ms" },
        lessThan: "less than {0}");
    CommonLanguagesTimeTextInfo.AddLanguage(language, custom)
  • Changed:
    1. This notation - using formats as formatter options - was allowed in Smart.Format v2.x, but is now depreciated. It is still detected and working, as long as the format part is left empty
    var formatDepreciated = "{0:time(abbr hours noless)}";
    1. This format string is recommended for Smart.Format v3 and later. It allows for including the language as an option to the TimeFormatter:
    // Without language option:
    var formatRecommended = "{0:time:abbr hours noless:}";
    // With language option:
    var formatRecommended = "{0:time(en):abbr hours noless:}";
  • PRs for extending built-in languages are welcome.
  • Example:
    var timeSpan = new TimeSpan(1,1,1,1,1)
    Smart.Format("{0:time(en):hours minutes}", timeSpan);
    // result: "25 hours 1 minute"
    Smart.Format("{0:time(fr):hours minutes}", timeSpan);
    // result: "25 heures 1 minute"
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (58)

Showing the top 5 NuGet packages that depend on SmartFormat.NET:

Package Downloads

The C# SDK for the Messaging Hub


Elephant storage library


Elephant storage library


BLiP C# client


Package Description

GitHub repositories (12)

Showing the top 5 popular GitHub repositories that depend on SmartFormat.NET:

Repository Stars
Windows tray utility / filesystem watcher / launcher for Syncthing
A lightweight text templating library written in C# which can be a drop-in replacement for string.Format
A simple recipe for .NET Core microservices.
DSLR camera remote control open source software
LocalizationExtension is a the easy way to localize any type of DependencyProperties or native Properties on DependencyObjects
Version Downloads Last updated
3.4.0 2,024 5/15/2024
3.3.2 103,458 1/29/2024
3.3.1 24,667 1/14/2024
3.3.0 134,491 9/25/2023
3.2.2 65,489 8/3/2023
3.2.1 336,904 2/19/2023
3.2.0 346,574 9/18/2022
3.1.0 379,027 5/17/2022
3.0.0 180,200 3/14/2022
3.0.0-rc.2 2,846 2/15/2022
3.0.0-rc.1 1,855 2/1/2022
3.0.0-alpha.5 1,153 12/3/2021
3.0.0-alpha.4 1,799 10/25/2021
3.0.0-alpha.3 1,018 9/20/2021
3.0.0-alpha.2 731 9/5/2021
3.0.0-alpha.1 2,915 8/29/2021
2.7.3 292,456 6/28/2022
2.7.2 312,480 12/26/2021
2.7.1 295,318 10/21/2021
2.7.0 1,278,875 4/10/2021
2.6.2 347,267 2/17/2021
2.6.1 9,948 2/12/2021
2.5.3 956,792 11/10/2020
2.5.3-preview1 4,342 11/2/2020
2.5.2 366,473 6/16/2020
2.5.1 16,667 6/9/2020
2.5.0 782,531 11/2/2019
2.4.2 1,179,534 2/9/2019
2.4.1 51,789 1/5/2019
2.4.0 26,311 1/4/2019 164,388 11/15/2018
2.3.0 763,117 6/13/2018
2.2.0 146,649 2/17/2018 1,033,819 8/15/2017 561,991 6/27/2017
2.0.0 419,590 12/4/2016 16,216 11/28/2016
1.6.1 136,366 3/4/2015
1.6.0 5,685 3/4/2015
1.5.0 5,617 2/12/2015
1.4.0 4,196 2/12/2015
1.3.0 4,143 2/12/2015
1.2.0 8,580 2/7/2015
1.1.0 33,220 2/20/2014
1.0.0 21,534 5/15/2013

See the change log for details of this release: