Feature: #93334 - Translation Domain Mapping
See forge#93334
Description
Translation domains have been introduced as a shorter alternative to file-based
references for label resources (.xlf
XLIFF files). The syntax uses the format
package
and is fully backward compatible
with existing LLL:
references. "Package" refers to the extension key,
like "backend" for "EXT:backend".
This synax is designed to improve readability, remove a clear reference
to the used file extension and to add convenience for new developers and
integrators: The previous locallang.
convention as well as the
name has been removed in favor of a more generic "messages" resource name,
which is common for localization systems or Symfony-based applications.
This is also where the term "translation domain" stems from.
Example:
// Domain-based reference
$languageService->sL('backend.toolbar:save');
// Equivalent file-based reference (existing syntax, still supported)
$languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang_toolbar.xlf:save');
Note
The existing syntax and naming
(LLL:
)
will be around for a long while without a deprecation notice.
Translation Domain Format
The format defines two parts: The package part (extension key) and the resource part. These are separated by a dot.
As mentioned, the resource part will leave out previous historical
namings, especially locallang.
and the locallang_
prefix.
The actual identifier within the resource is separated by a colon.
Format
$languageService->sL('backend.toolbar:save');
// Resolves to: EXT:backend/Resources/Private/Language/locallang_toolbar.xlf and
// returns the translated "save" identifier.
Domain Resolution
Deterministic File-Based Mapping
Translation domains are resolved deterministically by scanning the file system. When a domain is first requested for a package:
- All label files in
Resources/
are discoveredPrivate/ Language/ - A domain name is generated from each file name
- The domain-to-file mapping is cached in
cache.
l10n - Subsequent requests use the cached mapping
This ensures domain names always correspond to existing files and prevents speculative file system lookups.
When there are filename conflicts, such as locallang_
and db.
,
then locallang_
will be ignored.
Performance Characteristics
The implementation reduces file system operations compared to traditional file-based lookups, as all label files within an extension are discovered once.
Domain Generation Rules
Domain names are generated from file paths using these transformation rules:
- The base path
Resources/
is omittedPrivate/ Language/ -
Standard filename patterns:
locallang.
→xlf .messages
locallang_
→toolbar. xlf .toolbar
locallang_
→sudo_ mode. xlf .sudo_
mode
-
Subdirectories use dot notation:
Form/
→locallang_ tabs. xlf .form.
tabs
-
Site Set labels receive the
.sets
prefix:Configuration/
→Sets/ Felogin/ labels. xlf .sets.
felogin
-
Case conversion:
- UpperCamelCase → snake_case (
Sudo
→Mode sudo_
)mode - snake_case → preserved (
sudo_
→mode sudo_
)mode
- UpperCamelCase → snake_case (
-
Locale prefixes are irrelevant for the resource identifier resolving. These prefixes will be properly evaluated internally for later locale-based translations:
- (
de.
→locallang. xlf messages
) - (
de-
→AT. tabs. xlf tabs
)
- (
Examples:
File Path → Domain
────────────────────────────────────────────────────────────
EXT:backend/.../locallang.xlf → backend.messages
EXT:backend/.../locallang_toolbar.xlf → backend.toolbar
EXT:core/.../Form/locallang_tabs.xlf → core.form.tabs
EXT:felogin/Configuration/Sets/.../labels.xlf → felogin.sets.felogin
Usage
The translation domain system integrates with the existing
\TYPO3\
API. Both
domain-based and file-based references are supported:
$languageService = $this->languageServiceFactory->createFromSiteLanguage(
$request->getAttribute('language')
);
// Domain-based reference
$label = $languageService->sL('backend.toolbar:menu.item');
// Another domain-based reference
$label = $languageService->sL('backend.messages:button.save');
// Traditional file reference (still supported)
$label = $languageService->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:button.save');
Domain-based references are shorter and expose less implementation detail compared to full file paths.
CLI Command
The command
bin/
lists all available translation domains
with their available translations and label counts:
# List domains in active extensions
php bin/typo3 language:domain:list
# Filter by extension
php bin/typo3 language:domain:list --extension=backend
Output:
+--------------------+---------------------------------------+----------+
| Translation Domain | Label Resource | # Labels |
+--------------------+---------------------------------------+----------+
| backend.messages | EXT:backend/.../locallang.xlf | 84 |
| backend.toolbar | EXT:backend/.../locallang_toolbar.xlf | 42 |
+--------------------+---------------------------------------+----------+
The Labels column displays the number of translatable labels within the English source file.
PSR-14 Event
The event
Before
is dispatched after domain
generation, allowing customization of domain names.
Event:
\TYPO3\
The event provides these public properties:
file
- The file path being processedName extension
- The extension keyKey domain
- The generated domain name (can be modified)
Example
Event listener implementation:
namespace MyVendor\MyExtension\EventListener;
use TYPO3\CMS\Core\Attribute\AsEventListener;
use TYPO3\CMS\Core\Localization\Event\BeforeLabelResourceResolvedEvent;
final readonly class CustomDomainResolver
{
#[AsEventListener(identifier: 'my-extension/custom-domain-names')]
public function __invoke(BeforeLabelResourceResolvedEvent $event): void
{
// Shorten domain names for specific extension
if ($event->extensionKey === 'my_extension') {
$resourcePart = explode('.', $event->domain, 2)[1] ?? '';
$event->domain = 'myext.' . $resourcePart;
}
}
}
Impact
Translation domains provide a shorter, more readable alternative to file-based label references. The implementation uses deterministic file-system scanning with per-package caching to reduce file system operations.
All existing LLL:
file references continue to work. Translation domains
are optional and can be adopted incrementally. Both syntaxes can be mixed
within the same codebase. This affects TypoScript, Fluid
<f:
usages, TCA configuration, and PHP code using the
Language
API.
TYPO3 Core will slowly migrate internal references to use translation domains over time, as this increases readability, especially in Fluid templates, or TCA references.
Technical components:
Translation
- Maps domains to file paths, manages cacheDomain Mapper Label
- Discovers label files and handles locale resolutionFile Resolver Localization
- Integrates domain resolution transparentlyFactory
The
Translation
automatically detects file references
(EXT:
prefix) and passes them through unchanged.