v10: Make language name editable (#12243)
* Update language models to get and set manual name * Save custom language name in controller * Rewrite AngularJS language edit view and controller * Cleanup language overview * Remove icon from language overview * Make styling of control group the same as properties * Ensure both ISO code and culture name are set in language model * Use new language model constructor * Update tests to use new language constructor * Update culture name in dictionary package export * Use language name in dictionary * Fix language nullability issues * Cleanup GetAllCultures and added null checks * Re-add obsolete constructors * Make language name required and update Cypress test * Fix routing/saveNewLanguages Cypress test * Make language name optional (improved backwards compatibility) Co-authored-by: Ronald Barendse <ronald@panoramastudios.nl>
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using System.ComponentModel;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
@@ -12,10 +11,9 @@ namespace Umbraco.Cms.Core.Models.ContentEditing
|
||||
|
||||
[DataMember(Name = "culture", IsRequired = true)]
|
||||
[Required(AllowEmptyStrings = false)]
|
||||
public string? IsoCode { get; set; }
|
||||
public string IsoCode { get; set; } = null!;
|
||||
|
||||
[DataMember(Name = "name")]
|
||||
[ReadOnly(true)]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[DataMember(Name = "isDefault")]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Globalization;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Umbraco.Cms.Core.Models
|
||||
/// Gets or sets the culture name of the language.
|
||||
/// </summary>
|
||||
[DataMember]
|
||||
string? CultureName { get; set; }
|
||||
string CultureName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="CultureInfo"/> object for the language.
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading;
|
||||
using Umbraco.Cms.Core.Configuration.Models;
|
||||
using Umbraco.Cms.Core.Models.Entities;
|
||||
|
||||
@@ -14,18 +12,28 @@ namespace Umbraco.Cms.Core.Models
|
||||
[DataContract(IsReference = true)]
|
||||
public class Language : EntityBase, ILanguage
|
||||
{
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
|
||||
private string _isoCode = null!;
|
||||
private string? _cultureName;
|
||||
private string _isoCode;
|
||||
private string _cultureName;
|
||||
private bool _isDefaultVariantLanguage;
|
||||
private bool _mandatory;
|
||||
private int? _fallbackLanguageId;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Language" /> class.
|
||||
/// </summary>
|
||||
/// <param name="isoCode">The ISO code of the language.</param>
|
||||
/// <param name="cultureName">The name of the language.</param>
|
||||
public Language(string isoCode, string cultureName)
|
||||
{
|
||||
_isoCode = isoCode ?? throw new ArgumentNullException(nameof(isoCode));
|
||||
_cultureName = cultureName ?? throw new ArgumentNullException(nameof(cultureName));
|
||||
}
|
||||
|
||||
[Obsolete("Use the constructor not requiring global settings and accepting an explicit name instead, scheduled for removal in V11.")]
|
||||
public Language(GlobalSettings globalSettings, string isoCode)
|
||||
{
|
||||
IsoCode = isoCode;
|
||||
_globalSettings = globalSettings;
|
||||
_isoCode = isoCode ?? throw new ArgumentNullException(nameof(isoCode));
|
||||
_cultureName = CultureInfo.GetCultureInfo(isoCode).EnglishName;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -33,64 +41,25 @@ namespace Umbraco.Cms.Core.Models
|
||||
public string IsoCode
|
||||
{
|
||||
get => _isoCode;
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _isoCode!, nameof(IsoCode));
|
||||
set
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
SetPropertyValueAndDetectChanges(value, ref _isoCode!, nameof(IsoCode));
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
[DataMember]
|
||||
public string? CultureName
|
||||
public string CultureName
|
||||
{
|
||||
// CultureInfo.DisplayName is the name in the installed .NET language
|
||||
// .NativeName is the name in culture info's language
|
||||
// .EnglishName is the name in English
|
||||
//
|
||||
// there is no easy way to get the name in a specified culture (which would need to be installed on the server)
|
||||
// this works:
|
||||
// var rm = new ResourceManager("mscorlib", typeof(int).Assembly);
|
||||
// var name = rm.GetString("Globalization.ci_" + culture.Name, displayCulture);
|
||||
// but can we rely on it?
|
||||
//
|
||||
// and... DisplayName is captured and cached in culture infos returned by GetCultureInfo(), using
|
||||
// the value for the current thread culture at the moment it is first retrieved - whereas creating
|
||||
// a new CultureInfo() creates a new instance, which _then_ can get DisplayName again in a different
|
||||
// culture
|
||||
//
|
||||
// I assume that, on a site, all language names should be in the SAME language, in DB,
|
||||
// and that would be the Umbraco.Core.DefaultUILanguage (app setting) - BUT if by accident
|
||||
// ANY culture has been retrieved with another current thread culture - it's now corrupt
|
||||
//
|
||||
// so, the logic below ensures that the name always end up being the correct name
|
||||
// see also LanguageController.GetAllCultures which is doing the same
|
||||
//
|
||||
// all this, including the ugly settings injection, because se store language names in db,
|
||||
// otherwise it would be ok to simply return new CultureInfo(IsoCode).DisplayName to get the name
|
||||
// in whatever culture is current - we should not do it, see task #3623
|
||||
//
|
||||
// but then, some tests that compare audit strings (for culture names) would need to be fixed
|
||||
|
||||
get
|
||||
get => _cultureName;
|
||||
set
|
||||
{
|
||||
if (_cultureName != null) return _cultureName;
|
||||
ArgumentNullException.ThrowIfNull(value);
|
||||
|
||||
// capture
|
||||
var threadUiCulture = Thread.CurrentThread.CurrentUICulture;
|
||||
|
||||
try
|
||||
{
|
||||
var defaultUiCulture = CultureInfo.GetCultureInfo(_globalSettings.DefaultUILanguage);
|
||||
Thread.CurrentThread.CurrentUICulture = defaultUiCulture;
|
||||
|
||||
// get name - new-ing an instance to get proper display name
|
||||
return new CultureInfo(IsoCode).DisplayName;
|
||||
}
|
||||
finally
|
||||
{
|
||||
// restore
|
||||
Thread.CurrentThread.CurrentUICulture = threadUiCulture;
|
||||
}
|
||||
SetPropertyValueAndDetectChanges(value, ref _cultureName!, nameof(CultureName));
|
||||
}
|
||||
|
||||
set => SetPropertyValueAndDetectChanges(value, ref _cultureName, nameof(CultureName));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
target.Translations.Add(new DictionaryTranslationDisplay
|
||||
{
|
||||
IsoCode = lang.IsoCode,
|
||||
DisplayName = lang.CultureInfo?.DisplayName,
|
||||
DisplayName = lang.CultureName,
|
||||
Translation = translation?.Value ?? string.Empty,
|
||||
LanguageId = lang.Id
|
||||
});
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
target.Translations.Add(
|
||||
new DictionaryOverviewTranslationDisplay
|
||||
{
|
||||
DisplayName = lang.CultureInfo?.DisplayName,
|
||||
DisplayName = lang.CultureName,
|
||||
HasTranslation = translation != null && string.IsNullOrEmpty(translation.Value) == false
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Cms.Core.Mapping;
|
||||
@@ -31,7 +31,7 @@ namespace Umbraco.Cms.Core.Models.Mapping
|
||||
{
|
||||
target.Id = source.Id;
|
||||
target.IsoCode = source.IsoCode;
|
||||
target.Name = source.CultureInfo?.DisplayName;
|
||||
target.Name = source.CultureName;
|
||||
target.IsDefault = source.IsDefault;
|
||||
target.IsMandatory = source.IsMandatory;
|
||||
target.FallbackLanguageId = source.FallbackLanguageId;
|
||||
|
||||
Reference in New Issue
Block a user