🌐 Localize the new feature: thinking

This commit is contained in:
2025-10-26 01:46:49 +08:00
parent aa72ce08e8
commit 19db8309c4
4 changed files with 49 additions and 49 deletions

View File

@@ -1290,5 +1290,14 @@
"purchase": "Purchase", "purchase": "Purchase",
"multiplierLabel": "Multiplier", "multiplierLabel": "Multiplier",
"specialOnly": "Special Only", "specialOnly": "Special Only",
"matches": "Matches" "matches": "Matches",
"thoughtDefaultTopic": "Reflection",
"thoughtAiName": "SN-chan",
"thoughtUserName": "You",
"thoughtStreamingHint": "Sn-chan is thinking...",
"thoughtInputHint": "Ask sn-chan anything...",
"thoughtNewConversation": "Start New Conversation",
"thoughtParseError": "Failed to parse AI response",
"aiThought": "AI Thought",
"aiThoughtTitle": "Let sn-chan think"
} }

View File

@@ -1081,5 +1081,14 @@
"postPublish": "发布帖子", "postPublish": "发布帖子",
"restoreDraftTitle": "恢复草稿", "restoreDraftTitle": "恢复草稿",
"restoreDraftMessage": "发现了一个草稿。你想要恢复它吗?", "restoreDraftMessage": "发现了一个草稿。你想要恢复它吗?",
"draft": "草稿" "draft": "草稿",
"thoughtDefaultTopic": "寻思",
"thoughtAiName": "SN 酱",
"thoughtUserName": "您",
"thoughtStreamingHint": "Sn-chan 正在思考...",
"thoughtInputHint": "问 sn-chan 任何问题...",
"thoughtNewConversation": "开始新对话",
"thoughtParseError": "解析 AI 响应失败",
"aiThought": "寻思",
"aiThoughtTitle": "让 SN 酱寻思寻思"
} }

View File

