Fix unlocalized strings in frontend components (#8585)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
tofarr
2025-05-19 13:12:26 -06:00
committed by GitHub
parent e69d6b3ef1
commit aa55da27fa
7 changed files with 237 additions and 22 deletions

View File

@@ -119,6 +119,18 @@ function isExcludedTechnicalString(str) {
return EXCLUDED_TECHNICAL_STRINGS.includes(str);
}
function isLikelyCode(str) {
// A string with no spaces and at least one underscore or colon is likely a code.
// (e.g.: "browser_interactive" or "error:")
if (str.includes(" ")) {
return false
}
if (str.includes(":") || str.includes("_")){
return true
}
return false
}
function isCommonDevelopmentString(str) {
// Technical patterns that are definitely not UI strings
const technicalPatterns = [
@@ -385,6 +397,11 @@ function isLikelyUserFacingText(str) {
return false;
}
// Check if it looks like a code rather than a key
if (isLikelyCode(str)) {
return false
}
// Check if it's a raw translation key that should be wrapped in t()
if (isRawTranslationKey(str)) {
return true;

View File

@@ -11,18 +11,19 @@ import {
FinishAction,
} from "#/types/core/actions";
import { getDefaultEventContent, MAX_CONTENT_LENGTH } from "./shared";
import i18n from "#/i18n";
const getRiskText = (risk: ActionSecurityRisk) => {
switch (risk) {
case ActionSecurityRisk.LOW:
return "Low Risk";
return i18n.t("SECURITY$LOW_RISK");
case ActionSecurityRisk.MEDIUM:
return "Medium Risk";
return i18n.t("SECURITY$MEDIUM_RISK");
case ActionSecurityRisk.HIGH:
return "High Risk";
return i18n.t("SECURITY$HIGH_RISK");
case ActionSecurityRisk.UNKNOWN:
default:
return "Unknown Risk";
return i18n.t("SECURITY$UNKNOWN_RISK");
}
};
@@ -81,15 +82,14 @@ const getFinishActionContent = (event: FinishAction): string => {
switch (event.args.task_completed) {
case "success":
content +=
"\n\n\nI believe that the task was **completed successfully**.";
content += `\n\n\n${i18n.t("FINISH$TASK_COMPLETED_SUCCESSFULLY")}`;
break;
case "failure":
content += "\n\n\nI believe that the task was **not completed**.";
content += `\n\n\n${i18n.t("FINISH$TASK_NOT_COMPLETED")}`;
break;
case "partial":
default:
content += "\n\n\nI believe that the task was **completed partially**.";
content += `\n\n\n${i18n.t("FINISH$TASK_COMPLETED_PARTIALLY")}`;
break;
}

View File

@@ -6,6 +6,7 @@ import { MonoComponent } from "../mono-component";
import { PathComponent } from "../path-component";
import { getActionContent } from "./get-action-content";
import { getObservationContent } from "./get-observation-content";
import i18n from "#/i18n";
const hasPathProperty = (
obj: Record<string, unknown>,
@@ -64,7 +65,7 @@ export const getEventContent = (
}
return {
title: title ?? "Unknown event",
details: details ?? "Unknown event",
title: title ?? i18n.t("EVENT$UNKNOWN_EVENT"),
details: details ?? i18n.t("EVENT$UNKNOWN_EVENT"),
};
};

View File

@@ -9,6 +9,7 @@ import {
} from "#/types/core/observations";
import { getObservationResult } from "./get-observation-result";
import { getDefaultEventContent, MAX_CONTENT_LENGTH } from "./shared";
import i18n from "#/i18n";
const getReadObservationContent = (event: ReadObservation): string =>
`\`\`\`\n${event.content}\n\`\`\``;
@@ -20,7 +21,7 @@ const getCommandObservationContent = (
if (content.length > MAX_CONTENT_LENGTH) {
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...`;
}
return `Output:\n\`\`\`sh\n${content.trim() || "[Command finished execution with no output]"}\n\`\`\``;
return `Output:\n\`\`\`sh\n${content.trim() || i18n.t("OBSERVATION$COMMAND_NO_OUTPUT")}\n\`\`\``;
};
const getEditObservationContent = (
@@ -50,7 +51,7 @@ const getMcpObservationContent = (event: OpenHandsObservation): string => {
if (content.length > MAX_CONTENT_LENGTH) {
content = `${content.slice(0, MAX_CONTENT_LENGTH)}...`;
}
return `**Output:**\n\`\`\`\n${content.trim() || "[MCP Tool finished execution with no output]"}\n\`\`\``;
return `**Output:**\n\`\`\`\n${content.trim() || i18n.t("OBSERVATION$MCP_NO_OUTPUT")}\n\`\`\``;
};
const getRecallObservationContent = (event: RecallObservation): string => {

View File

@@ -40,7 +40,7 @@ export function TaskCard({ task }: TaskCardProps) {
const handleLaunchConversation = () => {
const repo = getRepo(task.repo, task.git_provider);
setOptimisticUserMessage("Addressing task...");
setOptimisticUserMessage(t("TASK$ADDRESSING_TASK"));
return createConversation({
selectedRepository: repo,

View File

@@ -1,5 +1,18 @@
// this file generate by script, don't modify it manually!!!
export enum I18nKey {
SECURITY$LOW_RISK = "SECURITY$LOW_RISK",
SECURITY$MEDIUM_RISK = "SECURITY$MEDIUM_RISK",
SECURITY$HIGH_RISK = "SECURITY$HIGH_RISK",
SECURITY$UNKNOWN_RISK = "SECURITY$UNKNOWN_RISK",
FINISH$TASK_COMPLETED_SUCCESSFULLY = "FINISH$TASK_COMPLETED_SUCCESSFULLY",
FINISH$TASK_NOT_COMPLETED = "FINISH$TASK_NOT_COMPLETED",
FINISH$TASK_COMPLETED_PARTIALLY = "FINISH$TASK_COMPLETED_PARTIALLY",
EVENT$UNKNOWN_EVENT = "EVENT$UNKNOWN_EVENT",
OBSERVATION$COMMAND_NO_OUTPUT = "OBSERVATION$COMMAND_NO_OUTPUT",
OBSERVATION$MCP_NO_OUTPUT = "OBSERVATION$MCP_NO_OUTPUT",
OBSERVATION$ERROR_PREFIX = "OBSERVATION$ERROR_PREFIX",
TASK$ADDRESSING_TASK = "TASK$ADDRESSING_TASK",
CHAT_INTERFACE$AGENT_ERROR_MESSAGE = "CHAT_INTERFACE$AGENT_ERROR_MESSAGE",
SECRETS$SECRET_VALUE_REQUIRED = "SECRETS$SECRET_VALUE_REQUIRED",
SECRETS$ADD_SECRET = "SECRETS$ADD_SECRET",
SECRETS$EDIT_SECRET = "SECRETS$EDIT_SECRET",
@@ -279,7 +292,6 @@ export enum I18nKey {
CHAT_INTERFACE$AGENT_STOPPED_MESSAGE = "CHAT_INTERFACE$AGENT_STOPPED_MESSAGE",
CHAT_INTERFACE$AGENT_FINISHED_MESSAGE = "CHAT_INTERFACE$AGENT_FINISHED_MESSAGE",
CHAT_INTERFACE$AGENT_REJECTED_MESSAGE = "CHAT_INTERFACE$AGENT_REJECTED_MESSAGE",
CHAT_INTERFACE$AGENT_ERROR_MESSAGE = "CHAT_INTERFACE$AGENT_ERROR_MESSAGE",
CHAT_INTERFACE$AGENT_AWAITING_USER_CONFIRMATION_MESSAGE = "CHAT_INTERFACE$AGENT_AWAITING_USER_CONFIRMATION_MESSAGE",
CHAT_INTERFACE$AGENT_ACTION_USER_CONFIRMED_MESSAGE = "CHAT_INTERFACE$AGENT_ACTION_USER_CONFIRMED_MESSAGE",
CHAT_INTERFACE$AGENT_ACTION_USER_REJECTED_MESSAGE = "CHAT_INTERFACE$AGENT_ACTION_USER_REJECTED_MESSAGE",
@@ -529,12 +541,4 @@ export enum I18nKey {
TIPS$API_USAGE = "TIPS$API_USAGE",
TIPS$LEARN_MORE = "TIPS$LEARN_MORE",
TIPS$PROTIP = "TIPS$PROTIP",
INCREASE_TEST_COVERAGE = "INCREASE_TEST_COVERAGE",
AUTO_MERGE_PRS = "AUTO_MERGE_PRS",
FIX_README = "FIX_README",
CLEAN_DEPENDENCIES = "CLEAN_DEPENDENCIES",
BILLING$YOUVE_GOT_50 = "BILLING$YOUVE_GOT_50",
BILLING$CLAIM_YOUR_50 = "BILLING$CLAIM_YOUR_50",
SETTINGS$RUNTIME_OPTION_1X = "SETTINGS$RUNTIME_OPTION_1X",
SETTINGS$RUNTIME_OPTION_2X = "SETTINGS$RUNTIME_OPTION_2X",
}

View File

@@ -1,4 +1,196 @@
{
"SECURITY$LOW_RISK": {
"en": "Low Risk",
"ja": "低リスク",
"zh-CN": "低风险",
"zh-TW": "低風險",
"ko-KR": "낮은 위험",
"no": "Lav risiko",
"it": "Rischio basso",
"pt": "Baixo risco",
"es": "Riesgo bajo",
"ar": "مخاطر منخفضة",
"fr": "Risque faible",
"tr": "Düşük risk",
"de": "Geringes Risiko",
"uk": "Низький ризик"
},
"SECURITY$MEDIUM_RISK": {
"en": "Medium Risk",
"ja": "中リスク",
"zh-CN": "中等风险",
"zh-TW": "中等風險",
"ko-KR": "중간 위험",
"no": "Middels risiko",
"it": "Rischio medio",
"pt": "Risco médio",
"es": "Riesgo medio",
"ar": "مخاطر متوسطة",
"fr": "Risque moyen",
"tr": "Orta risk",
"de": "Mittleres Risiko",
"uk": "Середній ризик"
},
"SECURITY$HIGH_RISK": {
"en": "High Risk",
"ja": "高リスク",
"zh-CN": "高风险",
"zh-TW": "高風險",
"ko-KR": "높은 위험",
"no": "Høy risiko",
"it": "Rischio alto",
"pt": "Alto risco",
"es": "Riesgo alto",
"ar": "مخاطر عالية",
"fr": "Risque élevé",
"tr": "Yüksek risk",
"de": "Hohes Risiko",
"uk": "Високий ризик"
},
"SECURITY$UNKNOWN_RISK": {
"en": "Unknown Risk",
"ja": "不明なリスク",
"zh-CN": "未知风险",
"zh-TW": "未知風險",
"ko-KR": "알 수 없는 위험",
"no": "Ukjent risiko",
"it": "Rischio sconosciuto",
"pt": "Risco desconhecido",
"es": "Riesgo desconocido",
"ar": "مخاطر غير معروفة",
"fr": "Risque inconnu",
"tr": "Bilinmeyen risk",
"de": "Unbekanntes Risiko",
"uk": "Невідомий ризик"
},
"FINISH$TASK_COMPLETED_SUCCESSFULLY": {
"en": "I believe that the task was **completed successfully**.",
"ja": "タスクは**正常に完了**したと思います。",
"zh-CN": "我相信任务已**成功完成**。",
"zh-TW": "我相信任務已**成功完成**。",
"ko-KR": "작업이 **성공적으로 완료**되었다고 생각합니다.",
"no": "Jeg tror at oppgaven ble **fullført vellykket**.",
"it": "Ritengo che l'attività sia stata **completata con successo**.",
"pt": "Acredito que a tarefa foi **concluída com sucesso**.",
"es": "Creo que la tarea se ha **completado con éxito**.",
"ar": "أعتقد أن المهمة قد **اكتملت بنجاح**.",
"fr": "Je pense que la tâche a été **accomplie avec succès**.",
"tr": "Görevin **başarıyla tamamlandığına** inanıyorum.",
"de": "Ich glaube, dass die Aufgabe **erfolgreich abgeschlossen** wurde.",
"uk": "Я вважаю, що завдання було **успішно виконано**."
},
"FINISH$TASK_NOT_COMPLETED": {
"en": "I believe that the task was **not completed**.",
"ja": "タスクは**完了していない**と思います。",
"zh-CN": "我相信任务**未完成**。",
"zh-TW": "我相信任務**未完成**。",
"ko-KR": "작업이 **완료되지 않았다**고 생각합니다.",
"no": "Jeg tror at oppgaven **ikke ble fullført**.",
"it": "Ritengo che l'attività **non sia stata completata**.",
"pt": "Acredito que a tarefa **não foi concluída**.",
"es": "Creo que la tarea **no se ha completado**.",
"ar": "أعتقد أن المهمة **لم تكتمل**.",
"fr": "Je pense que la tâche **n'a pas été accomplie**.",
"tr": "Görevin **tamamlanmadığına** inanıyorum.",
"de": "Ich glaube, dass die Aufgabe **nicht abgeschlossen** wurde.",
"uk": "Я вважаю, що завдання **не було виконано**."
},
"FINISH$TASK_COMPLETED_PARTIALLY": {
"en": "I believe that the task was **completed partially**.",
"ja": "タスクは**部分的に完了**したと思います。",
"zh-CN": "我相信任务已**部分完成**。",
"zh-TW": "我相信任務已**部分完成**。",
"ko-KR": "작업이 **부분적으로 완료**되었다고 생각합니다.",
"no": "Jeg tror at oppgaven ble **delvis fullført**.",
"it": "Ritengo che l'attività sia stata **completata parzialmente**.",
"pt": "Acredito que a tarefa foi **concluída parcialmente**.",
"es": "Creo que la tarea se ha **completado parcialmente**.",
"ar": "أعتقد أن المهمة قد **اكتملت جزئيًا**.",
"fr": "Je pense que la tâche a été **partiellement accomplie**.",
"tr": "Görevin **kısmen tamamlandığına** inanıyorum.",
"de": "Ich glaube, dass die Aufgabe **teilweise abgeschlossen** wurde.",
"uk": "Я вважаю, що завдання було **частково виконано**."
},
"EVENT$UNKNOWN_EVENT": {
"en": "Unknown event",
"ja": "不明なイベント",
"zh-CN": "未知事件",
"zh-TW": "未知事件",
"ko-KR": "알 수 없는 이벤트",
"no": "Ukjent hendelse",
"it": "Evento sconosciuto",
"pt": "Evento desconhecido",
"es": "Evento desconocido",
"ar": "حدث غير معروف",
"fr": "Événement inconnu",
"tr": "Bilinmeyen olay",
"de": "Unbekanntes Ereignis",
"uk": "Невідома подія"
},
"OBSERVATION$COMMAND_NO_OUTPUT": {
"en": "[Command finished execution with no output]",
"ja": "[コマンドは出力なしで実行を終了しました]",
"zh-CN": "[命令执行完毕,没有输出]",
"zh-TW": "[命令執行完畢,沒有輸出]",
"ko-KR": "[명령이 출력 없이 실행을 완료했습니다]",
"no": "[Kommandoen ble fullført uten utdata]",
"it": "[Il comando ha terminato l'esecuzione senza output]",
"pt": "[Comando terminou a execução sem saída]",
"es": "[El comando terminó la ejecución sin salida]",
"ar": "[انتهى تنفيذ الأمر بدون مخرجات]",
"fr": "[La commande s'est terminée sans sortie]",
"tr": "[Komut çıktı olmadan yürütmeyi tamamladı]",
"de": "[Befehl wurde ohne Ausgabe ausgeführt]",
"uk": "[Команда завершила виконання без виводу]"
},
"OBSERVATION$MCP_NO_OUTPUT": {
"en": "[MCP Tool finished execution with no output]",
"ja": "[MCPツールは出力なしで実行を終了しました]",
"zh-CN": "[MCP工具执行完毕没有输出]",
"zh-TW": "[MCP工具執行完畢沒有輸出]",
"ko-KR": "[MCP 도구가 출력 없이 실행을 완료했습니다]",
"no": "[MCP-verktøyet ble fullført uten utdata]",
"it": "[Lo strumento MCP ha terminato l'esecuzione senza output]",
"pt": "[Ferramenta MCP terminou a execução sem saída]",
"es": "[La herramienta MCP terminó la ejecución sin salida]",
"ar": "[انتهى تنفيذ أداة MCP بدون مخرجات]",
"fr": "[L'outil MCP s'est terminé sans sortie]",
"tr": "[MCP Aracı çıktı olmadan yürütmeyi tamamladı]",
"de": "[MCP-Tool wurde ohne Ausgabe ausgeführt]",
"uk": "[Інструмент MCP завершив виконання без виводу]"
},
"OBSERVATION$ERROR_PREFIX": {
"en": "error:",
"ja": "エラー:",
"zh-CN": "错误:",
"zh-TW": "錯誤:",
"ko-KR": "오류:",
"no": "feil:",
"it": "errore:",
"pt": "erro:",
"es": "error:",
"ar": "خطأ:",
"fr": "erreur:",
"tr": "hata:",
"de": "Fehler:",
"uk": "помилка:"
},
"TASK$ADDRESSING_TASK": {
"en": "Addressing task...",
"ja": "タスクに対応中...",
"zh-CN": "正在处理任务...",
"zh-TW": "正在處理任務...",
"ko-KR": "작업 처리 중...",
"no": "Adresserer oppgave...",
"it": "Affrontando l'attività...",
"pt": "Abordando tarefa...",
"es": "Abordando tarea...",
"ar": "معالجة المهمة...",
"fr": "Traitement de la tâche...",
"tr": "Görev ele alınıyor...",
"de": "Aufgabe wird bearbeitet...",
"uk": "Вирішення завдання..."
},
"SECRETS$SECRET_VALUE_REQUIRED": {
"en": "Secret value is required",
"ja": "シークレット値は必須です",