V12: Add ISO codes to make the migration from language IDs easier (#14567)

* Change the obsoletion messages for language IDs to target V14 instead of V13.

* Wrong Language file

* Add ISO codes required to migrate custom code from language IDs

* Population of the new language FallbackIsoCode prop

* Changing obsoletion msgs from v13 to v14

* Fix breaking changes
This commit is contained in:
Elitsa Marinovska
2023-07-18 11:53:14 +03:00
committed by GitHub
parent 103a792074
commit ba423a0108
10 changed files with 98 additions and 26 deletions

View File

@@ -22,7 +22,7 @@ public class Language
[DataMember(Name = "isMandatory")]
public bool IsMandatory { get; set; }
[Obsolete("This will be replaced by fallback language ISO code in V13.")]
[Obsolete("This will be replaced by fallback language ISO code in V14.")]
[DataMember(Name = "fallbackLanguageId")]
public int? FallbackLanguageId { get; set; }
}

View File

@@ -34,7 +34,7 @@ public class DictionaryItem : EntityBase, IDictionaryItem
_translations = new List<IDictionaryTranslation>();
}
[Obsolete("This will be removed in V13.")]
[Obsolete("This will be removed in V14.")]
public Func<int, ILanguage?>? GetLanguage { get; set; }
/// <summary>

View File

