Files
crank/apps/ui/js/i18n.js
T
2026-03-30 00:15:02 +03:00

290 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* ═══════════════════════════════════════════════
Crank — i18n
Reads `crank_lang` from localStorage ('en'|'ru')
═══════════════════════════════════════════════ */
var TRANSLATIONS = {
en: {
// Nav
'nav.operations': 'Operations',
'nav.agents': 'Agents',
'nav.apikeys': 'API Keys',
'nav.logs': 'Logs',
'nav.usage': 'Usage',
'nav.settings': 'Settings',
'nav.logout': 'Log out',
// Operations page
'ops.title': 'Operations',
'ops.subtitle': 'Tool contracts that expose your upstream APIs as MCP tools',
'ops.new': 'New operation',
'ops.loading': 'Loading operations…',
'ops.empty.title': 'No operations found',
'ops.empty.sub': 'Try adjusting your search or filters.',
// Stats
'stats.total': 'Total operations',
'stats.active': 'Active',
'stats.requests': 'Requests today',
'stats.latency': 'Avg. latency',
// Tabs
'tab.all': 'All',
'tab.active': 'Active',
'tab.draft': 'Draft',
'tab.error': 'Error',
'tab.inactive': 'Inactive',
// Filters & sort
'filter.search': 'Search operations...',
'filter.protocol': 'Protocol',
'filter.status': 'Status',
'filter.category': 'Category',
'filter.showing': 'Showing',
'filter.of': 'of',
'filter.clear': 'Clear filter',
'sort.created_desc':'Sort: Last created',
'sort.created_asc': 'Sort: Oldest first',
'sort.name_asc': 'Sort: Name AZ',
'sort.name_desc': 'Sort: Name ZA',
// Table headers
'th.name': 'NAME',
'th.protocol': 'PROTOCOL',
'th.status': 'STATUS',
'th.created': 'CREATED',
'th.url': 'TARGET URL',
// Pagination
'page.showing': 'Showing',
'page.of': 'of',
'page.ops': 'operations',
// API Keys page
'apikeys.title': 'API Keys',
'apikeys.subtitle': 'Manage access tokens for external integrations',
'apikeys.new': 'Create key',
'apikeys.th.name': 'NAME',
'apikeys.th.scope': 'SCOPE',
'apikeys.th.created':'CREATED',
'apikeys.th.last': 'LAST USED',
// Logs page
'logs.title': 'Logs',
'logs.subtitle': 'Real-time request and event log',
'logs.search': 'Search logs...',
'logs.level.all': 'All',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
// Usage page
'usage.title': 'Usage',
'usage.subtitle': 'Request volume and performance metrics',
// Settings sidebar
'settings.title': 'Settings',
'settings.nav.workspace': 'Workspace',
'settings.nav.profile': 'Profile',
'settings.nav.members': 'Members',
'settings.nav.security': 'Security',
'settings.nav.notif': 'Notifications',
'settings.nav.danger': 'Danger zone',
// Settings — workspace section
'settings.ws.title': 'Workspace settings',
'settings.ws.subtitle': 'General information about your workspace',
'settings.ws.name': 'Workspace name',
'settings.ws.display': 'Display name',
'settings.ws.desc': 'Description',
'settings.ws.tz': 'Timezone',
'settings.ws.protocol': 'Default protocol',
// Settings — language
'settings.lang.title': 'Language',
'settings.lang.subtitle': 'Interface display language',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Profile',
'settings.profile.name': 'Full name',
'settings.profile.email': 'Email',
'settings.profile.role': 'Role',
// Common buttons
'btn.save': 'Save changes',
'btn.cancel': 'Cancel',
'btn.create': 'Create',
'btn.delete': 'Delete',
'btn.revoke': 'Revoke',
'btn.copy': 'Copy',
// Login
'login.title': 'Sign in',
'login.subtitle': 'Welcome back to your workspace',
'login.email': 'Email',
'login.password': 'Password',
'login.submit': 'Sign in',
'login.sso': 'Continue with Google',
},
ru: {
// Nav
'nav.operations': 'Операции',
'nav.agents': 'Агенты',
'nav.apikeys': 'API Ключи',
'nav.logs': 'Логи',
'nav.usage': 'Использование',
'nav.settings': 'Настройки',
'nav.logout': 'Выйти',
// Operations page
'ops.title': 'Операции',
'ops.subtitle': 'Контракты инструментов, открывающие ваши API как MCP-инструменты',
'ops.new': 'Новая операция',
'ops.loading': 'Загрузка операций…',
'ops.empty.title': 'Операции не найдены',
'ops.empty.sub': 'Попробуйте изменить поисковый запрос или фильтры.',
// Stats
'stats.total': 'Всего операций',
'stats.active': 'Активные',
'stats.requests': 'Запросов сегодня',
'stats.latency': 'Ср. задержка',
// Tabs
'tab.all': 'Все',
'tab.active': 'Активные',
'tab.draft': 'Черновик',
'tab.error': 'Ошибка',
'tab.inactive': 'Неактивные',
// Filters & sort
'filter.search': 'Поиск операций...',
'filter.protocol': 'Протокол',
'filter.status': 'Статус',
'filter.category': 'Категория',
'filter.showing': 'Показано',
'filter.of': 'из',
'filter.clear': 'Сбросить',
'sort.created_desc':'Сначала новые',
'sort.created_asc': 'Сначала старые',
'sort.name_asc': 'Название А–Я',
'sort.name_desc': 'Название Я–А',
// Table headers
'th.name': 'НАЗВАНИЕ',
'th.protocol': 'ПРОТОКОЛ',
'th.status': 'СТАТУС',
'th.created': 'СОЗДАН',
'th.url': 'ЦЕЛЕВОЙ URL',
// Pagination
'page.showing': 'Показано',
'page.of': 'из',
'page.ops': 'операций',
// API Keys page
'apikeys.title': 'API Ключи',
'apikeys.subtitle': 'Управление токенами доступа для внешних интеграций',
'apikeys.new': 'Создать ключ',
'apikeys.th.name': 'НАЗВАНИЕ',
'apikeys.th.scope': 'ОБЛАСТЬ',
'apikeys.th.created':'СОЗДАН',
'apikeys.th.last': 'ПОСЛЕДНЕЕ ИСПОЛЬЗОВАНИЕ',
// Logs page
'logs.title': 'Логи',
'logs.subtitle': 'Журнал запросов и событий в реальном времени',
'logs.search': 'Поиск в логах...',
'logs.level.all': 'Все',
'logs.level.info': 'Info',
'logs.level.warn': 'Warn',
'logs.level.error': 'Error',
'logs.level.debug': 'Debug',
// Usage page
'usage.title': 'Использование',
'usage.subtitle': 'Объём запросов и метрики производительности',
// Settings sidebar
'settings.title': 'Настройки',
'settings.nav.workspace': 'Воркспейс',
'settings.nav.profile': 'Профиль',
'settings.nav.members': 'Участники',
'settings.nav.security': 'Безопасность',
'settings.nav.notif': 'Уведомления',
'settings.nav.danger': 'Опасная зона',
// Settings — workspace section
'settings.ws.title': 'Настройки воркспейса',
'settings.ws.subtitle': 'Общая информация о вашем воркспейсе',
'settings.ws.name': 'Имя воркспейса',
'settings.ws.display': 'Отображаемое имя',
'settings.ws.desc': 'Описание',
'settings.ws.tz': 'Часовой пояс',
'settings.ws.protocol': 'Протокол по умолчанию',
// Settings — language
'settings.lang.title': 'Язык',
'settings.lang.subtitle': 'Язык отображения интерфейса',
'settings.lang.en': 'English',
'settings.lang.ru': 'Русский',
// Settings — profile section
'settings.profile.title': 'Профиль',
'settings.profile.name': 'Полное имя',
'settings.profile.email': 'Email',
'settings.profile.role': 'Роль',
// Common buttons
'btn.save': 'Сохранить',
'btn.cancel': 'Отмена',
'btn.create': 'Создать',
'btn.delete': 'Удалить',
'btn.revoke': 'Отозвать',
'btn.copy': 'Копировать',
// Login
'login.title': 'Войти',
'login.subtitle': 'Добро пожаловать обратно',
'login.email': 'Email',
'login.password': 'Пароль',
'login.submit': 'Войти',
'login.sso': 'Войти через Google',
}
};
function t(key) {
var lang = localStorage.getItem('crank_lang') || 'en';
var tr = TRANSLATIONS[lang] || TRANSLATIONS.en;
return tr[key] !== undefined ? tr[key] : (TRANSLATIONS.en[key] !== undefined ? TRANSLATIONS.en[key] : key);
}
function applyLang() {
// text content
document.querySelectorAll('[data-i18n]').forEach(function(el) {
el.textContent = t(el.getAttribute('data-i18n'));
});
// placeholder
document.querySelectorAll('[data-i18n-ph]').forEach(function(el) {
el.placeholder = t(el.getAttribute('data-i18n-ph'));
});
// highlight active language button in settings
var lang = localStorage.getItem('crank_lang') || 'en';
document.querySelectorAll('.lang-btn').forEach(function(btn) {
btn.classList.toggle('active', btn.dataset.lang === lang);
});
}
function setLang(lang) {
localStorage.setItem('crank_lang', lang);
applyLang();
// Notify Alpine components to re-render reactive getters
window.dispatchEvent(new CustomEvent('crank:langchange', { detail: { lang: lang } }));
}
document.addEventListener('DOMContentLoaded', applyLang);