Permissions & Roles

PerfLocale provides a comprehensive permissions system with a custom Translator role and granular capabilities that integrate with WordPress's built-in role system.

Custom Role: Translator

Slug: perflocale_translator

The Translator role is designed for team members who only need to translate content. They can edit posts and pages (to translate them) but cannot publish, delete, or manage site settings.

WordPress capabilities included:

CapabilityGranted
readYes
edit_postsYes
edit_others_postsYes
edit_published_postsYes
edit_pagesYes
edit_others_pagesYes
edit_published_pagesYes
upload_filesYes
publish_postsNo
delete_postsNo
manage_optionsNo

Custom Capabilities

PerfLocale adds 7 custom capabilities that control access to specific plugin features.

Capability Matrix

CapabilityTranslatorEditorAdministratorDescription
perflocale_translateYesYesYesCreate and edit translations
perflocale_manage_translationsNoYesYesManage translation settings, assign workflow
perflocale_manage_languagesNoNoYesAdd, edit, delete languages
perflocale_manage_glossaryNoYesYesAdd, edit, delete glossary terms
perflocale_manage_addonsNoNoYesManage plugin addons
perflocale_use_mtYesYesYesUse machine translation (incl. bulk jobs)
perflocale_import_exportNoNoYesImport/export translations (XLIFF, PO/MO, JSON, CSV)

Capability Details

perflocale_translate

The core translation capability. Users with this cap can:

  • Create translation versions of posts, pages, and terms
  • Edit existing translations
  • See the Translations panel in the block/classic editor

perflocale_manage_translations

Management-level access:

  • Assign translations to other users via the workflow system
  • Change workflow statuses (assigned, in review, approved)
  • Manage translation priorities and deadlines

perflocale_manage_languages

Administrative control over languages:

  • Add new languages
  • Edit language settings (name, locale, flag, direction)
  • Delete languages
  • Set the default language
  • Reorder languages

perflocale_manage_glossary

Glossary management:

  • Add glossary terms
  • Edit existing terms
  • Delete terms
  • These terms are enforced during machine translation

perflocale_manage_addons

Addon lifecycle management:

  • View the Addons page
  • (Reserved for future manual addon install/uninstall)
  • Currently addons auto-activate based on compatible plugins

perflocale_use_mt

Machine translation access:

  • Trigger machine translation for individual posts
  • Access the "MT" button in the editor sidebar
  • Start, monitor, and cancel bulk translation jobs (entire post types)
  • Uses configured MT provider (DeepL, Google, Microsoft, LibreTranslate)

perflocale_import_export

Data portability:

  • Export translations to XLIFF 2.0 format
  • Import XLIFF files
  • Export string translations to PO/MO files
  • Migrate from WPML or Polylang

Admin Menu Visibility

Menu ItemRequired CapabilityNotes
Dashboardperflocale_translate
Languagesperflocale_manage_languages
Stringsperflocale_manage_translations
Translationsperflocale_translateFilterable content list with per-language status
Glossaryperflocale_manage_glossaryHidden unless the mt_glossary_enabled setting is on
Assignmentsperflocale_translateHidden unless the workflow_enabled setting is on
Addonsperflocale_manage_addons
Settingsmanage_options (WP core)

REST API Permissions

