diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js
index a11aa8bff8..5f1c46de4c 100644
--- a/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js
+++ b/src/Umbraco.Web.UI.Client/src/views/languages/edit.controller.js
@@ -1,7 +1,7 @@
(function () {
"use strict";
- function LanguagesEditController($scope, $timeout, $location, $routeParams, navigationService, notificationsService, localizationService, languageResource, contentEditingHelper, formHelper, eventsService) {
+ function LanguagesEditController($scope, $q, $timeout, $location, $routeParams, overlayService, navigationService, notificationsService, localizationService, languageResource, contentEditingHelper, formHelper, eventsService) {
var vm = this;
@@ -20,6 +20,8 @@
vm.toggleMandatory = toggleMandatory;
vm.toggleDefault = toggleDefault;
+ var currCulture = null;
+
function init() {
// localize labels
@@ -32,7 +34,8 @@
"languages_addLanguage",
"languages_noFallbackLanguageOption",
"languages_fallbackLanguageDescription",
- "languages_fallbackLanguage"
+ "languages_fallbackLanguage",
+ "defaultdialogs_confirmSure"
];
localizationService.localizeMany(labelKeys).then(function (values) {
@@ -43,6 +46,7 @@
vm.labels.defaultLanguageHelp = values[4];
vm.labels.addLanguage = values[5];
vm.labels.noFallbackLanguageOption = values[6];
+ vm.labels.areYouSure = values[9];
$scope.properties = {
fallbackLanguage: {
@@ -53,46 +57,56 @@
};
if ($routeParams.create) {
- vm.page.name = vm.labels.addLanguage;
- languageResource.getCultures().then(function (culturesDictionary) {
- var cultures = [];
- angular.forEach(culturesDictionary, function (value, key) {
- cultures.push({
- name: key,
- displayName: value
- });
- });
- vm.availableCultures = cultures;
- });
+ vm.page.name = vm.labels.addLanguage;
}
});
vm.loading = true;
- languageResource.getAll().then(function (languages) {
+
+ var promises = [];
+
+ //load all culture/languages
+ promises.push(languageResource.getCultures().then(function (culturesDictionary) {
+ var cultures = [];
+ angular.forEach(culturesDictionary, function (value, key) {
+ cultures.push({
+ name: key,
+ displayName: value
+ });
+ });
+ vm.availableCultures = cultures;
+ }));
+
+ //load all possible fallback languages
+ promises.push(languageResource.getAll().then(function (languages) {
vm.availableLanguages = languages.filter(function (l) {
return $routeParams.id != l.id;
});
vm.loading = false;
- });
+ }));
if (!$routeParams.create) {
- vm.loading = true;
-
- languageResource.getById($routeParams.id).then(function(lang) {
+ promises.push(languageResource.getById($routeParams.id).then(function(lang) {
vm.language = lang;
vm.page.name = vm.language.name;
- /* we need to store the initial default state so we can disabel the toggle if it is the default.
+ /* we need to store the initial default state so we can disable the toggle if it is the default.
we need to prevent from not having a default language. */
vm.initIsDefault = angular.copy(vm.language.isDefault);
- vm.loading = false;
makeBreadcrumbs();
- });
+
+ //store to check if we are changing the lang culture
+ currCulture = vm.language.culture;
+ }));
}
+ $q.all(promises, function () {
+ vm.loading = false;
+ });
+
$timeout(function () {
navigationService.syncTree({ tree: "languages", path: "-1" });
});
@@ -103,31 +117,54 @@
if (formHelper.submitForm({ scope: $scope })) {
vm.page.saveButtonState = "busy";
- languageResource.save(vm.language).then(function (lang) {
+ //check if the culture is being changed
+ if (currCulture && vm.language.culture !== currCulture) {
- formHelper.resetForm({ scope: $scope });
+ const changeCultureAlert = {
+ title: vm.labels.areYouSure,
+ view: "views/languages/overlays/change.html",
+ submitButtonLabelKey: "general_continue",
+ submit: function (model) {
+ saveLanguage();
+ overlayService.close();
+ },
+ close: function () {
+ overlayService.close();
+ vm.page.saveButtonState = "init";
+ }
+ };
- vm.language = lang;
- vm.page.saveButtonState = "success";
- localizationService.localize("speechBubbles_languageSaved").then(function(value){
- notificationsService.success(value);
- });
-
- // emit event when language is created or updated/saved
- var args = { language: lang, isNew: $routeParams.create ? true : false };
- eventsService.emit("editors.languages.languageSaved", args);
-
- back();
-
- }, function (err) {
- vm.page.saveButtonState = "error";
-
- formHelper.handleError(err);
-
- });
+ overlayService.open(changeCultureAlert);
+ }
+ else {
+ saveLanguage();
+ }
}
+ }
+ function saveLanguage() {
+ languageResource.save(vm.language).then(function (lang) {
+ formHelper.resetForm({ scope: $scope });
+
+ vm.language = lang;
+ vm.page.saveButtonState = "success";
+ localizationService.localize("speechBubbles_languageSaved").then(function (value) {
+ notificationsService.success(value);
+ });
+
+ // emit event when language is created or updated/saved
+ var args = { language: lang, isNew: $routeParams.create ? true : false };
+ eventsService.emit("editors.languages.languageSaved", args);
+
+ back();
+
+ }, function (err) {
+ vm.page.saveButtonState = "error";
+
+ formHelper.handleError(err);
+
+ });
}
function back() {
diff --git a/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html b/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html
new file mode 100644
index 0000000000..c5e813fa51
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/views/languages/overlays/change.html
@@ -0,0 +1,7 @@
+
+
+
+ Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt.
+
+
+
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
index d7d743e7e9..86283310fd 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en.xml
@@ -414,6 +414,7 @@
Click to add a Macro
Insert table
This will delete the language
+ Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt
Last Edited
Link
Internal link:
diff --git a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
index eff344d4f3..5fa5ad6ea3 100644
--- a/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
+++ b/src/Umbraco.Web.UI/Umbraco/config/lang/en_us.xml
@@ -417,6 +417,7 @@
Click to add a Macro
Insert table
This will delete the language
+ Changing the culture for a language may be an expensive operation and will result in the content cache and indexes being rebuilt
Last Edited
Link
Internal link:
diff --git a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
index 65b29aabc6..b00a4818f6 100644
--- a/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
+++ b/src/Umbraco.Web/Cache/DistributedCacheExtensions.cs
@@ -266,13 +266,21 @@ namespace Umbraco.Web.Cache
public static void RefreshLanguageCache(this DistributedCache dc, ILanguage language)
{
if (language == null) return;
- dc.Refresh(LanguageCacheRefresher.UniqueId, language.Id);
+
+ var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode,
+ language.WasPropertyDirty(nameof(ILanguage.IsoCode))
+ ? LanguageCacheRefresher.JsonPayload.LanguageChangeType.ChangeCulture
+ : LanguageCacheRefresher.JsonPayload.LanguageChangeType.Update);
+
+ dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
}
public static void RemoveLanguageCache(this DistributedCache dc, ILanguage language)
{
if (language == null) return;
- dc.Remove(LanguageCacheRefresher.UniqueId, language.Id);
+
+ var payload = new LanguageCacheRefresher.JsonPayload(language.Id, language.IsoCode, LanguageCacheRefresher.JsonPayload.LanguageChangeType.Remove);
+ dc.RefreshByPayload(LanguageCacheRefresher.UniqueId, new[] { payload });
}
#endregion
diff --git a/src/Umbraco.Web/Editors/LanguageController.cs b/src/Umbraco.Web/Editors/LanguageController.cs
index 650dcea6e9..cb7fde23db 100644
--- a/src/Umbraco.Web/Editors/LanguageController.cs
+++ b/src/Umbraco.Web/Editors/LanguageController.cs
@@ -99,24 +99,28 @@ namespace Umbraco.Web.Editors
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
// this is prone to race conditions but the service will not let us proceed anyways
- var existing = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
+ var existingByCulture = Services.LocalizationService.GetLanguageByIsoCode(language.IsoCode);
// the localization service might return the generic language even when queried for specific ones (e.g. "da" when queried for "da-DK")
// - we need to handle that explicitly
- if (existing?.IsoCode != language.IsoCode)
+ if (existingByCulture?.IsoCode != language.IsoCode)
{
- existing = null;
+ existingByCulture = null;
}
- if (existing != null && language.Id != existing.Id)
+ if (existingByCulture != null && language.Id != existingByCulture.Id)
{
//someone is trying to create a language that already exist
ModelState.AddModelError("IsoCode", "The language " + language.IsoCode + " already exists");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
- if (existing == null)
+ var existingById = language.Id != default ? Services.LocalizationService.GetLanguageById(language.Id) : null;
+
+ if (existingById == null)
{
+ //Creating a new lang...
+
CultureInfo culture;
try
{
@@ -141,38 +145,39 @@ namespace Umbraco.Web.Editors
return Mapper.Map(newLang);
}
- existing.IsMandatory = language.IsMandatory;
+ existingById.IsMandatory = language.IsMandatory;
// note that the service will prevent the default language from being "un-defaulted"
// but does not hurt to test here - though the UI should prevent it too
- if (existing.IsDefault && !language.IsDefault)
+ if (existingById.IsDefault && !language.IsDefault)
{
ModelState.AddModelError("IsDefault", "Cannot un-default the default language.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
- existing.IsDefault = language.IsDefault;
- existing.FallbackLanguageId = language.FallbackLanguageId;
+ existingById.IsDefault = language.IsDefault;
+ existingById.FallbackLanguageId = language.FallbackLanguageId;
+ existingById.IsoCode = language.IsoCode;
// modifying an existing language can create a fallback, verify
// note that the service will check again, dealing with race conditions
- if (existing.FallbackLanguageId.HasValue)
+ if (existingById.FallbackLanguageId.HasValue)
{
var languages = Services.LocalizationService.GetAllLanguages().ToDictionary(x => x.Id, x => x);
- if (!languages.ContainsKey(existing.FallbackLanguageId.Value))
+ if (!languages.ContainsKey(existingById.FallbackLanguageId.Value))
{
ModelState.AddModelError("FallbackLanguage", "The selected fall back language does not exist.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
- if (CreatesCycle(existing, languages))
+ if (CreatesCycle(existingById, languages))
{
- ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existing.FallbackLanguageId.Value].IsoCode} would create a circular path.");
+ ModelState.AddModelError("FallbackLanguage", $"The selected fall back language {languages[existingById.FallbackLanguageId.Value].IsoCode} would create a circular path.");
throw new HttpResponseException(Request.CreateValidationErrorResponse(ModelState));
}
}
- Services.LocalizationService.Save(existing);
- return Mapper.Map(existing);
+ Services.LocalizationService.Save(existingById);
+ return Mapper.Map(existingById);
}
// see LocalizationService