feat(i18n): migrate settings literals
Build & Deploy Pluriwave / Análisis de código (push) Successful in 24s
Build & Deploy Pluriwave / Build APK + AAB release (push) Successful in 1m44s

This commit is contained in:
2026-05-22 13:49:12 +02:00
parent 116d878a98
commit 6480c56f99
6 changed files with 1049 additions and 51 deletions
+48 -49
View File
@@ -33,9 +33,9 @@ class PantallaAjustes extends StatelessWidget {
title: l10n.settingsTitle,
subtitle: l10n.settingsSubtitle,
glyph: PluriIconGlyph.settings,
trailing: const PluriStatusPill(
trailing: PluriStatusPill(
icon: Icons.security_rounded,
label: 'Seguro',
label: l10n.settingsSafeStatus,
),
),
const Padding(
@@ -80,8 +80,9 @@ class _SeccionGrabaciones extends StatelessWidget {
Future<void> _seleccionarRuta(BuildContext context) async {
final estado = context.read<EstadoRadio>();
final messenger = ScaffoldMessenger.of(context);
final l10n = AppLocalizations.of(context);
final ruta = await FilePicker.platform.getDirectoryPath(
dialogTitle: 'Selecciona la carpeta de grabaciones',
dialogTitle: l10n.recordingsFolderDialogTitle,
);
if (ruta == null) return;
try {
@@ -91,7 +92,7 @@ class _SeccionGrabaciones extends StatelessWidget {
);
} catch (e) {
messenger.showSnackBar(
SnackBar(content: Text('No se pudo guardar la ruta: $e')),
SnackBar(content: Text(l10n.recordingsPathSaveError(e.toString()))),
);
}
}
@@ -118,7 +119,7 @@ class _SeccionGrabaciones extends StatelessWidget {
const Icon(Icons.radio_button_checked),
const SizedBox(width: 12),
Text(
'Grabaciones',
AppLocalizations.of(context).recordingsSectionTitle,
style: Theme.of(context).textTheme.titleMedium,
),
],
@@ -131,7 +132,7 @@ class _SeccionGrabaciones extends StatelessWidget {
leading: const Icon(Icons.folder_outlined),
title: const Text('Carpeta de grabación'),
subtitle: Text(
snap.data ?? 'Calculando ruta...',
snap.data ?? AppLocalizations.of(context).recordingsPathCalculating,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
@@ -143,13 +144,13 @@ class _SeccionGrabaciones extends StatelessWidget {
Expanded(
child: OutlinedButton.icon(
icon: const Icon(Icons.folder_open_rounded),
label: const Text('Cambiar ruta'),
label: Text(AppLocalizations.of(context).recordingsChangePath),
onPressed: () => _seleccionarRuta(context),
),
),
const SizedBox(width: 8),
IconButton.filledTonal(
tooltip: 'Usar ruta por defecto',
tooltip: AppLocalizations.of(context).recordingsUseDefaultPath,
icon: const Icon(Icons.restore_rounded),
onPressed: () => _restaurarRuta(context),
),
@@ -157,7 +158,7 @@ class _SeccionGrabaciones extends StatelessWidget {
),
const SizedBox(height: 8),
Text(
'La radio se guarda desde el stream original, sin recomprimir.',
AppLocalizations.of(context).recordingsOriginalStreamHint,
style: Theme.of(context).textTheme.bodySmall,
),
],
@@ -456,13 +457,13 @@ class _SeccionEcualizador extends StatelessWidget {
const Icon(Icons.equalizer),
const SizedBox(width: 12),
Text(
'Ecualizador',
AppLocalizations.of(ctx).equalizerTitle,
style: Theme.of(ctx).textTheme.titleMedium,
),
const Spacer(),
Chip(
label: Text(
estado.ecualizadorActivo ? 'Activo' : 'Desactivado',
estado.ecualizadorActivo ? AppLocalizations.of(ctx).equalizerActive : AppLocalizations.of(ctx).equalizerDisabled,
),
visualDensity: VisualDensity.compact,
),
@@ -471,7 +472,7 @@ class _SeccionEcualizador extends StatelessWidget {
const SizedBox(height: 8),
SwitchListTile.adaptive(
contentPadding: EdgeInsets.zero,
title: const Text('Activar ecualizador'),
title: Text(AppLocalizations.of(ctx).equalizerEnable),
subtitle: Text(
disponible
? 'Los cambios se aplican en tiempo real a la emisora actual.'
@@ -484,11 +485,11 @@ class _SeccionEcualizador extends StatelessWidget {
const SizedBox(height: 8),
SwitchListTile.adaptive(
contentPadding: EdgeInsets.zero,
title: const Text('Usar EQ propio para esta favorita'),
title: Text(AppLocalizations.of(ctx).equalizerPerStationTitle),
subtitle: Text(
usandoEqPropio
? 'Activo para ${emisoraActual.nombre}'
: 'Usando EQ principal para ${emisoraActual.nombre}',
? AppLocalizations.of(ctx).equalizerPerStationActive(emisoraActual.nombre)
: AppLocalizations.of(ctx).equalizerPerStationMain(emisoraActual.nombre),
),
value: usandoEqPropio,
onChanged:
@@ -535,7 +536,7 @@ class _SeccionEmisoraPreferida extends StatelessWidget {
const Icon(Icons.radio_rounded),
const SizedBox(width: 12),
Text(
'Emisora preferida',
AppLocalizations.of(context).preferredStationTitle,
style: Theme.of(context).textTheme.titleMedium,
),
],
@@ -593,7 +594,7 @@ class _SeccionEmisoraPreferida extends StatelessWidget {
alignment: Alignment.centerLeft,
child: FilledButton.tonalIcon(
icon: const Icon(Icons.play_arrow_rounded),
label: const Text('Reproducir preferida'),
label: Text(AppLocalizations.of(context).preferredStationPlay),
onPressed:
() => context.read<EstadoRadio>().reproducirEmisoraPreferida(),
),
@@ -634,7 +635,7 @@ class _SeccionEmisoras extends StatelessWidget {
const Icon(Icons.add_circle_outline),
const SizedBox(width: 12),
Text(
'Emisoras personalizadas',
AppLocalizations.of(context).customStationsTitle,
style: Theme.of(context).textTheme.titleMedium,
),
const Spacer(),
@@ -646,11 +647,11 @@ class _SeccionEmisoras extends StatelessWidget {
],
),
if (custom.isEmpty)
const Padding(
padding: EdgeInsets.only(top: 8),
Padding(
padding: const EdgeInsets.only(top: 8),
child: Text(
'No hay emisoras personalizadas.',
style: TextStyle(color: Colors.grey),
AppLocalizations.of(context).customStationsEmpty,
style: const TextStyle(color: Colors.grey),
),
)
else
@@ -669,13 +670,13 @@ class _SeccionEmisoras extends StatelessWidget {
children: [
IconButton(
icon: const Icon(Icons.play_arrow),
tooltip: 'Reproducir',
tooltip: AppLocalizations.of(context).playAction,
onPressed:
() => context.read<EstadoRadio>().reproducir(emisora),
),
IconButton(
icon: const Icon(Icons.delete_outline),
tooltip: 'Eliminar',
tooltip: AppLocalizations.of(context).deleteAction,
onPressed:
() => context
.read<EstadoRadio>()
@@ -753,27 +754,27 @@ class _FormularioEmisoraState extends State<_FormularioEmisora> {
const SizedBox(height: 16),
TextFormField(
controller: _nombreCtrl,
decoration: const InputDecoration(
labelText: 'Nombre *',
border: OutlineInputBorder(),
decoration: InputDecoration(
labelText: AppLocalizations.of(context).stationNameLabel,
border: const OutlineInputBorder(),
),
validator:
(v) =>
v == null || v.trim().isEmpty
? 'Campo obligatorio'
? AppLocalizations.of(context).requiredField
: null,
),
const SizedBox(height: 12),
TextFormField(
controller: _urlCtrl,
decoration: const InputDecoration(
labelText: 'URL del stream *',
decoration: InputDecoration(
labelText: AppLocalizations.of(context).streamUrlLabel,
hintText: 'http://stream.ejemplo.com:8000/radio',
border: OutlineInputBorder(),
border: const OutlineInputBorder(),
),
keyboardType: TextInputType.url,
validator: (v) {
if (v == null || v.trim().isEmpty) return 'Campo obligatorio';
if (v == null || v.trim().isEmpty) return AppLocalizations.of(context).requiredField;
final uri = Uri.tryParse(v.trim());
if (uri == null || !uri.hasScheme) return 'URL no válida';
return null;
@@ -797,7 +798,7 @@ class _FormularioEmisoraState extends State<_FormularioEmisora> {
width: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Guardar emisora'),
: Text(AppLocalizations.of(context).saveStation),
),
],
),
@@ -829,7 +830,7 @@ class _SeccionBackup extends StatelessWidget {
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error al exportar: $e')));
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).backupExportError(e.toString()))));
}
}
}
@@ -858,11 +859,11 @@ class _SeccionBackup extends StatelessWidget {
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: const Text('Cancelar'),
child: Text(AppLocalizations.of(ctx).cancelAction),
),
FilledButton(
onPressed: () => Navigator.pop(ctx, true),
child: const Text('Importar'),
child: Text(AppLocalizations.of(ctx).backupImportTitle),
),
],
),
@@ -883,7 +884,7 @@ class _SeccionBackup extends StatelessWidget {
if (context.mounted) {
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Error al importar: $e')));
).showSnackBar(SnackBar(content: Text(AppLocalizations.of(context).backupImportError(e.toString()))));
}
}
}
@@ -899,7 +900,7 @@ class _SeccionBackup extends StatelessWidget {
const Icon(Icons.backup_outlined),
const SizedBox(width: 12),
Text(
'Copia de seguridad',
AppLocalizations.of(context).backupSectionTitle,
style: Theme.of(context).textTheme.titleMedium,
),
],
@@ -908,16 +909,14 @@ class _SeccionBackup extends StatelessWidget {
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.upload_outlined),
title: const Text('Exportar configuración'),
subtitle: const Text('Favoritos, emisoras custom y presets de EQ'),
subtitle: Text(AppLocalizations.of(context).backupExportSubtitle),
onTap: () => _exportar(context),
),
ListTile(
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.download_outlined),
title: const Text('Importar configuración'),
subtitle: const Text(
'Restaurar desde un fichero de copia de seguridad',
),
subtitle: Text(AppLocalizations.of(context).backupImportSubtitle),
onTap: () => _importar(context),
),
],
@@ -950,7 +949,7 @@ class _SeccionInfo extends StatelessWidget {
variant: PluriIconVariant.filled,
),
title: const Text('PluriWave'),
subtitle: Text('$version - Radio mundial'),
subtitle: Text(AppLocalizations.of(ctx).appVersionSubtitle(version)),
);
},
),
@@ -960,19 +959,19 @@ class _SeccionInfo extends StatelessWidget {
(ctx, snap) => ListTile(
contentPadding: EdgeInsets.zero,
leading: const Icon(Icons.favorite_outline),
title: const Text('Favoritos guardados'),
title: Text(AppLocalizations.of(ctx).savedFavoritesTitle),
trailing: Text(
snap.data?.toString() ?? '',
style: Theme.of(ctx).textTheme.bodyLarge,
),
),
),
const ListTile(
ListTile(
contentPadding: EdgeInsets.zero,
leading: Icon(Icons.verified_outlined),
title: Text('Filtro de emisoras'),
subtitle: Text('Solo emisoras verificadas como activas'),
trailing: Icon(Icons.check_circle, color: Colors.green),
leading: const Icon(Icons.verified_outlined),
title: Text(AppLocalizations.of(ctx).stationFilterTitle),
subtitle: Text(AppLocalizations.of(ctx).stationFilterSubtitle),
trailing: const Icon(Icons.check_circle, color: Colors.green),
),
const ListTile(
contentPadding: EdgeInsets.zero,