Localized Attribute Migration for OptionsViewModel
Background
DAX Studio's Options UI is built by PropertyList.cs which reads attributes via reflection:
DisplayNameAttribute → option label
DescriptionAttribute → help text below option
CategoryAttribute → tab grouping
SubcategoryAttribute → sub-grouping within tab
These standard attributes require compile-time constant strings. To localize them, we use custom subclasses that accept a resource key and override the virtual property to return a localized value at runtime.
Localized Attribute Classes
Located in DaxStudio.Common:
LocalizedDisplayNameAttribute extends DisplayNameAttribute
LocalizedDescriptionAttribute extends DescriptionAttribute
LocalizedCategoryAttribute extends CategoryAttribute
LocalizedSubcategoryAttribute extends SubcategoryAttribute
Migration Procedure
Step 1: Replace Attributes
For each property in OptionsViewModel.cs:
// BEFORE
[Category("Editor")]
[DisplayName("Editor Font Size")]
[Description("The font size used for the DAX editor")]
[Subcategory("Formatting")]
[SortOrder(20)]
[MinValue(6), MaxValue(120)]
[DataMember, DefaultValue(11d)]
public double EditorFontSize { get; set; }
// AFTER
[LocalizedCategory("Category_Editor")]
[LocalizedDisplayName("Options_EditorFontSize_DisplayName")]
[LocalizedDescription("Options_EditorFontSize_Description")]
[LocalizedSubcategory("Options_Formatting_Subcategory")]
[SortOrder(20)]
[MinValue(6), MaxValue(120)]
[DataMember, DefaultValue(11d)]
public double EditorFontSize { get; set; }
Step 2: Resource Key Naming
| Attribute |
Pattern |
Example |
| Category |
Category_{Name} |
Category_Editor |
| DisplayName |
Options_{PropertyName}_DisplayName |
Options_EditorFontSize_DisplayName |
| Description |
Options_{PropertyName}_Description |
Options_EditorFontSize_Description |
| Subcategory |
Options_{Name}_Subcategory |
Options_Formatting_Subcategory |
Step 3: Add Resx Entries
Add each key-value pair to src\DaxStudio.UI\Resources\Strings.resx with the original English text as the value.
Known Categories
These are the existing category values to create resource keys for:
- Editor, Results, Trace, Server Timings, Proxy, Query History
- VertiPaq Analyzer, Timeouts, Defaults, Sounds, DAX Formatter
- Metadata Pane, Custom Export Format, Preview, Privacy, Logging
Known Subcategories
- Grid, Tooltips, Double-Click, Defaults, Theme, Excel File
- Detect Metadata Changes, Hidden Objects, Scrollbars
- Preview Data, Sorting, Benchmark
Important Notes
- Do NOT change
[SortOrder], [MinValue], [MaxValue], [DataMember], [DefaultValue] — these are not user-visible text.
- Do NOT change
[EnumDisplay] or [EnvironmentVariableAttribute] — these control behavior, not display text.
- Properties without a
[DisplayName] attribute are intentionally hidden from the UI — do not add one.
PropertyList.cs should require ZERO changes — the localized attributes extend the standard ones, so GetCustomAttribute(typeof(DisplayNameAttribute)) still finds them.
1---2name: localized-attribute-migration3description: Procedure for migrating DAX Studio OptionsViewModel attributes ([DisplayName], [Description], [Category], [Subcategory]) to their localized equivalents. Use when localizing the Options UI.4---56# Localized Attribute Migration for OptionsViewModel78## Background910DAX Studio's Options UI is built by `PropertyList.cs` which reads attributes via reflection:11- `DisplayNameAttribute` → option label12- `DescriptionAttribute` → help text below option13- `CategoryAttribute` → tab grouping14- `SubcategoryAttribute` → sub-grouping within tab1516These standard attributes require compile-time constant strings. To localize them, we use custom subclasses that accept a resource key and override the virtual property to return a localized value at runtime.1718## Localized Attribute Classes1920Located in `DaxStudio.Common`:21- `LocalizedDisplayNameAttribute` extends `DisplayNameAttribute`22- `LocalizedDescriptionAttribute` extends `DescriptionAttribute`23- `LocalizedCategoryAttribute` extends `CategoryAttribute`24- `LocalizedSubcategoryAttribute` extends `SubcategoryAttribute`2526## Migration Procedure2728### Step 1: Replace Attributes2930For each property in `OptionsViewModel.cs`:3132```csharp33// BEFORE34[Category("Editor")]35[DisplayName("Editor Font Size")]36[Description("The font size used for the DAX editor")]37[Subcategory("Formatting")]38[SortOrder(20)]39[MinValue(6), MaxValue(120)]40[DataMember, DefaultValue(11d)]41public double EditorFontSize { get; set; }4243// AFTER44[LocalizedCategory("Category_Editor")]45[LocalizedDisplayName("Options_EditorFontSize_DisplayName")]46[LocalizedDescription("Options_EditorFontSize_Description")]47[LocalizedSubcategory("Options_Formatting_Subcategory")]48[SortOrder(20)]49[MinValue(6), MaxValue(120)]50[DataMember, DefaultValue(11d)]51public double EditorFontSize { get; set; }52```5354### Step 2: Resource Key Naming5556| Attribute | Pattern | Example |57|-----------|---------|---------|58| Category | `Category_{Name}` | `Category_Editor` |59| DisplayName | `Options_{PropertyName}_DisplayName` | `Options_EditorFontSize_DisplayName` |60| Description | `Options_{PropertyName}_Description` | `Options_EditorFontSize_Description` |61| Subcategory | `Options_{Name}_Subcategory` | `Options_Formatting_Subcategory` |6263### Step 3: Add Resx Entries6465Add each key-value pair to `src\DaxStudio.UI\Resources\Strings.resx` with the original English text as the value.6667## Known Categories6869These are the existing category values to create resource keys for:70- Editor, Results, Trace, Server Timings, Proxy, Query History71- VertiPaq Analyzer, Timeouts, Defaults, Sounds, DAX Formatter72- Metadata Pane, Custom Export Format, Preview, Privacy, Logging7374## Known Subcategories7576- Grid, Tooltips, Double-Click, Defaults, Theme, Excel File77- Detect Metadata Changes, Hidden Objects, Scrollbars78- Preview Data, Sorting, Benchmark7980## Important Notes8182- Do NOT change `[SortOrder]`, `[MinValue]`, `[MaxValue]`, `[DataMember]`, `[DefaultValue]` — these are not user-visible text.83- Do NOT change `[EnumDisplay]` or `[EnvironmentVariableAttribute]` — these control behavior, not display text.84- Properties without a `[DisplayName]` attribute are intentionally hidden from the UI — do not add one.85- `PropertyList.cs` should require ZERO changes — the localized attributes extend the standard ones, so `GetCustomAttribute(typeof(DisplayNameAttribute))` still finds them.