Skip to main content

LingoTracker API Reference

Overview​

The LingoTracker API provides REST endpoints for managing translation resources, collections, and configuration.

  • Base URL: http://localhost:3030/api
  • Default Port: 3030 (configurable via LINGO_TRACKER_PORT environment variable)
  • Cache revalidation interval: 2000 ms (configurable via LINGO_TRACKER_REVALIDATE_INTERVAL_MS environment variable)
  • Cached collections: 4 at most, least-recently-used evicted (configurable via LINGO_TRACKER_MAX_CACHED_COLLECTIONS environment variable)
  • Content-Type: application/json
  • Response Format: JSON
  • CORS: Enabled with wildcard origin (*) in development
  • Swagger Documentation: Available at http://localhost:3030/api when the server is running

Authentication​

Currently, the API does not require authentication. It is meant to run on your machine when you need it.

Health Check​

Get Health Status​

Returns the health status of the API server.

Endpoint: GET /api/health

Response:

{
"status": "all is good"
}

Status Codes:

  • 200 OK: Server is healthy

Example:

curl http://localhost:3030/api/health

Configuration API​

Get Configuration​

Retrieves the current LingoTracker configuration including global settings and all collections.

Endpoint: GET /api/config

Response:

interface LingoTrackerConfigDto {
exportFolder: string;
importFolder: string;
baseLocale: string;
locales: string[];
collections: Record<string, LingoTrackerCollectionDto>;
}

interface LingoTrackerCollectionDto {
translationsFolder: string;
exportFolder?: string;
importFolder?: string;
baseLocale?: string;
locales?: string[];
}

Example Response:

{
"exportFolder": "./exports",
"importFolder": "./imports",
"baseLocale": "en",
"locales": ["en", "es", "fr", "de"],
"collections": {
"web-app": {
"translationsFolder": "./apps/web/src/assets/i18n",
"baseLocale": "en",
"locales": ["en", "es", "fr"]
},
"mobile-app": {
"translationsFolder": "./apps/mobile/src/i18n",
"baseLocale": "en",
"locales": ["en", "es"]
}
}
}

Status Codes:

  • 200 OK: Configuration retrieved successfully
  • 500 Internal Server Error: Configuration file could not be read

Example:

curl http://localhost:3030/api/config

Collections API​

Create Collection​

Creates a new translation collection with specified configuration.

Endpoint: POST /api/collections

Request Body:

interface CreateCollectionDto {
name: string;
collection: LingoTrackerCollectionDto;
}

interface LingoTrackerCollectionDto {
translationsFolder: string;
exportFolder?: string;
importFolder?: string;
baseLocale?: string;
locales?: string[];
}

Example Request:

{
"name": "admin-portal",
"collection": {
"translationsFolder": "./apps/admin/src/i18n",
"baseLocale": "en",
"locales": ["en", "es", "fr", "de"]
}
}

Response:

{
"message": "Collection 'admin-portal' added successfully"
}

Status Codes:

  • 201 Created: Collection created successfully
  • 400 Bad Request: Invalid request body or collection already exists
  • 500 Internal Server Error: Unexpected error during collection creation

Example:

curl -X POST http://localhost:3030/api/collections \
-H "Content-Type: application/json" \
-d '{
"name": "admin-portal",
"collection": {
"translationsFolder": "./apps/admin/src/i18n",
"baseLocale": "en",
"locales": ["en", "es", "fr"]
}
}'

Error Example (Collection Already Exists):

curl -X POST http://localhost:3030/api/collections \
-H "Content-Type: application/json" \
-d '{
"name": "web-app",
"collection": {
"translationsFolder": "./apps/web/src/i18n"
}
}'

Response:

{
"statusCode": 400,
"message": "Collection 'web-app' already exists"
}

Update Collection​

Updates or renames an existing collection.

Endpoint: PUT /api/collections/:collectionName

Path Parameters:

  • collectionName (string, required): The name of the collection to update (URL-encoded if necessary)

Request Body:

interface UpdateCollectionDto {
/** Optional new name to rename the collection */
name?: string;
/** Updated collection configuration */
collection: LingoTrackerCollectionDto;
}

Response:

{
"message": "Collection 'admin-portal' updated successfully"
}

Status Codes:

  • 200 OK: Collection updated successfully
  • 400 Bad Request: Invalid request body or collection not found
  • 500 Internal Server Error: Unexpected error during update

Example:

