Skip to content

Commit 06e72d1

Browse files
committed
feat(localization): ✨ Add JSON localization support for Plaster
* Introduced `ExportLocalizationJson` task to convert `.psd1` files to JSON format. * Created `Plaster.Resources.en-US.json` for localized error messages and prompts. * Updated `Plaster.Resources.psd1` to reflect auto-generation from JSON.
1 parent 796534b commit 06e72d1

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

Plaster/en-US/Plaster.Resources.psd1

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
#### WARNING: This file is auto-generated from Plaster.Resources.en-US.json.
2+
#### Do not edit this file directly. Instead, update the source JSON file in the i18n directory.
13
# Localized PlasterResources.psd1
4+
# This file was generated from Plaster.Resources.en-US.json using a Psake task.
25
ConvertFrom-StringData @'
36
DestPath_F1=Destination path: {0}
47
ErrorFailedToLoadStoreFile_F1=Failed to load the default value store file: '{0}'.

i18n/Plaster.Resources.en-US.json

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
{
2+
"ManifestSchemaInvalidChoiceDefault_F3": "Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify a zero-based integer index that corresponds to the default choice.",
3+
"RequireModuleMinVersion_F1": "minimum version: {0}",
4+
"ManifestPlasterVersionNotSupported_F2": "The template file '{0}' specifies a plasterVersion of {1} which is greater than the installed version of Plaster. Update the Plaster module and try again.",
5+
"ManifestSchemaInvalidMultichoiceDefault_F3": "Invalid default attribute value '{0}' for parameter '{1}' in file '{2}'. The default value must specify one or more zero-based integer indexes in a comma separated list that correspond to the default choices.",
6+
"ManifestMissingDocTargetNamespace_F2": "The Plaster manifest file '{0}' is missing or has an invalid target namespace on the document element. It should be specified as <plasterManifest xmlns=\"{1}\"></plasterManifest>.",
7+
"ManifestNotValid_F1": "The Plaster manifest '{0}' is not valid.",
8+
"ShouldCreateNewPlasterManifest": "Create Plaster manifest",
9+
"NewModManifest_CreatingDir_F1": "Creating destination directory for module manifest: {0}",
10+
"ManifestErrorReading_F1": "Error reading Plaster manifest: {0}",
11+
"ErrorUnencryptingSecureString_F1": "Failed to unencrypt value for parameter '{0}'.",
12+
"ParameterTypeChoiceMultipleDefault_F1": "Parameter name {0} is of type='choice' and can only have one default value.",
13+
"OpConflict": "Conflict",
14+
"ManifestSchemaInvalidRequireModuleAttrs_F2": "The requireModule attribute 'requiredVersion' for module '{0}' in file '{1}' cannot be used together with either the 'minimumVersion' or 'maximumVersion' attribute.",
15+
"ErrorPathMustBeUnderDestPath_F2": "The path '{0}' must be under the specified DestinationPath '{1}'.",
16+
"ErrorProcessingDynamicParams_F1": "Failed to create dynamic parameters from the template's manifest file. Template-based dynamic parameters will not be available until the error is corrected. The error was: {0}",
17+
"ShouldProcessExpandTemplate": "Expand template file",
18+
"OpMissing": "Missing",
19+
"ExpressionNonTermErrors_F2": "The expression '{0}' generated error output - {1}",
20+
"RequireModuleMaxVersion_F1": "maximum version: {0}",
21+
"RequireModuleVerified_F2": "The required module {0}{1} is already installed.",
22+
"ErrorPathMustBeRelativePath_F2": "The path '{0}' specified in the {1} directive in the template manifest cannot be an absolute path. Change the path to a relative path.",
23+
"OpCreate": "Create",
24+
"OpVerify": "Verify",
25+
"ErrorTemplatePathIsInvalid_F1": "The TemplatePath parameter value must refer to an existing directory. The specified path '{0}' does not.",
26+
"ExpressionErrorLocationParameter_F2": "<parameter> name='{0}', attribute '{1}'",
27+
"TestPlasterNoXmlSchemaValidationWarning": "The version of .NET Core that PowerShell is running on does not support XML schema-based validation. Test-PlasterManifest will operate in \"limited validation\" mode primarily verifying the specified manifest file is well-formed XML. For full, XML schema-based validation, run this command on Windows PowerShell.",
28+
"ManifestNotValidVerbose_F1": "The Plaster manifest '{0}' is not valid. Specify -Verbose to see the specific schema errors.",
29+
"ShouldProcessCreateDir": "Create directory",
30+
"OpIdentical": "Identical",
31+
"ExpressionInvalid_F2": "The expression '{0}' is invalid or threw an exception. Error: {1}",
32+
"UnrecognizedContentElement_F1": "Unrecognized manifest content child element: {0}.",
33+
"ManifestNotWellFormedXml_F2": "The Plaster manifest '{0}' is not a well-formed XML file. {1}",
34+
"ExpressionExecError_F2": "PowerShell expression failed execution. Location: {0}. Error: {1}",
35+
"OpForce": "Force",
36+
"ManifestSchemaInvalidCondition_F3": "Invalid condition '{0}' in file '{1}'. Error: {2}",
37+
"OverwriteFile_F1": "Overwrite {0}",
38+
"InterpolationError_F3": "The Plaster manifest attribute value '{0}' failed string interpolation. Location: {1}. Error: {2}",
39+
"FileConflict": "Plaster file conflict",
40+
"ExpressionErrorLocationFile_F2": "<{0}> attribute '{1}'",
41+
"ManifestSchemaVersionNotSupported_F2": "The template's manifest schema version ({0}) in file '{1}' requires a newer version of Plaster. Update the Plaster module and try again.",
42+
"RequireModuleMissing_F2": "The required module {0}{1} was not found.",
43+
"UnrecognizedParameterType_F2": "Unrecognized parameter type '{0}' on parameter name '{1}'.",
44+
"ManifestSchemaInvalidAttrValue_F5": "Invalid '{0}' attribute value '{1}' on '{2}' element in file '{3}'. Error: {4}",
45+
"ManifestFileMissing_F1": "The Plaster manifest file '{0}' was not found.",
46+
"OpModify": "Modify",
47+
"ErrorFailedToLoadStoreFile_F1": "Failed to load the default value store file: '{0}'.",
48+
"ExpressionErrorLocationModify_F1": "<modify> attribute '{0}'",
49+
"RequireModuleRequiredVersion_F1": "required version: {0}",
50+
"ShouldProcessNewModuleManifest": "Create new module manifest",
51+
"TempFileTarget_F1": "temp file for '{0}'",
52+
"DestPath_F1": "Destination path: {0}",
53+
"ManifestWrongFilename_F1": "The Plaster manifest filename '{0}' is not valid. The value of the Path argument must refer to a file named 'plasterManifest.xml' or 'plasterManifest_<culture>.xml'. Change the Plaster manifest filename and then try again.",
54+
"MissingParameterPrompt_F1": "<Missing prompt value for parameter '{0}'>",
55+
"TempFileOperation_F1": "{0} into temp file before copying to destination",
56+
"OpUpdate": "Update",
57+
"UnrecognizedParametersElement_F1": "Unrecognized manifest parameters child element: {0}.",
58+
"ExpressionInvalidCondition_F3": "The Plaster manifest condition '{0}' failed. Location: {1}. Error: {2}",
59+
"ExpressionErrorLocationRequireModule_F2": "<requireModule> name='{0}', attribute '{1}'",
60+
"ExpressionErrorLocationNewModManifest_F1": "<newModuleManifest> attribute '{0}'",
61+
"ManifestSchemaValidationError_F2": "Plaster manifest schema error in file '{0}'. Error: {1}",
62+
"ManifestMissingDocElement_F2": "The Plaster manifest file '{0}' is missing the document element. It should be specified as <plasterManifest xmlns=\"{1}\"></plasterManifest>.",
63+
"ErrorPathDoesNotExist_F1": "Cannot find path '{0}' because it does not exist."
64+
}