@@ -10,7 +10,7 @@ public static class DictionaryItemExtensions
/// <param name="d"></param>
/// <param name="languageId"></param>
/// <returns></returns>
[Obsolete("This will be replaced in V13 by a corresponding method accepting language ISO code instead of language ID.")]
[Obsolete("This will be replaced in V14 by a corresponding method accepting language ISO code instead of language ID.")]
public static string? GetTranslatedValue(this IDictionaryItem d, int languageId)
{
IDictionaryTranslation? trans = d.Translations.FirstOrDefault(x => x.LanguageId == languageId);
@@ -22,7 +22,7 @@ public static class DictionaryItemExtensions
/// </summary>
/// <param name="d"></param>
/// <returns></returns>
[Obsolete("Warning: This method ONLY works in very specific scenarios. It will be removed in V13.")]
[Obsolete("Warning: This method ONLY works in very specific scenarios. It will be removed in V14.")]
public static string? GetDefaultValue(this IDictionaryItem d)
{
IDictionaryTranslation? defaultTranslation = d.Translations.FirstOrDefault(x => x.Language?.Id == 1);

View File

@@ -1,5 +1,8 @@
using System.Runtime.Serialization;
using Microsoft.Extensions.DependencyInjection;
using Umbraco.Cms.Core.DependencyInjection;
using Umbraco.Cms.Core.Models.Entities;
using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Core.Models;
@@ -11,6 +14,7 @@ namespace Umbraco.Cms.Core.Models;
public class DictionaryTranslation : EntityBase, IDictionaryTranslation
{
private ILanguage? _language;
private string? _languageIsoCode;
// note: this will be memberwise cloned
private string _value;
@@ -20,6 +24,7 @@ public class DictionaryTranslation : EntityBase, IDictionaryTranslation
_language = language ?? throw new ArgumentNullException("language");
LanguageId = _language.Id;
_value = value;
LanguageIsoCode = language.IsoCode;
}
public DictionaryTranslation(ILanguage language, string value, Guid uniqueId)
@@ -27,17 +32,18 @@ public class DictionaryTranslation : EntityBase, IDictionaryTranslation
_language = language ?? throw new ArgumentNullException("language");
LanguageId = _language.Id;
_value = value;
LanguageIsoCode = language.IsoCode;
Key = uniqueId;
}
[Obsolete("Please use constructor that accepts ILanguage. This will be removed in V13.")]
[Obsolete("Please use constructor that accepts ILanguage. This will be removed in V14.")]
public DictionaryTranslation(int languageId, string value)
{
LanguageId = languageId;
_value = value;
}
[Obsolete("Please use constructor that accepts ILanguage. This will be removed in V13.")]
[Obsolete("Please use constructor that accepts ILanguage. This will be removed in V14.")]
public DictionaryTranslation(int languageId, string value, Guid uniqueId)
{
LanguageId = languageId;
@@ -58,7 +64,7 @@ public class DictionaryTranslation : EntityBase, IDictionaryTranslation
/// returned
/// on a callback.
/// </remarks>
[Obsolete("This will be removed in V13. From V13 onwards you should get languages by ISO code from ILanguageService.")]
[Obsolete("This will be removed in V14. From V14 onwards you should get languages by ISO code from ILanguageService.")]
[DataMember]
[DoNotClone]
public ILanguage? Language
@@ -86,7 +92,7 @@ public class DictionaryTranslation : EntityBase, IDictionaryTranslation
}
}
[Obsolete("This will be replaced by language ISO code in V13.")]
[Obsolete("This will be replaced by language ISO code in V14.")]
public int LanguageId { get; private set; }
/// <summary>
@@ -99,6 +105,24 @@ public class DictionaryTranslation : EntityBase, IDictionaryTranslation
set => SetPropertyValueAndDetectChanges(value, ref _value!, nameof(Value));
}
/// <inheritdoc />
public string LanguageIsoCode
{
get
{
// TODO: this won't be necessary after obsoleted ctors are removed in v14.
if (_languageIsoCode is null)
{
var _languageService = StaticServiceProvider.Instance.GetRequiredService<ILocalizationService>();
_languageIsoCode = _languageService.GetLanguageById(LanguageId)?.IsoCode ?? string.Empty;
}
return _languageIsoCode;
}
private set => SetPropertyValueAndDetectChanges(value, ref _languageIsoCode!, nameof(LanguageIsoCode));
}
protected override void PerformDeepClone(object clone)
{
base.PerformDeepClone(clone);

View File

@@ -6,18 +6,24 @@ namespace Umbraco.Cms.Core.Models;
public interface IDictionaryTranslation : IEntity, IRememberBeingDirty
{
/// <summary>
/// Gets or sets the <see cref="Language" /> for the translation
/// Gets or sets the <see cref="Language" /> for the translation.
/// </summary>
[Obsolete("This will be removed in V13. From V13 onwards you should get languages by ISO code from ILanguageService.")]
[Obsolete("This will be removed in V14. From V14 onwards you should get languages by ISO code from ILanguageService.")]
[DataMember]
ILanguage? Language { get; set; }
[Obsolete("This will be replaced by language ISO code in V13.")]
[Obsolete("This will be replaced by language ISO code in V14.")]
int LanguageId { get; }
/// <summary>
/// Gets or sets the translated text
/// Gets or sets the translated text.
/// </summary>
[DataMember]
string Value { get; set; }
/// <summary>
/// Gets the ISO code of the language.
/// </summary>
[DataMember]
string LanguageIsoCode => Language?.IsoCode ?? string.Empty;
}

View File

@@ -55,7 +55,24 @@ public interface ILanguage : IEntity, IRememberBeingDirty
/// define fallback strategies when a value does not exist for a requested language.
/// </para>
/// </remarks>
[Obsolete("This will be replaced by fallback language ISO code in V13.")]
[Obsolete("This will be replaced by fallback language ISO code in V14.")]
[DataMember]
int? FallbackLanguageId { get; set; }
/// <summary>
/// Gets or sets the ISO code of a fallback language.
/// </summary>
/// <remarks>
/// <para>
/// The fallback language can be used in multi-lingual scenarios, to help
/// define fallback strategies when a value does not exist for a requested language.
/// </para>
/// </remarks>
[DataMember]
string? FallbackIsoCode
{
get => null;
set { }
}
}

View File

