From 417e65479977f954b9cea52475d925d0f71a4ed6 Mon Sep 17 00:00:00 2001 From: Jo-Be-Co Date: Mon, 20 Apr 2026 23:06:21 +0200 Subject: [PATCH 1/2] #1754 Conditional output on format templates --- .../NamingTemplate/CommonFormatters.cs | 33 +++++++++++++++---- .../NamingTemplate/RegExpExtensions.cs | 4 +-- .../TemplatesTests.cs | 4 +++ 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Source/FileManager/NamingTemplate/CommonFormatters.cs b/Source/FileManager/NamingTemplate/CommonFormatters.cs index 2b2b668a..0636666c 100644 --- a/Source/FileManager/NamingTemplate/CommonFormatters.cs +++ b/Source/FileManager/NamingTemplate/CommonFormatters.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using Dinah.Core; namespace FileManager.NamingTemplate; @@ -43,7 +44,7 @@ public static partial class CommonFormatters if (string.IsNullOrWhiteSpace(formatString) || !StringFormatRegex().TryMatch(formatString, out var match)) return value; // first shorten the string if a number is specified in the format string - if (int.TryParse(match.Groups["left"].ValueSpan, out var length) && length < value.Length) + if (match.TryParseInt("left", out var length) && length < value.Length) value = value[..length]; culture ??= CultureInfo.CurrentCulture; @@ -68,7 +69,7 @@ public static partial class CommonFormatters // is this function is called from toString implementation of the IFormattable interface, we only get a IFormatProvider var culture = GetCultureInfo(provider); var oldUiCulture = Thread.CurrentThread.CurrentUICulture; - var result = CollapseSpacesAndTrimRegex().Replace(TagFormatRegex().ReplaceWithGaps(templateString, GetValueForMatchingTag, Unescape), ""); + var result = CollapseSpacesAndTrimRegex().Replace(TagFormatRegex().ReplaceWithGaps(templateString, GetValueForMatchingTag, Unescape), string.Empty); Thread.CurrentThread.CurrentUICulture = oldUiCulture; return result; @@ -77,17 +78,19 @@ public static partial class CommonFormatters var tag = m.Groups["tag"].Value; if (!replacements.TryGetValue(tag, out var getter)) return m.Value; - var lang = m.Groups["lang"].ValueOrNull(); + var lang = m.ResolveValue("lang"); var cultureToUse = lang is null ? culture : CultureInfo.GetCultureInfo(lang); Thread.CurrentThread.CurrentUICulture = cultureToUse ?? oldUiCulture; - + var value = getter(toFormat); - var format = m.Groups["format"].ValueOrNull(); - return value switch + var format = m.ResolveValue("format"); + + var formatted = value switch { IFormattable formattable => formattable.ToString(format, cultureToUse), _ => _StringFormatter(value?.ToString(), format, cultureToUse), }; + return formatted.IsNullOrEmpty() ? string.Empty : m.UnescapeValue("pre") + formatted + m.UnescapeValue("post"); } } @@ -112,6 +115,12 @@ public static partial class CommonFormatters | "[^"]*" # - allow "string" to be included in the format, with "" being an escaped " character | [^\\'"] # - match any other character. This will not catch the tag format at first. Because ... ) *? ) # With *? the pattern above tries not to consume the tag format. + (?: \{ (?
 (?:            # With an optional extra '{' surroundings of the tag format might be specified in a group called '
'.
+	                    \\.                       # - '\' escapes always the next character. Especially further '\' and the opening '{'
+	                    | '[^']*'                 # - allow 'string' to be included in the format
+	                    | "[^"]*"                 # - allow "string" to be included in the format
+	                    | [^\\'"{}]               # - match any other character. We also don't want further unescaped '{' or '}' in the format.
+	                ) * ) )?                      # Capture all up to the next unescaped '{' to get the optional '
' group.
 	                \{ (? [A-Z0-9]+ | \#)    # Capture the tags name as ''. It is always enclosed in '{' and '}'. It may only contain letters and numbers or be a single '#'.
 	                    (?:@(?[a-z-]+))?    # Introduced by '@' the tag name may optionally be followed by a language specifier captured in a group called ''.
 	                    (?::(?(?:
@@ -121,7 +130,17 @@ public static partial class CommonFormatters
 	                        | .
 	                    ) *? ))?
 	                \}
-	                """, RegexOptions.IgnoreCase)]
+	                (?(pre)
+	                    (?(?:
+	                        \\.
+	                        | '[^']*'
+	                        | "[^"]*"
+	                        | [^\\'"{}]
+	                    ) * ) \}
+	                    |
+	                )
+	                """,
+		RegexOptions.IgnoreCase)]
 	public static partial Regex TagFormatRegex();
 
 	public static string FormattableFormatter(ITemplateTag _, IFormattable? value, string? formatString, CultureInfo? culture)