curl -X PUT http://localhost:3030/api/collections/admin-portal \
-H "Content-Type: application/json" \
-d '{
"name": "admin-portal-v2",
"collection": {
"translationsFolder": "./apps/admin/src/i18n",
"baseLocale": "en",
"locales": ["en", "es", "fr", "de"]
}
}'

Delete Collection​

Deletes a collection by name.

Endpoint: DELETE /api/collections/:collectionName

Path Parameters:

  • collectionName (string, required): The name of the collection to delete (URL-encoded if necessary)

Response:

{
"message": "Collection \"admin-portal\" deleted successfully"
}

Status Codes:

  • 200 OK: Collection deleted successfully
  • 400 Bad Request: Collection not found or deletion error
  • 500 Internal Server Error: Unexpected error during deletion

Example:

curl -X DELETE http://localhost:3030/api/collections/admin-portal

Example with URL-Encoded Name:

# For a collection named "my collection" (with space)
curl -X DELETE http://localhost:3030/api/collections/my%20collection

Error Example (Collection Not Found):

curl -X DELETE http://localhost:3030/api/collections/non-existent

Response:

{
"statusCode": 400,
"message": "Collection 'non-existent' not found"
}

Locales API​

Manage locales within a specific collection.

Add Locale​

Adds a locale to a collection and backfills all existing resources with the new locale (value set to the base value, status: "new").

Endpoint: POST /api/collections/:collectionName/locales

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)

Request Body:

{
"locale": "de"
}

Response:

{
"message": "Locale \"de\" added to collection \"main\" successfully",
"entriesBackfilled": 42,
"filesUpdated": 8
}

Status Codes:

  • 200 OK: Locale added and resources backfilled
  • 400 Bad Request: Invalid locale format, locale already exists, cannot modify base locale, or collection not found

Example:

curl -X POST http://localhost:3030/api/collections/main/locales \
-H 'Content-Type: application/json' \
-d '{"locale": "de"}'

Remove Locale​

Removes a locale from a collection and purges all translation data for that locale from resource files.

Endpoint: DELETE /api/collections/:collectionName/locales/:locale

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)
  • locale (string, required): The locale to remove (URL-encoded if necessary)

Response:

{
"message": "Locale \"de\" removed from collection \"main\" successfully",
"entriesPurged": 42,
"filesUpdated": 8
}

Status Codes:

  • 200 OK: Locale removed and data purged
  • 400 Bad Request: Invalid locale format, locale not found in collection, cannot modify base locale, or collection not found

Example:

curl -X DELETE http://localhost:3030/api/collections/main/locales/de

Notes:

  • Translation data for the removed locale is permanently deleted from files (recoverable via git)
  • Removing the last non-base locale is allowed
  • Cannot remove the base locale

Resources API​

All resource endpoints are scoped to a specific collection.

Add Resource(s)​

Creates one or more translation resource entries within a collection. Supports both single resource and bulk operations.

Endpoint: POST /api/collections/:collectionName/resources

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)

Request Body (Single Resource):

interface CreateResourceDto {
/** Dot-delimited key, e.g., "apps.common.buttons.ok" */
key: string;
/** Base locale value (the source text) */
baseValue: string;
/** Optional context for translators */
comment?: string;
/** Optional tags (stored as array) */
tags?: string[];
/** Optional target folder to override part of the path */
targetFolder?: string;
/** Base locale (defaults to collection's baseLocale) */
baseLocale?: string;
/** Localized translations with locale, value, and status */
translations?: Array<{
locale: string;
value: string;
status: 'new' | 'translated' | 'stale' | 'verified';
}>;
}

Request Body (Multiple Resources):

type CreateResourcesRequest = CreateResourceDto[];

Example Request (Single Resource with Manual Translations):

{
"key": "apps.common.buttons.ok",
"baseValue": "OK",
"comment": "Standard confirmation button",
"tags": ["ui", "button"],
"translations": [
{
"locale": "es",
"value": "Aceptar",
"status": "translated"
},
{
"locale": "fr",
"value": "D'accord",
"status": "verified"
},
{
"locale": "de",
"value": "OK",
"status": "new"
}
]
}

Example Request (Single Resource with Auto-Generated Translations):

If translations is not provided, the system will automatically create entries for all configured locales with the base value and status "new".

{
"key": "apps.common.buttons.cancel",
"baseValue": "Cancel",
"comment": "Standard cancel button"
}

Example Request (Bulk Creation):

