Skip to content

Code Tidy: Clean up further obsoleted code scheduled for removal in Umbraco 18 (ILocalizationService)#22677

Merged
AndyButland merged 5 commits into
v18/devfrom
v18/task/further-todos-and-obsoletes-8
May 5, 2026
Merged

Code Tidy: Clean up further obsoleted code scheduled for removal in Umbraco 18 (ILocalizationService)#22677
AndyButland merged 5 commits into
v18/devfrom
v18/task/further-todos-and-obsoletes-8

Conversation

@AndyButland

@AndyButland AndyButland commented May 2, 2026

Copy link
Copy Markdown
Contributor

Description

This continues the v18 cleanup series, removing the ILocalizationService interface and its LocalizationService implementation. Both were [Obsolete] and scheduled for removal in Umbraco 18; every public method on ILocalizationService had a named replacement (ILanguageService, IDictionaryItemService) and all internal callers have been migrated.

Removed

  • Umbraco.Cms.Core.Services.ILocalizationService
  • Umbraco.Cms.Core.Services.LocalizationService
  • Services.AddUnique<ILocalizationService, LocalizationService>() registration
  • The integration test file LocalizationServiceTests (existing LanguageServiceTests and DictionaryItemServiceTests cover all the behaviour the deleted suite covered, plus more)

Migrated callers

  • LogPageUrlJob, LanguagesTelemetryProvider, SystemTroubleshootingInformationTelemetryProvider, UmbracoTreeSearcherFields, DefaultCultureAccessor, ContentValueSetBuilder, UmbracoContentIndex (Lucene) → ILanguageService
  • TemplateRenderer → unused ILocalizationService parameter dropped
  • DictionaryService, EntityXmlSerializerIDictionaryItemService
  • DefaultCultureDictionary + DefaultCultureDictionaryFactory, PublishedValueFallback, NotificationServiceILanguageService + IDictionaryItemService
  • ContentEditingServiceBase (and the six derived services: ContentEditingService, ContentEditingServiceWithSortingBase, ContentBlueprintEditingService, MediaEditingService, MemberContentEditingService, ElementEditingService) → ILanguageService (consumed via the migrated IUser.CalculateAllowedLanguageIdsAsync extension)
  • PackageDataInstallationILanguageService + IDictionaryItemService + IUserIdKeyResolver
  • PackagesRepositoryILanguageRepository + IDictionaryRepository + ICoreScopeProvider (mirrors what the deleted LocalizationService.GetLanguageById(int) / GetDictionaryItemById(int) did internally — these int-ID lookups have no equivalent on the new public services)

ServiceContext

The ILocalizationService LocalizationService property and its corresponding constructor / CreatePartial parameter have been removed. Equivalents for the replacement services have been added so they remain accessible from ServiceContext: LanguageService, DictionaryItemService.

Async refactors

IUser.CalculateAllowedLanguageIds(ILocalizationService) extension has been removed and replaced with CalculateAllowedLanguageIdsAsync(ILanguageService). The single internal caller (ContentEditingServiceBase.GetAllowedCulturesForEditingUser) was already async, so this removes a sync-over-async hop on the editing/validation path.

IDictionaryService.CalculatePath has been replaced with CalculatePathAsync. The implementation now uses true async recursion via IDictionaryItemService.GetAsync instead of .GetAwaiter().GetResult().

Tests

  • Added tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DictionaryServiceTests.cs with four unit tests covering the null-parent, root-parent, nested-hierarchy, and missing-parent paths through CalculatePathAsync.
  • Added two unit tests to UserExtensionsTests.cs covering CalculateAllowedLanguageIdsAsync for the access-to-all and group-restricted paths.
  • Added GivenLanguages_WhenPackageExported_ThenTheXmlContainsThem to CreatedPackagesRepositoryTests — exercises the int-ID → _languageRepository.Get(int) path in PackagesRepository.PackageLanguages, which previously had no test coverage.
  • Updated unit/integration test fixtures that previously injected Mock.Of<ILocalizationService>() to use the appropriate replacement service mock.

