PowershellFrameworkCollective / psframework

A module that provides tools for other modules and scripts
MIT License
435 stars 41 forks source link

PsfValidateScript doesn't show localized message provided via ErrorString #640

Closed azra1l closed 2 months ago

azra1l commented 2 months ago

When i use the PsfValidateScript attribute with the ErrorString parameter, it won't show the localized error message and instead show a generic message including the scriptblock. It doesn't matter if scriptblock or message come predefined.

See the example module i included, and this test scenario:

PS C:\Test> Import-Module Test -Force

PS C:\Test> Get-PSFConfigValue -FullName PSFramework.Localization.Language
en-US

PS C:\Test> Get-PSFLocalizedString -Module Test -Name Test.PSF.ValidateMessage
Value {0} is invalid!

PS C:\Test> Test-PSFValidateScript -TestSBN_ES 123
Test-PSFValidateScript: Cannot validate argument on parameter 'TestSBN_ES'. Error executing validation script: 123 against { $_ -in ("xyz","456") }

PS C:\Test> Test-PSFValidateScript -TestSBN_EM 123
Test-PSFValidateScript: Cannot validate argument on parameter 'TestSBN_EM'. Value 123 is invalid!

PS C:\Test> Test-PSFValidateScript -TestSB_ES 123
Test-PSFValidateScript: Cannot validate argument on parameter 'TestSB_ES'. Error executing validation script: 123 against { $_ -in ("xyz","456") }

PS C:\Test> Test-PSFValidateScript -TestSB_EM 123
Test-PSFValidateScript: Cannot validate argument on parameter 'TestSB_EM'. Value 123 is invalid!

PS C:\Test> Test-PSFValidateScript -TestSBN_ES xyz -TestSBN_EM xyz -TestSB_ES xyz -TestSB_EM xyz
True

PS C:\Test> (Get-Module PSFramework).Version.ToString()
1.11.343

Test.zip

FriedrichWeinmann commented 2 months ago

Heya @azra1l , in order to help avoid collision between modules, I try to enforce module-namespacing with the strings. When you import your language files like this:

Import-PSFLocalizedString -Path "$($PSScriptRoot)\strings_en.psd1" -Module 'Test' -Language 'en-US'

It is going to update the keys of all strings by prepending the module name as another name segment. The String 'Test.PSF.ValidateMessage' will in this case then be stored as 'Test.Test.PSF.ValidateMessage'. Almost all localization-using components can properly detect from which module they are called, so in most cases you never notice.

The validation attributes are an exception to that unfortunately and you will need to explicitly specify the module name as part of the string:

[PsfValidateScript({$_ -in ("xyz","456")}, ErrorString = 'Test.PSF.ValidateMessage')]

then becomes:

[PsfValidateScript({$_ -in ("xyz","456")}, ErrorString = 'Test.Test.PSF.ValidateMessage')]

Notes on Set-PSFScriptBlock

When defining the scriptblock centrally, there are two switch parameters that are easy to miss:

Global will run the scriptblock outside of your module's context. No internas visible, for good or ill. It will also break runspace affinity, so if you want to access it from a background runspace, that's useful. Local will restrict the scriptblock to the current runspace - if you do multiple runspaces, you will need to define it in each runspace you want to use it first.

Both parameters really matter only when doing multiple runspaces, Local is usually preferred if you plan to load your module in each runspace, Global makes it still work smoothly, if you only inject your function without the full module.

And if you're wondering "Why is 'Local' not the default behavior?" - that would be a breaking change, so we're stuck with it.

azra1l commented 2 months ago

Aye, that makes total sense. No idea why that didn't occur to me, as it's also how the naming works for all the other functions with a module parameter.