diff --git a/Source/FileManager/NamingTemplate/RegExpExtensions.cs b/Source/FileManager/NamingTemplate/RegExpExtensions.cs
index e29f745d..9830d3ad 100644
--- a/Source/FileManager/NamingTemplate/RegExpExtensions.cs
+++ b/Source/FileManager/NamingTemplate/RegExpExtensions.cs
@@ -10,8 +10,8 @@ public static class RegExpExtensions
 	extension(Group group)
 	{
 		public string? ValueOrNull() => group.Success ? group.Value : null;
-		public string? UnescapeValueOrNull() => group.Success ? CommonFormatters.Unescape(group.ValueSpan, []) : null;
-		public string UnescapeValue() => group.Success ? CommonFormatters.Unescape(group.ValueSpan, []) : string.Empty;
+		public string? UnescapeValueOrNull() => group.Success ? CommonFormatters.Unescape(group.ValueSpan, ['\'', '"']) : null;
+		public string UnescapeValue() => group.Success ? CommonFormatters.Unescape(group.ValueSpan, ['\'', '"']) : string.Empty;
 		public ReadOnlySpan ValueSpanOrNull() => group.Success ? group.ValueSpan : null;
 	}
 
diff --git a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs
index cf1a36a1..adc1d1f4 100644
--- a/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs
+++ b/Source/_Tests/LibationFileManager.Tests/TemplatesTests.cs
@@ -416,6 +416,10 @@ namespace TemplatesTests
 		[DataRow("", "Jill Conner Browne")]
 		[DataRow("", "Jill Conner Browne")]
 		[DataRow(@"()", "()[Conner]()()[E.]()()[John]()()[Maud]()()[]()()[]()()[]()")]
+		[DataRow("", "Conner; E.; John; Maud; ; ;")]
+		[DataRow(@"", "[{Conner}]; [{E.}]; [{John}]; [{Maud}]; [{}]; [{}]; [{}]")]
+		[DataRow(@"", "[{Conner}]; [{E.}]; [{John}]; [{Maud}]; ; ;")]
+		[DataRow(@"", "Lucy-Maud-MONTGOMERY-[B4], Jon-BON JOVI-[B5]")]
 		[DataRow("", "Jill Conner Browne")]
 		[DataRow("", "Jill Conner Browne")]
 		[DataRow("", "Browne, Jill")]

From 2b25ced65be05f5fdc202223ae44005e3100038a Mon Sep 17 00:00:00 2001
From: Jo-Be-Co 
Date: Tue, 21 Apr 2026 00:18:14 +0200
Subject: [PATCH 2/2] documentation for format-templates

---
 docs/features/naming-templates.md | 120 +++++++++++++++++++-----------
 1 file changed, 77 insertions(+), 43 deletions(-)

diff --git a/docs/features/naming-templates.md b/docs/features/naming-templates.md
index a02b95a4..1d445b30 100644
--- a/docs/features/naming-templates.md
+++ b/docs/features/naming-templates.md
@@ -66,13 +66,13 @@ Anything between the opening tag (``) and closing tag (`<-tagname>`) w
 | \...\<-if series\>                                    | Only include if part of a book series or podcast                                                | Conditional |
 | \...\<-if podcast\>                                  | Only include if part of a podcast                                                               | Conditional |
 | \...\<-if bookseries\>                            | Only include if part of a book series                                                           | Conditional |