Other notable changes

  • PackageDataInstallation.Import* methods now log a warning when a Create/Update attempt fails. Previously LocalizationService.Save(ILanguage) threw InvalidOperationException on LanguageOperationStatus.InvalidFallback; now a single bad language no longer aborts the whole package install — it logs and continues, matching how ImportScripts, ImportPartialViews, etc. behave.
  • PackageDataInstallation resolves user ids to user keys via IUserIdKeyResolver.TryGetAsync, falling back to SuperUserKey for unknown ids — same pattern introduced in Code Tidy: Clean up further obsoleted code scheduled for removal in Umbraco 18 (IFileService) #22675.
  • PackagesRepository.PackageLanguages and PackageDictionaryItems now open a single scope around the loop rather than one per iteration. The class is [Obsolete] at class level but ExportPackage doesn't establish an ambient scope, so the scopes are still required.
  • ContentValueSetBuilder had ILocalizationService injected alongside the already-present ILanguageService — the redundant dependency has been dropped and the second _localizationService.GetDefaultLanguageIsoCode() call site reuses the existing defaultCulture local from earlier in the same method.
  • Doc comments on the Language/DictionaryItem saving/saved/deleting/deleted notifications updated to reference the per-domain services instead of ILocalizationService.

Breaking changes

All driven by the ILocalizationService removal; unavoidable while keeping the public API coherent in v18:

  • ServiceContext: constructor signature changed; LocalizationService property removed; CreatePartial parameter removed. New LanguageService / DictionaryItemService properties added in their place.
  • NotificationService, DictionaryService, DefaultCultureAccessor, DefaultCultureDictionaryFactory, ContentValueSetBuilder, UmbracoTreeSearcherFields, LanguagesTelemetryProvider constructors: ILocalizationService parameter replaced by the relevant per-domain service(s).
  • UmbracoContentIndex (Lucene): optional ILocalizationService? constructor parameter is now ILanguageService?; the protected LanguageService property type has changed to match.
  • IUser.CalculateAllowedLanguageIds(ILocalizationService) extension has been removed; new CalculateAllowedLanguageIdsAsync(ILanguageService) added.
  • IDictionaryService.CalculatePath(Guid?, int) has been removed; new CalculatePathAsync(Guid?, int) added.
  • PackageDataInstallation primary constructor adds ILanguageService, IDictionaryItemService, IUserIdKeyResolver and drops ILocalizationService. The two [Obsolete] v19-scheduled overloads have been deleted (each took an ILocalizationService parameter that no longer exists, so they could not be retained for the originally scheduled deprecation window).
  • PackagesRepository constructor (already [Obsolete] at the class level): ILocalizationService parameter replaced by ILanguageRepository + IDictionaryRepository + ICoreScopeProvider.

Testing

Solution builds and tests pass.

I've done manual checks of:

  • Backoffice management of languages and dictionary items (create, rename, add translation, delete).
  • A package import via AutomaticPackageMigrationPlan containing a language and a dictionary item with translations, to verify the PackageDataInstallation.ImportLanguages and ImportDictionaryItems paths still work end-to-end.

Copilot AI review requested due to automatic review settings May 2, 2026 14:34
@claude

claude Bot commented May 2, 2026

Copy link
Copy Markdown

Claude finished @AndyButland's task in 6m 6s —— View job


PR Review

Target: origin/v18/dev · Based on commit: d4c854da · Skipped: 3 deleted files out of 52 total

