diff --git a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs
index 2c665f1710..e6df70621a 100644
--- a/src/Umbraco.Core/Models/PublishedContent/Fallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/Fallback.cs
@@ -3,56 +3,82 @@ using System.Collections;
namespace Umbraco.Cms.Core.Models.PublishedContent;
///
-/// Manages the built-in fallback policies.
+/// Manages the built-in fallback policies.
///
public struct Fallback : IEnumerable
{
///
- /// Do not fallback.
+ /// Do not fallback.
///
public const int None = 0;
private readonly int[] _values;
///
- /// Initializes a new instance of the struct with values.
+ /// Initializes a new instance of the struct with values.
///
+ /// The values.
private Fallback(int[] values) => _values = values;
///
- /// Gets an ordered set of fallback policies.
+ /// Gets an ordered set of fallback policies.
///
- ///
+ /// The values.
+ ///
+ /// The fallback policy.
+ ///
public static Fallback To(params int[] values) => new(values);
///
- /// Fallback to default value.
+ /// Fallback to the default value.
///
public const int DefaultValue = 1;
///
- /// Fallback to other languages.
+ /// Fallback to other languages.
///
public const int Language = 2;
///
- /// Fallback to tree ancestors.
+ /// Fallback to tree ancestors.
///
public const int Ancestors = 3;
///
- /// Gets the fallback to default value policy.
+ /// Fallback to the default language.
///
+ public const int DefaultLanguage = 4;
+
+ ///
+ /// Gets the fallback to the default language policy.
+ ///
+ ///
+ /// The default language fallback policy.
+ ///
+ public static Fallback ToDefaultLanguage => new Fallback(new[] { DefaultLanguage });
+
+ ///
+ /// Gets the fallback to the default value policy.
+ ///
+ ///
+ /// The default value fallback policy.
+ ///
public static Fallback ToDefaultValue => new(new[] { DefaultValue });
///
- /// Gets the fallback to language policy.
+ /// Gets the fallback to language policy.
///
+ ///
+ /// The language fallback policy.
+ ///
public static Fallback ToLanguage => new(new[] { Language });
///
- /// Gets the fallback to tree ancestors policy.
+ /// Gets the fallback to tree ancestors policy.
///
+ ///
+ /// The tree ancestors fallback policy.
+ ///
public static Fallback ToAncestors => new(new[] { Ancestors });
///
diff --git a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
index 64f0160383..0e43a2617f 100644
--- a/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
+++ b/src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs
@@ -44,6 +44,13 @@ public class PublishedValueFallback : IPublishedValueFallback
return true;
}
+ break;
+ case Fallback.DefaultLanguage:
+ if (TryGetValueWithDefaultLanguageFallback(property, culture, segment, out value))
+ {
+ return true;
+ }
+
break;
default:
throw NotSupportedFallbackMethod(f, "property");
@@ -85,6 +92,13 @@ public class PublishedValueFallback : IPublishedValueFallback
return true;
}
+ break;
+ case Fallback.DefaultLanguage:
+ if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value))
+ {
+ return true;
+ }
+
break;
default:
throw NotSupportedFallbackMethod(f, "element");
@@ -141,6 +155,13 @@ public class PublishedValueFallback : IPublishedValueFallback
return true;
}
+ break;
+ case Fallback.DefaultLanguage:
+ if (TryGetValueWithDefaultLanguageFallback(content, alias, culture, segment, out value))
+ {
+ return true;
+ }
+
break;
default:
throw NotSupportedFallbackMethod(f, "content");
@@ -347,4 +368,42 @@ public class PublishedValueFallback : IPublishedValueFallback
language = language2;
}
}
+
+ private bool TryGetValueWithDefaultLanguageFallback(IPublishedProperty property, string? culture, string? segment, out T? value)
+ {
+ value = default;
+
+ if (culture.IsNullOrWhiteSpace())
+ {
+ return false;
+ }
+
+ string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode();
+ if (culture.InvariantEquals(defaultCulture) == false && property.HasValue(defaultCulture, segment))
+ {
+ value = property.Value(this, defaultCulture, segment);
+ return true;
+ }
+
+ return false;
+ }
+
+ private bool TryGetValueWithDefaultLanguageFallback(IPublishedElement element, string alias, string? culture, string? segment, out T? value)
+ {
+ value = default;
+
+ if (culture.IsNullOrWhiteSpace())
+ {
+ return false;
+ }
+
+ string? defaultCulture = _localizationService?.GetDefaultLanguageIsoCode();
+ if (culture.InvariantEquals(defaultCulture) == false && element.HasValue(alias, defaultCulture, segment))
+ {
+ value = element.Value(this, alias, defaultCulture, segment);
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs
index 342e08acbe..37a5f08286 100644
--- a/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs
+++ b/tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/PublishedCache/PublishedContentLanguageVariantTests.cs
@@ -242,6 +242,15 @@ public class PublishedContentLanguageVariantTests : PublishedSnapshotServiceTest
Assert.IsNull(value);
}
+ [Test]
+ public void Can_Get_Content_For_Unpopulated_Requested_DefaultLanguage_With_Fallback()
+ {
+ var snapshot = GetPublishedSnapshot();
+ var content = snapshot.Content.GetAtRoot().First();
+ var value = content.Value(PublishedValueFallback, "welcomeText", "fr", fallback: Fallback.ToDefaultLanguage);
+ Assert.AreEqual("Welcome", value);
+ }
+
[Test]
public void Do_Not_Get_Content_Recursively_Unless_Requested()
{