feat(i18n): persist language preference and thread through chat, memoir, TTS

- Add users.language_preference (Alembic 0018, default zh); capture at signup/SMS
  only; expose on auth and profile APIs
- Lite English prompts for chat and memoir; localized stage labels and agent
  names (Life Echo / 岁月知己)
- Tencent TTS: language-aware synthesis, ModelType=1 for 501004, English chunking
- WebSocket pipeline: emit all AGENT_RESPONSE segments when TTS cancels; INFO logs
  for tts_this_turn and TTS decisions; on-demand TTS logging
- Expo: device language on auth, i18n tiers/agent name, [SPLIT] streaming UX fixes
- Tests for migration, prompts, pipeline, router tts_this_turn, reply segments

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin
2026-05-11 16:16:49 +08:00
parent 5ce29aad64
commit ccdc4e4277
64 changed files with 3233 additions and 208 deletions

View File

@@ -3,6 +3,7 @@ import { File, Paths } from 'expo-file-system';
import { useCallback, useEffect, useRef, useState } from 'react';
import { AppState, type AppStateStatus } from 'react-native';
import i18n from '@/i18n';
import type { WsConnectionState } from '@/core/ws/types';
import { conversationApi } from './api';
@@ -111,7 +112,7 @@ export function useCreateConversation() {
const now = Date.now();
const item: ConversationListItem = {
id: newConversation.id,
title: '岁月知己',
title: i18n.t('agentName', { ns: 'conversation' }),
avatarUrl: null,
latestMessagePreview: '',
latestMessageTime: now,
@@ -247,6 +248,14 @@ export function useRealtimeSession({
setAwaitingAssistantReply(false);
return;
}
/**
* 空文本 + 未完成时不能写成 `{text: '', isComplete: false}`
* UI 会渲染一只空 `StreamingBubbles`pulsing 气泡 + 光标),看上去与
* 「正在回复…」typing 气泡难以区分,且会一直挂在底部不消失。
*/
if (text.length === 0) {
return;
}
setStreamingMessage({ text, isComplete });
},
[],