296 lines
11 KiB
Go
296 lines
11 KiB
Go
package tui
|
||
|
||
import (
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
)
|
||
|
||
// Lang is the UI language code.
|
||
type Lang string
|
||
|
||
const (
|
||
LangEn Lang = "en"
|
||
LangRu Lang = "ru"
|
||
)
|
||
|
||
// L holds all localizable UI strings.
|
||
type L struct {
|
||
// General
|
||
Help string
|
||
Quit string
|
||
Cancel string
|
||
Send string
|
||
New string
|
||
Clear string
|
||
Complete string
|
||
ScrollMore string
|
||
ScrollClose string
|
||
ScrollPct string
|
||
|
||
// Placeholder & input
|
||
Placeholder string
|
||
|
||
// Help overlay
|
||
KeyboardShortcuts string
|
||
InputShortcuts string
|
||
SlashCommands string
|
||
SendMessage string
|
||
NewLineInInput string
|
||
CycleMode string
|
||
QuickModelSwitch string
|
||
CancelStreaming string
|
||
QuitKeys string
|
||
ClearScreen string
|
||
NewConversation string
|
||
ToggleHelp string
|
||
ExpandTools string
|
||
ToggleToolDetails string
|
||
CopyLastResponse string
|
||
ToggleThinking string
|
||
ToggleCompact string
|
||
OpenInEditor string
|
||
BrowseHistory string
|
||
ScrollViewport string
|
||
HalfPageScroll string
|
||
Autocomplete string
|
||
AttachFile string
|
||
ActivateSkill string
|
||
RunSlashCommand string
|
||
Language string
|
||
LanguageF2 string
|
||
|
||
// Side panel
|
||
SidePanelAIAgent string
|
||
SidePanelTagline string
|
||
SidePanelModels string
|
||
SidePanelServers string
|
||
SidePanelICE string
|
||
SidePanelQuickActions string
|
||
SidePanelHelp string
|
||
SidePanelHelpDesc string
|
||
SidePanelServersDesc string
|
||
SidePanelModelDesc string
|
||
SidePanelLoadDesc string
|
||
SidePanelLoad string
|
||
ToolsConnected string
|
||
NoServersConnected string
|
||
ICEConversations string
|
||
ICECrossSessionActive string
|
||
ICEDisabled string
|
||
ICECrossSessionInactive string
|
||
|
||
// Model picker
|
||
SelectModel string
|
||
|
||
// Modes
|
||
ModeAsk string
|
||
ModePlan string
|
||
ModeBuild string
|
||
|
||
// Window title
|
||
WindowTitle string
|
||
WindowTitleThink string
|
||
WindowTitleStream string
|
||
WindowTitleDone string
|
||
|
||
// Key hints (short action names)
|
||
HintSend string
|
||
HintComplete string
|
||
HintHelp string
|
||
HintCancel string
|
||
HintQuit string
|
||
HintNew string
|
||
HintClear string
|
||
HintCommands string
|
||
HintFiles string
|
||
HintSkills string
|
||
|
||
// Toasts / messages
|
||
NoModelsAvailable string
|
||
LanguageSet string
|
||
}
|
||
|
||
var localeEn = L{
|
||
Help: "Help", Quit: "quit", Cancel: "cancel", Send: "send", New: "new", Clear: "clear", Complete: "complete",
|
||
ScrollMore: "↓ scroll for more", ScrollClose: "Esc or q to close", ScrollPct: "%.0f%% · j/k to scroll",
|
||
Placeholder: "Ask anything... (Enter to send, ctrl+b for sidebar)",
|
||
KeyboardShortcuts: "Keyboard Shortcuts",
|
||
InputShortcuts: "Input Shortcuts",
|
||
SlashCommands: "Slash Commands",
|
||
SendMessage: "Send message",
|
||
NewLineInInput: "New line in input",
|
||
CycleMode: "Cycle mode (ASK/PLAN/BUILD)",
|
||
QuickModelSwitch: "Quick model switch",
|
||
CancelStreaming: "Cancel streaming / close overlay",
|
||
QuitKeys: "Quit",
|
||
ClearScreen: "Clear screen (keep history)",
|
||
NewConversation: "New conversation",
|
||
ToggleHelp: "Toggle this help (when input empty)",
|
||
ExpandTools: "Expand/collapse all tools",
|
||
ToggleToolDetails: "Toggle last tool details",
|
||
CopyLastResponse: "Copy last response",
|
||
ToggleThinking: "Toggle thinking display",
|
||
ToggleCompact: "Toggle compact mode",
|
||
OpenInEditor: "Open input in $EDITOR",
|
||
BrowseHistory: "Browse input history",
|
||
ScrollViewport: "Scroll viewport",
|
||
HalfPageScroll: "Half-page scroll",
|
||
Autocomplete: "Autocomplete (commands/files/skills)",
|
||
AttachFile: "Attach file or agent",
|
||
ActivateSkill: "Activate skill",
|
||
RunSlashCommand: "Run slash command",
|
||
Language: "Language",
|
||
LanguageF2: "Switch interface language (F2)",
|
||
SidePanelAIAgent: "AI AGENT",
|
||
SidePanelTagline: "100% local · Your data never leaves",
|
||
SidePanelModels: "Models",
|
||
SidePanelServers: "Servers",
|
||
SidePanelICE: "ICE",
|
||
SidePanelQuickActions: "Quick Actions",
|
||
SidePanelHelp: "Help",
|
||
SidePanelHelpDesc: "Keyboard shortcuts",
|
||
SidePanelServersDesc: "List connected tools",
|
||
SidePanelModelDesc: "Switch model",
|
||
SidePanelLoad: "Load",
|
||
SidePanelLoadDesc: "Add context from file",
|
||
ToolsConnected: "%d tools connected",
|
||
NoServersConnected: "No servers connected",
|
||
ICEConversations: "%d conversations",
|
||
ICECrossSessionActive: "Cross-session memory active",
|
||
ICEDisabled: "ICE disabled",
|
||
ICECrossSessionInactive: "Cross-session memory inactive",
|
||
SelectModel: "Select Model",
|
||
ModeAsk: "ASK", ModePlan: "PLAN", ModeBuild: "BUILD",
|
||
WindowTitle: "AI AGENT", WindowTitleThink: "AI AGENT · thinking...",
|
||
WindowTitleStream: "AI AGENT · streaming...", WindowTitleDone: "AI AGENT · done",
|
||
HintSend: "send", HintComplete: "complete", HintHelp: "help", HintCancel: "cancel", HintQuit: "quit",
|
||
HintNew: "new", HintClear: "clear", HintCommands: "commands", HintFiles: "files", HintSkills: "skills",
|
||
NoModelsAvailable: "No models available. Check Ollama connection.",
|
||
LanguageSet: "Language: %s",
|
||
}
|
||
|
||
var localeRu = L{
|
||
Help: "Справка", Quit: "выход", Cancel: "отмена", Send: "отправить", New: "новый", Clear: "очистить", Complete: "дополнение",
|
||
ScrollMore: "↓ листать вниз", ScrollClose: "Esc или q — закрыть", ScrollPct: "%.0f%% · j/k листать",
|
||
Placeholder: "Спросите что угодно... (Enter — отправить, ctrl+b — панель)",
|
||
KeyboardShortcuts: "Горячие клавиши",
|
||
InputShortcuts: "Клавиши ввода",
|
||
SlashCommands: "Слэш-команды",
|
||
SendMessage: "Отправить сообщение",
|
||
NewLineInInput: "Новая строка в поле ввода",
|
||
CycleMode: "Режим (ASK/PLAN/BUILD)",
|
||
QuickModelSwitch: "Быстрая смена модели",
|
||
CancelStreaming: "Отмена / закрыть окно",
|
||
QuitKeys: "Выход",
|
||
ClearScreen: "Очистить экран (история сохраняется)",
|
||
NewConversation: "Новый диалог",
|
||
ToggleHelp: "Показать справку (при пустом вводе)",
|
||
ExpandTools: "Развернуть/свернуть инструменты",
|
||
ToggleToolDetails: "Детали последнего инструмента",
|
||
CopyLastResponse: "Копировать последний ответ",
|
||
ToggleThinking: "Показать процесс размышления",
|
||
ToggleCompact: "Компактный режим",
|
||
OpenInEditor: "Открыть в $EDITOR",
|
||
BrowseHistory: "История ввода",
|
||
ScrollViewport: "Прокрутка",
|
||
HalfPageScroll: "На полстраницы",
|
||
Autocomplete: "Дополнение (команды/файлы/навыки)",
|
||
AttachFile: "Прикрепить файл или агента",
|
||
ActivateSkill: "Подключить навык",
|
||
RunSlashCommand: "Выполнить слэш-команду",
|
||
Language: "Язык",
|
||
LanguageF2: "Язык интерфейса (F2)",
|
||
SidePanelAIAgent: "AI AGENT",
|
||
SidePanelTagline: "100% локально · Ваши данные не покидают устройство",
|
||
SidePanelModels: "Модели",
|
||
SidePanelServers: "Серверы",
|
||
SidePanelICE: "ICE",
|
||
SidePanelQuickActions: "Быстрые действия",
|
||
SidePanelHelp: "Справка",
|
||
SidePanelHelpDesc: "Горячие клавиши",
|
||
SidePanelServersDesc: "Подключённые инструменты",
|
||
SidePanelModelDesc: "Сменить модель",
|
||
SidePanelLoad: "Загрузить",
|
||
SidePanelLoadDesc: "Добавить контекст из файла",
|
||
ToolsConnected: "Подключено инструментов: %d",
|
||
NoServersConnected: "Серверы не подключены",
|
||
ICEConversations: "Диалогов: %d",
|
||
ICECrossSessionActive: "Память между сессиями активна",
|
||
ICEDisabled: "ICE выключен",
|
||
ICECrossSessionInactive: "Память между сессиями неактивна",
|
||
SelectModel: "Выбор модели",
|
||
ModeAsk: "ASK", ModePlan: "PLAN", ModeBuild: "BUILD",
|
||
WindowTitle: "AI AGENT", WindowTitleThink: "AI AGENT · думает...",
|
||
WindowTitleStream: "AI AGENT · отвечает...", WindowTitleDone: "AI AGENT · готово",
|
||
HintSend: "отправить", HintComplete: "дополнение", HintHelp: "справка", HintCancel: "отмена", HintQuit: "выход",
|
||
HintNew: "новый", HintClear: "очистить", HintCommands: "команды", HintFiles: "файлы", HintSkills: "навыки",
|
||
NoModelsAvailable: "Нет моделей. Проверьте подключение к Ollama.",
|
||
LanguageSet: "Язык: %s",
|
||
}
|
||
|
||
// Locale returns the strings for the given language. Unknown lang falls back to English.
|
||
func Locale(lang Lang) L {
|
||
switch lang {
|
||
case LangRu:
|
||
return localeRu
|
||
default:
|
||
return localeEn
|
||
}
|
||
}
|
||
|
||
// LangName returns a display name for the language.
|
||
func LangName(lang Lang) string {
|
||
switch lang {
|
||
case LangRu:
|
||
return "Русский"
|
||
default:
|
||
return "English"
|
||
}
|
||
}
|
||
|
||
// NextLang cycles to the next language (en -> ru -> en).
|
||
func NextLang(lang Lang) Lang {
|
||
switch lang {
|
||
case LangEn:
|
||
return LangRu
|
||
case LangRu:
|
||
return LangEn
|
||
default:
|
||
return LangEn
|
||
}
|
||
}
|
||
|
||
// DefaultLangPath returns the path for storing UI language preference.
|
||
func DefaultLangPath() string {
|
||
home, err := os.UserHomeDir()
|
||
if err != nil {
|
||
return "lang"
|
||
}
|
||
return filepath.Join(home, ".config", "ai-agent", "lang")
|
||
}
|
||
|
||
// LoadLang reads the saved language from DefaultLangPath(). Returns LangEn if missing or invalid.
|
||
func LoadLang() Lang {
|
||
data, err := os.ReadFile(DefaultLangPath())
|
||
if err != nil {
|
||
return LangEn
|
||
}
|
||
switch strings.TrimSpace(strings.ToLower(string(data))) {
|
||
case "ru", "русский":
|
||
return LangRu
|
||
default:
|
||
return LangEn
|
||
}
|
||
}
|
||
|
||
// SaveLang writes the language to DefaultLangPath().
|
||
func SaveLang(lang Lang) error {
|
||
path := DefaultLangPath()
|
||
dir := filepath.Dir(path)
|
||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||
return err
|
||
}
|
||
return os.WriteFile(path, []byte(lang), 0o644)
|
||
}
|