1
+ function Export-PSMDString
2
+ {
3
+ <#
4
+ . SYNOPSIS
5
+ Parses a module that uses the PSFramework localization feature for strings and their value.
6
+
7
+ . DESCRIPTION
8
+ Parses a module that uses the PSFramework localization feature for strings and their value.
9
+ This command can be used to generate and update the language files used by the module.
10
+ It is also used in automatic tests, ensuring no abandoned string has been left behind and no key is unused.
11
+
12
+ . PARAMETER ModuleRoot
13
+ The root of the module to process.
14
+ Must be the root folder where the psd1 file is stored in.
15
+
16
+ . EXAMPLE
17
+ PS C:\> Export-PSMDString -ModuleRoot 'C:\Code\Github\MyModuleProject\MyModule'
18
+
19
+ Generates the strings data for the MyModule module.
20
+ #>
21
+ [CmdletBinding ()]
22
+ param (
23
+ [Parameter (Mandatory = $true , ValueFromPipeline = $true , ValueFromPipelineByPropertyName = $true )]
24
+ [Alias (' ModuleBase' )]
25
+ [string ]
26
+ $ModuleRoot
27
+ )
28
+
29
+ process
30
+ {
31
+ # region Find Language Files : $languageFiles
32
+ $languageFiles = @ { }
33
+ $languageFolders = Get-ChildItem - Path $ModuleRoot - Directory | Where-Object Name -match ' ^\w\w-\w\w$'
34
+ foreach ($languageFolder in $languageFolders )
35
+ {
36
+ $languageFiles [$languageFolder.Name ] = @ { }
37
+ foreach ($file in (Get-ChildItem - Path $languageFolder.FullName - Filter * .psd1))
38
+ {
39
+ $languageFiles [$languageFolder.Name ] += Import-PSFPowerShellDataFile - Path $file.FullName
40
+ }
41
+ }
42
+ # endregion Find Language Files : $languageFiles
43
+
44
+ # region Find Keys : $foundKeys
45
+ $foundKeys = foreach ($file in (Get-ChildItem - Path $ModuleRoot - Recurse | Where-Object Extension -match ' ^\.ps1$|^\.psm1$' ))
46
+ {
47
+ $ast = (Read-PSMDScript - Path $file.FullName ).Ast
48
+ $commandAsts = $ast.FindAll ({
49
+ if ($args [0 ] -isnot [System.Management.Automation.Language.CommandAst ]) { return $false }
50
+ if ($args [0 ].CommandElements[0 ].Value -notmatch ' ^Invoke-PSFProtectedCommand$|^Write-PSFMessage$|^Stop-PSFFunction$' ) { return $false }
51
+ if (-not ($args [0 ].CommandElements.ParameterName -match ' ^String$|^ActionString$' )) { return $false }
52
+ $true
53
+ }, $true )
54
+
55
+ foreach ($commandAst in $commandAsts )
56
+ {
57
+ $stringParam = $commandAst.CommandElements | Where-Object ParameterName -match ' ^String$|^ActionString$'
58
+ $stringParamValue = $commandAst.CommandElements [($commandAst.CommandElements.IndexOf ($stringParam ) + 1 )].Value
59
+
60
+ $stringValueParam = $commandAst.CommandElements | Where-Object ParameterName -match ' ^StringValues$|^ActionStringValues$'
61
+ if ($stringValueParam )
62
+ {
63
+ $stringValueParamValue = $commandAst.CommandElements [($commandAst.CommandElements.IndexOf ($stringValueParam ) + 1 )].Extent.Text
64
+ }
65
+ else { $stringValueParamValue = ' ' }
66
+ [PSCustomObject ]@ {
67
+ PSTypeName = ' PSModuleDevelopment.String.ParsedItem'
68
+ File = $file.FullName
69
+ Line = $commandAst.Extent.StartLineNumber
70
+ CommandName = $commandAst.CommandElements [0 ].Value
71
+ String = $stringParamValue
72
+ StringValues = $stringValueParamValue
73
+ }
74
+ }
75
+
76
+ $validateAsts = $ast.FindAll ({
77
+ if ($args [0 ] -isnot [System.Management.Automation.Language.AttributeAst ]) { return $false }
78
+ if ($args [0 ].TypeName -notmatch ' ^PsfValidateScript$|^PsfValidatePattern$' ) { return $false }
79
+ if (-not ($args [0 ].NamedArguments.ArgumentName -eq ' ErrorString' )) { return $false }
80
+ $true
81
+ }, $true )
82
+
83
+ foreach ($validateAst in $validateAsts )
84
+ {
85
+ [PSCustomObject ]@ {
86
+ PSTypeName = ' PSModuleDevelopment.String.ParsedItem'
87
+ File = $file.FullName
88
+ Line = $commandAst.Extent.StartLineNumber
89
+ CommandName = ' [{0}]' -f $validateAst.TypeName
90
+ String = (($validateAst.NamedArguments | Where-Object ArgumentName -eq ' ErrorString' ).Argument.Value -split " \." , 2 )[1 ] # The first element is the module element
91
+ StringValues = ' <user input>, <validation item>'
92
+ }
93
+ }
94
+ }
95
+ # endregion Find Keys : $foundKeys
96
+
97
+ # region Report Findings
98
+ $totalResults = foreach ($languageFile in $languageFiles.Keys )
99
+ {
100
+ # region Phase 1: Matching parsed strings to language file
101
+ $results = @ { }
102
+ foreach ($foundKey in $foundKeys )
103
+ {
104
+ if ($results [$foundKey.String ])
105
+ {
106
+ $results [$foundKey.String ].Entries += $foundKey
107
+ continue
108
+ }
109
+
110
+ $results [$foundKey.String ] = [PSCustomObject ] @ {
111
+ PSTypeName = ' PSmoduleDevelopment.String.LanguageFinding'
112
+ Language = $languageFile
113
+ Surplus = $false
114
+ String = $foundKey.String
115
+ StringValues = $foundKey.StringValues
116
+ Text = $languageFiles [$languageFile ][$foundKey.String ]
117
+ Line = " '{0}' = '{1}' # {2}" -f $foundKey.String , $languageFiles [$languageFile ][$foundKey.String ], $foundKey.StringValues
118
+ Entries = @ ($foundKey )
119
+ }
120
+ }
121
+ $results.Values
122
+ # endregion Phase 1: Matching parsed strings to language file
123
+
124
+ # region Phase 2: Finding unneeded strings
125
+ foreach ($key in $languageFiles [$languageFile ].Keys)
126
+ {
127
+ if ($key -notin $foundKeys.String )
128
+ {
129
+ [PSCustomObject ] @ {
130
+ PSTypeName = ' PSmoduleDevelopment.String.LanguageFinding'
131
+ Language = $languageFile
132
+ Surplus = $true
133
+ String = $key
134
+ StringValues = ' '
135
+ Text = $languageFiles [$languageFile ][$key ]
136
+ Line = ' '
137
+ Entries = @ ()
138
+ }
139
+ }
140
+ }
141
+ # endregion Phase 2: Finding unneeded strings
142
+ }
143
+ $totalResults | Sort-Object String
144
+ # endregion Report Findings
145
+ }
146
+ }
0 commit comments