There is no definitive answer to that as YMMV.
We do literally have 100+ types of enums, and we implemented record helpers for each one, simply to ensure that all enums had the same capabilities. AsString, FromString, Name, and in some cases more texts, or biz.logic associated with the enum.
We actually did the last push for this model this autumn, to get rid of the last couple of dozens of standalone TypeNameToStr functions.
We also introduced app wide translations (Norwegian, Swedish, English) using Sisulizer.
It turned out that using attributes for naming was a bit of a challenge, since you can't use a resourcestring as an argument to an attribute - go figure.
resourcestring
sName = 'Name';
type
AttrNameAttribute = class(TCustomAttribute)
constructor Create(const aName: String);
end;
type
TSomeType = class
private
FaName: string;
procedure SetaName(const Value: string);
public
[AttrName(sName)] // <-- [dcc32 Error] : E2026 Constant expression expected
property aName: string read FaName write SetaName;
end;
We ended up setting names explicitly in code instead.