EndpointRequired Check
GET /languagesPublic by default (no auth required) - gateable via perflocale/api/languages_public filter
POST /languagesperflocale_manage_languages
PUT/DELETE /languages/{slug}perflocale_manage_languages
POST /languages/reorderperflocale_manage_languages
GET /translations/{type}/{id}perflocale_translate + edit_post
POST /translations/{type}/{id}perflocale_translate + edit_post
POST /translations/post/{id}/languageperflocale_translate + edit_post
PUT /translations/{type}/{id}/{lang}perflocale_translate + edit_post
DELETE /translations/{type}/{id}/{lang}perflocale_translate + delete_post
GET /stringsperflocale_translate
POST /strings/scanmanage_options
POST /machine-translateperflocale_use_mt + edit_post
POST /block-translate, POST /block-translate/from-sourceperflocale_use_mt + edit_post
POST /glossary/scan, GET /glossary/candidates, POST /glossary/candidates/accept, POST /glossary/candidates/rejectperflocale_manage_glossary
GET /translation-memory, POST /translation-memory/suggestperflocale_translate
DELETE /translation-memory/{id}manage_options
POST /import/*perflocale_import_export
POST /xliff/*perflocale_import_export
PUT /workflow/{id}/{lang_id}perflocale_translate + edit_post
GET /workflow/usersperflocale_translate
POST /webhooks, GET /webhooks, DELETE /webhooks/{id}manage_options
GET /configPublic, but only registered when the edge_integration_enabled setting is on
GET /jobs, GET /jobs/{id}perflocale_translate (args redacted for non-creator non-supervisor users)
POST /jobs/{id}/cancel, POST /jobs/{id}/retry, DELETE /jobs/{id}perflocale_translate + (creator OR perflocale_manage_translations)

WordPress Abilities API Permissions

When the Abilities API integration is enabled (via the perflocale/abilities/enabled filter on WP 6.9+), each registered ability ships its own permission_callback. AI tools and external consumers must satisfy these checks per call.

AbilityRequired Check
perflocale/list-languagesPublic
perflocale/get-translationsperflocale_translate
perflocale/detect-languageperflocale_translate
perflocale/convert-urlPublic
perflocale/translate-postperflocale_use_mt (per-target edit_post applied inside the callback)
perflocale/create-translationperflocale_translate (per-target edit_post applied inside the callback)

Background Jobs Permissions

The background-processing system runs each operation under the originating user's capability set. Two distinct cap checks happen:

At dispatch time

Each job type declares the capability it needs in its get_required_capability() method. Dispatcher::dispatch() checks current_user_can() against that cap; if the caller lacks it, the dispatch returns ['mode' => 'denied'] without queueing anything.

Job typeRequired capability
string_scanperflocale_translate
glossary_importperflocale_manage_glossary
data_import, data_exportperflocale_import_export
bulk_translateperflocale_manage_translations (worker also re-checks edit_post per source)
wpml_migration, polylang_migration, translatepress_migrationmanage_options (WP core)

At worker execution time

When the async worker fires (possibly minutes after dispatch), it re-checks the cap against the originating user ID stored on the job row — not against the user running the cron / Action Scheduler context (which is typically user 0). If the originating user has been deleted or had their role downgraded between dispatch and execution, the job is force-failed with "Permission revoked or dispatching user no longer has access."

This is defense-in-depth against a user's translator cap being removed while a job they dispatched is still in flight.

When managing other users' jobs

Mutation endpoints (cancel, retry, delete via REST or CLI) check the dispatching user's identity against the current user:

  • If you dispatched the job yourself: you can cancel / retry / delete it.
  • If someone else dispatched it: you need perflocale_manage_translations (the supervisor cap).

The list / get endpoints also redact the args field for jobs you didn't dispatch unless you hold the supervisor cap. Use case: in a team where translators dispatch their own scans, a translator can see other translators' jobs are in flight but can't read their args (which may contain file paths) or interfere with them.

Workflow States

Translations move through a workflow tracked in the wp_perflocale_workflow table:

Unassigned → Assigned → In Progress → In Review → Approved → Published
StateWho can set itDescription
unassignedSystemDefault state for new translations
assignedEditor, AdminTranslation assigned to a translator
in_progressTranslatorTranslator has started working
reviewTranslatorSubmitted for review
approvedEditor, AdminTranslation approved
publishedEditor, AdminTranslation published on the site

Programmatic Usage

Check capabilities in PHP

use PerfLocale\Admin\TranslatorRole;

// Check a specific capability.
if ( TranslatorRole::current_user_can( 'perflocale_translate' ) ) {
	// User can translate.
}

// Or use WordPress core function.
if ( current_user_can( 'perflocale_manage_languages' ) ) {
	// User can manage languages.
}

Grant a capability to a custom role

$role = get_role( 'my_custom_role' );

if ( $role ) {
	$role->add_cap( 'perflocale_translate' );
	$role->add_cap( 'perflocale_use_mt' );
}

Remove all PerfLocale capabilities

On plugin deactivation, call:

PerfLocale\Admin\TranslatorRole::remove_roles();

This removes the Translator role and all custom capabilities from all roles.

Hooks

perflocale/roles/editor_caps

Filters the capabilities granted to the Editor role when the plugin activates. Return an empty array to prevent Editors from receiving any PerfLocale capabilities. Return a subset to restrict them to specific ones. The Administrator role is unaffected by this filter.

// Remove ALL PerfLocale capabilities from the Editor role.
add_filter( 'perflocale/roles/editor_caps', '__return_empty_array' );

// Grant only translation access to Editors (no bulk-translate, no glossary management).
add_filter( 'perflocale/roles/editor_caps', function ( array $caps ): array {
	return [
		'perflocale_translate' => true,
		'perflocale_use_mt'    => true,
	];
} );

Note: Capability grants are written to the database the first time the plugin activates (version-gated). If you add this filter to an existing install, reset the grant by running delete_option('perflocale_caps_version') in a one-time hook, then let WordPress reload - the filter will apply on the next admin_init.

perflocale/roles/cap_roles

Filters which WordPress roles have PerfLocale capabilities removed on plugin deactivation or uninstall. This filter fires in three places - TranslatorRole::remove_roles() (deactivation), and both the full-wipe and preserve-data branches of uninstall.php.

// Do NOT strip caps from the Editor role on deactivation / uninstall.
// Useful if you manage editor access separately and don't want it wiped.
add_filter( 'perflocale/roles/cap_roles', function ( array $roles ): array {
	return array_diff( $roles, [ 'editor' ] ); // keeps ['administrator']
} );

// Extend cleanup to a custom role that was granted caps programmatically.
add_filter( 'perflocale/roles/cap_roles', function ( array $roles ): array {
	$roles[] = 'shop_manager';
	return $roles;
} );

Workflow hooks

// Filter workflow assignment.
add_action( 'perflocale/workflow/assigned', function( $object_id, $type, $lang_id, $user_id ) {
	// Send notification email to the assigned translator.
}, 10, 4 );

// Filter workflow status changes.
add_action( 'perflocale/workflow/status_changed', function( $object_id, $type, $lang_id, $status ) {
	// Log status change or trigger automation.
}, 10, 4 );