Removes ILocalizationService and LocalizationService (obsoleted in v16, scheduled for v18 removal), migrating all callers to ILanguageService and IDictionaryItemService. Net result is -577 lines across Core, Infrastructure, Examine.Lucene, and Web.Common layers.

  • Modified public API: IDictionaryService (method renamed sync→async), ServiceContext (constructor + LocalizationService property), DefaultCultureDictionaryFactory constructor, UmbracoContentIndex.LanguageService property type, IUser.CalculateAllowedLanguageIds extension
  • Affected implementations (outside this PR): Any external code implementing IDictionaryService or instantiating the changed classes directly
  • Breaking changes: All documented in the PR description; forced by the ILocalizationService removal and expected for a v18 major-version cleanup
  • Other changes: PackageDataInstallation.ImportLanguages now logs-and-continues on failure instead of throwing; PackageDataInstallation.ImportDictionaryItem does the same (see Important finding)

Important

  • src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs:1585: When a dictionary item save fails, items.Add(dictionaryItem!) at line 1594 still executes — there is no continue or early return. On a create failure the returned item has Id == 0 (no database row), and immediately below, dictionaryItem.Key is passed as parentId for any nested children (line 1599), so child items will also fail since their parent never existed. Compare to ImportLanguages (line ~1710) which correctly uses continue after logging the warning, keeping the return list clean. The fix is the same pattern:

    if (saveResult.Success is false)
    {
        _logger.LogWarning(...);\
        continue; // ← add this
    }
    items.Add(dictionaryItem!);

    Wait — ImportDictionaryItem uses items (a List<>) not an iteration variable, so a straight continue won't work (it's not in a loop at that point). The cleanest fix is:

    if (saveResult.Success)
    {
        items.Add(dictionaryItem!);
        items.AddRange(ImportDictionaryItems(
            dictionaryItemElement.Elements("DictionaryItem"),
            languages,
            dictionaryItem.Key,
            userId));
    }
    return items;

    This guards both the parent and its recursive children behind the save success check, matching the behaviour of ImportLanguages.


Suggestions

  • src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs:1714: In ImportLanguages, list.Add(langauge) adds the pre-save Language object. ILanguageService.CreateAsync returns the persisted entity via saveResult.Result — using that instead (list.Add(saveResult.Result)) is safer and clearer, in case any future service implementation returns a new object rather than mutating the passed one in-place.

  • tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DictionaryServiceTests.cs:15: New test methods use a MethodName_Scenario_Expected naming pattern (e.g., CalculatePathAsync_NullParentId_ReturnsRootPath) rather than the repository convention Can_/Cannot_ prefix (e.g., Can_CalculatePathAsync_When_ParentId_Is_Null). Same applies to the two new methods in UserExtensionsTests.cs:116. Minor, but worth keeping consistent.

  • src/Umbraco.Core/Dictionary/DefaultCultureDictionary.cs:64 and src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs:303: Several new _languageService.GetAsync(...).GetAwaiter().GetResult() calls appear in synchronous interface implementations that sit on request hot-paths (content rendering, dictionary lookup per request). These won't deadlock under ASP.NET Core's thread-pool context, but they do add unnecessary blocking overhead per request. The interfaces (ICultureDictionary, IPublishedValueFallback) would need async overloads to eliminate these properly — probably outside the scope of this cleanup PR, but worth a tracking issue.


Request Changes

One important bug (dictionary items added to the result list even when the save fails, including with Id == 0 for new items) should be addressed before merge. Everything else is either a minor style point or a known limitation of the synchronous interface contracts.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Removes the obsolete ILocalizationService API surface (and implementation) as part of the Umbraco 18 cleanup, migrating all remaining callers to ILanguageService and IDictionaryItemService (plus repositories where int-ID lookups are needed).

Changes:

  • Deleted ILocalizationService/LocalizationService and removed DI registration.
  • Migrated core/runtime callers (telemetry, search, packaging, published-value fallback, editing services) to ILanguageService + IDictionaryItemService and adjusted ServiceContext.
  • Refactored a couple of sync-over-async paths (notably IDictionaryService.CalculatePathAsync) and updated/added tests accordingly.

Reviewed changes

