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:
| Capability | Granted |
|---|---|
read | Yes |
edit_posts | Yes |
edit_others_posts | Yes |
edit_published_posts | Yes |
edit_pages | Yes |
edit_others_pages | Yes |
edit_published_pages | Yes |
upload_files | Yes |
publish_posts | No |
delete_posts | No |
manage_options | No |
Custom Capabilities
PerfLocale adds 7 custom capabilities that control access to specific plugin features.
Capability Matrix
| Capability | Translator | Editor | Administrator | Description |
|---|---|---|---|---|
perflocale_translate | Yes | Yes | Yes | Create and edit translations |
perflocale_manage_translations | No | Yes | Yes | Manage translation settings, assign workflow |
perflocale_manage_languages | No | No | Yes | Add, edit, delete languages |
perflocale_manage_glossary | No | Yes | Yes | Add, edit, delete glossary terms |
perflocale_manage_addons | No | No | Yes | Manage plugin addons |
perflocale_use_mt | Yes | Yes | Yes | Use machine translation (incl. bulk jobs) |
perflocale_import_export | No | No | Yes | Import/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 Item | Required Capability | Notes |
|---|---|---|
| Dashboard | perflocale_translate | |
| Languages | perflocale_manage_languages | |
| Strings | perflocale_manage_translations | |
| Translations | perflocale_translate | Filterable content list with per-language status |
| Glossary | perflocale_manage_glossary | Hidden unless the mt_glossary_enabled setting is on |
| Assignments | perflocale_translate | Hidden unless the workflow_enabled setting is on |
| Addons | perflocale_manage_addons | |
| Settings | manage_options (WP core) |
REST API Permissions
| Endpoint | Required Check |
|---|---|
GET /languages | Public by default (no auth required) - gateable via perflocale/api/languages_public filter |
POST /languages | perflocale_manage_languages |
PUT/DELETE /languages/{slug} | perflocale_manage_languages |
POST /languages/reorder | perflocale_manage_languages |
GET /translations/{type}/{id} | perflocale_translate + edit_post |
POST /translations/{type}/{id} | perflocale_translate + edit_post |
POST /translations/post/{id}/language | perflocale_translate + edit_post |
PUT /translations/{type}/{id}/{lang} | perflocale_translate + edit_post |
DELETE /translations/{type}/{id}/{lang} | perflocale_translate + delete_post |
GET /strings | perflocale_translate |
POST /strings/scan | manage_options |
POST /machine-translate | perflocale_use_mt + edit_post |
POST /block-translate, POST /block-translate/from-source | perflocale_use_mt + edit_post |
POST /glossary/scan, GET /glossary/candidates, POST /glossary/candidates/accept, POST /glossary/candidates/reject | perflocale_manage_glossary |
GET /translation-memory, POST /translation-memory/suggest | perflocale_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/users | perflocale_translate |
POST /webhooks, GET /webhooks, DELETE /webhooks/{id} | manage_options |
GET /config | Public, 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.
| Ability | Required Check |
|---|---|
perflocale/list-languages | Public |
perflocale/get-translations | perflocale_translate |
perflocale/detect-language | perflocale_translate |
perflocale/convert-url | Public |
perflocale/translate-post | perflocale_use_mt (per-target edit_post applied inside the callback) |
perflocale/create-translation | perflocale_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 type | Required capability |
|---|---|
string_scan | perflocale_translate |
glossary_import | perflocale_manage_glossary |
data_import, data_export | perflocale_import_export |
bulk_translate | perflocale_manage_translations (worker also re-checks edit_post per source) |
wpml_migration, polylang_migration, translatepress_migration | manage_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| State | Who can set it | Description |
|---|---|---|
unassigned | System | Default state for new translations |
assigned | Editor, Admin | Translation assigned to a translator |
in_progress | Translator | Translator has started working |
review | Translator | Submitted for review |
approved | Editor, Admin | Translation approved |
published | Editor, Admin | Translation 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 );