[
{
"key": "apps.common.buttons.save",
"baseValue": "Save",
"comment": "Save button"
},
{
"key": "apps.common.buttons.delete",
"baseValue": "Delete",
"comment": "Delete button",
"tags": ["dangerous"]
},
{
"key": "apps.common.messages.success",
"baseValue": "Operation completed successfully",
"translations": [
{
"locale": "es",
"value": "Operación completada con éxito",
"status": "verified"
}
]
}
]

Response:

interface CreateResourceResponseDto {
/** Number of entries that were created (count of new resources) */
entriesCreated: number;
/** Whether at least one resource entry was newly created */
created: boolean;
/**
* Locales skipped during auto-translation (ICU message format not supported).
* Only present when auto-translation is enabled and some locales were skipped.
*/
skippedLocales?: string[];
}

Example Response (Bulk Creation):

{
"entriesCreated": 3,
"created": true
}

Status Codes:

  • 201 Created: Resources processed successfully (check entriesCreated to see how many were new)
  • 400 Bad Request: Invalid request body, validation error, or empty resource array
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error during resource creation

Translation Status Values:

  • new: Not yet translated (default for auto-generated translations)
  • translated: Has translation but not verified
  • stale: Base value changed, translation is out of sync
  • verified: Reviewed and approved

Key Format:

  • Keys must be dot-delimited (e.g., apps.common.buttons.ok)
  • Keys are decomposed into folder paths: apps.common.buttons.ok → apps/common/buttons/ folder with ok as the entry key
  • The file structure is created automatically based on the key

Examples:

Single Resource with Default Translations:

curl -X POST http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"key": "apps.common.buttons.ok",
"baseValue": "OK",
"comment": "Confirmation button"
}'

Single Resource with Specific Translations:

curl -X POST http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"key": "apps.common.buttons.cancel",
"baseValue": "Cancel",
"comment": "Cancel button",
"tags": ["ui", "button"],
"translations": [
{
"locale": "es",
"value": "Cancelar",
"status": "verified"
},
{
"locale": "fr",
"value": "Annuler",
"status": "translated"
}
]
}'

Bulk Resource Creation:

curl -X POST http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '[
{
"key": "apps.common.buttons.save",
"baseValue": "Save"
},
{
"key": "apps.common.buttons.delete",
"baseValue": "Delete",
"tags": ["dangerous"]
},
{
"key": "apps.common.messages.success",
"baseValue": "Success!"
}
]'

Error Examples:

Collection Not Found:

curl -X POST http://localhost:3030/api/collections/non-existent/resources \
-H "Content-Type: application/json" \
-d '{"key": "test.key", "baseValue": "Test"}'

Response:

{
"statusCode": 404,
"message": "Collection \"non-existent\" not found"
}

Invalid Key Format:

curl -X POST http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"key": "invalid key with spaces",
"baseValue": "Test"
}'

Response:

{
"statusCode": 400,
"message": "Validation error for resource: Invalid key format"
}

Empty Request:

curl -X POST http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '[]'

Response:

{
"statusCode": 400,
"message": "At least one resource is required"
}

Delete Resource(s)​

Deletes one or more translation resource entries from a collection. This is a best-effort operation: if some keys fail to delete, the operation continues and returns details about failures.

Endpoint: DELETE /api/collections/:collectionName/resources

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)

Request Body:

interface DeleteResourceDto {
/** Array of full dot-delimited keys to delete */
keys: string[];
}

Example Request (Single Key):

{
"keys": ["apps.common.buttons.ok"]
}

Example Request (Multiple Keys):

{
"keys": [
"apps.common.buttons.ok",
"apps.common.buttons.cancel",
"apps.common.buttons.save",
"apps.common.messages.success"
]
}

Response:

interface DeleteResourceResponseDto {
/** Number of resource entries successfully deleted */
entriesDeleted: number;
/** Optional array of errors for entries that failed to delete */
errors?: Array<{
/** The key that failed to delete */
key: string;
/** Error message describing why the deletion failed */
error: string;
}>;
}

Example Response (All Successful):

{
"entriesDeleted": 4,
"errors": []
}

Example Response (Partial Success):

{
"entriesDeleted": 2,
"errors": [
{
"key": "apps.common.buttons.nonexistent",
"error": "Resource key not found"
},
{
"key": "apps.invalid",
"error": "Invalid key format"
}
]
}

