Translation Memory API

Programmatic access to PerfLocale’s fuzzy Translation Memory and auto-Glossary systems. Use it to power custom editors, external review tools, CI pipelines that pre-fill translations from past work, or one-off data migrations.

All endpoints live under /wp-json/perflocale/v1/ and require either the perflocale_translate or perflocale_manage_glossary capability (noted per endpoint). Authenticate with the standard REST cookie + X-WP-Nonce header, or an application password.

Fuzzy lookup - POST /translation-memory/suggest

Return the best exact + fuzzy matches for a snippet of source text. Useful as a “did you already translate something like this?” helper in custom editors.

Capability: perflocale_translate

POST /wp-json/perflocale/v1/translation-memory/suggest
Content-Type: application/json

{
	"text": "Thanks for subscribing to our newsletter!",
	"source_lang": "en",
	"target_lang": "fr",
	"limit": 5
}

// Response
{
	"exact": "Благодарим за абонамента за бюлетина ни!",
	"suggestions": [
		{
			"source": "Thanks for subscribing",
			"target": "Благодарим за абонамента",
			"similarity": 94.5,
			"usage_count": 12
		},
		{
			"source": "Thanks for your subscription",
			"target": "Благодарим за абонамента ви",
			"similarity": 87.3,
			"usage_count": 4
		}
	]
}

How the ranking works: the query filters candidates by source-text length (±50-200%) and by up to three shared significant words (≥4 characters). That narrow set is ranked in PHP with similar_text(), then by usage_count as a tiebreaker. The top limit (default 5, max 20) are returned.

Browse stored entries - GET /translation-memory

Paginated list with optional filters.

Capability: perflocale_translate

GET /wp-json/perflocale/v1/translation-memory?source_lang=en&target_lang=fr&search=newsletter&page=1&per_page=50

// Response
{
	"total": 147,
	"page": 1,
	"per_page": 50,
	"items": [
		{
			"id": 42,
			"source_text": "Subscribe to our newsletter",
			"target_text": "Абонирай се за бюлетина ни",
			"source_language_id": 1,
			"target_language_id": 2,
			"usage_count": 18,
			"updated_at": "2026-04-01 12:34:56"
		}
	]
}

Query parameters: source_lang, target_lang, search, page, per_page (max 200).

Delete entry - DELETE /translation-memory/{id}

Capability: manage_options (admin only)

DELETE /wp-json/perflocale/v1/translation-memory/42

// Response
{ "deleted": 1 }

Scan for glossary candidates - POST /glossary/scan

Run a synchronous scan over published content to surface candidate glossary terms - brand names, product names, repeated proper nouns. Only available when the Translation Glossary addon is enabled (Settings → Addons → Machine Translation).

Capability: perflocale_manage_glossary

POST /wp-json/perflocale/v1/glossary/scan
{ "limit": 1000 }

// Response
{
	"count": 42,
	"candidates": [
		{
			"term": "Acme Studio",
			"occurrences": 38,
			"example_post_id": 1234
		},
		{
			"term": "RocketDrive Pro",
			"occurrences": 17,
			"example_post_id": 2456
		}
	]
}

The scanner:

  • Walks up to limit published posts in translatable post types (default 500, clamped to [10, 5000], filterable via perflocale/glossary/scan_limit)
  • Extracts capitalized multi-word phrases and single capitalized words
  • Filters common English stop-words, short all-caps acronyms, and terms already in the glossary
  • Requires ≥3 occurrences to report a candidate
  • Returns top 100 candidates ranked by occurrences then phrase length
  • Caches results in a transient (1-day TTL) so the review UI can be re-opened without re-scanning

Read cached candidates - GET /glossary/candidates

GET /wp-json/perflocale/v1/glossary/candidates

// Response
{ "candidates": [ /* same shape as /scan */ ] }

Accept a candidate - POST /glossary/candidates/accept

POST /wp-json/perflocale/v1/glossary/candidates/accept
{
	"term": "Acme Studio",
	"target": "Acme Studio",
	"source_lang": "en",
	"target_lang": "fr",
	"case_sensitive": true
}

// Response
{ "id": 31, "term": "Acme Studio" }

Persists the term into the glossary and prunes it from the cached candidate list.

Reject a candidate - POST /glossary/candidates/reject

POST /wp-json/perflocale/v1/glossary/candidates/reject
{ "term": "Not A Brand" }

Removes from the cache without adding to the glossary. Next scan may surface it again if occurrences still trip the threshold.

Automation example

Run a weekly scan and auto-accept terms you’ve vetted via WP-CLI:

# Run a scan
wp eval '$r = wp_remote_post( rest_url( "perflocale/v1/glossary/scan" ), [
	"headers" => [ "X-WP-Nonce" => wp_create_nonce( "wp_rest" ) ],
	"body"    => wp_json_encode( [ "limit" => 2000 ] ),
	"cookies" => $_COOKIE,
] ); var_export( json_decode( $r["body"], true ) );'

# Review + accept programmatically
wp eval '$list = json_decode( wp_remote_get( rest_url( "perflocale/v1/glossary/candidates" ), [
	"cookies" => $_COOKIE
] )["body"], true );
// ...then POST to /candidates/accept for each term you approve.'