Copilot reviewed 52 out of 52 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
tests/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Examine/ContentValueSetBuilderTests.cs Updates builder tests for removed localization dependency.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Telemetry/SystemTroubleshootingInformationTelemetryProviderTests.cs Switches test wiring from ILocalizationService to ILanguageService.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/SystemInformationServiceTests.cs Updates mocks to ILanguageService.GetDefaultIsoCodeAsync().
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Services/DictionaryServiceTests.cs Adds unit tests for new CalculatePathAsync behavior.
tests/Umbraco.Tests.UnitTests/Umbraco.Core/Models/UserExtensionsTests.cs Adds tests for CalculateAllowedLanguageIdsAsync.
tests/Umbraco.Tests.Integration/Umbraco.Web.BackOffice/UrlAndDomains/DomainAndUrlsTests.cs Updates default language retrieval to ILanguageService.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/LocalizationServiceTests.cs Removes integration tests for deleted localization service.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/EntityXmlSerializerTests.cs Uses IDictionaryItemService for dictionary item lookup.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/PropertyEditors/BlockListElementLevelVariationTests.Publishing.cs Updates default culture lookup to ILanguageService.
tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Persistence/Repositories/DataTypeDefinitionRepositoryTest.cs Removes unused ILocalizationService fixture property.
tests/Umbraco.Tests.Integration/Umbraco.Examine.Lucene/UmbracoExamine/IndexInitializer.cs Removes localization dependency and updates mocked language service usage.
tests/Umbraco.Tests.Integration/Umbraco.Core/Packaging/CreatedPackagesRepositoryTests.cs Adjusts PackagesRepository construction; adds export coverage for language int-ID path.
tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml Drops suppression tied to removed ctor signature.
src/Umbraco.Web.Common/Templates/TemplateRenderer.cs Drops unused ILocalizationService constructor parameter and doc entry.
src/Umbraco.Infrastructure/Telemetry/Providers/SystemTroubleshootingInformationTelemetryProvider.cs Swaps to ILanguageService for default language reporting.
src/Umbraco.Infrastructure/Telemetry/Providers/LanguagesTelemetryProvider.cs Swaps language counting to ILanguageService.
src/Umbraco.Infrastructure/Search/UmbracoTreeSearcherFields.cs Swaps configured-language enumeration to ILanguageService.
src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs Splits localization concerns into ILanguageService/IDictionaryItemService, adds warning logging and userId→key resolution.
src/Umbraco.Infrastructure/Examine/ContentValueSetBuilder.cs Removes redundant ILocalizationService dependency and reuses earlier default culture value.
src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs Updates factory methods to new PackagesRepository/PackageDataInstallation constructors.
src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Examine.cs Removes localization service injection from ContentValueSetBuilder construction.
src/Umbraco.Examine.Lucene/UmbracoContentIndex.cs Changes optional ctor dependency from ILocalizationService? to ILanguageService?.
src/Umbraco.Core/Services/ServiceContext.cs Removes LocalizationService; adds LanguageService and DictionaryItemService.
src/Umbraco.Core/Services/NotificationService.cs Updates language lookups to ILanguageService.
src/Umbraco.Core/Services/MemberContentEditingService.cs Removes obsolete localization dependency from base ctor chain.
src/Umbraco.Core/Services/MediaEditingService.cs Removes obsolete localization dependency from base ctor chain.
src/Umbraco.Core/Services/LocalizationService.cs Deletes obsolete implementation.
src/Umbraco.Core/Services/ILocalizationService.cs Deletes obsolete public interface.
src/Umbraco.Core/Services/IDictionaryService.cs Replaces CalculatePath with async CalculatePathAsync.
src/Umbraco.Core/Services/EntityXmlSerializer.cs Uses IDictionaryItemService for child enumeration.
src/Umbraco.Core/Services/ElementEditingService.cs Removes obsolete localization dependency from base ctor chain.
src/Umbraco.Core/Services/DictionaryService.cs Refactors to async recursion using IDictionaryItemService.
src/Umbraco.Core/Services/ContentEditingServiceWithSortingBase.cs Removes obsolete localization dependency from base ctor chain.
src/Umbraco.Core/Services/ContentEditingServiceBase.cs Switches allowed-language calculation to new async extension.
src/Umbraco.Core/Services/ContentEditingService.cs Removes obsolete localization dependency from ctor.
src/Umbraco.Core/Services/ContentBlueprintEditingService.cs Removes obsolete localization dependency from base ctor chain.
src/Umbraco.Core/PublishedCache/DefaultCultureAccessor.cs Uses ILanguageService.GetDefaultIsoCodeAsync() instead of localization service.
src/Umbraco.Core/Packaging/PackagesRepository.cs Replaces localization service with repositories + scope provider for int-ID lookups; consolidates scope usage.
src/Umbraco.Core/Notifications/LanguageSavingNotification.cs Updates docs to reference ILanguageService (needs wording tweak—see comments).
src/Umbraco.Core/Notifications/LanguageSavedNotification.cs Updates docs to reference ILanguageService (needs wording tweak—see comments).
src/Umbraco.Core/Notifications/LanguageDeletingNotification.cs Updates docs to reference ILanguageService.
src/Umbraco.Core/Notifications/LanguageDeletedNotification.cs Updates docs to reference ILanguageService.
src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs Updates docs to reference IDictionaryItemService (needs wording tweak—see comments).
src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs Updates docs to reference IDictionaryItemService (needs wording tweak—see comments).
src/Umbraco.Core/Notifications/DictionaryItemDeletingNotification.cs Updates docs to reference IDictionaryItemService.
src/Umbraco.Core/Notifications/DictionaryItemDeletedNotification.cs Updates docs to reference IDictionaryItemService.
src/Umbraco.Core/Models/UserExtensions.cs Replaces sync CalculateAllowedLanguageIds with CalculateAllowedLanguageIdsAsync.
src/Umbraco.Core/Models/PublishedContent/PublishedValueFallback.cs Uses ServiceContext.LanguageService and async language lookups.
src/Umbraco.Core/Dictionary/DefaultCultureDictionaryFactory.cs Splits dependency into ILanguageService + IDictionaryItemService.
src/Umbraco.Core/Dictionary/DefaultCultureDictionary.cs Replaces localization service usage with language/dictionary item services.
src/Umbraco.Core/DependencyInjection/UmbracoBuilder.cs Removes ILocalizationService registration from core services.
src/Umbraco.Core/CLAUDE.md Updates service list documentation to replace localization service with language/dictionary services.