@@ -1,6 +1,5 @@
using System.Globalization;
using System.Runtime.Serialization;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Models.Entities;
namespace Umbraco.Cms.Core.Models;
@@ -14,6 +13,7 @@ public class Language : EntityBase, ILanguage
{
private string _cultureName;
private int? _fallbackLanguageId;
private string? _fallbackLanguageIsoCode;
private bool _isDefaultVariantLanguage;
private string _isoCode;
private bool _mandatory;
@@ -74,10 +74,17 @@ public class Language : EntityBase, ILanguage
}
/// <inheritdoc />
[Obsolete("This will be replaced by fallback language ISO code in V13.")]
[Obsolete("This will be replaced by fallback language ISO code in V14.")]
public int? FallbackLanguageId
{
get => _fallbackLanguageId;
set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguageId, nameof(FallbackLanguageId));
}
/// <inheritdoc />
public string? FallbackIsoCode
{
get => _fallbackLanguageIsoCode;
set => SetPropertyValueAndDetectChanges(value, ref _fallbackLanguageIsoCode, nameof(FallbackIsoCode));
}
}

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Factories;
internal static class LanguageFactory
{
public static ILanguage BuildEntity(LanguageDto dto)
public static ILanguage BuildEntity(LanguageDto dto, string? fallbackIsoCode)
{
ArgumentNullException.ThrowIfNull(dto);
if (dto.IsoCode is null)
@@ -22,6 +22,7 @@ internal static class LanguageFactory
IsDefault = dto.IsDefault,
IsMandatory = dto.IsMandatory,
FallbackLanguageId = dto.FallbackLanguageId,
FallbackIsoCode = fallbackIsoCode
};
// Reset dirty initial properties

View File

@@ -120,7 +120,19 @@ internal class LanguageRepository : EntityRepositoryBase<int, ILanguage>, ILangu
new FullDataSetRepositoryCachePolicy<ILanguage, int>(GlobalIsolatedCache, ScopeAccessor, GetEntityId, /*expires:*/ false);
protected ILanguage ConvertFromDto(LanguageDto dto)
=> LanguageFactory.BuildEntity(dto);
{
// yes, we want to lock _codeIdMap
lock (_codeIdMap)
{
string? fallbackIsoCode = null;
if (dto.FallbackLanguageId.HasValue && _idCodeMap.TryGetValue(dto.FallbackLanguageId.Value, out fallbackIsoCode) == false)
{
throw new ArgumentException($"The ISO code map did not contain ISO code for fallback language ID: {dto.FallbackLanguageId}. Please reload the caches.");
}
return LanguageFactory.BuildEntity(dto, fallbackIsoCode);
}
}
// do NOT leak that language, it's not deep-cloned!
private ILanguage GetDefault()
@@ -172,20 +184,25 @@ internal class LanguageRepository : EntityRepositoryBase<int, ILanguage>, ILangu
sql.OrderBy<LanguageDto>(x => x.Id);
// get languages
var languages = Database.Fetch<LanguageDto>(sql).Select(ConvertFromDto).OrderBy(x => x.Id).ToList();
List<LanguageDto>? languageDtos = Database.Fetch<LanguageDto>(sql) ?? new List<LanguageDto>();
// initialize the code-id map
lock (_codeIdMap)
// initialize the code-id map if we've reloaded the entire set of languages
if (ids?.Any() == false)
{
_codeIdMap.Clear();
_idCodeMap.Clear();
foreach (ILanguage language in languages)
lock (_codeIdMap)
{
_codeIdMap[language.IsoCode] = language.Id;
_idCodeMap[language.Id] = language.IsoCode.ToLowerInvariant();
_codeIdMap.Clear();
_idCodeMap.Clear();
foreach (LanguageDto languageDto in languageDtos)
{
ArgumentException.ThrowIfNullOrEmpty(languageDto.IsoCode, nameof(LanguageDto.IsoCode));
_codeIdMap[languageDto.IsoCode] = languageDto.Id;
_idCodeMap[languageDto.Id] = languageDto.IsoCode;
}
}
}
var languages = languageDtos.Select(ConvertFromDto).OrderBy(x => x.Id).ToList();
return languages;
}

View File

@@ -95,7 +95,7 @@ public class LanguageBuilder<TParent>
return this;
}
[Obsolete("This will be replaced in V13 by a corresponding method accepting language ISO code instead of language ID.")]
[Obsolete("This will be replaced in V14 by a corresponding method accepting language ISO code instead of language ID.")]
public LanguageBuilder<TParent> WithFallbackLanguageId(int fallbackLanguageId)
{
_fallbackLanguageId = fallbackLanguageId;