Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes to allow localisation/overwriting strings #15

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
@@ -1,12 +1,30 @@
using System;
using System.Resources;
using System.Globalization;

namespace GovUkDesignSystem.Attributes.DataBinding
{
/// <summary>
/// Abstract base class for all attributes that hold data binding error text
/// </summary>
[AttributeUsage(AttributeTargets.Property, Inherited = true)]
[AttributeUsage(AttributeTargets.Property)]
public abstract class GovUkDataBindingErrorTextAttribute : Attribute
{
private ResourceManager _resourceManager;

protected Type ResourceType { get; set; }

protected string ResourceName { get; set; }

protected string GetResourceValue(string resourceKey)
{
var resourceAssembly = ResourceType?.Assembly;
if (resourceAssembly != null && _resourceManager == null)
{
_resourceManager = new ResourceManager(ResourceName, resourceAssembly);
}

return _resourceManager?.GetString(resourceKey, CultureInfo.CurrentCulture) ?? resourceKey;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ public GovUkDataBindingMandatoryDecimalErrorTextAttribute(string errorMessageIfM
/// <br/>e.g. "[Full name] must be 2 characters or more"
/// <br/>e.g. "[Median age] must be a number"
/// </summary>
public string NameAtStartOfSentence { get; private set; }
public string NameAtStartOfSentence { get; }

/// <summary>
/// A complete sentence of the form: ‘Enter [whatever it is]’.
/// <br/>For example, ‘Enter your first name’.
/// </summary>
public string ErrorMessageIfMissing { get; private set; }
public virtual string ErrorMessageIfMissing { get; }

public virtual string MustBeNumberErrorMessage => "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ public GovUkDataBindingMandatoryIntErrorTextAttribute(string errorMessageIfMissi
/// <br/>e.g. "[Full name] must be 2 characters or more"
/// <br/>e.g. "[Median age] must be a number"
/// </summary>
public string NameAtStartOfSentence { get; private set; }
public string NameAtStartOfSentence { get; protected set; }

/// <summary>
/// A complete sentence of the form: ‘Enter [whatever it is]’.
/// <br/>For example, ‘Enter your first name’.
/// </summary>
public string ErrorMessageIfMissing { get; private set; }
public virtual string ErrorMessageIfMissing { get; }

public virtual string IsWholeNumberErrorMessage => "";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not keen on adding these properties to these classes that will never use them, but I don't think it's worth the effort to change it at the moment.
I'm not sure exactly what I'd do differently to simplify things, but perhaps the model binder could look for two different attribute types. I might even go further and create two different model binders (one localisable) with a shared base.

public virtual string MustBeNumberErrorMessage => "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public GovUkDataBindingOptionalDecimalErrorTextAttribute(string nameAtStartOfSen
/// <br/>e.g. "[Full name] must be 2 characters or more"
/// <br/>e.g. "[Median age] must be a number"
/// </summary>
public string NameAtStartOfSentence { get; private set; }
public string NameAtStartOfSentence { get; protected set; }

public virtual string MustBeNumberErrorMessage => "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public class GovUkDataBindingOptionalIntErrorTextAttribute : GovUkDataBindingErr
{
public GovUkDataBindingOptionalIntErrorTextAttribute(string nameAtStartOfSentence)
{
if (string.IsNullOrEmpty(nameAtStartOfSentence))
if(string.IsNullOrEmpty(nameAtStartOfSentence))
{
throw new ArgumentNullException("nameAtStartOfSentence cannot be null or empty");
}
Expand All @@ -18,6 +18,9 @@ public GovUkDataBindingOptionalIntErrorTextAttribute(string nameAtStartOfSentenc
/// <br/>e.g. "[Full name] must be 2 characters or more"
/// <br/>e.g. "[Median age] must be a number"
/// </summary>
public string NameAtStartOfSentence { get; private set; }
public string NameAtStartOfSentence { get; protected set; }

public virtual string IsWholeNumberErrorMessage => "";
public virtual string MustBeNumberErrorMessage => "";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;

namespace GovUkDesignSystem.Attributes.DataBinding
{
public class GovUkDataBindingMandatoryDecimalLocalisableErrorTextAttribute : GovUkDataBindingMandatoryDecimalErrorTextAttribute
{
public GovUkDataBindingMandatoryDecimalLocalisableErrorTextAttribute(string errorMessageIfMissing, string nameAtStartOfSentence = "", string mustBeNumberErrorMessage = "", string resourceName = "", Type resourceType = null): base(errorMessageIfMissing, nameAtStartOfSentence)
{
if(string.IsNullOrEmpty(nameAtStartOfSentence) && string.IsNullOrEmpty(mustBeNumberErrorMessage))
{
throw new ArgumentNullException("nameAtStartOfSentence cannot be null or empty unless all error messages are overwritten");
}

if (resourceType == null ^ string.IsNullOrEmpty(resourceName))
{
throw new ArgumentNullException("resourceName or resourceType cannot be null or empty while the other is not null or empty");
}

if(string.IsNullOrEmpty(errorMessageIfMissing))
{
throw new ArgumentNullException("errorMessageIfMissing cannot be null or empty");
}

_mustBeNumberErrorMessage = mustBeNumberErrorMessage;
ResourceType = resourceType;
ResourceName = resourceName;
}

/// <summary>
/// A complete sentence of the form: ‘Enter [whatever it is]’.
/// <br/>For example, ‘Enter your first name’.
/// </summary>
public override string ErrorMessageIfMissing => GetResourceValue(base.ErrorMessageIfMissing);

/// <summary>
/// An override for the error message that is displayed if the value entered is not a number.
/// A complete sentence of the form: ‘[Whatever it is] must be a number’
/// <br/>e.g. "Median age must be a number"
/// </summary>
private readonly string _mustBeNumberErrorMessage;
public override string MustBeNumberErrorMessage => GetResourceValue(_mustBeNumberErrorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using System;


namespace GovUkDesignSystem.Attributes.DataBinding
{
public class GovUkDataBindingMandatoryIntLocalisableErrorTextAttribute : GovUkDataBindingMandatoryIntErrorTextAttribute
{
public GovUkDataBindingMandatoryIntLocalisableErrorTextAttribute(string errorMessageIfMissing, string nameAtStartOfSentence = "", Type resourceType = null, string resourceName = "", string isWholeNumberErrorMessage = "", string mustBeNumberErrorMessage = ""): base(errorMessageIfMissing, nameAtStartOfSentence)
{
if(string.IsNullOrEmpty(nameAtStartOfSentence) && (string.IsNullOrEmpty(isWholeNumberErrorMessage) || string.IsNullOrEmpty(mustBeNumberErrorMessage)))
{
throw new ArgumentNullException("nameAtStartOfSentence cannot be null or empty unless all error messages are overwritten");
}

if (resourceType == null ^ string.IsNullOrEmpty(resourceName))
{
throw new ArgumentNullException("resourceName or resourceType cannot be null or empty while the other is not null or empty");
}

if(string.IsNullOrEmpty(errorMessageIfMissing))
{
throw new ArgumentNullException("errorMessageIfMissing cannot be null or empty");
}

_mustBeNumberErrorMessage = mustBeNumberErrorMessage;
_isWholeNumberErrorMessage = isWholeNumberErrorMessage;
ResourceType = resourceType;
ResourceName = resourceName;
}

/// <summary>
/// An override for the error message that is displayed if the value entered is not a whole number.
/// A complete sentence of the form: ‘[Whatever it is] must be a whole number’
/// <br/>e.g. "Median age must be a whole number"
/// </summary>
private readonly string _isWholeNumberErrorMessage;
public override string IsWholeNumberErrorMessage => GetResourceValue(_isWholeNumberErrorMessage);

/// <summary>
/// An override for the error message that is displayed if the value entered is not a number.
/// A complete sentence of the form: ‘[Whatever it is] must be a number’
/// <br/>e.g. "Median age must be a number"
/// </summary>
private readonly string _mustBeNumberErrorMessage;
public override string MustBeNumberErrorMessage => GetResourceValue(_mustBeNumberErrorMessage);

/// <summary>
/// A complete sentence of the form: ‘Enter [whatever it is]’.
/// <br/>For example, ‘Enter your first name’.
/// </summary>
public override string ErrorMessageIfMissing => GetResourceValue(base.ErrorMessageIfMissing);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;

namespace GovUkDesignSystem.Attributes.DataBinding
{
public class GovUkDataBindingOptionalDecimalLocalisableErrorTextAttribute : GovUkDataBindingOptionalDecimalErrorTextAttribute
{
public GovUkDataBindingOptionalDecimalLocalisableErrorTextAttribute(string nameAtStartOfSentence = "", string resourceName = "", string mustBeNumberErrorMessage = "", Type resourceType = null): base(nameAtStartOfSentence)
{
if (resourceType == null ^ string.IsNullOrEmpty(resourceName))
{
throw new ArgumentNullException("resourceName or resourceType cannot be null or empty while the other is not null or empty");
}

if (string.IsNullOrEmpty(nameAtStartOfSentence))
{
throw new ArgumentNullException("nameAtStartOfSentence cannot be null or empty");
}
_mustBeNumberErrorMessage = mustBeNumberErrorMessage;
ResourceType = resourceType;
ResourceName = resourceName;
}

/// <summary>
/// An override for the error message that is displayed if the value entered is not a number.
/// A complete sentence of the form: ‘[Whatever it is] must be a number’
/// <br/>e.g. "Median age must be a number"
/// </summary>
private readonly string _mustBeNumberErrorMessage;
public override string MustBeNumberErrorMessage => GetResourceValue(_mustBeNumberErrorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;

namespace GovUkDesignSystem.Attributes.DataBinding
{
public class GovUkDataBindingOptionalIntLocalisableErrorTextAttribute : GovUkDataBindingOptionalIntErrorTextAttribute
{
public GovUkDataBindingOptionalIntLocalisableErrorTextAttribute(string nameAtStartOfSentence = "", string mustBeNumberErrorMessage = "", string resourceName = "", string isWholeNumberErrorMessage = "", Type resourceType = null): base(nameAtStartOfSentence)
{

if(string.IsNullOrEmpty(nameAtStartOfSentence) && (string.IsNullOrEmpty(isWholeNumberErrorMessage) || string.IsNullOrEmpty(mustBeNumberErrorMessage)))
{
throw new ArgumentNullException("nameAtStartOfSentence cannot be null or empty unless all error messages are overwritten");
}

if (resourceType == null ^ string.IsNullOrEmpty(resourceName))
{
throw new ArgumentNullException("resourceName or resourceType cannot be null or empty while the other is not null or empty");
}

_mustBeNumberErrorMessage = mustBeNumberErrorMessage;
_isWholeNumberErrorMessage = isWholeNumberErrorMessage;
ResourceType = resourceType;
ResourceName = resourceName;
}

/// <summary>
/// An override for the error message that is displayed if the value entered is not a whole number.
/// A complete sentence of the form: ‘[Whatever it is] must be a whole number’
/// <br/>e.g. "Median age must be a whole number"
/// </summary>
private readonly string _isWholeNumberErrorMessage;
public override string IsWholeNumberErrorMessage => GetResourceValue(_isWholeNumberErrorMessage);

/// <summary>
/// An override for the error message that is displayed if the value entered is not a number.
/// A complete sentence of the form: ‘[Whatever it is] must be a number’
/// <br/>e.g. "Median age must be a number"
/// </summary>
private readonly string _mustBeNumberErrorMessage;
public override string MustBeNumberErrorMessage => GetResourceValue(_mustBeNumberErrorMessage);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

Expand All @@ -12,10 +13,11 @@ namespace GovUkDesignSystem.Attributes
public class GovUkRadioCheckboxLabelTextAttribute : Attribute
{
public string Text { get; set; }

/// <summary>
/// Returns the Text property of any GovUkRadioCheckboxLabelTextAttribute on the enum value
/// If no attribute exists returns enumValue.ToString()
/// Returns the Text property of any GovUkRadioCheckboxLabelTextAttribute on the enum value.
/// If no copy of this attribute exists, instead returns the value of the Display attribute.
/// If neither attribute exists returns enumValue.ToString()
/// </summary>
/// <param name="enumValue"></param>
/// <returns></returns>
Expand All @@ -25,9 +27,10 @@ public static string GetLabelText(Enum enumValue)
.GetMember(enumValue.ToString())
.Single()
.GetCustomAttribute<GovUkRadioCheckboxLabelTextAttribute>();
string displayName = enumValue.GetType().GetMember(enumValue.ToString()).Single().GetCustomAttribute<DisplayAttribute>()?.GetDescription();

return attribute == null ? enumValue.ToString() : attribute.Text;
return attribute != null ? attribute.Text : displayName ?? enumValue.ToString();
}

}
}
}
23 changes: 19 additions & 4 deletions GovUkDesignSystem/GovUkDesignSystemComponents/Footer.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,30 @@
<path fill="currentColor" d="M421.5 142.8V.1l-50.7 32.3v161.1h112.4v-50.7zm-122.3-9.6A47.12 47.12 0 0 1 221 97.8c0-26 21.1-47.1 47.1-47.1 16.7 0 31.4 8.7 39.7 21.8l42.7-27.2A97.63 97.63 0 0 0 268.1 0c-36.5 0-68.3 20.1-85.1 49.7A98 98 0 0 0 97.8 0C43.9 0 0 43.9 0 97.8s43.9 97.8 97.8 97.8c36.5 0 68.3-20.1 85.1-49.7a97.76 97.76 0 0 0 149.6 25.4l19.4 22.2h3v-87.8h-80l24.3 27.5zM97.8 145c-26 0-47.1-21.1-47.1-47.1s21.1-47.1 47.1-47.1 47.2 21 47.2 47S123.8 145 97.8 145" />
</svg>
<span class="govuk-footer__licence-description">
All content is available under the
<a class="govuk-footer__link" href="https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" rel="license">Open Government Licence v3.0</a>, except where otherwise stated
@{if(Model?.LicenseDescription?.Html != null)
{
await Html.RenderPartialAsync("/GovUkDesignSystemComponents/SubComponents/HtmlText.cshtml", Model.LicenseDescription);
}
else
{
<text>All content is available under the <a class="govuk-footer__link" href = "https://www.nationalarchives.gov.uk/doc/open-government-licence/version/3/" rel = "license" > Open Government Licence v3.0 </a>, except where otherwise stated</text>
}
}
</span>
</div>
<div class="govuk-footer__meta-item">
<a class="govuk-footer__link govuk-footer__copyright-logo"
href="https://www.nationalarchives.gov.uk/information-management/re-using-public-sector-information/uk-government-licensing-framework/crown-copyright/">
© Crown copyright
</a>
@{if(string.IsNullOrEmpty(Model?.CopyrightText?.Text))
{
<text>© Crown copyright</text>
}
else
{
@Model.CopyrightText.Text;
}
}
</a>
</div>
</div>
</div>
Expand Down
38 changes: 38 additions & 0 deletions GovUkDesignSystem/GovUkDesignSystemComponents/FooterViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ public class FooterViewModel
/// </summary>
public Dictionary<string, string> Attributes { get; set; }

/// <summary>
/// HTML override to the text in the license description, to allow for localisation.
/// </summary>
public FooterLicenseDescriptionViewModel LicenseDescription { get; set; } = null;

/// <summary>
/// Text override to the copyright text, to allow for localisation.
/// </summary>
public FooterCopyrightTextViewModel CopyrightText { get; set; }
}

public class FooterMetaNavigationViewModel : IHtmlText
Expand Down Expand Up @@ -106,4 +115,33 @@ public class FooterLinksViewModel
public Dictionary<string, string> Attributes { get; set; }

}

/// <summary>
/// If either the HTML or Text are specified here, they will override the default English text for the license description
/// </summary>
public class FooterLicenseDescriptionViewModel: IHtmlText
{
/// <summary>
/// HTML to add to the license description in the footer
/// Set this using the @&lt;text&gt;&lt;/text&gt; syntax
/// </summary>
public Func<object, object> Html { get; set; } = null;

/// <summary>
/// Text to override the licence description in the footer.
/// If either the 'Html' specified, this option is ignored.
/// </summary>
public string Text { get; set; }
}

/// <summary>
/// If either the HTML or Text are specified here, they will override the default English text for copyright in the footer
/// </summary>
public class FooterCopyrightTextViewModel
{
/// <summary>
/// Text to override the copyright text in the footer.
/// </summary>
public string Text { get; set; }
}
}
Loading