---
title: "Custom translation servers"
manual: "TYPO3 Explained"
version: "12.4"
permalink: "https://docs.typo3.org/permalink/t3coreapi:custom-translation-server@12.4"
source: "ApiOverview/Localization/TranslationServer/Custom.rst"
rendered: "2026-10-21T15:29:37+00:00"
---

> [!WARNING]
> **Attention**
>
> TYPO3 v12 has reached end-of-life as of April 30th 2026 and
> is no longer being maintained. Use the version switcher on the top
> left of this page to select documentation for a supported version of TYPO3.
>
> Need more time before upgrading?
> You can purchase Extended Long Term Support (ELTS) for TYPO3 v12 here:
> [TYPO3 ELTS](https://typo3.com/services/extended-support-elts).

# Custom translation servers {#custom-translation-server}

With the usage of [XLIFF](https://docs.typo3.org/permalink/t3coreapi:xliff@12.4) and the freely available [Pootle](http://pootle.translatehouse.org/)
translation server, companies and individuals may easily set up a custom
translation server for their extensions.

The event [ModifyLanguagePackRemoteBaseUrlEvent](https://docs.typo3.org/permalink/t3coreapi:modifylanguagepackremotebaseurlevent@12.4) can be caught to change
the translation server URL. The first step is to register your custom listener
for the event. Such code would be placed in an extension's `Services.yml`
file:

**EXT:my_extension/Configuration/Services.yaml**

```yaml
services:
  # Place here the default dependency injection configuration

  MyVendor\MyExtension\EventListener\CustomMirror:
    tags:
      - name: event.listener
        identifier: 'my-extension/custom-mirror'

```

Read [how to configure dependency injection in extensions](https://docs.typo3.org/permalink/t3coreapi:dependency-injection-in-extensions@12.4).

The class (listener) that receives the event might look something like this:

**EXT:my_extension/Classes/EventListener/CustomMirror.php**

```php
<?php

declare(strict_types=1);

namespace MyVendor\MyExtension\EventListener;

use TYPO3\CMS\Core\Http\Uri;
use TYPO3\CMS\Install\Service\Event\ModifyLanguagePackRemoteBaseUrlEvent;

final class CustomMirror
{
    private const EXTENSION_KEY = 'my_extension';
    private const MIRROR_URL = 'https://example.org/typo3-packages/';

    public function __invoke(ModifyLanguagePackRemoteBaseUrlEvent $event): void
    {
        if ($event->getPackageKey() === self::EXTENSION_KEY) {
            $event->setBaseUrl(new Uri(self::MIRROR_URL));
        }
    }
}

```

In the above example, the URL is changed only for a given extension, but of
course it could be changed on a more general basis.

On the custom translation server side, the structure needs to be:

```text
https://example.org/typo3-packages/
`-- <first-letter-of-extension-key>
    `-- <second-letter-of-extension-key>
        `-- <extension-key>-l10n
            |-- <extension-key>-l10n-de.zip
            |-- <extension-key>-l10n-fr.zip
            `-- <extension-key>-l10n-it.zip
```

hence in our example:

```text
https://example.org/typo3-packages/
`-- m
    `-- y
        `-- my_extension-l10n
            |-- my_extension-l10n-de.zip
            |-- my_extension-l10n-fr.zip
            `-- my_extension-l10n-it.zip
```
