diff --git a/internal/httpapi/diagnostic.go b/internal/httpapi/diagnostic.go
index e1e5820..980b7bf 100644
--- a/internal/httpapi/diagnostic.go
+++ b/internal/httpapi/diagnostic.go
@@ -13,6 +13,7 @@ type createDiagnosticSessionRequest struct {
TargetRole string `json:"target_role"`
Stack []string `json:"stack"`
InterviewTimeline string `json:"interview_timeline"`
+ Lang string `json:"lang"`
}
type submitDiagnosticAnswerRequest struct {
@@ -27,11 +28,19 @@ func (h Handler) createDiagnosticSession(w http.ResponseWriter, r *http.Request)
return
}
+ lang := req.Lang
+ if lang == "" {
+ lang = r.Header.Get("X-Lang")
+ }
+ if lang == "" {
+ lang = "en"
+ }
session, err := h.diagnostic.CreateSession(r.Context(), interview.CreateSessionInput{
UserID: req.UserID,
TargetRole: req.TargetRole,
Stack: req.Stack,
InterviewTimeline: req.InterviewTimeline,
+ Lang: lang,
})
if err != nil {
writeError(w, http.StatusBadRequest, err.Error())
diff --git a/internal/interview/catalog.go b/internal/interview/catalog.go
index 8475184..055df5f 100644
--- a/internal/interview/catalog.go
+++ b/internal/interview/catalog.go
@@ -2,28 +2,49 @@ package interview
import "tutor/internal/workflows"
-func BackendDeveloperQuestions() []Question {
- return []Question{
+var questionPrompts = map[string]map[string]string{
+ "ko": {
+ "backend-http-idempotency": "HTTP 메서드가 멱등성을 가지려면 어떤 조건이 필요하며, 재시도 시 왜 중요한가요?",
+ "backend-db-index-tradeoff": "데이터베이스 인덱스를 추가하면 API가 어떻게 개선되며, 어떤 트레이드오프가 발생할 수 있나요?",
+ "backend-cache-invalidation": "API 응답을 캐싱할지 어떻게 결정하며, 오래된 데이터는 어떻게 처리하나요?",
+ },
+ "en": {
+ "backend-http-idempotency": "What makes an HTTP method idempotent, and why does that matter for retries?",
+ "backend-db-index-tradeoff": "When would adding a database index improve an API, and what tradeoffs can it introduce?",
+ "backend-cache-invalidation": "How would you decide whether to cache an API response, and how would you handle stale data?",
+ },
+}
+
+func BackendDeveloperQuestions(lang string) []Question {
+ if lang == "" {
+ lang = "en"
+ }
+ base := []Question{
{
- ID: "backend-http-idempotency",
- Prompt: "What makes an HTTP method idempotent, and why does that matter for retries?",
+ ID: "backend-http-idempotency",
Concepts: []workflows.ConceptRef{
{ID: "http-idempotency", Label: "HTTP idempotency", Track: BackendDeveloperTrack},
},
},
{
- ID: "backend-db-index-tradeoff",
- Prompt: "When would adding a database index improve an API, and what tradeoffs can it introduce?",
+ ID: "backend-db-index-tradeoff",
Concepts: []workflows.ConceptRef{
{ID: "database-indexes", Label: "Database indexes", Track: BackendDeveloperTrack},
},
},
{
- ID: "backend-cache-invalidation",
- Prompt: "How would you decide whether to cache an API response, and how would you handle stale data?",
+ ID: "backend-cache-invalidation",
Concepts: []workflows.ConceptRef{
{ID: "cache-invalidation", Label: "Cache invalidation", Track: BackendDeveloperTrack},
},
},
}
+ for i := range base {
+ if p, ok := questionPrompts[lang][base[i].ID]; ok {
+ base[i].Prompt = p
+ } else if p, ok := questionPrompts["en"][base[i].ID]; ok {
+ base[i].Prompt = p
+ }
+ }
+ return base
}
diff --git a/internal/interview/service.go b/internal/interview/service.go
index 067b998..45f785f 100644
--- a/internal/interview/service.go
+++ b/internal/interview/service.go
@@ -44,7 +44,7 @@ func (s *Service) CreateSession(_ context.Context, input CreateSessionInput) (Se
TargetRole: input.TargetRole,
Stack: append([]string(nil), input.Stack...),
InterviewTimeline: input.InterviewTimeline,
- Questions: BackendDeveloperQuestions(),
+ Questions: BackendDeveloperQuestions(input.Lang),
CreatedAt: time.Now().UTC(),
}
if s.memory != nil {
diff --git a/internal/interview/types.go b/internal/interview/types.go
index fe16d5a..e276a4e 100644
--- a/internal/interview/types.go
+++ b/internal/interview/types.go
@@ -47,6 +47,7 @@ type CreateSessionInput struct {
TargetRole string
Stack []string
InterviewTimeline string
+ Lang string
}
type SubmitAnswerInput struct {
diff --git a/internal/webapp/static/app.js b/internal/webapp/static/app.js
index 4ab75b3..e9ba8c4 100644
--- a/internal/webapp/static/app.js
+++ b/internal/webapp/static/app.js
@@ -89,6 +89,7 @@ els.sessionForm.addEventListener("submit", async (event) => {
.map((item) => item.trim())
.filter(Boolean),
interview_timeline: value("#timeline"),
+ lang: localStorage.getItem("tutor_lang") || document.documentElement.lang || "ko",
};
try {
@@ -216,7 +217,7 @@ function renderSession() {
"aria-pressed",
String(state.selectedQuestion?.id === question.id)
);
- button.innerHTML = `${escapeHTML(question.id)}${escapeHTML(question.prompt)}`;
+ button.innerHTML = `${escapeHTML(question.id)}${escapeHTML(tq(question.id) || question.prompt)}`;
button.addEventListener("click", () => {
state.selectedQuestion = question;
els.answerText.value = "";
@@ -465,7 +466,8 @@ els.logoutButton.addEventListener("click", () => {
async function request(url, options = {}) {
const token = localStorage.getItem("tutor_token");
- const headers = { "Content-Type": "application/json" };
+ const lang = localStorage.getItem("tutor_lang") || document.documentElement.lang || "ko";
+ const headers = { "Content-Type": "application/json", "X-Lang": lang };
if (token) headers["Authorization"] = `Bearer ${token}`;
const response = await fetch(url, { headers, ...options });
const body = await response.json();
diff --git a/internal/webapp/static/i18n.js b/internal/webapp/static/i18n.js
index ecc3e65..8c1b116 100644
--- a/internal/webapp/static/i18n.js
+++ b/internal/webapp/static/i18n.js
@@ -181,6 +181,33 @@ window.t = function (key, ...args) {
return typeof text === "function" ? text(...args) : text;
}
+var questionTexts = {
+ ko: {
+ "backend-http-idempotency":
+ "HTTP 메서드가 멱등성을 가지려면 어떤 조건이 필요하며, 재시도 시 왜 중요한가요?",
+ "backend-db-index-tradeoff":
+ "데이터베이스 인덱스를 추가하면 API가 어떻게 개선되며, 어떤 트레이드오프가 발생할 수 있나요?",
+ "backend-cache-invalidation":
+ "API 응답을 캐싱할지 어떻게 결정하며, 오래된 데이터는 어떻게 처리하나요?",
+ },
+ en: {
+ "backend-http-idempotency":
+ "What makes an HTTP method idempotent, and why does that matter for retries?",
+ "backend-db-index-tradeoff":
+ "When would adding a database index improve an API, and what tradeoffs can it introduce?",
+ "backend-cache-invalidation":
+ "How would you decide whether to cache an API response, and how would you handle stale data?",
+ },
+};
+
+window.tq = function (id) {
+ const lang =
+ localStorage.getItem("tutor_lang") ||
+ document.documentElement.lang ||
+ "ko";
+ return questionTexts[lang]?.[id] ?? questionTexts["en"]?.[id] ?? "";
+};
+
window.updateStaticText = function () {
const lang =
localStorage.getItem("tutor_lang") ||