Skip to content

Commit 4c535b3

Browse files
JohanYPclaude
andcommitted
i18n: translate /personality + /show_tools replies; expose memory_remove + memory_export
User feedback after running the new commands: the reply messages for /personality and /show_tools were hardcoded English (no translation fallback for es/de/fr/ru/zh users), and /memory_remove + /memory_export were registered handlers but didn't appear in the Telegram autocomplete menu. Fix: 1. New i18n keys in all six locales: - cmd.description.memory_remove, cmd.description.memory_export - personality.updated, personality.label, personality.empty_help, personality.error - show_tools.current_visible, show_tools.current_hidden, show_tools.usage, show_tools.invalid_value, show_tools.now_visible, show_tools.now_hidden, show_tools.error Spanish, German, French, Russian and Chinese translations included. The empty_help texts keep the example list locale-agnostic enough that the Spanish-speaker default audience still gets useful prompts even on other locales. 2. memory-commands.ts now imports `t()` from i18n and calls it for every /personality and /show_tools reply. No more hardcoded English in those handlers. The two commands also drop the inline literals from the source so future tweaks live in one place per locale. 3. definitions.ts adds memory_remove and memory_export to BOT_COMMANDS so they appear in Telegram's command autocomplete. They were already wired up via bot.command() — only the menu listing was missing. Test suite still 942/942. After this commit, on an existing install: git pull docker compose down docker compose up -d --build Then on first /help (or after Telegram refreshes the menu) the two new entries appear, and /personality + /show_tools reply in the locale selected via BOT_LOCALE. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a4315e1 commit 4c535b3

8 files changed

Lines changed: 125 additions & 29 deletions

File tree

src/bot/commands/definitions.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ const COMMAND_DEFINITIONS: BotCommandI18nDefinition[] = [
4040
{ command: "personality", descriptionKey: "cmd.description.personality" },
4141
{ command: "memory", descriptionKey: "cmd.description.memory" },
4242
{ command: "memory_search", descriptionKey: "cmd.description.memory_search" },
43+
{ command: "memory_remove", descriptionKey: "cmd.description.memory_remove" },
44+
{ command: "memory_export", descriptionKey: "cmd.description.memory_export" },
4345
{ command: "context", descriptionKey: "cmd.description.context" },
4446
{ command: "memfiles", descriptionKey: "cmd.description.memfiles" },
4547
{ command: "show_tools", descriptionKey: "cmd.description.show_tools" },

src/bot/commands/memory-commands.ts

Lines changed: 12 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
type SkillStatus,
3333
} from "../../memory/skill-service.js";
3434
import { getUiPreferences, setUiPreferences } from "../../settings/manager.js";
35+
import { t } from "../../i18n/index.js";
3536

3637
const MAX_TELEGRAM_MESSAGE = 4000;
3738

@@ -139,31 +140,19 @@ export function registerMemoryCommands(bot: Bot<Context>): void {
139140
if (arg) {
140141
setDocument("personality", arg);
141142
appendAudit("document_updated", { name: "personality", source: "telegram" });
142-
await ctx.reply(
143-
"Personality updated. Open a new session (/new) for the change to apply " +
144-
"to the assistant's behaviour.",
145-
);
143+
await ctx.reply(t("personality.updated"));
146144
return;
147145
}
148146

149147
const doc = getDocument("personality");
150148
if (!doc || !doc.content.trim()) {
151-
await ctx.reply(
152-
"Personality is empty.\n\n" +
153-
"Use /personality <text> to set behaviour rules. Examples:\n" +
154-
' /personality dime siempre "señor". Tono formal y en español.\n' +
155-
" /personality respuestas concisas, máximo 3 líneas\n" +
156-
" /personality habla siempre en inglés salvo que pregunte explícitamente en otro idioma\n\n" +
157-
"Personality is for HOW you want the assistant to respond. " +
158-
"Save FACTS about you with /memory or by telling the assistant to " +
159-
"remember them.",
160-
);
149+
await ctx.reply(t("personality.empty_help"));
161150
return;
162151
}
163-
await ctx.reply(`Personality\n\n${truncate(doc.content)}`);
152+
await ctx.reply(`${t("personality.label")}\n\n${truncate(doc.content)}`);
164153
} catch (error) {
165154
logger.error("[MemoryCommands] /personality error:", error);
166-
await ctx.reply("Failed to access personality.");
155+
await ctx.reply(t("personality.error"));
167156
}
168157
});
169158

