diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs
index a6642e81c5..c70eb34cdf 100644
--- a/src/System.Management.Automation/engine/Utils.cs
+++ b/src/System.Management.Automation/engine/Utils.cs
@@ -1514,11 +1514,14 @@ namespace System.Management.Automation.Internal
internal static bool UseDebugAmsiImplementation;
internal static bool BypassAppLockerPolicyCaching;
internal static bool BypassOnlineHelpRetrieval;
- internal static bool ThrowHelpCultureNotSupported;
internal static bool ForcePromptForChoiceDefaultOption;
internal static bool NoPromptForPassword;
internal static bool ForceFormatListFixedLabelWidth;
+ // Update-Help tests
+ internal static bool ThrowHelpCultureNotSupported;
+ internal static CultureInfo CurrentUICulture;
+
// Stop/Restart/Rename Computer tests
internal static bool TestStopComputer;
internal static bool TestWaitStopComputer;
diff --git a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs
index b759a281bd..48e44bd301 100644
--- a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs
+++ b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs
@@ -557,7 +557,8 @@ namespace Microsoft.PowerShell.Commands
#endif
catch (UpdatableHelpSystemException e)
{
- if (e.FullyQualifiedErrorId == "HelpCultureNotSupported")
+ if (e.FullyQualifiedErrorId == "HelpCultureNotSupported"
+ || e.FullyQualifiedErrorId == "UnableToRetrieveHelpInfoXml")
{
installed = false;
@@ -672,7 +673,7 @@ namespace Microsoft.PowerShell.Commands
}
// Culture check
- if (!newHelpInfo.IsCultureSupported(culture))
+ if (!newHelpInfo.IsCultureSupported(culture.Name))
{
throw new UpdatableHelpSystemException("HelpCultureNotSupported",
StringUtil.Format(HelpDisplayStrings.HelpCultureNotSupported,
diff --git a/src/System.Management.Automation/help/UpdatableHelpInfo.cs b/src/System.Management.Automation/help/UpdatableHelpInfo.cs
index 5af3fd57b6..8170de9065 100644
--- a/src/System.Management.Automation/help/UpdatableHelpInfo.cs
+++ b/src/System.Management.Automation/help/UpdatableHelpInfo.cs
@@ -1,9 +1,11 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
+using System.Linq;
using System.Management.Automation.Internal;
using System.Text;
@@ -37,6 +39,44 @@ namespace System.Management.Automation.Help
/// Supported culture.
///
internal CultureInfo Culture { get; set; }
+
+ ///
+ /// Enumerates fallback chain (parents) of the culture, including itself.
+ ///
+ /// Culture to enumerate
+ ///
+ /// Examples:
+ /// en-GB => { en-GB, en }
+ /// zh-Hans-CN => { zh-Hans-CN, zh-Hans, zh }.
+ ///
+ /// An enumerable list of culture names.
+ internal static IEnumerable GetCultureFallbackChain(CultureInfo culture)
+ {
+ // We use just names instead because comparing two CultureInfo objects
+ // can fail if they are created using different means
+ while (culture != null)
+ {
+ if (string.IsNullOrEmpty(culture.Name))
+ {
+ yield break;
+ }
+
+ yield return culture.Name;
+
+ culture = culture.Parent;
+ }
+ }
+
+ ///
+ /// Checks if a culture is supported.
+ ///
+ /// Name of the culture to check.
+ /// True if supported, false if not.
+ internal bool IsCultureSupported(string cultureName)
+ {
+ Debug.Assert(cultureName != null, $"{nameof(cultureName)} may not be null");
+ return GetCultureFallbackChain(Culture).Any(fallback => fallback == cultureName);
+ }
}
///
@@ -99,22 +139,12 @@ namespace System.Management.Automation.Help
///
/// Checks if a culture is supported.
///
- /// Culture to check.
+ /// Name of the culture to check.
/// True if supported, false if not.
- internal bool IsCultureSupported(CultureInfo culture)
+ internal bool IsCultureSupported(string cultureName)
{
- Debug.Assert(culture != null);
-
- foreach (CultureSpecificUpdatableHelp updatableHelpItem in UpdatableHelpItems)
- {
- if (string.Equals(updatableHelpItem.Culture.Name, culture.Name,
- StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
- }
-
- return false;
+ Debug.Assert(cultureName != null, $"{nameof(cultureName)} may not be null");
+ return UpdatableHelpItems.Any(item => item.IsCultureSupported(cultureName));
}
///
diff --git a/src/System.Management.Automation/help/UpdatableHelpSystem.cs b/src/System.Management.Automation/help/UpdatableHelpSystem.cs
index eb9796a207..3a504a37fd 100644
--- a/src/System.Management.Automation/help/UpdatableHelpSystem.cs
+++ b/src/System.Management.Automation/help/UpdatableHelpSystem.cs
@@ -294,19 +294,13 @@ namespace System.Management.Automation.Help
{
CultureInfo culture = CultureInfo.CurrentUICulture;
- while (culture != null)
+ // Allow tests to override system culture
+ if (InternalTestHooks.CurrentUICulture != null)
{
- if (string.IsNullOrEmpty(culture.Name))
- {
- yield break;
- }
-
- yield return culture.Name;
-
- culture = culture.Parent;
+ culture = InternalTestHooks.CurrentUICulture;
}
- yield break;
+ return CultureSpecificUpdatableHelp.GetCultureFallbackChain(culture);
}
#region Help Metadata Retrieval
@@ -594,13 +588,9 @@ namespace System.Management.Automation.Help
if (!string.IsNullOrEmpty(currentCulture))
{
- IEnumerable patternList = SessionStateUtilities.CreateWildcardsFromStrings(
- globPatterns: new[] { currentCulture },
- options: WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant);
-
for (int i = 0; i < updatableHelpItem.Length; i++)
{
- if (SessionStateUtilities.MatchesAnyWildcardPattern(updatableHelpItem[i].Culture.Name, patternList, true))
+ if (updatableHelpItem[i].IsCultureSupported(currentCulture))
{
helpInfo.HelpContentUriCollection.Add(new UpdatableHelpUri(moduleName, moduleGuid, updatableHelpItem[i].Culture, uri));
}
diff --git a/test/powershell/engine/Help/HelpSystem.Tests.ps1 b/test/powershell/engine/Help/HelpSystem.Tests.ps1
index 2786e64ee0..53ce9ad749 100644
--- a/test/powershell/engine/Help/HelpSystem.Tests.ps1
+++ b/test/powershell/engine/Help/HelpSystem.Tests.ps1
@@ -30,7 +30,7 @@ function UpdateHelpFromLocalContentPath {
throw "Unable to find help content at '$helpContentPath'"
}
- # Test files are 'en-US', set explicit culture so test does not help on non-US systems
+ # Test files are 'en-US', set explicit culture so test does not fail on non-US systems
Update-Help -Module $ModuleName -SourcePath $helpContentPath -UICulture 'en-US' -Force -ErrorAction Stop -Scope $Scope
}
@@ -635,3 +635,54 @@ Describe 'help renders when using a PAGER with a space in the path' -Tags 'CI' {
help Get-Command | Should -Be "R2V0LUNvbW1hbmQ="
}
}
+
+Describe 'Update-Help allows partial culture matches' -Tags 'CI' {
+ BeforeAll {
+ function Test-UpdateHelpAux($UICulture, $Pass)
+ {
+ # If null (in system culture tests), omit entirely
+ $CultureArg = $UICulture ? @{ UICulture = $UICulture } : @{}
+ $Args = @{
+ Module = 'Microsoft.PowerShell.Core'
+ SourcePath = Join-Path $PSScriptRoot 'assets'
+ Force = $true
+ ErrorAction = $Pass ? 'Stop' : 'SilentlyContinue'
+ ErrorVariable = 'ErrorVariable'
+ }
+
+ Update-Help @Args @CultureArg
+
+ if (-not $Pass) {
+ $ErrorVariable | Should -Match 'Failed to update Help for the module.*'
+ }
+ }
+ }
+
+ AfterEach {
+ [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('CurrentUICulture', $null)
+ }
+
+ It 'Checks culture match against en-US: ' -TestCases @(
+ @{ UICulture = 'en-US' }
+ @{ UICulture = 'en' }
+ @{ UICulture = 'en-GB'; Pass = $false }
+ @{ UICulture = 'de-DE'; Pass = $false }
+ ) {
+ param($UICulture, $Pass = $true)
+
+ Test-UpdateHelpAux $UICulture $Pass
+ }
+
+ # When using system culture, "en-GB" will use "en" as fallback, so passes
+ It 'Checks system culture match against en-US: ' -TestCases @(
+ @{ UICulture = 'en-US' }
+ @{ UICulture = 'en' }
+ @{ UICulture = 'en-GB' }
+ @{ UICulture = 'de-DE'; Pass = $false }
+ ) {
+ param($UICulture, $Pass = $true)
+
+ [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook('CurrentUICulture', [System.Globalization.CultureInfo]::new($UICulture))
+ Test-UpdateHelpAux $null $Pass
+ }
+}