1
0
forked from baron/baron-sso

i18n 대대적 변경

This commit is contained in:
Lectom C Han
2026-02-10 19:13:00 +09:00
parent 798d37bed9
commit 8df95c8a13
27 changed files with 3910 additions and 594 deletions

View File

@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import '../../../core/services/auth_proxy_service.dart';
import 'package:userfront/i18n.dart';
class ResetPasswordScreen extends StatefulWidget {
final String? loginId; // Now receiving loginId
@@ -66,7 +67,12 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
Future<void> _handlePasswordReset() async {
if (_formKey.currentState?.validate() != true) return;
if ((_loginId == null || _loginId!.isEmpty) && (_token == null || _token!.isEmpty)) {
_showError("유효하지 않은 재설정 링크입니다. (loginId/token 누락)");
_showError(
tr(
'msg.userfront.reset.invalid_link',
fallback: '유효하지 않은 재설정 링크입니다. (loginId/token 누락)',
),
);
return;
}
@@ -81,8 +87,13 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text("비밀번호가 성공적으로 변경되었습니다. 다시 로그인해주세요."),
SnackBar(
content: Text(
tr(
'msg.userfront.reset.success',
fallback: '비밀번호가 성공적으로 변경되었습니다. 다시 로그인해주세요.',
),
),
backgroundColor: Colors.green,
),
);
@@ -90,7 +101,13 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
}
} catch (e) {
if (mounted) {
_showError("비밀번호 변경에 실패했습니다: ${e.toString()}");
_showError(
tr(
'msg.userfront.reset.error.generic',
fallback: '비밀번호 변경에 실패했습니다: {{error}}',
params: {'error': e.toString()},
),
);
}
} finally {
if (mounted) {
@@ -107,7 +124,10 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
String _buildPolicyDescription() {
if (_isPolicyLoading) {
return "비밀번호 정책을 불러오는 중입니다...";
return tr(
'msg.userfront.reset.policy_loading',
fallback: '비밀번호 정책을 불러오는 중입니다...',
);
}
final minLength = (_policy?['minLength'] as int?) ?? 12;
final minTypes = (_policy?['minCharacterTypes'] as int?) ?? 0;
@@ -116,14 +136,42 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
final requiresNumber = _policy?['number'] ?? true;
final requiresSymbol = _policy?['nonAlphanumeric'] ?? true;
final parts = <String>["최소 ${minLength}자 이상"];
final parts = <String>[
tr(
'msg.userfront.reset.policy.min_length',
fallback: '최소 {{count}}자 이상',
params: {'count': '$minLength'},
),
];
if (minTypes > 0) {
parts.add("영문 대/소문자/숫자/특수문자 중 ${minTypes}가지 이상");
parts.add(
tr(
'msg.userfront.reset.policy.min_types',
fallback: '영문 대/소문자/숫자/특수문자 중 {{count}}가지 이상',
params: {'count': '$minTypes'},
),
);
}
if (requiresLower) {
parts.add(
tr('msg.userfront.reset.policy.lowercase', fallback: '소문자 1개 이상'),
);
}
if (requiresUpper) {
parts.add(
tr('msg.userfront.reset.policy.uppercase', fallback: '대문자 1개 이상'),
);
}
if (requiresNumber) {
parts.add(
tr('msg.userfront.reset.policy.number', fallback: '숫자 1개 이상'),
);
}
if (requiresSymbol) {
parts.add(
tr('msg.userfront.reset.policy.symbol', fallback: '특수문자 1개 이상'),
);
}
if (requiresLower) parts.add("소문자 1개 이상");
if (requiresUpper) parts.add("대문자 1개 이상");
if (requiresNumber) parts.add("숫자 1개 이상");
if (requiresSymbol) parts.add("특수문자 1개 이상");
return parts.join(", ");
}
@@ -132,7 +180,9 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("새 비밀번호 설정"),
title: Text(
tr('ui.userfront.reset.title', fallback: '새 비밀번호 설정'),
),
centerTitle: true,
),
body: Center(
@@ -148,7 +198,10 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
"새로운 비밀번호 설정",
tr(
'ui.userfront.reset.subtitle',
fallback: '새로운 비밀번호 설정',
),
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
@@ -166,7 +219,10 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
controller: _passwordController,
obscureText: _isPasswordObscured,
decoration: InputDecoration(
labelText: "새 비밀번호",
labelText: tr(
'ui.userfront.reset.new_password',
fallback: '새 비밀번호',
),
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
@@ -183,11 +239,18 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
validator: (value) {
final val = value ?? "";
if (val.isEmpty) {
return '비밀번호를 입력해주세요.';
return tr(
'msg.userfront.reset.error.empty_password',
fallback: '비밀번호를 입력해주세요.',
);
}
final minLength = (_policy?['minLength'] as int?) ?? 12;
if (val.length < minLength) {
return '비밀번호는 최소 $minLength자 이상이어야 합니다.';
return tr(
'msg.userfront.reset.error.min_length',
fallback: '비밀번호는 최소 {{count}}자 이상이어야 합니다.',
params: {'count': '$minLength'},
);
}
final hasLower = RegExp(r'[a-z]').hasMatch(val);
final hasUpper = RegExp(r'[A-Z]').hasMatch(val);
@@ -201,20 +264,37 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
final minTypes = (_policy?['minCharacterTypes'] as int?) ?? 0;
if (minTypes > 0 && typeCount < minTypes) {
return '비밀번호는 영문 대/소문자/숫자/특수문자 중 $minTypes가지 이상 포함해야 합니다.';
return tr(
'msg.userfront.reset.error.min_types',
fallback:
'비밀번호는 영문 대/소문자/숫자/특수문자 중 {{count}}가지 이상 포함해야 합니다.',
params: {'count': '$minTypes'},
);
}
if ((_policy?['lowercase'] ?? true) && !hasLower) {
return '최소 1개 이상의 소문자를 포함해야 합니다.';
return tr(
'msg.userfront.reset.error.lowercase',
fallback: '최소 1개 이상의 소문자를 포함해야 합니다.',
);
}
if ((_policy?['uppercase'] ?? false) && !hasUpper) {
return '최소 1개 이상의 대문자를 포함해야 합니다.';
return tr(
'msg.userfront.reset.error.uppercase',
fallback: '최소 1개 이상의 대문자를 포함해야 합니다.',
);
}
if ((_policy?['number'] ?? true) && !hasNumber) {
return '최소 1개 이상의 숫자를 포함해야 합니다.';
return tr(
'msg.userfront.reset.error.number',
fallback: '최소 1개 이상의 숫자를 포함해야 합니다.',
);
}
if ((_policy?['nonAlphanumeric'] ?? true) && !hasSymbol) {
return '최소 1개 이상의 특수문자를 포함해야 합니다.';
return tr(
'msg.userfront.reset.error.symbol',
fallback: '최소 1개 이상의 특수문자를 포함해야 합니다.',
);
}
return null;
},
@@ -224,7 +304,10 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
controller: _confirmPasswordController,
obscureText: _isConfirmPasswordObscured,
decoration: InputDecoration(
labelText: "새 비밀번호 확인",
labelText: tr(
'ui.userfront.reset.confirm_password',
fallback: '새 비밀번호 확인',
),
border: const OutlineInputBorder(),
prefixIcon: const Icon(Icons.lock_outline),
suffixIcon: IconButton(
@@ -240,7 +323,10 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
),
validator: (value) {
if (value != _passwordController.text) {
return '비밀번호가 일치하지 않습니다.';
return tr(
'msg.userfront.reset.error.mismatch',
fallback: '비밀번호가 일치하지 않습니다.',
);
}
return null;
},
@@ -255,9 +341,17 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
? const SizedBox(
height: 20,
width: 20,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
child: CircularProgressIndicator(
strokeWidth: 2,
color: Colors.white,
),
)
: const Text("비밀번호 변경"),
: Text(
tr(
'ui.userfront.reset.submit',
fallback: '비밀번호 변경',
),
),
),
],
),
@@ -268,20 +362,24 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
}
Widget _buildInvalidTokenView() {
return const Center(
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.error_outline, color: Colors.red, size: 60),
SizedBox(height: 16),
const Icon(Icons.error_outline, color: Colors.red, size: 60),
const SizedBox(height: 16),
Text(
"유효하지 않은 링크입니다.",
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
tr('msg.userfront.reset.invalid_title',
fallback: '유효하지 않은 링크입니다.'),
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(height: 8),
const SizedBox(height: 8),
Text(
"비밀번호 재설정 링크가 만료되었거나 잘못되었습니다. 다시 시도해주세요.",
tr(
'msg.userfront.reset.invalid_body',
fallback: '비밀번호 재설정 링크가 만료되었거나 잘못되었습니다. 다시 시도해주세요.',
),
textAlign: TextAlign.center,
),
],