Status Codes:

  • 200 OK: Deletion attempted (check entriesDeleted and errors for details)
  • 400 Bad Request: Invalid request (missing or empty keys array)
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error during deletion

Best-Effort Behavior:

The delete operation uses a best-effort approach:

  • If some keys are valid and others are invalid, valid keys will be deleted
  • If some keys exist and others don't, existing keys will be deleted
  • The response includes entriesDeleted (count of successful deletions) and errors (array of failures)
  • A 200 OK response doesn't guarantee all deletions succeeded - always check the response body

Examples:

Delete Single Resource:

curl -X DELETE http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"keys": ["apps.common.buttons.ok"]
}'

Delete Multiple Resources:

curl -X DELETE http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"keys": [
"apps.common.buttons.save",
"apps.common.buttons.delete",
"apps.common.buttons.cancel"
]
}'

Error Examples:

Collection Not Found:

curl -X DELETE http://localhost:3030/api/collections/non-existent/resources \
-H "Content-Type: application/json" \
-d '{"keys": ["test.key"]}'

Response:

{
"statusCode": 404,
"message": "Collection \"non-existent\" not found"
}

Empty Keys Array:

curl -X DELETE http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{"keys": []}'

Response:

{
"statusCode": 400,
"message": "Invalid request: keys array is required and must not be empty"
}

Missing Keys Field:

curl -X DELETE http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{}'

Response:

{
"statusCode": 400,
"message": "Invalid request: keys array is required and must not be empty"
}

Partial Failure (Some Keys Don't Exist):

curl -X DELETE http://localhost:3030/api/collections/web-app/resources \
-H "Content-Type: application/json" \
-d '{
"keys": [
"apps.common.buttons.ok",
"apps.common.nonexistent.key",
"apps.common.buttons.cancel"
]
}'

Response:

{
"entriesDeleted": 2,
"errors": [
{
"key": "apps.common.nonexistent.key",
"error": "Resource key not found in resource_entries.json"
}
]
}

Edit Resource​

Modifies an existing translation resource. Supports updating base value, comments, tags, and locale-specific translations.

Endpoint: PATCH /api/collections/:collectionName/resources

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)

Request Body:

interface UpdateResourceDto {
/** The key of the resource to update */
key: string;
/** Optional new base value */
baseValue?: string;
/** Optional new comment */
comment?: string;
/** Optional new tags (replaces existing tags) */
tags?: string[];
/** Optional target folder override */
targetFolder?: string;
/** Optional map of locale updates */
locales?: Record<string, {
value: string;
status: 'new' | 'translated' | 'stale' | 'verified';
}>;
}

Example Request:

{
"key": "apps.common.buttons.save",
"baseValue": "Save Item",
"comment": "Main save button",
"tags": ["ui", "primary"],
"locales": {
"fr-ca": { "value": "Enregistrer l'article" }
}
}

Response:

interface UpdateResourceResponseDto {
/** The resolved key of the updated resource */
resolvedKey: string;
/** Whether any changes were actually made */
updated: boolean;
/** Optional message (e.g., "No changes detected") */
message?: string;
/** The updated resource data (present when updated is true) */
resource?: ResourceSummaryDto;
/**
* Locales skipped during auto-translation (ICU message format not supported).
* Only present when auto-translation is enabled and some locales were skipped.
*/
skippedLocales?: string[];
}

Example Response:

{
"resolvedKey": "apps.common.buttons.save",
"updated": true
}

Status Codes:

  • 200 OK: Resource updated successfully (or no changes detected)
  • 400 Bad Request: Invalid request body or validation error
  • 404 Not Found: Collection or resource not found
  • 500 Internal Server Error: Unexpected error

Notes:

  • Updating baseValue will mark all non-base translations as stale (unless they are also updated in the same request).
  • Updating a locale value will set its status to translated and update its checksums.
  • If no changes are detected (values match existing), updated will be false.

Move Resource(s)​

Moves or renames translation resources within a collection. Supports single resource moves and wildcard pattern moves.

Endpoint: POST /api/collections/:collectionName/resources/move

Path Parameters:

  • collectionName (string, required): The name of the collection (URL-encoded if necessary)

Request Body:

interface MoveResourceDto {
moves: MoveOperationDto[];
}

interface MoveOperationDto {
/** Source key or pattern (e.g., "common.buttons.ok" or "common.buttons.*") */
source: string;
/** Destination key (e.g., "common.actions.ok" or "common.actions") */
destination: string;
/** Whether to overwrite the destination if it exists */
override?: boolean;
/** Optional destination collection name (defaults to source collection) */
toCollection?: string;
}

