feat: localize settings workspace and wizard
This commit is contained in:
@@ -9,6 +9,12 @@ var workspaceFormState = {
|
||||
invitations: [],
|
||||
currentUserId: null,
|
||||
};
|
||||
function tKey(key) {
|
||||
return typeof t === 'function' ? t(key) : key;
|
||||
}
|
||||
function tfKey(key, vars) {
|
||||
return typeof tf === 'function' ? tf(key, vars) : key;
|
||||
}
|
||||
|
||||
function normalizeSlug(value) {
|
||||
return value
|
||||
@@ -26,9 +32,10 @@ function uiRoleToApi(value) {
|
||||
}
|
||||
|
||||
function roleLabel(value) {
|
||||
if (value === 'operator') {
|
||||
return 'Developer';
|
||||
}
|
||||
if (value === 'operator') return tKey('workspace_setup.role.operator');
|
||||
if (value === 'owner') return tKey('workspace_setup.role.owner');
|
||||
if (value === 'admin') return tKey('workspace_setup.role.admin');
|
||||
if (value === 'viewer') return tKey('workspace_setup.role.viewer');
|
||||
return value.charAt(0).toUpperCase() + value.slice(1);
|
||||
}
|
||||
|
||||
@@ -142,7 +149,9 @@ function renderMembers() {
|
||||
workspaceFormState.memberships.forEach(function (membership) {
|
||||
addMemberRow(container, membership);
|
||||
});
|
||||
label.textContent = workspaceFormState.memberships.length + ' member' + (workspaceFormState.memberships.length === 1 ? '' : 's');
|
||||
label.textContent = workspaceFormState.memberships.length === 1
|
||||
? tKey('workspace_setup.member_count_one')
|
||||
: tfKey('workspace_setup.member_count', { count: workspaceFormState.memberships.length });
|
||||
}
|
||||
|
||||
function renderInvitations() {
|
||||
@@ -165,7 +174,7 @@ function renderInvitations() {
|
||||
pending.style.display = '';
|
||||
pending.innerHTML =
|
||||
'<div style="font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.06em;color:var(--text-muted);margin-bottom:10px;">' +
|
||||
'Pending invites <span id="pending-count" style="background:rgba(139,148,158,0.15);border-radius:10px;padding:1px 7px;font-size:10px;">' + items.length + '</span>' +
|
||||
tKey('workspace_setup.members.pending') + ' <span id="pending-count" style="background:rgba(139,148,158,0.15);border-radius:10px;padding:1px 7px;font-size:10px;">' + items.length + '</span>' +
|
||||
'</div>';
|
||||
|
||||
items.forEach(function (record) {
|
||||
@@ -176,7 +185,7 @@ function renderInvitations() {
|
||||
'<div class="member-avatar" style="background:var(--bg-canvas);border:1.5px dashed var(--border);color:var(--text-muted);font-size:16px;">?</div>' +
|
||||
'<div class="member-info">' +
|
||||
'<div class="member-name">' + invitation.email + '</div>' +
|
||||
'<div class="member-email">Invited ' + invitation.created_at.slice(0, 10) + ' · pending</div>' +
|
||||
'<div class="member-email">' + tfKey('workspace_setup.members.invited_pending', { date: invitation.created_at.slice(0, 10) }) + '</div>' +
|
||||
'</div>' +
|
||||
'<div class="member-last-active">—</div>' +
|
||||
'<span class="member-role-badge" style="background:rgba(139,148,158,0.12);color:var(--text-muted);border:1px solid var(--border);">' + roleLabel(invitation.role) + '</span>' +
|
||||
@@ -213,19 +222,19 @@ function updatePageMode() {
|
||||
|
||||
var submit = document.getElementById('submit-btn');
|
||||
if (workspaceFormState.isCreateMode) {
|
||||
document.title = 'Crank — Create workspace';
|
||||
document.getElementById('page-title').textContent = 'Create a new workspace';
|
||||
document.getElementById('page-subtitle').textContent = 'A workspace is a shared environment for your team\'s MCP operations and agents. Each workspace gets its own MCP endpoint namespace.';
|
||||
document.title = 'Crank — ' + tKey('workspace_setup.create.title');
|
||||
document.getElementById('page-title').textContent = tKey('workspace_setup.create.title');
|
||||
document.getElementById('page-subtitle').textContent = tKey('workspace_setup.create.subtitle');
|
||||
document.getElementById('section-invite').style.display = '';
|
||||
document.getElementById('footer-note').style.display = '';
|
||||
submit.textContent = 'Create workspace';
|
||||
submit.textContent = tKey('workspace_setup.actions.create');
|
||||
submit.disabled = true;
|
||||
submit.style.opacity = '0.5';
|
||||
submit.style.cursor = 'not-allowed';
|
||||
} else {
|
||||
document.getElementById('section-members').style.display = '';
|
||||
document.getElementById('section-danger').style.display = '';
|
||||
submit.textContent = 'Save changes';
|
||||
submit.textContent = tKey('workspace_setup.actions.save');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +298,7 @@ async function submitForm() {
|
||||
var submit = document.getElementById('submit-btn');
|
||||
var originalLabel = submit.textContent;
|
||||
submit.disabled = true;
|
||||
submit.textContent = workspaceFormState.isCreateMode ? 'Creating…' : 'Saving…';
|
||||
submit.textContent = workspaceFormState.isCreateMode ? tKey('workspace_setup.saving_create') : tKey('workspace_setup.saving_update');
|
||||
|
||||
try {
|
||||
var response;
|
||||
@@ -317,7 +326,7 @@ async function submitForm() {
|
||||
id: workspace.id,
|
||||
slug: workspace.slug,
|
||||
name: workspace.display_name,
|
||||
role: 'Owner',
|
||||
role: tKey('workspace_setup.role.owner'),
|
||||
letter: (workspace.display_name || workspace.slug).charAt(0).toUpperCase(),
|
||||
color: payload.settings.color || '#0d9488',
|
||||
status: workspace.status,
|
||||
@@ -336,14 +345,14 @@ async function submitForm() {
|
||||
|
||||
applyWorkspaceRecord({ workspace: workspace });
|
||||
await loadWorkspaceAccessData();
|
||||
submit.textContent = 'Saved ✓';
|
||||
submit.textContent = tKey('workspace_setup.saved');
|
||||
setTimeout(function () {
|
||||
submit.textContent = originalLabel;
|
||||
submit.disabled = false;
|
||||
}, 1400);
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to save workspace', 'Save failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.save_error'), tKey('workspace_setup.save_error_title'));
|
||||
}
|
||||
submit.disabled = false;
|
||||
submit.textContent = originalLabel;
|
||||
@@ -357,11 +366,11 @@ function addInviteRow() {
|
||||
node.innerHTML =
|
||||
'<input class="form-input" type="email" placeholder="colleague@company.com" autocomplete="off" style="flex:1;margin-bottom:0;">' +
|
||||
'<select class="form-input" style="width:130px;margin-bottom:0;">' +
|
||||
'<option value="admin">Admin</option>' +
|
||||
'<option value="developer" selected>Developer</option>' +
|
||||
'<option value="viewer">Viewer</option>' +
|
||||
'<option value="admin">' + tKey('workspace_setup.role.admin') + '</option>' +
|
||||
'<option value="developer" selected>' + tKey('workspace_setup.role.operator') + '</option>' +
|
||||
'<option value="viewer">' + tKey('workspace_setup.role.viewer') + '</option>' +
|
||||
'</select>' +
|
||||
'<button class="invite-row-remove" type="button" onclick="removeInviteRow(this)" title="Remove">' +
|
||||
'<button class="invite-row-remove" type="button" onclick="removeInviteRow(this)" title="' + tKey('workspace_setup.members.remove') + '">' +
|
||||
'<svg width="12" height="12"><use href="' + (window.APP_BASE || '') + 'icons/general/close.svg#icon"/></svg>' +
|
||||
'</button>';
|
||||
rows.appendChild(node);
|
||||
@@ -407,7 +416,7 @@ async function sendInvite() {
|
||||
await loadWorkspaceAccessData();
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to send invite', 'Invite failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.invite_error'), tKey('workspace_setup.invite_error_title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -429,7 +438,7 @@ async function updateRole(select, userId) {
|
||||
} catch (error) {
|
||||
select.value = previous;
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to update role', 'Role update failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.role_error'), tKey('workspace_setup.role_error_title'));
|
||||
}
|
||||
} finally {
|
||||
select.disabled = false;
|
||||
@@ -440,7 +449,7 @@ async function removeMember(userId, name) {
|
||||
if (!workspaceFormState.workspaceId || !window.CrankApi) {
|
||||
return;
|
||||
}
|
||||
if (!confirm('Remove ' + name + ' from this workspace?')) {
|
||||
if (!confirm(tfKey('workspace_setup.remove_confirm', { name: name }))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -449,7 +458,7 @@ async function removeMember(userId, name) {
|
||||
await loadWorkspaceAccessData();
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to remove member', 'Member removal failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.remove_error'), tKey('workspace_setup.remove_error_title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -464,7 +473,7 @@ async function revokeInviteById(invitationId) {
|
||||
await loadWorkspaceAccessData();
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to revoke invite', 'Invite revoke failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.revoke_error'), tKey('workspace_setup.revoke_error_title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,11 +505,11 @@ async function exportWorkspaceSnapshot() {
|
||||
var snapshot = await window.CrankApi.exportWorkspace(workspaceFormState.workspaceId);
|
||||
var slug = workspaceFormState.workspaceRecord && workspaceFormState.workspaceRecord.workspace
|
||||
? workspaceFormState.workspaceRecord.workspace.slug
|
||||
: 'workspace';
|
||||
: tKey('settings.nav.workspace');
|
||||
downloadJsonFile(slug + '-snapshot.json', snapshot);
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to export workspace', 'Export failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.export_error'), tKey('workspace_setup.export_error_title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -509,7 +518,7 @@ async function deleteWorkspaceAction() {
|
||||
if (!workspaceFormState.workspaceId || !window.CrankApi || !window.CrankAuth) {
|
||||
return;
|
||||
}
|
||||
if (!confirm('Delete this workspace? This cannot be undone.')) {
|
||||
if (!confirm(tKey('workspace_setup.delete_confirm'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -518,7 +527,7 @@ async function deleteWorkspaceAction() {
|
||||
await window.CrankAuth.logout();
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || 'Failed to delete workspace', 'Delete failed');
|
||||
window.CrankUi.error(error.message || tKey('workspace_setup.delete_error'), tKey('workspace_setup.delete_error_title'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user