-| \...\<-if podcastparent\>**†**                 | Only include if item is a podcast series parent                                                 | Conditional |
+| \...\<-if podcastparent\> **†**                | Only include if item is a podcast series parent                                                 | Conditional |
 | \...\<-if abridged\>                                | Only include if item is abridged                                                                | Conditional |
 | \...\<-has\>                                       | Only include if the PROPERTY has a value (i.e. not null or empty)                               | Conditional |
 | \...\<-is\>                       | Only include if the PROPERTY or a single value of a list PROPERTY satisfies the CHECK           | Conditional |
 | \...\<-is\>               | Only include if the formatted PROPERTY or a single value of a list PROPERTY satisfies the CHECK | Conditional |
 | \...\<-is\> | Only include if the joined form of all formatted values of a list PROPERTY satisfies the CHECK  | Conditional |
-| \...\<-cmp\>              | Only include if two given PROPERTIES satisfy the CHECK                                          | Conditional |
+| \...\<-cmp\>   | Only include if two given PROPERTIES satisfy the CHECK                                          | Conditional |
 
 **†** Only affects the podcast series folder naming if "Save all podcast episodes to the series parent folder" option is checked.
 
@@ -85,7 +85,7 @@ You can invert the condition (instead of displaying the text when the condition
 | \...\<-if series\>                       | Only include if _not_ part of a book series or podcast                                           | Conditional |
 | \...\<-if podcast\>                     | Only include if _not_ part of a podcast                                                          | Conditional |
 | \...\<-if bookseries\>               | Only include if _not_ part of a book series                                                      | Conditional |
-| \...\<-if podcastparent\>**†**    | Only include if item is _not_ a podcast series parent                                            | Conditional |
+| \...\<-if podcastparent\> **†**   | Only include if item is _not_ a podcast series parent                                            | Conditional |
 | \...\<-has\>                          | Only include if the PROPERTY _does not_ have a value (i.e. is null or empty)                     | Conditional |
 | \...\<-is\>          | Only include if neither the whole PROPERTY nor the values of a list PROPERTY satisfies the CHECK | Conditional |
 | \...\<-cmp\> | Only include if two given PROPERTIES _do not_ satisfy the CHECK                                  | Conditional |
@@ -127,49 +127,59 @@ Text formatting can change length and case of the text. Use \<#\>, \<#\>\
 
 ### Text List Formatters
 
-| Formatter     | Description                                                                                                                                                              | Example Usage                                | Example Result                               |
-| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |--------------------------------------------- | ---------------------------------------------|
-| separator()   | Specify the text used to join
multiple entries.

Default is ", " | `` | Tag1_Tag2_Tag3_Tag4_Tag5 | -| format(\{S\}) | Formats the entries by placing their values into the specified template.
Use {S:[Text_Formatter](#text-formatters)} to place the entry and optionally apply a format. | ``separator(;)]>` | Tag=tag1;Tag=tag2;Tag=tag3;Tag=tag4;Tag=tag5 | -| sort(S) | Sorts the elements by their value.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``separator(;)]>` | Tag5;Tag4;Tag3;Tag2;Tag1 | -| max(#) | Only use the first # of entries | `` | Tag1 | -| slice(#) | Only use the nth entry of the list | `` | Tag2 | -| slice(#..) | Only use entries of the list starting from # | `` | Tag2, Tag3, Tag4, Tag5 | -| slice(..#) | Like max(#). Only use the first # of entries | `` | Tag1 | -| slice(#..#) | Only use entries of the list starting from # and ending at # (inclusive) | `` | Tag2, Tag3, Tag4 | -| slice(-#..-#) | Numbers may be specified negative. In that case positions ar counted from the end with -1 pointing at the last member | `` | Tag3, Tag4 | +| Formatter | Description | Example Usage | Example Result | +|----------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |--------------------------------------------- | ---------------------------------------------| +| separator() | Specify the text used to join
multiple entries.

Default is ", " | `` | Tag1_Tag2_Tag3_Tag4_Tag5 | +| format(\{S\}) **†** | Formats the entries by placing their values into the specified template.
Use {S:[Text_Formatter](#text-formatters)} to place the entry and optionally apply a format. | ``separator(;)]>` | Tag=tag1;Tag=tag2;Tag=tag3;Tag=tag4;Tag=tag5 | +| sort(S) | Sorts the elements by their value.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``separator(;)]>` | Tag5;Tag4;Tag3;Tag2;Tag1 | +| max(#) | Only use the first # of entries | `` | Tag1 | +| slice(#) | Only use the nth entry of the list | `` | Tag2 | +| slice(#..) | Only use entries of the list starting from # | `` | Tag2, Tag3, Tag4, Tag5 | +| slice(..#) | Like max(#). Only use the first # of entries | `` | Tag1 | +| slice(#..#) | Only use entries of the list starting from # and ending at # (inclusive) | `` | Tag2, Tag3, Tag4 | +| slice(-#..-#) | Numbers may be specified negative. In that case positions ar counted from the end with -1 pointing at the last member | `` | Tag3, Tag4 | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. ### Series Formatters -| Formatter | Description | Example Usage | Example Result | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -| \{N \| # \| ID\} | Formats the series using
the series part tags.
\{N:[Text_Formatter](#text-formatters)\} = Series Name
\{#:[Number_Formatter](#number-formatters)\} = Number order in series
\{ID:[Text_Formatter](#text-formatters)\} = Audible Series ID

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{N\} | ``\``\``\`` | Sherlock Holmes\sherlock holmes\Sherlock Holmes, 1-6, B08376S3R2\SHERLOCK H, B08376S3R2, 01.0-06.0 | +| Formatter | Description | Example Usage | Example Result | +|-------------------------| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| \{N \| # \| ID\} **†** | Formats the series using
the series part tags.
\{N:[Text_Formatter](#text-formatters)\} = Series Name
\{#:[Number_Formatter](#number-formatters)\} = Number order in series
\{ID:[Text_Formatter](#text-formatters)\} = Audible Series ID

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{N\} | ``\``\``\`` | Sherlock Holmes\sherlock holmes\Sherlock Holmes, 1-6, B08376S3R2\SHERLOCK H, B08376S3R2, 01.0-06.0 | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. ### Series List Formatters -| Formatter | Description | Example Usage | Example Result | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| separator() | Specify the text used to join
multiple series names.

Default is ", " | `` | Sherlock Holmes; Some Other Series | -| format(\{N \| # \| ID\}) | Formats the series properties
using the name series tags.
See [Series Formatter Usage](#series-formatters) above. | ``separator(; )]>`\`` | Sherlock Holmes, 1-6; Book Collection, 1\B08376S3R2-Sherlock Holmes, 01.0-06.0, B000000000-Book Collection, 01.0 | -| sort(N \| # \| ID) | Sorts the series by name, number or ID.

These terms define the primary, secondary, tertiary, … sorting order.
You may combine multiple terms in sequence to specify multi‑level sorting.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``separator(; )]>` | Book Collection, 1; Sherlock Holmes, 1-6 | -| max(#) | Only use the first # of series | `` | Sherlock Holmes | -| slice(#..#) | Only use entries of the series list starting from # and ending at # (inclusive)

See [Text List Formatter Usage](#Text-List-Formatters) above for details on all the variants of `slice()` | `` | Sherlock Holmes | +| Formatter | Description | Example Usage | Example Result | +|---------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | +| separator() | Specify the text used to join
multiple series names.

Default is ", " | `` | Sherlock Holmes; Some Other Series | +| format(\{N \| # \| ID\}) **†** | Formats the series properties
using the name series tags.
See [Series Formatter Usage](#series-formatters) above. | ``separator(; )]>`\`` | Sherlock Holmes, 1-6; Book Collection, 1\B08376S3R2-Sherlock Holmes, 01.0-06.0, B000000000-Book Collection, 01.0 | +| sort(N \| # \| ID) | Sorts the series by name, number or ID.

These terms define the primary, secondary, tertiary, … sorting order.
You may combine multiple terms in sequence to specify multi‑level sorting.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``separator(; )]>` | Book Collection, 1; Sherlock Holmes, 1-6 | +| max(#) | Only use the first # of series | `` | Sherlock Holmes | +| slice(#..#) | Only use entries of the series list starting from # and ending at # (inclusive)

See [Text List Formatter Usage](#Text-List-Formatters) above for details on all the variants of `slice()` | `` | Sherlock Holmes | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. ### Name Formatters -| Formatter | Description | Example Usage | Example Result | -| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------- | -| \{T \| F \| M \| L \| S \| ID\} | Formats the human name using
the name part tags.
\{T:[Text_Formatter](#text-formatters)\} = Title (e.g. "Dr.")
\{F:[Text_Formatter](#Text-Formatters)\} = First name
\{M:[Text_Formatter](#text-formatters)\} = Middle name
\{L:[Text_Formatter](#text-formatters)\} = Last Name
\{S:[Text_Formatter](#text-formatters)\} = Suffix (e.g. "PhD")
\{ID:[Text_Formatter](#text-formatters)\} = Audible Contributor ID

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the correspoing formatter.

Default is \{T\} \{F\} \{M\} \{L\} \{S\} | ``\`` | Fry, S.\DOYLE, Arthur \_B000AQ43GQ\_ | +| Formatter | Description | Example Usage | Example Result | +|----------------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | --------------------------------------- | +| \{T \| F \| M \| L \| S \| ID\} **†** | Formats the human name using
the name part tags.
\{T:[Text_Formatter](#text-formatters)\} = Title (e.g. "Dr.")
\{F:[Text_Formatter](#Text-Formatters)\} = First name
\{M:[Text_Formatter](#text-formatters)\} = Middle name
\{L:[Text_Formatter](#text-formatters)\} = Last Name
\{S:[Text_Formatter](#text-formatters)\} = Suffix (e.g. "PhD")
\{ID:[Text_Formatter](#text-formatters)\} = Audible Contributor ID

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the correspoing formatter.

Default is \{T\} \{F\} \{M\} \{L\} \{S\} | ``\`` | Fry, S.\DOYLE, Arthur \_B000AQ43GQ\_ | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. ### Name List Formatters -| Formatter | Description | Example Usage | Example Result | -| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | -| separator() | Specify the text used to join
multiple people's names.

Default is ", " | `` | Arthur Conan Doyle; Stephen Fry | -| format(\{T \| F \| M \| L \| S \| ID\}) | Formats the human name using
the name part tags.
See [Name Formatter Usage](#name-formatters) above. | ``separator(; )]>`\``_{ID}_) separator(; )]>` | DOYLE, Arthur; FRY, Stephen\Doyle, A. \_B000AQ43GQ\_;
Fry, S. \_B000APAGVS\_ | -| sort(T \| F \| M \| L \| S \| ID) | Sorts the names by title,
first, middle, or last name,
suffix or Audible Contributor ID

These terms define the primary, secondary, tertiary, … sorting order.
You may combine multiple terms in sequence to specify multi‑level sorting.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``\``\`` | Stephen Fry, Arthur Conan Doyle\Stephen King, Stephen Fry\John P. Smith \_B000TTTBBB\_, John P. Smith \_B000TTTCCC\_, John S. Smith \_B000HHHVVV\_ | -| max(#) | Only use the first # of names

Default is all names | `` | Arthur Conan Doyle | -| slice(#..#) | Only use entries of the names list starting from # and ending at # (inclusive)

See [Text List Formatter Usage](#Text-List-Formatters) above for details on all the variants of `slice()` | `` | Arthur Conan Doyle | +| Formatter | Description | Example Usage | Example Result | +|------------------------------------------------| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| separator() | Specify the text used to join
multiple people's names.

Default is ", " | `` | Arthur Conan Doyle; Stephen Fry | +| format(\{T \| F \| M \| L \| S \| ID\}) **†** | Formats the human name using
the name part tags.
See [Name Formatter Usage](#name-formatters) above. | ``separator(; )]>`\``_{ID}_) separator(; )]>` | DOYLE, Arthur; FRY, Stephen\Doyle, A. \_B000AQ43GQ\_;
Fry, S. \_B000APAGVS\_ | +| sort(T \| F \| M \| L \| S \| ID) | Sorts the names by title,
first, middle, or last name,
suffix or Audible Contributor ID

These terms define the primary, secondary, tertiary, … sorting order.
You may combine multiple terms in sequence to specify multi‑level sorting.

*Sorting direction:*
uppercase = ascending
lowercase = descending

Default is unsorted | ``\``\`` | Stephen Fry, Arthur Conan Doyle\Stephen King, Stephen Fry\John P. Smith \_B000TTTBBB\_, John P. Smith \_B000TTTCCC\_, John S. Smith \_B000HHHVVV\_ | +| max(#) | Only use the first # of names

Default is all names | `` | Arthur Conan Doyle | +| slice(#..#) | Only use entries of the names list starting from # and ending at # (inclusive)

See [Text List Formatter Usage](#Text-List-Formatters) above for details on all the variants of `slice()` | `` | Arthur Conan Doyle | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. ### TimeSpan Formatters For more custom formatters and examples, [see this guide from Microsoft](https://learn.microsoft.com/en-us/dotnet/standard/base-types/custom-timespan-format-strings). @@ -228,21 +238,45 @@ You can use custom formatters to construct customized DateTime string. For more You can specify which part of a region you are interested in. -| Formatter | Description | Example Usage | Example Result | -|-------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|---------------------------------------| -| \{O \| I \| I2 \| I3 \| E \| N \| W \| L \| T \| ID\} | Formats the region using
the region part tags.
\{O:[Text_Formatter](#text-formatters)\} = Region as used in Libation
\{I:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I2:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I3:[Text_Formatter](#text-formatters)\} = Three letter ISO code
\{E:[Text_Formatter](#text-formatters)\} = English name
\{N:[Text_Formatter](#text-formatters)\} = Native name - OS dependent
\{W:[Text_Formatter](#number-formatters)\} = Unique Windows code
\{L:[Text_Formatter](#text-formatters)\} = Lang code used for this region/store
\{T:[Text_Formatter](#number-formatters)\} = TLD under which the audible store is hosted
\{ID:[Text_Formatter](#text-formatters)\} = Region code


Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{O\} | ``
`www.audible.` | US (United States)
www.audible.com | -| \{D\} **†** | Display name interpreted by the current language settings.
To ensure output in a specific language the lang-code to use might be specified with a leading '@'.
Formatter part is also optional and introduced by the colon.
\{D@LANG:[Text_Formatter](#text-formatters)\} | `` | ESTADOS UNIDOS | +| Formatter | Description | Example Usage | Example Result | +|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------|---------------------------------------| +| \{O \| I \| I2 \| I3 \| E \| N \| W \| L \| T \| ID\} **†** | Formats the region using
the region part tags.
\{O:[Text_Formatter](#text-formatters)\} = Region as used in Libation
\{I:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I2:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I3:[Text_Formatter](#text-formatters)\} = Three letter ISO code
\{E:[Text_Formatter](#text-formatters)\} = English name
\{N:[Text_Formatter](#text-formatters)\} = Native name - OS dependent
\{W:[Text_Formatter](#number-formatters)\} = Unique Windows code
\{L:[Text_Formatter](#text-formatters)\} = Lang code used for this region/store
\{T:[Text_Formatter](#number-formatters)\} = TLD under which the audible store is hosted
\{ID:[Text_Formatter](#text-formatters)\} = Region code


Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{O\} | ``
`www.audible.` | US (United States)
www.audible.com | +| \{D\} **†**,**‡** | Display name interpreted by the current language settings.
To ensure output in a specific language the lang-code to use might be specified with a leading '@'.
Formatter part is also optional and introduced by the colon.
\{D@LANG:[Text_Formatter](#text-formatters)\} | `` | ESTADOS UNIDOS | -**†** LANG may be any code from the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) standard like `es` for Spanish, `en` for English, `de` for German, etc. or even a [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag) like 'fr-CA'. +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. + +**‡** LANG may be any code from the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) standard like `es` for Spanish, `en` for English, `de` for German, etc. or even a [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag) like 'fr-CA'. ### Language Formatters You can specify which part of a language you are interested in. -| Formatter | Description | Example Usage | Example Result | -|---------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|----------------| -| \{O \| I \| I2 \| I3 \| E \| N \| W \| ID\} | Formats the language using
the language part tags.
\{O:[Text_Formatter](#text-formatters)\} = Language as provided by audible
\{I:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I2:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I3:[Text_Formatter](#text-formatters)\} = Three letter ISO code
\{E:[Text_Formatter](#text-formatters)\} = English name
\{N:[Text_Formatter](#text-formatters)\} = Native name - OS dependent
\{W:[Text_Formatter](#number-formatters)\} = Unique Windows code
\{ID:[Text_Formatter](#text-formatters)\} = Lang code

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{O\} | `` | fra (French) | -| \{D\} | Display name interpreted by the current language settings.
To ensure output in a specific language the lang-code to use might be specified with a leading '@'.
Formatter part is also optional and introduced by the colon.
\{D@LANG:[Text_Formatter](#text-formatters)\} | `` | francés | +| Formatter | Description | Example Usage | Example Result | +|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|----------------| +| \{O \| I \| I2 \| I3 \| E \| N \| W \| ID\} **†** | Formats the language using
the language part tags.
\{O:[Text_Formatter](#text-formatters)\} = Language as provided by audible
\{I:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I2:[Text_Formatter](#text-formatters)\} = Two letter ISO code
\{I3:[Text_Formatter](#text-formatters)\} = Three letter ISO code
\{E:[Text_Formatter](#text-formatters)\} = English name
\{N:[Text_Formatter](#text-formatters)\} = Native name - OS dependent
\{W:[Text_Formatter](#number-formatters)\} = Unique Windows code
\{ID:[Text_Formatter](#text-formatters)\} = Lang code

Formatter parts are optional and introduced by the colon. If specified the string will be used to format the part using the corresponding formatter.

Default is \{O\} | `` | fra (French) | +| \{D\} **†**,**‡** | Display name interpreted by the current language settings.
To ensure output in a specific language the lang-code to use might be specified with a leading '@'.
Formatter part is also optional and introduced by the colon.
\{D@LANG:[Text_Formatter](#text-formatters)\} | `` | francés | + +**†** For further information on format templates, please refer to the [Format_Templates](#format-templates) section. + +**‡** LANG may be any code from the [ISO 639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) standard like `es` for Spanish, `en` for English, `de` for German, etc. or even a [IETF language tag](https://en.wikipedia.org/wiki/IETF_language_tag) like 'fr-CA'. + +### Format Templates + +Depending on which property is to be displayed, one or more placeholders can be used in a format template. The placeholders are defined in the form `{A}`: + +`` + +The format template must sometimes be enclosed in square brackets and sometimes in round brackets. In addition to placeholders, the format template may also contain arbitrary text. To prevent this text from being mistaken for a bracket at the end of the template or a placeholder, escapes can be used within the text: +* `\x` - Escapes the next character. +* `\\` - Escapes a backslash. +* `"text"` - encloses text that may contain special characters. To include a double quote in the text, escape it by doubling it: `"She said ""Hello"""` will output `She said "Hello"`. +* `'text'`- encloses text that may contain special characters. To include a single quote in the text, escape it by doubling it: `'It''s a test'` will output `It's a test`. + +`` + +Not all elements of a property are always present or have content. In this case, format templates would contain gaps after substitution. Groups of spaces are automatically merged. Other characters, however, remain unchanged. By doubling the curly brackets, you can specify text fragments before and after the placeholder, which are only used if the placeholder is replaced with content. + +`` → `Neil Gaiman, Christopher (Evan) Welch` ### Checks