Example Request:

{
"moves": [
{
"source": "common.buttons.ok",
"destination": "common.actions.ok"
},
{
"source": "common.buttons.cancel",
"destination": "common.actions.cancel",
"override": true
},
{
"source": "common.buttons.save",
"destination": "common.buttons.save",
"toCollection": "admin-portal"
}
]
}

Response:

interface MoveResourceResponseDto {
/** Total number of resources moved */
movedCount: number;
/** List of warnings (e.g., destination exists and override was false) */
warnings: string[];
/** List of errors */
errors: string[];
}

Example Response:

{
"movedCount": 2,
"warnings": [],
"errors": []
}

Status Codes:

  • 201 Created: Move operation completed (check movedCount and errors for details)
  • 400 Bad Request: Invalid request body
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error during move

Notes:

  • Wildcard moves (source: "prefix.*") will move all matching resources to the destination prefix.
  • The operation is atomic per move entry but not transactional across all moves.

Get Resource Tree​

Returns the resource tree for a collection, optionally scoped to a sub-path. The tree is built from an in-memory cache — the first request triggers background indexing and returns a 202 Accepted with a status response until the cache is ready.

The cache is revalidated on read. Before the tree is served, the API takes a stat-only fingerprint of the collection's translations folder and compares it with the one recorded at index time; if they differ, the cache is dropped and re-indexed, so the request answers 202 Accepted and the next one returns fresh data. This is what makes changes written outside the running app — a lingo-tracker CLI command, a git checkout, a hand edit — visible on a browser refresh instead of only after a restart. The scan is throttled to once every 2000 ms, configurable via the LINGO_TRACKER_REVALIDATE_INTERVAL_MS environment variable.

Endpoint: GET /api/collections/:collectionName/resources/tree

Path Parameters:

  • collectionName (string, required): The name of the collection

Query Parameters:

  • path (string, optional): Dot-delimited folder path to scope the tree (e.g., apps.common)
  • includeNested (boolean, optional): When true and path is set, returns all nested resources flattened into the resources array

Response (cache ready — 200 OK):

interface ResourceTreeDto {
/** Current folder path (dot-delimited, empty for root) */
path: string;
/** Resources in this folder */
resources: ResourceSummaryDto[];
/** Child folders */
children: FolderNodeDto[];
}

interface ResourceSummaryDto {
key: string;
translations: Record<string, string>;
status: Record<string, 'new' | 'translated' | 'stale' | 'verified' | undefined>;
comment?: string;
tags?: string[];
}

interface FolderNodeDto {
name: string;
fullPath: string;
loaded: boolean;
tree?: ResourceTreeDto;
}

Response (cache not ready — 202 Accepted):

interface TreeStatusResponseDto {
status: 'not-ready' | 'indexing';
message: string;
}

Status Codes:

  • 200 OK: Tree returned successfully
  • 202 Accepted: Cache is indexing — retry shortly
  • 404 Not Found: Collection or path not found
  • 500 Internal Server Error: Unexpected error

Example:

# Full tree
curl http://localhost:3030/api/collections/web-app/resources/tree

# Scoped subtree
curl "http://localhost:3030/api/collections/web-app/resources/tree?path=apps.common"

# Scoped subtree with all nested resources
curl "http://localhost:3030/api/collections/web-app/resources/tree?path=apps.common&includeNested=true"

Get Cache Status​

Returns the current indexing status of a collection's resource cache.

Endpoint: GET /api/collections/:collectionName/resources/cache/status

Response:

interface CacheStatusDto {
status: 'not-started' | 'indexing' | 'ready' | 'error';
collectionName?: string;
/** ISO date string — present when status is 'ready' */
indexedAt?: string;
/** Error message — present when status is 'error' */
error?: string;
/** Collection statistics — present when status is 'ready' */
stats?: {
totalKeys: number;
localeCount: number;
};
}

Example Response:

{
"status": "ready",
"collectionName": "web-app",
"indexedAt": "2026-04-09T12:00:00.000Z",
"stats": {
"totalKeys": 342,
"localeCount": 4
}
}

Status Codes:

  • 200 OK: Status retrieved
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl http://localhost:3030/api/collections/web-app/resources/cache/status

Search Translations​

