summaryrefslogtreecommitdiff
path: root/src/SMAPI/Translation.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI/Translation.cs')
-rw-r--r--src/SMAPI/Translation.cs32
1 files changed, 18 insertions, 14 deletions
diff --git a/src/SMAPI/Translation.cs b/src/SMAPI/Translation.cs
index 149f6728..01cb92b2 100644
--- a/src/SMAPI/Translation.cs
+++ b/src/SMAPI/Translation.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Text.RegularExpressions;
@@ -15,14 +16,14 @@ namespace StardewModdingAPI
/// <summary>The placeholder text when the translation is <c>null</c> or empty, where <c>{0}</c> is the translation key.</summary>
internal const string PlaceholderText = "(no translation:{0})";
- /// <summary>The locale for which the translation was fetched.</summary>
+ /// <summary>The locale for which the translation was fetched like <c>fr-FR</c>, or an empty string for English.</summary>
private readonly string Locale;
/// <summary>The underlying translation text.</summary>
- private readonly string Text;
+ private readonly string? Text;
/// <summary>The value to return if the translations is undefined.</summary>
- private readonly string Placeholder;
+ private readonly string? Placeholder;
/*********
@@ -39,12 +40,12 @@ namespace StardewModdingAPI
/// <param name="locale">The locale for which the translation was fetched.</param>
/// <param name="key">The translation key.</param>
/// <param name="text">The underlying translation text.</param>
- internal Translation(string locale, string key, string text)
+ internal Translation(string locale, string key, string? text)
: this(locale, key, text, string.Format(Translation.PlaceholderText, key)) { }
/// <summary>Replace the text if it's <c>null</c> or empty. If you set a <c>null</c> or empty value, the translation will show the fallback "no translation" placeholder (see <see cref="UsePlaceholder"/> if you want to disable that). Returns a new instance if changed.</summary>
/// <param name="default">The default value.</param>
- public Translation Default(string @default)
+ public Translation Default(string? @default)
{
return this.HasValue()
? this
@@ -52,7 +53,7 @@ namespace StardewModdingAPI
}
/// <summary>Whether to return a "no translation" placeholder if the translation is <c>null</c> or empty. Returns a new instance.</summary>
- /// <param name="use">Whether to return a placeholder.</param>
+ /// <param name="use">Whether to return a placeholder. <strong>Due to limitations with nullable reference types, setting this to <c>false</c> will still mark the text non-nullable.</strong></param>
public Translation UsePlaceholder(bool use)
{
return new Translation(this.Locale, this.Key, this.Text, use ? string.Format(Translation.PlaceholderText, this.Key) : null);
@@ -61,20 +62,20 @@ namespace StardewModdingAPI
/// <summary>Replace tokens in the text like <c>{{value}}</c> with the given values. Returns a new instance.</summary>
/// <param name="tokens">An object containing token key/value pairs. This can be an anonymous object (like <c>new { value = 42, name = "Cranberries" }</c>), a dictionary, or a class instance.</param>
/// <exception cref="ArgumentNullException">The <paramref name="tokens"/> argument is <c>null</c>.</exception>
- public Translation Tokens(object tokens)
+ public Translation Tokens(object? tokens)
{
if (string.IsNullOrWhiteSpace(this.Text) || tokens == null)
return this;
// get dictionary of tokens
- IDictionary<string, string> tokenLookup = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ Dictionary<string, string?> tokenLookup = new(StringComparer.OrdinalIgnoreCase);
{
// from dictionary
if (tokens is IDictionary inputLookup)
{
foreach (DictionaryEntry entry in inputLookup)
{
- string key = entry.Key?.ToString().Trim();
+ string? key = entry.Key.ToString()?.Trim();
if (key != null)
tokenLookup[key] = entry.Value?.ToString();
}
@@ -95,8 +96,8 @@ namespace StardewModdingAPI
string text = Regex.Replace(this.Text, @"{{([ \w\.\-]+)}}", match =>
{
string key = match.Groups[1].Value.Trim();
- return tokenLookup.TryGetValue(key, out string value)
- ? value
+ return tokenLookup.TryGetValue(key, out string? value)
+ ? (value ?? "")
: match.Value;
});
return new Translation(this.Locale, this.Key, text);
@@ -109,18 +110,21 @@ namespace StardewModdingAPI
}
/// <summary>Get the translation text. Calling this method isn't strictly necessary, since you can assign a <see cref="Translation"/> value directly to a string.</summary>
+ /// <remarks><strong>Limitation with nullable reference types: if there's no text and you disabled the fallback via <see cref="UsePlaceholder"/>, this will return null but the return value will still be marked non-nullable.</strong></remarks>
public override string ToString()
{
return this.Placeholder != null && !this.HasValue()
? this.Placeholder
- : this.Text;
+ : this.Text!;
}
/// <summary>Get a string representation of the given translation.</summary>
/// <param name="translation">The translation key.</param>
+ /// <remarks><strong>Limitation with nullable reference types: if there's no text and you disabled the fallback via <see cref="UsePlaceholder"/>, this will return null but the return value will still be marked non-nullable.</strong></remarks>
+ [SuppressMessage("ReSharper", "ConstantConditionalAccessQualifier", Justification = "The null check is required due to limitations in nullable type annotations (see remarks).")]
public static implicit operator string(Translation translation)
{
- return translation?.ToString();
+ return translation?.ToString()!;
}
@@ -132,7 +136,7 @@ namespace StardewModdingAPI
/// <param name="key">The translation key.</param>
/// <param name="text">The underlying translation text.</param>
/// <param name="placeholder">The value to return if the translations is undefined.</param>
- private Translation(string locale, string key, string text, string placeholder)
+ private Translation(string locale, string key, string? text, string? placeholder)
{
this.Locale = locale;
this.Key = key;