Fix questionnaire localization source

This commit is contained in:
daixiawu 2026-07-01 20:07:03 +08:00
parent c0da5e9a0b
commit f6e832aa21
2 changed files with 171 additions and 51 deletions

View File

@ -279,7 +279,8 @@ namespace TH1_UI.View.Outside
if (label == null) return;
var buttonText = string.Empty;
var dataAssets = ResourceLoader.Load<QuestionnaireDataAssets>("DataAssets/QuestionnaireDataAssets");
var dataAssets = ResourceLoader.Load<QuestionnaireDataAssets>("Export/QuestionnaireDataAssets")
?? ResourceLoader.Load<QuestionnaireDataAssets>("DataAssets/QuestionnaireDataAssets");
if (dataAssets != null && !string.IsNullOrEmpty(dataAssets.MenuButtonText))
{
buttonText = ResolveQuestionnaireText(dataAssets.MenuButtonText);

View File

@ -18,9 +18,47 @@ namespace TH1_UI.View.Outside
{
public class UIOutsideQuestionnaireView : Base.View
{
private const string DataAssetPath = "DataAssets/QuestionnaireDataAssets";
private const string DataAssetPath = "Export/QuestionnaireDataAssets";
private const string QuestionPrefabPath = "Prefab/UI/Outside/UIOutsideQuestionnaireQuestion";
private const string OptionPrefabPath = "Prefab/UI/Outside/UIOutsideQuestionnaireOption";
private const uint TextQuestionnaireCenter = 30978;
private const uint TextCurrentQuestionnaire = 30979;
private const uint TextLatestSubmission = 30984;
private const uint TextReturn = 1048;
private const uint TextView = 19671;
private const uint TextNoAvailableQuestionnaires = 31171;
private const uint TextNoCurrentQuestionnaire = 31172;
private const uint TextLongTermQuestionnaire = 31173;
private const uint TextNoLongTermQuestionnaires = 31174;
private const uint TextPastQuestionnaires = 31175;
private const uint TextNoPastQuestionnaires = 31176;
private const uint TextNoQuestionnairesToDisplay = 31177;
private const uint TextPastQuestionnairesCollapsed = 31178;
private const uint TextViewRecordsCount = 31179;
private const uint TextFillQuestionnaire = 31180;
private const uint TextFormSubmitHint = 31181;
private const uint TextClear = 31182;
private const uint TextResponseRecords = 31183;
private const uint TextNoResponseRecords = 31184;
private const uint TextReadOnlyRecord = 31185;
private const uint TextBackToRecords = 31186;
private const uint TextRecordIndex = 31187;
private const uint TextQuestionnaireMissing = 31188;
private const uint TextQuestionnaireEnded = 31189;
private const uint TextQuestionnaireUnavailable = 31190;
private const uint TextQuestionnaireAlreadySubmitted = 31191;
private const uint TextQuestionnaireSubmissionLimit = 31192;
private const uint TextQuestionnaireInterval = 31193;
private const uint TextListMetaNotFilled = 31194;
private const uint TextListMetaSubmitted = 31195;
private const uint TextStatusCurrent = 30981;
private const uint TextStatusLongTerm = 31198;
private const uint TextStatusPast = 31199;
private const uint TextStatusHidden = 31200;
private const uint TextUnknownTime = 31201;
private const uint TextUploaded = 31202;
private const uint TextLocalOnly = 31203;
private const uint TextWaitingUploadResult = 31204;
public static readonly Color InkColor = new Color(0.25490198f, 0.23137257f, 0.41960788f, 1f);
public static readonly Color InkMutedColor = new Color(0.53f, 0.53f, 0.53f, 1f);
@ -168,7 +206,7 @@ namespace TH1_UI.View.Outside
if (selected == null)
{
ShowEmptyState("暂无可填写问卷");
ShowEmptyState(L(TextNoAvailableQuestionnaires));
return;
}
@ -190,6 +228,26 @@ namespace TH1_UI.View.Outside
return raw;
}
private static string L(uint id)
{
return MultilingualManager.Instance.GetMultilingualText(id);
}
private static string L(uint id, params object[] values)
{
var text = L(id);
if (string.IsNullOrEmpty(text) || values == null || values.Length == 0) return text;
foreach (var value in values)
{
var index = text.IndexOf("{param}", StringComparison.Ordinal);
if (index < 0) break;
text = text.Remove(index, 7).Insert(index, value?.ToString() ?? string.Empty);
}
return text;
}
public static TextMeshProUGUI CreateText(Transform parent, string objectName, string text, float fontSize,
Color color, TextAlignmentOptions alignment)
{
@ -290,7 +348,7 @@ namespace TH1_UI.View.Outside
{
if (info == null)
{
ShowEmptyState("暂无可填写问卷");
ShowEmptyState(L(TextNoAvailableQuestionnaires));
return;
}
@ -318,29 +376,30 @@ namespace TH1_UI.View.Outside
var longTerm = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.LongTerm).ToList();
var expired = items.Where(item => item.GetEffectiveStatus(DateTime.UtcNow) == QuestionnaireStatus.Expired).ToList();
CreateSectionHeader(_sidebarContent, "当期问卷");
CreateQuestionnaireListItems(featured, "暂无当期问卷");
CreateSectionHeader(_sidebarContent, L(TextCurrentQuestionnaire));
CreateQuestionnaireListItems(featured, L(TextNoCurrentQuestionnaire));
CreateDivider(_sidebarContent);
CreateSectionHeader(_sidebarContent, "长期问卷");
CreateQuestionnaireListItems(longTerm, "暂无长期问卷");
CreateSectionHeader(_sidebarContent, L(TextLongTermQuestionnaire));
CreateQuestionnaireListItems(longTerm, L(TextNoLongTermQuestionnaires));
CreateDivider(_sidebarContent);
CreateExpiredHeader(expired.Count);
if (_showExpired)
{
CreateQuestionnaireListItems(expired, "暂无往期问卷");
CreateQuestionnaireListItems(expired, L(TextNoPastQuestionnaires));
}
else if (expired.Count > 0)
{
var hint = CreateText(_sidebarContent, "ExpiredHiddenHint", $"{expired.Count} 份往期问卷已收起", 20f,
var hint = CreateText(_sidebarContent, "ExpiredHiddenHint",
L(TextPastQuestionnairesCollapsed, expired.Count), 20f,
InkMutedColor, TextAlignmentOptions.Left);
hint.enableWordWrapping = true;
}
if (items.Count == 0)
{
var empty = CreateText(_sidebarContent, "EmptyList", "暂无可显示问卷", 20f,
var empty = CreateText(_sidebarContent, "EmptyList", L(TextNoQuestionnairesToDisplay), 20f,
InkMutedColor, TextAlignmentOptions.Left);
empty.enableWordWrapping = true;
}
@ -460,7 +519,7 @@ namespace TH1_UI.View.Outside
{
if (_questionnaireInfo == null)
{
ShowEmptyState("暂无可填写问卷");
ShowEmptyState(L(TextNoAvailableQuestionnaires));
return;
}
@ -500,7 +559,8 @@ namespace TH1_UI.View.Outside
if (sheets.Count > 0 && _questionnaireInfo.CanViewHistory)
{
_recordsButton = CreateButton(_rightFooter, "RecordsButton", $"查看记录 ({sheets.Count})", false,
_recordsButton = CreateButton(_rightFooter, "RecordsButton",
L(TextViewRecordsCount, sheets.Count), false,
out _recordsButtonText, 170f);
_recordsButton.onClick.AddListener(ShowRecords);
}
@ -532,14 +592,14 @@ namespace TH1_UI.View.Outside
}
ClearRight();
SetRightHeader("填写问卷", ResolveText(_questionnaireInfo.Title));
SetRightHeader(L(TextFillQuestionnaire), ResolveText(_questionnaireInfo.Title));
TitleText = CreateText(_rightBody, "FormTitle", ResolveText(_questionnaireInfo.Title), 30f,
InkColor, TextAlignmentOptions.Left);
TitleText.fontStyle = FontStyles.Bold;
TitleText.enableWordWrapping = true;
var hint = CreateText(_rightBody, "FormHint", "提交后会保留为一条新的填写记录,可在历史中只读查看。", 18f,
var hint = CreateText(_rightBody, "FormHint", L(TextFormSubmitHint), 18f,
InkMutedColor, TextAlignmentOptions.Left);
hint.enableWordWrapping = true;
@ -551,15 +611,20 @@ namespace TH1_UI.View.Outside
StatusText.enableWordWrapping = true;
StatusText.gameObject.AddComponent<LayoutElement>().flexibleWidth = 1f;
var backButton = CreateButton(_rightFooter, "BackButton", "返回", false, out _, 120f);
ConfigureFormFooter(_rightFooter);
var backButton = CreateButton(_rightFooter, "BackButton", L(TextReturn), false, out var backButtonText, 150f);
ConfigureFormFooterButton(backButton, backButtonText, 150f);
backButton.onClick.AddListener(ShowDetail);
RefillButton = CreateButton(_rightFooter, "RefillButton", "清空", false, out RefillButtonText, 120f);
RefillButton = CreateButton(_rightFooter, "RefillButton", L(TextClear), false, out RefillButtonText, 150f);
ConfigureFormFooterButton(RefillButton, RefillButtonText, 150f);
RefillButton.onClick.AddListener(OnRefillClicked);
var submitText = ResolveText(_questionnaireInfo.SubmitButtonText);
if (string.IsNullOrEmpty(submitText)) submitText = "提交";
SubmitButton = CreateButton(_rightFooter, "SubmitButton", submitText, true, out SubmitButtonText, 140f);
if (string.IsNullOrEmpty(submitText)) submitText = ResolveText("20099");
SubmitButton = CreateButton(_rightFooter, "SubmitButton", submitText, true, out SubmitButtonText, 168f);
ConfigureFormFooterButton(SubmitButton, SubmitButtonText, 168f);
SubmitButton.onClick.AddListener(OnSubmitClicked);
SetUploading(false);
SetStatus(string.Empty);
@ -570,16 +635,16 @@ namespace TH1_UI.View.Outside
if (_questionnaireInfo == null) return;
ClearRight();
SetRightHeader("填写记录", ResolveText(_questionnaireInfo.Title));
SetRightHeader(L(TextResponseRecords), ResolveText(_questionnaireInfo.Title));
var sheets = QuestionnaireAnswerStore.Instance.GetAnswerSheets(_questionnaireInfo.QuestionnaireId);
TitleText = CreateText(_rightBody, "RecordsTitle", "填写记录", 31f,
TitleText = CreateText(_rightBody, "RecordsTitle", L(TextResponseRecords), 31f,
InkColor, TextAlignmentOptions.Left);
TitleText.fontStyle = FontStyles.Bold;
if (sheets.Count == 0)
{
var empty = CreateText(_rightBody, "EmptyRecords", "还没有填写记录。", 20f,
var empty = CreateText(_rightBody, "EmptyRecords", L(TextNoResponseRecords), 20f,
InkMutedColor, TextAlignmentOptions.Left);
empty.enableWordWrapping = true;
}
@ -591,7 +656,7 @@ namespace TH1_UI.View.Outside
}
}
var backButton = CreateButton(_rightFooter, "BackButton", "返回", false, out _, 120f);
var backButton = CreateButton(_rightFooter, "BackButton", L(TextReturn), false, out _, 120f);
backButton.onClick.AddListener(ShowDetail);
}
@ -600,7 +665,7 @@ namespace TH1_UI.View.Outside
if (sheet == null) return;
ClearRight();
SetRightHeader("查看记录", ResolveText(_questionnaireInfo?.Title));
SetRightHeader(L(TextView), ResolveText(_questionnaireInfo?.Title));
var snapshotTitle = sheet.DefinitionSnapshot != null && !string.IsNullOrEmpty(sheet.DefinitionSnapshot.Title)
? sheet.DefinitionSnapshot.Title
@ -611,7 +676,7 @@ namespace TH1_UI.View.Outside
TitleText.fontStyle = FontStyles.Bold;
TitleText.enableWordWrapping = true;
CreateInfoStrip(_rightBody, $"{FormatSubmittedAt(sheet)} · {GetUploadStateText(sheet)} · 只读记录");
CreateInfoStrip(_rightBody, $"{FormatSubmittedAt(sheet)} · {GetUploadStateText(sheet)} · {L(TextReadOnlyRecord)}");
CreateQuestionListRoot();
var questions = BuildQuestionsFromSnapshot(sheet.DefinitionSnapshot);
@ -622,7 +687,7 @@ namespace TH1_UI.View.Outside
BuildQuestions(questions, true, sheet);
var backButton = CreateButton(_rightFooter, "BackButton", "返回记录", false, out _, 140f);
var backButton = CreateButton(_rightFooter, "BackButton", L(TextBackToRecords), false, out _, 140f);
backButton.onClick.AddListener(ShowRecords);
}
@ -646,7 +711,7 @@ namespace TH1_UI.View.Outside
layout.childForceExpandWidth = true;
layout.childForceExpandHeight = false;
var title = EnsureText(preview.transform, "Title", "最近一次填写", 20f,
var title = EnsureText(preview.transform, "Title", L(TextLatestSubmission), 20f,
InkColor, TextAlignmentOptions.Left);
title.fontStyle = FontStyles.Bold;
@ -718,7 +783,7 @@ namespace TH1_UI.View.Outside
textLayout.childForceExpandWidth = true;
textLayout.childForceExpandHeight = false;
var title = EnsureText(textRoot.transform, "Title", $"记录 {index}", 20f,
var title = EnsureText(textRoot.transform, "Title", L(TextRecordIndex, index), 20f,
InkColor, TextAlignmentOptions.Left);
title.fontStyle = FontStyles.Bold;
@ -728,8 +793,8 @@ namespace TH1_UI.View.Outside
var viewButtonTransform = row.transform.Find("ViewButton");
var viewButton = viewButtonTransform != null
? ConfigureButton(viewButtonTransform.gameObject, "查看", false, out _, 104f)
: CreateButton(row.transform, "ViewButton", "查看", false, out _, 104f);
? ConfigureButton(viewButtonTransform.gameObject, L(TextView), false, out _, 104f)
: CreateButton(row.transform, "ViewButton", L(TextView), false, out _, 104f);
viewButton.onClick.AddListener(() => ShowReadonlyRecord(sheet));
}
@ -976,7 +1041,7 @@ namespace TH1_UI.View.Outside
reason = string.Empty;
if (info == null)
{
reason = "问卷不存在";
reason = L(TextQuestionnaireMissing);
return false;
}
@ -984,21 +1049,21 @@ namespace TH1_UI.View.Outside
if (!info.CanSubmitAt(now))
{
reason = info.GetEffectiveStatus(now) == QuestionnaireStatus.Expired
? "这份问卷已经结束,只能查看历史记录。"
: "这份问卷当前不可填写。";
? L(TextQuestionnaireEnded)
: L(TextQuestionnaireUnavailable);
return false;
}
var submittedCount = sheets?.Count ?? 0;
if (submittedCount > 0 && !info.AllowMultipleSubmissions)
{
reason = "这份问卷已经填写过。";
reason = L(TextQuestionnaireAlreadySubmitted);
return false;
}
if (info.MaxSubmissionCount > 0 && submittedCount >= info.MaxSubmissionCount)
{
reason = "这份问卷已经达到填写次数上限。";
reason = L(TextQuestionnaireSubmissionLimit);
return false;
}
@ -1011,7 +1076,7 @@ namespace TH1_UI.View.Outside
.AddHours(info.MinSubmitIntervalHours);
if (now < nextAt)
{
reason = $"请在 {nextAt.ToLocalTime():yyyy-MM-dd HH:mm} 后再次填写。";
reason = L(TextQuestionnaireInterval, nextAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm"));
return false;
}
}
@ -1647,14 +1712,14 @@ namespace TH1_UI.View.Outside
rootLayout.childForceExpandWidth = true;
rootLayout.childForceExpandHeight = false;
var title = EnsureText(textRoot, "PanelTitle", "问卷中心", 34f, InkLightColor, TextAlignmentOptions.Left);
var title = EnsureText(textRoot, "PanelTitle", L(TextQuestionnaireCenter), 34f, InkLightColor, TextAlignmentOptions.Left);
title.fontStyle = FontStyles.Bold;
title.enableAutoSizing = true;
title.fontSizeMin = 22f;
title.fontSizeMax = 34f;
title.enableWordWrapping = false;
var subtitle = EnsureText(textRoot, "PanelSubtitle", "当前反馈 / 长期收集 / 往期记录", 18f,
var subtitle = EnsureText(textRoot, "PanelSubtitle", L(31170), 18f,
InkLightColor, TextAlignmentOptions.Left);
subtitle.enableWordWrapping = false;
}
@ -1958,6 +2023,7 @@ namespace TH1_UI.View.Outside
{
DestroyChildren(_rightBody);
DestroyChildren(_rightFooter);
ConfigureFooter(_rightFooter);
_questionItems.Clear();
QuestionListContent = null;
SubmitButton = null;
@ -1970,7 +2036,7 @@ namespace TH1_UI.View.Outside
private void ShowEmptyState(string text)
{
ClearRight();
SetRightHeader("问卷中心", string.Empty);
SetRightHeader(L(TextQuestionnaireCenter), string.Empty);
var empty = CreateText(_rightBody, "EmptyState", text, 24f,
InkMutedColor, TextAlignmentOptions.Center);
empty.enableWordWrapping = true;
@ -2097,8 +2163,12 @@ namespace TH1_UI.View.Outside
private void CreateExpiredHeader(int expiredCount)
{
var button = CreateButton(_sidebarContent, "ExpiredToggle", _showExpired ? "往期问卷 v" : "往期问卷 >",
var button = CreateButton(_sidebarContent, "ExpiredToggle",
_showExpired ? $"{L(TextPastQuestionnaires)} v" : $"{L(TextPastQuestionnaires)} >",
false, out _expiredToggleText, 0f);
var image = button.targetGraphic as Image;
if (image == null) image = button.GetComponent<Image>();
ApplySprite(image, SpriteItemButton, PanelTint, false, ItemButtonPixelsPerUnitMultiplier);
var layout = button.GetComponent<LayoutElement>();
layout.minHeight = 56f;
layout.preferredHeight = 60f;
@ -2106,7 +2176,9 @@ namespace TH1_UI.View.Outside
_expiredToggleText.fontSizeMin = 20f;
if (expiredCount > 0)
{
_expiredToggleText.text = _showExpired ? $"往期问卷 ({expiredCount}) v" : $"往期问卷 ({expiredCount}) >";
_expiredToggleText.text = _showExpired
? $"{L(TextPastQuestionnaires)} ({expiredCount}) v"
: $"{L(TextPastQuestionnaires)} ({expiredCount}) >";
}
button.onClick.AddListener(() =>
@ -2222,6 +2294,53 @@ namespace TH1_UI.View.Outside
return button;
}
private static void ConfigureFormFooter(Transform footer)
{
if (footer == null) return;
var layout = footer.GetComponent<HorizontalLayoutGroup>();
if (layout != null)
{
layout.spacing = 24f;
layout.childAlignment = TextAnchor.MiddleRight;
layout.childControlWidth = true;
layout.childControlHeight = true;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
}
var layoutElement = footer.GetComponent<LayoutElement>();
if (layoutElement != null)
{
layoutElement.minHeight = 88f;
layoutElement.preferredHeight = 92f;
}
}
private static void ConfigureFormFooterButton(Button button, TextMeshProUGUI label, float width)
{
if (button == null) return;
var layout = button.GetComponent<LayoutElement>();
if (layout != null)
{
layout.minWidth = width;
layout.preferredWidth = width;
layout.minHeight = 68f;
layout.preferredHeight = 72f;
}
if (button.transform is RectTransform rect)
{
rect.sizeDelta = new Vector2(width, 72f);
}
if (label == null) return;
label.fontSize = 27f;
label.fontStyle = FontStyles.Bold;
label.enableAutoSizing = false;
}
private static Button ConfigureButton(GameObject go, string text, bool primary, out TextMeshProUGUI label,
float preferredWidth)
{
@ -2584,8 +2703,8 @@ namespace TH1_UI.View.Outside
{
var sheets = QuestionnaireAnswerStore.Instance.GetAnswerSheets(info.QuestionnaireId);
var questionCount = info.Questions?.Count ?? 0;
if (sheets.Count == 0) return $"{questionCount} 题 · 未填写";
return $"{questionCount} 题 · 已填写 {sheets.Count} 次";
if (sheets.Count == 0) return L(TextListMetaNotFilled, questionCount);
return L(TextListMetaSubmitted, questionCount, sheets.Count);
}
private static string GetStatusText(QuestionnaireInfo info)
@ -2593,10 +2712,10 @@ namespace TH1_UI.View.Outside
if (info == null) return string.Empty;
return info.GetEffectiveStatus(DateTime.UtcNow) switch
{
QuestionnaireStatus.Featured => "当期",
QuestionnaireStatus.LongTerm => "长期",
QuestionnaireStatus.Expired => "往期",
_ => "隐藏"
QuestionnaireStatus.Featured => L(TextStatusCurrent),
QuestionnaireStatus.LongTerm => L(TextStatusLongTerm),
QuestionnaireStatus.Expired => L(TextStatusPast),
_ => L(TextStatusHidden)
};
}
@ -2614,15 +2733,15 @@ namespace TH1_UI.View.Outside
return parsed.ToLocalTime().ToString("yyyy-MM-dd HH:mm");
}
return "未知时间";
return L(TextUnknownTime);
}
private static string GetUploadStateText(QuestionnaireAnswerSheet sheet)
{
if (sheet == null) return string.Empty;
if (!string.IsNullOrEmpty(sheet.LastUploadObjectKey)) return "已上传";
if (!string.IsNullOrEmpty(sheet.LastUploadError)) return "仅本地保存";
return "等待上传结果";
if (!string.IsNullOrEmpty(sheet.LastUploadObjectKey)) return L(TextUploaded);
if (!string.IsNullOrEmpty(sheet.LastUploadError)) return L(TextLocalOnly);
return L(TextWaitingUploadResult);
}
private static void DestroyChildren(Transform parent)