Searches resources by key or translation value within a collection. Uses the in-memory cache when available, otherwise falls back to disk.

Endpoint: GET /api/collections/:collectionName/resources/search

Query Parameters:

  • query (string, required): Search string (case-insensitive, matches keys and values)
  • maxResults (number, optional): Maximum results to return. Default: 100, max: 500

Response:

interface SearchResultsDto {
query: string;
results: SearchResultDto[];
totalFound: number;
/** True when results were capped by maxResults */
limited: boolean;
}

interface SearchResultDto extends ResourceSummaryDto {
matchType: 'exact-key' | 'partial-key' | 'exact-value' | 'partial-value';
/** Locales where value match was found */
matchedLocales?: string[];
}

Example Response:

{
"query": "cancel",
"results": [
{
"key": "apps.common.buttons.cancel",
"translations": { "en": "Cancel", "es": "Cancelar", "fr": "Annuler" },
"status": { "en": null, "es": "verified", "fr": "translated" },
"matchType": "partial-key"
}
],
"totalFound": 1,
"limited": false
}

Status Codes:

  • 200 OK: Search completed
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl "http://localhost:3030/api/collections/web-app/resources/search?query=cancel&maxResults=50"

Translate Resource​

Triggers auto-translation for a resource using the collection's configured translation provider. Returns an error if auto-translation is not enabled for the collection.

Endpoint: POST /api/collections/:collectionName/resources/translate

Request Body:

interface TranslateResourceDto {
/** Full dot-delimited key of the resource to translate */
key: string;
}

Response:

interface TranslateResourceResponseDto {
/** The resource with translated values applied */
resource: ResourceSummaryDto;
/** Number of locales successfully translated */
translatedCount: number;
/** Locales skipped because the base value uses ICU message format */
skippedLocales: string[];
}

Status Codes:

  • 201 Created: Translation completed
  • 404 Not Found: Collection or resource not found
  • 422 Unprocessable Entity: Auto-translation not enabled for this collection
  • 502 Bad Gateway: Translation provider error
  • 500 Internal Server Error: Unexpected error

Example:

curl -X POST http://localhost:3030/api/collections/web-app/resources/translate \
-H "Content-Type: application/json" \
-d '{"key": "apps.common.buttons.cancel"}'

Start Bulk Locale Translation Job​

Starts an async job that translates all new and stale resources in a collection for a single target locale. Returns immediately with a jobId that can be polled for progress. Requires auto-translation to be enabled for the collection.

Endpoint: POST /api/collections/:collectionName/resources/translate-locale

Path Parameters:

  • collectionName (string, required): The name of the collection

Request Body:

{
"locale": "fr"
}

Response (202 Accepted):

interface TranslateLocaleJobDto {
jobId: string;
collectionName: string;
targetLocale: string;
status: 'pending' | 'running' | 'completed' | 'failed';
totalResources: number;
translatedCount: number;
failedCount: number;
skippedCount: number;
failures?: Array<{ key: string; error: string }>;
skippedKeys?: string[];
startedAt?: string;
completedAt?: string;
}

Example Response:

{
"jobId": "uuid-here",
"collectionName": "playground",
"targetLocale": "fr",
"status": "pending",
"totalResources": 0,
"translatedCount": 0,
"failedCount": 0,
"skippedCount": 0
}

Status Codes:

  • 202 Accepted: Job started — poll the GET endpoint for progress
  • 400 Bad Request: Invalid or missing locale
  • 422 Unprocessable Entity: Auto-translation not enabled for this collection
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl -X POST http://localhost:3030/api/collections/playground/resources/translate-locale \
-H 'Content-Type: application/json' \
-d '{"locale":"fr"}'

Get Bulk Locale Translation Job Status​

Returns the current status and progress of a bulk locale translation job. Poll this endpoint every 2–5 seconds until status is completed or failed.

Endpoint: GET /api/collections/:collectionName/resources/translate-locale/:jobId

Path Parameters:

  • collectionName (string, required): The name of the collection
  • jobId (string, required): The job ID returned by the POST endpoint

Response:

TranslateLocaleJobDto with current status. See the POST endpoint above for the full type definition.

Status progression: pending → running → completed / failed

Example Response (completed):

{
"jobId": "uuid-here",
"collectionName": "playground",
"targetLocale": "fr",
"status": "completed",
"totalResources": 48,
"translatedCount": 45,
"failedCount": 0,
"skippedCount": 3,
"failures": [],
"skippedKeys": ["forms.upload.acceptedFormats"],
"startedAt": "2026-04-11T12:00:00.000Z",
"completedAt": "2026-04-11T12:00:15.000Z"
}

