diff --git a/internal/i18n/dict_en.go b/internal/i18n/dict_en.go index 8732e3e..c11ac5e 100644 --- a/internal/i18n/dict_en.go +++ b/internal/i18n/dict_en.go @@ -88,41 +88,49 @@ var enDict = map[string]string{ "explore.noVotes": "There are no public polls available right now.", // Ask page - "ask.scan": "Scan to ask @%s", - "ask.anonymous": "Anyone can ask anonymously", - "ask.registerOnly": "Only registered users can ask", - "ask.nextOpenAt": "Next opening: %s", - "ask.lastCloseAt": "Last closed: %s", - "weekday.0": "Sun", - "weekday.1": "Mon", - "weekday.2": "Tue", - "weekday.3": "Wed", - "weekday.4": "Thu", - "weekday.5": "Fri", - "weekday.6": "Sat", - "ask.pinned": "Pinned", - "ask.share": "Share this box", - "ask.placeholder": "Write your question here...", - "ask.chatPlaceholder": "Write your chat message here...", - "ask.addImage": "Add image", - "ask.private": "Don't publish after reply", - "ask.notify": "Notify me", - "ask.emailPlaceholder": "Enter your email address", - "ask.submit": "Send question", - "ask.chat": "Start a chat", - "ask.chatDisabled": "Chat is disabled on this box", - "ask.chatLimit": "Message limit: %d", - "ask.history": "Previously answered by @%s", - "ask.loginRequired": "Log in required", - "ask.loginRequiredChat": "You need to log in to start a chat", - "ask.goLogin": "Go to login", - "ask.needPassword": "This box requires a password", - "ask.needLogin": "The owner only allows registered users to ask questions. Please log in first.", - "ask.pleaseEmail": "Please enter your email to receive notifications", - "ask.generating": "Generating share card…", - "ask.genFailed": "Failed to generate share card. Please try again later", - "ask.networkError": "Network error", - "ask.shareFilename": "share.png", + "ask.scan": "Scan to ask @%s", + "ask.anonymous": "Anyone can ask anonymously", + "ask.registerOnly": "Only registered users can ask", + "ask.nextOpenAt": "Next opening: %s", + "ask.lastCloseAt": "Last closed: %s", + "weekday.0": "Sun", + "weekday.1": "Mon", + "weekday.2": "Tue", + "weekday.3": "Wed", + "weekday.4": "Thu", + "weekday.5": "Fri", + "weekday.6": "Sat", + "ask.pinned": "Pinned", + "ask.share": "Share this box", + "ask.placeholder": "Write your question here...", + "ask.chatPlaceholder": "Write your chat message here...", + "ask.addImage": "Add image", + "ask.private": "Don't publish after reply", + "ask.notify": "Notify me", + "ask.emailPlaceholder": "Enter your email address", + "ask.submit": "Send question", + "ask.chat": "Start a chat", + "ask.chatDisabled": "Chat is disabled on this box", + "ask.chatLimit": "Message limit: %d", + "ask.history": "Previously answered by @%s", + "askHistory.banner": "You have {n} question(s) saved on this device", + "askHistory.answeredSuffix": ", {m} of them answered", + "askHistory.expand": "View", + "askHistory.collapse": "Hide", + "askHistory.answered": "Answered", + "askHistory.pending": "Awaiting reply", + "askHistory.open": "Open", + "askHistory.note": "Saved only in this browser. Switching devices or clearing browser data will lose them.", + "ask.loginRequired": "Log in required", + "ask.loginRequiredChat": "You need to log in to start a chat", + "ask.goLogin": "Go to login", + "ask.needPassword": "This box requires a password", + "ask.needLogin": "The owner only allows registered users to ask questions. Please log in first.", + "ask.pleaseEmail": "Please enter your email to receive notifications", + "ask.generating": "Generating share card…", + "ask.genFailed": "Failed to generate share card. Please try again later", + "ask.networkError": "Network error", + "ask.shareFilename": "share.png", // Question item "qitem.answer": "Answer", @@ -846,6 +854,7 @@ var enDict = map[string]string{ "vote.err.editClosed": "Votes that are ended or archived cannot be edited", "vote.err.publishDraftOnly": "Only draft polls can be published", "vote.err.restrictedPublish": "Publishing is suspended while this account is under moderation", + "feature.stateUnavailable": "Unable to verify the status of this feature, please try again later", "vote.err.closePublishedOnly": "Only active polls can be closed", "vote.err.publishTitleRequired": "Please fill in the poll title before publishing", "vote.err.publishFormFieldsRequired": "Form polls need at least 1 field", @@ -911,6 +920,12 @@ var enDict = map[string]string{ "chat.flash.operated": "Operation successful", "image.errTooLarge": "Image is too large, maximum supported size is %d×%d pixels, please compress and re-upload", "internal.errFlash": "Internal server error, please try again later.", + "internal.errBadRequest": "Invalid request", + "error.notFoundTitle": "Page not found", + "error.notFoundDesc": "There is nothing at this address. The link may be mistyped, or the content has been removed.", + "error.internalTitle": "Something went wrong", + "error.internalDesc": "This request could not be completed. It is usually temporary — try again in a moment.", + "error.traceHint": "Please include this ID when reporting:", "internal.errServer": "Internal server error", "home.errStats": "Failed to retrieve statistics", "pixel.errPath": "Invalid request path", diff --git a/internal/i18n/dict_ja.go b/internal/i18n/dict_ja.go index 86a08e0..641e808 100644 --- a/internal/i18n/dict_ja.go +++ b/internal/i18n/dict_ja.go @@ -88,41 +88,49 @@ var jaDict = map[string]string{ "explore.noVotes": "現在、参加できる公開投票はありません。", // 質問ページ - "ask.scan": "QRコードで@%sに質問", - "ask.anonymous": "誰でも匿名で質問できます", - "ask.registerOnly": "登録ユーザーのみ質問できます", - "ask.nextOpenAt": "次回開放:%s", - "ask.lastCloseAt": "前回終了:%s", - "weekday.0": "日", - "weekday.1": "月", - "weekday.2": "火", - "weekday.3": "水", - "weekday.4": "木", - "weekday.5": "金", - "weekday.6": "土", - "ask.pinned": "ピン留め済み", - "ask.share": "質問箱を共有", - "ask.placeholder": "ここに質問を書いてください...", - "ask.chatPlaceholder": "ここにチャットメッセージを書いてください...", - "ask.addImage": "画像を追加", - "ask.private": "返信後に公開しない", - "ask.notify": "通知を受け取る", - "ask.emailPlaceholder": "メールアドレスを入力してください", - "ask.submit": "質問を送信", - "ask.chat": "チャットを開始", - "ask.chatDisabled": "この質問箱ではチャットが無効です", - "ask.chatLimit": "メッセージ上限:%d件", - "ask.history": "@%s が以前回答した質問", - "ask.loginRequired": "ログインが必要", - "ask.loginRequiredChat": "チャットを開始するにはログインが必要です", - "ask.goLogin": "ログインへ", - "ask.needPassword": "この質問箱にはパスワードが必要です", - "ask.needLogin": "質問箱の所有者は登録ユーザーのみ質問できるように設定しています。先にログインしてください。", - "ask.pleaseEmail": "通知を受け取るメールアドレスを入力してください", - "ask.generating": "共有カードを生成中…", - "ask.genFailed": "共有カードの生成に失敗しました。しばらくしてからお試しください", - "ask.networkError": "ネットワークエラー", - "ask.shareFilename": "share.png", + "ask.scan": "QRコードで@%sに質問", + "ask.anonymous": "誰でも匿名で質問できます", + "ask.registerOnly": "登録ユーザーのみ質問できます", + "ask.nextOpenAt": "次回開放:%s", + "ask.lastCloseAt": "前回終了:%s", + "weekday.0": "日", + "weekday.1": "月", + "weekday.2": "火", + "weekday.3": "水", + "weekday.4": "木", + "weekday.5": "金", + "weekday.6": "土", + "ask.pinned": "ピン留め済み", + "ask.share": "質問箱を共有", + "ask.placeholder": "ここに質問を書いてください...", + "ask.chatPlaceholder": "ここにチャットメッセージを書いてください...", + "ask.addImage": "画像を追加", + "ask.private": "返信後に公開しない", + "ask.notify": "通知を受け取る", + "ask.emailPlaceholder": "メールアドレスを入力してください", + "ask.submit": "質問を送信", + "ask.chat": "チャットを開始", + "ask.chatDisabled": "この質問箱ではチャットが無効です", + "ask.chatLimit": "メッセージ上限:%d件", + "ask.history": "@%s が以前回答した質問", + "askHistory.banner": "この端末に {n} 件の質問履歴があります", + "askHistory.answeredSuffix": "(うち {m} 件は回答済み)", + "askHistory.expand": "表示", + "askHistory.collapse": "閉じる", + "askHistory.answered": "回答済み", + "askHistory.pending": "回答待ち", + "askHistory.open": "開く", + "askHistory.note": "履歴はこのブラウザにのみ保存されます。端末の変更やブラウザデータの削除で失われます。", + "ask.loginRequired": "ログインが必要", + "ask.loginRequiredChat": "チャットを開始するにはログインが必要です", + "ask.goLogin": "ログインへ", + "ask.needPassword": "この質問箱にはパスワードが必要です", + "ask.needLogin": "質問箱の所有者は登録ユーザーのみ質問できるように設定しています。先にログインしてください。", + "ask.pleaseEmail": "通知を受け取るメールアドレスを入力してください", + "ask.generating": "共有カードを生成中…", + "ask.genFailed": "共有カードの生成に失敗しました。しばらくしてからお試しください", + "ask.networkError": "ネットワークエラー", + "ask.shareFilename": "share.png", // 質問アイテム "qitem.answer": "回答", @@ -845,6 +853,7 @@ var jaDict = map[string]string{ "vote.err.editClosed": "終了またはアーカイブされた投票は編集できません", "vote.err.publishDraftOnly": "草稿状態の投票のみ公開できます", "vote.err.restrictedPublish": "現在はプラットフォームによる措置期間中のため、投票の公開を停止しています", + "feature.stateUnavailable": "機能の状態を確認できませんでした。しばらくしてから再度お試しください", "vote.err.closePublishedOnly": "進行中の投票のみ終了できます", "vote.err.publishTitleRequired": "公開前に投票のタイトルを入力してください", "vote.err.publishFormFieldsRequired": "フォーム型投票には最低 1 つの項目が必要です", @@ -910,6 +919,12 @@ var jaDict = map[string]string{ "chat.flash.operated": "操作が完了しました", "image.errTooLarge": "画像が大きすぎます。最大%d×%dピクセルまで対応しています。圧縮して再アップロードしてください", "internal.errFlash": "サーバー内部エラーが発生しました。後でもう一度お試しください。", + "internal.errBadRequest": "リクエストの内容が正しくありません", + "error.notFoundTitle": "ページが見つかりません", + "error.notFoundDesc": "このアドレスにコンテンツはありません。リンクが間違っているか、削除された可能性があります。", + "error.internalTitle": "問題が発生しました", + "error.internalDesc": "このリクエストを完了できませんでした。多くは一時的なものです。しばらくしてから再度お試しください。", + "error.traceHint": "ご報告の際はこの番号を添えてください:", "internal.errServer": "サーバー内部エラー", "home.errStats": "統計情報の取得に失敗しました", "pixel.errPath": "不正なリクエストパス", diff --git a/internal/i18n/dict_zh_cn.go b/internal/i18n/dict_zh_cn.go index 4539a23..01ca1f7 100644 --- a/internal/i18n/dict_zh_cn.go +++ b/internal/i18n/dict_zh_cn.go @@ -88,41 +88,49 @@ var zhCNDict = map[string]string{ "explore.noVotes": "暂时没有可参与的公开投票。", // 提问页 - "ask.scan": "扫码向@%s提问", - "ask.anonymous": "谁都可以以匿名的形式提问", - "ask.registerOnly": "仅允许注册用户向TA提问", - "ask.nextOpenAt": "下次开放时间:%s", - "ask.lastCloseAt": "上次关闭时间:%s", - "weekday.0": "周日", - "weekday.1": "周一", - "weekday.2": "周二", - "weekday.3": "周三", - "weekday.4": "周四", - "weekday.5": "周五", - "weekday.6": "周六", - "ask.pinned": "已置顶", - "ask.share": "分享提问箱", - "ask.placeholder": "在此处撰写你的问题...", - "ask.chatPlaceholder": "在此处撰写你的私聊消息...", - "ask.addImage": "添加图片", - "ask.private": "回复后不公开", - "ask.notify": "接收通知", - "ask.emailPlaceholder": "请输入你的邮箱地址", - "ask.submit": "发送提问", - "ask.chat": "发起私聊", - "ask.chatDisabled": "该提问箱未开启私聊", - "ask.chatLimit": "消息上限:%d条", - "ask.history": "@%s 以前回答过的问题", - "ask.loginRequired": "需要登录", - "ask.loginRequiredChat": "发起私聊需要登录账号", - "ask.goLogin": "去登录", - "ask.needPassword": "此提问箱需要密码", - "ask.needLogin": "提问箱的主人设置了仅注册用户才能提问,你需要先登录才能向他提问。", - "ask.pleaseEmail": "请填写接收通知的邮箱地址", - "ask.generating": "正在生成分享图…", - "ask.genFailed": "生成分享图失败,请稍后重试", - "ask.networkError": "网络错误", - "ask.shareFilename": "share.png", + "ask.scan": "扫码向@%s提问", + "ask.anonymous": "谁都可以以匿名的形式提问", + "ask.registerOnly": "仅允许注册用户向TA提问", + "ask.nextOpenAt": "下次开放时间:%s", + "ask.lastCloseAt": "上次关闭时间:%s", + "weekday.0": "周日", + "weekday.1": "周一", + "weekday.2": "周二", + "weekday.3": "周三", + "weekday.4": "周四", + "weekday.5": "周五", + "weekday.6": "周六", + "ask.pinned": "已置顶", + "ask.share": "分享提问箱", + "ask.placeholder": "在此处撰写你的问题...", + "ask.chatPlaceholder": "在此处撰写你的私聊消息...", + "ask.addImage": "添加图片", + "ask.private": "回复后不公开", + "ask.notify": "接收通知", + "ask.emailPlaceholder": "请输入你的邮箱地址", + "ask.submit": "发送提问", + "ask.chat": "发起私聊", + "ask.chatDisabled": "该提问箱未开启私聊", + "ask.chatLimit": "消息上限:%d条", + "ask.history": "@%s 以前回答过的问题", + "askHistory.banner": "这台设备上有 {n} 条提问记录", + "askHistory.answeredSuffix": ",其中 {m} 条已有回答", + "askHistory.expand": "查看", + "askHistory.collapse": "收起", + "askHistory.answered": "已有回答", + "askHistory.pending": "等待回答", + "askHistory.open": "打开", + "askHistory.note": "记录只保存在这台设备的浏览器里,换设备或清除浏览器数据后会丢失。", + "ask.loginRequired": "需要登录", + "ask.loginRequiredChat": "发起私聊需要登录账号", + "ask.goLogin": "去登录", + "ask.needPassword": "此提问箱需要密码", + "ask.needLogin": "提问箱的主人设置了仅注册用户才能提问,你需要先登录才能向他提问。", + "ask.pleaseEmail": "请填写接收通知的邮箱地址", + "ask.generating": "正在生成分享图…", + "ask.genFailed": "生成分享图失败,请稍后重试", + "ask.networkError": "网络错误", + "ask.shareFilename": "share.png", // 问题条目 "qitem.answer": "回答", @@ -846,6 +854,7 @@ var zhCNDict = map[string]string{ "vote.err.editClosed": "已结束或归档的投票不允许编辑", "vote.err.publishDraftOnly": "只有草稿状态的投票可以发布", "vote.err.restrictedPublish": "当前处于平台处置期,已暂停发布投票", + "feature.stateUnavailable": "暂时无法确认该功能的状态,请稍后重试", "vote.err.closePublishedOnly": "只有进行中的投票可以结束", "vote.err.publishTitleRequired": "请先填写投票标题再发布", "vote.err.publishFormFieldsRequired": "表单类型投票至少需要 1 个字段", @@ -923,6 +932,12 @@ var zhCNDict = map[string]string{ "chat.flash.operated": "操作成功", "image.errTooLarge": "图片尺寸过大,最大支持 %d×%d 像素,请压缩后重新上传", "internal.errFlash": "服务内部错误,请稍后重试。", + "internal.errBadRequest": "请求参数不正确", + "error.notFoundTitle": "页面不存在", + "error.notFoundDesc": "这个地址没有内容,可能是链接输错了,或者内容已经被删除。", + "error.internalTitle": "服务出了点问题", + "error.internalDesc": "这次请求没能完成,通常是临时的。稍后重试一般就好了。", + "error.traceHint": "反馈时请带上这个编号:", "internal.errServer": "服务器内部错误", "home.errStats": "获取统计信息失败", "pixel.errPath": "非法的请求路径", diff --git a/internal/i18n/dict_zh_tw.go b/internal/i18n/dict_zh_tw.go index 7dd4449..73692de 100644 --- a/internal/i18n/dict_zh_tw.go +++ b/internal/i18n/dict_zh_tw.go @@ -88,41 +88,49 @@ var zhTWDict = map[string]string{ "explore.noVotes": "暫時沒有可參與的公開投票。", // 提問頁 - "ask.scan": "掃碼向@%s提問", - "ask.anonymous": "誰都可以以匿名的方式提問", - "ask.registerOnly": "僅允許註冊使用者向TA提問", - "ask.nextOpenAt": "下次開放時間:%s", - "ask.lastCloseAt": "上次關閉時間:%s", - "weekday.0": "週日", - "weekday.1": "週一", - "weekday.2": "週二", - "weekday.3": "週三", - "weekday.4": "週四", - "weekday.5": "週五", - "weekday.6": "週六", - "ask.pinned": "已置頂", - "ask.share": "分享提問箱", - "ask.placeholder": "在此處撰寫你的問題...", - "ask.chatPlaceholder": "在此處撰寫你的私聊訊息...", - "ask.addImage": "新增圖片", - "ask.private": "回覆後不公開", - "ask.notify": "接收通知", - "ask.emailPlaceholder": "請輸入你的信箱地址", - "ask.submit": "傳送提問", - "ask.chat": "發起私聊", - "ask.chatDisabled": "該提問箱未開啟私聊", - "ask.chatLimit": "訊息上限:%d則", - "ask.history": "@%s 以前回答過的問題", - "ask.loginRequired": "需要登入", - "ask.loginRequiredChat": "發起私聊需要登入帳號", - "ask.goLogin": "去登入", - "ask.needPassword": "此提問箱需要密碼", - "ask.needLogin": "提問箱的主人設定了僅註冊使用者才能提問,你需要先登入才能向他提問。", - "ask.pleaseEmail": "請填寫接收通知的信箱地址", - "ask.generating": "正在產生分享圖…", - "ask.genFailed": "產生分享圖失敗,請稍後重試", - "ask.networkError": "網路錯誤", - "ask.shareFilename": "share.png", + "ask.scan": "掃碼向@%s提問", + "ask.anonymous": "誰都可以以匿名的方式提問", + "ask.registerOnly": "僅允許註冊使用者向TA提問", + "ask.nextOpenAt": "下次開放時間:%s", + "ask.lastCloseAt": "上次關閉時間:%s", + "weekday.0": "週日", + "weekday.1": "週一", + "weekday.2": "週二", + "weekday.3": "週三", + "weekday.4": "週四", + "weekday.5": "週五", + "weekday.6": "週六", + "ask.pinned": "已置頂", + "ask.share": "分享提問箱", + "ask.placeholder": "在此處撰寫你的問題...", + "ask.chatPlaceholder": "在此處撰寫你的私聊訊息...", + "ask.addImage": "新增圖片", + "ask.private": "回覆後不公開", + "ask.notify": "接收通知", + "ask.emailPlaceholder": "請輸入你的信箱地址", + "ask.submit": "傳送提問", + "ask.chat": "發起私聊", + "ask.chatDisabled": "該提問箱未開啟私聊", + "ask.chatLimit": "訊息上限:%d則", + "ask.history": "@%s 以前回答過的問題", + "askHistory.banner": "這台裝置上有 {n} 筆提問紀錄", + "askHistory.answeredSuffix": ",其中 {m} 筆已有回答", + "askHistory.expand": "查看", + "askHistory.collapse": "收合", + "askHistory.answered": "已有回答", + "askHistory.pending": "等待回答", + "askHistory.open": "開啟", + "askHistory.note": "紀錄只保存在這台裝置的瀏覽器裡,換裝置或清除瀏覽器資料後會遺失。", + "ask.loginRequired": "需要登入", + "ask.loginRequiredChat": "發起私聊需要登入帳號", + "ask.goLogin": "去登入", + "ask.needPassword": "此提問箱需要密碼", + "ask.needLogin": "提問箱的主人設定了僅註冊使用者才能提問,你需要先登入才能向他提問。", + "ask.pleaseEmail": "請填寫接收通知的信箱地址", + "ask.generating": "正在產生分享圖…", + "ask.genFailed": "產生分享圖失敗,請稍後重試", + "ask.networkError": "網路錯誤", + "ask.shareFilename": "share.png", // 問題條目 "qitem.answer": "回答", @@ -845,6 +853,7 @@ var zhTWDict = map[string]string{ "vote.err.editClosed": "已結束或歸檔的投票不允許編輯", "vote.err.publishDraftOnly": "只有草稿狀態的投票可以發布", "vote.err.restrictedPublish": "當前處於平台處置期,已暫停發布投票", + "feature.stateUnavailable": "暫時無法確認該功能的狀態,請稍後重試", "vote.err.closePublishedOnly": "只有進行中的投票可以結束", "vote.err.publishTitleRequired": "請先填寫投票標題再發布", "vote.err.publishFormFieldsRequired": "表單類型投票至少需要 1 個欄位", @@ -910,6 +919,12 @@ var zhTWDict = map[string]string{ "chat.flash.operated": "操作成功", "image.errTooLarge": "圖片尺寸過大,最大支援 %d×%d 像素,請壓縮後重新上傳", "internal.errFlash": "服務內部錯誤,請稍後重試。", + "internal.errBadRequest": "請求參數不正確", + "error.notFoundTitle": "頁面不存在", + "error.notFoundDesc": "這個位址沒有內容,可能是連結輸入錯誤,或者內容已經被刪除。", + "error.internalTitle": "服務出了點問題", + "error.internalDesc": "這次請求沒能完成,通常是暫時的。稍後重試一般就好了。", + "error.traceHint": "回報時請帶上這個編號:", "internal.errServer": "伺服器內部錯誤", "home.errStats": "獲取統計資訊失敗", "pixel.errPath": "非法的請求路徑", diff --git a/internal/route/errorpage.go b/internal/route/errorpage.go new file mode 100644 index 0000000..b835fff --- /dev/null +++ b/internal/route/errorpage.go @@ -0,0 +1,172 @@ +// Copyright 2026 tamakyi. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package route + +import ( + "fmt" + "net/http" + "runtime/debug" + "strings" + + "github.com/flamego/flamego" + "github.com/sirupsen/logrus" + "go.opentelemetry.io/otel/trace" + + "github.com/tamakyi/TamaBox/internal/i18n" + "github.com/tamakyi/TamaBox/internal/shared" +) + +// recoveryMiddleware 替代 flamego.Recovery(),把 panic 渲染成 HTML 500 页。 +// +// 为什么不用框架自带的:flamego.Recovery() 直接写纯文本 "Internal Server Error", +// 用户看到的是白底黑字的两行英文,地址栏还停在出错的 URL 上 —— +// 既不知道发生了什么,也没有回去的入口,更没法把这次错误的编号报给我们。 +func recoveryMiddleware() flamego.Handler { + return func(ctx flamego.Context) { + defer func() { + if r := recover(); r != nil { + // 带栈:没有栈的 panic 日志几乎无法定位。 + logrus.WithContext(ctx.Request().Context()). + Errorf("panic recovered: %v\n%s", r, debug.Stack()) + + if ctx.ResponseWriter().Written() { + // 头已经发出去了,没法再改状态码,只能让连接断在这里。 + return + } + writeErrorPage(ctx, http.StatusInternalServerError, "error.internalTitle", "error.internalDesc") + } + }() + ctx.Next() + } +} + +// writeErrorPage 输出不依赖模板系统的完整错误页。 +// +// 为什么不用模板:这两个页面出现在两处「拿不到模板」的地方 —— +// NotFound 处理器不在组级中间件链里(template.Templater 与 Contexter 都挂在组级, +// 包括 session 与 CSRF),而 recovery 中间件挂在最外层,那时模板还没装配。 +// 错误页恰恰是最不该依赖出错链路的页面,所以这里用内联 HTML + i18n.T。 +func writeErrorPage(ctx flamego.Context, status int, titleKey, descKey string) { + lang := pickErrorPageLang(ctx.Request().Request) + title := i18n.T(lang, titleKey) + desc := i18n.T(lang, descKey) + home := i18n.T(lang, "deactivated.backHome") + hint := i18n.T(lang, "error.traceHint") + + traceID := "" + if span := trace.SpanFromContext(ctx.Request().Context()); span.SpanContext().TraceID().IsValid() { + traceID = span.SpanContext().TraceID().String() + } + + body := fmt.Sprintf(` +
+ + +` + esc(hint) + ` ` + esc(traceID) + `