i18n/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Internationalization (i18n) Files
2+
3+
⚠️ **IMPORTANT: DO NOT EDIT THESE FILES DIRECTLY** ⚠️
4+
5+
## About These Files
6+
7+
The files in this directory contain localized resources for the Plaster
8+
PowerShell module. These files are automatically generated and updated by
9+
[Crowdin](https://crowdin.com/), our localization management platform.
10+
11+
## What's In This Directory
12+
13+
- `*.json` files: Localized string resources for different languages and cultures
14+
- Translation files are named using the format: `Plaster.Resources.<culture-code>.json`
15+
16+
## How Localization Works
17+
18+
1. **Source strings** are maintained in the main codebase
19+
2. **Crowdin** automatically syncs these strings for translation
20+
3. **Translators** use the Crowdin platform to provide translations
21+
4. **Updated translations** are automatically pushed back to this repository
22+
via Crowdin integration
23+
24+
## Contributing Translations
25+
26+
If you'd like to help translate Plaster:
27+
28+
1. Visit our Crowdin project page
29+
2. Request access to contribute translations
30+
3. Use the Crowdin web interface to submit translations
31+
4. Your contributions will be automatically included in future releases
32+
33+
## For Maintainers
34+
35+
- Never edit the `*.json` files in this directory directly
36+
- Changes to source strings should be made in the main PowerShell code
37+
- The source of truth for necessary strings will be
38+
`Plaster\en-US\Plaster.Resources.psd1`.
39+
- The `en-US` json file is exported by running the `ExportLocalizationJson`
40+
build task. i.e. `.\build.ps1 -Task ExportLocalizationJson`
41+
- Crowdin will automatically detect and sync new or modified strings
42+
- The Crowdin integration will create pull requests with updated translations
43+
44+
## Questions?
45+
46+
If you have questions about the localization process, please open an issue
47+
in the main repository.

psakeFile.ps1

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,51 @@ $InformationPreference = 'Continue'
1818
Task Default -Depends Test
1919

2020
Task Test -FromModule PowerShellBuild -MinimumVersion '0.6.1'
21+
22+
# Export-LocalizationJson: Convert all Plaster.Resources.psd1 files to JSON for CrowdIn
23+
Task ExportLocalizationJson {
24+
$importLocalizedDataSplat = @{
25+
BindingVariable = 'LocalizedData'
26+
FileName = 'Plaster.Resources.psd1'
27+
ErrorAction = 'SilentlyContinue'
28+
}
29+
# Import the en-US localized data
30+
Microsoft.PowerShell.Utility\Import-LocalizedData @importLocalizedDataSplat
31+
# Create the i18n directory if it doesn't exist
32+
if (-not (Test-Path "i18n")) {
33+
New-Item -Path "i18n" -ItemType Directory | Out-Null
34+
}
35+
36+
# Convert the localized data to JSON and save it
37+
$jsonContent = $LocalizedData | ConvertTo-Json -Depth 10
38+
$jsonPath = Join-Path "i18n" "Plaster.Resources.en-US.json"
39+
Set-Content -Path $jsonPath -Value $jsonContent -Encoding UTF8
40+
}
41+
42+
# Convert all i18n/Plaster.Resources.<locale>.json files to psd1 for runtime
43+
Task GenerateLocalizationPsd1 {
44+
$jsonFiles = Get-ChildItem -Path "i18n" -Filter "Plaster.Resources.*.json" -File
45+
foreach ($jsonFile in $jsonFiles) {
46+
if ($jsonFile.Name -match "^Plaster\.Resources\.([^.]+)\.json$") {
47+
$locale = $matches[1]
48+
$psd1Dir = Join-Path "Plaster" $locale
49+
if (-not (Test-Path $psd1Dir)) { New-Item -Path $psd1Dir -ItemType Directory | Out-Null }
50+
$psd1Path = Join-Path $psd1Dir "Plaster.Resources.psd1"
51+
$data = Get-Content $jsonFile.FullName | ConvertFrom-Json
52+
$stringData = ($data.PSObject.Properties | Sort-Object Name | ForEach-Object {
53+
"$($_.Name)=$($_.Value)"
54+
}) -join "`n"
55+
$psd1Content = @"
56+
#### WARNING: This file is auto-generated from $($jsonFile.Name).
57+
#### Do not edit this file directly. Instead, update the source JSON file in the i18n directory.
58+
# Localized PlasterResources.psd1
59+
# This file was generated from $($jsonFile.Name) using a Psake task.
60+
ConvertFrom-StringData @'
61+
$stringData
62+
'@
63+
"@
64+
Set-Content $psd1Path $psd1Content -Encoding UTF8
65+
Write-Host "Imported $jsonFile to $psd1Path"
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)