diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 9293839..0323540 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "ابحث عن المحطات", + "searchScreenSubtitle": "اعثر على المحطات بالاسم أو البلد أو اللغة باستخدام فلاتر سريعة وتباين عالٍ.", + "searchFiltersLabel": "الفلاتر", + "searchHint": "راديو هورايزن، جاز، أخبار...", + "searchCountryFilterLabel": "البلد", + "searchLanguageFilterLabel": "اللغة", + "searchMinQualityFilterLabel": "الحد الأدنى للجودة", + "searchEmptyTitle": "ابحث عن محطة", + "searchNoResultsTitle": "لا توجد نتائج", + "searchEmptySubtitle": "استخدم الشريط العلوي أو الوسوم لاكتشاف محطات من جميع أنحاء العالم.", + "searchNoResultsSubtitle": "جرّب إزالة الفلاتر أو كتابة اسم آخر للعثور على محطة نشطة.", + "countrySpain": "إسبانيا", + "countryUsa": "الولايات المتحدة", + "countryMexico": "المكسيك", + "countryArgentina": "الأرجنتين", + "countryUk": "المملكة المتحدة", + "countryFrance": "فرنسا", + "countryGermany": "ألمانيا", + "countryItaly": "إيطاليا", + "countryBrazil": "البرازيل", + "countryJapan": "اليابان", + "languageNameSpanish": "الإسبانية", + "languageNameEnglish": "الإنجليزية", + "languageNameFrench": "الفرنسية", + "languageNameGerman": "الألمانية", + "languageNamePortuguese": "البرتغالية", + "languageNameItalian": "الإيطالية", + "languageNameJapanese": "اليابانية", + "languageNameArabic": "العربية", + "languageNameRussian": "الروسية", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_bn.arb b/lib/l10n/app_bn.arb index 1a40298..435ab39 100644 --- a/lib/l10n/app_bn.arb +++ b/lib/l10n/app_bn.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "সিগন্যাল খুঁজুন", + "searchScreenSubtitle": "দ্রুত ফিল্টার আর উচ্চ কনট্রাস্টে নাম, দেশ বা ভাষা দিয়ে স্টেশন খুঁজে নিন।", + "searchFiltersLabel": "ফিল্টার", + "searchHint": "রেডিও হরাইজন, জ্যাজ, খবর...", + "searchCountryFilterLabel": "দেশ", + "searchLanguageFilterLabel": "ভাষা", + "searchMinQualityFilterLabel": "ন্যূনতম মান", + "searchEmptyTitle": "একটি স্টেশন খুঁজুন", + "searchNoResultsTitle": "কোনো ফলাফল নেই", + "searchEmptySubtitle": "উপরের বার বা চিপগুলো ব্যবহার করে সারা বিশ্বের স্টেশন খুঁজে দেখুন।", + "searchNoResultsSubtitle": "ফিল্টার সরিয়ে বা অন্য নাম লিখে একটি সক্রিয় স্টেশন খুঁজে দেখুন।", + "countrySpain": "স্পেন", + "countryUsa": "যুক্তরাষ্ট্র", + "countryMexico": "মেক্সিকো", + "countryArgentina": "আর্জেন্টিনা", + "countryUk": "যুক্তরাজ্য", + "countryFrance": "ফ্রান্স", + "countryGermany": "জার্মানি", + "countryItaly": "ইতালি", + "countryBrazil": "ব্রাজিল", + "countryJapan": "জাপান", + "languageNameSpanish": "স্প্যানিশ", + "languageNameEnglish": "ইংরেজি", + "languageNameFrench": "ফরাসি", + "languageNameGerman": "জার্মান", + "languageNamePortuguese": "পর্তুগিজ", + "languageNameItalian": "ইতালীয়", + "languageNameJapanese": "জাপানি", + "languageNameArabic": "আরবি", + "languageNameRussian": "রুশ", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 68192fd..84c4f68 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", + "searchScreenTitle": "Signal suchen", + "searchScreenSubtitle": "Finde Sender nach Name, Land oder Sprache mit schnellen Filtern und hohem Kontrast.", + "searchFiltersLabel": "Filter", + "searchHint": "Radio Horizont, Jazz, Nachrichten...", + "searchCountryFilterLabel": "Land", + "searchLanguageFilterLabel": "Sprache", + "searchMinQualityFilterLabel": "Mindestqualität", + "searchEmptyTitle": "Suche nach einem Sender", + "searchNoResultsTitle": "Keine Ergebnisse", + "searchEmptySubtitle": "Nutze die obere Leiste oder die Chips, um Sender aus aller Welt zu entdecken.", + "searchNoResultsSubtitle": "Versuche, Filter zu entfernen oder einen anderen Namen einzugeben, um einen aktiven Sender zu finden.", + "countrySpain": "Spanien", "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", + "countryMexico": "Mexiko", + "countryArgentina": "Argentinien", + "countryUk": "Vereinigtes Königreich", + "countryFrance": "Frankreich", + "countryGermany": "Deutschland", + "countryItaly": "Italien", + "countryBrazil": "Brasilien", "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "languageNameSpanish": "Spanisch", + "languageNameEnglish": "Englisch", + "languageNameFrench": "Französisch", + "languageNameGerman": "Deutsch", + "languageNamePortuguese": "Portugiesisch", + "languageNameItalian": "Italienisch", + "languageNameJapanese": "Japanisch", + "languageNameArabic": "Arabisch", + "languageNameRussian": "Russisch", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 35779dd..0b1022c 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -226,38 +226,38 @@ "stationName": {} } }, - "alarmPostponedCurrentExecution": "Alarma pospuesta para esta ejecuci?n.", - "searchScreenTitle": "Buscar se?al", - "searchScreenSubtitle": "Encontr? radios por nombre, pa?s o idioma con filtros r?pidos y alto contraste.", + "alarmPostponedCurrentExecution": "Alarma pospuesta para esta ejecución.", + "searchScreenTitle": "Buscar señal", + "searchScreenSubtitle": "Encontrá radios por nombre, país o idioma con filtros rápidos y alto contraste.", "searchFiltersLabel": "Filtros", "searchHint": "Radio Horizonte, jazz, noticias...", - "searchCountryFilterLabel": "Pa?s", + "searchCountryFilterLabel": "País", "searchLanguageFilterLabel": "Idioma", - "searchMinQualityFilterLabel": "Calidad m?nima", - "searchEmptyTitle": "Busc? una emisora", + "searchMinQualityFilterLabel": "Calidad mínima", + "searchEmptyTitle": "Buscá una emisora", "searchNoResultsTitle": "Sin resultados", - "searchEmptySubtitle": "Us? la barra superior o los chips para descubrir se?ales de todo el mundo.", - "searchNoResultsSubtitle": "Prob? quitar filtros o escribir otro nombre para encontrar una se?al activa.", - "countrySpain": "Espa?a", + "searchEmptySubtitle": "Usá la barra superior o los chips para descubrir señales de todo el mundo.", + "searchNoResultsSubtitle": "Probá quitar filtros o escribir otro nombre para encontrar una señal activa.", + "countrySpain": "España", "countryUsa": "EE. UU.", - "countryMexico": "M?xico", + "countryMexico": "México", "countryArgentina": "Argentina", "countryUk": "Reino Unido", "countryFrance": "Francia", "countryGermany": "Alemania", "countryItaly": "Italia", "countryBrazil": "Brasil", - "countryJapan": "Jap?n", - "languageNameSpanish": "Espa?ol", - "languageNameEnglish": "Ingl?s", - "languageNameFrench": "Franc?s", - "languageNameGerman": "Alem?n", - "languageNamePortuguese": "Portugu?s", + "countryJapan": "Japón", + "languageNameSpanish": "Español", + "languageNameEnglish": "Inglés", + "languageNameFrench": "Francés", + "languageNameGerman": "Alemán", + "languageNamePortuguese": "Portugués", "languageNameItalian": "Italiano", - "languageNameJapanese": "Japon?s", - "languageNameArabic": "?rabe", + "languageNameJapanese": "Japonés", + "languageNameArabic": "Árabe", "languageNameRussian": "Ruso", - "homeScreenSubtitle": "Radio global en vivo con se?ales limpias, favoritos inteligentes y una experiencia visual de concurso.", + "homeScreenSubtitle": "Radio global en vivo con señales limpias, favoritos inteligentes y una experiencia visual de concurso.", "exploreStations": "Explorar emisoras", "stationsCount": "{count} radios", "@stationsCount": { @@ -269,7 +269,7 @@ }, "qualityHd": "Calidad HD", "nearYou": "Cerca de vos", - "nearYouInCountry": "Cerca de vos ? {country}", + "nearYouInCountry": "Cerca de vos · {country}", "@nearYouInCountry": { "placeholders": { "country": {} @@ -277,15 +277,15 @@ }, "detectAction": "Detectar", "liveRadar": "Radar en directo", - "genresTitle": "G?neros", + "genresTitle": "Géneros", "retryAction": "Reintentar", "noStationsAvailable": "No hay emisoras disponibles", - "noStationsAvailableSubtitle": "Prob? refrescar o elegir otro g?nero para volver a capturar se?al.", + "noStationsAvailableSubtitle": "Probá refrescar o elegir otro género para volver a capturar señal.", "genrePop": "Pop", "genreRock": "Rock", "genreJazz": "Jazz", - "genreClassical": "Cl?sica", - "genreElectronic": "Electr?nica", + "genreClassical": "Clásica", + "genreElectronic": "Electrónica", "genreNews": "Noticias", "genreTalk": "Charlas", "genreHipHop": "Hip-hop", @@ -294,7 +294,7 @@ "genreReggae": "Reggae", "genreLatin": "Latina", "alarmScreenTitle": "Despertar musical", - "alarmScreenSubtitle": "Alarmas con radio, sonido seguro, vacaciones inteligentes y pr?xima ejecuci?n siempre visible.", + "alarmScreenSubtitle": "Alarmas con radio, sonido seguro, vacaciones inteligentes y próxima ejecución siempre visible.", "createAlarmAction": "Crear alarma", "alarmsCount": "{count} alarmas", "@alarmsCount": { @@ -304,10 +304,10 @@ } } }, - "activeAlarmsWithoutNextTitle": "Alarmas activas sin pr?xima ejecuci?n", + "activeAlarmsWithoutNextTitle": "Alarmas activas sin próxima ejecución", "noActiveAlarms": "Sin alarmas activas", - "nextAlarmTitle": "Pr?xima alarma", - "activeAlarmsWithoutNextSubtitle": "Hay {count} alarma(s) activas, pero ahora mismo no tienen una fecha futura v?lida. Revis? fecha, d?as y vacaciones.", + "nextAlarmTitle": "Próxima alarma", + "activeAlarmsWithoutNextSubtitle": "Hay {count} alarma(s) activas, pero ahora mismo no tienen una fecha futura válida. Revisá fecha, días y vacaciones.", "@activeAlarmsWithoutNextSubtitle": { "placeholders": { "count": { @@ -315,7 +315,7 @@ } } }, - "createAlarmHint": "Cre? una alarma y PluriWave calcular? la siguiente ejecuci?n autom?ticamente.", + "createAlarmHint": "Creá una alarma y PluriWave calculará la siguiente ejecución automáticamente.", "alarmVacationPlay": "Suena en vacaciones", "alarmVacationPause": "Pausa en vacaciones", "alarmFadeInLabel": "Fade-in {seconds}s", @@ -326,14 +326,14 @@ } } }, - "alarmNextExecution": "Siguiente ejecuci?n: {date}", + "alarmNextExecution": "Siguiente ejecución: {date}", "@alarmNextExecution": { "placeholders": { "date": {} } }, - "alarmNoNextExecution": "No tiene pr?xima ejecuci?n activa.", - "alarmSkippedExecution": "Una ejecuci?n fue omitida: {date}.", + "alarmNoNextExecution": "No tiene próxima ejecución activa.", + "alarmSkippedExecution": "Una ejecución fue omitida: {date}.", "@alarmSkippedExecution": { "placeholders": { "date": {} @@ -342,27 +342,27 @@ "editAction": "Editar", "skipNextAction": "Omitir siguiente", "deleteTooltip": "Eliminar", - "alarmSkippedNoNextSnackbar": "Alarma omitida. No queda pr?xima ejecuci?n.", - "alarmSkippedReturnsSnackbar": "Alarma omitida. Volver? el {date}.", + "alarmSkippedNoNextSnackbar": "Alarma omitida. No queda próxima ejecución.", + "alarmSkippedReturnsSnackbar": "Alarma omitida. Volverá el {date}.", "@alarmSkippedReturnsSnackbar": { "placeholders": { "date": {} } }, - "alarmVacationPausedNoNext": "Est? pausada por vacaciones ({vacationName}) y sin pr?xima ejecuci?n.", + "alarmVacationPausedNoNext": "Está pausada por vacaciones ({vacationName}) y sin próxima ejecución.", "@alarmVacationPausedNoNext": { "placeholders": { "vacationName": {} } }, - "alarmVacationPausedReturns": "Est? pausada por vacaciones ({vacationName}) y vuelve el {date}.", + "alarmVacationPausedReturns": "Está pausada por vacaciones ({vacationName}) y vuelve el {date}.", "@alarmVacationPausedReturns": { "placeholders": { "vacationName": {}, "date": {} } }, - "alarmVacationReturns": "Con vacaciones activas, volver? a sonar el {date}.", + "alarmVacationReturns": "Con vacaciones activas, volverá a sonar el {date}.", "@alarmVacationReturns": { "placeholders": { "date": {} @@ -376,10 +376,10 @@ "dateField": "Fecha", "onceOption": "Una vez", "dailyOption": "Diaria", - "weekdaysOption": "D?as", + "weekdaysOption": "Días", "soundAndVolumeSection": "Sonido y volumen", "alarmFadeInTitle": "Fade-in de alarma", - "alarmFadeInOff": "0 s (sin transici?n)", + "alarmFadeInOff": "0 s (sin transición)", "alarmFadeInSummary": "{seconds} s (de 5% al volumen elegido)", "@alarmFadeInSummary": { "placeholders": { @@ -389,21 +389,21 @@ } }, "internalSafeSoundLabel": "Sonido seguro interno", - "soundWarmSunrise": "Amanecer c?lido", + "soundWarmSunrise": "Amanecer cálido", "soundSoftBell": "Campana suave", "soundDigitalPulse": "Pulso digital", "favoriteStationLabel": "Emisora favorita", "noStationUseInternalSound": "Sin emisora: usar sonido interno", - "saveFavoritesAlarmHint": "Guard? emisoras en Favoritos para usarlas como alarma musical.", + "saveFavoritesAlarmHint": "Guardá emisoras en Favoritos para usarlas como alarma musical.", "useCurrentStationAction": "Usar emisora actual", "playDuringVacations": "Sonar durante vacaciones", - "playDuringVacationsHint": "Si lo apag?s, la pr?xima ejecuci?n saltar? al primer d?a v?lido.", + "playDuringVacationsHint": "Si lo apagás, la próxima ejecución saltará al primer día válido.", "saveAlarmAction": "Guardar alarma", - "chooseOneWeekdayError": "Eleg? al menos un d?a de la semana.", + "chooseOneWeekdayError": "Elegí al menos un día de la semana.", "androidReliabilityReview": "Revisar fiabilidad Android", "statusOk": "OK", "statusPending": "pendiente", - "androidReliabilityStatus": "Fiabilidad: exactas {exact} ? notificaciones {notifications} ? pantalla {screen}", + "androidReliabilityStatus": "Fiabilidad: exactas {exact} · notificaciones {notifications} · pantalla {screen}", "@androidReliabilityStatus": { "placeholders": { "exact": {}, @@ -413,7 +413,7 @@ }, "vacationRangesTitle": "Rangos de vacaciones", "addAction": "Agregar", - "vacationRangesHint": "Si una alarma tiene \"Pausa en vacaciones\", se salta autom?ticamente estos rangos.", + "vacationRangesHint": "Si una alarma tiene \"Pausa en vacaciones\", se salta automáticamente estos rangos.", "noVacationRangesLoaded": "Sin rangos cargados.", "deleteRangeTooltip": "Eliminar rango", "vacationsDefaultName": "Vacaciones", @@ -421,8 +421,8 @@ "startField": "Inicio", "endField": "Fin", "saveRangeAction": "Guardar rango", - "noAlarmsYetTitle": "Todav?a no hay alarmas.", - "noAlarmsYetSubtitle": "Cre? una para dise?ar tu despertar musical.", + "noAlarmsYetTitle": "Todavía no hay alarmas.", + "noAlarmsYetSubtitle": "Creá una para diseñar tu despertar musical.", "ringingInternalAudioActive": "Sonando con audio seguro interno.", "ringingPreparingInternalAudio": "Preparando audio seguro interno.", "stopAlarmAction": "Detener alarma" diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index eb0aaf5..a671b6d 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", + "searchScreenTitle": "Rechercher un signal", + "searchScreenSubtitle": "Trouvez des stations par nom, pays ou langue grâce à des filtres rapides et à un contraste élevé.", + "searchFiltersLabel": "Filtres", + "searchHint": "Radio Horizon, jazz, infos...", + "searchCountryFilterLabel": "Pays", + "searchLanguageFilterLabel": "Langue", + "searchMinQualityFilterLabel": "Qualité minimale", + "searchEmptyTitle": "Recherchez une station", + "searchNoResultsTitle": "Aucun résultat", + "searchEmptySubtitle": "Utilisez la barre du haut ou les pastilles pour découvrir des stations du monde entier.", + "searchNoResultsSubtitle": "Essayez de retirer des filtres ou de saisir un autre nom pour trouver une station active.", + "countrySpain": "Espagne", + "countryUsa": "États-Unis", + "countryMexico": "Mexique", + "countryArgentina": "Argentine", + "countryUk": "Royaume-Uni", "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "countryGermany": "Allemagne", + "countryItaly": "Italie", + "countryBrazil": "Brésil", + "countryJapan": "Japon", + "languageNameSpanish": "espagnol", + "languageNameEnglish": "anglais", + "languageNameFrench": "français", + "languageNameGerman": "allemand", + "languageNamePortuguese": "portugais", + "languageNameItalian": "italien", + "languageNameJapanese": "japonais", + "languageNameArabic": "arabe", + "languageNameRussian": "russe", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb index afc1c59..85ef4e4 100644 --- a/lib/l10n/app_hi.arb +++ b/lib/l10n/app_hi.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "सिग्नल खोजें", + "searchScreenSubtitle": "तेज़ फ़िल्टर और उच्च कॉन्ट्रास्ट के साथ नाम, देश या भाषा से स्टेशन खोजें।", + "searchFiltersLabel": "फ़िल्टर", + "searchHint": "रेडियो होराइज़न, जैज़, समाचार...", + "searchCountryFilterLabel": "देश", + "searchLanguageFilterLabel": "भाषा", + "searchMinQualityFilterLabel": "न्यूनतम गुणवत्ता", + "searchEmptyTitle": "कोई स्टेशन खोजें", + "searchNoResultsTitle": "कोई नतीजा नहीं", + "searchEmptySubtitle": "दुनिया भर के स्टेशन खोजने के लिए ऊपर की बार या चिप्स का इस्तेमाल करें।", + "searchNoResultsSubtitle": "फ़िल्टर हटाकर देखें या सक्रिय स्टेशन खोजने के लिए कोई दूसरा नाम लिखें।", + "countrySpain": "स्पेन", + "countryUsa": "अमेरिका", + "countryMexico": "मेक्सिको", + "countryArgentina": "अर्जेंटीना", + "countryUk": "यूनाइटेड किंगडम", + "countryFrance": "फ़्रांस", + "countryGermany": "जर्मनी", + "countryItaly": "इटली", + "countryBrazil": "ब्राज़ील", + "countryJapan": "जापान", + "languageNameSpanish": "स्पैनिश", + "languageNameEnglish": "अंग्रेज़ी", + "languageNameFrench": "फ़्रेंच", + "languageNameGerman": "जर्मन", + "languageNamePortuguese": "पुर्तगाली", + "languageNameItalian": "इतालवी", + "languageNameJapanese": "जापानी", + "languageNameArabic": "अरबी", + "languageNameRussian": "रूसी", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index b6bcd0d..5c40cfe 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", + "searchScreenTitle": "Cari sinyal", + "searchScreenSubtitle": "Temukan stasiun berdasarkan nama, negara, atau bahasa dengan filter cepat dan kontras tinggi.", + "searchFiltersLabel": "Filter", + "searchHint": "Radio Horizon, jazz, berita...", + "searchCountryFilterLabel": "Negara", + "searchLanguageFilterLabel": "Bahasa", + "searchMinQualityFilterLabel": "Kualitas minimum", + "searchEmptyTitle": "Cari stasiun", + "searchNoResultsTitle": "Tidak ada hasil", + "searchEmptySubtitle": "Gunakan bilah atas atau chip untuk menemukan stasiun dari seluruh dunia.", + "searchNoResultsSubtitle": "Coba hapus filter atau ketik nama lain untuk menemukan stasiun yang aktif.", + "countrySpain": "Spanyol", + "countryUsa": "Amerika Serikat", + "countryMexico": "Meksiko", "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "countryUk": "Britania Raya", + "countryFrance": "Prancis", + "countryGermany": "Jerman", + "countryItaly": "Italia", + "countryBrazil": "Brasil", + "countryJapan": "Jepang", + "languageNameSpanish": "Spanyol", + "languageNameEnglish": "Inggris", + "languageNameFrench": "Prancis", + "languageNameGerman": "Jerman", + "languageNamePortuguese": "Portugis", + "languageNameItalian": "Italia", + "languageNameJapanese": "Jepang", + "languageNameArabic": "Arab", + "languageNameRussian": "Rusia", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 87955b2..bcf2b07 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", + "searchScreenTitle": "Cerca segnale", + "searchScreenSubtitle": "Trova emittenti per nome, Paese o lingua con filtri rapidi e contrasto elevato.", + "searchFiltersLabel": "Filtri", + "searchHint": "Radio Orizzonte, jazz, notizie...", + "searchCountryFilterLabel": "Paese", + "searchLanguageFilterLabel": "Lingua", + "searchMinQualityFilterLabel": "Qualità minima", + "searchEmptyTitle": "Cerca un'emittente", + "searchNoResultsTitle": "Nessun risultato", + "searchEmptySubtitle": "Usa la barra in alto o i chip per scoprire emittenti da tutto il mondo.", + "searchNoResultsSubtitle": "Prova a rimuovere i filtri o a digitare un altro nome per trovare un'emittente attiva.", + "countrySpain": "Spagna", "countryUsa": "USA", - "countryMexico": "Mexico", + "countryMexico": "Messico", "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "countryUk": "Regno Unito", + "countryFrance": "Francia", + "countryGermany": "Germania", + "countryItaly": "Italia", + "countryBrazil": "Brasile", + "countryJapan": "Giappone", + "languageNameSpanish": "spagnolo", + "languageNameEnglish": "inglese", + "languageNameFrench": "francese", + "languageNameGerman": "tedesco", + "languageNamePortuguese": "portoghese", + "languageNameItalian": "italiano", + "languageNameJapanese": "giapponese", + "languageNameArabic": "arabo", + "languageNameRussian": "russo", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb index d42c32e..98e549a 100644 --- a/lib/l10n/app_ja.arb +++ b/lib/l10n/app_ja.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "放送局を探す", + "searchScreenSubtitle": "名前・国・言語から、すばやい絞り込みと高コントラスト表示で放送局を見つけましょう。", + "searchFiltersLabel": "絞り込み", + "searchHint": "Radio Horizon、ジャズ、ニュース...", + "searchCountryFilterLabel": "国", + "searchLanguageFilterLabel": "言語", + "searchMinQualityFilterLabel": "最低品質", + "searchEmptyTitle": "放送局を検索", + "searchNoResultsTitle": "結果がありません", + "searchEmptySubtitle": "上部のバーやチップを使って、世界中の放送局を見つけましょう。", + "searchNoResultsSubtitle": "フィルターを減らすか別の名前で入力して、配信中の放送局を探してみましょう。", + "countrySpain": "スペイン", + "countryUsa": "アメリカ", + "countryMexico": "メキシコ", + "countryArgentina": "アルゼンチン", + "countryUk": "イギリス", + "countryFrance": "フランス", + "countryGermany": "ドイツ", + "countryItaly": "イタリア", + "countryBrazil": "ブラジル", + "countryJapan": "日本", + "languageNameSpanish": "スペイン語", + "languageNameEnglish": "英語", + "languageNameFrench": "フランス語", + "languageNameGerman": "ドイツ語", + "languageNamePortuguese": "ポルトガル語", + "languageNameItalian": "イタリア語", + "languageNameJapanese": "日本語", + "languageNameArabic": "アラビア語", + "languageNameRussian": "ロシア語", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_localizations_ext.dart b/lib/l10n/app_localizations_ext.dart new file mode 100644 index 0000000..8a372ad --- /dev/null +++ b/lib/l10n/app_localizations_ext.dart @@ -0,0 +1,932 @@ +import 'gen/app_localizations.dart'; + +extension PluriAppLocalizationsExt on AppLocalizations { + String _lang() { + final locale = localeName.toLowerCase(); + if (locale.startsWith('es')) return 'es'; + if (locale.startsWith('en')) return 'en'; + if (locale.startsWith('fr')) return 'fr'; + if (locale.startsWith('de')) return 'de'; + if (locale.startsWith('it')) return 'it'; + if (locale.startsWith('pt')) return 'pt'; + if (locale.startsWith('ru')) return 'ru'; + if (locale.startsWith('ja')) return 'ja'; + if (locale.startsWith('zh')) return 'zh'; + if (locale.startsWith('ar')) return 'ar'; + if (locale.startsWith('hi')) return 'hi'; + if (locale.startsWith('bn')) return 'bn'; + if (locale.startsWith('id')) return 'id'; + return 'en'; + } + + String _pick(Map values) => + values[_lang()] ?? values['en'] ?? values.values.first; + + String get searchScreenTitle => _pick({ + 'es': 'Buscar señal', + 'en': 'Search station', + 'fr': 'Rechercher une station', + 'de': 'Sender suchen', + 'it': 'Cerca stazione', + 'pt': 'Buscar estação', + 'ru': 'Поиск станций', + 'ja': '放送局を探す', + 'zh': '搜索电台', + 'ar': 'ابحث عن محطة', + 'hi': 'स्टेशन खोजें', + 'bn': 'স্টেশন খুঁজুন', + 'id': 'Cari stasiun', + }); + + String get searchScreenSubtitle => _pick({ + 'es': 'Encontrá radios por nombre, país o idioma con filtros rápidos y alto contraste.', + 'en': 'Find stations by name, country, or language with quick filters and high contrast.', + 'fr': 'Trouvez des stations par nom, pays ou langue avec des filtres rapides et un contraste élevé.', + 'de': 'Finde Sender nach Name, Land oder Sprache mit schnellen Filtern und hohem Kontrast.', + 'it': 'Trova stazioni per nome, paese o lingua con filtri rapidi e alto contrasto.', + 'pt': 'Encontre estações por nome, país ou idioma com filtros rápidos e alto contraste.', + 'ru': 'Ищите станции по названию, стране или языку с быстрыми фильтрами и высокой контрастностью.', + 'ja': '名前・国・言語で放送局を検索できます。すばやいフィルターと高コントラスト表示に対応しています。', + 'zh': '可按名称、国家或语言查找电台,并提供快速筛选和高对比度显示。', + 'ar': 'اعثر على المحطات حسب الاسم أو البلد أو اللغة باستخدام فلاتر سريعة وتباين عالٍ.', + 'hi': 'नाम, देश या भाषा के आधार पर स्टेशन खोजें, तेज़ फ़िल्टर और उच्च कंट्रास्ट के साथ।', + 'bn': 'নাম, দেশ বা ভাষা অনুযায়ী স্টেশন খুঁজুন—দ্রুত ফিল্টার ও উচ্চ কনট্রাস্টসহ।', + 'id': 'Temukan stasiun berdasarkan nama, negara, atau bahasa dengan filter cepat dan kontras tinggi.', + }); + + String get searchFiltersLabel => _pick({ + 'es': 'Filtros', + 'en': 'Filters', + 'fr': 'Filtres', + 'de': 'Filter', + 'it': 'Filtri', + 'pt': 'Filtros', + 'ru': 'Фильтры', + 'ja': '絞り込み', + 'zh': '筛选', + 'ar': 'الفلاتر', + 'hi': 'फ़िल्टर', + 'bn': 'ফিল্টার', + 'id': 'Filter', + }); + + String get searchHint => _pick({ + 'es': 'Nombre de emisora, país o idioma', + 'en': 'Station name, country, or language', + 'fr': 'Nom de la station, pays ou langue', + 'de': 'Sendername, Land oder Sprache', + 'it': 'Nome stazione, paese o lingua', + 'pt': 'Nome da estação, país ou idioma', + 'ru': 'Название станции, страна или язык', + 'ja': '放送局名・国・言語', + 'zh': '电台名、国家或语言', + 'ar': 'اسم المحطة أو البلد أو اللغة', + 'hi': 'स्टेशन का नाम, देश या भाषा', + 'bn': 'স্টেশনের নাম, দেশ বা ভাষা', + 'id': 'Nama stasiun, negara, atau bahasa', + }); + + String get searchCountryFilterLabel => _pick({ + 'es': 'País', + 'en': 'Country', + 'fr': 'Pays', + 'de': 'Land', + 'it': 'Paese', + 'pt': 'País', + 'ru': 'Страна', + 'ja': '国', + 'zh': '国家', + 'ar': 'البلد', + 'hi': 'देश', + 'bn': 'দেশ', + 'id': 'Negara', + }); + + String get searchLanguageFilterLabel => _pick({ + 'es': 'Idioma', + 'en': 'Language', + 'fr': 'Langue', + 'de': 'Sprache', + 'it': 'Lingua', + 'pt': 'Idioma', + 'ru': 'Язык', + 'ja': '言語', + 'zh': '语言', + 'ar': 'اللغة', + 'hi': 'भाषा', + 'bn': 'ভাষা', + 'id': 'Bahasa', + }); + + String get searchMinQualityFilterLabel => _pick({ + 'es': 'Calidad mínima', + 'en': 'Minimum quality', + 'fr': 'Qualité minimale', + 'de': 'Mindestqualität', + 'it': 'Qualità minima', + 'pt': 'Qualidade mínima', + 'ru': 'Минимальное качество', + 'ja': '最低音質', + 'zh': '最低质量', + 'ar': 'الحد الأدنى للجودة', + 'hi': 'न्यूनतम गुणवत्ता', + 'bn': 'সর্বনিম্ন মান', + 'id': 'Kualitas minimum', + }); + + String get searchEmptyTitle => _pick({ + 'es': 'Buscá una emisora', + 'en': 'Search for a station', + 'fr': 'Rechercher une station', + 'de': 'Suche nach einem Sender', + 'it': 'Cerca una stazione', + 'pt': 'Busque uma estação', + 'ru': 'Найдите станцию', + 'ja': '放送局を検索', + 'zh': '搜索电台', + 'ar': 'ابحث عن محطة', + 'hi': 'किसी स्टेशन को खोजें', + 'bn': 'একটি স্টেশন খুঁজুন', + 'id': 'Cari sebuah stasiun', + }); + + String get searchNoResultsTitle => _pick({ + 'es': 'Sin resultados', + 'en': 'No results', + 'fr': 'Aucun résultat', + 'de': 'Keine Ergebnisse', + 'it': 'Nessun risultato', + 'pt': 'Sem resultados', + 'ru': 'Ничего не найдено', + 'ja': '結果がありません', + 'zh': '没有结果', + 'ar': 'لا توجد نتائج', + 'hi': 'कोई परिणाम नहीं', + 'bn': 'কোনও ফলাফল নেই', + 'id': 'Tidak ada hasil', + }); + + String get searchEmptySubtitle => _pick({ + 'es': 'Usá la barra superior o los chips para descubrir señales de todo el mundo.', + 'en': 'Use the top bar or the chips to discover stations from around the world.', + 'fr': 'Utilisez la barre supérieure ou les filtres pour découvrir des stations du monde entier.', + 'de': 'Nutze die obere Leiste oder die Chips, um Sender aus aller Welt zu entdecken.', + 'it': 'Usa la barra superiore o i filtri per scoprire stazioni da tutto il mondo.', + 'pt': 'Use a barra superior ou os chips para descobrir estações do mundo todo.', + 'ru': 'Используйте верхнюю строку или фильтры, чтобы находить станции со всего мира.', + 'ja': '上部バーやチップを使って、世界中の放送局を見つけましょう。', + 'zh': '使用顶部搜索栏或筛选标签,发现来自世界各地的电台。', + 'ar': 'استخدم الشريط العلوي أو الشرائح لاكتشاف محطات من جميع أنحاء العالم.', + 'hi': 'दुनिया भर के स्टेशन खोजने के लिए ऊपर की बार या चिप्स का उपयोग करें।', + 'bn': 'সারা বিশ্বের স্টেশন খুঁজতে উপরের বার বা চিপগুলো ব্যবহার করুন।', + 'id': 'Gunakan bilah atas atau chip untuk menemukan stasiun dari seluruh dunia.', + }); + + String get searchNoResultsSubtitle => _pick({ + 'es': 'Probá quitar filtros o escribir otro nombre para encontrar una señal activa.', + 'en': 'Try removing filters or typing another name to find an active station.', + 'fr': 'Essayez de retirer des filtres ou de saisir un autre nom pour trouver une station active.', + 'de': 'Versuche, Filter zu entfernen oder einen anderen Namen einzugeben, um einen aktiven Sender zu finden.', + 'it': 'Prova a rimuovere i filtri o a digitare un altro nome per trovare una stazione attiva.', + 'pt': 'Tente remover os filtros ou digitar outro nome para encontrar uma estação ativa.', + 'ru': 'Попробуйте убрать фильтры или ввести другое название, чтобы найти активную станцию.', + 'ja': 'フィルターを減らすか別の名前を入力して、再生可能な放送局を探してみてください。', + 'zh': '试着移除筛选条件或输入其他名称,以找到可用电台。', + 'ar': 'جرّب إزالة بعض الفلاتر أو كتابة اسم آخر للعثور على محطة نشطة.', + 'hi': 'सक्रिय स्टेशन खोजने के लिए फ़िल्टर हटाएँ या कोई दूसरा नाम लिखें।', + 'bn': 'সক্রিয় স্টেশন খুঁজতে কিছু ফিল্টার সরান বা অন্য নাম লিখুন।', + 'id': 'Coba hapus filter atau ketik nama lain untuk menemukan stasiun yang aktif.', + }); + + String get countrySpain => _pick({ + 'es': 'España', + 'en': 'Spain', + 'fr': 'Espagne', + 'de': 'Spanien', + 'it': 'Spagna', + 'pt': 'Espanha', + 'ru': 'Испания', + 'ja': 'スペイン', + 'zh': '西班牙', + 'ar': 'إسبانيا', + 'hi': 'स्पेन', + 'bn': 'স্পেন', + 'id': 'Spanyol', + }); + + String get countryUsa => _pick({ + 'es': 'Estados Unidos', + 'en': 'United States', + 'fr': 'États-Unis', + 'de': 'Vereinigte Staaten', + 'it': 'Stati Uniti', + 'pt': 'Estados Unidos', + 'ru': 'США', + 'ja': 'アメリカ合衆国', + 'zh': '美国', + 'ar': 'الولايات المتحدة', + 'hi': 'संयुक्त राज्य', + 'bn': 'যুক্তরাষ্ট্র', + 'id': 'Amerika Serikat', + }); + + String get countryMexico => _pick({ + 'es': 'México', + 'en': 'Mexico', + 'fr': 'Mexique', + 'de': 'Mexiko', + 'it': 'Messico', + 'pt': 'México', + 'ru': 'Мексика', + 'ja': 'メキシコ', + 'zh': '墨西哥', + 'ar': 'المكسيك', + 'hi': 'मेक्सिको', + 'bn': 'মেক্সিকো', + 'id': 'Meksiko', + }); + + String get countryArgentina => _pick({ + 'es': 'Argentina', + 'en': 'Argentina', + 'fr': 'Argentine', + 'de': 'Argentinien', + 'it': 'Argentina', + 'pt': 'Argentina', + 'ru': 'Аргентина', + 'ja': 'アルゼンチン', + 'zh': '阿根廷', + 'ar': 'الأرجنتين', + 'hi': 'अर्जेंटीना', + 'bn': 'আর্জেন্টিনা', + 'id': 'Argentina', + }); + + String get countryUk => _pick({ + 'es': 'Reino Unido', + 'en': 'United Kingdom', + 'fr': 'Royaume-Uni', + 'de': 'Vereinigtes Königreich', + 'it': 'Regno Unito', + 'pt': 'Reino Unido', + 'ru': 'Великобритания', + 'ja': 'イギリス', + 'zh': '英国', + 'ar': 'المملكة المتحدة', + 'hi': 'यूनाइटेड किंगडम', + 'bn': 'যুক্তরাজ্য', + 'id': 'Britania Raya', + }); + + String get countryFrance => _pick({ + 'es': 'Francia', + 'en': 'France', + 'fr': 'France', + 'de': 'Frankreich', + 'it': 'Francia', + 'pt': 'França', + 'ru': 'Франция', + 'ja': 'フランス', + 'zh': '法国', + 'ar': 'فرنسا', + 'hi': 'फ़्रांस', + 'bn': 'ফ্রান্স', + 'id': 'Prancis', + }); + + String get countryGermany => _pick({ + 'es': 'Alemania', + 'en': 'Germany', + 'fr': 'Allemagne', + 'de': 'Deutschland', + 'it': 'Germania', + 'pt': 'Alemanha', + 'ru': 'Германия', + 'ja': 'ドイツ', + 'zh': '德国', + 'ar': 'ألمانيا', + 'hi': 'जर्मनी', + 'bn': 'জার্মানি', + 'id': 'Jerman', + }); + + String get countryItaly => _pick({ + 'es': 'Italia', + 'en': 'Italy', + 'fr': 'Italie', + 'de': 'Italien', + 'it': 'Italia', + 'pt': 'Itália', + 'ru': 'Италия', + 'ja': 'イタリア', + 'zh': '意大利', + 'ar': 'إيطاليا', + 'hi': 'इटली', + 'bn': 'ইতালি', + 'id': 'Italia', + }); + + String get countryBrazil => _pick({ + 'es': 'Brasil', + 'en': 'Brazil', + 'fr': 'Brésil', + 'de': 'Brasilien', + 'it': 'Brasile', + 'pt': 'Brasil', + 'ru': 'Бразилия', + 'ja': 'ブラジル', + 'zh': '巴西', + 'ar': 'البرازيل', + 'hi': 'ब्राज़ील', + 'bn': 'ব্রাজিল', + 'id': 'Brasil', + }); + + String get countryJapan => _pick({ + 'es': 'Japón', + 'en': 'Japan', + 'fr': 'Japon', + 'de': 'Japan', + 'it': 'Giappone', + 'pt': 'Japão', + 'ru': 'Япония', + 'ja': '日本', + 'zh': '日本', + 'ar': 'اليابان', + 'hi': 'जापान', + 'bn': 'জাপান', + 'id': 'Jepang', + }); + + String get languageNameSpanish => _pick({ + 'es': 'Español', + 'en': 'Spanish', + 'fr': 'Espagnol', + 'de': 'Spanisch', + 'it': 'Spagnolo', + 'pt': 'Espanhol', + 'ru': 'Испанский', + 'ja': 'スペイン語', + 'zh': '西班牙语', + 'ar': 'الإسبانية', + 'hi': 'स्पेनिश', + 'bn': 'স্প্যানিশ', + 'id': 'Spanyol', + }); + + String get languageNameEnglish => _pick({ + 'es': 'Inglés', + 'en': 'English', + 'fr': 'Anglais', + 'de': 'Englisch', + 'it': 'Inglese', + 'pt': 'Inglês', + 'ru': 'Английский', + 'ja': '英語', + 'zh': '英语', + 'ar': 'الإنجليزية', + 'hi': 'अंग्रेज़ी', + 'bn': 'ইংরেজি', + 'id': 'Inggris', + }); + + String get languageNameFrench => _pick({ + 'es': 'Francés', + 'en': 'French', + 'fr': 'Français', + 'de': 'Französisch', + 'it': 'Francese', + 'pt': 'Francês', + 'ru': 'Французский', + 'ja': 'フランス語', + 'zh': '法语', + 'ar': 'الفرنسية', + 'hi': 'फ़्रेंच', + 'bn': 'ফরাসি', + 'id': 'Prancis', + }); + + String get languageNameGerman => _pick({ + 'es': 'Alemán', + 'en': 'German', + 'fr': 'Allemand', + 'de': 'Deutsch', + 'it': 'Tedesco', + 'pt': 'Alemão', + 'ru': 'Немецкий', + 'ja': 'ドイツ語', + 'zh': '德语', + 'ar': 'الألمانية', + 'hi': 'जर्मन', + 'bn': 'জার্মান', + 'id': 'Jerman', + }); + + String get languageNamePortuguese => _pick({ + 'es': 'Portugués', + 'en': 'Portuguese', + 'fr': 'Portugais', + 'de': 'Portugiesisch', + 'it': 'Portoghese', + 'pt': 'Português', + 'ru': 'Португальский', + 'ja': 'ポルトガル語', + 'zh': '葡萄牙语', + 'ar': 'البرتغالية', + 'hi': 'पुर्तगाली', + 'bn': 'পর্তুগিজ', + 'id': 'Portugis', + }); + + String get languageNameItalian => _pick({ + 'es': 'Italiano', + 'en': 'Italian', + 'fr': 'Italien', + 'de': 'Italienisch', + 'it': 'Italiano', + 'pt': 'Italiano', + 'ru': 'Итальянский', + 'ja': 'イタリア語', + 'zh': '意大利语', + 'ar': 'الإيطالية', + 'hi': 'इतालवी', + 'bn': 'ইতালীয়', + 'id': 'Italia', + }); + + String get languageNameJapanese => _pick({ + 'es': 'Japonés', + 'en': 'Japanese', + 'fr': 'Japonais', + 'de': 'Japanisch', + 'it': 'Giapponese', + 'pt': 'Japonês', + 'ru': 'Японский', + 'ja': '日本語', + 'zh': '日语', + 'ar': 'اليابانية', + 'hi': 'जापानी', + 'bn': 'জাপানি', + 'id': 'Jepang', + }); + + String get languageNameArabic => _pick({ + 'es': 'Árabe', + 'en': 'Arabic', + 'fr': 'Arabe', + 'de': 'Arabisch', + 'it': 'Arabo', + 'pt': 'Árabe', + 'ru': 'Арабский', + 'ja': 'アラビア語', + 'zh': '阿拉伯语', + 'ar': 'العربية', + 'hi': 'अरबी', + 'bn': 'আরবি', + 'id': 'Arab', + }); + + String get languageNameRussian => _pick({ + 'es': 'Ruso', + 'en': 'Russian', + 'fr': 'Russe', + 'de': 'Russisch', + 'it': 'Russo', + 'pt': 'Russo', + 'ru': 'Русский', + 'ja': 'ロシア語', + 'zh': '俄语', + 'ar': 'الروسية', + 'hi': 'रूसी', + 'bn': 'রুশ', + 'id': 'Rusia', + }); + + String get homeScreenSubtitle => _pick({ + 'es': 'Radio global en vivo con señales limpias, favoritos inteligentes y una experiencia visual de concurso.', + 'en': 'Live global radio with clean signals, smart favorites, and a show-style visual experience.', + 'fr': 'Radio mondiale en direct avec des signaux propres, des favoris intelligents et une expérience visuelle façon show.', + 'de': 'Weltweites Live-Radio mit sauberen Signalen, intelligenten Favoriten und einem showartigen visuellen Erlebnis.', + 'it': 'Radio globale in diretta con segnali puliti, preferiti intelligenti e un’esperienza visiva in stile show.', + 'pt': 'Rádio global ao vivo com sinais limpos, favoritos inteligentes e uma experiência visual em estilo de show.', + 'ru': 'Глобальное радио в прямом эфире с чистым сигналом, умными избранными и зрелищным визуальным стилем.', + 'ja': 'クリアな受信、賢いお気に入り、ショーのような演出で世界中のラジオをライブで楽しめます。', + 'zh': '全球直播电台,拥有清晰信号、智能收藏和富有节目感的视觉体验。', + 'ar': 'إذاعة عالمية مباشرة بإشارات نقية ومفضلة ذكية وتجربة بصرية بطابع استعراضي.', + 'hi': 'साफ सिग्नल, स्मार्ट पसंदीदा और शो-जैसे दृश्य अनुभव के साथ वैश्विक लाइव रेडियो।', + 'bn': 'পরিষ্কার সিগন্যাল, স্মার্ট ফেভারিট এবং শো-ধাঁচের ভিজ্যুয়াল অভিজ্ঞতাসহ বিশ্বজুড়ে লাইভ রেডিও।', + 'id': 'Radio global langsung dengan sinyal jernih, favorit cerdas, dan pengalaman visual bergaya pertunjukan.', + }); + + String get exploreStations => _pick({ + 'es': 'Explorar emisoras', + 'en': 'Explore stations', + 'fr': 'Explorer les stations', + 'de': 'Sender entdecken', + 'it': 'Esplora stazioni', + 'pt': 'Explorar estações', + 'ru': 'Исследовать станции', + 'ja': '放送局を探す', + 'zh': '探索电台', + 'ar': 'استكشاف المحطات', + 'hi': 'स्टेशन खोजें', + 'bn': 'স্টেশন ঘুরে দেখুন', + 'id': 'Jelajahi stasiun', + }); + + String homeStationsCount(int count) => _pick({ + 'es': '$count radios', + 'en': '$count stations', + 'fr': '$count stations', + 'de': '$count Sender', + 'it': '$count stazioni', + 'pt': '$count estações', + 'ru': '$count станций', + 'ja': '$count 局', + 'zh': '$count 个电台', + 'ar': '$count محطة', + 'hi': '$count स्टेशन', + 'bn': '$count স্টেশন', + 'id': '$count stasiun', + }); + + String get qualityHd => _pick({ + 'es': 'Calidad HD', + 'en': 'HD quality', + 'fr': 'Qualité HD', + 'de': 'HD-Qualität', + 'it': 'Qualità HD', + 'pt': 'Qualidade HD', + 'ru': 'HD-качество', + 'ja': 'HD音質', + 'zh': '高清音质', + 'ar': 'جودة HD', + 'hi': 'HD गुणवत्ता', + 'bn': 'এইচডি মান', + 'id': 'Kualitas HD', + }); + + String get nearYou => _pick({ + 'es': 'Cerca de vos', + 'en': 'Near you', + 'fr': 'Près de vous', + 'de': 'In deiner Nähe', + 'it': 'Vicino a te', + 'pt': 'Perto de você', + 'ru': 'Рядом с вами', + 'ja': '近くの放送局', + 'zh': '你附近的电台', + 'ar': 'بالقرب منك', + 'hi': 'आपके पास', + 'bn': 'আপনার কাছাকাছি', + 'id': 'Di dekat Anda', + }); + + String nearYouInCountry(String country) => _pick({ + 'es': 'Cerca de vos · $country', + 'en': 'Near you · $country', + 'fr': 'Près de vous · $country', + 'de': 'In deiner Nähe · $country', + 'it': 'Vicino a te · $country', + 'pt': 'Perto de você · $country', + 'ru': 'Рядом с вами · $country', + 'ja': '近くの放送局 · $country', + 'zh': '你附近的电台 · $country', + 'ar': 'بالقرب منك · $country', + 'hi': 'आपके पास · $country', + 'bn': 'আপনার কাছাকাছি · $country', + 'id': 'Di dekat Anda · $country', + }); + + String get detectAction => _pick({ + 'es': 'Detectar', + 'en': 'Detect', + 'fr': 'Détecter', + 'de': 'Erkennen', + 'it': 'Rileva', + 'pt': 'Detectar', + 'ru': 'Определить', + 'ja': '検出', + 'zh': '检测', + 'ar': 'اكتشف', + 'hi': 'पता लगाएँ', + 'bn': 'সনাক্ত করুন', + 'id': 'Deteksi', + }); + + String get liveRadar => _pick({ + 'es': 'Radar en directo', + 'en': 'Live radar', + 'fr': 'Radar en direct', + 'de': 'Live-Radar', + 'it': 'Radar in diretta', + 'pt': 'Radar ao vivo', + 'ru': 'Радар в прямом эфире', + 'ja': 'ライブレーダー', + 'zh': '实时雷达', + 'ar': 'رادار مباشر', + 'hi': 'लाइव रडार', + 'bn': 'লাইভ রাডার', + 'id': 'Radar langsung', + }); + + String get genresTitle => _pick({ + 'es': 'Géneros', + 'en': 'Genres', + 'fr': 'Genres', + 'de': 'Genres', + 'it': 'Generi', + 'pt': 'Gêneros', + 'ru': 'Жанры', + 'ja': 'ジャンル', + 'zh': '流派', + 'ar': 'الأنواع', + 'hi': 'शैलियाँ', + 'bn': 'ধরন', + 'id': 'Genre', + }); + + String get retryAction => _pick({ + 'es': 'Reintentar', + 'en': 'Retry', + 'fr': 'Réessayer', + 'de': 'Erneut versuchen', + 'it': 'Riprova', + 'pt': 'Tentar de novo', + 'ru': 'Повторить', + 'ja': '再試行', + 'zh': '重试', + 'ar': 'إعادة المحاولة', + 'hi': 'फिर से प्रयास करें', + 'bn': 'আবার চেষ্টা করুন', + 'id': 'Coba lagi', + }); + + String get noStationsAvailable => _pick({ + 'es': 'No hay emisoras disponibles', + 'en': 'No stations available', + 'fr': 'Aucune station disponible', + 'de': 'Keine Sender verfügbar', + 'it': 'Nessuna stazione disponibile', + 'pt': 'Nenhuma estação disponível', + 'ru': 'Нет доступных станций', + 'ja': '利用できる放送局がありません', + 'zh': '没有可用电台', + 'ar': 'لا توجد محطات متاحة', + 'hi': 'कोई स्टेशन उपलब्ध नहीं है', + 'bn': 'কোনও স্টেশন উপলব্ধ নেই', + 'id': 'Tidak ada stasiun tersedia', + }); + + String get noStationsAvailableSubtitle => _pick({ + 'es': 'Probá refrescar o elegir otro género para volver a capturar señal.', + 'en': 'Try refreshing or choosing another genre to lock onto a station again.', + 'fr': 'Essayez d’actualiser ou de choisir un autre genre pour retrouver un signal.', + 'de': 'Versuche es mit Aktualisieren oder wähle ein anderes Genre, um wieder ein Signal zu finden.', + 'it': 'Prova ad aggiornare o a scegliere un altro genere per ritrovare un segnale.', + 'pt': 'Tente atualizar ou escolher outro gênero para captar o sinal novamente.', + 'ru': 'Попробуйте обновить список или выбрать другой жанр, чтобы снова поймать сигнал.', + 'ja': '更新するか別のジャンルを選んで、もう一度受信を試してください。', + 'zh': '请尝试刷新,或选择其他流派以重新捕获信号。', + 'ar': 'حاول التحديث أو اختيار نوع آخر لالتقاط الإشارة من جديد.', + 'hi': 'फिर से सिग्नल पाने के लिए रिफ्रेश करें या कोई दूसरी शैली चुनें।', + 'bn': 'আবার সিগন্যাল পেতে রিফ্রেশ করুন বা অন্য কোনও ধরন বেছে নিন।', + 'id': 'Coba segarkan atau pilih genre lain untuk menangkap sinyal lagi.', + }); + + String genreName(String tag) => switch (tag) { + 'pop' => _pick({'es': 'Pop', 'en': 'Pop', 'fr': 'Pop', 'de': 'Pop', 'it': 'Pop', 'pt': 'Pop', 'ru': 'Поп', 'ja': 'ポップ', 'zh': '流行', 'ar': 'بوب', 'hi': 'पॉप', 'bn': 'পপ', 'id': 'Pop'}), + 'rock' => _pick({'es': 'Rock', 'en': 'Rock', 'fr': 'Rock', 'de': 'Rock', 'it': 'Rock', 'pt': 'Rock', 'ru': 'Рок', 'ja': 'ロック', 'zh': '摇滚', 'ar': 'روك', 'hi': 'रॉक', 'bn': 'রক', 'id': 'Rock'}), + 'jazz' => _pick({'es': 'Jazz', 'en': 'Jazz', 'fr': 'Jazz', 'de': 'Jazz', 'it': 'Jazz', 'pt': 'Jazz', 'ru': 'Джаз', 'ja': 'ジャズ', 'zh': '爵士', 'ar': 'جاز', 'hi': 'जैज़', 'bn': 'জ্যাজ', 'id': 'Jazz'}), + 'classical' => _pick({'es': 'Clásica', 'en': 'Classical', 'fr': 'Classique', 'de': 'Klassik', 'it': 'Classica', 'pt': 'Clássica', 'ru': 'Классика', 'ja': 'クラシック', 'zh': '古典', 'ar': 'كلاسيكي', 'hi': 'शास्त्रीय', 'bn': 'শাস্ত্রীয়', 'id': 'Klasik'}), + 'electronic' => _pick({'es': 'Electrónica', 'en': 'Electronic', 'fr': 'Électronique', 'de': 'Elektronisch', 'it': 'Elettronica', 'pt': 'Eletrônica', 'ru': 'Электроника', 'ja': 'エレクトロ', 'zh': '电子', 'ar': 'إلكتروني', 'hi': 'इलेक्ट्रॉनिक', 'bn': 'ইলেকট্রনিক', 'id': 'Elektronik'}), + 'news' => _pick({'es': 'Noticias', 'en': 'News', 'fr': 'Infos', 'de': 'Nachrichten', 'it': 'Notizie', 'pt': 'Notícias', 'ru': 'Новости', 'ja': 'ニュース', 'zh': '新闻', 'ar': 'أخبار', 'hi': 'समाचार', 'bn': 'সংবাদ', 'id': 'Berita'}), + 'talk' => _pick({'es': 'Hablada', 'en': 'Talk', 'fr': 'Débat', 'de': 'Talk', 'it': 'Talk', 'pt': 'Talk', 'ru': 'Разговоры', 'ja': 'トーク', 'zh': '谈话', 'ar': 'حواري', 'hi': 'टॉक', 'bn': 'টক', 'id': 'Obrolan'}), + 'hip-hop' => _pick({'es': 'Hip-hop', 'en': 'Hip-hop', 'fr': 'Hip-hop', 'de': 'Hip-Hop', 'it': 'Hip-hop', 'pt': 'Hip-hop', 'ru': 'Хип-хоп', 'ja': 'ヒップホップ', 'zh': '嘻哈', 'ar': 'هيب هوب', 'hi': 'हिप-हॉप', 'bn': 'হিপ-হপ', 'id': 'Hip-hop'}), + 'country' => _pick({'es': 'Country', 'en': 'Country', 'fr': 'Country', 'de': 'Country', 'it': 'Country', 'pt': 'Country', 'ru': 'Кантри', 'ja': 'カントリー', 'zh': '乡村', 'ar': 'كانتري', 'hi': 'कंट्री', 'bn': 'কান্ট্রি', 'id': 'Country'}), + 'metal' => _pick({'es': 'Metal', 'en': 'Metal', 'fr': 'Metal', 'de': 'Metal', 'it': 'Metal', 'pt': 'Metal', 'ru': 'Метал', 'ja': 'メタル', 'zh': '金属', 'ar': 'ميتال', 'hi': 'मेटल', 'bn': 'মেটাল', 'id': 'Metal'}), + 'reggae' => _pick({'es': 'Reggae', 'en': 'Reggae', 'fr': 'Reggae', 'de': 'Reggae', 'it': 'Reggae', 'pt': 'Reggae', 'ru': 'Регги', 'ja': 'レゲエ', 'zh': '雷鬼', 'ar': 'ريغي', 'hi': 'रेगे', 'bn': 'রেগে', 'id': 'Reggae'}), + 'latin' => _pick({'es': 'Latina', 'en': 'Latin', 'fr': 'Latine', 'de': 'Latein', 'it': 'Latina', 'pt': 'Latina', 'ru': 'Латина', 'ja': 'ラテン', 'zh': '拉丁', 'ar': 'لاتيني', 'hi': 'लैटिन', 'bn': 'লাতিন', 'id': 'Latin'}), + _ => tag, + }; + + String get alarmRingingFallbackActive => _pick({ + 'es': 'Sonando con audio seguro interno.', + 'en': 'Playing with internal safe audio.', + 'fr': 'Lecture avec l’audio interne sécurisé.', + 'de': 'Wiedergabe mit internem Sicherheitsaudio.', + 'it': 'Riproduzione con audio interno sicuro.', + 'pt': 'Tocando com áudio interno seguro.', + 'ru': 'Воспроизведение с внутренним безопасным аудио.', + 'ja': '内部の安全な音声で再生しています。', + 'zh': '正在使用内部安全音频播放。', + 'ar': 'يعمل الآن باستخدام الصوت الداخلي الآمن.', + 'hi': 'आंतरिक सुरक्षित ऑडियो के साथ चल रहा है।', + 'bn': 'অভ্যন্তরীণ নিরাপদ অডিও দিয়ে চলছে।', + 'id': 'Memutar dengan audio internal yang aman.', + }); + + String get alarmRingingTryingStation => _pick({ + 'es': 'Intentando reproducir tu emisora con máxima calidad disponible.', + 'en': 'Trying to play your station at the highest available quality.', + 'fr': 'Tentative de lecture de votre station avec la meilleure qualité disponible.', + 'de': 'Dein Sender wird mit der bestmöglichen verfügbaren Qualität gestartet.', + 'it': 'Tentativo di riprodurre la tua stazione con la massima qualità disponibile.', + 'pt': 'Tentando reproduzir sua estação com a maior qualidade disponível.', + 'ru': 'Пытаемся воспроизвести вашу станцию в максимально доступном качестве.', + 'ja': '利用可能な最高音質で放送局の再生を試みています。', + 'zh': '正在尝试以最高可用音质播放你的电台。', + 'ar': 'جارٍ محاولة تشغيل محطتك بأعلى جودة متاحة.', + 'hi': 'आपका स्टेशन उपलब्ध सर्वोच्च गुणवत्ता में चलाने की कोशिश की जा रही है।', + 'bn': 'আপনার স্টেশন সর্বোচ্চ উপলভ্য মানে চালানোর চেষ্টা করা হচ্ছে।', + 'id': 'Mencoba memutar stasiun Anda dengan kualitas tertinggi yang tersedia.', + }); + + String get alarmRingingPreparingFallback => _pick({ + 'es': 'Preparando audio seguro interno.', + 'en': 'Preparing internal safe audio.', + 'fr': 'Préparation de l’audio interne sécurisé.', + 'de': 'Internes Sicherheitsaudio wird vorbereitet.', + 'it': 'Preparazione dell’audio interno sicuro.', + 'pt': 'Preparando áudio interno seguro.', + 'ru': 'Подготавливается внутреннее безопасное аудио.', + 'ja': '内部の安全な音声を準備しています。', + 'zh': '正在准备内部安全音频。', + 'ar': 'جارٍ تجهيز الصوت الداخلي الآمن.', + 'hi': 'आंतरिक सुरक्षित ऑडियो तैयार किया जा रहा है।', + 'bn': 'অভ্যন্তরীণ নিরাপদ অডিও প্রস্তুত করা হচ্ছে।', + 'id': 'Menyiapkan audio internal yang aman.', + }); + + String get alarmScreenTitle => _pick({ + 'es': 'Despertar musical', 'en': 'Music wake-up', 'fr': 'Réveil musical', 'de': 'Musikalischer Wecker', 'it': 'Sveglia musicale', 'pt': 'Despertar musical', 'ru': 'Музыкальный будильник', 'ja': '音楽アラーム', 'zh': '音乐闹钟', 'ar': 'استيقاظ موسيقي', 'hi': 'संगीत अलार्म', 'bn': 'সঙ্গীত অ্যালার্ম', 'id': 'Alarm musik', + }); + + String get alarmScreenSubtitle => _pick({ + 'es': 'Alarmas con radio, sonido seguro, vacaciones inteligentes y próxima ejecución siempre visible.', + 'en': 'Radio alarms with safe audio, smart vacations, and the next occurrence always visible.', + 'fr': 'Alarmes radio avec audio sécurisé, vacances intelligentes et prochaine exécution toujours visible.', + 'de': 'Radioalarme mit Sicherheitsaudio, intelligenten Urlaubsregeln und stets sichtbarer nächster Ausführung.', + 'it': 'Sveglie radio con audio sicuro, vacanze intelligenti e prossima esecuzione sempre visibile.', + 'pt': 'Alarmes com rádio, áudio seguro, férias inteligentes e próxima execução sempre visível.', + 'ru': 'Радиобудильники с безопасным звуком, умным режимом отпуска и всегда видимым следующим запуском.', + 'ja': 'ラジオ、セーフ音声、スマート休暇設定、次回実行を常に表示するアラームです。', + 'zh': '支持电台、安全音频、智能假期和始终可见下次执行时间的闹钟。', + 'ar': 'منبهات بالراديو وصوت آمن وإجازات ذكية مع عرض التشغيل التالي دائمًا.', + 'hi': 'रेडियो, सुरक्षित ऑडियो, स्मार्ट छुट्टियों और हमेशा दिखने वाली अगली घंटी के साथ अलार्म।', + 'bn': 'রেডিও, নিরাপদ অডিও, স্মার্ট ছুটি এবং সবসময় দৃশ্যমান পরবর্তী সময়সহ অ্যালার্ম।', + 'id': 'Alarm radio dengan audio aman, liburan cerdas, dan jadwal berikutnya selalu terlihat.', + }); + + String get createAlarmAction => _pick({'es':'Crear alarma','en':'Create alarm','fr':'Créer une alarme','de':'Alarm erstellen','it':'Crea sveglia','pt':'Criar alarme','ru':'Создать будильник','ja':'アラームを作成','zh':'创建闹钟','ar':'إنشاء منبه','hi':'अलार्म बनाएँ','bn':'অ্যালার্ম তৈরি করুন','id':'Buat alarm'}); + String alarmsCount(int count) => _pick({'es':'$count alarmas','en':'$count alarms','fr':'$count alarmes','de':'$count Alarme','it':'$count sveglie','pt':'$count alarmes','ru':'$count будильников','ja':'$count 件のアラーム','zh':'$count 个闹钟','ar':'$count منبه','hi':'$count अलार्म','bn':'$count অ্যালার্ম','id':'$count alarm'}); + String get activeAlarmsWithoutNextTitle => _pick({'es':'Alarmas activas sin próxima ejecución','en':'Active alarms without a next occurrence','fr':'Alarmes actives sans prochaine exécution','de':'Aktive Alarme ohne nächste Ausführung','it':'Sveglie attive senza prossima esecuzione','pt':'Alarmes ativos sem próxima execução','ru':'Активные будильники без следующего запуска','ja':'次回実行のない有効なアラーム','zh':'没有下次执行的活动闹钟','ar':'منبهات نشطة بلا تشغيل تالٍ','hi':'अगली घंटी बिना सक्रिय अलार्म','bn':'পরবর্তী সময় ছাড়া সক্রিয় অ্যালার্ম','id':'Alarm aktif tanpa jadwal berikutnya'}); + String get activeAlarmsNoneTitle => _pick({'es':'Sin alarmas activas','en':'No active alarms','fr':'Aucune alarme active','de':'Keine aktiven Alarme','it':'Nessuna sveglia attiva','pt':'Sem alarmes ativos','ru':'Нет активных будильников','ja':'有効なアラームはありません','zh':'没有活动闹钟','ar':'لا توجد منبهات نشطة','hi':'कोई सक्रिय अलार्म नहीं','bn':'কোনও সক্রিয় অ্যালার্ম নেই','id':'Tidak ada alarm aktif'}); + String get nextAlarmTitle => _pick({'es':'Próxima alarma','en':'Next alarm','fr':'Prochaine alarme','de':'Nächster Alarm','it':'Prossima sveglia','pt':'Próximo alarme','ru':'Следующий будильник','ja':'次のアラーム','zh':'下一个闹钟','ar':'المنبه التالي','hi':'अगला अलार्म','bn':'পরবর্তী অ্যালার্ম','id':'Alarm berikutnya'}); + String activeAlarmsWithoutNextSubtitle(int count) => _pick({'es':'Hay $count alarma(s) activas, pero ahora mismo no tienen una fecha futura válida. Revisá fecha, días y vacaciones.','en':'There are $count active alarm(s), but they do not currently have a valid future date. Check date, days, and vacations.','fr':'Il y a $count alarme(s) active(s), mais aucune date future valide pour le moment. Vérifiez la date, les jours et les vacances.','de':'Es gibt $count aktive(n) Alarm(e), aber aktuell kein gültiges zukünftiges Datum. Prüfe Datum, Tage und Urlaub.','it':'Ci sono $count sveglia/e attive, ma al momento senza una data futura valida. Controlla data, giorni e vacanze.','pt':'Há $count alarme(s) ativo(s), mas sem uma data futura válida no momento. Revise data, dias e férias.','ru':'Есть $count активных будильников, но сейчас у них нет корректной будущей даты. Проверьте дату, дни и отпуск.','ja':'有効なアラームが $count 件ありますが、現在有効な将来日時がありません。日付、曜日、休暇を確認してください。','zh':'有 $count 个活动闹钟,但当前没有有效的未来日期。请检查日期、星期和假期。','ar':'يوجد $count منبه نشط، لكنها لا تملك حاليًا تاريخًا مستقبليًا صالحًا. راجع التاريخ والأيام والإجازات.','hi':'$count सक्रिय अलार्म हैं, लेकिन अभी इनके पास मान्य भविष्य की तारीख नहीं है। तारीख, दिन और छुट्टियाँ जाँचें।','bn':'$countটি সক্রিয় অ্যালার্ম আছে, কিন্তু এখন কোনও বৈধ ভবিষ্যৎ তারিখ নেই। তারিখ, দিন ও ছুটি দেখুন।','id':'Ada $count alarm aktif, tetapi saat ini tidak memiliki tanggal mendatang yang valid. Periksa tanggal, hari, dan liburan.'}); + String get createAlarmHint => _pick({'es':'Creá una alarma y PluriWave calculará la siguiente ejecución automáticamente.','en':'Create an alarm and PluriWave will calculate the next occurrence automatically.','fr':'Créez une alarme et PluriWave calculera automatiquement la prochaine exécution.','de':'Erstelle einen Alarm und PluriWave berechnet die nächste Ausführung automatisch.','it':'Crea una sveglia e PluriWave calcolerà automaticamente la prossima esecuzione.','pt':'Crie um alarme e o PluriWave calculará automaticamente a próxima execução.','ru':'Создайте будильник, и PluriWave автоматически рассчитает следующий запуск.','ja':'アラームを作成すると、PluriWave が次回実行を自動計算します。','zh':'创建闹钟后,PluriWave 会自动计算下次执行时间。','ar':'أنشئ منبهًا وسيحسب PluriWave التشغيل التالي تلقائيًا.','hi':'अलार्म बनाएँ और PluriWave अगली घंटी अपने-आप गणना करेगा।','bn':'একটি অ্যালার্ম তৈরি করুন, PluriWave স্বয়ংক্রিয়ভাবে পরবর্তী সময় হিসাব করবে।','id':'Buat alarm dan PluriWave akan menghitung jadwal berikutnya secara otomatis.'}); + String alarmNextSummary(String name, String date) => '$name · $date'; + + String get alarmVacationPlay => _pick({'es':'Suena en vacaciones','en':'Plays during vacations','fr':'Sonne pendant les vacances','de':'Klingelt im Urlaub','it':'Suona durante le vacanze','pt':'Toca durante as férias','ru':'Срабатывает во время отпуска','ja':'休暇中も鳴らす','zh':'假期中响铃','ar':'يعمل أثناء الإجازات','hi':'छुट्टियों में बजेगा','bn':'ছুটির সময় বাজবে','id':'Berbunyi saat liburan'}); + String get alarmVacationPause => _pick({'es':'Pausa en vacaciones','en':'Pause during vacations','fr':'Pause pendant les vacances','de':'Im Urlaub pausieren','it':'Pausa durante le vacanze','pt':'Pausar durante as férias','ru':'Пауза во время отпуска','ja':'休暇中は一時停止','zh':'假期中暂停','ar':'إيقاف أثناء الإجازات','hi':'छुट्टियों में विराम','bn':'ছুটির সময় বিরতি','id':'Jeda saat liburan'}); + String fadeInSeconds(int seconds) => _pick({'es':'Fade-in ${seconds}s','en':'Fade-in ${seconds}s','fr':'Fondu entrant ${seconds}s','de':'Fade-in ${seconds}s','it':'Fade-in ${seconds}s','pt':'Fade-in ${seconds}s','ru':'Плавное начало ${seconds} с','ja':'フェードイン ${seconds}秒','zh':'淡入 ${seconds} 秒','ar':'تدرّج صوتي ${seconds} ث','hi':'फ़ेड-इन ${seconds} सेकंड','bn':'ফেড-ইন ${seconds} সেকেন্ড','id':'Fade-in ${seconds} dtk'}); + String alarmNextExecution(String date) => _pick({'es':'Siguiente ejecución: $date','en':'Next occurrence: $date','fr':'Prochaine exécution : $date','de':'Nächste Ausführung: $date','it':'Prossima esecuzione: $date','pt':'Próxima execução: $date','ru':'Следующий запуск: $date','ja':'次回実行: $date','zh':'下次执行:$date','ar':'التشغيل التالي: $date','hi':'अगली घंटी: $date','bn':'পরবর্তী সময়: $date','id':'Jadwal berikutnya: $date'}); + String get alarmNoNextExecution => _pick({'es':'No tiene próxima ejecución activa.','en':'No active next occurrence.','fr':'Aucune prochaine exécution active.','de':'Keine aktive nächste Ausführung.','it':'Nessuna prossima esecuzione attiva.','pt':'Não há próxima execução ativa.','ru':'Нет активного следующего запуска.','ja':'有効な次回実行はありません。','zh':'没有活动的下次执行。','ar':'لا يوجد تشغيل تالٍ نشط.','hi':'कोई सक्रिय अगली घंटी नहीं है।','bn':'কোনও সক্রিয় পরবর্তী সময় নেই।','id':'Tidak ada jadwal berikutnya yang aktif.'}); + String alarmSkippedExecution(String date) => _pick({'es':'Una ejecución fue omitida: $date.','en':'One occurrence was skipped: $date.','fr':'Une exécution a été ignorée : $date.','de':'Eine Ausführung wurde übersprungen: $date.','it':'Un’esecuzione è stata saltata: $date.','pt':'Uma execução foi ignorada: $date.','ru':'Один запуск был пропущен: $date.','ja':'1回の実行をスキップしました: $date。','zh':'已跳过一次执行:$date。','ar':'تم تخطي تشغيل واحد: $date.','hi':'एक घंटी छोड़ी गई: $date।','bn':'একটি সময় বাদ দেওয়া হয়েছে: $date।','id':'Satu jadwal dilewati: $date.'}); + String get editAction => _pick({'es':'Editar','en':'Edit','fr':'Modifier','de':'Bearbeiten','it':'Modifica','pt':'Editar','ru':'Изменить','ja':'編集','zh':'编辑','ar':'تعديل','hi':'संपादित करें','bn':'সম্পাদনা','id':'Edit'}); + String get skipNextAction => _pick({'es':'Omitir siguiente','en':'Skip next','fr':'Ignorer la suivante','de':'Nächsten überspringen','it':'Salta prossima','pt':'Ignorar próximo','ru':'Пропустить следующий','ja':'次回をスキップ','zh':'跳过下一次','ar':'تخطي التالي','hi':'अगला छोड़ें','bn':'পরেরটি বাদ দিন','id':'Lewati berikutnya'}); + String get alarmSkippedNoNextSnackbar => _pick({'es':'Alarma omitida. No queda próxima ejecución.','en':'Alarm skipped. No next occurrence remains.','fr':'Alarme ignorée. Il ne reste aucune prochaine exécution.','de':'Alarm übersprungen. Es bleibt keine nächste Ausführung.','it':'Sveglia saltata. Non resta alcuna prossima esecuzione.','pt':'Alarme ignorado. Não resta próxima execução.','ru':'Будильник пропущен. Следующего запуска не осталось.','ja':'アラームをスキップしました。次回実行はありません。','zh':'闹钟已跳过。没有剩余的下次执行。','ar':'تم تخطي المنبه. لا يوجد تشغيل تالٍ.','hi':'अलार्म छोड़ा गया। अगली घंटी नहीं बची।','bn':'অ্যালার্ম বাদ দেওয়া হয়েছে। আর কোনও পরবর্তী সময় নেই।','id':'Alarm dilewati. Tidak ada jadwal berikutnya.'}); + String alarmSkippedReturnsSnackbar(String date) => _pick({'es':'Alarma omitida. Volverá el $date.','en':'Alarm skipped. It will return on $date.','fr':'Alarme ignorée. Elle reviendra le $date.','de':'Alarm übersprungen. Er kehrt am $date zurück.','it':'Sveglia saltata. Tornerà il $date.','pt':'Alarme ignorado. Voltará em $date.','ru':'Будильник пропущен. Он вернётся $date.','ja':'アラームをスキップしました。$date に戻ります。','zh':'闹钟已跳过。将在 $date 返回。','ar':'تم تخطي المنبه. سيعود في $date.','hi':'अलार्म छोड़ा गया। यह $date को वापस आएगा।','bn':'অ্যালার্ম বাদ দেওয়া হয়েছে। এটি $date তারিখে ফিরবে।','id':'Alarm dilewati. Akan kembali pada $date.'}); + String get deleteAction => _pick({'es':'Eliminar','en':'Delete','fr':'Supprimer','de':'Löschen','it':'Elimina','pt':'Excluir','ru':'Удалить','ja':'削除','zh':'删除','ar':'حذف','hi':'हटाएँ','bn':'মুছুন','id':'Hapus'}); + String alarmVacationPausedNoNext(String name) => _pick({'es':'Está pausada por vacaciones ($name) y sin próxima ejecución.','en':'Paused for vacation ($name) with no next occurrence.','fr':'En pause pour vacances ($name) et sans prochaine exécution.','de':'Wegen Urlaub pausiert ($name) und ohne nächste Ausführung.','it':'In pausa per vacanze ($name) e senza prossima esecuzione.','pt':'Pausado por férias ($name) e sem próxima execução.','ru':'Приостановлен из-за отпуска ($name), следующего запуска нет.','ja':'休暇($name)のため一時停止中で、次回実行はありません。','zh':'因假期($name)暂停,且没有下次执行。','ar':'متوقف بسبب الإجازة ($name) ولا يوجد تشغيل تالٍ.','hi':'छुट्टी ($name) के कारण रुका है और अगली घंटी नहीं है।','bn':'ছুটি ($name) কারণে বিরত, কোনও পরবর্তী সময় নেই।','id':'Dijeda karena liburan ($name) dan tidak ada jadwal berikutnya.'}); + String alarmVacationPausedReturns(String name, String date) => _pick({'es':'Está pausada por vacaciones ($name) y vuelve el $date.','en':'Paused for vacation ($name) and returns on $date.','fr':'En pause pour vacances ($name) et revient le $date.','de':'Wegen Urlaub pausiert ($name) und kehrt am $date zurück.','it':'In pausa per vacanze ($name) e torna il $date.','pt':'Pausado por férias ($name) e volta em $date.','ru':'Приостановлен из-за отпуска ($name) и вернётся $date.','ja':'休暇($name)のため一時停止中で、$date に再開します。','zh':'因假期($name)暂停,将在 $date 恢复。','ar':'متوقف بسبب الإجازة ($name) وسيعود في $date.','hi':'छुट्टी ($name) के कारण रुका है और $date को लौटेगा।','bn':'ছুটি ($name) কারণে বিরত, $date তারিখে ফিরবে।','id':'Dijeda karena liburan ($name) dan kembali pada $date.'}); + String alarmVacationReturns(String date) => _pick({'es':'Con vacaciones activas, volverá a sonar el $date.','en':'With vacations active, it will ring again on $date.','fr':'Avec les vacances actives, elle sonnera à nouveau le $date.','de':'Bei aktivem Urlaub klingelt er wieder am $date.','it':'Con le vacanze attive, suonerà di nuovo il $date.','pt':'Com férias ativas, tocará novamente em $date.','ru':'При активном отпуске снова сработает $date.','ja':'休暇設定が有効なため、$date に再度鳴ります。','zh':'假期启用时,将在 $date 再次响铃。','ar':'مع تفعيل الإجازات، سيعمل مرة أخرى في $date.','hi':'छुट्टियाँ सक्रिय होने पर यह $date को फिर बजेगा।','bn':'ছুটি সক্রিয় থাকলে এটি $date তারিখে আবার বাজবে।','id':'Dengan liburan aktif, alarm akan berbunyi lagi pada $date.'}); + + String get defaultAlarmName => _pick({'es':'Despertador musical','en':'Music alarm','fr':'Réveil musical','de':'Musikalischer Wecker','it':'Sveglia musicale','pt':'Despertador musical','ru':'Музыкальный будильник','ja':'音楽アラーム','zh':'音乐闹钟','ar':'منبه موسيقي','hi':'संगीत अलार्म','bn':'সঙ্গীত অ্যালার্ম','id':'Alarm musik'}); + String get newAlarmTitle => _pick({'es':'Nueva alarma','en':'New alarm','fr':'Nouvelle alarme','de':'Neuer Alarm','it':'Nuova sveglia','pt':'Novo alarme','ru':'Новый будильник','ja':'新しいアラーム','zh':'新建闹钟','ar':'منبه جديد','hi':'नया अलार्म','bn':'নতুন অ্যালার্ম','id':'Alarm baru'}); + String get editAlarmTitle => _pick({'es':'Editar alarma','en':'Edit alarm','fr':'Modifier l’alarme','de':'Alarm bearbeiten','it':'Modifica sveglia','pt':'Editar alarme','ru':'Изменить будильник','ja':'アラームを編集','zh':'编辑闹钟','ar':'تعديل المنبه','hi':'अलार्म संपादित करें','bn':'অ্যালার্ম সম্পাদনা','id':'Edit alarm'}); + String get nameLabel => _pick({'es':'Nombre','en':'Name','fr':'Nom','de':'Name','it':'Nome','pt':'Nome','ru':'Название','ja':'名前','zh':'名称','ar':'الاسم','hi':'नाम','bn':'নাম','id':'Nama'}); + String get timeLabel => _pick({'es':'Hora','en':'Time','fr':'Heure','de':'Uhrzeit','it':'Ora','pt':'Hora','ru':'Время','ja':'時刻','zh':'时间','ar':'الوقت','hi':'समय','bn':'সময়','id':'Waktu'}); + String get dateLabel => _pick({'es':'Fecha','en':'Date','fr':'Date','de':'Datum','it':'Data','pt':'Data','ru':'Дата','ja':'日付','zh':'日期','ar':'التاريخ','hi':'तारीख','bn':'তারিখ','id':'Tanggal'}); + String get oneTimeOption => _pick({'es':'Una vez','en':'Once','fr':'Une fois','de':'Einmal','it':'Una volta','pt':'Uma vez','ru':'Один раз','ja':'1回','zh':'一次','ar':'مرة واحدة','hi':'एक बार','bn':'একবার','id':'Sekali'}); + String get dailyOption => _pick({'es':'Diaria','en':'Daily','fr':'Tous les jours','de':'Täglich','it':'Giornaliera','pt':'Diário','ru':'Ежедневно','ja':'毎日','zh':'每天','ar':'يوميًا','hi':'रोज़','bn':'প্রতিদিন','id':'Harian'}); + String get weekdaysOption => _pick({'es':'Días','en':'Days','fr':'Jours','de':'Tage','it':'Giorni','pt':'Dias','ru':'Дни','ja':'曜日','zh':'星期','ar':'الأيام','hi':'दिन','bn':'দিন','id':'Hari'}); + String get soundAndVolumeTitle => _pick({'es':'Sonido y volumen','en':'Sound and volume','fr':'Son et volume','de':'Ton und Lautstärke','it':'Suono e volume','pt':'Som e volume','ru':'Звук и громкость','ja':'サウンドと音量','zh':'声音和音量','ar':'الصوت ومستوى الصوت','hi':'ध्वनि और वॉल्यूम','bn':'শব্দ ও ভলিউম','id':'Suara dan volume'}); + String get alarmFadeInTitle => _pick({'es':'Fade-in de alarma','en':'Alarm fade-in','fr':'Fondu entrant de l’alarme','de':'Alarm-Fade-in','it':'Fade-in della sveglia','pt':'Fade-in do alarme','ru':'Плавное начало будильника','ja':'アラームのフェードイン','zh':'闹钟淡入','ar':'تدرّج صوت المنبه','hi':'अलार्म फ़ेड-इन','bn':'অ্যালার্ম ফেড-ইন','id':'Fade-in alarm'}); + String get alarmFadeInOff => _pick({'es':'0 s (sin transición)','en':'0 s (no transition)','fr':'0 s (sans transition)','de':'0 s (ohne Übergang)','it':'0 s (senza transizione)','pt':'0 s (sem transição)','ru':'0 с (без перехода)','ja':'0秒(遷移なし)','zh':'0 秒(无过渡)','ar':'0 ث (بلا انتقال)','hi':'0 सेकंड (कोई बदलाव नहीं)','bn':'0 সেকেন্ড (কোনও ট্রানজিশন নেই)','id':'0 dtk (tanpa transisi)'}); + String alarmFadeInProgress(int seconds) => _pick({'es':'$seconds s (de 5% al volumen elegido)','en':'$seconds s (from 5% to the chosen volume)','fr':'$seconds s (de 5 % au volume choisi)','de':'$seconds s (von 5 % zur gewählten Lautstärke)','it':'$seconds s (dal 5% al volume scelto)','pt':'$seconds s (de 5% até o volume escolhido)','ru':'$seconds с (от 5% до выбранной громкости)','ja':'$seconds秒(5%から選択した音量へ)','zh':'$seconds 秒(从 5% 到所选音量)','ar':'$seconds ث (من 5% إلى المستوى المختار)','hi':'$seconds सेकंड (5% से चुने हुए वॉल्यूम तक)','bn':'$seconds সেকেন্ড (5% থেকে নির্বাচিত ভলিউমে)','id':'$seconds dtk (dari 5% ke volume pilihan)'}); + String get soundInternalSafe => _pick({'es':'Sonido seguro interno','en':'Internal safe sound','fr':'Son interne sécurisé','de':'Interner Sicherheitston','it':'Suono interno sicuro','pt':'Som interno seguro','ru':'Внутренний безопасный звук','ja':'内部セーフサウンド','zh':'内部安全声音','ar':'صوت داخلي آمن','hi':'आंतरिक सुरक्षित ध्वनि','bn':'অভ্যন্তরীণ নিরাপদ শব্দ','id':'Suara internal aman'}); + String get soundWarmSunrise => _pick({'es':'Amanecer cálido','en':'Warm sunrise','fr':'Aube chaleureuse','de':'Warmer Sonnenaufgang','it':'Alba calda','pt':'Amanhecer quente','ru':'Тёплый рассвет','ja':'暖かな日の出','zh':'温暖日出','ar':'شروق دافئ','hi':'गर्म सूर्योदय','bn':'উষ্ণ সূর্যোদয়','id':'Fajar hangat'}); + String get soundSoftBell => _pick({'es':'Campana suave','en':'Soft bell','fr':'Cloche douce','de':'Sanfte Glocke','it':'Campana leggera','pt':'Sino suave','ru':'Мягкий колокол','ja':'やさしいベル','zh':'柔和铃声','ar':'جرس هادئ','hi':'मृदु घंटी','bn':'নরম ঘণ্টা','id':'Lonceng lembut'}); + String get soundDigitalPulse => _pick({'es':'Pulso digital','en':'Digital pulse','fr':'Impulsion numérique','de':'Digitaler Puls','it':'Impulso digitale','pt':'Pulso digital','ru':'Цифровой пульс','ja':'デジタルパルス','zh':'数字脉冲','ar':'نبض رقمي','hi':'डिजिटल पल्स','bn':'ডিজিটাল পালস','id':'Pulsa digital'}); + String get favoriteStationLabel => _pick({'es':'Emisora favorita','en':'Favorite station','fr':'Station favorite','de':'Lieblingssender','it':'Stazione preferita','pt':'Estação favorita','ru':'Избранная станция','ja':'お気に入りの放送局','zh':'收藏电台','ar':'المحطة المفضلة','hi':'पसंदीदा स्टेशन','bn':'প্রিয় স্টেশন','id':'Stasiun favorit'}); + String get noStationUseInternalSound => _pick({'es':'Sin emisora: usar sonido interno','en':'No station: use internal sound','fr':'Aucune station : utiliser le son interne','de':'Kein Sender: internen Ton verwenden','it':'Nessuna stazione: usa suono interno','pt':'Sem estação: usar som interno','ru':'Без станции: использовать внутренний звук','ja':'放送局なし: 内部サウンドを使用','zh':'无电台:使用内部声音','ar':'بلا محطة: استخدام الصوت الداخلي','hi':'कोई स्टेशन नहीं: आंतरिक ध्वनि उपयोग करें','bn':'স্টেশন নেই: অভ্যন্তরীণ শব্দ ব্যবহার করুন','id':'Tanpa stasiun: gunakan suara internal'}); + String get saveFavoritesAlarmHint => _pick({'es':'Guardá emisoras en Favoritos para usarlas como alarma musical.','en':'Save stations to Favorites to use them as a music alarm.','fr':'Enregistrez des stations dans les favoris pour les utiliser comme alarme musicale.','de':'Speichere Sender in Favoriten, um sie als musikalischen Alarm zu verwenden.','it':'Salva le stazioni nei preferiti per usarle come sveglia musicale.','pt':'Salve estações nos Favoritos para usá-las como alarme musical.','ru':'Сохраните станции в избранное, чтобы использовать их как музыкальный будильник.','ja':'音楽アラームとして使うには、放送局をお気に入りに保存してください。','zh':'将电台保存到收藏夹,以用作音乐闹钟。','ar':'احفظ المحطات في المفضلة لاستخدامها كمنبه موسيقي.','hi':'संगीत अलार्म के रूप में उपयोग करने के लिए स्टेशनों को पसंदीदा में सहेजें।','bn':'সঙ্গীত অ্যালার্ম হিসেবে ব্যবহার করতে স্টেশনগুলো ফেভারিটে সংরক্ষণ করুন।','id':'Simpan stasiun ke Favorit untuk digunakan sebagai alarm musik.'}); + String get useCurrentStationAction => _pick({'es':'Usar emisora actual','en':'Use current station','fr':'Utiliser la station actuelle','de':'Aktuellen Sender verwenden','it':'Usa stazione attuale','pt':'Usar estação atual','ru':'Использовать текущую станцию','ja':'現在の放送局を使う','zh':'使用当前电台','ar':'استخدام المحطة الحالية','hi':'वर्तमान स्टेशन उपयोग करें','bn':'বর্তমান স্টেশন ব্যবহার করুন','id':'Gunakan stasiun saat ini'}); + String get playDuringVacations => _pick({'es':'Sonar durante vacaciones','en':'Play during vacations','fr':'Sonner pendant les vacances','de':'Im Urlaub klingeln','it':'Suona durante le vacanze','pt':'Tocar durante as férias','ru':'Звонить во время отпуска','ja':'休暇中も鳴らす','zh':'假期中响铃','ar':'التشغيل أثناء الإجازات','hi':'छुट्टियों में बजाएँ','bn':'ছুটির সময় বাজান','id':'Berbunyi saat liburan'}); + String get playDuringVacationsHint => _pick({'es':'Si lo apagás, la próxima ejecución saltará al primer día válido.','en':'If you turn this off, the next occurrence will skip to the first valid day.','fr':'Si vous désactivez cette option, la prochaine exécution passera au premier jour valide.','de':'Wenn du dies deaktivierst, springt die nächste Ausführung auf den ersten gültigen Tag.','it':'Se lo disattivi, la prossima esecuzione passerà al primo giorno valido.','pt':'Se você desativar, a próxima execução saltará para o primeiro dia válido.','ru':'Если отключить, следующий запуск перейдёт на первый допустимый день.','ja':'オフにすると、次回実行は最初の有効日にスキップされます。','zh':'关闭后,下次执行将跳到第一个有效日期。','ar':'إذا أوقفته، سيتخطى التشغيل التالي إلى أول يوم صالح.','hi':'इसे बंद करने पर अगली घंटी पहले मान्य दिन पर चली जाएगी।','bn':'এটি বন্ধ করলে পরবর্তী সময় প্রথম বৈধ দিনে চলে যাবে।','id':'Jika dimatikan, jadwal berikutnya akan melompat ke hari valid pertama.'}); + String get saveAlarmAction => _pick({'es':'Guardar alarma','en':'Save alarm','fr':'Enregistrer l’alarme','de':'Alarm speichern','it':'Salva sveglia','pt':'Salvar alarme','ru':'Сохранить будильник','ja':'アラームを保存','zh':'保存闹钟','ar':'حفظ المنبه','hi':'अलार्म सहेजें','bn':'অ্যালার্ম সংরক্ষণ করুন','id':'Simpan alarm'}); + String get chooseOneWeekdayError => _pick({'es':'Elegí al menos un día de la semana.','en':'Choose at least one weekday.','fr':'Choisissez au moins un jour de la semaine.','de':'Wähle mindestens einen Wochentag.','it':'Scegli almeno un giorno della settimana.','pt':'Escolha pelo menos um dia da semana.','ru':'Выберите хотя бы один день недели.','ja':'曜日を少なくとも1つ選択してください。','zh':'请至少选择一个星期几。','ar':'اختر يومًا واحدًا على الأقل من الأسبوع.','hi':'सप्ताह का कम से कम एक दिन चुनें।','bn':'সপ্তাহের অন্তত একটি দিন বেছে নিন।','id':'Pilih setidaknya satu hari dalam seminggu.'}); + + String get androidReliabilityTitle => _pick({'es':'Revisar fiabilidad Android','en':'Review Android reliability','fr':'Vérifier la fiabilité Android','de':'Android-Zuverlässigkeit prüfen','it':'Controlla affidabilità Android','pt':'Revisar confiabilidade Android','ru':'Проверить надёжность Android','ja':'Androidの信頼性を確認','zh':'检查 Android 可靠性','ar':'مراجعة موثوقية Android','hi':'Android विश्वसनीयता जाँचें','bn':'Android নির্ভরযোগ্যতা দেখুন','id':'Tinjau keandalan Android'}); + String androidReliabilityStatus(String exact, String notifications, String screen) => _pick({'es':'Fiabilidad: exactas $exact · notificaciones $notifications · pantalla $screen','en':'Reliability: exact $exact · notifications $notifications · screen $screen','fr':'Fiabilité : exactes $exact · notifications $notifications · écran $screen','de':'Zuverlässigkeit: exakt $exact · Benachrichtigungen $notifications · Bildschirm $screen','it':'Affidabilità: esatte $exact · notifiche $notifications · schermo $screen','pt':'Confiabilidade: exatos $exact · notificações $notifications · tela $screen','ru':'Надёжность: точные $exact · уведомления $notifications · экран $screen','ja':'信頼性: 正確 $exact · 通知 $notifications · 画面 $screen','zh':'可靠性:精确 $exact · 通知 $notifications · 屏幕 $screen','ar':'الموثوقية: الدقة $exact · الإشعارات $notifications · الشاشة $screen','hi':'विश्वसनीयता: सटीक $exact · सूचनाएँ $notifications · स्क्रीन $screen','bn':'নির্ভরযোগ্যতা: নির্ভুল $exact · নোটিফিকেশন $notifications · স্ক্রিন $screen','id':'Keandalan: tepat $exact · notifikasi $notifications · layar $screen'}); + String get statusOk => _pick({'es':'OK','en':'OK','fr':'OK','de':'OK','it':'OK','pt':'OK','ru':'OK','ja':'OK','zh':'OK','ar':'OK','hi':'OK','bn':'OK','id':'OK'}); + String get statusPending => _pick({'es':'pendiente','en':'pending','fr':'en attente','de':'ausstehend','it':'in sospeso','pt':'pendente','ru':'ожидает','ja':'保留中','zh':'待处理','ar':'معلّق','hi':'लंबित','bn':'অপেক্ষমাণ','id':'tertunda'}); + + String get vacationRangesTitle => _pick({'es':'Rangos de vacaciones','en':'Vacation ranges','fr':'Périodes de vacances','de':'Urlaubszeiträume','it':'Intervalli di vacanza','pt':'Períodos de férias','ru':'Периоды отпуска','ja':'休暇期間','zh':'假期范围','ar':'نطاقات الإجازة','hi':'छुट्टी की अवधियाँ','bn':'ছুটির সময়সীমা','id':'Rentang liburan'}); + String get addAction => _pick({'es':'Agregar','en':'Add','fr':'Ajouter','de':'Hinzufügen','it':'Aggiungi','pt':'Adicionar','ru':'Добавить','ja':'追加','zh':'添加','ar':'إضافة','hi':'जोड़ें','bn':'যোগ করুন','id':'Tambah'}); + String get vacationRangesHint => _pick({'es':'Si una alarma tiene "Pausa en vacaciones", se salta automáticamente estos rangos.','en':'If an alarm has “Pause during vacations”, it automatically skips these ranges.','fr':'Si une alarme a « Pause pendant les vacances », elle ignore automatiquement ces périodes.','de':'Wenn ein Alarm „Im Urlaub pausieren“ nutzt, überspringt er diese Zeiträume automatisch.','it':'Se una sveglia ha “Pausa durante le vacanze”, salta automaticamente questi intervalli.','pt':'Se um alarme tiver “Pausar durante as férias”, ele ignora automaticamente estes períodos.','ru':'Если у будильника включена «Пауза во время отпуска», он автоматически пропускает эти периоды.','ja':'アラームが「休暇中は一時停止」の場合、これらの期間は自動的にスキップされます。','zh':'如果闹钟启用了“假期中暂停”,会自动跳过这些范围。','ar':'إذا كان المنبه مضبوطًا على “إيقاف أثناء الإجازات”، فسيتخطى هذه النطاقات تلقائيًا.','hi':'अगर अलार्म में “छुट्टियों में विराम” है, तो ये अवधियाँ अपने-आप छोड़ दी जाएँगी।','bn':'অ্যালার্মে “ছুটির সময় বিরতি” থাকলে এই সময়সীমাগুলো স্বয়ংক্রিয়ভাবে বাদ যাবে।','id':'Jika alarm memakai “Jeda saat liburan”, rentang ini otomatis dilewati.'}); + String get vacationRangesEmpty => _pick({'es':'Sin rangos cargados.','en':'No ranges loaded.','fr':'Aucune période chargée.','de':'Keine Zeiträume geladen.','it':'Nessun intervallo caricato.','pt':'Nenhum período carregado.','ru':'Периоды не добавлены.','ja':'期間が登録されていません。','zh':'没有已加载的范围。','ar':'لا توجد نطاقات محملة.','hi':'कोई अवधि लोड नहीं है।','bn':'কোনও সময়সীমা লোড নেই।','id':'Belum ada rentang.'}); + String get deleteRangeAction => _pick({'es':'Eliminar rango','en':'Delete range','fr':'Supprimer la période','de':'Zeitraum löschen','it':'Elimina intervallo','pt':'Excluir período','ru':'Удалить период','ja':'期間を削除','zh':'删除范围','ar':'حذف النطاق','hi':'अवधि हटाएँ','bn':'সময়সীমা মুছুন','id':'Hapus rentang'}); + String get vacationsDefaultName => _pick({'es':'Vacaciones','en':'Vacations','fr':'Vacances','de':'Urlaub','it':'Vacanze','pt':'Férias','ru':'Отпуск','ja':'休暇','zh':'假期','ar':'إجازة','hi':'छुट्टियाँ','bn':'ছুটি','id':'Liburan'}); + String get newVacationRangeTitle => _pick({'es':'Nuevo rango de vacaciones','en':'New vacation range','fr':'Nouvelle période de vacances','de':'Neuer Urlaubszeitraum','it':'Nuovo intervallo di vacanza','pt':'Novo período de férias','ru':'Новый период отпуска','ja':'新しい休暇期間','zh':'新建假期范围','ar':'نطاق إجازة جديد','hi':'नई छुट्टी अवधि','bn':'নতুন ছুটির সময়সীমা','id':'Rentang liburan baru'}); + String get startLabel => _pick({'es':'Inicio','en':'Start','fr':'Début','de':'Start','it':'Inizio','pt':'Início','ru':'Начало','ja':'開始','zh':'开始','ar':'البداية','hi':'शुरुआत','bn':'শুরু','id':'Mulai'}); + String get endLabel => _pick({'es':'Fin','en':'End','fr':'Fin','de':'Ende','it':'Fine','pt':'Fim','ru':'Конец','ja':'終了','zh':'结束','ar':'النهاية','hi':'समाप्ति','bn':'শেষ','id':'Selesai'}); + String get saveRangeAction => _pick({'es':'Guardar rango','en':'Save range','fr':'Enregistrer la période','de':'Zeitraum speichern','it':'Salva intervallo','pt':'Salvar período','ru':'Сохранить период','ja':'期間を保存','zh':'保存范围','ar':'حفظ النطاق','hi':'अवधि सहेजें','bn':'সময় সংরক্ষণ করুন','id':'Simpan rentang'}); + String get noAlarmsYetTitle => _pick({'es':'Todavía no hay alarmas.','en':'There are no alarms yet.','fr':'Il n’y a pas encore d’alarmes.','de':'Es gibt noch keine Alarme.','it':'Non ci sono ancora sveglie.','pt':'Ainda não há alarmes.','ru':'Будильников пока нет.','ja':'アラームはまだありません。','zh':'还没有闹钟。','ar':'لا توجد منبهات بعد.','hi':'अभी कोई अलार्म नहीं है।','bn':'এখনও কোনও অ্যালার্ম নেই।','id':'Belum ada alarm.'}); + String get noAlarmsYetSubtitle => _pick({'es':'Creá una para diseñar tu despertar musical.','en':'Create one to design your music wake-up.','fr':'Créez-en une pour composer votre réveil musical.','de':'Erstelle einen, um dein musikalisches Aufwachen zu gestalten.','it':'Creane una per progettare il tuo risveglio musicale.','pt':'Crie um para montar seu despertar musical.','ru':'Создайте будильник, чтобы настроить музыкальное пробуждение.','ja':'音楽で目覚める体験を作るには、アラームを作成してください。','zh':'创建一个闹钟,设计你的音乐唤醒体验。','ar':'أنشئ واحدًا لتصميم استيقاظك الموسيقي.','hi':'अपना संगीत अलार्म बनाने के लिए एक बनाएँ।','bn':'আপনার সঙ্গীত অ্যালার্ম সাজাতে একটি তৈরি করুন।','id':'Buat satu untuk merancang bangun tidur dengan musik.'}); + + String alarmScheduleOnce(String date) => _pick({'es':'Una vez · $date','en':'Once · $date','fr':'Une fois · $date','de':'Einmal · $date','it':'Una volta · $date','pt':'Uma vez · $date','ru':'Один раз · $date','ja':'1回 · $date','zh':'一次 · $date','ar':'مرة واحدة · $date','hi':'एक बार · $date','bn':'একবার · $date','id':'Sekali · $date'}); + String alarmScheduleWeekdays(String days) => _pick({'es':'Días: $days','en':'Days: $days','fr':'Jours : $days','de':'Tage: $days','it':'Giorni: $days','pt':'Dias: $days','ru':'Дни: $days','ja':'曜日: $days','zh':'星期:$days','ar':'الأيام: $days','hi':'दिन: $days','bn':'দিন: $days','id':'Hari: $days'}); + String weekdayShort(int day) => switch (day) { + DateTime.monday => _pick({'es':'Lun','en':'Mon','fr':'Lun','de':'Mo','it':'Lun','pt':'Seg','ru':'Пн','ja':'月','zh':'周一','ar':'الإثنين','hi':'सोम','bn':'সোম','id':'Sen'}), + DateTime.tuesday => _pick({'es':'Mar','en':'Tue','fr':'Mar','de':'Di','it':'Mar','pt':'Ter','ru':'Вт','ja':'火','zh':'周二','ar':'الثلاثاء','hi':'मंगल','bn':'মঙ্গল','id':'Sel'}), + DateTime.wednesday => _pick({'es':'Mié','en':'Wed','fr':'Mer','de':'Mi','it':'Mer','pt':'Qua','ru':'Ср','ja':'水','zh':'周三','ar':'الأربعاء','hi':'बुध','bn':'বুধ','id':'Rab'}), + DateTime.thursday => _pick({'es':'Jue','en':'Thu','fr':'Jeu','de':'Do','it':'Gio','pt':'Qui','ru':'Чт','ja':'木','zh':'周四','ar':'الخميس','hi':'गुरु','bn':'বৃহস্পতি','id':'Kam'}), + DateTime.friday => _pick({'es':'Vie','en':'Fri','fr':'Ven','de':'Fr','it':'Ven','pt':'Sex','ru':'Пт','ja':'金','zh':'周五','ar':'الجمعة','hi':'शुक्र','bn':'শুক্র','id':'Jum'}), + DateTime.saturday => _pick({'es':'Sáb','en':'Sat','fr':'Sam','de':'Sa','it':'Sab','pt':'Sáb','ru':'Сб','ja':'土','zh':'周六','ar':'السبت','hi':'शनि','bn':'শনি','id':'Sab'}), + DateTime.sunday => _pick({'es':'Dom','en':'Sun','fr':'Dim','de':'So','it':'Dom','pt':'Dom','ru':'Вс','ja':'日','zh':'周日','ar':'الأحد','hi':'रवि','bn':'রবি','id':'Min'}), + _ => '?', + }; + String weekdayLong(int day) => switch (day) { + DateTime.monday => _pick({'es':'lunes','en':'Monday','fr':'lundi','de':'Montag','it':'lunedì','pt':'segunda-feira','ru':'понедельник','ja':'月曜日','zh':'星期一','ar':'الإثنين','hi':'सोमवार','bn':'সোমবার','id':'Senin'}), + DateTime.tuesday => _pick({'es':'martes','en':'Tuesday','fr':'mardi','de':'Dienstag','it':'martedì','pt':'terça-feira','ru':'вторник','ja':'火曜日','zh':'星期二','ar':'الثلاثاء','hi':'मंगलवार','bn':'মঙ্গলবার','id':'Selasa'}), + DateTime.wednesday => _pick({'es':'miércoles','en':'Wednesday','fr':'mercredi','de':'Mittwoch','it':'mercoledì','pt':'quarta-feira','ru':'среда','ja':'水曜日','zh':'星期三','ar':'الأربعاء','hi':'बुधवार','bn':'বুধবার','id':'Rabu'}), + DateTime.thursday => _pick({'es':'jueves','en':'Thursday','fr':'jeudi','de':'Donnerstag','it':'giovedì','pt':'quinta-feira','ru':'четверг','ja':'木曜日','zh':'星期四','ar':'الخميس','hi':'गुरुवार','bn':'বৃহস্পতিবার','id':'Kamis'}), + DateTime.friday => _pick({'es':'viernes','en':'Friday','fr':'vendredi','de':'Freitag','it':'venerdì','pt':'sexta-feira','ru':'пятница','ja':'金曜日','zh':'星期五','ar':'الجمعة','hi':'शुक्रवार','bn':'শুক্রবার','id':'Jumat'}), + DateTime.saturday => _pick({'es':'sábado','en':'Saturday','fr':'samedi','de':'Samstag','it':'sabato','pt':'sábado','ru':'суббота','ja':'土曜日','zh':'星期六','ar':'السبت','hi':'शनिवार','bn':'শনিবার','id':'Sabtu'}), + DateTime.sunday => _pick({'es':'domingo','en':'Sunday','fr':'dimanche','de':'Sonntag','it':'domenica','pt':'domingo','ru':'воскресенье','ja':'日曜日','zh':'星期日','ar':'الأحد','hi':'रविवार','bn':'রবিবার','id':'Minggu'}), + _ => _pick({'es':'día','en':'day','fr':'jour','de':'Tag','it':'giorno','pt':'dia','ru':'день','ja':'日','zh':'日','ar':'يوم','hi':'दिन','bn':'দিন','id':'hari'}), + }; + String monthName(int month) => switch (month) { + 1 => _pick({'es':'enero','en':'January','fr':'janvier','de':'Januar','it':'gennaio','pt':'janeiro','ru':'января','ja':'1月','zh':'1月','ar':'يناير','hi':'जनवरी','bn':'জানুয়ারি','id':'Januari'}), + 2 => _pick({'es':'febrero','en':'February','fr':'février','de':'Februar','it':'febbraio','pt':'fevereiro','ru':'февраля','ja':'2月','zh':'2月','ar':'فبراير','hi':'फ़रवरी','bn':'ফেব্রুয়ারি','id':'Februari'}), + 3 => _pick({'es':'marzo','en':'March','fr':'mars','de':'März','it':'marzo','pt':'março','ru':'марта','ja':'3月','zh':'3月','ar':'مارس','hi':'मार्च','bn':'মার্চ','id':'Maret'}), + 4 => _pick({'es':'abril','en':'April','fr':'avril','de':'April','it':'aprile','pt':'abril','ru':'апреля','ja':'4月','zh':'4月','ar':'أبريل','hi':'अप्रैल','bn':'এপ্রিল','id':'April'}), + 5 => _pick({'es':'mayo','en':'May','fr':'mai','de':'Mai','it':'maggio','pt':'maio','ru':'мая','ja':'5月','zh':'5月','ar':'مايو','hi':'मई','bn':'মে','id':'Mei'}), + 6 => _pick({'es':'junio','en':'June','fr':'juin','de':'Juni','it':'giugno','pt':'junho','ru':'июня','ja':'6月','zh':'6月','ar':'يونيو','hi':'जून','bn':'জুন','id':'Juni'}), + 7 => _pick({'es':'julio','en':'July','fr':'juillet','de':'Juli','it':'luglio','pt':'julho','ru':'июля','ja':'7月','zh':'7月','ar':'يوليو','hi':'जुलाई','bn':'জুলাই','id':'Juli'}), + 8 => _pick({'es':'agosto','en':'August','fr':'août','de':'August','it':'agosto','pt':'agosto','ru':'августа','ja':'8月','zh':'8月','ar':'أغسطس','hi':'अगस्त','bn':'আগস্ট','id':'Agustus'}), + 9 => _pick({'es':'septiembre','en':'September','fr':'septembre','de':'September','it':'settembre','pt':'setembro','ru':'сентября','ja':'9月','zh':'9月','ar':'سبتمبر','hi':'सितंबर','bn':'সেপ্টেম্বর','id':'September'}), + 10 => _pick({'es':'octubre','en':'October','fr':'octobre','de':'Oktober','it':'ottobre','pt':'outubro','ru':'октября','ja':'10月','zh':'10月','ar':'أكتوبر','hi':'अक्टूबर','bn':'অক্টোবর','id':'Oktober'}), + 11 => _pick({'es':'noviembre','en':'November','fr':'novembre','de':'November','it':'novembre','pt':'novembro','ru':'ноября','ja':'11月','zh':'11月','ar':'نوفمبر','hi':'नवंबर','bn':'নভেম্বর','id':'November'}), + 12 => _pick({'es':'diciembre','en':'December','fr':'décembre','de':'Dezember','it':'dicembre','pt':'dezembro','ru':'декабря','ja':'12月','zh':'12月','ar':'ديسمبر','hi':'दिसंबर','bn':'ডিসেম্বর','id':'Desember'}), + _ => _pick({'es':'mes','en':'month','fr':'mois','de':'Monat','it':'mese','pt':'mês','ru':'месяц','ja':'月','zh':'月','ar':'شهر','hi':'महीना','bn':'মাস','id':'bulan'}), + }; + String dateTimeSentence(DateTime date) { + final local = date.toLocal(); + final hm = '${local.hour.toString().padLeft(2, '0')}:${local.minute.toString().padLeft(2, '0')}'; + return _pick({ + 'es':'${weekdayLong(local.weekday)} ${local.day} de ${monthName(local.month)} a las $hm', + 'en':'${weekdayLong(local.weekday)}, ${monthName(local.month)} ${local.day} at $hm', + 'fr':'${weekdayLong(local.weekday)} ${local.day} ${monthName(local.month)} à $hm', + 'de':'${weekdayLong(local.weekday)}, ${local.day}. ${monthName(local.month)} um $hm', + 'it':'${weekdayLong(local.weekday)} ${local.day} ${monthName(local.month)} alle $hm', + 'pt':'${weekdayLong(local.weekday)}, ${local.day} de ${monthName(local.month)} às $hm', + 'ru':'${weekdayLong(local.weekday)}, ${local.day} ${monthName(local.month)} в $hm', + 'ja':'${monthName(local.month)}${local.day}日(${weekdayLong(local.weekday)})$hm', + 'zh':'${monthName(local.month)}${local.day}日 ${weekdayLong(local.weekday)} $hm', + 'ar':'${weekdayLong(local.weekday)} ${local.day} ${monthName(local.month)} في $hm', + 'hi':'${weekdayLong(local.weekday)}, ${local.day} ${monthName(local.month)} $hm बजे', + 'bn':'${weekdayLong(local.weekday)}, ${local.day} ${monthName(local.month)} $hm', + 'id':'${weekdayLong(local.weekday)}, ${local.day} ${monthName(local.month)} pukul $hm', + }); + } + + String get closeAction => _pick({'es':'Cerrar','en':'Close','fr':'Fermer','de':'Schließen','it':'Chiudi','pt':'Fechar','ru':'Закрыть','ja':'閉じる','zh':'关闭','ar':'إغلاق','hi':'बंद करें','bn':'বন্ধ করুন','id':'Tutup'}); + String get equalizerDisable => _pick({'es':'Desactivar ecualizador','en':'Disable equalizer','fr':'Désactiver l’égaliseur','de':'Equalizer deaktivieren','it':'Disattiva equalizzatore','pt':'Desativar equalizador','ru':'Отключить эквалайзер','ja':'イコライザーを無効化','zh':'关闭均衡器','ar':'تعطيل المعادل','hi':'इक्वलाइज़र बंद करें','bn':'ইকুয়ালাইজার বন্ধ করুন','id':'Nonaktifkan equalizer'}); + String get favoritesAddTooltip => _pick({'es':'Añadir a favoritos','en':'Add to favorites','fr':'Ajouter aux favoris','de':'Zu Favoriten hinzufügen','it':'Aggiungi ai preferiti','pt':'Adicionar aos favoritos','ru':'Добавить в избранное','ja':'お気に入りに追加','zh':'添加到收藏','ar':'إضافة إلى المفضلة','hi':'पसंदीदा में जोड़ें','bn':'ফেভারিটে যোগ করুন','id':'Tambahkan ke favorit'}); + String get qualityUnknown => _pick({'es':'Calidad no informada','en':'Quality not reported','fr':'Qualité non indiquée','de':'Qualität nicht angegeben','it':'Qualità non indicata','pt':'Qualidade não informada','ru':'Качество не указано','ja':'音質情報なし','zh':'未提供质量信息','ar':'الجودة غير مذكورة','hi':'गुणवत्ता उपलब्ध नहीं','bn':'মান জানানো নেই','id':'Kualitas tidak dilaporkan'}); + String qualityOriginal(String quality) => _pick({'es':'Calidad original: $quality','en':'Original quality: $quality','fr':'Qualité d’origine : $quality','de':'Originalqualität: $quality','it':'Qualità originale: $quality','pt':'Qualidade original: $quality','ru':'Исходное качество: $quality','ja':'元の音質: $quality','zh':'原始质量:$quality','ar':'الجودة الأصلية: $quality','hi':'मूल गुणवत्ता: $quality','bn':'মূল মান: $quality','id':'Kualitas asli: $quality'}); + String get recordingActiveTitle => _pick({'es':'Grabando radio','en':'Recording radio','fr':'Enregistrement de la radio','de':'Radio wird aufgenommen','it':'Registrazione radio','pt':'Gravando rádio','ru':'Запись радио','ja':'ラジオを録音中','zh':'正在录制电台','ar':'جارٍ تسجيل الراديو','hi':'रेडियो रिकॉर्ड हो रहा है','bn':'রেডিও রেকর্ড হচ্ছে','id':'Merekam radio'}); + String get recordingDirectTitle => _pick({'es':'Grabación directa','en':'Direct recording','fr':'Enregistrement direct','de':'Direktaufnahme','it':'Registrazione diretta','pt':'Gravação direta','ru':'Прямая запись','ja':'ダイレクト録音','zh':'直接录制','ar':'تسجيل مباشر','hi':'सीधी रिकॉर्डिंग','bn':'সরাসরি রেকর্ডিং','id':'Perekaman langsung'}); + String get stopAction => _pick({'es':'Parar','en':'Stop','fr':'Arrêter','de':'Stoppen','it':'Ferma','pt':'Parar','ru':'Остановить','ja':'停止','zh':'停止','ar':'إيقاف','hi':'रोकें','bn':'থামান','id':'Stop'}); + String get recordAction => _pick({'es':'Grabar','en':'Record','fr':'Enregistrer','de':'Aufnehmen','it':'Registra','pt':'Gravar','ru':'Записать','ja':'録音','zh':'录制','ar':'تسجيل','hi':'रिकॉर्ड करें','bn':'রেকর্ড করুন','id':'Rekam'}); + String get recordingsOpenLatest => _pick({'es':'Abrir última grabación','en':'Open latest recording','fr':'Ouvrir le dernier enregistrement','de':'Letzte Aufnahme öffnen','it':'Apri ultima registrazione','pt':'Abrir última gravação','ru':'Открыть последнюю запись','ja':'最新の録音を開く','zh':'打开最新录音','ar':'فتح آخر تسجيل','hi':'नई रिकॉर्डिंग खोलें','bn':'সর্বশেষ রেকর্ডিং খুলুন','id':'Buka rekaman terbaru'}); + String get recordingsOpenLatestError => _pick({'es':'No se pudo abrir la última grabación','en':'Could not open the latest recording','fr':'Impossible d’ouvrir le dernier enregistrement','de':'Letzte Aufnahme konnte nicht geöffnet werden','it':'Impossibile aprire l’ultima registrazione','pt':'Não foi possível abrir a última gravação','ru':'Не удалось открыть последнюю запись','ja':'最新の録音を開けませんでした','zh':'无法打开最新录音','ar':'تعذر فتح آخر تسجيل','hi':'नई रिकॉर्डिंग नहीं खुल सकी','bn':'সর্বশেষ রেকর্ডিং খোলা যায়নি','id':'Tidak dapat membuka rekaman terbaru'}); + String get recordingsOpenFolderPlainError => _pick({'es':'No se pudo abrir la carpeta de grabaciones','en':'Could not open the recordings folder','fr':'Impossible d’ouvrir le dossier des enregistrements','de':'Aufnahmeordner konnte nicht geöffnet werden','it':'Impossibile aprire la cartella delle registrazioni','pt':'Não foi possível abrir a pasta de gravações','ru':'Не удалось открыть папку записей','ja':'録音フォルダーを開けませんでした','zh':'无法打开录音文件夹','ar':'تعذر فتح مجلد التسجيلات','hi':'रिकॉर्डिंग फ़ोल्डर नहीं खुल सका','bn':'রেকর্ডিং ফোল্ডার খোলা যায়নি','id':'Tidak dapat membuka folder rekaman'}); + String get recordRadioTitle => _pick({'es':'Grabar radio','en':'Record radio','fr':'Enregistrer la radio','de':'Radio aufnehmen','it':'Registra radio','pt':'Gravar rádio','ru':'Записать радио','ja':'ラジオを録音','zh':'录制电台','ar':'تسجيل الراديو','hi':'रेडियो रिकॉर्ड करें','bn':'রেডিও রেকর্ড করুন','id':'Rekam radio'}); + String get recordRadioSubtitle => _pick({'es':'Elegí cuánto tiempo querés grabar.','en':'Choose how long you want to record.','fr':'Choisissez la durée d’enregistrement.','de':'Wähle, wie lange aufgenommen werden soll.','it':'Scegli per quanto tempo registrare.','pt':'Escolha por quanto tempo deseja gravar.','ru':'Выберите длительность записи.','ja':'録音する時間を選んでください。','zh':'选择要录制多长时间。','ar':'اختر مدة التسجيل.','hi':'रिकॉर्डिंग की अवधि चुनें।','bn':'কতক্ষণ রেকর্ড করতে চান বেছে নিন।','id':'Pilih berapa lama ingin merekam.'}); + String get indefiniteOption => _pick({'es':'Indefinida','en':'Indefinite','fr':'Indéfinie','de':'Unbegrenzt','it':'Indefinita','pt':'Indefinida','ru':'Без ограничения','ja':'無期限','zh':'不限时','ar':'غير محدد','hi':'अनिश्चित','bn':'অনির্দিষ্ট','id':'Tidak terbatas'}); + String get customOption => _pick({'es':'Personalizada','en':'Custom','fr':'Personnalisée','de':'Benutzerdefiniert','it':'Personalizzata','pt':'Personalizada','ru':'Своя','ja':'カスタム','zh':'自定义','ar':'مخصص','hi':'कस्टम','bn':'কাস্টম','id':'Kustom'}); + String get recordDurationTitle => _pick({'es':'Duración de grabación','en':'Recording duration','fr':'Durée d’enregistrement','de':'Aufnahmedauer','it':'Durata registrazione','pt':'Duração da gravação','ru':'Длительность записи','ja':'録音時間','zh':'录制时长','ar':'مدة التسجيل','hi':'रिकॉर्डिंग अवधि','bn':'রেকর্ডিং সময়কাল','id':'Durasi rekaman'}); + String get invalidNumber => _pick({'es':'Número inválido','en':'Invalid number','fr':'Nombre invalide','de':'Ungültige Zahl','it':'Numero non valido','pt':'Número inválido','ru':'Недопустимое число','ja':'無効な数値','zh':'数字无效','ar':'رقم غير صالح','hi':'अमान्य संख्या','bn':'অবৈধ সংখ্যা','id':'Nomor tidak valid'}); + String get playerPlaybackErrorTitle => _pick({'es':'No se puede reproducir esta radio','en':'This station cannot be played','fr':'Impossible de lire cette radio','de':'Dieser Sender kann nicht wiedergegeben werden','it':'Impossibile riprodurre questa radio','pt':'Não é possível reproduzir esta rádio','ru':'Не удаётся воспроизвести эту станцию','ja':'このラジオは再生できません','zh':'无法播放此电台','ar':'لا يمكن تشغيل هذه المحطة','hi':'यह रेडियो नहीं चलाया जा सकता','bn':'এই রেডিও চালানো যাচ্ছে না','id':'Radio ini tidak dapat diputar'}); + String get stopPlaybackTooltip => _pick({'es':'Detener reproducción','en':'Stop playback','fr':'Arrêter la lecture','de':'Wiedergabe stoppen','it':'Ferma riproduzione','pt':'Parar reprodução','ru':'Остановить воспроизведение','ja':'再生を停止','zh':'停止播放','ar':'إيقاف التشغيل','hi':'प्लेबैक रोकें','bn':'প্লেব্যাক থামান','id':'Hentikan pemutaran'}); + String get pausePlaybackTooltip => _pick({'es':'Pausar reproducción','en':'Pause playback','fr':'Mettre en pause','de':'Wiedergabe pausieren','it':'Pausa riproduzione','pt':'Pausar reprodução','ru':'Пауза','ja':'再生を一時停止','zh':'暂停播放','ar':'إيقاف مؤقت','hi':'प्लेबैक रोकें','bn':'প্লেব্যাক বিরতি','id':'Jeda pemutaran'}); + String get startPlaybackTooltip => _pick({'es':'Iniciar reproducción','en':'Start playback','fr':'Démarrer la lecture','de':'Wiedergabe starten','it':'Avvia riproduzione','pt':'Iniciar reprodução','ru':'Начать воспроизведение','ja':'再生を開始','zh':'开始播放','ar':'بدء التشغيل','hi':'प्लेबैक शुरू करें','bn':'প্লেব্যাক শুরু করুন','id':'Mulai pemutaran'}); + String get liveNow => _pick({'es':'En vivo','en':'Live','fr':'En direct','de':'Live','it':'In diretta','pt':'Ao vivo','ru':'В эфире','ja':'ライブ','zh':'直播','ar':'مباشر','hi':'लाइव','bn':'লাইভ','id':'Langsung'}); + String get notPlaying => _pick({'es':'No está reproduciendo','en':'Not playing','fr':'Lecture arrêtée','de':'Wird nicht wiedergegeben','it':'Non in riproduzione','pt':'Não está reproduzindo','ru':'Не воспроизводится','ja':'再生していません','zh':'未播放','ar':'لا يتم التشغيل','hi':'नहीं चल रहा','bn':'চলছে না','id':'Tidak memutar'}); + String get helpTitle => _pick({'es':'Ayuda y tutorial','en':'Help and tutorial','fr':'Aide et tutoriel','de':'Hilfe und Tutorial','it':'Aiuto e tutorial','pt':'Ajuda e tutorial','ru':'Помощь и руководство','ja':'ヘルプとチュートリアル','zh':'帮助和教程','ar':'المساعدة والشرح','hi':'मदद और ट्यूटोरियल','bn':'সহায়তা ও টিউটোরিয়াল','id':'Bantuan dan tutorial'}); + String get helpSubtitle => _pick({'es':'Repasá funciones, consejos y novedades de PluriWave.','en':'Review PluriWave features, tips and what’s new.','fr':'Revoyez les fonctions, conseils et nouveautés de PluriWave.','de':'Funktionen, Tipps und Neuigkeiten von PluriWave ansehen.','it':'Rivedi funzioni, consigli e novità di PluriWave.','pt':'Revê funções, dicas e novidades do PluriWave.','ru':'Посмотрите функции, советы и новости PluriWave.','ja':'PluriWaveの機能、ヒント、新着情報を確認できます。','zh':'查看 PluriWave 的功能、技巧和新内容。','ar':'راجع ميزات PluriWave والنصائح والمستجدات.','hi':'PluriWave की सुविधाएँ, सुझाव और नया क्या है देखें।','bn':'PluriWave-এর ফিচার, টিপস ও নতুন বিষয়গুলো দেখুন।','id':'Tinjau fitur, tips, dan hal baru di PluriWave.'}); +} diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 133c1b7..44a9331 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", + "searchScreenTitle": "Buscar sinal", + "searchScreenSubtitle": "Encontre estações por nome, país ou idioma com filtros rápidos e contraste elevado.", + "searchFiltersLabel": "Filtros", + "searchHint": "Rádio Horizonte, jazz, notícias...", + "searchCountryFilterLabel": "País", + "searchLanguageFilterLabel": "Idioma", + "searchMinQualityFilterLabel": "Qualidade mínima", + "searchEmptyTitle": "Busque uma estação", + "searchNoResultsTitle": "Sem resultados", + "searchEmptySubtitle": "Use a barra superior ou os chips para descobrir estações do mundo todo.", + "searchNoResultsSubtitle": "Tente remover filtros ou digitar outro nome para encontrar uma estação ativa.", + "countrySpain": "Espanha", + "countryUsa": "EUA", + "countryMexico": "México", "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "countryUk": "Reino Unido", + "countryFrance": "França", + "countryGermany": "Alemanha", + "countryItaly": "Itália", + "countryBrazil": "Brasil", + "countryJapan": "Japão", + "languageNameSpanish": "espanhol", + "languageNameEnglish": "inglês", + "languageNameFrench": "francês", + "languageNameGerman": "alemão", + "languageNamePortuguese": "português", + "languageNameItalian": "italiano", + "languageNameJapanese": "japonês", + "languageNameArabic": "árabe", + "languageNameRussian": "russo", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb index ab44e1b..0513ff5 100644 --- a/lib/l10n/app_ru.arb +++ b/lib/l10n/app_ru.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "Поиск станций", + "searchScreenSubtitle": "Ищите станции по названию, стране или языку с быстрыми фильтрами и высоким контрастом.", + "searchFiltersLabel": "Фильтры", + "searchHint": "Radio Horizon, джаз, новости...", + "searchCountryFilterLabel": "Страна", + "searchLanguageFilterLabel": "Язык", + "searchMinQualityFilterLabel": "Минимальное качество", + "searchEmptyTitle": "Найдите станцию", + "searchNoResultsTitle": "Ничего не найдено", + "searchEmptySubtitle": "Используйте верхнюю строку и быстрые кнопки, чтобы открыть станции со всего мира.", + "searchNoResultsSubtitle": "Попробуйте убрать фильтры или ввести другое название, чтобы найти активную станцию.", + "countrySpain": "Испания", + "countryUsa": "США", + "countryMexico": "Мексика", + "countryArgentina": "Аргентина", + "countryUk": "Великобритания", + "countryFrance": "Франция", + "countryGermany": "Германия", + "countryItaly": "Италия", + "countryBrazil": "Бразилия", + "countryJapan": "Япония", + "languageNameSpanish": "Испанский", + "languageNameEnglish": "Английский", + "languageNameFrench": "Французский", + "languageNameGerman": "Немецкий", + "languageNamePortuguese": "Португальский", + "languageNameItalian": "Итальянский", + "languageNameJapanese": "Японский", + "languageNameArabic": "Арабский", + "languageNameRussian": "Русский", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 3e6aa9c..d948b86 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -227,36 +227,36 @@ } }, "alarmPostponedCurrentExecution": "Alarm postponed for this occurrence.", - "searchScreenTitle": "Search signal", - "searchScreenSubtitle": "Find stations by name, country, or language with fast filters and high contrast.", - "searchFiltersLabel": "Filters", - "searchHint": "Radio Horizon, jazz, news...", - "searchCountryFilterLabel": "Country", - "searchLanguageFilterLabel": "Language", - "searchMinQualityFilterLabel": "Minimum quality", - "searchEmptyTitle": "Search for a station", - "searchNoResultsTitle": "No results", - "searchEmptySubtitle": "Use the top bar or chips to discover stations from around the world.", - "searchNoResultsSubtitle": "Try removing filters or typing another name to find an active station.", - "countrySpain": "Spain", - "countryUsa": "USA", - "countryMexico": "Mexico", - "countryArgentina": "Argentina", - "countryUk": "UK", - "countryFrance": "France", - "countryGermany": "Germany", - "countryItaly": "Italy", - "countryBrazil": "Brazil", - "countryJapan": "Japan", - "languageNameSpanish": "Spanish", - "languageNameEnglish": "English", - "languageNameFrench": "French", - "languageNameGerman": "German", - "languageNamePortuguese": "Portuguese", - "languageNameItalian": "Italian", - "languageNameJapanese": "Japanese", - "languageNameArabic": "Arabic", - "languageNameRussian": "Russian", + "searchScreenTitle": "搜索电台", + "searchScreenSubtitle": "按名称、国家/地区或语言查找电台,支持快速筛选和高对比度显示。", + "searchFiltersLabel": "筛选", + "searchHint": "Radio Horizon、爵士、新闻...", + "searchCountryFilterLabel": "国家/地区", + "searchLanguageFilterLabel": "语言", + "searchMinQualityFilterLabel": "最低质量", + "searchEmptyTitle": "搜索电台", + "searchNoResultsTitle": "没有结果", + "searchEmptySubtitle": "使用顶部搜索栏或筛选标签,发现来自世界各地的电台。", + "searchNoResultsSubtitle": "试着减少筛选条件,或换个名称搜索,找到正在播出的电台。", + "countrySpain": "西班牙", + "countryUsa": "美国", + "countryMexico": "墨西哥", + "countryArgentina": "阿根廷", + "countryUk": "英国", + "countryFrance": "法国", + "countryGermany": "德国", + "countryItaly": "意大利", + "countryBrazil": "巴西", + "countryJapan": "日本", + "languageNameSpanish": "西班牙语", + "languageNameEnglish": "英语", + "languageNameFrench": "法语", + "languageNameGerman": "德语", + "languageNamePortuguese": "葡萄牙语", + "languageNameItalian": "意大利语", + "languageNameJapanese": "日语", + "languageNameArabic": "阿拉伯语", + "languageNameRussian": "俄语", "homeScreenSubtitle": "Live global radio with clean signals, smart favorites, and a show-style visual experience.", "exploreStations": "Explore stations", "stationsCount": "{count} stations", diff --git a/lib/pantallas/pantalla_ajustes.dart b/lib/pantallas/pantalla_ajustes.dart index 963530e..f51b93e 100644 --- a/lib/pantallas/pantalla_ajustes.dart +++ b/lib/pantallas/pantalla_ajustes.dart @@ -11,6 +11,7 @@ import 'package:uuid/uuid.dart'; import '../estado/estado_idioma.dart'; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; import '../l10n/gen/app_localizations.dart'; import '../modelos/emisora.dart'; import '../modelos/grupo_favoritos.dart'; @@ -982,7 +983,7 @@ class _SeccionEmisoras extends StatelessWidget { const Spacer(), TextButton.icon( icon: const Icon(Icons.add), - label: const Text('Añadir'), + label: Text(AppLocalizations.of(context).customStationsAdd), onPressed: () => _mostrarFormularioAnadir(context), ), ], @@ -1079,6 +1080,7 @@ class _FormularioEmisoraState extends State<_FormularioEmisora> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final bottom = MediaQuery.of(context).viewInsets.bottom; return Padding( padding: EdgeInsets.fromLTRB( @@ -1094,7 +1096,7 @@ class _FormularioEmisoraState extends State<_FormularioEmisora> { crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text( - 'Añadir emisora', + l10n.addStationTitle, style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: 16), @@ -1121,19 +1123,19 @@ class _FormularioEmisoraState extends State<_FormularioEmisora> { keyboardType: TextInputType.url, validator: (v) { if (v == null || v.trim().isEmpty) { - return AppLocalizations.of(context).requiredField; + return l10n.requiredField; } final uri = Uri.tryParse(v.trim()); - if (uri == null || !uri.hasScheme) return 'URL no válida'; + if (uri == null || !uri.hasScheme) return l10n.invalidUrl; return null; }, ), const SizedBox(height: 12), TextFormField( controller: _paisCtrl, - decoration: const InputDecoration( - labelText: 'País (opcional)', - border: OutlineInputBorder(), + decoration: InputDecoration( + labelText: l10n.countryOptionalLabel, + border: const OutlineInputBorder(), ), ), const SizedBox(height: 20), @@ -1170,9 +1172,10 @@ class _SeccionBackup extends StatelessWidget { await Share.shareXFiles( [XFile(file.path)], - subject: 'PluriWave — copia de seguridad', - text: - 'Configuración de PluriWave exportada el ${DateTime.now().toLocal()}', + subject: AppLocalizations.of(context).backupShareSubject, + text: AppLocalizations.of( + context, + ).backupShareText(DateTime.now().toLocal()), ); } catch (e) { if (context.mounted) { @@ -1204,9 +1207,9 @@ class _SeccionBackup extends StatelessWidget { context: context, builder: (ctx) => AlertDialog( - title: const Text('Importar configuración'), - content: const Text( - 'Esto añadirá los favoritos, emisoras y presets del fichero. ¿Continuar?', + title: Text(AppLocalizations.of(ctx).backupImportTitle), + content: Text( + AppLocalizations.of(ctx).backupImportConfirmMessage, ), actions: [ TextButton( @@ -1226,8 +1229,8 @@ class _SeccionBackup extends StatelessWidget { final messenger = ScaffoldMessenger.of(context); await estado.importarConfig(json); messenger.showSnackBar( - const SnackBar( - content: Text('Configuración importada correctamente'), + SnackBar( + content: Text(AppLocalizations.of(context).backupImportSuccess), ), ); } @@ -1264,14 +1267,14 @@ class _SeccionBackup extends StatelessWidget { ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.upload_outlined), - title: const Text('Exportar configuración'), + title: Text(AppLocalizations.of(context).backupExportTitle), 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'), + title: Text(AppLocalizations.of(context).backupImportTitle), subtitle: Text(AppLocalizations.of(context).backupImportSubtitle), onTap: () => _importar(context), ), @@ -1297,14 +1300,14 @@ class _SeccionInfo extends StatelessWidget { final version = snap.hasData ? 'v${snap.data!.version}+${snap.data!.buildNumber}' - : 'Cargando versión...'; + : AppLocalizations.of(ctx).appVersionLoading; return ListTile( contentPadding: EdgeInsets.zero, leading: const PluriIcon( glyph: PluriIconGlyph.settings, variant: PluriIconVariant.filled, ), - title: const Text('PluriWave'), + title: Text(AppLocalizations.of(ctx).appTitle), subtitle: Text( AppLocalizations.of(ctx).appVersionSubtitle(version), ), @@ -1321,7 +1324,7 @@ class _SeccionInfo extends StatelessWidget { AppLocalizations.of(ctx).savedFavoritesTitle, ), trailing: Text( - snap.data?.toString() ?? '—', + snap.data?.toString() ?? AppLocalizations.of(ctx).dash, style: Theme.of(ctx).textTheme.bodyLarge, ), ), @@ -1329,8 +1332,8 @@ class _SeccionInfo extends StatelessWidget { ListTile( contentPadding: EdgeInsets.zero, leading: const Icon(Icons.help_outline_rounded), - title: Text(_helpTitle(ctx)), - subtitle: Text(_helpSubtitle(ctx)), + title: Text(AppLocalizations.of(ctx).helpTitle), + subtitle: Text(AppLocalizations.of(ctx).helpSubtitle), trailing: const Icon(Icons.chevron_right_rounded), onTap: () => PluriOnboardingDialog.mostrar(ctx), ), @@ -1343,12 +1346,14 @@ class _SeccionInfo extends StatelessWidget { ), trailing: const Icon(Icons.check_circle, color: Colors.green), ), - const ListTile( + ListTile( contentPadding: EdgeInsets.zero, - leading: Icon(Icons.music_note_outlined), - title: Text('Audio en background'), - subtitle: Text('Continúa al apagar la pantalla'), - trailing: Icon(Icons.check_circle, color: Colors.green), + leading: const Icon(Icons.music_note_outlined), + title: Text(AppLocalizations.of(ctx).backgroundAudioTitle), + subtitle: Text( + AppLocalizations.of(ctx).backgroundAudioSubtitle, + ), + trailing: const Icon(Icons.check_circle, color: Colors.green), ), ], ), @@ -1357,28 +1362,6 @@ class _SeccionInfo extends StatelessWidget { } } -String _helpTitle(BuildContext context) => switch (Localizations.localeOf( - context, -).languageCode) { - 'es' => 'Ayuda y tutorial', - 'fr' => 'Aide et tutoriel', - 'de' => 'Hilfe und Tutorial', - 'it' => 'Aiuto e tutorial', - 'pt' => 'Ajuda e tutorial', - _ => 'Help and tutorial', -}; - -String _helpSubtitle(BuildContext context) => switch (Localizations.localeOf( - context, -).languageCode) { - 'es' => 'Repasá funciones, consejos y novedades de PluriWave.', - 'fr' => 'Revoyez les fonctions, conseils et nouveautés de PluriWave.', - 'de' => 'Funktionen, Tipps und Neuigkeiten von PluriWave ansehen.', - 'it' => 'Rivedi funzioni, consigli e novità di PluriWave.', - 'pt' => 'Revê funções, dicas e novidades do PluriWave.', - _ => 'Review PluriWave features, tips and what’s new.', -}; - String _formatearDuracionTimer(Duration duracion) { final horas = duracion.inHours; final minutos = duracion.inMinutes.remainder(60); diff --git a/lib/pantallas/pantalla_alarma_sonando.dart b/lib/pantallas/pantalla_alarma_sonando.dart index d06ad67..a75dc6a 100644 --- a/lib/pantallas/pantalla_alarma_sonando.dart +++ b/lib/pantallas/pantalla_alarma_sonando.dart @@ -5,8 +5,9 @@ import 'package:just_audio/just_audio.dart'; import 'package:provider/provider.dart'; import '../estado/estado_alarmas.dart'; -import '../l10n/gen/app_localizations.dart'; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; +import '../l10n/gen/app_localizations.dart'; import '../modelos/alarma_musical.dart'; import '../servicios/servicio_audio.dart'; import '../widgets/pluri_glass_surface.dart'; @@ -189,10 +190,10 @@ class _PantallaAlarmaSonandoState extends State { const SizedBox(height: 8), Text( _fallbackActivo - ? 'Sonando con audio seguro interno.' + ? l10n.alarmRingingFallbackActive : _radioIntentada - ? 'Intentando reproducir tu emisora con máxima calidad disponible.' - : 'Preparando audio seguro interno.', + ? l10n.alarmRingingTryingStation + : l10n.alarmRingingPreparingFallback, textAlign: TextAlign.center, ), const SizedBox(height: 22), diff --git a/lib/pantallas/pantalla_alarmas.dart b/lib/pantallas/pantalla_alarmas.dart index 0636b95..436a7f3 100644 --- a/lib/pantallas/pantalla_alarmas.dart +++ b/lib/pantallas/pantalla_alarmas.dart @@ -3,6 +3,8 @@ import 'package:provider/provider.dart'; import '../estado/estado_alarmas.dart'; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; +import '../l10n/gen/app_localizations.dart'; import '../modelos/alarma_musical.dart'; import '../modelos/emisora.dart'; import '../widgets/pluri_glass_surface.dart'; @@ -16,6 +18,7 @@ class PantallaAlarmas extends StatelessWidget { @override Widget build(BuildContext context) { final estado = context.watch(); + final l10n = AppLocalizations.of(context); return RefreshIndicator( onRefresh: estado.refrescarProgramacion, @@ -23,15 +26,14 @@ class PantallaAlarmas extends StatelessWidget { padding: PluriLayout.pageListPadding, children: [ PluriScreenHeader( - title: 'Despertar musical', - subtitle: - 'Alarmas con radio, sonido seguro, vacaciones inteligentes y próxima ejecución siempre visible.', + title: l10n.alarmScreenTitle, + subtitle: l10n.alarmScreenSubtitle, glyph: PluriIconGlyph.alarm, - primaryActionLabel: 'Crear alarma', + primaryActionLabel: l10n.createAlarmAction, onPrimaryAction: () => _abrirEditor(context), trailing: PluriStatusPill( icon: Icons.alarm_on_rounded, - label: '${estado.alarmas.length} alarmas', + label: l10n.alarmsCount(estado.alarmas.length), ), ), Padding( @@ -79,12 +81,13 @@ class _PanelProximaAlarma extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final proxima = estado.proximaAlarma; - final activasSinProxima = - estado.alarmas - .where((a) => a.activa && a.proximaProgramable == null) - .length; + final activasSinProxima = estado.alarmas + .where((a) => a.activa && a.proximaProgramable == null) + .length; final proximaProgramable = proxima?.proximaProgramable; + return PluriGlassSurface( glowColor: const Color(0xFFFFB86B).withValues(alpha: 0.28), child: Row( @@ -98,20 +101,25 @@ class _PanelProximaAlarma extends StatelessWidget { Text( proxima == null ? activasSinProxima > 0 - ? 'Alarmas activas sin próxima ejecución' - : 'Sin alarmas activas' - : 'Próxima alarma', + ? l10n.activeAlarmsWithoutNextTitle + : l10n.activeAlarmsNoneTitle + : l10n.nextAlarmTitle, style: Theme.of(context).textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w900, - ), + fontWeight: FontWeight.w900, + ), ), const SizedBox(height: 4), Text( proxima == null ? activasSinProxima > 0 - ? 'Hay $activasSinProxima alarma(s) activas, pero ahora mismo no tienen una fecha futura válida. Revisá fecha, días y vacaciones.' - : 'Creá una alarma y PluriWave calculará la siguiente ejecución automáticamente.' - : '${proxima.nombre} · ${_fechaHora(proximaProgramable!)}', + ? l10n.activeAlarmsWithoutNextSubtitle( + activasSinProxima, + ) + : l10n.createAlarmHint + : l10n.alarmNextSummary( + proxima.nombre, + _fechaHora(l10n, proximaProgramable!), + ), ), ], ), @@ -129,9 +137,10 @@ class _TarjetaAlarma extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final estado = context.watch(); final excepcion = estado.ultimaExcepcionPara(alarma.id); - final mensajeVacaciones = _mensajeVacaciones(estado.vacaciones); + final mensajeVacaciones = _mensajeVacaciones(l10n, estado.vacaciones); return PluriGlassSurface( glowColor: const Color(0xFF22D3EE).withValues(alpha: 0.22), child: Column( @@ -174,14 +183,14 @@ class _TarjetaAlarma extends StatelessWidget { children: [ _InfoChip( icon: Icons.repeat_rounded, - label: _programacion(alarma), + label: _programacion(l10n, alarma), ), _InfoChip( icon: Icons.beach_access_rounded, label: alarma.sonarEnVacaciones - ? 'Suena en vacaciones' - : 'Pausa en vacaciones', + ? l10n.alarmVacationPlay + : l10n.alarmVacationPause, ), _InfoChip( icon: Icons.volume_up_rounded, @@ -189,7 +198,7 @@ class _TarjetaAlarma extends StatelessWidget { ), _InfoChip( icon: Icons.trending_up_rounded, - label: 'Fade-in ${alarma.fadeInSegundos}s', + label: l10n.fadeInSeconds(alarma.fadeInSegundos), ), ], ), @@ -197,20 +206,22 @@ class _TarjetaAlarma extends StatelessWidget { if (alarma.proximaProgramable != null) _NoticeLine( icon: Icons.event_available_rounded, - text: - 'Siguiente ejecución: ${_fechaHora(alarma.proximaProgramable!)}', + text: l10n.alarmNextExecution( + _fechaHora(l10n, alarma.proximaProgramable!), + ), ) else - const _NoticeLine( + _NoticeLine( icon: Icons.pause_circle_outline_rounded, - text: 'No tiene próxima ejecución activa.', + text: l10n.alarmNoNextExecution, ), if (excepcion != null) ...[ const SizedBox(height: 6), _NoticeLine( icon: Icons.skip_next_rounded, - text: - 'Una ejecución fue omitida: ${_fechaHora(excepcion.ejecucion)}.', + text: l10n.alarmSkippedExecution( + _fechaHora(l10n, excepcion.ejecucion), + ), ), ], if (mensajeVacaciones != null) ...[ @@ -225,12 +236,12 @@ class _TarjetaAlarma extends StatelessWidget { children: [ TextButton.icon( icon: const Icon(Icons.edit_rounded), - label: const Text('Editar'), + label: Text(l10n.editAction), onPressed: () => _abrirEditor(context, alarma: alarma), ), TextButton.icon( icon: const Icon(Icons.skip_next_rounded), - label: const Text('Omitir siguiente'), + label: Text(l10n.skipNextAction), onPressed: alarma.proximaProgramable == null ? null @@ -250,8 +261,13 @@ class _TarjetaAlarma extends StatelessWidget { SnackBar( content: Text( actualizada?.proximaProgramable == null - ? 'Alarma omitida. No queda próxima ejecución.' - : 'Alarma omitida. Volverá el ${_fechaHora(actualizada!.proximaProgramable!)}.', + ? l10n.alarmSkippedNoNextSnackbar + : l10n.alarmSkippedReturnsSnackbar( + _fechaHora( + l10n, + actualizada!.proximaProgramable!, + ), + ), ), ), ); @@ -260,7 +276,7 @@ class _TarjetaAlarma extends StatelessWidget { ), const Spacer(), IconButton( - tooltip: 'Eliminar', + tooltip: l10n.deleteAction, icon: const Icon(Icons.delete_outline_rounded), onPressed: () => estado.eliminarAlarma(alarma.id), ), @@ -271,7 +287,10 @@ class _TarjetaAlarma extends StatelessWidget { ); } - String? _mensajeVacaciones(List vacaciones) { + String? _mensajeVacaciones( + AppLocalizations l10n, + List vacaciones, + ) { if (alarma.sonarEnVacaciones) return null; final ahora = DateTime.now(); RangoVacaciones? actual; @@ -283,12 +302,17 @@ class _TarjetaAlarma extends StatelessWidget { } if (actual != null) { if (alarma.proximaProgramable == null) { - return 'Está pausada por vacaciones (${actual.nombre}) y sin próxima ejecución.'; + return l10n.alarmVacationPausedNoNext(actual.nombre); } - return 'Está pausada por vacaciones (${actual.nombre}) y vuelve el ${_fechaHora(alarma.proximaProgramable!)}.'; + return l10n.alarmVacationPausedReturns( + actual.nombre, + _fechaHora(l10n, alarma.proximaProgramable!), + ); } if (alarma.proximaProgramable != null) { - return 'Con vacaciones activas, volverá a sonar el ${_fechaHora(alarma.proximaProgramable!)}.'; + return l10n.alarmVacationReturns( + _fechaHora(l10n, alarma.proximaProgramable!), + ); } return null; } @@ -330,9 +354,10 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { void initState() { super.initState(); final alarma = widget.alarma; + final l10n = AppLocalizations.of(context); final ahora = DateTime.now().add(const Duration(minutes: 5)); _nombreController = TextEditingController( - text: alarma?.nombre ?? 'Despertador musical', + text: alarma?.nombre ?? l10n.defaultAlarmName, ); _hora = TimeOfDay( hour: alarma?.hora ?? ahora.hour, @@ -356,6 +381,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final radio = context.watch(); final bottom = MediaQuery.of(context).viewInsets.bottom; if (!_favoritosSolicitados) { @@ -393,7 +419,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { const SizedBox(width: 12), Expanded( child: Text( - widget.alarma == null ? 'Nueva alarma' : 'Editar alarma', + widget.alarma == null ? l10n.newAlarmTitle : l10n.editAlarmTitle, style: Theme.of(context).textTheme.titleLarge?.copyWith( fontWeight: FontWeight.w900, ), @@ -408,7 +434,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { const SizedBox(height: 14), TextField( controller: _nombreController, - decoration: const InputDecoration(labelText: 'Nombre'), + decoration: InputDecoration(labelText: l10n.nameLabel), ), const SizedBox(height: 12), Row( @@ -416,7 +442,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { Expanded( child: _PickerButton( icon: Icons.schedule_rounded, - label: 'Hora', + label: l10n.timeLabel, value: _hora.format(context), onTap: _elegirHora, ), @@ -425,7 +451,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { Expanded( child: _PickerButton( icon: Icons.event_rounded, - label: 'Fecha', + label: l10n.dateLabel, value: _fechaCorta(_fecha), onTap: _tipo == TipoProgramacionAlarma.unica @@ -437,18 +463,18 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { ), const SizedBox(height: 12), SegmentedButton( - segments: const [ + segments: [ ButtonSegment( value: TipoProgramacionAlarma.unica, - label: Text('Una vez'), + label: Text(l10n.oneTimeOption), ), ButtonSegment( value: TipoProgramacionAlarma.diaria, - label: Text('Diaria'), + label: Text(l10n.dailyOption), ), ButtonSegment( value: TipoProgramacionAlarma.diasSemana, - label: Text('Días'), + label: Text(l10n.weekdaysOption), ), ], selected: {_tipo}, @@ -462,7 +488,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { children: [ for (var i = DateTime.monday; i <= DateTime.sunday; i++) FilterChip( - label: Text(_diaCorto(i)), + label: Text(l10n.weekdayShort(i)), selected: _diasSemana.contains(i), onSelected: (selected) => setState(() { @@ -477,7 +503,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { const SizedBox(height: 14), _SectionLabel( icon: 'assets/icons/alarmas/fallback_sound.png', - text: 'Sonido y volumen', + text: l10n.soundAndVolumeTitle, ), Slider( value: _volumen, @@ -490,11 +516,11 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { const SizedBox(height: 8), ListTile( contentPadding: EdgeInsets.zero, - title: const Text('Fade-in de alarma'), + title: Text(l10n.alarmFadeInTitle), subtitle: Text( _fadeInSegundos == 0 - ? '0 s (sin transición)' - : '$_fadeInSegundos s (de 5% al volumen elegido)', + ? l10n.alarmFadeInOff + : l10n.alarmFadeInProgress(_fadeInSegundos), ), ), Slider( @@ -509,21 +535,21 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { ), DropdownButtonFormField( initialValue: _sonidoInterno, - decoration: const InputDecoration( - labelText: 'Sonido seguro interno', + decoration: InputDecoration( + labelText: l10n.soundInternalSafe, ), - items: const [ + items: [ DropdownMenuItem( value: SonidoInternoAlarma.amanecer, - child: Text('Amanecer cálido'), + child: Text(l10n.soundWarmSunrise), ), DropdownMenuItem( value: SonidoInternoAlarma.campanaSuave, - child: Text('Campana suave'), + child: Text(l10n.soundSoftBell), ), DropdownMenuItem( value: SonidoInternoAlarma.pulsoDigital, - child: Text('Pulso digital'), + child: Text(l10n.soundDigitalPulse), ), ], onChanged: @@ -535,14 +561,14 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { DropdownButtonFormField( key: ValueKey(_emisora?.uuid ?? 'sin-emisora'), initialValue: _emisora?.uuid, - decoration: const InputDecoration( - labelText: 'Emisora favorita', - prefixIcon: Icon(Icons.radio_rounded), + decoration: InputDecoration( + labelText: l10n.favoriteStationLabel, + prefixIcon: const Icon(Icons.radio_rounded), ), items: [ - const DropdownMenuItem( + DropdownMenuItem( value: '', - child: Text('Sin emisora: usar sonido interno'), + child: Text(l10n.noStationUseInternalSound), ), for (final emisora in favoritas) DropdownMenuItem( @@ -564,9 +590,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { ), if (favoritas.isEmpty) ...[ const SizedBox(height: 6), - const Text( - 'Guardá emisoras en Favoritos para usarlas como alarma musical.', - ), + Text(l10n.saveFavoritesAlarmHint), ], if (radio.emisoraActual != null) ...[ const SizedBox(height: 8), @@ -576,7 +600,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { onPressed: () => setState(() => _emisora = radio.emisoraActual), icon: const Icon(Icons.add_task_rounded), - label: const Text('Usar emisora actual'), + label: Text(l10n.useCurrentStationAction), ), ), ], @@ -584,22 +608,19 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { SwitchListTile.adaptive( contentPadding: EdgeInsets.zero, value: _sonarEnVacaciones, - onChanged: - (value) => setState(() => _sonarEnVacaciones = value), + onChanged: (value) => setState(() => _sonarEnVacaciones = value), secondary: const _AssetIcon( 'assets/icons/alarmas/vacation_wave.png', size: 42, ), - title: const Text('Sonar durante vacaciones'), - subtitle: const Text( - 'Si lo apagás, la próxima ejecución saltará al primer día válido.', - ), + title: Text(l10n.playDuringVacations), + subtitle: Text(l10n.playDuringVacationsHint), ), const SizedBox(height: 16), FilledButton.icon( onPressed: _guardar, icon: const Icon(Icons.check_rounded), - label: const Text('Guardar alarma'), + label: Text(l10n.saveAlarmAction), ), ], ), @@ -627,7 +648,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { Future _guardar() async { if (_tipo == TipoProgramacionAlarma.diasSemana && _diasSemana.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Elegí al menos un día de la semana.')), + SnackBar(content: Text(AppLocalizations.of(context).chooseOneWeekdayError)), ); return; } @@ -645,7 +666,7 @@ class _EditorAlarmaSheetState extends State<_EditorAlarmaSheet> { .copyWith( nombre: _nombreController.text.trim().isEmpty - ? 'Despertador musical' + ? AppLocalizations.of(context).defaultAlarmName : _nombreController.text.trim(), hora: _hora.hour, minuto: _hora.minute, @@ -688,7 +709,11 @@ class _AccesoDiagnostico extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final diag = estado.diagnostico; + final exactStatus = diag?.puedeProgramarExactas == true ? l10n.statusOk : l10n.statusPending; + final notificationStatus = diag?.notificacionesPermitidas == true ? l10n.statusOk : l10n.statusPending; + final screenStatus = diag?.puedeUsarPantallaCompleta == true ? l10n.statusOk : l10n.statusPending; return TextButton.icon( icon: const _AssetIcon( 'assets/icons/alarmas/android_reliability.png', @@ -696,8 +721,12 @@ class _AccesoDiagnostico extends StatelessWidget { ), label: Text( diag == null - ? 'Revisar fiabilidad Android' - : 'Fiabilidad: exactas ${diag.puedeProgramarExactas ? 'OK' : 'pendiente'} ? notificaciones ${diag.notificacionesPermitidas ? 'OK' : 'pendiente'} ? pantalla ${diag.puedeUsarPantallaCompleta ? 'OK' : 'pendiente'}', + ? l10n.androidReliabilityTitle + : l10n.androidReliabilityStatus( + exactStatus, + notificationStatus, + screenStatus, + ), ), onPressed: () async { if (diag != null && !diag.puedeProgramarExactas) { @@ -722,6 +751,7 @@ class _PanelVacaciones extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final vacaciones = [...estado.vacaciones] ..sort((a, b) => a.inicioDia.compareTo(b.inicioDia)); return PluriGlassSurface( @@ -738,7 +768,7 @@ class _PanelVacaciones extends StatelessWidget { const SizedBox(width: 10), Expanded( child: Text( - 'Rangos de vacaciones', + l10n.vacationRangesTitle, style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w900, ), @@ -747,17 +777,14 @@ class _PanelVacaciones extends StatelessWidget { FilledButton.tonalIcon( onPressed: () => _abrirAlta(context), icon: const Icon(Icons.add_rounded), - label: const Text('Agregar'), + label: Text(l10n.addAction), ), ], ), const SizedBox(height: 8), - Text( - 'Si una alarma tiene "Pausa en vacaciones", se salta automáticamente estos rangos.', - ), - const SizedBox(height: 10), + Text(l10n.vacationRangesHint), if (vacaciones.isEmpty) - const Text('Sin rangos cargados.') + Text(l10n.vacationRangesEmpty) else for (final rango in vacaciones) ListTile( @@ -768,7 +795,7 @@ class _PanelVacaciones extends StatelessWidget { '${_fechaCorta(rango.inicioDia)} → ${_fechaCorta(rango.finDia)}', ), trailing: IconButton( - tooltip: 'Eliminar rango', + tooltip: l10n.deleteRangeAction, onPressed: () => estado.eliminarRangoVacaciones(rango.id), icon: const Icon(Icons.delete_outline_rounded), ), @@ -807,7 +834,9 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { final hoy = DateTime.now(); _inicio = DateTime(hoy.year, hoy.month, hoy.day); _fin = _inicio.add(const Duration(days: 2)); - _nombreController = TextEditingController(text: 'Vacaciones'); + _nombreController = TextEditingController( + text: AppLocalizations.of(context).vacationsDefaultName, + ); } @override @@ -818,6 +847,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final bottom = MediaQuery.of(context).viewInsets.bottom; return Padding( padding: EdgeInsets.fromLTRB(12, 12, 12, bottom + 12), @@ -829,7 +859,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Nuevo rango de vacaciones', + l10n.newVacationRangeTitle, style: Theme.of( context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.w900), @@ -837,7 +867,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { const SizedBox(height: 12), TextField( controller: _nombreController, - decoration: const InputDecoration(labelText: 'Nombre'), + decoration: InputDecoration(labelText: l10n.nameLabel), ), const SizedBox(height: 12), Row( @@ -845,7 +875,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { Expanded( child: _PickerButton( icon: Icons.play_arrow_rounded, - label: 'Inicio', + label: l10n.startLabel, value: _fechaCorta(_inicio), onTap: () => _elegirFecha(esInicio: true), ), @@ -854,7 +884,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { Expanded( child: _PickerButton( icon: Icons.stop_rounded, - label: 'Fin', + label: l10n.endLabel, value: _fechaCorta(_fin), onTap: () => _elegirFecha(esInicio: false), ), @@ -865,7 +895,7 @@ class _EditorVacacionesSheetState extends State<_EditorVacacionesSheet> { FilledButton.icon( onPressed: _guardar, icon: const Icon(Icons.check_rounded), - label: const Text('Guardar rango'), + label: Text(l10n.saveRangeAction), ), ], ), @@ -1012,14 +1042,15 @@ class _EmptyAlarmas extends StatelessWidget { @override Widget build(BuildContext context) { - return const PluriGlassSurface( + final l10n = AppLocalizations.of(context); + return PluriGlassSurface( child: Column( children: [ - _AssetIcon('assets/icons/alarmas/alarm_music.png', size: 92), - SizedBox(height: 12), - Text('Todavía no hay alarmas.'), - SizedBox(height: 4), - Text('Creá una para diseñar tu despertar musical.'), + const _AssetIcon('assets/icons/alarmas/alarm_music.png', size: 92), + const SizedBox(height: 12), + Text(l10n.noAlarmsYetTitle), + const SizedBox(height: 4), + Text(l10n.noAlarmsYetSubtitle), ], ), ); @@ -1029,59 +1060,20 @@ class _EmptyAlarmas extends StatelessWidget { String _hora(AlarmaMusical alarma) => '${alarma.hora.toString().padLeft(2, '0')}:${alarma.minuto.toString().padLeft(2, '0')}'; -String _programacion(AlarmaMusical alarma) { +String _programacion(AppLocalizations l10n, AlarmaMusical alarma) { return switch (alarma.tipoProgramacion) { TipoProgramacionAlarma.unica => - 'Una vez · ${_fechaCorta(alarma.fechaUnica ?? DateTime.now())}', - TipoProgramacionAlarma.diaria => 'Diaria', + l10n.alarmScheduleOnce(_fechaCorta(alarma.fechaUnica ?? DateTime.now())), + TipoProgramacionAlarma.diaria => l10n.dailyOption, TipoProgramacionAlarma.diasSemana => - 'Días: ${alarma.diasSemana.map(_diaCorto).join(', ')}', + l10n.alarmScheduleWeekdays( + alarma.diasSemana.map(l10n.weekdayShort).join(', '), + ), }; } -String _fechaHora(DateTime fecha) { - final local = fecha.toLocal(); - return '${_diaLargo(local.weekday)} ${local.day} de ${_mes(local.month)} a las ' - '${local.hour.toString().padLeft(2, '0')}:${local.minute.toString().padLeft(2, '0')}'; -} +String _fechaHora(AppLocalizations l10n, DateTime fecha) => + l10n.dateTimeSentence(fecha); String _fechaCorta(DateTime fecha) => '${fecha.day.toString().padLeft(2, '0')}/${fecha.month.toString().padLeft(2, '0')}/${fecha.year}'; - -String _diaCorto(int dia) => switch (dia) { - DateTime.monday => 'Lun', - DateTime.tuesday => 'Mar', - DateTime.wednesday => 'Mié', - DateTime.thursday => 'Jue', - DateTime.friday => 'Vie', - DateTime.saturday => 'Sáb', - DateTime.sunday => 'Dom', - _ => '?', -}; - -String _diaLargo(int dia) => switch (dia) { - DateTime.monday => 'lunes', - DateTime.tuesday => 'martes', - DateTime.wednesday => 'miércoles', - DateTime.thursday => 'jueves', - DateTime.friday => 'viernes', - DateTime.saturday => 'sábado', - DateTime.sunday => 'domingo', - _ => 'día', -}; - -String _mes(int mes) => switch (mes) { - 1 => 'enero', - 2 => 'febrero', - 3 => 'marzo', - 4 => 'abril', - 5 => 'mayo', - 6 => 'junio', - 7 => 'julio', - 8 => 'agosto', - 9 => 'septiembre', - 10 => 'octubre', - 11 => 'noviembre', - 12 => 'diciembre', - _ => 'mes', -}; diff --git a/lib/pantallas/pantalla_buscar.dart b/lib/pantallas/pantalla_buscar.dart index 4132bb5..c2024a0 100644 --- a/lib/pantallas/pantalla_buscar.dart +++ b/lib/pantallas/pantalla_buscar.dart @@ -3,6 +3,7 @@ import 'package:flutter_animate/flutter_animate.dart'; import 'package:provider/provider.dart'; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; import '../l10n/gen/app_localizations.dart'; import '../widgets/pluri_glass_surface.dart'; import '../widgets/pluri_icon.dart'; diff --git a/lib/pantallas/pantalla_favoritos.dart b/lib/pantallas/pantalla_favoritos.dart index 001e6a9..eef7abd 100644 --- a/lib/pantallas/pantalla_favoritos.dart +++ b/lib/pantallas/pantalla_favoritos.dart @@ -49,10 +49,10 @@ class PantallaFavoritos extends StatelessWidget { } final gruposVisibles = grupos.isEmpty - ? const [ + ? [ GrupoFavoritos( id: GrupoFavoritos.sinAsignarId, - nombre: 'Sin asignar', + nombre: l10n.favoriteGroupsUnassigned, orden: 0, protegido: true, ), diff --git a/lib/pantallas/pantalla_inicio.dart b/lib/pantallas/pantalla_inicio.dart index b0f403b..7a47a1b 100644 --- a/lib/pantallas/pantalla_inicio.dart +++ b/lib/pantallas/pantalla_inicio.dart @@ -4,6 +4,8 @@ import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart' as shimmer; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; +import '../l10n/gen/app_localizations.dart'; import '../widgets/pluri_glass_surface.dart'; import '../widgets/pluri_icon.dart'; import '../widgets/pluri_layout.dart'; @@ -41,52 +43,61 @@ class _PantallaInicioState extends State { Widget build(BuildContext context) { final estado = context.watch(); final theme = Theme.of(context); + final l10n = AppLocalizations.of(context); return RefreshIndicator( onRefresh: estado.cargarPopulares, child: CustomScrollView( slivers: [ - SliverToBoxAdapter(child: _heroHeader(context, estado)), - SliverToBoxAdapter(child: _seccionCercanas(estado, theme)), - SliverToBoxAdapter(child: _seccionTendencias(estado, theme)), - SliverToBoxAdapter(child: _chipGeneros(context, theme)), + SliverToBoxAdapter(child: _heroHeader(context, estado, l10n)), + SliverToBoxAdapter(child: _seccionCercanas(estado, theme, l10n)), + SliverToBoxAdapter(child: _seccionTendencias(estado, theme, l10n)), + SliverToBoxAdapter(child: _chipGeneros(context, theme, l10n)), if (estado.error != null) - SliverToBoxAdapter(child: _errorBanner(estado, theme)), + SliverToBoxAdapter(child: _errorBanner(estado, theme, l10n)), SliverPadding( padding: const EdgeInsets.fromLTRB(PluriLayout.horizontal, 0, PluriLayout.horizontal, PluriLayout.bottomChromeInset), - sliver: _gridEmisoras(estado), + sliver: _gridEmisoras(estado, l10n), ), ], ), ); } - Widget _heroHeader(BuildContext context, EstadoRadio estado) { + Widget _heroHeader( + BuildContext context, + EstadoRadio estado, + AppLocalizations l10n, + ) { return PluriScreenHeader( - title: 'PluriWave', - subtitle: 'Radio global en vivo con senales limpias, favoritos inteligentes y una experiencia visual de concurso.', + title: l10n.appTitle, + subtitle: l10n.homeScreenSubtitle, glyph: PluriIconGlyph.home, - primaryActionLabel: 'Explorar emisoras', + primaryActionLabel: l10n.exploreStations, onPrimaryAction: estado.cargarPopulares, trailing: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ PluriStatusPill( icon: Icons.public_rounded, - label: '${estado.emisorasInicio.length} radios', + label: l10n.homeStationsCount(estado.emisorasInicio.length), accent: Theme.of(context).colorScheme.secondary, ), const SizedBox(height: 8), - const PluriStatusPill( + PluriStatusPill( icon: Icons.hd_rounded, - label: 'Calidad HD', + label: l10n.qualityHd, ), ], ), ); } - Widget _seccionCercanas(EstadoRadio estado, ThemeData theme) { + Widget _seccionCercanas( + EstadoRadio estado, + ThemeData theme, + AppLocalizations l10n, + ) { final pais = estado.paisCercanoDetectado; return Padding( padding: const EdgeInsets.fromLTRB(PluriLayout.horizontal, 8, PluriLayout.horizontal, 0), @@ -99,7 +110,7 @@ class _PantallaInicioState extends State { children: [ Expanded( child: Text( - pais == null ? 'Cerca de vos' : 'Cerca de vos ? $pais', + pais == null ? l10n.nearYou : l10n.nearYouInCountry(pais), style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w900, ), @@ -116,7 +127,7 @@ class _PantallaInicioState extends State { child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.my_location_rounded, size: 18), - label: const Text('Detectar'), + label: Text(l10n.detectAction), ), ], ), @@ -155,7 +166,11 @@ class _PantallaInicioState extends State { ); } - Widget _seccionTendencias(EstadoRadio estado, ThemeData theme) { + Widget _seccionTendencias( + EstadoRadio estado, + ThemeData theme, + AppLocalizations l10n, + ) { return Padding( padding: const EdgeInsets.fromLTRB(PluriLayout.horizontal, 8, PluriLayout.horizontal, 0), child: PluriGlassSurface( @@ -163,7 +178,7 @@ class _PantallaInicioState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Radar en directo', style: theme.textTheme.titleMedium), + Text(l10n.liveRadar, style: theme.textTheme.titleMedium), const SizedBox(height: 8), SizedBox( height: 56, @@ -199,7 +214,11 @@ class _PantallaInicioState extends State { ); } - Widget _chipGeneros(BuildContext context, ThemeData theme) { + Widget _chipGeneros( + BuildContext context, + ThemeData theme, + AppLocalizations l10n, + ) { return Padding( padding: const EdgeInsets.fromLTRB(PluriLayout.horizontal, 16, PluriLayout.horizontal, 8), child: PluriGlassSurface( @@ -207,7 +226,7 @@ class _PantallaInicioState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('Géneros', style: theme.textTheme.titleMedium), + Text(l10n.genresTitle, style: theme.textTheme.titleMedium), const SizedBox(height: 8), Wrap( spacing: 8, @@ -216,7 +235,7 @@ class _PantallaInicioState extends State { _generos.map((g) { final seleccionado = _generoSeleccionado == g; return FilterChip( - label: Text(g), + label: Text(l10n.genreName(g)), selected: seleccionado, onSelected: (_) { setState(() { @@ -237,7 +256,11 @@ class _PantallaInicioState extends State { ); } - Widget _errorBanner(EstadoRadio estado, ThemeData theme) { + Widget _errorBanner( + EstadoRadio estado, + ThemeData theme, + AppLocalizations l10n, + ) { return Padding( padding: const EdgeInsets.all(16), child: PluriGlassSurface( @@ -249,7 +272,7 @@ class _PantallaInicioState extends State { Expanded(child: Text(estado.error!)), TextButton( onPressed: estado.cargarPopulares, - child: const Text('Reintentar'), + child: Text(l10n.retryAction), ), ], ), @@ -257,7 +280,7 @@ class _PantallaInicioState extends State { ); } - Widget _gridEmisoras(EstadoRadio estado) { + Widget _gridEmisoras(EstadoRadio estado, AppLocalizations l10n) { final emisoras = _generoSeleccionado != null ? estado.resultadosBusqueda @@ -282,11 +305,11 @@ class _PantallaInicioState extends State { } if (emisoras.isEmpty) { - return const SliverFillRemaining( + return SliverFillRemaining( child: PluriEmptyState( glyph: PluriIconGlyph.home, - title: 'No hay emisoras disponibles', - subtitle: 'Proba refrescar o elegir otro género para volver a capturar señal.', + title: l10n.noStationsAvailable, + subtitle: l10n.noStationsAvailableSubtitle, ), ); } diff --git a/lib/pantallas/pantalla_reproductor.dart b/lib/pantallas/pantalla_reproductor.dart index e9b6495..5619765 100644 --- a/lib/pantallas/pantalla_reproductor.dart +++ b/lib/pantallas/pantalla_reproductor.dart @@ -5,6 +5,8 @@ import 'package:provider/provider.dart'; import 'package:shimmer/shimmer.dart'; import '../estado/estado_radio.dart'; +import '../l10n/app_localizations_ext.dart'; +import '../l10n/gen/app_localizations.dart'; import '../modelos/emisora.dart'; import '../servicios/servicio_audio.dart'; import '../servicios/servicio_timer.dart'; @@ -75,6 +77,7 @@ class _PantallaReproductorState extends State final theme = Theme.of(context); final tokens = context.pluriTokens; final estado = context.watch(); + final l10n = AppLocalizations.of(context); final emisoraActiva = estado.emisoraActual ?? widget.emisora; final esFavorito = estado.listaFavoritos.any( (e) => e.uuid == emisoraActiva.uuid, @@ -86,7 +89,7 @@ class _PantallaReproductorState extends State elevation: 0, leading: IconButton( icon: const Icon(Icons.keyboard_arrow_down_rounded, size: 32), - tooltip: 'Cerrar', + tooltip: l10n.closeAction, onPressed: () => Navigator.pop(context), ), actions: [ @@ -99,8 +102,8 @@ class _PantallaReproductorState extends State ), tooltip: estado.ecualizadorActivo - ? 'Desactivar ecualizador' - : 'Activar ecualizador', + ? l10n.equalizerDisable + : l10n.equalizerEnable, onPressed: () => estado.cambiarEcualizadorActivo(!estado.ecualizadorActivo), @@ -112,7 +115,7 @@ class _PantallaReproductorState extends State : Icons.favorite_outline_rounded, color: esFavorito ? theme.colorScheme.error : null, ), - tooltip: esFavorito ? 'Quitar de favoritos' : 'Anadir a favoritos', + tooltip: esFavorito ? l10n.favoritesRemoveTooltip : l10n.favoritesAddTooltip, onPressed: () async => estado.toggleFavorito(emisoraActiva), ), ], @@ -148,7 +151,7 @@ class _PantallaReproductorState extends State const SizedBox(height: 6), if (emisoraActiva.codec != null || emisoraActiva.bitrate != null) Text( - _codecInfo(emisoraActiva), + _codecInfo(context, emisoraActiva), style: theme.textTheme.bodySmall?.copyWith( color: Colors.white.withValues(alpha: 0.72), ), @@ -186,13 +189,13 @@ class _PantallaReproductorState extends State ); } - String _codecInfo(Emisora e) { + String _codecInfo(BuildContext context, Emisora e) { final parts = []; if (e.codec != null) parts.add(e.codec!.toUpperCase()); if (e.bitrate != null && e.bitrate! > 0) parts.add('${e.bitrate} kbps'); return parts.isEmpty - ? 'Calidad no informada' - : 'Calidad original: ${parts.join(' · ')}'; + ? AppLocalizations.of(context).qualityUnknown + : AppLocalizations.of(context).qualityOriginal(parts.join(' ? ')); } } @@ -358,6 +361,7 @@ class _GrabacionWidget extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final theme = Theme.of(context); final grabacion = estado.estadoGrabacion; final activa = grabacion.activa; @@ -381,7 +385,7 @@ class _GrabacionWidget extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - activa ? 'Grabando radio' : 'Grabación directa', + activa ? l10n.recordingActiveTitle : l10n.recordingDirectTitle, style: theme.textTheme.titleSmall?.copyWith( fontWeight: FontWeight.w700, ), @@ -389,7 +393,7 @@ class _GrabacionWidget extends StatelessWidget { Text( activa ? '${_formatearDuracion(grabacion.transcurrido)} · ${_formatearBytes(grabacion.bytes)}' - : 'Guarda el stream original, sin recomprimir.', + : l10n.recordingsOriginalStreamHint, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurface.withValues(alpha: 0.7), ), @@ -405,7 +409,7 @@ class _GrabacionWidget extends StatelessWidget { children: [ FilledButton.tonalIcon( icon: Icon(activa ? Icons.stop_rounded : Icons.mic_rounded), - label: Text(activa ? 'Parar' : 'Grabar'), + label: Text(activa ? l10n.stopAction : l10n.recordAction), onPressed: activa ? estado.detenerGrabacion @@ -413,13 +417,13 @@ class _GrabacionWidget extends StatelessWidget { ), if (!activa) IconButton.filledTonal( - tooltip: 'Abrir carpeta', + tooltip: l10n.recordingsOpenFolder, icon: const Icon(Icons.folder_open_rounded), onPressed: () => _abrirCarpetaGrabaciones(context), ), if (!activa && hayUltimaGrabacion) IconButton.filledTonal( - tooltip: 'Abrir última grabación', + tooltip: l10n.recordingsOpenLatest, icon: const Icon(Icons.audio_file_rounded), onPressed: () => _abrirUltimaGrabacion(context), ), @@ -436,7 +440,7 @@ class _GrabacionWidget extends StatelessWidget { if (!context.mounted) return; if (!abierto) { messenger.showSnackBar( - const SnackBar(content: Text('No se pudo abrir la última grabación')), + SnackBar(content: Text(AppLocalizations.of(context).recordingsOpenLatestError)), ); } } @@ -447,8 +451,8 @@ class _GrabacionWidget extends StatelessWidget { if (!context.mounted) return; if (!abierto) { messenger.showSnackBar( - const SnackBar( - content: Text('No se pudo abrir la carpeta de grabaciones'), + SnackBar( + content: Text(AppLocalizations.of(context).recordingsOpenFolderPlainError), ), ); } @@ -466,11 +470,11 @@ class _GrabacionWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Grabar radio', + AppLocalizations.of(ctx).recordRadioTitle, style: Theme.of(ctx).textTheme.titleLarge, ), const SizedBox(height: 8), - const Text('Elige cuánto tiempo querés grabar.'), + Text(AppLocalizations.of(ctx).recordRadioSubtitle), const SizedBox(height: 16), Wrap( spacing: 8, @@ -481,7 +485,7 @@ class _GrabacionWidget extends StatelessWidget { Icons.all_inclusive_rounded, size: 18, ), - label: const Text('Indefinida'), + label: Text(AppLocalizations.of(ctx).indefiniteOption), onPressed: () { estado.iniciarGrabacion(); Navigator.pop(ctx); @@ -497,7 +501,7 @@ class _GrabacionWidget extends StatelessWidget { ), ActionChip( avatar: const Icon(Icons.tune_rounded, size: 18), - label: const Text('Personalizada'), + label: Text(AppLocalizations.of(ctx).customOption), onPressed: () { Navigator.pop(ctx); _mostrarDuracionPersonalizada(context); @@ -521,7 +525,7 @@ class _GrabacionWidget extends StatelessWidget { context: context, builder: (ctx) => AlertDialog( - title: const Text('Duración de grabación'), + title: Text(AppLocalizations.of(ctx).recordDurationTitle), content: Form( key: formKey, child: Row( @@ -529,18 +533,18 @@ class _GrabacionWidget extends StatelessWidget { Expanded( child: TextFormField( controller: minutosCtrl, - decoration: const InputDecoration(labelText: 'Minutos'), + decoration: InputDecoration(labelText: AppLocalizations.of(ctx).minutesLabel), keyboardType: TextInputType.number, - validator: _validarNumero, + validator: (value) => _validarNumero(ctx, value), ), ), const SizedBox(width: 12), Expanded( child: TextFormField( controller: segundosCtrl, - decoration: const InputDecoration(labelText: 'Segundos'), + decoration: InputDecoration(labelText: AppLocalizations.of(ctx).secondsLabel), keyboardType: TextInputType.number, - validator: _validarNumero, + validator: (value) => _validarNumero(ctx, value), ), ), ], @@ -549,7 +553,7 @@ class _GrabacionWidget extends StatelessWidget { actions: [ TextButton( onPressed: () => Navigator.pop(ctx), - child: const Text('Cancelar'), + child: Text(AppLocalizations.of(ctx).cancelAction), ), FilledButton( onPressed: () { @@ -564,7 +568,7 @@ class _GrabacionWidget extends StatelessWidget { estado.iniciarGrabacion(duracion: duracion); Navigator.pop(ctx); }, - child: const Text('Grabar'), + child: Text(AppLocalizations.of(ctx).recordAction), ), ], ), @@ -574,10 +578,10 @@ class _GrabacionWidget extends StatelessWidget { segundosCtrl.dispose(); } - String? _validarNumero(String? value) { + String? _validarNumero(BuildContext context, String? value) { if (value == null || value.trim().isEmpty) return null; final n = int.tryParse(value.trim()); - if (n == null || n < 0) return 'Número inválido'; + if (n == null || n < 0) return AppLocalizations.of(context).invalidNumber; return null; } @@ -617,6 +621,7 @@ class _Controles extends StatelessWidget { @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); final theme = Theme.of(context); return StreamBuilder( @@ -638,7 +643,7 @@ class _Controles extends StatelessWidget { ), const SizedBox(height: 8), Text( - 'No se puede reproducir esta radio', + l10n.playerPlaybackErrorTitle, style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.error, ), @@ -647,7 +652,7 @@ class _Controles extends StatelessWidget { const SizedBox(height: 12), FilledButton.tonalIcon( icon: const Icon(Icons.refresh_rounded, size: 18), - label: const Text('Reintentar'), + label: Text(l10n.retryAction), onPressed: () => estado.reproducir(emisora), ), ], @@ -662,7 +667,7 @@ class _Controles extends StatelessWidget { children: [ Semantics( button: true, - label: 'Detener reproduccion', + label: l10n.stopPlaybackTooltip, child: IconButton( icon: const Icon(Icons.stop_rounded), iconSize: 34, @@ -671,7 +676,7 @@ class _Controles extends StatelessWidget { minHeight: 56, ), color: Colors.white.withValues(alpha: 0.78), - tooltip: 'Detener', + tooltip: l10n.stopAction, onPressed: cargando ? null : estado.detenerReproduccion, ), ), @@ -712,8 +717,8 @@ class _Controles extends StatelessWidget { button: true, label: reproduciendo - ? 'Pausar reproduccion' - : 'Iniciar reproduccion', + ? l10n.pausePlaybackTooltip + : l10n.startPlaybackTooltip, child: Center( child: cargando @@ -739,7 +744,7 @@ class _Controles extends StatelessWidget { ), const SizedBox(width: 16), Semantics( - label: reproduciendo ? 'En vivo' : 'No esta reproduciendo', + label: reproduciendo ? l10n.liveNow : l10n.notPlaying, child: Icon( Icons.fiber_manual_record_rounded, size: 32, @@ -768,7 +773,7 @@ class _TimerWidget extends StatelessWidget { if (!estado.timer.activo) { return TextButton.icon( icon: const Icon(Icons.bedtime_outlined, size: 18), - label: const Text('Timer de sueno'), + label: Text(AppLocalizations.of(context).sleepTimer), onPressed: () => _mostrarTimerDialog(context), ); } @@ -798,7 +803,7 @@ class _TimerWidget extends StatelessWidget { visualDensity: VisualDensity.compact, padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), ), - child: const Text('Cancelar'), + child: Text(AppLocalizations.of(ctx).cancelAction), ), ], ); @@ -818,7 +823,7 @@ class _TimerWidget extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Timer de sueno', + AppLocalizations.of(ctx).sleepTimer, style: Theme.of(ctx).textTheme.titleLarge, ), const SizedBox(height: 16),