| name | typo3-translations |
| description | Fixes TYPO3 labels and localization: label files (locallang.xlf, labels.xlf) in XLIFF 1.2 and 2.0, singular and plural forms through ICU MessageFormat, LLL references, translation domains and Content Blocks labels. Use when an untranslated key appears on screen in place of its text, when trans-unit ids end up duplicated after merging two branches, when keys are missing, or when relocating localization files to the v14 directory conventions. |
| compatibility | TYPO3 13.4 and TYPO3 14.x; TYPO3 14 preferred |
| metadata | {"version":"2.1.0","related_skills":"typo3-content-blocks, typo3-shadcn-content-elements, typo3-conformance, typo3-v14-reference","origin":"webconsulting"} |
| license | MIT / CC-BY-SA-4.0 |
TYPO3 13/14 Translations
Source: https://github.com/dirnbauer/webconsulting-skills
Use this skill for TYPO3 13 and TYPO3 14 translation files, XLIFF format
decisions, localization keys, ICU strings, LLL: references, and v14
translation-domain adoption. Start from the TYPO3 13-compatible baseline, then
apply TYPO3 14 upgrades when the extension or project is v14-only.
Core Rules
- For TYPO3 13+14 compatibility, keep XLIFF 1.2 and full
LLL:EXT:
references.
- For TYPO3 14-only work, prefer XLIFF 2.0 and consider translation domains in
PHP where they improve readability.
- Keep one source language file in English, unprefixed, for example
Resources/Private/Language/locallang.xlf or labels.xlf.
- Store target languages beside the source with locale prefixes, for example
de.locallang.xlf, de_CH.locallang.xlf, or de.labels.xlf.
- Do not create
en.locallang.xlf; English is the unprefixed source.
- Keep exactly one
<file> element per XLIFF file.
- Keep one target language per target file.
- Mark approved translations with
approved="yes" in XLIFF 1.2 or
state="reviewed" / state="final" in XLIFF 2.0.
- Treat ICU MessageFormat as TYPO3 14.2+ only; do not use ICU for code that
must run unchanged on TYPO3 13.
- Use TYPO3 localization APIs and Fluid ViewHelpers; do not add custom label
loaders.
Default Workflow
- Inventory source files in
Resources/Private/Language/*.xlf and
ContentBlocks/**/language/*.xlf.
- Inventory consumers in PHP, TCA, YAML, Fluid, TypoScript, TSconfig, and
Content Blocks configuration.
- Decide the compatibility mode:
- TYPO3 13+14: stay on XLIFF 1.2 and
LLL:EXT:.
- TYPO3 14-only: migrate selected catalogs to XLIFF 2.0 and optionally use
domains/ICU.
- Normalize paths, filenames, XML namespaces, and key namespaces.
- Add or migrate labels with natural target-language copy.
- Validate XML, duplicate IDs, source/target parity, and
LLL: resolution.
- Flush TYPO3 caches and smoke-test backend and frontend language contexts.
TYPO3 13-Compatible Baseline
Use this baseline when an extension must support TYPO3 13 and 14.
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="EXT:my_extension/Resources/Private/Language/locallang.xlf" product-name="my_extension">
<header/>
<body>
<trans-unit id="button.save">
<source>Save</source>
</trans-unit>
<trans-unit id="items.count">
<source>Items: %d</source>
</trans-unit>
</body>
</file>
</xliff>
Target file:
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" target-language="de" datatype="plaintext" original="EXT:my_extension/Resources/Private/Language/locallang.xlf" product-name="my_extension">
<header/>
<body>
<trans-unit id="button.save" approved="yes">
<source>Save</source>
<target>Speichern</target>
</trans-unit>
<trans-unit id="items.count" approved="yes">
<source>Items: %d</source>
<target>Eintraege: %d</target>
</trans-unit>
</body>
TYPO3 13 notes:
- Use
source-language="en" and target-language="<locale>".
- Use
<trans-unit> inside <body>.
- Use
approved="yes" for reviewed translations when approval state matters.
- Use
%s / %d style placeholders and runtime formatting, not ICU.
- Use full
LLL:EXT: references; translation domains are a TYPO3 14 feature.
TYPO3 14 Changes
Use these changes when the project is TYPO3 14-only, or when preparing a v14
branch while keeping the v13 branch on the baseline above.
XLIFF 2.0
Use this source shape for v14-only catalogs:
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">
<file id="messages">
<unit id="button.save">
<segment>
<source>Save</source>
</segment>
</unit>
<unit id="items.count">
<segment>
<source>{count, plural, one {# item} other {# items}}</source>
</segment>
</unit>
</file>
</xliff>
Use this target shape:
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en" trgLang="de">
<file id="messages">
<unit id="button.save">
<segment state="final">
<source>Save</source>
<target>Speichern</target>
</segment>
</unit>
<unit id="items.count">
<segment state="final">
<source>{count, plural, one {# item} other {# items}}</source>
<target>{count, plural, one {# Eintrag} other {# Eintraege}}</target>
</segment>
</unit>
</file>
</>
Migrating XLIFF 1.2 To 2.0
Use this checklist only after the affected label family no longer needs TYPO3
13 compatibility:
- Confirm
composer.json and ext_emconf.php no longer support TYPO3 13.
- Create a branch and convert one label family first.
- Change
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
to <xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en">.
- Remove the XLIFF 1.2
<header> and <body> wrapper.
- Convert each
<trans-unit id="key"> to <unit id="key"><segment>....
- Move
<source> and <target> inside <segment>.
- Convert source-language/target-language attributes to
srcLang/trgLang.
- Convert
approved="yes" to state="reviewed" or state="final".
- Split files that contain more than one
<file> or more than one target
language.
- Preserve unit IDs exactly; change references only when deliberately
renaming keys.
- Validate XML and compare unit IDs between source and every target file.
- Flush TYPO3 caches and verify labels in all affected backend/frontend views.
Do not migrate to a newer XLIFF dialect just because tooling can emit it. For a
TYPO3 14-only branch, use the documented XLIFF 2.0 shape unless the project has
verified support for another 2.x variant.
ICU MessageFormat
TYPO3 14.2+ supports ICU MessageFormat when translation calls pass named
arguments. Store ICU strings as normal XLIFF source and target text.
For TYPO3 13+14 compatibility, avoid ICU and keep classic placeholders. Add ICU
only in v14-only code paths or v14-only label files.
Rules:
- Use named placeholders such as
{count} and {name}.
- Do not translate placeholder names.
- Include
other in every plural and select expression.
- Test plural messages with
0, 1, and multiple values.
- Use
LanguageService::translate(), LocalizationUtility::translate(), or
Fluid <f:translate arguments="{...}">; sL() resolves plain labels only.
PHP:
$label = $languageService->translate(
'items.count',
'my_extension.messages',
['count' => 5],
);
Fluid:
<f:translate key="items.count" arguments="{count: itemCount}" />
Paths, Namespaces, And Domains
Use stable file paths and key namespaces so references remain readable during
v13-to-v14 upgrades.
File Paths
- Shared extension labels:
EXT:my_extension/Resources/Private/Language/messages.xlf
- Traditional extension labels:
EXT:my_extension/Resources/Private/Language/locallang.xlf
- Database/TCA labels:
EXT:my_extension/Resources/Private/Language/locallang_db.xlf
- Content Blocks labels:
ContentBlocks/<Type>/<name>/language/labels.xlf
Use full LLL:EXT: references where configuration is loaded outside an
Extbase context:
'label' => 'LLL:EXT:my_extension/Resources/Private/Language/locallang_db.xlf:tx_my_table.title',
XML Namespace
Use the XLIFF 2.0 namespace exactly:
xmlns="urn:oasis:names:tc:xliff:document:2.0"
XLIFF IDs Versus TYPO3 14 Domains
Keep the XLIFF unit ID independent from the file/domain reference:
- XLIFF unit ID:
top, button.save, field.ctaText.label.
- TYPO3 14 domain:
my_extension.messages, my_extension.label,
my_extension.backend.dashboard.
- Combined v14 reference:
my_extension.label:top.
Do not write my_extension.label.top when you mean a TYPO3 14 domain
reference. Without the colon, it is just a dotted label ID in the current
Extbase/default context.
TYPO3 14 derives domains from language file paths:
| File | Domain |
|---|
Resources/Private/Language/locallang.xlf | my_extension.messages |
Resources/Private/Language/messages.xlf | my_extension.messages |
Resources/Private/Language/label.xlf | my_extension.label |
Resources/Private/Language/locallang_label.xlf | my_extension.label |
Resources/Private/Language/Backend/locallang_dashboard.xlf | my_extension.backend.dashboard |
Configuration/Sets/Blog/labels.xlf | my_extension.sets.blog |
If two files map to the same domain, such as label.xlf and
locallang_label.xlf, the simplified filename wins. Avoid these conflicts.
TYPO3 14 Fluid And API Syntax
Use domains for v14-only Fluid templates:
<f:translate domain="my_extension.label" key="top" />
<f:translate key="my_extension.label:top" />
{f:translate(domain: 'my_extension.messages', key: 'button.save')}
Use the same combined syntax in PHP:
$languageService->sL('my_extension.label:top');
$languageService->sL('my_extension.messages:button.save');
For TYPO3 13 compatibility, keep:
<f:translate key="top" extensionName="MyExtension" />
<f:translate key="LLL:EXT:my_extension/Resources/Private/Language/label.xlf:top" />
The v14 domain argument takes precedence over extensionName. Inspect domains
with php bin/typo3 language:domain:list when EXT:lowlevel is available.
TYPO3 API First
Use LanguageServiceFactory when a language service must be created explicitly.
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Localization\LanguageServiceFactory;
final readonly class LabelService
{
public function __construct(
private LanguageServiceFactory $languageServiceFactory,
) {}
public function saveLabel(ServerRequestInterface $request): string
{
$languageService = $this->languageServiceFactory->createFromSiteLanguage(
$request->getAttribute('language'),
);
return $languageService->sL(
'LLL:EXT:my_extension/Resources/Private/Language/messages.xlf:button.save',
);
}
}
For backend-only code, $GLOBALS['LANG'] can be used when the backend bootstrap
has initialized it. Keep access wrapped in a helper so the dependency is obvious.
Content Blocks Labels
Content Blocks labels commonly live beside the block in
ContentBlocks/<Type>/<name>/language/labels.xlf. The source file is
labels.xlf; translated files use locale prefixes such as de.labels.xlf.
-
Put editor-facing title and description in the block language file.
-
Use field IDs such as <field>.label and <field>.description.
-
Generate or inspect expected keys before editing:
vendor/bin/typo3 content-blocks:language:generate vendor/block --print
vendor/bin/typo3 content-blocks:language:generate vendor/block --extension=my_extension
-
Remember that labels.xlf overrides inline labels from config.yaml.
-
Convert generated v14-only catalogs to XLIFF 2.0 after the key list is known.
Detailed Reference
Read the full guide when the task needs detailed examples, long templates, troubleshooting matrices, appendices, or sections not included above. Keep this file unloaded for narrow tasks so the skill follows progressive disclosure.