feat: localize demo ui content

This commit is contained in:
a.tolmachev
2026-04-05 21:07:36 +03:00
parent 4667c82514
commit d33c52d51d
5 changed files with 128 additions and 8 deletions
+6 -2
View File
@@ -5,7 +5,7 @@ function agentUiStatus(status) {
}
function mapAgent(agent) {
return {
var mapped = {
id: agent.id,
slug: agent.slug,
display_name: agent.display_name,
@@ -21,10 +21,12 @@ function mapAgent(agent) {
latest_published_version: agent.latest_published_version,
mcp_endpoint: agent.mcp_endpoint || '',
};
return window.localizeDemoAgent ? window.localizeDemoAgent(mapped) : mapped;
}
function mapOperation(operation) {
return {
var mapped = {
id: operation.id,
name: operation.name,
display_name: operation.display_name,
@@ -33,6 +35,8 @@ function mapOperation(operation) {
current_draft_version: operation.current_draft_version || 1,
latest_published_version: operation.latest_published_version,
};
return window.localizeDemoOperation ? window.localizeDemoOperation(mapped) : mapped;
}
function currentLocale() {
+3 -1
View File
@@ -33,7 +33,7 @@ function currentLocale() {
}
function mapOperation(item) {
return {
var mapped = {
id: item.id,
name: item.name,
display_name: item.display_name,
@@ -53,6 +53,8 @@ function mapOperation(item) {
},
agent_refs: item.agent_refs || [],
};
return window.localizeDemoOperation ? window.localizeDemoOperation(mapped) : mapped;
}
function emptyStats() {
+86
View File
@@ -750,6 +750,20 @@ var TRANSLATIONS = {
'agents.toast.lifecycle_error_message': 'Failed to update agent lifecycle',
'agents.toast.endpoint_title': 'MCP endpoint copied',
// Demo content
'demo.agent.revops-copilot.display_name': 'RevOps Copilot',
'demo.agent.revops-copilot.description': 'Revenue operations assistant with CRM and billing tools.',
'demo.agent.support-triage.display_name': 'Support Triage',
'demo.agent.support-triage.description': 'Draft support agent focused on unary gRPC workflows.',
'demo.operation.crm_create_lead.display_name': 'Create CRM Lead',
'demo.operation.crm_create_lead.description': 'Create a lead record in the CRM system.',
'demo.operation.billing_get_invoice.display_name': 'Get Invoice Status',
'demo.operation.billing_get_invoice.description': 'Fetch invoice state and amount from billing.',
'demo.operation.support_lookup_ticket.display_name': 'Lookup Support Ticket',
'demo.operation.support_lookup_ticket.description': 'Draft unary gRPC support lookup using a descriptor set.',
'demo.operation.marketing_archive_contact.display_name': 'Archive Marketing Contact',
'demo.operation.marketing_archive_contact.description': 'Legacy archived flow kept for audit only.',
// Login
'login.title': 'Sign in',
'login.subtitle': 'Welcome back to your workspace',
@@ -1515,6 +1529,20 @@ var TRANSLATIONS = {
'agents.toast.lifecycle_error_message': 'Не удалось обновить состояние агента',
'agents.toast.endpoint_title': 'MCP endpoint скопирован',
// Demo content
'demo.agent.revops-copilot.display_name': 'Помощник RevOps',
'demo.agent.revops-copilot.description': 'Ассистент Revenue Operations с CRM- и billing-инструментами.',
'demo.agent.support-triage.display_name': 'Триаж поддержки',
'demo.agent.support-triage.description': 'Черновой агент поддержки, сфокусированный на unary gRPC-сценариях.',
'demo.operation.crm_create_lead.display_name': 'Создать CRM-лид',
'demo.operation.crm_create_lead.description': 'Создает запись лида в CRM-системе.',
'demo.operation.billing_get_invoice.display_name': 'Статус инвойса',
'demo.operation.billing_get_invoice.description': 'Получает состояние и сумму инвойса из billing-системы.',
'demo.operation.support_lookup_ticket.display_name': 'Найти тикет поддержки',
'demo.operation.support_lookup_ticket.description': 'Черновой unary gRPC lookup поддержки через descriptor set.',
'demo.operation.marketing_archive_contact.display_name': 'Архивировать маркетинговый контакт',
'demo.operation.marketing_archive_contact.description': 'Устаревший архивный сценарий, оставленный только для аудита.',
// Login
'login.title': 'Войти',
'login.subtitle': 'Добро пожаловать обратно',
@@ -1547,6 +1575,64 @@ function tf(key, vars) {
}, t(key));
}
function hasTranslation(key) {
return t(key) !== key;
}
function localizeDemoAgent(agent) {
if (!agent || !agent.slug) {
return agent;
}
var displayNameKey = 'demo.agent.' + agent.slug + '.display_name';
var descriptionKey = 'demo.agent.' + agent.slug + '.description';
if (!hasTranslation(displayNameKey) && !hasTranslation(descriptionKey)) {
return agent;
}
return Object.assign({}, agent, {
display_name: hasTranslation(displayNameKey) ? t(displayNameKey) : agent.display_name,
description: hasTranslation(descriptionKey) ? t(descriptionKey) : agent.description,
});
}
function localizeDemoOperation(operation) {
if (!operation || !operation.name) {
return operation;
}
var displayNameKey = 'demo.operation.' + operation.name + '.display_name';
var descriptionKey = 'demo.operation.' + operation.name + '.description';
if (!hasTranslation(displayNameKey) && !hasTranslation(descriptionKey)) {
return operation;
}
var next = Object.assign({}, operation);
if (hasTranslation(displayNameKey)) {
next.display_name = t(displayNameKey);
if (Object.prototype.hasOwnProperty.call(operation, 'operation_display_name')) {
next.operation_display_name = t(displayNameKey);
}
}
if (hasTranslation(descriptionKey)) {
if (Object.prototype.hasOwnProperty.call(operation, 'description')) {
next.description = t(descriptionKey);
}
if (Object.prototype.hasOwnProperty.call(operation, 'tool_description')) {
next.tool_description = t(descriptionKey);
}
}
return next;
}
window.localizeDemoAgent = localizeDemoAgent;
window.localizeDemoOperation = localizeDemoOperation;
function applyLang() {
// text content
document.querySelectorAll('[data-i18n]').forEach(function(el) {
+17 -3
View File
@@ -69,6 +69,20 @@ document.addEventListener('DOMContentLoaded', function () {
function normalizeLog(record) {
var log = record.log;
var agent = window.localizeDemoAgent
? window.localizeDemoAgent({
slug: record.agent_slug,
display_name: record.agent_display_name,
description: '',
})
: null;
var operation = window.localizeDemoOperation
? window.localizeDemoOperation({
name: record.operation_name,
operation_display_name: record.operation_display_name,
})
: null;
return {
id: log.id,
createdAt: log.created_at,
@@ -80,9 +94,9 @@ document.addEventListener('DOMContentLoaded', function () {
toolName: log.tool_name,
message: log.message,
operationName: record.operation_name,
operationDisplayName: record.operation_display_name,
operationDisplayName: operation ? operation.operation_display_name : record.operation_display_name,
agentSlug: record.agent_slug,
agentDisplayName: record.agent_display_name,
agentDisplayName: agent ? agent.display_name : record.agent_display_name,
requestPreview: log.request_preview,
responsePreview: log.response_preview,
errorKind: log.error_kind,
@@ -143,7 +157,7 @@ document.addEventListener('DOMContentLoaded', function () {
var opSpan = document.createElement('span');
opSpan.className = 'log-op-name';
opSpan.textContent = item.operationName;
opSpan.textContent = item.operationDisplayName || item.operationName;
msgDiv.appendChild(opSpan);
msgDiv.appendChild(document.createTextNode('\u00a0\u00a0'));
+16 -2
View File
@@ -71,6 +71,18 @@ document.addEventListener('DOMContentLoaded', function () {
return 'REST';
}
function localizedUsageOperation(operation) {
if (!window.localizeDemoOperation) {
return operation;
}
return window.localizeDemoOperation({
name: operation.operation_name,
operation_display_name: operation.operation_display_name,
description: operation.operation_description || null,
});
}
function chartBucketLabel(timestamp, period, index) {
var date = new Date(timestamp);
if (period === '7d') {
@@ -276,6 +288,7 @@ document.addEventListener('DOMContentLoaded', function () {
}, 0);
operations.forEach(function (operation) {
var localizedOperation = localizedUsageOperation(operation);
var node = rowTemplate.content.cloneNode(true);
var errorRate = operation.calls_total === 0
? 0
@@ -284,7 +297,7 @@ document.addEventListener('DOMContentLoaded', function () {
? 0
: ((operation.calls_total / totalCalls) * 100);
node.querySelector('.col-name').textContent = operation.operation_display_name;
node.querySelector('.col-name').textContent = localizedOperation.operation_display_name || operation.operation_display_name;
node.querySelector('.col-op-slug').textContent = operation.operation_name;
var proto = node.querySelector('.col-proto');
@@ -371,11 +384,12 @@ document.addEventListener('DOMContentLoaded', function () {
var rows = [tKey('usage.csv.header')];
state.usage.operations.forEach(function (operation) {
var localizedOperation = localizedUsageOperation(operation);
var errorRate = operation.calls_total === 0
? 0
: ((operation.calls_error / operation.calls_total) * 100);
rows.push([
'"' + operation.operation_display_name + '"',
'"' + (localizedOperation.operation_display_name || operation.operation_display_name) + '"',
'"' + protocolLabel(operation.protocol) + '"',
operation.calls_total,
operation.calls_error,