Skip to content

Add Internalization Support #86

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,10 @@ jobs:
- uses: actions/checkout@v4
- name: Test
shell: pwsh
run: ./build.ps1 -Task Test -Bootstrap
env:
DEBUG: ${{ runner.debug == '1' }}
run: |
if($env:DEBUG -eq 'true' -or $env:DEBUG -eq '1') {
$DebugPreference = 'Continue'
}
./build.ps1 -Task Test -Bootstrap
10 changes: 9 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,13 @@
"powershell.codeFormatting.addWhitespaceAroundPipe": true,
"powershell.codeFormatting.useCorrectCasing": true,
"powershell.codeFormatting.newLineAfterOpenBrace": true,
"powershell.codeFormatting.alignPropertyValuePairs": true
"powershell.codeFormatting.alignPropertyValuePairs": true,
"search.useIgnoreFiles": true,
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.code-search": true,
"**/.ruby-lsp": true,
"Output/**": true
}
}
25 changes: 20 additions & 5 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,42 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",

// Start PowerShell (pwsh on *nix)
"windows": {
"options": {
"shell": {
"executable": "powershell.exe",
"args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command" ]
"args": [
"-NoProfile",
"-ExecutionPolicy",
"Bypass",
"-Command"
]
}
}
},
"linux": {
"options": {
"shell": {
"executable": "/usr/bin/pwsh",
"args": [ "-NoProfile", "-Command" ]
"args": [
"-NoProfile",
"-Command"
]
}
}
},
"osx": {
"options": {
"shell": {
"executable": "/usr/local/bin/pwsh",
"args": [ "-NoProfile", "-Command" ]
"args": [
"-NoProfile",
"-Command"
]
}
}
},

"tasks": [
{
"label": "Clean",
Expand Down Expand Up @@ -69,6 +78,12 @@
"label": "Publish",
"type": "shell",
"command": "${cwd}/build.ps1 -Task Publish -Verbose"
},
{
"label": "Bootstrap",
"type": "shell",
"command": "${cwd}/build.ps1 -Task Init -Bootstrap -Verbose",
"problemMatcher": []
}
]
}
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

- Add new dependencies variables to allow end user to modify which tasks are
run.
- Add localization support.

## [0.7.2] 2025-05-21

