Spaces:
Sleeping
Sleeping
KevinIsInCoding Claude Sonnet 4.6 commited on
FEAT: Add multi-language support (EN / ZH / ES / HI) (#2)
Browse files- New translations.py with UI string dicts and language directives for all 4 languages
- Language dropdown in UI header — switching resets the session in the chosen language
- Intake and research agents prepend a language directive to their system prompts so
Claude responds entirely in the selected language (Chinese, Spanish, Hindi, English)
- PatientProfile.lang field carries language through to the research agent
- All hardcoded English UI strings replaced with UI[lang] lookups
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py +69 -26
- clinical_trials_guru.py +3 -0
- translations.py +82 -0
app.py
CHANGED
|
@@ -20,20 +20,22 @@ from clinical_trials_guru import (
|
|
| 20 |
geocode_zip,
|
| 21 |
search_trials_api,
|
| 22 |
)
|
|
|
|
| 23 |
|
| 24 |
load_dotenv()
|
| 25 |
|
| 26 |
|
| 27 |
def _intake_turn(
|
| 28 |
-
user_text: str, messages: list
|
| 29 |
) -> tuple[str, list, PatientProfile | None]:
|
| 30 |
today = datetime.date.today().strftime("%B %d, %Y")
|
| 31 |
messages = messages + [{"role": "user", "content": user_text}]
|
| 32 |
client = anthropic.Anthropic()
|
|
|
|
| 33 |
response = client.messages.create(
|
| 34 |
model=INTAKE_MODEL,
|
| 35 |
max_tokens=1024,
|
| 36 |
-
system=
|
| 37 |
tools=[SUBMIT_PROFILE_TOOL],
|
| 38 |
messages=messages,
|
| 39 |
)
|
|
@@ -63,13 +65,15 @@ def _intake_turn(
|
|
| 63 |
radius_miles=data.get("radius_miles", 100),
|
| 64 |
phases=data.get("phases") or [],
|
| 65 |
include_eap=data.get("include_eap", False),
|
|
|
|
| 66 |
)
|
| 67 |
-
return text or "
|
| 68 |
|
| 69 |
return text, messages, None
|
| 70 |
|
| 71 |
|
| 72 |
def _run_research(profile: PatientProfile) -> str:
|
|
|
|
| 73 |
client = anthropic.Anthropic()
|
| 74 |
messages: list[anthropic.types.MessageParam] = [
|
| 75 |
{
|
|
@@ -84,7 +88,7 @@ def _run_research(profile: PatientProfile) -> str:
|
|
| 84 |
response = client.messages.create(
|
| 85 |
model=RESEARCH_MODEL,
|
| 86 |
max_tokens=8096,
|
| 87 |
-
system=RESEARCH_SYSTEM,
|
| 88 |
tools=[SEARCH_TRIALS_TOOL],
|
| 89 |
messages=messages,
|
| 90 |
)
|
|
@@ -92,7 +96,7 @@ def _run_research(profile: PatientProfile) -> str:
|
|
| 92 |
if response.stop_reason == "end_turn":
|
| 93 |
return next(
|
| 94 |
(b.text for b in response.content if b.type == "text"),
|
| 95 |
-
"
|
| 96 |
)
|
| 97 |
tool_results: list[anthropic.types.ToolResultBlockParam] = []
|
| 98 |
for block in response.content:
|
|
@@ -112,7 +116,7 @@ def _run_research(profile: PatientProfile) -> str:
|
|
| 112 |
content = json.dumps(ranked)
|
| 113 |
is_error = False
|
| 114 |
except Exception as exc:
|
| 115 |
-
content =
|
| 116 |
is_error = True
|
| 117 |
tool_results.append(
|
| 118 |
{
|
|
@@ -125,11 +129,9 @@ def _run_research(profile: PatientProfile) -> str:
|
|
| 125 |
messages.append({"role": "user", "content": tool_results})
|
| 126 |
|
| 127 |
|
| 128 |
-
def initialize():
|
| 129 |
-
text, msgs, _ = _intake_turn("Please begin.", [])
|
| 130 |
chat = [{"role": "assistant", "content": text}]
|
| 131 |
-
# Strip the seed "Please begin." turn so subsequent user messages append cleanly.
|
| 132 |
-
# msgs already includes both the seed user turn and the assistant turn; keep it.
|
| 133 |
return chat, msgs, None, "intake"
|
| 134 |
|
| 135 |
|
|
@@ -139,27 +141,27 @@ def respond(
|
|
| 139 |
intake_msgs: list,
|
| 140 |
profile,
|
| 141 |
phase: str,
|
|
|
|
| 142 |
) -> Generator:
|
| 143 |
if not user_msg.strip() or phase == "done":
|
| 144 |
yield chat_history, intake_msgs, profile, phase, gr.update(), gr.update()
|
| 145 |
return
|
| 146 |
|
|
|
|
| 147 |
chat_history = chat_history + [{"role": "user", "content": user_msg}]
|
| 148 |
yield chat_history, intake_msgs, profile, phase, gr.update(value=""), gr.update()
|
| 149 |
|
| 150 |
-
assistant_text, updated_msgs, new_profile = _intake_turn(user_msg, intake_msgs)
|
| 151 |
|
| 152 |
if new_profile:
|
| 153 |
-
status = (assistant_text + "\n\n" if assistant_text else "") +
|
| 154 |
-
"*Searching ClinicalTrials.gov — this may take a minute…*"
|
| 155 |
-
)
|
| 156 |
chat_history = chat_history + [{"role": "assistant", "content": status}]
|
| 157 |
yield (
|
| 158 |
chat_history,
|
| 159 |
updated_msgs,
|
| 160 |
new_profile,
|
| 161 |
"researching",
|
| 162 |
-
gr.update(interactive=False, placeholder="
|
| 163 |
gr.update(visible=False),
|
| 164 |
)
|
| 165 |
|
|
@@ -170,7 +172,7 @@ def respond(
|
|
| 170 |
updated_msgs,
|
| 171 |
new_profile,
|
| 172 |
"done",
|
| 173 |
-
gr.update(interactive=False, placeholder="
|
| 174 |
gr.update(visible=True),
|
| 175 |
)
|
| 176 |
else:
|
|
@@ -185,43 +187,84 @@ def respond(
|
|
| 185 |
)
|
| 186 |
|
| 187 |
|
| 188 |
-
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
|
| 191 |
chatbot = gr.Chatbot(height=550, show_label=False)
|
| 192 |
with gr.Row():
|
| 193 |
msg_box = gr.Textbox(
|
| 194 |
-
placeholder="
|
| 195 |
show_label=False,
|
| 196 |
scale=9,
|
| 197 |
autofocus=True,
|
| 198 |
)
|
| 199 |
-
send_btn = gr.Button("
|
| 200 |
-
new_search_btn = gr.Button("
|
| 201 |
|
| 202 |
# State
|
| 203 |
intake_msgs_state = gr.State([])
|
| 204 |
profile_state = gr.State(None)
|
| 205 |
phase_state = gr.State("intake")
|
|
|
|
| 206 |
|
| 207 |
-
|
|
|
|
| 208 |
|
| 209 |
demo.load(
|
| 210 |
-
initialize,
|
| 211 |
outputs=[chatbot, intake_msgs_state, profile_state, phase_state],
|
| 212 |
)
|
| 213 |
|
| 214 |
-
msg_box.submit(respond,
|
| 215 |
-
send_btn.click(respond,
|
| 216 |
|
| 217 |
new_search_btn.click(
|
| 218 |
initialize,
|
|
|
|
| 219 |
outputs=[chatbot, intake_msgs_state, profile_state, phase_state],
|
| 220 |
).then(
|
| 221 |
-
lambda: (
|
|
|
|
|
|
|
|
|
|
|
|
|
| 222 |
outputs=[msg_box, new_search_btn],
|
| 223 |
)
|
| 224 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 225 |
|
| 226 |
if __name__ == "__main__":
|
| 227 |
demo.launch()
|
|
|
|
| 20 |
geocode_zip,
|
| 21 |
search_trials_api,
|
| 22 |
)
|
| 23 |
+
from translations import LANGUAGE_DIRECTIVE, LANGUAGES, UI
|
| 24 |
|
| 25 |
load_dotenv()
|
| 26 |
|
| 27 |
|
| 28 |
def _intake_turn(
|
| 29 |
+
user_text: str, messages: list, lang: str = "en"
|
| 30 |
) -> tuple[str, list, PatientProfile | None]:
|
| 31 |
today = datetime.date.today().strftime("%B %d, %Y")
|
| 32 |
messages = messages + [{"role": "user", "content": user_text}]
|
| 33 |
client = anthropic.Anthropic()
|
| 34 |
+
system = f"Today's date is {today}.\n\n" + LANGUAGE_DIRECTIVE[lang] + INTAKE_SYSTEM
|
| 35 |
response = client.messages.create(
|
| 36 |
model=INTAKE_MODEL,
|
| 37 |
max_tokens=1024,
|
| 38 |
+
system=system,
|
| 39 |
tools=[SUBMIT_PROFILE_TOOL],
|
| 40 |
messages=messages,
|
| 41 |
)
|
|
|
|
| 65 |
radius_miles=data.get("radius_miles", 100),
|
| 66 |
phases=data.get("phases") or [],
|
| 67 |
include_eap=data.get("include_eap", False),
|
| 68 |
+
lang=lang,
|
| 69 |
)
|
| 70 |
+
return text or UI[lang]["got_it"], messages, profile
|
| 71 |
|
| 72 |
return text, messages, None
|
| 73 |
|
| 74 |
|
| 75 |
def _run_research(profile: PatientProfile) -> str:
|
| 76 |
+
lang = profile.lang
|
| 77 |
client = anthropic.Anthropic()
|
| 78 |
messages: list[anthropic.types.MessageParam] = [
|
| 79 |
{
|
|
|
|
| 88 |
response = client.messages.create(
|
| 89 |
model=RESEARCH_MODEL,
|
| 90 |
max_tokens=8096,
|
| 91 |
+
system=LANGUAGE_DIRECTIVE[lang] + RESEARCH_SYSTEM,
|
| 92 |
tools=[SEARCH_TRIALS_TOOL],
|
| 93 |
messages=messages,
|
| 94 |
)
|
|
|
|
| 96 |
if response.stop_reason == "end_turn":
|
| 97 |
return next(
|
| 98 |
(b.text for b in response.content if b.type == "text"),
|
| 99 |
+
UI[lang]["no_analysis"],
|
| 100 |
)
|
| 101 |
tool_results: list[anthropic.types.ToolResultBlockParam] = []
|
| 102 |
for block in response.content:
|
|
|
|
| 116 |
content = json.dumps(ranked)
|
| 117 |
is_error = False
|
| 118 |
except Exception as exc:
|
| 119 |
+
content = UI[lang]["api_error"].format(exc=exc)
|
| 120 |
is_error = True
|
| 121 |
tool_results.append(
|
| 122 |
{
|
|
|
|
| 129 |
messages.append({"role": "user", "content": tool_results})
|
| 130 |
|
| 131 |
|
| 132 |
+
def initialize(lang: str = "en"):
|
| 133 |
+
text, msgs, _ = _intake_turn("Please begin.", [], lang=lang)
|
| 134 |
chat = [{"role": "assistant", "content": text}]
|
|
|
|
|
|
|
| 135 |
return chat, msgs, None, "intake"
|
| 136 |
|
| 137 |
|
|
|
|
| 141 |
intake_msgs: list,
|
| 142 |
profile,
|
| 143 |
phase: str,
|
| 144 |
+
lang: str,
|
| 145 |
) -> Generator:
|
| 146 |
if not user_msg.strip() or phase == "done":
|
| 147 |
yield chat_history, intake_msgs, profile, phase, gr.update(), gr.update()
|
| 148 |
return
|
| 149 |
|
| 150 |
+
t = UI[lang]
|
| 151 |
chat_history = chat_history + [{"role": "user", "content": user_msg}]
|
| 152 |
yield chat_history, intake_msgs, profile, phase, gr.update(value=""), gr.update()
|
| 153 |
|
| 154 |
+
assistant_text, updated_msgs, new_profile = _intake_turn(user_msg, intake_msgs, lang=lang)
|
| 155 |
|
| 156 |
if new_profile:
|
| 157 |
+
status = (assistant_text + "\n\n" if assistant_text else "") + t["status_searching"]
|
|
|
|
|
|
|
| 158 |
chat_history = chat_history + [{"role": "assistant", "content": status}]
|
| 159 |
yield (
|
| 160 |
chat_history,
|
| 161 |
updated_msgs,
|
| 162 |
new_profile,
|
| 163 |
"researching",
|
| 164 |
+
gr.update(interactive=False, placeholder=t["searching"]),
|
| 165 |
gr.update(visible=False),
|
| 166 |
)
|
| 167 |
|
|
|
|
| 172 |
updated_msgs,
|
| 173 |
new_profile,
|
| 174 |
"done",
|
| 175 |
+
gr.update(interactive=False, placeholder=t["search_complete"]),
|
| 176 |
gr.update(visible=True),
|
| 177 |
)
|
| 178 |
else:
|
|
|
|
| 187 |
)
|
| 188 |
|
| 189 |
|
| 190 |
+
def change_language(lang: str):
|
| 191 |
+
t = UI[lang]
|
| 192 |
+
chat, msgs, _, phase = initialize(lang)
|
| 193 |
+
return (
|
| 194 |
+
chat, msgs, None, phase,
|
| 195 |
+
gr.update(value=t["heading"]),
|
| 196 |
+
gr.update(placeholder=t["placeholder"], interactive=True),
|
| 197 |
+
gr.update(value=t["send"]),
|
| 198 |
+
gr.update(value=t["new_search"], visible=False),
|
| 199 |
+
lang,
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
with gr.Blocks(title=UI["en"]["page_title"]) as demo:
|
| 204 |
+
heading_md = gr.Markdown(UI["en"]["heading"])
|
| 205 |
+
|
| 206 |
+
with gr.Row():
|
| 207 |
+
gr.Markdown("") # spacer
|
| 208 |
+
lang_dropdown = gr.Dropdown(
|
| 209 |
+
choices=[(label, code) for code, label in LANGUAGES.items()],
|
| 210 |
+
value="en",
|
| 211 |
+
show_label=False,
|
| 212 |
+
scale=1,
|
| 213 |
+
min_width=140,
|
| 214 |
+
container=False,
|
| 215 |
+
)
|
| 216 |
|
| 217 |
chatbot = gr.Chatbot(height=550, show_label=False)
|
| 218 |
with gr.Row():
|
| 219 |
msg_box = gr.Textbox(
|
| 220 |
+
placeholder=UI["en"]["placeholder"],
|
| 221 |
show_label=False,
|
| 222 |
scale=9,
|
| 223 |
autofocus=True,
|
| 224 |
)
|
| 225 |
+
send_btn = gr.Button(UI["en"]["send"], scale=1, variant="primary")
|
| 226 |
+
new_search_btn = gr.Button(UI["en"]["new_search"], visible=False, variant="secondary")
|
| 227 |
|
| 228 |
# State
|
| 229 |
intake_msgs_state = gr.State([])
|
| 230 |
profile_state = gr.State(None)
|
| 231 |
phase_state = gr.State("intake")
|
| 232 |
+
lang_state = gr.State("en")
|
| 233 |
|
| 234 |
+
respond_inputs = [msg_box, chatbot, intake_msgs_state, profile_state, phase_state, lang_state]
|
| 235 |
+
respond_outputs = [chatbot, intake_msgs_state, profile_state, phase_state, msg_box, new_search_btn]
|
| 236 |
|
| 237 |
demo.load(
|
| 238 |
+
lambda: initialize("en"),
|
| 239 |
outputs=[chatbot, intake_msgs_state, profile_state, phase_state],
|
| 240 |
)
|
| 241 |
|
| 242 |
+
msg_box.submit(respond, respond_inputs, respond_outputs)
|
| 243 |
+
send_btn.click(respond, respond_inputs, respond_outputs)
|
| 244 |
|
| 245 |
new_search_btn.click(
|
| 246 |
initialize,
|
| 247 |
+
inputs=[lang_state],
|
| 248 |
outputs=[chatbot, intake_msgs_state, profile_state, phase_state],
|
| 249 |
).then(
|
| 250 |
+
lambda lang: (
|
| 251 |
+
gr.update(interactive=True, placeholder=UI[lang]["placeholder"]),
|
| 252 |
+
gr.update(visible=False),
|
| 253 |
+
),
|
| 254 |
+
inputs=[lang_state],
|
| 255 |
outputs=[msg_box, new_search_btn],
|
| 256 |
)
|
| 257 |
|
| 258 |
+
lang_dropdown.change(
|
| 259 |
+
change_language,
|
| 260 |
+
inputs=[lang_dropdown],
|
| 261 |
+
outputs=[
|
| 262 |
+
chatbot, intake_msgs_state, profile_state, phase_state,
|
| 263 |
+
heading_md, msg_box, send_btn, new_search_btn,
|
| 264 |
+
lang_state,
|
| 265 |
+
],
|
| 266 |
+
)
|
| 267 |
+
|
| 268 |
|
| 269 |
if __name__ == "__main__":
|
| 270 |
demo.launch()
|
clinical_trials_guru.py
CHANGED
|
@@ -6,6 +6,8 @@ import time
|
|
| 6 |
from dataclasses import dataclass, field
|
| 7 |
from typing import Optional, TypedDict
|
| 8 |
|
|
|
|
|
|
|
| 9 |
import anthropic
|
| 10 |
import httpx
|
| 11 |
from rich import box
|
|
@@ -231,6 +233,7 @@ class PatientProfile:
|
|
| 231 |
radius_miles: int = 100
|
| 232 |
phases: list[str] = field(default_factory=list)
|
| 233 |
include_eap: bool = False
|
|
|
|
| 234 |
|
| 235 |
def summary(self) -> str:
|
| 236 |
lines = [
|
|
|
|
| 6 |
from dataclasses import dataclass, field
|
| 7 |
from typing import Optional, TypedDict
|
| 8 |
|
| 9 |
+
from translations import LANGUAGE_DIRECTIVE
|
| 10 |
+
|
| 11 |
import anthropic
|
| 12 |
import httpx
|
| 13 |
from rich import box
|
|
|
|
| 233 |
radius_miles: int = 100
|
| 234 |
phases: list[str] = field(default_factory=list)
|
| 235 |
include_eap: bool = False
|
| 236 |
+
lang: str = "en"
|
| 237 |
|
| 238 |
def summary(self) -> str:
|
| 239 |
lines = [
|
translations.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
LANGUAGES: dict[str, str] = {
|
| 4 |
+
"en": "English",
|
| 5 |
+
"zh": "中文",
|
| 6 |
+
"es": "Español",
|
| 7 |
+
"hi": "हिन्दी",
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
UI: dict[str, dict[str, str]] = {
|
| 11 |
+
"en": {
|
| 12 |
+
"page_title": "Beacon — Clinical Trial Finder",
|
| 13 |
+
"heading": "# 🔦 Beacon — Rare Disease Clinical Trial Finder",
|
| 14 |
+
"placeholder": "Type your message and press Enter…",
|
| 15 |
+
"send": "Send",
|
| 16 |
+
"new_search": "New Search",
|
| 17 |
+
"searching": "Searching…",
|
| 18 |
+
"search_complete": "Search complete.",
|
| 19 |
+
"status_searching": "*Searching ClinicalTrials.gov — this may take a minute…*",
|
| 20 |
+
"no_analysis": "No analysis produced.",
|
| 21 |
+
"api_error": "API request failed: {exc}",
|
| 22 |
+
"got_it": "Got it — searching for trials now…",
|
| 23 |
+
},
|
| 24 |
+
"zh": {
|
| 25 |
+
"page_title": "Beacon — 临床试验查找器",
|
| 26 |
+
"heading": "# 🔦 Beacon — 罕见病临床试验查找器",
|
| 27 |
+
"placeholder": "输入消息并按回车…",
|
| 28 |
+
"send": "发送",
|
| 29 |
+
"new_search": "新搜索",
|
| 30 |
+
"searching": "搜索中…",
|
| 31 |
+
"search_complete": "搜索完成。",
|
| 32 |
+
"status_searching": "*正在搜索 ClinicalTrials.gov,请稍候…*",
|
| 33 |
+
"no_analysis": "未生成分析结果。",
|
| 34 |
+
"api_error": "API 请求失败:{exc}",
|
| 35 |
+
"got_it": "好的,正在为您搜索临床试验…",
|
| 36 |
+
},
|
| 37 |
+
"es": {
|
| 38 |
+
"page_title": "Beacon — Buscador de Ensayos Clínicos",
|
| 39 |
+
"heading": "# 🔦 Beacon — Buscador de Ensayos Clínicos para Enfermedades Raras",
|
| 40 |
+
"placeholder": "Escribe tu mensaje y presiona Enter…",
|
| 41 |
+
"send": "Enviar",
|
| 42 |
+
"new_search": "Nueva Búsqueda",
|
| 43 |
+
"searching": "Buscando…",
|
| 44 |
+
"search_complete": "Búsqueda completada.",
|
| 45 |
+
"status_searching": "*Buscando en ClinicalTrials.gov, esto puede tomar un minuto…*",
|
| 46 |
+
"no_analysis": "No se produjo ningún análisis.",
|
| 47 |
+
"api_error": "Solicitud de API fallida: {exc}",
|
| 48 |
+
"got_it": "Entendido, buscando ensayos ahora…",
|
| 49 |
+
},
|
| 50 |
+
"hi": {
|
| 51 |
+
"page_title": "Beacon — क्लिनिकल ट्रायल खोजक",
|
| 52 |
+
"heading": "# 🔦 Beacon — दुर्लभ रोग क्लिनिकल ट्रायल खोजक",
|
| 53 |
+
"placeholder": "अपना संदेश टाइप करें और Enter दबाएं…",
|
| 54 |
+
"send": "भेजें",
|
| 55 |
+
"new_search": "नई खोज",
|
| 56 |
+
"searching": "खोज रहे हैं…",
|
| 57 |
+
"search_complete": "खोज पूर्ण।",
|
| 58 |
+
"status_searching": "*ClinicalTrials.gov पर खोज हो रही है, एक मिनट लग सकता है…*",
|
| 59 |
+
"no_analysis": "कोई विश्लेषण नहीं बना।",
|
| 60 |
+
"api_error": "API अनुरोध विफल: {exc}",
|
| 61 |
+
"got_it": "समझ गया — अभी ट्रायल खोज रहे हैं…",
|
| 62 |
+
},
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
LANGUAGE_DIRECTIVE: dict[str, str] = {
|
| 66 |
+
"en": "",
|
| 67 |
+
"zh": (
|
| 68 |
+
"IMPORTANT: You must respond ONLY in Simplified Chinese (简体中文). "
|
| 69 |
+
"Every word you write to the patient must be in Chinese. "
|
| 70 |
+
"Standard medical abbreviations (ALSFRS-R, FVC, SOD1, etc.) may remain in their original form.\n\n"
|
| 71 |
+
),
|
| 72 |
+
"es": (
|
| 73 |
+
"IMPORTANTE: Debes responder ÚNICAMENTE en español. "
|
| 74 |
+
"Cada palabra que escribas al paciente debe estar en español. "
|
| 75 |
+
"Las abreviaturas médicas estándar (ALSFRS-R, FVC, SOD1, etc.) pueden mantenerse en su forma original.\n\n"
|
| 76 |
+
),
|
| 77 |
+
"hi": (
|
| 78 |
+
"महत्वपूर्ण: आपको केवल हिन्दी में उत्तर देना है। "
|
| 79 |
+
"रोगी को लिखी गई हर बात हिन्दी में होनी चाहिए। "
|
| 80 |
+
"मानक चिकित्सा संक्षेप (ALSFRS-R, FVC, SOD1, आदि) अपने मूल रूप में रह सकते हैं।\n\n"
|
| 81 |
+
),
|
| 82 |
+
}
|