Skip to content

Commit 5142c37

Browse files
Add Internalization Support (#86)
- **feat: ✨ Add localization support and improve error handling** - **feat: ✨ Add localization support to CHANGELOG** - **fix: 🐛 Correct variable name typo in Build-PSBuildMAMLHelp function**
1 parent abd30f9 commit 5142c37

17 files changed

+325
-142
lines changed

.github/workflows/test.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,10 @@ jobs:
1616
- uses: actions/checkout@v4
1717
- name: Test
1818
shell: pwsh
19-
run: ./build.ps1 -Task Test -Bootstrap
19+
env:
20+
DEBUG: ${{ runner.debug == '1' }}
21+
run: |
22+
if($env:DEBUG -eq 'true' -or $env:DEBUG -eq '1') {
23+
$DebugPreference = 'Continue'
24+
}
25+
./build.ps1 -Task Test -Bootstrap

.vscode/settings.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,13 @@
66
"powershell.codeFormatting.addWhitespaceAroundPipe": true,
77
"powershell.codeFormatting.useCorrectCasing": true,
88
"powershell.codeFormatting.newLineAfterOpenBrace": true,
9-
"powershell.codeFormatting.alignPropertyValuePairs": true
9+
"powershell.codeFormatting.alignPropertyValuePairs": true,
10+
"search.useIgnoreFiles": true,
11+
"search.exclude": {
12+
"**/node_modules": true,
13+
"**/bower_components": true,
14+
"**/*.code-search": true,
15+
"**/.ruby-lsp": true,
16+
"Output/**": true
17+
}
1018
}

.vscode/tasks.json

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,42 @@
22
// See https://go.microsoft.com/fwlink/?LinkId=733558
33
// for the documentation about the tasks.json format
44
"version": "2.0.0",
5-
65
// Start PowerShell (pwsh on *nix)
76
"windows": {
87
"options": {
98
"shell": {
109
"executable": "powershell.exe",
11-
"args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command" ]
10+
"args": [
11+
"-NoProfile",
12+
"-ExecutionPolicy",
13+
"Bypass",
14+
"-Command"
15+
]
1216
}
1317
}
1418
},
1519
"linux": {
1620
"options": {
1721
"shell": {
1822
"executable": "/usr/bin/pwsh",
19-
"args": [ "-NoProfile", "-Command" ]
23+
"args": [
24+
"-NoProfile",
25+
"-Command"
26+
]
2027
}
2128
}
2229
},
2330
"osx": {
2431
"options": {
2532
"shell": {
2633
"executable": "/usr/local/bin/pwsh",
27-
"args": [ "-NoProfile", "-Command" ]
34+
"args": [
35+
"-NoProfile",
36+
"-Command"
37+
]
2838
}
2939
}
3040
},
31-
3241
"tasks": [
3342
{
3443
"label": "Clean",
@@ -69,6 +78,12 @@
6978
"label": "Publish",
7079
"type": "shell",
7180
"command": "${cwd}/build.ps1 -Task Publish -Verbose"
81+
},
82+
{
83+
"label": "Bootstrap",
84+
"type": "shell",
85+
"command": "${cwd}/build.ps1 -Task Init -Bootstrap -Verbose",
86+
"problemMatcher": []
7287
}
7388
]
7489
}

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
99

1010
- Add new dependencies variables to allow end user to modify which tasks are
1111
run.
12+
- Add localization support.
1213

1314
## [0.7.2] 2025-05-21
1415

