# Multilingual The multilingual feature builds on `plone.app.multilingual` and adds a translation-aware `@translations` endpoint, a "closest translation" fallback so language switching never dead-ends, content-type constraint checks when creating translations, protection for language root folders, and an optional fixed backend editing language. ## Overview Content lives under per-language root folders (for example `/Plone/de` and `/Plone/en`). Each translation group links the language variants of a content item. The frontend uses the `@translations` endpoint to render a language switcher that always points the visitor at the best available target in each language. ## REST API ### GET @translations The `@translations` endpoint returns the available languages for the current content, with a resolved URL for each. For every supported site language it returns the direct translation if one exists, otherwise the **closest** translation: it walks up the parent chain of the translated branch and returns the nearest translated ancestor, falling back to the language root or site root. This guarantees every language entry has a usable URL. ```http GET /Plone/de/some-page/@translations HTTP/1.1 Host: localhost:8080 Accept: application/json ``` ```json { "items": [ { "@id": "http://localhost:8080/Plone/de/some-page", "language": "de", "current": true, "name": "German", "native": "Deutsch" }, { "@id": "http://localhost:8080/Plone/en", "language": "en", "current": false, "name": "English", "native": "English" } ], "root": { "de": "http://localhost:8080/Plone/de", "en": "http://localhost:8080/Plone/en" } } ``` Each item carries: - `@id` -- the resolved URL for that language (direct or closest translation). - `language` -- the language code. - `current` -- whether this is the language of the current content. - `name` / `native` -- the English and native display names of the language. The `root` mapping gives the translation URLs of the current language root folder, useful for a top-level language switcher independent of the current page. Targets are filtered by view permission, so anonymous users only receive languages they may actually view. ```javascript const response = await fetch('/Plone/de/some-page/@translations', { headers: { 'Accept': 'application/json' } }); const data = await response.json(); data.items.forEach(lang => { console.log(lang.language, lang.current, lang['@id']); }); ``` ### Inline expansion `@translations` is also an expandable component. Request it inline with `?expand=translations`: ```javascript const response = await fetch('/Plone/de/some-page?expand=translations', { headers: { 'Accept': 'application/json' } }); const data = await response.json(); const translations = data['@components']['translations']; console.log(translations.items); ``` ## Translation management Creating translations goes through `plone.app.multilingual`, with two behavioral additions: - **Content-type constraints are enforced.** When a translation is requested for a language, the target language folder is checked for whether it allows the content type. If the type is not addable there, the request is refused with an explanatory status message instead of creating an orphaned object. - **Language root folders are protected.** The language root folders themselves cannot be disconnected from their translation group; the disconnect action is hidden for them and blocked server-side. Regular content can still be connected and disconnected normally. ## Fixed backend language By default the editing UI follows the content language. The registry record `wcs.backend.backend_language` lets you pin a fixed language for authenticated editors regardless of the content language they are viewing. `wcs.backend.backend_language` A language code (for example `de`). When set and present in the site's available languages, logged-in users (those carrying the authentication cookie) get the editing interface in this language while anonymous visitors continue to be served in the content's own language. Empty by default, meaning the standard per-content negotiation applies.