---
title: "Writing records from the frontend"
manual: "TYPO3 Explained"
version: "main"
permalink: "https://docs.typo3.org/permalink/t3coreapi:extbase-localisation-writing@main"
source: "ExtensionArchitecture/Extbase/Localization/Writing.rst"
rendered: "2026-09-26T10:15:30+00:00"
---

# Writing records from the frontend {#extbase-localisation-writing}

Everything about reading records applies to plugins that only display data.
As soon as a plugin writes — a registration form, a comment, a submitted
conference proposal — the language question changes shape: not "which records
do I get back", but "what language does the record I just created belong to".

## New records are created in the default language {#extbase-localisation-writing-default}

When Extbase persists a new object it fills in the language fields itself:

-   The language field is set to `0`, the default language, unless the
    object already carries a language of its own.
-   The translation parent field is set to `0`.

A record submitted by a visitor browsing the Polish version of a site is
therefore stored as a default-language record, not as a Polish one. Setting the
language explicitly on the object before persisting it stores the record in
that language:

**EXT:my_extension/Classes/Controller/ConferenceController.php**

```php
<?php

namespace MyVendor\MyExtension\Controller;

use MyVendor\MyExtension\Domain\Model\Conference;
use MyVendor\MyExtension\Domain\Repository\ConferenceRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Extbase\DomainObject\AbstractDomainObject;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;

class ConferenceController extends ActionController
{
  public function __construct(
    protected readonly ConferenceRepository $conferenceRepository,
    protected readonly Context $context,
  ) {}

  public function createAction(Conference $conference): ResponseInterface
  {
    $languageId = $this->context
      ->getPropertyFromAspect('language', 'contentId');
    $conference->_setProperty(
      AbstractDomainObject::PROPERTY_LANGUAGE_UID,
      $languageId,
    );

    $this->conferenceRepository->add($conference);

    return $this->redirect('list');
  }
}

```

That is enough to file a record under a language. It is not enough to make it
a translation.

## Extbase does not create translations {#extbase-localisation-writing-translations}

A translation is a record that points at the record it translates, through the
translation parent field. Extbase always writes `0` into that field when
it creates a record, and never writes anything else into it.

The consequence is that Extbase can create a record *in* a language, but it
cannot create a record that is *a translation of* another record. Two records
written this way — one with the language field set to English, one to Polish —
remain two unrelated records. Nothing connects them, and no overlay will ever
combine them.

Editing an existing translation does work: when a translated record is fetched
and modified, Extbase writes the changes back to the translated record rather
than to its default-language original.

## What this means for a multilingual site {#extbase-localisation-writing-consequences}

On a site using `fallbackType: strict`, records without a translation are not
shown. Records created through a frontend form are default-language records
without a translation. A visitor who submits a conference proposal on the
Polish site will therefore not see it on the Polish site afterwards — it
exists, but the language configuration hides it until somebody actually relates
it to its default language parent and so translates it.

On `fallback` the same record stays visible in every language, because
untranslated records show through from the default language.

## When to use something else {#extbase-localisation-writing-alternatives}

Extbase persistence is a good fit for frontend writes when the object is
structurally simple: a single record, no relations to create alongside it, and
no translation involved. Registration forms, contact requests and comments are
all comfortably within that.

Beyond it, other tools are a better fit:

-   The [DataHandler](https://docs.typo3.org/permalink/t3coreapi:tce-database-basics@main) creates real
    translations, maintains relations and respects workspaces and record
    history. Anything that has to produce a translated record belongs here.
-   [`typo3/cms-form`](https://packagist.org/packages/typo3/cms-form) covers form-shaped work — building,
    validating and processing forms — without hand-writing a controller for
    it.
-   A mixture is often the right answer: Extbase for reading and for the
    simple write, the DataHandler for the step that creates or updates a
    translation.

**What this means for relations**

Relations are where frontend writing most often outgrows Extbase.

Extbase can persist relations of an object it creates, but it does so with the
same limitation as everywhere else: the related records are written as
default-language records too, and no translation relationship is established
between them and anything else. An object graph created from a frontend form
is therefore entirely default-language, however the form was labeled. The
exception is a language you set explicitly on each object before persisting
it, which is the developer's responsibility. Even then, Extbase alone cannot
create a valid translation record connected to its default language record.

Where a frontend form has to create or modify a translated object together
with its relations, the DataHandler is the appropriate tool, rather than
Extbase persistence with corrections applied afterwards.
