Import a file containing translations

POST /api/translations/import

Success: 200 OK

Overview

Uploads a CSV file of translated text for one language and one translation type, and writes it to the platform. Despite the name, this is a write operation, not a staging or preview step — every accepted row is saved before the response is returned.

It merges; it never replaces. Each row is created or updated against the combination of row ID, translation type, culture and property. A translation that already exists for that combination is overwritten with the value in your file; one that does not is inserted. Rows you do not mention are left exactly as they were, and nothing is ever deleted. There is no way to remove a translation through this endpoint: a blank cell is skipped, not treated as an instruction to clear the existing value. To empty a translation you must overwrite it with some other value, or use the translation write endpoints directly.

The request is multipart/form-data with two parts, both required:

PartContents
requestA JSON string — not a JSON body — with two properties: Language (the culture code to import into, matched case-insensitively against the platform's culture list) and TranslationType (matched case-insensitively against the configured translation types, for example Site, Product, ActivityGroup, PriceLevel). Both must be present and non-empty.
fileThe CSV. Its Content-Type must be exactly text/csv — it must match exactly, so application/vnd.ms-excel or an omitted type is rejected with 400.

The CSV shape

Export first. The column count is derived from the translation type's own configuration and is validated strictly against your header row, so hand-building a file is the most common way to fail this call. A type has three columns as standard, five if it carries a name, and seven if it carries both a name and web comments. The exported header labels the read-only source columns (DO NOT CHANGE):

Header for a type with a name (5 columns)
ID (DO NOT CHANGE),Name (DO NOT CHANGE),Translated Name,Description (DO NOT CHANGE),Translated Description

Only the first column and the Translated … columns are read on import. The (DO NOT CHANGE) columns are there so a human editing the file can see the source text; their contents are ignored.

One quirk worth knowing: for a translation type that does not carry a name, the value in the Translated Name column is saved against the description property rather than name. If you read those translations back and find your text under description, that is why.

HTML in translated text

Angle brackets break the CSV parsing, so markup is carried in double square brackets. Export writes <b> as [[b]], and import converts [[b]] back to <b> before saving. Write markup that way in files you build yourself, and never "fix" the brackets by hand in an exported file.

Reading the result

The whole file is validated first — language, translation type, minimum line count, header column count. If any of those fail, nothing is written and you get a 400. Once past those checks the import is per row and partial: valid rows are saved and invalid ones are reported, in a single 200. Always inspect erroredRows rather than treating 200 as success.

Endpoint

POST {base_url}/api/translations/import

{base_url} is the environment URL issued to your integration. See Authenticating with Gladstone APIs.

Sample request

cURL
curl -X POST '{base_url}/api/translations/import' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true'
cURL — with both form parts
curl -X POST '{base_url}/api/translations/import' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -F 'request={"Language":"cy-GB","TranslationType":"Site"}' \
  -F 'file=@site-translations.csv;type=text/csv'

Response fields

FieldTypeDescription
totalRowsinteger (int32)The number of data rows found in the file — every non-empty line except the header. This is the denominator, not a success count. It does not always equal rowsImported + the length of erroredRows: a row that carries an ID but leaves every Translated … column blank, and a row that is blank throughout, are both skipped silently — counted here, reported nowhere else, and written nowhere. A gap between the three numbers usually means blank cells, not a fault.
rowsImportedinteger (int32)The number of rows that were written. Note that a single row can write up to three translation records — name, description and web comments — but still counts as one here. When this is 0 nothing is written at all, so an all-blank file is a no-op rather than an error.
erroredRowsarray of stringOne human-readable string per rejected row, prefixed with its 1-based line number in the file so you can point a user at it — for example Line 4: Translations for ID "GYM01" have already been provided in the file. The three messages you can get are Incorrect number of columns (that row has a different column count from the header), ID cannot be blank when translations are provided, and Translations for ID "…" have already been provided in the file (the same ID appears twice, compared case-insensitively — only the first occurrence is imported). Empty when every row was accepted or silently skipped.

Sample response

200 OK
{
  "totalRows": 1,
  "rowsImported": 1,
  "erroredRows": [
    "string"
  ]
}

Errors

Every 400 below is whole-file: nothing is written. Per-row problems do not produce a 400 — they come back as erroredRows inside a 200. The response body is the bare message string, not a JSON error object.

StatusMeaningWhen
400Bad requestNo request has been supplied — the request form part is missing or empty.
400Bad requestUnable to read request object, possibly invalid JSON format — the request part is not parseable JSON.
400Bad requestThe format of the request is incorrect, both Language amd TranslationType must be supplied — the JSON parsed but Language or TranslationType is missing or empty. The typo in amd is present in the live message.
400Bad requestNo file has been supplied — the file form part is missing.
400Bad requestFile has no content — the file was uploaded but is zero bytes.
400Bad requestFile must be in text/csv format — the uploaded part's content type is not exactly text/csv.
400Bad requestLanguage "{language}" is not a valid language — the Language value does not match any culture configured on the tenant.
400Bad requestTranslationType "{type}" is not a valid translation type — the TranslationType value does not match any configured translation type. Take the value from the translation types endpoint rather than typing it.
400Bad requestFile must contain more than one line (the first line is the header) — the file has fewer than two non-empty lines. A header on its own is not enough.
400Bad requestFile must contain {expected} columns but actually contains {actual} — the header row's column count does not match what the translation type requires. Export the file first rather than guessing.
500Internal server errorAn unexpected server-side failure during parsing or saving. The response body is empty. Note that a translated value containing certain special characters — a lone backslash, for instance — can fail the whole call here rather than being reported as a row error.

This page as markdown · All pages · llms.txt