Status Codes:

  • 200 OK: Job status returned
  • 404 Not Found: Collection or job not found
  • 500 Internal Server Error: Unexpected error

Example:

curl http://localhost:3030/api/collections/playground/resources/translate-locale/uuid-here

Folders API​

All folder endpoints are scoped to a specific collection.

Create Folder​

Creates a new folder node in the resource hierarchy.

Endpoint: POST /api/collections/:collectionName/folders

Request Body:

interface CreateFolderDto {
/** Single-segment folder name (alphanumeric, dashes, underscores) */
folderName: string;
/** Optional dot-delimited parent path (e.g., "apps.common"). Root if omitted. */
parentPath?: string;
}

Response:

interface CreateFolderResponseDto {
/** Filesystem path of the created folder */
folderPath: string;
/** False if the folder already existed */
created: boolean;
/** The new folder node for inserting into the tree */
folder: FolderNodeDto;
}

Status Codes:

  • 201 Created: Folder created (or already existed)
  • 400 Bad Request: Invalid folder name
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl -X POST http://localhost:3030/api/collections/web-app/folders \
-H "Content-Type: application/json" \
-d '{"folderName": "navigation", "parentPath": "apps.common"}'

Delete Folder​

Deletes a folder and all resources within it.

Endpoint: DELETE /api/collections/:collectionName/folders

Request Body:

interface DeleteFolderDto {
/** Dot-delimited folder path to delete (e.g., "apps.common.buttons") */
folderPath: string;
}

Response:

interface DeleteFolderResponseDto {
deleted: boolean;
folderPath: string;
/** Number of resource entries deleted with the folder */
resourcesDeleted: number;
error?: string;
}

Status Codes:

  • 200 OK: Deletion attempted (check deleted and error fields)
  • 400 Bad Request: Invalid folder path
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl -X DELETE http://localhost:3030/api/collections/web-app/folders \
-H "Content-Type: application/json" \
-d '{"folderPath": "apps.common.buttons"}'

Move Folder​

Moves a folder (and all its resources) to a new location. Supports cross-collection moves.

Endpoint: POST /api/collections/:collectionName/folders/move

Request Body:

interface MoveFolderDto {
/** Dot-delimited source folder path (e.g., "apps.common.buttons") */
sourceFolderPath: string;
/** Dot-delimited destination folder path (e.g., "apps.shared") */
destinationFolderPath: string;
/** If true, overwrite existing resources at destination. Default: false */
override?: boolean;
/** Optional destination collection name for cross-collection moves */
toCollection?: string;
/**
* When true, the source folder is nested under the destination as a child.
* Default: true
*/
nestUnderDestination?: boolean;
}

Response:

interface MoveFolderResponseDto {
movedCount: number;
foldersDeleted: number;
warnings: string[];
errors: string[];
}

Status Codes:

  • 201 Created: Move completed (check movedCount and errors for details)
  • 400 Bad Request: Invalid paths or validation error
  • 404 Not Found: Collection not found
  • 500 Internal Server Error: Unexpected error

Example:

curl -X POST http://localhost:3030/api/collections/web-app/folders/move \
-H "Content-Type: application/json" \
-d '{
"sourceFolderPath": "apps.common.buttons",
"destinationFolderPath": "apps.shared",
"nestUnderDestination": true
}'

Error Handling​

All error responses follow a consistent format:

interface ErrorResponse {
statusCode: number;
message: string;
error?: string; // Optional error type
}

Common HTTP Status Codes​

  • 200 OK: Request succeeded
  • 400 Bad Request: Invalid request body, missing required fields, or validation error
  • 404 Not Found: Resource or collection not found
  • 500 Internal Server Error: Unexpected server error

Error Response Examples​

Validation Error (400):

{
"statusCode": 400,
"message": "Validation error for resource: Invalid key format"
}

Not Found (404):

{
"statusCode": 404,
"message": "Collection \"unknown-collection\" not found"
}

Server Error (500):

{
"statusCode": 500,
"message": "Error creating resources"
}

Complete Workflow Examples​

Example 1: Create a Collection and Add Resources​

