1
0
forked from baron/baron-sso

i18n 경로 오류 및 placeholder 표시 수정

This commit is contained in:
2026-04-06 17:07:26 +09:00
parent 2e04c5a893
commit 69d7f053be
3 changed files with 1754 additions and 716 deletions

View File

@@ -1,22 +1,59 @@
import 'dart:ui';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
import 'package:toml/toml.dart';
import '../../i18n_data.dart';
class TomlAssetLoader extends AssetLoader {
const TomlAssetLoader();
@override
Future<Map<String, dynamic>> load(String path, Locale locale) async {
final assetPath = '$path/${locale.languageCode}.toml';
try {
final content = await rootBundle.loadString(assetPath);
final document = TomlDocument.parse(content);
return document.toMap();
} catch (e) {
// 로딩 실패 시 빈 맵을 반환해 렌더링을 지속합니다.
return {};
}
final languageCode = locale.languageCode.toLowerCase();
final source = switch (languageCode) {
'ko' => koStrings,
'en' => enStrings,
_ => enStrings,
};
return _expandFlatTranslations(source);
}
}
Map<String, dynamic> _expandFlatTranslations(Map<String, String> flatMap) {
final nested = <String, dynamic>{};
for (final entry in flatMap.entries) {
final key = entry.key;
if (key.isEmpty) {
continue;
}
final segments = key.split('.');
Map<String, dynamic> cursor = nested;
for (var index = 0; index < segments.length; index++) {
final segment = segments[index];
if (segment.isEmpty) {
continue;
}
final isLeaf = index == segments.length - 1;
if (isLeaf) {
cursor[segment] = _normalizeLocalizationValue(entry.value);
continue;
}
final next = cursor.putIfAbsent(segment, () => <String, dynamic>{});
if (next is Map<String, dynamic>) {
cursor = next;
continue;
}
final replacement = <String, dynamic>{};
cursor[segment] = replacement;
cursor = replacement;
}
}
return nested;
}
String _normalizeLocalizationValue(String value) {
return value.replaceAllMapped(
RegExp(r'\{\{[[:space:]]*([a-zA-Z0-9_]+)[[:space:]]*\}\}'),
(match) => '{${match.group(1)}}',
);
}

View File

@@ -57,6 +57,40 @@ class _ConsentScreenState extends State<ConsentScreen> {
};
}
String _renderConsentText(String key, {String? fallback}) {
return tr(key, fallback: fallback)
.replaceAll(r'\\n', '\n')
.replaceAll(r'\n', '\n')
.replaceAll('\\\n', '\n');
}
String _renderScopeCountLabel(int count) {
return tr(
'msg.userfront.consent.scope_count',
fallback: 'Total {{count}}',
params: {'count': '$count'},
).replaceAll('{$count}', '$count');
}
String _scopeDisplayLabel(String scope) {
if (scope == 'offline_access') {
return 'offline access';
}
return scope.replaceAll('_', ' ');
}
String _renderClientIdLabel(String clientId) {
final raw = tr(
'msg.userfront.consent.client_id',
fallback: 'Client ID: {{id}}',
);
final normalized = raw
.replaceAll('{{id}}', '')
.replaceAll('{id}', '')
.trimRight();
return '$normalized $clientId';
}
Future<void> _fetchConsentInfo() async {
try {
final info = await AuthProxyService.getConsentInfo(
@@ -271,7 +305,7 @@ class _ConsentScreenState extends State<ConsentScreen> {
),
const SizedBox(height: 12),
Text(
tr('msg.userfront.consent.description'),
_renderConsentText('msg.userfront.consent.description'),
style: TextStyle(fontSize: 14, color: Colors.grey[600]),
textAlign: TextAlign.center,
),
@@ -318,11 +352,7 @@ class _ConsentScreenState extends State<ConsentScreen> {
),
const SizedBox(height: 4),
Text(
tr(
'msg.userfront.consent.client_id',
fallback: 'Client ID: {{id}}',
params: {'id': clientId},
),
_renderClientIdLabel(clientId),
style: TextStyle(
fontSize: 12,
color: Colors.grey[500],
@@ -349,11 +379,7 @@ class _ConsentScreenState extends State<ConsentScreen> {
),
),
Text(
tr(
'msg.userfront.consent.scope_count',
fallback: 'Total {{count}}',
params: {'count': '${requestedScopes.length}'},
),
_renderScopeCountLabel(requestedScopes.length),
style: TextStyle(
fontSize: 14,
color: Theme.of(context).primaryColor,
@@ -371,7 +397,7 @@ class _ConsentScreenState extends State<ConsentScreen> {
return CheckboxListTile(
title: Text(
scope, // 스코프 키 (예: openid)
_scopeDisplayLabel(scope),
style: const TextStyle(fontWeight: FontWeight.w500),
),
subtitle: Text(description),

File diff suppressed because one or more lines are too long