@@ -173,7 +173,7 @@ class TabsScreen extends HookConsumerWidget {
horizontal: 24, horizontal: 24,
), ),
leading: const Icon(Symbols.bubble_chart), leading: const Icon(Symbols.bubble_chart),
title: Text('让 SN 酱寻思寻思'), title: Text('aiThoughtTitle'.tr()),
onTap: () async { onTap: () async {
Navigator.of(context).pop(); Navigator.of(context).pop();
context.pushNamed('thought'); context.pushNamed('thought');

View File

@@ -1,6 +1,7 @@
import "dart:async"; import "dart:async";
import "dart:convert"; import "dart:convert";
import "package:dio/dio.dart"; import "package:dio/dio.dart";
import "package:easy_localization/easy_localization.dart";
import "package:flutter/material.dart"; import "package:flutter/material.dart";
import "package:flutter_hooks/flutter_hooks.dart"; import "package:flutter_hooks/flutter_hooks.dart";
import "package:gap/gap.dart"; import "package:gap/gap.dart";
@@ -43,7 +44,7 @@ class ThoughtScreen extends HookConsumerWidget {
: const AsyncValue<List<SnThinkingThought>>.data([]); : const AsyncValue<List<SnThinkingThought>>.data([]);
final localThoughts = useState<List<SnThinkingThought>>([]); final localThoughts = useState<List<SnThinkingThought>>([]);
final currentTopic = useState<String?>('寻思'); final currentTopic = useState<String?>('aiThought'.tr());
final messageController = useTextEditingController(); final messageController = useTextEditingController();
final scrollController = useScrollController(); final scrollController = useScrollController();
@@ -61,7 +62,7 @@ class ThoughtScreen extends HookConsumerWidget {
if (data.isNotEmpty && data.first.sequence?.topic != null) { if (data.isNotEmpty && data.first.sequence?.topic != null) {
currentTopic.value = data.first.sequence!.topic; currentTopic.value = data.first.sequence!.topic;
} else { } else {
currentTopic.value = '寻思'; currentTopic.value = 'aiThought'.tr();
} }
}); });
return null; return null;
@@ -186,7 +187,7 @@ class ThoughtScreen extends HookConsumerWidget {
currentTopic.value = topic; currentTopic.value = topic;
} }
} catch (e) { } catch (e) {
showErrorAlert('Failed to parse AI response'); showErrorAlert('thoughtParseError'.tr());
} }
}, },
onError: (error) { onError: (error) {
@@ -195,7 +196,7 @@ class ThoughtScreen extends HookConsumerWidget {
// Handle streaming response errors differently // Handle streaming response errors differently
if (error is DioException && error.response?.data is ResponseBody) { if (error is DioException && error.response?.data is ResponseBody) {
// For streaming responses, show a generic error message // For streaming responses, show a generic error message
showErrorAlert('Failed to get AI response. Please try again.'); showErrorAlert('toughtParseError'.tr());
} else { } else {
showErrorAlert(error); showErrorAlert(error);
} }
@@ -243,8 +244,8 @@ class ThoughtScreen extends HookConsumerWidget {
children: [ children: [
Text( Text(
thought.role == ThinkingThoughtRole.assistant thought.role == ThinkingThoughtRole.assistant
? 'SN 酱' ? 'toughtAiName'.tr()
: '', : 'thoughtUserName'.tr(),
style: Theme.of(context).textTheme.titleSmall, style: Theme.of(context).textTheme.titleSmall,
), ),
Tooltip( Tooltip(
@@ -309,7 +310,10 @@ class ThoughtScreen extends HookConsumerWidget {
children: [ children: [
Icon(Symbols.smart_toy, size: 20), Icon(Symbols.smart_toy, size: 20),
const Gap(8), const Gap(8),
Text('SN 酱', style: Theme.of(context).textTheme.titleSmall), Text(
'thoughtAiName'.tr(),
style: Theme.of(context).textTheme.titleSmall,
),
const Spacer(), const Spacer(),
SizedBox( SizedBox(
width: 16, width: 16,
@@ -327,27 +331,9 @@ class ThoughtScreen extends HookConsumerWidget {
), ),
); );
Widget newConversationButton() => Container(
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: FilledButton.icon(
onPressed: () {
// Clear current conversation and start new one
selectedSequenceId.value = null;
localThoughts.value = [];
currentTopic.value = '寻思';
messageController.clear();
},
icon: const Icon(Symbols.add),
label: const Text('开始新对话'),
style: FilledButton.styleFrom(
minimumSize: const Size(double.infinity, 48),
),
),
);
return AppScaffold( return AppScaffold(
appBar: AppBar( appBar: AppBar(
title: Text(currentTopic.value ?? '寻思'), title: Text(currentTopic.value ?? 'aiThought'.tr()),
actions: [ actions: [
IconButton( IconButton(
icon: const Icon(Symbols.history), icon: const Icon(Symbols.history),
@@ -364,29 +350,25 @@ class ThoughtScreen extends HookConsumerWidget {
); );
}, },
), ),
if (localThoughts.value.isNotEmpty &&
!isStreaming.value &&
localThoughts.value.last.role == ThinkingThoughtRole.assistant)
IconButton(
icon: const Icon(Symbols.add),
tooltip: 'thoughtNewConversation'.tr(),
onPressed: () {
// Clear current conversation and start new one
selectedSequenceId.value = null;
localThoughts.value = [];
currentTopic.value = 'aiThought'.tr();
messageController.clear();
},
),
const Gap(8), const Gap(8),
], ],
), ),
body: Column( body: Column(
children: [ children: [
// New conversation button - only show when there are messages and last is AI
AnimatedOpacity(
opacity:
(localThoughts.value.isNotEmpty &&
localThoughts.value.last.role ==
ThinkingThoughtRole.assistant &&
!isStreaming.value)
? 1.0
: 0.0,
duration: const Duration(milliseconds: 300),
child:
(localThoughts.value.isNotEmpty &&
localThoughts.value.last.role ==
ThinkingThoughtRole.assistant &&
!isStreaming.value)
? newConversationButton()
: const SizedBox.shrink(),
),
Expanded( Expanded(
child: thoughts.when( child: thoughts.when(
data: data:
@@ -447,8 +429,8 @@ class ThoughtScreen extends HookConsumerWidget {
decoration: InputDecoration( decoration: InputDecoration(
hintText: hintText:
isStreaming.value isStreaming.value
? 'Sn-chan is thinking...' ? 'thoughtStreamingHint'.tr()
: 'Ask sn-chan anything...', : 'thoughtInputHint'.tr(),
border: InputBorder.none, border: InputBorder.none,
isDense: true, isDense: true,
contentPadding: const EdgeInsets.symmetric( contentPadding: const EdgeInsets.symmetric(