# Step 1: Create a new collection
curl -X POST http://localhost:3030/api/collections \
-H "Content-Type: application/json" \
-d '{
"name": "my-app",
"collection": {
"translationsFolder": "./src/i18n",
"baseLocale": "en",
"locales": ["en", "es", "fr"]
}
}'

# Response:
# {
# "message": "Collection 'my-app' added successfully"
# }

# Step 2: Add resources to the collection
curl -X POST http://localhost:3030/api/collections/my-app/resources \
-H "Content-Type: application/json" \
-d '[
{
"key": "app.welcome.title",
"baseValue": "Welcome",
"comment": "Main welcome message"
},
{
"key": "app.welcome.subtitle",
"baseValue": "Start your journey here",
"comment": "Welcome subtitle"
}
]'

# Response:
# {
# "entriesCreated": 2,
# "created": true
# }

# Step 3: Verify the configuration
curl http://localhost:3030/api/config

# The response will include "my-app" in the collections

Example 2: Add Translations and Update Status​

# Add a resource with verified translations
curl -X POST http://localhost:3030/api/collections/my-app/resources \
-H "Content-Type: application/json" \
-d '{
"key": "app.buttons.submit",
"baseValue": "Submit",
"comment": "Form submit button",
"tags": ["form", "button"],
"translations": [
{
"locale": "es",
"value": "Enviar",
"status": "verified"
},
{
"locale": "fr",
"value": "Soumettre",
"status": "verified"
}
]
}'

# Response:
# {
# "entriesCreated": 1,
# "created": true
# }

Example 3: Bulk Operations with Cleanup​

# Add multiple resources
curl -X POST http://localhost:3030/api/collections/my-app/resources \
-H "Content-Type: application/json" \
-d '[
{
"key": "temp.test1",
"baseValue": "Test 1"
},
{
"key": "temp.test2",
"baseValue": "Test 2"
},
{
"key": "temp.test3",
"baseValue": "Test 3"
}
]'

# Response:
# {
# "entriesCreated": 3,
# "created": true
# }

# Clean up - delete the temporary resources
curl -X DELETE http://localhost:3030/api/collections/my-app/resources \
-H "Content-Type: application/json" \
-d '{
"keys": ["temp.test1", "temp.test2", "temp.test3"]
}'

# Response:
# {
# "entriesDeleted": 3,
# "errors": []
# }

Example 4: Error Handling in Bulk Operations​

# Attempt to delete a mix of existing and non-existing keys
curl -X DELETE http://localhost:3030/api/collections/my-app/resources \
-H "Content-Type: application/json" \
-d '{
"keys": [
"app.welcome.title",
"app.nonexistent.key",
"app.welcome.subtitle",
"another.nonexistent.key"
]
}'

# Response (partial success):
# {
# "entriesDeleted": 2,
# "errors": [
# {
# "key": "app.nonexistent.key",
# "error": "Resource key not found"
# },
# {
# "key": "another.nonexistent.key",
# "error": "Resource key not found"
# }
# ]
# }

Notes and Best Practices​

Resource Keys​

  • Use dot-delimited format: namespace.category.subcategory.item
  • Keys are case-sensitive
  • Avoid special characters except dots and underscores
  • Keys should be descriptive and hierarchical

Translation Status Lifecycle​

The typical status progression is:

  1. new - Newly created, needs translation
  2. translated - Translation provided, needs review
  3. verified - Translation reviewed and approved
  4. stale - Source text changed, translation needs updating (automatically set by checksum comparison)

Bulk Operations​

  • The resources API supports both single and array payloads for creation
  • Bulk creation processes all resources and returns the total count of created entries
  • Individual resource errors during bulk creation are handled gracefully (partial success)
  • Delete operations always use an array of keys and support partial success

Best-Effort Deletes​

  • Delete operations continue even if some keys fail
  • Always check both entriesDeleted and errors in the response
  • Non-existent keys are reported in errors but don't stop deletion of valid keys
  • This allows for idempotent cleanup scripts

Checksums and Stale Detection​

The system automatically:

  • Computes MD5 checksums for base values and translations
  • Detects when source text changes (marks translations as stale)
  • Maintains metadata in tracker_meta.json files alongside resource_entries.json

File Structure​

Resources are stored as:

translations-folder/
locale/
namespace/
category/
resource_entries.json
tracker_meta.json

Where:

  • Key apps.common.buttons.ok becomes folder path apps/common/buttons/
  • Entry key ok is stored in resource_entries.json
  • Metadata (checksums, status) stored in tracker_meta.json