Comment thread src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs Outdated
Comment thread src/Umbraco.Core/Notifications/LanguageSavingNotification.cs Outdated
Comment thread src/Umbraco.Core/Notifications/LanguageSavedNotification.cs Outdated
Comment thread src/Umbraco.Core/Notifications/DictionaryItemSavingNotification.cs Outdated
Comment thread src/Umbraco.Core/Notifications/DictionaryItemSavedNotification.cs Outdated
AndyButland and others added 3 commits May 2, 2026 17:05
# Conflicts:
#	src/Umbraco.Core/Services/EntityXmlSerializer.cs
#	src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs
#	src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs
#	tests/Umbraco.Tests.Integration/CompatibilitySuppressions.xml

@nikolajlauridsen nikolajlauridsen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I've merged v18/dev into this branch 👍

@AndyButland AndyButland enabled auto-merge (squash) May 5, 2026 09:45
# Conflicts:
#	src/Umbraco.Infrastructure/DependencyInjection/UmbracoBuilder.Services.cs
#	src/Umbraco.Infrastructure/Packaging/PackageDataInstallation.cs
#	tests/Umbraco.Tests.Integration/Umbraco.Infrastructure/Services/LocalizationServiceTests.cs
@AndyButland AndyButland merged commit 8e6e0e7 into v18/dev May 5, 2026
26 of 27 checks passed
@AndyButland AndyButland deleted the v18/task/further-todos-and-obsoletes-8 branch May 5, 2026 11:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants