' },
}}
/>
### `createTranslation(client)` [#createtranslationclient]
`createTranslation(client: RuntimeClient): Translation` builds the same reactive object without a provider. Call it inside a reactive owner (a component or `createRoot`): it subscribes to the client and unsubscribes when the owner is cleaned up.
### Other exports [#other-exports]
- `TransglotContext`: the Solid context the provider fills.
- Types: `TransglotProviderProps`, `TProps`, `Translation`.
## Errors [#errors]
The provider does not catch a failed first load, and `setLocale` rejects with the runtime's `RuntimeError` (see [Errors](https://transglot.ai/docs/developers/sdks/javascript#errors)). To handle the first load, create the client yourself and track the load with a resource:
```tsx title="src/index.tsx"
import { TransglotProvider, createClient } from '@transglot/solid';
import { ErrorBoundary, Show, createResource } from 'solid-js';
import { render } from 'solid-js/web';
import { App } from './App';
const client = createClient({
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: 'taicdn_your_key',
locale: 'en',
authIn: 'query',
});
render(() => {
const [ready] = createResource(() => client.loadLocale('en').then(() => true));
return (
Translations could not load: {error.slug}
}>
);
}, document.getElementById('root')!);
```
## Related [#related]
- [JavaScript runtime](https://transglot.ai/docs/developers/sdks/javascript)
- [Runtime SDKs overview](https://transglot.ai/docs/developers/sdks)
# Svelte (https://transglot.ai/docs/developers/sdks/svelte/)
`@transglot/svelte` connects [`@transglot/runtime`](https://transglot.ai/docs/developers/sdks/javascript) to Svelte 5. It puts the client in component context and gives you a `t` store, so `{$t('key')}` re-renders when the locale changes or a new publish lands for the active locale. For SvelteKit with server rendering, use [`@transglot/sveltekit`](https://transglot.ai/docs/developers/sdks/sveltekit), which builds on this package.
## Install [#install]
```npm
npm install @transglot/svelte
```
Svelte 5 is a peer dependency. `@transglot/runtime` is installed with the package, and `createClient`, `RuntimeError` and the `RuntimeClient` and `ClientOptions` types are re-exported from it.
## Minimal setup [#minimal-setup]
Set the context once, in your root component:
```svelte title="src/App.svelte"
```
Read it in any component below. `t` and `locale` are Svelte stores, so use the `$` prefix:
```svelte title="src/Header.svelte"
{$t('home.title')}
{$t('cart.items', { count: 3 })}
{$t('home.greeting', { name: 'Ada' })}
```
`setTransglotContext` starts loading the initial locale and does not wait for it. Until the bundle arrives, `$t` returns keys; markup re-renders when it lands.
Pass `authIn: 'query'` when the client runs in a browser. The CDN does not answer CORS preflight requests, which also limits revalidation from a browser. See [In a browser](https://transglot.ai/docs/developers/sdks/javascript#in-a-browser).
## API [#api]
### `setTransglotContext(source)` [#settransglotcontextsource]
`setTransglotContext(source: TransglotSource): TranslationStore`
Call it while a component initializes, as with Svelte's `setContext`. `source` is either the runtime [`ClientOptions`](https://transglot.ai/docs/developers/sdks/javascript#options) or `{ client }` with a client you created. It creates or adopts the client, puts a `TranslationStore` in context, starts loading the initial locale, and returns the store so the same component can use it.
### `useTranslations()` [#usetranslations]
`useTranslations(): TranslationStore`
Call it while a component initializes. Throws `[transglot] useTranslations() must be called under a component that ran setTransglotContext().` when no ancestor set the context.
) => string>',
},
locale: {
description: 'A store holding the active locale.',
type: 'Readable',
},
setLocale: {
description: 'Load a locale, then switch to it.',
type: '(locale: string) => Promise',
},
client: {
description: 'The underlying runtime client.',
type: 'RuntimeClient',
},
}}
/>
### `createTranslationStore(client)` [#createtranslationstoreclient]
`createTranslationStore(client: RuntimeClient): TranslationStore` wraps a client in the same stores without touching context, for when you manage context yourself or use the stores outside components. The store subscribes to the client when it gets its first subscriber and unsubscribes when the last one leaves.
Types: `TransglotSource`, `TranslationStore`, `TranslateFn`.
## Errors and server rendering [#errors-and-server-rendering]
`setTransglotContext` does not catch a failed first load, and `setLocale` rejects with the runtime's `RuntimeError` (see [Errors](https://transglot.ai/docs/developers/sdks/javascript#errors)). To handle load errors, create the client, wait for the locale, then pass `{ client }`:
```svelte title="src/App.svelte"
{#await ready then}
{:catch error}
Translations could not load: {error.slug}
{/await}
```
The context does not wait for translations, so server-rendered markup from this package contains keys. In SvelteKit, [`@transglot/sveltekit`](https://transglot.ai/docs/developers/sdks/sveltekit) loads the bundle in a `load` function and seeds the context before rendering.
## Related [#related]
- [SvelteKit](https://transglot.ai/docs/developers/sdks/sveltekit)
- [JavaScript runtime](https://transglot.ai/docs/developers/sdks/javascript)
# SvelteKit (https://transglot.ai/docs/developers/sdks/sveltekit/)
`@transglot/sveltekit` fetches a locale's published bundle in a SvelteKit `load` function and seeds the [`@transglot/svelte`](https://transglot.ai/docs/developers/sdks/svelte) context from it synchronously. Server-rendered markup contains translations, and hydration does not fetch the seeded locale again.
It needs SvelteKit 2 and Svelte 5.
## Install [#install]
```npm
npm install @transglot/sveltekit
```
`@transglot/svelte` and `@transglot/runtime` are installed with it, and the package re-exports what you need from both, so you import everything from `@transglot/sveltekit`.
## Setup [#setup]
Add your CDN key to `.env` as `TRANSGLOT_CDN_KEY`, then load the snapshot in a server `load` function:
```ts title="src/routes/[locale]/+layout.server.ts"
import { TRANSGLOT_CDN_KEY } from '$env/static/private';
import { loadTranslations } from '@transglot/sveltekit';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async (event) => ({
snapshot: await loadTranslations(event, {
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: TRANSGLOT_CDN_KEY,
locale: event.params.locale,
}),
});
```
Seed the context in the matching layout component:
```svelte title="src/routes/[locale]/+layout.svelte"
{@render children()}
```
Translate in any component below it:
```svelte title="src/routes/[locale]/+page.svelte"
{$t('home.title')}
{$t('home.greeting', { name: 'Ada' })}
```
The CDN key stays on the server: the snapshot carries the bundle and its headers, not the key.
Load the snapshot in `+layout.server.ts` or `+page.server.ts`. In a universal `+layout.ts`, the key ends up in your client code, the load runs in the browser on client-side navigation (where the CDN's CORS rules apply, see [In a browser](https://transglot.ai/docs/developers/sdks/javascript#in-a-browser)), and during server rendering SvelteKit refuses to let the loader read the `etag`, `x-cdn-format` and `x-cdn-version` response headers unless your `handle` hook allows them with `filterSerializedResponseHeaders`.
## Switching locale [#switching-locale]
`setTransglotContextFromSnapshot` runs once, when the layout component initializes. SvelteKit keeps a layout mounted when only its route parameters change, so navigating from `/en` to `/fr` does not reseed it. Either:
- reload the page on a locale change, for example with `data-sveltekit-reload` on your language links, or
- seed every locale you serve and switch with `setLocale`, which needs no network request between seeded locales:
```ts title="src/routes/+layout.server.ts"
import { TRANSGLOT_CDN_KEY } from '$env/static/private';
import { loadTranslations } from '@transglot/sveltekit';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async (event) => ({
snapshots: await Promise.all(
['en', 'fr', 'de'].map((locale) =>
loadTranslations(event, {
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: TRANSGLOT_CDN_KEY,
locale,
}),
),
),
});
```
```svelte title="src/routes/+layout.svelte"
{@render children()}
```
Switching to a locale that was not seeded, without a `loadSnapshot`, keeps the current locale and logs a dev warning.
## Freshness [#freshness]
The seeded client does not poll or revalidate. A new publish reaches users when a `load` function runs `loadTranslations` again, which happens on each server render of the route unless you cache it.
## API [#api]
### `loadTranslations(event, options)` [#loadtranslationsevent-options]
`loadTranslations(event: LoadEventLike, options: LoadTranslationsOptions): Promise`
`event` is the `load` event, or any object with a `fetch` function; the loader uses `event.fetch`. It fetches one locale with the runtime client (the same URL, authentication, `429` retries and error mapping) and returns the raw body with its headers.
`TranslateSnapshot` has the same fields as in the Next.js package: `project`, `locale`, `baseUrl`, `format`, `version`, `etag` and `bundle`. See [TranslateSnapshot](https://transglot.ai/docs/developers/sdks/nextjs#translatesnapshot).
### `setTransglotContextFromSnapshot(options)` [#settransglotcontextfromsnapshotoptions]
`setTransglotContextFromSnapshot(options: CreateSeededClientOptions): TranslationStore`
Call it while a component initializes. It builds a seeded client, sets the `@transglot/svelte` context with it, and returns the store (`{ t, locale, setLocale, client }`).
Promise' },
dev: { description: 'Log console warnings for missing keys and locales that were not seeded.', type: 'boolean', default: "true unless NODE_ENV is 'production'" },
}}
/>
### `createSeededClient(options)` [#createseededclientoptions]
`createSeededClient(options: CreateSeededClientOptions): RuntimeClient` returns the seeded client on its own, with the same options. Its `refresh()` does nothing for a locale it holds, `hydrate(locale?)` resolves `true` for a seeded locale, and `stop()` does nothing.
### Re-exports [#re-exports]
- From `@transglot/svelte`: `setTransglotContext`, `useTranslations`, `createTranslationStore`, and the types `TransglotSource`, `TranslationStore`, `TranslateFn`. See [Svelte](https://transglot.ai/docs/developers/sdks/svelte#api).
- From `@transglot/runtime`: `createClient`, `RuntimeError`, `parseBundle`, `interpolate`, and the types `RuntimeClient`, `ClientOptions`.
- Types: `LoadTranslationsOptions`, `LoadEventLike`, `TranslateSnapshot`, `CreateSeededClientOptions`.
## Errors [#errors]
`loadTranslations` rejects with the runtime's `RuntimeError`; the slugs are listed on the [JavaScript runtime](https://transglot.ai/docs/developers/sdks/javascript#errors) page. Map it to a SvelteKit error in your `load` function:
```ts title="src/routes/[locale]/+layout.server.ts"
import { TRANSGLOT_CDN_KEY } from '$env/static/private';
import { RuntimeError, loadTranslations } from '@transglot/sveltekit';
import { error } from '@sveltejs/kit';
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async (event) => {
try {
return {
snapshot: await loadTranslations(event, {
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: TRANSGLOT_CDN_KEY,
locale: event.params.locale,
}),
};
} catch (err) {
if (err instanceof RuntimeError && err.slug === 'not-found') {
error(404, 'Unknown locale');
}
throw err;
}
};
```
Seeding throws `Error('[transglot] createSeededClient() requires at least one snapshot.')` for an empty array, and a `RuntimeError` with the slug `unsupported-format` when the project's delivery format is not one of `json_flat`, `json_nested`, `laravel_json` or `flutter_arb`.
## Related [#related]
- [Svelte](https://transglot.ai/docs/developers/sdks/svelte)
- [JavaScript runtime](https://transglot.ai/docs/developers/sdks/javascript)
- [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale)
# Vue (https://transglot.ai/docs/developers/sdks/vue/)
`@transglot/vue` connects [`@transglot/runtime`](https://transglot.ai/docs/developers/sdks/javascript) to Vue 3.3 or later. Every `t(...)` call in a template re-renders when the locale changes or a new publish lands for the active locale.
## Install [#install]
```npm
npm install @transglot/vue
```
Vue is a peer dependency. `@transglot/runtime` is installed with the package, and `createClient`, `RuntimeError` and the `RuntimeClient` and `ClientOptions` types are re-exported from it.
## Minimal setup [#minimal-setup]
Install the plugin with the runtime client options:
```ts title="src/main.ts"
import { Transglot } from '@transglot/vue';
import { createApp } from 'vue';
import App from './App.vue';
createApp(App)
.use(Transglot, {
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: 'taicdn_your_key',
locale: 'en',
authIn: 'query',
})
.mount('#app');
```
Then translate in any component:
```vue title="src/components/AppHeader.vue"
{{ t('home.title') }}
{{ t('cart.items', { count: 3 }) }}
```
The plugin starts loading the initial locale when you install it and does not wait for it. Until the bundle arrives, `t` returns keys; templates re-render when it lands.
Pass `authIn: 'query'` when the client runs in a browser. The CDN does not answer CORS preflight requests, which also limits revalidation from a browser. See [In a browser](https://transglot.ai/docs/developers/sdks/javascript#in-a-browser).
## API [#api]
### `app.use(Transglot, options)` [#appusetransglot-options]
`options` is either the runtime [`ClientOptions`](https://transglot.ai/docs/developers/sdks/javascript#options), from which the plugin creates the client, or `{ client }` with a client you created. The plugin:
- provides the client to the app,
- registers `T` as a global component,
- adds `$t` as a global property for the Options API,
- starts loading the initial locale.
### `useTranslations()` [#usetranslations]
Returns `{ t, locale, setLocale, client }`. Call it in `setup`. Throws `[transglot] useTranslations()/useT() must be used after app.use(Transglot, …).` when the plugin is not installed.
) => string',
},
locale: {
description: 'The active locale. Read .value in script; templates unwrap it.',
type: 'Readonly[>',
},
setLocale: {
description: 'Load a locale, then switch to it.',
type: '(locale: string) => Promise',
},
client: {
description: 'The underlying runtime client.',
type: 'RuntimeClient',
},
}}
/>
### `useT()` [#uset]
Returns only the reactive `t` from `useTranslations()`.
### `T` [#t]
Renders a translation as a bare text node, or inside an element when you pass `tag`.
' },
tag: { description: 'An element to wrap the text in, such as span.', type: 'string' },
}}
/>
### `$t` in the Options API [#t-in-the-options-api]
```vue
]{{ $t('home.title') }}
```
`$t` is reactive like `t`. The package does not declare it for TypeScript; if you use it in typed components, add the declaration yourself:
```ts title="src/transglot.d.ts"
export {};
declare module 'vue' {
interface ComponentCustomProperties {
$t: (key: string, params?: Record) => string;
}
}
```
### Other exports [#other-exports]
- `TRANSGLOT_KEY`: the injection key the plugin provides. Its value is a `TransglotContext` (`{ client, locale, revision }`).
- Types: `TransglotOptions`, `UseTranslations`, `TransglotContext`.
## Errors and server rendering [#errors-and-server-rendering]
The plugin does not catch a failed first load, and `setLocale` rejects with the runtime's `RuntimeError` (see [Errors](https://transglot.ai/docs/developers/sdks/javascript#errors)).
The plugin does not wait for translations, so server-rendered output contains keys. To handle load errors, or to render translated HTML on the server, create the client yourself, wait for the locale, then install the plugin with it:
```ts title="src/main.ts"
import { createClient, RuntimeError, Transglot } from '@transglot/vue';
import { createApp } from 'vue';
import App from './App.vue';
const client = createClient({
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: 'taicdn_your_key',
locale: 'en',
authIn: 'query',
});
try {
await client.loadLocale('en');
} catch (error) {
if (!(error instanceof RuntimeError)) throw error;
console.error(error.slug, error.hint);
}
createApp(App).use(Transglot, { client }).mount('#app');
```
## Related [#related]
- [JavaScript runtime](https://transglot.ai/docs/developers/sdks/javascript)
- [Runtime SDKs overview](https://transglot.ai/docs/developers/sdks)
# Editor and IDE plugins (https://transglot.ai/docs/developers/tools/editor-and-ide-plugins/)
transglot reaches your editor in two ways. Both are npm packages, both authenticate with a [deploy token](https://transglot.ai/docs/developers/deploy-tokens), and both can do only what the token's abilities allow.
| Tool | Package | Works in | What you get |
| --- | --- | --- | --- |
| [Language server](https://transglot.ai/docs/developers/tools/language-server) | `@transglot/lsp` | Neovim, JetBrains IDEs (through LSP4IJ), Helix, Sublime Text, and any editor with an LSP client | Missing translations flagged as diagnostics, translations on hover and in CodeLens, and a code action that turns a hardcoded string into a key. |
| [MCP server](https://transglot.ai/docs/mcp) | `@transglot/mcp` | Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, and any other MCP client | An AI agent that reads and edits translations, syncs files, starts translation runs and manages the glossary. |
## Which one to use [#which-one-to-use]
- **You want to see translations while you edit keys**: the language server. It reads your `transglot.json` and works on JSON message files.
- **You want an assistant to do localization work for you**: the MCP server. Ask it to fill missing translations, check a key in every language, or add a glossary term.
- **You want to sync files from a terminal or CI**: neither. Use the [CLI](https://transglot.ai/docs/developers/cli) or the [GitHub Action](https://transglot.ai/docs/developers/github-action).
## Set up [#set-up]
# Language server (https://transglot.ai/docs/developers/tools/language-server/)
`@transglot/lsp` is a [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) server. Any editor with an LSP client can start it and get the same features, configured once per editor.
## Features [#features]
- **Missing translations as diagnostics.** In a JSON source file that one of your `sources` rules names, every key with no translation in a target locale is flagged: `"cart.title" is missing a translation in: fr, de`.
- **Hover.** Hovering a key shows its translation in every target locale.
- **CodeLens.** A summary line above each key, such as `fr: Bonjour · de: Hallo · es: Hola`.
- **Extract to transglot key.** On a string literal in a `.js`, `.jsx`, `.ts` or `.tsx` file, the code action **Extract to transglot key** adds the string to your messages file under a generated key, replaces the literal with an i18n call such as `t('key')`, and offers **Push key to transglot** to upload the messages file.
Diagnostics, hover and CodeLens work on the JSON formats: `json_flat`, `json_nested`, `laravel_json` and `flutter_arb`. The server downloads each target locale from the project, so it shows what the project holds rather than your local target files.
## Requirements [#requirements]
- **Node.js 20 or later.**
- **A deploy token** with the **pull** ability, plus **push** to upload extracted keys. See [Deploy tokens](https://transglot.ai/docs/developers/deploy-tokens).
- **A `transglot.json`** in the workspace root. The server reads its `sources` rules to know which files are source files. See [Configuration file](https://transglot.ai/docs/developers/cli/configuration).
Version 0.1.0 of the server cannot read a `transglot.json` whose rules use `metadata`, `unity_csv` or `unreal_po`. It then logs `transglot: could not load transglot.json` and shows no diagnostics.
## Install [#install]
```npm
npm install -g @transglot/lsp
```
This puts a `transglot-lsp` command on your `PATH`. It speaks LSP over stdio.
## Settings [#settings]
The server reads its settings from the `initializationOptions` your editor sends when it starts the server. They are read once, at start.
Keep the token out of files you commit. The examples below read it from the `TRANSGLOT_TOKEN` environment variable where the editor allows it.
## Set up your editor [#set-up-your-editor]
### Neovim [#neovim]
Neovim 0.11 and later configure language servers with `vim.lsp.config`:
```lua title="init.lua"
vim.lsp.config('transglot', {
cmd = { 'transglot-lsp' },
filetypes = { 'json', 'javascript', 'javascriptreact', 'typescript', 'typescriptreact' },
root_markers = { 'transglot.json', '.git' },
init_options = {
serverUrl = 'https://api.transglot.ai',
token = os.getenv('TRANSGLOT_TOKEN'),
messagesFile = 'src/messages/en.json',
i18nFunction = 't',
format = 'json_flat',
},
})
vim.lsp.enable('transglot')
```
Use your usual LSP keymaps: hover with `K`, code actions with `gra`, diagnostics in the location list.
### JetBrains IDEs [#jetbrains-ides]
IntelliJ IDEA, WebStorm, PhpStorm and the other JetBrains IDEs (2023.2 and later) run external language servers through the free [LSP4IJ](https://plugins.jetbrains.com/plugin/23257-lsp4ij) plugin.
### Install LSP4IJ [#install-lsp4ij]
In **Settings** → **Plugins** → **Marketplace**, search for LSP4IJ, install it and restart the IDE.
### Add the server [#add-the-server]
Open **Settings** → **Languages & Frameworks** → **Language Servers**, select **+** and choose **New Language Server**. Set:
- **Name:** `transglot`
- **Command:** `transglot-lsp`
- **Mappings** → **File name patterns:** `*.json`, `*.js`, `*.jsx`, `*.ts`, `*.tsx`
### Add the settings [#add-the-settings]
Under **Configuration** → **Initialization Options**, paste:
```json
{
"serverUrl": "https://api.transglot.ai",
"token": "tgl_xxx",
"messagesFile": "src/messages/en.json",
"i18nFunction": "t",
"format": "json_flat"
}
```
Replace `tgl_xxx` with your token and select **Apply**. LSP4IJ's **LSP Consoles** tool window shows the server's log if nothing appears.
If the IDE cannot find `transglot-lsp`, use the full path that `which transglot-lsp` prints as the command.
### Helix [#helix]
```toml title="languages.toml"
[language-server.transglot]
command = "transglot-lsp"
[language-server.transglot.config]
serverUrl = "https://api.transglot.ai"
token = "tgl_xxx"
messagesFile = "src/messages/en.json"
[[language]]
name = "json"
language-servers = ["vscode-json-language-server", "transglot"]
[[language]]
name = "typescript"
language-servers = ["typescript-language-server", "transglot"]
```
Keep each language's existing servers in its `language-servers` list and add `transglot`.
### Sublime Text [#sublime-text]
With the [LSP](https://packagecontrol.io/packages/LSP) package installed, open **Preferences** → **Package Settings** → **LSP** → **Settings** and add a client:
```json title="LSP.sublime-settings"
{
"clients": {
"transglot": {
"enabled": true,
"command": ["transglot-lsp"],
"selector": "source.json | source.js | source.jsx | source.ts | source.tsx",
"initializationOptions": {
"serverUrl": "https://api.transglot.ai",
"token": "tgl_xxx",
"messagesFile": "src/messages/en.json"
}
}
}
}
```
### Other editors [#other-editors]
Any LSP client that can start a stdio server works: run `transglot-lsp` and pass the [settings](#settings) as initialization options.
## Commands [#commands]
The server registers `transglot.pushKey`, which pushes `messagesFile` to the project. **Extract to transglot key** offers it as **Push key to transglot** after it writes a key, and when it succeeds the editor shows how many keys were created.
## Troubleshooting [#troubleshooting]
Messages from the server appear in your editor's LSP log:
| Message | Cause |
| --- | --- |
| `transglot: not connected. Set serverUrl and token in the LSP initializationOptions.` | One of the two required settings is missing. |
| `transglot: could not load transglot.json: ...` | The file is missing from the workspace root, is invalid, or uses a field or format version 0.1.0 does not read. |
| `transglot: pull fr failed: ...` | The token lacks **pull**, or the locale is not a language of the project. |
# Create a webhook endpoint (https://transglot.ai/docs/developers/webhooks/create-a-webhook-endpoint/)
You need to be an Admin of the workspace or an Owner of its account, on the Starter plan or higher. Build and deploy the receiving endpoint first, so it can answer the first delivery.
## In the console [#in-the-console]
### Open the project's developer settings [#open-the-projects-developer-settings]
Open the project, then **Settings** → **Developer**. The **Webhooks** section lists the project's endpoints.
### Start a new endpoint [#start-a-new-endpoint]
Select **New endpoint**. The **Create webhook endpoint** dialog opens.
### Enter the URL [#enter-the-url]
Enter the **Endpoint URL**, for example `https://ci.example.com/hooks/transglot`. It must be an HTTPS URL on the default port (443) whose host resolves to a public address.
### Choose the events [#choose-the-events]
Under **Events**, tick each event the endpoint should receive. They are grouped as **Batches**, **Keys**, **Translations**, **Project** and **Quality**. The [event reference](https://transglot.ai/docs/developers/webhooks/events) says when each one fires.
### Create it and copy the secret [#create-it-and-copy-the-secret]
Select **Create endpoint**. **Copy your signing secret** appears with the secret, which starts with `whsec_`. Select **Copy** and store it where your receiver can read it, for example as an environment variable. It is shown only once and cannot be recovered later.
The endpoint is now listed with its **Events**, when it **Last delivered**, an **Active** switch, and **Deliveries** and **Delete** actions.
There is no way to show it again. Delete the endpoint and create a new one, then update your receiver with the new secret.
## With the API [#with-the-api]
`POST /v1/webhooks` creates an endpoint with a deploy token that has the `manage` ability. The response is the only place the secret appears.
```bash title="Create an endpoint"
curl https://api.transglot.ai/v1/webhooks \
-H "Authorization: Bearer $TRANSGLOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"url": "https://ci.example.com/hooks/transglot",
"events": ["batch.completed", "batch.failed"]
}'
```
```json title="201 Created"
{
"id": 8,
"url": "https://ci.example.com/hooks/transglot",
"events": ["batch.completed", "batch.failed"],
"active": true,
"created_at": "2026-10-12T10:02:19+00:00",
"last_delivered_at": null,
"secret": "whsec_Zq8mB3vN6xR1tY4uI7oP0aS2dF5gH8jK9lQ3wE6r"
}
```
`GET /v1/webhooks` lists the project's endpoints (never their secrets), and `DELETE /v1/webhooks/{id}` removes one. `GET /v1/webhooks/events` returns the event catalog with a representative payload for each event, which is useful for building a field mapper. See the [API reference](https://transglot.ai/docs/api-reference).
## Pause or delete an endpoint [#pause-or-delete-an-endpoint]
- **Pause.** Turn the **Active** switch off. Events stop being sent to it until you switch it back on. Events that happen while it is off are not sent later.
- **Delete.** Select **Delete**, then **Delete endpoint**. The endpoint stops receiving deliveries immediately, and events that fire while it is gone are not queued or replayed.
## If it does not work [#if-it-does-not-work]
| What you see | Why |
| --- | --- |
| `The webhook URL must use https.` | The URL is `http://`. Only HTTPS is accepted |
| `The webhook URL port 8443 is not allowed.` | Only port 443 is accepted |
| `The webhook URL resolves to a private or reserved address.` | The host resolves to a private, loopback or otherwise internal address. Endpoints must be publicly reachable |
| `The webhook URL host could not be resolved.` | The host name has no DNS record |
| **Paused: the account plan does not include webhooks.** | The account's plan no longer includes webhooks. Endpoints are kept, and deliveries resume after an upgrade |
| **Deliveries are paused.** | The workspace cannot send data out right now. An account owner can see why in the console |
Through the API, each of the first four comes back as `422` with the type `/problems/webhook-url-not-allowed` and that sentence as `detail`.
Once deliveries are flowing, the endpoint's delivery log shows each attempt and what your endpoint answered. See [Retries and redelivery](https://transglot.ai/docs/developers/webhooks/retries-and-redelivery).
## Related [#related]
- [Verify signatures](https://transglot.ai/docs/developers/webhooks/verify-signatures)
- [Event reference](https://transglot.ai/docs/developers/webhooks/events)
- [Project developer settings](https://transglot.ai/docs/developers/project-developer-settings)
# Event reference (https://transglot.ai/docs/developers/webhooks/events/)
An endpoint receives only the events it subscribed to. The event name arrives in the `X-Transglot-Event` header and again as `event` in the body.
| Event | Fires when | Delivery |
| --- | --- | --- |
| [`batch.completed`](#batchcompleted) | A translation run finished: `completed` or `completed_with_errors` | Once per run |
| [`batch.failed`](#batchfailed) | A translation run ended `failed` or `cancelled` | Once per run |
| [`key.created`](#keycreated) | Keys were created | Coalesced, 30 s |
| [`key.updated`](#keyupdated) | Keys changed name, description, plural flag or length limit | Coalesced, 30 s |
| [`key.deleted`](#keydeleted) | Keys were deleted | Coalesced, 30 s |
| [`translation.updated`](#translationupdated) | Translation values were written | Coalesced, 15 s |
| [`translation.reviewed`](#translationreviewed) | Translations were approved | Coalesced, 30 s |
| [`language.added`](#languageadded) | A target language was added | Once |
| [`language.removed`](#languageremoved) | A target language was removed | Once |
| [`glossary.updated`](#glossaryupdated) | Glossary terms changed | Coalesced, 30 s |
| [`qa.completed`](#qacompleted) | A quality check finished scoring translations | Coalesced, 60 s |
| [`source_drift.repaired`](#source_driftrepaired) | Translations were repaired after their source changed | Coalesced, 60 s |
## The envelope [#the-envelope]
Every payload carries these fields:
The examples on this page are formatted for reading. The body you receive is compact JSON and its keys can come in a different order, so read fields by name.
## Coalesced events [#coalesced-events]
A coalesced event is not delivered once per occurrence. The first occurrence opens a window (15, 30 or 60 seconds, depending on the event). Every further occurrence in that window adds to a counter. When the window closes, your endpoint receives one delivery for the whole window, and the next occurrence opens a new window.
`samples` is for display and logging. To act on exactly what changed, use the event as a trigger and fetch the current state, for example with [`GET /v1/pull`](https://transglot.ai/docs/api-reference) or [`POST /v1/translations/batch`](https://transglot.ai/docs/api-reference).
## batch.completed [#batchcompleted]
A translation run reached a successful final state: `completed`, or `completed_with_errors` when some pairs failed. Delivered exactly once per run, whatever started it (a push, the console, `POST /v1/translate`, a language being added, a retry).
`batch` has the same shape as [`GET /v1/batches/{batch}`](https://transglot.ai/docs/api-reference).
```json title="batch.completed"
{
"event": "batch.completed",
"delivered_at": "2026-10-12T09:41:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"batch": {
"id": 4821,
"status": "completed",
"trigger": "push",
"total": 240,
"completed": 240,
"failed": 0,
"skipped": 12,
"skipped_reasons": { "human_value_stale": 12 },
"tm_served": 37,
"language_breakdown": { "fr": 120, "de": 120 },
"started_at": "2026-10-12T09:39:13+00:00",
"finished_at": "2026-10-12T09:41:00+00:00"
}
}
```
## batch.failed [#batchfailed]
A translation run ended `failed`, or was `cancelled` from the console or with `POST /v1/batches/{batch}/cancel`. Delivered exactly once per run, with the same payload shape as `batch.completed`.
```json title="batch.failed"
{
"event": "batch.failed",
"delivered_at": "2026-10-12T09:41:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"batch": {
"id": 4821,
"status": "failed",
"trigger": "push",
"total": 240,
"completed": 191,
"failed": 49,
"skipped": 0,
"skipped_reasons": null,
"tm_served": 37,
"language_breakdown": { "fr": 120, "de": 120 },
"started_at": "2026-10-12T09:39:13+00:00",
"finished_at": "2026-10-12T09:41:00+00:00"
}
}
```
To see which pairs failed and why, call `GET /v1/batches/{batch}?include=failures`.
## key.created [#keycreated]
Keys were created in the console or by a source-locale push. Coalesced over 30 seconds. `count` is the number of keys created; `samples` holds key names, and `push:` for a push.
```json title="key.created"
{
"event": "key.created",
"delivered_at": "2026-10-12T09:50:31+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 14,
"samples": ["push:en"],
"window_seconds": 30
}
```
## key.updated [#keyupdated]
Keys changed name, description, plural flag or length limit: an edit in the console, a bulk rename (sample `bulk-rename`), or a source push whose metadata sidecar changed descriptions or length limits (sample `push-metadata:`). Coalesced over 30 seconds.
```json title="key.updated"
{
"event": "key.updated",
"delivered_at": "2026-10-12T10:12:30+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 2,
"samples": ["checkout.submit", "checkout.error.declined"],
"window_seconds": 30
}
```
## key.deleted [#keydeleted]
Keys were deleted: one at a time in the console (sample: the key name), in bulk (sample `bulk`), or by a source push with `mode=sync_removals` (sample `push:`). Coalesced over 30 seconds.
```json title="key.deleted"
{
"event": "key.deleted",
"delivered_at": "2026-10-12T10:20:04+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 31,
"samples": ["bulk"],
"window_seconds": 30
}
```
## translation.updated [#translationupdated]
Translation values were written. Coalesced over 15 seconds. `count` counts written values only: a pair a translation run left alone, because a person had already edited or reviewed it or because its source moved during the run, writes nothing and is not counted.
| Source of the write | Sample |
| --- | --- |
| An edit in the console editor | `:`, for example `checkout.submit:fr` |
| A push (source or target) | `push:` |
| A translation run | `batch::` |
| Copying the source into a locale in bulk | `bulk-copy-source:` |
```json title="translation.updated"
{
"event": "translation.updated",
"delivered_at": "2026-10-12T09:41:12+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 240,
"samples": ["batch:4821:fr", "batch:4821:de"],
"window_seconds": 15
}
```
A value written with `PUT /v1/translations` does not fire `translation.updated`. If your own integration writes values that way, you already know about the change.
## translation.reviewed [#translationreviewed]
Translations were approved in the review workflow. Coalesced over 30 seconds. `samples` holds `:` for a single approval and `bulk:` for a bulk approval.
```json title="translation.reviewed"
{
"event": "translation.reviewed",
"delivered_at": "2026-10-12T11:02:45+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 37,
"samples": ["checkout.submit:fr", "bulk:de"],
"window_seconds": 30
}
```
## language.added [#languageadded]
A target language was added to the project. Delivered once, as soon as the language exists. A translation run to fill it may follow and reports through the `batch.*` events.
```json title="language.added"
{
"event": "language.added",
"delivered_at": "2026-10-12T12:00:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"language": { "locale": "pt-BR" }
}
```
## language.removed [#languageremoved]
A target language was removed from the project. Every translation in that locale and its version history were deleted with it, and neither can be recovered through the product. Delivered once. React to it by dropping the locale from your builds, your CDN configuration or your CMS.
```json title="language.removed"
{
"event": "language.removed",
"delivered_at": "2026-10-12T12:30:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"language": { "locale": "pt-BR", "translations_deleted": 1204 }
}
```
`language.translations_deleted` tells you whether the locale held content or was an empty placeholder.
## glossary.updated [#glossaryupdated]
Glossary terms were created, edited, approved, rejected, imported or deleted in the console. Coalesced over 30 seconds. `samples` holds source terms, and `import` for a file import, whose `count` is the number of terms it created or updated.
```json title="glossary.updated"
{
"event": "glossary.updated",
"delivered_at": "2026-10-12T13:05:30+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 3,
"samples": ["Checkout", "Acme"],
"window_seconds": 30
}
```
Changes made through the glossary API endpoints do not fire this event.
## qa.completed [#qacompleted]
A quality check finished scoring translations. A check is processed in groups, and the 60-second window collapses a whole check into one delivery. `count` is the number of translations checked. `samples` holds flagged `:` pairs, at most one per group, so an empty `samples` means nothing was flagged. The findings themselves are on the translations: read them with `GET /v1/translations` or in the editor.
```json title="qa.completed"
{
"event": "qa.completed",
"delivered_at": "2026-10-12T14:01:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 74,
"samples": ["checkout.error.declined:de"],
"window_seconds": 60
}
```
## source_drift.repaired [#source_driftrepaired]
Source strings changed, and the translations that depended on them were checked again and repaired, either as a suggestion or applied directly, according to the project's source-drift setting. Coalesced over 60 seconds. `count` is the number of translations repaired; `samples` holds `:` pairs.
```json title="source_drift.repaired"
{
"event": "source_drift.repaired",
"delivered_at": "2026-10-12T15:10:00+00:00",
"project": { "id": 12, "slug": "acme-web" },
"count": 6,
"samples": ["checkout.submit:fr", "checkout.submit:de"],
"window_seconds": 60
}
```
## Related [#related]
- [Verify signatures](https://transglot.ai/docs/developers/webhooks/verify-signatures)
- [Retries and redelivery](https://transglot.ai/docs/developers/webhooks/retries-and-redelivery)
- [Create a webhook endpoint](https://transglot.ai/docs/developers/webhooks/create-a-webhook-endpoint)
# Webhooks (https://transglot.ai/docs/developers/webhooks/)
A webhook endpoint is an HTTPS URL of yours that transglot sends a `POST` to when something happens in a project: a translation run finishes, keys are created, a language is removed. Use webhooks to start a deploy when translations land, rebuild a static site, invalidate a cache or post to a channel, without polling the API.
## How it works [#how-it-works]
- **Per project.** Endpoints belong to one project. A project can have several, each subscribed to its own set of events.
- **You choose the events.** An endpoint receives only the events it subscribed to. There are twelve; see the [event reference](https://transglot.ai/docs/developers/webhooks/events).
- **Signed.** Every delivery carries an `X-Transglot-Signature` header, an HMAC-SHA256 of the timestamp and body keyed with the endpoint's signing secret. [Verify it](https://transglot.ai/docs/developers/webhooks/verify-signatures) before you act on a delivery.
- **Coalesced where it matters.** High-frequency events are counted over a short window and delivered once, carrying a `count` and up to 20 samples. A bulk edit of 5,000 strings becomes one request, not 5,000.
- **Retried.** A delivery that does not get a `2xx` within 10 seconds is retried three more times over about six minutes. Every attempt is in the endpoint's delivery log, where you can redeliver it. See [Retries and redelivery](https://transglot.ai/docs/developers/webhooks/retries-and-redelivery).
## A delivery [#a-delivery]
```http title="POST https://ci.example.com/hooks/transglot"
Content-Type: application/json
User-Agent: transglot-webhooks/1
X-Transglot-Event: batch.completed
X-Transglot-Delivery: 0f6d1c0e-6a4b-4f7e-9a51-3c2b8d7e4a19
X-Transglot-Timestamp: 1791820860
X-Transglot-Signature: sha256=5d1c3e8f0a2b4c6d8e0f1a3b5c7d9e1f3a5b7c9d1e3f5a7b9c1d3e5f7a9b1c3d
{"batch":{"id":4821,"total":240,"failed":0,"status":"completed","skipped":12,"trigger":"push","completed":240,"tm_served":37,"started_at":"2026-10-12T09:39:13+00:00","finished_at":"2026-10-12T09:41:00+00:00","skipped_reasons":{"human_value_stale":12},"language_breakdown":{"de":120,"fr":120}},"event":"batch.completed","project":{"id":12,"slug":"acme-web"},"delivered_at":"2026-10-12T09:41:00+00:00"}
```
Every payload has the same three fields, `event`, `delivered_at` and `project` (`id` and `slug`), plus fields that depend on the event. The body is compact JSON and the order of its keys is not meaningful: read fields by name, and verify the signature against the raw bytes you received.
## Requirements [#requirements]
- **Plan.** Webhooks need the Starter plan or higher. If the account's plan stops including them, deliveries stop until it does again; endpoints are kept.
- **Permission.** Owners and admins create and manage endpoints.
- **A public HTTPS URL.** Endpoints must be publicly reachable HTTPS URLs on port 443; private and internal addresses are refused. The address is checked when you create the endpoint and again before every delivery.
## Get started [#get-started]
## Related [#related]
- [Zapier, Make and n8n](https://transglot.ai/docs/integrations/automation/zapier-make-and-n8n)
- [REST API overview](https://transglot.ai/docs/developers/api)
# Retries and redelivery (https://transglot.ai/docs/developers/webhooks/retries-and-redelivery/)
## What counts as delivered [#what-counts-as-delivered]
A delivery succeeds when your endpoint answers with any `2xx` status within 10 seconds. Everything else is a failed attempt:
- any `3xx`, `4xx` or `5xx` status (redirects are not followed, so a `301` to a new address fails);
- no answer within 10 seconds;
- a connection or TLS error.
Answer first and do slow work afterwards. A receiver that processes the event before responding risks the 10-second limit and a retry of something it already handled.
## The retry schedule [#the-retry-schedule]
Each delivery gets four attempts: the first, then three retries.
| Attempt | When |
| --- | --- |
| 1 | As soon as the event is emitted |
| 2 | 10 seconds after attempt 1 fails |
| 3 | 60 seconds after attempt 2 fails |
| 4 | 5 minutes after attempt 3 fails |
Every attempt of one delivery carries the same body and the same `X-Transglot-Delivery` id, with a fresh `X-Transglot-Timestamp` and `X-Transglot-Signature`. After the fourth failure, the delivery is marked `failed` and is not tried again.
When a delivery fails for good, transglot emails the workspace's developers and admins, **A webhook endpoint is failing**, naming the endpoint, the event and the last error. You get at most one of these a day per endpoint.
Deliveries are not held for later. Once a delivery has failed, the event it carried reaches your endpoint only if you redeliver it from the log. The same is true for events that happen while an endpoint is paused or deleted: they are not sent afterwards.
## Deliveries that are not attempted [#deliveries-that-are-not-attempted]
| Situation | What happens |
| --- | --- |
| The endpoint was paused after the event was emitted | The delivery is marked `failed` without a request |
| The endpoint was deleted | Nothing more is sent, and its delivery log is deleted with it |
| The account's plan no longer includes webhooks, or the workspace cannot send data out | No delivery is created, and pending ones are marked `failed` without a request |
| The endpoint's host now resolves to a private or internal address | The delivery is refused before sending, marked `failed` and not retried. transglot emails **A webhook endpoint was refused** |
Endpoints must be publicly reachable HTTPS URLs; private and internal addresses are refused. The address is checked when the endpoint is created and again immediately before every attempt, so a host whose DNS later points inside a private network is still refused.
## The delivery log [#the-delivery-log]
In the console, open **Settings** → **Developer**, find the endpoint under **Webhooks** and select **Deliveries**. **Recent deliveries** lists every attempt sequence for that endpoint, newest first:
| Column | Shows |
| --- | --- |
| **Event** | The event name |
| **Status** | `pending`, `delivering`, `succeeded` or `failed` |
| **Code** | The HTTP status your endpoint returned on the latest attempt |
| **Attempts** | How many attempts have been made |
| **Next retry** | When the next attempt is due, while retries remain |
| **When** | When it was delivered, or when it was first dispatched |
Filter by **Delivery status** to see only failures, and select **Load more** for older entries.
## Redeliver an event [#redeliver-an-event]
Select **Redeliver** on any entry in the log. transglot queues a new delivery of the same event to the same endpoint and confirms: "Redelivery queued. The new attempt appears at the top of this delivery log."
A redelivery:
- carries **byte-for-byte the same body** as the original, including its original `delivered_at`;
- gets a **new** `X-Transglot-Delivery` id, a fresh timestamp and a fresh signature;
- follows the same retry schedule;
- fails without a request if the endpoint is paused, so switch it back on first.
Redelivery is the quickest way to debug a receiver: fix it, then redeliver the failed entry instead of waiting for the event to happen again.
## Handling duplicates and order [#handling-duplicates-and-order]
Design the receiver to tolerate the same event more than once and events out of order:
- **Duplicates.** A retry repeats the same `X-Transglot-Delivery` id, so store the ids you have processed and skip repeats. A redelivery has a new id; when you redeliver on purpose, you usually want it processed.
- **Order.** Deliveries are sent independently and can arrive in any order. Do not infer state from arrival order: read `batch.status` and `batch.finished_at`, or fetch the current state from the API.
- **Coalesced events.** A `count` covers one window. Two windows of the same event are two separate deliveries.
```js title="Idempotent handler"
const seen = new Set(); // use a database or cache with a TTL in production
export async function handle(deliveryId, payload) {
if (seen.has(deliveryId)) return; // a retry of something already processed
seen.add(deliveryId);
if (payload.event === 'batch.completed') {
await triggerDeploy(payload.project.slug, payload.batch.id); // your own deploy step
}
}
```
## Related [#related]
- [Verify signatures](https://transglot.ai/docs/developers/webhooks/verify-signatures)
- [Event reference](https://transglot.ai/docs/developers/webhooks/events)
- [Create a webhook endpoint](https://transglot.ai/docs/developers/webhooks/create-a-webhook-endpoint)
# Verify signatures (https://transglot.ai/docs/developers/webhooks/verify-signatures/)
Anyone who learns your endpoint's URL can send it a request. The signature is what tells a genuine delivery from everything else, so verify it on every request and reject the ones that fail.
## The headers [#the-headers]
| Header | Value |
| --- | --- |
| `X-Transglot-Signature` | `sha256=` followed by the lowercase hex HMAC-SHA256 of the signed message |
| `X-Transglot-Timestamp` | Unix time in seconds when this attempt was signed, for example `1791820860` |
| `X-Transglot-Event` | The event name, for example `batch.completed` |
| `X-Transglot-Delivery` | A UUID for the delivery |
## How the signature is computed [#how-the-signature-is-computed]
### Build the signed message [#build-the-signed-message]
Join the `X-Transglot-Timestamp` value, a full stop, and the raw request body:
```text title="Signed message"
1791820860.{"batch":{"id":4821, ... },"event":"batch.completed", ... }
```
### Compute the HMAC [#compute-the-hmac]
Compute HMAC-SHA256 of that message, keyed with the endpoint's whole signing secret, including the `whsec_` prefix. Hex-encode the result in lowercase and prefix it with `sha256=`.
### Compare [#compare]
Compare your value with `X-Transglot-Signature` using a constant-time comparison. If they differ, reject the request.
Sign the exact bytes you received, before any JSON parsing. Parsing and re-serialising changes whitespace, key order and escaping, and the signature will not match. The body is UTF-8 JSON with slashes and non-ASCII characters unescaped.
## Reject old timestamps [#reject-old-timestamps]
Each attempt is signed when it is sent, so `X-Transglot-Timestamp` is always recent for a genuine delivery, including a retry or a redelivery. Reject a timestamp more than five minutes from your clock: that stops someone who captured a delivery from replaying it later. Because the timestamp is part of the signed message, it cannot be changed without breaking the signature.
## Verification code [#verification-code]
Each function returns `true` only for a genuine, recent delivery. Store the secret in an environment variable such as `TRANSGLOT_WEBHOOK_SECRET`.
```js tab="Node.js" title="verify.mjs"
import crypto from 'node:crypto';
const TOLERANCE_SECONDS = 300;
export function verifyTransglotSignature(rawBody, timestamp, signature, secret) {
if (!timestamp || !signature) return false;
const age = Math.abs(Math.floor(Date.now() / 1000) - Number(timestamp));
if (!Number.isFinite(age) || age > TOLERANCE_SECONDS) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(`${timestamp}.`)
.update(rawBody)
.digest('hex');
const a = Buffer.from(expected);
const b = Buffer.from(signature);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
```
```python tab="Python" title="verify.py"
import hashlib
import hmac
import time
from typing import Optional
TOLERANCE_SECONDS = 300
def verify_transglot_signature(raw_body: bytes, timestamp: Optional[str], signature: Optional[str], secret: str) -> bool:
if not timestamp or not signature:
return False
try:
age = abs(int(time.time()) - int(timestamp))
except ValueError:
return False
if age > TOLERANCE_SECONDS:
return False
expected = "sha256=" + hmac.new(
secret.encode(),
timestamp.encode() + b"." + raw_body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected.encode(), signature.encode())
```
```php tab="PHP" title="verify.php"
$tolerance) {
return false;
}
$expected = 'sha256='.hash_hmac('sha256', $timestamp.'.'.$rawBody, $secret);
return hash_equals($expected, $signature);
}
```
## Wiring it into a receiver [#wiring-it-into-a-receiver]
The receiver has to hand the function the raw body. Then answer with a `2xx` quickly and do slow work afterwards: a delivery that takes longer than 10 seconds to answer counts as failed and is retried.
```js tab="Node.js" title="server.mjs (Express)"
import express from 'express';
import { verifyTransglotSignature } from './verify.mjs';
const app = express();
// express.raw keeps req.body as the unparsed Buffer the signature covers.
app.post('/hooks/transglot', express.raw({ type: 'application/json' }), (req, res) => {
const genuine = verifyTransglotSignature(
req.body,
req.get('X-Transglot-Timestamp'),
req.get('X-Transglot-Signature'),
process.env.TRANSGLOT_WEBHOOK_SECRET,
);
if (!genuine) return res.status(400).send('invalid signature');
const payload = JSON.parse(req.body.toString('utf8'));
res.sendStatus(204);
// Handle payload.event after answering.
});
app.listen(3000);
```
```python tab="Python" title="app.py (Flask)"
import os
from flask import Flask, abort, request
from verify import verify_transglot_signature
app = Flask(__name__)
@app.post("/hooks/transglot")
def transglot_webhook():
raw_body = request.get_data() # the unparsed bytes the signature covers
if not verify_transglot_signature(
raw_body,
request.headers.get("X-Transglot-Timestamp"),
request.headers.get("X-Transglot-Signature"),
os.environ["TRANSGLOT_WEBHOOK_SECRET"],
):
abort(400)
payload = request.get_json()
# Queue payload["event"] for processing, then answer.
return "", 204
```
```php tab="PHP" title="webhook.php"
The badge is part of the [open-source plan](https://transglot.ai/docs/administration/billing/open-source-plan). When a workspace is granted the plan, the badge is switched on for the projects the workspace has at that point. There is no setting in the console to switch it on or off for a single project.
## What the badge makes public [#what-the-badge-makes-public]
The badge shows exactly two things: the project's name and its number of target languages. It shows no strings, no translations, and nothing about your other projects.
## Add the badge [#add-the-badge]
### Find your workspace and project slugs [#find-your-workspace-and-project-slugs]
They are in the console address of the project: `https://app.transglot.ai/orgs/{workspace}/projects/{project}`.
### Build the badge address [#build-the-badge-address]
```text title="Badge address"
https://api.transglot.ai/badge/{workspace}/{project}.svg
```
Open it in a browser to check it renders.
### Add it to your README [#add-it-to-your-readme]
```md title="README.md"
[](https://transglot.ai)
```
## Caching [#caching]
The badge is cached for five minutes and revalidated with an `ETag`. After you add a language, it can take a few minutes to show through an image proxy such as GitHub's.
## If it doesn't work [#if-it-doesnt-work]
- **The badge returns `404`.** A project without the badge switched on returns the same `404` as a project that does not exist, so nobody can use the badge to discover private project names. Check the slugs in the address, and that the workspace is on the open-source plan.
- **The badge stopped working after a rename.** The badge address uses the project's slug. Changing the project's URL in its settings breaks badges that use the old one; update the address in your README.
- **The badge disappeared.** If the open-source grant ends, the badge is switched off for the workspace's projects.
# Connect your app to the CDN (https://transglot.ai/docs/guides/delivery/connect-your-app-to-the-cdn/)
Your app fetches the published version of each language from the CDN when it needs it, instead of carrying translation files in its build. After that, publishing in transglot is enough to change what users see.
You need a plan that includes runtime delivery (Starter and above), and an Owner or Admin to create the CDN key. Wiring up the app is a developer task.
## Connect [#connect]
### Publish at least one language [#publish-at-least-one-language]
The CDN only serves published versions. Publish a language from **Releases** first; see [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale). Until then every request for it returns `404 not-published`.
### Create a CDN key [#create-a-cdn-key]
In the project's **Settings → Developer → Runtime delivery (CDN)**, enter a **Name** under **CDN keys** and select **New CDN key**. Copy the key from **Your new CDN key**: it is shown once. See [CDN keys](https://transglot.ai/docs/guides/delivery/the-cdn#cdn-keys).
### Note the project id [#note-the-project-id]
The same card shows each language's **Latest URL**, for example `https://api.transglot.ai/v1/cdn/42/de`. The number after `/cdn/` is the project id the SDKs ask for.
### Install the runtime package [#install-the-runtime-package]
Use the package for your framework (see [the runtime SDKs](https://transglot.ai/docs/developers/sdks)), or the framework-agnostic client:
```npm
npm install @transglot/runtime
```
### Configure it [#configure-it]
Give the client the API address, the project id, the CDN key and the starting language:
```ts title="i18n.ts"
import { createClient } from '@transglot/runtime';
export const i18n = createClient({
baseUrl: 'https://api.transglot.ai',
project: 42,
cdnKey: 'taicdn_...',
locale: 'de',
});
await i18n.loadLocale('de');
i18n.t('home.title');
```
The client caches each language in memory and revalidates it with the `ETag`, so a repeat load that finds nothing new costs an empty `304`. Full options are on [the JavaScript SDK page](https://transglot.ai/docs/developers/sdks/javascript).
### Deploy your app [#deploy-your-app]
From now on, a publish in transglot reaches users without a rebuild.
## Choose latest or a pinned version [#choose-latest-or-a-pinned-version]
By default the SDKs read the **latest** address, so each publish reaches users within about a minute (the latest address may be cached for up to 60 seconds). If you want a change to go live only when you ship, pin a version instead (the runtime client takes a `version` option). A pinned version never changes, so you move to a new one deliberately. See [Latest and versioned addresses](https://transglot.ai/docs/guides/delivery/the-cdn#latest-and-versioned-addresses).
## Things to know [#things-to-know]
- **Use a JSON delivery format.** The runtime SDKs read `json_flat`, `json_nested`, `laravel_json` and `flutter_arb`. Set **Delivery format** in **Settings → Developer** if your project's default is something else.
- **Published versions are sparse.** A key nobody has translated yet is not in the version. The runtime client returns the key itself for a missing string; to show your source text instead, load the source language underneath. The [SDK pages](https://transglot.ai/docs/developers/sdks) show how for each framework.
- **The CDN key is read-only.** It can read what you already published and nothing else, so it can sit in client-side code. To keep it out of the browser altogether, fetch on your server.
- **Do not use a deploy token here.** Deploy tokens are secrets with write access. The CDN only accepts CDN keys.
## If it doesn't work [#if-it-doesnt-work]
| What you see | What to check |
| --- | --- |
| `404 not-published` | The language has not been published yet, or you pinned a version older than the 20 kept. |
| `404 not-found` | The project id in the address is not the key's project, or the language is not in the project. |
| `401 cdn-key-invalid` | The key was deleted or mistyped. Create a new one. |
| `403 plan-feature-required` | The account's plan no longer includes runtime delivery. |
| Old text after a publish | See [Why are my translations not reaching my app?](https://transglot.ai/docs/guides/delivery/why-are-my-translations-not-reaching-my-app) |
# Publish a locale (https://transglot.ai/docs/guides/delivery/publish-a-locale/)
Publishing takes one language as it stands right now, freezes it into a new numbered version, and makes that version the one the CDN serves. Your translations stay editable in transglot; the published version does not change after the fact. Each language is published on its own, so a finished language never waits for an unfinished one.
Owners and Admins, on a plan that includes runtime delivery (Starter and above). An Admin whose access is limited to some languages can publish those languages only.
## Publish from the Releases screen [#publish-from-the-releases-screen]
### Open Releases [#open-releases]
Open the project and select **Releases** in the sidebar.
### Check the language is ready [#check-the-language-is-ready]
In **Departures**, the language's **Status** should read **ready to publish** (or **published**, to release it again). If it reads **in progress**, **not translated** or **waiting for approval**, use the row's **Next** action to finish it first. See [The Releases screen](https://transglot.ai/docs/guides/delivery/the-deploy-screen#departures).
### Start the publish [#start-the-publish]
Select **publish** in the language's row, or the **Publish German →** button in the page header (it names the language the page suggests).
### Confirm [#confirm]
The confirmation, **Publish German?**, states the version number the language will freeze at and what goes into it. Select **Publish German**.
A notice confirms the result, for example **"de published as version 4."** The row's **Live** column and the **Release log** update, and your app starts receiving the new version (see [how quickly](https://transglot.ai/docs/guides/delivery/the-cdn#caching)).
If nothing in the language has changed since its live version, no new version is made: the notice reads **"de is already live at version 3. Nothing has changed since it was published."**
## What goes into a version [#what-goes-into-a-version]
| Included | Not included |
| --- | --- |
| Every string someone wrote, edited, uploaded or approved | Strings with no translation yet. Your app's runtime handles missing keys (see [the SDKs](https://transglot.ai/docs/developers/sdks)). |
| AI drafts, unless the project holds them back (see below) | AI drafts, when the project holds them back |
| Flagged strings: a review flag does not stop a publish | |
Two more cases leave strings out:
- If the project's **Message syntax** check setting is **ICU**, a translation that is not valid ICU MessageFormat is left out, and the notice says so: **"1 key is left out of the bundle because its translation is not valid ICU MessageFormat."** Fix the translation and publish again.
- Strings the delivery format cannot represent are left out (for example, plural strings in a format with no plural support). See [Delivery settings](https://transglot.ai/docs/guides/delivery/the-cdn#delivery-settings) to choose the format.
If a nested format cannot represent your key names (one key is both a value and a parent of another key), the publish is refused with **"de was not published."** followed by the reason, and the live version stays as it was.
## Choose whether AI drafts are published [#choose-whether-ai-drafts-are-published]
An AI draft is a translation Glossa wrote that nobody has approved or edited yet. Each project has one rule for them:
- **Yes, publish drafts too**: drafts, edits and approved strings all go in. This is the default.
- **No, hold drafts back**: approved strings and the text your team wrote or uploaded go in. Drafts wait until someone approves or edits them. While a language still has drafts, its status reads **waiting for approval**.
The first time someone publishes from **Releases**, the confirmation asks **"Can GLOSSA drafts go to your website before someone approves them?"** It is asked once per project. To change the answer later, select **Change** next to **Drafts** in the **This release** card. A change takes effect at each language's next publish; nothing that is live now is removed or rewritten.
Only Owners and Admins with access to every language in the project can set the rule.
## Other ways to publish [#other-ways-to-publish]
**Settings → Developer → Runtime delivery (CDN)** lists every language in the project, including the source language, each with **Publish now**. This publishes the language as it stands, whatever its status on **Releases**, so check the language before you use it.
Call the publish endpoint with a [deploy token](https://transglot.ai/docs/developers/deploy-tokens) that has the `manage` ability:
```bash title="Terminal"
curl -X POST https://api.transglot.ai/v1/cdn/publish \
-H "Authorization: Bearer $TRANSGLOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"locale": "de"}'
```
The response includes `version`, `created` (`false` when nothing changed and the live version was kept), `key_count`, `omitted_invalid_icu_keys`, and the two delivery addresses, `immutable_url` and `latest_url`, so a pipeline can pin the new version or purge a cache in front of it.
Set **Freshness** to **Auto: publish on every push** in **Settings → Developer**. Each push then publishes the language it uploaded, at the moment of the push. Translations that arrive later, such as the AI translation a push of source strings starts, reach your app at that language's next publish.
## Roll back [#roll-back]
Versions are immutable, and transglot keeps the 20 most recent versions of each language. To serve an earlier version, point your app at that version's address instead of the latest one. See [Latest and versioned addresses](https://transglot.ai/docs/guides/delivery/the-cdn#latest-and-versioned-addresses).
## If it doesn't work [#if-it-doesnt-work]
- **"Not available on this account’s plan."**: runtime publishing needs Starter or above. Readiness and the release log still work.
- **"A workspace admin publishes runtime versions."**: your role cannot publish. Ask an Owner or Admin.
- **No source strings yet** or **No target languages yet**: there is nothing to publish. Import strings or add a language first.
- **Your app still shows the old text**: see [Why are my translations not reaching my app?](https://transglot.ai/docs/guides/delivery/why-are-my-translations-not-reaching-my-app)
# The CDN (https://transglot.ai/docs/guides/delivery/the-cdn/)
The CDN serves the versions you [publish](https://transglot.ai/docs/guides/delivery/publish-a-locale) over plain HTTPS, so your app can load translations at runtime and a corrected string reaches production without a new build. It only ever serves published versions. Work in progress in the editor is never reachable through it.
Runtime delivery is included on Starter and above. Owners and Admins manage it from **Settings → Developer → Runtime delivery (CDN)**.
## Latest and versioned addresses [#latest-and-versioned-addresses]
Every published language has two addresses on the API host:
```text title="Delivery addresses"
https://api.transglot.ai/v1/cdn/{project-id}/{locale} # latest
https://api.transglot.ai/v1/cdn/{project-id}/{locale}/v{n} # one version
```
`{project-id}` is the project's numeric id. You do not need to look it up: **Settings → Developer → Runtime delivery (CDN)** shows each language's **Latest URL**, and its **Immutable URL** once it has been published, ready to copy.
- The **latest** address always serves the newest published version of that language. Point your app here when you want every publish to reach users without a deploy.
- A **versioned** address serves exactly that version, forever. Point your app here when you want to control when a change goes live, or to roll back to an earlier version.
transglot keeps the 20 most recent versions of each language. A versioned address older than that returns `404`.
## Caching [#caching]
| Address | `Cache-Control` | What it means for you |
| --- | --- | --- |
| Latest | `public, max-age=60, must-revalidate` | A cache may serve the previous version for up to a minute after a publish, then checks again. |
| Versioned | `public, max-age=31536000, immutable` | Cached for a year and never rechecked, because its content can never change. |
Every response carries an `ETag`. A client or cache that sends it back in `If-None-Match` gets an empty `304 Not Modified` when nothing has changed, so revalidating the latest address is cheap. Responses also carry `X-Cdn-Version` (the version served) and `X-Cdn-Format` (its file format).
Published versions are public content meant to be fetched from any app, so responses allow every origin (`Access-Control-Allow-Origin: *`).
Requests are rate limited per key and IP address. Let a CDN of your own, or the runtime SDK's cache, absorb repeat reads instead of fetching on every page view.
## CDN keys [#cdn-keys]
Every request needs a **CDN key** for the project. It is the narrowest credential transglot issues: it reads this project's published versions and nothing else, and it cannot change anything. Send it in the `X-Transglot-Cdn-Key` header, or as a `?key=` query parameter where you cannot set headers.
```bash title="Terminal"
curl https://api.transglot.ai/v1/cdn/42/de \
-H "X-Transglot-Cdn-Key: taicdn_..."
```
A CDN key is not a deploy token. Deploy tokens are secrets that push and pull strings; a CDN key only reads what you already chose to publish.
### Create a key [#create-a-key]
### Open the delivery settings [#open-the-delivery-settings]
Open the project's **Settings → Developer** and scroll to **Runtime delivery (CDN)**.
### Name the key [#name-the-key]
Under **CDN keys**, enter a **Name** that says where the key is used, such as the CDN or app that will hold it.
### Create and copy it [#create-and-copy-it]
Select **New CDN key**. The key is shown once, in **Your new CDN key**: copy it before you close the dialog. It is stored hashed and cannot be shown again; if you lose it, create another.
### Delete a key [#delete-a-key]
Select **Delete** next to the key and type its name to confirm. Anything still fetching with that key gets `401` straight away, including caches you do not control, so roll out a replacement first if the key serves production.
## Delivery settings [#delivery-settings]
Two settings in **Runtime delivery (CDN)** apply to every language in the project. Change them, then select **Save settings**.
| Setting | Options |
| --- | --- |
| **Freshness** | **Manual: publish explicitly** (the default): a version is made only when someone publishes. **Auto: publish on every push**: each push publishes the language it uploaded. |
| **Delivery format** | **Project default**, which follows the format your project last pushed (or flat JSON if it has never pushed), or a specific format. |
A version is published in one format. A new format applies from each language's next publish. The runtime SDKs read the JSON formats (`json_flat`, `json_nested`, `laravel_json`, `flutter_arb`); see [the SDKs](https://transglot.ai/docs/developers/sdks).
You can ask for a specific format with `?format=`. If it is not the format the version was published in, the CDN returns `404` rather than converting it.
## Errors [#errors]
Errors are JSON problem responses. The `type` field names the problem, for example `/problems/cdn-key-invalid`.
| Status | Slug | Cause |
| --- | --- | --- |
| `401` | `cdn-key-required` | No key in the header or the query string. |
| `401` | `cdn-key-invalid` | The key does not exist or was deleted. |
| `404` | `not-found` | The project id is not the key's project, or the language is not in the project. |
| `404` | `not-published` | The language has never been published, the version is older than the 20 kept, or the requested `format` is not the published one. |
| `403` | `plan-feature-required` | The account's plan no longer includes runtime delivery. Your keys are kept and work again after an upgrade. |
| `403` | `workspace-closed` | The workspace has been closed, so its published versions are no longer served. |
The `403` responses are sent with `Cache-Control: no-store`, so a cache in front of the CDN never keeps them.
## Related [#related]
# The Releases screen (https://transglot.ai/docs/guides/delivery/the-deploy-screen/)
**Releases** is the project page that gets translations out of transglot and into your product. Open it from the project's sidebar. Everyone who can open the project can read it; publishing needs the Admin role or an account Owner, and a plan that includes runtime delivery (Starter and above).
The page has two views, switched with the tabs under the title: **Releases** for the release work, and **Connect & SDK** for the setup snippets.
## The Releases view [#the-releases-view]
### The banners [#the-banners]
Above the tables, a banner states where the project stands:
| Banner | What it means |
| --- | --- |
| **"N locales are not ready to publish"** | At least one language still has something outstanding. Each row of the table says what. |
| **"N locales have strings that need checking"** | Some strings carry a review flag. A flag does not stop a publish, so flagged strings go to your website as they stand. **Read them** opens the flagged strings in the editor. |
| **"Every locale is ready to publish"** | Every target language would publish complete, and nothing is flagged. |
If your role could publish but your plan cannot, a notice reads **"Runtime publishing unlocks on the Starter plan."** Readiness and the release log still work on every plan.
### Departures [#departures]
**Departures** has one row per target language. The source language is not listed: you never ship the source to itself. Each row shows:
| Column | What it shows |
| --- | --- |
| **Locale** | The language. |
| **Coverage** | How much of the language is written, against the project's source strings. |
| **Approved** | How many strings someone has approved. |
| **Needs checking** | How many strings carry a review flag. |
| **Status** | One of the five states below. |
| **Live** | The version your app is being served, and when it was published. |
| **Next** | The one action that moves the row forward. |
The **Status** column uses five words:
| Status | Meaning | Next |
| --- | --- | --- |
| **not translated** | Nothing is written yet (or the project has no source strings). | **translate** |
| **in progress** | Some strings are written, not all. | **translate** |
| **waiting for approval** | Everything is written, but this project holds AI drafts back from publishing and some strings are still drafts. | **approve** |
| **ready to publish** | Everything that would go into a release is written. | **publish** (or **review** if strings are flagged) |
| **published** | Ready, and a version is already live. | **publish** to release again |
Whether AI drafts count towards a release is a project setting. See [Drafts](https://transglot.ai/docs/guides/delivery/publish-a-locale#choose-whether-ai-drafts-are-published).
### The release card [#the-release-card]
The card beside the table puts one language forward. It prefers a language that has never been published, and it skips languages with flagged strings. It reads, for example, **"German is ready to publish"** with the next version number, or **"German is live at v3"** for a language already being served. When nothing can be offered it says why: **No source strings yet**, **No target languages yet**, or **No locale is ready to publish**, with a link to fix it.
If you cannot publish, the card says so in place of the button: **"A workspace admin publishes runtime versions."** or **"Not available on this account’s plan."**
The same language is offered in the page header as **Publish German →**. See [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale).
### This release [#this-release]
A summary of the project's delivery:
- **Publishing mode**: **manual**, or **on every push**. Shown to Owners and Admins.
- **Delivered as**: the file format the CDN serves. Shown to Owners and Admins.
- **Locales live**: how many target languages have a published version.
- **Last release**: when the most recent version was published.
- **Never published**: how much written work has not reached your app yet.
- **Drafts**: whether AI drafts go into a release, with **Change** for the people allowed to set it.
Publishing mode and format are set in **Settings → Developer**. See [The CDN](https://transglot.ai/docs/guides/delivery/the-cdn#delivery-settings).
### Release log [#release-log]
**Release log** lists the ten most recent publishes, newest first: the language and version, when it was published, how many keys it holds, its format, who published it (**system** for a publish made through the API or by a push), and a short content fingerprint. Two versions with the same fingerprint hold the same content. Before anything is published it reads **"No runtime versions published yet."**
## The Connect & SDK view [#the-connect--sdk-view]
The setup half of the page. Every snippet is pre-filled with this project's real API address, and with a real credential right after you create one.
| Section | What it gives you |
| --- | --- |
| **Connect & verify** | Whether the project has a deploy token yet, with a link to the project's setup steps. |
| **One provider** | A React snippet that adds the in-context editor for your reviewers, pre-filled with the project's publishable key. See [Connect the in-context editor](https://transglot.ai/docs/guides/quality/connect-the-in-context-editor). |
| **Command line (CLI)** | A `transglot.json` config file and the terminal commands to push your source strings and pull translations back. See [CLI](https://transglot.ai/docs/developers/cli). |
| **GitHub Action** | A workflow file for `.github/workflows/transglot.yml`. See [GitHub Action](https://transglot.ai/docs/developers/github-action). |
| **Tokens, keys, webhooks & CDN** | A pointer to **Settings → Developer**, where credentials and CDN delivery are managed. |
| **Token usage** | API calls per deploy token over the last 14 days. Owners and Admins only. |
| **API playground** | Runs read-only API requests against this project. Shown on plans with API access. |
| **Event catalog** | Every webhook event with an example payload. See [Webhook events](https://transglot.ai/docs/developers/webhooks/events). |
The CLI, the GitHub Action and the API authenticate with a deploy token, which needs a plan with API access (Starter and above). On the Free plan the page says so and points you to uploading files from the project's setup page instead.
## Related [#related]
# Why are my translations not reaching my app? (https://transglot.ai/docs/guides/delivery/why-are-my-translations-not-reaching-my-app/)
The editor shows your translations as they are now. Your app shows whatever version it last received, from the CDN or from files in its build. When the two disagree, one of the causes below is almost always the reason. Work through them in order.
Editing a translation does not publish it. Open **Releases** and look at the language's **Live** column in **Departures**. If it has no version, there is nothing for your app to fetch, and the CDN answers `404 not-published`. [Publish it](https://transglot.ai/docs/guides/delivery/publish-a-locale).
A published version never changes. Edits made after it are not in it, so publish the language again. When nothing has changed, the publish says the language is already live and keeps the current version.
If the project's **Freshness** is **Auto: publish on every push**, each push publishes the language it uploaded at that moment. Translations that arrive afterwards, such as the AI translation a push of source strings starts, wait for that language's next publish.
If the project's **Drafts** rule is **No, hold drafts back**, a translation Glossa wrote goes into a version only once someone approves or edits it. The language's status reads **waiting for approval** while drafts remain. Approve them and publish again, or change the rule in the **This release** card on **Releases**. See [Choose whether AI drafts are published](https://transglot.ai/docs/guides/delivery/publish-a-locale#choose-whether-ai-drafts-are-published).
An app that requests a versioned address (`.../v3`) gets that version forever: that is what makes it safe to cache. Move it to the newer version, or to the latest address. See [Latest and versioned addresses](https://transglot.ai/docs/guides/delivery/the-cdn#latest-and-versioned-addresses).
The latest address may be cached for up to 60 seconds after a publish, so give a new version about a minute. If you put your own CDN in front of transglot, check it honours the `Cache-Control` the origin sends rather than caching for longer. Your app's own storage, such as a persisted runtime cache, also serves the old version until it revalidates.
A version only holds strings that have a translation. A key nobody has translated is absent, and the runtime client shows the key itself. Two other cases leave a string out:
- The project's **Message syntax** is **ICU** and the translation is not valid ICU MessageFormat. The publish notice says how many keys were left out. Fix them in the editor and publish again.
- The delivery format cannot represent the string, such as a plural string in a format without plural support.
Each version is published in one format. A request with `?format=` naming another format returns `404`. Request the published format, which **Releases** shows under **Delivered as**, or change **Delivery format** in **Settings → Developer** and publish again.
Changes to **Delivery format**, **Message syntax** or the **Drafts** rule apply when a version is built. What is live keeps its content until the language's next publish.
If your build pulls translations with the [CLI](https://transglot.ai/docs/developers/cli) or the [GitHub Action](https://transglot.ai/docs/developers/github-action) rather than reading the CDN at runtime, nothing changes in your app until you run that build again. Publishing affects only the CDN.
Check the status code:
- `401`: the CDN key is missing, mistyped or deleted. Use the `X-Transglot-Cdn-Key` header or `?key=`, and a key from this project.
- `404 not-found`: the project id in the address does not belong to the key's project.
- `403 plan-feature-required`: the account's plan no longer includes runtime delivery. The keys are kept and serve again after an upgrade.
See [Errors](https://transglot.ai/docs/guides/delivery/the-cdn#errors) for the full list.
## Related [#related]
# Guides (https://transglot.ai/docs/guides/)
These guides follow the work in the order it happens: set up a project and bring your strings in, translate and review them, keep quality up, then deliver the result to your app. Each page covers one screen or one task in the transglot console at [app.transglot.ai](https://app.transglot.ai).
If you are new to transglot, start with the [Quickstart](https://transglot.ai/docs/quickstart) and [How transglot works](https://transglot.ai/docs/how-transglot-works).
## Guide areas [#guide-areas]
## Most-used tasks [#most-used-tasks]
## Working from code [#working-from-code]
Developers who push and pull strings from a repository can do most of this without opening the console. See the [CLI](https://transglot.ai/docs/developers/cli), the [GitHub Action](https://transglot.ai/docs/developers/github-action) and the [API](https://transglot.ai/docs/developers/api).
# Add a target language (https://transglot.ai/docs/guides/projects/add-a-target-language/)
Adding a target language needs the Developer role or above. Translators and Reviewers can work in a language but cannot add one.
## Add the language [#add-the-language]
### Find the add row [#find-the-add-row]
Open the project. The last row of the **Locales** table on the [overview](https://transglot.ai/docs/guides/projects/your-project) is **Add a language**. The same control is on **Settings**, then **Languages**.
### Pick the language [#pick-the-language]
Type into **Search languages** to search by English name, native name or tag (for example "German", "Deutsch" or `de`), and pick it from the list. The source language and the languages already in the project are not offered.
### Add it [#add-it]
Select **Add**.
### Confirm the translation [#confirm-the-translation]
If the project has strings to translate into the new language and **Auto-translate** is on, the console asks first: **Add** *language* **and translate into it?** "Everything already in this project is translated into *language* as soon as it is added." The dialog shows how many words that is. Confirm to add the language and start the translation.
If you pick a regional form of a language the project already has, for example `pt-PT` when it has `pt-BR`, a warning says so before you add it. Both are allowed, but each is translated separately: no strings carry over between them.
## What happens next [#what-happens-next]
The language joins the **Locales** table, and a message says what happened to the project's existing strings:
| Message | Meaning |
| --- | --- |
| "*Language* added. GLOSSA is translating 240 strings into it. Reload the page to see its progress." | A translation run has started. |
| "*Language* added. 12 strings came straight from translation memory, so they are in the language already." | Every string was reused from earlier translations, at no cost. |
| "*Language* added. Nothing in the project was waiting to be translated into it." | There was nothing to translate. |
| "*Language* added. Auto-translate is off for this project, so nothing has been translated into it yet." | Translate it from the editor with **Translate missing** when you are ready. |
| "*Language* added. There are no source strings in the project yet, so nothing is being translated into this language." | Import your strings first. |
| "*Language* added. There was not enough word quota left this cycle to translate the existing strings into this language." | The language was added, untranslated. |
| "*Language* added. AI translation is paused for this project, so the existing strings were not translated into this language." | The language was added, untranslated. |
| "*Language* added. Your access does not cover this locale, so the strings already in the project were left untranslated." | You can manage languages but are not scoped to this one. |
Whatever you type or pick is stored as a standard BCP-47 tag, so `pt-br` and `pt-BR` are the same language. See [Languages and locales](https://transglot.ai/docs/concepts/languages-and-locales).
## Language limits [#language-limits]
On the Free plan, an account can use a limited number of languages across all its projects, counting source languages; a language already used by another project in the account does not count again. Paid plans have no language limit. See [pricing](https://transglot.ai/pricing).
## If it doesn't work [#if-it-doesnt-work]
- **"That language is already in this project."**: the language, or the same language under another spelling, is already a target or the source.
- **"Unknown locale: use a BCP-47 tag like de, pt-BR, or zh-Hans."**: the tag is not a language transglot recognises.
- **"The account this workspace belongs to has no room for another language on its plan."**: the account has reached its plan's language limit. Upgrade, or pick a language the account already uses. See [Change your plan](https://transglot.ai/docs/administration/billing/change-your-plan).
- **Add a language** is not shown: you need the Developer role or above.
## Related [#related]
- [Remove a language](https://transglot.ai/docs/guides/projects/remove-a-language)
- [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai)
- [Assign a language to a translator](https://transglot.ai/docs/guides/translating/assign-a-language-to-a-translator)
# Add or edit a key (https://transglot.ai/docs/guides/projects/add-or-edit-a-key/)
A key is the name your code asks for, such as `checkout.title`. Most keys arrive with your files, through an [import](https://transglot.ai/docs/guides/projects/import-content), the [CLI](https://transglot.ai/docs/developers/cli) or an integration, together with their source text. You can also create and edit keys by hand in the editor.
Adding, editing and deleting keys needs the Developer role or above.
The key dialog sets a key's name, description, plural setting and length limit. It does not hold source text: source strings arrive by import, push or integration, and the editor shows them read-only. A key you create by hand has no source text until a file that contains it is imported or pushed. See [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings).
## Add a key [#add-a-key]
### Open the dialog [#open-the-dialog]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), select **New key**. In a project with no strings yet, the editor offers **Add one key by hand** instead.
### Fill in the fields [#fill-in-the-fields]
| Field | What to enter |
| --- | --- |
| **Key name** | The name your code uses, for example `checkout.title`. Up to 500 characters, and unique in the project. |
| **Description (context for translators & AI)** | What the string is and where it appears, for example "Headline shown on the checkout page". Translators see it in the editor, and Glossa reads it when translating. Up to 2,000 characters. |
| **Plural key (one form per CLDR category)** | Turn on for a string that changes with a number, such as "1 item" and "3 items". See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms). |
| **Max length (optional)** | The most characters a translation may have, for text that must fit a button or a label. |
### Create it [#create-it]
Select **Create**. A message confirms "Key created."
## Edit a key [#edit-a-key]
### Open the key [#open-the-key]
In the editor, switch to **Table** and select the key's name in the first column. The dialog opens as **Edit** *key name*.
### Change it [#change-it]
Change any of the fields, then select **Save**. A message confirms "Key updated."
Things to know when editing:
- **Renaming.** If your code or files still use the old name, the next import or push brings it back as a separate key. Rename it in your source files too. To rename many keys that share a prefix, use **Keys**, then **Rename prefix** in the editor's [bulk actions](https://transglot.ai/docs/guides/translating/bulk-actions).
- **Descriptions from files.** If your files carry descriptions or comments for their strings, the next push or import of the source language replaces the description with the file's.
- **Plural setting.** You can only switch a key between plural and simple while it has no translations.
- **Length limit.** Changing **Max length (optional)** re-checks the key's translations: any that now run over the limit are flagged for review.
## Delete a key [#delete-a-key]
### Open the key [#open-the-key-1]
Open the key as described in [Edit a key](#edit-a-key).
### Delete it [#delete-it]
Select **Delete key**. The confirmation reads "The key and every translation of it, in every language, are removed. Anything reading this key from your app falls back to the key name until you push a replacement."
### Confirm [#confirm]
Select **Delete key**. A message confirms "Key deleted."
Deleting a key cannot be undone. To delete many keys at once, select them in the table and use **Delete** in the bulk bar. A member scoped to particular languages cannot delete a key that has translations in languages outside their access.
## If it doesn't work [#if-it-doesnt-work]
- **"A key with that name already exists in this project."**: choose another name, or edit the existing key.
- **"Cannot change the plural setting of a key that already has translations. Delete them first."**: the key already has translations, so its plural setting is fixed.
- **New key** is not shown: you need the Developer role or above.
## Related [#related]
- [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings)
- [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms)
- [Act on many strings at once](https://transglot.ai/docs/guides/translating/bulk-actions)
# Delete a project (https://transglot.ai/docs/guides/projects/delete-a-project/)
Deleting a project needs the Admin role in the workspace, or Owner of its account. Someone whose admin access to the project is limited to some of its languages cannot delete it.
Every key, translation, version, screenshot, glossary term, webhook endpoint and API token in the project is removed immediately and permanently. Export or publish anything you want to keep first. The workspace's billing history is kept.
## Before you start [#before-you-start]
- Export the project's languages from **Settings**, then **Export**, or pull them with the [CLI](https://transglot.ai/docs/developers/cli), if you might need them again.
- Anything that reads from the project stops working: CLI pushes and pulls with its tokens, webhooks, integrations and README badges. Translations your app has already built in keep working.
## Delete the project [#delete-the-project]
### Open the project settings [#open-the-project-settings]
Open the project, select **Settings**, then **General**.
### Start the deletion [#start-the-deletion]
Under **Delete this project**, select **Delete project**.
### Type the name [#type-the-name]
In the dialog, type the project's name in **Project name**, exactly as it appears. "This cannot be undone. Type *name* to confirm."
### Confirm [#confirm]
Select **Delete project**.
## What happens next [#what-happens-next]
You return to the workspace, and a message confirms it: "Project “*name*” deleted." The workspace's Admins and the account's Owners get an email that the project was deleted, and the deletion is recorded in the workspace [audit log](https://transglot.ai/docs/administration/security/the-audit-log) and its activity feed.
## If it doesn't work [#if-it-doesnt-work]
- **"Type the project name exactly to confirm."**: the name you typed does not match. Check capitals and spaces.
- **Delete this project** is not shown: you are not an Admin of this workspace or an Owner of its account.
## Related [#related]
- [Rename a project or change its URL](https://transglot.ai/docs/guides/projects/rename-a-project-or-change-its-url)
- [Remove a language](https://transglot.ai/docs/guides/projects/remove-a-language)
- [Delete a workspace](https://transglot.ai/docs/administration/workspace/delete-a-workspace)
# Import content (https://transglot.ai/docs/guides/projects/import-content/)
The browser upload is the quickest way to get strings into a project, and it works on every plan, including Free. You drop your language files on the project's **Get started** page, see what transglot detected, and nothing is written until you confirm.
Uploading needs the Developer role or above. For other ways in, see [Other ways to bring strings in](#other-ways-to-bring-strings-in).
## Upload your files [#upload-your-files]
### Open Get started [#open-get-started]
On a new project, select **Add source strings →** on the [overview](https://transglot.ai/docs/guides/projects/your-project) or **Import source strings →** in the editor. On a project that already has strings, open **Releases**, then **Connect & SDK**, and select **Open the setup steps**.
### Add the files [#add-the-files]
Under **Upload your language files**, drag your files onto **Drag your files here**, or select **Choose files**. You can upload several files at once, or a zip of your locales folder. Include the source-language file: only it can create keys.
### Check what was detected [#check-what-was-detected]
transglot reads the files without writing anything and lists each one with its language, its format and how many strings it holds.
- If a file's language could not be worked out, the row reads "Choose the language for this file." Pick it under **Language for** *file name*.
- If a format was guessed wrong, or reads "Unrecognised format.", pick the right one from the format list on the row. **Detect** goes back to automatic detection.
- If the files bring languages the project does not have yet, a line says which "will be added to this project."
**Choose different files** starts again.
### Import [#import]
Select **Import**. It stays unavailable while a file still needs its language, or when no file is ready.
## What is written [#what-is-written]
- **The source-language file** creates keys that do not exist yet and sets the source text of keys that do. The file wins: changed source text replaces what was there, and translations made from the old text are marked stale.
- **Target-language files** write translations for keys that already exist, as **edited**. They never overwrite a translation that a person has edited or approved; that entry is skipped and reported. Keys that are only in a target file, and not in the source, are not created.
- **Descriptions** and **plural forms** come across where the format carries them. See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms).
- **Nothing is deleted.** An upload adds and updates; keys missing from your files stay in the project.
Files are processed source first, whatever order you upload them in, so translations always land on keys that exist.
## After the upload [#after-the-upload]
Small uploads finish while you wait. Larger ones run in the background with a progress bar, **Importing your files**, which "keeps running if you close this window or leave the page". You can cancel a running import; cancelling does not undo what has already been written.
The first time strings arrive in a project, **Get started** asks whether to translate them now, and whether to keep translating new text automatically. After that, new and changed source text is translated as it arrives while **Auto-translate** is on. See [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai).
Uploading the same files again is safe: identical values are recognised and left alone.
## If something was not imported [#if-something-was-not-imported]
When some files could not be imported, the panel says "N files were not imported." and gives the reason for each:
| Message | What to do |
| --- | --- |
| "None of these keys exist yet. Import your source language first." | Upload the source-language file, then the translations. |
| "This language is outside your project grant." | Your access is limited to other languages. Ask an Admin, or upload only your languages. |
| "*Languages* will not be added: your plan has no room for more languages in this project. Everything else below still imports." | Your plan's language limit is reached. See [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language#language-limits). |
| "Your role cannot add strings to this project. An admin or developer can, or they can grant you access." | Uploading needs the Developer role or above. |
| "Those files are too large to upload here. Split them, or push from the CLI." | Upload fewer or smaller files at a time, or use the [CLI](https://transglot.ai/docs/developers/cli). |
| "No language files were found in what you uploaded. A zip should contain the locale files themselves, not just folders." | Zip the files, not an empty folder structure. |
| "The import stopped before it finished. Some of your strings may already be in the project, so check it before importing again." | Check the project; importing again is safe. |
[My import did not bring everything across](https://transglot.ai/docs/resources/troubleshooting/my-import-did-not-bring-everything-across) covers more cases.
## Other ways to bring strings in [#other-ways-to-bring-strings-in]
| Way | Best for |
| --- | --- |
| [CLI](https://transglot.ai/docs/developers/cli) `push` | Keeping a project in step with your codebase. Needs a deploy token, which starts on the Starter plan. |
| [GitHub Action](https://transglot.ai/docs/developers/github-action) | Pushing on every merge. |
| [Git and content integrations](https://transglot.ai/docs/integrations) | Syncing from a repository or a CMS. |
| [Import from another tool](https://transglot.ai/docs/integrations/migrate/import-from-another-tool) | Moving a project, with its translations, from another localization platform. |
See [Which file formats can I use?](https://transglot.ai/docs/guides/projects/which-file-formats-can-i-use) for the formats every route accepts.
## Related [#related]
- [Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key)
- [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings)
- [File formats](https://transglot.ai/docs/developers/formats)
# Plural forms (https://transglot.ai/docs/guides/projects/plural-forms/)
Languages count differently. English needs two forms of "1 item" and "3 items"; Polish needs more; Japanese needs one. transglot stores a plural string as one form per plural category, using the standard CLDR categories (`zero`, `one`, `two`, `few`, `many` and `other`), and each language gets exactly the categories its own grammar uses.
## Where plural keys come from [#where-plural-keys-come-from]
A key is a plural key when its source file says so. These formats carry plurals as separate forms:
| Format | How plurals are written |
| --- | --- |
| Android XML | `` resources |
| Apple `.stringsdict` and String Catalog (`.xcstrings`) | Plural variations |
| Flutter ARB | ICU plural messages |
| Gettext PO | `msgid_plural` entries |
| CSV and XLSX | One row per form, named `key[category]` |
You can also mark a key as plural by hand: turn on **Plural key (one form per CLDR category)** when you [add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key). A key can only be switched between plural and simple while it has no translations.
Formats without plural forms, such as JSON and YAML, can still hold plurals as ICU messages inside an ordinary string, for example `{count, plural, one {# item} other {# items}}`. Those are simple keys whose text contains the plural, and the quality checks read them according to the project's **Message syntax** setting, under **Settings**, then **Checks**. See [File formats](https://transglot.ai/docs/developers/formats).
## Translating plurals [#translating-plurals]
- **With Glossa.** An AI translation of a plural key writes every form the target language needs, and the automatic checks flag a translation with the wrong set of forms.
- **By hand.** A plural key cannot be typed into a single box. In the editor, select **Edit forms** on the row in **Table** view, or **Edit plural forms** in **Side by side** view.
## Edit the forms [#edit-the-forms]
### Open the plural dialog [#open-the-plural-dialog]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), choose the language and open the string's plural forms. The dialog is titled *key*: *language* **plural forms**.
### Compare with the source [#compare-with-the-source]
The source forms are shown at the top, one per category, "for comparison".
### Fill in each form [#fill-in-each-form]
There is one box per category the target language uses, labelled with the category name. As you type, problems such as a missing placeholder are listed under the boxes.
### Save [#save]
Select **Save**.
In the table, a plural string shows each form on its own line, labelled with its category, and its key carries a **plural** chip.
## Importing plurals [#importing-plurals]
When you import or push a translation file, a plural entry is only written to a plural key, and a simple entry only to a simple key. An entry whose shape does not match the key is skipped and reported as a conflict, so a file can never silently turn a plural string into a simple one.
## Related [#related]
- [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings)
- [Quality checks](https://transglot.ai/docs/guides/quality/quality-checks)
- [Which file formats can I use?](https://transglot.ai/docs/guides/projects/which-file-formats-can-i-use)
# Project activity (https://transglot.ai/docs/guides/projects/project-activity/)
The activity feed answers "what happened in this project, and who did it?". Everyone who can open the project can read it.
## Open the feed [#open-the-feed]
The [project overview](https://transglot.ai/docs/guides/projects/your-project) shows the latest few events under **Recent activity**. Select **View all** to open the project's **Activity** page, which lists everything that happened, newest first, with who did it and when. **Load more** fetches older events.
Events that no person started, such as a translation run that finished, are shown as done by **System**.
## What appears [#what-appears]
| Area | Examples |
| --- | --- |
| Keys | A key created, keys deleted, renamed or given a character limit in bulk, source text copied into a language |
| Languages | A language added or removed, with how many translations went with it |
| Review | Translations approved, flagged or restored to an earlier version, a review requested |
| Translation runs | A run finished, with what it translated, reused from memory or could not translate |
| Quality | Self-heal and source drift repairs |
| Assignments | Work assigned, started, submitted, withdrawn, approved or sent back |
| Comments | A comment posted |
| People and access | A member's access to the project granted, changed or removed |
| Project | The project's web address changed, API tokens and webhooks created |
| Delivery | A language published to the CDN |
The feed records actions on the project, not every keystroke. To see each version of one translation, with **Restore** on each, use its [version history](https://transglot.ai/docs/guides/translating/version-history).
## Activity and the audit log [#activity-and-the-audit-log]
The activity feed is a product history, meant for the people working on the project. The workspace [audit log](https://transglot.ai/docs/administration/security/the-audit-log) is the security record: an append-only trail of who was given access, whose role changed, which credentials were created, and destructive changes such as deleted projects and removed languages. Use the audit log when you need evidence; use the activity feed to follow the work.
## Related [#related]
- [The project overview](https://transglot.ai/docs/guides/projects/your-project)
- [Version history](https://transglot.ai/docs/guides/translating/version-history)
- [The audit log](https://transglot.ai/docs/administration/security/the-audit-log)
# Remove a language (https://transglot.ai/docs/guides/projects/remove-a-language/)
Removing a language deletes its translations and their version history, not only the language. Keys, source text and every other language are untouched.
Removing a language needs the Developer role or above. A member scoped to particular languages can only remove a language they have access to.
The translations and their history are deleted, and adding the language again gives you an empty language. If you might want them back, export the language first from **Settings**, then **Export**, or pull it with the [CLI](https://transglot.ai/docs/developers/cli).
## Remove the language [#remove-the-language]
### Find the language [#find-the-language]
Open the project. Find the language's row in the **Locales** table on the [overview](https://transglot.ai/docs/guides/projects/your-project), or on **Settings**, then **Languages**.
### Select Remove [#select-remove]
Select **Remove** on its row.
### Confirm [#confirm]
The dialog, **Remove** *language*?, says how many translated strings are deleted with it, or that nothing has been translated into it yet. For a language with 100 or more translated strings, type the language's name to confirm. Select **Remove** *language*.
## What happens next [#what-happens-next]
- The row disappears, and a message confirms it, such as "German removed, along with 240 translations."
- Translation runs no longer include the language.
- The workspace's Admins and the account's Owners, including you, get an email saying what was removed and how much went with it. The removal is recorded in the project's [activity](https://transglot.ai/docs/guides/projects/project-activity) and in the workspace [audit log](https://transglot.ai/docs/administration/security/the-audit-log).
- Translations already published to the CDN or built into your app keep working until your next deploy.
If this was a mistake, contact support the same day: a backup is the only remaining copy, and backups are not kept forever. See [Get help](https://transglot.ai/docs/resources/get-help).
## If it doesn't work [#if-it-doesnt-work]
- **"The source language cannot be removed."**: a project always has exactly one source language.
- **Remove** is not shown on a row: you need the Developer role or above, with access to that language.
## Related [#related]
- [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language)
- [Languages and locales](https://transglot.ai/docs/concepts/languages-and-locales)
- [Delete a project](https://transglot.ai/docs/guides/projects/delete-a-project)
# Rename a project or change its URL (https://transglot.ai/docs/guides/projects/rename-a-project-or-change-its-url/)
A project has two names. **Name** is what everyone sees across the console, and changing it moves nothing. **Web address** is the part of the console's page addresses that names the project, and changing it breaks links that use the old one.
Admins and the account's Owners can change both.
## Rename the project [#rename-the-project]
### Open the settings [#open-the-settings]
Open the project, select **Settings**, then **General**.
### Edit the name [#edit-the-name]
Under **Names**, edit **Name** ("The project's display name across the console."). Names can be up to 100 characters.
### Save [#save]
Select **Save changes**. **Discard** puts the field back.
The new name shows everywhere the project is listed. A rename is not announced to anyone, because nothing outside the console changes.
## Change the web address [#change-the-web-address]
Bookmarks and links to the project's pages in the console, links in emails already sent, export download links already shared, and README badges all use the old address and stop working. Translations delivered through the CDN and the API keep working.
The old address is not held for you: another project in the workspace can take it later, and links that still use it would then lead to that project.
### Edit the web address [#edit-the-web-address]
On **Settings**, then **General**, under **Names**, edit **Web address**. Under the field, **Web address after you save:** shows the address the project will have.
### Save [#save-1]
Select **Save changes**.
### Confirm [#confirm]
The dialog **Change this project's web address?** shows the current and the new address and what stops working. Select **Change address** to save, or cancel to go back without saving anything.
The project opens at its new address straight away. Because a new address breaks links for other people too, the change is announced: the workspace's Admins and the account's Owners, including you, get an email and a console notification naming the old and the new address. The change is also added to the project's [activity](https://transglot.ai/docs/guides/projects/project-activity) and recorded in the workspace [audit log](https://transglot.ai/docs/administration/security/the-audit-log).
### Address rules [#address-rules]
A web address uses lowercase letters, numbers and hyphens only, up to 120 characters, and must be unique within the workspace. If you type anything else, the message under the field says so and, where it can, suggests an address that is allowed: for "My Website!" it suggests `my-website`.
## If it doesn't work [#if-it-doesnt-work]
- **"Use lowercase letters, numbers and hyphens only, for example my-website."**: use the suggested address, or edit yours to match the rule.
- **"That web address is already used by another project in this workspace."**: choose another address.
- **"Use 120 characters or fewer for the web address."**: shorten it.
- **General** is not offered under **Settings**: you are not an Admin of this workspace or an Owner of its account.
## Related [#related]
- [The project overview](https://transglot.ai/docs/guides/projects/your-project)
- [Add a progress badge to your README](https://transglot.ai/docs/guides/delivery/add-a-progress-badge-to-your-readme)
- [Delete a project](https://transglot.ai/docs/guides/projects/delete-a-project)
# Which file formats can I use? (https://transglot.ai/docs/guides/projects/which-file-formats-can-i-use/)
Every format below works for bringing strings in (browser upload, CLI push, API) and for getting translations out (CLI pull, export, API). Placeholders and plural forms are kept intact, and the automatic checks flag a translation that breaks them before it reaches your app.
| Ecosystem | Formats | Separate plural forms |
| --- | --- | --- |
| Web and JavaScript | [JSON (flat)](https://transglot.ai/docs/developers/formats/json), [JSON (nested)](https://transglot.ai/docs/developers/formats/json), [YAML](https://transglot.ai/docs/developers/formats/yaml) | No |
| Apple | [iOS Strings](https://transglot.ai/docs/developers/formats/apple) (`.strings`), [iOS Stringsdict](https://transglot.ai/docs/developers/formats/apple) (`.stringsdict`), [Apple String Catalog](https://transglot.ai/docs/developers/formats/apple) (`.xcstrings`) | Stringsdict and String Catalog |
| Android | [Android XML](https://transglot.ai/docs/developers/formats/android) | Yes |
| Flutter | [Flutter ARB](https://transglot.ai/docs/developers/formats/flutter-arb) | Yes |
| PHP and Laravel | [Laravel JSON](https://transglot.ai/docs/developers/formats/laravel), [Laravel PHP](https://transglot.ai/docs/developers/formats/laravel) | No |
| Interchange | [XLIFF 1.2 and XLIFF 2.0](https://transglot.ai/docs/developers/formats/xliff), [gettext PO](https://transglot.ai/docs/developers/formats/gettext) | gettext PO |
| .NET and Java | [.NET .resx](https://transglot.ai/docs/developers/formats/resx), [Java .properties](https://transglot.ai/docs/developers/formats/java-properties) | No |
| Spreadsheets | [CSV and XLSX](https://transglot.ai/docs/developers/formats/spreadsheets) | Yes, one row per form |
| Documents | [Markdown / MDX](https://transglot.ai/docs/developers/formats/markdown) | No |
| Games | Unity CSV (Unity Localization string tables), Unreal PO (Unreal Engine Localization Dashboard) | No |
## Things to know [#things-to-know]
- **Plurals in any format.** Formats without separate plural forms can still hold plurals as ICU messages inside a string, such as `{count, plural, one {# item} other {# items}}`. See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms).
- **Laravel.** Use Laravel JSON (`lang/{locale}.json`) to bring Laravel strings in. Laravel PHP array files that start with the standard comment header cannot be read, so the browser upload does not offer `.php` files.
- **Apple.** `.stringsdict` holds plural strings only; keep simple strings in `.strings`, or use a String Catalog for both.
- **Descriptions.** Flutter ARB, XLIFF 1.2, Apple String Catalog, gettext PO, CSV, XLSX, Unity CSV and Unreal PO carry a description for each string, which becomes the key's description. In other formats, write descriptions in the console. See [Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key).
- **Detection.** transglot detects a file's format from its contents and name. When it guesses wrong in the browser upload, pick the right format on the file's row before importing. See [Import content](https://transglot.ai/docs/guides/projects/import-content).
## Related [#related]
- [File formats reference](https://transglot.ai/docs/developers/formats)
- [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms)
- [The command line tool](https://transglot.ai/docs/developers/cli)
# The project overview (https://transglot.ai/docs/guides/projects/your-project/)
The overview is the page a project opens on. It answers "where is this project up to?": how much is translated and approved in each language, what is running, and what to do next. Everyone who can open the project sees it.
## The header [#the-header]
Under the project name, a line gives the source language, the number of target languages and the number of keys, for example `en → 3 locales · 1,204 keys`. When the project reused translations from memory this month, it adds how many words, as in "90 words from memory this month".
On the right, **Open editor** opens the [editor](https://transglot.ai/docs/guides/translating/the-editor), and **Deploy →** opens the project's releases, where you [publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale). A project with no keys yet shows **Add source strings →** instead, which opens the setup page where you [import content](https://transglot.ai/docs/guides/projects/import-content).
## The pipeline [#the-pipeline]
A strip of five figures sums up the project's work, left to right. Each one opens the place where that work is done.
| Figure | Counts | Opens |
| --- | --- | --- |
| **source** | Source strings (keys) in the project | The project |
| **translate** | Target strings that have a translation, of all target strings | The editor, filtered to untranslated strings |
| **review** | Translated strings flagged as needing a check by a person | The editor, filtered to strings that need checking |
| **approve** | Translated strings a person has approved | The quality dashboard |
| **deploy** | Target languages whose next publish would carry every string the project expects them to hold | Releases |
## Locales [#locales]
The **Locales** table has one row per target language. A sentence at the top totals the project, for example "1,040 of 1,200 translated, 610 approved, 12 need checking". Rows are sorted by what blocks shipping: languages with strings that need checking first, then the least translated.
| Column | Shows |
| --- | --- |
| **Locale** | The language name and its tag. |
| **Coverage** | A bar of translated strings, and the same sentence for this language alone. |
| **Approved** | Approved strings, out of the language's total. |
| **Quality** | How many strings need checking, or **clear**. |
| **State** | What is happening to the language now: "GLOSSA is translating, started 4 minutes ago", "queued to translate", "all keys translated", or how many strings are not translated. If the last run for the language failed, the state links to that run so you can see which strings failed. |
| **Next** | The one thing to do next in this language, as a link into the editor. |
The **Next** link is:
- **Review** when the language has strings that need checking and you can review it. It opens the editor on exactly those strings.
- **Fill gap** when strings are untranslated. It opens the editor on the untranslated strings.
- **Approve** when everything is translated but some strings are not approved yet, and you can review. It opens the editor on the strings waiting for approval.
- **Import** when the project has no source strings yet.
Select anywhere on a row to follow its **Next** link. **Remove** at the end of a row deletes that language; see [Remove a language](https://transglot.ai/docs/guides/projects/remove-a-language).
### Add a language [#add-a-language]
Below the rows, **Add a language** adds a target language: search by name, native name or tag, then select **Add**. See [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language).
## What to do next [#what-to-do-next]
The foot of the **Locales** table names the single most useful next step for you, worked out from the project's real state. While the project is being set up, it is the first unfinished setup step (add a target language, bring in source strings, run the first translation) with **Continue setup**. After that, it points at strings waiting for review first, then untranslated strings, with **Open editor**. When nothing is left that you can act on, it says nothing.
## Recent activity [#recent-activity]
**Recent activity** lists the latest changes to the project. **Translation runs** opens the list of every AI translation run, with its counts and any strings that failed. **View all** opens the full [project activity](https://transglot.ai/docs/guides/projects/project-activity).
## If something looks wrong [#if-something-looks-wrong]
- **"No target languages yet"**: the project has a source language but nothing to translate into. If you have the Developer role or above, add one in the row below.
- Every language reads "waiting for source strings": the project has no keys yet. Select **Add source strings →**.
- **Add a language** and **Remove** are not shown: managing languages needs the Developer role or above, with access to the language.
- **Review** or **Approve** is missing on a row: you cannot review that language. Members with the Reviewer or Admin role, and account Owners, can.
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Translation states](https://transglot.ai/docs/concepts/translation-states)
- [Workspaces and projects](https://transglot.ai/docs/concepts/workspaces-and-projects)
# Add a glossary term (https://transglot.ai/docs/guides/quality/add-a-glossary-term/)
Owners, Admins and Developers can add terms, on Starter and above.
## Add a term in the console [#add-a-term-in-the-console]
### Open the glossary [#open-the-glossary]
Open the project and select **Glossary** in the sidebar.
### Open the form [#open-the-form]
Select **Add term**. The **Add glossary term** dialog opens.
### Describe the term [#describe-the-term]
- **Source term**: the word or phrase as it appears in your source language. Up to 255 characters.
- **Notes** (optional): context for translators, such as what the term refers to.
- Tick **Case-sensitive** if capitals are part of the term.
- Tick **Do not translate** to keep the term exactly as written in every language, or **Forbidden** if the term must never appear in a translation.
### Save [#save]
Select **Add term**. The notice reads **"Term added."**
The term applies from the next translation, and the next quality check checks existing translations against it. It does not retranslate what you already have.
## Set the wording for each language [#set-the-wording-for-each-language]
The **Add glossary term** dialog sets the term and its rules. To fix the wording each language must use, such as `Kasse` for `Checkout` in German, import a CSV or TBX file with a column per language. Importing a term that is already in the glossary updates it rather than adding it twice, so you can add wording to terms you created in the console. See [Import and export a glossary](https://transglot.ai/docs/guides/quality/import-and-export-a-glossary).
## Add terms through the API [#add-terms-through-the-api]
A [deploy token](https://transglot.ai/docs/developers/deploy-tokens) with the `manage` ability can create a term, wording included, with `POST https://api.transglot.ai/v1/glossary`:
```bash title="Terminal"
curl -X POST https://api.transglot.ai/v1/glossary \
-H "Authorization: Bearer $TRANSGLOT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_term": "Checkout",
"part_of_speech": "noun",
"notes": "The payment page.",
"translations": { "de": "Kasse", "fr": "Paiement" }
}'
```
`case_sensitive`, `do_not_translate` and `forbidden` are optional booleans. A term marked do not translate or forbidden ignores `translations`. See the [API reference](https://transglot.ai/docs/api-reference).
## If it doesn't work [#if-it-doesnt-work]
- **No Add term button**: your role cannot change the glossary. Ask a Developer, an Admin or an account Owner.
- **"That term is already in this glossary."**: each source term appears once. To change an existing term's wording, import a file that includes it.
- **A translation ignores the term**: check that the source text contains the term as written (with the same capitals, if the term is case-sensitive), and that **Use the glossary** is on in the project's [AI profile](https://transglot.ai/docs/guides/quality/brand-voice#the-ai-profile). With [enforcement](https://transglot.ai/docs/guides/quality/the-glossary#enforcement) set to **Off**, the checks do not flag a translation that departs from the term.
# Brand voice (https://transglot.ai/docs/guides/quality/brand-voice/)
A project's voice is how its translations should sound: how formal, how warm, which words you always use and which you never do. You set it once in the project's **Settings → Voice**, and it travels with every translation request for the project.
Owners and Admins can change the voice. The style guide is available on every plan; inferring a guide from your work and the AI profile are included on Team and above.
## The style guide [#the-style-guide]
### How you sound [#how-you-sound]
The **How you sound** card holds the guide itself:
| Field | What it sets |
| --- | --- |
| **Formality** | How the product addresses the reader, from **very informal** to **very formal**. In French, for example, this is the choice between *tu* and *vous*. |
| **Warmth** | How much the writing sounds like a person, from **clinical** to **very warm**. |
| **Brand voice** | The product's personality in a line: the words you prefer and the ones you never use. |
| **Regional notes** | Anything true of one market and not the others, such as currency, address order, or a word that means something else there. |
| **Anything else worth saying** | Free-text guidance that does not fit the fields above. |
### Lines you never cross [#lines-you-never-cross]
**Lines you never cross** holds two lists of rules: **Always** and **Never**. Type a rule and press Enter to add it. The card shows how many rules travel with each request and how long each can be.
### One voice is not enough [#one-voice-is-not-enough]
**One voice is not enough** lets one language differ from the rest. Select **Every locale** to edit the base guide, or a language to edit its override. A language inherits the base guide and overrides only the fields you change, so German can be formal while Spanish is not, and both keep the same rules.
### Save [#save]
Changes are not applied until you select **Save changes** in the bar at the bottom of the page. **Discard** throws them away.
## Infer the guide from your work [#infer-the-guide-from-your-work]
Instead of writing the guide from scratch, select **Re-infer from your work** at the top of the page. Glossa reads the translations your team has already approved for the language you have selected (the first target language when **Every locale** is selected) and proposes a guide from them. The button reads **Reading your work…** while it works.
What comes back is a draft, filled into the language's fields: **"Draft style guide inferred for de. Review it below, then save."** Nothing is applied until you correct it and select **Save changes**.
Inferring uses your AI allowance and is included on Team and above.
## The AI profile [#the-ai-profile]
On Team and above, the AI profile adds two things to the style guide.
**Does it hold** holds example pairs: a source string, the translation you want matched, and an optional note on why it is right. Glossa is told to match them. One pair you have already signed off often teaches more than any adjective. The card shows how many pairs travel with each request.
**What it is built from** shows what goes into every request, with three switches:
| Switch | What it decides |
| --- | --- |
| **Use the glossary** | Whether matched [glossary](https://transglot.ai/docs/guides/quality/the-glossary) terms are given to Glossa for this project. |
| **Use the style guide** | Whether the style guide on this page is applied. Turned off, the guide reaches no request. The **applied to every draft** pill at the top of the page switches the same thing. |
| **Use semantic memory** | Whether Glossa may draw on similar translations a person has already reviewed. See [Translation memory](https://transglot.ai/docs/concepts/translation-memory). |
With an AI profile in place, quality checks also re-read what comes back and flag a translation that has drifted from the register you set. See [Quality checks](https://transglot.ai/docs/guides/quality/quality-checks).
To go back to no profile, select **Remove profile** and confirm. Translations made from the next run on no longer use the profile; strings already translated keep their wording.
## When changes apply [#when-changes-apply]
The voice applies to translations made after you save. It does not rewrite existing translations. To bring older strings in line, retranslate them, or [run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check) to find the ones that drifted.
## Related [#related]
# Configure checks and automation (https://transglot.ai/docs/guides/quality/configure-checks/)
The project's check settings live in **Settings → Checks**, which **Configure** on the Quality page also opens. Owners and Admins can change them. Every setting saves the moment you change it.
## Confidence routing [#confidence-routing]
**Route AI translations by confidence** is the first panel on the page. Each AI translation carries a confidence signal. Translations that pass the automatic checks and come back confident can be marked as not needing review, so your reviewers only see the ones worth their time. Everything else is flagged for review. Automatic checks always win: a translation with a broken placeholder, the wrong plural forms, or a glossary conflict is always sent to review no matter how confident it was.
It is off by default and available on Team and above. When you switch it on, a **Threshold** field sets how confident a translation has to be.
## Checks & automation [#checks--automation]
| Setting | Options | What it decides |
| --- | --- | --- |
| **Self-heal** | **Repair 12 strings** | Starts a one-off repair of the failing AI translations. See [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation). |
| **Glossary enforcement** | **Off**, **Warn** (default), **Enforce** | How strictly the terminology check treats glossary terms. See below. |
| **Message syntax** | **Auto** (default), **ICU**, **Legacy** | How the placeholder and plural checks read your strings. |
| **ICU dialect** | **ICU4C** (default), **Flutter** | Which ICU rules apply to apostrophes. |
| **Auto-heal mode** | **Off**, **Suggest** (default), **Apply** | What happens to an AI translation a quality check flags. |
| **Source drift repair** | **Off**, **Suggest** (default), **Auto** | What happens to translations when their source string changes. |
**Auto-heal mode** and **Source drift repair** run on Team and above. On other plans they can only be set to **Off**, and whatever is selected does nothing.
### Glossary enforcement [#glossary-enforcement]
The glossary is always given to Glossa when it translates. Enforcement decides what the terminology check does when a translation still departs from it:
- **Off**: terminology is not checked.
- **Warn**: a translation that misses a required term is kept and flagged for review.
- **Enforce**: a translation that misses a required term is treated as failing the check rather than only flagged.
Under **Warn** and **Enforce**, a forbidden term in a translation always fails the check. You can also change enforcement from the [Glossary](https://transglot.ai/docs/guides/quality/the-glossary) page.
### Message syntax and ICU dialect [#message-syntax-and-icu-dialect]
**Message syntax** tells the placeholder and plural checks how to read your strings:
- **Auto** reads a string as ICU MessageFormat only when it holds a plural, select, ordinal, number, date or time argument.
- **ICU** reads every string as ICU and checks apostrophes and tags strictly.
- **Legacy** never reads ICU.
On **ICU**, a translation that is not valid ICU MessageFormat is left out of what your app receives, from a [pull](https://transglot.ai/docs/developers/cli) straight away and from the CDN at the language's next [publish](https://transglot.ai/docs/guides/delivery/publish-a-locale). A version already live keeps its content until then.
**ICU dialect** matters for apostrophes: **Flutter** reads an apostrophe as plain text, the way Flutter's own tooling does. Pushing a Flutter ARB file sets it for you.
### Auto-heal mode [#auto-heal-mode]
After a quality check flags an AI translation, auto-heal decides what to do with it:
- **Off**: nothing. The finding waits for a reviewer.
- **Suggest**: a suggested fix is attached to the finding. A reviewer sees it under **Quality findings** in the editor and can select **Accept suggestion**.
- **Apply**: Glossa writes a fixed translation, which must pass every automatic check before it is saved.
A translation you have edited or reviewed is never overwritten by AI. Auto-heal only acts on translations Glossa wrote. See [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation).
### Source drift repair [#source-drift-repair]
When someone changes a source string, the existing translations of it may no longer match. With source drift repair on, transglot re-checks those translations shortly after the change (several quick edits to the same string are checked once) and:
- **Suggest**: attaches a candidate fix for a reviewer to accept.
- **Auto**: rewrites the AI translations the change broke.
A translation you have edited or reviewed is never overwritten by AI. This setting uses your AI allowance on every source edit that triggers it. See [What counts towards my usage](https://transglot.ai/docs/administration/billing/what-counts-towards-my-usage).
# Connect the in-context editor (https://transglot.ai/docs/guides/quality/connect-the-in-context-editor/)
The in-context editor lets a reviewer open any string where it appears in your app, read it in place, and, once editing is turned on, change it and capture a screenshot for the [screenshot library](https://transglot.ai/docs/guides/quality/the-screenshot-library). This page covers what an Owner or Admin sets up in transglot. Adding the editor to your app's code is a developer task, covered in [Install the in-context editor](https://transglot.ai/docs/developers/in-context/install).
The in-context editor is included on Starter and above.
## Create a publishable key [#create-a-publishable-key]
The editor identifies your project with a **publishable key**. It is designed to sit in your site's code, where anyone can read it, so it only works from the web addresses you list.
### Open the Connection card [#open-the-connection-card]
Open the project and select **Screenshots** in the sidebar. The key form is in the **Connection** card. (The same keys are listed in **Settings → Developer** under **Publishable keys**.)
### Name the key [#name-the-key]
Enter a **Key name** that identifies the site, such as `Production web app`. Use one key per app you add the editor to.
### List the allowed origins [#list-the-allowed-origins]
In **Allowed origins (one per line)**, list each address the editor will run on, with its scheme and any port:
```text title="Allowed origins"
https://app.example.com
https://staging.example.com
```
### Create it [#create-it]
Select **Create publishable key**. The **SDK snippet** card below is now pre-filled with the key.
Once the editor runs on your site, the key's status in **Connection** changes from **Never connected** to **Connected**, then shows when it was **Last seen**.
## Show the editor to reviewers only [#show-the-editor-to-reviewers-only]
The editor cannot tell a reviewer from a visitor, so your app decides who gets it. The snippet on the **Screenshots** page, and under **One provider** in [Releases → Connect & SDK](https://transglot.ai/docs/guides/delivery/the-deploy-screen#the-connect--sdk-view), adds the editor only for signed-in users your app treats as reviewers. Keep that check when you adapt it.
## Turn on editing and screenshots [#turn-on-editing-and-screenshots]
With the publishable key alone, the editor is **read-only**: reviewers can read strings in place, but saving is disabled, AI suggestions are not offered, and nothing is captured for the screenshot library. A key that anyone can copy from your page source must never be enough to change your translations.
To let a reviewer edit, your own server vouches for them:
1. Your app passes the editor the strings this reviewer may change (`writableKeys`) and a function (`getNonce`) that asks **your** server for a one-time pass.
2. Your server calls `POST https://api.transglot.ai/v1/incontext/session-nonce` with a [deploy token](https://transglot.ai/docs/developers/deploy-tokens) that has the `write` ability, naming the publishable key and the strings, and returns the single-use nonce it gets back. The nonce expires about a minute after it is issued.
3. The editor trades the nonce for an editing session limited to those strings.
The deploy token is a secret: keep it on your server, never in the page. The snippet in the console already includes `writableKeys` and `getNonce`, wired to a `/api/transglot-nonce` route on your side for you to implement. [Install the in-context editor](https://transglot.ai/docs/developers/in-context/install) has the server code.
Once this is in place, **Save** works, AI suggestions appear, and the screenshots reviewers capture while editing reach the library.
## Delete a key [#delete-a-key]
Select **Delete** next to the key in **Connection** and confirm. Every site using it stops loading the editor straight away. Create a replacement and roll it out first if the key is in use.
## If it doesn't work [#if-it-doesnt-work]
| What you see | What to check |
| --- | --- |
| The editor does not load | The site's address must be in **Allowed origins** exactly, including `https://` and any port. Otherwise the editor is refused with **"This publishable key is not authorized for this origin."** |
| Every field is read-only and Save is disabled | The editor has no `getNonce`, or your server did not return a nonce. That is the read-only default, not a fault: see [Turn on editing and screenshots](#turn-on-editing-and-screenshots). |
| Nothing reaches the screenshot library | Same cause: screenshots are captured as part of an edit, so a read-only session never produces one. |
| The key form is not shown | Only Owners and Admins can create publishable keys or see the existing ones. |
| **"The in-context editor unlocks on the Starter plan."** | Your plan does not include the in-context editor. |
## Related [#related]
# Import and export a glossary (https://transglot.ai/docs/guides/quality/import-and-export-a-glossary/)
Import is the way to set the wording each language must use for a term, and to bring a termbase over from another tool. Export gives you the whole glossary as a file.
Owners, Admins and Developers can import and export, on Starter and above.
## Import a file [#import-a-file]
### Open the import [#open-the-import]
On the project's **Glossary** page, select **Import CSV or TBX**. The **Import terms from a file** dialog explains the columns a CSV can have.
### Start from the template (optional) [#start-from-the-template-optional]
Select **Download a CSV template** for a file with the right columns and one column per target language of this project.
### Choose your file [#choose-your-file]
Select **Choose a file** and pick a CSV or TBX file of up to 20 MB. Nothing is imported yet: transglot reads the file and shows what it would change.
### Check the preview [#check-the-preview]
**Import these changes?** counts the **New terms**, **Changed terms**, terms **Already the same**, and **Rows skipped**, and lists each change under **What changes**. Read the skipped rows and any columns the import will not read.
### Import [#import]
Select **Import 12 terms**. The notice confirms what happened, for example **"Imported 12 terms: 9 added, 3 updated."**
## CSV columns [#csv-columns]
The first row is the header. Column names are not case-sensitive.
| Column | Required | What it holds |
| --- | --- | --- |
| `source_term` | Yes | The term as it appears in your source text. Up to 255 characters. |
| One column per language | No | Headed by the language code, such as `fr` or `de_DE`. Each cell holds the wording that language must use. |
| `part_of_speech` | No | The kind of word, such as `noun`. Up to 30 characters. |
| `notes` | No | A note for translators. Up to 2,000 characters. |
| `case_sensitive` | No | `1` for yes, `0` for no. |
| `do_not_translate` | No | `1` for yes, `0` for no. |
| `forbidden` | No | `1` for yes, `0` for no. |
```csv title="glossary.csv"
source_term,part_of_speech,case_sensitive,do_not_translate,forbidden,notes,de,fr
Checkout,noun,0,0,0,The payment page.,Kasse,Paiement
Acme Cloud,,1,1,0,Product name.,,
```
## TBX files [#tbx-files]
A TBX file exported from a terminology tool works too. Each entry needs a term in the project's source language; entries without one are skipped.
## How an import changes the glossary [#how-an-import-changes-the-glossary]
- **A term already in the glossary is updated, not added twice.** This is how you add wording to terms you created in the console.
- **An empty cell, or a column the file does not have, leaves what is already there.** Wording in languages the file leaves out is kept.
- **Marking a term do not translate or forbidden removes its wording**, because those terms carry none. The preview warns you before you import.
- **Importing does not approve a candidate.** A term waiting under **Review candidates** stays there. A term you rejected earlier stays rejected.
- **Your language access applies.** If your access is limited to some languages and the file changes wording in others, the import is refused: **"This file changes wording in languages you do not have access to, so it cannot be imported."** Remove those changes from the file, or ask a project admin to import it.
## Export the glossary [#export-the-glossary]
Select **Export** on the **Glossary** page to download every approved term as `glossary-{project}.csv`, in the same columns the import reads. The file can be edited and imported again.
To export as TBX, use the API with a [deploy token](https://transglot.ai/docs/developers/deploy-tokens) that has the `read` ability:
```bash title="Terminal"
curl "https://api.transglot.ai/v1/glossary/export?format=tbx" \
-H "Authorization: Bearer $TRANSGLOT_TOKEN" \
-o glossary.tbx
```
The API also accepts an import at `POST https://api.transglot.ai/v1/glossary/import`. See the [API reference](https://transglot.ai/docs/api-reference).
## If it doesn't work [#if-it-doesnt-work]
- **"The columns in this file were not recognised, so no row can be read."**: the file has no `source_term` column. Check the header row.
- **"These columns are not read"**: a column is neither one of the fixed fields nor a language code. Head each language column with its code, such as `fr` or `de_DE`.
- **"That file is larger than 20 MB."**: split it into smaller files.
- **"That file could not be read."**: check it is a CSV or TBX file.
- **Rows skipped**: the preview gives each skipped row's number and reason, such as **"Row 7: no source term."**
# Quality checks (https://transglot.ai/docs/guides/quality/quality-checks/)
transglot checks translations for specific, nameable problems: a placeholder that went missing, a plural form the language needs, a glossary term that was not used, a sentence that changes the meaning. Each problem becomes a **finding** on the string, and a string with a serious finding is flagged for a person to check.
## What is checked [#what-is-checked]
There are two kinds of check. The automatic checks follow fixed rules and cost nothing. The AI review reads the translation against its source the way a reviewer would.
| Check | Kind | What it catches |
| --- | --- | --- |
| **Placeholders** | Automatic | A placeholder or variable from the source (such as `{name}` or `%s`) that is missing, extra or altered in the translation. |
| **Plurals** | Automatic | A plural string that lacks a form its language needs. |
| **Length** | Automatic | A translation longer than the maximum length set on its key. |
| **Overflow** | Automatic, advisory | A translation so much longer than its source that it may not fit where the source fits. It is reported but never blocks. |
| **Terminology** | Automatic | A [glossary](https://transglot.ai/docs/guides/quality/the-glossary) term translated differently from its required wording, or a forbidden term that appears. |
| **Accuracy** | AI review | The translation does not say what the source says: an omission, an addition or a mistranslation. |
| **Fluency** | AI review | Grammar, spelling or punctuation that is wrong in the target language. |
| **Style** | AI review | Wording that is awkward, inconsistent, or off the project's [voice](https://transglot.ai/docs/guides/quality/brand-voice). |
If the project's **Screenshot context** setting is on, the AI review also sees the screenshot captured for a key, so it can flag text that overflows the space it renders in. See [The screenshot library](https://transglot.ai/docs/guides/quality/the-screenshot-library).
## When the checks run [#when-the-checks-run]
- **As each AI translation is written.** Every translation Glossa produces goes through the automatic checks before it is saved. Automatic checks always win: a translation with a broken placeholder, the wrong plural forms, or a glossary conflict is always sent to review no matter how confident it was.
- **While you type in the editor.** The editor checks your draft against the same rules before you save it.
- **When you run a quality check.** A full check runs the automatic checks and the AI review, and gives each translation a [quality score](https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean). See [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check).
- **When a source string changes**, if **Source drift repair** is on. See [Configure checks and automation](https://transglot.ai/docs/guides/quality/configure-checks).
The automatic checks run on every plan. The AI review, the quality score, the [scorecard](https://transglot.ai/docs/guides/quality/the-per-language-scorecard) and [self-heal](https://transglot.ai/docs/guides/quality/self-heal-a-translation) are included on Team and above. See [pricing](https://transglot.ai/pricing).
## Severity [#severity]
Every finding has one of three severities: **minor**, **major** or **critical**. Critical findings are counted as **blocking** on the Quality page. A translation is flagged for review when it has a finding of major severity or above, or when its quality score is low.
A flag does not stop a translation from being published. It puts the string in front of a reviewer: flagged strings appear under **Needs checking** in the editor and on [Releases](https://transglot.ai/docs/guides/delivery/the-deploy-screen). See [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
## The Quality page [#the-quality-page]
Open the project and select **Quality** in the sidebar. Everyone who can open the project can read it.
Before the first check has run, the page lists each language with how many of its translated strings are waiting to be scored, under **Nothing here has been checked yet**.
After a check, the page header shows:
- the **mqm · project average** score, with its change and trend over the last 12 weeks,
- how many translated strings have been checked (**checked 120 of 140 translated strings**),
- the count of blocking findings and warning findings,
- how many strings need checking, and when the last check ran.
Below the header:
| Section | What it shows |
| --- | --- |
| **Every check, every locale** | A grid of languages against checks, with the number of findings in each cell. A language added since the last check reads **not scored**. Select a cell to filter the findings list to that language and check. |
| **Findings** | The strings behind the selected cell: each finding's check, severity and message. |
| **MQM dimensions** | The selected language's findings grouped into the five MQM categories. |
| **All languages** | Each language's score. |
The buttons in the header:
- **Configure** opens the project's check settings (Owners and Admins).
- **Re-run checks** starts a quality check. See [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check).
- **Fix 12 with Glossa** repairs the failing AI translations. See [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation).
The **Scorecard** tab beside **Overview** opens the [per-language scorecard](https://transglot.ai/docs/guides/quality/the-per-language-scorecard).
## Findings in the editor [#findings-in-the-editor]
In the editor, a string with findings shows how many it has. Open them to see **Quality findings** for that string: each finding's category, severity and message. When a finding comes with a suggested fix, **Accept suggestion** saves it as your own edit.
# Review extracted glossary terms (https://transglot.ai/docs/guides/quality/review-extracted-glossary-terms/)
An extraction reads your project's source strings, finds the terms that recur, and proposes the ones worth pinning as glossary candidates. Nothing is added on your behalf: a candidate is not enforced until someone approves it.
Owners, Admins and Developers, on Team and above. An extraction uses your AI allowance, and each plan includes a set number of extraction runs per cycle.
## Run an extraction [#run-an-extraction]
### Open the candidates [#open-the-candidates]
Open the project's **Glossary** and select the **Review candidates** tab.
### Extract terms [#extract-terms]
Select **Extract terms**. The notice reads **"Extraction queued. Terms appear under Review candidates as the run finds them."**
An extraction only reads source strings that changed since the last one, so running it again on an unchanged project finds nothing new and costs nothing. Terms already in the glossary, in any state, are never proposed again. When there is nothing new, the notice reads **"Nothing to extract. No new candidate terms were found in the source strings."**
## Review the candidates [#review-the-candidates]
Candidates are listed under **Waiting for your decision**, most confident first. Each shows the term, its kind of word where one was found, and how confident the extraction is that it belongs in a glossary.
- **Approve** moves the term to the **Terms** tab, where it is matched and enforced from the next translation: **"“Checkout” approved. It is now under Terms."**
- **Reject** drops it: **"“Checkout” rejected. It will not be proposed again."**
A candidate can carry suggested wording for your target languages. The candidate list does not show it; once the term is approved, its wording appears in the language columns of the **Terms** tab. Check it there, and to change it, [import a file](https://transglot.ai/docs/guides/quality/import-and-export-a-glossary) that includes the term.
## From the API [#from-the-api]
A [deploy token](https://transglot.ai/docs/developers/deploy-tokens) with the `manage` ability can start an extraction with `POST https://api.transglot.ai/v1/glossary/extract`. A token with the `read` ability can list candidates with `GET https://api.transglot.ai/v1/glossary?status=pending`. See the [API reference](https://transglot.ai/docs/api-reference).
## If it doesn't work [#if-it-doesnt-work]
- **"Nothing awaiting review"**: no extraction has run, or you have already decided on everything it found.
- **"Candidate extraction unlocks on the Team plan."**: extraction is not on your plan. You can still [add terms](https://transglot.ai/docs/guides/quality/add-a-glossary-term) yourself.
- **No Extract terms button**: your role cannot change the glossary. Ask a Developer, an Admin or an account Owner.
# Run a quality check (https://transglot.ai/docs/guides/quality/run-a-quality-check/)
A quality check runs the automatic checks and the AI review over your translations, gives each one a [quality score](https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean), and records what it finds. See [Quality checks](https://transglot.ai/docs/guides/quality/quality-checks) for what is checked.
Owners, Admins and Developers, on Team and above. A check uses your AI allowance, and the confirmation tells you how much before anything starts.
## From the Quality page [#from-the-quality-page]
### Open Quality [#open-quality]
Open the project and select **Quality** in the sidebar.
### Pick a language (optional) [#pick-a-language-optional]
Select a language's row in **Every check, every locale** to check that language first. With no row selected, the check covers every language.
### Start the check [#start-the-check]
Select **Re-run checks**. The button reads **Working out the cost…** while transglot counts what the check would cover.
### Confirm [#confirm]
The confirmation, **Run the checks on German?**, shows how many strings and words the check covers, and what that leaves of this cycle's allowance. Under **Which languages**, choose **German only** or every language. Select the button that names the word count, for example **Check 1,240 words**, to start.
The check runs in the background, so you can leave the page. As it works through your strings, the grid, the findings and the scores fill in. When it finishes, the people on the project get a **QA finished** notification, unless they have turned **QA run completed** off in their [notification preferences](https://transglot.ai/docs/administration/account/notification-preferences).
## From the editor [#from-the-editor]
### Open the language [#open-the-language]
Open the **Editor** and choose the language to check.
### Run QA [#run-qa]
Select **Run QA** in the toolbar and confirm. The button reads **Checking quality…** while the check starts.
Findings appear on each string in the editor: a string with findings shows how many, and opening them shows **Quality findings** for that string.
## What a check covers [#what-a-check-covers]
A check only reads what has changed. A translation that has not changed, and whose source has not changed, since it was last checked is skipped, so running a second check soon after the first costs a fraction of the first.
Findings never change your translations. They tell you what to look at. To have transglot fix what it finds, see [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation). If **Auto-heal mode** is on for the project, the confirmation says so: strings the check flags are then sent for a fresh translation afterwards, which spends their own words on top of the check.
## From the API [#from-the-api]
A [deploy token](https://transglot.ai/docs/developers/deploy-tokens) with the `manage` ability can start a check with `POST https://api.transglot.ai/v1/qa/run`. See the [API reference](https://transglot.ai/docs/api-reference).
## If it doesn't work [#if-it-doesnt-work]
- **No Re-run checks button**: your role cannot run checks, or your plan does not include the AI review. On plans without it, the Quality page reads **"Agentic QA scoring unlocks on the Team plan."**
- **"Nothing to check. No string in German needs a check right now."**: every translation in that language has already been checked and nothing has changed since.
- **"The figures for this run could not be loaded, so nothing was started."**: try again in a moment.
- **The check is refused for your allowance**: the confirmation says so before anything starts, and nothing is charged. See [What counts towards my usage](https://transglot.ai/docs/administration/billing/what-counts-towards-my-usage).
# Self-heal a translation (https://transglot.ai/docs/guides/quality/self-heal-a-translation/)
Self-heal takes an AI translation that a [quality check](https://transglot.ai/docs/guides/quality/quality-checks) flagged and asks Glossa for a better one. It works in two ways: automatically after each quality check, following the project's **Auto-heal mode**, or as a one-off repair you start yourself.
Self-heal is included on Team and above.
## What self-heal may and may not change [#what-self-heal-may-and-may-not-change]
| Self-heal can | Self-heal never |
| --- | --- |
| Rewrite a translation Glossa wrote that a check flagged for review. | Touches a translation you have edited or reviewed. A translation you have edited or reviewed is never overwritten by AI. |
| Attach a suggested fix to a finding, for a reviewer to accept. | Saves a rewrite that breaks an automatic check: one with a broken placeholder, missing plural forms or a forbidden glossary term is discarded. If no attempt passes, the translation is left as it was. |
| Save its rewrite as a new version in the string's history. | Approves a translation. A repaired translation is still an AI draft until someone approves it. |
Because every applied repair is saved as a version, you can restore the previous wording from the string's [version history](https://transglot.ai/docs/guides/translating/version-history).
## Automatic self-heal [#automatic-self-heal]
Set **Auto-heal mode** in the project's **Settings → Checks** (Owners and Admins):
- **Off**: flagged translations wait for a reviewer.
- **Suggest** (the default): after a check, each flagged AI translation gets a suggested fix attached to its finding. Nothing in the translation changes. A reviewer opens **Quality findings** for the string in the editor and selects **Accept suggestion**, which saves the fix as their own edit.
- **Apply**: after a check, Glossa rewrites each flagged AI translation, within the limits above.
Automatic self-heal uses your AI allowance. When **Auto-heal mode** is on, the confirmation for a quality check says that flagged strings will be sent for a fresh translation afterwards.
## Repair the failing translations now [#repair-the-failing-translations-now]
Owners, Admins and Developers can repair every flagged AI translation in one go, whatever the **Auto-heal mode**.
### Start the repair [#start-the-repair]
On the project's **Quality** page, select **Fix 12 with Glossa** (the number is how many flagged AI translations there are). Owners and Admins can also use **Repair 12 strings** in **Settings → Checks**.
### Read the confirmation [#read-the-confirmation]
**Repair 12 strings?** states what will happen: Glossa re-translates the failing AI translations and overwrites their current values. It shows how many words that is and what it leaves of this cycle's allowance. If the project's **Auto-heal mode** is **Off**, it also says that running the repair overrides that setting this once.
### Confirm [#confirm]
Select the button that names the word count, for example **Repair 84 words**. The repair runs in the background.
When it finishes, the project's Owners, Admins and Developers get a **Self-heal completed** notification. The repaired translations are not scored yet: [run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check) to score them again.
If your access is limited to some languages, the repair covers those languages only.
## If it doesn't work [#if-it-doesnt-work]
- **No Fix with Glossa button**: your role cannot run repairs, or your plan does not include self-heal.
- **Nothing left to fix** or **"There is nothing for a repair to rewrite right now."**: no AI translation is currently flagged. Flagged translations that someone edited or reviewed are not repaired, by design.
- **A translation is still flagged after a repair**: no rewrite passed the automatic checks, so the original was kept. Fix it in the editor.
# The glossary (https://transglot.ai/docs/guides/quality/the-glossary/)
The glossary is the project's list of terms with fixed rules: your product name that is never translated, a feature name with an approved wording in each language, a word you never want to see. Glossary terms are matched against source text and enforced during translation and during quality checks; a translation that departs from a glossary term is flagged.
Open the project and select **Glossary** in the sidebar. The glossary is included on Starter and above.
## What a term can say [#what-a-term-can-say]
| Rule | What it means |
| --- | --- |
| **Wording per language** | The exact wording a language must use for the term, such as `Kasse` in German for `Checkout`. Set it by [importing a file](https://transglot.ai/docs/guides/quality/import-and-export-a-glossary) or through the API. |
| **Do not translate** | The term stays exactly as written in every language, such as a brand or product name. It carries no wording. |
| **Forbidden** | The term must never appear in a translation, such as a retired product name. It carries no wording. |
| **Case-sensitive** | The term only matches with the same capitals, so `Pro` and `pro` are different terms. |
| **Notes** | Context for translators, such as what the term refers to. |
| **Kind of word** | The part of speech, such as noun. Set by import or the API. |
## How terms are used [#how-terms-are-used]
- **During translation.** When a string contains a term, the term travels with the request, so Glossa uses the wording you pinned.
- **In the checks.** The terminology check compares each translation with the glossary. See [Quality checks](https://transglot.ai/docs/guides/quality/quality-checks).
- **In the editor.** The **Glossary** section of the editor's context panel lists the terms in the string you are on, with what each must become, so nobody has to remember the list.
Adding or changing a term does not retranslate what you already have. [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check) to find existing translations that conflict with it.
## Enforcement [#enforcement]
The **Enforcement** control on the **Locked terms** card sets how strictly the terminology check treats the glossary:
| Setting | A required term is missing | A forbidden term appears |
| --- | --- | --- |
| **Off** | Not checked | Not checked |
| **Warn** (default) | Flagged for review | Fails the check |
| **Enforce** | Fails the check | Fails the check |
Owners and Admins can change it, here or in [Settings → Checks](https://transglot.ai/docs/guides/quality/configure-checks#glossary-enforcement).
## The Glossary page [#the-glossary-page]
| Part | What it does |
| --- | --- |
| **Terms** tab | Every approved term, under **Locked terms**, with a column per language for its wording. **Filter by locale** narrows the list to one language. |
| **Review candidates** tab | Terms an AI extraction proposed, waiting for approval. See [Review extracted glossary terms](https://transglot.ai/docs/guides/quality/review-extracted-glossary-terms). |
| **Add term** | Adds a term. See [Add a glossary term](https://transglot.ai/docs/guides/quality/add-a-glossary-term). |
| **Import CSV or TBX** and **Export** | Bring terms in from a file, or download them. See [Import and export a glossary](https://transglot.ai/docs/guides/quality/import-and-export-a-glossary). |
Everyone who can open the project can read the glossary. Owners, Admins and Developers can add, import, export and delete terms. If your access is limited to some languages, you can change wording in those languages only.
## Delete a term [#delete-a-term]
Select **Delete** on the term's row, then **Delete term** to confirm. The term stops being enforced from the next translation run. Wording already written into existing translations is left alone.
## Related [#related]
# The per-language scorecard (https://transglot.ai/docs/guides/quality/the-per-language-scorecard/)
The scorecard turns the results of your [quality checks](https://transglot.ai/docs/guides/quality/quality-checks) into one score per language, in the industry-standard MQM vocabulary. Use it to compare languages, to see which kind of problem a language has, and to answer a procurement questionnaire or a renewal review without assembling the numbers by hand.
Open the project, select **Quality**, then the **Scorecard** tab. Everyone who can open the project can read it. The figures are included on Team and above; on other plans the page keeps its layout with no figures, under **"The MQM quality scorecard unlocks on the Team plan."**
## What it shows [#what-it-shows]
The scorecard re-expresses findings your quality checks already produced. It does not re-examine your translations, and reading or exporting it costs nothing from your AI allowance.
**Per-language quality** has one row per target language:
| Column | What it shows |
| --- | --- |
| **Language** | The target language. |
| **MQM score** | The language's quality score, 0 to 100. See [What does the quality score mean?](https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean) |
| **Checked of translated** | How many of the language's translated strings have been checked. A score covers only those. |
| **% needing a check** | The share of translated strings flagged for review. |
| **Avg confidence** | The average confidence of the language's AI translations. |
Below the table:
- **Auto-approve rate**, **Strings gate-scored** and **Mean confidence**: how much of the AI output was routed past review, for projects that route by confidence. See [Configure checks and automation](https://transglot.ai/docs/guides/quality/configure-checks#confidence-routing).
- **Top MQM error dimensions**: where findings fall across Accuracy, Fluency, Terminology, Style and Locale convention.
- **Quality over time**: each language's weekly score over the last 12 weeks.
- A methodology note stating how the figures are computed, for the reader who wants to check them.
## Export the scorecard [#export-the-scorecard]
Select **Print / PDF** and choose your browser's save-as-PDF option. The console's navigation is left out of the printout, so what you get is the scorecard alone, with the project name and when it was generated.
Select **Export CSV** (Owners and Admins). The file, `mqm-scorecard-{project}-{date}.csv`, has two blocks:
1. One row per language: `Language`, `Locale`, `MQM score`, `QA score`, `Checked`, `Total`, `% needs review`, `Avg confidence`.
2. One row per language with its number of findings in each MQM category.
On plans with [custom branding](https://transglot.ai/docs/administration/workspace/custom-branding), the printout carries your logo and product name, and the CSV is titled with your product name. Otherwise the CSV is titled with the workspace name.
## If it doesn't work [#if-it-doesnt-work]
- **"No scored languages yet"**: the project has nothing to score yet. Translate some strings, then [run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check).
- **A language has no MQM score**: none of its strings has been checked yet. Run a check on that language.
- **No Export CSV button**: exporting needs the Admin role, or an Owner of the account.
# The screenshot library (https://transglot.ai/docs/guides/quality/the-screenshot-library/)
A row in the editor does not tell you whether a translation fits its button. A screenshot does. The screenshot library collects the screenshots your reviewers capture with the [in-context editor](https://transglot.ai/docs/guides/quality/connect-the-in-context-editor) while they edit strings on your own site, each one tagged with the key it shows.
Open the project and select **Screenshots** in the sidebar. Everyone who can open the project can browse it. Capturing new screenshots needs the in-context editor, which is included on Starter and above.
## What is on the page [#what-is-on-the-page]
| Section | What it holds |
| --- | --- |
| **Connection** | The project's publishable keys, each with its status: **Connected**, **Last seen** with a time, or **Never connected**. Owners and Admins create keys here. See [Connect the in-context editor](https://transglot.ai/docs/guides/quality/connect-the-in-context-editor). |
| **SDK snippet** | The code to add the in-context editor to your app, pre-filled with this project's key. |
| **Screenshots can contain personal data.** | How screenshots are stored, and when they are deleted. |
| **What your reviewers saw** | The gallery. Each screenshot is captioned with the key it shows, or **Untagged**. |
## Where screenshots are used [#where-screenshots-are-used]
Screenshots are context, not content. They never change a translation. They are used:
- **By reviewers**, to judge whether a translation fits the space and the situation.
- **By Glossa**, when the project's **Screenshot context** setting is on (Settings → AI translation, Team and above). A key's screenshot then goes with its translation request, so the wording is written for where it appears, and [quality checks](https://transglot.ai/docs/guides/quality/quality-checks) can flag text that overflows the space it renders in. Screenshots add image cost to every request that uses them.
- **In review sheets** exported for a person to work on, which include a link to each string's screenshot.
## Privacy and retention [#privacy-and-retention]
Screenshots are taken of your live app, so they can show personal data. transglot strips the image metadata, stores screenshots privately, and serves them only through short-lived links. Every screenshot is deleted automatically 90 days after it is captured.
## Delete a screenshot [#delete-a-screenshot]
Owners, Admins and Developers can delete a screenshot sooner, for example one that captured sensitive information. Select **Delete** on the screenshot, then **Delete screenshot** to confirm. The image is removed permanently. The keys it showed keep their translations but lose the visual context.
## If it doesn't work [#if-it-doesnt-work]
- **"No screenshots yet"**: nobody has captured one. Screenshots are taken as part of an edit, so the in-context editor must be connected with editing turned on. A read-only session never captures one. See [Connect the in-context editor](https://transglot.ai/docs/guides/quality/connect-the-in-context-editor#turn-on-editing-and-screenshots).
- **"The in-context editor unlocks on the Starter plan."**: screenshots already captured stay visible and can be deleted, but new captures need a plan that includes the in-context editor.
# What does the quality score mean? (https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean/)
The quality score is a 0-100 rating that transglot produces for a translation, alongside a list of specific findings, each with a severity. The scorecard groups those findings using the industry-standard MQM categories so they can be compared with other tools' reports.
## Where you see it [#where-you-see-it]
- **On a string.** A checked translation carries its score and its findings. The editor's grid shows it as a colour-coded **MQM** chip, and the findings list on the Quality page labels it **passed**, **needs review** or **failed**.
- **Per language and per project.** The [Quality](https://transglot.ai/docs/guides/quality/quality-checks) page shows an MQM score for each language and the project average (**mqm · project average**), with its trend over the last 12 weeks.
- **On the scorecard.** The [per-language scorecard](https://transglot.ai/docs/guides/quality/the-per-language-scorecard) puts every language's score side by side, ready to export.
## The five categories [#the-five-categories]
Every finding falls into one of the five MQM categories:
| Category | What it covers | Checks that report it |
| --- | --- | --- |
| **Accuracy** | The translation does not say what the source says. | Accuracy |
| **Fluency** | Grammar, spelling or punctuation is wrong in the target language. | Fluency |
| **Terminology** | A term departs from your glossary. | Terminology |
| **Style** | Awkward, off-register or inconsistent wording. | Style |
| **Locale convention** | Placeholders, plural forms, length limits, and text that may not fit. | Placeholders, Plurals, Length, Overflow |
## Reading a score [#reading-a-score]
- **Read the findings, not only the number.** Two translations can score the same for different reasons. The findings say what is wrong and how serious it is; the score is the summary you report.
- **A score is not a gate.** A low score does not stop anything from being published. It flags the string for review, and a reviewer decides.
- **A score is not a certification.** It is transglot's assessment, expressed in a shared vocabulary so it can be compared with other reports.
- **Only checked translations have a score.** A language added since the last check reads **not scored**. [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check) to score it.
- **A score describes the translation as it was checked.** When a translation or its source changes, the next check scores it again.
The quality score is included on Team and above.
# AI translation settings (https://transglot.ai/docs/guides/translating/ai-translation-settings/)
Each project has its own AI translation settings. Open the project, select **Settings**, then **AI translation**. Admins and the account's Owners can change them; everyone else sees them read-only. Changes save as soon as you make them and apply to the next translation run.
Settings marked **Available on Team and above** are switched off and cannot be turned on while the workspace is on the Free or Starter plan.
## Drafting [#drafting]
### Auto-translate [#auto-translate]
"Automatically queue translations when the source text changes."
On by default. With it on, new and changed source strings are translated into every target language as soon as they arrive, whether they come from the CLI, an upload, the API or an integration, and adding a language translates the existing strings into it. With it off, nothing is translated until someone runs it from the editor. See [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai).
A translation you have edited or reviewed is never overwritten by AI, whether a run was started automatically or by hand.
### Quality mode [#quality-mode]
"Use the higher-quality model for every target language in this project."
With Quality mode off, Glossa translates with **Glossa Flux**, the fast default model. With it on, every target language in the project is translated with **Glossa Deep**, the quality model. Use it for projects where wording matters more than speed, such as marketing copy or legal text. Team and above.
Quality mode applies to runs started after you turn it on. Translations that already exist are not redone.
## Memory [#memory]
### Translation memory [#translation-memory]
"Reuse an identical machine translation instantly instead of paying to translate it again."
When the exact same source text has been translated before, transglot reuses that translation at no cost instead of translating it again. Exact-match reuse draws on an anonymised, shared pool as well as your own workspace's history. Under the switch, the page shows how many words this project reused this month.
Turn it off and the project neither reuses translations from the pool nor adds its own to it: every string is translated fresh. See [Translation memory](https://transglot.ai/docs/concepts/translation-memory).
### Semantic translation memory [#semantic-translation-memory]
"Find near-matching human-reviewed translations to suggest to reviewers and to ground the AI. Fuzzy matches are never applied automatically."
With it on, transglot also finds previously reviewed translations of similar text and offers them as suggestions. A similar match is only ever a suggestion; it is never written to your project automatically. Reviewers see these matches in the editor under **Similar approved translations**, and Glossa uses them as reference when it translates. Team and above.
Two settings refine it, and are available only while it is on:
| Setting | What it does |
| --- | --- |
| **Minimum match** | "How close a fuzzy match must be before it is worth showing. A legal project may want nothing under 95%; a marketing one is glad of 75%." **Exact only** shows no similar matches. |
| **Match sources** | Where similar matches come from: **All projects here** (every project in this workspace) or **This project only**. Choose **This project only** when this project's reviewed translations should not surface in other clients' work, or the other way round. |
## What the model can see [#what-the-model-can-see]
### Screenshot context [#screenshot-context]
When a key has a screenshot, Glossa is shown it with the string, so it can see where the text appears and write something that fits. It also lets quality checks flag translations that overflow the space they render in. Runs that use screenshots cost more. Team and above. See [The screenshot library](https://transglot.ai/docs/guides/quality/the-screenshot-library).
## Related settings [#related-settings]
These live on the project's other settings pages:
- **Route AI translations by confidence**, under **Settings**, then **Checks**: lets confident translations that pass every automatic check skip review. See [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
- **Voice**: the project's brand voice, which Glossa follows. See [Brand voice](https://transglot.ai/docs/guides/quality/brand-voice).
- **Glossary enforcement**, under **Checks**: how strictly glossary terms are enforced. See [The glossary](https://transglot.ai/docs/guides/quality/the-glossary).
## If it doesn't work [#if-it-doesnt-work]
- A switch is greyed out with **Available on Team and above**: the account's plan does not include it. See [Change your plan](https://transglot.ai/docs/administration/billing/change-your-plan).
- Every switch is greyed out: you are not an Admin of this workspace or an Owner of its account.
- **Minimum match** and **Match sources** are greyed out: turn on **Semantic translation memory** first.
## Related [#related]
- [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai)
- [AI translation](https://transglot.ai/docs/concepts/ai-translation)
- [Glossary and brand voice](https://transglot.ai/docs/concepts/glossary-and-brand-voice)
# Approve or flag a translation (https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation/)
Approving is a reviewer's decision, separate from writing. Members with the Reviewer or Admin role, and the account's Owners, can approve and flag. A reviewer scoped to particular languages can only review those languages. Translators and Developers can write translations but not approve them; in **Side by side** they see "Approving is a reviewer’s right. Yours is to translate, so this string moves on when a reviewer reads it."
## Review one string [#review-one-string]
### Open the strings to review [#open-the-strings-to-review]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), choose the language. To see only what needs you, open **Filters** and tick **Needs checking** or **Waiting for approval**, or follow **Review** or **Approve** from the [project overview](https://transglot.ai/docs/guides/projects/your-project).
### Read it [#read-it]
Read the translation against the source. If it needs a correction, make it and save it first.
### Decide [#decide]
Select **Approve** to approve it and move to the next string, or **Flag for a check** to mark it as needing another look. `⌘` `Enter` also approves and moves on.
### Select the row [#select-the-row]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), switch to **Table** and select the row.
### Decide [#decide-1]
In the context panel, under **Approval**, select **Approve** or **Flag**. `⌘` `Enter` approves and moves to the next row; `⌘` `Shift` `Enter` approves without moving.
**Approve** is unavailable, with the reason shown on it, when:
- you have an unsaved change: "Save your change first. Approve applies to the saved text."
- the string has no translation: "Nothing to approve yet: this string has no translation."
- it is already approved: "Already approved."
When the source text changed after the translation was written, the editor warns "The source changed after this was written. Read it again before approving it." You can still approve it.
## Review many at once [#review-many-at-once]
Tick the rows in **Table** view, then select **Approve** or **Flag** in the bar above the table. Approving more rows than fit on one screen asks you to confirm first. Rows that changed after you loaded them are skipped, never approved unseen. See [Act on many strings at once](https://transglot.ai/docs/guides/translating/bulk-actions).
## What happens next [#what-happens-next]
| Action | Result |
| --- | --- |
| **Approve** | The translation becomes **approved** and any flag on it is cleared. Approving changes the sign-off, never the text. |
| **Flag** | The translation is marked **needs checking**. An approved translation drops back to **edited**, because it is no longer signed off; a Glossa draft stays a draft. |
Both are recorded in the string's [history](https://transglot.ai/docs/guides/translating/version-history) with your name, and in the project's [activity](https://transglot.ai/docs/guides/projects/project-activity).
An approved translation is never overwritten by AI. If you publish to the CDN with drafts held back, approved strings are published and Glossa drafts wait until someone approves or edits them. See [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale).
## Require a second reviewer [#require-a-second-reviewer]
On Team plans and above, a workspace can stop anyone approving a translation they wrote themselves. An Admin turns on **Require a second reviewer** under **Policy** on the workspace's **People** page. It applies to every project, including to the account's Owners and Admins.
With it on, **Approve** is unavailable on your own translations and reads "You wrote this one, so someone else has to approve it. Flag it to send it back." In a bulk approval, your own rows are skipped and counted as needing a second reviewer.
## Ask a reviewer to look [#ask-a-reviewer-to-look]
Members who cannot approve can still ask for a review. In **Table** view, **Ask for a check** on a row flags it and notifies the project's reviewers. See [Translate a string](https://transglot.ai/docs/guides/translating/translate-a-string#ask-a-reviewer-to-check-it).
## If it doesn't work [#if-it-doesnt-work]
- The **Approval** section and the bulk **Approve** and **Flag** buttons are missing: you do not have review access to this project or language.
- "Someone else has to sign this off: this workspace requires a second reviewer.": the workspace requires a second reviewer and you wrote this translation.
- "An empty translation cannot be approved: there is nothing there.": the string has no text to approve.
## Related [#related]
- [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
- [Your assignments](https://transglot.ai/docs/guides/translating/your-assignments)
- [Quality checks](https://transglot.ai/docs/guides/quality/quality-checks)
# Assign a language to a translator (https://transglot.ai/docs/guides/translating/assign-a-language-to-a-translator/)
An assignment is a work order: one language, one translator, a status that moves from assigned to approved, and a progress figure. Assigning also opens the language to the translator, so it is how you give a vendor or freelancer exactly the languages they work on and nothing else.
Admins and the account's Owners can assign work.
## Before you start [#before-you-start]
The translator must already be a member of the workspace. To bring in someone new, [invite them](https://transglot.ai/docs/administration/team/invite-a-teammate) first; for an outside vendor, use a project-scoped invite as described in [Onboard a vendor or freelancer](https://transglot.ai/docs/administration/team/onboard-a-vendor-or-freelancer). The project also needs at least one target language.
## Assign the work [#assign-the-work]
### Open the project's assignments [#open-the-projects-assignments]
In the project, select **Review assignments** in the navigation. The page is headed **Assignments**.
### Choose the translator [#choose-the-translator]
Under **Assign to translator**, pick a person from **Translator** (it reads **Choose a translator** until you do).
### Set a due date (optional) [#set-a-due-date-optional]
Pick a date under **Due date (optional)**.
### Tick the languages [#tick-the-languages]
Tick each language to assign. Each language becomes its own assignment, tracked separately.
### Write a brief (optional) [#write-a-brief-optional]
Under **Brief (optional)**, add anything the translator should know: "Tone, deadlines, anything the translator should know."
### Assign [#assign]
Select **Assign to translator**. It stays unavailable until you have chosen a person and at least one language.
## What happens next [#what-happens-next]
- Each language appears under **Work orders** as **Assigned**, and a message confirms it: "Assigned de, fr to Ana."
- The translator is notified in the console and by email, and the work appears on their **Assignments** page. See [Your assignments](https://transglot.ai/docs/guides/translating/your-assignments).
- The translator can now open and write in the assigned languages. For a member whose access is limited to this project, the assignment adds exactly those languages to their access and nothing more. Assigning never takes access away, and it never changes anyone's role.
The progress on each work order counts the language's **approved** strings against the project's keys, so it moves as reviewers approve the translator's work.
## Review the work [#review-the-work]
When the translator selects **Submit for review**, you are notified, and the work order shows **Submitted for review**. Read the language in the editor and approve its strings (see [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)), then, on the work order:
- **Approve** signs off the work order. The confirmation says how many strings are not yet approved, if any: approving the work order does not approve them. "Approving is final: the work order cannot be reopened." The translator is told.
- **Request changes** opens **Request changes to** *language*. Describe "Which strings, and what is wrong with them.", then select **Send request**. The work order shows **Changes requested** and the translator is told.
Nobody can approve or send back a work order assigned to themselves.
## If it doesn't work [#if-it-doesnt-work]
- **"Invite a translator to this workspace before assigning work."**: there is nobody you can assign yet.
- **"Add a target language before assigning a translator."**: the project has no target language.
- **"That member reviews this project. A reviewer cannot also be the assignee."**: pick someone else, or change who reviews the project.
- **"Account owners and admins already have full access."**: Owners and Admins can already work in every language, so there is nothing to assign.
- **"Ana already has an open assignment for de."**: that language is already assigned to them and not yet approved. Other languages in the same request are still assigned.
- The **Assign to translator** form is not shown: you are not an Admin of this workspace or an Owner of its account.
## Related [#related]
- [Your assignments](https://transglot.ai/docs/guides/translating/your-assignments)
- [Grant access to one project](https://transglot.ai/docs/administration/team/grant-access-to-one-project)
- [Members and roles](https://transglot.ai/docs/administration/team/members-and-roles)
# Act on many strings at once (https://transglot.ai/docs/guides/translating/bulk-actions/)
In the editor's **Table** view, every row has a checkbox. Tick the rows you want and a bar appears above the table with the actions you are allowed to take on them. The selection belongs to the language on screen: approving or translating acts on that language only.
## Select rows [#select-rows]
### Narrow the table first [#narrow-the-table-first]
Use **Filters** or the search box so the table shows the strings you mean to act on. See [Filter the editor](https://transglot.ai/docs/guides/translating/filter-the-editor).
### Tick the rows [#tick-the-rows]
Tick rows one by one, or use the checkbox in the header row (**Select every key on this page**) to take every row that has loaded.
### Take every match, if you need to [#take-every-match-if-you-need-to]
The table loads rows as you scroll. When every loaded row is selected and more rows match your filter, a banner reads **All N loaded rows are selected.** Select **Select all N matching this filter** to add the rest.
The bar shows how many rows are **selected**. **Clear** empties the selection.
## Actions [#actions]
| Action | What it does | Who can use it |
| --- | --- | --- |
| **Translate** | Translates the selected strings with Glossa, after showing what the run covers. Anything a person has edited or approved is left as it is. See [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai). | Developer and above |
| **Approve** | Approves every selected translation. | Reviewer, Admin, account Owner |
| **Flag** | Marks every selected translation as needing a check. | Reviewer, Admin, account Owner |
| **Copy source** | Fills the translation with the source text, for strings that must stay identical in every language, such as brand names, product codes and URLs. **Fill empty rows only** leaves existing translations alone; **Replace existing translations too** overwrites them. | Anyone who can edit the language |
| **Keys**, then **Rename prefix** | Changes the start of every selected key name, for example from `checkout.` to `cart.`, then **Rename**. | Developer and above |
| **Keys**, then **Character limit** | Sets a maximum length on every selected key with **Set**, or removes it with **Clear**. | Developer and above |
| **Delete** | Deletes the selected keys and every translation of them, in every language. | Developer and above |
The key actions (**Rename prefix**, **Character limit**, **Delete**) work on up to 200 keys at a time.
## What to expect [#what-to-expect]
**Approving a large selection.** Approving more rows than fit on a screen asks first: "This approves every selected row, including rows nobody has opened. Rows changed since they were loaded, and rows you wrote yourself, are skipped." A large approval is sent in parts and shows its progress as it goes. When it finishes, a message such as "Approved 180. 4 changed since load." says how many rows were approved and why any were skipped.
**Empty translations are never approved.** A selected row with no translation is skipped.
**Rename is all or nothing.** If any new name would collide with a key that already exists, nothing is renamed and the bar says "Renaming would collide with an existing key:" followed by the name.
**Changing a character limit re-checks the strings.** Translations that now run over the new limit are flagged for review. Raising a limit does not clear a flag that is already there.
**Delete cannot be undone.** You type the number of keys to confirm. The workspace's Admins are notified that the keys were deleted, and the deletion is recorded in the project's [activity](https://transglot.ai/docs/guides/projects/project-activity). Apps that read a deleted key fall back to the key name.
A member scoped to particular languages cannot delete a key that holds translations in a language outside their access, because deleting a key removes its translations in every language.
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
- [Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key)
# Comment on a string (https://transglot.ai/docs/guides/translating/comment-on-a-string/)
Every key has one comment thread, shared across all of its languages. Anyone who can open the project can read, post and resolve comments, whatever their role.
## Post a comment [#post-a-comment]
### Open the thread [#open-the-thread]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), switch to **Table**. Under the key name, select **Comment**. When the key has open comments, the link shows how many instead.
### Write it [#write-it]
Type into **Add a comment…**. Comments can be up to 5,000 characters.
### Mention someone (optional) [#mention-someone-optional]
Select **Mention** and pick a teammate. Their name is added to your comment and they are notified when you post it. Only members who can open the project are offered, and only they are notified.
### Post [#post]
Select **Comment**.
## Who is notified [#who-is-notified]
| Who | Notification |
| --- | --- |
| Everyone you mention | "*Your name* mentioned you", with the key name and the start of your comment. Sent in the console and by email. |
| Everyone who has already commented on the key | "New comment on a string", in the console. |
You are never notified of your own comment. If your workspace sends notifications to Slack, the comment is posted there too. Each person can change which of these they receive by email in their [notification preferences](https://transglot.ai/docs/administration/account/notification-preferences).
## Resolve, reopen and delete [#resolve-reopen-and-delete]
- **Resolve** marks a comment as settled. **Reopen** brings it back. Anyone who can open the project can do both.
- **Delete** removes a comment. You can delete your own comments; Admins and the account's Owners can delete anyone's.
## Find strings with open discussions [#find-strings-with-open-discussions]
In the editor, open **Filters** and tick **Has unresolved comment** to see only the keys with a comment that has not been resolved. See [Filter the editor](https://transglot.ai/docs/guides/translating/filter-the-editor).
## If it doesn't work [#if-it-doesnt-work]
- **"Could not post comment."**: the comment did not save. Your text is still in the box; try again.
- A comment shows **Former member** as its author: the person who wrote it is no longer in the workspace.
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Your notifications](https://transglot.ai/docs/administration/account/your-notifications)
- [Connect Slack](https://transglot.ai/docs/integrations/notifications/connect-slack)
# Filter the editor (https://transglot.ai/docs/guides/translating/filter-the-editor/)
Filters, search and saved views work the same way in both editor views (**Side by side** and **Table**). In **Side by side** they decide which strings are in the queue; in **Table** they decide which rows are shown.
## Search [#search]
Type into **Search keys and text…** to find strings by key name or by text. The search matches the text of the source and of translations, and tolerates small differences in key names. It returns the 200 best matches; if there are more, the table says **showing top 200: refine your search**.
## Filter [#filter]
### Open the filters [#open-the-filters]
In the editor, select **Filters**. Under **Filter keys**, the options are:
| Filter | Shows |
| --- | --- |
| **Untranslated only** | Strings with no translation in this language yet. |
| **State** | One of **GLOSSA drafts** (written by Glossa, not edited since), **Edited** or **Approved**. Select the same state again to remove it. |
| **Waiting for approval** | Strings that are translated but not yet approved. |
| **Needs checking** | Strings flagged for a person to check. |
| **Has glossary issue** | Translations where a quality check found a glossary problem: a required term missing, or a forbidden one used. |
| **Has unresolved comment** | Keys with an open comment thread. |
| **Stale (source changed)** | Translations whose source text changed after they were written. |
### Combine them [#combine-them]
Filters combine: **Needs checking** with **Has glossary issue** shows only the flagged strings that also break a glossary term. **Filters** shows how many are on.
### Clear them [#clear-them]
**Clear filters** turns every filter off and keeps your search text.
The page address changes with your filters and language, so a filtered editor is a link you can send to a teammate. They see the same strings, limited to what their own access allows.
## Save a view [#save-a-view]
A saved view keeps a filter combination, your search text and the language, under a name.
### Set the filters [#set-the-filters]
Set the language, the filters and any search text you want to keep.
### Name it [#name-it]
Select **Save view**, type a name under **Name this view** (for example "Untranslated German"), then select **Save**.
Your views are listed under **Views**, under **Saved views**. Select a name to apply it; a view saved on another language switches the editor to that language. Select **✕** beside a view to delete it: only the saved filter is removed, never the strings it showed.
Saved views are personal: each member sees only their own, per project. Saving a view under a name you already use replaces it. Saving a view needs edit access to the project; anyone who can open the editor can filter.
## Links from the project overview [#links-from-the-project-overview]
The [project overview](https://transglot.ai/docs/guides/projects/your-project) opens the editor already filtered. On each language row, **Review** opens the strings that need checking, **Fill gap** the untranslated ones, and **Approve** the translated strings waiting for approval. The **translate** and **review** figures at the top of the project open the editor with the same filters.
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Act on many strings at once](https://transglot.ai/docs/guides/translating/bulk-actions)
- [Translation states](https://transglot.ai/docs/concepts/translation-states)
# Keyboard shortcuts (https://transglot.ai/docs/guides/translating/keyboard-shortcuts/)
Press `?` anywhere in the console, or `⌘/` (`Ctrl+/` on Windows and Linux), to open the shortcut list. In the editor, the **⌘** button in the toolbar opens the same list. It shows only the shortcuts your role can use: a translator who cannot approve is not shown the approve shortcut.
On Windows and Linux, read `⌘` as `Ctrl` throughout.
## Editing a string [#editing-a-string]
These work while you are typing in a translation.
| Keys | Action |
| --- | --- |
| `⌘` `Enter` | Save and go to the next string |
| `⌘` `Shift` `Enter` | Save without moving |
| `Esc` | Cancel the edit and drop what you typed |
Clicking away from a cell does not discard your text: the cell keeps it, marked as not saved, until you save it or press `Esc`.
## Moving around [#moving-around]
| Keys | Action |
| --- | --- |
| `j` / `k` | Move down and up the queue |
| `↑` / `↓` | Move between rows |
| `←` / `→` | Move between columns |
| `Alt` `↓` | Next untranslated string |
| `Alt` `↑` | Previous untranslated string |
| `Alt` `Shift` `↓` | Next string that needs checking |
| `Alt` `Shift` `↑` | Previous string that needs checking |
| `[` | Show or hide the context panel |
## In the context panel [#in-the-context-panel]
These work when focus is not in a text field.
| Keys | Action | Who can use it |
| --- | --- | --- |
| `⌘` `Enter` | Approve and go to the next string | Reviewers, Admins and account Owners |
| `⌘` `Shift` `Enter` | Approve without moving | Reviewers, Admins and account Owners |
| `Alt` `Enter` | Accept the Glossa suggestion on screen | Anyone who can edit the language |
| `Alt` `1` to `9` | Insert the first to ninth similar translation, numbered as the panel lists them | Anyone who can edit the language |
Memory matches use `Alt` rather than `Ctrl` with a number, because `Ctrl` and a number switches browser tabs on Windows and Linux.
## Anywhere in the console [#anywhere-in-the-console]
| Keys | Action |
| --- | --- |
| `⌘` `K` | Open the command palette and [the assistant](https://transglot.ai/docs/guides/translating/the-assistant) |
| `?` | Show the shortcut list |
| `G`, then a letter | Go to a page. The shortcut list shows the letters for the pages in your navigation. |
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Translate a string](https://transglot.ai/docs/guides/translating/translate-a-string)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
# The assistant (https://transglot.ai/docs/guides/translating/the-assistant/)
The assistant lives in the command palette. You tell it what you want, for example "translate everything missing in French, then run QA", and it answers with a plan: the steps it would take, in order, and what each one touches. Nothing happens until you run the plan.
The assistant is available on Team plans and above, inside a project. It acts with your permissions: every step is checked against your role, your language access and your plan before it runs, exactly as if you had clicked the button yourself.
## Ask the assistant [#ask-the-assistant]
### Open the command palette [#open-the-command-palette]
Inside a project, press `⌘` `K` (`Ctrl` `K` on Windows and Linux).
### Switch to the assistant [#switch-to-the-assistant]
Select **Ask the assistant**. The panel is headed **Ask about** *project name*.
### Describe what you want [#describe-what-you-want]
Type into **Ask the assistant…** and press `Enter`. For example: "add a glossary term for Checkout", "add Japanese and translate it", or "open the QA dashboard".
### Review the plan [#review-the-plan]
The assistant replies with a numbered plan of up to eight steps. Each step says what it will do and which strings or languages it covers. A step that translates or checks strings shows the words it would spend.
### Run it [#run-it]
Select **Run** (or **Run plan**, or **Run** *N* **steps, spending** *words* when the plan spends). To change the plan, ask again in different words instead.
When the plan has run, the panel says which steps ran, such as "All 2 steps ran." or "1 of 2 steps ran.", with the reason under any step that did not. **Back to command palette** returns you to the ordinary search.
## What it can do [#what-it-can-do]
| Step | Same as |
| --- | --- |
| Find keys by name or text | Searching in the [editor](https://transglot.ai/docs/guides/translating/the-editor) |
| Open a project page: the editor, quality, glossary, screenshots, deploy or overview | Using the project navigation |
| Create a key | **New key** in the editor ([Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key)) |
| Add a target language | [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language) |
| Translate missing or all strings, in one language or every language | [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai) |
| Run the quality checks | [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check) |
| Add a glossary term, or change an existing one | [Add a glossary term](https://transglot.ai/docs/guides/quality/add-a-glossary-term) |
| Repair Glossa translations that failed quality checks | [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation) |
It does not approve translations, change settings, or delete anything.
## When a step does not run [#when-a-step-does-not-run]
The assistant never claims a step ran when it did not. The reasons it gives include:
- "Your role or plan does not allow this step."
- "That language is outside your access to this project."
- "Nothing to translate. No string is waiting for this run."
- "Not run: a translation run is already in progress on this project. Try again when it finishes."
- "Not run: this workspace does not have enough words left this cycle."
- "Not run: the assistant has reached its limit for this billing cycle."
If the panel says "The assistant needs a Team plan or higher (Quality Mode).", the account's plan does not include the assistant. See [Change your plan](https://transglot.ai/docs/administration/billing/change-your-plan).
## Related [#related]
- [Keyboard shortcuts](https://transglot.ai/docs/guides/translating/keyboard-shortcuts)
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Roles and what each one can do](https://transglot.ai/docs/administration/team/roles-and-what-each-one-can-do)
# The editor (https://transglot.ai/docs/guides/translating/the-editor/)
The editor is where translations are written, read and approved. It shows one target language at a time: every key in the project, its source text, and the translation in that language. Open it from a project with **Open editor**, or from any **Fill gap**, **Review** or **Approve** link on the project overview, which open it already filtered to the strings that need you.
## Choose a language [#choose-a-language]
The strip at the top of the editor names the workspace, the project and the target language you are working in. Select the language to switch to another one; the list is searchable, and **Manage locales** at its foot takes you to the project's language settings.
If the project has no target language yet, the editor says **Nothing to translate into yet.** Someone with the Developer role or above has to [add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language) first. If it has languages but no source strings, the editor says **Nothing has been imported yet** and offers **Import source strings →** (see [Import content](https://transglot.ai/docs/guides/projects/import-content)) or, for Developers and above, **Add one key by hand**.
## Two ways to read a language [#two-ways-to-read-a-language]
The toggle next to the language picks how the strings are laid out:
| View | What it is for |
| --- | --- |
| **Side by side** | The default. One string at a time: the source above, a large box for the translation below, and the glossary terms, memory matches and suggestions for that string beside it. The queue on the left lists every string in your current filter. |
| **Table** | Every key as a row, with the key name, the source text, the translation, a **Quality** column (on wider screens) and the string's state. Use it to scan, to select rows for bulk actions, and to open comments and plural forms. |
**Queue** at the top left shows how many strings are in view. On narrow screens it opens the queue as a drawer. Move through the queue with `j` and `k`.
## Toolbar [#toolbar]
| Control | What it does | Who sees it |
| --- | --- | --- |
| **Translate missing** | Translates the strings in this language that have no translation, or whose AI draft is out of date, with Glossa. It reads **Translating…** while a run is going. See [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai). | Developer and above |
| **Run QA** | Runs the automatic quality checks. It reads **Checking quality…** while it works. See [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check). | Developer and above |
| **New key** | Adds a key by hand. See [Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key). | Developer and above |
| **⌘** | Opens the list of [keyboard shortcuts](https://transglot.ai/docs/guides/translating/keyboard-shortcuts). You can also press `?`. | Everyone |
| **Filters**, **Views**, **Save view** | Narrow the strings in view and keep that combination. See [Filter the editor](https://transglot.ai/docs/guides/translating/filter-the-editor). | Everyone (saving a view needs edit access) |
Both **Translate missing** and **Run QA** show what the run covers before anything starts. See [Translate with AI](https://transglot.ai/docs/guides/translating/translate-with-ai).
## String states [#string-states]
Every translation carries one state, shown as a chip in the table, in the queue and in the context panel:
| State | Meaning |
| --- | --- |
| **not translated** | No translation exists in this language yet. |
| **draft** | Written by Glossa and not edited by a person since. |
| **edited** | Written or changed by a person, or imported from a file. |
| **approved** | A reviewer has approved it. |
| **needs checking** | Flagged for a person to check, by an automatic check or by a teammate. See [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review) |
A second chip, **stale**, appears when the source text changed after the translation was written. A string can be approved and stale at the same time. [Translation states](https://transglot.ai/docs/concepts/translation-states) explains how strings move between states.
## The context panel [#the-context-panel]
The panel on the right, headed **In context**, shows everything known about the string you are on. Press `[` to hide or show it. Depending on the string and your plan, it holds:
- **Source**: the source text, with placeholders highlighted, the translation's state, and its quality score and confidence when it has them.
- **Overflow watch**: the length of the translation against the key's maximum length, or a note that no limit is set.
- **Glossary**: glossary terms found in the source and how each must be translated, marked **forbidden** or **do not translate** where that applies.
- **Translation memory**: an exact match from earlier work, which you can take with **Use this ($0)**.
- **Similar approved translations**: reviewed translations of similar source text, each with how close it is. **Use this** copies one into the translation. These are suggestions only.
- **Ask Glossa**: **Suggest** asks Glossa for a fresh draft of this one string and shows whether it passes the placeholder, plural, glossary and length checks. Nothing is saved until you select **Accept**. Available on Starter and above.
- **Quality findings**: each problem an automatic check found, with its category and severity.
- **Approval**: **Approve** and **Flag**, for reviewers. See [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation).
- **Concordance**: search earlier translations for a word or phrase, to see how your team has rendered it before.
- **Provenance**: the translation's recent versions, newest first, with **Restore** on each. See [Version history](https://transglot.ai/docs/guides/translating/version-history).
In **Side by side** view, the glossary terms, the memory match and the suggestions sit next to the translation box instead, and the context panel drops them so nothing appears twice.
## Translation runs [#translation-runs]
While a translation run is going, a progress card above the strings shows how far it has got, as **Translating… (done/total)**, and rows fill in as strings land. Developers and above can **Stop** a run; strings already translated are kept. If a run finishes with failures, the card says how many strings failed, lists them under **Show the strings that failed**, and offers **Retry failed**, which re-runs only those strings.
## What you can do depends on your role [#what-you-can-do-depends-on-your-role]
| Role | In the editor |
| --- | --- |
| Translator | Read every string, write translations in the languages they have access to, comment, save views, ask for a check. |
| Reviewer | Everything a Translator can do, plus approve and flag. |
| Developer | Everything a Translator can do, plus start AI translation and quality checks, and add, edit and delete keys. Developers do not approve. |
| Admin and account Owner | Everything above. |
A member scoped to particular languages can read the whole project but only write in, and review, the languages they were given. See [Roles and what each one can do](https://transglot.ai/docs/administration/team/roles-and-what-each-one-can-do).
## Related [#related]
- [Translate a string](https://transglot.ai/docs/guides/translating/translate-a-string)
- [Bulk actions](https://transglot.ai/docs/guides/translating/bulk-actions)
- [Keyboard shortcuts](https://transglot.ai/docs/guides/translating/keyboard-shortcuts)
# Translate a string (https://transglot.ai/docs/guides/translating/translate-a-string/)
Anyone who can edit a language can write its translations: Translators, Reviewers, Developers, Admins and the account's Owners. A member scoped to particular languages can only write in the languages they were given; in any other language the editor reads **Read only. Translating this locale is not one of your rights here.**
The editor writes translations only. Source text comes from your files, through an [import](https://transglot.ai/docs/guides/projects/import-content), the [CLI](https://transglot.ai/docs/developers/cli) or an integration.
## Write the translation [#write-the-translation]
### Open the string [#open-the-string]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), choose the language, then pick the string from the queue on the left. Search or [filter](https://transglot.ai/docs/guides/translating/filter-the-editor) the queue to find it, or press `Alt` `↓` to jump to the next untranslated string.
### Type the translation [#type-the-translation]
Type into the box under the source. Glossary terms in the string, an exact memory match and Glossa's alternatives are listed beside it; select one to use it as your starting point.
### Save [#save]
Select **Save**, or press `⌘` `Enter` to save and move to the next string (`⌘` `Shift` `Enter` saves without moving). **Discard**, or `Esc`, drops your change.
### Open the cell [#open-the-cell]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), switch to **Table**, find the row, and click its translation cell (or move to it and press `Enter`).
### Type the translation [#type-the-translation-1]
The context panel on the right shows the string's glossary terms, memory matches and length limit while you type.
### Save [#save-1]
Press `⌘` `Enter` to save and move to the next row, or `⌘` `Shift` `Enter` to save in place. `Esc` cancels.
On Windows and Linux, use `Ctrl` for `⌘`.
## Start from a suggestion [#start-from-a-suggestion]
You do not have to type from scratch. In the context panel, or beside the box in **Side by side**:
- **Use this ($0)** copies an exact match from translation memory.
- **Use this** under **Similar approved translations** copies a reviewed translation of similar source text. Check it before saving: the source it matched is shown above it.
- **Suggest** asks Glossa for a fresh draft of this one string, checked against the placeholders, plural forms, glossary terms and length limit. **Accept** puts it in the box. Available on Starter and above.
Whatever you take is saved as your edit, so you can change it before or after saving.
## What happens when you save [#what-happens-when-you-save]
The string's state becomes **edited**, and your name is added to its [history](https://transglot.ai/docs/guides/translating/version-history). A translation you have edited or reviewed is never overwritten by AI: later translation runs skip it, even when its source text changes.
While you type, the editor checks your draft against the source and lists problems under the box, such as a missing placeholder. If the key has a maximum length, a counter shows your length against it.
## Ask a reviewer to check it [#ask-a-reviewer-to-check-it]
In **Table** view, **Ask for a check** under the row's state flags the translation as needing a check and notifies the project's reviewers ("requested your review"). It is offered to anyone who can edit the language, on translations that are not already flagged or approved. See [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation).
## Plural strings [#plural-strings]
A key marked as plural has one form per plural category of the language. It cannot be typed into a single box: select **Edit forms** in the table, or **Edit plural forms** in **Side by side**. See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms).
## If it doesn't work [#if-it-doesnt-work]
- **"Over the limit. Saving keeps your text and flags this row for review."**: your translation is longer than the key's maximum length. It saves, and the string is flagged so a reviewer sees it.
- **"Someone else saved "…" while you were editing. Save again to replace it."**: a teammate saved this string after you opened it. Your text is kept; save again only if yours should win.
- **"Save failed."**: the change did not reach the server. Your text stays in the box; try again.
- The box is read-only: you do not have edit access to this language. Ask an Admin to [grant you access](https://transglot.ai/docs/administration/team/grant-access-to-one-project).
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [Keyboard shortcuts](https://transglot.ai/docs/guides/translating/keyboard-shortcuts)
- [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
# Translate with AI (https://transglot.ai/docs/guides/translating/translate-with-ai/)
Glossa, transglot's translation engine, drafts translations for you. **Glossa Flux** is the default model; projects with [Quality mode](https://transglot.ai/docs/guides/translating/ai-translation-settings#quality-mode) on use **Glossa Deep** instead. You can start a run yourself from the editor, and transglot also starts runs on its own when new source text arrives (see [Automatic translation](#automatic-translation)).
Starting a run needs the Developer role or above. Translators and Reviewers can edit and approve what Glossa writes, but cannot start a run.
## Before you start [#before-you-start]
Set up the project's [glossary](https://transglot.ai/docs/guides/quality/the-glossary) and [brand voice](https://transglot.ai/docs/guides/quality/brand-voice) first if you plan to use them. Glossa follows both on every run, and a translation that is already written is not redone when you change them later.
## Translate everything missing in a language [#translate-everything-missing-in-a-language]
### Open the language [#open-the-language]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), choose the target language.
### Start the run [#start-the-run]
Select **Translate missing**. It covers every string in the language that has no translation, plus Glossa drafts whose source text has changed since they were written.
### Check what it covers [#check-what-it-covers]
A dialog asks **Translate into** *language*? and shows how many strings and words the run covers, and how that compares with the words left in your plan this cycle. Under **Which languages**, keep *language* **only**, or choose the option that lists every language with strings waiting.
### Confirm [#confirm]
Select **Translate**. **Cancel** starts nothing.
## Translate selected strings [#translate-selected-strings]
### Select the rows [#select-the-rows]
In the editor's **Table** view, tick the strings you want translated. See [Act on many strings at once](https://transglot.ai/docs/guides/translating/bulk-actions).
### Start the run [#start-the-run-1]
Select **Translate** in the bar above the table, check the figures in the dialog, and confirm.
## What happens during a run [#what-happens-during-a-run]
- **Translate missing** reads **Translating…**, and a progress card shows how many strings are done. Rows fill in as strings land, each in the **draft** state.
- You can keep working. Leaving the page does not stop the run.
- **Stop** ends the run early. Strings already translated are kept; the rest need starting again.
- When the run ends, the card says what happened: how many strings were translated, how many came from translation memory, and how many failed. **Retry failed** re-runs only the failed strings.
Every run, with what started it and which strings failed, is listed on the project's **Translation runs** page: select **Runs** in the project's navigation, or **Translation runs** on the [project overview](https://transglot.ai/docs/guides/projects/your-project).
Only one run goes at a time in a project. Starting another while one is running is refused with "A translation run is already in progress on this project. Wait for it to finish, then start this one."
## What Glossa does and does not touch [#what-glossa-does-and-does-not-touch]
**Your own work is safe.** A translation you have edited or reviewed is never overwritten by AI. A run skips it even when its source text has changed, and the end-of-run summary counts strings "left as they are because someone had already edited or reviewed them".
**Repeated text is reused.** When the exact same source text has been translated before, transglot reuses that translation at no cost instead of translating it again. See [Translation memory](https://transglot.ai/docs/concepts/translation-memory).
**Placeholders, plurals and terms are checked.** Glossary terms are matched against source text and enforced during translation and during quality checks; a translation that departs from a glossary term is flagged. Plural keys get every plural form the target language needs. See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms).
**Review is routed for you, if you want.** Each AI translation carries a confidence signal. Translations that pass the automatic checks and come back confident can be marked as not needing review, so your reviewers only see the ones worth their time. Everything else is flagged for review. Automatic checks always win: a translation with a broken placeholder, the wrong plural forms, or a glossary conflict is always sent to review no matter how confident it was. This routing is off by default; turn it on with **Route AI translations by confidence** in the project's **Settings**, under **Checks** (Team and above).
## Automatic translation [#automatic-translation]
With **Auto-translate** on (the default), transglot starts a run without anyone pressing a button when:
- **Source text changes.** New or changed source strings that arrive by push, upload, API or an integration are translated into every target language. See [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings#auto-translate).
- **You add a language.** Everything already in the project is translated into the new language. The console asks first, with the word count: **Add** *language* **and translate into it?** See [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language).
When the first strings arrive in a new project, the project's setup page asks whether to translate them now, with the word count, and offers **Keep translating new text automatically**. Clearing that box, or choosing **Not now**, turns **Auto-translate** off for the project; you can turn it back on in the project's settings.
Runs started automatically follow the same rules as runs you start: edited and approved translations are never overwritten, and memory is reused first.
## Usage [#usage]
Your plan includes an allowance of AI translation each month. Usage is measured against that allowance, and what happens when you exceed it depends on whether usage-based billing is turned on for your workspace. Text reused from translation memory does not count against it. The confirmation dialog tells you before a run starts whether it fits. See [What counts towards my usage?](https://transglot.ai/docs/administration/billing/what-counts-towards-my-usage) and [pricing](https://transglot.ai/pricing).
## If it doesn't work [#if-it-doesnt-work]
- **Translate missing** is not shown: you need the Developer role or above, or the project has no source strings yet.
- **"Nothing to translate. No string in** *language* **is waiting for this run."**: every string in the language is translated and current, or has been edited by a person.
- **"The figures for this run could not be loaded, so nothing was started. Try again in a moment."**: nothing was started or charged; try again.
- A refusal that names your plan, payment or spend cap: AI translation is paused for the workspace's account. The message says why, and **Plan & billing** says what to do. See [Usage-based billing](https://transglot.ai/docs/administration/billing/usage-based-billing).
- The run finished with failures: open the run on **Translation runs** to see which strings failed and why, then select **Retry failed**.
## Related [#related]
- [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings)
- [AI translation](https://transglot.ai/docs/concepts/ai-translation)
- [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
# Version history (https://transglot.ai/docs/guides/translating/version-history/)
Every change to a translation is kept as a version: each Glossa draft, each edit, each approval and flag, and each restore. The editor shows a translation's recent versions in the context panel, so you can see how it got to where it is and undo a change without retyping it.
## See a translation's history [#see-a-translations-history]
### Select the string [#select-the-string]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), choose the language and select the string.
### Read the history [#read-the-history]
In the context panel on the right, scroll to **Provenance**. It lists the ten most recent versions of the translation in this language, newest first, with how many it is showing.
Each version shows:
- **What happened**, and who did it when the change was made by a person, for example "Edited by hand by Ana".
- **When**, as a short age. Hover over it for the exact date and time.
- **The text** of the translation at that point.
- For a person's edit of a Glossa draft, how much of the draft they changed: **AI kept as-is**, or a figure such as **35% post-edited**.
| Label | Meaning |
| --- | --- |
| **Glossa Flux draft**, **Glossa Deep draft** or **Glossa draft** | Glossa wrote this version. The name says which model, when it is known. |
| **Edited by hand** | A person typed or changed the text. |
| **From translation memory** | The text was reused from an earlier identical translation. |
| **Approved** | A reviewer approved the translation. The text did not change. |
| **Flagged** | Someone flagged the translation for a check. The text did not change. |
| **Reverted** | Someone restored an earlier version. |
| **In-context editor** | The change was made in the [in-context editor](https://transglot.ai/docs/developers/in-context). |
| **Auto-repair** | An automatic fix from [self-heal](https://transglot.ai/docs/guides/quality/self-heal-a-translation). |
If the panel says "No history yet. Every edit, approval and GLOSSA draft on this string will be listed here.", the translation has never been written in this language.
## Restore an earlier version [#restore-an-earlier-version]
Anyone who can edit the language can restore a version.
### Find the version [#find-the-version]
In **Provenance**, find the version whose text you want back.
### Restore it [#restore-it]
Select **Restore** under it. A message reads "Reverted." and the translation shows the restored text.
Restoring writes the old text back as a new version, labelled **Reverted**, so nothing in the history is lost and the restore itself can be undone the same way.
The restored translation becomes **edited**. Restoring cannot be used to approve a translation: the only exception is a reviewer restoring a version that was itself approved, which comes back **approved**. Restoring never brings back a Glossa draft as a draft.
If someone else saved the translation after you opened it, the restore is refused and the editor shows what they saved, so you can decide again with the current text in front of you.
## Where else changes are recorded [#where-else-changes-are-recorded]
Edits, approvals, flags and restores also appear in the project's [activity](https://transglot.ai/docs/guides/projects/project-activity), alongside changes to keys, languages and the glossary. Use the activity feed to see what changed across a whole project; use **Provenance** to see one translation's story.
## Related [#related]
- [Translate a string](https://transglot.ai/docs/guides/translating/translate-a-string)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
- [Translation states](https://transglot.ai/docs/concepts/translation-states)
# Why was my translation flagged for review? (https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review/)
A flagged translation shows **needs checking** in the editor and counts towards the **review** figure on the [project overview](https://transglot.ai/docs/guides/projects/your-project). The flag means a person should read it before you rely on it. It does not change the text.
## Find out why [#find-out-why]
In the [editor](https://transglot.ai/docs/guides/translating/the-editor), select the string:
- In **Table** view, a flagged row has a **Check** button under its state (or **QA** when it has findings but no flag). It opens **Quality findings** for that string, with each problem, its category, its severity and, where there is one, a suggested fix you can take with **Accept suggestion**.
- In the context panel, **Quality findings** lists the same problems, and **Provenance** shows whether a person flagged it, and who. See [Version history](https://transglot.ai/docs/guides/translating/version-history).
## The reasons [#the-reasons]
### An automatic check found a problem [#an-automatic-check-found-a-problem]
The [quality checks](https://transglot.ai/docs/guides/quality/quality-checks) flag a translation when they find a serious problem, when its quality score is low, or when it runs over the key's maximum length. Typical problems are a placeholder that is missing or changed, the wrong plural forms for the language, and a glossary term that was not followed. Glossary terms are matched against source text and enforced during translation and during quality checks; a translation that departs from a glossary term is flagged.
Some problems are caught the moment a translation is saved, without a quality check:
- A translation saved by hand that is longer than the key's maximum length. The editor warns before you save: "Over the limit. Saving keeps your text and flags this row for review."
- A translation that already existed when someone lowered the key's maximum length, and is now too long.
### It was not confident enough to skip review [#it-was-not-confident-enough-to-skip-review]
This applies only to projects with **Route AI translations by confidence** turned on, under **Settings**, then **Checks** (Team and above). It is off by default.
Each AI translation carries a confidence signal. Translations that pass the automatic checks and come back confident can be marked as not needing review, so your reviewers only see the ones worth their time. Everything else is flagged for review. Automatic checks always win: a translation with a broken placeholder, the wrong plural forms, or a glossary conflict is always sent to review no matter how confident it was.
With the setting off, Glossa's translations arrive as **draft** and are not flagged for confidence. Use the **Waiting for approval** filter to find drafts nobody has read yet.
### A person flagged it [#a-person-flagged-it]
- A reviewer selected **Flag** or **Flag for a check**. See [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation).
- A translator selected **Ask for a check**, which flags the string and notifies the project's reviewers.
## Clear the flag [#clear-the-flag]
Fix the translation if it needs fixing, then have a reviewer approve it: approving clears the flag. Editing a translation does not clear its flag on its own, so a fixed string still reaches a reviewer.
Only Reviewers, Admins and the account's Owners can approve. If you are a translator, the flag stays until a reviewer reads the string.
## Related [#related]
- [What does the quality score mean?](https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean)
- [Run a quality check](https://transglot.ai/docs/guides/quality/run-a-quality-check)
- [Filter the editor](https://transglot.ai/docs/guides/translating/filter-the-editor)
# Your assignments (https://transglot.ai/docs/guides/translating/your-assignments/)
When an Admin assigns you a language, it becomes a work order with a status, a progress figure and, sometimes, a due date and a brief. You are notified in the console and by email.
## Find your work [#find-your-work]
Select **Assignments** in the workspace navigation. **My assignments** lists every language assigned to you, across the workspace's projects, under **Assigned to you**: the language, its status, its progress and what you can do next. Only work assigned to you appears here.
Inside a project, **Review assignments** in the project navigation shows the work orders on that project. Admins see every work order there; everyone else sees their own.
**Progress** counts the language's approved strings against the project's keys, so it moves when a reviewer approves your translations, not when you save them.
## Work through an assignment [#work-through-an-assignment]
### Start [#start]
Select **Start**. The work order moves to **In progress** and the [editor](https://transglot.ai/docs/guides/translating/the-editor) opens on the assigned language. To come back to it later, select the language's name on the work order.
### Translate [#translate]
Translate and correct the strings. **Filters**, then **Untranslated only**, shows what is left. See [Translate a string](https://transglot.ai/docs/guides/translating/translate-a-string).
### Submit for review [#submit-for-review]
When you are done, select **Submit for review**. The confirmation lists anything still not translated or not approved, and says who will be told. The work order moves to **Submitted for review**.
Until a reviewer acts on it, you can select **Withdraw** to take it back: it returns to **In progress** and the people told about the submission are told it was withdrawn.
## Statuses [#statuses]
| Status | Meaning | What you can do |
| --- | --- | --- |
| **Assigned** | Created, not started. | **Start**, or **Submit for review** |
| **In progress** | You have started. | **Submit for review** |
| **Submitted for review** | You have said it is done. | **Withdraw** |
| **Changes requested** | A reviewer sent it back, with a note on what to change. | **Start**, or **Submit for review** |
| **Approved** | A reviewer signed it off. This is final. | Nothing: the work order is closed. |
Reviewers see **Approve** and **Request changes** on submitted work orders. Nobody can approve or send back their own. See [Assign a language to a translator](https://transglot.ai/docs/guides/translating/assign-a-language-to-a-translator#review-the-work).
## Who is told [#who-is-told]
| Event | Who is notified |
| --- | --- |
| A language is assigned to you | You |
| You submit or withdraw | The person who assigned it |
| The work order is approved, or changes are requested | You |
These notifications are emailed by default. Change that in your [notification preferences](https://transglot.ai/docs/administration/account/notification-preferences).
## If it doesn't work [#if-it-doesnt-work]
- **"No assignments yet"**: nothing is assigned to you in this workspace.
- **"Your access to** *language* **was removed."**: an Admin removed your access to that language or project after it was assigned. Ask them to restore it.
- **"de is waiting for review. Withdraw it to keep working on it."**: you selected **Start** on submitted work. Withdraw it first.
- **"de is not waiting for review, so there is nothing to withdraw. Reload the page to see where it stands."**: a reviewer acted on it before you withdrew it.
## Related [#related]
- [Assign a language to a translator](https://transglot.ai/docs/guides/translating/assign-a-language-to-a-translator)
- [Your notifications](https://transglot.ai/docs/administration/account/your-notifications)
- [Onboard a vendor or freelancer](https://transglot.ai/docs/administration/team/onboard-a-vendor-or-freelancer)
# Localize your App Store listing (https://transglot.ai/docs/integrations/app-stores/app-store-connect/)
The App Store Connect connector is two-way. It reads your app's listing in its source locale, turns each field into a key in your project, and publishes reviewed translations back into each target App Store locale, creating the localization if it does not exist yet. It never writes your source locale.
## What it translates [#what-it-translates]
One connection covers one app. You choose which listing fields to translate:
| Field | App Store Connect surface |
| --- | --- |
| `name`, `subtitle` | App information |
| `description`, `keywords`, `promotionalText`, `whatsNew`, `marketingUrl`, `supportUrl` | The app version |
Only the fields you select become keys, and only those are ever written back.
## Requirements [#requirements]
- **Plan:** Starter or above.
- **Role:** Admin of the project's workspace, or an Owner of its account.
- **In App Store Connect:** an API key. In **App Store Connect**, go to **Users and Access**, **Keys**, create a key, and download its `.p8` private key file. Note the key's **Key ID** and the **Issuer ID** shown on the same page.
- **Your app's Apple ID:** the numeric ID under **App Information**, **General**, **Apple ID**.
- **Target languages** added to your transglot project.
## Connect [#connect]
### Open the CMS page [#open-the-cms-page]
Open your project's settings, choose **CMS**, and pick **App Store Connect** in the **CMS** list.
### Enter the app and key [#enter-the-app-and-key]
| Field | What to enter |
| --- | --- |
| **App ID** | Your app's numeric Apple ID, for example `1234567890` |
| **Key ID** | The API key's ID |
| **Issuer ID** | The issuer ID from the Keys page |
| **Source App Store locale** | The App Store locale that holds your source listing, for example `en-US`. transglot only reads it |
| **Private key (.p8)** | Paste the whole contents of the `.p8` file, from `-----BEGIN PRIVATE KEY-----` to `-----END PRIVATE KEY-----` |
### Map your locales [#map-your-locales]
Under **Locale → App Store locale**, enter the App Store locale for each target language in the project, for example `fr-FR` for `fr`. A language you leave unmapped is skipped when publishing.
### Choose the fields [#choose-the-fields]
Under **Content types**, select `listing`. Under **Translatable fields**, select the listing fields to translate.
### Connect, test and sync [#connect-test-and-sync]
Select **Connect**, then **Test connection**, then **Sync from CMS** to import the listing. The private key is stored encrypted and never shown again.
## Publish translations back [#publish-translations-back]
Review the imported translations, then publish them with **Publish to CMS**, or turn on automatic publishing when a translation is reviewed. Only reviewed translations are published. [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms) describes reviewing and publishing in full; it works the same way here.
Apple accepts changes only while there is something editable:
- Version fields (description, keywords, promotional text, release notes, marketing and support URLs) are written to the app version that is being prepared for submission.
- `name` and `subtitle` are written to the app information that is editable with that version.
transglot checks this before writing anything: if the fields you publish have nowhere editable to go, the publish fails without writing any locale. Importing still works on a live app with no version in preparation.
## Troubleshooting [#troubleshooting]
The full message is "App Store Connect has no editable version. Create a new app version to publish store metadata (description, keywords, promotional text, release notes)." Create a new version of the app in App Store Connect, then publish again.
The full message is "App Store Connect has no editable app information. Create a new app version to publish the app name or subtitle." The app name and subtitle can only change alongside a new version. Create one, then publish again.
Check that the **Key ID** and **Issuer ID** belong to the `.p8` key you pasted, that the key has not been revoked, and that it has access to the app.
It has no App Store locale under **Locale → App Store locale**, or none of its translations are reviewed yet.
## Related [#related]
- [Localize your Google Play listing](https://transglot.ai/docs/integrations/app-stores/google-play)
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
# Localize your Google Play listing (https://transglot.ai/docs/integrations/app-stores/google-play/)
The Google Play connector is two-way. It reads your app's store listing in its source language, turns each field into a key in your project, and publishes reviewed translations back into each target language's listing. It never writes your source language.
## What it translates [#what-it-translates]
One connection covers one app. You choose which listing fields to translate: `title`, `shortDescription` and `fullDescription`. Only the fields you select become keys, and only those are written back. Other parts of a listing, such as the promo video, are left as they are.
## Requirements [#requirements]
- **Plan:** Starter or above.
- **Role:** Admin of the project's workspace, or an Owner of its account.
- **In Google Play Console:** a service account with the Android Publisher API enabled, granted access to your app. Download the service account's JSON key.
- **Your app's package name**, for example `com.example.app`.
- **Target languages** added to your transglot project.
## Connect [#connect]
### Open the CMS page [#open-the-cms-page]
Open your project's settings, choose **CMS**, and pick **Google Play** in the **CMS** list.
### Enter the app and key [#enter-the-app-and-key]
| Field | What to enter |
| --- | --- |
| **Package name** | Your app's application ID, for example `com.example.app` |
| **Source Play language** | The Play language that holds your source listing, for example `en-US`. transglot only reads it |
| **Service account JSON** | Paste the whole contents of the JSON key file |
### Map your languages [#map-your-languages]
Under **Locale → Play language**, enter the Play language for each target language in the project, for example `fr-FR` for `fr`. A language you leave unmapped is skipped when publishing.
### Choose the fields [#choose-the-fields]
Under **Content types**, select `listing`. Under **Translatable fields**, select the listing fields to translate.
### Connect, test and sync [#connect-test-and-sync]
Select **Connect**, then **Test connection**, then **Sync from CMS** to import the listing. The service account key is stored encrypted and never shown again.
## Publish translations back [#publish-translations-back]
Review the imported translations, then publish them with **Publish to CMS**, or turn on automatic publishing when a translation is reviewed. Only reviewed translations are published. [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms) describes reviewing and publishing in full; it works the same way here.
Each publish writes every target language inside one Google Play edit and commits it once all of them are written. If any language fails, the whole edit is discarded, so your listing never ends up half updated and no unfinished edit is left behind in Play Console. Importing never commits anything.
## Troubleshooting [#troubleshooting]
Check that the JSON key is complete and has not been deleted in Google Cloud, and that the service account has been granted access to this app in Play Console.
Use the application ID exactly as it appears in Play Console: segments separated by dots, each starting with a letter, for example `com.example.app`.
It has no Play language under **Locale → Play language**, or none of its translations are reviewed yet.
## Related [#related]
- [Localize your App Store listing](https://transglot.ai/docs/integrations/app-stores/app-store-connect)
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
# Connect Jira (https://transglot.ai/docs/integrations/automation/connect-jira/)
The Jira integration turns transglot's actionable events into Jira Cloud issues, one issue per event, in the project and issue type you choose. It is one-way: transglot writes to Jira and never reads from it, so your team can triage, move or close the issues without changing anything in transglot.
## Requirements [#requirements]
- **Plan:** Team or above. On a lower plan the **Jira** card offers **Upgrade plan** instead of **Connect**.
- **Role:** Admin of the workspace, or an Owner of its account. Other members can open the Jira page and see whether it is connected, but not change it.
- **In Jira:** a Jira Cloud site, an Atlassian account that can create issues in the target project, and an API token for that account.
## Connect Jira [#connect-jira]
### Create an API token [#create-an-api-token]
Sign in to Atlassian as the account transglot should act as, then go to **id.atlassian.com**, **Security**, **API tokens** and create a token. Copy it: Atlassian shows it once.
### Open the Jira page [#open-the-jira-page]
In your workspace, open **Integrations** and select **Connect** on the **Jira** card.
### Fill in the connection [#fill-in-the-connection]
| Field | What to enter |
| --- | --- |
| **Jira site URL** | Your site address, for example `https://your-team.atlassian.net` |
| **Atlassian account email** | The email of the account that owns the API token |
| **API token** | The token you created. It is stored encrypted and never shown again |
| **Project key** | The key of the Jira project issues should open in, for example `LOC` |
| **Issue type** | The issue type to create. Leave it as `Task` unless your project uses another type |
### Save [#save]
Select **Connect**. The confirmation reads "Jira connected." and the **Jira** card on the integrations page now shows **connected**.
## What opens an issue [#what-opens-an-issue]
transglot opens one issue each time one of these happens in the workspace:
- A review is requested.
- A source drift is repaired.
- An assignment is created, or sent back for changes.
- A translation batch fails, or finishes with some strings left untranslated.
Comments, mentions and completed batches never open an issue, so the Jira project stays a list of things that need somebody. Each event opens its own issue: two failed batches produce two issues. Issue titles and descriptions are written in English and link back to the place in transglot where the work is.
## Change or disconnect [#change-or-disconnect]
To change the project, issue type or account, open the Jira page, edit the fields and select **Save**. Leave **API token** blank to keep the stored token.
To stop opening issues, select **Disconnect** and confirm **Disconnect Jira?**. Issues already created stay in Jira with their links back to transglot.
## Troubleshooting [#troubleshooting]
The first connection needs a token. Paste the API token into **API token** and save again.
Check that the Atlassian account whose token you used can create issues in that project, that the **Project key** is the project's key (not its name), and that the **Issue type** exists in that project. Remember that only the events listed above open issues.
Jira is included on the Team plan and above. See [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements).
## Related [#related]
- [Connect Slack](https://transglot.ai/docs/integrations/notifications/connect-slack)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
- [Integrations overview](https://transglot.ai/docs/integrations)
# Zapier, Make and n8n (https://transglot.ai/docs/integrations/automation/zapier-make-and-n8n/)
Automation platforms connect to transglot in two directions, and you can use either or both:
- **transglot to your flow.** transglot sends a signed event to your flow when something happens in a project: a batch finishes, translations are reviewed, a language is added, a quality check completes. Use it for "when a batch completes, post to the release channel" or "when a locale is reviewed, start a deploy", without polling.
- **Your flow to transglot.** Your flow calls the public API to push source files, start a translation, read a batch's status or pull a translated file.
Both directions authenticate with a [deploy token](https://transglot.ai/docs/developers/deploy-tokens), which belongs to one project. There is no project id to configure: the token is the project.
## Requirements [#requirements]
- **Plan:** Starter or above. The API, deploy tokens and webhooks are not part of the Free plan.
- **A deploy token** for the project, created by an Owner or Admin. Give it only the abilities your flow uses:
| What the flow does | Ability needed |
| --- | --- |
| Read the project or a batch | any ability |
| Push source files | `push` |
| Pull translated files | `pull` |
| Start a translation | `manage` |
| Subscribe to events (turn a trigger on or off) | `manage` |
## n8n [#n8n]
transglot publishes an n8n community node package, `n8n-nodes-transglot`. It adds two nodes:
- **transglot**, an action node. Resource **Project**: **Get**, **Push Source**, **Translate**, **Pull**. Resource **Batch**: **Get**.
- **transglot Trigger**, which starts a workflow when the project event you pick in **Event** fires.
### Install the package [#install-the-package]
In your n8n instance, go to **Settings**, **Community Nodes**, **Install**, enter `n8n-nodes-transglot` and confirm. On a self-hosted instance you can also install it into your custom nodes directory and restart n8n:
```bash
npm install n8n-nodes-transglot
```
### Create the credential [#create-the-credential]
Add a **transglot API** credential:
| Field | Value |
| --- | --- |
| **Base URL** | `https://api.transglot.ai` (the default) |
| **API Token** | Your deploy token, starting `tgl_` |
Select **Test**. A successful test confirms the URL, the token and the project without changing anything.
### Build the workflow [#build-the-workflow]
Add a **transglot Trigger** node to react to events, or a **transglot** node to act on the project. When you activate a workflow that starts with **transglot Trigger**, the node subscribes to the event; when you deactivate it, the subscription is removed.
## Zapier and Make [#zapier-and-make]
Use the webhook and HTTP tools built into Zapier and Make.
**To start a Zap or scenario from a transglot event:**
1. Create a trigger in Zapier or Make that gives you an incoming webhook URL.
2. In transglot, [create a webhook endpoint](https://transglot.ai/docs/developers/webhooks/create-a-webhook-endpoint) on the project with that URL, and choose the events that should start the flow.
3. Zapier and Make learn the payload shape from the first delivery they receive. To map fields before then, use the examples in the [event reference](https://transglot.ai/docs/developers/webhooks/events), or fetch one from `GET /v1/webhooks/events/{event}/sample`.
Every delivery is signed. If your flow can run code, [verify the signature](https://transglot.ai/docs/developers/webhooks/verify-signatures) before acting on a delivery.
**To act on transglot from a Zap or scenario,** add an HTTP request step that calls the API with the header `Authorization: Bearer `. Common calls:
| Goal | Request |
| --- | --- |
| Push a source file | `POST https://api.transglot.ai/v1/push` |
| Start a translation | `POST https://api.transglot.ai/v1/translate` |
| Check a batch | `GET https://api.transglot.ai/v1/batches/{id}` |
| Pull a translated file | `GET https://api.transglot.ai/v1/pull` |
See [the API reference](https://transglot.ai/docs/developers/api) for parameters and responses.
## Manage event subscriptions over the API [#manage-event-subscriptions-over-the-api]
Any tool that can make HTTP requests can subscribe and unsubscribe itself, the way the n8n trigger does:
| Request | What it does |
| --- | --- |
| `GET /v1/webhooks/events` | Lists the events you can subscribe to |
| `GET /v1/webhooks/events/{event}/sample` | Returns a sample payload for one event |
| `POST /v1/webhooks` | Subscribes a URL to one or more events. Send `url` and an `events` array. The response includes the signing `secret`, shown only this once |
| `GET /v1/webhooks` | Lists this project's subscriptions |
| `DELETE /v1/webhooks/{id}` | Removes a subscription |
A subscription created this way is an ordinary webhook endpoint on the project, with the same signing, retries and delivery log as one created in the console.
## Troubleshooting [#troubleshooting]
The token is wrong, expired or revoked. Create a new deploy token and update the credential.
Either the token lacks the `manage` ability, or the account's plan does not include webhooks. Webhooks need the Starter plan or above.
transglot only delivers to public HTTPS addresses. The response says which rule the URL broke, for example "The webhook URL must use https." or "The webhook URL resolves to a private or reserved address." Use the webhook URL your automation platform gives you.
## Related [#related]
- [Webhooks](https://transglot.ai/docs/developers/webhooks)
- [Deploy tokens](https://transglot.ai/docs/developers/deploy-tokens)
- [Connect Jira](https://transglot.ai/docs/integrations/automation/connect-jira)
# Connect BigCommerce (https://transglot.ai/docs/integrations/content-platforms/bigcommerce/)
Nothing is written back to BigCommerce today: its per-language product write API is in beta and limited to Catalyst storefronts. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | Product `name` and `description` from the Catalog API |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
In your BigCommerce store's API settings, create a store-level API account with these scopes:
- **Products**: read-only
- **Store information**: read-only
Copy the access token, and the store hash from the account's API path (`api.bigcommerce.com/stores/{hash}/…`).
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **BigCommerce** in the **CMS** list. There is no URL to type.
| Field | What to enter |
| --- | --- |
| **Store hash** | The store hash, letters and digits only, for example `abc123` |
| **API token** | The store-level API account's access token |
| **Content types** | `products` |
| **Translatable fields** | `name`, `description`, or both |
Select **Connect**, then **Test connection**. A successful test names the store.
## What gets imported [#what-gets-imported]
transglot reads every product through the v3 Catalog API and imports the mapped fields that hold text.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong or the API account is missing a scope above.
- `... the store or product was not found, check the store hash.` Check **Store hash**.
- `... the store is rate-limited, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Confluence (https://transglot.ai/docs/integrations/content-platforms/confluence/)
Nothing is written back to Confluence. Confluence has no per-page translation: a translated page lives in a separate space or page, not in a field of the original. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The title and body of every page in one space |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- The connector works with Confluence sites on `atlassian.net`.
- Create an API token for your Atlassian account in your Atlassian account's security settings. The account needs read access to the space.
- Note the space key, which appears in the space URL (for example `/wiki/spaces/DOCS`).
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Confluence** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Atlassian site** | Only the site name, for example `acme` for `acme.atlassian.net` |
| **Space key** | The key of the space holding the pages, for example `DOCS` |
| **Email & API token** | Your Atlassian account email and the API token, as `you@example.com:api_token` |
| **Content types** | `pages` |
| **Translatable fields** | `title`, `body`, or both |
Select **Connect**, then **Test connection**. A successful test names the space.
## What gets imported [#what-gets-imported]
transglot reads every page in the space and imports its title and its body. The body is imported in Confluence's storage format, so the page markup comes with the text.
## Troubleshooting [#troubleshooting]
- `The Confluence space [DOCS] was not found. Check the space key.` The key is wrong, or the account cannot see the space.
- `... the credentials were rejected.` Check the email and token, entered as `email:token`.
- `... the space or page was not found, check the site and space key.` Check **Atlassian site** holds only the site name.
- `... Confluence is rate-limiting the request, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Contentful (https://transglot.ai/docs/integrations/content-platforms/contentful/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-locale value of the fields you map, on the content types you choose |
| Written back | Each entry's target-locale field slots. The source locale is never written |
| Sync | **Sync from CMS**, or a signed webhook to the **Inbound URL** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- In Contentful, mark the fields you want translated as localized and add your target locales to the space.
- Make sure the Contentful locale codes match the language codes in your transglot project. transglot reads the locale whose code is your project's source language, and writes each target language under its own code.
- Create a Content Management token in your Contentful settings. It needs read and write access to the space.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Contentful** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Space ID** | Your space ID, from your Contentful space settings |
| **Environment** | The environment to use. Leave blank for `master` |
| **Content Management token** | The token you created (it starts with `CFPAT-`) |
| **Content types** | Content type IDs, comma separated, for example `blogPost, landingPage` |
| **Translatable fields** | Field IDs, comma separated, for example `title, body` |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. A successful test names the space it reached. The test also checks that the environment exists.
## What gets imported [#what-gets-imported]
For each entry of each content type, transglot reads the source-locale value of every mapped field. Only fields whose value is a plain string are imported; a field with another kind of value (Rich Text, a reference, a number) is skipped. When the entry has a `title` field, its value becomes the description on each of the entry's keys.
## How translations are written back [#how-translations-are-written-back]
transglot reads the entry's current version, sets the target-locale slot of each mapped field, and saves the entry with Contentful's version check. Every other field and locale is kept as it was.
transglot saves the translations to the entry, but it does not run Contentful's publish action. Publish the entry in Contentful when you want the translations live.
## Sync on content change [#sync-on-content-change]
Once connected, the CMS page shows a **Sync on content change** card with an **Inbound URL**. A POST to that URL starts a sync without anyone pressing **Sync from CMS**. The CMS page suggests triggering it on `Entry.publish`.
transglot accepts the request only when it is signed with the inbound signing secret shown once when you first connected:
- `X-Transglot-Timestamp`: the current Unix time in seconds. A timestamp more than five minutes away from transglot's clock is refused.
- `X-Transglot-Signature`: `sha256=` followed by the hex HMAC-SHA256 of the timestamp, a full stop, and the raw request body, keyed with the secret. This is the same scheme as transglot's outbound webhooks ([Verify signatures](https://transglot.ai/docs/developers/webhooks/verify-signatures)).
To sync only the entries that changed, send a body that names them:
```json
{ "entries": [ { "type": "blogPost", "id": "6xRkQ2pZ" } ] }
```
The `type` is the content type ID and the `id` is the entry ID. A body that names no entries at all runs a full sync. A body with an `entries` list that holds no usable entry is accepted and does nothing.
## Troubleshooting [#troubleshooting]
- `Invalid signature.` The request was unsigned, signed with an old secret, or its timestamp was outside the five-minute window. If you no longer have the secret, select **Rotate signing secret** and update your webhook.
- `... the credentials were rejected.` The token is wrong or revoked. Paste a current Content Management token and save.
- `... the space, environment or entry was not found, check the settings.` Check **Space ID** and **Environment**.
- `... the entry changed underneath us (version conflict), retry.` Someone saved the entry in Contentful while transglot was writing it. Run **Publish to CMS** again.
- A field never appears as a key: check the field ID, check it has a value in the source locale, and check it is a plain text field.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Contentstack (https://transglot.ai/docs/integrations/content-platforms/contentstack/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-locale version of the fields you map, on the content types you choose |
| Written back | Each entry's target-locale version. The source locale is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target languages to the stack. Contentstack locale codes must match the language codes in your transglot project.
- Create a Management Token with read and write access in the stack's token settings.
- Note the stack API key from the stack settings. It identifies the stack and is not a secret.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Contentstack** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Data-hosting region** | Your stack's region: AWS-NA, AWS-EU, AWS-AU, AZURE-NA, AZURE-EU, GCP-NA or GCP-EU |
| **Stack API key** | The stack API key, for example `blt0123456789abcdef` |
| **Management token** | The Management Token you created |
| **Content types** | Content type UIDs, comma separated |
| **Translatable fields** | Field UIDs, comma separated |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. A successful test names the stack.
## What gets imported [#what-gets-imported]
For each content type, transglot lists the entries in your source locale and imports the mapped fields that hold text. Each entry's `title` becomes the description on its keys.
## How translations are written back [#how-translations-are-written-back]
Contentstack keeps a separate version of an entry for each locale. For each target locale, transglot reads that locale's version, replaces only the mapped fields with the reviewed values, and saves it. Saving a target locale for the first time localizes the entry into that locale. Every other field is kept as it was, and the source-locale version is never addressed.
transglot saves the localized entries but does not publish them. Publish in Contentstack when you want the translations live.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The Management Token or stack API key is wrong, or the token lacks write access.
- `... the stack, content type or entry was not found, check the settings.` Check **Data-hosting region**, **Stack API key** and the content type UIDs.
- `... Contentstack is rate-limiting the request, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect DatoCMS (https://transglot.ai/docs/integrations/content-platforms/datocms/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-locale value of the localized fields you map, on the models you choose |
| Written back | Each field's target-locale slot. The source locale and every other locale are kept |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Mark the fields you want translated as localized in each model.
- Enable your target locales in the project's locale settings. DatoCMS locale codes must match the language codes in your transglot project.
- Create an API token with read and write access to your records in the project's API token settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **DatoCMS** in the **CMS** list. There is no URL to type: the API token identifies the project.
| Field | What to enter |
| --- | --- |
| **API token** | The token you created |
| **Content types** | Model IDs (or model API keys), comma separated |
| **Translatable fields** | Localized field API keys, comma separated |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. A successful test names the project.
## What gets imported [#what-gets-imported]
For each record of each model, transglot reads the source-locale value of every mapped localized field and imports the ones that hold text.
## How translations are written back [#how-translations-are-written-back]
DatoCMS treats a localized field's value as a whole, so writing one locale on its own would drop the others. transglot reads the record's current field values, adds the target-locale values, and writes the merged field back. The source locale and every other locale keep their values.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, revoked, or read-only.
- `... the model or record was not found, check the settings.` Check the model IDs in **Content types**.
- `... DatoCMS rejected the write, check the field/locale mapping.` A mapped field is not localized, or a target locale is not enabled in the project.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Drupal (https://transglot.ai/docs/integrations/content-platforms/drupal/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-language value of the fields you map, on the content types (node bundles) you choose |
| Written back | Each node's target-language translation, which must already exist. The source language is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 nodes per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Enable the **JSON:API** and **Content Translation** modules, and the core **HTTP Basic Authentication** module.
- Use URL language negotiation with a path prefix per language. transglot addresses each language as `/{language}/jsonapi/…`, using your project's language codes, so the path prefixes must match them.
- Add the target-language translation to each node you want localized. JSON:API can update a translation but cannot create one.
- Create a Drupal user with JSON:API read and write access to those nodes.
- Your site must be reachable over https at a public address.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Drupal** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Drupal site URL** | Your site root, for example `https://drupal.example.com` |
| **User & password** | The user's credentials as `username:password` |
| **Content types** | Node bundle machine names, comma separated, for example `article, page` |
| **Translatable fields** | Field machine names, comma separated, for example `title, body` |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. The test reads one node of the first content type in your source language.
## What gets imported [#what-gets-imported]
For each bundle, transglot reads the source-language nodes and imports the mapped fields that hold text. A formatted text field (such as `body`) is imported from its `value`. The node's `title` becomes the description on its keys.
## How translations are written back [#how-translations-are-written-back]
For each target language, transglot reads the node's existing translation and updates only the mapped fields. A formatted text field keeps its text format and summary; only its value changes. The source-language node is never written.
If the target translation does not exist, that entry fails with a clear error instead of being skipped silently. The rest of the publish continues.
## Troubleshooting [#troubleshooting]
- `The fr translation of article #... does not exist in Drupal. Create it there first (JSON:API can update a translation but not create one).` Add the translation in Drupal, then publish again.
- `... the credentials were rejected.` The username or password is wrong, HTTP Basic Authentication is not enabled, or the user lacks JSON:API write access.
- `... the endpoint, node or translation was not found, check the base URL and content type.` Check **Drupal site URL**, the bundle names, and that language path prefixes match your project's language codes.
- `... Drupal rejected the request body, check the field mapping.` A mapped field cannot take a text value, or does not exist on the bundle.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Freshdesk (https://transglot.ai/docs/integrations/content-platforms/freshdesk/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The title and description (body) of every solution article |
| Written back | A translated article per target language. The primary article is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target languages to the help desk's supported languages first. Freshdesk rejects a translation for a language that is not enabled.
- Copy your API key from your Freshdesk profile settings. The agent needs read and write access to solutions.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Freshdesk** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Freshdesk domain** | Only the domain, for example `acme` for `acme.freshdesk.com` |
| **API key** | Your Freshdesk API key |
| **Content types** | `articles` |
| **Translatable fields** | `title`, `description`, or both |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the help desk.
## What gets imported [#what-gets-imported]
transglot walks your knowledge base (categories, then folders, then articles) and imports each article's title and description in the primary language. The description is the article body.
## How translations are written back [#how-translations-are-written-back]
For each target language, transglot updates the translated article if it exists and creates it if it does not. Only the title and description are sent, and the primary article is never addressed.
A translated article takes the primary article's status, so a translation is never published ahead of its source: a draft article gets a draft translation. Language codes are converted to Freshdesk's form, so `pt_BR` in transglot is published as `pt-BR`.
## Troubleshooting [#troubleshooting]
- `... the translation language is not enabled on this help desk.` Add the language to the help desk's supported languages, then publish again.
- `... the credentials were rejected.` The API key is wrong, or the agent lacks access to solutions.
- `... the article or knowledge base was not found.` Check **Freshdesk domain**, and that the article still exists.
- `... the help desk is rate-limited, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Ghost (https://transglot.ai/docs/integrations/content-platforms/ghost/)
Nothing is written back to Ghost. A Ghost post is single-language and has no per-language slot to write into. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The `title` and `plaintext` of posts and pages |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
In Ghost admin, add a custom integration in the integration settings and copy its **Admin API key**. It has the form `{key-id}:{hex-secret}`.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Ghost** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Ghost site URL** | Your Ghost site root, for example `https://blog.example.com`. transglot adds `/ghost/api/admin/…` itself. It must use https and be publicly reachable |
| **Admin API key** | The custom integration's Admin API key |
| **Content types** | `posts`, `pages`, or both |
| **Translatable fields** | `title`, `plaintext`, or both |
Select **Connect**, then **Test connection**. A successful test names the site.
## What gets imported [#what-gets-imported]
transglot reads each post and page through the Ghost Admin API and imports its title and its plain-text body. The `plaintext` field is the body without formatting, so markup and cards are not carried into transglot.
## Troubleshooting [#troubleshooting]
- The form refuses the key: an Admin API key is a 24-character hex ID, a colon, and a 64-character hex secret. A Content API key has a different form and will not work.
- `The Ghost Admin API key is malformed (expected "id:hex-secret").` or `The Ghost Admin API secret is not valid hexadecimal.` Copy the Admin API key again from the custom integration.
- `Failed verifying the Ghost Admin API key: the credentials were rejected.` The key is wrong or the integration was deleted.
- `... the resource was not found, check the site URL.` Check **Ghost site URL** points at the site root.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Help Scout (https://transglot.ai/docs/integrations/content-platforms/help-scout/)
Nothing is written back to Help Scout. Help Scout Docs models each language as a separate Docs site, not as a translation of an article. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The name and text of every Docs article |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
Copy a **Docs API key** from the API keys section of your Help Scout profile. It must be a Docs API key, not a Mailbox API key.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Help Scout** in the **CMS** list. There is no URL to type.
| Field | What to enter |
| --- | --- |
| **Docs API key** | Your Help Scout Docs API key |
| **Content types** | `articles` |
| **Translatable fields** | `name`, `text`, or both |
Select **Connect**, then **Test connection**. A successful test names the first collection it finds.
## What gets imported [#what-gets-imported]
transglot walks your collections and their articles, and imports each article's name (its title) and text (its body).
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The key is wrong, revoked, or is a Mailbox API key rather than a Docs API key.
- `... the collection or article was not found.` The article was removed while the sync ran. Run **Sync from CMS** again.
- `... Help Scout is rate-limiting the request, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect HubSpot (https://transglot.ai/docs/integrations/content-platforms/hubspot/)
Nothing is written back to HubSpot today: its per-language write path (creating a language variation of a published post, then editing it) is not supported yet. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The `htmlTitle` and `postBody` of source blog posts |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
Create a private app in your HubSpot integration settings with the `content` scope, and copy its access token.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **HubSpot** in the **CMS** list. There is no URL to type: the token identifies your HubSpot account.
| Field | What to enter |
| --- | --- |
| **Private app token** | The private app's access token (it starts with `pat-`) |
| **Content types** | `blog-posts` |
| **Translatable fields** | `htmlTitle`, `postBody`, or both |
Select **Connect**, then **Test connection**. The test reads one blog post.
## What gets imported [#what-gets-imported]
transglot reads your blog posts through the CMS Blogs API and imports each post's title and body. A post that is already a translation of another post (a language variation) is skipped, so a translation is never imported as source text.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, revoked, or the private app does not have the `content` scope.
- `... the blog or post was not found.` The post was removed while the sync ran. Run **Sync from CMS** again.
- `... HubSpot is rate-limiting the request, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Hygraph (https://transglot.ai/docs/integrations/content-platforms/hygraph/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-locale value of the localized fields you map, on the models you choose |
| Written back | Each entry's target-locale localization, created if it does not exist yet. The source locale is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target locales in the project's locale settings and mark the fields you want translated as localized. Hygraph's locale API IDs must match the language codes in your transglot project.
- Create a Permanent Auth Token with read and mutation permissions (including localizations) in the project's API access settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Hygraph** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Content API endpoint URL** | Your project's High Performance or Content API URL, from the project's API access settings |
| **Permanent Auth Token** | The token you created |
| **Content types** | Singular model API IDs in PascalCase, comma separated, for example `Post, Author` |
| **Translatable fields** | Localized field API IDs, comma separated |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. The test runs a minimal GraphQL query against the endpoint.
transglot builds its queries from Hygraph's default names: the model `Post` is read through `posts` and `post`, and written with `updatePost`. Model, field and locale IDs may contain only letters, digits and underscores, and cannot start with a digit.
## What gets imported [#what-gets-imported]
For each model, transglot queries the entries in your source locale and imports the mapped fields that hold text. When an entry has a `title` field, its value becomes the description on the entry's keys.
## How translations are written back [#how-translations-are-written-back]
Hygraph localizes whole entries rather than single fields. transglot runs one update mutation per entry that upserts the target-locale localizations with the reviewed values. A target locale that does not exist on the entry yet is created; one that exists is updated. The source localization is never addressed.
transglot does not run a publish mutation. Publish the entries in Hygraph when you want the translations live.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong or lacks the permissions above.
- `... the endpoint was not found, check the Content API URL.` Check **Content API endpoint URL**.
- `Hygraph rejected the GraphQL request. Check the model, field and locale mapping.` A model or field ID does not exist, is not localized, or a locale is not set up in the project.
- `The Hygraph connector cannot use the identifier [...] in a GraphQL query.` A model, field or locale ID contains characters other than letters, digits and underscores.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect a content platform (https://transglot.ai/docs/integrations/content-platforms/)
A content platform connection reads text out of a system such as Contentful, Shopify or Zendesk Guide, turns each translatable field into a key in your project, and (for two-way platforms) writes the reviewed translations back into that system's own per-language slots.
Each project holds one connection. You set it up on the project's **CMS** settings page.
## Who can do this [#who-can-do-this]
Owners and admins of the project. Content platform connections are included on the Starter plan and above; see [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements) and [pricing](https://transglot.ai/pricing).
## Two-way and import only [#two-way-and-import-only]
Every connector is one of two kinds, and the CMS page tells you which before you connect.
- **Two-way.** transglot imports your source content, and publishes reviewed translations back into the platform. It writes only the target-language slots and never touches your source content.
- **Import only.** transglot imports your source content and you translate it, but nothing is written back, because the platform has no per-language place to write to. In place of the publish controls, the CMS page shows a card such as **Notion is import only**.
For an import-only platform, you take the translations out another way: pull any locale as a localization file over the [REST API](https://transglot.ai/docs/developers/api) or the [transglot CLI](https://transglot.ai/docs/developers/cli), or publish it as a [CDN bundle](https://transglot.ai/docs/guides/delivery/the-cdn) your app reads at runtime.
## Every connector [#every-connector]
| Platform | Direction | Translations are written to |
| --- | --- | --- |
| [Contentful](https://transglot.ai/docs/integrations/content-platforms/contentful) | Two-way | Each entry's localized field slots |
| [Sanity](https://transglot.ai/docs/integrations/content-platforms/sanity) | Two-way | The target-language keys of field-level locale objects |
| [Storyblok](https://transglot.ai/docs/integrations/content-platforms/storyblok) | Two-way | Each story's field-level translation keys |
| [Webflow](https://transglot.ai/docs/integrations/content-platforms/webflow) | Two-way | Each CMS item's secondary-locale variants |
| [Strapi](https://transglot.ai/docs/integrations/content-platforms/strapi) | Two-way | Each document's target-locale localization |
| [DatoCMS](https://transglot.ai/docs/integrations/content-platforms/datocms) | Two-way | Each record's target-locale field slots |
| [Hygraph](https://transglot.ai/docs/integrations/content-platforms/hygraph) | Two-way | Each entry's target-locale localization |
| [Contentstack](https://transglot.ai/docs/integrations/content-platforms/contentstack) | Two-way | Each entry's target-locale version |
| [Kontent.ai](https://transglot.ai/docs/integrations/content-platforms/kontent-ai) | Two-way | Each item's target-language variant |
| [Prismic](https://transglot.ai/docs/integrations/content-platforms/prismic) | Import only | Not written back |
| [WordPress](https://transglot.ai/docs/integrations/content-platforms/wordpress) | Two-way | One translation bundle per post, page or term |
| [Drupal](https://transglot.ai/docs/integrations/content-platforms/drupal) | Two-way | Each node's existing target-language translation |
| [Ghost](https://transglot.ai/docs/integrations/content-platforms/ghost) | Import only | Not written back |
| [Notion](https://transglot.ai/docs/integrations/content-platforms/notion) | Import only | Not written back |
| [Confluence](https://transglot.ai/docs/integrations/content-platforms/confluence) | Import only | Not written back |
| [Shopify](https://transglot.ai/docs/integrations/content-platforms/shopify) | Two-way | Shopify translations in each secondary locale |
| [WooCommerce](https://transglot.ai/docs/integrations/content-platforms/woocommerce) | Two-way | One translation bundle per product |
| [Magento](https://transglot.ai/docs/integrations/content-platforms/magento) | Two-way | Each target store view |
| [BigCommerce](https://transglot.ai/docs/integrations/content-platforms/bigcommerce) | Import only | Not written back |
| [PrestaShop](https://transglot.ai/docs/integrations/content-platforms/prestashop) | Two-way | Each target language's field value |
| [Zendesk Guide](https://transglot.ai/docs/integrations/content-platforms/zendesk-guide) | Two-way | Per-locale article translations |
| [Intercom](https://transglot.ai/docs/integrations/content-platforms/intercom) | Two-way | Each article's per-locale translated content |
| [Freshdesk](https://transglot.ai/docs/integrations/content-platforms/freshdesk) | Two-way | Per-language translated articles |
| [Help Scout](https://transglot.ai/docs/integrations/content-platforms/help-scout) | Import only | Not written back |
| [HubSpot](https://transglot.ai/docs/integrations/content-platforms/hubspot) | Import only | Not written back |
| [Mailchimp](https://transglot.ai/docs/integrations/content-platforms/mailchimp) | Import only | Not written back |
| [Klaviyo](https://transglot.ai/docs/integrations/content-platforms/klaviyo) | Import only | Not written back |
App Store Connect and Google Play listings connect from the same CMS page. They have their own pages under [App stores](https://transglot.ai/docs/integrations/app-stores/app-store-connect).
## Connect a platform [#connect-a-platform]
### Prepare the platform [#prepare-the-platform]
Each connector page lists what to set up first: usually the target languages enabled on the platform side, and a token or key with read access (and write access, for a two-way platform).
### Open the CMS page [#open-the-cms-page]
Open your project's settings and choose **CMS**. In the **CMS** list, pick the platform. The page shows a short explanation of how that connector works and what it needs.
### Fill in the connection [#fill-in-the-connection]
Enter the address fields and the credential the form asks for, then choose the **Content types** and **Translatable fields** to import. For a two-way platform, tick the locales under **Publish these locales**.
Select **Connect**. The credential is stored encrypted and never shown again; on later edits, leave it blank to keep the stored one.
### Test the connection [#test-the-connection]
Select **Test connection**. On success the page reports **Connection verified.**, usually followed by the name of the site, space or store it reached.
### Run the first sync [#run-the-first-sync]
Under **Sync**, select **Sync from CMS**. The import runs in the background; **Last sync** updates when it finishes.
The platform is fixed for the life of a connection. To switch platforms, disconnect and connect again.
## How imported content becomes keys [#how-imported-content-becomes-keys]
Each sync reads the source-language value of every mapped field, for every entry of every selected content type. Each field becomes one key, named after the platform, the content type, the entry and the field (for example `wp:posts:42:title`). Because the name is stable, a later sync updates the same key instead of creating a duplicate.
- Empty fields are skipped, so they never create a key.
- The platform is the source of truth for source text. Edit source text in the platform: the next sync replaces the source text in transglot with what the platform holds.
- One sync reads up to 5,000 entries in total. Some platforms read fewer per content type; their pages give the limit.
Imported keys are ordinary keys. They go through translation memory, the glossary and quality checks like everything else. If **Auto-translate** is on in [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings), new and changed source text is translated by Glossa as it arrives. A translation you have edited or reviewed is never overwritten by AI.
## Review, then publish back [#review-then-publish-back]
Only translations in the **Reviewed** state are published. Review them in the [editor](https://transglot.ai/docs/guides/translating/the-editor) or with [Approve or flag a translation](https://transglot.ai/docs/guides/translating/approve-or-flag-a-translation).
For a two-way platform, publishing sends:
- only keys this connection imported,
- only the fields in **Translatable fields** and the types in **Content types**,
- only the locales ticked under **Publish these locales**. Your source locale is never published.
There are two ways to publish:
- **Publish to CMS** under **Sync** queues a publish straight away.
- **Publish automatically whenever a translation is reviewed** queues a publish each time a translation in the project is reviewed.
A publish is idempotent: running it again with nothing new reviewed writes the same values. One publish run writes up to 5,000 entries. If a single entry fails (for example because it was deleted on the platform), the rest of the run continues.
## Keep content in sync [#keep-content-in-sync]
transglot does not poll your platform. New and changed content arrives when:
- you select **Sync from CMS**, or
- the platform notifies transglot of a change. Today that covers the [WordPress plugin](https://transglot.ai/docs/integrations/content-platforms/wordpress), which sends a change notification when you save, and [Contentful](https://transglot.ai/docs/integrations/content-platforms/contentful), whose CMS page shows an **Inbound URL** for a signed webhook.
On the first save of a connection, the page shows **Your inbound signing secret: shown once**. A change notification must be signed with this secret, using the same scheme as transglot's outbound webhooks ([Verify signatures](https://transglot.ai/docs/developers/webhooks/verify-signatures)). An unsigned or stale request is refused with `Invalid signature.` If you lose the secret, select **Rotate signing secret**; the old secret stops working at once.
## Disconnect [#disconnect]
At the bottom of the CMS page, select the disconnect button (it names your platform, for example **Disconnect Contentful**), then **Yes, disconnect**. This removes the stored credential and stops all syncing. Keys already imported stay in the project.
## If something goes wrong [#if-something-goes-wrong]
Your workspace is on the Free plan. Content platform connections start at Starter. An attempt to connect on Free is refused with `This integration is not enabled for your workspace.`
The message ends in `the credentials were rejected.` The platform answered 401 or 403. Check the token is current and has the access the connector page lists, then paste it again and save.
`Could not reach the CMS. Check the site URL.` means the request never got an answer. For a self-hosted platform, the URL must use https and resolve to a public address. A URL that does not is refused with a message such as `The webhook URL must use https.` or `The webhook URL resolves to a private or reserved address.`
Check that the content types and field IDs you entered match the platform exactly, that the fields hold text in your project's source language, and that your credential can read those types. Fields with no source-language value are skipped.
If the page says `Syncing and publishing cover every language on this connection, and your access is limited to a subset of them. An administrator can run these.`, your role is limited to some languages. Ask an admin whose access covers every language on the connection.
The platform is import only. Take the translations out through the API, the CLI or the CDN instead.
## Related [#related]
- [The integrations hub](https://transglot.ai/docs/integrations)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
- [Translation states](https://transglot.ai/docs/concepts/translation-states)
- [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings)
# Connect Intercom (https://transglot.ai/docs/integrations/content-platforms/intercom/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | Article title, description and body; collection name and description |
| Written back | Each article's or collection's translated content for the target locales. The default-locale text is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 entries per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Enable your target languages in your Intercom Help Center settings. Intercom rejects a translation for a locale that is not enabled there, and transglot reports that error rather than hiding it.
- Create an app in Intercom's developer settings and copy its workspace access token.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Intercom** in the **CMS** list. There is no URL to type.
| Field | What to enter |
| --- | --- |
| **Data-hosting region** | Where your workspace is hosted: US, EU or AU |
| **Access token** | The workspace access token |
| **Content types** | `articles`, `collections`, or both |
| **Translatable fields** | `title`, `description` and `body` for articles; `name` and `description` for collections |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the workspace.
## What gets imported [#what-gets-imported]
transglot reads each article's title, description and body, and each collection's name and description, as they appear in the default locale.
## How translations are written back [#how-translations-are-written-back]
Intercom stores translations in each article's translated content, one entry per locale. transglot reads the article, adds or replaces the target locales, and saves only the translated content. Locales and fields transglot does not manage are kept, and the default-locale text is never in the request.
Each new locale on an article takes the article's own author and state, so a translation of a published article is published too. Language codes are converted to Intercom's form, so `pt_BR` in transglot is published as `pt-BR`.
## Keeping in sync [#keeping-in-sync]
Run **Sync from CMS** to pick up new and changed articles. Intercom's own webhooks cannot be verified by transglot's inbound signature check yet, so there is no automatic sync on change.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The access token is wrong or revoked, or the region does not match your workspace.
- `... the article or help center was not found.` Check **Data-hosting region**, and that the article still exists.
- `... the workspace is rate-limited, retry.` Wait and run the sync or publish again.
- A locale fails to publish: check the language is enabled in your Help Center settings.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Klaviyo (https://transglot.ai/docs/integrations/content-platforms/klaviyo/)
Nothing is written back to Klaviyo. A Klaviyo template is single-language and has no per-language slot to write into. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | Email template name, HTML and text |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Per sync | Up to 500 templates |
| Plan | Starter and above |
## Before you start [#before-you-start]
Create a private API key in your Klaviyo account's API key settings, with the `templates:read` scope.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Klaviyo** in the **CMS** list. There is no URL to type: the key identifies the account.
| Field | What to enter |
| --- | --- |
| **Private API key** | Your private API key (it starts with `pk_`) |
| **Content types** | `templates` |
| **Translatable fields** | Any of `name`, `html`, `text` |
Select **Connect**, then **Test connection**.
## What gets imported [#what-gets-imported]
transglot reads the email templates in your account and imports the mapped fields: the template's name, and its HTML and text bodies. One sync reads up to 500 templates.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The key is wrong, revoked, or lacks the `templates:read` scope. A public API key (site ID) does not work.
- `... the template was not found.` The template was removed while the sync ran. Run **Sync from CMS** again.
- `... Klaviyo is rate-limiting the request, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Kontent.ai (https://transglot.ai/docs/integrations/content-platforms/kontent-ai/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-language variant of the elements you map, on the content types you choose |
| Written back | Each item's target-language variant, created if it does not exist yet. The source variant is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target languages to the environment. Their codenames must match the language codes in your transglot project.
- Create a Management API key in the environment's API key settings.
- Note the environment ID.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Kontent.ai** in the **CMS** list. There is no URL to type.
| Field | What to enter |
| --- | --- |
| **Environment ID** | Your environment ID |
| **Management API key** | The key you created |
| **Content types** | Content type codenames, comma separated |
| **Translatable fields** | Element codenames, comma separated |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the environment.
## What gets imported [#what-gets-imported]
For each content type, transglot reads every item's source-language variant and imports the mapped elements whose value is text.
## How translations are written back [#how-translations-are-written-back]
Each language is a separate variant of the same item. For each target language, transglot reads that variant, replaces only the mapped elements' values, and saves it. Every other element keeps its value. A target variant that does not exist yet is created with only the mapped elements.
If the target variant is in the **Published** or **Scheduled** workflow step, it cannot be changed in place, so transglot first creates a new version of it and writes the translations into that new version. The source-language variant is never addressed.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The Management API key is wrong or revoked.
- `... the environment, type, item or variant was not found, check the settings.` Check **Environment ID** and the content type codenames.
- `... Kontent.ai is rate-limiting the request, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Magento (https://transglot.ai/docs/integrations/content-platforms/magento/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | Product name, description and short description, read from your source store view |
| Written back | The same fields in each target store view you map. The source store view is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 products |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Create a store view for each target language in your store configuration.
- Create an integration in Magento's integration settings and activate it to get an access token with catalog read and write access. An admin token also works.
- Your store must be reachable over https at a public address.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Magento** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Magento store URL** | Your Magento host, for example `https://shop.example.com`. transglot adds `/rest/{storeView}/V1/…` itself |
| **Source store-view code** | The store view that holds your source-language content. Leave it as `default` for most stores |
| **Access token** | The integration access token |
| **Content types** | `products` |
| **Translatable fields** | Any of `name`, `description`, `short_description` |
| **Locale → store view** | For each target language in your project, its Magento store-view code, for example `fr_fr` |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. The test runs a minimal product search, which checks both the address and the token.
## What gets imported [#what-gets-imported]
transglot reads every product from the source store view, by SKU, and imports the mapped fields. `name` is the product's own field; `description` and `short_description` are read from its custom attributes. A product whose SKU contains a colon (`:`) is skipped.
## How translations are written back [#how-translations-are-written-back]
For each target language, transglot writes the reviewed values to the product in that language's mapped store view. A store-view value overrides only that scope, so your source store view keeps its content.
- A language with no store view under **Locale → store view** is skipped.
- A language mapped to the source store view is skipped.
- The reserved scopes `all` and `admin` cannot be used as targets.
## Troubleshooting [#troubleshooting]
- `That URL did not return a Magento store (the products search was missing).` The address is not the Magento REST API root.
- `... the credentials were rejected.` The token is wrong, the integration is not active, or it lacks catalog access.
- `... the store, store view or product was not found, check the URL and store-view codes.` Check **Source store-view code** and the codes under **Locale → store view**.
- A language never receives translations: check it has a store-view code mapped and is ticked under **Publish these locales**.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Mailchimp (https://transglot.ai/docs/integrations/content-platforms/mailchimp/)
Nothing is written back to Mailchimp. A Mailchimp campaign is single-language and has no per-language slot to write into. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | Campaign subject line and preview text, and optionally the HTML and plain-text content |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 campaigns |
| Plan | Starter and above |
## Before you start [#before-you-start]
Create an API key in your Mailchimp account's API key settings. It ends in your data center, for example `-us21`.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Mailchimp** in the **CMS** list. There is no URL to type: transglot reads your data center from the end of the API key.
| Field | What to enter |
| --- | --- |
| **API key** | Your Mailchimp API key, for example `0123…abcde-us21` |
| **Content types** | `campaigns` |
| **Translatable fields** | Any of `subject_line`, `preview_text`, `html`, `plain_text` |
Select **Connect**, then **Test connection**. A successful test names the data center, for example **Mailchimp (us21)**.
## What gets imported [#what-gets-imported]
transglot reads every campaign in the account. The subject line and preview text come from the campaign's settings. When you map `html` or `plain_text`, transglot also reads each campaign's content. If one campaign's content cannot be read (for example a campaign with no content yet), the sync still imports its subject line and preview text and moves on.
## Troubleshooting [#troubleshooting]
- The form refuses the key: it must end in a data center suffix, such as `-us21`. Copy the whole key from Mailchimp.
- `... the credentials were rejected.` The key is wrong or was revoked.
- `... the campaign was not found, check the data center.` The key's suffix does not match your account's data center.
- `... Mailchimp is rate-limiting the request, retry.` Wait and run the sync again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Notion (https://transglot.ai/docs/integrations/content-platforms/notion/)
Nothing is written back to Notion. A Notion page is single-language and has no per-language slot to write into. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The title property and the text properties you map, on the pages of the databases you choose |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Create an internal integration in Notion's integration settings and copy its token.
- Share each database you want to import with the integration. A database is not visible to the API until you do.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Notion** in the **CMS** list. There is no URL to type.
| Field | What to enter |
| --- | --- |
| **Integration token** | The internal integration token, for example `ntn_…` |
| **Content types** | Database IDs, comma separated |
| **Translatable fields** | Property names, comma separated, for example `Name, Summary` |
Select **Connect**, then **Test connection**. A successful test names the integration's bot user.
## What gets imported [#what-gets-imported]
For each page in each database, transglot imports the plain text of the properties you map. Only `title` and `rich_text` properties are read, and their formatting is not kept. The page body (its blocks) is not imported.
## Troubleshooting [#troubleshooting]
- `Failed verifying the Notion token: the credentials were rejected.` The token is wrong or the integration was removed.
- `... the database or page was not found, check it is shared with the integration.` Share the database with the integration in Notion, and check the database ID.
- A property never appears as a key: check the name matches exactly and the property is a title or text property.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect PrestaShop (https://transglot.ai/docs/integrations/content-platforms/prestashop/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | Name, description and short description of products; name and description of categories, in your source language |
| Written back | The same fields in each target language you map. The source language is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 entries per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Enable the webservice in PrestaShop's advanced parameters, and create a webservice key with read and write access to products and categories.
- Add your target languages to the store and note each one's language ID.
- Your store must be reachable over https at a public address.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **PrestaShop** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **PrestaShop store URL** | Your store root, for example `https://shop.example.com`. transglot adds `/api/…` itself |
| **Source language ID** | The PrestaShop language ID that holds your source content, usually `1` |
| **Locale → language ID** | For each target language in your project, its PrestaShop language ID, for example `2` |
| **Webservice key** | The webservice key |
| **Content types** | `products`, `categories`, or both |
| **Translatable fields** | Any of `name`, `description`, `description_short` |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the store's host.
## What gets imported [#what-gets-imported]
transglot reads every product and category and imports the value of each mapped field in the source language ID. Categories have no short description, so that field is skipped for them.
## How translations are written back [#how-translations-are-written-back]
A PrestaShop multilingual field holds one value per language ID. For each product or category, transglot reads the resource, replaces only the values for the target language IDs you mapped, and saves it. The source-language value and every other field are kept exactly as they were.
- A language with no ID under **Locale → language ID** is skipped.
- A language mapped to the source language ID is skipped.
## Troubleshooting [#troubleshooting]
- `Failed verifying the PrestaShop webservice key: the credentials were rejected.` The key is wrong, disabled, or the webservice is off.
- `... the store, resource or id was not found, check the URL and webservice key.` Check **PrestaShop store URL** and that the key has access to products and categories.
- `... the store is rate-limited, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Prismic (https://transglot.ai/docs/integrations/content-platforms/prismic/)
Nothing is written back to Prismic. Its Content API is read only, and its Migration API cannot set the alternate language of an existing document. Take the translations out through the [REST API](https://transglot.ai/docs/developers/api), the [transglot CLI](https://transglot.ai/docs/developers/cli) or the [CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
| | |
| --- | --- |
| Direction | Import only |
| Imported | The top-level text fields you map, on published documents in your source locale |
| Written back | Nothing |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
Generate a permanent access token in your Prismic repository's API and security settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Prismic** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Prismic repository** | Only the repository name, for example `acme-repo`. transglot builds the `…cdn.prismic.io` address itself |
| **Source locale** | The Prismic locale to import from, usually the master language, for example `en-us`. Leave blank to use your project's source language |
| **Access token** | The permanent access token |
| **Content types** | Custom type API IDs, comma separated |
| **Translatable fields** | Top-level field API IDs, comma separated |
Select **Connect**, then **Test connection**. A successful test names the repository.
## What gets imported [#what-gets-imported]
transglot reads the published version of each document of each custom type, in the source locale, and imports the mapped top-level fields:
- A Key Text field is imported as it is.
- A Rich Text or Title field is imported as plain text, one line per block. Formatting is not kept.
When a document has a `title` field, its text becomes the description on the document's keys.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The access token is wrong or revoked.
- `... the repository or endpoint was not found, check the repository name.` Enter only the repository name, not a URL.
- `The Prismic repository did not return a master ref.` The repository answered without a master ref (the pointer to its published content), so transglot has nothing to read. Check the repository name.
- Nothing is imported: check **Source locale** matches the locale your documents are written in.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [The transglot CLI](https://transglot.ai/docs/developers/cli)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Sanity (https://transglot.ai/docs/integrations/content-platforms/sanity/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-language key of each mapped field, on published documents of the types you choose |
| Written back | The target-language keys of the same fields. The source-language key is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Your translatable fields must use field-level localization: an object keyed by language, such as `{ en: "…", fr: "…" }` (the `localeString` and `localeText` pattern). A plain string field has no per-language keys to read or write.
- The language keys in those objects must match the language codes in your transglot project.
- Create an API token with write access in your Sanity project's API settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Sanity** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Project ID** | Your Sanity project ID |
| **Dataset** | The dataset name, for example `production` |
| **API version** | A dated version such as `v2021-06-07`. Leave blank for the default |
| **API token** | The token you created |
| **Content types** | Document type names, comma separated |
| **Translatable fields** | Field names, comma separated |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the project and dataset.
## What gets imported [#what-gets-imported]
transglot reads published documents only; drafts are not imported. For each mapped field it reads the value under your project's source language key.
Localized Portable Text fields (an object keyed by language whose values are block arrays) are supported. Each one is imported as a single string per field, with markers that keep the block structure.
## How translations are written back [#how-translations-are-written-back]
transglot patches only the target-language keys of each mapped field (for example `title.fr`) on the published document. The source-language key and every other language are left as they are.
For a Portable Text field, transglot rebuilds the target-language blocks from a copy of the source blocks, with the reviewed text placed back in. If a block's translation cannot be placed back safely, that block keeps the source text, so the field always stays valid Portable Text.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, revoked, or has no write access.
- `... the project or dataset was not found, check the settings.` Check **Project ID** and **Dataset**.
- A field never appears as a key: check it uses the field-level localization pattern and has a value under your source language key on a published document.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Shopify (https://transglot.ai/docs/integrations/content-platforms/shopify/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The primary-locale value of the fields you choose, on products, collections and pages |
| Written back | Shopify translations registered for each secondary locale. The primary-locale content is never changed |
| Sync | **Sync from CMS** |
| Per sync | Up to 2,500 resources per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target languages to the store in Shopify's language settings. Their locale codes must match the language codes in your transglot project.
- Create a custom app in your Shopify admin and install it to get an **Admin API access token**. Give it these scopes: `read_translations` and `write_translations`, plus `read_products` (for products and collections) and `read_content` (for pages).
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Shopify** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Store domain** | Only the store handle, for example `acme` for `acme.myshopify.com` |
| **Admin API access token** | The custom app's token (it starts with `shpat_`) |
| **Content types** | Any of `products`, `collections`, `pages` |
| **Translatable fields** | Any of `title`, `body_html`, `handle`, `product_type` |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the store.
## What gets imported [#what-gets-imported]
transglot reads Shopify's translatable content for each resource in the store's primary locale. A field that is not translatable on a given resource is skipped; for example, `product_type` exists only on products.
Translating `handle` gives the resource a different URL handle in that language. Leave it out of **Translatable fields** if you want the same handle everywhere.
## How translations are written back [#how-translations-are-written-back]
Shopify keeps each secondary-locale translation as a separate record from the primary content, pinned to a fingerprint (digest) of the source value. At publish time transglot reads each resource's current digests and registers the reviewed values for the target locales against them. Your primary locale is never registered, so it cannot be overwritten.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, or the app is missing one of the scopes above.
- `... the shop or resource was not found, check the shop domain.` Enter only the store handle in **Store domain**.
- `... the shop is unavailable (frozen or locked).` The store is paused or locked in Shopify.
- `... the Shopify Admin API rejected the translation.` Shopify refused a value, for example because the target language has not been added to the store.
- `... the shop is rate-limited, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Storyblok (https://transglot.ai/docs/integrations/content-platforms/storyblok/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The base (source) value of the fields you map, on stories whose content type you choose |
| Written back | Each field's `__i18n__` key for the target language. The base field is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 1,250 stories per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- In the component schema, mark each field you want translated as translatable (field-level translation).
- Enable your target languages in the space settings. Their language codes must match the language codes in your transglot project.
- Create a personal access token in your Storyblok account settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Storyblok** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Space ID** | Your numeric space ID, from the space's general settings |
| **Region** | The data region your space is hosted in: EU, US, AP or CA |
| **Personal access token** | The token you created |
| **Content types** | The component names of the stories to import, comma separated |
| **Translatable fields** | Field names, comma separated |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the space.
## What gets imported [#what-gets-imported]
For every story whose content type is one you listed, transglot reads each mapped field from the story's top-level content. The base field (for example `content.title`) is the source. Fields with no text are skipped.
## How translations are written back [#how-translations-are-written-back]
Storyblok stores a field-level translation as a separate key next to the base field: `content.title__i18n__fr` for French. transglot reads the story, sets those target-language keys for the mapped fields, and saves the story. The base field and any content transglot does not manage are kept as they were.
transglot saves the story but does not publish it in Storyblok. Publish the story when you want the translations live.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The personal access token is wrong or revoked.
- `... the space or story was not found, check the settings.` Check **Space ID** and **Region**.
- A story is not imported: check its content type is in **Content types** and the mapped fields are text fields at the top level of the story.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Strapi (https://transglot.ai/docs/integrations/content-platforms/strapi/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The source-locale localization of the fields you map, on the content types you choose |
| Written back | Each document's target-locale localization. The source locale is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 1,250 documents per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- The connector targets **Strapi 5** (documents addressed by `documentId`).
- Enable the Internationalization (i18n) plugin, add your target locales, and mark the fields you want translated as localized. Strapi's locale codes must match the language codes in your transglot project.
- Your Strapi must be reachable over https at a public address.
- Create an API token with read and write access (or a custom token scoped to your content types) in Strapi's API token settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Strapi** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Strapi base URL** | Your Strapi instance root, for example `https://cms.example.com`. transglot adds `/api/…` itself |
| **API token** | The token you created |
| **Content types** | Plural API IDs of your collection types, comma separated, for example `articles, pages` |
| **Translatable fields** | Attribute names, comma separated |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. The test reads one entry of the first content type in your source locale.
## What gets imported [#what-gets-imported]
transglot lists each content type in your project's source locale and imports the mapped attributes that hold text. When a document has a `title` attribute, its value becomes the description on the document's keys.
## How translations are written back [#how-translations-are-written-back]
Each locale is a separate localization of the same document. transglot writes each target locale with a request addressed to that locale, which creates the localization if it does not exist yet and updates it if it does. Other locales, including the source, are left as they were.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, expired, or lacks write access to the content type.
- `... the endpoint or document was not found, check the base URL and content type.` Check **Strapi base URL** and that each content type is the plural API ID.
- `The webhook URL must use https.` or `The webhook URL resolves to a private or reserved address.` transglot only connects to a public https address.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Webflow (https://transglot.ai/docs/integrations/content-platforms/webflow/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | The primary-locale value of the fields you map, on the collections you choose |
| Written back | Each item's secondary-locale variant. The primary locale is never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add your target locales to the site in Webflow's localization settings.
- For each existing item, add its secondary-locale variants in the CMS. The Webflow API can update a locale variant of an existing item but cannot create one.
- Create a site API token with CMS read and write access in your site's settings.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Webflow** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Site ID** | Your site ID, from the site's general settings or the URL of the site dashboard |
| **API token** | The site API token you created |
| **Content types** | Collection IDs, comma separated |
| **Translatable fields** | Field slugs, comma separated |
| **Publish these locales** | The target locales to write back |
Select **Connect**, then **Test connection**. A successful test names the site.
## What gets imported [#what-gets-imported]
For every item in each collection, transglot reads the primary-locale variant and imports the mapped fields that hold text. When an item has a `name` field, its value becomes the description on the item's keys.
## How translations are written back [#how-translations-are-written-back]
In Webflow each locale is a separate variant of the same item. transglot matches each target language to one of the site's secondary locales (by the full locale tag first, then by the language alone) and updates only that variant's mapped fields.
- A language with no matching secondary locale on the site is skipped.
- The primary (source) variant is a separate record, and Webflow refuses writes to it through this path.
- transglot updates the items but does not publish the site. Publish in Webflow when you want the translations live.
## Troubleshooting [#troubleshooting]
- `... the credentials were rejected.` The token is wrong, revoked, or lacks CMS write access.
- `... the site, collection or item was not found, check the settings.` Check **Site ID** and the collection IDs.
- `... the item changed underneath us (conflict), retry.` Run **Publish to CMS** again.
- Translations for one language never arrive: check that language is a secondary locale on the site and that the item has a variant for it.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect WooCommerce (https://transglot.ai/docs/integrations/content-platforms/woocommerce/)
| | |
| --- | --- |
| Direction | Two-way for products. Categories are imported but not written back |
| Imported | Name, description and short description of products; name and description of categories |
| Written back | One translation bundle per product, in the `_transglot_translations` product meta. The product's own fields are never written |
| Sync | **Sync from CMS** |
| Plan | Starter and above |
## Before you start [#before-you-start]
- In WooCommerce's advanced settings, create a REST API key with **Read/Write** permission and copy the consumer key and consumer secret.
- Your store must be reachable over https at a public address.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **WooCommerce** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **WooCommerce store URL** | Your store root, for example `https://store.example.com`. transglot adds `/wp-json/wc/v3/…` itself |
| **Consumer key & secret** | The key and secret joined by a colon: `ck_…:cs_…` |
| **Content types** | `products`, `categories`, or both |
| **Translatable fields** | Any of `name`, `description`, `short_description` |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. The test checks that the address is a WooCommerce store and names it.
## What gets imported [#what-gets-imported]
transglot reads the store's own (native) fields for each product and category. Categories have no short description, so that field is skipped for them.
## How translations are written back [#how-translations-are-written-back]
WooCommerce has no built-in place for per-language product text, so transglot writes every reviewed target language into one meta key it owns on each product, `_transglot_translations`, shaped as language, then field, then value. It writes only that key, so the name, description and short description you wrote are never changed.
Your theme or headless front end reads the bundle from product meta (for example with `get_post_meta()`). The leading underscore keeps the key out of the product's Custom Fields panel.
Product categories have no meta channel in the WooCommerce REST API, so category translations are skipped at publish time with `WooCommerce categories require a term-meta capable endpoint; the translation was skipped.` This is expected and does not stop the rest of the publish.
## Troubleshooting [#troubleshooting]
- `That URL did not return a WooCommerce store (the wc/v3 system status was missing).` The address is not a WooCommerce store, or its REST API is blocked.
- `... the credentials were rejected.` The key or secret is wrong, or the key is read-only.
- `... the endpoint was not found, check the store URL.` Check **WooCommerce store URL** is the store root.
- `... the store is rate-limited, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect WordPress](https://transglot.ai/docs/integrations/content-platforms/wordpress)
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect WordPress (https://transglot.ai/docs/integrations/content-platforms/wordpress/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | Title, excerpt and content of posts and pages; name and description of categories and tags |
| Written back | One translation bundle per post, page or term, stored in post or term meta. The original fields are never written |
| Sync | The transglot plugin notifies transglot when content changes, or **Sync from CMS** |
| Plan | Starter and above |
## Two ways to connect [#two-ways-to-connect]
- **With the transglot plugin for WordPress (recommended).** You paste one pairing code into the plugin. The plugin creates its own credential, registers the meta key translations are written to, renders the translations on per-language URLs, and tells transglot when content changes.
- **With an application password only.** You enter the site URL and an application password on the CMS page. This imports and publishes over the WordPress REST API, but nothing on the site registers the meta key or renders the translations unless you build that yourself.
## Connect with the plugin [#connect-with-the-plugin]
The plugin requires WordPress 6.6 or later and PHP 7.4 or later. It cannot run alongside another multilingual plugin (WPML, Polylang or TranslatePress), and on multisite it is activated per site.
### Install the plugin [#install-the-plugin]
In wp-admin, install and activate the transglot plugin (it appears in wp-admin as **Transglot**).
### Generate a pairing code [#generate-a-pairing-code]
In transglot, open your project's settings, choose **CMS**, and pick **WordPress** in the **CMS** list. In **The Transglot plugin for WordPress** panel, select **Generate pairing code**.
The code is shown once. It pairs one site and stops working after 15 minutes. **Generate another code** replaces it; **Revoke outstanding code** cancels a code nobody has used yet.
### Paste the code in WordPress [#paste-the-code-in-wordpress]
In wp-admin, open **Transglot**, then **Setup**. Paste the code into **Your code** and select **Connect this site**. WordPress asks you to approve the application password the plugin creates for the connection.
### Finish the setup wizard [#finish-the-setup-wizard]
The wizard continues with your languages, the URL format for each language and the language switcher. Its last screen checks the round trip: it writes a test bundle through the same REST route transglot uses and reads it back.
A new pairing publishes to every target language of the project that your access covers; you can change the list under **Publish these locales** afterwards.
Pairing writes your project's source language, so the person generating the code needs access to the source language. Otherwise the panel says the code must be generated by an admin whose access covers it.
## Connect with an application password [#connect-with-an-application-password]
| Field | What to enter |
| --- | --- |
| **WordPress site URL** | The site root, for example `https://blog.example.com`. transglot adds `/wp-json/…` itself. It must use https and be publicly reachable |
| **Application password** | `username:application-password`, created under your WordPress user profile's application passwords |
| **Content types** | Any of `posts`, `pages`, `categories`, `tags` |
| **Translatable fields** | Any of `title`, `excerpt`, `content` (posts and pages) and `name`, `description` (categories and tags) |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. A successful test names the site.
Without the plugin, your site must register the `transglot_translations` meta key for the REST API on every post type and taxonomy you publish to. WordPress drops a meta write for a key nothing has registered and still answers success, so the publish reports entries that the site never stored.
## What gets imported [#what-gets-imported]
transglot reads the raw (unrendered) value of each mapped field over the WordPress REST API. Empty fields are skipped.
The REST API only exposes those fields. With the plugin, theme and plugin interface strings, menu labels, widget text, media alt text and SEO titles and descriptions reach your project through the plugin's own push instead.
## How translations are written back [#how-translations-are-written-back]
Each publish writes one bundle per entry into the `transglot_translations` meta key: every reviewed target language, and in it every mapped field. Only that meta key is written, so the post's or term's own title and content are never changed.
With the plugin, the bundle is rendered on per-language URLs with hreflang and per-language sitemaps. Without it, your theme or headless front end reads the bundle from post meta.
After a publish, the panel shows a receipt such as **Last publish: 12 entries, 30 values**, with the number that could not be written. The plugin compares it against what the site actually stored, and its **Health** screen reports any difference.
## Sync on content change [#sync-on-content-change]
With the plugin, saving, unpublishing, trashing or deleting a post, page or term sends a signed, batched notification. transglot then reads only the changed entries back. Nothing else is needed.
If the site's notifications start failing after you select **Rotate signing secret**, pair the site again so the plugin receives the new secret. The old secret stops working straight away.
## Troubleshooting [#troubleshooting]
- `That URL does not expose the WordPress REST API (wp/v2 namespace missing).` The REST API is disabled or blocked at that address. Check the URL and any security plugin.
- `The posts listing reported ... entries but no usable X-WP-TotalPages header.` A cache or security plugin is stripping the `X-WP-*` response headers. Syncing would import only the first page, so transglot stops instead. Let those headers through for `/wp-json/` requests.
- `... the credentials were rejected.` The application password is wrong or revoked, or the user cannot edit the content.
- The publish receipt says some entries could not be written, and that some of those are entries this CMS cannot accept: those are skipped on purpose and are not a fault.
- A pairing code is refused in the plugin: it has expired, been used, or been revoked. Generate a new one.
- `This project is already connected to ...` The project already has a connection to another platform. Each project holds one connection, so disconnect the other platform before pairing.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect Zendesk Guide (https://transglot.ai/docs/integrations/content-platforms/zendesk-guide/)
| | |
| --- | --- |
| Direction | Two-way |
| Imported | Article title and body; section and category name and description |
| Written back | A translation per target locale on each article, section or category. The source translation is never written |
| Sync | **Sync from CMS** |
| Per sync | Up to 1,500 entries per content type |
| Plan | Starter and above |
## Before you start [#before-you-start]
- Add each target language to your help center in Guide's language settings. Zendesk rejects a translation for a locale that is not enabled there, and transglot reports that error rather than hiding it.
- Create an API token in Zendesk's API settings (Zendesk API, token access). You connect with an agent's email address and that token.
## Connect [#connect]
Open your project's settings, choose **CMS**, and pick **Zendesk Guide** in the **CMS** list.
| Field | What to enter |
| --- | --- |
| **Zendesk subdomain** | Only the subdomain, for example `acme` for `acme.zendesk.com` |
| **Email + API token** | `email/token:api-token`, for example `agent@example.com/token:xxxxxxxx` |
| **Content types** | Any of `articles`, `sections`, `categories` |
| **Translatable fields** | `title` and `body` for articles; `name` and `description` for sections and categories |
| **Publish these locales** | The target languages to write back |
Select **Connect**, then **Test connection**. The test reads the help center's locales, which also proves Guide is enabled, and names the `….zendesk.com` address it reached.
## What gets imported [#what-gets-imported]
transglot reads the source text of each article (title and body), and of each section and category (name and description).
## How translations are written back [#how-translations-are-written-back]
Zendesk keeps each language as its own translation of the article (or section, or category). For each target locale, transglot updates the translation if it exists and creates it if it does not. An update changes only the fields being published, so a translation keeps its body when only the title changed.
Section and category names and descriptions are published as the translation's title and body. Language codes are converted to Zendesk's form, so `pt_BR` in transglot is published as `pt-br`.
A new translation needs both a title and a body. For articles, keep both `title` and `body` in **Translatable fields**.
## Troubleshooting [#troubleshooting]
- `... the translation was rejected, title and body are required.` The first publish of a locale needs both fields reviewed. Review the missing one, then publish again.
- `... the credentials were rejected.` Check the credential is in the form `email/token:api-token` and that token access is enabled.
- `... the article or help center was not found.` Check **Zendesk subdomain**, and that the article still exists.
- `... the account is rate-limited, retry.` Wait and run the sync or publish again.
## Related [#related]
- [Connect a content platform](https://transglot.ai/docs/integrations/content-platforms)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
# Connect GitLab, Bitbucket or Azure Repos (https://transglot.ai/docs/integrations/git/connect-another-git-host/)
transglot connects to GitLab, Bitbucket and Azure Repos with an access token you create on the repository side, plus a webhook that tells transglot when you push. On a push to the default branch, transglot extracts the changed source strings into your project, translates them, and opens one merge request (GitLab) or pull request (Bitbucket, Azure Repos) from its own `transglot/sync` branch. It never commits to your branches.
| Host | Supported | Not supported |
| --- | --- | --- |
| GitLab | GitLab.com | Self-managed GitLab |
| Bitbucket | Bitbucket Cloud | Bitbucket Server and Data Center |
| Azure Repos | Azure DevOps Services (`dev.azure.com`) | Azure DevOps Server |
For GitHub, use the [GitHub App](https://transglot.ai/docs/integrations/git/connect-github) instead.
## Requirements [#requirements]
- **Plan:** Team or above. On a lower plan the host's card on the **Integrations** page offers **Upgrade plan** instead of **Connect**.
- **Role:** Admin of the transglot workspace, or an Owner of its account.
- **On the host:** permission to create an access token and a webhook (or Service Hook) on the repository.
- **Your files:** one source locale file per repository, at a path that contains the locale code, for example `locales/en.json`.
## Connect a repository [#connect-a-repository]
Open **Integrations** in your workspace and select **Connect** on the **GitLab**, **Bitbucket** or **Azure Repos** card. The page shows the URL your webhook must call: copy it from there.
### Create a project access token [#create-a-project-access-token]
In your GitLab project, create a project access token with the `api` scope. It starts with `glpat-`.
### Add the webhook [#add-the-webhook]
In the GitLab project, go to **Settings**, **Webhooks**. Add the **Webhook URL** shown on the transglot GitLab page, choose a **Secret token** (any long random value) and enable the **Push events** trigger.
### Fill in the connection [#fill-in-the-connection]
Under **Connect a project**, enter:
| Field | What to enter |
| --- | --- |
| **Project** | The transglot project this repository feeds |
| **GitLab project ID (numeric)** | The numeric ID shown on the project's overview page in GitLab |
| **Project path (group/project)** | For example `acme/website` |
| **Default branch** | For example `main` |
| **Project access token** | The token from step 1 |
| **Webhook secret token** | The same value as the webhook's **Secret token** |
| **Source path**, **Format**, **Source locale**, **Target locales (comma-separated, blank = all)** | See [Describe your files](#describe-your-files) |
Select **Connect project**.
### Create an access token [#create-an-access-token]
Create a repository access token or a workspace access token in Bitbucket that can read the repository and open pull requests on it.
### Add the webhook [#add-the-webhook-1]
In the repository, go to **Repository settings**, **Webhooks**. Add the **Webhook URL** shown on the transglot Bitbucket page, set a **Secret** (any long random value) and enable the **Repository push** trigger.
### Fill in the connection [#fill-in-the-connection-1]
Under **Connect a repository**, enter:
| Field | What to enter |
| --- | --- |
| **Project** | The transglot project this repository feeds |
| **Workspace** | Your Bitbucket workspace, for example `acme` |
| **Repo slug** | For example `website` |
| **Repo UUID** | The repository's UUID, for example `{9970a9b6-2d86-...}` |
| **Default branch** | For example `main` |
| **Access token** | The token from step 1 |
| **Webhook secret** | The same value as the webhook's **Secret** |
| **Source path**, **Format**, **Source locale**, **Target locales (comma-separated, blank = all)** | See [Describe your files](#describe-your-files) |
Select **Connect repository**.
### Create a personal access token [#create-a-personal-access-token]
In Azure DevOps, create a personal access token with the **Code (Read & Write)** scope.
### Add the Service Hook [#add-the-service-hook]
In your Azure DevOps project, go to **Project settings**, **Service hooks**. Create a **Web Hooks** subscription on the **Code pushed** trigger and set its URL to the **Delivery URL** shown on the transglot Azure Repos page. Under **HTTP headers / Basic auth**, enter any username and a **password** you choose (any long random value).
### Fill in the connection [#fill-in-the-connection-2]
Under **Connect a repository**, enter:
| Field | What to enter |
| --- | --- |
| **Project** | The transglot project this repository feeds |
| **Azure organization** | For example `contoso` |
| **Azure project** | For example `Website` |
| **Repository ID (GUID)** | The repository's ID |
| **Default branch** | For example `main` |
| **Personal access token** | The token from step 1 |
| **Webhook secret** | The same value as the Service Hook's Basic auth **password** |
| **Source path**, **Format**, **Source locale**, **Target locales (comma-separated, blank = all)** | See [Describe your files](#describe-your-files) |
Select **Connect repository**.
When it saves, transglot confirms with the repository name, and the connection appears under **Connected projects** (GitLab) or **Connected repositories**. Tokens and secrets are stored encrypted and never shown again.
## Describe your files [#describe-your-files]
| Field | What to enter |
| --- | --- |
| **Source path** | Where your locale files live, with `{locale}` in place of the locale code, for example `locales/{locale}.json` |
| **Format** | The file format, for example `json_nested`. See [file formats](https://transglot.ai/docs/developers/formats) |
| **Source locale** | The locale your team writes in, for example `en` |
| **Target locales (comma-separated, blank = all)** | The locales to deliver, for example `fr, de, es`. Leave it blank for every target language in the project |
## What happens on a push [#what-happens-on-a-push]
1. You push to the default branch, changing the source locale file.
2. transglot extracts the new and changed strings into the project, where translation memory, the glossary and quality checks apply to them.
3. If automatic translation is on for the project, the new strings are translated. A translation your team has edited or reviewed is never overwritten by AI.
4. transglot writes one file per target locale, at the same path pattern, to the `transglot/sync` branch and opens a merge or pull request. If one is already open, it is updated instead of a second one being opened.
Each connection has its own webhook secret, and transglot checks it on every delivery: a delivery without the right secret is rejected, so one connection cannot be used to trigger another.
## Check what happened [#check-what-happened]
The host's card on the **Integrations** page shows the last sync attempt, its status (**Completed**, **Nothing to sync**, **Not configured**, **Failed** or **Blocked**) and a sentence saying what happened. "We did not run this sync." means transglot did not start, and the sentence after it says why. "This sync ran and did not finish." means it started and hit a problem. A card whose last run failed carries a **failing** flag.
## Change or disconnect [#change-or-disconnect]
To change a connection's files or locales, fill in the form again for the same repository and save. Leave the token and secret fields blank to keep the stored values.
To stop syncing a repository, select **Disconnect** on its row and confirm. No new merge or pull requests are opened; anything already open stays open. Delete the webhook or Service Hook on the host as well.
## Troubleshooting [#troubleshooting]
Write the path with `{locale}` where the locale code goes, for example `locales/{locale}.json`.
A repository can feed only one transglot workspace. GitLab says "This GitLab project is already linked to another transglot workspace.", Bitbucket and Azure Repos say the same of the repository. Disconnect it in the other workspace first.
Check that the push went to the default branch named on the connection and changed the source locale file. Then check the webhook on the host: its URL must be the one on the transglot page, its secret must match the connection's, and the push trigger must be enabled. GitLab, Bitbucket and Azure DevOps each keep a delivery history for webhooks that shows failed calls.
transglot could not read the source file at the pushed commit. Check the **Source path** and **Source locale**, and that the token can still read the repository.
The **Default branch** on the connection does not exist, or the token can no longer read the repository. Correct the branch name, or create a new token and save it on the connection.
The token can read but not write. Create a token with permission to push branches and open merge or pull requests, and save it on the connection.
For a step-by-step diagnosis, see [Why is my repository sync not running?](https://transglot.ai/docs/integrations/git/why-is-my-repository-sync-not-running).
## Related [#related]
- [Connect GitHub](https://transglot.ai/docs/integrations/git/connect-github)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
- [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings)
# Connect GitHub (https://transglot.ai/docs/integrations/git/connect-github/)
The transglot GitHub App gives you continuous localization with no CI setup. When you push a change to your source locale file on the default branch, transglot extracts the new strings into your project, translates them, and opens a pull request with the translated files. You review and merge it like any other pull request.
The direction is repository to project to pull request. transglot never commits to your branches: it writes only to its own `transglot/sync` branch and proposes the change from there.
## Requirements [#requirements]
- **Plan:** Team or above. On a lower plan the **GitHub App** card offers **Upgrade plan** instead of **Install on GitHub**.
- **Role:** Admin of the transglot workspace, or an Owner of its account.
- **On GitHub:** permission to install a GitHub App on the account or organization that owns the repository.
- **Your files:** one source locale file per repository, at a path that contains the locale code, for example `locales/en.json`.
## What the App can access [#what-the-app-can-access]
On the repositories you grant it, the App asks GitHub for:
| Permission | Why |
| --- | --- |
| Contents: read and write | Read your source locale file, and write the translated files to the `transglot/sync` branch |
| Pull requests: read and write | Open and update the translation pull request |
| Metadata: read | Required by GitHub for every App |
The App authenticates as itself, so there is no token for you to create, paste or rotate.
## Connect a repository [#connect-a-repository]
### Install the App [#install-the-app]
In your workspace, open **Integrations** and select **Install on GitHub** on the **GitHub App** card, then **Install on GitHub** again on the GitHub App page. GitHub asks which account to install on and which repositories to grant. Grant only the repositories you want to localize.
When GitHub sends you back, transglot confirms "GitHub App connected. Map a repository to a project below."
### Choose the repository and project [#choose-the-repository-and-project]
Under **Repositories**, pick the **Repository** and the **Project** it should feed. Each repository feeds one project.
### Describe your files [#describe-your-files]
| Field | What to enter |
| --- | --- |
| **Source path** | Where your locale files live, with `{locale}` in place of the locale code, for example `locales/{locale}.json` |
| **Format** | The file format, for example `json_nested` or `android_xml`. See [file formats](https://transglot.ai/docs/developers/formats) |
| **Source locale** | The locale your team writes in, for example `en` |
| **Target locales (comma-separated, blank = all)** | The locales to deliver, for example `fr, de, es`. Leave it blank to deliver every target language in the project |
| **Minimum state** | Which translations go into the pull request. **Any** includes every translation; **Human edited** or **Reviewed** leaves out translations that have not reached that state |
### Link it [#link-it]
Select **Link repository**. transglot confirms it with the repository name, for example "acme/web linked.", and the repository appears in the list with its project, last run and pull request.
## What happens on a push [#what-happens-on-a-push]
1. You **push** to the repository's default branch, and the push changes the source locale file (the **Source path** with your **Source locale** filled in).
2. transglot **extracts** the strings from that file into the project. New and changed strings become ordinary keys, so translation memory, the glossary and quality checks apply to them like any other content.
3. If the project has automatic translation on, the new strings are **translated**. A string your team has edited or reviewed is never overwritten by AI.
4. When the translation finishes, transglot opens a **pull request** from the `transglot/sync` branch with one file per target locale, written to the same path pattern. If a transglot pull request is already open, it is updated in place instead of a second one being opened.
Pushes to other branches, and pushes that do not touch the source locale file, do nothing. If nothing new needed translating, or automatic translation is off, no pull request is opened.
Translating new strings uses your plan's monthly AI translation allowance, as it does anywhere else in transglot.
## Check what happened [#check-what-happened]
The GitHub App page lists **Recent deliveries**: every push transglot acted on, with its commit, its pull request and a status.
| Status | Meaning |
| --- | --- |
| **Received** | The push matched your source file and extraction started |
| **Translating** | New strings are being translated; the pull request follows |
| **PR opened** | A new pull request was opened on `transglot/sync` |
| **PR updated** | The open pull request was refreshed with the latest translations |
| **Nothing to do** | Nothing new to translate, so no pull request. The detail says why, for example "No new source strings to translate." |
| **Failed** | Something went wrong; the detail gives the reason |
| **Blocked** | The run was held because of the workspace's account status; the detail says why |
## Unlink or uninstall [#unlink-or-uninstall]
To stop syncing one repository, select **Unlink** on its row and confirm. No new pull requests are opened for it; anything already open stays open, and the GitHub App stays installed.
To remove the App entirely, uninstall it from the account or organization's settings on GitHub.
## Troubleshooting [#troubleshooting]
The App was installed without access to any repository. Change the installation's repository access on GitHub, then reload the page.
Write the path with `{locale}` where the locale code goes, for example `src/i18n/{locale}.json`, not `src/i18n/en.json`.
A GitHub installation belongs to one transglot workspace. Unlink it there first, or install the App on a different GitHub account.
The install round trip took longer than 15 minutes. Select **Install on GitHub** again.
The strings were extracted, but the workspace has used its AI translation allowance for this cycle. See [usage-based billing](https://transglot.ai/docs/administration/billing/usage-based-billing).
transglot could not find or read the file at the commit you pushed. Check the **Source path**, **Source locale** and **Format** on the link match the file in the repository.
Someone suspended the App on GitHub. No pull requests open until you resume it in the account or organization's GitHub settings.
The GitHub App cannot be installed from this workspace. Use the [GitHub Action](https://transglot.ai/docs/developers/github-action) instead: it runs the transglot CLI in your workflow with a deploy token.
For a step-by-step diagnosis, see [Why is my repository sync not running?](https://transglot.ai/docs/integrations/git/why-is-my-repository-sync-not-running).
## Related [#related]
- [Connect GitLab, Bitbucket or Azure Repos](https://transglot.ai/docs/integrations/git/connect-another-git-host)
- [The GitHub Action](https://transglot.ai/docs/developers/github-action)
- [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings)
# Why is my repository sync not running? (https://transglot.ai/docs/integrations/git/why-is-my-repository-sync-not-running/)
Start with what transglot recorded. For GitHub, open the GitHub App page from **Integrations** and read **Recent deliveries**. For GitLab, Bitbucket and Azure Repos, read the last sync panel on the host's card on the **Integrations** page. If a run is listed, its status and sentence usually name the cause. If nothing is listed, transglot never acted on the push: work through the checks below in order.
### Was the push to the default branch? [#was-the-push-to-the-default-branch]
Only pushes to the repository's default branch start a sync. A push to a feature branch, or a merge or pull request that is still open, does nothing until it lands on the default branch.
### Did the push change the source locale file? [#did-the-push-change-the-source-locale-file]
transglot reacts only when the push changes the file at your **Source path** for your **Source locale**. With a source path of `locales/{locale}.json` and source locale `en`, that is `locales/en.json`. A push that changes only target locale files, or files elsewhere, does nothing.
### Is the repository linked to a project? [#is-the-repository-linked-to-a-project]
Installing the GitHub App or adding a webhook is only half of it. The repository must also appear in the list on the integration's page, linked to a project. On GitHub, also check that the App was granted access to that repository.
### Is the webhook reaching transglot? (GitLab, Bitbucket, Azure Repos) [#is-the-webhook-reaching-transglot-gitlab-bitbucket-azure-repos]
Open the webhook's delivery history on the host. Each delivery must go to the URL shown on the transglot page, carry the same secret you saved on the connection, and be for a push event. A rejected delivery means the secret does not match: set the same value on both sides.
### Does the account's plan include it? [#does-the-accounts-plan-include-it]
Repository sync needs the Team plan or above. On a lower plan, pushes are not processed, and the GitLab, Bitbucket and Azure Repos cards say so, for example "The GitLab integration is not included on the current plan." See [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements).
### Is automatic translation on? [#is-automatic-translation-on]
Strings are extracted whatever your settings, but they are only translated automatically if the project has automatic translation turned on. Otherwise they wait in the project until someone translates them, and there is nothing new for a pull request. See [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings).
### Is there AI translation allowance left? [#is-there-ai-translation-allowance-left]
If the workspace has used its AI translation allowance for the cycle and usage-based billing is off, extraction still runs but translation is skipped. GitHub records "Monthly word quota exceeded. Translation skipped." See [usage-based billing](https://transglot.ai/docs/administration/billing/usage-based-billing).
### Can the token still write? (GitLab, Bitbucket, Azure Repos) [#can-the-token-still-write-gitlab-bitbucket-azure-repos]
A token that expired or lost permissions fails on the way back, not on the way in: strings can arrive in the project while no merge or pull request ever appears. Create a new token with write access and save it on the connection.
### Is a pull request waiting for you? [#is-a-pull-request-waiting-for-you]
The result is a merge or pull request from transglot's `transglot/sync` branch, never a commit to yours. Until someone merges it, nothing reaches your default branch. Look for an open request from `transglot/sync`.
## Related [#related]
- [Connect GitHub](https://transglot.ai/docs/integrations/git/connect-github)
- [Connect GitLab, Bitbucket or Azure Repos](https://transglot.ai/docs/integrations/git/connect-another-git-host)
- [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings)
# Integrations (https://transglot.ai/docs/integrations/)
Integrations move strings into transglot from where they already live, and move translations back out. Each one is included in your plan once the plan reaches the integration's minimum: there are no add-ons to buy.
## Where to find them [#where-to-find-them]
Open your workspace's **Settings** and choose **Integrations**. Every member can open the page; connecting and changing integrations is for Owners and Admins. The page has two parts:
- **The app grid** lists what you can wire up now, each card with its real state, read from your actual tokens, endpoints and syncs rather than a checkbox: **connected**, **configured** (set up but not used yet), **available** or **coming soon**. Filter it with the search box, the category chips (**Notify**, **Pipeline**, **Context**, **Content**, **Data**) and the switch that toggles between **Showing everything** and **Installed only**.
- **The Marketplace** lists every connector by family (**Content connectors**, **Design tools** and **Developer apps**) with what it does, whether it is **Import only**, and whether your plan includes it.
Content platform and app store connections are set up per project, on the project's **CMS** settings page, rather than on the workspace's integrations page.
## Code repositories [#code-repositories]
A push to your default branch extracts new strings into a project, and transglot answers with a pull or merge request holding the translations. Team plan and above.
## Content, app stores and storage [#content-app-stores-and-storage]
Import the text you publish elsewhere, translate it, and publish reviewed translations back where the platform allows it. Starter plan and above.
## Notifications and automation [#notifications-and-automation]
## Moving to transglot [#moving-to-transglot]
## Developer tools [#developer-tools]
The integrations page also links the tools developers use from code and CI. They are documented in the developer section:
## Plans [#plans]
| Minimum plan | Integrations |
| --- | --- |
| Free | Slack, import from another tool |
| Starter | Content platforms, App Store Connect, Google Play, Amazon S3, Dropbox, Zapier, Make and n8n |
| Team | GitHub, GitLab, Bitbucket, Azure Repos, Jira |
See [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements) for what the page shows when your plan is below a minimum, and [pricing](https://transglot.ai/pricing) for what each plan includes.
# Import from another localization tool (https://transglot.ai/docs/integrations/migrate/import-from-another-tool/)
The importer is a one-time migration. It reads a project out of the tool you are leaving and writes its keys and translations into transglot. It only reads from the other tool and never writes back to it, and nothing stays connected after the run: the imported strings are ordinary transglot keys and translations.
Your existing translations arrive as human-authored translations, so AI never overwrites them. The source language seeds the keys, and any gaps can be filled by AI translation afterwards if the project has automatic translation on.
## Requirements [#requirements]
- **Plan:** any plan. An import that creates a new project needs room for another project on your plan, and new languages count towards your plan's language limit.
- **Role:** Admin of the workspace, or an Owner of its account.
- **A read API token** from the tool you are leaving. A read-only token is enough.
| Tool | Where to find the token |
| --- | --- |
| Lokalise | Profile settings, then API tokens |
| Phrase | Phrase Strings: Profile settings, then API tokens. Generate a token with read access |
| Crowdin | Account settings, then API, then Personal Access Tokens |
| Transifex | User settings, then API token. The token starts with `1/` |
| Weblate | `hosted.weblate.org` only: Your profile, then API access |
| POEditor | Account settings, then API access |
Self-hosted Weblate is not supported by the importer. Export your files from Weblate and [import them as files](https://transglot.ai/docs/guides/projects/import-content) instead.
## Run an import [#run-an-import]
### Source [#source]
Open **Settings** for your workspace and choose **Import**. Under **Where it is now**, pick the tool, paste the token into **API token** and select **Verify the token**. Use **show** to check what you pasted.
The token is used only for this import and is not kept after the run ends.
### What to read [#what-to-read]
Pick the source project to import. For Transifex and Weblate you pick one resource or component at a time, listed as `Project > Resource`, because the same key name can repeat across them.
Under **Import into**, choose **A new project**, or an existing project to merge into. An existing project keeps everything already translated and reviewed in it. Then select **Review this import**.
### Review [#review]
Nothing has been written yet. The review shows four figures before you commit:
| Figure | Meaning |
| --- | --- |
| **comes across** | Keys and translations that will be written, as human-authored |
| **does not** | Content that stays in your old tool |
| **stops the import** | Problems that must be cleared before the import can run |
| **makes it smaller** | Limits that trim the import; it still runs |
Select each figure to see its detail.
### Run [#run]
Select **Start import**. The import runs in the background and you can leave the page. When it finishes, **What this run did** shows the **Keys created**, **Translations written** and **Languages landed**.
To stop a run early, select **Cancel this import**. Cancelling stops the rest of the import; it does not undo what has already landed. An import cannot be undone once it has finished.
Every run is listed under **Import history**, newest first, with what it wrote and what it could not carry.
## What stops an import [#what-stops-an-import]
Your plan's project limit is reached. Choose an existing project under **Import into**, or [change your plan](https://transglot.ai/docs/administration/billing/change-your-plan).
When you import into an existing project, the source project's source language must match it. The review names both languages. Pick a project with a matching source language, or import into a new one.
## What makes an import smaller [#what-makes-an-import-smaller]
- **Languages beyond your plan.** If your plan has no room for every language in the source project, only the ones that fit are created, and the review says how many.
- **Very large projects.** One import run carries a limited number of keys and of keys times languages. The review tells you how much will come across before you start.
- **Untranslated strings** are not imported as translations: an empty translation is skipped.
- **Keys transglot cannot carry as they are.** After the run, **What this run did** lists them: **Keys that did not come across** (names longer than transglot allows) and **Translations that did not come across** (a plural form that does not match the key already in the project), alongside counts such as **Plural keys skipped** and **Keys with a repeated name**.
When you import into an existing project, translations already edited or reviewed there are kept, and the run lists them as **Left alone here**.
## Troubleshooting [#troubleshooting]
Check that it is a token for the tool you picked, that it has not expired, and that it can read the project you want. Picking a different tool clears the token field, so paste it again after switching.
The token is valid, but the account it belongs to holds nothing the importer can read. Check you pasted a token for the right account or workspace in the other tool.
This workspace has not run an import before. It is not an error.
## Related [#related]
- [Import content from files](https://transglot.ai/docs/guides/projects/import-content)
- [Which file formats can I use?](https://transglot.ai/docs/guides/projects/which-file-formats-can-i-use)
- [My import did not bring everything across](https://transglot.ai/docs/resources/troubleshooting/my-import-did-not-bring-everything-across)
# Connect Slack (https://transglot.ai/docs/integrations/notifications/connect-slack/)
transglot posts workspace events into one Slack channel through a Slack incoming webhook: translation batches finishing or failing, quality checks, comments and review activity. Your team sees them where it already talks, without opening transglot.
## Requirements [#requirements]
- **Plan:** any plan, including Free.
- **Role:** Admin of the workspace, or an Owner of its account.
- **In Slack:** permission to add an incoming webhook to the channel you want the messages in.
## Connect Slack [#connect-slack]
### Create an incoming webhook in Slack [#create-an-incoming-webhook-in-slack]
Follow Slack's guide to [incoming webhooks](https://api.slack.com/messaging/webhooks), choose the channel, and copy the webhook URL. It starts with `https://hooks.slack.com/services/`.
### Open the workspace's General settings [#open-the-workspaces-general-settings]
In transglot, open your workspace's **Settings**, then **General**. Find the **Slack notifications** card.
### Paste the URL and save [#paste-the-url-and-save]
Paste the URL into **Slack incoming-webhook URL**, then select **Save & send test**. transglot saves the URL and posts "transglot is connected" to the channel. Select **Save** instead if you do not want a test message.
The card now shows **Connected**, and the **Slack** card on the **Integrations** page reads **connected** with "Incoming webhook configured (stored encrypted)."
## How the URL is stored [#how-the-url-is-stored]
The webhook URL works like a password: anyone holding it can post to your channel. transglot stores it encrypted and never sends it back to the browser. After saving, the field shows "•••• connected, paste a new URL to replace", and the URL cannot be read out of transglot, only replaced.
## Change the channel [#change-the-channel]
Create a new incoming webhook for the other channel in Slack, paste its URL over the old one and select **Save**.
## Disconnect [#disconnect]
Select **Disconnect** on the **Slack notifications** card and confirm. The channel stops receiving this workspace's events immediately. To reconnect later, paste a webhook URL again.
## Troubleshooting [#troubleshooting]
transglot only posts to public HTTPS addresses. The message under the field names the rule the URL broke, for example "The webhook URL must use https." Copy the URL from Slack again, in full.
The test message is sent in the background and usually arrives within seconds. If it never does, the webhook may have been removed or disabled in Slack: create a new one and paste its URL.
**Save** and **Save & send test** stay disabled until the field holds a URL, and the card says "Paste a webhook URL to save". To clear a connected channel, use **Disconnect**.
## Related [#related]
- [Connect Jira](https://transglot.ai/docs/integrations/automation/connect-jira)
- [Notification preferences](https://transglot.ai/docs/administration/account/notification-preferences)
- [Integrations overview](https://transglot.ai/docs/integrations)
# Plan requirements (https://transglot.ai/docs/integrations/plan-requirements/)
Integrations are not sold separately. Each one has a minimum plan, and once your account's plan meets it the integration is included at no extra charge. There is nothing to buy or switch on: an Owner or Admin connects it, and it works.
## Minimum plan for each integration [#minimum-plan-for-each-integration]
| Integration | Minimum plan |
| --- | --- |
| [Slack](https://transglot.ai/docs/integrations/notifications/connect-slack) | Free (every plan) |
| [Import from Lokalise, Phrase or Crowdin](https://transglot.ai/docs/integrations/migrate/import-from-another-tool) | Free (every plan), within your plan's project, language and key limits |
| [Content platforms](https://transglot.ai/docs/integrations/content-platforms) (CMS, e-commerce, help center and marketing tools) | Starter |
| [App Store Connect](https://transglot.ai/docs/integrations/app-stores/app-store-connect) and [Google Play](https://transglot.ai/docs/integrations/app-stores/google-play) | Starter |
| [Amazon S3 and Dropbox](https://transglot.ai/docs/integrations/storage/connect-file-storage) | Starter |
| [Zapier, Make and n8n](https://transglot.ai/docs/integrations/automation/zapier-make-and-n8n) (through the API and webhooks) | Starter |
| [GitHub](https://transglot.ai/docs/integrations/git/connect-github) | Team |
| [GitLab, Bitbucket and Azure Repos](https://transglot.ai/docs/integrations/git/connect-another-git-host) | Team |
| [Jira](https://transglot.ai/docs/integrations/automation/connect-jira) | Team |
Plans above a minimum include it too: an integration that needs Starter is also included on Team, Business and Enterprise. The OSS plan for open-source projects meets the same minimums as Team.
For what each plan costs and includes, see [pricing](https://transglot.ai/pricing).
## How the integrations page shows it [#how-the-integrations-page-shows-it]
Open **Integrations** in your workspace. Two parts of the page tell you where you stand:
- **The app cards** at the top. A card whose minimum plan you have not reached carries the plan's name as a flag (for example **Team**), says "Requires the Team plan." and offers **Upgrade plan** instead of **Connect**.
- **The Marketplace** section below, grouped into **Content connectors**, **Design tools** and **Developer apps**. Each card ends with **Included** when your plan covers it, or **Upgrade to enable** when it does not.
**Upgrade plan** opens the account's **Plan & billing** page. If you are not allowed to change the account's plan, the card names the account Owners who can, instead of showing the button.
## If your plan drops below an integration's minimum [#if-your-plan-drops-below-an-integrations-minimum]
The integration stops doing work: no new syncs, pull requests or Jira issues. Its settings stay saved. Move back to a plan that includes it and the next push or sync runs as usual.
For GitLab, Bitbucket, Azure Repos, Amazon S3 and Dropbox, the card on the **Integrations** page shows the last sync attempt and why it did not run, for example "The GitLab integration is not included on the current plan." A sync can also be held for a reason that is not the plan, such as a hold on the account; the card names whichever reason applies.
## Related [#related]
- [Change your plan](https://transglot.ai/docs/administration/billing/change-your-plan)
- [Your plan and seats](https://transglot.ai/docs/administration/billing/your-plan-and-seats)
- [Integrations overview](https://transglot.ai/docs/integrations)
# Connect Amazon S3 or Dropbox (https://transglot.ai/docs/integrations/storage/connect-file-storage/)
If your locale files live in cloud storage rather than in a code repository, connect the bucket or folder to a project. Each time you select **Sync now**, transglot reads the source locale file, adds its new and changed strings to the project, and writes one file per target locale back into the same place. Your source file is never overwritten.
## Requirements [#requirements]
- **Plan:** Starter or above. On a lower plan the card offers **Upgrade plan** instead of **Connect**.
- **Role:** Admin of the workspace, or an Owner of its account.
- **Your files:** one source locale file whose name or path contains the locale code, for example `locales/en.json`.
Each workspace holds one Amazon S3 connection and one Dropbox connection, and each feeds one project.
## Connect [#connect]
Amazon S3 connects with an access key you create in AWS. There is no sign-in step.
### Create an access key [#create-an-access-key]
In AWS IAM, create a user or role for transglot with an access key. Grant it only `s3:GetObject`, `s3:PutObject` and `s3:ListBucket` on the bucket and prefix that holds your locale files.
### Open the Amazon S3 page [#open-the-amazon-s3-page]
In your workspace, open **Integrations** and select **Connect** on the **Amazon S3** card.
### Fill in Bucket & credentials [#fill-in-bucket--credentials]
| Field | What to enter |
| --- | --- |
| **Project** | The transglot project the files feed |
| **Region** | The bucket's region, for example `eu-west-1` |
| **Bucket** | The bucket name, for example `my-localization-bucket` |
| **Key prefix (blank = whole bucket)** | The folder inside the bucket, for example `app/locales` |
| **Access key ID** | The key ID, starting `AKIA` |
| **Secret access key** | The secret. It is stored encrypted and cannot be read back |
| **Source file name** | The file name under the prefix, with `{locale}` in place of the locale code, for example `{locale}.json` |
| **Format** | The file format. See [file formats](https://transglot.ai/docs/developers/formats) |
| **Source locale** | The locale your team writes in, for example `en` |
| **Target locales (comma-separated, blank = all)** | The locales to write, for example `fr, de, es`. Leave it blank for every target language in the project |
Select **Save configuration**. The confirmation reads "Amazon S3 connected. Use Sync now to run the first sync."
Dropbox connects by signing in to your Dropbox account and granting access.
### Connect your Dropbox account [#connect-your-dropbox-account]
In your workspace, open **Integrations** and select **Connect** on the **Dropbox** card, then **Connect Dropbox**. Sign in to Dropbox and allow access. Back in transglot, the confirmation reads "Dropbox connected."
### Choose the folder and files [#choose-the-folder-and-files]
| Field | What to enter |
| --- | --- |
| **Project** | The transglot project the files feed |
| **Dropbox folder path** | The folder that holds your locale files. Leave it blank for the root of what transglot can see |
| **Source file name** | The file name in that folder, with `{locale}` in place of the locale code, for example `locales/{locale}.json` |
| **Format** | The file format. See [file formats](https://transglot.ai/docs/developers/formats) |
| **Source locale** | The locale your team writes in, for example `en` |
| **Target locales (comma-separated, blank = all)** | The locales to write, for example `fr, de, es`. Leave it blank for every target language in the project |
Select **Save configuration**.
## Sync [#sync]
Select **Sync now** on the connector's page. The sync runs in the background:
1. transglot downloads the source locale file and adds its new and changed strings to the project, where translation memory, the glossary and quality checks apply to them like any other content. If automatic translation is on for the project, the new strings are translated.
2. transglot writes one file per target locale, named from your **Source file name** with each locale filled in, next to the source file. A file whose content has not changed is left alone.
Each sync writes the translations the project holds at that moment. Strings translated after the sync started reach your storage on the next **Sync now**. Syncing is manual: nothing runs on a schedule or when a file changes.
The connector's card on the **Integrations** page shows the last sync, for example "Source pulled in; 3 target files written."
## Disconnect [#disconnect]
Select **Disconnect** on the connector's page and confirm.
- **Amazon S3:** the stored access key and secret are deleted and cannot be recovered. Objects in the bucket are untouched.
- **Dropbox:** the stored access is deleted, so reconnecting means signing in to Dropbox again. Files already synced into transglot stay where they are.
## Troubleshooting [#troubleshooting]
Write the **Source file name** with `{locale}` where the locale code goes, for example `{locale}.json`.
transglot looked for the source file at the **Key prefix** plus the **Source file name** with your **Source locale** filled in, and found nothing. Check all three, and that the region matches the bucket: the address transglot calls is built from the region and the bucket, so a wrong region reads as a missing file.
Check the **Dropbox folder path**, the **Source file name** and the **Source locale** together name a file that exists.
Access was revoked on the Dropbox side. Select **Reconnect** and sign in again.
The **Source locale** on the connection is not a language of the project. Set it to the project's source language, as a locale code such as `en`, and save again.
That connector cannot be connected from this workspace. Use the [CLI](https://transglot.ai/docs/developers/cli) or the [API](https://transglot.ai/docs/developers/api) to push and pull files instead.
## Related [#related]
- [Which file formats can I use?](https://transglot.ai/docs/guides/projects/which-file-formats-can-i-use)
- [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements)
- [Integrations overview](https://transglot.ai/docs/integrations)
# Frequently asked questions (https://transglot.ai/docs/resources/faq/)
## Before you start [#before-you-start]
It keeps your product's text translated. You bring source strings in from a repository, a file or a content platform; Glossa drafts the translations; automatic checks and your reviewers catch what needs a person; and the finished languages go back out through your build, a pull request, the CDN or the content platform. See [How transglot works](https://transglot.ai/docs/how-transglot-works).
Yes. The Free plan includes one workspace, one project and a monthly allowance of AI translation, plus a one-time grant of welcome words. The paid plans are Starter, Team, Business and Enterprise. See [pricing](https://transglot.ai/pricing) for current allowances and prices.
Starter or above. The CLI, the GitHub Action, the API, the CDN, the glossary and the integrations all start on Starter. Quality mode, AI quality review and self-heal start on Team. Single sign-on and SCIM are on Enterprise. See [Plan requirements](https://transglot.ai/docs/integrations/plan-requirements).
Yes. When you connect the CLI from a Free account, the browser offers a free trial of the Starter plan to the account's owners and billing members. Stripe asks for a card and nothing is charged that day. See the [Quickstart](https://transglot.ai/docs/quickstart).
Yes. Projects with an OSI-approved licence and a public repository can apply for the open source plan, which is reviewed by hand. See [Open source plan](https://transglot.ai/docs/administration/billing/open-source-plan).
Glossa, the transglot translation engine. Glossa Flux is the fast default model; Glossa Deep is the higher-quality model you get with Quality mode on Team and above. See [AI translation](https://transglot.ai/docs/concepts/ai-translation).
No. transglot does not train any models on your content. The AI services it uses process your content under commercial terms, for translation only. See the [Trust Center](https://transglot.ai/trust-center).
Exact-match reuse draws on an anonymised, shared pool as well as your own workspace's history. You can turn that off per project with the **Translation memory** setting; the project then neither reads from nor adds to the pool. Similar-match suggestions only ever come from your own workspace. See [Translation memory](https://transglot.ai/docs/concepts/translation-memory).
Twenty, including JSON (flat and nested), YAML, XLIFF 1.2 and 2.0, gettext PO, Android XML, Apple strings, stringsdict and string catalogs, Flutter ARB, Laravel PHP and JSON, Java properties, .NET resx, Markdown, CSV and Excel. See [File formats](https://transglot.ai/docs/developers/formats).
Yes. You can bring your keys and translations across from Lokalise, Phrase, Crowdin, Transifex, Weblate or POEditor with a read-only token. Your existing translations arrive as human-authored. See [Import from another tool](https://transglot.ai/docs/integrations/migrate/import-from-another-tool).
Only to connect your codebase. Uploading files, migrating from another tool and connecting a content platform need no code. See [Do I need a developer to set transglot up?](https://transglot.ai/docs/first-steps/do-i-need-a-developer-to-set-transglot-up)
Yes. Invite them with the Translator or Reviewer role, limit them to the projects and languages they work on, and hand them languages as assignments with a due date. See [Onboard a vendor or freelancer](https://transglot.ai/docs/administration/team/onboard-a-vendor-or-freelancer).
Yes. Turn off **Auto-translate** for the project and nothing is translated until someone starts a run; your team can write every translation in the editor. See [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings).
Yes, with SAML 2.0 and OpenID Connect identity providers, plus SCIM provisioning, on the Enterprise plan. See [Set up single sign-on](https://transglot.ai/docs/administration/security/set-up-single-sign-on).
## Once you are using transglot [#once-you-are-using-transglot]
No. A translation you have edited or reviewed is never overwritten by AI. Glossa only writes into strings that are empty or still a Glossa draft. See [Translation states](https://transglot.ai/docs/concepts/translation-states).
In source words, once for each target language they are translated into. Reused translations and anything your team writes by hand do not count. See [Words and usage](https://transglot.ai/docs/concepts/words-and-usage).
Your plan includes an allowance of AI translation each month. Usage is measured against that allowance, and what happens when you exceed it depends on whether usage-based billing is turned on for your account. With it off, translation stops until the next cycle or until you upgrade; with it on, translation continues up to the spend cap you set. See [Usage-based billing](https://transglot.ai/docs/administration/billing/usage-based-billing).
A person flagged it, a quality check found a problem, or confidence routing sent it to review. See [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
Pull them into your build with the CLI or the GitHub Action, merge the pull request a connected repository opens, or publish them to the CDN and read them at runtime with an SDK. See [The CDN](https://transglot.ai/docs/guides/delivery/the-cdn) and [The runtime SDKs](https://transglot.ai/docs/developers/sdks).
Editing and publishing are separate. Publish the language again, or run the build that pulls translations. See [My translations are not reaching my app](https://transglot.ai/docs/resources/troubleshooting/my-translations-are-not-reaching-my-app).
Yes. Screenshots show where a string appears, and on Starter and above the in-context editor lets reviewers edit translations on your running app. See [The screenshot library](https://transglot.ai/docs/guides/quality/the-screenshot-library) and [In-context editor](https://transglot.ai/docs/developers/in-context).
Yes. A project's **Export** settings page builds one archive of the languages and formats you choose: files for your app in any supported format, or a bilingual sheet for a person to work on. The CLI's `pull` writes the languages named in your `transglot.json` to files.
See [What happens if I cancel?](https://transglot.ai/docs/administration/billing/what-happens-if-i-cancel)
Can't find your question? See [Get help](https://transglot.ai/docs/resources/get-help).
# Get help (https://transglot.ai/docs/resources/get-help/)
## Check the docs first [#check-the-docs-first]
- [Troubleshooting](https://transglot.ai/docs/resources/troubleshooting) covers the problems people run into most, with the fix for each.
- [Frequently asked questions](https://transglot.ai/docs/resources/faq) answers questions about plans, data and setup.
- The [Trust Center](https://transglot.ai/trust-center) answers security and procurement questions: subprocessors, retention, and the documents a reviewer asks for.
## Contact us [#contact-us]
When you are signed in, the chat button in the corner of the console opens a conversation with the team. The team sees the account you are signed in with, so name the workspace and project and leave out your email address.
To write by email instead, use the address that fits. There is no ticket form.
| What you need | Write to |
| --- | --- |
| Support, billing, plans, invoices and product questions | [hello@transglot.ai](mailto:hello@transglot.ai) |
| A security vulnerability | [security@transglot.ai](mailto:security@transglot.ai). See the [vulnerability disclosure policy](https://transglot.ai/legal/vulnerability-disclosure). |
| Access, export or deletion of personal data, or a signed Data Processing Addendum | [privacy@transglot.ai](mailto:privacy@transglot.ai). See the [DPA](https://transglot.ai/legal/dpa). |
When you are not sure which address fits, use hello@transglot.ai and a person moves it to the right place. To see the product run on your own content first, [book a walkthrough](https://transglot.ai/book-a-demo).
Business and Enterprise plans carry a contractual support response target, set out in the [service level agreement](https://transglot.ai/sla).
## What to include [#what-to-include]
A precise report gets a precise answer:
- **Where it happened**: the workspace and project name, or the address of the page in the console.
- **What you did and what you expected**, in a sentence each.
- **The exact message**, copied as text, or a screenshot.
- **For the CLI**: the command you ran, the CLI version (run the same `npx` command with `--version`), the exit code and the output. Remove any token from it first.
- **For the API**: the endpoint, the HTTP status and the response body. Never send a deploy token or CDN key.
- **For a translation**: the key name, the language, and what is wrong with it.
Send from the email address on your transglot login, so we can find your account.
## Product updates [#product-updates]
The [changelog](https://transglot.ai/changelog) lists what changed in the product, and the [API changelog](https://transglot.ai/api-changelog) lists changes to the API.
# Glossary of terms (https://transglot.ai/docs/resources/glossary/)
## Accounts and people [#accounts-and-people]
**Account**: who pays. An account holds the plan, billing, the usage allowance and one or more workspaces, and its plan applies to every workspace in it. See [Workspaces and projects](https://transglot.ai/docs/concepts/workspaces-and-projects).
**Billing member**: a person who manages an account's plan, payment and invoices.
**Owner**: a person who manages an account and holds every permission in every workspace of it, including those no workspace role can hold, such as single sign-on and deleting a workspace.
**Project access**: access to one project instead of a whole workspace, optionally limited to some languages. See [Grant access to one project](https://transglot.ai/docs/administration/team/grant-access-to-one-project).
**Role**: what a member can do in a workspace. The built-in roles are **Admin**, **Developer**, **Reviewer** and **Translator**; a workspace can add custom roles. See [Roles and what each one can do](https://transglot.ai/docs/administration/team/roles-and-what-each-one-can-do).
**Seat**: a member of a workspace. How many seats an account has depends on its plan. See [Your plan and seats](https://transglot.ai/docs/administration/billing/your-plan-and-seats).
**Workspace**: where your team works. A workspace holds members and their roles, projects, integrations and security settings, and belongs to one account.
## Content [#content]
**Key**: the name your code asks for, such as `checkout.title`. A key holds one source string and one translation per target language, plus a description, an optional maximum length and tags. See [Keys and strings](https://transglot.ai/docs/concepts/keys-and-strings).
**Locale**: a language, optionally with a region or script, named with a BCP-47 tag such as `de`, `pt-BR` or `zh-Hans`. See [Languages and locales](https://transglot.ai/docs/concepts/languages-and-locales).
**Placeholder**: a part of a string your code fills in at runtime, such as `{name}`, `{{count}}`, `%s` or `:name`. transglot keeps placeholders intact in every translation.
**Plural forms**: the separate versions of a plural string, one per plural category the language uses, such as `one` and `other` in English. See [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms).
**Project**: one set of keys translated into a set of languages, typically one app, site or codebase. A project has its own languages, settings, glossary, voice and delivery keys.
**Source language**: the language your strings are written in. Each project has exactly one, and everything is translated from it.
**Source string**: the text of a key in the source language. It arrives by push, upload or integration and is read-only in the console.
**Target language**: a language you translate into and ship. A project can have any number.
## Translation [#translation]
**Assignment**: a language handed to a translator or vendor as tracked work, with a due date and a brief. Assignees see it under **My assignments** and submit it for review when done. See [Assign a language to a translator](https://transglot.ai/docs/guides/translating/assign-a-language-to-a-translator).
**Auto-translate**: the project setting that translates new and changed source strings as they arrive. On by default. See [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings).
**Batch**: one translation run. A batch has a progress figure while it runs and can be cancelled; the CLI prints its number as `Batch: #`.
**Draft**: a translation Glossa wrote that nobody has read yet. See [Translation states](https://transglot.ai/docs/concepts/translation-states).
**Glossa**: the transglot translation engine. **Glossa Flux** is the fast default model that drafts; **Glossa Deep** is the quality model. See [AI translation](https://transglot.ai/docs/concepts/ai-translation).
**Needs checking**: a translation someone flagged, or that a quality check found a problem with.
**Quality mode**: the project setting that uses Glossa Deep for every target language. Team and above.
**Stale**: a translation whose source string changed after it was written.
**Translation memory**: past translations transglot reuses. An exact match is reused at no cost; a similar match is offered as a suggestion and never written automatically. See [Translation memory](https://transglot.ai/docs/concepts/translation-memory).
## Quality [#quality]
**Brand voice**: how your product sounds in every language: formality, warmth, rules and examples, set on the project's **Voice** page. See [Brand voice](https://transglot.ai/docs/guides/quality/brand-voice).
**Confidence routing**: the **Route AI translations by confidence** setting. When on, confident AI translations that pass the automatic checks can skip review and the rest are flagged. Off by default, Team and above.
**Glossary**: a project's list of terms with the wording each language must use, or that must not be translated or must never appear. Glossary terms are matched against source text and enforced during translation and during quality checks. See [The glossary](https://transglot.ai/docs/guides/quality/the-glossary).
**MQM**: Multidimensional Quality Metrics, the industry-standard set of categories transglot uses to group quality findings.
**Quality check**: the automatic checks (placeholders, plural forms, length and glossary) on every plan, and the AI review that scores translations on Team and above. See [Quality checks](https://transglot.ai/docs/concepts/quality-checks).
**Quality score**: a 0-100 rating that transglot produces for a translation, alongside a list of specific findings, each with a severity. See [What does the quality score mean?](https://transglot.ai/docs/guides/quality/what-does-the-quality-score-mean)
**Scorecard**: the per-language summary of quality scores, grouped by MQM category, that you can print or export. See [The per-language scorecard](https://transglot.ai/docs/guides/quality/the-per-language-scorecard).
**Self-heal**: retranslating machine translations that fail review, either as a suggestion for a reviewer or written in directly. Team and above. See [Self-heal a translation](https://transglot.ai/docs/guides/quality/self-heal-a-translation).
## Delivery and developers [#delivery-and-developers]
**CDN**: the service that serves your published translations to your app at runtime. See [The CDN](https://transglot.ai/docs/guides/delivery/the-cdn).
**CDN key**: a read-only key your app uses to fetch published translations from the CDN. Safe to use in client-side code. See [Connect your app to the CDN](https://transglot.ai/docs/guides/delivery/connect-your-app-to-the-cdn).
**CLI**: the `transglot` command line tool. `init` sets a repository up; `push` and `pull` move strings and translations. See [The command line tool](https://transglot.ai/docs/developers/cli).
**Deploy token**: a secret, project-scoped token for the CLI, the GitHub Action and the API. Shown once when created. See [Deploy tokens](https://transglot.ai/docs/developers/deploy-tokens).
**In-context editor**: an overlay that lets reviewers edit translations on your own running site. Starter and above. See [The in-context editor](https://transglot.ai/docs/developers/in-context).
**Publish**: turning a language's current translations into a new, immutable version on the CDN. See [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale).
**Publishable key**: a key that is safe to embed in client-side code, used to load the in-context editor in your app. See [Publishable keys](https://transglot.ai/docs/developers/in-context/publishable-keys).
**Releases**: the project screen where you publish languages and see what is live. See [The Releases screen](https://transglot.ai/docs/guides/delivery/the-deploy-screen).
**transglot.json**: the configuration file the CLI reads, naming the files to push and pull. See [Configuration](https://transglot.ai/docs/developers/cli/configuration).
**Webhook**: an HTTP request transglot sends to your endpoint when something happens in a project. See [Webhooks](https://transglot.ai/docs/developers/webhooks).
## Usage and billing [#usage-and-billing]
**Spend cap**: the most an account can be charged for usage beyond its allowance in a cycle. See [Usage-based billing](https://transglot.ai/docs/administration/billing/usage-based-billing).
**Usage-based overage**: the account setting that lets translation continue past the plan's allowance, charged per word up to the spend cap. Off by default.
**Welcome words**: a one-time grant of words for new accounts on the Free plan. See [Create your account](https://transglot.ai/docs/first-steps/create-your-account).
**Words**: the unit of usage: source words, counted once for each target language they are translated into. See [Words and usage](https://transglot.ai/docs/concepts/words-and-usage).
# I can't sign in (https://transglot.ai/docs/resources/troubleshooting/i-cant-sign-in/)
## You are sent to your identity provider instead of being asked for a password [#you-are-sent-to-your-identity-provider-instead-of-being-asked-for-a-password]
Your company requires single sign-on, so an address on its verified domain signs in through the company's identity provider, whether you use a password or a passkey. Google and GitHub sign-in stop working for those addresses. Sign in through the identity provider. If that fails, see [My single sign-on login fails](https://transglot.ai/docs/resources/troubleshooting/my-single-sign-on-login-fails).
## Your password is refused [#your-password-is-refused]
Use **Forgot your password?** on the sign-in form to get a reset link. If the email does not arrive, check your spam folder, and check that you are using the address the account was created with.
If you signed up with **Google** or **GitHub**, use the same button on the sign-in page.
## The authentication code is always wrong [#the-authentication-code-is-always-wrong]
Authentication codes are time-based, so a phone whose clock has drifted produces codes that never match. Set the phone's clock to update automatically and try again.
If you no longer have your authenticator, choose **log in using a recovery code** and enter one of the recovery codes you saved when you turned on two-factor authentication. Each code works once.
If you have lost both your authenticator and your recovery codes, nobody in your workspace can turn two-factor off for you. Email hello@transglot.ai from the address on the account.
## Your passkey is not offered [#your-passkey-is-not-offered]
A passkey is only available where it is stored: the device you created it on, or the password manager that keeps it. On another device, sign in with your password, then [add a passkey](https://transglot.ai/docs/administration/security/add-a-passkey) there.
## You are asked to confirm your email address [#you-are-asked-to-confirm-your-email-address]
The console needs a confirmed email address. Open the link transglot emailed you, or select **Resend verification email**. If the address is wrong, select **Wrong address? Change it**.
## You signed in but see no workspace [#you-signed-in-but-see-no-workspace]
Signing in worked; you are not in a workspace yet. If you are expecting an invitation, open the private link in the invitation email. There is no way to look an invitation up. See [Accept an invitation](https://transglot.ai/docs/first-steps/accept-an-invitation).
## Related [#related]
- [Sign in to transglot](https://transglot.ai/docs/first-steps/sign-in)
- [Turn on two-factor authentication](https://transglot.ai/docs/administration/security/turn-on-two-factor-authentication)
- [Secure your account](https://transglot.ai/docs/administration/security/secure-your-account)
# Troubleshooting (https://transglot.ai/docs/resources/troubleshooting/)
Each page lists the causes in the order you should check them, most common first.
## Signing in [#signing-in]
## Content and files [#content-and-files]
## Integrations and delivery [#integrations-and-delivery]
## Translating [#translating]
- [Why was my translation flagged for review?](https://transglot.ai/docs/guides/translating/why-was-my-translation-flagged-for-review)
- [CLI exit codes](https://transglot.ai/docs/developers/cli/exit-codes) and [API errors](https://transglot.ai/docs/developers/api/errors), for scripts and integrations.
Still stuck? See [Get help](https://transglot.ai/docs/resources/get-help).
# My import did not bring everything across (https://transglot.ai/docs/resources/troubleshooting/my-import-did-not-bring-everything-across/)
These checks apply to a migration from Lokalise, Phrase, Crowdin, Transifex, Weblate or POEditor. See [Import from another tool](https://transglot.ai/docs/integrations/migrate/import-from-another-tool).
## Is it still running? [#is-it-still-running]
An import runs in the background, so you can leave the page. Open **Import** again to see the run's state, and **Import history** for past runs. **What this run did** lists what actually happened: **Keys created**, **Translations written** and **Languages landed**.
## Did the run stop early? [#did-the-run-stop-early]
- **"This run stopped at the import limit, so your source project still holds more than came across."** Run the import again: it adds the rest rather than starting over.
- **"Import cancelled"** or **"Import stopped"**: whatever had already been imported is kept. Running the import again adds the rest.
## Could the token see everything? [#could-the-token-see-everything]
A read token imports exactly what it can see. If it was limited to some projects or some languages in the tool you are leaving, the rest stays behind. Check the token's scope there, and check which projects you picked under **Choose what to read**: projects you did not pick are never touched.
## Was it something an import does not carry? [#was-it-something-an-import-does-not-carry]
Before you start, **Review this import** shows what **comes across**, what **does not** (it stays in your old tool), and what **makes it smaller**. Two things in particular:
- **Plural keys skipped** in the run's ledger counts plural keys the import did not bring across.
- **Keys with a repeated name** counts keys whose name appeared more than once in the tool you are leaving. One project holds each key name once.
Translation memory and glossaries are separate from a migration: they do not come across with your keys. You can import glossary terms from a CSV or TBX file; see [The glossary](https://transglot.ai/docs/guides/quality/the-glossary).
## Are you looking at the right project? [#are-you-looking-at-the-right-project]
If you chose **A new project** under **Import into**, the content is in a newly created project, not in one you already had.
## Do the translations look untranslated? [#do-the-translations-look-untranslated]
Imported translations land as human-authored translations, marked **edited**, not as Glossa drafts. If a string is empty here, it was probably empty in the tool you are leaving too.
## Related [#related]
- [Import from another tool](https://transglot.ai/docs/integrations/migrate/import-from-another-tool)
- [Add a target language](https://transglot.ai/docs/guides/projects/add-a-target-language)
# My repository sync is not running (https://transglot.ai/docs/resources/troubleshooting/my-repository-sync-is-not-running/)
Work through these checks in order. For the full walkthrough, see [Why is my repository sync not running?](https://transglot.ai/docs/integrations/git/why-is-my-repository-sync-not-running)
1. **Was the push to the default branch?** Strings are extracted from pushes to the repository's default branch only. A push to any other branch does nothing.
2. **Did the push change a file the link watches?** Each repository link names a source file path and a format. A change to any other file is not picked up.
3. **Is the repository linked to a project?** Installing the app or adding a connection is half of it: a repository with no project link has nowhere to put strings. The integrations screen lists linked repositories with their project.
4. **Is Auto-translate on?** Strings are extracted either way, but they are only translated automatically while the project's **Auto-translate** setting is on. Otherwise they wait until someone starts a run.
5. **Is a pull request waiting?** transglot never commits to your branches. On GitHub it commits the translated files to its own `transglot/sync` branch and opens, or updates, one pull request for the repository link once the translation run finishes. Nothing reaches your default branch until someone merges it.
6. **Has the connection lost access?** On GitLab, Bitbucket and Azure Repos the connection uses an access token. A token that expired or lost permissions can let extraction look fine while nothing ever comes back.
## Related [#related]
- [Connect GitHub](https://transglot.ai/docs/integrations/git/connect-github)
- [Connect GitLab, Bitbucket or Azure Repos](https://transglot.ai/docs/integrations/git/connect-another-git-host)
- [AI translation settings](https://transglot.ai/docs/guides/translating/ai-translation-settings)
# My single sign-on login fails (https://transglot.ai/docs/resources/troubleshooting/my-single-sign-on-login-fails/)
This page is for the people who set up single sign-on: the Owners of the account that holds the workspace. Single sign-on works with SAML 2.0 and OpenID Connect identity providers and is available on the Enterprise plan. See [Set up single sign-on](https://transglot.ai/docs/administration/security/set-up-single-sign-on).
## First, get back in [#first-get-back-in]
The Owners of the account can always sign in with their password or passkey, even while single sign-on is required, so a broken identity provider cannot lock the workspace out. Sign in that way, open **Single sign-on & SCIM** in the workspace settings, and work through the checks below.
## Run Test connection [#run-test-connection]
Under **3. Test the connection**, select **Test connection**. It runs a real sign-in and, when it fails, shows **Last test failed:** with the reason. That is usually faster than reading your identity provider's logs.
## Check the domain [#check-the-domain]
Single sign-on applies to email addresses on the domain you verified with a TXT record. If the record has been removed from your DNS, publish it again and select **Verify domain**. Requiring single sign-on cannot be turned back on until the domain is verified and a connection test passes.
## Check the certificate or the client secret [#check-the-certificate-or-the-client-secret]
Sign-ins that worked for months and then started failing for everyone at once usually mean a credential changed on the identity provider's side:
- **SAML**: the provider rotated its signing certificate. transglot checks the signature on every sign-in, so paste the new one into **Signing certificate** and save.
- **OpenID Connect**: the client secret was rotated or expired. Paste the new **Client secret** into the connection and save.
## Check the values on both sides [#check-the-values-on-both-sides]
Your identity provider needs the values shown under **Values for your identity provider**. Copy them from that card rather than typing them: a redirect address or audience that differs by one character fails at sign-in, with an error from your provider rather than from transglot. In the other direction, the provider's own values in the connection (the **IdP entity ID** and **SSO URL** for SAML, or the **Issuer URL** and **Client ID** for OpenID Connect) must match what the provider publishes, character for character, including any trailing slash.
## Check the person [#check-the-person]
- **Are they assigned to the app in your identity provider?** A provider can sign someone in and still refuse to send transglot an assertion for an app they are not assigned to.
- **Is their email address the same on both sides?** The email address your provider sends is what matches a person in transglot. If your directory holds one address and transglot another, the sign-in succeeds at the provider and fails here.
## Related [#related]
- [Set up single sign-on](https://transglot.ai/docs/administration/security/set-up-single-sign-on)
- [Set up SCIM provisioning](https://transglot.ai/docs/administration/security/set-up-scim-provisioning)
- [I can't sign in](https://transglot.ai/docs/resources/troubleshooting/i-cant-sign-in)
# My strings are not showing up in the editor (https://transglot.ai/docs/resources/troubleshooting/my-strings-are-not-showing-up-in-the-editor/)
## Is a filter hiding it? [#is-a-filter-hiding-it]
The editor remembers your filters, and a saved view applies several at once. **Untranslated only**, a **State**, **Waiting for approval**, **Needs checking**, **Has glossary issue**, **Has unresolved comment**, **Tags** and **Stale (source changed)** all narrow the list. Open **Filters**, select **Clear filters**, and check again. This is the answer most of the time. See [Filter the editor](https://transglot.ai/docs/guides/translating/filter-the-editor).
## Are you in the right language? [#are-you-in-the-right-language]
The editor shows one target language at a time. Switch to the language you expected.
## Did the strings arrive? [#did-the-strings-arrive]
A key exists only once something created it: a push from the CLI or the API, an upload, an extraction from a connected repository, a sync from a content platform, or **New key** in the editor. Check the project's key count on its overview: if it has not gone up, nothing arrived.
- From the CLI, `push` prints a line for each file it uploaded and a summary of the keys it created. A file that matched no path in `sources` was never sent. See [Configuration](https://transglot.ai/docs/developers/cli/configuration).
- A push returns before translation finishes. The source strings are there straight away; their translations follow when the run completes.
## Did they go to the right project? [#did-they-go-to-the-right-project]
A deploy token belongs to one project. A token from another project pushes successfully, but into that other project. See [Deploy tokens](https://transglot.ai/docs/developers/deploy-tokens).
## Can you see the whole project? [#can-you-see-the-whole-project]
If you were given access to one project, or to some languages only, you see only what you were granted. Ask an admin of the workspace. See [Grant access to one project](https://transglot.ai/docs/administration/team/grant-access-to-one-project).
## Was the key deleted? [#was-the-key-deleted]
A CLI push does not delete keys that have disappeared from your source file, unless it ran as `transglot push --sync-removals`. A key can also be deleted in the editor by a developer, an admin or an account Owner. See [Add or edit a key](https://transglot.ai/docs/guides/projects/add-or-edit-a-key).
## Related [#related]
- [The editor](https://transglot.ai/docs/guides/translating/the-editor)
- [The command line tool](https://transglot.ai/docs/developers/cli)
- [Plural forms](https://transglot.ai/docs/guides/projects/plural-forms)
# My translations are not reaching my app (https://transglot.ai/docs/resources/troubleshooting/my-translations-are-not-reaching-my-app/)
Run through these quick checks. For every cause and its fix, see [Why are my translations not reaching my app?](https://transglot.ai/docs/guides/delivery/why-are-my-translations-not-reaching-my-app)
1. **Is the language published?** Editing a translation does not publish it. Open **Releases** and check that the language has a live version. See [Publish a locale](https://transglot.ai/docs/guides/delivery/publish-a-locale).
2. **Was it published after your change?** A published version never changes. Publish the language again to include edits made since.
3. **Are AI drafts held back?** If the project holds drafts back, a Glossa draft reaches your app only once someone approves or edits it.
4. **Is your app pinned to a version?** A versioned address serves that version forever. Point your app at the latest address, or at the new version.
5. **Is a cache still serving the old version?** Give a new publish about a minute.
6. **Does your app read files from its build?** If your build pulls translations with the CLI or the GitHub Action, nothing changes until you run that build again. Publishing affects only the CDN.
7. **Is it the right kind of key?** Your app reads the CDN with a CDN key. A deploy token is for the CLI and the API, not the CDN. See [Connect your app to the CDN](https://transglot.ai/docs/guides/delivery/connect-your-app-to-the-cdn).
## Related [#related]
- [The Releases screen](https://transglot.ai/docs/guides/delivery/the-deploy-screen)
- [The CDN](https://transglot.ai/docs/guides/delivery/the-cdn)