Descriptions Introducction
As applications get bigger is more difficult to keep all the names consistent (entity names, query names, property names...). Specially with the advent of Signum.Web, a lot of redundancy appears.
Description infrastructure has been included in Signum Framework 2.0
Description Attributes allow to put a user-friendly name over your types, properties and enums, so it can be used by the framework in many different scenarios. Examples:
- Windows and Web:
- Titles (NormalWindows, AdminWindow, SearchWindow)
- EntityControls labels and enum combo boxes
- SearchControls columns and filters
- MainMenu (AdminOptions and ExploreOptions)
- Error messages from Signum Framework (i.e: Validation)
- Future (Signum.Extensions)
- Operations?
- Reports?
- Authorization Menus?
- Help?
In order to do so, it provides different features:
- NiceName: Simple description for types, enums and properties.
- NicePluralName : Plural description for types only, necessary for MainMenu, SearchControl, some error messages....
- GetGender & GenderAwareResource: Gender detection for types only, necessary for some error messages and help in the future (not necessary in English)
- Localization
public static class DescriptionManager
{
public static Func<Type, string> CleanTypeName = t => t.Name;
public static Func<Type, Type> CleanType = t => t;
public static string NiceToString(this Enum a)
public static string NiceName(this Type type)
public static string NiceName(this PropertyInfo pi)
public static bool IsDefaultName(this PropertyInfo pi)
public static string NicePluralName(this Type type
public static Gender GetGender(this Type type)
public static string GetGenderAwareResource(this ResourceManager resource, string resourceKey, Gender gender)
public static string GetGenderAwareResource(this Type type, Expression<Func<string>> resource)
}
Fortunately, most of the work can be done automatically. NiceName is just the name of the type, enum or property after doing SpacePascal or splitting by underscore. For accuracy, it's is also able to clean 'hungarian' notations on your types (DN postfix, cls prefix) though the public static delegate CleanType, and is able to propagate overridden descriptions of types to properties for properties with default name.
Also, NaturalLanguageTool is able to infer the plural description and the gender from a the NiceName. We will see more about this class at the end of the page.
However, you always have the option to override the automatic behavior, in order to do so there are two models for description: Hard-coded and Resource-based.
Hard-coded
Using attributes with the text over the entities itself to override the default behavior:
- NiceName using System.ComponentModel.DescriptionAttribute (.Net Framework).
- PluralNiceName using PluralDescriptionAttribute.
- Gender using GenderAttribute.
public class DescriptionAttribute : Attribute
{
public virtual string Description { get; }
public DescriptionAttribute(string description);
}
[AttributeUsage(AttributeTargets.Class)]
public class PluralDescriptionAttribute : Attribute
{
public string PluralDescription { get; private set; }
public PluralDescriptionAttribute(string pluralDescription)
}
[AttributeUsage(AttributeTargets.Class)]
public class GenderAttribute : Attribute
{
public Gender Gender { get; private set; }
public GenderAttribute(Gender gender)
}
Example in a English-only application:
[PluralDescription("People")]
public class PersonDN : Entity
{
[Description("1st Name")]
public FirstName { get{...} set{..}}
public LastName { get{...} set{..}}
}
Or in a Spanish-only application:
[Gender(Gender.Femenine)]
public class SedeDN: Entity
{
}
Resource-based
As you see, Localization is orthogonal to NiceName, PluralNiceName and Gender. If you translate an application (or module) to Spanish, you will need to change the descriptions of your entities, enums and properties. You will also need to change the plural version of your entities, and the fact that a name is masculine, feminine or neutral (i.e: german) also changes from language to language. The natural place to put this information is on satellite assemblies of resources.
Once you place a LocalizeDescriptionsAttribute at the assembly level (AssemblyInfo.cs), whenever you call NiceName, NicePluralName or GetGender DescriptionManager will look at the resources of the satellite assembly first.
[AttributeUsage(AttributeTargets.Assembly)]
public class LocalizeDescriptionsAttribute : Attribute
{
}
The keys of the resources have to follow some rules:
- NiceName: Use "MyType" for a type. "MyType_MyProperty" for a property and "MyEnum_EnumValue" for an enum.
- PluralNiceName: Use "MyType_Plural" for a Type.
- Gender using GenderAttribute: Use "MyType_Gender" and use "m" (for Masculine), "f" (for Feminine) and "n" (for Neuter).
So an Example for this code in English:
public class PersonDN : Entity
{
public FirstName { get{...} set{..}}
public LastName { get{...} set{..}}
}
would be this Spanish resources:
| Key | Value |
|---|
| PersonDN | Persona |
| PersonDN_Plural | Personas |
| PersonDN_Gender | f |
| PersonDN_FirstName | Nombre |
| PersonDN_LastName | Apellido |
Fortunately, PersonDN_Plural and PersonDN_Gender can be inferred by NaturalLanguageTools once you change PersonDN to Persona.
Note that you can also mix Resources and Attributes, but if the assembly is marked with LocalizeDescriptionsAttribute, resources will take priority, otherwise resources will be ignored.
The algorithm is represented in the following diagram.
NaturalLanguageTools
Most of the logic that is able to infer the plural form of a singular text, or the gender of a name is found in this class.
The class itself doesn't do a lot of work, instead it serves as a repository for language-specific IPluralizer and IGenderDetector
public static class NaturalLanguageTools
{
public static Dictionary<string, IPluralizer> Pluralizers = new Dictionary<string, IPluralizer>
{
{"es", new SpanishPluralizer()},
{"en", new EnglishPluralizer()},
};
public static Dictionary<string, IGenderDetector> GenderDetectors = new Dictionary<string, IGenderDetector>
{
{"es", new SpanishGenderDetector()},
};
public static Gender GetGender(string name);
public static string Pluralize(string singularName);
}
public interface IPluralizer
{
string MakePlural(string singularName);
}
public interface IGenderDetector
{
Gender GetGender(string name);
}
If your speak a different language maybe is good to take a look at the current ones and write your own pluralizer/gender detector with some heuristics. You will never get it perfect but it's easy to save you 80% of the work.