From lokalise-pack
Configures Lokalise enterprise SSO, role-based access control, and team management with contributor roles and language scoping.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin lokalise-packThis skill is limited to using the following tools:
Manage fine-grained access to Lokalise translation projects using its built-in role hierarchy, language-level scoping, contributor groups, and organization-level SSO enforcement. Lokalise has four core roles — owner, admin, manager-level (via admin_rights), and contributor (translator/reviewer) — each configurable per project and per language.
Configures Lokalise for dev, staging, prod environments via separate projects or branching, with Node.js SDK, env-specific secrets, and translation promotion workflows.
Automates Crowdin localization tasks via Composio toolkit and Rube MCP. Discovers tools dynamically with RUBE_SEARCH_TOOLS and executes via RUBE_MULTI_EXECUTE_TOOL for translation workflows.
Configures Sentry enterprise RBAC, organization hierarchy, team permissions, SSO/SAML2, SCIM provisioning, API tokens, and audit logging for compliance.
Share bugs, ideas, or general feedback.
Manage fine-grained access to Lokalise translation projects using its built-in role hierarchy, language-level scoping, contributor groups, and organization-level SSO enforcement. Lokalise has four core roles — owner, admin, manager-level (via admin_rights), and contributor (translator/reviewer) — each configurable per project and per language.
LOKALISE_API_TOKEN environment variable set (admin-level token)@lokalise/node-api SDK or curl + jq for REST API accessLokalise uses a flat role model per project, controlled by three boolean flags on each contributor:
| Role | is_admin | is_reviewer | Can translate | Can review | Can manage keys | Can manage contributors |
|---|---|---|---|---|---|---|
| Admin | true | true | Yes | Yes | Yes | Yes |
| Manager | false | true | Yes | Yes | Limited (via admin_rights) | No |
| Reviewer | false | true | Yes | Yes | No | No |
| Translator | false | false | Yes | No | No | No |
At the team level, users are either admin or member. Team admins can create projects and manage billing. Team members can only access projects they are explicitly added to.
import { LokaliseApi } from '@lokalise/node-api';
const lok = new LokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN! });
// Add a translator restricted to French and Spanish only
await lok.contributors().create(PROJECT_ID, [{
email: 'translator@agency.com',
fullname: 'Marie Dupont',
is_admin: false,
is_reviewer: false,
languages: [
{ lang_iso: 'fr', is_writable: true },
{ lang_iso: 'es', is_writable: true },
],
}]);
// Add a reviewer who can review all languages but only translate German
await lok.contributors().create(PROJECT_ID, [{
email: 'reviewer@company.com',
fullname: 'Hans Mueller',
is_admin: false,
is_reviewer: true,
languages: [
{ lang_iso: 'de', is_writable: true },
{ lang_iso: 'fr', is_writable: false }, // Can review but not edit
{ lang_iso: 'es', is_writable: false },
],
}]);
set -euo pipefail
TEAM_ID="YOUR_TEAM_ID"
# List all team members with their roles
curl -s -X GET "https://api.lokalise.com/api2/teams/${TEAM_ID}/users" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
| jq '.team_users[] | {user_id: .user_id, email: .email, role: .role}'
# Demote a user from admin to member
curl -s -X PUT "https://api.lokalise.com/api2/teams/${TEAM_ID}/users/USER_ID" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"role": "member"}'
Groups let you assign the same permissions to multiple people at once. When you add a user to a group, they inherit the group's language scope and role across all projects the group is assigned to.
set -euo pipefail
TEAM_ID="YOUR_TEAM_ID"
# Create a group for APAC translators
curl -s -X POST "https://api.lokalise.com/api2/teams/${TEAM_ID}/groups" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "APAC Translators",
"is_reviewer": false,
"is_admin": false,
"admin_rights": [],
"languages": [
{"lang_iso": "ja", "is_writable": true},
{"lang_iso": "ko", "is_writable": true},
{"lang_iso": "zh_CN", "is_writable": true}
]
}'
# Add a member to the group
GROUP_ID=$(curl -s "https://api.lokalise.com/api2/teams/${TEAM_ID}/groups" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
| jq -r '.groups[] | select(.name == "APAC Translators") | .group_id')
curl -s -X PUT "https://api.lokalise.com/api2/teams/${TEAM_ID}/groups/${GROUP_ID}/members/add" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"users": [12345, 67890]}'
# Assign the group to specific projects
curl -s -X PUT "https://api.lokalise.com/api2/teams/${TEAM_ID}/groups/${GROUP_ID}/projects/add" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"projects": ["PROJECT_ID_1", "PROJECT_ID_2"]}'
SSO is configured in the Lokalise dashboard, not via API. Map your IdP groups to Lokalise roles:
Engineering-Localization -> AdminTranslators-EMEA -> Contributor group "EMEA Translators"Product-Managers -> ReviewerACS URL format: https://app.lokalise.com/sso/saml/YOUR_TEAM_ID/callback
import { LokaliseApi } from '@lokalise/node-api';
const lok = new LokaliseApi({ apiKey: process.env.LOKALISE_API_TOKEN! });
async function auditPermissions() {
const projects = await lok.projects().list({ limit: 100 });
const report: Array<{project: string; issue: string; detail: string}> = [];
for (const proj of projects.items) {
const contributors = await lok.contributors().list({
project_id: proj.project_id,
limit: 500,
});
// Flag: too many admins
const admins = contributors.items.filter(c => c.is_admin);
if (admins.length > 3) {
report.push({
project: proj.name,
issue: 'Excessive admins',
detail: `${admins.length} admins: ${admins.map(a => a.email).join(', ')}`,
});
}
// Flag: contributors with no language scope (can see all languages)
const unscopedTranslators = contributors.items.filter(
c => !c.is_admin && (!c.languages || c.languages.length === 0)
);
if (unscopedTranslators.length > 0) {
report.push({
project: proj.name,
issue: 'Unscoped contributors',
detail: `${unscopedTranslators.length} users can access all languages`,
});
}
// Respect rate limit
await new Promise(r => setTimeout(r, 200));
}
console.table(report);
return report;
}
await auditPermissions();
set -euo pipefail
# Get notified when contributors are added or removed
curl -s -X POST "https://api.lokalise.com/api2/projects/${PROJECT_ID}/webhooks" \
-H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.company.com/lokalise-audit",
"events": [
"project.contributor_added",
"project.contributor_deleted",
"project.contributor_added_to_language",
"project.contributor_deleted_from_language"
]
}'
| Issue | Cause | Solution |
|---|---|---|
403 on contributor create | Caller lacks Admin role on the project | Use an admin-level token or get elevated by an Owner |
| Translator sees all languages | No languages array set on contributor | Update contributor with explicit language scope array |
| SSO login loop | Mismatched ACS URL | Verify ACS URL matches https://app.lokalise.com/sso/saml/TEAM_ID/callback exactly |
| Cannot remove Owner | Last owner protection | Transfer ownership to another admin first |
400 on group create | admin_rights contains invalid values | Valid values: activity, statistics, settings, manage_keys, manage_screenshots, manage_languages, manage_contributors |
| Group members don't see project | Group not assigned to the project | Use the groups/projects/add endpoint to link them |
set -euo pipefail
# Quick CSV export of all contributors and their roles
curl -s -H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
"https://api.lokalise.com/api2/projects?limit=100" \
| jq -r '.projects[].project_id' \
| while read -r pid; do
curl -s -H "X-Api-Token: ${LOKALISE_API_TOKEN}" \
"https://api.lokalise.com/api2/projects/${pid}/contributors?limit=500" \
| jq -r --arg pid "$pid" '.contributors[] | [$pid, .email, (.is_admin|tostring), (.is_reviewer|tostring), (.languages|length|tostring)] | @csv'
sleep 0.2
done | sort -t, -k2
For a typical project with 3 languages (en, fr, de):
// Product team: admin access for key management
await lok.contributors().create(PROJECT_ID, [{
email: 'pm@company.com', fullname: 'PM', is_admin: true, is_reviewer: true, languages: [],
}]);
// French translator: only French, translate only
await lok.contributors().create(PROJECT_ID, [{
email: 'fr@agency.com', fullname: 'FR Translator', is_admin: false, is_reviewer: false,
languages: [{ lang_iso: 'fr', is_writable: true }],
}]);
// German reviewer: German write + French read-only for reference
await lok.contributors().create(PROJECT_ID, [{
email: 'de@agency.com', fullname: 'DE Reviewer', is_admin: false, is_reviewer: true,
languages: [
{ lang_iso: 'de', is_writable: true },
{ lang_iso: 'fr', is_writable: false },
],
}]);
lokalise-debug-bundle to verify access scoping works as expected.lokalise-migration-deep-dive.