# Languages and translations

## Starter languages are not a fixed limit

The theme declares its initial languages. These do not limit the languages a customer can later add in the site-wide template settings. GrowSite Skeleton starts with English, Polish and Dutch.

Storefront strings and editor labels serve different audiences. For example, store starter storefront strings in `locales/pl.default.json` and `locales/en.json`. Store schema labels in `locales/pl.default.schema.json` and `locales/en.schema.json`. Set `defaultLocale` to `pl` in the manifest for this example.

Storefront Polish dictionary:

```json
{"footer":{"rights":"Wszelkie prawa zastrzeżone."}}
```

English dictionary:

```json
{"footer":{"rights":"All rights reserved."}}
```

Render the key:

```html
<footer>© [[ date(&#x27;now&#x27;, &#x27;%Y&#x27;) ]] [[ site.name ]]. [[ t(&#x27;footer.rights&#x27;) ]]</footer>
```

Use the current site's name instead of a hardcoded example brand.

## Translate the inspector

Schema dictionary:

```json
{"sections":{"hero":"Hero section"},"settings":{"title":"Heading"}}
```

Reference it from a section schema:

```json
{"name":"t:sections.hero","settings":[{"type":"text","id":"title","label":"t:settings.title","default":"Welcome"}]}
```

A `t:` prefix is a schema translation reference. The `t` Loom filter is for storefront markup. Neither translates arbitrary customer-entered content automatically.

## Fallback and testing

Lookup checks the requested language, then the manifest default language, then returns the key. The current implementation does not provide parameter interpolation or pluralization. Keep dictionary values as complete strings.

Check both the editor and public page in each language. Remove a nondefault-language key to verify fallback. Do not assume switching the documentation portal language changes the customer site's locale. The workshop uses bilingual initial content deliberately; replace it with your theme's intended content strategy.


## Languages plugin and customer settings

Declare multilingual support in `loom.json`:

```json
{
  "plugins": { "languages": "required", "blog": "optional" },
  "languages": { "locales": ["en", "pl", "nl"], "default": "en" },
  "defaultLocale": "en"
}
```

Use `optional` instead of `required` when the theme can work without Languages. Omitting the Languages declaration means no multilingual support. Languages is free for every hosting plan. Required and optional plugins appear in template details and are checked before activation.

The customer can add languages in the builder’s site-wide template settings, choose the default language, and remove non-default languages. Up to 50 languages are supported, using language codes such as `de`, `fr`, `es`, `pt-BR`, `fil` or `zh-Hant`. The choices are stored with the website, not by editing the theme package:

```json
{
  "theme_settings": {
    "languages": { "locales": ["en", "pl", "nl", "de"], "default": "en" }
  }
}
```

Language tabs in the inspector, live text editor and multilingual forms use the site's configured languages. Each language keeps its own text while sharing the section structure and design. Removing a language disables its public URLs but retains its translations, so adding it again restores them. Publish the draft to make language settings and translations public.

Public addresses use the language prefix: `https://example.growsite.nl/de/home` (locally `http://example.growsite.test/de/home`). When an address has no language prefix, GrowSite selects a supported language from the browser/device’s `Accept-Language` preferences, respecting their priority. Regional variants also match the base language (for example, `pl-PL` matches `pl`). If no supported language matches, or the browser sends no preference, the configured default language is the fallback. This is a temporary redirect; it is not permanently cached. An explicit `/pl/...` or `/nl/...` URL always wins over device preferences. Switching languages preserves the page slug.

## Theme-owned language switcher

Every theme declaring Languages as required or optional must provide its own language switcher styling. Match the theme's typography, colors, surfaces, borders and corner radii. Include active, hover and keyboard-focus states and a usable mobile layout. Keep it usable when customers add more languages; do not hardcode EN, PL and NL into the markup.

Place the shared control in the theme header:

```html
<gs-languages variant="dropdown"></gs-languages>
```

The dropdown uses native `details` and `summary` with language links, not a native select. It displays the current language and supports keyboard activation, Escape to close and closing when focus leaves. Without `variant="dropdown"`, `gs-languages` renders the language links directly. Both variants use the site's configured languages and the existing locale-change behavior in the builder.

Provide CSS in `assets/languages.css` for `.gs-language-dropdown`, its `summary`, `.gs-language-switch` and `.gs-language-switch a[aria-current='true']`. CSS under `assets/` is scoped to the theme by GS Loom. Do not add global switcher styles that change other themes. Keep the control reachable on every multilingual page and make long language lists scroll inside the dropdown.

GrowSite Skeleton supplies a rounded Instant-style dropdown with dark surfaces and a yellow accent in its `assets/languages.css`. The complete implementation is in the [Skeleton package](/downloads/growsite-skeleton.zip). Paylio and Victorie Vending currently do not declare Languages support; adding it requires both the plugin declaration and a switcher styled for that theme.

This is a theme authoring and review requirement; the compiler does not assess the visual quality of CSS. Before approving a theme, test the control on desktop and mobile, using keyboard navigation and several customer-added languages. Check both required and optional plugin configurations.

## Translate with the AI assistant

The builder assistant offers **Fill missing translations** and **Translate again**. Select a source language, a target language already added to the website, and either the current page or the whole site. Whole-site translation includes editable section text, page titles, SEO text, menus and global template text. It does not change URLs, identifiers, colors or layout. Plugin content stored outside the builder snapshot is edited through that plugin's multilingual editor.

Fill mode preserves existing translation entries, including deliberately empty entries and bundled demo translations. Translate-again mode replaces target-language text within the selected scope. The source language and other languages remain unchanged.

The assistant returns a reviewable proposal with before/after text. Nothing is published automatically: apply the proposal, then publish the draft. Existing AI access, terms, usage limits and model settings also apply to translations. If a large website exceeds the generation timeout, translate one page at a time. Package dictionary keys used by `t(...)` remain developer-authored; AI translates editable customer content, not theme source files.


See [GS Loom UI components](/en/components/) for the shared HTML contract, component variants, theme tokens and working examples.