@@ -175,12 +164,10 @@ export function registerMemoryCommands(bot: Bot<Context>): void {
175164
const current = getUiPreferences().showToolMessages;
176165

177166
if (!arg) {
178-
await ctx.reply(
179-
`Tool messages are currently ${current ? "VISIBLE" : "HIDDEN"}.\n\n` +
180-
"Usage:\n" +
181-
" /show_tools on — show tool calls (default)\n" +
182-
" /show_tools off — hide them, only assistant responses appear",
183-
);
167+
const stateLine = current
168+
? t("show_tools.current_visible")
169+
: t("show_tools.current_hidden");
170+
await ctx.reply(`${stateLine}\n\n${t("show_tools.usage")}`);
184171
return;
185172
}
186173

@@ -190,19 +177,15 @@ export function registerMemoryCommands(bot: Bot<Context>): void {
190177
} else if (arg === "off" || arg === "false" || arg === "no" || arg === "0") {
191178
next = false;
192179
} else {
193-
await ctx.reply("Invalid value. Use /show_tools on or /show_tools off.");
180+
await ctx.reply(t("show_tools.invalid_value"));
194181
return;
195182
}
196183

197184
await setUiPreferences({ showToolMessages: next });
198-
await ctx.reply(
199-
next
200-
? "Tool messages are now VISIBLE."
201-
: "Tool messages are now HIDDEN. Only assistant responses will appear.",
202-
);
185+
await ctx.reply(next ? t("show_tools.now_visible") : t("show_tools.now_hidden"));
203186
} catch (error) {
204187
logger.error("[MemoryCommands] /show_tools error:", error);
205-
await ctx.reply("Failed to update tool visibility setting.");
188+
await ctx.reply(t("show_tools.error"));
206189
}
207190
});
208191

