diff --git a/Gu.Localization.Tests/Gu.Localization.Tests.csproj b/Gu.Localization.Tests/Gu.Localization.Tests.csproj
index 071eadb6..58ee216f 100644
--- a/Gu.Localization.Tests/Gu.Localization.Tests.csproj
+++ b/Gu.Localization.Tests/Gu.Localization.Tests.csproj
@@ -53,8 +53,11 @@
-
-
+
+
+
+
+
@@ -82,6 +85,7 @@
ResXFileCodeGenerator
Resources.Designer.cs
+ Designer
diff --git a/Gu.Localization.Tests/Internals/ResourceManagerExtTests.cs b/Gu.Localization.Tests/Internals/ResourceManagerExtTests.cs
index 6cb343c1..f42995c7 100644
--- a/Gu.Localization.Tests/Internals/ResourceManagerExtTests.cs
+++ b/Gu.Localization.Tests/Internals/ResourceManagerExtTests.cs
@@ -6,6 +6,12 @@
public class ResourceManagerExtTests
{
+ [OneTimeSetUp]
+ public void OneTimeSetUp()
+ {
+ Properties.Resources.ResourceManager.ReleaseAllResources();
+ }
+
[TestCase(null, true)]
[TestCase("sv", true)]
[TestCase("it", false)]
@@ -30,8 +36,6 @@ public void HasKey(string key, string cultureName, bool expected)
: CultureInfo.GetCultureInfo(cultureName);
var resourceManager = Properties.Resources.ResourceManager;
- resourceManager.GetString(key, culture); // warmup
-
Assert.AreEqual(expected, resourceManager.HasKey(key, culture));
Assert.IsNull(resourceManager.GetResourceSet(culture, false, false));
}
diff --git a/Gu.Localization.Tests/Properties/Resources.Designer.cs b/Gu.Localization.Tests/Properties/Resources.Designer.cs
index d6f3da1c..1473cf8c 100644
--- a/Gu.Localization.Tests/Properties/Resources.Designer.cs
+++ b/Gu.Localization.Tests/Properties/Resources.Designer.cs
@@ -87,6 +87,15 @@ internal static string first___0___second__1_ {
}
}
+ ///
+ /// Looks up a localized string similar to Value: {0}.
+ ///
+ internal static string InvalidFormat__0__ {
+ get {
+ return ResourceManager.GetString("InvalidFormat__0__", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to So neutral.
///
@@ -106,11 +115,29 @@ internal static string NoTranslation {
}
///
- /// Looks up a localized string similar to Value: {0}.
+ /// Looks up a localized string similar to Neutral: {0}.
+ ///
+ internal static string ValidFormat__0__ {
+ get {
+ return ResourceManager.GetString("ValidFormat__0__", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Neutral first: {0}, second {1:F2}.
+ ///
+ internal static string ValidFormat__0__1__ {
+ get {
+ return ResourceManager.GetString("ValidFormat__0__1__", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Neutral first: {0}, second {1:F2}, third: {2}.
///
- internal static string Value___0_ {
+ internal static string ValidFormat__0__1__2__ {
get {
- return ResourceManager.GetString("Value___0_", resourceCulture);
+ return ResourceManager.GetString("ValidFormat__0__1__2__", resourceCulture);
}
}
}
diff --git a/Gu.Localization.Tests/Properties/Resources.de.resx b/Gu.Localization.Tests/Properties/Resources.de.resx
index d2ab12f4..a987ebdc 100644
--- a/Gu.Localization.Tests/Properties/Resources.de.resx
+++ b/Gu.Localization.Tests/Properties/Resources.de.resx
@@ -123,4 +123,13 @@
zuerst: {0}, die zweite: {1}
+
+ Wert: {0}
+
+
+ zuerst: {0}, die zweite: {1:F2}
+
+
+ zuerst: {0}, die zweite: {1:F2} meh {2}
+
\ No newline at end of file
diff --git a/Gu.Localization.Tests/Properties/Resources.en.resx b/Gu.Localization.Tests/Properties/Resources.en.resx
index f275ea74..6593b8e6 100644
--- a/Gu.Localization.Tests/Properties/Resources.en.resx
+++ b/Gu.Localization.Tests/Properties/Resources.en.resx
@@ -126,7 +126,16 @@
first: {0}, second:{1}
-
- Value: {0} {1}
+
+ Value: {0} {2}
+
+
+ Value: {0}
+
+
+ English first: {0}, second {1:F2}
+
+
+ English first: {0}, second {1:F2}, third: {2}
\ No newline at end of file
diff --git a/Gu.Localization.Tests/Properties/Resources.resx b/Gu.Localization.Tests/Properties/Resources.resx
index 9ddaafcc..6e94805f 100644
--- a/Gu.Localization.Tests/Properties/Resources.resx
+++ b/Gu.Localization.Tests/Properties/Resources.resx
@@ -132,7 +132,16 @@
first: {0}, second:{1}
-
+
Value: {0}
+
+ Neutral: {0}
+
+
+ Neutral first: {0}, second {1:F2}
+
+
+ Neutral first: {0}, second {1:F2}, third: {2}
+
\ No newline at end of file
diff --git a/Gu.Localization.Tests/Properties/Resources.sv.resx b/Gu.Localization.Tests/Properties/Resources.sv.resx
index ffa0212c..c41f7e3b 100644
--- a/Gu.Localization.Tests/Properties/Resources.sv.resx
+++ b/Gu.Localization.Tests/Properties/Resources.sv.resx
@@ -123,7 +123,16 @@
första: {0}, andra:{1}
-
+
Värde:
+
+ Värde: {0}
+
+
+ första: {0}, andra: {1:F2}
+
+
+ första: {0}, andra: {1:F2}, tredje: {2}
+
\ No newline at end of file
diff --git a/Gu.Localization.Tests/TranslatorTests.OneParameter.cs b/Gu.Localization.Tests/TranslatorTests.OneParameter.cs
new file mode 100644
index 00000000..e7428421
--- /dev/null
+++ b/Gu.Localization.Tests/TranslatorTests.OneParameter.cs
@@ -0,0 +1,70 @@
+namespace Gu.Localization.Tests
+{
+ using System;
+ using System.Globalization;
+
+ using NUnit.Framework;
+
+ public partial class TranslatorTests
+ {
+ public class OneParameter
+ {
+ [TestCase("en", 1, "Value: 1")]
+ [TestCase("sv", 1, "Värde: 1")]
+ [TestCase(null, 1, "Neutral: 1")]
+ public void HappyPath(string cultureName, object arg, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.ValidFormat__0__);
+
+ Translator.CurrentCulture = culture;
+ var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg);
+ Assert.AreEqual(expected, actual);
+
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg);
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestCase("en", 1, "Invalid format string: \"Value: {0} {2}\".")]
+ [TestCase("sv", 1, "Invalid format string: \"Värde: \" for the single argument: 1.")]
+ public void Throws(string cultureName, object arg, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+ Translator.CurrentCulture = culture;
+ var actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, arg));
+ Assert.AreEqual(expected, actual.Message);
+
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg));
+ Assert.AreEqual(expected, actual.Message);
+ }
+
+ [TestCase("en", 1, "{\"Value: {0} {2}\" : 1}")]
+ [TestCase("sv", 1, "{\"Värde: \" : 1}")]
+ public void ReturnsInfo(string cultureName, object arg, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+ Translator.ErrorHandling = ErrorHandling.ReturnErrorInfo;
+ Translator.CurrentCulture = culture;
+ var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg);
+ Assert.AreEqual(expected, actual);
+
+ Translator.ErrorHandling = ErrorHandling.Throw;
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg, ErrorHandling.ReturnErrorInfo);
+ Assert.AreEqual(expected, actual);
+ }
+ }
+ }
+}
diff --git a/Gu.Localization.Tests/TranslatorTests.Parameters.cs b/Gu.Localization.Tests/TranslatorTests.Parameters.cs
deleted file mode 100644
index f80833f4..00000000
--- a/Gu.Localization.Tests/TranslatorTests.Parameters.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-namespace Gu.Localization.Tests
-{
- using System.Globalization;
-
- using NUnit.Framework;
-
- public partial class TranslatorTests
- {
- public class Parameters
- {
- [TestCase("en", 1, "Value: 1")]
- [TestCase("sv", 1, "Värde: 1")]
- [TestCase(null, 1, "Neutral: 1")]
- public void TranslateOneParameterThrow(string cultureName, object arg, string expected)
- {
- Assert.Inconclusive();
- var culture = cultureName != null
- ? CultureInfo.GetCultureInfo(cultureName)
- : CultureInfo.InvariantCulture;
- Translator.CurrentCulture = culture;
- var actual = Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.Value___0_), arg);
- Assert.AreEqual(expected, actual);
-
- Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
- actual = Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.Value___0_), culture, arg);
- Assert.AreEqual(expected, actual);
- }
-
- [Test]
- public void TranslateOneParameterReturnInfo()
- {
- Assert.Inconclusive();
- }
- }
- }
-}
diff --git a/Gu.Localization.Tests/TranslatorTests.Params.cs b/Gu.Localization.Tests/TranslatorTests.Params.cs
new file mode 100644
index 00000000..7739fbf2
--- /dev/null
+++ b/Gu.Localization.Tests/TranslatorTests.Params.cs
@@ -0,0 +1,70 @@
+////namespace Gu.Localization.Tests
+////{
+//// using System;
+//// using System.Globalization;
+
+//// using NUnit.Framework;
+
+//// public partial class TranslatorTests
+//// {
+//// public class Params
+//// {
+//// [TestCase("en", 1, 2.0, 3, "English first: 1, second 2.00, third: 3")]
+//// [TestCase("sv", 1, 2.0, 3, "första: 1, andra: 2,00, tredje: 3")]
+//// [TestCase(null, 1, 2.0, 3, "Neutral first: 1, second 2.00, third: 3")]
+//// public void HappyPath(string cultureName, object arg0, object arg1, object arg2, string expected)
+//// {
+//// var culture = cultureName != null
+//// ? CultureInfo.GetCultureInfo(cultureName)
+//// : CultureInfo.InvariantCulture;
+//// var key = nameof(Properties.Resources.ValidFormat__0__1__2__);
+
+//// Translator.CurrentCulture = culture;
+//// var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1, arg2);
+//// Assert.AreEqual(expected, actual);
+
+//// Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+//// actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg0, arg1, arg2);
+//// Assert.AreEqual(expected, actual);
+//// }
+
+//// [TestCase("en", 1, 2, 3, "Invalid format string: \"Value: {0} {2}\".")]
+//// [TestCase("sv", 1, 2,3, "Invalid format string: \"Värde: \" for the two arguments: 1, 2, 3.")]
+//// public void Throws(string cultureName, object arg0, object arg1, object arg2, string expected)
+//// {
+//// var culture = cultureName != null
+//// ? CultureInfo.GetCultureInfo(cultureName)
+//// : CultureInfo.InvariantCulture;
+//// var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+//// Translator.CurrentCulture = culture;
+//// var actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1, arg2));
+//// Assert.AreEqual(expected, actual.Message);
+
+//// Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+//// actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg0, arg1, arg2));
+//// Assert.AreEqual(expected, actual.Message);
+//// }
+
+//// [TestCase("en", 1, 2, 3, "{\"Value: {0} {2}\" : 1, 2, 3}")]
+//// [TestCase("sv", 1, 2, 3, "{\"Värde: \" : 1, 2, 3}")]
+//// public void ReturnsInfo(string cultureName, object arg0, object arg1, object arg2, string expected)
+//// {
+//// var culture = cultureName != null
+//// ? CultureInfo.GetCultureInfo(cultureName)
+//// : CultureInfo.InvariantCulture;
+//// var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+//// Translator.ErrorHandling = ErrorHandling.ReturnErrorInfo;
+//// Translator.CurrentCulture = culture;
+//// var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1, arg2);
+//// Assert.AreEqual(expected, actual);
+
+//// Translator.ErrorHandling = ErrorHandling.Throw;
+//// Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+//// actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, ErrorHandling.ReturnErrorInfo, arg0, arg1, arg2);
+//// Assert.AreEqual(expected, actual);
+//// }
+//// }
+//// }
+////}
\ No newline at end of file
diff --git a/Gu.Localization.Tests/TranslatorTests.TwoParameters.cs b/Gu.Localization.Tests/TranslatorTests.TwoParameters.cs
new file mode 100644
index 00000000..e9cbd509
--- /dev/null
+++ b/Gu.Localization.Tests/TranslatorTests.TwoParameters.cs
@@ -0,0 +1,70 @@
+namespace Gu.Localization.Tests
+{
+ using System;
+ using System.Globalization;
+
+ using NUnit.Framework;
+
+ public partial class TranslatorTests
+ {
+ public class TwoParameters
+ {
+ [TestCase("en", 1, 2.0, "English first: 1, second 2.00")]
+ [TestCase("sv", 1, 2.0, "första: 1, andra: 2,00")]
+ [TestCase(null, 1, 2.0, "Neutral first: 1, second 2.00")]
+ public void HappyPath(string cultureName, object arg0, object arg1, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.ValidFormat__0__1__);
+
+ Translator.CurrentCulture = culture;
+ var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1);
+ Assert.AreEqual(expected, actual);
+
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg0, arg1);
+ Assert.AreEqual(expected, actual);
+ }
+
+ [TestCase("en", 1, 2, "Invalid format string: \"Value: {0} {2}\".")]
+ [TestCase("sv", 1, 2, "Invalid format string: \"Värde: \" for the two arguments: 1, 2.")]
+ public void Throws(string cultureName, object arg0, object arg1, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+ Translator.CurrentCulture = culture;
+ var actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1));
+ Assert.AreEqual(expected, actual.Message);
+
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Assert.Throws(() => Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg0, arg1));
+ Assert.AreEqual(expected, actual.Message);
+ }
+
+ [TestCase("en", 1, 2, "{\"Value: {0} {2}\" : 1, 2}")]
+ [TestCase("sv", 1, 2, "{\"Värde: \" : 1, 2}")]
+ public void ReturnsInfo(string cultureName, object arg0, object arg1, string expected)
+ {
+ var culture = cultureName != null
+ ? CultureInfo.GetCultureInfo(cultureName)
+ : CultureInfo.InvariantCulture;
+ var key = nameof(Properties.Resources.InvalidFormat__0__);
+
+ Translator.ErrorHandling = ErrorHandling.ReturnErrorInfo;
+ Translator.CurrentCulture = culture;
+ var actual = Translator.Translate(Properties.Resources.ResourceManager, key, arg0, arg1);
+ Assert.AreEqual(expected, actual);
+
+ Translator.ErrorHandling = ErrorHandling.Throw;
+ Translator.CurrentCulture = CultureInfo.GetCultureInfo("it");
+ actual = Translator.Translate(Properties.Resources.ResourceManager, key, culture, arg0, arg1, ErrorHandling.ReturnErrorInfo);
+ Assert.AreEqual(expected, actual);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Gu.Localization.Tests/ValidateTests.Formats.cs b/Gu.Localization.Tests/ValidateTests.Formats.cs
new file mode 100644
index 00000000..7556454e
--- /dev/null
+++ b/Gu.Localization.Tests/ValidateTests.Formats.cs
@@ -0,0 +1,70 @@
+namespace Gu.Localization.Tests
+{
+ using System;
+ using NUnit.Framework;
+
+ public partial class ValidateTests
+ {
+ public class Formats
+ {
+ [TestCase("First: {0}")]
+ [TestCase("First: {0:N}")]
+ [TestCase("First: {0}, again: {0}")]
+ [TestCase("First: {0:F2}, Second: {0:F3}")]
+ public void OneArgumentHappyPath(string format)
+ {
+ Assert.DoesNotThrow(() => Validate.Format(format, 1));
+ Assert.IsTrue(Validate.IsValidFormat(format, 1));
+ }
+
+ [TestCase("First: {0} Second: {1}")]
+ [TestCase("First: {0:N}, second: {1:F3}")]
+ [TestCase("First: {0}, again: {0:F2}, second: {1}")]
+ [TestCase("First: {1}, again: {0:F2}, second: {0}")]
+ public void TwoArgumentsHappyPath(string format)
+ {
+ Assert.DoesNotThrow(() => Validate.Format(format, 1, 2));
+ Assert.IsTrue(Validate.IsValidFormat(format, 1, 2));
+ }
+
+ [TestCase("First: {0} second: {1}, third: {2}")]
+ [TestCase("First: {0:N}, second: {1:F3}, third: {2:G}")]
+ public void ParamsHappyPath(string format)
+ {
+ Assert.DoesNotThrow(() => Validate.Format(format, 1, 2, 3));
+ Assert.IsTrue(Validate.IsValidFormat(format, 1, 2, 3));
+ }
+
+ [TestCase("Hej", "Invalid format string: \"Hej\" for the single argument: 1.")]
+ [TestCase("First: {1}", "Invalid format string: \"First: {1}\".")]
+ [TestCase("First: {0}, Second: {1}", "Invalid format string: \"First: {0}, Second: {1}\" for the single argument: 1.")]
+ public void OneArgumentWithError(string format, string expected)
+ {
+ var exception = Assert.Throws(() => Validate.Format(format, 1));
+ Assert.AreEqual(expected, exception.Message);
+ Assert.IsFalse(Validate.IsValidFormat(format, 1));
+ }
+
+ [TestCase("Hej", "Invalid format string: \"Hej\" for the two arguments: 1, 2.")]
+ [TestCase("First: {1}", "Invalid format string: \"First: {1}\".")]
+ [TestCase("First: {0}, second: {1}, third: {2}", "Invalid format string: \"First: {0}, second: {1}, third: {2}\" for the two arguments: 1, 2.")]
+ public void TwoArgumentsWithError(string format, string expected)
+ {
+ var exception = Assert.Throws(() => Validate.Format(format, 1, 2));
+ Assert.AreEqual(expected, exception.Message);
+ Assert.IsFalse(Validate.IsValidFormat(format, 1, 2));
+ }
+
+
+ [TestCase("Hej", "Invalid format string: \"Hej\" for the arguments: 1, 2, 3.")]
+ [TestCase("First: {1}", "Invalid format string: \"First: {1}\".")]
+ [TestCase("First: {0}, second: {1}, third: {2} {3}", "Invalid format string: \"First: {0}, second: {1}, third: {2} {3}\" for the arguments: 1, 2, 3.")]
+ public void ParamsWithError(string format, string expected)
+ {
+ var exception = Assert.Throws(() => Validate.Format(format, 1, 2, 3));
+ Assert.AreEqual(expected, exception.Message);
+ Assert.IsFalse(Validate.IsValidFormat(format, 1, 2, 3));
+ }
+ }
+ }
+}
diff --git a/Gu.Localization.Tests/ValidateTests.Translations.cs b/Gu.Localization.Tests/ValidateTests.Translations.cs
new file mode 100644
index 00000000..7c1c26d4
--- /dev/null
+++ b/Gu.Localization.Tests/ValidateTests.Translations.cs
@@ -0,0 +1,115 @@
+namespace Gu.Localization.Tests
+{
+ using System;
+ using System.Globalization;
+ using System.Text;
+
+ using Gu.Localization.Tests.Errors;
+
+ using NUnit.Framework;
+
+ public partial class ValidateTests
+ {
+ public class Translations
+ {
+ [Test]
+ public void ResourceManager()
+ {
+ var errors = Validate.Translations(Properties.Resources.ResourceManager);
+ Assert.IsFalse(errors.IsEmpty);
+ var expectedKeys = new[] { "InvalidFormat__0__", "NeutralOnly", "EnglishOnly", "NoTranslation" };
+ CollectionAssert.AreEqual(expectedKeys, errors.Keys);
+ var builder = new StringBuilder();
+ builder.AppendLine("Key: InvalidFormat__0__")
+ .AppendLine(" Has format errors, the formats are:")
+ .AppendLine(" Value: {0}")
+ .AppendLine(" null")
+ .AppendLine(" Value: {0} {2}")
+ .AppendLine(" Värde: ")
+ .AppendLine(" Missing for: { de }")
+ .AppendLine("Key: NeutralOnly")
+ .AppendLine(" Missing for: { de, en, sv }")
+ .AppendLine("Key: EnglishOnly")
+ .AppendLine(" Missing for: { de, sv }")
+ .AppendLine("Key: NoTranslation")
+ .AppendLine(" Missing for: { de, en, sv }");
+ var expected = builder.ToString();
+ var actual = errors.ToString(" ", Environment.NewLine);
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void ResourceManagerExplicitCultures()
+ {
+ var cultures = new[] { CultureInfo.GetCultureInfo("sv"), CultureInfo.GetCultureInfo("en") };
+ var errors = Validate.Translations(Properties.Resources.ResourceManager, cultures);
+ Assert.IsFalse(errors.IsEmpty);
+ var expectedKeys = new[] { "InvalidFormat__0__", "NeutralOnly", "EnglishOnly", "NoTranslation" };
+ CollectionAssert.AreEqual(expectedKeys, errors.Keys);
+ var builder = new StringBuilder();
+ builder.AppendLine("Key: InvalidFormat__0__")
+ .AppendLine(" Has format errors, the formats are:")
+ .AppendLine(" Värde: ")
+ .AppendLine(" Value: {0} {2}")
+ .AppendLine("Key: NeutralOnly")
+ .AppendLine(" Missing for: { sv, en }")
+ .AppendLine("Key: EnglishOnly")
+ .AppendLine(" Missing for: { sv }")
+ .AppendLine("Key: NoTranslation")
+ .AppendLine(" Missing for: { sv, en }");
+ var expected = builder.ToString();
+ var actual = errors.ToString(" ", Environment.NewLine);
+ Assert.AreEqual(expected, actual);
+ }
+
+ [Test]
+ public void EnumTranslations()
+ {
+ var errors = Validate.EnumTranslations(Properties.Resources.ResourceManager);
+ Assert.IsFalse(errors.IsEmpty);
+ CollectionAssert.AreEqual(new[] { DummyEnum.MissingTranslation.ToString() }, errors.Keys);
+ Assert.AreEqual(
+ "Key: MissingTranslation Missing for: { invariant, de, en, sv } ",
+ errors.ToString("", " "));
+ }
+
+ [Test]
+ public void EnumTranslationsExplicitCultures()
+ {
+ var cultures = new[] { CultureInfo.GetCultureInfo("sv"), CultureInfo.GetCultureInfo("en") };
+ var errors = Validate.EnumTranslations(Properties.Resources.ResourceManager, cultures);
+ Assert.IsFalse(errors.IsEmpty);
+ CollectionAssert.AreEqual(new[] { DummyEnum.MissingTranslation.ToString() }, errors.Keys);
+ Assert.AreEqual("Key: MissingTranslation Missing for: { sv, en } ", errors.ToString("", " "));
+ }
+
+ [TestCase(nameof(Properties.Resources.AllLanguages))]
+ [TestCase(nameof(Properties.Resources.ValidFormat__0__))]
+ [TestCase(nameof(Properties.Resources.ValidFormat__0__1__))]
+ public void KeyWhenNoErrors(string key)
+ {
+ var resourceManager = Properties.Resources.ResourceManager;
+ var errors = Validate.Translations(resourceManager, key);
+ CollectionAssert.IsEmpty(errors);
+
+ var cultures = new[]
+ {
+ CultureInfo.InvariantCulture,
+ CultureInfo.GetCultureInfo("en"),
+ CultureInfo.GetCultureInfo("sv")
+ };
+ errors = Validate.Translations(resourceManager, key, cultures);
+ CollectionAssert.IsEmpty(errors);
+ }
+
+ [TestCase(nameof(Properties.Resources.InvalidFormat__0__))]
+ [TestCase(nameof(Properties.Resources.EnglishOnly))]
+ public void KeyWithErrors(string key)
+ {
+ var resourceManager = Properties.Resources.ResourceManager;
+ var errors = Validate.Translations(resourceManager, key);
+ CollectionAssert.IsNotEmpty(errors);
+ }
+ }
+ }
+}
diff --git a/Gu.Localization.Tests/ValidateTests.cs b/Gu.Localization.Tests/ValidateTests.cs
deleted file mode 100644
index e9bc1862..00000000
--- a/Gu.Localization.Tests/ValidateTests.cs
+++ /dev/null
@@ -1,109 +0,0 @@
-namespace Gu.Localization.Tests
-{
- using System;
- using System.Globalization;
- using System.Text;
-
- using Gu.Localization.Tests.Errors;
-
- using NUnit.Framework;
-
- public class ValidateTests
- {
- [Test]
- public void TranslationsForResourceManager()
- {
- var errors = Validate.Translations(Properties.Resources.ResourceManager);
- Assert.IsFalse(errors.IsEmpty);
- CollectionAssert.AreEqual(new[] { "NeutralOnly", "EnglishOnly", "NoTranslation", "Value___0_" }, errors.Keys);
- var builder = new StringBuilder();
- builder.AppendLine("Key: NeutralOnly")
- .AppendLine(" Missing for: { de, en, sv }")
- .AppendLine("Key: EnglishOnly")
- .AppendLine(" Missing for: { de, sv }")
- .AppendLine("Key: NoTranslation")
- .AppendLine(" Missing for: { de, en, sv }")
- .AppendLine("Key: Value___0_")
- .AppendLine(" Has format errors, the formats are:")
- .AppendLine(" Value: {0}")
- .AppendLine(" null")
- .AppendLine(" Value: {0} {1}")
- .AppendLine(" Värde: ")
- .AppendLine(" Missing for: { de }");
- var expected = builder.ToString();
- var actual = errors.ToString(" ", Environment.NewLine);
- Assert.AreEqual(expected, actual);
- }
-
- [Test]
- public void TranslationsForResourceManagerExplicitCultures()
- {
- var cultures = new[] { CultureInfo.GetCultureInfo("sv"), CultureInfo.GetCultureInfo("en") };
- var errors = Validate.Translations(Properties.Resources.ResourceManager, cultures);
- Assert.IsFalse(errors.IsEmpty);
- CollectionAssert.AreEqual(new[] { "NeutralOnly", "EnglishOnly", "NoTranslation", "Value___0_" }, errors.Keys);
- var builder = new StringBuilder();
- builder.AppendLine("Key: NeutralOnly")
- .AppendLine(" Missing for: { sv, en }")
- .AppendLine("Key: EnglishOnly")
- .AppendLine(" Missing for: { sv }")
- .AppendLine("Key: NoTranslation")
- .AppendLine(" Missing for: { sv, en }")
- .AppendLine("Key: Value___0_")
- .AppendLine(" Has format errors, the formats are:")
- .AppendLine(" Värde: ")
- .AppendLine(" Value: {0} {1}");
- var expected = builder.ToString();
- var actual = errors.ToString(" ", Environment.NewLine);
- Assert.AreEqual(expected, actual);
- }
-
- [Test]
- public void EnumTranslations()
- {
- var errors = Validate.EnumTranslations(Properties.Resources.ResourceManager);
- Assert.IsFalse(errors.IsEmpty);
- CollectionAssert.AreEqual(new[] { DummyEnum.MissingTranslation.ToString() }, errors.Keys);
- Assert.AreEqual("Key: MissingTranslation Missing for: { invariant, de, en, sv } ", errors.ToString("", " "));
- }
-
- [Test]
- public void EnumTranslationsExplicitCultures()
- {
- var cultures = new[] { CultureInfo.GetCultureInfo("sv"), CultureInfo.GetCultureInfo("en") };
- var errors = Validate.EnumTranslations(Properties.Resources.ResourceManager, cultures);
- Assert.IsFalse(errors.IsEmpty);
- CollectionAssert.AreEqual(new[] { DummyEnum.MissingTranslation.ToString() }, errors.Keys);
- Assert.AreEqual("Key: MissingTranslation Missing for: { sv, en } ", errors.ToString("", " "));
- }
-
- [Test]
- public void FormatsHappyPath()
- {
- var errors = Validate.Translations(Properties.Resources.ResourceManager, nameof(Properties.Resources.first___0___second__1_));
- CollectionAssert.IsEmpty(errors);
-
- var cultures = new[] { CultureInfo.InvariantCulture, CultureInfo.GetCultureInfo("en"), CultureInfo.GetCultureInfo("sv") };
- errors = Validate.Translations(Properties.Resources.ResourceManager, nameof(Properties.Resources.first___0___second__1_), cultures);
- CollectionAssert.IsEmpty(errors);
- }
-
- [Test]
- public void FormatsWithErrors()
- {
- var errors = Validate.Translations(Properties.Resources.ResourceManager, Properties.Resources.Value___0_);
- CollectionAssert.IsNotEmpty(errors);
- }
-
- [Test]
- public void TranslationsForKeyWhenNoErrors()
- {
- var errors = Validate.Translations(Properties.Resources.ResourceManager, nameof(Properties.Resources.AllLanguages));
- CollectionAssert.IsEmpty(errors);
-
- var cultures = new[] { CultureInfo.InvariantCulture, CultureInfo.GetCultureInfo("en"), CultureInfo.GetCultureInfo("sv") };
- errors = Validate.Translations(Properties.Resources.ResourceManager, nameof(Properties.Resources.AllLanguages), cultures);
- CollectionAssert.IsEmpty(errors);
- }
- }
-}
diff --git a/Gu.Localization/Gu.Localization.csproj b/Gu.Localization/Gu.Localization.csproj
index 7072693e..8931c5de 100644
--- a/Gu.Localization/Gu.Localization.csproj
+++ b/Gu.Localization/Gu.Localization.csproj
@@ -71,6 +71,7 @@
+
diff --git a/Gu.Localization/Internals/ResourceManagerExt.cs b/Gu.Localization/Internals/ResourceManagerExt.cs
index d3e37aac..d11d49e8 100644
--- a/Gu.Localization/Internals/ResourceManagerExt.cs
+++ b/Gu.Localization/Internals/ResourceManagerExt.cs
@@ -5,7 +5,6 @@
using System.Diagnostics;
using System.Globalization;
using System.Linq;
- using System.Reflection;
using System.Resources;
internal static class ResourceManagerExt
@@ -21,10 +20,18 @@ internal static class ResourceManagerExt
/// True if a translation exists
internal static bool HasKey(this ResourceManager resourceManager, string key, CultureInfo culture)
{
- using (var resourceSet = resourceManager.GetTempResourceSet(culture))
+ using (var clone = resourceManager.Clone())
{
- return resourceSet?.ResourceSet.OfType()
- .Any(x => Equals(x.Key, key)) == true;
+ if (clone?.ResourceManager == null)
+ {
+ return false;
+ }
+
+ using (var resourceSet = clone.ResourceManager.GetResourceSet(culture, true, false))
+ {
+ return resourceSet?.OfType()
+ .Any(x => Equals(x.Key, key)) == true;
+ }
}
}
@@ -38,56 +45,47 @@ internal static bool HasKey(this ResourceManager resourceManager, string key, Cu
/// True if a translation exists
internal static bool HasCulture(this ResourceManager resourceManager, CultureInfo culture)
{
- using (var resourceSet = resourceManager.GetTempResourceSet(culture))
+ using (var clone = resourceManager.Clone())
{
- return resourceSet != null;
+ return clone?.ResourceManager?.GetResourceSet(culture, true, false) != null;
}
}
- // Clones the resourcemanager and gets the resource set for the culture
+ // Clones the resourcemanager
// This is slow and backwards but can't think of another way that does not load a the resourceset into memory.
// Also calling resourceManager.ReleaseAllResources() feels really nasty in a lib like this.
// Keeping it slow and dumb until something better.
- private static Disposer GetTempResourceSet(this ResourceManager resourceManager, CultureInfo culture)
+ internal static ResourceManagerClone Clone(this ResourceManager resourceManager)
{
- var type = AppDomain.CurrentDomain.GetAssemblies()
- .Select(x => x.GetType(resourceManager.BaseName))
- .SingleOrDefault(x => x != null &&
- x.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
- .Any(p => p.PropertyType == typeof(ResourceManager)));
- if (type == null)
- {
- return null;
- }
-
- var clone = new ResourceManager(resourceManager.BaseName, type.Assembly);
- var resourceSet = clone.GetResourceSet(culture, true, false);
- if (resourceSet == null)
- {
- resourceManager.ReleaseAllResources();
- return null;
- }
+ return new ResourceManagerClone(resourceManager);
+ }
- return new Disposer(resourceManager, resourceSet);
+ internal static Type ContainingType(this ResourceManager resourceManager)
+ {
+ return ResourceManagers.TypeManagerCache.GetOrAdd(resourceManager);
}
- private class Disposer : IDisposable
+ /// Creates a clone of the passed in. Releases all resources on dispose.
+ internal sealed class ResourceManagerClone : IDisposable
{
- internal readonly ResourceSet ResourceSet;
- private readonly ResourceManager resourceManager;
+ internal readonly ResourceManager ResourceManager;
- public Disposer(ResourceManager resourceManager, ResourceSet resourceSet)
+ public ResourceManagerClone(ResourceManager source)
{
- Debug.Assert(resourceManager != null, "resourceManager == null");
- Debug.Assert(resourceSet != null, "resourceSet == null");
- this.resourceManager = resourceManager;
- this.ResourceSet = resourceSet;
+ Debug.Assert(source != null, "resourceManager == null");
+ var containingType = source.ContainingType();
+ Debug.Assert(containingType != null, "containingType == null");
+
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalse want this check in release build
+ if (containingType != null)
+ {
+ this.ResourceManager = new ResourceManager(source.BaseName, containingType.Assembly);
+ }
}
public void Dispose()
{
- this.resourceManager.ReleaseAllResources();
- this.ResourceSet.Dispose();
+ this.ResourceManager?.ReleaseAllResources();
}
}
}
diff --git a/Gu.Localization/Internals/ResourceManagers.cs b/Gu.Localization/Internals/ResourceManagers.cs
index 94f7c638..0637aa5a 100644
--- a/Gu.Localization/Internals/ResourceManagers.cs
+++ b/Gu.Localization/Internals/ResourceManagers.cs
@@ -2,21 +2,21 @@ namespace Gu.Localization
{
using System;
using System.Collections.Concurrent;
+ using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Resources;
/// A cache for resourcemanagers.
- internal static class ResourceManagers
+ internal static class ResourceManagers
{
- private static readonly ConcurrentDictionary TypeManagerMap = new ConcurrentDictionary();
-
/// Tries to get from cache or create a for
/// Ex. typeof(Properties.Resources)
/// The
/// True if a could be created for
internal static bool TryGetForType(Type resourcesType, out ResourceManager result)
{
- result = TypeManagerMap.GetOrAdd(resourcesType, CreateManagerForType);
+ result = TypeManagerCache.GetOrAdd(resourcesType, CreateManagerForTypeOrDefault);
return result != null;
}
@@ -25,7 +25,7 @@ internal static bool TryGetForType(Type resourcesType, out ResourceManager resul
/// A resource manager
internal static ResourceManager ForType(Type resourcesType)
{
- var resourceManager = TypeManagerMap.GetOrAdd(resourcesType, CreateManagerForType);
+ var resourceManager = TypeManagerCache.GetOrAdd(resourcesType, CreateManagerForType);
if (resourceManager == null)
{
var message = $"{nameof(resourcesType)} must have a property named ResourceManager of type ResourceManager";
@@ -35,15 +35,88 @@ internal static ResourceManager ForType(Type resourcesType)
return resourceManager;
}
+ private static ResourceManager CreateManagerForTypeOrDefault(Type type)
+ {
+ var property = GetResourceManagerProperty(type);
+ return property?.GetValue(null) as ResourceManager;
+ }
+
private static ResourceManager CreateManagerForType(Type type)
{
- var property = type.GetProperty(nameof(Properties.Resources.ResourceManager), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+ var property = GetResourceManagerProperty(type);
if (property == null || !typeof(ResourceManager).IsAssignableFrom(property.PropertyType))
{
- return null;
+ var message = $"{nameof(type)} must have a property named ResourceManager of type ResourceManager";
+ throw new ArgumentException(message);
}
return (ResourceManager)property.GetValue(null);
}
+
+ private static PropertyInfo GetResourceManagerProperty(this Type type)
+ {
+ var property = type.GetProperty(
+ nameof(Properties.Resources.ResourceManager),
+ BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
+ return property;
+ }
+
+ internal static class TypeManagerCache
+ {
+ private static readonly ConcurrentDictionary TypeManagerMap = new ConcurrentDictionary();
+ private static readonly ConcurrentDictionary ManagerTypeMap = new ConcurrentDictionary(ResourceManagerComparer.Default);
+
+ internal static ResourceManager GetOrAdd(Type type, Func create)
+ {
+ var manager = TypeManagerMap.GetOrAdd(type, create);
+ if (manager != null)
+ {
+ ManagerTypeMap.TryAdd(manager, type);
+ }
+
+ return manager;
+ }
+
+ internal static Type GetOrAdd(ResourceManager resourceManager)
+ {
+ var type = ManagerTypeMap.GetOrAdd(resourceManager, ContainingType);
+ if (type != null)
+ {
+ TypeManagerMap.TryAdd(type, resourceManager);
+ }
+
+ return type;
+ }
+
+ private static Type ContainingType(ResourceManager resourceManager)
+ {
+ var resourcesType = AppDomain.CurrentDomain.GetAssemblies()
+ .Select(x => x.GetType(resourceManager.BaseName))
+ .SingleOrDefault(x => x != null &&
+ x.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
+ .Any(p => p.PropertyType == typeof(ResourceManager)));
+ return resourcesType;
+ }
+
+ private class ResourceManagerComparer : IEqualityComparer
+ {
+ public static readonly ResourceManagerComparer Default = new ResourceManagerComparer();
+ private static readonly StringComparer StringComparer = StringComparer.Ordinal;
+
+ private ResourceManagerComparer()
+ {
+ }
+
+ public bool Equals(ResourceManager x, ResourceManager y)
+ {
+ return StringComparer.Equals(x.BaseName, y.BaseName);
+ }
+
+ public int GetHashCode(ResourceManager obj)
+ {
+ return StringComparer.GetHashCode(obj.BaseName);
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Gu.Localization/Properties/Resources.Designer.cs b/Gu.Localization/Properties/Resources.Designer.cs
index e7ab0a63..04cd5b55 100644
--- a/Gu.Localization/Properties/Resources.Designer.cs
+++ b/Gu.Localization/Properties/Resources.Designer.cs
@@ -60,6 +60,15 @@ internal Resources() {
}
}
+ ///
+ /// Looks up a localized string similar to {{"{0}" : {1}}}.
+ ///
+ public static string InvalidFormat {
+ get {
+ return ResourceManager.GetString("InvalidFormat", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to ~{0}~.
///
diff --git a/Gu.Localization/Properties/Resources.resx b/Gu.Localization/Properties/Resources.resx
index 962ab73f..7304fa62 100644
--- a/Gu.Localization/Properties/Resources.resx
+++ b/Gu.Localization/Properties/Resources.resx
@@ -135,4 +135,7 @@
#{0}#
+
+ {{"{0}" : {1}}}
+
\ No newline at end of file
diff --git a/Gu.Localization/Translator.Parameters.cs b/Gu.Localization/Translator.Parameters.cs
new file mode 100644
index 00000000..28ea9139
--- /dev/null
+++ b/Gu.Localization/Translator.Parameters.cs
@@ -0,0 +1,185 @@
+namespace Gu.Localization
+{
+ using System;
+ using System.Globalization;
+ using System.Resources;
+
+ public static partial class Translator
+ {
+ ///
+ /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///
+ /// The type of generic to avoid boxing
+ /// The containing translations.
+ /// The key in
+ /// The argument will be used as string.Format(format, )
+ /// Specifies how to handle errors.
+ /// The key translated to the
+ public static string Translate(ResourceManager resourceManager, string key, T arg0, ErrorHandling errorHandling = ErrorHandling.Default)
+ {
+ return Translate(resourceManager, key, CurrentCulture, arg0, errorHandling);
+ }
+
+ ///
+ /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///
+ /// The type of generic to avoid boxing
+ /// The containing translations.
+ /// The key in
+ /// The culture.
+ /// The argument will be used as string.Format(format, )
+ /// Specifies how to handle errors.
+ /// The key translated to the
+ public static string Translate(ResourceManager resourceManager, string key, CultureInfo culture, T arg0, ErrorHandling errorHandling = ErrorHandling.Default)
+ {
+ string format;
+ if (!TryTranslateOrThrow(resourceManager, key, culture, errorHandling, out format))
+ {
+ return format;
+ }
+
+ if (ShouldThrow(errorHandling))
+ {
+ Validate.Format(format, arg0);
+ return string.Format(culture, format, arg0);
+ }
+
+ if (!Validate.IsValidFormat(format, arg0))
+ {
+ return string.Format(culture, Properties.Resources.InvalidFormat, format, arg0);
+ }
+
+ try
+ {
+ return string.Format(format, arg0);
+ }
+ catch (Exception)
+ {
+ return string.Format(culture, Properties.Resources.InvalidFormat, format, arg0);
+ }
+ }
+
+ ///
+ /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///
+ /// The type of generic to avoid boxing
+ /// The type of generic to avoid boxing
+ /// The containing translations.
+ /// The key in
+ /// The argument will be used as first arguyment in string.Format(culture, format, , )
+ /// The argument will be used as second argument string.Format(culture, format, , )
+ /// Specifies how to handle errors.
+ /// The key translated to the
+ public static string Translate(ResourceManager resourceManager, string key, T0 arg0, T1 arg1, ErrorHandling errorHandling = ErrorHandling.Default)
+ {
+ return Translate(resourceManager, key, CurrentCulture, arg0, arg1, errorHandling);
+ }
+
+ ///
+ /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///
+ /// The type of generic to avoid boxing
+ /// The type of generic to avoid boxing
+ /// The containing translations.
+ /// The key in
+ /// The culture.
+ /// The argument will be used as first arguyment in string.Format(culture, format, , )
+ /// The argument will be used as second argument string.Format(culture, format, , )
+ /// Specifies how to handle errors.
+ /// The key translated to the
+ public static string Translate(ResourceManager resourceManager, string key, CultureInfo culture, T0 arg0, T1 arg1, ErrorHandling errorHandling = ErrorHandling.Default)
+ {
+ string format;
+ if (!TryTranslateOrThrow(resourceManager, key, culture, errorHandling, out format))
+ {
+ return format;
+ }
+
+ if (ShouldThrow(errorHandling))
+ {
+ Validate.Format(format, arg0, arg1);
+ return string.Format(culture, format, arg0, arg1);
+ }
+
+ if (!Validate.IsValidFormat(format, arg0, arg1))
+ {
+ return string.Format(culture, Properties.Resources.InvalidFormat, format, string.Join(", ", arg0, arg1));
+ }
+
+ try
+ {
+ return string.Format(format, arg0, arg1);
+ }
+ catch (Exception)
+ {
+ return string.Format(culture, Properties.Resources.InvalidFormat, format, string.Join(", ", arg0, arg1));
+ }
+ }
+
+ ///////
+ /////// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /////// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///////
+ /////// The containing translations.
+ /////// The key in
+ /////// Specifies how to handle errors.
+ /////// The arguments will be used as first arguyment in string.Format(culture, format, )
+ /////// The key translated to the
+ ////public static string Translate(
+ //// ResourceManager resourceManager,
+ //// string key,
+ //// ErrorHandling errorHandling = ErrorHandling.Default,
+ //// params object[] args)
+ ////{
+ //// return Translate(resourceManager, key, CurrentCulture, errorHandling, args);
+ ////}
+
+ ///////
+ /////// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
+ /////// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
+ ///////
+ /////// The containing translations.
+ /////// The key in
+ /////// The culture.
+ /////// Specifies how to handle errors.
+ /////// The arguments will be used as first arguyment in string.Format(culture, format, )
+ /////// The key translated to the
+ ////public static string Translate(
+ //// ResourceManager resourceManager,
+ //// string key,
+ //// CultureInfo culture,
+ //// ErrorHandling errorHandling = ErrorHandling.Default,
+ //// params object[] args)
+ ////{
+ //// string format;
+ //// if (!TryTranslateOrThrow(resourceManager, key, culture, errorHandling, out format))
+ //// {
+ //// return format;
+ //// }
+
+ //// if (ShouldThrow(errorHandling))
+ //// {
+ //// Validate.Format(format, args);
+ //// return string.Format(culture, format, args);
+ //// }
+
+ //// if (!Validate.IsValidFormat(format, args))
+ //// {
+ //// return string.Format(culture, Properties.Resources.InvalidFormat, format, string.Join(", ", args));
+ //// }
+
+ //// try
+ //// {
+ //// return string.Format(format, args);
+ //// }
+ //// catch (Exception)
+ //// {
+ //// return string.Format(culture, Properties.Resources.InvalidFormat, format, string.Join(", ", args));
+ //// }
+ ////}
+ }
+}
\ No newline at end of file
diff --git a/Gu.Localization/Translator.cs b/Gu.Localization/Translator.cs
index 5f2f3d45..f31cb3d6 100644
--- a/Gu.Localization/Translator.cs
+++ b/Gu.Localization/Translator.cs
@@ -10,7 +10,7 @@
using System.Threading;
/// Class for translating resources
- public static class Translator
+ public static partial class Translator
{
private static CultureInfo currentCulture = Thread.CurrentThread.CurrentUICulture;
private static DirectoryInfo resourceDirectory = ResourceCultures.DefaultResourceDirectory();
@@ -124,47 +124,6 @@ public static string Translate(
return result;
}
- ///
- /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
- /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
- ///
- /// The containing translations.
- /// The key in
- /// The argument will be used as string.Format(format, )
- /// Specifies how to handle errors.
- /// The key translated to the
- internal static string Translate(ResourceManager resourceManager, string key, object arg, ErrorHandling errorHandling = ErrorHandling.Default)
- {
- return Translate(resourceManager, key, CurrentCulture, arg, errorHandling);
- }
-
- ///
- /// Translator.Translate(Properties.Resources.ResourceManager, nameof(Properties.Resources.SomeKey));
- /// This assumes that the resource is something like 'Value: {0}' i.e. having one format parameter.
- ///
- /// The containing translations.
- /// The key in
- /// The culture.
- /// The argument will be used as string.Format(format, )
- /// Specifies how to handle errors.
- /// The key translated to the
- internal static string Translate(ResourceManager resourceManager, string key, CultureInfo culture, object arg, ErrorHandling errorHandling = ErrorHandling.Default)
- {
- string format;
- if (!TryTranslateOrThrow(resourceManager, key, culture, errorHandling, out format))
- {
- return format;
- }
-
- if (ShouldThrow(errorHandling))
- {
- Validate.Format(format, arg);
- return string.Format(format, arg);
- }
-
- throw new NotImplementedException("message");
- }
-
private static bool TryTranslateOrThrow(
ResourceManager resourceManager,
string key,
diff --git a/Gu.Localization/Validate.Formats.cs b/Gu.Localization/Validate.Formats.cs
index 13e35432..51b1aecc 100644
--- a/Gu.Localization/Validate.Formats.cs
+++ b/Gu.Localization/Validate.Formats.cs
@@ -1,22 +1,131 @@
-namespace Gu.Localization
+// ReSharper disable UnusedParameter.Global
+namespace Gu.Localization
{
using System;
+ /// Methods for validating format resources.
public partial class Validate
{
- internal static void Format(string format, object arg)
+ ///
+ /// Call with Validate.IsValidFormat("First: {0:N}", 1.2);
+ /// Throws a if error(s) are found.
+ ///
+ /// The type of generic to avoid boxing
+ /// The format string ex: 'First: {0:N}
+ /// The argument
+ public static void Format(string format, T0 arg0)
{
int count;
bool? anyItemHasFormat;
if (!FormatString.IsValidFormat(format, out count, out anyItemHasFormat))
{
- throw new FormatException($"Invalid format string: {format}.");
+ throw new FormatException($"Invalid format string: \"{format}\".");
}
+ // not sure if we should bother with checking individual format items here
if (count != 1)
{
- throw new FormatException($"Invalid format string: {format} for the single argument: {arg}.");
+ throw new FormatException($"Invalid format string: \"{format}\" for the single argument: {arg0}.");
}
}
+
+ ///
+ /// Call with Validate.IsValidFormat("First: {0:N}", 1.2);
+ /// Throws a if error(s) are found.
+ ///
+ /// The type of generic to avoid boxing
+ /// The type of generic to avoid boxing
+ /// The format string ex: 'First: {0:N}
+ /// The first argument.
+ /// The second argument.
+ public static void Format(string format, T0 arg0, T1 arg1)
+ {
+ int count;
+ bool? anyItemHasFormat;
+ if (!FormatString.IsValidFormat(format, out count, out anyItemHasFormat))
+ {
+ throw new FormatException($"Invalid format string: \"{format}\".");
+ }
+
+ // not sure if we should bother with checking individual format items here
+ if (count != 2)
+ {
+ throw new FormatException($"Invalid format string: \"{format}\" for the two arguments: {arg0}, {arg1}.");
+ }
+ }
+
+ ///
+ /// Call with Validate.IsValidFormat("First: {0:N}", 1, 2, 3..);
+ /// Throws a if error(s) are found.
+ ///
+ /// The format string ex: 'First: {0:N}
+ /// The arguments.
+ public static void Format(string format, params object[] args)
+ {
+ int count;
+ bool? anyItemHasFormat;
+ if (!FormatString.IsValidFormat(format, out count, out anyItemHasFormat))
+ {
+ throw new FormatException($"Invalid format string: \"{format}\".");
+ }
+
+ if (args == null || args.Length == 0)
+ {
+ if (count == 0)
+ {
+ return;
+ }
+
+ throw new FormatException($"Invalid format string: \"{format}\" when no arguments.");
+ }
+
+ if (count != args.Length)
+ {
+ throw new FormatException($"Invalid format string: \"{format}\" for the arguments: {string.Join(", ", args)}.");
+ }
+ }
+
+ /// Call with Validate.IsValidFormat("First: {0:N}", 1.2);
+ /// The type of generic to avoid boxing
+ /// The format string ex: 'First: {0:N}
+ /// The argument
+ /// True if is valid for the argument
+ public static bool IsValidFormat(string format, T arg0)
+ {
+ return IsValidFormat(format, 1);
+ }
+
+ /// Call with Validate.IsValidFormat("First: {0:N}, Second: {1}", 1, 2);
+ /// The type of generic to avoid boxing
+ /// The type of generic to avoid boxing
+ /// The format string ex: 'First: {0:N}
+ /// The first argument.
+ /// The second argument.
+ /// True if is valid for the two arguments and
+ public static bool IsValidFormat(string format, T0 arg0, T1 arg1)
+ {
+ return IsValidFormat(format, 2);
+ }
+
+ /// Call with Validate.IsValidFormat("First: {0:N}, Second: {1}", 2);
+ /// The format string ex: 'First: {0:N}
+ /// The arguments.
+ /// True if is valid for .
+ public static bool IsValidFormat(string format, params object[] args)
+ {
+ return IsValidFormat(format, args?.Length ?? 0);
+ }
+
+ private static bool IsValidFormat(string format, int argumentCount)
+ {
+ int count;
+ bool? anyItemHasFormat;
+ if (!FormatString.IsValidFormat(format, out count, out anyItemHasFormat))
+ {
+ return false;
+ }
+
+ return count == argumentCount;
+ }
}
}
diff --git a/Gu.Localization/Validate.Translations.cs b/Gu.Localization/Validate.Translations.cs
index 3ddcc72a..9bd8a5dc 100644
--- a/Gu.Localization/Validate.Translations.cs
+++ b/Gu.Localization/Validate.Translations.cs
@@ -45,28 +45,31 @@ public static TranslationErrors Translations(ResourceManager resourceManager)
/// An with all errors found in
public static TranslationErrors Translations(ResourceManager resourceManager, IEnumerable cultures)
{
- var resources = GetResources(resourceManager, cultures);
- var keys = GetKeys(resourceManager);
- Dictionary> errors = null;
- foreach (var key in keys)
+ using (var clone = resourceManager.Clone())
{
- var keyErrors = Translations(resources, key);
- if (keyErrors.Count == 0)
+ var resources = GetResources(clone, cultures);
+ var keys = GetKeys(resourceManager);
+ Dictionary> errors = null;
+ foreach (var key in keys)
{
- continue;
- }
-
- if (errors == null)
- {
- errors = new Dictionary>();
+ var keyErrors = Translations(resources, key);
+ if (keyErrors.Count == 0)
+ {
+ continue;
+ }
+
+ if (errors == null)
+ {
+ errors = new Dictionary>();
+ }
+
+ errors.Add(key, keyErrors);
}
- errors.Add(key, keyErrors);
+ return errors == null
+ ? TranslationErrors.Empty
+ : new TranslationErrors(errors);
}
-
- return errors == null
- ? TranslationErrors.Empty
- : new TranslationErrors(errors);
}
///
@@ -97,27 +100,30 @@ public static TranslationErrors EnumTranslations(ResourceManager resourceMana
public static TranslationErrors EnumTranslations(ResourceManager resourceManager, IEnumerable cultures)
where T : struct, IComparable, IFormattable, IConvertible
{
- var resources = GetResources(resourceManager, cultures);
- Dictionary> errors = null;
- foreach (var key in Enum.GetNames(typeof(T)))
+ using (var clone = resourceManager.Clone())
{
- var keyErrors = Translations(resources, key);
- if (keyErrors.Count == 0)
+ var resources = GetResources(clone, cultures);
+ Dictionary> errors = null;
+ foreach (var key in Enum.GetNames(typeof(T)))
{
- continue;
+ var keyErrors = Translations(resources, key);
+ if (keyErrors.Count == 0)
+ {
+ continue;
+ }
+
+ if (errors == null)
+ {
+ errors = new Dictionary>();
+ }
+
+ errors.Add(key, keyErrors);
}
- if (errors == null)
- {
- errors = new Dictionary>();
- }
-
- errors.Add(key, keyErrors);
+ return errors == null
+ ? TranslationErrors.Empty
+ : new TranslationErrors(errors);
}
-
- return errors == null
- ? TranslationErrors.Empty
- : new TranslationErrors(errors);
}
///
@@ -152,8 +158,11 @@ public static IReadOnlyList Translations(ResourceManager resou
/// A list with all errors for the key or an empty list if no errors.
public static IReadOnlyList Translations(ResourceManager resourceManager, string key, IEnumerable cultures)
{
- var resources = GetResources(resourceManager, cultures);
- return Translations(resources, key);
+ using (var clone = resourceManager.Clone())
+ {
+ var resources = GetResources(clone, cultures);
+ return Translations(resources, key);
+ }
}
private static IReadOnlyList Translations(IReadOnlyDictionary resources, string key)
@@ -214,9 +223,14 @@ private static IReadOnlyList GetKeys(ResourceManager resourceManager)
.ToArray();
}
- private static IReadOnlyDictionary GetResources(ResourceManager resourceManager, IEnumerable cultures)
+ private static IReadOnlyDictionary GetResources(ResourceManagerExt.ResourceManagerClone clone, IEnumerable cultures)
{
- return cultures.ToDictionary(c => c, c => resourceManager.GetResourceSet(c, true, false), CultureInfoComparer.Default);
+ if (clone == null || clone.ResourceManager == null)
+ {
+ return EmptyReadOnlyDictionary.Default;
+ }
+
+ return cultures.ToDictionary(c => c, c => clone.ResourceManager.GetResourceSet(c, true, false), CultureInfoComparer.Default);
}
}
}