PowerShellBuild/PowerShellBuild.psm1

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Dot source public functions
22
$private = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Private/*.ps1')) -Recurse)
3-
$public = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Public/*.ps1')) -Recurse)
3+
$public = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Public/*.ps1')) -Recurse)
44
foreach ($import in $public + $private) {
55
try {
66
. $import.FullName
@@ -9,6 +9,43 @@ foreach ($import in $public + $private) {
99
}
1010
}
1111

12+
data LocalizedData {
13+
# Load here in case Import-LocalizedData is not available
14+
ConvertFrom-StringData @'
15+
NoCommandsExported=No commands have been exported. Skipping markdown generation.
16+
FailedToGenerateMarkdownHelp=Failed to generate markdown help. : {0}
17+
AddingFileToPsm1=Adding [{0}] to PSM1
18+
MakeCabNotAvailable=MakeCab.exe is not available. Cannot create help cab.
19+
DirectoryAlreadyExists=Directory already exists [{0}].
20+
PathLongerThan3Chars=`$Path [{0}] must be longer than 3 characters.
21+
BuildSystemDetails=Build System Details:
22+
BuildModule=Build Module: {0}`:{1}
23+
PowerShellVersion=PowerShell Version: {0}
24+
EnvironmentVariables={0}`Environment variables:
25+
PublishingVersionToRepository=Publishing version [{0}] to repository [{1}]...
26+
FolderDoesNotExist=Folder does not exist: {0}
27+
PathArgumentMustBeAFolder=The Path argument must be a folder. File paths are not allowed.
28+
UnableToFindModuleManifest=Unable to find module manifest [{0}]. Can't import module
29+
PesterTestsFailed=One or more Pester tests failed
30+
CodeCoverage=Code Coverage
31+
Type=Type
32+
CodeCoverageLessThanThreshold=Code coverage: [{0}] is [{1:p}], which is less than the threshold of [{2:p}]
33+
CodeCoverageCodeCoverageFileNotFound=Code coverage file [{0}] not found.
34+
SeverityThresholdSetTo=SeverityThreshold set to: {0}
35+
PSScriptAnalyzerResults=PSScriptAnalyzer results:
36+
ScriptAnalyzerErrors=One or more ScriptAnalyzer errors were found!
37+
ScriptAnalyzerWarnings=One or more ScriptAnalyzer warnings were found!
38+
ScriptAnalyzerIssues=One or more ScriptAnalyzer issues were found!
39+
'@
40+
}
41+
$importLocalizedDataSplat = @{
42+
BindingVariable = 'LocalizedData'
43+
FileName = 'Messages.psd1'
44+
ErrorAction = 'SilentlyContinue'
45+
}
46+
Import-LocalizedData @importLocalizedDataSplat
47+
48+
1249
Export-ModuleMember -Function $public.Basename
1350

1451
# $psakeTaskAlias = 'PowerShellBuild.psake.tasks'

PowerShellBuild/Public/Build-PSBuildMAMLHelp.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ function Build-PSBuildMAMLHelp {
1414
Uses PlatyPS to generate MAML XML help from markdown files in ./docs
1515
and saves the XML file to a directory under ./output/MyModule
1616
#>
17-
[cmdletbinding()]
17+
[CmdletBinding()]
1818
param(
1919
[parameter(Mandatory)]
2020
[string]$Path,

PowerShellBuild/Public/Build-PSBuildMarkdown.ps1

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function Build-PSBuildMarkdown {
2525
2626
Analysis the comment-based help of the MyModule module and create markdown documents under ./docs/en-US.
2727
#>
28-
[cmdletbinding()]
28+
[CmdletBinding()]
2929
param(
3030
[parameter(Mandatory)]
3131
[string]$ModulePath,
@@ -56,7 +56,7 @@ function Build-PSBuildMarkdown {
5656

5757
try {
5858
if ($moduleInfo.ExportedCommands.Count -eq 0) {
59-
Write-Warning 'No commands have been exported. Skipping markdown generation.'
59+
Write-Warning $LocalizedData.NoCommandsExported
6060
return
6161
}
6262

@@ -93,7 +93,7 @@ function Build-PSBuildMarkdown {
9393
}
9494
New-MarkdownHelp @newMDParams > $null
9595
} catch {
96-
Write-Error "Failed to generate markdown help. : $_"
96+
Write-Error ($LocalizedData.FailedToGenerateMarkdownHelp -f $_)
9797
} finally {
9898
Remove-Module $moduleName
9999
}

PowerShellBuild/Public/Build-PSBuildModule.ps1

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,58 @@
1+
# spell-checker:ignore modulename
12
function Build-PSBuildModule {
23
<#
34
.SYNOPSIS
45
Builds a PowerShell module based on source directory
56
.DESCRIPTION
6-
Builds a PowerShell module based on source directory and optionally concatenates
7-
public/private functions from separete files into monolithic .PSM1 file.
7+
Builds a PowerShell module based on source directory and optionally
8+
concatenates public/private functions from separate files into
9+
monolithic .PSM1 file.
810
.PARAMETER Path
911
The source module path.
1012
.PARAMETER DestinationPath
1113
Destination path to write "built" module to.
1214
.PARAMETER ModuleName
1315
The name of the module.
1416
.PARAMETER Compile
15-
Switch to indicate if separete function files should be concatenated into monolithic .PSM1 file.
17+
Switch to indicate if separate function files should be concatenated
18+
into monolithic .PSM1 file.
1619
.PARAMETER CompileHeader
1720
String that will be at the top of your PSM1 file.
1821
.PARAMETER CompileFooter
1922
String that will be added to the bottom of your PSM1 file.
2023
.PARAMETER CompileScriptHeader
2124
String that will be added to your PSM1 file before each script file.
2225
.PARAMETER CompileScriptFooter
23-
String that will be added to your PSM1 file beforeafter each script file.
26+
String that will be added to your PSM1 file after each script file.
2427
.PARAMETER ReadMePath
25-
Path to project README. If present, this will become the "about_<ModuleName>.help.txt" file in the build module.
28+
Path to project README. If present, this will become the
29+
"about_<ModuleName>.help.txt" file in the build module.
2630
.PARAMETER CompileDirectories
27-
List of directories containing .ps1 files that will also be compiled into the PSM1.
31+
List of directories containing .ps1 files that will also be compiled
32+
into the PSM1.
2833
.PARAMETER CopyDirectories
2934
List of directories that will copying "as-is" into the build module.
3035
.PARAMETER Exclude
31-
Array of files (regular expressions) to exclude from copying into built module.
36+
Array of files (regular expressions) to exclude from copying into built
37+
module.
3238
.PARAMETER Culture
33-
UI Culture. This is used to determine what culture directory to store "about_<ModuleName>.help.txt" in.
39+
UI Culture. This is used to determine what culture directory to store
40+
"about_<ModuleName>.help.txt" in.
3441
.EXAMPLE
35-
PS> $buildParams = @{
42+
$buildParams = @{
3643
Path = ./MyModule
3744
DestinationPath = ./Output/MoModule/0.1.0
3845
ModuleName = MyModule
3946
Exclude = @()
4047
Compile = $false
4148
Culture = (Get-UICulture).Name
4249
}
43-
PS> Build-PSBuildModule @buildParams
50+
Build-PSBuildModule @buildParams
4451
45-
Build module from source directory './MyModule' and save to '/Output/MoModule/0.1.0'
52+
Build module from source directory './MyModule' and save to
53+
'/Output/MoModule/0.1.0'
4654
#>
47-
[cmdletbinding()]
55+
[CmdletBinding()]
4856
param(
4957
[parameter(Mandatory)]
5058
[string]$Path,
@@ -77,49 +85,80 @@ function Build-PSBuildModule {
7785
)
7886

7987
if (-not (Test-Path -LiteralPath $DestinationPath)) {
80-
New-Item -Path $DestinationPath -ItemType Directory -Verbose:$VerbosePreference > $null
88+
$newItemSplat = @{
89+
Path = $DestinationPath
90+
ItemType = 'Directory'
91+
Verbose = $VerbosePreference
92+
}
93+
New-Item @newItemSplat > $null
8194
}
8295

8396
# Copy "non-processed files"
84-
Get-ChildItem -Path $Path -Include '*.psm1', '*.psd1', '*.ps1xml' -Depth 1 | Copy-Item -Destination $DestinationPath -Force
97+
$getChildItemSplat = @{
98+
Path = $Path
99+
Include = '*.psm1', '*.psd1', '*.ps1xml'
100+
Depth = 1
101+
}
102+
Get-ChildItem @getChildItemSplat |
103+
Copy-Item -Destination $DestinationPath -Force
85104
foreach ($dir in $CopyDirectories) {
86105
$copyPath = [IO.Path]::Combine($Path, $dir)
87106
Copy-Item -Path $copyPath -Destination $DestinationPath -Recurse -Force
88107
}
89108

90109
# Copy README as about_<modulename>.help.txt
91110
if (-not [string]::IsNullOrEmpty($ReadMePath)) {
92-
$culturePath = [IO.Path]::Combine($DestinationPath, $Culture)
93-
$aboutModulePath = [IO.Path]::Combine($culturePath, "about_$($ModuleName).help.txt")
94-
if(-not (Test-Path $culturePath -PathType Container)) {
111+
$culturePath = [IO.Path]::Combine($DestinationPath, $Culture)
112+
$aboutModulePath = [IO.Path]::Combine(
113+
$culturePath,
114+
"about_$($ModuleName).help.txt"
115+
)
116+
if (-not (Test-Path $culturePath -PathType Container)) {
95117
New-Item $culturePath -Type Directory -Force > $null
96-
Copy-Item -LiteralPath $ReadMePath -Destination $aboutModulePath -Force
118+
$copyItemSplat = @{
119+
LiteralPath = $ReadMePath
120+
Destination = $aboutModulePath
121+
Force = $true
122+
}
123+
Copy-Item @copyItemSplat
97124
}
98125
}
99126

100-
# Copy source files to destination and optionally combine *.ps1 files into the PSM1
127+
# Copy source files to destination and optionally combine *.ps1 files
128+
# into the PSM1
101129
if ($Compile.IsPresent) {
102130
$rootModule = [IO.Path]::Combine($DestinationPath, "$ModuleName.psm1")
103131

104132
# Grab the contents of the copied over PSM1
105133
# This will be appended to the end of the finished PSM1
106134
$psm1Contents = Get-Content -Path $rootModule -Raw
107-
'' | Out-File -FilePath $rootModule -Encoding utf8
135+
'' | Out-File -FilePath $rootModule -Encoding 'utf8'
108136

109137
if ($CompileHeader) {
110-
$CompileHeader | Add-Content -Path $rootModule -Encoding utf8
138+
$CompileHeader | Add-Content -Path $rootModule -Encoding 'utf8'
111139
}
112140

113141
$resolvedCompileDirectories = $CompileDirectories | ForEach-Object {
114142
[IO.Path]::Combine($Path, $_)
115143
}
116-
$allScripts = Get-ChildItem -Path $resolvedCompileDirectories -Filter '*.ps1' -File -Recurse -ErrorAction SilentlyContinue
144+
$getChildItemSplat = @{
145+
Path = $resolvedCompileDirectories
146+
Filter = '*.ps1'
147+
File = $true
148+
Recurse = $true
149+
ErrorAction = 'SilentlyContinue'
150+
}
151+
$allScripts = Get-ChildItem @getChildItemSplat
117152

118153
$allScripts = $allScripts | Remove-ExcludedItem -Exclude $Exclude
119154

155+
$addContentSplat = @{
156+
Path = $rootModule
157+
Encoding = 'utf8'
158+
}
120159
$allScripts | ForEach-Object {
121160
$srcFile = Resolve-Path $_.FullName -Relative
122-
Write-Verbose "Adding [$srcFile] to PSM1"
161+
Write-Verbose ($LocalizedData.AddingFileToPsm1 -f $srcFile)
123162

124163
if ($CompileScriptHeader) {
125164
Write-Output $CompileScriptHeader
@@ -130,15 +169,14 @@ function Build-PSBuildModule {
130169
if ($CompileScriptFooter) {
131170
Write-Output $CompileScriptFooter
132171
}
172+
} | Add-Content @addContentSplat
133173

134-
} | Add-Content -Path $rootModule -Encoding utf8
135-
136-
$psm1Contents | Add-Content -Path $rootModule -Encoding utf8
174+
$psm1Contents | Add-Content @addContentSplat
137175

138176
if ($CompileFooter) {
139-
$CompileFooter | Add-Content -Path $rootModule -Encoding utf8
177+
$CompileFooter | Add-Content @addContentSplat
140178
}
141-
} else{
179+
} else {
142180
# Copy everything over, then remove stuff that should have been excluded
143181
# It's just easier this way
144182
$copyParams = @{
@@ -157,13 +195,26 @@ function Build-PSBuildModule {
157195
}
158196
}
159197
}
160-
$toRemove | Remove-Item -Recurse -Force -ErrorAction Ignore
198+
$toRemove | Remove-Item -Recurse -Force -ErrorAction 'Ignore'
161199
}
162200

163201
# Export public functions in manifest if there are any public functions
164-
$publicFunctions = Get-ChildItem $Path/Public/*.ps1 -Recurse -ErrorAction SilentlyContinue
202+
$getChildItemSplat = @{
203+
Recurse = $true
204+
ErrorAction = 'SilentlyContinue'
205+
Path = "$Path/Public/*.ps1"
206+
}
207+
$publicFunctions = Get-ChildItem @getChildItemSplat
165208
if ($publicFunctions) {
166-
$outputManifest = [IO.Path]::Combine($DestinationPath, "$ModuleName.psd1")
167-
Update-Metadata -Path $OutputManifest -PropertyName FunctionsToExport -Value $publicFunctions.BaseName
209+
$outputManifest = [IO.Path]::Combine(
210+
$DestinationPath,
211+
"$ModuleName.psd1"
212+
)
213+
$updateMetadataSplat = @{
214+
Path = $OutputManifest
215+
PropertyName = 'FunctionsToExport'
216+
Value = $publicFunctions.BaseName
217+
}
218+
Update-Metadata @updateMetadataSplat
168219
}
169220
}

0 commit comments

Comments
 (0)