Expand Down
39 changes: 38 additions & 1 deletion PowerShellBuild/PowerShellBuild.psm1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Dot source public functions
$private = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Private/*.ps1')) -Recurse)
$public = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Public/*.ps1')) -Recurse)
$public = @(Get-ChildItem -Path ([IO.Path]::Combine($PSScriptRoot, 'Public/*.ps1')) -Recurse)
foreach ($import in $public + $private) {
try {
. $import.FullName
Expand All @@ -9,6 +9,43 @@ foreach ($import in $public + $private) {
}
}

data LocalizedData {
# Load here in case Import-LocalizedData is not available
ConvertFrom-StringData @'
NoCommandsExported=No commands have been exported. Skipping markdown generation.
FailedToGenerateMarkdownHelp=Failed to generate markdown help. : {0}
AddingFileToPsm1=Adding [{0}] to PSM1
MakeCabNotAvailable=MakeCab.exe is not available. Cannot create help cab.
DirectoryAlreadyExists=Directory already exists [{0}].
PathLongerThan3Chars=`$Path [{0}] must be longer than 3 characters.
BuildSystemDetails=Build System Details:
BuildModule=Build Module: {0}`:{1}
PowerShellVersion=PowerShell Version: {0}
EnvironmentVariables={0}`Environment variables:
PublishingVersionToRepository=Publishing version [{0}] to repository [{1}]...
FolderDoesNotExist=Folder does not exist: {0}
PathArgumentMustBeAFolder=The Path argument must be a folder. File paths are not allowed.
UnableToFindModuleManifest=Unable to find module manifest [{0}]. Can't import module
PesterTestsFailed=One or more Pester tests failed
CodeCoverage=Code Coverage
Type=Type
CodeCoverageLessThanThreshold=Code coverage: [{0}] is [{1:p}], which is less than the threshold of [{2:p}]
CodeCoverageCodeCoverageFileNotFound=Code coverage file [{0}] not found.
SeverityThresholdSetTo=SeverityThreshold set to: {0}
PSScriptAnalyzerResults=PSScriptAnalyzer results:
ScriptAnalyzerErrors=One or more ScriptAnalyzer errors were found!
ScriptAnalyzerWarnings=One or more ScriptAnalyzer warnings were found!
ScriptAnalyzerIssues=One or more ScriptAnalyzer issues were found!
'@
}
$importLocalizedDataSplat = @{
BindingVariable = 'LocalizedData'
FileName = 'Messages.psd1'
ErrorAction = 'SilentlyContinue'
}
Import-LocalizedData @importLocalizedDataSplat


Export-ModuleMember -Function $public.Basename

# $psakeTaskAlias = 'PowerShellBuild.psake.tasks'
Expand Down
2 changes: 1 addition & 1 deletion PowerShellBuild/Public/Build-PSBuildMAMLHelp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function Build-PSBuildMAMLHelp {
Uses PlatyPS to generate MAML XML help from markdown files in ./docs
and saves the XML file to a directory under ./output/MyModule
#>
[cmdletbinding()]
[CmdletBinding()]
param(
[parameter(Mandatory)]
[string]$Path,
Expand Down
6 changes: 3 additions & 3 deletions PowerShellBuild/Public/Build-PSBuildMarkdown.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ function Build-PSBuildMarkdown {

Analysis the comment-based help of the MyModule module and create markdown documents under ./docs/en-US.
#>
[cmdletbinding()]
[CmdletBinding()]
param(
[parameter(Mandatory)]
[string]$ModulePath,
Expand Down Expand Up @@ -56,7 +56,7 @@ function Build-PSBuildMarkdown {

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

Expand Down Expand Up @@ -93,7 +93,7 @@ function Build-PSBuildMarkdown {
}
New-MarkdownHelp @newMDParams > $null
} catch {
Write-Error "Failed to generate markdown help. : $_"
Write-Error ($LocalizedData.FailedToGenerateMarkdownHelp -f $_)
} finally {
Remove-Module $moduleName
}
Expand Down
115 changes: 83 additions & 32 deletions PowerShellBuild/Public/Build-PSBuildModule.ps1
Original file line number Diff line number Diff line change
@@ -1,50 +1,58 @@
# spell-checker:ignore modulename
function Build-PSBuildModule {
<#
.SYNOPSIS
Builds a PowerShell module based on source directory
.DESCRIPTION
Builds a PowerShell module based on source directory and optionally concatenates
public/private functions from separete files into monolithic .PSM1 file.
Builds a PowerShell module based on source directory and optionally
concatenates public/private functions from separate files into
monolithic .PSM1 file.
.PARAMETER Path
The source module path.
.PARAMETER DestinationPath
Destination path to write "built" module to.
.PARAMETER ModuleName
The name of the module.
.PARAMETER Compile
Switch to indicate if separete function files should be concatenated into monolithic .PSM1 file.
Switch to indicate if separate function files should be concatenated
into monolithic .PSM1 file.
.PARAMETER CompileHeader
String that will be at the top of your PSM1 file.
.PARAMETER CompileFooter
String that will be added to the bottom of your PSM1 file.
.PARAMETER CompileScriptHeader
String that will be added to your PSM1 file before each script file.
.PARAMETER CompileScriptFooter
String that will be added to your PSM1 file beforeafter each script file.
String that will be added to your PSM1 file after each script file.
.PARAMETER ReadMePath
Path to project README. If present, this will become the "about_<ModuleName>.help.txt" file in the build module.
Path to project README. If present, this will become the
"about_<ModuleName>.help.txt" file in the build module.
.PARAMETER CompileDirectories
List of directories containing .ps1 files that will also be compiled into the PSM1.
List of directories containing .ps1 files that will also be compiled
into the PSM1.
.PARAMETER CopyDirectories
List of directories that will copying "as-is" into the build module.
.PARAMETER Exclude
Array of files (regular expressions) to exclude from copying into built module.
Array of files (regular expressions) to exclude from copying into built
module.
.PARAMETER Culture
UI Culture. This is used to determine what culture directory to store "about_<ModuleName>.help.txt" in.
UI Culture. This is used to determine what culture directory to store
"about_<ModuleName>.help.txt" in.
.EXAMPLE
PS> $buildParams = @{
$buildParams = @{
Path = ./MyModule
DestinationPath = ./Output/MoModule/0.1.0
ModuleName = MyModule
Exclude = @()
Compile = $false
Culture = (Get-UICulture).Name
}
PS> Build-PSBuildModule @buildParams
Build-PSBuildModule @buildParams

Build module from source directory './MyModule' and save to '/Output/MoModule/0.1.0'
Build module from source directory './MyModule' and save to
'/Output/MoModule/0.1.0'
#>
[cmdletbinding()]
[CmdletBinding()]
param(
[parameter(Mandatory)]
[string]$Path,
Expand Down Expand Up @@ -77,49 +85,80 @@ function Build-PSBuildModule {
)

if (-not (Test-Path -LiteralPath $DestinationPath)) {
New-Item -Path $DestinationPath -ItemType Directory -Verbose:$VerbosePreference > $null
$newItemSplat = @{
Path = $DestinationPath
ItemType = 'Directory'
Verbose = $VerbosePreference
}
New-Item @newItemSplat > $null
}

# Copy "non-processed files"
Get-ChildItem -Path $Path -Include '*.psm1', '*.psd1', '*.ps1xml' -Depth 1 | Copy-Item -Destination $DestinationPath -Force
$getChildItemSplat = @{
Path = $Path
Include = '*.psm1', '*.psd1', '*.ps1xml'
Depth = 1
}
Get-ChildItem @getChildItemSplat |
Copy-Item -Destination $DestinationPath -Force
foreach ($dir in $CopyDirectories) {
$copyPath = [IO.Path]::Combine($Path, $dir)
Copy-Item -Path $copyPath -Destination $DestinationPath -Recurse -Force
}

# Copy README as about_<modulename>.help.txt
if (-not [string]::IsNullOrEmpty($ReadMePath)) {
$culturePath = [IO.Path]::Combine($DestinationPath, $Culture)
$aboutModulePath = [IO.Path]::Combine($culturePath, "about_$($ModuleName).help.txt")
if(-not (Test-Path $culturePath -PathType Container)) {
$culturePath = [IO.Path]::Combine($DestinationPath, $Culture)
$aboutModulePath = [IO.Path]::Combine(
$culturePath,
"about_$($ModuleName).help.txt"
)
if (-not (Test-Path $culturePath -PathType Container)) {
New-Item $culturePath -Type Directory -Force > $null
Copy-Item -LiteralPath $ReadMePath -Destination $aboutModulePath -Force
$copyItemSplat = @{
LiteralPath = $ReadMePath
Destination = $aboutModulePath
Force = $true
}
Copy-Item @copyItemSplat
}
}

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

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

if ($CompileHeader) {
$CompileHeader | Add-Content -Path $rootModule -Encoding utf8
$CompileHeader | Add-Content -Path $rootModule -Encoding 'utf8'
}

$resolvedCompileDirectories = $CompileDirectories | ForEach-Object {
[IO.Path]::Combine($Path, $_)
}
$allScripts = Get-ChildItem -Path $resolvedCompileDirectories -Filter '*.ps1' -File -Recurse -ErrorAction SilentlyContinue
$getChildItemSplat = @{
Path = $resolvedCompileDirectories
Filter = '*.ps1'
File = $true
Recurse = $true
ErrorAction = 'SilentlyContinue'
}
$allScripts = Get-ChildItem @getChildItemSplat

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

$addContentSplat = @{
Path = $rootModule
Encoding = 'utf8'
}
$allScripts | ForEach-Object {
$srcFile = Resolve-Path $_.FullName -Relative
Write-Verbose "Adding [$srcFile] to PSM1"
Write-Verbose ($LocalizedData.AddingFileToPsm1 -f $srcFile)

if ($CompileScriptHeader) {
Write-Output $CompileScriptHeader
Expand All @@ -130,15 +169,14 @@ function Build-PSBuildModule {
if ($CompileScriptFooter) {
Write-Output $CompileScriptFooter
}
} | Add-Content @addContentSplat

} | Add-Content -Path $rootModule -Encoding utf8

$psm1Contents | Add-Content -Path $rootModule -Encoding utf8
$psm1Contents | Add-Content @addContentSplat

if ($CompileFooter) {
$CompileFooter | Add-Content -Path $rootModule -Encoding utf8
$CompileFooter | Add-Content @addContentSplat
}
} else{
} else {
# Copy everything over, then remove stuff that should have been excluded
# It's just easier this way
$copyParams = @{
Expand All @@ -157,13 +195,26 @@ function Build-PSBuildModule {
}
}
}
$toRemove | Remove-Item -Recurse -Force -ErrorAction Ignore
$toRemove | Remove-Item -Recurse -Force -ErrorAction 'Ignore'
}

# Export public functions in manifest if there are any public functions
$publicFunctions = Get-ChildItem $Path/Public/*.ps1 -Recurse -ErrorAction SilentlyContinue
$getChildItemSplat = @{
Recurse = $true
ErrorAction = 'SilentlyContinue'
Path = "$Path/Public/*.ps1"
}
$publicFunctions = Get-ChildItem @getChildItemSplat
if ($publicFunctions) {
$outputManifest = [IO.Path]::Combine($DestinationPath, "$ModuleName.psd1")
Update-Metadata -Path $OutputManifest -PropertyName FunctionsToExport -Value $publicFunctions.BaseName
$outputManifest = [IO.Path]::Combine(
$DestinationPath,
"$ModuleName.psd1"
)
$updateMetadataSplat = @{
Path = $OutputManifest
PropertyName = 'FunctionsToExport'
Value = $publicFunctions.BaseName
}
Update-Metadata @updateMetadataSplat
}
}
Loading
Loading