src/i18n/de.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const de: I18nDictionary = {
2727
"cmd.description.skill_verify": "sha256-Integrität der Skills prüfen",
2828
"cmd.description.skill_remove": "Skill deinstallieren",
2929
"cmd.description.memory_search": "In gespeicherten Fakten suchen",
30+
"cmd.description.memory_remove": "Einen gespeicherten Fakt nach ID löschen",
31+
"cmd.description.memory_export": "Speicher in Markdown-Dateien exportieren",
3032
"cmd.description.help": "Hilfe",
3133

3234
"callback.unknown_command": "Unbekannter Befehl",
@@ -134,6 +136,23 @@ export const de: I18nDictionary = {
134136
"tts.disabled": "🔇 Audioantworten global deaktiviert.",
135137
"tts.failed": "⚠️ Audioreply konnte nicht erzeugt werden.",
136138

139+
"personality.updated":
140+
"✅ Persönlichkeit aktualisiert. Öffne eine neue Sitzung (/new), damit die Änderung angewendet wird.",
141+
"personality.label": "Persönlichkeit",
142+
"personality.empty_help":
143+
"Persönlichkeit ist leer.\n\nVerwende /personality <text>, um Verhaltensregeln festzulegen. Beispiele:\n /personality sprich mich immer mit \"Sir\" an. Formeller Ton, auf Deutsch.\n /personality knappe Antworten, maximal 3 Zeilen\n /personality antworte auf Englisch, außer ich wechsle die Sprache\n\nPersonality ist für das WIE der Assistent antworten soll. Speichere FAKTEN über dich mit /memory oder indem du dem Assistenten sagst, dass er sich etwas merken soll.",
144+
"personality.error": "Persönlichkeit konnte nicht abgerufen werden.",
145+
146+
"show_tools.current_visible": "Tool-Nachrichten sind derzeit SICHTBAR.",
147+
"show_tools.current_hidden": "Tool-Nachrichten sind derzeit AUSGEBLENDET.",
148+
"show_tools.usage":
149+
"Verwendung:\n /show_tools on — Tool-Aufrufe anzeigen (Standard)\n /show_tools off — verbergen, nur Antworten des Assistenten anzeigen",
150+
"show_tools.invalid_value": "Ungültiger Wert. Verwende /show_tools on oder /show_tools off.",
151+
"show_tools.now_visible": "✅ Tool-Nachrichten sind jetzt SICHTBAR.",
152+
"show_tools.now_hidden":
153+
"✅ Tool-Nachrichten sind jetzt AUSGEBLENDET. Nur Assistenten-Antworten erscheinen.",
154+
"show_tools.error": "Tool-Sichtbarkeit konnte nicht aktualisiert werden.",
155+
137156
"projects.empty":
138157
"📭 Keine Projekte gefunden.\n\nÖffne ein Verzeichnis in OpenCode und erstelle mindestens eine Sitzung, dann erscheint es hier.",
139158
"projects.select": "Projekt auswählen:",

src/i18n/en.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export const en = {
2424
"cmd.description.skill_verify": "Check sha256 integrity of installed skills",
2525
"cmd.description.skill_remove": "Uninstall a skill",
2626
"cmd.description.memory_search": "Search saved facts",
27+
"cmd.description.memory_remove": "Delete a saved fact by id",
28+
"cmd.description.memory_export": "Export memory to markdown files",
2729
"cmd.description.help": "Help",
2830

2931
"callback.unknown_command": "Unknown command",
@@ -127,6 +129,22 @@ export const en = {
127129
"tts.disabled": "🔇 Audio replies disabled globally.",
128130
"tts.failed": "⚠️ Failed to generate audio reply.",
129131

132+
"personality.updated":
133+
"✅ Personality updated. Open a new session (/new) for the change to apply.",
134+
"personality.label": "Personality",
135+
"personality.empty_help":
136+
"Personality is empty.\n\nUse /personality <text> to set behaviour rules. Examples:\n /personality always address me as \"sir\". Formal tone, replies in English.\n /personality concise responses, max 3 lines\n /personality reply in English unless I explicitly switch language\n\nPersonality is for HOW you want the assistant to respond. Save FACTS about you with /memory or by telling the assistant to remember them.",
137+
"personality.error": "Failed to access personality.",
138+
139+
"show_tools.current_visible": "Tool messages are currently VISIBLE.",
140+
"show_tools.current_hidden": "Tool messages are currently HIDDEN.",
141+
"show_tools.usage":
142+
"Usage:\n /show_tools on — show tool calls (default)\n /show_tools off — hide them, only assistant responses appear",
143+
"show_tools.invalid_value": "Invalid value. Use /show_tools on or /show_tools off.",
144+
"show_tools.now_visible": "✅ Tool messages are now VISIBLE.",
145+
"show_tools.now_hidden": "✅ Tool messages are now HIDDEN. Only assistant responses will appear.",
146+
"show_tools.error": "Failed to update tool visibility setting.",
147+
130148
"projects.empty":
131149
"📭 No projects found.\n\nOpen a directory in OpenCode and create at least one session, then it will appear here.",
132150
"projects.select": "Select a project:",

src/i18n/es.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const es: I18nDictionary = {
2727
"cmd.description.skill_verify": "Verificar integridad sha256 de los skills",
2828
"cmd.description.skill_remove": "Desinstalar un skill",
2929
"cmd.description.memory_search": "Buscar en los datos guardados",
30+
"cmd.description.memory_remove": "Eliminar un dato guardado por id",
31+
"cmd.description.memory_export": "Exportar memoria a archivos markdown",
3032
"cmd.description.help": "Ayuda",
3133

3234
"callback.unknown_command": "Comando desconocido",
@@ -134,6 +136,23 @@ export const es: I18nDictionary = {
134136
"tts.disabled": "🔇 Respuestas de audio desactivadas globalmente.",
135137
"tts.failed": "⚠️ No se pudo generar la respuesta de audio.",
136138

139+
"personality.updated":
140+
"✅ Personalidad actualizada. Abre una sesión nueva (/new) para que el cambio se aplique.",
141+
"personality.label": "Personalidad",
142+
"personality.empty_help":
143+
"Personalidad vacía.\n\nUsa /personality <texto> para definir reglas de comportamiento. Ejemplos:\n /personality dime siempre \"señor\". Tono formal, en español.\n /personality respuestas concisas, máximo 3 líneas\n /personality habla en inglés salvo que pregunte en otro idioma\n\nPersonality es para CÓMO quieres que responda el asistente. Guarda DATOS sobre ti con /memory o pidiéndole al asistente que los recuerde.",
144+
"personality.error": "No se pudo acceder a la personalidad.",
145+
146+
"show_tools.current_visible": "Los mensajes de herramientas están actualmente VISIBLES.",
147+
"show_tools.current_hidden": "Los mensajes de herramientas están actualmente OCULTOS.",
148+
"show_tools.usage":
149+
"Uso:\n /show_tools on — mostrar llamadas a herramientas (por defecto)\n /show_tools off — ocultarlas, solo aparece la respuesta del asistente",
150+
"show_tools.invalid_value": "Valor inválido. Usa /show_tools on o /show_tools off.",
151+
"show_tools.now_visible": "✅ Los mensajes de herramientas ahora son VISIBLES.",
152+
"show_tools.now_hidden":
153+
"✅ Los mensajes de herramientas ahora están OCULTOS. Solo aparecerán las respuestas del asistente.",
154+
"show_tools.error": "No se pudo actualizar la visibilidad de las herramientas.",
155+
137156
"projects.empty":
138157
"📭 No se encontraron proyectos.\n\nAbre un directorio en OpenCode y crea al menos una sesión; entonces aparecerá aquí.",
139158
"projects.select": "Selecciona un proyecto:",

src/i18n/fr.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const fr: I18nDictionary = {
2727
"cmd.description.skill_verify": "Vérifier l'intégrité sha256 des compétences",
2828
"cmd.description.skill_remove": "Désinstaller une compétence",
2929
"cmd.description.memory_search": "Rechercher dans les faits enregistrés",
30+
"cmd.description.memory_remove": "Supprimer un fait enregistré par id",
31+
"cmd.description.memory_export": "Exporter la mémoire en fichiers markdown",
3032
"cmd.description.help": "Aide",
3133

3234
"callback.unknown_command": "Commande inconnue",
@@ -135,6 +137,23 @@ export const fr: I18nDictionary = {
135137
"tts.disabled": "🔇 Réponses audio désactivées globalement.",
136138
"tts.failed": "⚠️ Impossible de générer la réponse audio.",
137139

140+
"personality.updated":
141+
"✅ Personnalité mise à jour. Ouvre une nouvelle session (/new) pour que le changement s'applique.",
142+
"personality.label": "Personnalité",
143+
"personality.empty_help":
144+
"Personnalité vide.\n\nUtilise /personality <texte> pour définir des règles de comportement. Exemples :\n /personality appelle-moi toujours \"monsieur\". Ton formel, en français.\n /personality réponses concises, maximum 3 lignes\n /personality réponds en anglais sauf si je change de langue\n\nPersonality concerne COMMENT tu veux que l'assistant réponde. Enregistre des FAITS sur toi avec /memory ou en demandant à l'assistant de s'en souvenir.",
145+
"personality.error": "Impossible d'accéder à la personnalité.",
146+
147+
"show_tools.current_visible": "Les messages d'outils sont actuellement VISIBLES.",
148+
"show_tools.current_hidden": "Les messages d'outils sont actuellement MASQUÉS.",
149+
"show_tools.usage":
150+
"Utilisation :\n /show_tools on — afficher les appels d'outils (par défaut)\n /show_tools off — les masquer, seules les réponses de l'assistant apparaissent",
151+
"show_tools.invalid_value": "Valeur invalide. Utilise /show_tools on ou /show_tools off.",
152+
"show_tools.now_visible": "✅ Les messages d'outils sont maintenant VISIBLES.",
153+
"show_tools.now_hidden":
154+
"✅ Les messages d'outils sont maintenant MASQUÉS. Seules les réponses de l'assistant apparaîtront.",
155+
"show_tools.error": "Impossible de mettre à jour la visibilité des outils.",
156+
138157
"projects.empty":
139158
"📭 Aucun projet trouvé.\n\nOuvrez un répertoire dans OpenCode et créez au moins une session, il apparaîtra ensuite ici.",
140159
"projects.select": "Sélectionnez un projet :",

src/i18n/ru.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const ru: I18nDictionary = {
2727
"cmd.description.skill_verify": "Проверить целостность sha256 навыков",
2828
"cmd.description.skill_remove": "Удалить навык",
2929
"cmd.description.memory_search": "Поиск по сохранённым фактам",
30+
"cmd.description.memory_remove": "Удалить сохранённый факт по id",
31+
"cmd.description.memory_export": "Экспортировать память в markdown-файлы",
3032
"cmd.description.help": "Справка",
3133

3234
"callback.unknown_command": "Неизвестная команда",
@@ -127,6 +129,23 @@ export const ru: I18nDictionary = {
127129
"tts.disabled": "🔇 Аудиоответы выключены глобально.",
128130
"tts.failed": "⚠️ Не удалось создать аудиоответ.",
129131

132+
"personality.updated":
133+
"✅ Личность обновлена. Открой новую сессию (/new), чтобы изменения применились.",
134+
"personality.label": "Личность",
135+
"personality.empty_help":
136+
"Личность пуста.\n\nИспользуй /personality <текст>, чтобы задать правила поведения. Примеры:\n /personality всегда обращайся ко мне на «вы». Формальный тон, на русском.\n /personality краткие ответы, максимум 3 строки\n /personality отвечай на английском, пока я не переключу язык\n\nPersonality — это про КАК ассистент должен отвечать. Сохраняй ФАКТЫ о себе через /memory или просто скажи ассистенту запомнить.",
137+
"personality.error": "Не удалось получить доступ к личности.",
138+
139+
"show_tools.current_visible": "Сообщения инструментов сейчас ВИДИМЫ.",
140+
"show_tools.current_hidden": "Сообщения инструментов сейчас СКРЫТЫ.",
141+
"show_tools.usage":
142+
"Использование:\n /show_tools on — показывать вызовы инструментов (по умолчанию)\n /show_tools off — скрывать их, видны только ответы ассистента",
143+
"show_tools.invalid_value": "Неверное значение. Используй /show_tools on или /show_tools off.",
144+
"show_tools.now_visible": "✅ Сообщения инструментов теперь ВИДИМЫ.",
145+
"show_tools.now_hidden":
146+
"✅ Сообщения инструментов теперь СКРЫТЫ. Будут видны только ответы ассистента.",
147+
"show_tools.error": "Не удалось обновить видимость инструментов.",
148+
130149
"projects.empty":
131150
"📭 Проектов нет.\n\nОткройте директорию в OpenCode и создайте хотя бы одну сессию, после этого она появится здесь.",
132151
"projects.select": "Выберите проект:",

src/i18n/zh.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export const zh: I18nDictionary = {
2727
"cmd.description.skill_verify": "检查已安装技能的 sha256 完整性",
2828
"cmd.description.skill_remove": "卸载技能",
2929
"cmd.description.memory_search": "搜索已保存的事实",
30+
"cmd.description.memory_remove": "按 id 删除已保存的事实",
31+
"cmd.description.memory_export": "将记忆导出为 markdown 文件",
3032
"cmd.description.help": "帮助",
3133

3234
"callback.unknown_command": "未知命令",
@@ -117,6 +119,21 @@ export const zh: I18nDictionary = {
117119
"tts.disabled": "🔇 已全局关闭语音回复。",
118120
"tts.failed": "⚠️ 生成语音回复失败。",
119121

122+
"personality.updated": "✅ 个性已更新。开启新会话 (/new) 以使更改生效。",
123+
"personality.label": "个性",
124+
"personality.empty_help":
125+
"个性为空。\n\n使用 /personality <文本> 设置行为规则。示例:\n /personality 始终称呼我为\"先生\"。语气正式,使用中文。\n /personality 简洁的回复,最多 3 行\n /personality 用英文回复,除非我切换语言\n\nPersonality 是关于助手应如何回复你的。使用 /memory 或让助手记住的方式来保存关于你的事实。",
126+
"personality.error": "无法访问个性设置。",
127+
128+
"show_tools.current_visible": "工具消息当前可见。",
129+
"show_tools.current_hidden": "工具消息当前已隐藏。",
130+
"show_tools.usage":
131+
"用法:\n /show_tools on — 显示工具调用(默认)\n /show_tools off — 隐藏,仅显示助手的回复",
132+
"show_tools.invalid_value": "无效值。使用 /show_tools on 或 /show_tools off。",
133+
"show_tools.now_visible": "✅ 工具消息现在可见。",
134+
"show_tools.now_hidden": "✅ 工具消息现在已隐藏。仅显示助手的回复。",
135+
"show_tools.error": "无法更新工具可见性设置。",
136+
120137
"projects.empty":
121138
"📭 未找到项目。\n\n在 OpenCode 中打开一个目录并至少创建一个会话,然后它会出现在这里。",
122139
"projects.select": "请选择一个项目:",

0 commit comments

Comments
 (0)