feat(frontend): add multi-language support (en, zh-Hans, zh-Hant, ja)

This commit is contained in:
2026-05-25 11:49:53 +08:00
parent 1539e495e6
commit 261b1ab169
20 changed files with 1955 additions and 139 deletions

View File

@@ -2,6 +2,7 @@ import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/services.dart';
import '../l10n/app_localizations.dart';
import '../providers/auth_provider.dart';
import '../providers/settings_provider.dart';
import '../services/api_service.dart';
@@ -42,9 +43,10 @@ class _MyStreamPageState extends State<MyStreamPage> {
if (!mounted) {
return;
}
final l10n = AppLocalizations.of(context);
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text("Failed to fetch room info")));
).showSnackBar(SnackBar(content: Text(l10n?.failedToFetchRoomInfo ?? "Failed to fetch room info")));
} finally {
if (mounted) {
setState(() => _isLoading = false);
@@ -55,46 +57,47 @@ class _MyStreamPageState extends State<MyStreamPage> {
@override
Widget build(BuildContext context) {
final settings = context.watch<SettingsProvider>();
final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(title: Text("My Stream Console")),
appBar: AppBar(title: Text(l10n.myStreamConsole)),
body: _isLoading
? Center(child: CircularProgressIndicator())
? const Center(child: CircularProgressIndicator())
: _roomInfo == null
? Center(child: Text("No room info found."))
? Center(child: Text(l10n.noRoomInfo))
: SingleChildScrollView(
padding: EdgeInsets.all(20),
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildInfoCard(
title: "Room Title",
title: l10n.roomTitle,
value: _roomInfo!['title'],
icon: Icons.edit,
onTap: () {
// TODO: Implement title update API later
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Title editing coming soon!")),
const SnackBar(content: Text("Title editing coming soon!")),
);
},
),
SizedBox(height: 20),
const SizedBox(height: 20),
_buildInfoCard(
title: "RTMP Server URL",
title: l10n.rtmpServerUrl,
value: settings.rtmpUrl,
icon: Icons.copy,
onTap: () {
Clipboard.setData(ClipboardData(text: settings.rtmpUrl));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Server URL copied to clipboard"),
content: Text(l10n.copiedToClipboard),
),
);
},
),
SizedBox(height: 20),
const SizedBox(height: 20),
_buildInfoCard(
title: "Stream Key (Keep Secret!)",
title: l10n.streamKey,
value: _roomInfo!['stream_key'],
icon: Icons.copy,
isSecret: true,
@@ -104,18 +107,18 @@ class _MyStreamPageState extends State<MyStreamPage> {
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Stream Key copied to clipboard"),
content: Text(l10n.copiedToClipboard),
),
);
},
),
SizedBox(height: 24),
const SizedBox(height: 24),
AndroidQuickStreamPanel(
rtmpBaseUrl: settings.rtmpUrl,
streamKey: _roomInfo!['stream_key'],
),
SizedBox(height: 30),
Center(
const SizedBox(height: 30),
const Center(
child: Column(
children: [
Icon(Icons.info_outline, color: Colors.grey),
@@ -143,10 +146,10 @@ class _MyStreamPageState extends State<MyStreamPage> {
}) {
return Card(
child: ListTile(
title: Text(title, style: TextStyle(fontSize: 12, color: Colors.grey)),
title: Text(title, style: const TextStyle(fontSize: 12, color: Colors.grey)),
subtitle: Text(
isSecret ? "••••••••••••••••" : value,
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16),
),
trailing: IconButton(icon: Icon(icon), onPressed: onTap),
),