# -*- coding: utf-8 -*-
"""Lightweight runtime language layer for Velantis Wind.

This is intentionally simple and plugin-local: it stores the selected language
in QSettings and translates the visible Qt UI strings at runtime. It avoids
requiring .ts/.qm compilation during development and keeps the three modules
using the same language selected in the hub.
"""
from __future__ import annotations

import re
from typing import Dict

from qgis.PyQt import QtCore, QtWidgets

_ORG = "VelantisWind"
_APP = "VelantisWindPlugin"
_KEY = "ui/language"
_PATCHED = False

LANG_ES = "es"
LANG_EN = "en"
LANG_FR = "fr"
LANG_DE = "de"

# The UI is authored in Spanish, so Spanish is the canonical *source* language
# that every other language is pivoted through.  Adding a new language means
# registering a Spanish->X map (see register_language) — nothing else changes.
_SOURCE_LANG = LANG_ES
SUPPORTED_LANGUAGES = (LANG_ES, LANG_EN, LANG_FR, LANG_DE)

_LANGUAGE_LABELS = {
    LANG_ES: "Español",
    LANG_EN: "English",
    LANG_FR: "Français",
    LANG_DE: "Deutsch",
}


def _settings() -> QtCore.QSettings:
    return QtCore.QSettings(_ORG, _APP)


def current_language() -> str:
    lang = str(_settings().value(_KEY, _SOURCE_LANG) or _SOURCE_LANG).lower().strip()
    # Accept locale-style values such as "fr_FR" or "en-US".
    short = lang.replace("-", "_").split("_", 1)[0]
    if lang in SUPPORTED_LANGUAGES:
        return lang
    if short in SUPPORTED_LANGUAGES:
        return short
    return _SOURCE_LANG


def set_language(lang: str) -> None:
    lang = str(lang or _SOURCE_LANG).lower().strip()
    short = lang.replace("-", "_").split("_", 1)[0]
    if lang not in SUPPORTED_LANGUAGES:
        lang = short if short in SUPPORTED_LANGUAGES else _SOURCE_LANG
    _settings().setValue(_KEY, lang)


def is_spanish() -> bool:
    return current_language() == LANG_ES


def is_source_language() -> bool:
    """True when the active language is the canonical source (Spanish)."""
    return current_language() == _SOURCE_LANG


# Exact/fragment translations.  The layer translates both directions because a
# widget can already be in English and the user can switch back to Spanish.
_TO_EN: Dict[str, str] = {
    # Hub / global
    "Velantis Wind · Hub principal": "Velantis Wind · Main hub",
    "Selecciona el módulo de trabajo. Los tres módulos están operativos: Energía (AEP y wakes), Ruido, y Sombras (shadow flicker).": "Select the work module. The three modules are available: Energy (AEP and wakes), Noise, and Shadow Flicker.",
    "Idioma": "Language",
    "Idioma del plugin:": "Plugin language:",
    "El idioma seleccionado se aplicará al hub, módulos, avisos y resúmenes generados.": "The selected language will be applied to the hub, modules, warnings and generated summaries.",
    "Energía\nAEP y wakes": "Energy\nAEP and wakes",
    "Ruido": "Noise",
    "Sombras y\nparpadeo": "Shadow\nflicker",
    "Inicio": "Home",
    "← Inicio": "← Home",
    "Pulsa el logo para volver al inicio desde los módulos preparados.": "Click the logo to return home from the prepared modules.",
    "Resumen del proyecto": "Project summary",
    "Proyecto:": "Project:",
    "CRS:": "CRS:",
    "Layout activo:": "Active layout:",
    "Recurso:": "Resource:",
    "TI WRG:": "WRG TI:",
    "Estado módulos:": "Module status:",
    "Energía: operativa · Ruido: operativo · Sombras: operativo": "Energy: ready · Noise: ready · Shadow flicker: ready",
    "Proyecto sin nombre": "Unnamed project",
    "CRS no disponible": "CRS unavailable",
    "Sin capas de coordenadas generadas todavía": "No coordinate layers generated yet",
    "Sin recurso seleccionado todavía": "No resource selected yet",
    "No seleccionado (fallback previsto a TI=10% en flujo WRG)": "Not selected (expected fallback to TI=10% in WRG flow)",
    "No se ha encontrado la factoría del módulo de energía.": "The energy module factory was not found.",
    "No se pudo abrir el módulo de energía.": "Could not open the energy module.",
    "Apoyar VelantisWind": "Support VelantisWind",
    "♡ Apoyar VelantisWind": "♡ Support VelantisWind",
    "Apoya el mantenimiento, la documentación y el desarrollo open source futuro.": "Support maintenance, documentation and future open-source development.",
    "Apoya el mantenimiento, la documentación, las pruebas y el desarrollo open source futuro.": "Support maintenance, documentation, testing and future open-source development.",

    # Energy module
    "Calcular AEP · Modelos, coordenadas y recurso eólico": "Calculate AEP · Models, coordinates and wind resource",
    "Energía · AEP del parque": "Energy · Wind farm AEP",
    "Cálculo de AEP con PyWake: define los modelos de aerogenerador, selecciona el recurso eólico (WAsP/Surfer grids o WRG), elige los modelos físicos (estela, turbulencia, bloqueo, rotor-average) y lanza el cálculo. Los resultados se vuelcan automáticamente sobre las capas de coordenadas del proyecto.": "AEP calculation with PyWake: define turbine models, select the wind resource (WAsP/Surfer grids or WRG), choose the physical models (wake, turbulence, blockage, rotor-average) and run the calculation. Results are automatically written to the project coordinate layers.",
    "Modelos de aerogenerador": "Wind turbine models",
    "Número de modelos:": "Number of models:",
    "Carpeta con grids WAsP": "Folder with WAsP grids",
    "Carpeta:": "Folder:",
    "Elegir carpeta…": "Choose folder…",
    "Unión Recurso": "Merge resource",
    "Modelo de PyWake": "PyWake model",
    "¿Qué modelo quieres utilizar?": "Which model do you want to use?",
    "Modelo de estela (PyWake)": "Wake deficit model (PyWake)",
    "¿Qué modelo de estela quieres usar?": "Which wake model do you want to use?",
    "Modelo de turbulencia añadida": "Added turbulence model",
    "Modelo de bloqueo": "Blockage model",
    "Modelo rotor-average": "Rotor-average model",
    "Recurso eólico (WAsP grids / WRG)": "Wind resource (WAsP grids / WRG)",
    "Archivo WRG/ZIP": "WRG/ZIP file",
    "Acciones": "Actions",
    "Generar capas de puntos": "Generate point layers",
    "Graficar estelas": "Plot wakes",
    "Calcular AEP": "Calculate AEP",
    "Mapa interactivo": "Interactive map",
    "Exportar layout editado…": "Export edited layout…",
    "Guardar escenario A": "Save scenario A",
    "Guardar escenario B": "Save scenario B",
    "Comparar escenarios": "Compare scenarios",
    "Comparar A/B": "Compare A/B",
    "Volver al hub": "Back to hub",
    "Cierra el módulo de energía y vuelve al hub principal de Velantis Wind sin borrar las capas ni los resultados cargados en el proyecto.": "Close the energy module and return to the Velantis Wind main hub without deleting project layers or loaded results.",
    "Modelo": "Model",
    "sin definir": "not defined",
    "Definir…": "Define…",
    "Ver curva": "View curve",
    "Coordenadas (CSV X,Y):": "Coordinates (CSV X,Y):",
    "Coords CSV…": "Coords CSV…",
    "Cargar capa": "Load layer",
    "Selecciona CSV con columnas X,Y para este modelo": "Select a CSV with X,Y columns for this model",
    "Faltan modelos": "Missing models",
    "Define todos los modelos antes de continuar.": "Define all models before continuing.",
    "Coordenadas": "Coordinates",
    "Recurso eólico": "Wind resource",
    "Selecciona una carpeta WAsP válida o un WRG/ZIP.": "Select a valid WAsP folder or a WRG/ZIP file.",
    "Comparador de escenarios": "Scenario comparison",
    "Último cálculo": "Last calculation",
    "No se pudo abrir la comparación:": "Could not open the comparison:",
    "No se pudo activar el modo interactivo:": "Could not activate interactive mode:",
    "No se pudo obtener el map canvas de QGIS.": "Could not access the QGIS map canvas.",
    "El archivo no existe:": "The file does not exist:",
    "Extensión no soportada (use .wrg o .zip):": "Unsupported extension (use .wrg or .zip):",
    "No se pudo abrir el diálogo de turbina:": "Could not open the turbine dialog:",
    "No se pudo importar 'graficar.py':": "Could not import 'graficar.py':",
    "Error al generar las gráficas:": "Error while generating plots:",
    "Turbulencia WRG": "WRG turbulence",
    "El/los raster(s) TI no existen:": "The TI raster(s) do not exist:",

    # AEP result dialog
    "Resultados AEP": "AEP results",
    "Resultados AEP – Cálculo AEP (PyWake)": "AEP Results – AEP calculation (PyWake)",
    "Abrir el hub principal de Velantis Wind": "Open the main Velantis Wind hub",
    "Resumen global": "Global summary",
    "Por modelo": "By model",
    "Por turbina": "By turbine",
    "Por sector": "By sector",
    "Informe": "Report",
    "Configuración": "Configuration",
    "AEP libre": "Free-flow AEP",
    "AEP neta": "Net AEP",
    "Pérdidas wake": "Wake losses",
    "Pérdidas turbulencia": "Turbulence losses",
    "Pérdidas bloqueo": "Blockage losses",
    "Otras pérdidas": "Other losses",
    "Pérdidas totales": "Total losses",
    "Nº turbinas": "No. turbines",
    "Potencia instalada": "Installed power",
    "Factor de capacidad neto": "Net capacity factor",
    "Producción específica": "Specific yield",
    "año": "year",
    "Modelo WT": "WT model",
    "Turbinas": "Turbines",
    "Energía": "Energy",
    "Pérdida": "Loss",
    "Sector": "Sector",
    "Frecuencia": "Frequency",
    "Velocidad": "Speed",
    "Dirección": "Direction",
    "Informe técnico AEP": "AEP technical report",

    # Noise module / results
    "Noise": "Noise",
    "Noise · Calculation summary": "Noise · Calculation summary",
    "Noise · Technical summary": "Noise · Technical summary",
    "Módulo de ruido: motor de screening eólico con receptores, grupos fuente, propagación ISO-aligned por bandas, mapas raster y salidas GIS.": "Noise module: wind screening engine with receivers, source groups, ISO-aligned octave-band propagation, raster maps and GIS outputs.",
    "Contexto del proyecto": "Project context",
    "Entradas del cálculo": "Calculation inputs",
    "Sources / acoustic layout:": "Sources / acoustic layout:",
    "Marcar todas": "Select all",
    "Desmarcar todas": "Clear all",
    "Capa de receptores:": "Receiver layer:",
    "Usar varias capas de receptores por categoría": "Use several receiver layers by category",
    "Criterio de límite:": "Limit criterion:",
    "Manual / global": "Manual / global",
    "Diurno": "Daytime",
    "Nocturno": "Night-time",
    "Añadir capa receptores": "Add receiver layer",
    "Quitar capa seleccionada": "Remove selected layer",
    "Usar layout activo del proyecto": "Use active project layout",
    "Generar mapa GIS de ruido (malla de cálculo)": "Generate GIS noise map (calculation grid)",
    "Altura de receptor [m]:": "Receiver height [m]:",
    "Resumen": "Summary",
    "Modelos": "Models",
    "Top receptores": "Top receivers",
    "Capas creadas": "Created layers",
    "Exportar resumen…": "Export summary…",
    "Exportar informe…": "Export report…",
    "Guarda el resumen técnico en HTML o TXT.": "Save the technical summary as HTML or TXT.",
    "Guarda una tabla limpia con una fila por receptor y las columnas necesarias para consultoría.": "Save a clean table with one row per receiver and the columns needed for consultancy.",
    "Guarda solo los receptores que superan su límite acústico.": "Save only the receivers exceeding their acoustic limit.",
    "Guarda resumen, modelos, receptores y excedencias en un único libro Excel.": "Save summary, models, receivers and exceedances in a single Excel workbook.",
    "Exportar receptores CSV…": "Export receivers CSV…",
    "Exportar Top receivers CSV…": "Export Top receivers CSV…",
    "Exportar grupos fuente CSV…": "Export source groups CSV…",
    "Exportar excedencias CSV…": "Export exceedances CSV…",
    "Exportar paquete XLSX…": "Export XLSX package…",
    "Cerrar": "Close",
    "No hay capa de receptores para exportar.": "There is no receiver layer to export.",
    "No hay capa de fuentes para exportar.": "There is no source layer to export.",
    "No hay receptores que superen el límite en este cálculo.": "No receivers exceed the limit in this calculation.",
    "No se pudo exportar el resumen:": "Could not export the summary:",
    "No se pudo exportar el CSV:": "Could not export the CSV:",
    "No se pudo exportar el XLSX:": "Could not export the XLSX:",
    "openpyxl no está disponible en este entorno de QGIS. Usa las exportaciones CSV o instala openpyxl.": "openpyxl is not available in this QGIS environment. Use CSV exports or install openpyxl.",
    "nivel total dB(A)": "total level dB(A)",
    "límite dB(A)": "limit dB(A)",
    "margen límite dB": "limit margin dB",
    "estado": "status",
    "supera límite": "exceeds limit",
    "dist. turbina cercana (m)": "nearest turbine dist. (m)",
    "modelo dominante": "dominant model",
    "grupo fuente dom.": "dominant source group",
    "parque dom.": "dominant wind farm",
    "capa fuente dom.": "dominant source layer",
    "pérdida Adiv dB": "Adiv loss dB",
    "pérdida Aatm dB": "Aatm loss dB",
    "pérdida Agr/Aground dB": "Agr/Aground loss dB",
    "pérdida Abar dB": "Abar loss dB",
    "banda dom. Hz": "dominant band Hz",

    # Shadow module
    "Shadow Flicker": "Shadow Flicker",
    "Shadow flicker calculation module for wind turbines. Connected to the project layout, with point-receiver calculation and QGIS output layers with detailed results by receiver.": "Shadow flicker calculation module for wind turbines. Connected to the project layout, with point-receiver calculation and QGIS output layers with detailed results by receiver.",
    "Project context": "Project context",
    "Site configuration": "Site configuration",
    "Project:": "Project:",
    "Active layout:": "Active layout:",
    "Detected WT models:": "Detected WT models:",
    "Receiver layer:": "Receiver layer:",
    "Latitude [°]:": "Latitude [°]:",
    "Longitude [°]:": "Longitude [°]:",
    "Analysis year:": "Analysis year:",
    "Time basis:": "Time basis:",
    "Local civil time (IANA/DST)": "Local civil time (IANA/DST)",
    "Fixed UTC offset": "Fixed UTC offset",
    "IANA time zone:": "IANA time zone:",
    "Fixed UTC offset:": "Fixed UTC offset:",
    "Auto-detect coordinates and time zone": "Auto-detect coordinates and time zone",
    "No layout detected": "No layout detected",
    "No turbines": "No turbines",
    "No receivers": "No receivers",
    "Invalid CRS": "Invalid CRS",
    "No coordinates": "No coordinates",
    "Error": "Error",
    "Export Failed": "Export failed",
    "Could not load the filtered TIF": "Could not load the filtered TIF",
}

# Spanish target includes the reverse plus natural Spanish equivalents for strings
# that are originally English in the current code base.
_TO_ES: Dict[str, str] = {v: k for k, v in _TO_EN.items() if v != k}
_TO_ES.update({
    "Velantis Wind · Main hub": "Velantis Wind · Hub principal",
    "Language": "Idioma",
    "Plugin language:": "Idioma del plugin:",
    "Energy\nAEP and wakes": "Energía\nAEP y wakes",
    "Noise": "Ruido",
    "Shadow\nflicker": "Sombras y\nparpadeo",
    "Home": "Inicio",
    "← Home": "← Inicio",
    "Project summary": "Resumen del proyecto",
    "Project context": "Contexto del proyecto",
    "Project:": "Proyecto:",
    "CRS unavailable": "CRS no disponible",
    "Active layout:": "Layout activo:",
    "Resource:": "Recurso:",
    "WRG TI:": "TI WRG:",
    "Module status:": "Estado módulos:",
    "Energy": "Energía",
    "Energy · Wind farm AEP": "Energía · AEP del parque",
    "Wind turbine models": "Modelos de aerogenerador",
    "Number of models:": "Número de modelos:",
    "Folder with WAsP grids": "Carpeta con grids WAsP",
    "Calculate AEP · Models, coordinates and wind resource": "Calcular AEP · Modelos, coordenadas y recurso eólico",
    "Folder:": "Carpeta:",
    "Choose folder…": "Elegir carpeta…",
    "Merge resource": "Unión Recurso",
    "PyWake model": "Modelo de PyWake",
    "Which model do you want to use?": "¿Qué modelo quieres utilizar?",
    "Wake deficit model (PyWake)": "Modelo de estela (PyWake)",
    "Which wake model do you want to use?": "¿Qué modelo de estela quieres usar?",
    "Added turbulence model": "Modelo de turbulencia añadida",
    "Blockage model": "Modelo de bloqueo",
    "Rotor-average model": "Modelo rotor-average",
    "Wind resource (WAsP grids / WRG)": "Recurso eólico (WAsP grids / WRG)",
    "WRG/ZIP file": "Archivo WRG/ZIP",
    "Actions": "Acciones",
    "Generate point layers": "Generar capas de puntos",
    "Plot wakes": "Graficar estelas",
    "Calculate AEP": "Calcular AEP",
    "Interactive map": "Mapa interactivo",
    "Export edited layout…": "Exportar layout editado…",
    "Save scenario A": "Guardar escenario A",
    "Save scenario B": "Guardar escenario B",
    "Compare scenarios": "Comparar escenarios",
    "Compare A/B": "Comparar A/B",
    "Back to hub": "Volver al hub",
    "Close the energy module and return to the Velantis Wind main hub without deleting project layers or loaded results.": "Cierra el módulo de energía y vuelve al hub principal de Velantis Wind sin borrar las capas ni los resultados cargados en el proyecto.",
    "Define…": "Definir…",
    "View curve": "Ver curva",
    "Coordinates (CSV X,Y):": "Coordenadas (CSV X,Y):",
    "Load layer": "Cargar capa",
    "Missing models": "Faltan modelos",
    "Wind resource": "Recurso eólico",
    "Wind resource (WAsP grids / WRG)": "Recurso eólico (WAsP grids / WRG)",
    "Scenario comparison": "Comparador de escenarios",
    "Last calculation": "Último cálculo",
    "AEP results": "Resultados AEP",
    "AEP Results – AEP calculation (PyWake)": "Resultados AEP – Cálculo AEP (PyWake)",
    "Open the main Velantis Wind hub": "Abrir el hub principal de Velantis Wind",
    "Global summary": "Resumen global",
    "By model": "Por modelo",
    "By turbine": "Por turbina",
    "By sector": "Por sector",
    "Report": "Informe",
    "Configuration": "Configuración",
    "Free-flow AEP": "AEP libre",
    "Net AEP": "AEP neta",
    "Wake losses": "Pérdidas wake",
    "Turbulence losses": "Pérdidas turbulencia",
    "Blockage losses": "Pérdidas bloqueo",
    "Other losses": "Otras pérdidas",
    "Total losses": "Pérdidas totales",
    "No. turbines": "Nº turbinas",
    "Installed power": "Potencia instalada",
    "Net capacity factor": "Factor de capacidad neto",
    "Specific yield": "Producción específica",
    "year": "año",
    "WT model": "Modelo WT",
    "Turbines": "Turbinas",
    "Loss": "Pérdida",
    "Sector": "Sector",
    "Frequency": "Frecuencia",
    "Speed": "Velocidad",
    "Direction": "Dirección",
    "AEP technical report": "Informe técnico AEP",
    "Calculation inputs": "Entradas del cálculo",
    "Select all": "Marcar todas",
    "Clear all": "Desmarcar todas",
    "Use several receiver layers by category": "Usar varias capas de receptores por categoría",
    "Limit criterion:": "Criterio de límite:",
    "Daytime": "Diurno",
    "Night-time": "Nocturno",
    "Add receiver layer": "Añadir capa receptores",
    "Remove selected layer": "Quitar capa seleccionada",
    "Use active project layout": "Usar layout activo del proyecto",
    "Generate GIS noise map (calculation grid)": "Generar mapa GIS de ruido (malla de cálculo)",
    "Receiver height [m]:": "Altura de receptor [m]:",
    "Summary": "Resumen",
    "Models": "Modelos",
    "Top receivers": "Top receptores",
    "Created layers": "Capas creadas",
    "Export summary…": "Exportar resumen…",
    "Export report…": "Exportar informe…",
    "Save the technical summary as HTML or TXT.": "Guarda el resumen técnico en HTML o TXT.",
    "Save a clean table with one row per receiver and the columns needed for consultancy.": "Guarda una tabla limpia con una fila por receptor y las columnas necesarias para consultoría.",
    "Save only the receivers exceeding their acoustic limit.": "Guarda solo los receptores que superan su límite acústico.",
    "Save summary, models, receivers and exceedances in a single Excel workbook.": "Guarda resumen, modelos, receptores y excedencias en un único libro Excel.",
    "Export receivers CSV…": "Exportar receptores CSV…",
    "Export Top receivers CSV…": "Exportar Top receivers CSV…",
    "Export source groups CSV…": "Exportar grupos fuente CSV…",
    "Export exceedances CSV…": "Exportar excedencias CSV…",
    "Export XLSX package…": "Exportar paquete XLSX…",
    "Close": "Cerrar",
    "Site configuration": "Configuración del emplazamiento",
    "Detected WT models:": "Modelos WT detectados:",
    "Latitude [°]:": "Latitud [°]:",
    "Longitude [°]:": "Longitud [°]:",
    "Analysis year:": "Año de análisis:",
    "Time basis:": "Base temporal:",
    "Local civil time (IANA/DST)": "Hora civil local (IANA/DST)",
    "Fixed UTC offset": "Offset UTC fijo",
    "IANA time zone:": "Zona horaria IANA:",
    "Fixed UTC offset:": "Offset UTC fijo:",
    "Auto-detect coordinates and time zone": "Detectar coordenadas y zona horaria automáticamente",
    "No layout detected": "No se ha detectado layout",
    "No turbines": "Sin turbinas",
    "No receivers": "Sin receptores",
    "Invalid CRS": "CRS no válido",
    "No coordinates": "Sin coordenadas",
    "Export failed": "Exportación fallida",
    "Export Failed": "Exportación fallida",
})

# Phrase replacements for generated HTML / alerts / dynamic text.  Keep this
# conservative so file paths, layer names and numeric outputs are not touched.
_FRAGMENT_TO_EN: Dict[str, str] = {
    **_TO_EN,
    "capa(s) de modelo": "model layer(s)",
    "turbina(s)": "turbine(s)",
    "receptor(es)": "receiver(s)",
    "No se puede lanzar el cálculo": "Cannot start the calculation",
    "Could not calculate noise": "Could not calculate noise",
    "Could not display the visual noise summary": "Could not display the visual noise summary",
    "Could not read the acoustic curve": "Could not read the acoustic curve",
    "Configure at least one valid layer in the receiver-category table.": "Configure at least one valid layer in the receiver-category table.",
    "Select a valid receiver layer.": "Select a valid receiver layer.",
    "Error detecting coordinates": "Error detecting coordinates",
    "Error during calculation": "Error during calculation",
    "Error regenerating raster": "Error regenerating raster",
    "The project does not have a valid CRS.": "The project does not have a valid CRS.",
    "No turbines were found in the layout.": "No turbines were found in the layout.",
    "Select a turbine layer.": "Select a turbine layer.",
    "Turbine layer not found.": "Turbine layer not found.",
    "Select a receiver layer.": "Select a receiver layer.",
    "Receiver layer not found.": "Receiver layer not found.",
    "The receiver layer is empty.": "The receiver layer is empty.",
    "No se pudo exportar": "Could not export",
    "Exportar": "Export",
    "Configuración": "Configuration",
    "Resultados": "Results",
    "Resumen": "Summary",
    "Informe": "Report",
    "Parámetro": "Parameter",
    "Valor": "Value",
    "Estado": "Status",
    "Capas": "Layers",
    "Modelo": "Model",
    "Fuente": "Source",
    "Receptor": "Receiver",
    "Receptores": "Receivers",
    "Límite": "Limit",
    "Cumple": "Pass",
    "Excede": "Exceeds",
    "Sí": "Yes",
    "No": "No",
    "día": "day",
    "noche": "night",
    "horas/año": "hours/year",
    "h/año": "h/year",
    "m/s": "m/s",
    "año": "year",
}
_FRAGMENT_TO_ES: Dict[str, str] = {v: k for k, v in _FRAGMENT_TO_EN.items() if v != k}
_FRAGMENT_TO_ES.update(_TO_ES)
_FRAGMENT_TO_ES.update({
    "Cannot start the calculation": "No se puede lanzar el cálculo",
    "Could not calculate noise": "No se pudo calcular el ruido",
    "Could not display the visual noise summary": "No se pudo mostrar el resumen visual de ruido",
    "Could not read the acoustic curve": "No se pudo leer la curva acústica",
    "Configure at least one valid layer in the receiver-category table.": "Configura al menos una capa válida en la tabla de categorías de receptores.",
    "Select a valid receiver layer.": "Selecciona una capa de receptores válida.",
    "Error detecting coordinates": "Error al detectar coordenadas",
    "Error during calculation": "Error durante el cálculo",
    "Error regenerating raster": "Error al regenerar el raster",
    "The project does not have a valid CRS.": "El proyecto no tiene un CRS válido.",
    "No turbines were found in the layout.": "No se encontraron turbinas en el layout.",
    "Select a turbine layer.": "Selecciona una capa de turbinas.",
    "Turbine layer not found.": "No se encontró la capa de turbinas.",
    "Select a receiver layer.": "Selecciona una capa de receptores.",
    "Receiver layer not found.": "No se encontró la capa de receptores.",
    "The receiver layer is empty.": "La capa de receptores está vacía.",
    "Export": "Exportar",
    "Results": "Resultados",
    "Parameter": "Parámetro",
    "Value": "Valor",
    "Status": "Estado",
    "Layers": "Capas",
    "Source": "Fuente",
    "Receiver": "Receptor",
    "Receivers": "Receptores",
    "Limit": "Límite",
    "Pass": "Cumple",
    "Exceeds": "Excede",
    "Yes": "Sí",
    "hours/year": "horas/año",
    "h/year": "h/año",
})


# ---------------------------------------------------------------------------
# Iteration 2: broader runtime vocabulary for the remaining UI, reports,
# message-bar warnings, tooltips and generated summaries.
# ---------------------------------------------------------------------------
_I18N_EXTRA_TO_EN = {
    # Hub / project status
    "capa(s) de modelo ·": "model layer(s) ·",
    "turbina(s)": "turbine(s)",
    "receptor(es)": "receiver(s)",
    "WAsP grids:": "WAsP grids:",
    "WRG:": "WRG:",

    # Energy module labels / tooltips / dynamic messages
    "Modelo rotor-average (PyWake)": "Rotor-average model (PyWake)",
    "¿Qué modelo de rotor-average quieres usar?": "Which rotor-average model do you want to use?",
    "Modelo de bloqueo (PyWake)": "Blockage model (PyWake)",
    "¿Qué modelo de bloqueo quieres usar?": "Which blockage model do you want to use?",
    "Modelo de turbulencia (PyWake)": "Turbulence model (PyWake)",
    "¿Qué modelo de turbulencia quieres usar?": "Which turbulence model do you want to use?",
    "Recurso eólico (WAsP grids / WRG)": "Wind resource (WAsP grids / WRG)",
    "WAsP/Surfer grids:": "WAsP/Surfer grids:",
    "Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake.": "Select a folder with WAsP/Surfer grids compatible with PyWake.",
    "Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.": "If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.",
    "Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos.": "Select one or more .wrg files or .zip files containing .wrg files.",
    "Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.": "If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.",
    "Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif).": "Optionally provide one or more ambient TI raster(s), for example .asc/.tif.",
    "Los raster(s) se reproyectarán al CRS del proyecto actual": "The raster(s) will be reprojected to the current project CRS",
    "si hace falta.": "if needed.",
    "Ninguno": "None",
    "Limpiar": "Clear",
    "Elegir WRG/ZIP…": "Choose WRG/ZIP…",
    "Elegir raster(s) TI…": "Choose TI raster(s)…",
    "WRG/ZIP:": "WRG/ZIP:",
    "TI raster(s) (opcional):": "TI raster(s) (optional):",
    "Alturas TI override [m]:": "TI height override [m]:",
    "Opcional: alturas TI por archivo, ej. 90;120": "Optional: TI heights by file, e.g. 90;120",
    "Ayuda sobre cuándo conviene cada modelo de estela según el tipo de parque y el objetivo del estudio": "Help on when each wake model is appropriate depending on the wind farm type and study goal",
    "Wake deficit principal. No todos los modelos usan la turbulencia del mismo modo.": "Main wake deficit model. Not all models use turbulence in the same way.",
    "Ayuda sobre cuándo conviene cada modelo de bloqueo y por qué a veces se desactiva automáticamente": "Help on when each blockage model is appropriate and why it is sometimes disabled automatically",
    "El bloqueo es la reducción de velocidad upstream del rotor. Solo se aplica con motores A2A o PUD.": "Blockage is the upstream velocity reduction at the rotor. It is only applied with A2A or PUD engines.",
    "Ayuda sobre TI ambiente, turbulencia añadida y cuándo influye realmente en el AEP": "Help on ambient TI, added turbulence and when it actually affects AEP",
    "Este selector calcula turbulencia añadida por estela. No sustituye a la TI ambiente del site/WRG.": "This selector calculates wake-added turbulence. It does not replace the site/WRG ambient TI.",
    "Ayuda sobre turbulencia WRG y modelos de estela": "Help on WRG turbulence and wake models",
    "Override manual de altura para cada raster TI, en el mismo orden que los archivos. Útil cuando el nombre del archivo no contiene 90m, 120m, etc.": "Manual height override for each TI raster, in the same order as the files. Useful when the file name does not contain 90 m, 120 m, etc.",
    "Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos. Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.": "Select one or more .wrg files or .zip files containing .wrg files. If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.",
    "Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif). Se usan solo en el flujo WRG. Si no se selecciona ninguno, el plugin avisará y usará TI=10% como fallback. Consulta el botón de información para ver cuándo conviene una sola capa, cuándo convienen varias alturas y en qué modelos de estela influye más.": "Optionally provide one or more ambient TI raster(s), for example .asc/.tif. They are used only in the WRG flow. If none are selected, the plugin will warn and use TI=10% as fallback. Use the information button to see when a single layer is enough, when several heights are useful, and which wake models are most affected.",
    "Dibuja 4 mapas de estelas (N/E/S/O) usando los modelos y el recurso seleccionado (carpeta WAsP o WRG/ZIP).": "Draws 4 wake maps (N/E/S/W) using the selected models and resource (WAsP folder or WRG/ZIP).",
    "Activa edición en el canvas: click izq añade turbina, click der borra. Las coordenadas editadas se usan directamente en el cálculo de AEP (la capa en memoria es la fuente de verdad). El CSV original NO se modifica.": "Enables canvas editing: left click adds a turbine, right click deletes it. Edited coordinates are used directly in the AEP calculation (the memory layer is the source of truth). The original CSV is NOT modified.",
    "Guardar a CSV los puntos editados en el mapa interactivo.\nDefault: <original>_edited.csv (no sobrescribe el CSV original).": "Save the points edited in the interactive map to CSV.\nDefault: <original>_edited.csv (does not overwrite the original CSV).",
    "Guarda el último cálculo AEP como escenario A para compararlo después.": "Save the latest AEP calculation as scenario A for later comparison.",
    "Guarda el último cálculo AEP como escenario B para compararlo después.": "Save the latest AEP calculation as scenario B for later comparison.",
    "Compara los dos escenarios guardados y muestra delta global y por modelo.": "Compare the two saved scenarios and show global and per-model deltas.",
    "Qué significa cada parámetro físico del modelo seleccionado": "What each physical parameter of the selected model means",
    "Abrir diálogo para cargar curva de potencia": "Open dialog to load the power curve",
    "Crear/actualizar la capa de puntos para este modelo": "Create/update the point layer for this model",
    "Raster(s) TI no seleccionado(s) → fallback automático a 10% si usas WRG.": "No TI raster(s) selected → automatic fallback to 10% if you use WRG.",
    "Raster(s) TI no encontrado(s):": "TI raster(s) not found:",
    "Lector WRG no disponible en esta instalación": "WRG reader not available in this installation",
    "WRG inválido o no legible:": "Invalid or unreadable WRG:",
    "Calculando AEP": "Calculating AEP",
    "Calculando shadow flicker...": "Calculating shadow flicker...",
    "Cancelar": "Cancel",
    "Comparar A/B": "Compare A/B",
    "Comparador de escenarios AEP": "AEP scenario comparison",
    "Resumen delta": "Delta summary",
    "Métrica": "Metric",
    "AEP neto": "Net AEP",
    "AEP bruto": "Gross AEP",
    "Pérdidas por estela": "Wake losses",
    "Impacto TI/turbulencia": "TI/turbulence impact",
    "Pérdidas por bloqueo": "Blockage losses",
    "Nº aerogeneradores": "No. turbines",
    "Comparación global": "Global comparison",
    "Pérd. total": "Total loss",
    "Wake": "Wake",
    "MWh/año": "MWh/year",
    "Notas:": "Notes:",
    "Sin configuración registrada": "No configuration recorded",
    "frente a": "versus",
    "El delta se calcula como B − A.": "The delta is calculated as B − A.",

    # AEP result dialog
    "AEP, pérdidas y capacidad global": "AEP, losses and overall capacity",
    "AEP bruto (free-stream):": "Gross AEP (free-stream):",
    "AEP neto (operativo):": "Net AEP (operational):",
    "Pérdidas por estelas:": "Wake losses:",
    "Impacto TI/turbulencia:": "TI/turbulence impact:",
    "Pérdidas por bloqueo:": "Blockage losses:",
    "Otras pérdidas:": "Other losses:",
    "Pérdidas totales:": "Total losses:",
    "Nº de aerogeneradores:": "No. of turbines:",
    "Potencia instalada:": "Installed power:",
    "Factor de capacidad (neto):": "Capacity factor (net):",
    "Energía específica neta:": "Net specific yield:",
    "Balance total AEP": "Total AEP balance",
    "Por aerogenerador": "By turbine",
    "AEP bruto [MWh]": "Gross AEP [MWh]",
    "AEP neto [MWh]": "Net AEP [MWh]",
    "Pérd. estelas [MWh]": "Wake loss [MWh]",
    "Pérd. estela [MWh]": "Wake loss [MWh]",
    "Pérd. bloqueo [MWh]": "Blockage loss [MWh]",
    "Impacto TI [MWh]": "TI impact [MWh]",
    "FC neto [%]": "Net CF [%]",
    "Potencia inst. [MW]": "Installed power [MW]",
    "AEP/MW [MWh/MW·año]": "AEP/MW [MWh/MW·year]",
    "Pérd. estelas [%]": "Wake loss [%]",
    "Rosa de pérdidas por sector.": "Sector loss rose.",
    "Distribución del AEP libre y neto por dirección del viento.": "Distribution of free-flow and net AEP by wind direction.",
    "dirección del viento": "wind direction",
    "pérdidas por estela": "wake losses",
    "rendimiento del parque": "wind farm performance",
    "Total sectores:": "Sector totals:",
    "AEP libre": "Free-flow AEP",
    "pérdidas": "losses",
    "Comparación con resumen:": "Comparison with summary:",
    "revisar": "review",

    # Noise report/results additional text
    "INFORME TÉCNICO DE IMPACTO ACÚSTICO": "TECHNICAL NOISE IMPACT REPORT",
    "Evaluación de ruido generado por aerogeneradores": "Assessment of wind turbine noise",
    "RESUMEN EJECUTIVO": "EXECUTIVE SUMMARY",
    "Aerogeneradores": "Wind turbines",
    "Receptores Evaluados": "Evaluated receptors",
    "Nivel Máximo (dB(A))": "Maximum level (dB(A))",
    "Cobertura de Análisis": "Analysis coverage",
    "receptores dentro del radio": "receivers within the radius",
    "de cobertura": "coverage",
    "receptores fuera de radio": "receivers outside the radius",
    "Cumplimiento Normativo": "Regulatory compliance",
    "receptores superan límites": "receivers exceed limits",
    "de cumplimiento": "compliance",
    "Límite:": "Limit:",
    "Metodología de Cálculo": "Calculation methodology",
    "Motor utilizado:": "Engine used:",
    "Grupos fuente acústicos:": "Acoustic source groups:",
    "modelo(s) de aerogenerador": "wind turbine model(s)",
    "Método:": "Method:",
    "Mapa raster:": "Raster map:",
    "Propagación por bandas de octava según metodología ISO-aligned": "Octave-band propagation using an ISO-aligned methodology",
    "Cálculo acústico simplificado para screening": "Simplified acoustic calculation for screening",
    "Receptores Cubiertos": "Covered receivers",
    "Se muestran magnitudes brutas de atenuación": "Raw attenuation magnitudes are shown",
    "CONFIGURACIÓN Y PARÁMETROS": "CONFIGURATION AND PARAMETERS",
    "Ecuación Utilizada": "Equation used",
    "Parámetros del Cálculo": "Calculation parameters",
    "Términos Activos": "Active terms",
    "Atenuaciones": "Attenuations",
    "Receptor crítico": "Critical receiver",
    "Receptor crítico": "Critical receiver",
    "Media [dB]": "Mean [dB]",
    "Máximo [dB]": "Maximum [dB]",
    "Término": "Term",
    "divergencia geométrica": "geometrical divergence",
    "absorción atmosférica": "atmospheric absorption",
    "efecto suelo": "ground effect",
    "apantallamiento": "screening",
    "Trayectos con G distinto del global:": "Paths with G different from the global value:",
    "Curvas acústicas LwA(ws) en peor caso": "Worst-case LwA(ws) acoustic curves",
    "Curvas acústicas LwA(ws) a": "LwA(ws) acoustic curves at",
    "Curvas acústicas LwA(ws)": "LwA(ws) acoustic curves",
    "LwA fijo por grupo fuente acústico": "Fixed LwA by acoustic source group",
    "Grupo": "Group",
    "sin valor": "no value",
    "parque": "wind farm",
    "espectro": "spectrum",
    "sin nombre": "unnamed",
    "no activo": "inactive",
    "Hoja": "Sheet",
    "Excedencias": "Exceedances",
    "Grupos_fuente": "Source_groups",
    "no creada": "not created",
    "superan el límite": "exceed the limit",
    "cubiertos": "covered",
    "sí": "yes",

    # Shadow module UI/status/results
    "Refresh": "Actualizar",
    "Site latitude (e.g. 42.465 for Logroño)": "Latitud del emplazamiento (ej. 42.465 para Logroño)",
    "Site longitude (e.g. -2.445 for Logroño)": "Longitud del emplazamiento (ej. -2.445 para Logroño)",
    "IANA/DST: the table uses the real local civil time of the site.\nFixed UTC offset: uses the same UTC offset for the full year.": "IANA/DST: la tabla usa la hora civil local real del emplazamiento.\nOffset UTC fijo: usa el mismo offset UTC durante todo el año.",
    "IANA time zone, for example Europe/Madrid, America/Santiago or Asia/Tokyo.\nThe plugin includes a broad IANA catalogue and local TZif database to apply DST without timezonefinder.": "Zona horaria IANA, por ejemplo Europe/Madrid, America/Santiago o Asia/Tokyo.\nEl plugin incluye un catálogo IANA amplio y base TZif local para aplicar DST sin timezonefinder.",
    "Fixed offset for fixed-offset mode. Daylight saving time is not applied.": "Offset fijo para modo con offset fijo. No se aplica horario de verano.",
    "Computes latitude/longitude from the layout centroid and tries to detect the IANA time zone": "Calcula latitud/longitud desde el centroide del layout e intenta detectar la zona horaria IANA",
    "Turbine layer:": "Capa de turbinas:",
    "Select the turbine-coordinate layer from the Energy module": "Selecciona la capa de coordenadas de turbinas del módulo de energía",
    "Select the point layer with receiver locations": "Selecciona la capa de puntos con ubicaciones receptoras",
    "DEM raster (optional):": "Raster DEM/MDT (opcional):",
    "Optional digital elevation model (MDT) raster.": "Raster de modelo digital de elevaciones (MDT) opcional.",
    "Observer height [m]:": "Altura del observador [m]:",
    "Observation point height (typical window: 2 m)": "Altura del punto de observación (ventana típica: 2 m)",
    "Time step [min]:": "Paso temporal [min]:",
    "Calculation time resolution": "Resolución temporal del cálculo",
    "Min solar elevation [°]:": "Elevación solar mínima [°]:",
    "Max solar elevation [°]:": "Elevación solar máxima [°]:",
    "Turbine availability:": "Disponibilidad de turbina:",
    "Fraction of time the turbine is operational (0.97 = 97%)": "Fracción de tiempo en que la turbina está operativa (0,97 = 97%)",
    "Parallelization:": "Paralelización:",
    "Enable parallel calculation": "Activar cálculo paralelo",
    "Workers (cores):": "Workers (núcleos):",
    "Number of cores to use (your CPU has": "Número de núcleos a usar (tu CPU tiene",
    "Turbine model configuration": "Configuración de modelos de turbina",
    "Configure the geometric parameters for each turbine model.": "Configura los parámetros geométricos de cada modelo de turbina.",
    "Hub Height: hub height above ground level [m]": "Hub Height: altura de buje sobre el terreno [m]",
    "Rotor Diameter: rotor diameter [m]": "Rotor Diameter: diámetro de rotor [m]",
    "Calculation preparation": "Preparación del cálculo",
    "Create shadow flicker raster map": "Crear mapa raster de shadow flicker",
    "Generates a continuous raster map of shadow flicker hours": "Genera un mapa raster continuo de horas de shadow flicker",
    "Resolution [m]:": "Resolución [m]:",
    "Month:": "Mes:",
    "Hour:": "Hora:",
    "All": "Todos",
    "January": "Enero",
    "February": "Febrero",
    "March": "Marzo",
    "April": "Abril",
    "May": "Mayo",
    "June": "Junio",
    "July": "Julio",
    "August": "Agosto",
    "September": "Septiembre",
    "October": "Octubre",
    "November": "Noviembre",
    "December": "Diciembre",
    "Regenerate filtered TIF": "Regenerar TIF filtrado",
    "Check configuration": "Comprobar configuración",
    "Calculate Shadow Flicker": "Calcular shadow flicker",
    "Unnamed project": "Proyecto sin nombre",
    "— Select turbine layer —": "— Selecciona capa de turbinas —",
    "— Select receiver layer —": "— Selecciona capa de receptores —",
    "— No DEM (flat terrain) —": "— Sin DEM/MDT (terreno plano) —",
    "Coordinates detected": "Coordenadas detectadas",
    "No turbine layer selected.": "No hay capa de turbinas seleccionada.",
    "No receiver layer selected.": "No hay capa de receptores seleccionada.",
    "Coordinates out of range.": "Coordenadas fuera de rango.",
    "Analysis year:": "Año de análisis:",
    "Solar elevation:": "Elevación solar:",
    "No models detected. Check that turbine layers exist.": "No se detectaron modelos. Comprueba que existen capas de turbinas.",
    "Valid configuration. You can proceed with the calculation.": "Configuración válida. Puedes continuar con el cálculo.",
    "Incomplete configuration. Check the flagged items.": "Configuración incompleta. Revisa los elementos marcados.",
    "Missing configuration": "Configuración incompleta",
    "No model configuration is available.\n\nPlease fill Hub Height and Rotor Diameter in the table.": "No hay configuración de modelos disponible.\n\nRellena Hub Height y Rotor Diameter en la tabla.",
    "Invalid configuration": "Configuración no válida",
    "Hub Height and Rotor Diameter must be valid numbers.": "Hub Height y Rotor Diameter deben ser números válidos.",
    "No valid turbines were found with the model configuration.": "No se encontraron turbinas válidas con la configuración de modelo.",
    "The receiver layer is empty.": "La capa de receptores está vacía.",
    "SHADOW FLICKER CALCULATION SUMMARY": "RESUMEN DEL CÁLCULO DE SHADOW FLICKER",
    "Calculation completed successfully!": "¡Cálculo completado correctamente!",
    "Shadow Flicker Calculation Summary": "Resumen del cálculo de shadow flicker",
    "Shadow Statistics:": "Estadísticas de sombra:",
    "Results layer created successfully.": "Capa de resultados creada correctamente.",
    "Check the attribute table for detailed monthly breakdown.": "Consulta la tabla de atributos para el desglose mensual detallado.",
    "Calculation Complete": "Cálculo completado",
    "Shadow Flicker - Calculation Summary": "Shadow Flicker - Resumen de cálculo",
    "Site Configuration": "Configuración del emplazamiento",
    "Latitude:": "Latitud:",
    "Longitude:": "Longitud:",
    "Year:": "Año:",
    "Analysis Summary": "Resumen del análisis",
    "Monthly Breakdown (Average across all receptors)": "Desglose mensual (media de todos los receptores)",
    "All Receptors (sum)": "Todos los receptores (suma)",
    "Hours of shadow flicker by hour of day": "Horas de shadow flicker por hora del día",
    "Hour × Month Matrix": "Matriz hora × mes",
    "Detailed values for each receptor.": "Valores detallados por receptor.",
    "By Receptor": "Por receptor",
    "Export hour × month matrix to CSV": "Exportar matriz hora × mes a CSV",
    "Generating shadow flicker raster map": "Generando mapa raster de shadow flicker",
    "Raster in progress": "Raster en progreso",
    "Raster completed": "Raster completado",
    "Raster map created successfully.": "Mapa raster creado correctamente.",
    "Calculated points:": "Puntos calculados:",
    "Elapsed time:": "Tiempo transcurrido:",
    "seconds": "segundos",
    "File:": "Archivo:",
    "You can now regenerate TIFs filtered by month/hour": "Ahora puedes regenerar TIF filtrados por mes/hora",
    "Raster generation was cancelled.": "La generación del raster fue cancelada.",
    "Filtered raster generated": "Raster filtrado generado",
    "Applied filter:": "Filtro aplicado:",
    "Mean:": "Media:",
    "Export Successful": "Exportación correcta",
    "Exported": "Exportados",
    "receptors to:": "receptores a:",
    "Model": "Modelo",
    "Hub Height [m]": "Altura de buje [m]",
    "Rotor Diameter [m]": "Diámetro de rotor [m]",
    "Notes": "Notas",
    "Only": "Solo",
    "Only a las": "Solo a las",
    "all Energy-module layers": "todas las capas del módulo de energía",
    "WT model(s) ·": "modelo(s) WT ·",
    "turbine(s).": "turbina(s).",
}
_TO_EN.update(_I18N_EXTRA_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_EXTRA_TO_EN)
_EXTRA_REVERSE = {v: k for k, v in _I18N_EXTRA_TO_EN.items() if v != k}
_TO_ES.update(_EXTRA_REVERSE)
_FRAGMENT_TO_ES.update(_EXTRA_REVERSE)
_TO_ES.update({
    "Energy": "Energía",
    "Noise": "Ruido",
    "Shadow Flicker": "Shadow Flicker",
    "Velantis Wind": "Velantis Wind",
})
_FRAGMENT_TO_ES.update(_TO_ES)
_I18N_EXTRA2_TO_EN = {
    # Noise module remaining UI
    "Modelos WT detectados:": "Detected WT models:",
    "Usar": "Use",
    "Tipo": "Type",
    "Límite día": "Day limit",
    "Límite noche": "Night limit",
    "Altura [m]": "Height [m]",
    "Radio máximo [m]:": "Maximum radius [m]:",
    "MDT / DSM (opcional):": "DTM / DSM (optional):",
    "Resolución del mapa [m]:": "Map resolution [m]:",
    "Generar isófonas": "Generate isophones",
    "Niveles isófonas [dB(A)]:": "Isophone levels [dB(A)]:",
    "Límite receptor [dB(A)]:": "Receiver limit [dB(A)]:",
    "Atenuación lineal α [dB/m]:": "Linear attenuation α [dB/m]:",
    "Factor de suelo G:": "Ground factor G:",
    "Capa uso del suelo (opcional):": "Land-use layer (optional):",
    "Motor de cálculo:": "Calculation engine:",
    "⚡ Rápido - LwA global simplificado": "⚡ Fast - simplified global LwA",
    "Motor rápido: cálculo simplificado para cribado acústico\nMotor ISO: cálculo por bandas según ISO 9613-2:2024": "Fast engine: simplified screening calculation\nISO engine: band-based calculation following ISO 9613-2:2024",
    "⛅ Condiciones Atmosféricas (Motor ISO)": "⛅ Atmospheric conditions (ISO engine)",
    "Presión atmosférica:": "Atmospheric pressure:",
    "💡 Usa condiciones típicas del emplazamiento": "💡 Use typical site conditions",
    "Configuración de grupos fuente acústicos": "Acoustic source-group configuration",
    "Cada fila representa un grupo fuente acústico. Puedes renombrar grupo y parque para que el resumen y las exportaciones sean más legibles.": "Each row represents an acoustic source group. You can rename the group and wind farm to make summaries and exports easier to read.",
    "Importar curva acústica para el grupo fuente seleccionado…": "Import acoustic curve for the selected source group…",
    "Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo.": "You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group.",
    "Preparación del cálculo": "Calculation preparation",
    "Comprobar configuración": "Check configuration",
    "Calcular ruido": "Calculate noise",
    "Método fuente-receptor eólico (Adiv + Aatm + Aground)": "Wind source-receiver method (Adiv + Aatm + Aground)",
    "Escenario acústico:": "Acoustic scenario:",
    "Modelo base": "Base model",
    "Cálculo acústico completado.": "Acoustic calculation completed.",
    "Resultado acústico:": "Acoustic result:",
    "AVISO:": "WARNING:",
    "Método usado:": "Method used:",
    "Modelo fuente-receptor eólico": "Wind source-receiver model",
    "curvas acústicas en peor caso": "worst-case acoustic curves",
    "curvas acústicas": "acoustic curves",
    "LwA fijo por grupo fuente acústico": "fixed LwA by acoustic source group",
    "Escenario acústico efectivo por grupo fuente:": "Effective acoustic scenario by source group:",
    "Main output layer:": "Main output layer:",
    "Receivers by category:": "Receivers by category:",
    "activos": "active",
    "grupo(s) con criterio": "group(s) with criterion",
    "GIS source layer created:": "GIS source layer created:",
    "GIS dominant-links layer created:": "GIS dominant-links layer created:",
    "GIS receivers-outside-radius layer created:": "GIS receivers-outside-radius layer created:",
    "Mapa GIS creado:": "GIS map created:",
    "resolución autoajustada": "auto-adjusted resolution",
    "se solicitó el raster pero no se pudo generar": "the raster was requested but could not be generated",
    "GIS isophone layer created:": "GIS isophone layer created:",
    "Calculated receivers:": "Calculated receivers:",
    "Receivers with at least one turbine inside the radius:": "Receivers with at least one turbine inside the radius:",
    "todos los receptores han quedado fuera del radio máximo": "all receivers are outside the maximum radius",
    "Receivers exceeding the limit:": "Receivers exceeding the limit:",
    "Maximum calculated level:": "Maximum calculated level:",
    "α usado:": "α used:",
    "G usado:": "G used:",
    "Effective ground:": "Effective ground:",
    "modo global": "global mode",
    "aplicado en todos los trayectos": "applied to all paths",
    "No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.": "Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas fuente/receptor.",
    "DEM/DSM was used to sample terrain elevation at sources and receivers.": "DEM/DSM usado para muestrear elevación del terreno en fuentes y receptores.",
    "Receiver/source layers, and the noise raster map if enabled, have been added to the project.": "Las capas de receptores/fuentes y, si estaba activado, el mapa raster de ruido se han añadido al proyecto.",

    # AEP runtime remaining messages
    "Parámetros avanzados del modelo (opcional)": "Advanced model parameters (optional)",
    "Override de parámetros físicos del modelo. Si dejas «Default PyWake», se usa el valor canónico del modelo seleccionado.": "Override physical model parameters. If you leave 'Default PyWake', the canonical value for the selected model is used.",
    "Ayuda · Modelo de estela": "Help · Wake model",
    "Ayuda · Turbulencia": "Help · Turbulence",
    "Ayuda · Bloqueo": "Help · Blockage",
    "Ayuda · Turbulencia WRG": "Help · WRG turbulence",
    "Sin bloqueo:": "No blockage:",
    "Sin turbulencia añadida:": "No added turbulence:",
    "Ahora mismo has dejado el modelo de turbulencia en Ninguno": "You currently left the turbulence model as None",
    "Con PropagateDownwind, el bloqueo se desactiva automáticamente.": "With PropagateDownwind, blockage is disabled automatically.",
    "Inicializando cálculo…": "Initializing calculation…",
    "Pérdidas:": "Losses:",
    "Primero lanza un cálculo AEP. Después podrás guardarlo como escenario A o B.": "Run an AEP calculation first. Then you can save it as scenario A or B.",
    "Guarda primero un escenario A y un escenario B.": "Save scenario A and scenario B first.",
    "Exportar layout editado": "Export edited layout",
    "Activa primero una capa de turbinas": "Activate a turbine layer first",
    "Click izq: añadir turbina · Click der: borrar · ESC: salir": "Left click: add turbine · Right click: delete · ESC: exit",
    "¿Continuar?": "Continue?",
    "Ocurrió un error:": "An error occurred:",
    "La simulación requirió degradación automática:": "The simulation required automatic degradation:",
    "Revisa el CSV del Modelo": "Check the CSV for Model",
    "Añade turbinas en la capa del Modelo": "Add turbines to the Model layer",
    "o selecciona un CSV X,Y válido.": "or select a valid X,Y CSV.",
    "no tiene coordenadas. Añade turbinas en el mapa interactivo o selecciona un CSV X,Y válido.": "has no coordinates. Add turbines in the interactive map or select a valid X,Y CSV.",
    "Selecciona una carpeta WAsP válida o un WRG/ZIP antes de graficar.": "Select a valid WAsP folder or WRG/ZIP before plotting.",

    # Results remaining labels
    "AEP por dirección [MWh]": "AEP by direction [MWh]",
    "Dirección [°]": "Direction [°]",
    "Pérdidas por estela [%]": "Wake losses [%]",
    "Pérdidas por estela por sector": "Wake losses by sector",
    "Pérd. estela [%]": "Wake loss [%]",
    "Neto+pérd.": "Net+losses",
    "Comprobación del balance total": "Total balance check",
    "Modelo de turbulencia": "Turbulence model",
    "Superposición": "Superposition",
    "Métricas adicionales": "Additional metrics",
    "Desglose de pérdidas": "Loss breakdown",
    "Estelas:": "Wakes:",
    "Selección del usuario": "User selection",
    "Configuración solicitada al solver": "Configuration requested from solver",
    "Configuración finalmente ejecutada": "Configuration finally executed",
    "No se pudo generar el gráfico total:": "Could not generate the total chart:",

    # Shadow remaining UI / dialogs
    "Raster cell size\n100 m = fast, less detail\n50 m = medium\n25 m = slow, maximum detail": "Tamaño de celda raster\n100 m = rápido, menos detalle\n50 m = medio\n25 m = lento, máximo detalle",
    "Time step ONLY for the raster (independent from receivers).": "Paso temporal SOLO para el raster (independiente de los receptores).",
    "📊 Export 12×24 CSV": "📊 Exportar CSV 12×24",
    "Export Shadow Flicker 12×24": "Exportar Shadow Flicker 12×24",
    "Month": "Mes",
    "Site Configuration:": "Configuración del emplazamiento:",
    "Shadow Flicker Statistics": "Estadísticas de Shadow Flicker",
    "Time zone: not automatically detected.": "Zona horaria: no detectada automáticamente.",
    "Select an IANA time zone manually.": "Selecciona una zona horaria IANA manualmente.",
    "Turbine layer not found.": "Capa de turbinas no encontrada.",
    "Layer ID:": "ID de capa:",
    "No DEM selected - flat terrain assumed": "No se ha seleccionado DEM/MDT; se asume terreno plano",
    "Time step:": "Paso temporal:",
    "Turbine centroid:": "Centroide de turbinas:",
    "Creating raster map": "Creando mapa raster",
    "Regenerating filtered raster": "Regenerando raster filtrado",
    "Filtered TIF saved": "TIF filtrado guardado",
    "Valid points:": "Puntos válidos:",
    "no valid points": "sin puntos válidos",
    "No valid timesteps": "Sin pasos temporales válidos",
    "Raster saved successfully": "Raster guardado correctamente",
    "Completed successfully": "Completado correctamente",
    "Cancelled": "Cancelado",
    "Calculation cancelled by the user": "Cálculo cancelado por el usuario",
}
_TO_EN.update(_I18N_EXTRA2_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_EXTRA2_TO_EN)
_EXTRA2_REVERSE = {v: k for k, v in _I18N_EXTRA2_TO_EN.items() if v != k}
_TO_ES.update(_EXTRA2_REVERSE)
_FRAGMENT_TO_ES.update(_EXTRA2_REVERSE)


# ---------------------------------------------------------------------------
# Iteration 3: remaining Energy explanatory notes, information dialogs and
# turbine-parameter dialog strings.
# ---------------------------------------------------------------------------
_I18N_EXTRA3_TO_EN = {
    # Energy notes below selectors
    "Bloqueo desactivado": "Blockage disabled",
    "porque el motor es": "because the engine is",
    "solo propaga estelas hacia abajo, no soporta bloqueo": "only propagates wakes downstream, it does not support blockage",
    "Si quieres incluir bloqueo, cambia el motor a": "To include blockage, change the engine to",
    "Sin bloqueo:": "No blockage:",
    "el cálculo no incluirá la reducción de velocidad upstream.": "the calculation will not include upstream velocity reduction.",
    "En parques offshore grandes esto suele subestimar las pérdidas": "In large offshore wind farms this usually underestimates losses",
    "Modelo activo:": "Active model:",
    "modelo no estándar; revisar disponibilidad en esta versión de PyWake.": "non-standard model; check availability in this PyWake version.",
    "default industrial post-2020": "post-2020 industrial default",
    "Constantes calibradas LES fijas del paper.": "Fixed LES-calibrated constants from the paper.",
    "solución potencial tipo dipolo": "dipole-type potential-flow solution",
    "más conservador en near-field": "more conservative in the near field",
    "Sin parámetros tunables habituales.": "No usual tunable parameters.",
    "mezcla near-field (vortex) y far-field (self-similarity).": "near-field (vortex) and far-field (self-similarity) blend.",
    "Constantes de blend fijas.": "Fixed blend constants.",
    "ajuste analítico clásico": "classic analytical fit",
    "más rápido que SS2020": "faster than SS2020",
    "Constantes fijas del paper.": "Fixed paper constants.",
    "Con PUD se fuerza use_effective_ws=True para evitar errores de PyWake.": "With PUD, use_effective_ws=True is forced to avoid PyWake errors.",
    "Sin turbulencia añadida:": "No added turbulence:",
    "el cálculo solo usa la TI ambiente del recurso": "the calculation only uses the resource ambient TI",
    "Si tu wake model es TI-driven": "If your wake model is TI-driven",
    "conviene activar un modelo aquí.": "it is advisable to enable a model here.",
    "Coeficientes empíricos fijos del paper.": "Fixed empirical coefficients from the paper.",
    "Coeficientes empíricos fijos.": "Fixed empirical coefficients.",
    "Recomendado por defecto": "Recommended by default",
    "Útil para comparar con configuraciones antiguas o más conservadoras.": "Useful for comparison with older or more conservative configurations.",
    "derivado Frandsen, formulación clásica. Constantes fijas.": "Frandsen-derived, classic formulation. Fixed constants.",
    "ajuste empírico clásico. Constantes fijas del paper.": "classic empirical fit. Fixed paper constants.",
    "Para mover la sensibilidad del AEP a turbulencia": "To adjust AEP sensitivity to turbulence",
    "ajusta la": "adjust the",
    "TI ambiente": "ambient TI",
    "o cambia a un wake model TI-driven": "or switch to a TI-driven wake model",
    "Las constantes internas del modelo no se exponen porque están fijadas por el paper de referencia.": "The model internal constants are not exposed because they are fixed by the reference paper.",
    "recomendado para benchmarking, estudios rápidos y muchos casos offshore limpios.": "recommended for benchmarking, quick studies and many clean offshore cases.",
    "Es robusto, pero la TI apenas cambia el déficit base.": "It is robust, but TI barely changes the base deficit.",
    "recomendado para screening rápido y parques grandes cuando prima la velocidad.": "recommended for fast screening and large wind farms when speed is the priority.",
    "la TI no cambia el AEP de forma importante en la configuración estándar.": "TI does not significantly change AEP in the standard configuration.",
    "útil si quieres mantener un modelo ligero tipo NOJ": "useful if you want to keep a light NOJ-style model",
    "buena opción gaussiana para onshore": "good Gaussian option for onshore",
    "opción avanzada y sensible a TI": "advanced TI-sensitive option",
    "opción gaussiana sensible a TI": "TI-sensitive Gaussian option",
    "No todos los modelos de estela usan la TI del mismo modo": "Not all wake models use TI in the same way",
    "consulta la ayuda para elegir una combinación físicamente coherente.": "check the help dialog to choose a physically consistent combination.",
    "Ahora mismo has dejado el modelo de turbulencia en Ninguno": "You currently left the turbulence model as None",
    "el cálculo seguirá": "the calculation will continue",
    "pero la TI añadida por estela no entrará explícitamente.": "but wake-added TI will not explicitly enter the calculation.",
    "Con PropagateDownwind, el bloqueo se desactiva automáticamente.": "With PropagateDownwind, blockage is automatically disabled.",
    "k se deriva de la TI local": "k is derived from local TI",
    "ajusta TI/turbulencia para mover el AEP.": "adjust TI/turbulence to change AEP.",
    "k y σ se derivan de la TI": "k and σ are derived from TI",
    "Modelo basado en LUT/RANS": "LUT/RANS-based model",
    "sin pendiente k explícita.": "no explicit k slope.",
    "Coeficiente activo:": "Active coefficient:",
    "Default PyWake": "PyWake default",
    "override usuario": "user override",

    # Wake / turbulence / blockage information dialogs
    "Cómo elegir el modelo de estela": "How to choose the wake model",
    "Opciones recomendadas según el caso": "Recommended options by case",
    "buena opción general para": "good general option for",
    "comparación entre layouts y estudios rápidos": "layout comparison and quick studies",
    "offshore limpio": "clean offshore",
    "suele ser una elección robusta y estable": "it is usually a robust and stable choice",
    "su déficit base no cambia mucho con la TI": "its base deficit does not change much with TI",
    "recomendable cuando quieres un modelo gaussiano": "recommended when you want a Gaussian model",
    "TI sí influya": "TI really affects",
    "recuperación de la estela": "wake recovery",
    "Suele tener sentido en": "It usually makes sense in",
    "parques con turbulencia ambiente relevante": "wind farms with relevant ambient turbulence",
    "cuando mezclas distintas alturas": "when mixing different heights",
    "opción más avanzada dentro de la familia gaussiana": "more advanced option within the Gaussian family",
    "más sensibilidad a la TI": "more sensitivity to TI",
    "zona de near wake": "near-wake region",
    "modelo gaussiano con sensibilidad a la TI local/efectiva": "Gaussian model with sensitivity to local/effective TI",
    "cribado rápido": "quick screening",
    "evaluaciones preliminares": "preliminary assessments",
    "prima la velocidad y la robustez": "speed and robustness are the priority",
    "muy ligera computacionalmente": "computationally very light",
    "Guía práctica muy corta": "Very short practical guide",
    "rapidez": "speed",
    "Screening masivo": "large-scale screening",
    "Nota": "Note",
    "La explicación detallada": "The detailed explanation",
    "está en el botón de información": "is in the information button",
    "Qué significa la turbulencia en este plugin": "What turbulence means in this plugin",
    "describe la": "describes the",
    "turbulencia ambiente": "ambient turbulence",
    "dato de entrada del recurso": "resource input datum",
    "calcula la": "calculates the",
    "turbulencia añadida por estela": "wake-added turbulence",
    "detrás de las turbinas": "behind the turbines",
    "decide si esa TI ambiente/efectiva cambia realmente el déficit": "decides whether ambient/effective TI actually changes the deficit",
    "Cómo leerlo físicamente": "Physical interpretation",
    "Si solo aportas un raster TI": "If you only provide one TI raster",
    "aproximación vertical": "vertical approximation",
    "Si aportas varios raster(s) TI": "If you provide several TI raster(s)",
    "interpolar por altura": "interpolate by height",
    "fallback uniforme": "uniform fallback",
    "Cuándo importa mucho en el AEP": "When it matters significantly for AEP",
    "la TI no cambia mucho el déficit base del wake": "TI does not change the base wake deficit much",
    "impacto sobre el AEP suele ser limitado": "impact on AEP is usually limited",
    "Qué conviene según el parque": "What is advisable depending on the wind farm",
    "Offshore con inflow relativamente limpio": "Offshore with relatively clean inflow",
    "terreno rugoso": "rough terrain",
    "alturas de buje distintas": "different hub heights",
    "varias capas TI": "several TI layers",
    "Qué elegir en el selector de modelo de turbulencia": "What to choose in the turbulence-model selector",
    "buena opción general": "good general option",
    "alternativa clásica y sencilla": "classic and simple alternative",
    "Regla práctica": "Rule of thumb",
    "Qué es el bloqueo y cuándo importa": "What blockage is and when it matters",
    "reducción de velocidad": "velocity reduction",
    "aguas arriba": "upstream",
    "causada por la propia turbina": "caused by the turbine itself",
    "turbinas vecinas": "neighboring turbines",
    "Magnitud típica en AEP": "Typical magnitude in AEP",
    "Parques offshore grandes": "Large offshore wind farms",
    "Parques onshore pequeños o muy espaciados": "Small or widely spaced onshore wind farms",
    "Interacción crítica con el motor": "Critical interaction with the engine",
    "El bloqueo SOLO entra en el cálculo": "Blockage ONLY enters the calculation",
    "si el motor lo soporta": "if the engine supports it",
    "NO calcula bloqueo": "does NOT calculate blockage",
    "incluye bloqueo iterando todas las interacciones": "includes blockage by iterating all interactions",
    "incluye bloqueo con propagación bidireccional": "includes blockage with bidirectional propagation",
    "Suele ser la opción más completa": "It is usually the most complete option",
    "Cómo elegir el modelo": "How to choose the model",
    "Calibrado contra LES": "Calibrated against LES",
    "opción de referencia": "reference option",
    "análisis de sensibilidad": "sensitivity analysis",
    "alternativa ligera": "lightweight alternative",
    "entrega de consultoría seria": "serious consulting deliverable",
    "Por qué no se exponen las constantes internas del modelo": "Why the model internal constants are not exposed",
    "calibraciones del paper de referencia": "calibrations from the reference paper",
    "no perillas de sitio": "not site-specific knobs",
    "Cambiarlos rompe la citabilidad del modelo": "Changing them breaks the model traceability/citability",
    "Turbulencia opcional para WRG": "Optional turbulence for WRG",
    "Este campo solo se usa con": "This field is only used with",
    "Si no seleccionas raster TI": "If you do not select a TI raster",
    "Una capa o varias capas": "One layer or several layers",
    "Un solo raster TI": "Single TI raster",
    "Varios raster(s) TI a distintas alturas": "Several TI raster(s) at different heights",
    "escribir la altura manualmente": "enter the height manually",
    "detectarla en el nombre del archivo": "detect it in the file name",
    "Cuándo merece la pena aportar TI": "When it is worth providing TI",
    "Consejo": "Tip",
    "el plugin sigue siendo usable": "the plugin remains usable",
    "representación física del recurso mejora bastante": "physical representation of the resource improves significantly",

    # Turbine-definition dialog
    "Definir turbina": "Define turbine",
    "Plantilla rápida:": "Quick template:",
    "Sin plantilla": "No template",
    "genérico": "generic",
    "Las plantillas son genéricas para pre-screening. Para entrega final usa la curva oficial del fabricante/cliente.": "Templates are generic for pre-screening. For final delivery, use the official manufacturer/client curve.",
    "Nombre:": "Name:",
    "Diámetro rotor:": "Rotor diameter:",
    "Altura buje:": "Hub height:",
    "Velocidad viento (m/s):": "Wind speed (m/s):",
    "Potencia (kW):": "Power (kW):",
    "Potencia en kW, misma longitud que ws": "Power in kW, same length as ws",
    "opcional": "optional",
    "Crear turbina (Manual)": "Create turbine (Manual)",
    "Fichero TXT/CSV:": "TXT/CSV file:",
    "Selecciona TXT/CSV…": "Select TXT/CSV…",
    "Examinar…": "Browse…",
    "Delimitador:": "Delimiter:",
    "Tabulación": "Tab",
    "Coma": "Comma",
    "Punto y coma": "Semicolon",
    "Espacio": "Space",
    "Columna ws (0-based):": "ws column (0-based):",
    "Columna potencia kW (0-based):": "power kW column (0-based):",
    "Incluir columna CT": "Include CT column",
    "Filas de cabecera a saltar:": "Header rows to skip:",
    "Crear turbina (TXT/CSV)": "Create turbine (TXT/CSV)",
    "Aceptar": "OK",
    "Abrir TXT/CSV": "Open TXT/CSV",
    "Todos": "All",
    "Fichero seleccionado:": "Selected file:",
    "Plantilla de turbina": "Turbine template",
    "No se pudo aplicar la plantilla:": "Could not apply the template:",
    "Listo": "Ready",
    "Turbina creada (manual). Pulsa Aceptar para usarla.": "Turbine created (manual). Press OK to use it.",
    "Error creando turbina": "Error creating turbine",
    "Selecciona un archivo TXT/CSV válido.": "Select a valid TXT/CSV file.",
    "Turbina creada desde TXT/CSV. Pulsa Aceptar para usarla.": "Turbine created from TXT/CSV. Press OK to use it.",
    "Error leyendo TXT/CSV": "Error reading TXT/CSV",
    "Sin turbina": "No turbine",
    "Primero pulsa «Crear turbina» en la pestaña correspondiente.": "First press “Create turbine” in the corresponding tab.",
}
_TO_EN.update(_I18N_EXTRA3_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_EXTRA3_TO_EN)
_EXTRA3_REVERSE = {v: k for k, v in _I18N_EXTRA3_TO_EN.items() if v != k}
_TO_ES.update(_EXTRA3_REVERSE)
_FRAGMENT_TO_ES.update(_EXTRA3_REVERSE)

# ---------------------------------------------------------------------------
# Translation engine (multi-language, Spanish-pivot, resilient).
#
# Design notes:
#   * Spanish is the canonical *source* language. Every translation is stored
#     as Spanish -> X.  Going from any language A to any language B is done by
#     pivoting A -> Spanish -> B, so switching e.g. English -> French works
#     even though no direct EN->FR map exists.
#   * Lookups tolerate cosmetic differences (collapsed whitespace, non-breaking
#     spaces, surrounding blanks) via a normalized index, so a stray space no
#     longer leaves a string untranslated.
#   * Missing translations fall back to the source text instead of breaking,
#     and `find_untranslated()` lists what is still pending for a language.
# ---------------------------------------------------------------------------

# lang -> {"full": {es: tgt}, "frag": {es: tgt}}.  English is folded in from the
# legacy module-level dicts on first build; other languages register themselves.
_REGISTERED: Dict[str, Dict[str, Dict[str, str]]] = {}
_STATE: Dict[str, object] = {}
_BUILT = False
_CACHE: Dict = {}

_WS_RE = re.compile(r"\s+")


def _norm_key(s: str) -> str:
    """Whitespace/NBSP-insensitive key used as a resilient lookup fallback."""
    return _WS_RE.sub(" ", str(s).replace("\u00a0", " ")).strip()


def register_language(lang, full_map=None, fragment_map=None, label=None) -> None:
    """Register (or extend) a Spanish->`lang` translation table.

    `full_map` matches whole strings; `fragment_map` is used for substring
    replacement inside larger blobs (HTML, multi-line text).  If only
    `full_map` is given it is reused for fragments, mirroring the original
    behaviour.
    """
    lang = str(lang or "").lower().strip()
    if not lang:
        return
    entry = _REGISTERED.setdefault(lang, {"full": {}, "frag": {}})
    if full_map:
        entry["full"].update(full_map)
    if fragment_map is not None:
        entry["frag"].update(fragment_map)
    elif full_map:
        entry["frag"].update(full_map)
    if lang not in SUPPORTED_LANGUAGES and lang != _SOURCE_LANG:
        globals()["SUPPORTED_LANGUAGES"] = tuple(SUPPORTED_LANGUAGES) + (lang,)
    if label:
        _LANGUAGE_LABELS[lang] = label
    _invalidate()


def _invalidate() -> None:
    global _BUILT
    _BUILT = False
    _CACHE.clear()


def _build() -> None:
    global _BUILT
    # English historically lives in the legacy module-level dicts; fold it in.
    if LANG_EN not in _REGISTERED:
        _REGISTERED[LANG_EN] = {"full": dict(_TO_EN), "frag": dict(_FRAGMENT_TO_EN)}

    full = {_SOURCE_LANG: {}}
    frag = {_SOURCE_LANG: {}}
    for lang, data in _REGISTERED.items():
        full[lang] = dict(data.get("full") or {})
        frag[lang] = dict(data.get("frag") or {})

    # Reverse maps: any translated text -> canonical Spanish (the pivot step).
    # Do not reverse a target fragment if that same text is also a known Spanish
    # source fragment.  Otherwise Spanish source strings such as "Raster(s) TI"
    # can be damaged during normalization before being translated to FR/DE.
    source_full_keys = set()
    for _m in full.values():
        source_full_keys.update((_m or {}).keys())
    source_frag_keys = set()
    for _m in frag.values():
        source_frag_keys.update((_m or {}).keys())

    reverse_fragment_blocklist = {"Raster"}

    src_full: Dict[str, str] = {}
    src_frag = []
    for lang, m in full.items():
        if lang == _SOURCE_LANG:
            continue
        for es, tgt in m.items():
            if tgt and tgt != es and tgt not in source_full_keys:
                src_full.setdefault(tgt, es)
    src_frag_map: Dict[str, str] = {}
    for lang, m in frag.items():
        if lang == _SOURCE_LANG:
            continue
        for es, tgt in m.items():
            if tgt and tgt != es and tgt not in source_frag_keys and tgt not in reverse_fragment_blocklist:
                src_frag_map.setdefault(tgt, es)

    _STATE.clear()
    _STATE.update(
        full=full,
        frag=frag,
        full_norm={lang: {_norm_key(es): tgt for es, tgt in m.items()} for lang, m in full.items()},
        src_full=src_full,
        src_full_norm={_norm_key(k): v for k, v in src_full.items()},
        frag_sorted={lang: sorted(m.items(), key=lambda kv: len(kv[0]), reverse=True) for lang, m in frag.items()},
        src_frag_sorted=sorted(src_frag_map.items(), key=lambda kv: len(kv[0]), reverse=True),
    )
    _BUILT = True


def _ensure() -> None:
    if not _BUILT:
        _build()


def _to_source(s: str):
    """Map a possibly-translated string back to canonical Spanish, or None."""
    src_full = _STATE["src_full"]  # type: ignore[index]
    if s in src_full:
        return src_full[s]
    n = _norm_key(s)
    src_norm = _STATE["src_full_norm"]  # type: ignore[index]
    return src_norm.get(n)


_DYNAMIC_FRAGMENT_MARKERS = (
    "turbine(s)", "receiver(s)", "WT model(s)", "model(s)",
    "Model ", "Modelo ", "Modelo:", "Model:", "modelo(s)",
    "turbina(s)", "receptor(es)", "capa(s)", "capas/modelos",
    "No se pudo", "No se puede", "No hay", "Selecciona", "Revisa",
    "Raster(s) TI", "Perímetro", "Malla:", "WRG con distinta", "CRS destino",
    "Layout detectado", "Acoustic sources", "Grupos fuente",
    "No turbine", "Turbine layer", "Receiver layer", "Analysis year",
    "Solar elevation", "Max shadow distance", "Valid configuration",
    "Incomplete configuration", "Hub Height=", "Rotor Diameter=",
    # French dynamic fragments left by the FR pass and used in Shadow/Noise summaries.
    "éolienne(s)", "récepteur(s)", "modèle(s)", "couche", "Couche",
    "Importé", "Aucun layout", "Fuseau horaire", "Ombres", "Bruit",
    "•", "✓", "⚠️", "✅", "❌", " · ",
)


def _should_fragment_short_text(s: str) -> bool:
    try:
        return any(m in s for m in _DYNAMIC_FRAGMENT_MARKERS)
    except Exception:
        return False

def tr_text(text):
    """Translate a whole UI string into the active language."""
    if text is None:
        return text
    s = str(text)
    if not s.strip():
        return s
    lang = current_language()
    key = (lang, s)
    if key in _CACHE:
        return _CACHE[key]
    _ensure()

    # Normalize the incoming text to the Spanish pivot first (it may already be
    # in English or French because setters get re-applied on language switch).
    base = _to_source(s)
    base = base if base is not None else s

    if lang == _SOURCE_LANG:
        result = base
    else:
        fmap = _STATE["full"][lang]  # type: ignore[index]
        if base in fmap:
            result = fmap[base]
        else:
            nmap = _STATE["full_norm"][lang]  # type: ignore[index]
            hit = nmap.get(_norm_key(base))
            if hit is not None:
                result = hit
            else:
                # Whole-string miss.  For rich or long text (HTML, multi-line,
                # long paragraphs) fragment replacement still helps; for short
                # plain labels it would yield a half-translated result, so we
                # keep the clean Spanish source instead.
                if ("<" in base) or ("\n" in base) or (len(base) > 60) or _should_fragment_short_text(base):
                    result = _fragment_impl(base, lang)
                else:
                    result = base
    _CACHE[key] = result
    return result


def _apply_fragments(s: str, pairs) -> str:
    """Apply fragment replacements without re-processing inserted text.

    Earlier builds used sequential ``str.replace`` calls. That can cascade when
    a target word contains another source word, e.g. ``Modelo`` -> ``Modell``
    followed by ``Model`` -> ``Modell`` could create malformed German labels.

    We also avoid matching a short alphabetic key inside a longer word during
    the normalization pass, e.g. English ``Model`` must not match the Spanish
    source word ``Modelo``. Longest-first precedence is still preserved.
    """
    replacements = []
    for src, dst in pairs:
        if not src:
            continue
        token = f"\uE000VW_I18N_{len(replacements)}\uE000"
        # Single word keys must be whole words.  This prevents reverse maps such
        # as Model->Modelo from touching the Spanish source word Modelo.
        if all((ch.isalnum() or ch == "_") for ch in src):
            pattern = re.compile(r"(?<!\w)" + re.escape(src) + r"(?!\w)")
            s, n = pattern.subn(token, s)
            if n:
                replacements.append((token, dst))
        elif src in s:
            s = s.replace(src, token)
            replacements.append((token, dst))
    for token, dst in replacements:
        s = s.replace(token, dst)
    return s

def _fragment_impl(text: str, lang: str) -> str:
    _ensure()
    s = str(text)
    # Step 1: normalize any English/French fragments back to Spanish.
    s = _apply_fragments(s, _STATE["src_frag_sorted"])  # type: ignore[index]
    # Step 2: Spanish -> target.
    if lang != _SOURCE_LANG:
        s = _apply_fragments(s, _STATE["frag_sorted"][lang])  # type: ignore[index]
    return s


def translate_fragment(text):
    if text is None:
        return text
    s = str(text)
    if not s:
        return s
    lang = current_language()
    _ensure()

    # Important for long help dialogs: try an exact/normalized whole-string
    # translation first.  If we go directly to fragment replacement, a missed
    # long paragraph may become a mixed Spanish/French text because short words
    # such as "sí" -> "oui" are still replaced.
    base = _to_source(s)
    base = base if base is not None else s
    if lang == _SOURCE_LANG:
        return base

    fmap = _STATE["full"].get(lang, {})  # type: ignore[index,union-attr]
    if base in fmap:
        return fmap[base]
    nmap = _STATE["full_norm"].get(lang, {})  # type: ignore[index,union-attr]
    hit = nmap.get(_norm_key(base))
    if hit is not None:
        return hit

    return _fragment_impl(base, lang)


def translate_html(html):
    return translate_fragment(html)


def find_untranslated(lang) -> list:
    """Return the Spanish source strings that have no entry for `lang`.

    Handy during development: `i18n.find_untranslated("fr")` lists exactly
    what is still pending so coverage can be completed incrementally.
    """
    lang = str(lang or "").lower().strip()
    _ensure()
    if lang in (_SOURCE_LANG, ""):
        return []
    target = _STATE["full"].get(lang, {})  # type: ignore[union-attr]
    # The full set of known source strings = union of every language's keys.
    sources = set()
    for m in _STATE["full"].values():  # type: ignore[union-attr]
        sources.update(m.keys())
    return sorted(s for s in sources if s and s not in target)


# Backward-compatible shims (kept in case external code referenced them).
def _map_for_target() -> Dict[str, str]:
    _ensure()
    lang = current_language()
    return {} if lang == _SOURCE_LANG else _STATE["full"][lang]  # type: ignore[index]


def _fragment_map_for_target() -> Dict[str, str]:
    _ensure()
    lang = current_language()
    return {} if lang == _SOURCE_LANG else _STATE["frag"][lang]  # type: ignore[index]


def _safe_call(fn, *args, **kwargs):
    try:
        return fn(*args, **kwargs)
    except Exception:
        return None


def apply_i18n(widget: QtWidgets.QWidget) -> None:
    """Translate visible text in an already-built widget tree."""
    if widget is None:
        return
    try:
        if hasattr(widget, "windowTitle") and hasattr(widget, "setWindowTitle"):
            title = widget.windowTitle()
            if title:
                widget.setWindowTitle(tr_text(title))
    except Exception:
        pass

    for w in [widget] + list(widget.findChildren(QtWidgets.QWidget)):
        try:
            # Tooltips and input placeholder texts are user-visible too.
            try:
                if hasattr(w, "toolTip") and hasattr(w, "setToolTip"):
                    tip = w.toolTip()
                    if tip:
                        w.setToolTip(translate_fragment(tip))
            except Exception:
                pass
            try:
                if isinstance(w, QtWidgets.QLineEdit):
                    ph = w.placeholderText()
                    if ph:
                        w.setPlaceholderText(translate_fragment(ph))
            except Exception:
                pass

            if isinstance(w, QtWidgets.QLabel):
                txt = w.text()
                if txt:
                    w.setText(tr_text(txt))
            elif isinstance(w, (QtWidgets.QPushButton, QtWidgets.QCheckBox, QtWidgets.QRadioButton)):
                txt = w.text()
                if txt:
                    w.setText(tr_text(txt))
            elif isinstance(w, QtWidgets.QGroupBox):
                txt = w.title()
                if txt:
                    w.setTitle(tr_text(txt))
            elif isinstance(w, QtWidgets.QTabWidget):
                for i in range(w.count()):
                    txt = w.tabText(i)
                    if txt:
                        w.setTabText(i, tr_text(txt))
            elif isinstance(w, QtWidgets.QComboBox):
                for i in range(w.count()):
                    txt = w.itemText(i)
                    if txt:
                        w.setItemText(i, tr_text(txt))
            elif isinstance(w, QtWidgets.QTableWidget):
                for c in range(w.columnCount()):
                    item = w.horizontalHeaderItem(c)
                    if item is not None and item.text():
                        item.setText(tr_text(item.text()))
                for r in range(w.rowCount()):
                    item = w.verticalHeaderItem(r)
                    if item is not None and item.text():
                        item.setText(tr_text(item.text()))
                    for c in range(w.columnCount()):
                        item = w.item(r, c)
                        if item is not None and item.text():
                            item.setText(tr_text(item.text()))
            elif isinstance(w, QtWidgets.QTextBrowser):
                html = w.toHtml()
                if html:
                    w.setHtml(translate_html(html))
            elif isinstance(w, QtWidgets.QTextEdit):
                txt = w.toPlainText()
                if txt:
                    w.setPlainText(translate_fragment(txt))
            elif isinstance(w, QtWidgets.QPlainTextEdit):
                txt = w.toPlainText()
                if txt:
                    w.setPlainText(translate_fragment(txt))
        except Exception:
            continue


def install_runtime_i18n_patches() -> None:
    """Patch common Qt setters so dynamic alerts and summaries follow language."""
    global _PATCHED
    if _PATCHED:
        return
    _PATCHED = True

    def patch_method(cls, name, translator):
        original = getattr(cls, name, None)
        if original is None or getattr(original, "_velantis_i18n", False):
            return
        def wrapped(self, text, *args, **kwargs):
            return original(self, translator(text), *args, **kwargs)
        wrapped._velantis_i18n = True  # type: ignore[attr-defined]
        setattr(cls, name, wrapped)

    patch_method(QtWidgets.QLabel, "setText", tr_text)
    patch_method(QtWidgets.QPushButton, "setText", tr_text)
    patch_method(QtWidgets.QCheckBox, "setText", tr_text)
    patch_method(QtWidgets.QRadioButton, "setText", tr_text)
    patch_method(QtWidgets.QGroupBox, "setTitle", tr_text)
    patch_method(QtWidgets.QWidget, "setWindowTitle", tr_text)
    patch_method(QtWidgets.QWidget, "setToolTip", translate_fragment)
    patch_method(QtWidgets.QLineEdit, "setPlaceholderText", translate_fragment)
    patch_method(QtWidgets.QTextBrowser, "setHtml", translate_html)
    try:
        patch_method(QtWidgets.QTextEdit, "setPlainText", translate_fragment)
        patch_method(QtWidgets.QTextEdit, "setText", translate_fragment)
        patch_method(QtWidgets.QTextEdit, "append", translate_fragment)
    except Exception:
        pass
    try:
        patch_method(QtWidgets.QPlainTextEdit, "setPlainText", translate_fragment)
        patch_method(QtWidgets.QPlainTextEdit, "appendPlainText", translate_fragment)
    except Exception:
        pass
    try:
        patch_method(QtWidgets.QProgressDialog, "setLabelText", translate_fragment)
    except Exception:
        pass

    # QTableWidget headers are often set after construction.
    try:
        original_headers = getattr(QtWidgets.QTableWidget, "setHorizontalHeaderLabels", None)
        if original_headers is not None and not getattr(original_headers, "_velantis_i18n", False):
            def headers_wrapped(self, labels, *args, **kwargs):
                try:
                    labels = [tr_text(x) for x in labels]
                except Exception:
                    pass
                return original_headers(self, labels, *args, **kwargs)
            headers_wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QTableWidget.setHorizontalHeaderLabels = headers_wrapped
    except Exception:
        pass

    # QGIS message bar: translate main title/text in pushMessage/createMessage.
    try:
        from qgis.gui import QgsMessageBar
        for meth in ("pushMessage", "createMessage"):
            original = getattr(QgsMessageBar, meth, None)
            if original is None or getattr(original, "_velantis_i18n", False):
                continue
            def make_bar_wrapper(orig):
                def wrapped(self, *args, **kwargs):
                    args = list(args)
                    for i in range(min(2, len(args))):
                        if isinstance(args[i], str):
                            args[i] = translate_fragment(args[i])
                    return orig(self, *args, **kwargs)
                wrapped._velantis_i18n = True  # type: ignore[attr-defined]
                return wrapped
            setattr(QgsMessageBar, meth, make_bar_wrapper(original))
    except Exception:
        pass


    # Remaining dynamic Qt methods used heavily by forms/combos/tabs.
    try:
        orig_combo_add_item = getattr(QtWidgets.QComboBox, "addItem", None)
        if orig_combo_add_item is not None and not getattr(orig_combo_add_item, "_velantis_i18n", False):
            def combo_add_item_wrapped(self, *args, **kwargs):
                args = list(args)
                # Supports both addItem(text, userData) and addItem(icon, text, userData).
                if args and isinstance(args[0], str):
                    args[0] = tr_text(args[0])
                elif len(args) >= 2 and isinstance(args[1], str):
                    args[1] = tr_text(args[1])
                return orig_combo_add_item(self, *args, **kwargs)
            combo_add_item_wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QComboBox.addItem = combo_add_item_wrapped
    except Exception:
        pass
    try:
        orig_combo_add_items = getattr(QtWidgets.QComboBox, "addItems", None)
        if orig_combo_add_items is not None and not getattr(orig_combo_add_items, "_velantis_i18n", False):
            def combo_add_items_wrapped(self, texts, *args, **kwargs):
                try:
                    texts = [tr_text(t) for t in texts]
                except Exception:
                    pass
                return orig_combo_add_items(self, texts, *args, **kwargs)
            combo_add_items_wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QComboBox.addItems = combo_add_items_wrapped
    except Exception:
        pass
    try:
        patch_method(QtWidgets.QComboBox, "setItemText", tr_text)
    except Exception:
        pass
    try:
        orig_tab_add = getattr(QtWidgets.QTabWidget, "addTab", None)
        if orig_tab_add is not None and not getattr(orig_tab_add, "_velantis_i18n", False):
            def tab_add_wrapped(self, widget, *args, **kwargs):
                args = list(args)
                if args and isinstance(args[-1], str):
                    args[-1] = tr_text(args[-1])
                return orig_tab_add(self, widget, *args, **kwargs)
            tab_add_wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QTabWidget.addTab = tab_add_wrapped
    except Exception:
        pass
    try:
        orig_form_add = getattr(QtWidgets.QFormLayout, "addRow", None)
        if orig_form_add is not None and not getattr(orig_form_add, "_velantis_i18n", False):
            def form_add_wrapped(self, *args, **kwargs):
                args = list(args)
                if args and isinstance(args[0], str):
                    args[0] = tr_text(args[0])
                return orig_form_add(self, *args, **kwargs)
            form_add_wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QFormLayout.addRow = form_add_wrapped
    except Exception:
        pass

    # QMessageBox instance setters are used by the clickable help icons in
    # Noise/Shadow modules (QMessageBox(self); setText(...); exec_()).
    # Static information/warning wrappers below do not cover those calls.
    try:
        patch_method(QtWidgets.QMessageBox, "setText", tr_text)
        patch_method(QtWidgets.QMessageBox, "setInformativeText", tr_text)
        patch_method(QtWidgets.QMessageBox, "setDetailedText", tr_text)
    except Exception:
        pass

    # QMessageBox static methods: information / warning / critical / question
    for name in ("information", "warning", "critical", "question"):
        original = getattr(QtWidgets.QMessageBox, name, None)
        if original is None or getattr(original, "_velantis_i18n", False):
            continue
        def make_wrapper(orig):
            def wrapped(parent, title, text, *args, **kwargs):
                return orig(parent, tr_text(title), tr_text(text), *args, **kwargs)
            wrapped._velantis_i18n = True  # type: ignore[attr-defined]
            return wrapped
        setattr(QtWidgets.QMessageBox, name, make_wrapper(original))

    # QFileDialog static helpers: translate captions used by export/import dialogs.
    try:
        _orig_get_save = getattr(QtWidgets.QFileDialog, "getSaveFileName", None)
        if _orig_get_save is not None and not getattr(_orig_get_save, "_velantis_i18n", False):
            def _vw_qfiledialog_get_save_name(parent=None, caption="", directory="", filter="", *args, **kwargs):
                return _orig_get_save(parent, tr_text(caption), directory, translate_fragment(filter), *args, **kwargs)
            _vw_qfiledialog_get_save_name._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QFileDialog.getSaveFileName = _vw_qfiledialog_get_save_name
        _orig_get_open = getattr(QtWidgets.QFileDialog, "getOpenFileName", None)
        if _orig_get_open is not None and not getattr(_orig_get_open, "_velantis_i18n", False):
            def _vw_qfiledialog_get_open_name(parent=None, caption="", directory="", filter="", *args, **kwargs):
                return _orig_get_open(parent, tr_text(caption), directory, translate_fragment(filter), *args, **kwargs)
            _vw_qfiledialog_get_open_name._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QFileDialog.getOpenFileName = _vw_qfiledialog_get_open_name
        _orig_get_open_names = getattr(QtWidgets.QFileDialog, "getOpenFileNames", None)
        if _orig_get_open_names is not None and not getattr(_orig_get_open_names, "_velantis_i18n", False):
            def _vw_qfiledialog_get_open_names(parent=None, caption="", directory="", filter="", *args, **kwargs):
                return _orig_get_open_names(parent, tr_text(caption), directory, translate_fragment(filter), *args, **kwargs)
            _vw_qfiledialog_get_open_names._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QFileDialog.getOpenFileNames = _vw_qfiledialog_get_open_names
        _orig_get_dir = getattr(QtWidgets.QFileDialog, "getExistingDirectory", None)
        if _orig_get_dir is not None and not getattr(_orig_get_dir, "_velantis_i18n", False):
            def _vw_qfiledialog_get_existing_dir(parent=None, caption="", directory="", *args, **kwargs):
                return _orig_get_dir(parent, tr_text(caption), directory, *args, **kwargs)
            _vw_qfiledialog_get_existing_dir._velantis_i18n = True  # type: ignore[attr-defined]
            QtWidgets.QFileDialog.getExistingDirectory = _vw_qfiledialog_get_existing_dir
    except Exception:
        pass

    # Visible confirmation in the QGIS log that this i18n build is the one loaded.
    try:
        from qgis.core import QgsMessageLog, Qgis  # type: ignore
        QgsMessageLog.logMessage(
            "Velantis i18n runtime patches active (build %s)" % globals().get("I18N_BUILD", "?"),
            "Velantis Wind",
            Qgis.Info,
        )
    except Exception:
        pass


def language_label(lang: str | None = None) -> str:
    lang = (lang or current_language()).lower()
    return _LANGUAGE_LABELS.get(lang, _LANGUAGE_LABELS.get(_SOURCE_LANG, lang))

# ---------------------------------------------------------------------------
# Iteration 4: noise report coverage and critical-receiver wording.
# Added after function definitions on purpose: dictionaries are read at runtime.
# ---------------------------------------------------------------------------
_I18N_ITER4_NOISE_TO_EN = {
    # Noise summary tabs/buttons/headers that can still appear in Spanish/English mix
    "Descripción": "Description",
    "Capa": "Layer",
    "Estado": "Status",
    "creada": "created",
    "no creada": "not created",
    "id receptor": "receiver id",
    "tipo": "type",
    "nivel total dB(A)": "total level dB(A)",
    "límite dB(A)": "limit dB(A)",
    "margen límite dB": "limit margin dB",
    "estado": "status",
    "supera límite": "exceeds limit",
    "nº turbinas": "no. turbines",
    "dist. turbina cercana (m)": "nearest turbine dist. (m)",
    "modelo dominante": "dominant model",
    "grupo fuente dom.": "dominant source group",
    "parque dom.": "dominant wind farm",
    "capa fuente dom.": "dominant source layer",
    "LwA fuente dom. dB(A)": "dominant source LwA dB(A)",
    "pérdida Adiv dB": "Adiv loss dB",
    "pérdida Aatm dB": "Aatm loss dB",
    "pérdida Agr/Aground dB": "Agr/Aground loss dB",
    "pérdida Abar dB": "Abar loss dB",
    "banda dom. Hz": "dominant band Hz",

    # Critical receiver block
    "Critical Receiver (Highest Sound Level)": "Critical receiver (highest sound level)",
    "ID Receptor": "Receiver ID",
    "Nivel total": "Total level",
    "Límite aplicable": "Applicable limit",
    "Margen": "Margin",
    "CUMPLE": "PASS",
    "EXCEDE": "EXCEEDS",
    "Modelo dominante": "Dominant model",
    "Grupo fuente": "Source group",
    "Nº turbinas": "No. turbines",
    "Distancia": "Distance",
    "Desglose de Atenuaciones": "Attenuation breakdown",
    "Los valores mostrados a continuación son las magnitudes de atenuación usadas por el modelo. En la ecuación principal estos términos se restan al nivel de fuente.": "The values shown below are the attenuation magnitudes used by the model. In the main equation these terms are subtracted from the source level.",
    "Término": "Term",
    "Valor [dB]": "Value [dB]",
    "LwA fuente dominante": "Dominant source LwA",
    "Potencia sonora de la turbina": "Turbine sound power",
    "Adiv (divergencia)": "Adiv (divergence)",
    "Dispersión geométrica": "Geometrical spreading",
    "Aatm (atmosférica)": "Aatm (atmospheric)",
    "Absorción en el aire": "Air absorption",
    "Agr (suelo)": "Agr (ground)",
    "Atenuación por efecto del terreno": "Ground-effect attenuation",
    "Abar (apantallamiento)": "Abar (screening)",
    "Atenuación adicional por difracción topográfica": "Additional topographic diffraction attenuation",
    "NIVEL RESULTANTE": "RESULTING LEVEL",
    "Banda dominante": "Dominant band",
    "Origen espectro": "Spectrum source",
    "Critical receiver no disponible.": "Critical receiver unavailable.",

    # Attenuation statistics block
    "Estadísticos de Atenuaciones (receptores cubiertos)": "Attenuation statistics (covered receivers)",
    "Estadísticos de Atenuaciones": "Attenuation statistics",
    "receptores cubiertos": "covered receivers",
    "Raw attenuation magnitudes are shown (no el signo algebraico dentro de la ecuación).": "Raw attenuation magnitudes are shown (not the algebraic sign inside the equation).",
    "Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación).": "Raw attenuation magnitudes are shown (not the algebraic sign inside the equation).",
    "Media [dB]": "Mean [dB]",
    "Máximo [dB]": "Maximum [dB]",
    "Adiv (geometrical divergence)": "Adiv (geometrical divergence)",
    "Aatm (atmospheric absorption)": "Aatm (atmospheric absorption)",
    "Agr/Aground (ground effect)": "Agr/Aground (ground effect)",
    "Abar (screening)": "Abar (screening)",

    # Method/report phrases commonly visible in the long HTML report
    "Informe técnico de ruido": "Noise technical report",
    "INFORME TÉCNICO DE IMPACTO ACÚSTICO": "TECHNICAL NOISE IMPACT REPORT",
    "Evaluación de ruido generado por aerogeneradores": "Assessment of wind turbine noise",
    "CONFIGURACIÓN Y PARÁMETROS": "CONFIGURATION AND PARAMETERS",
    "Ecuación Utilizada": "Equation used",
    "Parámetros del Cálculo": "Calculation parameters",
    "Términos Activos": "Active terms",
    "Motor": "Engine",
    "Altura de receptor": "Receiver height",
    "Radio máximo": "Maximum radius",
    "Modo suelo": "Ground mode",
    "G global de respaldo": "Global fallback G",
    "G_eff medio usado": "Mean G_eff used",
    "G_eff critical receiver usado": "Critical-receiver G_eff used",
    "G usado": "G used",
    "G_eff medio": "Mean G_eff",
    "G_eff critical receiver": "Critical-receiver G_eff",
    "Uso del suelo": "Land use",
    "Escenario acústico": "Acoustic scenario",
    "Temperatura": "Temperature",
    "Humedad relativa": "Relative humidity",
    "Presión": "Pressure",
    "activo": "active",
    "no activo": "inactive",
    "no": "no",
    "sí": "yes",
    "desde capa": "from layer",
    "sin nombre": "unnamed",
    "no generado": "not generated",
    "autoajustada": "auto-adjusted",
    "Límites aplicados": "Applied limits",
    "desde campos de receptor": "from receiver fields",
    "valor único": "single value",
    "rango": "range",
    "Límite de referencia": "Reference limit",

    # Layer names shown in the created layers tab
    "Noise · Receivers": "Noise · Receivers",
    "Noise · Sources": "Noise · Sources",
    "Noise · Dominant links": "Noise · Dominant links",
    "Noise · Receivers outside radius": "Noise · Receivers outside radius",
    "Noise · Mapa": "Noise · Map",
    "Noise · Isophones": "Noise · Isophones",

    # Export dialogs/messages
    "Exportar resumen": "Export summary",
    "Exportar receptores": "Export receivers",
    "Exportar grupos fuente": "Export source groups",
    "Exportar excedencias": "Export exceedances",
    "Exportar paquete XLSX": "Export XLSX package",
    "No hay capa de receptores para exportar.": "There is no receiver layer to export.",
    "No hay capa de fuentes para exportar.": "There is no source layer to export.",
    "No hay receptores que superen el límite en este cálculo.": "No receivers exceed the limit in this calculation.",
    "No se pudo exportar el resumen": "Could not export the summary",
    "No se pudo exportar el CSV": "Could not export the CSV",
    "No se pudo exportar el XLSX": "Could not export the XLSX",
}

_TO_EN.update(_I18N_ITER4_NOISE_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER4_NOISE_TO_EN)

_I18N_ITER4_NOISE_TO_ES = {v: k for k, v in _I18N_ITER4_NOISE_TO_EN.items() if v != k}
_I18N_ITER4_NOISE_TO_ES.update({
    "Critical receiver (highest sound level)": "Receptor crítico (mayor nivel sonoro)",
    "Receiver ID": "ID receptor",
    "Total level": "Nivel total",
    "Applicable limit": "Límite aplicable",
    "Margin": "Margen",
    "PASS": "CUMPLE",
    "EXCEEDS": "EXCEDE",
    "Dominant model": "Modelo dominante",
    "Source group": "Grupo fuente",
    "No. turbines": "Nº turbinas",
    "Distance": "Distancia",
    "Attenuation breakdown": "Desglose de atenuaciones",
    "The values shown below are the attenuation magnitudes used by the model. In the main equation these terms are subtracted from the source level.": "Los valores mostrados a continuación son las magnitudes de atenuación usadas por el modelo. En la ecuación principal estos términos se restan al nivel de fuente.",
    "Description": "Descripción",
    "Dominant source LwA": "LwA fuente dominante",
    "Turbine sound power": "Potencia sonora de la turbina",
    "Adiv (divergence)": "Adiv (divergencia)",
    "Geometrical spreading": "Dispersión geométrica",
    "Aatm (atmospheric)": "Aatm (atmosférica)",
    "Air absorption": "Absorción en el aire",
    "Agr (ground)": "Agr (suelo)",
    "Ground-effect attenuation": "Atenuación por efecto del terreno",
    "Abar (screening)": "Abar (apantallamiento)",
    "Additional topographic diffraction attenuation": "Atenuación adicional por difracción topográfica",
    "RESULTING LEVEL": "NIVEL RESULTANTE",
    "Dominant band": "Banda dominante",
    "Spectrum source": "Origen espectro",
    "Attenuation statistics (covered receivers)": "Estadísticos de atenuaciones (receptores cubiertos)",
    "Raw attenuation magnitudes are shown (not the algebraic sign inside the equation).": "Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación).",
    "Mean [dB]": "Media [dB]",
    "Maximum [dB]": "Máximo [dB]",
    "Noise technical report": "Informe técnico de ruido",
    "TECHNICAL NOISE IMPACT REPORT": "INFORME TÉCNICO DE IMPACTO ACÚSTICO",
    "Assessment of wind turbine noise": "Evaluación de ruido generado por aerogeneradores",
    "CONFIGURATION AND PARAMETERS": "CONFIGURACIÓN Y PARÁMETROS",
    "Equation used": "Ecuación utilizada",
    "Calculation parameters": "Parámetros del cálculo",
    "Active terms": "Términos activos",
    "Created layers": "Capas creadas",
    "created": "creada",
    "not created": "no creada",
})
_TO_ES.update(_I18N_ITER4_NOISE_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER4_NOISE_TO_ES)

# ---------------------------------------------------------------------------
# Iteration 5: noise report wording/physics traceability fixes.
# ---------------------------------------------------------------------------
_I18N_ITER5_NOISE_TO_EN = {
    "Receptor crítico (mayor nivel sonoro)": "Critical receiver (highest sound level)",
    "RECEPTOR CRÍTICO": "CRITICAL RECEIVER",
    "Turbinas contribuyentes dentro del radio": "Contributing turbines within radius",
    "Desglose de atenuaciones": "Attenuation breakdown",
    "Receptor crítico no disponible.": "Critical receiver unavailable.",
    "cumplimiento sobre receptores cubiertos": "compliance over covered receivers",
    "Nota: el nivel resultante incluye la suma energética multi-fuente y multi-banda; no es una resta directa de una única turbina.": "Note: the resulting level includes multi-source and multi-band energetic summation; it is not a direct subtraction from one single turbine.",
    "Lectura del receptor crítico": "Critical-receiver reading",
    "la tabla del apartado 2 muestra magnitudes de atenuación para trazabilidad. El nivel resultante no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.": "the table in section 2 shows attenuation magnitudes for traceability. The resulting level should not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and contributing sources within the calculation radius.",
    "Curva acústica global LwA(ws)": "Global acoustic curve LwA(ws)",
    "Capa usada": "Layer used",
    "Interpretación física": "Physical interpretation",
    "receptor crítico": "critical receiver",
    "G_eff receptor crítico": "Critical-receiver G_eff",
    "G_eff receptor crítico usado": "Critical-receiver G_eff used",
    "Limitaciones y recomendaciones": "Limitations and recommendations",
    "Revisión recomendada": "Recommended review",
    "la presión atmosférica introducida": "the entered atmospheric pressure",
    "está fuera del rango típico usado como referencia en muchos estudios preliminares": "is outside the typical range used as a reference in many preliminary studies",
    "Si no es un dato medido del emplazamiento, revisa si debería estar cerca de 101.325 kPa o ajustada por altitud.": "If it is not a measured site value, check whether it should be close to 101.325 kPa or adjusted for altitude.",
    "Presión atmosférica": "Atmospheric pressure",
    "Trayectos con G distinto del global": "Paths with G different from global",
}
_TO_EN.update(_I18N_ITER5_NOISE_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER5_NOISE_TO_EN)
_I18N_ITER5_NOISE_TO_ES = {v: k for k, v in _I18N_ITER5_NOISE_TO_EN.items() if v != k}
_I18N_ITER5_NOISE_TO_ES.update({
    "Critical Receiver (Highest Sound Level)": "Receptor crítico (mayor nivel sonoro)",
    "CRITICAL RECEIVER": "RECEPTOR CRÍTICO",
    "Critical receiver unavailable.": "Receptor crítico no disponible.",
    "Contributing turbines within radius": "Turbinas contribuyentes dentro del radio",
    "Attenuation breakdown": "Desglose de atenuaciones",
    "Critical-receiver reading": "Lectura del receptor crítico",
    "Global acoustic curve LwA(ws)": "Curva acústica global LwA(ws)",
    "Layer used": "Capa usada",
    "Physical interpretation": "Interpretación física",
    "critical receiver": "receptor crítico",
    "Critical-receiver G_eff": "G_eff receptor crítico",
    "Critical-receiver G_eff used": "G_eff receptor crítico usado",
    "Limitations and recommendations": "Limitaciones y recomendaciones",
    "Recommended review": "Revisión recomendada",
})
_TO_ES.update(_I18N_ITER5_NOISE_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER5_NOISE_TO_ES)

# Iteration 5b: atmospheric-pressure UI help.
_I18N_ITER5B_TO_EN = {
    "Presión atmosférica local en kPa. Valor de referencia habitual a nivel del mar: 101.325 kPa. Usa un dato medido o ajustado por altitud si está disponible.": "Local atmospheric pressure in kPa. Usual reference value at sea level: 101.325 kPa. Use a measured or altitude-adjusted value if available.",
    "💡 Usa condiciones típicas del emplazamiento. Si no tienes presión medida, 101.325 kPa es una referencia estándar a nivel del mar; en altitud suele ser menor.": "💡 Use typical site conditions. If you do not have measured pressure, 101.325 kPa is a standard sea-level reference; at altitude it is usually lower.",
}
_TO_EN.update(_I18N_ITER5B_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER5B_TO_EN)
_I18N_ITER5B_TO_ES = {v: k for k, v in _I18N_ITER5B_TO_EN.items()}
_TO_ES.update(_I18N_ITER5B_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER5B_TO_ES)


# ---------------------------------------------------------------------------
# Energy final polish: PyWake docs, superposition, rotor-average and corporate AEP report.
# ---------------------------------------------------------------------------
_I18N_ENERGY_FINAL_TO_EN = {
    "Actualizar": "Refresh",
    "Refresca el estado de capas/modelos detectados en el proyecto.": "Refresh the detected layers/models state in the project.",
    "Documentación PyWake": "PyWake documentation",
    "Abre la documentación oficial de PyWake, base del cálculo AEP y de estelas del módulo de Energía.": "Open the official PyWake documentation, the basis for AEP and wake calculations in the Energy module.",
    "TI ambiente fallback:": "Ambient TI fallback:",
    "TI ambiente constante usada cuando no hay raster TI disponible.": "Constant ambient TI used when no TI raster is available.",
    "Se pasa al Site de PyWake como fracción, por ejemplo 10% = 0.10.": "It is passed to the PyWake Site as a fraction, for example 10% = 0.10.",
    "Modelo rotor-average (PyWake)": "Rotor-average model (PyWake)",
    "¿Qué modelo de rotor-average quieres usar?": "Which rotor-average model do you want to use?",
    "Fast · RotorCenter": "Fast · RotorCenter",
    "Balanced · CGIRotorAvg(7)": "Balanced · CGIRotorAvg(7)",
    "Accurate · CGIRotorAvg(9)": "Accurate · CGIRotorAvg(9)",
    "Very accurate · CGIRotorAvg(21)": "Very accurate · CGIRotorAvg(21)",
        "RotorCenter es rápido; CGIRotorAvg(7) es el equilibrio recomendado; CGIRotorAvg(9/21) aumenta precisión y coste computacional.": "RotorCenter is fast; CGIRotorAvg(7) is the recommended balanced option; CGIRotorAvg(9/21) increases accuracy and computation time.",
    "Sin rotor-average: PyWake evaluará la velocidad efectiva en el centro del rotor.": "No rotor-average: PyWake will evaluate the effective wind speed at the rotor centre.",
    "Fast: RotorCenter es el modo más rápido y recomendado para iteraciones rápidas o mapa interactivo.": "Fast: RotorCenter is the fastest mode and is recommended for quick iterations or the interactive map.",
    "Balanced: CGIRotorAvg(7) es el valor recomendado para cálculo AEP normal.": "Balanced: CGIRotorAvg(7) is the recommended value for normal AEP calculations.",
    "Accurate: CGIRotorAvg(9) aumenta algo la fidelidad y el coste computacional.": "Accurate: CGIRotorAvg(9) slightly increases fidelity and computation time.",
    "Very accurate: CGIRotorAvg(21) puede ser bastante más caro; úsalo para validaciones finales.": "Very accurate: CGIRotorAvg(21) can be much more expensive; use it for final validation.",
    "GaussianOverlapAvgModel solo debe usarse con modelos de déficit gaussianos compatibles.": "GaussianOverlapAvgModel should only be used with compatible Gaussian deficit models.",
    "El rotor-average incrementa la fidelidad al muestrear el disco del rotor, a costa de más tiempo de cálculo.": "Rotor averaging increases fidelity by sampling the rotor disk, at the cost of more computation time.",
    "Modelo de superposición de estelas (PyWake)": "Wake superposition model (PyWake)",
    "¿Cómo se combinan las estelas de varias turbinas?": "How are wakes from several turbines combined?",
    "Ayuda sobre LinearSum, SquaredSum, MaxSum y WeightedSum": "Help about LinearSum, SquaredSum, MaxSum and WeightedSum",
    "Auto recomendado": "Recommended auto",
    "WeightedSum (avanzado · Gaussian)": "WeightedSum (advanced · Gaussian)",
    "Auto recomendado elige una opción segura según wake/rotor-average. LinearSum es conservador; SquaredSum es habitual en arrays densos; WeightedSum se reserva para modelos gaussianos compatibles.": "Recommended auto chooses a safe option based on wake/rotor-average. LinearSum is conservative; SquaredSum is common for dense arrays; WeightedSum is reserved for compatible Gaussian models.",
    "Auto recomendado: el plugin elige una superposición segura según el wake deficit y el rotor-average activo.": "Recommended auto: the plugin chooses a safe superposition according to the active wake deficit and rotor-average model.",
    "LinearSum suma los déficits linealmente; suele ser conservador en parques densos.": "LinearSum adds deficits linearly; it is usually conservative in dense wind farms.",
    "SquaredSum usa raíz de suma de cuadrados; es una opción habitual para arrays densos y evita sumar déficits de forma tan agresiva como LinearSum.": "SquaredSum uses the square root of the sum of squares; it is a common option for dense arrays and avoids adding deficits as aggressively as LinearSum.",
    "MaxSum considera solo el déficit más severo; útil como comprobación robusta, pero puede infraestimar pérdidas acumuladas.": "MaxSum considers only the most severe deficit; useful as a robust check, but it may underestimate cumulative losses.",
    "WeightedSum es avanzado y se reserva para déficits gaussianos compatibles; si PyWake no acepta la combinación, el solver degradará automáticamente.": "WeightedSum is advanced and reserved for compatible Gaussian deficits; if PyWake rejects the combination, the solver will automatically degrade it.",
    "Selecciona cómo se combinan las estelas de varias turbinas antes de calcular la velocidad efectiva.": "Select how wakes from several turbines are combined before calculating the effective wind speed.",
    "PyWake permite varias formas de combinar déficits de velocidad cuando una turbina recibe varias estelas.": "PyWake allows several ways of combining wind-speed deficits when a turbine receives several wakes.",
    "• Auto recomendado: selección segura del plugin.": "• Recommended auto: safe plugin selection.",
    "• LinearSum: suma lineal; conservador en parques densos.": "• LinearSum: linear sum; conservative in dense wind farms.",
    "• SquaredSum: raíz de suma de cuadrados; buena opción práctica para arrays densos.": "• SquaredSum: square root of the sum of squares; a good practical option for dense arrays.",
    "• MaxSum: solo domina la estela más severa; útil para diagnóstico.": "• MaxSum: only the most severe wake dominates; useful for diagnostics.",
    "• WeightedSum: avanzado, pensado para modelos gaussianos compatibles y más sensible al coste/compatibilidad.": "• WeightedSum: advanced, intended for compatible Gaussian models and more sensitive to cost/compatibility.",
    "Para pruebas, usa Auto recomendado o SquaredSum como referencia de sensibilidad.": "For testing, use Recommended auto or SquaredSum as the sensitivity reference.",
    "Nota: con PropagateUpDownIterative se fuerza automáticamente use_effective_ws=True (wake y bloqueo) para evitar errores de compatibilidad.": "Note: with PropagateUpDownIterative, use_effective_ws=True is automatically forced for wake and blockage to avoid compatibility errors.",
    "Exportar informe HTML…": "Export HTML report…",
    "Guarda el resumen AEP con logo Velantis Wind y formato corporativo en un archivo HTML.": "Save the AEP summary with the Velantis Wind logo and corporate formatting as an HTML file.",
    "Exportar informe AEP": "Export AEP report",
    "Todos los archivos": "All files",
    "Informe exportado": "Report exported",
    "Informe AEP exportado correctamente:": "AEP report exported successfully:",
    "No se pudo exportar el informe": "Could not export the report",
    "No se pudo exportar el informe AEP:": "Could not export the AEP report:",
    "AEP technical report": "Informe técnico AEP",
    "Energy module · PyWake workflow": "Módulo Energía · Flujo PyWake",
    "Informe técnico generado desde el módulo Energía de Velantis Wind.": "Technical report generated from the Velantis Wind Energy module.",
    "AEP bruto (free-stream)": "Free-flow AEP",
    "AEP neto (operativo)": "Net AEP (operational)",
    "Desglose de pérdidas": "Loss breakdown",
    "Generated by Velantis Wind · QGIS energy module · PyWake-based AEP workflow.": "Generado por Velantis Wind · Módulo Energía de QGIS · Flujo AEP basado en PyWake.",
    "Aviso de TI ambiente:": "Ambient TI warning:",
    "no se ha proporcionado un raster de TI.": "no TI raster has been provided.",
    "uniforme como fallback.": "uniformly as fallback.",
    "Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.": "For representative results, load a TI raster in the resource section or review the turbulence sensitivity.",
}
_TO_EN.update(_I18N_ENERGY_FINAL_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_FINAL_TO_EN)
_I18N_ENERGY_FINAL_TO_ES = {v: k for k, v in _I18N_ENERGY_FINAL_TO_EN.items() if v != k}
_I18N_ENERGY_FINAL_TO_ES.update({
    "Refresh": "Actualizar",
    "PyWake documentation": "Documentación PyWake",
    "Recommended auto": "Auto recomendado",
    "Wake superposition model (PyWake)": "Modelo de superposición de estelas (PyWake)",
    "Export HTML report…": "Exportar informe HTML…",
    "Export AEP report": "Exportar informe AEP",
    "Report exported": "Informe exportado",
    "AEP report exported successfully:": "Informe AEP exportado correctamente:",
    "Could not export the report": "No se pudo exportar el informe",
    "Could not export the AEP report:": "No se pudo exportar el informe AEP:",
    "Technical report generated from the Velantis Wind Energy module.": "Informe técnico generado desde el módulo Energía de Velantis Wind.",
    "Free-flow AEP": "AEP bruto (free-stream)",
    "Net AEP (operational)": "AEP neto (operativo)",
    "Loss breakdown": "Desglose de pérdidas",
    "Ambient TI warning:": "Aviso de TI ambiente:",
})
_TO_ES.update(_I18N_ENERGY_FINAL_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_FINAL_TO_ES)


# Correct direction for final AEP-report labels that are authored in Spanish.
_I18N_ENERGY_FINAL_FIX_TO_EN = {
    "Informe técnico AEP": "AEP technical report",
    "Módulo Energía · Flujo PyWake": "Energy module · PyWake workflow",
    "Velantis Wind · Informe técnico AEP": "Velantis Wind · AEP technical report",
    "Generado por Velantis Wind · Módulo Energía de QGIS · Flujo AEP basado en PyWake.": "Generated by Velantis Wind · QGIS energy module · PyWake-based AEP workflow.",
}
for _bad in (
    "AEP technical report",
    "Energy module · PyWake workflow",
    "Generated by Velantis Wind · QGIS energy module · PyWake-based AEP workflow.",
):
    _TO_EN.pop(_bad, None)
    _FRAGMENT_TO_EN.pop(_bad, None)
_TO_EN.update(_I18N_ENERGY_FINAL_FIX_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_FINAL_FIX_TO_EN)
_I18N_ENERGY_FINAL_FIX_TO_ES = {v: k for k, v in _I18N_ENERGY_FINAL_FIX_TO_EN.items()}
_TO_ES.update(_I18N_ENERGY_FINAL_FIX_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_FINAL_FIX_TO_ES)


# ---------------------------------------------------------------------------
# Energy UX polish: plain-language help, missing ES/EN labels and CSV/report wording.
# ---------------------------------------------------------------------------
_I18N_ENERGY_UX_POLISH_TO_EN = {
    "Motor de propagación PyWake": "PyWake propagation engine",
    "¿Cómo debe propagar PyWake las estelas entre turbinas?": "How should PyWake propagate wakes between turbines?",
    "Modelo de pérdida por estela (wake deficit)": "Wake-loss model (wake deficit)",
    "¿Cómo quieres calcular la pérdida de viento detrás de cada turbina?": "How do you want to calculate wind-speed loss behind each turbine?",
    "Raster(s) de turbulencia TI (opcional):": "TI turbulence raster(s) (optional):",
    "Alturas de TI por archivo [m]:": "TI heights per file [m]:",
    "Opcional: altura de cada raster TI, ej. 90;120": "Optional: height of each TI raster, e.g. 90;120",
    "TI ambiente manual si no hay raster:": "Manual ambient TI if no raster:",
    "Promedio sobre el rotor (rotor-average)": "Rotor-disk averaging (rotor-average)",
    "¿Cómo debe promediarse el viento dentro del disco del rotor?": "How should wind speed be averaged across the rotor disk?",
    "Combinación de varias estelas (superposición)": "Combination of multiple wakes (superposition)",
    "Si una turbina recibe varias estelas, ¿cómo se suman sus efectos?": "If a turbine receives several wakes, how should their effects be combined?",
    "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)": "Advanced model parameters (optional · keep defaults unless calibrating)",
    "Valor PyWake por defecto": "PyWake default",
    "Parámetros activos:": "Active parameters:",
    "Ayuda · Rotor-average": "Help · Rotor-average",
    "Ayuda · Superposición de estelas": "Help · Wake superposition",
    "Ayuda · Wake effect y modelo de estela": "Help · Wake effect and wake model",
    "Ayuda · Turbulencia TI": "Help · TI turbulence",
    "Resumen AEP del parque": "Wind farm AEP summary",
    "Por aerogenerador": "By turbine",
    "AEP, pérdidas y capacidad global": "AEP, losses and global capacity",
    "Balance total AEP": "Total AEP balance",
    "Comprobación del balance total": "Total balance check",
    "Neto+pérd.": "Net+losses",
    "Pérd. estela [MWh]": "Wake loss [MWh]",
    "Pérd. bloqueo [MWh]": "Blockage loss [MWh]",
    "Pérd. estelas [MWh]": "Wake losses [MWh]",
    "Pérd. estelas [%]": "Wake losses [%]",
    "Pérd. bloqueo [MWh]": "Blockage loss [MWh]",
    "Impacto TI [MWh]": "TI impact [MWh]",
    "P_nom [MW]": "Rated power [MW]",
    "Potencia inst. [MW]": "Installed power [MW]",
    "FC neto [%]": "Net CF [%]",
    "AEP/MW [MWh/MW·año]": "AEP/MW [MWh/MW·year]",
    "Por modelo de aerogenerador": "By turbine model",
    "Informe técnico generado desde el módulo Energía de Velantis Wind.": "Technical report generated from the Velantis Wind Energy module.",
    "Esta tabla replica el resumen por turbina visible en la interfaz. Para análisis GIS/Excel, usa también el CSV por turbina que genera el flujo AEP.": "This table mirrors the per-turbine summary visible in the interface. For GIS/Excel analysis, also use the per-turbine CSV generated by the AEP workflow.",
}
_TO_EN.update(_I18N_ENERGY_UX_POLISH_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_UX_POLISH_TO_EN)
_I18N_ENERGY_UX_POLISH_TO_ES = {v: k for k, v in _I18N_ENERGY_UX_POLISH_TO_EN.items() if v != k}
_I18N_ENERGY_UX_POLISH_TO_ES.update({
    "Choose WRG/ZIP…": "Elegir WRG/ZIP…",
    "Choose TI raster(s)…": "Elegir raster(s) TI…",
    "Clear": "Limpiar",
    "Which model do you want to use?": "¿Cómo debe propagar PyWake las estelas entre turbinas?",
    "Which wake model do you want to use?": "¿Cómo quieres calcular la pérdida de viento detrás de cada turbina?",
    "Advanced model parameters (optional)": "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)",
    "Override physical model parameters. If you leave 'PyWake default', the canonical value for the selected model is used.": "Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.",
    "Override physical model parameters. If you leave ‘PyWake default’, the canonical value for the selected model is used.": "Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.",
    "Default PyWake": "Valor PyWake por defecto",
    "Parameters active:": "Parámetros activos:",
    "Ambient TI fallback:": "TI ambiente manual si no hay raster:",
    "Wake deficit model (PyWake)": "Modelo de pérdida por estela (wake deficit)",
    "PyWake model": "Motor de propagación PyWake",
    "Rotor-average model (PyWake)": "Promedio sobre el rotor (rotor-average)",
    "Wake superposition model (PyWake)": "Combinación de varias estelas (superposición)",
    "Export HTML report…": "Exportar informe HTML…",
    "Close": "Cerrar",
})
_TO_ES.update(_I18N_ENERGY_UX_POLISH_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_UX_POLISH_TO_ES)


_I18N_ENERGY_CSV_BUTTON_TO_EN = {
    "Exportar CSV por turbina…": "Export per-turbine CSV…",
    "Guarda una tabla limpia con una fila por aerogenerador: coordenadas, modelo, AEP neto/bruto y pérdidas principales.": "Save a clean table with one row per turbine: coordinates, model, net/free-flow AEP and main losses.",
    "Exportar CSV por turbina": "Export per-turbine CSV",
    "No hay datos por turbina para exportar.": "There are no per-turbine data to export.",
    "CSV exportado": "CSV exported",
    "CSV por turbina exportado correctamente:": "Per-turbine CSV exported successfully:",
    "No se pudo exportar el CSV por turbina:": "Could not export the per-turbine CSV:",
}
_TO_EN.update(_I18N_ENERGY_CSV_BUTTON_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_CSV_BUTTON_TO_EN)
_I18N_ENERGY_CSV_BUTTON_TO_ES = {v: k for k, v in _I18N_ENERGY_CSV_BUTTON_TO_EN.items() if v != k}
_I18N_ENERGY_CSV_BUTTON_TO_ES.update({
    "Export per-turbine CSV…": "Exportar CSV por turbina…",
    "Export per-turbine CSV": "Exportar CSV por turbina",
    "CSV exported": "CSV exportado",
    "Per-turbine CSV exported successfully:": "CSV por turbina exportado correctamente:",
    "Could not export the per-turbine CSV:": "No se pudo exportar el CSV por turbina:",
})
_TO_ES.update(_I18N_ENERGY_CSV_BUTTON_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_CSV_BUTTON_TO_ES)


# ---------------------------------------------------------------------------
# Energy final UX consistency: remove mixed ES/EN labels, simplify wording and
# hide GaussianOverlap from the experimental workflow.
# ---------------------------------------------------------------------------
_I18N_ENERGY_FINAL_UX2_TO_EN = {
    "Unir recurso": "Merge resource",
    "Grids WAsP/Surfer:": "WAsP/Surfer grids:",
    "Motor de cálculo PyWake": "PyWake calculation engine",
    "¿Cómo se calculan las interacciones entre turbinas?": "How should turbine interactions be calculated?",
    "Modelo de estela: pérdida de velocidad": "Wake model: wind-speed loss",
    "¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?": "How should the wind-speed loss behind each turbine be calculated?",
    "Rápido · RotorCenter": "Fast · RotorCenter",
    "Más realista · CGIRotorAvg(7)": "More realistic · CGIRotorAvg(7)",
    "Más realista · CGIRotorAvg(9)": "More realistic · CGIRotorAvg(9)",
    "Muy realista · CGIRotorAvg(21)": "Very realistic · CGIRotorAvg(21)",
    "Malla uniforme · EqGridRotorAvg": "Uniform grid · EqGridRotorAvg",
    "Automático": "Automatic",
    "WeightedSum (solo gaussianos)": "WeightedSum (Gaussian only)",
    "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)": "Advanced model parameters (optional · keep defaults unless calibrating)",
    "Ayuda · Promedio sobre el rotor": "Help · Rotor-disk averaging",
    "Modelo de estela: pérdida de velocidad": "Wake model: wind-speed loss",
    "Promedio sobre el rotor (rotor-average)": "Rotor-disk averaging (rotor-average)",
    "Combinación de varias estelas (superposición)": "Combination of multiple wakes (superposition)",
    "Modelo de bloqueo (PyWake)": "Blockage model (PyWake)",
    "¿Qué modelo de bloqueo quieres usar?": "Which blockage model should be used?",
    "Modelo de turbulencia (PyWake)": "Turbulence model (PyWake)",
    "¿Qué modelo de turbulencia quieres usar?": "Which turbulence model should be used?",
    "Elegir carpeta…": "Choose folder…",
    "Elegir WRG/ZIP…": "Choose WRG/ZIP…",
    "Elegir raster(s) TI…": "Choose TI raster(s)…",
    "Limpiar": "Clear",
}
_TO_EN.update(_I18N_ENERGY_FINAL_UX2_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_FINAL_UX2_TO_EN)
_I18N_ENERGY_FINAL_UX2_TO_ES = {v: k for k, v in _I18N_ENERGY_FINAL_UX2_TO_EN.items() if v != k}
_I18N_ENERGY_FINAL_UX2_TO_ES.update({
    "WAsP/Surfer grids:": "Grids WAsP/Surfer:",
    "Choose folder...": "Elegir carpeta…",
    "Choose folder…": "Elegir carpeta…",
    "Merge resource": "Unir recurso",
    "Choose WRG/ZIP...": "Elegir WRG/ZIP…",
    "Choose WRG/ZIP…": "Elegir WRG/ZIP…",
    "Choose TI raster(s)...": "Elegir raster(s) TI…",
    "Choose TI raster(s)…": "Elegir raster(s) TI…",
    "Clear": "Limpiar",
    "Manual ambient TI if no raster:": "TI ambiente manual si no hay raster:",
    "TI turbulence raster(s) (optional):": "Raster(s) de turbulencia TI (opcional):",
    "TI heights per file [m]:": "Alturas de TI por archivo [m]:",
    "PyWake propagation engine": "Motor de cálculo PyWake",
    "PyWake calculation engine": "Motor de cálculo PyWake",
    "How should PyWake propagate wakes between turbines?": "¿Cómo se calculan las interacciones entre turbinas?",
    "How should turbine interactions be calculated?": "¿Cómo se calculan las interacciones entre turbinas?",
    "Wake-loss model (wake deficit)": "Modelo de estela: pérdida de velocidad",
    "Wake model: wind-speed loss": "Modelo de estela: pérdida de velocidad",
    "How do you want to calculate wind-speed loss behind each turbine?": "¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?",
    "How should the wind-speed loss behind each turbine be calculated?": "¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?",
    "Advanced model parameters (optional · leave unchanged for normal use)": "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)",
    "Advanced model parameters (optional · keep defaults unless calibrating)": "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)",
    "Fast · RotorCenter": "Rápido · RotorCenter",
    "Balanced · CGIRotorAvg(7)": "Más realista · CGIRotorAvg(7)",
    "Accurate · CGIRotorAvg(9)": "Más realista · CGIRotorAvg(9)",
    "Very accurate · CGIRotorAvg(21)": "Muy realista · CGIRotorAvg(21)",
    "More realistic · CGIRotorAvg(7)": "Más realista · CGIRotorAvg(7)",
    "More realistic · CGIRotorAvg(9)": "Más realista · CGIRotorAvg(9)",
    "Uniform grid · EqGridRotorAvg": "Malla uniforme · EqGridRotorAvg",
    "Auto recommended": "Automático",
    "Recommended auto": "Automático",
    "Automatic": "Automático",
    "WeightedSum (advanced · Gaussian)": "WeightedSum (solo gaussianos)",
    "WeightedSum (Gaussian only)": "WeightedSum (solo gaussianos)",
    "Blockage model (PyWake)": "Modelo de bloqueo (PyWake)",
    "Which blockage model should be used?": "¿Qué modelo de bloqueo quieres usar?",
    "Turbulence model (PyWake)": "Modelo de turbulencia (PyWake)",
    "Which turbulence model should be used?": "¿Qué modelo de turbulencia quieres usar?",
})
_TO_ES.update(_I18N_ENERGY_FINAL_UX2_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_FINAL_UX2_TO_ES)

# ---------------------------------------------------------------------------
# Energy i18n hardening: translate all dynamic help/notes in both directions.
# Added after previous dictionaries intentionally; these maps are read at runtime.
# ---------------------------------------------------------------------------
_I18N_ENERGY_HELP_NOTES_TO_EN = {
    # Resource section labels / buttons / introductory notes
    "Recurso eólico (WAsP grids / WRG)": "Wind resource (WAsP grids / WRG)",
    "Grids WAsP/Surfer:": "WAsP/Surfer grids:",
    "Elegir carpeta...": "Choose folder...",
    "Elegir carpeta…": "Choose folder...",
    "Unir recurso": "Merge resource",
    "Elegir WRG/ZIP...": "Choose WRG/ZIP...",
    "Elegir WRG/ZIP…": "Choose WRG/ZIP...",
    "Elegir raster(s) TI...": "Choose TI raster(s)...",
    "Elegir raster(s) TI…": "Choose TI raster(s)...",
    "Limpiar": "Clear",
    "Raster(s) de turbulencia TI (opcional):": "TI turbulence raster(s) (optional):",
    "Alturas de TI por archivo [m]:": "TI heights per file [m]:",
    "TI ambiente manual si no hay raster:": "Manual ambient TI if no raster:",
    "Opcional: altura de cada raster TI, ej. 90;120": "Optional: height of each TI raster, e.g. 90;120",
    "Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake. Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.": "Select a folder with WAsP/Surfer grids compatible with PyWake. If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.",
    "El recurso eólico describe el viento que llega al parque antes de aplicar estelas: frecuencia por dirección, velocidades y, según el formato, parámetros Weibull. Puedes usar WAsP/Surfer grids o WRG/ZIP. Si aportas varios WRG a distintas alturas, PyWake podrá interpolar por altura cuando sea necesario.": "The wind resource describes the wind arriving at the wind farm before wake effects: directional frequency, wind speeds and, depending on the format, Weibull parameters. You can use WAsP/Surfer grids or WRG/ZIP. If you provide WRG files at several heights, PyWake can interpolate by height when needed.",
    "La TI es la intensidad de turbulencia ambiente: una medida de lo variable o 'mezclado' que llega el viento al rotor. Si no cargas raster TI, se usa el valor manual de arriba como hipótesis uniforme. La TI no siempre cambia mucho el AEP: se nota sobre todo en modelos TI-sensitive como Niayifar, Zong, TurboGaussian o TurboNOJ. Los raster(s) TI se reproyectarán al CRS del proyecto actual": "TI is ambient turbulence intensity: a measure of how variable or mixed the wind is when it reaches the rotor. If you do not load a TI raster, the manual value above is used as a uniform assumption. TI does not always change AEP strongly: it is mainly visible in TI-sensitive models such as Niayifar, Zong, TurboGaussian or TurboNOJ. TI raster(s) will be reprojected to the current project CRS",
    "si hace falta.": "if needed.",
    "Explica qué es la TI, cuándo usar raster de turbulencia y cómo afecta al AEP": "Explains what TI is, when to use a turbulence raster and how it affects AEP",
    "Valor de turbulencia ambiente que se usa si no cargas un raster TI. Ejemplo: 10 % significa TI = 0.10 dentro del Site de PyWake.": "Ambient turbulence value used if no TI raster is loaded. Example: 10% means TI = 0.10 inside the PyWake Site.",

    # Engine/wake model section
    "Motor de cálculo PyWake": "PyWake calculation engine",
    "¿Cómo se calculan las interacciones entre turbinas?": "How should turbine interactions be calculated?",
    "Nota sencilla: este motor calcula interacciones hacia abajo y hacia arriba. Para que PyWake no falle con algunas combinaciones, el plugin activa automáticamente use_effective_ws=True en estelas y bloqueo.": "Simple note: this engine calculates interactions downstream and upstream. To avoid PyWake errors with some combinations, the plugin automatically sets use_effective_ws=True for wake and blockage calculations.",
    "Modelo de estela: pérdida de velocidad": "Wake model: wind-speed loss",
    "¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?": "How should the wind-speed loss behind each turbine be calculated?",
    "Explica qué es el wake effect, cómo entra la TI y por qué algunos modelos son más sensibles a turbulencia": "Explains what the wake effect is, how TI enters the calculation and why some models are more sensitive to turbulence",
    "El wake effect reduce el viento disponible detrás de cada turbina. Cada modelo aproxima esa pérdida de forma distinta.": "The wake effect reduces the wind available behind each turbine. Each model approximates that loss differently.",
    "NOJ/Jensen: modelo clásico y muy rápido. Usa una expansión fija; la TI ambiente suele tener poco efecto directo en el déficit.": "NOJ/Jensen: classic and very fast model. It uses a fixed expansion; ambient TI usually has little direct effect on the deficit.",
    "TurboNOJ: versión ligera tipo NOJ, pero con más sensibilidad a turbulencia y recuperación de estela.": "TurboNOJ: light NOJ-like version, but with more sensitivity to turbulence and wake recovery.",
    "Niayifar: gaussiano TI-sensitive. Recomendable si quieres que el raster TI o la TI manual influyan en la recuperación de estela.": "Niayifar: TI-sensitive Gaussian model. Useful when you want the TI raster or manual TI to influence wake recovery.",
    "Zong: avanzado y sensible a TI. Útil para estudiar near wake y recuperación por turbulencia con más detalle.": "Zong: advanced and TI-sensitive. Useful for studying near wake and turbulence-driven recovery in more detail.",
    "TurboGaussian: gaussiano sensible a TI efectiva. Tiene sentido cuando la turbulencia ambiente y añadida deben influir en el AEP.": "TurboGaussian: Gaussian model sensitive to effective TI. It makes sense when ambient and added turbulence should influence AEP.",
    "Aviso: el modelo es sensible a TI, pero has dejado la turbulencia añadida en Ninguno; se usará la TI ambiente, pero no se sumará TI generada por estelas.": "Warning: the model is TI-sensitive, but added turbulence is set to None; ambient TI will be used, but wake-generated TI will not be added.",
    "Con PropagateDownwind, el bloqueo se desactiva automáticamente.": "With PropagateDownwind, blockage is automatically disabled.",
    "Parámetros activos:": "Active parameters:",
    "valor PyWake por defecto": "PyWake default value",
    "Valor PyWake por defecto": "PyWake default value",
    "por defecto": "default",

    # Advanced parameters
    "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)": "Advanced model parameters (optional · keep defaults unless calibrating)",
    "Estos parámetros modifican la forma de la estela. Déjalos en «PyWake default» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.": "These parameters modify the wake shape. Leave them as “PyWake default” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.",
    "Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.": "These parameters calibrate the wake shape. For normal use, leave them as PyWake default values.",
    "a1 en k = a1·TI + a2": "a1 in k = a1·TI + a2",
    "a2 en k = a1·TI + a2": "a2 in k = a1·TI + a2",
    "δw/D (longitud fuente near-wake)": "δw/D (near-wake source length)",
    "ε coeff": "ε coefficient",
    "λ tip-speed ratio": "λ tip-speed ratio",
    "B número de palas": "B number of blades",
    "A expansión TurboPark": "A TurboPark expansion",

    # Rotor-average / superposition
    "Promedio sobre el rotor (rotor-average)": "Rotor-disk averaging (rotor-average)",
    "¿Cómo debe promediarse el viento dentro del disco del rotor?": "How should wind speed be averaged across the rotor disk?",
    "Explica por qué el rotor no debe tratarse siempre como un solo punto": "Explains why the rotor should not always be treated as a single point",
    "Ninguno": "None",
    "Rápido · RotorCenter": "Fast · RotorCenter",
    "Más realista · CGIRotorAvg(7)": "More realistic · CGIRotorAvg(7)",
    "Más realista · CGIRotorAvg(9)": "More realistic · CGIRotorAvg(9)",
    "Muy realista · CGIRotorAvg(21)": "Very realistic · CGIRotorAvg(21)",
    "Malla uniforme · EqGridRotorAvg": "Uniform grid · EqGridRotorAvg",
    "Sin promedio de rotor: PyWake usa el viento en el centro del rotor. Es lo más simple y rápido, pero pierde detalle si una estela afecta solo a una parte del disco.": "No rotor averaging: PyWake uses the wind speed at the rotor centre. This is the simplest and fastest option, but it loses detail if a wake affects only part of the rotor disk.",
    "RotorCenter: usa un único punto en el centro del rotor. Es rápido para pruebas y mapas, pero no representa la variación de velocidad dentro del disco.": "RotorCenter: uses one single point at the rotor centre. It is fast for tests and maps, but it does not represent wind-speed variation inside the disk.",
    "CGIRotorAvg(7): calcula varios puntos dentro del rotor. Aporta más realismo cuando hay estelas parciales sin aumentar demasiado el coste.": "CGIRotorAvg(7): calculates several points inside the rotor. It adds more realism for partial wakes without increasing computation too much.",
    "CGIRotorAvg(9): usa más puntos que CGI7. Puede aportar algo más de realismo si el tiempo de cálculo sigue siendo asumible.": "CGIRotorAvg(9): uses more points than CGI7. It can add some extra realism if computation time remains acceptable.",
    "CGIRotorAvg(21): usa muchos puntos del rotor. Es más pesado y solo compensa si buscas una sensibilidad de mayor fidelidad.": "CGIRotorAvg(21): uses many rotor points. It is heavier and mainly useful for higher-fidelity sensitivity checks.",
    "Rotor-average significa promediar el viento en varios puntos del rotor en vez de mirar un único punto central. Más puntos = más realismo y más coste.": "Rotor averaging means averaging wind speed at several rotor points instead of looking at one central point. More points = more realism and more computation.",
    "El rotor-average calcula el viento en varios puntos del disco del rotor, no solo en el centro. Más puntos dan más realismo cuando una estela corta parte del rotor, pero aumentan el tiempo de cálculo.": "Rotor averaging calculates wind speed at several points of the rotor disk, not only at the centre. More points add realism when a wake cuts part of the rotor, but increase computation time.",
    "Combinación de varias estelas (superposición)": "Combination of multiple wakes (superposition)",
    "Si una turbina recibe varias estelas, ¿cómo se suman sus efectos?": "If a turbine receives several wakes, how should their effects be combined?",
    "Explica qué pasa cuando varias estelas llegan a la misma turbina": "Explains what happens when several wakes reach the same turbine",
    "Automático": "Automatic",
    "WeightedSum (solo gaussianos)": "WeightedSum (Gaussian only)",
    "Automático: el plugin escoge una opción segura según la combinación de modelos disponible.": "Automatic: the plugin chooses a safe option based on the available model combination.",
    "LinearSum: suma directamente varias pérdidas de velocidad. Puede aumentar las pérdidas en parques densos.": "LinearSum: directly adds several velocity deficits. It can increase losses in dense wind farms.",
    "SquaredSum: combina varias estelas de forma cuadrática. Suele dar un comportamiento más equilibrado cuando varias estelas se solapan.": "SquaredSum: combines several wakes quadratically. It usually gives a more balanced behaviour when several wakes overlap.",
    "MaxSum: usa solo la estela dominante. Sirve para diagnosticar, pero puede quedarse corto si varias estelas se acumulan.": "MaxSum: uses only the dominant wake. Useful for diagnosis, but it can underestimate accumulated losses from several wakes.",
    "WeightedSum: opción avanzada para gaussianos compatibles. Si PyWake rechaza la combinación, el plugin volverá a una opción segura.": "WeightedSum: advanced option for compatible Gaussian models. If PyWake rejects the combination, the plugin will fall back to a safe option.",
    "La superposición dice cómo sumar varias estelas cuando una turbina está detrás de más de una turbina.": "Superposition defines how to add several wakes when one turbine is behind more than one upstream turbine.",
    "La superposición decide cómo sumar varias estelas sobre una misma turbina. SquaredSum suele ser una opción equilibrada; LinearSum acumula más pérdidas; MaxSum solo toma la estela dominante.": "Superposition decides how to combine several wakes on the same turbine. SquaredSum is usually balanced; LinearSum accumulates more losses; MaxSum only takes the dominant wake.",

    # Blockage / turbulence selectors and notes
    "Modelo de bloqueo (PyWake)": "Blockage model (PyWake)",
    "¿Qué modelo de bloqueo quieres usar?": "Which blockage model should be used?",
    "Ayuda sobre cuándo conviene cada modelo de bloqueo y por qué a veces se desactiva automáticamente": "Help on when each blockage model makes sense and why it is sometimes disabled automatically",
    "El bloqueo representa la reducción de velocidad aguas arriba del rotor. Solo se aplica con motores All2AllIterative o PropagateUpDownIterative.": "Blockage represents wind-speed reduction upstream of the rotor. It is only applied with All2AllIterative or PropagateUpDownIterative engines.",
    "<b>Bloqueo desactivado</b> porque el motor es <b>PropagateDownwind</b> (solo propaga estelas hacia abajo, no soporta bloqueo). Si quieres incluir bloqueo, cambia el motor a <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b>.": "<b>Blockage disabled</b> because the engine is <b>PropagateDownwind</b> (it only propagates wakes downstream and does not support blockage). To include blockage, change the engine to <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b>.",
    "<b>Sin bloqueo:</b> el cálculo no incluirá la reducción de velocidad upstream. En parques offshore grandes esto suele subestimar las pérdidas en ~1–3 % AEP.": "<b>No blockage:</b> the calculation will not include upstream wind-speed reduction. In large offshore wind farms this often underestimates losses by about ~1–3% AEP.",
    "<b>Modelo activo:</b>": "<b>Active model:</b>",
    "Modelo activo:": "Active model:",
    "Con PUD se fuerza use_effective_ws=True para evitar errores de PyWake.": "With PUD, use_effective_ws=True is forced to avoid PyWake errors.",
    "SelfSimilarityDeficit2020 — opción principal para bloqueo. No requiere parámetros de usuario en esta versión experimental.": "SelfSimilarityDeficit2020 — main blockage option. It does not require user parameters in this experimental release.",
    "SelfSimilarityDeficit — formulación self-similarity clásica. No requiere parámetros de usuario en esta versión experimental.": "SelfSimilarityDeficit — classic self-similarity formulation. It does not require user parameters in this experimental release.",
    "VortexCylinder — modelo de inducción/bloqueo basado en cilindro de vorticidad. Sin parámetros de usuario en esta versión experimental.": "VortexCylinder — induction/blockage model based on a vortex cylinder. No user parameters in this experimental release.",
    "VortexDipole — alternativa de inducción. Sin parámetros de usuario en esta versión experimental.": "VortexDipole — induction alternative. No user parameters in this experimental release.",
    "HybridInduction — combina aproximaciones de inducción. Sin parámetros de usuario en esta versión experimental.": "HybridInduction — combines induction approximations. No user parameters in this experimental release.",
    "Rathmann — modelo de compatibilidad para configuraciones antiguas. Sin parámetros de usuario en esta versión experimental.": "Rathmann — compatibility model for older settings. No user parameters in this experimental release.",
    "modelo no estándar; revisar disponibilidad en esta versión de PyWake.": "non-standard model; check availability in this PyWake version.",
    "Modelo de turbulencia (PyWake)": "Turbulence model (PyWake)",
    "¿Qué modelo de turbulencia quieres usar?": "Which turbulence model should be used?",
    "Ayuda sobre TI ambiente, turbulencia añadida y cuándo influye realmente en el AEP": "Help on ambient TI, added turbulence and when it really affects AEP",
    "Este selector calcula turbulencia añadida por estela. No sustituye a la TI ambiente del site/WRG.": "This selector calculates wake-added turbulence. It does not replace the ambient TI from the site/WRG.",
    "<b>Sin turbulencia añadida:</b> el cálculo usa la TI ambiente del recurso (WRG/raster TI o valor manual), pero no suma la turbulencia creada por las estelas. Esto es válido para modelos poco sensibles a TI; si usas Niayifar, Zong, TurboGaussian o TurboNOJ, la turbulencia añadida puede afectar más al AEP.": "<b>No added turbulence:</b> the calculation uses ambient TI from the resource (WRG/TI raster or manual value), but it does not add turbulence created by wakes. This is valid for models with low TI sensitivity; if you use Niayifar, Zong, TurboGaussian or TurboNOJ, added turbulence can affect AEP more.",
    "STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Coeficientes empíricos fijos del paper. <b>Recomendado por defecto</b>.": "STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Fixed empirical coefficients from the paper. <b>Recommended default</b>.",
    "STF2005 — Frandsen original. Coeficientes empíricos fijos. Útil para comparar con configuraciones antiguas o más conservadoras.": "STF2005 — original Frandsen model. Fixed empirical coefficients. Useful for comparison with older or more conservative settings.",
    "GCLTurbulence — derivado Frandsen, formulación clásica. Constantes fijas.": "GCLTurbulence — Frandsen-derived classic formulation. Fixed constants.",
    "CrespoHernandez — Crespo &amp; Hernández 1996, ajuste empírico clásico. Constantes fijas del paper.": "CrespoHernandez — Crespo &amp; Hernández 1996, classic empirical fit. Fixed constants from the paper.",
    "La TI ambiente siempre viene del recurso/raster o del valor manual. Este selector añade turbulencia generada por estelas. Su impacto en AEP será mayor si el wake model usa TI de forma explícita, como Niayifar, Zong, TurboGaussian o TurboNOJ.": "Ambient TI always comes from the resource/raster or the manual value. This selector adds wake-generated turbulence. Its AEP impact will be larger if the wake model uses TI explicitly, such as Niayifar, Zong, TurboGaussian or TurboNOJ.",

    # Action buttons
    "Acciones": "Actions",
    "Generar capas de puntos": "Generate point layers",
    "Mapa interactivo": "Interactive map",
    "Exportar layout editado...": "Export edited layout...",
    "Exportar layout editado…": "Export edited layout...",
    "Graficar estelas": "Plot wakes",
    "Calcular AEP": "Calculate AEP",
    "Guardar escenario A": "Save scenario A",
    "Guardar escenario B": "Save scenario B",
    "Comparar A/B": "Compare A/B",
    "Dibuja 4 mapas de estelas (N/E/S/O) usando los modelos y el recurso seleccionado (carpeta WAsP o WRG/ZIP).": "Draws 4 wake maps (N/E/S/W) using the selected models and wind resource (WAsP folder or WRG/ZIP).",
    "Guardar a CSV los puntos editados en el mapa interactivo.\nDefault: <original>_edited.csv (no sobrescribe el CSV original).": "Save the points edited in the interactive map to CSV.\nDefault: <original>_edited.csv (does not overwrite the original CSV).",
    "Compara los dos escenarios guardados y muestra delta global y por modelo.": "Compares the two saved scenarios and shows global and per-model deltas.",
}
_TO_EN.update(_I18N_ENERGY_HELP_NOTES_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ENERGY_HELP_NOTES_TO_EN)
_I18N_ENERGY_HELP_NOTES_TO_ES = {v: k for k, v in _I18N_ENERGY_HELP_NOTES_TO_EN.items() if v != k}
_I18N_ENERGY_HELP_NOTES_TO_ES.update({
    # Common English strings as they may appear from older saved builds or Qt native text
    "Wind resource (WAsP grids / WRG)": "Recurso eólico (WAsP grids / WRG)",
    "WAsP/Surfer grids:": "Grids WAsP/Surfer:",
    "Choose folder...": "Elegir carpeta...",
    "Choose folder…": "Elegir carpeta...",
    "Merge resource": "Unir recurso",
    "Choose WRG/ZIP...": "Elegir WRG/ZIP...",
    "Choose WRG/ZIP…": "Elegir WRG/ZIP...",
    "Choose TI raster(s)...": "Elegir raster(s) TI...",
    "Choose TI raster(s)…": "Elegir raster(s) TI...",
    "Clear": "Limpiar",
    "TI turbulence raster(s) (optional):": "Raster(s) de turbulencia TI (opcional):",
    "TI heights per file [m]:": "Alturas de TI por archivo [m]:",
    "Manual ambient TI if no raster:": "TI ambiente manual si no hay raster:",
    "PyWake calculation engine": "Motor de cálculo PyWake",
    "How should turbine interactions be calculated?": "¿Cómo se calculan las interacciones entre turbinas?",
    "Wake model: wind-speed loss": "Modelo de estela: pérdida de velocidad",
    "How should the wind-speed loss behind each turbine be calculated?": "¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?",
    "Advanced model parameters (optional · keep defaults unless calibrating)": "Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)",
    "Rotor-disk averaging (rotor-average)": "Promedio sobre el rotor (rotor-average)",
    "How should wind speed be averaged across the rotor disk?": "¿Cómo debe promediarse el viento dentro del disco del rotor?",
    "Combination of multiple wakes (superposition)": "Combinación de varias estelas (superposición)",
    "If a turbine receives several wakes, how should their effects be combined?": "Si una turbina recibe varias estelas, ¿cómo se suman sus efectos?",
    "Blockage model (PyWake)": "Modelo de bloqueo (PyWake)",
    "Which blockage model should be used?": "¿Qué modelo de bloqueo quieres usar?",
    "Turbulence model (PyWake)": "Modelo de turbulencia (PyWake)",
    "Which turbulence model should be used?": "¿Qué modelo de turbulencia quieres usar?",
    "None": "Ninguno",
    "Fast · RotorCenter": "Rápido · RotorCenter",
    "More realistic · CGIRotorAvg(7)": "Más realista · CGIRotorAvg(7)",
    "More realistic · CGIRotorAvg(9)": "Más realista · CGIRotorAvg(9)",
    "Very realistic · CGIRotorAvg(21)": "Muy realista · CGIRotorAvg(21)",
    "Uniform grid · EqGridRotorAvg": "Malla uniforme · EqGridRotorAvg",
    "Automatic": "Automático",
    "WeightedSum (Gaussian only)": "WeightedSum (solo gaussianos)",
    "PyWake default value": "Valor PyWake por defecto",
    "Active parameters:": "Parámetros activos:",
})
_TO_ES.update(_I18N_ENERGY_HELP_NOTES_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ENERGY_HELP_NOTES_TO_ES)


# ---------------------------------------------------------------------------
# Iteration 6: remaining UI strings (Energy help/tooltips/messages, Noise
# page+validation+results, resource-boundary feature, deps). ES<->EN.
# ---------------------------------------------------------------------------
_I18N_ITER6_TO_EN = {
    '<b>Qué es</b><br><br>Una turbina no ve exactamente el mismo viento en todo el rotor. La parte alta, baja, izquierda o derecha del disco puede recibir velocidades distintas, sobre todo si una estela solo atraviesa una parte del rotor.<br><br><b>Qué hace esta opción</b><br>• <b>Sin promedio / RotorCenter</b>: usa solo el centro del rotor. Es rápido, pero más simplificado.<br>• <b>CGIRotorAvg(7)</b>: calcula varios puntos dentro del disco. Aporta más realismo con un coste moderado.<br>• <b>CGIRotorAvg(9/21)</b>: usa más puntos. Puede aportar más fidelidad, pero aumenta el tiempo de cálculo.<br><br><b>Idea sencilla</b><br>Si una estela corta solo media turbina, mirar únicamente el centro puede ser demasiado simplificado. Promediar el rotor ayuda a representar mejor ese efecto.': '<b>What it is</b><br><br>A turbine does not see exactly the same wind across the whole rotor. The top, bottom, left or right of the disk can receive different wind speeds, especially when a wake crosses only part of the rotor.<br><br><b>What this option does</b><br>• <b>No averaging / RotorCenter</b>: uses only the rotor centre. It is fast, but more simplified.<br>• <b>CGIRotorAvg(7)</b>: calculates several points inside the disk. It adds more realism at a moderate cost.<br>• <b>CGIRotorAvg(9/21)</b>: uses more points. It can add more fidelity, but increases computation time.<br><br><b>Simple idea</b><br>If a wake cuts only half the turbine, looking only at the centre can be too simplified. Averaging the rotor helps to represent that effect better.',
    '<b>Qué problema resuelve</b><br><br>Una turbina puede recibir la estela de varias turbinas al mismo tiempo. La superposición decide cómo se combinan esas pérdidas de velocidad antes de calcular la potencia.<br><br>• <b>Automático</b>: el plugin escoge una opción segura según la combinación de modelos.<br>• <b>LinearSum</b>: suma directa. Puede acumular muchas pérdidas en parques densos.<br>• <b>SquaredSum</b>: suma cuadrática. Suele ser una opción equilibrada cuando varias estelas se solapan.<br>• <b>MaxSum</b>: usa solo la estela más fuerte. Útil para diagnóstico, pero puede infravalorar pérdidas acumuladas.<br>• <b>WeightedSum</b>: opción avanzada para modelos gaussianos compatibles.<br><br><b>Idea sencilla</b><br>Si dos turbinas frenan el viento antes de llegar a una tercera, hay que decidir si esas pérdidas se suman mucho, poco o solo domina una de ellas.': '<b>What problem it solves</b><br><br>A turbine can receive the wake of several turbines at the same time. Superposition decides how those wind-speed losses are combined before calculating power.<br><br>• <b>Automatic</b>: the plugin chooses a safe option based on the model combination.<br>• <b>LinearSum</b>: direct sum. It can accumulate large losses in dense wind farms.<br>• <b>SquaredSum</b>: quadratic sum. It is usually a balanced option when several wakes overlap.<br>• <b>MaxSum</b>: uses only the strongest wake. Useful for diagnostics, but it can underestimate accumulated losses.<br>• <b>WeightedSum</b>: advanced option for compatible Gaussian models.<br><br><b>Simple idea</b><br>If two turbines slow the wind before it reaches a third one, you must decide whether those losses add up strongly, weakly, or whether only one of them dominates.',
    'Explica qué cambia cada parámetro sin asumir conocimiento previo': 'Explains what each parameter changes, without assuming prior knowledge',
    'Explica qué es una estela y qué cambia entre modelos': 'Explains what a wake is and what changes between models',
    'Modelo que estima cuánto viento pierde una turbina cuando está dentro de la estela de otra. Algunos modelos usan la turbulencia TI y otros apenas la notan.': "Model that estimates how much wind a turbine loses when it is inside another turbine's wake. Some models use TI turbulence and others barely notice it.",
    'Perímetro del recurso': 'Resource boundary',
    'Capas para estelas': 'Layers for wakes',
    'Dibuja una capa temporal con los límites del WRG o de los grids WAsP/Surfer. Sirve para comprobar si el layout queda dentro del dominio del recurso.': 'Draws a temporary layer with the boundaries of the WRG or the WAsP/Surfer grids. It is used to check whether the layout lies inside the resource domain.',
    'Vuelve a dibujar el perímetro del recurso eólico seleccionado.': 'Redraws the boundary of the selected wind resource.',
    'Elimina del proyecto la capa temporal del perímetro del recurso.': 'Removes the temporary resource-boundary layer from the project.',
    "Activa 'Mostrar perímetro del recurso en el mapa' para dibujarlo.": "Enable 'Show resource boundary on the map' to draw it.",
    'No había perímetro del recurso que limpiar.': 'There was no resource boundary to clear.',
    'Perímetro del recurso eliminado del mapa.': 'Resource boundary removed from the map.',
    'Perímetro del recurso no disponible en esta instalación.': 'Resource boundary not available in this installation.',
    'No se pudo limpiar el perímetro del recurso:': 'Could not clear the resource boundary:',
    'Ver la curva de potencia (y Ct si está disponible) de la turbina definida.\nÚtil para detectar errores de carga (kW vs W, columnas invertidas, etc.).': 'View the power curve (and Ct if available) of the defined turbine.\nUseful to detect loading errors (kW vs W, swapped columns, etc.).',
    'Esta turbina no tiene la curva guardada en metadatos. Vuelve a pulsar «Definir…» para regenerarla.': 'This turbine has no curve stored in its metadata. Press “Define…” again to regenerate it.',
    'Altura manual de cada raster TI, en el mismo orden que los archivos. Útil si el nombre del archivo no indica claramente 90 m, 120 m, etc.': 'Manual height for each TI raster, in the same order as the files. Useful when the file name does not clearly indicate 90 m, 120 m, etc.',
    'Has definido varios hub heights pero no has seleccionado raster(s) TI. El cálculo seguirá con TI uniforme =': 'You have defined several hub heights but have not selected TI raster(s). The calculation will continue with a uniform TI =',
    '%. Es válido para una prueba, pero no representa variación vertical de turbulencia.': '%. It is valid for a test, but it does not represent vertical variation of turbulence.',
    ', sin altura)': ', no height)',
    '. El plugin aplicará esa TI a todas las alturas como aproximación.': '. The plugin will apply that TI to all heights as an approximation.',
    'Se han seleccionado varios raster(s) TI, pero no he podido identificar al menos dos alturas distintas en sus nombres. El plugin no podrá reconstruir una TI vertical fiable; revisa nombres como TImean.90.asc o TI_120m.tif.': 'Several TI raster(s) were selected, but at least two different heights could not be identified in their names. The plugin will not be able to reconstruct a reliable vertical TI; check names such as TImean.90.asc or TI_120m.tif.',
    'Raster(s) TI no seleccionado(s) → fallback automático a': 'TI raster(s) not selected → automatic fallback to',
    'No hay capas de turbinas válidas con puntos para calcular.\n\nCrea una capa con «Generar capas de puntos», define su modelo de turbina y añade al menos un aerogenerador en el mapa.': 'There are no valid turbine layers with points to calculate.\n\nCreate a layer with “Generate point layers”, define its turbine model and add at least one turbine on the map.',
    'No encuentro una capa de turbinas «{Modelo} (CSV)» para editar.\n\nPulsa primero «Generar capas de puntos» o carga una capa del modelo en el proyecto.': 'No turbine layer “{Modelo} (CSV)” was found to edit.\n\nPress “Generate point layers” first, or load a model layer into the project.',
    'Has movido/añadido/borrado turbinas en el mapa interactivo.\nEl cálculo de AEP usa esas coordenadas vivas (no el CSV original).\nPulsa «Exportar layout editado…» si quieres persistirlas a CSV.': 'You have moved/added/deleted turbines in the interactive map.\nThe AEP calculation uses those live coordinates (not the original CSV).\nPress “Export edited layout…” if you want to persist them to CSV.',
    'Guarda primero un escenario A y un escenario B.\n\nFlujo recomendado: calcula el caso base → Guardar escenario A → cambia layout/modelo/física → calcula otra vez → Guardar escenario B → Comparar A/B.': 'Save scenario A and scenario B first.\n\nRecommended workflow: calculate the base case → Save scenario A → change layout/model/physics → calculate again → Save scenario B → Compare A/B.',
    'Selecciona al menos una capa para calcular.': 'Select at least one layer to calculate.',
    'No hay layouts editados pendientes de exportar.': 'There are no edited layouts pending export.',
    'No se pudo cargar la herramienta de mapa interactivo (import fallido).': 'Could not load the interactive map tool (import failed).',
    'No se pudo crear la capa del modelo': 'Could not create the model layer',
    'No se pudo generar la capa de puntos:': 'Could not generate the point layer:',
    'No se pudo preparar la selección de capas:': 'Could not prepare the layer selection:',
    'Ya has alcanzado el máximo de': 'You have already reached the maximum of',
    'modelos. Aumenta el límite en el código si necesitas más familias de aerogeneradores.': 'models. Increase the limit in the code if you need more turbine families.',
    'sin extensión': 'no extension',
    'BastankhahGaussianDeficit usa una k fija: la TI ambiente y el modelo de turbulencia apenas cambian el déficit base del wake.': 'BastankhahGaussianDeficit uses a fixed k: ambient TI and the turbulence model barely change the base wake deficit.',
    'GaussianOverlapAvgModel se ha sustituido por CGIRotorAvg(7) por compatibilidad.': 'GaussianOverlapAvgModel was replaced by CGIRotorAvg(7) for compatibility.',
    'Has seleccionado PropagateDownwind: PyWake no calcula bloqueo con este engine y el plugin lo ha desactivado automáticamente.': 'You selected PropagateDownwind: PyWake does not calculate blockage with this engine and the plugin has disabled it automatically.',
    'Hay varios hub heights pero solo una TI ambiente verticalmente uniforme; el cálculo sigue siendo válido, aunque es una aproximación en altura.': 'There are several hub heights but only a vertically uniform ambient TI; the calculation is still valid, although it is an approximation in height.',
    'La simulación principal no pudo ejecutarse con la combinación pedida y PyWake requirió la degradación automática:': 'The main simulation could not run with the requested combination and PyWake required automatic degradation:',
    'NOJDeficit usa una expansión top-hat constante: el modelo de turbulencia no cambia el AEP en la configuración estándar.': 'NOJDeficit uses a constant top-hat expansion: the turbulence model does not change AEP in the standard configuration.',
    'No se pudo instanciar el modelo de turbulencia seleccionado; el plugin ha intentado un fallback seguro (STF2017/STF2005/GCL).': 'The selected turbulence model could not be instantiated; the plugin attempted a safe fallback (STF2017/STF2005/GCL).',
    'Sustitución del modelo de bloqueo: la combinación seleccionada con': 'Blockage-model substitution: the selected combination with',
    'WRG: no se ha proporcionado raster TI, así que la TI ambiente se ha fijado al valor fallback definido por el usuario como campo uniforme.': 'WRG: no TI raster was provided, so the ambient TI was set to the user-defined fallback value as a uniform field.',
    'WRG: se ha usado un único raster de TI ambiente; si mezclas varios hub heights, esa TI se replica/interpela como aproximación vertical.': 'WRG: a single ambient TI raster was used; if you mix several hub heights, that TI is replicated/interpolated as a vertical approximation.',
    'ZongGaussianDeficit requiere un modelo de turbulencia añadida; se ha forzado STF2017.': 'ZongGaussianDeficit requires an added-turbulence model; STF2017 was forced.',
    'como modelo alternativo de acoplamiento más débil con la TI efectiva. Esto preserva la partida de pérdidas por bloqueo manteniendo intactos wake, rotor-average y modelo de turbulencia.': 'as an alternative model with weaker coupling to the effective TI. This preserves the blockage-loss component while keeping wake, rotor-average and the turbulence model intact.',
    'no convergió en PyWake (la iteración entre wake TI-driven y bloqueo SS2020 no siempre alcanza un punto fijo estable). Se ha mantenido el bloqueo usando': 'did not converge in PyWake (the iteration between a TI-driven wake and SS2020 blockage does not always reach a stable fixed point). Blockage was kept using',
    'puede ser sensible a la TI, pero se ha ejecutado sin modelo de turbulencia añadida; la sensibilidad queda limitada a la TI ambiente disponible.': 'can be sensitive to TI, but it ran without an added-turbulence model; the sensitivity is limited to the available ambient TI.',
    'puede usar la TI ambiente/efectiva para abrir la estela; el modelo de turbulencia activo (': 'can use ambient/effective TI to open the wake; the active turbulence model (',
    'raster(s) de TI ambiente para reconstruir TI por altura cuando es posible.': 'ambient TI raster(s) to reconstruct TI by height when possible.',
    'sin bloqueo + sin turbulencia': 'no blockage + no turbulence',
    'sin rotor-average + sin bloqueo': 'no rotor-average + no blockage',
    'sin rotor-average + sin bloqueo + sin turbulencia': 'no rotor-average + no blockage + no turbulence',
    'sin rotor-average + sin turbulencia': 'no rotor-average + no turbulence',
    'Cerrar esta ventana y volver al diálogo principal sin crear la capa.': 'Close this window and return to the main dialog without creating the layer.',
    'Plantillas de screening: rellenan D, hub, potencia nominal y una curva genérica editable. No sustituyen la curva oficial del fabricante.': 'Screening templates: they fill in D, hub height, rated power and a generic editable curve. They do not replace the official manufacturer curve.',
    'Cálculo AEP': 'AEP calculation',
    'Cálculo con': 'Calculation with',
    'La capa seleccionada': 'The selected layer',
    'No se pudo crear la capa resumen:': 'Could not create the summary layer:',
    'No se pudo mostrar el resumen visual de resultados:': 'Could not display the visual results summary:',
    'No se pudo usar la selección de capas; se usará el flujo normal:': 'Could not use the layer selection; the normal workflow will be used:',
    'Validación AEP': 'AEP validation',
    'Validación avanzada no disponible; se usa flujo de compatibilidad:': 'Advanced validation not available; the compatibility workflow is used:',
    'capa(s) seleccionada(s) desde el mapa interactivo.': 'layer(s) selected from the interactive map.',
    ': falta definir la turbina/curva de potencia.': ': the turbine / power curve is not defined.',
    ': faltan coordenadas. Selecciona CSV o añade puntos en el mapa.': ': coordinates are missing. Select a CSV or add points on the map.',
    'Extensión WRG no soportada. Usa .wrg o .zip:': 'Unsupported WRG extension. Use .wrg or .zip:',
    'No se ha definido ningún modelo de aerogenerador.': 'No turbine model has been defined.',
    'No se ha seleccionado raster TI para WRG; se usará TI fija/fallback si el modelo lo necesita.': 'No TI raster was selected for WRG; a fixed/fallback TI will be used if the model needs it.',
    'PropagateDownwind no aplica bloqueo; el cálculo lo desactivará automáticamente.': 'PropagateDownwind does not apply blockage; the calculation will disable it automatically.',
    'No hay turbina cerca del clic.': 'There is no turbine near the click.',
    'No se pudo añadir turbina:': 'Could not add turbine:',
    'No se pudo borrar turbina:': 'Could not delete turbine:',
    'Ya existe una turbina en ese punto.': 'There is already a turbine at that point.',
    "<span style='color:#a85a00;'><b>Sin capa activa</b></span> de turbinas «<i>Modelo (CSV)</i>».<br>Selecciónala en el panel de capas, o usa «Generar capas de puntos» en el diálogo.": "<span style='color:#a85a00;'><b>No active layer</b></span> of turbines “<i>Model (CSV)</i>”.<br>Select it in the layers panel, or use “Generate point layers” in the dialog.",
    'Actualizar lista de capas de turbinas editables.': 'Refresh the list of editable turbine layers.',
    'Calcula AEP con la capa de turbinas activa y los modelos seleccionados.': 'Calculate AEP with the active turbine layer and the selected models.',
    'Deja la lista de raster(s) TI vacía.': 'Leaves the TI raster(s) list empty.',
    'Dibuja mapas de estelas (N/E/S/O) con los modelos seleccionados.': 'Draws wake maps (N/E/S/W) with the selected models.',
    'Elige qué capa/modelo editas con el click izquierdo/derecho del mapa. Esto permite modificar cualquiera de las capas cargadas, no solo la última creada.': 'Choose which layer/model you edit with the left/right click on the map. This lets you modify any of the loaded layers, not only the last one created.',
    'Exporta los puntos editados de la capa activa a CSV. No sobrescribe el CSV original sin confirmar.': 'Exports the edited points of the active layer to CSV. It does not overwrite the original CSV without confirmation.',
    'Guarda el último cálculo como escenario A.': 'Saves the latest calculation as scenario A.',
    'Guarda el último cálculo como escenario B.': 'Saves the latest calculation as scenario B.',
    'No se pudo activar la capa seleccionada:': 'Could not activate the selected layer:',
    'Salir del modo interactivo (ESC también funciona).': 'Exit interactive mode (ESC also works).',
    'Selecciona uno o varios raster(s) .asc/.tif de TI ambiente (p.ej. de Vortex). Si no hay ninguno, se usa TI=10% como fallback.': 'Select one or more ambient-TI .asc/.tif raster(s) (e.g. from Vortex). If none is provided, TI=10% is used as fallback.',
    'Sin capas editables': 'No editable layers',
    'Vuelve al diálogo principal de Velantis Wind sin perder la edición.': 'Return to the main Velantis Wind dialog without losing your edits.',
    'Vuelve al diálogo principal sin salir del modo interactivo.': 'Return to the main dialog without leaving interactive mode.',
    'falló:': 'failed:',
    'más)</span>': 'more)</span>',
    'EPSG no válido. Se seguirá usando EPSG:': 'Invalid EPSG. EPSG will keep being used:',
    'No se seleccionó carpeta de salida.': 'No output folder was selected.',
    "Se generará UNA carpeta con GRD mosaico (Sector/Altura/Variable).\nSelecciona 2 o más carpetas de tiles exportadas desde WAsP.\n• 'Sector All': se tomarán TODAS las variables presentes (mosaico por altura).\n• Además, se crea subcarpeta 'pywake_compat' estricta (sin necesidad de parches).\n• Salida en Surfer ASCII (DSAA) con formato WAsP.": "ONE folder with a GRD mosaic (Sector/Height/Variable) will be generated.\nSelect 2 or more tile folders exported from WAsP.\n• 'Sector All': ALL present variables will be taken (mosaic by height).\n• In addition, a strict 'pywake_compat' subfolder is created (no patches needed).\n• Output in Surfer ASCII (DSAA) with WAsP format.",
    'Cálculo acústico completado en segundo plano.\n\nLas capas de receptores/fuentes y el raster, si estaba activado, ya se han añadido al proyecto.': 'Acoustic calculation completed in the background.\n\nThe receiver/source layers, and the raster if it was enabled, have already been added to the project.',
    'Cálculo acústico completado.\n\nLas capas de receptores/fuentes ya se han añadido al proyecto. Si el raster estaba activado y no usa uso del suelo, se está generando en segundo plano cuando es seguro hacerlo.': 'Acoustic calculation completed.\n\nThe receiver/source layers have already been added to the project. If the raster was enabled and does not use land use, it is being generated in the background when it is safe to do so.',
    'No se pudo calcular el ruido en segundo plano:': 'Could not calculate noise in the background:',
    'No se pudo calcular el ruido:': 'Could not calculate noise:',
    'No se pudo lanzar el cálculo en segundo plano:': 'Could not start the background calculation:',
    'No se pudo mostrar el resumen visual de ruido:': 'Could not display the visual noise summary:',
    'No se pudo preparar el cálculo en segundo plano:': 'Could not prepare the background calculation:',
    'celdas | resolución pedida': 'cells | requested resolution',
    'receptor(es) con G distinto del global y': 'receiver(s) with G different from the global value and',
    '• AVISO: se solicitó el raster pero no se pudo generar. Revisa la resolución, el CRS y el extent de las fuentes.': '• WARNING: the raster was requested but could not be generated. Check the resolution, the CRS and the source extent.',
    '• AVISO: todos los receptores han quedado fuera del radio máximo. Revisa radio, layout o ubicación de receptores.': '• WARNING: all receivers ended up outside the maximum radius. Check the radius, layout or receiver locations.',
    '• Aground representa la pérdida simplificada asociada al efecto suelo/terreno en el método acústico.': '• Aground represents the simplified loss associated with the ground/terrain effect in the acoustic method.',
    '• Isófonas: se generarán al finalizar el raster en segundo plano.': '• Isophones: they will be generated once the background raster is finished.',
    "• Raster de ruido: lanzado en segundo plano con QgsTask. Los resultados por receptor ya están disponibles; la capa 'Noise · Map' se añadirá al terminar.": "• Noise raster: launched in the background with QgsTask. Per-receiver results are already available; the 'Noise · Map' layer will be added when it finishes.",
    'Espectro vacío': 'Empty spectrum',
    'Valor inválido en banda': 'Invalid value in band',
    '): altura negativa no válida.': '): invalid negative height.',
    '): límites negativos no válidos.': '): invalid negative limits.',
    ': falta el nombre de capa.': ': the layer name is missing.',
    "' está vacía.": "' is empty.",
    ': LwA fijo no válido.': ': invalid fixed LwA.',
    ': altura de buje no válida.': ': invalid hub height.',
    ': modo curva activo pero sin CSV de curva acústica.': ': curve mode active but no acoustic-curve CSV.',
    ": no existe la curva acústica '": ": the acoustic curve does not exist '",
    'El MDT/DSM no está en el mismo CRS que el proyecto.': 'The DTM/DSM is not in the same CRS as the project.',
    'El MDT/DSM seleccionado no es raster válido y se ignorará.': 'The selected DTM/DSM is not a valid raster and will be ignored.',
    'El coeficiente α no es válido.': 'The α coefficient is not valid.',
    'El factor de suelo G debe estar entre 0 y 1.': 'The ground factor G must be between 0 and 1.',
    'El límite acústico no es válido.': 'The acoustic limit is not valid.',
    'El radio máximo debe ser mayor que 0.': 'The maximum radius must be greater than 0.',
    'La altura de receptor no es válida.': 'The receiver height is not valid.',
    "La capa de receptores '": "The receiver layer '",
    'La capa de receptores no está en el mismo CRS que el proyecto.': 'The receiver layer is not in the same CRS as the project.',
    'La capa de suelo no es vectorial válida y se ignorará.': 'The ground layer is not a valid vector layer and will be ignored.',
    'La capa de uso del suelo debe ser poligonal.': 'The land-use layer must be polygonal.',
    'La capa de uso del suelo no está en el mismo CRS que el proyecto.': 'The land-use layer is not in the same CRS as the project.',
    'La distancia mínima debe ser mayor que 0.': 'The minimum distance must be greater than 0.',
    'La resolución del raster debe ser mayor que 0.': 'The raster resolution must be greater than 0.',
    'Selecciona una capa válida de receptores.': 'Select a valid receiver layer.',
    '(se muestreará terreno fuente/receptor en el método acústico).': '(source/receiver terrain will be sampled in the acoustic method).',
    ': LwA fijo inválido.': ': invalid fixed LwA.',
    ': capa de receptores no válida.': ': invalid receiver layer.',
    ": la capa '": ": the layer '",
    'Desde capa de uso del suelo': 'From land-use layer',
    'Modo suelo desde capa activo sin capa válida: will use G global como respaldo.': 'Ground-from-layer mode active without a valid layer: the global G will be used as fallback.',
    'Método ISO-aligned: ISO 9613-2 con bandas de octava (Adiv + Aatm(T,RH,P) + Agr(f))': 'ISO-aligned method: ISO 9613-2 with octave bands (Adiv + Aatm(T,RH,P) + Agr(f))',
    'No hay capas activas de receptores por categoría.': 'There are no active receiver layers by category.',
    'Selecciona qué campo de límite se aplica a cada receptor. No cambia la física del ruido, solo el criterio de evaluación.': 'Select which limit field applies to each receiver. It does not change the noise physics, only the evaluation criterion.',
    'Selecciona una o varias capas fuente detectadas desde Energía. Si marcas varias, el cálculo de ruido usará todas ellas a la vez.': 'Select one or more source layers detected from Energy. If you select several, the noise calculation will use all of them at once.',
    'Sin layout detectado en el grupo de coordenadas por modelo': 'No layout detected in the per-model coordinate group',
    'grupo(s) con LwA válido.': 'group(s) with a valid LwA.',
    'nombre(s) duplicados. Conviene renombrarlos para exportación y trazabilidad.': 'duplicated name(s). It is advisable to rename them for export and traceability.',
    'α se calcula automáticamente por banda en Motor ISO': 'α is calculated automatically per band in the ISO engine',
    '— Selecciona capa de receptores —': '— Select receiver layer —',
    '— Sin capa de uso del suelo —': '— No land-use layer —',
    '• Altura de receptor: se toma de cada capa configurada en la tabla multi-capa.': '• Receiver height: taken from each layer configured in the multi-layer table.',
    '• Atenuación lineal α:': '• Linear attenuation α:',
    '• Criterio de límite por capa:': '• Per-layer limit criterion:',
    '• Falta configurar al menos una capa de receptores en la tabla multi-capa.': '• At least one receiver layer must be configured in the multi-layer table.',
    '• Falta seleccionar la capa de receptores. Para este flujo se recomienda una capa de puntos o polígonos de viviendas/receptores.': '• The receiver layer must be selected. For this workflow, a point or polygon layer of dwellings/receivers is recommended.',
    '• La capa de receptores coincide con el layout de turbinas. Elige una capa distinta de viviendas/receptores.': '• The receiver layer matches the turbine layout. Choose a different dwellings/receivers layer.',
    '• Límite de receptor: se toma de cada capa configurada en la tabla multi-capa.': '• Receiver limit: taken from each layer configured in the multi-layer table.',
    '• Método actual: cálculo acústico fuente-receptor para consultoría eólica (Lp = LwA - Adiv - Aatm - Aground). Además puede crear capas GIS de fuentes, enlaces dominantes, receptores fuera de radio, mapa de ruido ráster e isófonas.': '• Current method: wind source-receiver acoustic calculation for consultancy (Lp = LwA - Adiv - Aatm - Aground). It can also create GIS layers of sources, dominant links, receivers outside the radius, a raster noise map and isophones.',
    '• No se han detectado capas de coordenadas por modelo. Genera primero el layout desde Energía.': '• No per-model coordinate layers were detected. Generate the layout from Energy first.',
    '• Parámetros de consultoría acústica: límite de receptor, isófonas y factor de suelo G para revisión rápida de cumplimiento e influencia del terreno.': '• Acoustic consultancy parameters: receiver limit, isophones and ground factor G for a quick review of compliance and terrain influence.',
    '• Sin MDT/DSM seleccionado. El cálculo acústico arrancará en coordenadas planas con alturas relativas de fuente/receptor.': '• No DTM/DSM selected. The acoustic calculation will start in flat coordinates with relative source/receiver heights.',
    "• Suelo/terreno: desde capa de uso del suelo '": "• Ground/terrain: from land-use layer '",
    '• Suelo/terreno: modo capa activado pero sin capa válida; will use G global =': '• Ground/terrain: layer mode enabled but no valid layer; the global G will be used =',
    '.</p>\n                    <p><b>Qué representa cada columna:</b> <code>S_b^ref</code> es la plantilla espectral de referencia (si existe), <code>A_weight,b</code> la ponderación A de cada banda y <code>Lw,b</code> el nivel final en dB realmente usado por el cálculo.</p>': '.</p>\n                    <p><b>What each column represents:</b> <code>S_b^ref</code> is the reference spectral template (if any), <code>A_weight,b</code> the A-weighting of each band and <code>Lw,b</code> the final level in dB actually used by the calculation.</p>',
    "<div class='card'>\n                    <h4>2.1 Espectro usado por el grupo fuente:": "<div class='card'>\n                    <h4>2.1 Spectrum used by the source group:",
    '<li><b>α atmosférico:</b>': '<li><b>Atmospheric α:</b>',
    'Exportar diagnóstico MDT': 'Export DTM diagnostics',
    'No hay diagnóstico por pares fuente-receptor disponible. Recalcula con motor ISO-aligned y fuentes dentro del radio. Este CSV es el que permite auditar cada turbina contra cada receptor.': 'No source-receiver pairwise diagnostics are available. Recalculate with the ISO-aligned engine and sources inside the radius. This CSV is the one that lets you audit each turbine against each receiver.',
    'No hay filas de Top receivers para exportar.': 'There are no Top receivers rows to export.',
    'No hay filas de receptores para exportar.': 'There are no receiver rows to export.',
    'No hay filas de screening MDT para exportar.': 'There are no DTM screening rows to export.',
    'No se pudo exportar el CSV de diagnóstico MDT:': 'Could not export the DTM diagnostics CSV:',
    'Tabla resumida para consultoría: resultados acústicos por receptor, cumplimiento, fuente dominante y atenuaciones principales. Los diagnósticos internos MDT por pares se conservan en memoria, pero no se muestran por defecto.': 'Summary table for consultancy: acoustic results per receiver, compliance, dominant source and main attenuations. The internal pairwise DTM diagnostics are kept in memory, but are not shown by default.',
    'z acústica receptor m': 'receiver acoustic z m',
    'z acústica turb. dom. m': 'dominant turbine acoustic z m',
    'z acústica turb. max Abar m': 'max-Abar turbine acoustic z m',
    'z terreno receptor m': 'receiver terrain z m',
    '- Módulo:': '- Module:',
    'Instálalas en el entorno de Python de QGIS y vuelve a intentarlo.': 'Install them in the QGIS Python environment and try again.',
}
_TO_EN.update(_I18N_ITER6_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER6_TO_EN)
_I18N_ITER6_TO_ES = {v: k for k, v in _I18N_ITER6_TO_EN.items() if v != k}
_TO_ES.update(_I18N_ITER6_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER6_TO_ES)


# ---------------------------------------------------------------------------
# Iteration 7: EN->ES coverage (Shadow module authored in English, plus a
# few Noise/Energy/Results strings). Fixes mis-directed Logroño keys too.
# ---------------------------------------------------------------------------
# Remove English keys that were wrongly mapped to Spanish inside _TO_EN
# (they made the EN target turn those English tooltips into Spanish).
for _bad in (
    'Site latitude (e.g. 42.465 for Logroño)',
    'Site longitude (e.g. -2.445 for Logroño)',
):
    _TO_EN.pop(_bad, None)
    _FRAGMENT_TO_EN.pop(_bad, None)

_I18N_ITER7_TO_ES = {
    'Calculation in progress': 'Cálculo en curso',
    'Could not create raster map:': 'No se pudo crear el mapa raster:',
    'Hub Height and Rotor Diameter must be valid numbers.': 'Hub Height y Rotor Diameter deben ser números válidos.',
    'Invalid configuration': 'Configuración no válida',
    'No model configuration is available.\n\nPlease fill Hub Height and Rotor Diameter in the table.': 'No hay configuración de modelos disponible.\n\nRellena Hub Height y Rotor Diameter en la tabla.',
    'No valid turbines were found with the model configuration.\n\nCheck that:\n1. Turbine layers are selected\n2. The model table has Hub Height and Rotor Diameter configured\n3. Model names match the layers': 'No se encontraron turbinas válidas con la configuración de modelo.\n\nComprueba que:\n1. Hay capas de turbinas seleccionadas\n2. La tabla de modelos tiene Hub Height y Rotor Diameter configurados\n3. Los nombres de modelo coinciden con las capas',
    'Raster error': 'Error de raster',
    'The point layer was created successfully.': 'La capa de puntos se creó correctamente.',
    'A shadow-flicker calculation is already running.': 'Ya hay un cálculo de shadow flicker en ejecución.',
    'Invalid shadow configuration': 'Configuración de sombras no válida',
    'Shadow calculation running': 'Cálculo de sombras en ejecución',
    'Starting shadow-flicker calculation...': 'Iniciando cálculo de shadow flicker...',
    'Could not create the raster file.': 'No se pudo crear el archivo raster.',
    'Could not load the filtered TIF': 'No se pudo cargar el TIF filtrado',
    'Error regenerating raster:': 'Error al regenerar el raster:',
    'No raster data are available. Generate a raster first.': 'No hay datos raster disponibles. Genera primero un raster.',
    'Raster generation was cancelled.': 'La generación del raster fue cancelada.',
    'Raster in progress': 'Raster en progreso',
    'Raster map created successfully.\n\nCalculated points:': 'Mapa raster creado correctamente.\n\nPuntos calculados:',
    'The raster map is being generated in the background.\n\nResolution:': 'El mapa raster se está generando en segundo plano.\n\nResolución:',
    'The raster was created but could not be loaded in QGIS.': 'El raster se creó pero no se pudo cargar en QGIS.',
    'm\nTime step:': 'm\nPaso temporal:',
    'm\nYou can continue working in QGIS.\n\nYou will be notified when it finishes.': 'm\nPuedes seguir trabajando en QGIS.\n\nSe te avisará cuando termine.',
    '💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).': '💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).',
    'Export hour × month matrix to CSV': 'Exportar matriz hora × mes a CSV',
    'Hour × Month': 'Hora × Mes',
    'Shadow Flicker - Calculation Summary': 'Shadow Flicker - Resumen del cálculo',
    'Time step:': 'Paso temporal:',
    'Observer height cannot be negative.': 'La altura del observador no puede ser negativa.',
    'Receiver layer not found in the current QGIS project.': 'No se encontró la capa de receptores en el proyecto QGIS actual.',
    'The maximum shadow distance must be greater than 0 m.': 'La distancia máxima de sombra debe ser mayor que 0 m.',
    'The raster resolution must be greater than 0 m.': 'La resolución del raster debe ser mayor que 0 m.',
    'The raster time step must be greater than 0 minutes.': 'El paso temporal del raster debe ser mayor que 0 minutos.',
    'The receiver time step must be greater than 0 minutes.': 'El paso temporal de receptores debe ser mayor que 0 minutos.',
    'The selected DEM/DSM input is not a raster layer.': 'La entrada DEM/DSM seleccionada no es una capa raster.',
    'The selected DEM/DSM layer was not found; refresh the shadow module.': 'No se encontró la capa DEM/DSM seleccionada; actualiza el módulo de sombras.',
    'The selected DEM/DSM raster is not valid.': 'El raster DEM/DSM seleccionado no es válido.',
    'The selected receiver layer has no features.': 'La capa de receptores seleccionada no tiene elementos.',
    'The selected receiver layer is not a vector layer.': 'La capa de receptores seleccionada no es una capa vectorial.',
    'The selected turbine layer has no features.': 'La capa de turbinas seleccionada no tiene elementos.',
    'The selected turbine layer is not a vector layer.': 'La capa de turbinas seleccionada no es una capa vectorial.',
    'The turbine layer must contain point geometries.': 'La capa de turbinas debe contener geometrías de punto.',
    'Turbine availability must be between 0 and 1.': 'La disponibilidad de turbina debe estar entre 0 y 1.',
    'Turbine layer not found in the current QGIS project.': 'No se encontró la capa de turbinas en el proyecto QGIS actual.',
    '. Solar calculation uses UTC internally and accumulates results by local month/hour.': '. El cálculo solar usa UTC internamente y acumula resultados por mes/hora local.',
    'Calculation time resolution\n5 min = optimal speed/accuracy balance\n1 min = maximum accuracy (very slow)': 'Resolución temporal del cálculo\n5 min = equilibrio óptimo velocidad/precisión\n1 min = máxima precisión (muy lento)',
    'Computes latitude/longitude from the layout centroid and tries to detect the IANA time zone': 'Calcula latitud/longitud desde el centroide del layout e intenta detectar la zona horaria IANA',
    'Configure the geometric parameters for each turbine model.\nHub Height: hub height above ground level [m]\nRotor Diameter: rotor diameter [m]': 'Configura los parámetros geométricos de cada modelo de turbina.\nHub Height: altura de buje sobre el terreno [m]\nRotor Diameter: diámetro de rotor [m]',
    'Creates a new TIF using the selected month/hour filter': 'Crea un nuevo TIF usando el filtro de mes/hora seleccionado',
    'Fixed offset for fixed-offset mode. Daylight saving time is not applied.': 'Offset fijo para el modo de offset fijo. No se aplica horario de verano.',
    'Fraction of time the turbine is operational (0.97 = 97%)': 'Fracción de tiempo en que la turbina está operativa (0,97 = 97%)',
    'Generates a continuous raster map of shadow flicker hours\nDisplays a color gradient across the whole analysis area\nWARNING: this may take several minutes depending on the area': 'Genera un mapa raster continuo de horas de shadow flicker\nMuestra un gradiente de color en toda el área de análisis\nAVISO: esto puede tardar varios minutos según el área',
    'Hourly table: local civil time with DST · Zone:': 'Tabla horaria: hora civil local con DST · Zona:',
    'IANA time zone, for example Europe/Madrid, America/Santiago or Asia/Tokyo.\nThe plugin includes a broad IANA catalogue and local TZif database to apply DST without timezonefinder.': 'Zona horaria IANA, por ejemplo Europe/Madrid, America/Santiago o Asia/Tokyo.\nEl plugin incluye un catálogo IANA amplio y base TZif local para aplicar DST sin timezonefinder.',
    'Maximum distance from a turbine to consider shadow flicker.\nDefault: 2000 m, matching the conservative Continuum-style screening distance.\nThis value is used for receiver filtering and for the raster extent/mask.': 'Distancia máxima desde una turbina para considerar shadow flicker.\nPor defecto: 2000 m, coincidiendo con la distancia de screening conservadora tipo Continuum.\nEste valor se usa para filtrar receptores y para el extent/máscara del raster.',
    'Maximum solar elevation to include (90° = no high-sun cut; lower values are an optional screening assumption)': 'Elevación solar máxima a incluir (90° = sin recorte de sol alto; valores menores son una hipótesis opcional de screening)',
    'Number of cores to use (your CPU has': 'Número de núcleos a usar (tu CPU tiene',
    'Once the raster has been generated, you can regenerate filtered TIFs by month and/or hour\nwithout recalculating the full annual geometry.': 'Una vez generado el raster, puedes regenerar TIF filtrados por mes y/o hora\nsin recalcular toda la geometría anual.',
    'Optional digital elevation model (MDT) raster.\nIf provided, the ground elevation under each turbine and receiver is\nsampled from the raster. This makes elev_diff = (hub + ground_turb) -\n(observer + ground_recv), using terrain-aware elevation correction.\nIf left empty, flat terrain (z=0) is assumed.': 'Raster de modelo digital de elevaciones (MDT) opcional.\nSi se proporciona, la elevación del terreno bajo cada turbina y receptor se\nmuestrea del raster. Así elev_diff = (buje + terreno_turb) -\n(observador + terreno_recep), usando corrección de elevación según terreno.\nSi se deja vacío, se asume terreno plano (z=0).',
    'Please configure Hub Height and Rotor Diameter for all models.': 'Configura Hub Height y Rotor Diameter para todos los modelos.',
    'Select the point layer with receiver locations': 'Selecciona la capa de puntos con las ubicaciones de receptores',
    'Time step ONLY for the raster (independent from receivers).\n1 min  = maximum accuracy, VERY slow\n5 min  = recommended (accuracy >98%, 5x faster)\n10 min = very fast, accuracy ~95%\n20 min = ultra-fast for previewing': 'Paso temporal SOLO para el raster (independiente de los receptores).\n1 min  = máxima precisión, MUY lento\n5 min  = recomendado (precisión >98%, 5x más rápido)\n10 min = muy rápido, precisión ~95%\n20 min = ultrarrápido para previsualizar',
    'Use multiple cores to speed up the calculation\nExperimental - may cause errors on some systems\nRecommended: disabled (more stable)': 'Usa varios núcleos para acelerar el cálculo\nExperimental: puede causar errores en algunos sistemas\nRecomendado: desactivado (más estable)',
    'models have valid configuration.': 'modelos tienen configuración válida.',
    '— Select turbine layer —': '— Selecciona capa de turbinas —',
    '⚠️ No model has valid configuration (Hub Height and Rotor Diameter).': '⚠️ Ningún modelo tiene configuración válida (Hub Height y Rotor Diameter).',
    '⚠️ No models detected. Check that turbine layers exist.': '⚠️ No se detectaron modelos. Comprueba que existen capas de turbinas.',
    '⚠️ No receiver layer selected.': '⚠️ No hay capa de receptores seleccionada.',
    '⚠️ The turbine layer is empty.': '⚠️ La capa de turbinas está vacía.',
    '✅ Valid configuration. You can proceed with the calculation.': '✅ Configuración válida. Puedes continuar con el cálculo.',
    '✓ Site coordinates: (': '✓ Coordenadas del emplazamiento: (',
    '✓ Turbine geometry to be used in the shadow calculation:': '✓ Geometría de turbina que se usará en el cálculo de sombras:',
    '✓ Turbine layer:': '✓ Capa de turbinas:',
    '❌ Incomplete configuration. Check the flagged items.': '❌ Configuración incompleta. Revisa los elementos marcados.',
    '• DEM/DSM was used to sample terrain elevation at sources and receivers.': '• Se usó DEM/DSM para muestrear la elevación del terreno en fuentes y receptores.',
    '• No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.': '• Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas de fuente/receptor.',
    '• Receivers with at least one turbine inside the radius:': '• Receptores con al menos una turbina dentro del radio:',
    '• GIS receivers-outside-radius layer created: Noise · Receivers fuera de radio (': '• Capa GIS de receptores fuera de radio creada: Noise · Receivers fuera de radio (',
    '• Acoustic sources: will use 1 source layer manually selected from Inputs.': '• Fuentes acústicas: se usará 1 capa de fuentes seleccionada manualmente en Entradas.',
    'rows. The CSV export contains the full detail.</small></p>': 'filas. La exportación CSV contiene el detalle completo.</small></p>',
    'Site latitude (e.g. 42.465 for Logroño)': 'Latitud del emplazamiento (ej. 42.465 para Logroño)',
    'Site longitude (e.g. -2.445 for Logroño)': 'Longitud del emplazamiento (ej. -2.445 para Logroño)',
}
_TO_ES.update(_I18N_ITER7_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER7_TO_ES)
# Reverse so switching back to English also works (skip identity).
_I18N_ITER7_TO_EN = {v: k for k, v in _I18N_ITER7_TO_ES.items() if v != k}
_TO_EN.update(_I18N_ITER7_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER7_TO_EN)


# ---------------------------------------------------------------------------
# Iteration 8: thorough second pass. Energy model-help HTML, parameter
# tooltips, status/progress messages, surfaced exceptions, support dialog,
# shadow page remainders. ES<->EN both directions.
# ---------------------------------------------------------------------------
_I18N_ITER8_TO_EN = {
    '<i>(valor editado por usuario)</i>': '<i>(value edited by user)</i>',
    'A expansión TurbOPark': 'A TurbOPark expansion',
    'A expansión TurboNOJ': 'A TurboNOJ expansion',
    'Calcular con capas seleccionadas': 'Calculate with selected layers',
    'Coeficiente de expansión dependiente de TI en la formulación Zong.': 'TI-dependent expansion coefficient in the Zong formulation.',
    'Coeficiente que convierte la TI local/efectiva en expansión de estela.': 'Coefficient that converts local/effective TI into wake expansion.',
    'Constante principal de expansión de TurboGaussian/TurbOPark.': 'Main expansion constant for TurboGaussian/TurbOPark.',
    'Constante principal de expansión para la familia TurboNOJ cuando la versión de PyWake la expone.': 'Main expansion constant for the TurboNOJ family when the PyWake version exposes it.',
    'Lector WRG no disponible; no se puede dibujar el perímetro WRG.': 'WRG reader not available; the WRG boundary cannot be drawn.',
    'No se pudo dibujar el perímetro del recurso:': 'Could not draw the resource boundary:',
    'Número de palas usado en la longitud near-wake. Normalmente 3.': 'Number of blades used in the near-wake length. Usually 3.',
    'Offset mínimo de expansión cuando la TI tiende a cero.': 'Minimum expansion offset when TI tends to zero.',
    'Offset mínimo de expansión de la estela.': 'Minimum wake-expansion offset.',
    'Pendiente de expansión del cono de estela top-hat. Subir k acelera la recuperación; bajarlo alarga la estela.': 'Expansion slope of the top-hat wake cone. Increasing k speeds up recovery; decreasing it lengthens the wake.',
    'Primer coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.': 'First TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.',
    'Primer coeficiente que multiplica la TI en la formulación TurboGaussian.': 'First coefficient multiplying TI in the TurboGaussian formulation.',
    'Segundo coeficiente TI/CT en la formulación TurboGaussian.': 'Second TI/CT coefficient in the TurboGaussian formulation.',
    'Segundo coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.': 'Second TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.',
    'Selecciona una carpeta WAsP/Surfer o un WRG/ZIP para dibujar el perímetro.': 'Select a WAsP/Surfer folder or a WRG/ZIP to draw the boundary.',
    'altura(s) para': 'height(s) for',
    'k (expansión de estela)': 'k (wake expansion)',
    'lista para editar en el mapa': 'ready to edit on the map',
    'Mostrar perímetro del recurso en el mapa': 'Show resource boundary on the map',
    'Actualizar perímetro': 'Refresh boundary',
    'Limpiar perímetro': 'Clear boundary',
    'Volver al diálogo': 'Back to dialog',
    '<i>Este modelo no expone parámetros físicos manualmente. Pulsa el botón ℹ️ para ver por qué.</i>': '<i>This model does not expose physical parameters manually. Press the ℹ️ button to see why.</i>',
    '● editado en mapa, sin guardar': '● edited on map, unsaved',
    '<i>Sin información específica para este modelo.</i>': '<i>No specific information for this model.</i>',
    'Selecciona carpeta con grids WAsP': 'Select a folder with WAsP grids',
    'Selecciona CSV con coordenadas X,Y del modelo': 'Select a CSV with the model X,Y coordinates',
    'Resolución del grid [m] (menor = más detalle, más lento):': 'Grid resolution [m] (lower = more detail, slower):',
    'Graficar con capas seleccionadas': 'Plot with selected layers',
    'Capas para graficar estelas': 'Layers for plotting wakes',
    'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.  Puedes marcar varias capas para simular un parque con varios modelos de turbina a la vez.': 'Select the turbine layers you want to include in this calculation.  You can check several layers to simulate a wind farm with several turbine models at once.',
    'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.  Marca varias capas si el parque combina distintos modelos de turbina.': 'Select the turbine layers you want to include in the wake maps.  Check several layers if the wind farm combines different turbine models.',
    'Estos parámetros modifican la forma de la estela. Déjalos en «Valor PyWake por defecto» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.': 'These parameters modify the wake shape. Leave them as “PyWake default value” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.',
    'CrespoHernandez — Crespo &amp; Hernández 1996, ajuste empírico clásico. Constantes fijas del paper.': 'CrespoHernandez — Crespo &amp; Hernández 1996, classic empirical fit. Fixed constants from the paper.',
    '(acoplamiento más estable). El resto de la selección se ha respetado.': '(more stable coupling). The rest of the selection was preserved.',
    "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Sustitución de modelo de bloqueo:</b>": "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Blockage-model substitution:</b>",
    'Energy específica neta': 'Net specific yield',
    'Energía específica neta': 'Net specific yield',
    'La combinación de wake TI-driven con SelfSimilarity2020 no convergió en PyWake. Se ha mantenido el bloqueo sustituyéndolo por': 'The combination of a TI-driven wake with SelfSimilarity2020 did not converge in PyWake. Blockage was kept by replacing it with',
    'La simulación no pudo ejecutarse exactamente con la combinación pedida y PyWake requirió una degradación automática.': 'The simulation could not run exactly with the requested combination and PyWake required automatic degradation.',
    'se ha usado un único raster de ambient TI': 'a single ambient TI raster was used',
    'No se pudo generar la rosa de sectores:': 'Could not generate the sector rose:',
    "' es bytes (lectura errónea).": "' is bytes (incorrect read).",
    "' es str (lectura errónea).": "' is str (incorrect read).",
    "' no tiene suficientes valores para definir bounds.": "' does not have enough values to define bounds.",
    "No se encontró un WindTurbines en 'models' y V80 no está disponible.": "No WindTurbines was found in 'models' and V80 is not available.",
    'No se pudo importar load_wrg_site. Revisa que exista ag_core/wrg_site.py.': 'Could not import load_wrg_site. Check that ag_core/wrg_site.py exists.',
    'Selecciona una carpeta WAsP válida o un WRG (.wrg/.zip).': 'Select a valid WAsP folder or a WRG (.wrg/.zip).',
    'CSV vacío.': 'Empty CSV.',
    "El CSV debe tener cabeceras 'X' y 'Y' (o easting/northing).": "The CSV must have 'X' and 'Y' headers (or easting/northing).",
    'No hay coordenadas válidas en las capas/CSV proporcionados.': 'There are no valid coordinates in the provided layers/CSV.',
    'No se encontraron coordenadas válidas en el CSV.': 'No valid coordinates were found in the CSV.',
    'Mapa de Estelas (': 'Wake map (',
    'Hay varias capas/modelos seleccionados, pero PyWake no ha podido activar type_i. Las estelas se dibujarán con el primer modelo como fallback.': 'Several layers/models are selected, but PyWake could not enable type_i. Wakes will be drawn with the first model as fallback.',
    'No se pudo construir el WindTurbines combinado para varios modelos. Se usará el primer modelo como fallback.': 'The combined WindTurbines for several models could not be built. The first model will be used as fallback.',
    'turbinas están fuera del dominio y se omitirán (ej.:': 'turbines are outside the domain and will be omitted (e.g.:',
    'Todas las turbinas están fuera del dominio del site (': 'All turbines are outside the site domain (',
    'celdas. Resolución efectiva': 'cells. Effective resolution',
    'Sector más energético': 'Most energetic sector',
    'mosaic: lista de entrada vacía': 'mosaic: empty input list',
    'BuildVRT devolvió None; revisa los paths': 'BuildVRT returned None; check the paths',
    'La GUI de QGIS/Qt no está disponible en este entorno.': 'The QGIS/Qt GUI is not available in this environment.',
    'No se encontraron GRD válidos en los directorios dados': 'No valid GRD files were found in the given directories',
    'Selecciona carpeta de salida': 'Select output folder',
    'Introduce el EPSG en el que están las coordenadas de los tiles.\nPor ejemplo:\n  • 25829 → ETRS89 / UTM 29N (Galicia oeste)\n  • 25830 → ETRS89 / UTM 30N (gran parte de España)\n\nDeja vacío para usar': 'Enter the EPSG in which the tile coordinates are.\nFor example:\n  • 25829 → ETRS89 / UTM 29N (western Galicia)\n  • 25830 → ETRS89 / UTM 30N (most of Spain)\n\nLeave empty to use',
    'La capa activa no es de puntos.': 'The active layer is not a point layer.',
    'La capa activa no es vectorial.': 'The active layer is not a vector layer.',
    'Click izq: añadir · Click der: borrar · ESC: salir': 'Left click: add · Right click: delete · ESC: exit',
    'Física PyWake': 'PyWake physics',
    "<br><span style='color:#a85a00;'>Aún no hay turbinas en esta capa: haz click izq en el mapa para añadirlas.</span>": "<br><span style='color:#a85a00;'>There are no turbines in this layer yet: left click on the map to add them.</span>",
    'capa(s) con': 'layer(s) with',
    'AEP con estelas:': 'AEP with wakes:',
    'Crea una capa de resumen por modelo y permite guardar un CSV con AEP por turbina.': 'Creates a per-model summary layer and lets you save a per-turbine AEP CSV.',
    'Guardar resultados por turbina': 'Save per-turbine results',
    'Calculando desglose por modelo / cluster…': 'Calculating per-model / cluster breakdown…',
    'Cargando recurso eólico (WAsP/WRG)…': 'Loading wind resource (WAsP/WRG)…',
    'Generando tabla por aerogenerador…': 'Generating per-turbine table…',
    'Simulación principal con PyWake…': 'Main simulation with PyWake…',
    '. Se usará fallback fijo.': '. A fixed fallback will be used.',
    "'. Revisa la estructura exportada de WAsP.": "'. Check the exported WAsP structure.",
    "'. Selecciona los CSV de coordenadas por modelo en el diálogo o crea un turbines.csv con columnas x,y[,model_id].": "'. Select the per-model coordinate CSVs in the dialog or create a turbines.csv with x,y[,model_id] columns.",
    'CSV de coordenadas no válido para «': 'Invalid coordinate CSV for «',
    "No se ha podido encontrar una subcarpeta válida para PyWake en '": "Could not find a valid PyWake subfolder in '",
    'No se pudieron resolver las coordenadas del layout: faltan los CSV por modelo y no existe una carpeta base válida para buscar turbines.csv.': 'The layout coordinates could not be resolved: the per-model CSVs are missing and there is no valid base folder to look for turbines.csv.',
    'Todas las turbinas del CSV están fuera del área del grid WAsP. Revisa unidades (m vs km) y reproyección.': 'All turbines in the CSV are outside the WAsP grid area. Check units (m vs km) and reprojection.',
    'coords_xy inválido para «': 'invalid coords_xy for «',
    'Directorio WAsP inválido o no existente': 'Invalid or non-existent WAsP directory',
    'Extensión WRG no soportada (use .wrg o .zip):': 'Unsupported WRG extension (use .wrg or .zip):',
    'Cálculo completado.': 'Calculation completed.',
    'Volcando resultados a las capas QGIS…': 'Writing results to the QGIS layers…',
    "No hay motor WFM disponible para engine='": "No WFM engine available for engine='",
    'No se pudo importar ningún superposition model (LinearSum/SquaredSum/WeightedSum).': 'No superposition model could be imported (LinearSum/SquaredSum/WeightedSum).',
    'deficit_cls es None (no se pudo importar un deficit model válido).': 'deficit_cls is None (a valid deficit model could not be imported).',
    '-> usando selección automática segura.': '-> using safe automatic selection.',
    ': usando LinearSum (selección usuario).': ': using LinearSum (user selection).',
    ': usando MaxSum (selección usuario).': ': using MaxSum (user selection).',
    ': usando SquaredSum (selección usuario).': ': using SquaredSum (user selection).',
    ': usando WeightedSum (selección usuario, deficit=': ': using WeightedSum (user selection, deficit=',
    'Clave de superposición no reconocida:': 'Unrecognized superposition key:',
    "error creando superposición '": "error creating superposition '",
    "' no disponible en esta instalación de PyWake. Intentando fallback a SelfSimilarityDeficit2020...": "' not available in this PyWake installation. Trying fallback to SelfSimilarityDeficit2020...",
    'El archivo WRG/ZIP no existe:': 'The WRG/ZIP file does not exist:',
    'La carpeta WAsP/Surfer no existe.': 'The WAsP/Surfer folder does not exist.',
    'Las extensiones de los grids no tienen intersección válida.': 'The grid extents have no valid intersection.',
    'No se ha seleccionado ningún WRG/ZIP.': 'No WRG/ZIP has been selected.',
    'No se han encontrado grids .grd/.asc/.tif en la carpeta seleccionada.': 'No .grd/.asc/.tif grids were found in the selected folder.',
    'No se pudo leer la extensión espacial de los grids seleccionados.': 'Could not read the spatial extent of the selected grids.',
    'No se pudo leer una extensión válida desde el WRG/ZIP.': 'Could not read a valid extent from the WRG/ZIP.',
    'Perímetro WAsP/Surfer dibujado: dominio común de': 'WAsP/Surfer boundary drawn: common domain of',
    'Perímetro WAsP/Surfer dibujado: envolvente de': 'WAsP/Surfer boundary drawn: envelope of',
    'Perímetro WRG dibujado:': 'WRG boundary drawn:',
    'grid(s) sin intersección común.': 'grid(s) with no common intersection.',
    'No se pudo crear la capa temporal de perímetro del recurso.': 'Could not create the temporary resource-boundary layer.',
    'Extensión no válida:': 'Invalid extent:',
    'Proporciona una capa de puntos válida.': 'Provide a valid point layer.',
    'El dataset WRG no contiene coordenadas x/y válidas': 'The WRG dataset does not contain valid x/y coordinates',
    'GDAL.Warp devolvió None al alinear el raster TI al grid WRG': 'GDAL.Warp returned None while aligning the TI raster to the WRG grid',
    'GDAL/OSR no disponible para leer el raster de turbulencia': 'GDAL/OSR not available to read the turbulence raster',
    'No se han indicado raster(s) TI válidos': 'No valid TI raster(s) were provided',
    'No se pudo resolver el CRS destino del WRG/proyecto para el raster TI': 'Could not resolve the target WRG/project CRS for the TI raster',
    'Raster TI no válido:': 'Invalid TI raster:',
    'xarray no disponible para construir TI por altura:': 'xarray not available to build TI by height:',
    "'; se conserva el primer raster para esa altura.": "'; the first raster for that height is kept.",
    ') sin altura identificable; se aplicará a todas las alturas del WRG como aproximación.': ') with no identifiable height; it will be applied to all WRG heights as an approximation.',
    'm; se aplicará a todas las alturas del WRG como aproximación.': 'm; it will be applied to all WRG heights as an approximation.',
    '. Probando loader estándar…': '. Trying the standard loader…',
    'nombres que no casan con py_wake en:': 'names that do not match py_wake in:',
    'pérdida': 'loss',
    'No se pudo ejecutar la simulación con los modelos seleccionados. Último error:': 'The simulation could not run with the selected models. Last error:',
    'Listas `ws` y `power_kw` deben tener la misma longitud y no estar vacías.': 'Lists `ws` and `power_kw` must have the same length and not be empty.',
    'No se han leído datos válidos de': 'No valid data were read from',
    'Si aportas `ct`, su longitud debe coincidir con `ws`.': 'If you provide `ct`, its length must match `ws`.',
    'No se pudo cargar PyWake para construir la turbina. Comprueba que py_wake está instalado en el Python de QGIS y que Windows no está bloqueando alguna DLL/.pyd de sus dependencias.': 'PyWake could not be loaded to build the turbine. Check that py_wake is installed in the QGIS Python and that Windows is not blocking a DLL/.pyd of its dependencies.',
    'No se encontró una PowerCtFunction válida.': 'No valid PowerCtFunction was found.',
    'PowerCtFunctionList no está disponible para combinar varios modelos.': 'PowerCtFunctionList is not available to combine several models.',
    'PyWake WindTurbines no está disponible en este entorno.': 'PyWake WindTurbines is not available in this environment.',
    "No se encontró/creó una PowerCtFunction válida para el modelo '": "No valid PowerCtFunction was found/created for model '",
    'Cabecera WRG inválida:': 'Invalid WRG header:',
    'El ZIP no contiene ningún .wrg:': 'The ZIP does not contain any .wrg:',
    'Línea de punto WRG inválida:': 'Invalid WRG point line:',
    'No se pudo leer ningún WRG desde:': 'No WRG could be read from:',
    'WRG sin puntos (sólo cabecera)': 'WRG with no points (header only)',
    'WRG vacío': 'Empty WRG',
    'xarray es necesario para usar WRG con PyWake': 'xarray is required to use WRG with PyWake',
    ', leído': ', read',
    'Extensión no soportada:': 'Unsupported extension:',
    'Hay WRG duplicados para la altura': 'There are duplicated WRG files for height',
    'Header WRG inválido (nx,ny,dx): nx=': 'Invalid WRG header (nx,ny,dx): nx=',
    "Header WRG inválido: '": "Invalid WRG header: '",
    'Lista WRG vacía': 'Empty WRG list',
    'Línea WRG demasiado corta': 'WRG line too short',
    'Línea WRG incompleta: esperados >=': 'Incomplete WRG line: expected >=',
    'No se encontró ningún WRG legible en:': 'No readable WRG was found in:',
    'No se pudo leer ningún WRG válido': 'No valid WRG could be read',
    'WRG con distinta resolución/extent: no se pueden combinar por altura': 'WRG files with different resolution/extent: they cannot be combined by height',
    'WRG con distinto dx: no se pueden combinar por altura': 'WRG files with different dx: they cannot be combined by height',
    'WRG con distinto origen x0/y0: no se pueden combinar por altura': 'WRG files with different origin x0/y0: they cannot be combined by height',
    'líneas esperadas; hay': 'expected lines; there are',
    'Módulo de ruido: conectado al layout y modelos WT del proyecto, con cálculo sobre receptores, tabla de emisión por modelo y salidas en capa. La física actual sigue un enfoque fuente-receptor propio simplificado y documentación de limitaciones para validación técnica.': 'Noise module: connected to the project layout and WT models, with per-receiver calculation, a per-model emission table and layer outputs. The current physics follows a simplified source-receiver approach with documented limitations for technical validation.',
    'polígonos': 'polygons',
    '· inválida (': '· invalid (',
    'Calculando ruido en segundo plano…': 'Calculating noise in the background…',
    'uso del suelo / G efectivo por polígonos todavía usa intersecciones de capas QGIS': 'land use / effective G by polygons still uses QGIS layer intersections',
    'Calculando ruido en segundo plano… 0%': 'Calculating noise in the background… 0%',
    'Añadiendo capas de resultados a QGIS…': 'Adding result layers to QGIS…',
    'Calculando receptores y resumen…': 'Calculating receivers and summary…',
    'Calculando ruido y raster…': 'Calculating noise and raster…',
    'Preparando datos para cálculo en segundo plano…': 'Preparing data for the background calculation…',
    'Preparando fuentes, receptores y MDT para el cálculo en segundo plano…': 'Preparing sources, receivers and DTM for the background calculation…',
    'Velantis Wind · preparando ruido': 'Velantis Wind · preparing noise',
    'Cálculo de ruido cancelado o fallido.': 'Noise calculation cancelled or failed.',
    'Preparando fuentes, receptores y MDT…': 'Preparing sources, receivers and DTM…',
    'Cálculo de ruido completado en segundo plano.': 'Noise calculation completed in the background.',
    'Cálculo de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.': 'Noise calculation launched in the background. You can see it in the QGIS task manager.',
    'No se pudo completar el cálculo:': 'Could not complete the calculation:',
    'No se pudo generar el raster:': 'Could not generate the raster:',
    'Raster de ruido generado y añadido al proyecto.': 'Noise raster generated and added to the project.',
    'Raster de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.': 'Noise raster launched in the background. You can see it in the QGIS task manager.',
    'celdas, resolución efectiva': 'cells, effective resolution',
    '• Cálculo pesado lanzado como QgsTask. Puedes seguir usando QGIS mientras avanza.': '• Heavy calculation launched as a QgsTask. You can keep using QGIS while it runs.',
    '• Isófonas terminadas en segundo plano:': '• Isophones finished in the background:',
    'Cálculo de ruido completado.': 'Noise calculation completed.',
    'Raster de ruido completado.': 'Noise raster completed.',
    'Raster de ruido finalizado con aviso.': 'Noise raster finished with a warning.',
    'Configura al menos una capa válida en la tabla de receptores por categoría.': 'Configure at least one valid layer in the receiver-category table.',
    'No hay configuración acústica de modelos.': 'There is no acoustic model configuration.',
    'La capa de uso del suelo debe ser vectorial poligonal.': 'The land-use layer must be a polygonal vector layer.',
    'No se han detectado turbinas/layout válidos para el cálculo acústico.': 'No valid turbines/layout were detected for the acoustic calculation.',
    'rápido (<5 seg para 100 receptores)': 'fast (<5 s for 100 receivers)',
    'Experimental - Adiv ✓, Aatm ✓ (simplificado), Agr ✓ (simplificado), Abar ✓ (MDT/DSM básico)': 'Experimental - Adiv ✓, Aatm ✓ (simplified), Agr ✓ (simplified), Abar ✓ (basic DTM/DSM)',
    'medio (~8x más lento que motor rápido)': 'medium (~8x slower than the fast engine)',
    'genérica': 'generic',
    'Espectro inválido:': 'Invalid spectrum:',
    'No se pudieron leer datos válidos de': 'Could not read valid data from',
    'No se pudo invertir la geotransformación del MDT/DSM.': 'Could not invert the DTM/DSM geotransform.',
    'No hay fuentes acústicas válidas para crear el raster.': 'There are no valid acoustic sources to create the raster.',
    'GDAL no pudo crear el GeoTIFF del raster de ruido.': 'GDAL could not create the GeoTIFF of the noise raster.',
    'No se pudo cargar el raster generado:': 'Could not load the generated raster:',
    'Curva inválida -> fallback LwA fijo (': 'Invalid curve -> fixed LwA fallback (',
    'Sin fuentes válidas en la capa': 'No valid sources in the layer',
    'Rápido LwA global': 'Fast global LwA',
    'Acoustic curve vacía': 'Empty acoustic curve',
    'No se pudieron leer pares ws/LwA válidos de': 'Could not read valid ws/LwA pairs from',
    'Sin ruta de curva acústica': 'No acoustic-curve path',
    'sin fuentes dentro del radio': 'no sources within the radius',
    "Faltan las siguientes dependencias de Python para ejecutar Velantis Wind:": "The following Python dependencies are missing to run Velantis Wind:",
    'Autodetección aproximada sin polígonos mundiales; verifica la zona horaria en el desplegable IANA.': 'Approximate auto-detection without world polygons; check the time zone in the IANA dropdown.',
    'No se pudo detectar una zona horaria fiable sin una base mundial de polígonos. Selecciona manualmente la zona IANA en el desplegable; la repo incluye un catálogo amplio y una base TZif con reglas DST.': 'Could not detect a reliable time zone without a world polygon database. Select the IANA zone manually in the dropdown; the repo includes a broad catalogue and a TZif database with DST rules.',
    'Canadá tiene varias zonas; verifica manualmente.': 'Canada has several zones; check manually.',
    'China usa una zona oficial; verifica si el proyecto usa otra referencia.': 'China uses one official zone; check whether the project uses another reference.',
    'México tiene varias zonas; verifica manualmente.': 'Mexico has several zones; check manually.',
    'Ayuda a mantener herramientas eólicas abiertas, útiles y actualizadas.': 'Help keep open wind-energy tools useful, maintained and up to date.',
    'Corrección de errores y compatibilidad con nuevas versiones de QGIS.': 'Bug fixes and compatibility with new QGIS versions.',
    'Documentación, tutoriales, ejemplos y datasets de prueba.': 'Documentation, tutorials, examples and test datasets.',
    'Financiación de una funcionalidad concreta si encaja con la dirección open source.': 'Fund a specific feature if it fits the open-source direction.',
    'Logo destacado y conversación sobre prioridades del roadmap.': 'Featured logo and a conversation about roadmap priorities.',
    'Mejoras en AEP, ruido, sombras, lectura de recursos y exportación de resultados.': 'Improvements in AEP, noise, shadow flicker, resource ingestion and result exports.',
    'Nombre en README y acceso a notas de actualización/actualizaciones cuando estén disponibles.': 'Name in README and experimental/update notes when available.',
    'Para empresas y equipos técnicos': 'For companies and technical teams',
    'Página de GitHub': 'GitHub page',
    'Tiempo dedicado a validar casos reales y mejorar la estabilidad del plugin.': 'Dedicated time for real-case validation and plugin stability improvements.',
    '¿Qué ayuda a financiar el apoyo?': 'What does the support help fund?',
    '<b>BastankhahGaussianDeficit</b><br><br>Modelo gaussiano de Bastankhah & Porté-Agel (2014). El perfil radial del déficit es una campana de Gauss cuyo ancho σ(x) crece linealmente.<br><br><b>k</b> — Pendiente del crecimiento de σ. σ(x)/D = k·(x/D) + cεps. Default PyWake = 0.0324, valor calibrado contra LES en el paper original. Subirlo = estela que se ensancha y recupera antes; bajarlo = estela más estrecha y persistente.<br><br><b>cεps</b> — Tamaño inicial de la estela junto al rotor (σ₀/D). Default PyWake = 0.2. Casi nunca se cambia; afecta sobre todo al near-wake.<br><br><i>Nota:</i> en BG estándar la TI ambiente NO entra en k. Es robusto para benchmarking (ej. IEA Task 37) pero poco sensible al recurso TI.': '<b>BastankhahGaussianDeficit</b><br><br>Gaussian model by Bastankhah & Porté-Agel (2014). The radial deficit profile is a Gaussian bell whose width σ(x) grows linearly.<br><br><b>k</b> — Growth slope of σ. σ(x)/D = k·(x/D) + cεps. PyWake default = 0.0324, a value calibrated against LES in the original paper. Increasing it = a wake that widens and recovers sooner; decreasing it = a narrower, more persistent wake.<br><br><b>cεps</b> — Initial wake size next to the rotor (σ₀/D). PyWake default = 0.2. Rarely changed; it mainly affects the near wake.<br><br><i>Note:</i> in standard BG the ambient TI does NOT enter k. It is robust for benchmarking (e.g. IEA Task 37) but not very sensitive to the TI resource.',
    '<b>NOJDeficit (Jensen)</b><br><br>Modelo top-hat clásico: la estela es un cilindro que se ensancha linealmente con una sola pendiente <b>k</b>.<br><br><b>k</b> — Pendiente de expansión. <u>Físicamente</u> representa cuánto crece el radio de la estela por metro avanzado aguas abajo: r(x) = R + k·x. Cuanto mayor k, más rápido se recupera la estela y más pequeño el déficit lejano.<br><br><b>Valores de referencia</b><br>• Offshore limpio: k ≈ 0.04 (default PyWake)<br>• Onshore típico: k ≈ 0.075<br>• Onshore complejo / alta TI: k ≈ 0.10<br><br><i>Nota:</i> NOJ no usa la TI ambiente para calcular k. Si tu sitio tiene TI alta y necesitas que esto se note en el AEP, usa TurboNOJ o un modelo gaussiano TI-sensible.': '<b>NOJDeficit (Jensen)</b><br><br>Classic top-hat model: the wake is a cylinder that widens linearly with a single slope <b>k</b>.<br><br><b>k</b> — Expansion slope. <u>Physically</u> it represents how much the wake radius grows per metre downstream: r(x) = R + k·x. The larger k is, the faster the wake recovers and the smaller the far-field deficit.<br><br><b>Reference values</b><br>• Clean offshore: k ≈ 0.04 (PyWake default)<br>• Typical onshore: k ≈ 0.075<br>• Complex onshore / high TI: k ≈ 0.10<br><br><i>Note:</i> NOJ does not use ambient TI to compute k. If your site has high TI and you need that to show in AEP, use TurboNOJ or a TI-sensitive Gaussian model.',
    '<b>NiayifarGaussianDeficit</b><br><br>Variante gaussiana donde la pendiente k es función explícita de la <b>TI local efectiva</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>cεps</b> — Tamaño inicial σ₀/D. Default PyWake = 0.2.<br><br>Cambiar la TI ambiente del WRG o el modelo de turbulencia añadida sigue siendo la palanca principal del AEP. Los coeficientes se exponen solo como ajuste avanzado.': '<b>NiayifarGaussianDeficit</b><br><br>Gaussian variant where the slope k is an explicit function of the <b>effective local TI</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>cεps</b> — Initial size σ₀/D. PyWake default = 0.2.<br><br>Changing the WRG ambient TI or the added-turbulence model is still the main AEP lever. The coefficients are exposed only as an advanced adjustment.',
    '<b>Qué es TI</b><br><br>TI significa <b>turbulence intensity</b>. En simple: mide cuánto fluctúa el viento alrededor de su velocidad media. Más TI suele significar más mezcla del flujo y una recuperación de estela distinta.<br><br><b>Dos turbulencias diferentes</b><br>• <b>TI ambiente</b>: viene del recurso eólico o de un raster TI. Es el viento que llega al parque antes de las turbinas.<br>• <b>TI añadida por estela</b>: la genera cada turbina detrás de sí. La calcula el modelo de turbulencia de PyWake, si lo activas.<br><br><b>Qué hace el plugin</b><br>• Si cargas raster TI, se usa como turbulencia ambiente espacial.<br>• Si no cargas raster TI, se usa el valor manual de la interfaz como hipótesis uniforme.<br>• Si tienes varios raster a distintas alturas, puedes indicar 90;120;... para interpolar por altura de buje.<br><br><b>Cuándo importa más</b><br>BG/NOJ son robustos pero poco sensibles a TI. Niayifar, Zong, TurboGaussian y TurboNOJ hacen que la TI afecte más a la recuperación de estela y al AEP.<br><br><b>Idea sencilla</b><br>Si no tienes datos de turbulencia, una TI manual uniforme permite correr el cálculo. Para más realismo en onshore, terreno complejo o varias alturas de buje, usa raster TI, idealmente por altura.': '<b>What TI is</b><br><br>TI stands for <b>turbulence intensity</b>. In simple terms: it measures how much the wind fluctuates around its mean speed. More TI usually means more flow mixing and a different wake recovery.<br><br><b>Two different turbulences</b><br>• <b>Ambient TI</b>: comes from the wind resource or from a TI raster. It is the wind reaching the wind farm before the turbines.<br>• <b>Wake-added TI</b>: generated by each turbine behind it. It is computed by the PyWake turbulence model, if you enable it.<br><br><b>What the plugin does</b><br>• If you load a TI raster, it is used as spatial ambient turbulence.<br>• If you do not load a TI raster, the manual value in the interface is used as a uniform assumption.<br>• If you have several rasters at different heights, you can enter 90;120;... to interpolate by hub height.<br><br><b>When it matters most</b><br>BG/NOJ are robust but not very TI-sensitive. Niayifar, Zong, TurboGaussian and TurboNOJ make TI affect wake recovery and AEP more.<br><br><b>Simple idea</b><br>If you have no turbulence data, a uniform manual TI lets you run the calculation. For more realism onshore, in complex terrain or with several hub heights, use a TI raster, ideally by height.',
    '<b>Qué es el bloqueo</b><br><br>Además de crear una estela detrás, una turbina también puede frenar ligeramente el viento antes de que el flujo llegue al rotor. Ese efecto aguas arriba se llama <b>bloqueo</b> o <i>blockage</i>.<br><br><b>Compatibilidad</b><br>• No depende del recurso WRG/GRD: depende del motor de PyWake y del modelo de bloqueo.<br>• Con <b>PropagateDownwind</b> se desactiva, porque ese motor solo calcula efectos aguas abajo.<br>• Con <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b> el plugin puede intentar incluirlo con los wake models disponibles.<br><br><b>Parámetros</b><br>En esta versión experimental no se exponen parámetros internos de bloqueo. Son calibraciones propias del modelo de PyWake; para comparar sensibilidad, cambia de modelo en lugar de tocar constantes internas.<br><br><b>Qué opción usar</b><br>SelfSimilarityDeficit2020 es la opción principal. VortexCylinder/VortexDipole/HybridInduction se dejan como alternativas si tu instalación de PyWake las soporta.': '<b>What blockage is</b><br><br>Besides creating a wake behind it, a turbine can also slow the wind slightly before the flow reaches the rotor. That upstream effect is called <b>blockage</b>.<br><br><b>Compatibility</b><br>• It does not depend on the WRG/GRD resource: it depends on the PyWake engine and the blockage model.<br>• With <b>PropagateDownwind</b> it is disabled, because that engine only computes downstream effects.<br>• With <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b> the plugin can try to include it with the available wake models.<br><br><b>Parameters</b><br>In this experimental release no internal blockage parameters are exposed. They are model-specific PyWake calibrations; to compare sensitivity, switch model instead of touching internal constants.<br><br><b>Which option to use</b><br>SelfSimilarityDeficit2020 is the main option. VortexCylinder/VortexDipole/HybridInduction are left as alternatives if your PyWake installation supports them.',
    '<b>Qué es el wake effect, en simple</b><br><br>Cuando una turbina extrae energía del viento, detrás de ella queda una zona con menos velocidad y más turbulencia. Esa zona es la <b>estela</b>. Si otra turbina cae dentro, producirá menos AEP.<br><br>El <b>wake deficit model</b> decide cuánta velocidad se pierde y cómo se recupera la estela aguas abajo.<br><br><b>Cómo elegir sin ser experto</b><br>• <b>BastankhahGaussianDeficit</b>: opción robusta para comparar layouts y parques offshore limpios.<br>• <b>NiayifarGaussianDeficit</b>: parecido a Bastankhah, pero la turbulencia TI pesa más en la expansión de la estela.<br>• <b>ZongGaussianDeficit</b>: más avanzado; útil si quieres una representación más rica del near wake y de la recuperación por TI.<br>• <b>TurboGaussianDeficit</b>: gaussiano sensible a turbulencia efectiva; interesante cuando quieres que la TI ambiente/añadida influya en el AEP.<br>• <b>NOJDeficit</b>: muy rápido y clásico; bueno para screening masivo, pero más simplificado.<br>• <b>TurboNOJDeficit</b>: mantiene la ligereza de NOJ, pero permite que la TI influya más.<br><br><b>Idea sencilla</b><br>Para más realismo cuando la turbulencia debe influir en la recuperación de estela, usa <b>Niayifar/Zong/TurboGaussian</b>. Para un cálculo rápido, usa <b>NOJ/TurboNOJ</b>. Para una base robusta de comparación, usa <b>Bastankhah</b>.<br><br>Importante: cargar un raster TI no garantiza por sí solo que cambie mucho el AEP. El efecto se ve de verdad si el modelo de estela elegido usa la TI de forma explícita.': "<b>What the wake effect is, in simple terms</b><br><br>When a turbine extracts energy from the wind, a zone with lower speed and more turbulence is left behind it. That zone is the <b>wake</b>. If another turbine falls inside it, it will produce less AEP.<br><br>The <b>wake deficit model</b> decides how much speed is lost and how the wake recovers downstream.<br><br><b>How to choose without being an expert</b><br>• <b>BastankhahGaussianDeficit</b>: robust option for comparing layouts and clean offshore wind farms.<br>• <b>NiayifarGaussianDeficit</b>: similar to Bastankhah, but TI turbulence weighs more in the wake expansion.<br>• <b>ZongGaussianDeficit</b>: more advanced; useful if you want a richer representation of the near wake and TI-driven recovery.<br>• <b>TurboGaussianDeficit</b>: Gaussian model sensitive to effective turbulence; interesting when you want ambient/added TI to influence AEP.<br>• <b>NOJDeficit</b>: very fast and classic; good for large-scale screening, but more simplified.<br>• <b>TurboNOJDeficit</b>: keeps NOJ's lightness, but lets TI have more influence.<br><br><b>Simple idea</b><br>For more realism when turbulence should influence wake recovery, use <b>Niayifar/Zong/TurboGaussian</b>. For a quick calculation, use <b>NOJ/TurboNOJ</b>. For a robust comparison baseline, use <b>Bastankhah</b>.<br><br>Important: loading a TI raster does not by itself guarantee a large change in AEP. The effect really shows if the chosen wake model uses TI explicitly.",
    '<b>Qué significa la turbulencia en este plugin</b><br><br>• <b>TI raster / WRG</b>: describe la <b>turbulencia ambiente</b> del site. Es un dato de entrada del recurso.<br>• <b>Modelo de turbulencia</b>: calcula la <b>turbulencia añadida por estela</b> detrás de las turbinas.<br>• <b>Modelo de estela</b>: decide si esa TI ambiente/efectiva cambia realmente el déficit y, por tanto, el AEP.<br><br><b>Cómo leerlo físicamente</b><br>• Si solo aportas un raster TI, el plugin usa esa TI horizontal para todas las turbinas; si hay varias alturas de buje, eso es una <b>aproximación vertical</b>.<br>• Si aportas varios raster(s) TI a distintas alturas, el plugin intenta reconstruir <b>TI(x,y,h)</b> e interpolar por altura.<br>• Si no aportas raster TI en WRG, el plugin avisa y usa la <b>TI ambiente fallback definida por el usuario</b> como campo uniforme.<br><br><b>Cuándo importa mucho en el AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> y <b>NOJDeficit</b>: la TI no cambia mucho el déficit base del wake; el impacto sobre el AEP suele ser limitado.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> y <b>TurboNOJDeficit</b>: la TI ambiente/efectiva sí puede influir más en la apertura y recuperación de la estela.<br><br><b>Qué conviene según el parque</b><br>• <b>Offshore con viento relativamente homogéneo</b>: una sola capa TI puede ser suficiente si las alturas de buje son parecidas.<br>• <b>Onshore, terreno rugoso o alturas de buje distintas</b>: para más realismo, usa raster(s) TI y un wake model sensible a TI (Niayifar, Zong, TurboGaussian o TurboNOJ).<br>• <b>Varios modelos de aerogenerador</b>: si mezclas alturas de buje, una sola capa TI es una aproximación; varias alturas de TI representan mejor la variación vertical.<br><br><b>Qué elegir en el selector de modelo de turbulencia</b><br>• <b>STF2017</b>: buena opción general y la recomendación por defecto cuando necesitas un modelo de turbulencia añadida robusto.<br>• <b>STF2005</b>: alternativa clásica y sencilla si quieres comparar con configuraciones más antiguas o más conservadoras.<br>• <b>CrespoHernandez</b> y <b>GCLTurbulence</b>: útiles para comparativas o compatibilidad con ciertos modelos/estudios, pero el impacto final depende del wake model que elijas.<br><br><b>Regla práctica</b><br>Si tu objetivo es que la turbulencia influya físicamente en el AEP, no basta con seleccionar un modelo de turbulencia: también necesitas un <b>modelo de estela sensible a TI</b>.': "<b>What turbulence means in this plugin</b><br><br>• <b>TI raster / WRG</b>: describes the site's <b>ambient turbulence</b>. It is a resource input.<br>• <b>Turbulence model</b>: computes the <b>wake-added turbulence</b> behind the turbines.<br>• <b>Wake model</b>: decides whether that ambient/effective TI actually changes the deficit and therefore the AEP.<br><br><b>Physical interpretation</b><br>• If you provide only one TI raster, the plugin uses that horizontal TI for all turbines; if there are several hub heights, that is a <b>vertical approximation</b>.<br>• If you provide several TI raster(s) at different heights, the plugin tries to reconstruct <b>TI(x,y,h)</b> and interpolate by height.<br>• If you do not provide a TI raster in WRG, the plugin warns and uses the <b>user-defined ambient TI fallback</b> as a uniform field.<br><br><b>When it matters a lot for AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> and <b>NOJDeficit</b>: TI does not change the base wake deficit much; the impact on AEP is usually limited.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> and <b>TurboNOJDeficit</b>: ambient/effective TI can influence wake opening and recovery more.<br><br><b>What is advisable depending on the wind farm</b><br>• <b>Offshore with relatively homogeneous wind</b>: a single TI layer may be enough if hub heights are similar.<br>• <b>Onshore, rough terrain or different hub heights</b>: for more realism, use TI raster(s) and a TI-sensitive wake model (Niayifar, Zong, TurboGaussian or TurboNOJ).<br>• <b>Several turbine models</b>: if you mix hub heights, a single TI layer is an approximation; several TI heights better represent the vertical variation.<br><br><b>What to choose in the turbulence-model selector</b><br>• <b>STF2017</b>: good general option and the default recommendation when you need a robust added-turbulence model.<br>• <b>STF2005</b>: classic, simple alternative if you want to compare with older or more conservative configurations.<br>• <b>CrespoHernandez</b> and <b>GCLTurbulence</b>: useful for comparisons or compatibility with certain models/studies, but the final impact depends on the wake model you choose.<br><br><b>Rule of thumb</b><br>If your goal is for turbulence to physically influence AEP, selecting a turbulence model is not enough: you also need a <b>TI-sensitive wake model</b>.",
    '<b>TurboGaussianDeficit</b><br><br>Versión TI-driven de la familia gaussiana. Tanto k como el ancho cerca del rotor se derivan de la TI efectiva.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI/cεps se exponen como ajuste avanzado para calibración controlada.': '<b>TurboGaussianDeficit</b><br><br>TI-driven version of the Gaussian family. Both k and the width near the rotor are derived from the effective TI.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI/cεps coefficients are exposed as an advanced adjustment for controlled calibration.',
    '<b>TurboNOJDeficit</b><br><br>Variante TI-sensible del Jensen: la pendiente k se calcula a partir de la <b>turbulencia efectiva</b> en cada turbina.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI se exponen como ajuste avanzado y solo se pasan si tu PyWake los acepta.': '<b>TurboNOJDeficit</b><br><br>TI-sensitive variant of the Jensen model: the slope k is computed from the <b>effective turbulence</b> at each turbine.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI coefficients are exposed as an advanced adjustment and are only passed if your PyWake accepts them.',
    '<b>ZongGaussianDeficit</b><br><br>Modelo gaussiano avanzado (Zong &amp; Porté-Agel 2020) con near-wake explícito y pendiente k dependiente de TI local.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>δw/D, ε coeff, λ y B</b> — parámetros near-wake avanzados. El plugin fuerza STF2017 si lo dejas en Ninguno.': '<b>ZongGaussianDeficit</b><br><br>Advanced Gaussian model (Zong &amp; Porté-Agel 2020) with explicit near wake and a slope k dependent on local TI.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>δw/D, ε coeff, λ and B</b> — advanced near-wake parameters. The plugin forces STF2017 if you leave it as None.',
}
_TO_EN.update(_I18N_ITER8_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER8_TO_EN)
_I18N_ITER8_TO_ES = {v: k for k, v in _I18N_ITER8_TO_EN.items() if v != k}
_TO_ES.update(_I18N_ITER8_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER8_TO_ES)
_I18N_ITER8_EN2ES = {
    'Max shadow distance [m]:': 'Distancia máxima de sombra [m]:',
    'Shadow flicker calculation module for wind turbines. Connected to the project layout, with point-receiver calculation and QGIS output layers with detailed results by receiver.': 'Módulo de cálculo de shadow flicker para aerogeneradores. Conectado al layout del proyecto, con cálculo por receptores puntuales y capas de salida QGIS con resultados detallados por receptor.',
    'Time step [min]:': 'Paso temporal [min]:',
    'Turbine layer:': 'Capa de turbinas:',
    '💡 Configure hub height and rotor diameter for each detected model. These parameters are required for the shadow flicker calculation.': '💡 Configura la altura de buje y el diámetro de rotor de cada modelo detectado. Estos parámetros son necesarios para el cálculo de shadow flicker.',
    'Create shadow flicker raster map': 'Crear mapa raster de shadow flicker',
    'Enable parallel calculation': 'Activar cálculo paralelo',
    'Filter raster by month/hour (after generation)': 'Filtrar raster por mes/hora (tras la generación)',
    'Turbine model configuration': 'Configuración de modelos de turbina',
    '✓ Max shadow distance:': '✓ Distancia máxima de sombra:',
    'Detected time zone:': 'Zona horaria detectada:',
    'Select an IANA time zone manually.': 'Selecciona manualmente una zona horaria IANA.',
    'Time zone: not automatically detected.': 'Zona horaria: no detectada automáticamente.',
    'Cannot start the shadow-flicker calculation:': 'No se puede iniciar el cálculo de shadow flicker:',
    '<i>Detailed values for each receptor. Columns are filled before sorting is enabled to avoid blank cells after sorting.</i>': '<i>Valores detallados por receptor. Las columnas se rellenan antes de habilitar la ordenación para evitar celdas vacías al ordenar.</i>',
    'Max shadow distance:': 'Distancia máxima de sombra:',
    'Generating shadow flicker raster map': 'Generando mapa raster de shadow flicker',
    'No valid points to calculate': 'No hay puntos válidos para calcular',
    'ShadowPage compatibility calculation entry point is missing.': 'Falta el punto de entrada de compatibilidad del cálculo de ShadowPage.',
    '· local civil time with DST': '· hora civil local con DST',
    'No valid receiver layer was provided.': 'No se proporcionó una capa de receptores válida.',
    'The receiver layer contains no valid features for calculation.': 'La capa de receptores no contiene elementos válidos para el cálculo.',
    'Bug fixes and compatibility with new QGIS versions.': 'Corrección de errores y compatibilidad con nuevas versiones de QGIS.',
    'Company logo on README and VelantisWind website.': 'Logo de empresa en el README y en la web de VelantisWind.',
    'Dedicated time for real-case validation and plugin stability improvements.': 'Tiempo dedicado a validar casos reales y mejorar la estabilidad del plugin.',
    'For companies and technical teams': 'Para empresas y equipos técnicos',
    'Fund a specific feature if it fits the open-source direction.': 'Financiación de una funcionalidad concreta si encaja con la dirección open source.',
    'Help keep open wind-energy tools useful, maintained and up to date.': 'Ayuda a mantener herramientas eólicas abiertas, útiles y actualizadas.',
    'Improvements in AEP, noise, shadow flicker, resource ingestion and result exports.': 'Mejoras en AEP, ruido, sombras, lectura de recursos y exportación de resultados.',
    'Name in README and experimental/update notes when available.': 'Nombre en README y acceso a notas de actualización/actualizaciones cuando estén disponibles.',
    'Optional acknowledgement on GitHub or the GitHub supporters page.': 'Agradecimiento opcional en GitHub o en la página de supporters de GitHub.',
    'Sponsorship does not grant ownership or exclusive control over the roadmap. Final technical decisions remain with the maintainer to preserve the quality and open-source direction of the project.': 'El patrocinio no otorga propiedad ni control exclusivo sobre el roadmap. Las decisiones técnicas finales se mantienen en el mantenedor para preservar la calidad y la dirección open source del proyecto.',
}
for _k in list(_I18N_ITER8_EN2ES.keys()):
    if _k in _TO_EN and _TO_EN.get(_k) != _k:
        _TO_EN.pop(_k, None); _FRAGMENT_TO_EN.pop(_k, None)
_TO_ES.update(_I18N_ITER8_EN2ES)
_FRAGMENT_TO_ES.update(_I18N_ITER8_EN2ES)
_I18N_ITER8_EN2ES_REV = {v: k for k, v in _I18N_ITER8_EN2ES.items() if v != k}
_TO_EN.update(_I18N_ITER8_EN2ES_REV)
_FRAGMENT_TO_EN.update(_I18N_ITER8_EN2ES_REV)


# ---------------------------------------------------------------------------
# Iteration 8: report bodies & remaining UI built indirectly (Noise technical
# report, Energy results/help, validation msgs). ES<->EN both directions.
# ---------------------------------------------------------------------------
_I18N_ITER8_TO_EN = {
    '<b>BastankhahGaussianDeficit</b><br><br>Modelo gaussiano de Bastankhah & Porté-Agel (2014). El perfil radial del déficit es una campana de Gauss cuyo ancho σ(x) crece linealmente.<br><br><b>k</b> — Pendiente del crecimiento de σ. σ(x)/D = k·(x/D) + cεps. Default PyWake = 0.0324, valor calibrado contra LES en el paper original. Subirlo = estela que se ensancha y recupera antes; bajarlo = estela más estrecha y persistente.<br><br><b>cεps</b> — Tamaño inicial de la estela junto al rotor (σ₀/D). Default PyWake = 0.2. Casi nunca se cambia; afecta sobre todo al near-wake.<br><br><i>Nota:</i> en BG estándar la TI ambiente NO entra en k. Es robusto para benchmarking (ej. IEA Task 37) pero poco sensible al recurso TI.': '<b>BastankhahGaussianDeficit</b><br><br>Gaussian model by Bastankhah & Porté-Agel (2014). The radial deficit profile is a Gaussian bell whose width σ(x) grows linearly.<br><br><b>k</b> — Slope of σ growth. σ(x)/D = k·(x/D) + cεps. PyWake default = 0.0324, a value calibrated against LES in the original paper. Increasing it = wake that widens and recovers earlier; decreasing it = narrower, more persistent wake.<br><br><b>cεps</b> — Initial wake size near the rotor (σ₀/D). PyWake default = 0.2. It is rarely changed; it mainly affects the near wake.<br><br><i>Note:</i> in standard BG the ambient TI does NOT enter k. It is robust for benchmarking (e.g. IEA Task 37) but not very sensitive to the TI resource.',
    '<b>NiayifarGaussianDeficit</b><br><br>Variante gaussiana donde la pendiente k es función explícita de la <b>TI local efectiva</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>cεps</b> — Tamaño inicial σ₀/D. Default PyWake = 0.2.<br><br>Cambiar la TI ambiente del WRG o el modelo de turbulencia añadida sigue siendo la palanca principal del AEP. Los coeficientes se exponen solo como ajuste avanzado.': '<b>NiayifarGaussianDeficit</b><br><br>Gaussian variant where the slope k is an explicit function of the <b>effective local TI</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>cεps</b> — Initial size σ₀/D. PyWake default = 0.2.<br><br>Changing the WRG ambient TI or the added-turbulence model is still the main AEP lever. The coefficients are exposed only as an advanced adjustment.',
    '<b>ZongGaussianDeficit</b><br><br>Modelo gaussiano avanzado (Zong &amp; Porté-Agel 2020) con near-wake explícito y pendiente k dependiente de TI local.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>δw/D, ε coeff, λ y B</b> — parámetros near-wake avanzados. El plugin fuerza STF2017 si lo dejas en Ninguno.': '<b>ZongGaussianDeficit</b><br><br>Advanced Gaussian model (Zong &amp; Porté-Agel 2020) with an explicit near wake and a slope k that depends on local TI.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>δw/D, ε coeff, λ and B</b> — advanced near-wake parameters. The plugin forces STF2017 if you leave it as None.',
    'Ayuda · Parámetros del modelo (': 'Help · Model parameters (',
    'Bastankhah: opción robusta para comparar layouts. Usa una formulación gaussiana con parámetros fijos; la TI ambiente no cambia mucho el déficit base.': 'Bastankhah: robust option for comparing layouts. It uses a Gaussian formulation with fixed parameters; ambient TI does not change the base deficit much.',
    'CrespoHernandez — Crespo &amp; Hernández 1996, ajuste empírico clásico. Constantes fijas del paper.': 'CrespoHernandez — Crespo &amp; Hernández 1996, classic empirical fit. Fixed constants from the paper.',
    'Estado de capas/modelos refrescado.': 'Layers/models state refreshed.',
    'Parámetro λ usado en la longitud near-wake de Vermeulen.': 'λ parameter used in the Vermeulen near-wake length.',
    'Pendiente del ancho σ(x) de la estela gaussiana. Default PyWake ≈ 0.0324555.': 'Slope of the Gaussian wake width σ(x). PyWake default ≈ 0.0324555.',
    'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.\nPuedes marcar varias capas para simular un parque con varios modelos de turbina.': 'Select the turbine layers you want to include in this calculation.\nYou can check several layers to simulate a wind farm with several turbine models.',
    'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.\nMarca varias capas si el parque combina distintos modelos de turbina.': 'Select the turbine layers you want to include in the wake maps.\nCheck several layers if the wind farm combines different turbine models.',
    'Tamaño inicial de la estela en la base (σ₀/D). Default PyWake = 0.2.': 'Initial wake size at the base (σ₀/D). PyWake default = 0.2.',
    'Tamaño inicial de la estela. Default PyWake = 0.25.': 'Initial wake size. PyWake default = 0.25.',
    'CSV de coordenadas corrupto para «': 'Corrupt coordinate CSV for «',
    ': usando SquaredSum (auto, opción robusta por defecto).': ': using SquaredSum (auto, robust default option).',
    'No hay wake deficit model disponible: ni BG ni NoWakeDeficit.': 'No wake deficit model available: neither BG nor NoWakeDeficit.',
    ", el desglose de turbulencia se interpreta como TI ambiente only frente a TI ambiente + turbulencia añadida por estela. No se ejecuta un caso 'sin TI', porque el Site de PyWake necesita una TI ambiente física.": ", the turbulence breakdown is interpreted as ambient-TI only versus ambient TI + wake-added turbulence. A 'no TI' case is not run, because the PyWake Site needs a physical ambient TI.",
    'No se pudo calcular la variante TI ambiente only para el modelo TI-driven; se mantiene el AEP principal. Error: ': 'The ambient-TI-only variant could not be computed for the TI-driven model; the main AEP is kept. Error: ',
    ': el CSV de coordenadas no existe: ': ': the coordinate CSV does not exist: ',
    'No hay una capa de turbinas válida activa para el mapa interactivo.': 'There is no valid active turbine layer for the interactive map.',
    ' % del AEP neto del parque)': ' % of the wind farm net AEP)',
    ' %</b> uniforme como fallback. Este valor afecta directamente al AEP en wakes TI-driven (Niayifar, Zong, TurboGaussian, TurboNOJ). Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.</p>': ' %</b> uniformly as fallback. This value directly affects AEP in TI-driven wakes (Niayifar, Zong, TurboGaussian, TurboNOJ). For representative results, load a TI raster in the resource section or review the turbulence sensitivity.</p>',
    '<b>Rosa de pérdidas por sector.</b> Distribución del AEP libre y neto por dirección del viento. La diferencia entre cada barra representa las pérdidas por estela en ese sector — útil para ver qué direcciones limitan más el rendimiento del parque.': '<b>Sector loss rose.</b> Distribution of free-flow and net AEP by wind direction. The difference between each pair of bars represents the wake losses in that sector — useful to see which directions limit the wind farm performance the most.',
    '<br><small>Diagnóstico incremental: positivo = recupera AEP frente al wake-only; negativo = reduce AEP. No se debe interpretar siempre como pérdida contable.</small></li>\n          <li><b>Bloqueo:</b> ': '<br><small>Incremental diagnostics: positive = recovers AEP versus wake-only; negative = reduces AEP. It should not always be interpreted as an accountable loss.</small></li>\n          <li><b>Blockage:</b> ',
    '<h4>Notas físicas y de compatibilidad</h4><ul>': '<h4>Physical and compatibility notes</h4><ul>',
    "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Aviso de TI ambiente:</b> no se ha proporcionado un raster de TI. El cálculo ha usado <b>TI = ": "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Ambient TI warning:</b> no TI raster has been provided. The calculation used <b>TI = ",
    "<p style='color:#888; font-style:italic;'><small>Métricas adicionales no disponibles: no se pudo determinar la potencia nominal de las turbinas. Vuelve a definir el modelo desde «Definir…» para que la curva de potencia se guarde con la metadata.</small></p>": "<p style='color:#888; font-style:italic;'><small>Additional metrics not available: the rated power of the turbines could not be determined. Define the model again from “Define…” so that the power curve is stored with the metadata.</small></p>",
    'Resultados por receptor calculados.': 'Per-receiver results calculated.',
    '• AVISO: esta configuración usa ruta síncrona porque ': '• WARNING: this configuration uses the synchronous path because ',
    ' receptor(es) con al menos una turbina dentro del radio máximo.': ' receiver(s) with at least one turbine inside the maximum radius.',
    'Configuración acústica inválida:\n- ': 'Invalid acoustic configuration:\n- ',
    'El MDT/DSM no está en el mismo CRS que el proyecto. Reproyéctalo antes de calcular.': 'The DTM/DSM is not in the same CRS as the project. Reproject it before calculating.',
    'La capa de uso del suelo no está en el mismo CRS que el proyecto. Reproyéctala antes de calcular.': 'The land-use layer is not in the same CRS as the project. Reproject it before calculating.',
    'Modelo simplificado LwA global, absorción fija, efecto suelo empírico': 'Simplified global LwA model, fixed absorption, empirical ground effect',
    'Motor Rápido (actual)': 'Fast engine (current)',
    'Cálculo por bandas de octava, absorción atmosférica por condiciones, efecto suelo tipo ISO': 'Octave-band calculation, atmospheric absorption by conditions, ISO-type ground effect',
    'ISO-aligned por bandas': 'ISO-aligned by bands',
    "' no está en el mismo CRS que el proyecto. Se ignora en el cálculo de ruido.": "' is not in the same CRS as the project. It is ignored in the noise calculation.",
    'En modo curva, cada grupo fuente puede usar un CSV ws/LwA distinto.': 'In curve mode, each source group can use a different ws/LwA CSV.',
    'G=0 suelo duro · G=1 suelo poroso': 'G=0 hard ground · G=1 porous ground',
    'Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.': 'You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group. If you enable curve mode, the plugin will evaluate the emission at a specific wind speed or for the worst case.',
    '📊 ISO-aligned - ISO 9613-2 por bandas': '📊 ISO-aligned - ISO 9613-2 by bands',
    "\n        \n        <div class='card'>\n            <h3>📊 Estadísticos de Atenuaciones (Receptores Cubiertos)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación). Para Abar se usa el máximo entre las turbinas contribuyentes de cada receptor, no solo la trayectoria dominante.</i></p>\n            <table>\n                <tr>\n                    <th>Término</th>\n                    <th style='text-align: right;'>Media [dB]</th>\n                    <th style='text-align: right;'>Máximo [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (divergencia geométrica)</td>\n                    <td style='text-align: right;'>": "\n        \n        <div class='card'>\n            <h3>📊 Attenuation statistics (covered receivers)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Raw attenuation magnitudes are shown (not the algebraic sign inside the equation). For Abar, the maximum among the contributing turbines of each receiver is used, not only the dominant path.</i></p>\n            <table>\n                <tr>\n                    <th>Term</th>\n                    <th style='text-align: right;'>Mean [dB]</th>\n                    <th style='text-align: right;'>Maximum [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (geometrical divergence)</td>\n                    <td style='text-align: right;'>",
    "\n                </table>\n                <h4>5. Efecto suelo por regiones</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>El término de suelo se descompone en <b>As</b> (región de fuente), <b>Am</b> (región media) y <b>Ar</b> (región de receptor). En esta implementación no se usan tres parámetros de suelo independientes <code>Gs/Gm/Gr</code>, sino un <b>único G por trayecto</b>. Matemáticamente, el plugin aplica:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_medio)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>donde <b>h_s</b> es la altura característica de la fuente, <b>h_r</b> la del receptor, <b>h_medio</b> la altura media del trayecto y <b>G_m≈0</b> en la aproximación actual para condiciones favorables de propagación. Ese valor único de suelo puede ser:</p>\n                <ul>\n                    <li><b>G manual/global</b>, si el usuario fija un único valor.</li>\n                    <li><b>G_eff</b>, si existe capa de uso del suelo y se calcula una media ponderada por longitud del trayecto.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Significado físico de G:</b> representa el carácter acústico del terreno y controla cómo influye el suelo en la propagación. <b>G≈0</b> indica suelo duro (urbano, asfalto, roca), <b>G≈1</b> suelo blando/poroso (agrícola, pradera, forestal) y valores intermedios representan terreno mixto.</p>\n                <p><b>Qué significa “desde capa”:</b> el plugin corta el trayecto fuente–receptor contra la capa de uso del suelo, asigna un valor <b>G_i</b> a cada polígono interceptado y calcula un único <b>G_eff</b> para ese trayecto. Ese es el valor que entra realmente en <b>Agr,b</b>; el <b>G global</b> mostrado en el informe queda solo como respaldo.</p>\n                <p><b>Convención del informe:</b> <b>Agr,b</b> se muestra aquí como una <b>magnitud positiva de atenuación</b>. En la ecuación principal se resta al nivel de fuente igual que Adiv, Aatm y Abar.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>Término base A_ground(h)</th></tr>\n                    ": "\n                </table>\n                <h4>5. Ground effect by regions</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>The ground term is split into <b>As</b> (source region), <b>Am</b> (middle region) and <b>Ar</b> (receiver region). This implementation does not use three independent ground parameters <code>Gs/Gm/Gr</code>, but a <b>single G per path</b>. Mathematically, the plugin applies:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_mid)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>where <b>h_s</b> is the characteristic source height, <b>h_r</b> the receiver height, <b>h_mid</b> the mean path height and <b>G_m≈0</b> in the current approximation for favourable propagation conditions. That single ground value can be:</p>\n                <ul>\n                    <li><b>Manual/global G</b>, if the user sets a single value.</li>\n                    <li><b>G_eff</b>, if a land-use layer exists and a length-weighted mean along the path is computed.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Physical meaning of G:</b> it represents the acoustic character of the terrain and controls how the ground influences propagation. <b>G≈0</b> indicates hard ground (urban, asphalt, rock), <b>G≈1</b> soft/porous ground (agricultural, grassland, forest) and intermediate values represent mixed terrain.</p>\n                <p><b>What “from layer” means:</b> the plugin clips the source–receiver path against the land-use layer, assigns a <b>G_i</b> value to each intercepted polygon and computes a single <b>G_eff</b> for that path. That is the value actually entering <b>Agr,b</b>; the <b>global G</b> shown in the report is kept only as a fallback.</p>\n                <p><b>Report convention:</b> <b>Agr,b</b> is shown here as a <b>positive attenuation magnitude</b>. In the main equation it is subtracted from the source level just like Adiv, Aatm and Abar.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>Base term A_ground(h)</th></tr>\n                    ",
    "\n                </table>\n                <h4>6. Apantallamiento topográfico con MDT</h4>\n                <p>El MDT <b>no cambia la emisión</b> de la turbina ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto</b> y alimentar el término <b>Abar,b</b>.</p>\n                <ol>\n                    <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con muestreo adaptativo.</li>\n                    <li><b>Línea de visión:</b> se construye la recta entre la altura efectiva de fuente y la altura del receptor. Si el terreno queda siempre por debajo, entonces <b>Abar,b = 0</b>.</li>\n                    <li><b>Obstáculo dominante:</b> si una loma o cresta sobresale, se calcula la altura sobre la línea de visión:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                <p>Cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                <ol start='4'>\n                    <li><b>Geometría real del obstáculo:</b> el plugin usa la posición real del obstáculo dominante y calcula <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor).</li>\n                    <li><b>Activación conservadora:</b> no se activa <b>Abar</b> por pequeñas irregularidades del MDT; se exige un umbral mínimo ligado a la resolución del raster.</li>\n                    <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos y un número de Fresnel:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>Ese número se transforma después en una atenuación <b>Abar,b</b> dependiente de la frecuencia mediante la aproximación actual del plugin:</p>\n                <div class='formula'>si C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>En la implementación actual, <b>Abar</b> se limita además a valores razonables (capado superior) para evitar sobreatenuaciones espurias. Si no hay MDT o no hay obstáculo relevante, entonces <b>Abar,b = 0</b>.</p>\n                <h4>7. Ponderación A usada al final</h4>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ": "\n                </table>\n                <h4>6. Topographic screening with DTM</h4>\n                <p>The DTM <b>does not change the emission</b> of the turbine nor the atmospheric absorption. Its role is to describe the <b>real path geometry</b> and feed the <b>Abar,b</b> term.</p>\n                <ol>\n                    <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with adaptive sampling.</li>\n                    <li><b>Line of sight:</b> the straight line between the effective source height and the receiver height is built. If the terrain always stays below, then <b>Abar,b = 0</b>.</li>\n                    <li><b>Dominant obstacle:</b> if a hill or ridge sticks out, its height above the line of sight is computed:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                <p>When <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                <ol start='4'>\n                    <li><b>Real obstacle geometry:</b> the plugin uses the real position of the dominant obstacle and computes <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver).</li>\n                    <li><b>Conservative activation:</b> <b>Abar</b> is not triggered by small DTM irregularities; a minimum threshold linked to the raster resolution is required.</li>\n                    <li><b>Fresnel-type diffraction:</b> from that geometry a path difference and a Fresnel number are estimated:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>That number is then turned into a frequency-dependent <b>Abar,b</b> attenuation through the plugin's current approximation:</p>\n                <div class='formula'>if C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>In the current implementation, <b>Abar</b> is additionally capped to reasonable values (upper cap) to avoid spurious over-attenuation. If there is no DTM or no relevant obstacle, then <b>Abar,b = 0</b>.</p>\n                <h4>7. A-weighting used at the end</h4>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ",
    '\n                </table>\n                <p><b>Lectura del receptor crítico:</b> la tabla del apartado de receptor crítico muestra magnitudes de atenuación para trazabilidad. El <b>nivel resultante</b> no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.</p>\n            </div>\n            ': '\n                </table>\n                <p><b>Critical-receiver reading:</b> the table in the critical-receiver section shows attenuation magnitudes for traceability. The <b>resulting level</b> must not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and over the contributing sources within the calculation radius.</p>\n            </div>\n            ',
    "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G</h3>\n                    <p>En esta corrida el efecto suelo se ha calculado con un <b>G único manual</b> para todo el trayecto:</p>\n                    <div class='formula'>G = ": "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G calculation</h3>\n                    <p>In this run the ground effect was computed with a <b>single manual G</b> for the whole path:</p>\n                    <div class='formula'>G = ",
    "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G_eff</h3>\n                    <p>Cuando el modo suelo es <b>desde capa</b>, el cálculo no usa un único valor manual para todo el parque. Para cada trayecto fuente–receptor se obtiene un <b>G_eff</b> calculado desde la capa de uso del suelo:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>donde <b>G_i</b> es el valor asignado a cada polígono interceptado por el trayecto y <b>L_i</b> es la longitud del trayecto dentro de ese polígono.</p>\n                    <ul>\n                        <li><b>G = 0</b>: suelo duro (urbano/asfalto/roca).</li>\n                        <li><b>G = 0.5</b>: terreno mixto.</li>\n                        <li><b>G = 1</b>: suelo blando/poroso (agrícola, pradera, forestal, vegetado).</li>\n                    </ul>\n                    <p><b>Importante:</b> el <b>G global</b> mostrado en el informe es solo un valor de respaldo. Cuando hay capa de uso del suelo, el cálculo usa realmente <b>G_eff</b> por trayecto. En esta corrida, el valor medio efectivo fue <b>": "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G_eff calculation</h3>\n                    <p>When the ground mode is <b>from layer</b>, the calculation does not use a single manual value for the whole wind farm. For each source–receiver path a <b>G_eff</b> is computed from the land-use layer:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>where <b>G_i</b> is the value assigned to each polygon intercepted by the path and <b>L_i</b> is the path length inside that polygon.</p>\n                    <ul>\n                        <li><b>G = 0</b>: hard ground (urban/asphalt/rock).</li>\n                        <li><b>G = 0.5</b>: mixed terrain.</li>\n                        <li><b>G = 1</b>: soft/porous ground (agricultural, grassland, forest, vegetated).</li>\n                    </ul>\n                    <p><b>Important:</b> the <b>global G</b> shown in the report is only a fallback value. When a land-use layer exists, the calculation actually uses <b>G_eff</b> per path. In this run, the effective mean value was <b>",
    "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En el motor ISO-aligned, el MDT <b>no cambia la emisión de la turbina</b> ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto fuente–receptor</b> y alimentar el término de apantallamiento topográfico <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>Cómo entra el MDT en el cálculo</h4>\n                    <ol>\n                        <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con un <b>muestreo adaptativo</b>, ajustado a la distancia y a la resolución del raster. El perfil se calcula <b>una sola vez</b> por par fuente–receptor y se reutiliza en las 8 bandas para reducir tiempo de cálculo.</li>\n                        <li><b>Línea de visión directa:</b> se compara el perfil con la recta que une la fuente acústica a su altura efectiva y el receptor a su altura de evaluación. Si el terreno queda siempre por debajo de esa recta, no hay obstáculo topográfico relevante y <b>Abar,b = 0</b>.</li>\n                        <li><b>Detección de obstáculo dominante:</b> si una loma o cresta del MDT sobresale por encima de la línea de visión, el modelo interpreta que existe pantalla topográfica. La magnitud clave es la altura del obstáculo sobre la línea de visión:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                    <p>cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                    <ol start='4'>\n                        <li><b>Activación conservadora:</b> no se activa Abar por pequeñas irregularidades del relieve; se aplica un umbral mínimo ligado a la resolución del MDT.</li>\n                        <li><b>Geometría real del obstáculo:</b> el cálculo usa la <b>posición real</b> del obstáculo dominante y obtiene <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor) reales, en lugar de asumir siempre un obstáculo en el punto medio.</li>\n                        <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos aproximada y se transforma en una atenuación dependiente de la frecuencia:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>donde <b>δ</b> es la diferencia de caminos aproximada, <b>f</b> la frecuencia y <b>c</b> la velocidad del sonido. El número <b>C</b> se traduce después a una atenuación <b>Abar,b</b> mayor cuanto más bloquea el relieve el trayecto. Esta es la misma aproximación implementada en el cálculo.</p>\n                    <p><b>Interpretación física:</b> en terreno plano o cuando no hay intersección con la línea de visión, <b>Abar</b> suele ser despreciable. En terreno complejo, el MDT puede introducir varios dB de atenuación adicional y cambiar el receptor crítico.</p>\n                    <p><b>Implementación actual:</b> obstáculo dominante único, perfil adaptativo con límites de coste, geometría real del obstáculo, activación conservadora y atenuación capada a valores razonables.</p>\n                    <p><b>MDT usado en esta corrida:</b> ": "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In the ISO-aligned engine, the DTM <b>does not change the turbine emission</b> nor the atmospheric absorption. Its role is to describe the <b>real source–receiver path geometry</b> and feed the topographic screening term <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>How the DTM enters the calculation</h4>\n                    <ol>\n                        <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with <b>adaptive sampling</b>, adjusted to distance and raster resolution. The profile is computed <b>only once</b> per source–receiver pair and reused across the 8 bands to reduce computation time.</li>\n                        <li><b>Direct line of sight:</b> the profile is compared with the straight line joining the acoustic source at its effective height and the receiver at its evaluation height. If the terrain always stays below that line, there is no relevant topographic obstacle and <b>Abar,b = 0</b>.</li>\n                        <li><b>Dominant-obstacle detection:</b> if a hill or ridge in the DTM rises above the line of sight, the model interprets that a topographic screen exists. The key magnitude is the obstacle height above the line of sight:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                    <p>when <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                    <ol start='4'>\n                        <li><b>Conservative activation:</b> Abar is not triggered by small relief irregularities; a minimum threshold linked to the DTM resolution is applied.</li>\n                        <li><b>Real obstacle geometry:</b> the calculation uses the <b>real position</b> of the dominant obstacle and obtains real <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver), instead of always assuming an obstacle at the midpoint.</li>\n                        <li><b>Fresnel-type diffraction:</b> from that geometry an approximate path difference is estimated and turned into a frequency-dependent attenuation:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>where <b>δ</b> is the approximate path difference, <b>f</b> the frequency and <b>c</b> the speed of sound. The number <b>C</b> is then translated into an <b>Abar,b</b> attenuation that grows the more the relief blocks the path. This is the same approximation implemented in the calculation.</p>\n                    <p><b>Physical interpretation:</b> on flat terrain or when there is no intersection with the line of sight, <b>Abar</b> is usually negligible. On complex terrain, the DTM can introduce several dB of additional attenuation and change the critical receiver.</p>\n                    <p><b>Current implementation:</b> single dominant obstacle, adaptive profile with cost limits, real obstacle geometry, conservative activation and attenuation capped to reasonable values.</p>\n                    <p><b>DTM used in this run:</b> ",
    "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En esta corrida <b>no se ha usado MDT/DSM</b>, por lo que el término de apantallamiento topográfico se fija en:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>La evaluación se realiza sin introducir pantallas topográficas. La geometría del trayecto se resuelve sin perfil del terreno y el cálculo depende de Lw,b, Adiv, Aatm,b y Agr,b.</p>\n                </div>\n                ": "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In this run <b>no DTM/DSM was used</b>, so the topographic screening term is set to:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>The assessment is performed without introducing topographic screens. The path geometry is resolved without a terrain profile and the calculation depends on Lw,b, Adiv, Aatm,b and Agr,b.</p>\n                </div>\n                ",
    "\n                <div class='note'>\n                    <b>Lectura correcta de Abar:</b> el valor <b>Abar trayectoria dominante</b> corresponde solo a la turbina que más contribuye al receptor y a su banda dominante. El nivel total del receptor se obtiene sumando energéticamente todas las turbinas y bandas.\n                    <br><b>Abar máximo entre turbinas contribuyentes:</b> ": "\n                <div class='note'>\n                    <b>Correct reading of Abar:</b> the <b>dominant-path Abar</b> value corresponds only to the turbine that contributes most to the receiver and to its dominant band. The total receiver level is obtained by energetically summing all turbines and bands.\n                    <br><b>Maximum Abar among contributing turbines:</b> ",
    "\n                <h4>3. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor. Aquí <b>d</b> sale de las coordenadas de turbina y receptor junto con sus alturas de evaluación.</p>\n                <h4>4. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm,b = α(f, T, HR, P) · d</div>\n                <p>La absorción atmosférica se calcula por banda a partir de un coeficiente base de referencia y tres factores correctores. La dependencia física con temperatura, humedad relativa y presión <b>sí se representa</b>, pero mediante una <b>aproximación simplificada del plugin</b>, no mediante la formulación analítica completa de ISO 9613-1.</p>\n                <div class='formula'>α(f, T, HR, P) = α_ref(f) · corr_T · corr_HR · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_HR = 1 + 0.003·|HR - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretación de las correcciones:</b> <b>T</b> se introduce en °C respecto a una referencia de 15 °C; <b>HR</b> se compara con una humedad óptima de referencia del 50% y la corrección crece al alejarse de ese valor; y <b>P</b> se introduce en kPa respecto a una referencia de 101.325 kPa con una corrección inversa. Estos factores modifican únicamente el bloque atmosférico <b>Aatm,b</b>: no alteran ni la emisión de la turbina, ni el efecto del suelo, ni el término de MDT/apantallamiento.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ": "\n                <h4>3. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance. Here <b>d</b> comes from the turbine and receiver coordinates together with their evaluation heights.</p>\n                <h4>4. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm,b = α(f, T, RH, P) · d</div>\n                <p>Atmospheric absorption is computed per band from a reference base coefficient and three correction factors. The physical dependence on temperature, relative humidity and pressure <b>is represented</b>, but through a <b>simplified plugin approximation</b>, not through the full analytical formulation of ISO 9613-1.</p>\n                <div class='formula'>α(f, T, RH, P) = α_ref(f) · corr_T · corr_RH · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_RH = 1 + 0.003·|RH - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretation of the corrections:</b> <b>T</b> is entered in °C relative to a 15 °C reference; <b>RH</b> is compared with a reference optimum humidity of 50% and the correction grows as it moves away from that value; and <b>P</b> is entered in kPa relative to a 101.325 kPa reference with an inverse correction. These factors modify only the atmospheric block <b>Aatm,b</b>: they alter neither the turbine emission, nor the ground effect, nor the DTM/screening term.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ",
    "\n                <h4>3. Efecto suelo simplificado con uso del suelo</h4>\n                <p>En el motor rápido el término <b>Aground</b> sigue siendo empírico, pero el parámetro de suelo puede venir de la capa de uso del suelo como un <b>G_eff</b> por trayecto:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>Ese <b>G_eff</b> se usa después dentro de la corrección simplificada de terreno del motor rápido. El valor global <b>G = ": "\n                <h4>3. Simplified ground effect with land use</h4>\n                <p>In the fast engine the <b>Aground</b> term is still empirical, but the ground parameter can come from the land-use layer as a per-path <b>G_eff</b>:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>That <b>G_eff</b> is then used inside the fast engine's simplified terrain correction. The global value <b>G = ",
    "\n                <h4>3. Efecto suelo simplificado</h4>\n                <p>El término <b>Aground</b> es una corrección empírica del terreno controlada por un único parámetro manual:</p>\n                <div class='formula'>G = ": "\n                <h4>3. Simplified ground effect</h4>\n                <p>The <b>Aground</b> term is an empirical terrain correction controlled by a single manual parameter:</p>\n                <div class='formula'>G = ",
    '\n                <h4>4. MDT / topografía</h4>\n                <p>En el motor rápido el MDT no introduce un término explícito de apantallamiento topográfico. Aunque exista una capa de relieve en el proyecto, este modo no calcula <b>Abar</b>, no extrae línea de visión ni aplica difracción; por tanto la física se basa únicamente en <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> y la corrección empírica de terreno <b>Aground</b>.</p>\n            ': '\n                <h4>4. DTM / topography</h4>\n                <p>In the fast engine the DTM does not introduce an explicit topographic screening term. Even if a relief layer exists in the project, this mode does not compute <b>Abar</b>, does not extract a line of sight and does not apply diffraction; therefore the physics relies only on <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> and the empirical terrain correction <b>Aground</b>.</p>\n            ',
    '\n                <h4>5. Qué no hace este modo</h4>\n                <p>El motor rápido no trabaja por bandas, no calcula <b>Lw,b</b>, no introduce <b>Abar</b> y no extrae línea de visión ni difracción desde MDT. Por tanto, es apropiado para screening y comparativas rápidas, pero no para análisis espectral detallado.</p>\n            </div>\n            ': '\n                <h4>5. What this mode does not do</h4>\n                <p>The fast engine does not work by bands, does not compute <b>Lw,b</b>, does not introduce <b>Abar</b> and does not extract a line of sight or diffraction from a DTM. It is therefore appropriate for screening and quick comparisons, but not for detailed spectral analysis.</p>\n            </div>\n            ',
    "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo ISO-aligned</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin para que el resultado por receptor sea trazable. El nivel final de cada receptor <b>no sale de una única resta simple</b>, sino de calcular todas las contribuciones fuente–receptor dentro del radio de cálculo y sumarlas energéticamente.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 How the ISO-aligned calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows so that the per-receiver result is traceable. The final level of each receiver <b>does not come from a single simple subtraction</b>, but from computing all source–receiver contributions within the calculation radius and summing them energetically.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>",
    "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo Screening</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin en el modo rápido. El objetivo es obtener una estimación ágil para mapas, comparación de alternativas y detección inicial de receptores sensibles.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 How the Screening calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows in the fast mode. The goal is to obtain a quick estimate for maps, comparison of alternatives and initial detection of sensitive receivers.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>",
    "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor ISO-aligned</h3>\n                <p>Este motor trabaja en <b>8 bandas de octava</b> (63–8000 Hz). Las bandas no son un resultado del cálculo, sino la <b>malla frecuencial del método</b>. Para aplicar la propagación por bandas, el cálculo necesita un <b>input acústico por banda</b> de la fuente <code>Lw,b</code>. Ese input puede venir de un espectro medido/importado o de una plantilla/fallback ajustada al nivel global operativo.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ": "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the ISO-aligned engine</h3>\n                <p>This engine works in <b>8 octave bands</b> (63–8000 Hz). The bands are not a calculation result, but the <b>frequency grid of the method</b>. To apply band-wise propagation, the calculation needs a per-band acoustic input <code>Lw,b</code> from the source. That input can come from a measured/imported spectrum or from a template/fallback adjusted to the global operating level.</p>\n                <p><b>Operating scenario of this run:</b> ",
    "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor rápido</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>El motor rápido trabaja con un único nivel global <b>LwA</b> por grupo fuente. Está pensado para screening, mapas ágiles y comparativas rápidas, sacrificando detalle espectral frente a velocidad. En este modo <b>no hay propagación por bandas</b> ni término explícito de apantallamiento topográfico.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ": "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the fast engine</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>The fast engine works with a single global level <b>LwA</b> per source group. It is intended for screening, quick maps and fast comparisons, trading spectral detail for speed. In this mode there is <b>no band-wise propagation</b> and no explicit topographic screening term.</p>\n                <p><b>Operating scenario of this run:</b> ",
    ' Otros receptores sí presentan apantallamiento (Abar máx. ': ' Other receivers do show screening (max Abar ',
    ' Umbral de activación: ': ' Activation threshold: ',
    ' dB · <b>Abar ponderado por contribución energética:</b> ': ' dB · <b>Abar weighted by energetic contribution:</b> ',
    ' dB. Este desplazamiento eleva o reduce toda la plantilla espectral para que su suma A-ponderada reproduzca el <code>LwA_objetivo</code> de la curva acústica o del LwA fijo.</p>': ' dB. This shift raises or lowers the entire spectral template so that its A-weighted sum reproduces the <code>LwA_target</code> of the acoustic curve or of the fixed LwA.</p>',
    ' dB/m</b>. En el motor rápido, la absorción atmosférica se resume en un único coeficiente constante, por lo que <b>T</b>, <b>HR</b> y <b>P</b> <b>no entran explícitamente</b> en el cálculo. Esa es una de las simplificaciones clave frente al modo ISO-aligned.</p>\n                ': ' dB/m</b>. In the fast engine, atmospheric absorption is summarized in a single constant coefficient, so <b>T</b>, <b>RH</b> and <b>P</b> <b>do not enter explicitly</b> in the calculation. That is one of the key simplifications compared with the ISO-aligned mode.</p>\n                ',
    ' m AGL · altura acústica receptor=': ' m AGL · receiver acoustic height=',
    ' m AGL · altura acústica turbina=': ' m AGL · turbine acoustic height=',
    ' m · terreno receptor=': ' m · receiver terrain=',
    ' m</b>) y la capa de uso del suelo si existe.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> cada modelo o grupo de turbinas trabaja con un único <b>LwA operativo</b>, definido por un valor fijo o por una curva <code>LwA(ws)</code>. En esta corrida: <b>': ' m</b>) and the land-use layer if it exists.</li>\n                    <li><b>Acoustic state of each source group:</b> each turbine model or group works with a single <b>operating LwA</b>, defined by a fixed value or by an <code>LwA(ws)</code> curve. In this run: <b>',
    ' m</b>), la capa de uso del suelo si existe y el MDT/DSM si está activo.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> para cada modelo o grupo de turbinas se obtiene un <b>LwA operativo</b> desde un valor fijo o desde una curva <code>LwA(ws)</code>. En esta corrida: <b>': ' m</b>), the land-use layer if it exists and the DTM/DSM if it is active.</li>\n                    <li><b>Acoustic state of each source group:</b> for each turbine model or group an <b>operating LwA</b> is obtained from a fixed value or from an <code>LwA(ws)</code> curve. In this run: <b>',
    ' receptores</strong> dentro del radio<br>\n                <strong>': ' receivers</strong> within the radius<br>\n                <strong>',
    ' receptores</strong> superan límites<br>\n                <strong>': ' receivers</strong> exceed limits<br>\n                <strong>',
    ".</p>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> un único nivel global <b>LwA</b> por grupo fuente.</li>\n                    <li><b>Nivel operativo global:</b> sale de un <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> para la velocidad o peor caso seleccionados.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> en este modo no se usan T/HR/P; la absorción se resume en un único coeficiente <b>α</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> el MDT no entra como apantallamiento explícito en este modo.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Input global de la fuente. Sale de un valor fijo por grupo o de una curva acústica <code>LwA(ws)</code> para la velocidad/peor caso seleccionados.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Se calcula con un único coeficiente constante <code>α</code> multiplicado por la distancia.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Corrección empírica del efecto suelo. El parámetro de suelo puede ser un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                </table>\n                <h4>2. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor.</p>\n                <h4>3. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>En esta corrida se ha usado <b>α = ": ".</p>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> a single global level <b>LwA</b> per source group.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> for the selected wind speed or worst case.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> in this mode T/RH/P are not used; absorption is summarized in a single coefficient <b>α</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> the DTM does not enter as explicit screening in this mode.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Global source input. It comes from a fixed value per group or from an <code>LwA(ws)</code> acoustic curve for the selected wind speed/worst case.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Computed with a single constant coefficient <code>α</code> multiplied by the distance.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Empirical ground-effect correction. The ground parameter can be a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</td></tr>\n                </table>\n                <h4>2. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance.</p>\n                <h4>3. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>In this run <b>α = ",
    ".</p>\n                <p><b>Ecuación general por banda:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Suma A-ponderada final:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> <code>Lw,b</code> por bandas de octava. Si existe espectro específico del grupo fuente, ese es el input usado. Si no, el plugin usa una biblioteca/plantilla/fallback y la ajusta al nivel global operativo.</li>\n                    <li><b>Nivel operativo global:</b> viene de <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> según el escenario seleccionado. Ese nivel global no sustituye a las bandas: fija el estado operativo y el espectro aporta el reparto frecuencial.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> temperatura <b>T</b>, humedad relativa <b>HR</b> y presión <b>P</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> MDT/DSM opcional. Solo afecta al cálculo de <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Input acústico por bandas. Sale del espectro del grupo fuente (CSV, biblioteca, plantilla o fallback ajustado al nivel global). La curva acústica LwA(ws) o el LwA fijo definen el nivel global operativo de la turbina, y el espectro por bandas reparte ese nivel entre las 8 bandas.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Se calcula por banda con una tabla base de absorción <code>α_ref(f)</code> y correcciones simplificadas por temperatura, humedad relativa y presión. La implementación actual usa la matemática exacta del plugin: <code>α = α_ref(f)·corr_T·corr_HR·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Se calcula como efecto suelo por regiones. El parámetro de suelo usado es un <b>G único por trayecto</b>: manual/global o <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>Solo entra si hay MDT/DSM y se detecta apantallamiento topográfico. Si no hay MDT o no hay obstáculo relevante, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Input acústico de fuente y bandas</h4>\n                <p>En este motor, el término <code>Lw,b</code> es un <b>dato de entrada por banda</b>. Las <b>bandas de octava</b> (63–8000 Hz) no son un resultado de la ISO ni una tabla calculada por el plugin: son la <b>malla frecuencial</b> sobre la que se resuelve la propagación.</p>\n                <p>El plugin combina dos piezas:</p>\n                <ul>\n                    <li><b>Curva acústica global LwA(ws)</b>: fija el <b>nivel operativo global</b> de la turbina para la velocidad de viento o el peor caso seleccionado.</li>\n                    <li><b>Espectro por bandas Lw,b</b>: reparte ese nivel global entre las 8 bandas y es el input real usado en la ecuación por bandas.</li>\n                </ul>\n                <p>Ese espectro puede proceder de un archivo específico del fabricante/usuario o de una plantilla de referencia. Si solo se dispone de una curva global <code>LwA(ws)</code>, el plugin fija primero el nivel global operativo <code>LwA_objetivo</code> y después construye un espectro absoluto por bandas a partir de una forma espectral de referencia <code>S_b^ref</code>.</p>\n                <p><b>Reconstrucción matemática de las bandas cuando solo existe LwA(ws):</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_objetivo - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>Es decir: la curva acústica aporta el <b>nivel global operativo</b> y la plantilla/biblioteca aporta la <b>forma espectral</b>. El desplazamiento <b>Δ</b> se calcula para que, al ponderar en A y sumar energéticamente las 8 bandas, el espectro reconstruido reproduzca exactamente el <code>LwA_objetivo</code> de la curva importada.</p>\n                ": ".</p>\n                <p><b>General per-band equation:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Final A-weighted sum:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> <code>Lw,b</code> by octave bands. If a specific spectrum of the source group exists, that is the input used. If not, the plugin uses a library/template/fallback and adjusts it to the global operating level.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> according to the selected scenario. That global level does not replace the bands: it sets the operating state and the spectrum provides the frequency distribution.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> temperature <b>T</b>, relative humidity <b>RH</b> and pressure <b>P</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> optional DTM/DSM. It only affects the calculation of <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Per-band acoustic input. It comes from the source-group spectrum (CSV, library, template or fallback adjusted to the global level). The LwA(ws) acoustic curve or the fixed LwA define the turbine's global operating level, and the band spectrum distributes that level among the 8 bands.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Computed per band with a base absorption table <code>α_ref(f)</code> and simplified corrections for temperature, relative humidity and pressure. The current implementation uses the plugin's exact math: <code>α = α_ref(f)·corr_T·corr_RH·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Computed as ground effect by regions. The ground parameter used is a <b>single G per path</b>: manual/global or <b>G_eff</b> derived from the land-use layer.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>It only enters if there is a DTM/DSM and topographic screening is detected. If there is no DTM or no relevant obstacle, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Source acoustic input and bands</h4>\n                <p>In this engine, the term <code>Lw,b</code> is a <b>per-band input datum</b>. The <b>octave bands</b> (63–8000 Hz) are not a result of the ISO nor a table computed by the plugin: they are the <b>frequency grid</b> on which propagation is solved.</p>\n                <p>The plugin combines two pieces:</p>\n                <ul>\n                    <li><b>Global acoustic curve LwA(ws)</b>: it sets the <b>global operating level</b> of the turbine for the selected wind speed or worst case.</li>\n                    <li><b>Band spectrum Lw,b</b>: it distributes that global level among the 8 bands and is the real input used in the band-wise equation.</li>\n                </ul>\n                <p>That spectrum can come from a specific manufacturer/user file or from a reference template. If only a global curve <code>LwA(ws)</code> is available, the plugin first sets the global operating level <code>LwA_target</code> and then builds an absolute per-band spectrum from a reference spectral shape <code>S_b^ref</code>.</p>\n                <p><b>Mathematical reconstruction of the bands when only LwA(ws) exists:</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_target - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>That is: the acoustic curve provides the <b>global operating level</b> and the template/library provides the <b>spectral shape</b>. The shift <b>Δ</b> is computed so that, when A-weighting and energetically summing the 8 bands, the reconstructed spectrum reproduces exactly the <code>LwA_target</code> of the imported curve.</p>\n                ",
    '</b> queda solo como respaldo si la capa no aporta información válida.</p>\n                <p><b>Capa usada:</b> ': '</b> is kept only as a fallback if the layer does not provide valid information.</p>\n                <p><b>Layer used:</b> ',
    '</b> y el del receptor crítico <b>': '</b> and that of the critical receiver <b>',
    "</b>.</li>\n                    <li><b>Conversión a bandas:</b> el motor ISO-aligned necesita un espectro <code>Lw,b</code> en 8 bandas de octava. Si no hay espectro específico, el plugin reconstruye uno desde una plantilla/fallback y lo ajusta para reproducir el LwA operativo.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b> y no generan nivel acústico útil.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula distancia 3D, cotas acústicas, <b>G</b> o <b>G_eff</b> del suelo y, si hay MDT/DSM, el posible apantallamiento topográfico del trayecto.</li>\n                    <li><b>Propagación por banda:</b> en cada banda se aplica <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code>. Adiv depende de la distancia, Aatm,b de frecuencia/atmósfera, Agr,b del suelo y Abar,b del MDT si hay obstáculo relevante.</li>\n                    <li><b>Suma por fuente:</b> las 8 bandas se ponderan en A y se suman energéticamente para obtener el nivel A-ponderado de esa turbina en el receptor.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(LpA,fuente/10))</div>\n                <p><b>Lectura práctica:</b> el receptor crítico es el de mayor nivel total o el que queda con peor margen frente al límite. La columna “fuente dominante” identifica la turbina/grupo que más contribuye, pero el resultado final del receptor incluye todas las fuentes dentro del radio.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo Screening</h3>\n                <p>El modo ISO-aligned es más pesado pero más trazable: usa bandas de octava, ponderación A final, absorción atmosférica dependiente de frecuencia, suelo por regiones y apantallamiento topográfico <b>Abar</b> cuando hay MDT/DSM. Es el modo recomendado para informes técnicos preliminares y revisión de receptores sensibles.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Conversion to bands:</b> the ISO-aligned engine needs a spectrum <code>Lw,b</code> in 8 octave bands. If there is no specific spectrum, the plugin reconstructs one from a template/fallback and adjusts it to reproduce the operating LwA.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b> and do not generate a useful acoustic level.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine, the 3D distance, acoustic heights, ground <b>G</b> or <b>G_eff</b> and, if there is a DTM/DSM, the possible topographic screening of the path are computed.</li>\n                    <li><b>Per-band propagation:</b> in each band <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code> is applied. Adiv depends on distance, Aatm,b on frequency/atmosphere, Agr,b on ground and Abar,b on the DTM if there is a relevant obstacle.</li>\n                    <li><b>Per-source sum:</b> the 8 bands are A-weighted and summed energetically to obtain the A-weighted level of that turbine at the receiver.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(LpA,source/10))</div>\n                <p><b>Practical reading:</b> the critical receiver is the one with the highest total level or the worst margin against the limit. The “dominant source” column identifies the turbine/group that contributes the most, but the receiver's final result includes all sources within the radius.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the Screening mode</h3>\n                <p>The ISO-aligned mode is heavier but more traceable: it uses octave bands, a final A-weighting, frequency-dependent atmospheric absorption, ground by regions and topographic screening <b>Abar</b> when there is a DTM/DSM. It is the recommended mode for preliminary technical reports and review of sensitive receivers.</p>\n            </div>\n            ",
    "</b>.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b>.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula la distancia 3D, la divergencia geométrica, una absorción atmosférica simplificada <code>α·d</code> y una corrección empírica de suelo.</li>\n                    <li><b>Uso del suelo:</b> si hay capa de land-use, el plugin puede calcular un <b>G_eff</b> por trayecto; si no, usa el <b>G global</b> definido por el usuario.</li>\n                    <li><b>Propagación simplificada:</b> se aplica <code>Lp = LwA - Adiv - Aatm - Aground</code>. No hay bandas de octava ni apantallamiento topográfico explícito <code>Abar</code>.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(Lp,fuente/10))</div>\n                <p><b>Lectura práctica:</b> este modo es útil para screening inicial. Si un receptor aparece cerca del límite o en excedencia, conviene recalcularlo con el modo ISO-aligned y revisar espectros, terreno, uso del suelo y límites aplicados.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo ISO-aligned</h3>\n                <p>El modo Screening sacrifica detalle para ganar velocidad. No propaga por bandas, no usa T/HR/P por frecuencia, no calcula <b>Abar</b> desde MDT y resume la atmósfera con un único coeficiente <b>α</b>. Por eso debe interpretarse como preevaluación rápida, no como informe acústico detallado.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b>.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine the 3D distance, the geometrical divergence, a simplified atmospheric absorption <code>α·d</code> and an empirical ground correction are computed.</li>\n                    <li><b>Land use:</b> if there is a land-use layer, the plugin can compute a per-path <b>G_eff</b>; otherwise it uses the <b>global G</b> defined by the user.</li>\n                    <li><b>Simplified propagation:</b> <code>Lp = LwA - Adiv - Aatm - Aground</code> is applied. There are no octave bands and no explicit topographic screening <code>Abar</code>.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(Lp,source/10))</div>\n                <p><b>Practical reading:</b> this mode is useful for initial screening. If a receiver appears close to the limit or in exceedance, it is advisable to recalculate it with the ISO-aligned mode and review spectra, terrain, land use and applied limits.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the ISO-aligned mode</h3>\n                <p>The Screening mode trades detail for speed. It does not propagate by bands, does not use T/RH/P per frequency, does not compute <b>Abar</b> from a DTM and summarizes the atmosphere with a single coefficient <b>α</b>. It should therefore be interpreted as a quick pre-assessment, not as a detailed acoustic report.</p>\n            </div>\n            ",
    '</div>\n                    <p>Ese valor se aplica en el término de suelo del modelo. No se ha derivado un G_eff desde capa de uso del suelo.</p>\n                </div>\n                ': '</div>\n                    <p>That value is applied in the ground term of the model. No G_eff was derived from a land-use layer.</p>\n                </div>\n                ',
    "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>En esta corrida no se ha derivado un G_eff desde una capa de uso del suelo. Aquí <b>d_xy</b> es la distancia horizontal, <b>h_s</b> la altura de fuente y <b>h_r</b> la altura del receptor.</p>\n                ": "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>In this run no G_eff was derived from a land-use layer. Here <b>d_xy</b> is the horizontal distance, <b>h_s</b> the source height and <b>h_r</b> the receiver height.</p>\n                ",
    '</p>\n        </div>\n\n        <h2>2. CÓMO SE HA GENERADO EL RESULTADO</h2>\n        ': '</p>\n        </div>\n\n        <h2>2. HOW THE RESULT WAS GENERATED</h2>\n        ',
    "</td>\n                    <td>Máximo Abar entre todas las turbinas que contribuyen al receptor</td>\n                </tr>\n                <tr>\n                    <td>Abar ponderado por energía</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Maximum Abar among all turbines contributing to the receiver</td>\n                </tr>\n                <tr>\n                    <td>Energy-weighted Abar</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Número de turbinas contribuyentes con Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>NIVEL RESULTANTE</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Number of contributing turbines with Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>RESULTING LEVEL</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Promedio ponderado por la contribución acústica de cada turbina</td>\n                </tr>\n                <tr>\n                    <td>Trayectorias apantalladas</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Average weighted by the acoustic contribution of each turbine</td>\n                </tr>\n                <tr>\n                    <td>Screened paths</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (máximo entre contribuyentes)</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (maximum among contributors)</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td>Abar máximo contribuyentes</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td>Maximum contributors Abar</td>\n                    <td style='text-align: right;'>",
    "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Cumplimiento por Categoría</h3>\n                <ul>": "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Compliance by category</h3>\n                <ul>",
    "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitaciones y recomendaciones</strong>\n            <p><b>Motor Rápido:</b> Apropiado para screening preliminar y mapas ágiles.</p>\n            <p><b>Motor ISO-aligned:</b> Apropiado para estudios técnicos preliminares, comparativas e iteración de diseño.</p>\n            <p><b>Simplificaciones conocidas:</b> Aatm simplificado (tablas + correcciones); Agr y Abar con aproximaciones básicas; directividad Dc asumida 0 dB; Cmet/corrección meteorológica de largo plazo no aplicada.</p>\n            <p><b>Modelos múltiples:</b> soportados mediante capas/grupos fuente independientes. Mezclar varios modelos dentro de una sola capa por atributos no está habilitado en esta versión experimental.</p>\n            <p><b>Raster ISO + MDT:</b> usa la misma lógica de apantallamiento topográfico que los receptores puntuales, pero puede ser costoso en mapas grandes.</p>\n            <p><b>Recomendación:</b> Para estudios regulatorios críticos, validar con mediciones o software comercial certificado.</p>\n        </div>\n        ": "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitations and recommendations</strong>\n            <p><b>Fast engine:</b> Appropriate for preliminary screening and quick maps.</p>\n            <p><b>ISO-aligned engine:</b> Appropriate for preliminary technical studies, comparisons and design iteration.</p>\n            <p><b>Known simplifications:</b> simplified Aatm (tables + corrections); Agr and Abar with basic approximations; directivity Dc assumed 0 dB; long-term Cmet/meteorological correction not applied.</p>\n            <p><b>Multiple models:</b> supported through independent source layers/groups. Mixing several models within a single layer by attributes is not enabled in this experimental release.</p>\n            <p><b>ISO raster + DTM:</b> it uses the same topographic-screening logic as point receivers, but it can be expensive on large maps.</p>\n            <p><b>Recommendation:</b> For critical regulatory studies, validate with measurements or certified commercial software.</p>\n        </div>\n        ",
    '</ul>\n        </div>\n        \n        <h2>5. FÍSICA DETALLADA Y TRAZABILIDAD DEL CÁLCULO</h2>\n        ': '</ul>\n        </div>\n        \n        <h2>5. DETAILED PHYSICS AND CALCULATION TRACEABILITY</h2>\n        ',
    "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUCIÓN POR TIPO DE RECEPTOR</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receptores por Categoría</h3>\n                <ul>": "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUTION BY RECEIVER TYPE</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receivers by category</h3>\n                <ul>",
    '<br><b>Alturas trayectoria dominante:</b> terreno turbina=': '<br><b>Dominant-path heights:</b> turbine terrain=',
    '<br><b>Trayectoria con Abar máximo:</b> fuente=': '<br><b>Path with maximum Abar:</b> source=',
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: Abar=0 en el receptor crítico no implica que el MDT esté desactivado; significa que ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: Abar=0 at the critical receiver does not imply that the DTM is disabled; it means that ",
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: obstáculo dominante estimado ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: estimated dominant obstacle ",
    '<p><b>Interpretación:</b> el espectro final <code>Lw,b</code> es el que entra realmente en la ecuación por bandas. Si existe <code>S_b^ref</code>, corresponde a la plantilla de referencia antes del ajuste global <code>Δ</code>.</p>': '<p><b>Interpretation:</b> the final spectrum <code>Lw,b</code> is the one that actually enters the band-wise equation. If <code>S_b^ref</code> exists, it corresponds to the reference template before the global adjustment <code>Δ</code>.</p>',
    '<p><b>Interpretación:</b> en este grupo no se ha usado una plantilla interna visible; el espectro final <code>Lw,b</code> procede directamente del espectro cargado/importado o de una biblioteca externa.</p>': '<p><b>Interpretation:</b> in this group no visible internal template was used; the final spectrum <code>Lw,b</code> comes directly from the loaded/imported spectrum or from an external library.</p>',
    'Abar máx. dB': 'max Abar dB',
    'Adiv representa la divergencia geométrica, Aatm la atenuación atmosférica simplificada α·distancia y Aground una corrección simplificada del efecto suelo/terreno.': 'Adiv represents the geometrical divergence, Aatm the simplified atmospheric attenuation α·distance and Aground a simplified correction of the ground/terrain effect.',
    'Adiv representa la divergencia geométrica. Aatm se calcula por banda y depende de T, HR y presión, con formulación simplificada. Agr se aplica como término de suelo/terreno y Abar como apantallamiento topográfico básico cuando hay MDT.': 'Adiv represents the geometrical divergence. Aatm is computed per band and depends on T, RH and pressure, with a simplified formulation. Agr is applied as a ground/terrain term and Abar as basic topographic screening when there is a DTM.',
    'Atenuación por MDT en la trayectoria dominante': 'DTM attenuation on the dominant path',
    'Difracción topográfica': 'Topographic diffraction',
    'Efecto del terreno': 'Terrain effect',
    'la línea de visión entre la turbina dominante y este receptor queda despejada según el MDT': 'the line of sight between the dominant turbine and this receiver is clear according to the DTM',
    'no había MDT disponible en este trayecto': 'no DTM was available on this path',
    'no se detectó obstáculo topográfico relevante en el trayecto dominante': 'no relevant topographic obstacle was detected on the dominant path',
    'no se pudo extraer un perfil MDT válido para el trayecto dominante': 'a valid DTM profile could not be extracted for the dominant path',
    'se detectó relieve, pero por debajo del umbral conservador de activación': 'relief was detected, but below the conservative activation threshold',
    'sí · resolución pedida ': 'yes · requested resolution ',
    "La zona '{}' no existe en la base IANA incluida.": "Time zone '{}' does not exist in the bundled IANA database.",
    "No se pudo cargar la zona horaria IANA '{}'. La repo incluye una base IANA local, pero esa zona no se encontró o no pudo abrirse. Prueba con una zona como 'Europe/Madrid' o usa el modo UTC offset fijo. Error zoneinfo: {}; error base incluida: {}": "Could not load the IANA time zone '{}'. The repo bundles a local IANA database, but that zone was not found or could not be opened. Try a zone like 'Europe/Madrid' or use the fixed UTC offset mode. zoneinfo error: {}; bundled database error: {}",
    "Nombre de zona horaria inválido: '{}'": "Invalid time-zone name: '{}'",
    'Python zoneinfo no está disponible en este entorno: {}': 'Python zoneinfo is not available in this environment: {}',
    'Además de patrocinar el proyecto, VelantisWind puede adaptarse a flujos de trabajo existentes: formatos internos de datos, plantillas de informe, procesos de validación, instalación en equipos, formación, automatización o funcionalidades específicas para proyectos eólicos.': 'Beyond sponsoring the project, VelantisWind can be adapted to existing workflows: internal data formats, reporting templates, validation processes, team deployment, training, automation or specific features for wind-energy projects.',
    'VelantisWind es un plugin open source para QGIS orientado a la evaluación preliminar de parques eólicos. Si la herramienta te ahorra tiempo o aporta valor a tu flujo de trabajo, puedes apoyar su mantenimiento o solicitar soporte profesional.': 'VelantisWind is an open-source QGIS plugin for early-stage wind farm assessment. If the tool saves you time or adds value to your workflow, you can support its maintenance or request professional support.',
}
_TO_EN.update(_I18N_ITER8_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER8_TO_EN)
_I18N_ITER8_TO_ES = {v: k for k, v in _I18N_ITER8_TO_EN.items() if v != k}
_TO_ES.update(_I18N_ITER8_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER8_TO_ES)

# Iteration 9: EN-authored strings (Shadow + a few Noise/Energy) -> Spanish.
_I18N_ITER9_TO_ES = {
    'Velantis Wind · Noise calculation': 'Velantis Wind · Cálculo de ruido',
    'Noise · Calculation summary': 'Ruido · Resumen del cálculo',
    'Variantes: wake-only / wake+TI / wake+bloqueo…': 'Variantes: solo estela / estela+TI / estela+bloqueo…',
    'Calculation cancelled by the user': 'Cálculo cancelado por el usuario',
    '\n\n💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).': '\n\n💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).',
    'Raster map created successfully.\n\nCalculated points: ': 'Mapa raster creado correctamente.\n\nPuntos calculados: ',
    'No valid solar positions were found': 'No se encontraron posiciones solares válidas',
    '<h2>Shadow Flicker Calculation Summary</h2>': '<h2>Resumen del cálculo de shadow flicker</h2>',
    'CALCULATION CONFIGURATION': 'CONFIGURACIÓN DEL CÁLCULO',
    'Shadow Flicker Hours by Month and Hour of Day': 'Horas de shadow flicker por mes y hora del día',
    'Time step': 'Paso temporal',
    'Latitude must be between -90° and 90°.': 'La latitud debe estar entre -90° y 90°.',
    'Longitude must be between -180° and 180°.': 'La longitud debe estar entre -180° y 180°.',
    'Minimum sun elevation must be lower than maximum sun elevation.': 'La elevación solar mínima debe ser menor que la máxima.',
    'Calculate Shadow Flicker': 'Calcular shadow flicker',
    'Beyond sponsoring the project, VelantisWind can be adapted to existing workflows: internal data formats, reporting templates, validation processes, team deployment, training, automation or specific features for wind-energy projects.': 'Además de patrocinar el proyecto, VelantisWind puede adaptarse a flujos de trabajo existentes: formatos internos de datos, plantillas de informe, procesos de validación, instalación en equipos, formación, automatización o funcionalidades específicas para proyectos eólicos.',
    'VelantisWind is an open-source QGIS plugin for early-stage wind farm assessment. If the tool saves you time or adds value to your workflow, you can support its maintenance or request professional support.': 'VelantisWind es un plugin open source para QGIS orientado a la evaluación preliminar de parques eólicos. Si la herramienta te ahorra tiempo o aporta valor a tu flujo de trabajo, puedes apoyar su mantenimiento o solicitar soporte profesional.',
}
for _k in list(_I18N_ITER9_TO_ES.keys()):
    _TO_EN.pop(_k, None); _FRAGMENT_TO_EN.pop(_k, None)
_TO_ES.update(_I18N_ITER9_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER9_TO_ES)
_I18N_ITER9_TO_EN = {v: k for k, v in _I18N_ITER9_TO_ES.items() if v != k}
_TO_EN.update(_I18N_ITER9_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER9_TO_EN)


# ===========================================================================
# Iteration 10 (DIRECTION NORMALIZATION): make _TO_EN strictly ES->EN and
# _TO_ES strictly EN->ES. Fixes mixed-language UI where English labels were
# wrongly stored in the EN map (and vice-versa). Authoritative final pass.
# ===========================================================================
_CANON_TO_EN = {
    '\n\n💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).': '\n\n💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).',
    "\n        \n        <div class='card'>\n            <h3>📊 Estadísticos de Atenuaciones (Receptores Cubiertos)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación). Para Abar se usa el máximo entre las turbinas contribuyentes de cada receptor, no solo la trayectoria dominante.</i></p>\n            <table>\n                <tr>\n                    <th>Término</th>\n                    <th style='text-align: right;'>Media [dB]</th>\n                    <th style='text-align: right;'>Máximo [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (divergencia geométrica)</td>\n                    <td style='text-align: right;'>": "\n        \n        <div class='card'>\n            <h3>📊 Attenuation statistics (covered receivers)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Raw attenuation magnitudes are shown (not the algebraic sign inside the equation). For Abar, the maximum among the contributing turbines of each receiver is used, not only the dominant path.</i></p>\n            <table>\n                <tr>\n                    <th>Term</th>\n                    <th style='text-align: right;'>Mean [dB]</th>\n                    <th style='text-align: right;'>Maximum [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (geometrical divergence)</td>\n                    <td style='text-align: right;'>",
    "\n                </table>\n                <h4>5. Efecto suelo por regiones</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>El término de suelo se descompone en <b>As</b> (región de fuente), <b>Am</b> (región media) y <b>Ar</b> (región de receptor). En esta implementación no se usan tres parámetros de suelo independientes <code>Gs/Gm/Gr</code>, sino un <b>único G por trayecto</b>. Matemáticamente, el plugin aplica:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_medio)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>donde <b>h_s</b> es la altura característica de la fuente, <b>h_r</b> la del receptor, <b>h_medio</b> la altura media del trayecto y <b>G_m≈0</b> en la aproximación actual para condiciones favorables de propagación. Ese valor único de suelo puede ser:</p>\n                <ul>\n                    <li><b>G manual/global</b>, si el usuario fija un único valor.</li>\n                    <li><b>G_eff</b>, si existe capa de uso del suelo y se calcula una media ponderada por longitud del trayecto.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Significado físico de G:</b> representa el carácter acústico del terreno y controla cómo influye el suelo en la propagación. <b>G≈0</b> indica suelo duro (urbano, asfalto, roca), <b>G≈1</b> suelo blando/poroso (agrícola, pradera, forestal) y valores intermedios representan terreno mixto.</p>\n                <p><b>Qué significa “desde capa”:</b> el plugin corta el trayecto fuente–receptor contra la capa de uso del suelo, asigna un valor <b>G_i</b> a cada polígono interceptado y calcula un único <b>G_eff</b> para ese trayecto. Ese es el valor que entra realmente en <b>Agr,b</b>; el <b>G global</b> mostrado en el informe queda solo como respaldo.</p>\n                <p><b>Convención del informe:</b> <b>Agr,b</b> se muestra aquí como una <b>magnitud positiva de atenuación</b>. En la ecuación principal se resta al nivel de fuente igual que Adiv, Aatm y Abar.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>Término base A_ground(h)</th></tr>\n                    ": "\n                </table>\n                <h4>5. Ground effect by regions</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>The ground term is split into <b>As</b> (source region), <b>Am</b> (middle region) and <b>Ar</b> (receiver region). This implementation does not use three independent ground parameters <code>Gs/Gm/Gr</code>, but a <b>single G per path</b>. Mathematically, the plugin applies:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_mid)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>where <b>h_s</b> is the characteristic source height, <b>h_r</b> the receiver height, <b>h_mid</b> the mean path height and <b>G_m≈0</b> in the current approximation for favourable propagation conditions. That single ground value can be:</p>\n                <ul>\n                    <li><b>Manual/global G</b>, if the user sets a single value.</li>\n                    <li><b>G_eff</b>, if a land-use layer exists and a length-weighted mean along the path is computed.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Physical meaning of G:</b> it represents the acoustic character of the terrain and controls how the ground influences propagation. <b>G≈0</b> indicates hard ground (urban, asphalt, rock), <b>G≈1</b> soft/porous ground (agricultural, grassland, forest) and intermediate values represent mixed terrain.</p>\n                <p><b>What “from layer” means:</b> the plugin clips the source–receiver path against the land-use layer, assigns a <b>G_i</b> value to each intercepted polygon and computes a single <b>G_eff</b> for that path. That is the value actually entering <b>Agr,b</b>; the <b>global G</b> shown in the report is kept only as a fallback.</p>\n                <p><b>Report convention:</b> <b>Agr,b</b> is shown here as a <b>positive attenuation magnitude</b>. In the main equation it is subtracted from the source level just like Adiv, Aatm and Abar.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>Base term A_ground(h)</th></tr>\n                    ",
    "\n                </table>\n                <h4>6. Apantallamiento topográfico con MDT</h4>\n                <p>El MDT <b>no cambia la emisión</b> de la turbina ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto</b> y alimentar el término <b>Abar,b</b>.</p>\n                <ol>\n                    <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con muestreo adaptativo.</li>\n                    <li><b>Línea de visión:</b> se construye la recta entre la altura efectiva de fuente y la altura del receptor. Si el terreno queda siempre por debajo, entonces <b>Abar,b = 0</b>.</li>\n                    <li><b>Obstáculo dominante:</b> si una loma o cresta sobresale, se calcula la altura sobre la línea de visión:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                <p>Cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                <ol start='4'>\n                    <li><b>Geometría real del obstáculo:</b> el plugin usa la posición real del obstáculo dominante y calcula <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor).</li>\n                    <li><b>Activación conservadora:</b> no se activa <b>Abar</b> por pequeñas irregularidades del MDT; se exige un umbral mínimo ligado a la resolución del raster.</li>\n                    <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos y un número de Fresnel:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>Ese número se transforma después en una atenuación <b>Abar,b</b> dependiente de la frecuencia mediante la aproximación actual del plugin:</p>\n                <div class='formula'>si C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>En la implementación actual, <b>Abar</b> se limita además a valores razonables (capado superior) para evitar sobreatenuaciones espurias. Si no hay MDT o no hay obstáculo relevante, entonces <b>Abar,b = 0</b>.</p>\n                <h4>7. Ponderación A usada al final</h4>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ": "\n                </table>\n                <h4>6. Topographic screening with DTM</h4>\n                <p>The DTM <b>does not change the emission</b> of the turbine nor the atmospheric absorption. Its role is to describe the <b>real path geometry</b> and feed the <b>Abar,b</b> term.</p>\n                <ol>\n                    <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with adaptive sampling.</li>\n                    <li><b>Line of sight:</b> the straight line between the effective source height and the receiver height is built. If the terrain always stays below, then <b>Abar,b = 0</b>.</li>\n                    <li><b>Dominant obstacle:</b> if a hill or ridge sticks out, its height above the line of sight is computed:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                <p>When <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                <ol start='4'>\n                    <li><b>Real obstacle geometry:</b> the plugin uses the real position of the dominant obstacle and computes <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver).</li>\n                    <li><b>Conservative activation:</b> <b>Abar</b> is not triggered by small DTM irregularities; a minimum threshold linked to the raster resolution is required.</li>\n                    <li><b>Fresnel-type diffraction:</b> from that geometry a path difference and a Fresnel number are estimated:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>That number is then turned into a frequency-dependent <b>Abar,b</b> attenuation through the plugin's current approximation:</p>\n                <div class='formula'>if C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>In the current implementation, <b>Abar</b> is additionally capped to reasonable values (upper cap) to avoid spurious over-attenuation. If there is no DTM or no relevant obstacle, then <b>Abar,b = 0</b>.</p>\n                <h4>7. A-weighting used at the end</h4>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ",
    '\n                </table>\n                <p><b>Lectura del receptor crítico:</b> la tabla del apartado de receptor crítico muestra magnitudes de atenuación para trazabilidad. El <b>nivel resultante</b> no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.</p>\n            </div>\n            ': '\n                </table>\n                <p><b>Critical-receiver reading:</b> the table in the critical-receiver section shows attenuation magnitudes for traceability. The <b>resulting level</b> must not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and over the contributing sources within the calculation radius.</p>\n            </div>\n            ',
    "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G</h3>\n                    <p>En esta corrida el efecto suelo se ha calculado con un <b>G único manual</b> para todo el trayecto:</p>\n                    <div class='formula'>G = ": "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G calculation</h3>\n                    <p>In this run the ground effect was computed with a <b>single manual G</b> for the whole path:</p>\n                    <div class='formula'>G = ",
    "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G_eff</h3>\n                    <p>Cuando el modo suelo es <b>desde capa</b>, el cálculo no usa un único valor manual para todo el parque. Para cada trayecto fuente–receptor se obtiene un <b>G_eff</b> calculado desde la capa de uso del suelo:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>donde <b>G_i</b> es el valor asignado a cada polígono interceptado por el trayecto y <b>L_i</b> es la longitud del trayecto dentro de ese polígono.</p>\n                    <ul>\n                        <li><b>G = 0</b>: suelo duro (urbano/asfalto/roca).</li>\n                        <li><b>G = 0.5</b>: terreno mixto.</li>\n                        <li><b>G = 1</b>: suelo blando/poroso (agrícola, pradera, forestal, vegetado).</li>\n                    </ul>\n                    <p><b>Importante:</b> el <b>G global</b> mostrado en el informe es solo un valor de respaldo. Cuando hay capa de uso del suelo, el cálculo usa realmente <b>G_eff</b> por trayecto. En esta corrida, el valor medio efectivo fue <b>": "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G_eff calculation</h3>\n                    <p>When the ground mode is <b>from layer</b>, the calculation does not use a single manual value for the whole wind farm. For each source–receiver path a <b>G_eff</b> is computed from the land-use layer:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>where <b>G_i</b> is the value assigned to each polygon intercepted by the path and <b>L_i</b> is the path length inside that polygon.</p>\n                    <ul>\n                        <li><b>G = 0</b>: hard ground (urban/asphalt/rock).</li>\n                        <li><b>G = 0.5</b>: mixed terrain.</li>\n                        <li><b>G = 1</b>: soft/porous ground (agricultural, grassland, forest, vegetated).</li>\n                    </ul>\n                    <p><b>Important:</b> the <b>global G</b> shown in the report is only a fallback value. When a land-use layer exists, the calculation actually uses <b>G_eff</b> per path. In this run, the effective mean value was <b>",
    "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En el motor ISO-aligned, el MDT <b>no cambia la emisión de la turbina</b> ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto fuente–receptor</b> y alimentar el término de apantallamiento topográfico <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>Cómo entra el MDT en el cálculo</h4>\n                    <ol>\n                        <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con un <b>muestreo adaptativo</b>, ajustado a la distancia y a la resolución del raster. El perfil se calcula <b>una sola vez</b> por par fuente–receptor y se reutiliza en las 8 bandas para reducir tiempo de cálculo.</li>\n                        <li><b>Línea de visión directa:</b> se compara el perfil con la recta que une la fuente acústica a su altura efectiva y el receptor a su altura de evaluación. Si el terreno queda siempre por debajo de esa recta, no hay obstáculo topográfico relevante y <b>Abar,b = 0</b>.</li>\n                        <li><b>Detección de obstáculo dominante:</b> si una loma o cresta del MDT sobresale por encima de la línea de visión, el modelo interpreta que existe pantalla topográfica. La magnitud clave es la altura del obstáculo sobre la línea de visión:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                    <p>cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                    <ol start='4'>\n                        <li><b>Activación conservadora:</b> no se activa Abar por pequeñas irregularidades del relieve; se aplica un umbral mínimo ligado a la resolución del MDT.</li>\n                        <li><b>Geometría real del obstáculo:</b> el cálculo usa la <b>posición real</b> del obstáculo dominante y obtiene <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor) reales, en lugar de asumir siempre un obstáculo en el punto medio.</li>\n                        <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos aproximada y se transforma en una atenuación dependiente de la frecuencia:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>donde <b>δ</b> es la diferencia de caminos aproximada, <b>f</b> la frecuencia y <b>c</b> la velocidad del sonido. El número <b>C</b> se traduce después a una atenuación <b>Abar,b</b> mayor cuanto más bloquea el relieve el trayecto. Esta es la misma aproximación implementada en el cálculo.</p>\n                    <p><b>Interpretación física:</b> en terreno plano o cuando no hay intersección con la línea de visión, <b>Abar</b> suele ser despreciable. En terreno complejo, el MDT puede introducir varios dB de atenuación adicional y cambiar el receptor crítico.</p>\n                    <p><b>Implementación actual:</b> obstáculo dominante único, perfil adaptativo con límites de coste, geometría real del obstáculo, activación conservadora y atenuación capada a valores razonables.</p>\n                    <p><b>MDT usado en esta corrida:</b> ": "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In the ISO-aligned engine, the DTM <b>does not change the turbine emission</b> nor the atmospheric absorption. Its role is to describe the <b>real source–receiver path geometry</b> and feed the topographic screening term <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>How the DTM enters the calculation</h4>\n                    <ol>\n                        <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with <b>adaptive sampling</b>, adjusted to distance and raster resolution. The profile is computed <b>only once</b> per source–receiver pair and reused across the 8 bands to reduce computation time.</li>\n                        <li><b>Direct line of sight:</b> the profile is compared with the straight line joining the acoustic source at its effective height and the receiver at its evaluation height. If the terrain always stays below that line, there is no relevant topographic obstacle and <b>Abar,b = 0</b>.</li>\n                        <li><b>Dominant-obstacle detection:</b> if a hill or ridge in the DTM rises above the line of sight, the model interprets that a topographic screen exists. The key magnitude is the obstacle height above the line of sight:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                    <p>when <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                    <ol start='4'>\n                        <li><b>Conservative activation:</b> Abar is not triggered by small relief irregularities; a minimum threshold linked to the DTM resolution is applied.</li>\n                        <li><b>Real obstacle geometry:</b> the calculation uses the <b>real position</b> of the dominant obstacle and obtains real <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver), instead of always assuming an obstacle at the midpoint.</li>\n                        <li><b>Fresnel-type diffraction:</b> from that geometry an approximate path difference is estimated and turned into a frequency-dependent attenuation:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>where <b>δ</b> is the approximate path difference, <b>f</b> the frequency and <b>c</b> the speed of sound. The number <b>C</b> is then translated into an <b>Abar,b</b> attenuation that grows the more the relief blocks the path. This is the same approximation implemented in the calculation.</p>\n                    <p><b>Physical interpretation:</b> on flat terrain or when there is no intersection with the line of sight, <b>Abar</b> is usually negligible. On complex terrain, the DTM can introduce several dB of additional attenuation and change the critical receiver.</p>\n                    <p><b>Current implementation:</b> single dominant obstacle, adaptive profile with cost limits, real obstacle geometry, conservative activation and attenuation capped to reasonable values.</p>\n                    <p><b>DTM used in this run:</b> ",
    "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En esta corrida <b>no se ha usado MDT/DSM</b>, por lo que el término de apantallamiento topográfico se fija en:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>La evaluación se realiza sin introducir pantallas topográficas. La geometría del trayecto se resuelve sin perfil del terreno y el cálculo depende de Lw,b, Adiv, Aatm,b y Agr,b.</p>\n                </div>\n                ": "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In this run <b>no DTM/DSM was used</b>, so the topographic screening term is set to:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>The assessment is performed without introducing topographic screens. The path geometry is resolved without a terrain profile and the calculation depends on Lw,b, Adiv, Aatm,b and Agr,b.</p>\n                </div>\n                ",
    "\n                <div class='note'>\n                    <b>Lectura correcta de Abar:</b> el valor <b>Abar trayectoria dominante</b> corresponde solo a la turbina que más contribuye al receptor y a su banda dominante. El nivel total del receptor se obtiene sumando energéticamente todas las turbinas y bandas.\n                    <br><b>Abar máximo entre turbinas contribuyentes:</b> ": "\n                <div class='note'>\n                    <b>Correct reading of Abar:</b> the <b>dominant-path Abar</b> value corresponds only to the turbine that contributes most to the receiver and to its dominant band. The total receiver level is obtained by energetically summing all turbines and bands.\n                    <br><b>Maximum Abar among contributing turbines:</b> ",
    "\n                <h4>3. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor. Aquí <b>d</b> sale de las coordenadas de turbina y receptor junto con sus alturas de evaluación.</p>\n                <h4>4. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm,b = α(f, T, HR, P) · d</div>\n                <p>La absorción atmosférica se calcula por banda a partir de un coeficiente base de referencia y tres factores correctores. La dependencia física con temperatura, humedad relativa y presión <b>sí se representa</b>, pero mediante una <b>aproximación simplificada del plugin</b>, no mediante la formulación analítica completa de ISO 9613-1.</p>\n                <div class='formula'>α(f, T, HR, P) = α_ref(f) · corr_T · corr_HR · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_HR = 1 + 0.003·|HR - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretación de las correcciones:</b> <b>T</b> se introduce en °C respecto a una referencia de 15 °C; <b>HR</b> se compara con una humedad óptima de referencia del 50% y la corrección crece al alejarse de ese valor; y <b>P</b> se introduce en kPa respecto a una referencia de 101.325 kPa con una corrección inversa. Estos factores modifican únicamente el bloque atmosférico <b>Aatm,b</b>: no alteran ni la emisión de la turbina, ni el efecto del suelo, ni el término de MDT/apantallamiento.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ": "\n                <h4>3. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance. Here <b>d</b> comes from the turbine and receiver coordinates together with their evaluation heights.</p>\n                <h4>4. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm,b = α(f, T, RH, P) · d</div>\n                <p>Atmospheric absorption is computed per band from a reference base coefficient and three correction factors. The physical dependence on temperature, relative humidity and pressure <b>is represented</b>, but through a <b>simplified plugin approximation</b>, not through the full analytical formulation of ISO 9613-1.</p>\n                <div class='formula'>α(f, T, RH, P) = α_ref(f) · corr_T · corr_RH · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_RH = 1 + 0.003·|RH - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretation of the corrections:</b> <b>T</b> is entered in °C relative to a 15 °C reference; <b>RH</b> is compared with a reference optimum humidity of 50% and the correction grows as it moves away from that value; and <b>P</b> is entered in kPa relative to a 101.325 kPa reference with an inverse correction. These factors modify only the atmospheric block <b>Aatm,b</b>: they alter neither the turbine emission, nor the ground effect, nor the DTM/screening term.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ",
    "\n                <h4>3. Efecto suelo simplificado con uso del suelo</h4>\n                <p>En el motor rápido el término <b>Aground</b> sigue siendo empírico, pero el parámetro de suelo puede venir de la capa de uso del suelo como un <b>G_eff</b> por trayecto:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>Ese <b>G_eff</b> se usa después dentro de la corrección simplificada de terreno del motor rápido. El valor global <b>G = ": "\n                <h4>3. Simplified ground effect with land use</h4>\n                <p>In the fast engine the <b>Aground</b> term is still empirical, but the ground parameter can come from the land-use layer as a per-path <b>G_eff</b>:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>That <b>G_eff</b> is then used inside the fast engine's simplified terrain correction. The global value <b>G = ",
    "\n                <h4>3. Efecto suelo simplificado</h4>\n                <p>El término <b>Aground</b> es una corrección empírica del terreno controlada por un único parámetro manual:</p>\n                <div class='formula'>G = ": "\n                <h4>3. Simplified ground effect</h4>\n                <p>The <b>Aground</b> term is an empirical terrain correction controlled by a single manual parameter:</p>\n                <div class='formula'>G = ",
    '\n                <h4>4. MDT / topografía</h4>\n                <p>En el motor rápido el MDT no introduce un término explícito de apantallamiento topográfico. Aunque exista una capa de relieve en el proyecto, este modo no calcula <b>Abar</b>, no extrae línea de visión ni aplica difracción; por tanto la física se basa únicamente en <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> y la corrección empírica de terreno <b>Aground</b>.</p>\n            ': '\n                <h4>4. DTM / topography</h4>\n                <p>In the fast engine the DTM does not introduce an explicit topographic screening term. Even if a relief layer exists in the project, this mode does not compute <b>Abar</b>, does not extract a line of sight and does not apply diffraction; therefore the physics relies only on <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> and the empirical terrain correction <b>Aground</b>.</p>\n            ',
    '\n                <h4>5. Qué no hace este modo</h4>\n                <p>El motor rápido no trabaja por bandas, no calcula <b>Lw,b</b>, no introduce <b>Abar</b> y no extrae línea de visión ni difracción desde MDT. Por tanto, es apropiado para screening y comparativas rápidas, pero no para análisis espectral detallado.</p>\n            </div>\n            ': '\n                <h4>5. What this mode does not do</h4>\n                <p>The fast engine does not work by bands, does not compute <b>Lw,b</b>, does not introduce <b>Abar</b> and does not extract a line of sight or diffraction from a DTM. It is therefore appropriate for screening and quick comparisons, but not for detailed spectral analysis.</p>\n            </div>\n            ',
    "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo ISO-aligned</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin para que el resultado por receptor sea trazable. El nivel final de cada receptor <b>no sale de una única resta simple</b>, sino de calcular todas las contribuciones fuente–receptor dentro del radio de cálculo y sumarlas energéticamente.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 How the ISO-aligned calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows so that the per-receiver result is traceable. The final level of each receiver <b>does not come from a single simple subtraction</b>, but from computing all source–receiver contributions within the calculation radius and summing them energetically.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>",
    "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo Screening</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin en el modo rápido. El objetivo es obtener una estimación ágil para mapas, comparación de alternativas y detección inicial de receptores sensibles.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 How the Screening calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows in the fast mode. The goal is to obtain a quick estimate for maps, comparison of alternatives and initial detection of sensitive receivers.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>",
    "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor ISO-aligned</h3>\n                <p>Este motor trabaja en <b>8 bandas de octava</b> (63–8000 Hz). Las bandas no son un resultado del cálculo, sino la <b>malla frecuencial del método</b>. Para aplicar la propagación por bandas, el cálculo necesita un <b>input acústico por banda</b> de la fuente <code>Lw,b</code>. Ese input puede venir de un espectro medido/importado o de una plantilla/fallback ajustada al nivel global operativo.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ": "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the ISO-aligned engine</h3>\n                <p>This engine works in <b>8 octave bands</b> (63–8000 Hz). The bands are not a calculation result, but the <b>frequency grid of the method</b>. To apply band-wise propagation, the calculation needs a per-band acoustic input <code>Lw,b</code> from the source. That input can come from a measured/imported spectrum or from a template/fallback adjusted to the global operating level.</p>\n                <p><b>Operating scenario of this run:</b> ",
    "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor rápido</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>El motor rápido trabaja con un único nivel global <b>LwA</b> por grupo fuente. Está pensado para screening, mapas ágiles y comparativas rápidas, sacrificando detalle espectral frente a velocidad. En este modo <b>no hay propagación por bandas</b> ni término explícito de apantallamiento topográfico.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ": "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the fast engine</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>The fast engine works with a single global level <b>LwA</b> per source group. It is intended for screening, quick maps and fast comparisons, trading spectral detail for speed. In this mode there is <b>no band-wise propagation</b> and no explicit topographic screening term.</p>\n                <p><b>Operating scenario of this run:</b> ",
    ' % del AEP neto del parque)': ' % of the wind farm net AEP)',
    ' %</b> uniforme como fallback. Este valor afecta directamente al AEP en wakes TI-driven (Niayifar, Zong, TurboGaussian, TurboNOJ). Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.</p>': ' %</b> uniformly as fallback. This value directly affects AEP in TI-driven wakes (Niayifar, Zong, TurboGaussian, TurboNOJ). For representative results, load a TI raster in the resource section or review the turbulence sensitivity.</p>',
    ' Otros receptores sí presentan apantallamiento (Abar máx. ': ' Other receivers do show screening (max Abar ',
    ' Umbral de activación: ': ' Activation threshold: ',
    ' dB · <b>Abar ponderado por contribución energética:</b> ': ' dB · <b>Abar weighted by energetic contribution:</b> ',
    ' dB. Este desplazamiento eleva o reduce toda la plantilla espectral para que su suma A-ponderada reproduzca el <code>LwA_objetivo</code> de la curva acústica o del LwA fijo.</p>': ' dB. This shift raises or lowers the entire spectral template so that its A-weighted sum reproduces the <code>LwA_target</code> of the acoustic curve or of the fixed LwA.</p>',
    ' dB/m</b>. En el motor rápido, la absorción atmosférica se resume en un único coeficiente constante, por lo que <b>T</b>, <b>HR</b> y <b>P</b> <b>no entran explícitamente</b> en el cálculo. Esa es una de las simplificaciones clave frente al modo ISO-aligned.</p>\n                ': ' dB/m</b>. In the fast engine, atmospheric absorption is summarized in a single constant coefficient, so <b>T</b>, <b>RH</b> and <b>P</b> <b>do not enter explicitly</b> in the calculation. That is one of the key simplifications compared with the ISO-aligned mode.</p>\n                ',
    ' m AGL · altura acústica receptor=': ' m AGL · receiver acoustic height=',
    ' m AGL · altura acústica turbina=': ' m AGL · turbine acoustic height=',
    ' m · terreno receptor=': ' m · receiver terrain=',
    ' m</b>) y la capa de uso del suelo si existe.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> cada modelo o grupo de turbinas trabaja con un único <b>LwA operativo</b>, definido por un valor fijo o por una curva <code>LwA(ws)</code>. En esta corrida: <b>': ' m</b>) and the land-use layer if it exists.</li>\n                    <li><b>Acoustic state of each source group:</b> each turbine model or group works with a single <b>operating LwA</b>, defined by a fixed value or by an <code>LwA(ws)</code> curve. In this run: <b>',
    ' m</b>), la capa de uso del suelo si existe y el MDT/DSM si está activo.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> para cada modelo o grupo de turbinas se obtiene un <b>LwA operativo</b> desde un valor fijo o desde una curva <code>LwA(ws)</code>. En esta corrida: <b>': ' m</b>), the land-use layer if it exists and the DTM/DSM if it is active.</li>\n                    <li><b>Acoustic state of each source group:</b> for each turbine model or group an <b>operating LwA</b> is obtained from a fixed value or from an <code>LwA(ws)</code> curve. In this run: <b>',
    ' receptor(es) con al menos una turbina dentro del radio máximo.': ' receiver(s) with at least one turbine inside the maximum radius.',
    ' receptores</strong> dentro del radio<br>\n                <strong>': ' receivers</strong> within the radius<br>\n                <strong>',
    ' receptores</strong> superan límites<br>\n                <strong>': ' receivers</strong> exceed limits<br>\n                <strong>',
    '%. Es válido para una prueba, pero no representa variación vertical de turbulencia.': '%. It is valid for a test, but it does not represent vertical variation of turbulence.',
    "' es bytes (lectura errónea).": "' is bytes (incorrect read).",
    "' es str (lectura errónea).": "' is str (incorrect read).",
    "' está vacía.": "' is empty.",
    "' no disponible en esta instalación de PyWake. Intentando fallback a SelfSimilarityDeficit2020...": "' not available in this PyWake installation. Trying fallback to SelfSimilarityDeficit2020...",
    "' no está en el mismo CRS que el proyecto. Se ignora en el cálculo de ruido.": "' is not in the same CRS as the project. It is ignored in the noise calculation.",
    "' no tiene suficientes valores para definir bounds.": "' does not have enough values to define bounds.",
    "'. Revisa la estructura exportada de WAsP.": "'. Check the exported WAsP structure.",
    "'. Selecciona los CSV de coordenadas por modelo en el diálogo o crea un turbines.csv con columnas x,y[,model_id].": "'. Select the per-model coordinate CSVs in the dialog or create a turbines.csv with x,y[,model_id] columns.",
    "'; se conserva el primer raster para esa altura.": "'; the first raster for that height is kept.",
    '(acoplamiento más estable). El resto de la selección se ha respetado.': '(more stable coupling). The rest of the selection was preserved.',
    '(se muestreará terreno fuente/receptor en el método acústico).': '(source/receiver terrain will be sampled in the acoustic method).',
    ') sin altura identificable; se aplicará a todas las alturas del WRG como aproximación.': ') with no identifiable height; it will be applied to all WRG heights as an approximation.',
    '): altura negativa no válida.': '): invalid negative height.',
    '): límites negativos no válidos.': '): invalid negative limits.',
    ", el desglose de turbulencia se interpreta como TI ambiente only frente a TI ambiente + turbulencia añadida por estela. No se ejecuta un caso 'sin TI', porque el Site de PyWake necesita una TI ambiente física.": ", the turbulence breakdown is interpreted as ambient-TI only versus ambient TI + wake-added turbulence. A 'no TI' case is not run, because the PyWake Site needs a physical ambient TI.",
    ', leído': ', read',
    ', sin altura)': ', no height)',
    '- Módulo:': '- Module:',
    '-> usando selección automática segura.': '-> using safe automatic selection.',
    '. El cálculo solar usa UTC internamente y acumula resultados por mes/hora local.': '. Solar calculation uses UTC internally and accumulates results by local month/hour.',
    '. El plugin aplicará esa TI a todas las alturas como aproximación.': '. The plugin will apply that TI to all heights as an approximation.',
    '. Probando loader estándar…': '. Trying the standard loader…',
    '. Se usará fallback fijo.': '. A fixed fallback will be used.',
    '.</p>\n                    <p><b>Qué representa cada columna:</b> <code>S_b^ref</code> es la plantilla espectral de referencia (si existe), <code>A_weight,b</code> la ponderación A de cada banda y <code>Lw,b</code> el nivel final en dB realmente usado por el cálculo.</p>': '.</p>\n                    <p><b>What each column represents:</b> <code>S_b^ref</code> is the reference spectral template (if any), <code>A_weight,b</code> the A-weighting of each band and <code>Lw,b</code> the final level in dB actually used by the calculation.</p>',
    ".</p>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> un único nivel global <b>LwA</b> por grupo fuente.</li>\n                    <li><b>Nivel operativo global:</b> sale de un <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> para la velocidad o peor caso seleccionados.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> en este modo no se usan T/HR/P; la absorción se resume en un único coeficiente <b>α</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> el MDT no entra como apantallamiento explícito en este modo.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Input global de la fuente. Sale de un valor fijo por grupo o de una curva acústica <code>LwA(ws)</code> para la velocidad/peor caso seleccionados.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Se calcula con un único coeficiente constante <code>α</code> multiplicado por la distancia.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Corrección empírica del efecto suelo. El parámetro de suelo puede ser un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                </table>\n                <h4>2. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor.</p>\n                <h4>3. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>En esta corrida se ha usado <b>α = ": ".</p>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> a single global level <b>LwA</b> per source group.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> for the selected wind speed or worst case.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> in this mode T/RH/P are not used; absorption is summarized in a single coefficient <b>α</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> the DTM does not enter as explicit screening in this mode.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Global source input. It comes from a fixed value per group or from an <code>LwA(ws)</code> acoustic curve for the selected wind speed/worst case.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Computed with a single constant coefficient <code>α</code> multiplied by the distance.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Empirical ground-effect correction. The ground parameter can be a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</td></tr>\n                </table>\n                <h4>2. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance.</p>\n                <h4>3. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>In this run <b>α = ",
    ".</p>\n                <p><b>Ecuación general por banda:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Suma A-ponderada final:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> <code>Lw,b</code> por bandas de octava. Si existe espectro específico del grupo fuente, ese es el input usado. Si no, el plugin usa una biblioteca/plantilla/fallback y la ajusta al nivel global operativo.</li>\n                    <li><b>Nivel operativo global:</b> viene de <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> según el escenario seleccionado. Ese nivel global no sustituye a las bandas: fija el estado operativo y el espectro aporta el reparto frecuencial.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> temperatura <b>T</b>, humedad relativa <b>HR</b> y presión <b>P</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> MDT/DSM opcional. Solo afecta al cálculo de <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Input acústico por bandas. Sale del espectro del grupo fuente (CSV, biblioteca, plantilla o fallback ajustado al nivel global). La curva acústica LwA(ws) o el LwA fijo definen el nivel global operativo de la turbina, y el espectro por bandas reparte ese nivel entre las 8 bandas.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Se calcula por banda con una tabla base de absorción <code>α_ref(f)</code> y correcciones simplificadas por temperatura, humedad relativa y presión. La implementación actual usa la matemática exacta del plugin: <code>α = α_ref(f)·corr_T·corr_HR·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Se calcula como efecto suelo por regiones. El parámetro de suelo usado es un <b>G único por trayecto</b>: manual/global o <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>Solo entra si hay MDT/DSM y se detecta apantallamiento topográfico. Si no hay MDT o no hay obstáculo relevante, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Input acústico de fuente y bandas</h4>\n                <p>En este motor, el término <code>Lw,b</code> es un <b>dato de entrada por banda</b>. Las <b>bandas de octava</b> (63–8000 Hz) no son un resultado de la ISO ni una tabla calculada por el plugin: son la <b>malla frecuencial</b> sobre la que se resuelve la propagación.</p>\n                <p>El plugin combina dos piezas:</p>\n                <ul>\n                    <li><b>Curva acústica global LwA(ws)</b>: fija el <b>nivel operativo global</b> de la turbina para la velocidad de viento o el peor caso seleccionado.</li>\n                    <li><b>Espectro por bandas Lw,b</b>: reparte ese nivel global entre las 8 bandas y es el input real usado en la ecuación por bandas.</li>\n                </ul>\n                <p>Ese espectro puede proceder de un archivo específico del fabricante/usuario o de una plantilla de referencia. Si solo se dispone de una curva global <code>LwA(ws)</code>, el plugin fija primero el nivel global operativo <code>LwA_objetivo</code> y después construye un espectro absoluto por bandas a partir de una forma espectral de referencia <code>S_b^ref</code>.</p>\n                <p><b>Reconstrucción matemática de las bandas cuando solo existe LwA(ws):</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_objetivo - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>Es decir: la curva acústica aporta el <b>nivel global operativo</b> y la plantilla/biblioteca aporta la <b>forma espectral</b>. El desplazamiento <b>Δ</b> se calcula para que, al ponderar en A y sumar energéticamente las 8 bandas, el espectro reconstruido reproduzca exactamente el <code>LwA_objetivo</code> de la curva importada.</p>\n                ": ".</p>\n                <p><b>General per-band equation:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Final A-weighted sum:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> <code>Lw,b</code> by octave bands. If a specific spectrum of the source group exists, that is the input used. If not, the plugin uses a library/template/fallback and adjusts it to the global operating level.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> according to the selected scenario. That global level does not replace the bands: it sets the operating state and the spectrum provides the frequency distribution.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> temperature <b>T</b>, relative humidity <b>RH</b> and pressure <b>P</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> optional DTM/DSM. It only affects the calculation of <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Per-band acoustic input. It comes from the source-group spectrum (CSV, library, template or fallback adjusted to the global level). The LwA(ws) acoustic curve or the fixed LwA define the turbine's global operating level, and the band spectrum distributes that level among the 8 bands.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Computed per band with a base absorption table <code>α_ref(f)</code> and simplified corrections for temperature, relative humidity and pressure. The current implementation uses the plugin's exact math: <code>α = α_ref(f)·corr_T·corr_RH·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Computed as ground effect by regions. The ground parameter used is a <b>single G per path</b>: manual/global or <b>G_eff</b> derived from the land-use layer.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>It only enters if there is a DTM/DSM and topographic screening is detected. If there is no DTM or no relevant obstacle, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Source acoustic input and bands</h4>\n                <p>In this engine, the term <code>Lw,b</code> is a <b>per-band input datum</b>. The <b>octave bands</b> (63–8000 Hz) are not a result of the ISO nor a table computed by the plugin: they are the <b>frequency grid</b> on which propagation is solved.</p>\n                <p>The plugin combines two pieces:</p>\n                <ul>\n                    <li><b>Global acoustic curve LwA(ws)</b>: it sets the <b>global operating level</b> of the turbine for the selected wind speed or worst case.</li>\n                    <li><b>Band spectrum Lw,b</b>: it distributes that global level among the 8 bands and is the real input used in the band-wise equation.</li>\n                </ul>\n                <p>That spectrum can come from a specific manufacturer/user file or from a reference template. If only a global curve <code>LwA(ws)</code> is available, the plugin first sets the global operating level <code>LwA_target</code> and then builds an absolute per-band spectrum from a reference spectral shape <code>S_b^ref</code>.</p>\n                <p><b>Mathematical reconstruction of the bands when only LwA(ws) exists:</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_target - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>That is: the acoustic curve provides the <b>global operating level</b> and the template/library provides the <b>spectral shape</b>. The shift <b>Δ</b> is computed so that, when A-weighting and energetically summing the 8 bands, the reconstructed spectrum reproduces exactly the <code>LwA_target</code> of the imported curve.</p>\n                ",
    ': LwA fijo no válido.': ': invalid fixed LwA.',
    ': altura de buje no válida.': ': invalid hub height.',
    ': capa de receptores no válida.': ': invalid receiver layer.',
    ': el CSV de coordenadas no existe: ': ': the coordinate CSV does not exist: ',
    ': falta definir la turbina/curva de potencia.': ': the turbine / power curve is not defined.',
    ': falta el nombre de capa.': ': the layer name is missing.',
    ': faltan coordenadas. Selecciona CSV o añade puntos en el mapa.': ': coordinates are missing. Select a CSV or add points on the map.',
    ": la capa '": ": the layer '",
    ': modo curva activo pero sin CSV de curva acústica.': ': curve mode active but no acoustic-curve CSV.',
    ": no existe la curva acústica '": ": the acoustic curve does not exist '",
    ': usando LinearSum (selección usuario).': ': using LinearSum (user selection).',
    ': usando MaxSum (selección usuario).': ': using MaxSum (user selection).',
    ': usando SquaredSum (auto, opción robusta por defecto).': ': using SquaredSum (auto, robust default option).',
    ': usando SquaredSum (selección usuario).': ': using SquaredSum (user selection).',
    ': usando WeightedSum (selección usuario, deficit=': ': using WeightedSum (user selection, deficit=',
    '</b> queda solo como respaldo si la capa no aporta información válida.</p>\n                <p><b>Capa usada:</b> ': '</b> is kept only as a fallback if the layer does not provide valid information.</p>\n                <p><b>Layer used:</b> ',
    '</b> y el del receptor crítico <b>': '</b> and that of the critical receiver <b>',
    "</b>.</li>\n                    <li><b>Conversión a bandas:</b> el motor ISO-aligned necesita un espectro <code>Lw,b</code> en 8 bandas de octava. Si no hay espectro específico, el plugin reconstruye uno desde una plantilla/fallback y lo ajusta para reproducir el LwA operativo.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b> y no generan nivel acústico útil.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula distancia 3D, cotas acústicas, <b>G</b> o <b>G_eff</b> del suelo y, si hay MDT/DSM, el posible apantallamiento topográfico del trayecto.</li>\n                    <li><b>Propagación por banda:</b> en cada banda se aplica <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code>. Adiv depende de la distancia, Aatm,b de frecuencia/atmósfera, Agr,b del suelo y Abar,b del MDT si hay obstáculo relevante.</li>\n                    <li><b>Suma por fuente:</b> las 8 bandas se ponderan en A y se suman energéticamente para obtener el nivel A-ponderado de esa turbina en el receptor.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(LpA,fuente/10))</div>\n                <p><b>Lectura práctica:</b> el receptor crítico es el de mayor nivel total o el que queda con peor margen frente al límite. La columna “fuente dominante” identifica la turbina/grupo que más contribuye, pero el resultado final del receptor incluye todas las fuentes dentro del radio.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo Screening</h3>\n                <p>El modo ISO-aligned es más pesado pero más trazable: usa bandas de octava, ponderación A final, absorción atmosférica dependiente de frecuencia, suelo por regiones y apantallamiento topográfico <b>Abar</b> cuando hay MDT/DSM. Es el modo recomendado para informes técnicos preliminares y revisión de receptores sensibles.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Conversion to bands:</b> the ISO-aligned engine needs a spectrum <code>Lw,b</code> in 8 octave bands. If there is no specific spectrum, the plugin reconstructs one from a template/fallback and adjusts it to reproduce the operating LwA.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b> and do not generate a useful acoustic level.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine, the 3D distance, acoustic heights, ground <b>G</b> or <b>G_eff</b> and, if there is a DTM/DSM, the possible topographic screening of the path are computed.</li>\n                    <li><b>Per-band propagation:</b> in each band <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code> is applied. Adiv depends on distance, Aatm,b on frequency/atmosphere, Agr,b on ground and Abar,b on the DTM if there is a relevant obstacle.</li>\n                    <li><b>Per-source sum:</b> the 8 bands are A-weighted and summed energetically to obtain the A-weighted level of that turbine at the receiver.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(LpA,source/10))</div>\n                <p><b>Practical reading:</b> the critical receiver is the one with the highest total level or the worst margin against the limit. The “dominant source” column identifies the turbine/group that contributes the most, but the receiver's final result includes all sources within the radius.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the Screening mode</h3>\n                <p>The ISO-aligned mode is heavier but more traceable: it uses octave bands, a final A-weighting, frequency-dependent atmospheric absorption, ground by regions and topographic screening <b>Abar</b> when there is a DTM/DSM. It is the recommended mode for preliminary technical reports and review of sensitive receivers.</p>\n            </div>\n            ",
    "</b>.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b>.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula la distancia 3D, la divergencia geométrica, una absorción atmosférica simplificada <code>α·d</code> y una corrección empírica de suelo.</li>\n                    <li><b>Uso del suelo:</b> si hay capa de land-use, el plugin puede calcular un <b>G_eff</b> por trayecto; si no, usa el <b>G global</b> definido por el usuario.</li>\n                    <li><b>Propagación simplificada:</b> se aplica <code>Lp = LwA - Adiv - Aatm - Aground</code>. No hay bandas de octava ni apantallamiento topográfico explícito <code>Abar</code>.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(Lp,fuente/10))</div>\n                <p><b>Lectura práctica:</b> este modo es útil para screening inicial. Si un receptor aparece cerca del límite o en excedencia, conviene recalcularlo con el modo ISO-aligned y revisar espectros, terreno, uso del suelo y límites aplicados.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo ISO-aligned</h3>\n                <p>El modo Screening sacrifica detalle para ganar velocidad. No propaga por bandas, no usa T/HR/P por frecuencia, no calcula <b>Abar</b> desde MDT y resume la atmósfera con un único coeficiente <b>α</b>. Por eso debe interpretarse como preevaluación rápida, no como informe acústico detallado.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b>.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine the 3D distance, the geometrical divergence, a simplified atmospheric absorption <code>α·d</code> and an empirical ground correction are computed.</li>\n                    <li><b>Land use:</b> if there is a land-use layer, the plugin can compute a per-path <b>G_eff</b>; otherwise it uses the <b>global G</b> defined by the user.</li>\n                    <li><b>Simplified propagation:</b> <code>Lp = LwA - Adiv - Aatm - Aground</code> is applied. There are no octave bands and no explicit topographic screening <code>Abar</code>.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(Lp,source/10))</div>\n                <p><b>Practical reading:</b> this mode is useful for initial screening. If a receiver appears close to the limit or in exceedance, it is advisable to recalculate it with the ISO-aligned mode and review spectra, terrain, land use and applied limits.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the ISO-aligned mode</h3>\n                <p>The Screening mode trades detail for speed. It does not propagate by bands, does not use T/RH/P per frequency, does not compute <b>Abar</b> from a DTM and summarizes the atmosphere with a single coefficient <b>α</b>. It should therefore be interpreted as a quick pre-assessment, not as a detailed acoustic report.</p>\n            </div>\n            ",
    '</div>\n                    <p>Ese valor se aplica en el término de suelo del modelo. No se ha derivado un G_eff desde capa de uso del suelo.</p>\n                </div>\n                ': '</div>\n                    <p>That value is applied in the ground term of the model. No G_eff was derived from a land-use layer.</p>\n                </div>\n                ',
    "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>En esta corrida no se ha derivado un G_eff desde una capa de uso del suelo. Aquí <b>d_xy</b> es la distancia horizontal, <b>h_s</b> la altura de fuente y <b>h_r</b> la altura del receptor.</p>\n                ": "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>In this run no G_eff was derived from a land-use layer. Here <b>d_xy</b> is the horizontal distance, <b>h_s</b> the source height and <b>h_r</b> the receiver height.</p>\n                ",
    '</p>\n        </div>\n\n        <h2>2. CÓMO SE HA GENERADO EL RESULTADO</h2>\n        ': '</p>\n        </div>\n\n        <h2>2. HOW THE RESULT WAS GENERATED</h2>\n        ',
    "</td>\n                    <td>Máximo Abar entre todas las turbinas que contribuyen al receptor</td>\n                </tr>\n                <tr>\n                    <td>Abar ponderado por energía</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Maximum Abar among all turbines contributing to the receiver</td>\n                </tr>\n                <tr>\n                    <td>Energy-weighted Abar</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Número de turbinas contribuyentes con Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>NIVEL RESULTANTE</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Number of contributing turbines with Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>RESULTING LEVEL</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Promedio ponderado por la contribución acústica de cada turbina</td>\n                </tr>\n                <tr>\n                    <td>Trayectorias apantalladas</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Average weighted by the acoustic contribution of each turbine</td>\n                </tr>\n                <tr>\n                    <td>Screened paths</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (máximo entre contribuyentes)</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (maximum among contributors)</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td>Abar máximo contribuyentes</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td>Maximum contributors Abar</td>\n                    <td style='text-align: right;'>",
    "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Cumplimiento por Categoría</h3>\n                <ul>": "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Compliance by category</h3>\n                <ul>",
    "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitaciones y recomendaciones</strong>\n            <p><b>Motor Rápido:</b> Apropiado para screening preliminar y mapas ágiles.</p>\n            <p><b>Motor ISO-aligned:</b> Apropiado para estudios técnicos preliminares, comparativas e iteración de diseño.</p>\n            <p><b>Simplificaciones conocidas:</b> Aatm simplificado (tablas + correcciones); Agr y Abar con aproximaciones básicas; directividad Dc asumida 0 dB; Cmet/corrección meteorológica de largo plazo no aplicada.</p>\n            <p><b>Modelos múltiples:</b> soportados mediante capas/grupos fuente independientes. Mezclar varios modelos dentro de una sola capa por atributos no está habilitado en esta versión experimental.</p>\n            <p><b>Raster ISO + MDT:</b> usa la misma lógica de apantallamiento topográfico que los receptores puntuales, pero puede ser costoso en mapas grandes.</p>\n            <p><b>Recomendación:</b> Para estudios regulatorios críticos, validar con mediciones o software comercial certificado.</p>\n        </div>\n        ": "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitations and recommendations</strong>\n            <p><b>Fast engine:</b> Appropriate for preliminary screening and quick maps.</p>\n            <p><b>ISO-aligned engine:</b> Appropriate for preliminary technical studies, comparisons and design iteration.</p>\n            <p><b>Known simplifications:</b> simplified Aatm (tables + corrections); Agr and Abar with basic approximations; directivity Dc assumed 0 dB; long-term Cmet/meteorological correction not applied.</p>\n            <p><b>Multiple models:</b> supported through independent source layers/groups. Mixing several models within a single layer by attributes is not enabled in this experimental release.</p>\n            <p><b>ISO raster + DTM:</b> it uses the same topographic-screening logic as point receivers, but it can be expensive on large maps.</p>\n            <p><b>Recommendation:</b> For critical regulatory studies, validate with measurements or certified commercial software.</p>\n        </div>\n        ",
    '</ul>\n        </div>\n        \n        <h2>5. FÍSICA DETALLADA Y TRAZABILIDAD DEL CÁLCULO</h2>\n        ': '</ul>\n        </div>\n        \n        <h2>5. DETAILED PHYSICS AND CALCULATION TRACEABILITY</h2>\n        ',
    "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUCIÓN POR TIPO DE RECEPTOR</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receptores por Categoría</h3>\n                <ul>": "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUTION BY RECEIVER TYPE</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receivers by category</h3>\n                <ul>",
    '<b>BastankhahGaussianDeficit</b><br><br>Modelo gaussiano de Bastankhah & Porté-Agel (2014). El perfil radial del déficit es una campana de Gauss cuyo ancho σ(x) crece linealmente.<br><br><b>k</b> — Pendiente del crecimiento de σ. σ(x)/D = k·(x/D) + cεps. Default PyWake = 0.0324, valor calibrado contra LES en el paper original. Subirlo = estela que se ensancha y recupera antes; bajarlo = estela más estrecha y persistente.<br><br><b>cεps</b> — Tamaño inicial de la estela junto al rotor (σ₀/D). Default PyWake = 0.2. Casi nunca se cambia; afecta sobre todo al near-wake.<br><br><i>Nota:</i> en BG estándar la TI ambiente NO entra en k. Es robusto para benchmarking (ej. IEA Task 37) pero poco sensible al recurso TI.': '<b>BastankhahGaussianDeficit</b><br><br>Gaussian model by Bastankhah & Porté-Agel (2014). The radial deficit profile is a Gaussian bell whose width σ(x) grows linearly.<br><br><b>k</b> — Growth slope of σ. σ(x)/D = k·(x/D) + cεps. PyWake default = 0.0324, a value calibrated against LES in the original paper. Increasing it = a wake that widens and recovers sooner; decreasing it = a narrower, more persistent wake.<br><br><b>cεps</b> — Initial wake size next to the rotor (σ₀/D). PyWake default = 0.2. Rarely changed; it mainly affects the near wake.<br><br><i>Note:</i> in standard BG the ambient TI does NOT enter k. It is robust for benchmarking (e.g. IEA Task 37) but not very sensitive to the TI resource.',
    '<b>Bloqueo desactivado</b> porque el motor es <b>PropagateDownwind</b> (solo propaga estelas hacia abajo, no soporta bloqueo). Si quieres incluir bloqueo, cambia el motor a <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b>.': '<b>Blockage disabled</b> because the engine is <b>PropagateDownwind</b> (it only propagates wakes downstream and does not support blockage). To include blockage, change the engine to <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b>.',
    '<b>Modelo activo:</b>': '<b>Active model:</b>',
    '<b>NOJDeficit (Jensen)</b><br><br>Modelo top-hat clásico: la estela es un cilindro que se ensancha linealmente con una sola pendiente <b>k</b>.<br><br><b>k</b> — Pendiente de expansión. <u>Físicamente</u> representa cuánto crece el radio de la estela por metro avanzado aguas abajo: r(x) = R + k·x. Cuanto mayor k, más rápido se recupera la estela y más pequeño el déficit lejano.<br><br><b>Valores de referencia</b><br>• Offshore limpio: k ≈ 0.04 (default PyWake)<br>• Onshore típico: k ≈ 0.075<br>• Onshore complejo / alta TI: k ≈ 0.10<br><br><i>Nota:</i> NOJ no usa la TI ambiente para calcular k. Si tu sitio tiene TI alta y necesitas que esto se note en el AEP, usa TurboNOJ o un modelo gaussiano TI-sensible.': '<b>NOJDeficit (Jensen)</b><br><br>Classic top-hat model: the wake is a cylinder that widens linearly with a single slope <b>k</b>.<br><br><b>k</b> — Expansion slope. <u>Physically</u> it represents how much the wake radius grows per metre downstream: r(x) = R + k·x. The larger k is, the faster the wake recovers and the smaller the far-field deficit.<br><br><b>Reference values</b><br>• Clean offshore: k ≈ 0.04 (PyWake default)<br>• Typical onshore: k ≈ 0.075<br>• Complex onshore / high TI: k ≈ 0.10<br><br><i>Note:</i> NOJ does not use ambient TI to compute k. If your site has high TI and you need that to show in AEP, use TurboNOJ or a TI-sensitive Gaussian model.',
    '<b>NiayifarGaussianDeficit</b><br><br>Variante gaussiana donde la pendiente k es función explícita de la <b>TI local efectiva</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>cεps</b> — Tamaño inicial σ₀/D. Default PyWake = 0.2.<br><br>Cambiar la TI ambiente del WRG o el modelo de turbulencia añadida sigue siendo la palanca principal del AEP. Los coeficientes se exponen solo como ajuste avanzado.': '<b>NiayifarGaussianDeficit</b><br><br>Gaussian variant where the slope k is an explicit function of the <b>effective local TI</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>cεps</b> — Initial size σ₀/D. PyWake default = 0.2.<br><br>Changing the WRG ambient TI or the added-turbulence model is still the main AEP lever. The coefficients are exposed only as an advanced adjustment.',
    '<b>Qué es TI</b><br><br>TI significa <b>turbulence intensity</b>. En simple: mide cuánto fluctúa el viento alrededor de su velocidad media. Más TI suele significar más mezcla del flujo y una recuperación de estela distinta.<br><br><b>Dos turbulencias diferentes</b><br>• <b>TI ambiente</b>: viene del recurso eólico o de un raster TI. Es el viento que llega al parque antes de las turbinas.<br>• <b>TI añadida por estela</b>: la genera cada turbina detrás de sí. La calcula el modelo de turbulencia de PyWake, si lo activas.<br><br><b>Qué hace el plugin</b><br>• Si cargas raster TI, se usa como turbulencia ambiente espacial.<br>• Si no cargas raster TI, se usa el valor manual de la interfaz como hipótesis uniforme.<br>• Si tienes varios raster a distintas alturas, puedes indicar 90;120;... para interpolar por altura de buje.<br><br><b>Cuándo importa más</b><br>BG/NOJ son robustos pero poco sensibles a TI. Niayifar, Zong, TurboGaussian y TurboNOJ hacen que la TI afecte más a la recuperación de estela y al AEP.<br><br><b>Idea sencilla</b><br>Si no tienes datos de turbulencia, una TI manual uniforme permite correr el cálculo. Para más realismo en onshore, terreno complejo o varias alturas de buje, usa raster TI, idealmente por altura.': '<b>What TI is</b><br><br>TI stands for <b>turbulence intensity</b>. In simple terms: it measures how much the wind fluctuates around its mean speed. More TI usually means more flow mixing and a different wake recovery.<br><br><b>Two different turbulences</b><br>• <b>Ambient TI</b>: comes from the wind resource or from a TI raster. It is the wind reaching the wind farm before the turbines.<br>• <b>Wake-added TI</b>: generated by each turbine behind it. It is computed by the PyWake turbulence model, if you enable it.<br><br><b>What the plugin does</b><br>• If you load a TI raster, it is used as spatial ambient turbulence.<br>• If you do not load a TI raster, the manual value in the interface is used as a uniform assumption.<br>• If you have several rasters at different heights, you can enter 90;120;... to interpolate by hub height.<br><br><b>When it matters most</b><br>BG/NOJ are robust but not very TI-sensitive. Niayifar, Zong, TurboGaussian and TurboNOJ make TI affect wake recovery and AEP more.<br><br><b>Simple idea</b><br>If you have no turbulence data, a uniform manual TI lets you run the calculation. For more realism onshore, in complex terrain or with several hub heights, use a TI raster, ideally by height.',
    '<b>Qué es el bloqueo</b><br><br>Además de crear una estela detrás, una turbina también puede frenar ligeramente el viento antes de que el flujo llegue al rotor. Ese efecto aguas arriba se llama <b>bloqueo</b> o <i>blockage</i>.<br><br><b>Compatibilidad</b><br>• No depende del recurso WRG/GRD: depende del motor de PyWake y del modelo de bloqueo.<br>• Con <b>PropagateDownwind</b> se desactiva, porque ese motor solo calcula efectos aguas abajo.<br>• Con <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b> el plugin puede intentar incluirlo con los wake models disponibles.<br><br><b>Parámetros</b><br>En esta versión experimental no se exponen parámetros internos de bloqueo. Son calibraciones propias del modelo de PyWake; para comparar sensibilidad, cambia de modelo en lugar de tocar constantes internas.<br><br><b>Qué opción usar</b><br>SelfSimilarityDeficit2020 es la opción principal. VortexCylinder/VortexDipole/HybridInduction se dejan como alternativas si tu instalación de PyWake las soporta.': '<b>What blockage is</b><br><br>Besides creating a wake behind it, a turbine can also slow the wind slightly before the flow reaches the rotor. That upstream effect is called <b>blockage</b>.<br><br><b>Compatibility</b><br>• It does not depend on the WRG/GRD resource: it depends on the PyWake engine and the blockage model.<br>• With <b>PropagateDownwind</b> it is disabled, because that engine only computes downstream effects.<br>• With <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b> the plugin can try to include it with the available wake models.<br><br><b>Parameters</b><br>In this experimental release no internal blockage parameters are exposed. They are model-specific PyWake calibrations; to compare sensitivity, switch model instead of touching internal constants.<br><br><b>Which option to use</b><br>SelfSimilarityDeficit2020 is the main option. VortexCylinder/VortexDipole/HybridInduction are left as alternatives if your PyWake installation supports them.',
    '<b>Qué es el wake effect, en simple</b><br><br>Cuando una turbina extrae energía del viento, detrás de ella queda una zona con menos velocidad y más turbulencia. Esa zona es la <b>estela</b>. Si otra turbina cae dentro, producirá menos AEP.<br><br>El <b>wake deficit model</b> decide cuánta velocidad se pierde y cómo se recupera la estela aguas abajo.<br><br><b>Cómo elegir sin ser experto</b><br>• <b>BastankhahGaussianDeficit</b>: opción robusta para comparar layouts y parques offshore limpios.<br>• <b>NiayifarGaussianDeficit</b>: parecido a Bastankhah, pero la turbulencia TI pesa más en la expansión de la estela.<br>• <b>ZongGaussianDeficit</b>: más avanzado; útil si quieres una representación más rica del near wake y de la recuperación por TI.<br>• <b>TurboGaussianDeficit</b>: gaussiano sensible a turbulencia efectiva; interesante cuando quieres que la TI ambiente/añadida influya en el AEP.<br>• <b>NOJDeficit</b>: muy rápido y clásico; bueno para screening masivo, pero más simplificado.<br>• <b>TurboNOJDeficit</b>: mantiene la ligereza de NOJ, pero permite que la TI influya más.<br><br><b>Idea sencilla</b><br>Para más realismo cuando la turbulencia debe influir en la recuperación de estela, usa <b>Niayifar/Zong/TurboGaussian</b>. Para un cálculo rápido, usa <b>NOJ/TurboNOJ</b>. Para una base robusta de comparación, usa <b>Bastankhah</b>.<br><br>Importante: cargar un raster TI no garantiza por sí solo que cambie mucho el AEP. El efecto se ve de verdad si el modelo de estela elegido usa la TI de forma explícita.': "<b>What the wake effect is, in simple terms</b><br><br>When a turbine extracts energy from the wind, a zone with lower speed and more turbulence is left behind it. That zone is the <b>wake</b>. If another turbine falls inside it, it will produce less AEP.<br><br>The <b>wake deficit model</b> decides how much speed is lost and how the wake recovers downstream.<br><br><b>How to choose without being an expert</b><br>• <b>BastankhahGaussianDeficit</b>: robust option for comparing layouts and clean offshore wind farms.<br>• <b>NiayifarGaussianDeficit</b>: similar to Bastankhah, but TI turbulence weighs more in the wake expansion.<br>• <b>ZongGaussianDeficit</b>: more advanced; useful if you want a richer representation of the near wake and TI-driven recovery.<br>• <b>TurboGaussianDeficit</b>: Gaussian model sensitive to effective turbulence; interesting when you want ambient/added TI to influence AEP.<br>• <b>NOJDeficit</b>: very fast and classic; good for large-scale screening, but more simplified.<br>• <b>TurboNOJDeficit</b>: keeps NOJ's lightness, but lets TI have more influence.<br><br><b>Simple idea</b><br>For more realism when turbulence should influence wake recovery, use <b>Niayifar/Zong/TurboGaussian</b>. For a quick calculation, use <b>NOJ/TurboNOJ</b>. For a robust comparison baseline, use <b>Bastankhah</b>.<br><br>Important: loading a TI raster does not by itself guarantee a large change in AEP. The effect really shows if the chosen wake model uses TI explicitly.",
    '<b>Qué es</b><br><br>Una turbina no ve exactamente el mismo viento en todo el rotor. La parte alta, baja, izquierda o derecha del disco puede recibir velocidades distintas, sobre todo si una estela solo atraviesa una parte del rotor.<br><br><b>Qué hace esta opción</b><br>• <b>Sin promedio / RotorCenter</b>: usa solo el centro del rotor. Es rápido, pero más simplificado.<br>• <b>CGIRotorAvg(7)</b>: calcula varios puntos dentro del disco. Aporta más realismo con un coste moderado.<br>• <b>CGIRotorAvg(9/21)</b>: usa más puntos. Puede aportar más fidelidad, pero aumenta el tiempo de cálculo.<br><br><b>Idea sencilla</b><br>Si una estela corta solo media turbina, mirar únicamente el centro puede ser demasiado simplificado. Promediar el rotor ayuda a representar mejor ese efecto.': '<b>What it is</b><br><br>A turbine does not see exactly the same wind across the whole rotor. The top, bottom, left or right of the disk can receive different wind speeds, especially when a wake crosses only part of the rotor.<br><br><b>What this option does</b><br>• <b>No averaging / RotorCenter</b>: uses only the rotor centre. It is fast, but more simplified.<br>• <b>CGIRotorAvg(7)</b>: calculates several points inside the disk. It adds more realism at a moderate cost.<br>• <b>CGIRotorAvg(9/21)</b>: uses more points. It can add more fidelity, but increases computation time.<br><br><b>Simple idea</b><br>If a wake cuts only half the turbine, looking only at the centre can be too simplified. Averaging the rotor helps to represent that effect better.',
    '<b>Qué problema resuelve</b><br><br>Una turbina puede recibir la estela de varias turbinas al mismo tiempo. La superposición decide cómo se combinan esas pérdidas de velocidad antes de calcular la potencia.<br><br>• <b>Automático</b>: el plugin escoge una opción segura según la combinación de modelos.<br>• <b>LinearSum</b>: suma directa. Puede acumular muchas pérdidas en parques densos.<br>• <b>SquaredSum</b>: suma cuadrática. Suele ser una opción equilibrada cuando varias estelas se solapan.<br>• <b>MaxSum</b>: usa solo la estela más fuerte. Útil para diagnóstico, pero puede infravalorar pérdidas acumuladas.<br>• <b>WeightedSum</b>: opción avanzada para modelos gaussianos compatibles.<br><br><b>Idea sencilla</b><br>Si dos turbinas frenan el viento antes de llegar a una tercera, hay que decidir si esas pérdidas se suman mucho, poco o solo domina una de ellas.': '<b>What problem it solves</b><br><br>A turbine can receive the wake of several turbines at the same time. Superposition decides how those wind-speed losses are combined before calculating power.<br><br>• <b>Automatic</b>: the plugin chooses a safe option based on the model combination.<br>• <b>LinearSum</b>: direct sum. It can accumulate large losses in dense wind farms.<br>• <b>SquaredSum</b>: quadratic sum. It is usually a balanced option when several wakes overlap.<br>• <b>MaxSum</b>: uses only the strongest wake. Useful for diagnostics, but it can underestimate accumulated losses.<br>• <b>WeightedSum</b>: advanced option for compatible Gaussian models.<br><br><b>Simple idea</b><br>If two turbines slow the wind before it reaches a third one, you must decide whether those losses add up strongly, weakly, or whether only one of them dominates.',
    '<b>Qué significa la turbulencia en este plugin</b><br><br>• <b>TI raster / WRG</b>: describe la <b>turbulencia ambiente</b> del site. Es un dato de entrada del recurso.<br>• <b>Modelo de turbulencia</b>: calcula la <b>turbulencia añadida por estela</b> detrás de las turbinas.<br>• <b>Modelo de estela</b>: decide si esa TI ambiente/efectiva cambia realmente el déficit y, por tanto, el AEP.<br><br><b>Cómo leerlo físicamente</b><br>• Si solo aportas un raster TI, el plugin usa esa TI horizontal para todas las turbinas; si hay varias alturas de buje, eso es una <b>aproximación vertical</b>.<br>• Si aportas varios raster(s) TI a distintas alturas, el plugin intenta reconstruir <b>TI(x,y,h)</b> e interpolar por altura.<br>• Si no aportas raster TI en WRG, el plugin avisa y usa la <b>TI ambiente fallback definida por el usuario</b> como campo uniforme.<br><br><b>Cuándo importa mucho en el AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> y <b>NOJDeficit</b>: la TI no cambia mucho el déficit base del wake; el impacto sobre el AEP suele ser limitado.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> y <b>TurboNOJDeficit</b>: la TI ambiente/efectiva sí puede influir más en la apertura y recuperación de la estela.<br><br><b>Qué conviene según el parque</b><br>• <b>Offshore con viento relativamente homogéneo</b>: una sola capa TI puede ser suficiente si las alturas de buje son parecidas.<br>• <b>Onshore, terreno rugoso o alturas de buje distintas</b>: para más realismo, usa raster(s) TI y un wake model sensible a TI (Niayifar, Zong, TurboGaussian o TurboNOJ).<br>• <b>Varios modelos de aerogenerador</b>: si mezclas alturas de buje, una sola capa TI es una aproximación; varias alturas de TI representan mejor la variación vertical.<br><br><b>Qué elegir en el selector de modelo de turbulencia</b><br>• <b>STF2017</b>: buena opción general y la recomendación por defecto cuando necesitas un modelo de turbulencia añadida robusto.<br>• <b>STF2005</b>: alternativa clásica y sencilla si quieres comparar con configuraciones más antiguas o más conservadoras.<br>• <b>CrespoHernandez</b> y <b>GCLTurbulence</b>: útiles para comparativas o compatibilidad con ciertos modelos/estudios, pero el impacto final depende del wake model que elijas.<br><br><b>Regla práctica</b><br>Si tu objetivo es que la turbulencia influya físicamente en el AEP, no basta con seleccionar un modelo de turbulencia: también necesitas un <b>modelo de estela sensible a TI</b>.': "<b>What turbulence means in this plugin</b><br><br>• <b>TI raster / WRG</b>: describes the site's <b>ambient turbulence</b>. It is a resource input.<br>• <b>Turbulence model</b>: computes the <b>wake-added turbulence</b> behind the turbines.<br>• <b>Wake model</b>: decides whether that ambient/effective TI actually changes the deficit and therefore the AEP.<br><br><b>Physical interpretation</b><br>• If you provide only one TI raster, the plugin uses that horizontal TI for all turbines; if there are several hub heights, that is a <b>vertical approximation</b>.<br>• If you provide several TI raster(s) at different heights, the plugin tries to reconstruct <b>TI(x,y,h)</b> and interpolate by height.<br>• If you do not provide a TI raster in WRG, the plugin warns and uses the <b>user-defined ambient TI fallback</b> as a uniform field.<br><br><b>When it matters a lot for AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> and <b>NOJDeficit</b>: TI does not change the base wake deficit much; the impact on AEP is usually limited.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> and <b>TurboNOJDeficit</b>: ambient/effective TI can influence wake opening and recovery more.<br><br><b>What is advisable depending on the wind farm</b><br>• <b>Offshore with relatively homogeneous wind</b>: a single TI layer may be enough if hub heights are similar.<br>• <b>Onshore, rough terrain or different hub heights</b>: for more realism, use TI raster(s) and a TI-sensitive wake model (Niayifar, Zong, TurboGaussian or TurboNOJ).<br>• <b>Several turbine models</b>: if you mix hub heights, a single TI layer is an approximation; several TI heights better represent the vertical variation.<br><br><b>What to choose in the turbulence-model selector</b><br>• <b>STF2017</b>: good general option and the default recommendation when you need a robust added-turbulence model.<br>• <b>STF2005</b>: classic, simple alternative if you want to compare with older or more conservative configurations.<br>• <b>CrespoHernandez</b> and <b>GCLTurbulence</b>: useful for comparisons or compatibility with certain models/studies, but the final impact depends on the wake model you choose.<br><br><b>Rule of thumb</b><br>If your goal is for turbulence to physically influence AEP, selecting a turbulence model is not enough: you also need a <b>TI-sensitive wake model</b>.",
    '<b>Rosa de pérdidas por sector.</b> Distribución del AEP libre y neto por dirección del viento. La diferencia entre cada barra representa las pérdidas por estela en ese sector — útil para ver qué direcciones limitan más el rendimiento del parque.': '<b>Sector loss rose.</b> Distribution of free-flow and net AEP by wind direction. The difference between each pair of bars represents the wake losses in that sector — useful to see which directions limit the wind farm performance the most.',
    '<b>Sin bloqueo:</b> el cálculo no incluirá la reducción de velocidad upstream. En parques offshore grandes esto suele subestimar las pérdidas en ~1–3 % AEP.': '<b>No blockage:</b> the calculation will not include upstream wind-speed reduction. In large offshore wind farms this often underestimates losses by about ~1–3% AEP.',
    '<b>Sin turbulencia añadida:</b> el cálculo usa la TI ambiente del recurso (WRG/raster TI o valor manual), pero no suma la turbulencia creada por las estelas. Esto es válido para modelos poco sensibles a TI; si usas Niayifar, Zong, TurboGaussian o TurboNOJ, la turbulencia añadida puede afectar más al AEP.': '<b>No added turbulence:</b> the calculation uses ambient TI from the resource (WRG/TI raster or manual value), but it does not add turbulence created by wakes. This is valid for models with low TI sensitivity; if you use Niayifar, Zong, TurboGaussian or TurboNOJ, added turbulence can affect AEP more.',
    '<b>TurboGaussianDeficit</b><br><br>Versión TI-driven de la familia gaussiana. Tanto k como el ancho cerca del rotor se derivan de la TI efectiva.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI/cεps se exponen como ajuste avanzado para calibración controlada.': '<b>TurboGaussianDeficit</b><br><br>TI-driven version of the Gaussian family. Both k and the width near the rotor are derived from the effective TI.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI/cεps coefficients are exposed as an advanced adjustment for controlled calibration.',
    '<b>TurboNOJDeficit</b><br><br>Variante TI-sensible del Jensen: la pendiente k se calcula a partir de la <b>turbulencia efectiva</b> en cada turbina.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI se exponen como ajuste avanzado y solo se pasan si tu PyWake los acepta.': '<b>TurboNOJDeficit</b><br><br>TI-sensitive variant of the Jensen model: the slope k is computed from the <b>effective turbulence</b> at each turbine.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI coefficients are exposed as an advanced adjustment and are only passed if your PyWake accepts them.',
    '<b>ZongGaussianDeficit</b><br><br>Modelo gaussiano avanzado (Zong &amp; Porté-Agel 2020) con near-wake explícito y pendiente k dependiente de TI local.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>δw/D, ε coeff, λ y B</b> — parámetros near-wake avanzados. El plugin fuerza STF2017 si lo dejas en Ninguno.': '<b>ZongGaussianDeficit</b><br><br>Advanced Gaussian model (Zong &amp; Porté-Agel 2020) with an explicit near wake and a slope k that depends on local TI.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>δw/D, ε coeff, λ and B</b> — advanced near-wake parameters. The plugin forces STF2017 if you leave it as None.',
    '<br><b>Alturas trayectoria dominante:</b> terreno turbina=': '<br><b>Dominant-path heights:</b> turbine terrain=',
    '<br><b>Trayectoria con Abar máximo:</b> fuente=': '<br><b>Path with maximum Abar:</b> source=',
    '<br><small>Diagnóstico incremental: positivo = recupera AEP frente al wake-only; negativo = reduce AEP. No se debe interpretar siempre como pérdida contable.</small></li>\n          <li><b>Bloqueo:</b> ': '<br><small>Incremental diagnostics: positive = recovers AEP versus wake-only; negative = reduces AEP. It should not always be interpreted as an accountable loss.</small></li>\n          <li><b>Blockage:</b> ',
    "<br><span style='color:#a85a00;'>Aún no hay turbinas en esta capa: haz click izq en el mapa para añadirlas.</span>": "<br><span style='color:#a85a00;'>There are no turbines in this layer yet: left click on the map to add them.</span>",
    "<div class='card'>\n                    <h4>2.1 Espectro usado por el grupo fuente:": "<div class='card'>\n                    <h4>2.1 Spectrum used by the source group:",
    '<h2>Resumen del cálculo de shadow flicker</h2>': '<h2>Shadow Flicker Calculation Summary</h2>',
    '<h4>Notas físicas y de compatibilidad</h4><ul>': '<h4>Physical and compatibility notes</h4><ul>',
    '<i>(valor editado por usuario)</i>': '<i>(value edited by user)</i>',
    '<i>Este modelo no expone parámetros físicos manualmente. Pulsa el botón ℹ️ para ver por qué.</i>': '<i>This model does not expose physical parameters manually. Press the ℹ️ button to see why.</i>',
    '<i>Sin información específica para este modelo.</i>': '<i>No specific information for this model.</i>',
    '<i>Valores detallados por receptor. Las columnas se rellenan antes de habilitar la ordenación para evitar celdas vacías al ordenar.</i>': '<i>Detailed values for each receptor. Columns are filled before sorting is enabled to avoid blank cells after sorting.</i>',
    '<li><b>α atmosférico:</b>': '<li><b>Atmospheric α:</b>',
    "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Sustitución de modelo de bloqueo:</b>": "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Blockage-model substitution:</b>",
    "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Aviso de TI ambiente:</b> no se ha proporcionado un raster de TI. El cálculo ha usado <b>TI = ": "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Ambient TI warning:</b> no TI raster has been provided. The calculation used <b>TI = ",
    "<p style='color:#888; font-style:italic;'><small>Métricas adicionales no disponibles: no se pudo determinar la potencia nominal de las turbinas. Vuelve a definir el modelo desde «Definir…» para que la curva de potencia se guarde con la metadata.</small></p>": "<p style='color:#888; font-style:italic;'><small>Additional metrics not available: the rated power of the turbines could not be determined. Define the model again from “Define…” so that the power curve is stored with the metadata.</small></p>",
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: Abar=0 en el receptor crítico no implica que el MDT esté desactivado; significa que ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: Abar=0 at the critical receiver does not imply that the DTM is disabled; it means that ",
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: obstáculo dominante estimado ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: estimated dominant obstacle ",
    '<p><b>Interpretación:</b> el espectro final <code>Lw,b</code> es el que entra realmente en la ecuación por bandas. Si existe <code>S_b^ref</code>, corresponde a la plantilla de referencia antes del ajuste global <code>Δ</code>.</p>': '<p><b>Interpretation:</b> the final spectrum <code>Lw,b</code> is the one that actually enters the band-wise equation. If <code>S_b^ref</code> exists, it corresponds to the reference template before the global adjustment <code>Δ</code>.</p>',
    '<p><b>Interpretación:</b> en este grupo no se ha usado una plantilla interna visible; el espectro final <code>Lw,b</code> procede directamente del espectro cargado/importado o de una biblioteca externa.</p>': '<p><b>Interpretation:</b> in this group no visible internal template was used; the final spectrum <code>Lw,b</code> comes directly from the loaded/imported spectrum or from an external library.</p>',
    "<span style='color:#a85a00;'><b>Sin capa activa</b></span> de turbinas «<i>Modelo (CSV)</i>».<br>Selecciónala en el panel de capas, o usa «Generar capas de puntos» en el diálogo.": "<span style='color:#a85a00;'><b>No active layer</b></span> of turbines “<i>Model (CSV)</i>”.<br>Select it in the layers panel, or use “Generate point layers” in the dialog.",
    'A expansión TurbOPark': 'A TurbOPark expansion',
    'A expansión TurboNOJ': 'A TurboNOJ expansion',
    'A expansión TurboPark': 'A TurboPark expansion',
    'AEP bruto': 'Gross AEP',
    'AEP bruto (free-stream):': 'Gross AEP (free-stream):',
    'AEP bruto [MWh]': 'Gross AEP [MWh]',
    'AEP con estelas:': 'AEP with wakes:',
    'AEP libre': 'Free-flow AEP',
    'AEP neta': 'Net AEP',
    'AEP neto (operativo)': 'Net AEP (operational)',
    'AEP neto (operativo):': 'Net AEP (operational):',
    'AEP neto [MWh]': 'Net AEP [MWh]',
    'AEP por dirección [MWh]': 'AEP by direction [MWh]',
    'AEP, pérdidas y capacidad global': 'AEP, losses and global capacity',
    'AEP/MW [MWh/MW·año]': 'AEP/MW [MWh/MW·year]',
    'AVISO:': 'WARNING:',
    'Aatm (atmosférica)': 'Aatm (atmospheric)',
    'Abar (apantallamiento)': 'Abar (screening)',
    'Abar máx. dB': 'max Abar dB',
    'Abre la documentación oficial de PyWake, base del cálculo AEP y de estelas del módulo de Energía.': 'Open the official PyWake documentation, the basis for AEP and wake calculations in the Energy module.',
    'Abril': 'April',
    'Abrir TXT/CSV': 'Open TXT/CSV',
    'Abrir diálogo para cargar curva de potencia': 'Open dialog to load the power curve',
    'Abrir el hub principal de Velantis Wind': 'Open the main Velantis Wind hub',
    'Absorción en el aire': 'Air absorption',
    'Acciones': 'Actions',
    'Accurate: CGIRotorAvg(9) aumenta algo la fidelidad y el coste computacional.': 'Accurate: CGIRotorAvg(9) slightly increases fidelity and computation time.',
    'Aceptar': 'OK',
    'Acoustic curve vacía': 'Empty acoustic curve',
    "Activa 'Mostrar perímetro del recurso en el mapa' para dibujarlo.": "Enable 'Show resource boundary on the map' to draw it.",
    'Activa edición en el canvas: click izq añade turbina, click der borra. Las coordenadas editadas se usan directamente en el cálculo de AEP (la capa en memoria es la fuente de verdad). El CSV original NO se modifica.': 'Enables canvas editing: left click adds a turbine, right click deletes it. Edited coordinates are used directly in the AEP calculation (the memory layer is the source of truth). The original CSV is NOT modified.',
    'Activa primero una capa de turbinas': 'Activate a turbine layer first',
    'Activar cálculo paralelo': 'Enable parallel calculation',
    'Actualizar': 'Refresh',
    'Actualizar lista de capas de turbinas editables.': 'Refresh the list of editable turbine layers.',
    'Actualizar perímetro': 'Refresh boundary',
    'Además de patrocinar el proyecto, VelantisWind puede adaptarse a flujos de trabajo existentes: formatos internos de datos, plantillas de informe, procesos de validación, instalación en equipos, formación, automatización o funcionalidades específicas para proyectos eólicos.': 'Beyond sponsoring the project, VelantisWind can be adapted to existing workflows: internal data formats, reporting templates, validation processes, team deployment, training, automation or specific features for wind-energy projects.',
    'Adiv (divergencia)': 'Adiv (divergence)',
    'Adiv representa la divergencia geométrica, Aatm la atenuación atmosférica simplificada α·distancia y Aground una corrección simplificada del efecto suelo/terreno.': 'Adiv represents the geometrical divergence, Aatm the simplified atmospheric attenuation α·distance and Aground a simplified correction of the ground/terrain effect.',
    'Adiv representa la divergencia geométrica. Aatm se calcula por banda y depende de T, HR y presión, con formulación simplificada. Agr se aplica como término de suelo/terreno y Abar como apantallamiento topográfico básico cuando hay MDT.': 'Adiv represents the geometrical divergence. Aatm is computed per band and depends on T, RH and pressure, with a simplified formulation. Agr is applied as a ground/terrain term and Abar as basic topographic screening when there is a DTM.',
    'Aerogeneradores': 'Wind turbines',
    'Agosto': 'August',
    'Agr (suelo)': 'Agr (ground)',
    'Agradecimiento opcional en GitHub o en la página de supporters de GitHub.': 'Optional acknowledgement on GitHub or the GitHub supporters page.',
    'Ahora mismo has dejado el modelo de turbulencia en Ninguno': 'You currently left the turbulence model as None',
    'Ahora puedes regenerar TIF filtrados por mes/hora': 'You can now regenerate TIFs filtered by month/hour',
    'Altura [m]': 'Height [m]',
    'Altura buje:': 'Hub height:',
    'Altura de buje [m]': 'Hub Height [m]',
    'Altura de receptor': 'Receiver height',
    'Altura de receptor [m]:': 'Receiver height [m]:',
    'Altura del observador [m]:': 'Observer height [m]:',
    'Altura del punto de observación (ventana típica: 2 m)': 'Observation point height (typical window: 2 m)',
    'Altura manual de cada raster TI, en el mismo orden que los archivos. Útil si el nombre del archivo no indica claramente 90 m, 120 m, etc.': 'Manual height for each TI raster, in the same order as the files. Useful when the file name does not clearly indicate 90 m, 120 m, etc.',
    'Alturas TI override [m]:': 'TI height override [m]:',
    'Alturas de TI por archivo [m]:': 'TI heights per file [m]:',
    'Apoya el mantenimiento, la documentación y el desarrollo open source futuro.': 'Support maintenance, documentation and future open-source development.',
    'Apoya el mantenimiento, la documentación, las pruebas y el desarrollo open source futuro.': 'Support maintenance, documentation, testing and future open-source development.',
    'Apoyar VelantisWind': 'Support VelantisWind',
    'Archivo WRG/ZIP': 'WRG/ZIP file',
    'Archivo:': 'File:',
    'Atenuaciones': 'Attenuations',
    'Atenuación adicional por difracción topográfica': 'Additional topographic diffraction attenuation',
    'Atenuación lineal α [dB/m]:': 'Linear attenuation α [dB/m]:',
    'Atenuación por MDT en la trayectoria dominante': 'DTM attenuation on the dominant path',
    'Atenuación por efecto del terreno': 'Ground-effect attenuation',
    'Auto recomendado': 'Recommended auto',
    'Auto recomendado elige una opción segura según wake/rotor-average. LinearSum es conservador; SquaredSum es habitual en arrays densos; WeightedSum se reserva para modelos gaussianos compatibles.': 'Recommended auto chooses a safe option based on wake/rotor-average. LinearSum is conservative; SquaredSum is common for dense arrays; WeightedSum is reserved for compatible Gaussian models.',
    'Auto recomendado: el plugin elige una superposición segura según el wake deficit y el rotor-average activo.': 'Recommended auto: the plugin chooses a safe superposition according to the active wake deficit and rotor-average model.',
    'Autodetección aproximada sin polígonos mundiales; verifica la zona horaria en el desplegable IANA.': 'Approximate auto-detection without world polygons; check the time zone in the IANA dropdown.',
    'Automático': 'Auto recommended',
    'Automático: el plugin escoge una opción segura según la combinación de modelos disponible.': 'Automatic: the plugin chooses a safe option based on the available model combination.',
    'Aviso de TI ambiente:': 'Ambient TI warning:',
    'Aviso: el modelo es sensible a TI, pero has dejado la turbulencia añadida en Ninguno; se usará la TI ambiente, pero no se sumará TI generada por estelas.': 'Warning: the model is TI-sensitive, but added turbulence is set to None; ambient TI will be used, but wake-generated TI will not be added.',
    'Ayuda a mantener herramientas eólicas abiertas, útiles y actualizadas.': 'Help keep open wind-energy tools useful, maintained and up to date.',
    'Ayuda sobre LinearSum, SquaredSum, MaxSum y WeightedSum': 'Help about LinearSum, SquaredSum, MaxSum and WeightedSum',
    'Ayuda sobre TI ambiente, turbulencia añadida y cuándo influye realmente en el AEP': 'Help on ambient TI, added turbulence and when it actually affects AEP',
    'Ayuda sobre cuándo conviene cada modelo de bloqueo y por qué a veces se desactiva automáticamente': 'Help on when each blockage model is appropriate and why it is sometimes disabled automatically',
    'Ayuda sobre cuándo conviene cada modelo de estela según el tipo de parque y el objetivo del estudio': 'Help on when each wake model is appropriate depending on the wind farm type and study goal',
    'Ayuda sobre turbulencia WRG y modelos de estela': 'Help on WRG turbulence and wake models',
    'Ayuda · Bloqueo': 'Help · Blockage',
    'Ayuda · Modelo de estela': 'Help · Wake model',
    'Ayuda · Parámetros del modelo (': 'Help · Model parameters (',
    'Ayuda · Promedio sobre el rotor': 'Help · Rotor-disk averaging',
    'Ayuda · Rotor-average': 'Help · Rotor-average',
    'Ayuda · Superposición de estelas': 'Help · Wake superposition',
    'Ayuda · Turbulencia': 'Help · Turbulence',
    'Ayuda · Turbulencia TI': 'Help · TI turbulence',
    'Ayuda · Turbulencia WRG': 'Help · WRG turbulence',
    'Ayuda · Wake effect y modelo de estela': 'Help · Wake effect and wake model',
    'Añade turbinas en la capa del Modelo': 'Add turbines to the Model layer',
    'Añadiendo capas de resultados a QGIS…': 'Adding result layers to QGIS…',
    'Añadir capa receptores': 'Add receiver layer',
    'Año de análisis:': 'Analysis year:',
    'Año:': 'Year:',
    'B número de palas': 'B number of blades',
    'Balance total AEP': 'Total AEP balance',
    'Balanced: CGIRotorAvg(7) es el valor recomendado para cálculo AEP normal.': 'Balanced: CGIRotorAvg(7) is the recommended value for normal AEP calculations.',
    'Banda dominante': 'Dominant band',
    'Base temporal:': 'Time basis:',
    'Bastankhah: opción robusta para comparar layouts. Usa una formulación gaussiana con parámetros fijos; la TI ambiente no cambia mucho el déficit base.': 'Bastankhah: robust option for comparing layouts. It uses a Gaussian formulation with fixed parameters; ambient TI does not change the base deficit much.',
    'BastankhahGaussianDeficit usa una k fija: la TI ambiente y el modelo de turbulencia apenas cambian el déficit base del wake.': 'BastankhahGaussianDeficit uses a fixed k: ambient TI and the turbulence model barely change the base wake deficit.',
    'Experimental - Adiv ✓, Aatm ✓ (simplificado), Agr ✓ (simplificado), Abar ✓ (MDT/DSM básico)': 'Experimental - Adiv ✓, Aatm ✓ (simplified), Agr ✓ (simplified), Abar ✓ (basic DTM/DSM)',
    'Bloqueo desactivado': 'Blockage disabled',
    'BuildVRT devolvió None; revisa los paths': 'BuildVRT returned None; check the paths',
    'CGIRotorAvg(21): usa muchos puntos del rotor. Es más pesado y solo compensa si buscas una sensibilidad de mayor fidelidad.': 'CGIRotorAvg(21): uses many rotor points. It is heavier and mainly useful for higher-fidelity sensitivity checks.',
    'CGIRotorAvg(7): calcula varios puntos dentro del rotor. Aporta más realismo cuando hay estelas parciales sin aumentar demasiado el coste.': 'CGIRotorAvg(7): calculates several points inside the rotor. It adds more realism for partial wakes without increasing computation too much.',
    'CGIRotorAvg(9): usa más puntos que CGI7. Puede aportar algo más de realismo si el tiempo de cálculo sigue siendo asumible.': 'CGIRotorAvg(9): uses more points than CGI7. It can add some extra realism if computation time remains acceptable.',
    'CONFIGURACIÓN DEL CÁLCULO': 'CALCULATION CONFIGURATION',
    'CONFIGURACIÓN Y PARÁMETROS': 'CONFIGURATION AND PARAMETERS',
    'CRS no disponible': 'CRS unavailable',
    'CRS no válido': 'Invalid CRS',
    'CSV de coordenadas corrupto para «': 'Corrupt coordinate CSV for «',
    'CSV de coordenadas no válido para «': 'Invalid coordinate CSV for «',
    'CSV exportado': 'CSV exported',
    'CSV por turbina exportado correctamente:': 'Per-turbine CSV exported successfully:',
    'CSV vacío.': 'Empty CSV.',
    'CUMPLE': 'PASS',
    'Cabecera WRG inválida:': 'Invalid WRG header:',
    'Cada fila representa un grupo fuente acústico. Puedes renombrar grupo y parque para que el resumen y las exportaciones sean más legibles.': 'Each row represents an acoustic source group. You can rename the group and wind farm to make summaries and exports easier to read.',
    'Calcula AEP con la capa de turbinas activa y los modelos seleccionados.': 'Calculate AEP with the active turbine layer and the selected models.',
    'Calcula latitud/longitud desde el centroide del layout e intenta detectar la zona horaria IANA': 'Computes latitude/longitude from the layout centroid and tries to detect the IANA time zone',
    'Calculando AEP': 'Calculating AEP',
    'Calculando desglose por modelo / cluster…': 'Calculating per-model / cluster breakdown…',
    'Calculando receptores y resumen…': 'Calculating receivers and summary…',
    'Calculando ruido en segundo plano…': 'Calculating noise in the background…',
    'Calculando ruido en segundo plano… 0%': 'Calculating noise in the background… 0%',
    'Calculando ruido y raster…': 'Calculating noise and raster…',
    'Calculando shadow flicker...': 'Calculating shadow flicker...',
    'Calcular AEP': 'Calculate AEP',
    'Calcular AEP · Modelos, coordenadas y recurso eólico': 'Calculate AEP · Models, coordinates and wind resource',
    'Calcular con capas seleccionadas': 'Calculate with selected layers',
    'Calcular ruido': 'Calculate noise',
    'Calcular shadow flicker': 'Calculate Shadow Flicker',
    'Calibrado contra LES': 'Calibrated against LES',
    'Cambiarlos rompe la citabilidad del modelo': 'Changing them breaks the model traceability/citability',
    'Canadá tiene varias zonas; verifica manualmente.': 'Canada has several zones; check manually.',
    'Cancelado': 'Cancelled',
    'Cancelar': 'Cancel',
    'Capa': 'Layer',
    'Capa de receptores:': 'Receiver layer:',
    'Capa de resultados creada correctamente.': 'Results layer created successfully.',
    'Capa de turbinas:': 'Turbine layer:',
    'Capa usada': 'Layer used',
    'Capa uso del suelo (opcional):': 'Land-use layer (optional):',
    'Capas': 'Layers',
    'Capas creadas': 'Created layers',
    'Capas para estelas': 'Layers for wakes',
    'Capas para graficar estelas': 'Layers for plotting wakes',
    'Cargando recurso eólico (WAsP/WRG)…': 'Loading wind resource (WAsP/WRG)…',
    'Cargar capa': 'Load layer',
    'Carpeta con grids WAsP': 'Folder with WAsP grids',
    'Carpeta:': 'Folder:',
    'Centroide de turbinas:': 'Turbine centroid:',
    'Cerrar': 'Close',
    'Cerrar esta ventana y volver al diálogo principal sin crear la capa.': 'Close this window and return to the main dialog without creating the layer.',
    'China usa una zona oficial; verifica si el proyecto usa otra referencia.': 'China uses one official zone; check whether the project uses another reference.',
    'Cierra el módulo de energía y vuelve al hub principal de Velantis Wind sin borrar las capas ni los resultados cargados en el proyecto.': 'Close the energy module and return to the Velantis Wind main hub without deleting project layers or loaded results.',
    'Clave de superposición no reconocida:': 'Unrecognized superposition key:',
    'Click izq: añadir turbina · Click der: borrar · ESC: salir': 'Left click: add turbine · Right click: delete · ESC: exit',
    'Click izq: añadir · Click der: borrar · ESC: salir': 'Left click: add · Right click: delete · ESC: exit',
    'Cobertura de Análisis': 'Analysis coverage',
    'Coeficiente activo:': 'Active coefficient:',
    'Coeficiente de expansión dependiente de TI en la formulación Zong.': 'TI-dependent expansion coefficient in the Zong formulation.',
    'Coeficiente que convierte la TI local/efectiva en expansión de estela.': 'Coefficient that converts local/effective TI into wake expansion.',
    'Coeficientes empíricos fijos del paper.': 'Fixed empirical coefficients from the paper.',
    'Coeficientes empíricos fijos.': 'Fixed empirical coefficients.',
    'Columna potencia kW (0-based):': 'power kW column (0-based):',
    'Columna ws (0-based):': 'ws column (0-based):',
    'Coma': 'Comma',
    'Combinación de varias estelas (superposición)': 'Wake superposition model (PyWake)',
    'Compara los dos escenarios guardados y muestra delta global y por modelo.': 'Compare the two saved scenarios and show global and per-model deltas.',
    'Comparación con resumen:': 'Comparison with summary:',
    'Comparación global': 'Global comparison',
    'Comparador de escenarios': 'Scenario comparison',
    'Comparador de escenarios AEP': 'AEP scenario comparison',
    'Comparar A/B': 'Compare A/B',
    'Comparar escenarios': 'Compare scenarios',
    'Completado correctamente': 'Completed successfully',
    'Comprobación del balance total': 'Total balance check',
    'Comprobar configuración': 'Check configuration',
    'Con PUD se fuerza use_effective_ws=True para evitar errores de PyWake.': 'With PUD, use_effective_ws=True is forced to avoid PyWake errors.',
    'Con PropagateDownwind, el bloqueo se desactiva automáticamente.': 'With PropagateDownwind, blockage is disabled automatically.',
    'Configura Hub Height y Rotor Diameter para todos los modelos.': 'Please configure Hub Height and Rotor Diameter for all models.',
    'Configura al menos una capa válida en la tabla de receptores por categoría.': 'Configure at least one valid layer in the receiver-category table.',
    'Configura los parámetros geométricos de cada modelo de turbina.': 'Configure the geometric parameters for each turbine model.',
    'Configura los parámetros geométricos de cada modelo de turbina.\nHub Height: altura de buje sobre el terreno [m]\nRotor Diameter: diámetro de rotor [m]': 'Configure the geometric parameters for each turbine model.\nHub Height: hub height above ground level [m]\nRotor Diameter: rotor diameter [m]',
    'Configuración': 'Configuration',
    'Configuración acústica inválida:\n- ': 'Invalid acoustic configuration:\n- ',
    'Configuración de grupos fuente acústicos': 'Acoustic source-group configuration',
    'Configuración de modelos de turbina': 'Turbine model configuration',
    'Configuración de sombras no válida': 'Invalid shadow configuration',
    'Configuración del emplazamiento': 'Site configuration',
    'Configuración del emplazamiento:': 'Site Configuration:',
    'Configuración finalmente ejecutada': 'Configuration finally executed',
    'Configuración incompleta': 'Missing configuration',
    'Configuración incompleta. Revisa los elementos marcados.': 'Incomplete configuration. Check the flagged items.',
    'Configuración no válida': 'Invalid configuration',
    'Configuración solicitada al solver': 'Configuration requested from solver',
    'Configuración válida. Puedes continuar con el cálculo.': 'Valid configuration. You can proceed with the calculation.',
    'Consejo': 'Tip',
    'Constante principal de expansión de TurboGaussian/TurbOPark.': 'Main expansion constant for TurboGaussian/TurbOPark.',
    'Constante principal de expansión para la familia TurboNOJ cuando la versión de PyWake la expone.': 'Main expansion constant for the TurboNOJ family when the PyWake version exposes it.',
    'Constantes calibradas LES fijas del paper.': 'Fixed LES-calibrated constants from the paper.',
    'Constantes de blend fijas.': 'Fixed blend constants.',
    'Constantes fijas del paper.': 'Fixed paper constants.',
    'Consulta la tabla de atributos para el desglose mensual detallado.': 'Check the attribute table for detailed monthly breakdown.',
    'Modelo fuente-receptor eólico': 'Wind source-receiver model',
    'Contexto del proyecto': 'Project context',
    'Coordenadas': 'Coordinates',
    'Coordenadas (CSV X,Y):': 'Coordinates (CSV X,Y):',
    'Coordenadas detectadas': 'Coordinates detected',
    'Coordenadas fuera de rango.': 'Coordinates out of range.',
    'Corrección de errores y compatibilidad con nuevas versiones de QGIS.': 'Bug fixes and compatibility with new QGIS versions.',
    'Crea un nuevo TIF usando el filtro de mes/hora seleccionado': 'Creates a new TIF using the selected month/hour filter',
    'Crea una capa de resumen por modelo y permite guardar un CSV con AEP por turbina.': 'Creates a per-model summary layer and lets you save a per-turbine AEP CSV.',
    'Creando mapa raster': 'Creating raster map',
    'Crear mapa raster de shadow flicker': 'Create shadow flicker raster map',
    'Crear turbina (Manual)': 'Create turbine (Manual)',
    'Crear turbina (TXT/CSV)': 'Create turbine (TXT/CSV)',
    'Crear/actualizar la capa de puntos para este modelo': 'Create/update the point layer for this model',
    'CrespoHernandez — Crespo &amp; Hernández 1996, ajuste empírico clásico. Constantes fijas del paper.': 'CrespoHernandez — Crespo &amp; Hernández 1996, classic empirical fit. Fixed constants from the paper.',
    'Criterio de límite:': 'Limit criterion:',
    'Critical receiver no disponible.': 'Critical receiver unavailable.',
    'Cumple': 'Pass',
    'Cumplimiento Normativo': 'Regulatory compliance',
    'Curva acústica global LwA(ws)': 'Global acoustic curve LwA(ws)',
    'Curva inválida -> fallback LwA fijo (': 'Invalid curve -> fixed LwA fallback (',
    'Curvas acústicas LwA(ws)': 'LwA(ws) acoustic curves',
    'Curvas acústicas LwA(ws) a': 'LwA(ws) acoustic curves at',
    'Curvas acústicas LwA(ws) en peor caso': 'Worst-case LwA(ws) acoustic curves',
    'Cuándo importa mucho en el AEP': 'When it matters significantly for AEP',
    'Cuándo merece la pena aportar TI': 'When it is worth providing TI',
    'Cálculo AEP': 'AEP calculation',
    'Cálculo acústico completado.': 'Acoustic calculation completed.',
    'Cálculo acústico completado en segundo plano.\n\nLas capas de receptores/fuentes y el raster, si estaba activado, ya se han añadido al proyecto.': 'Acoustic calculation completed in the background.\n\nThe receiver/source layers, and the raster if it was enabled, have already been added to the project.',
    'Cálculo acústico completado.\n\nLas capas de receptores/fuentes ya se han añadido al proyecto. Si el raster estaba activado y no usa uso del suelo, se está generando en segundo plano cuando es seguro hacerlo.': 'Acoustic calculation completed.\n\nThe receiver/source layers have already been added to the project. If the raster was enabled and does not use land use, it is being generated in the background when it is safe to do so.',
    'Cálculo acústico simplificado para screening': 'Simplified acoustic calculation for screening',
    'Cálculo cancelado por el usuario': 'Calculation cancelled by the user',
    'Cálculo completado': 'Calculation Complete',
    'Cálculo completado.': 'Calculation completed.',
    'Cálculo con': 'Calculation with',
    'Cálculo de AEP con PyWake: define los modelos de aerogenerador, selecciona el recurso eólico (WAsP/Surfer grids o WRG), elige los modelos físicos (estela, turbulencia, bloqueo, rotor-average) y lanza el cálculo. Los resultados se vuelcan automáticamente sobre las capas de coordenadas del proyecto.': 'AEP calculation with PyWake: define turbine models, select the wind resource (WAsP/Surfer grids or WRG), choose the physical models (wake, turbulence, blockage, rotor-average) and run the calculation. Results are automatically written to the project coordinate layers.',
    'Cálculo de ruido cancelado o fallido.': 'Noise calculation cancelled or failed.',
    'Cálculo de ruido completado en segundo plano.': 'Noise calculation completed in the background.',
    'Cálculo de ruido completado.': 'Noise calculation completed.',
    'Cálculo de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.': 'Noise calculation launched in the background. You can see it in the QGIS task manager.',
    'Cálculo de sombras en ejecución': 'Shadow calculation running',
    'Cálculo en curso': 'Calculation in progress',
    'Cálculo por bandas de octava, absorción atmosférica por condiciones, efecto suelo tipo ISO': 'Octave-band calculation, atmospheric absorption by conditions, ISO-type ground effect',
    'Cómo elegir el modelo': 'How to choose the model',
    'Cómo elegir el modelo de estela': 'How to choose the wake model',
    'DEM/DSM usado para muestrear elevación del terreno en fuentes y receptores.': 'DEM/DSM was used to sample terrain elevation at sources and receivers.',
    'Define todos los modelos antes de continuar.': 'Define all models before continuing.',
    'Definir turbina': 'Define turbine',
    'Definir…': 'Define…',
    'Deja la lista de raster(s) TI vacía.': 'Leaves the TI raster(s) list empty.',
    'Delimitador:': 'Delimiter:',
    'Descripción': 'Description',
    'Desde capa de uso del suelo': 'From land-use layer',
    'Desglose de Atenuaciones': 'Attenuation breakdown',
    'Desglose de pérdidas': 'Loss breakdown',
    'Desglose mensual (media de todos los receptores)': 'Monthly Breakdown (Average across all receptors)',
    'Desmarcar todas': 'Clear all',
    'Detectar coordenadas y zona horaria automáticamente': 'Auto-detect coordinates and time zone',
    'Dibuja 4 mapas de estelas (N/E/S/O) usando los modelos y el recurso seleccionado (carpeta WAsP o WRG/ZIP).': 'Draws 4 wake maps (N/E/S/W) using the selected models and resource (WAsP folder or WRG/ZIP).',
    'Dibuja mapas de estelas (N/E/S/O) con los modelos seleccionados.': 'Draws wake maps (N/E/S/W) with the selected models.',
    'Dibuja una capa temporal con los límites del WRG o de los grids WAsP/Surfer. Sirve para comprobar si el layout queda dentro del dominio del recurso.': 'Draws a temporary layer with the boundaries of the WRG or the WAsP/Surfer grids. It is used to check whether the layout lies inside the resource domain.',
    'Diciembre': 'December',
    'Difracción topográfica': 'Topographic diffraction',
    'Dirección': 'Direction',
    'Dirección [°]': 'Direction [°]',
    'Directorio WAsP inválido o no existente': 'Invalid or non-existent WAsP directory',
    'Dispersión geométrica': 'Geometrical spreading',
    'Disponibilidad de turbina:': 'Turbine availability:',
    'Distancia': 'Distance',
    'Distancia máxima de sombra [m]:': 'Max shadow distance [m]:',
    'Distancia máxima de sombra:': 'Max shadow distance:',
    'Distancia máxima desde una turbina para considerar shadow flicker.\nPor defecto: 2000 m, coincidiendo con la distancia de screening conservadora tipo Continuum.\nEste valor se usa para filtrar receptores y para el extent/máscara del raster.': 'Maximum distance from a turbine to consider shadow flicker.\nDefault: 2000 m, matching the conservative Continuum-style screening distance.\nThis value is used for receiver filtering and for the raster extent/mask.',
    'Distribución del AEP libre y neto por dirección del viento.': 'Distribution of free-flow and net AEP by wind direction.',
    'Diurno': 'Daytime',
    'Diámetro de rotor [m]': 'Rotor Diameter [m]',
    'Diámetro rotor:': 'Rotor diameter:',
    'Documentación PyWake': 'PyWake documentation',
    'Documentación, tutoriales, ejemplos y datasets de prueba.': 'Documentation, tutorials, examples and test datasets.',
    'EPSG no válido. Se seguirá usando EPSG:': 'Invalid EPSG. EPSG will keep being used:',
    'EXCEDE': 'EXCEEDS',
    'Ecuación utilizada': 'Equation used',
    'Efecto del terreno': 'Terrain effect',
    "El CSV debe tener cabeceras 'X' y 'Y' (o easting/northing).": "The CSV must have 'X' and 'Y' headers (or easting/northing).",
    'El MDT/DSM no está en el mismo CRS que el proyecto.': 'The DTM/DSM is not in the same CRS as the project.',
    'El MDT/DSM no está en el mismo CRS que el proyecto. Reproyéctalo antes de calcular.': 'The DTM/DSM is not in the same CRS as the project. Reproject it before calculating.',
    'El MDT/DSM seleccionado no es raster válido y se ignorará.': 'The selected DTM/DSM is not a valid raster and will be ignored.',
    'El ZIP no contiene ningún .wrg:': 'The ZIP does not contain any .wrg:',
    'El archivo WRG/ZIP no existe:': 'The WRG/ZIP file does not exist:',
    'El archivo no existe:': 'The file does not exist:',
    'El bloqueo SOLO entra en el cálculo': 'Blockage ONLY enters the calculation',
    'El bloqueo es la reducción de velocidad upstream del rotor. Solo se aplica con motores A2A o PUD.': 'Blockage is the upstream velocity reduction at the rotor. It is only applied with A2A or PUD engines.',
    'El bloqueo representa la reducción de velocidad aguas arriba del rotor. Solo se aplica con motores All2AllIterative o PropagateUpDownIterative.': 'Blockage represents wind-speed reduction upstream of the rotor. It is only applied with All2AllIterative or PropagateUpDownIterative engines.',
    'El coeficiente α no es válido.': 'The α coefficient is not valid.',
    'El dataset WRG no contiene coordenadas x/y válidas': 'The WRG dataset does not contain valid x/y coordinates',
    'El delta se calcula como B − A.': 'The delta is calculated as B − A.',
    'El factor de suelo G debe estar entre 0 y 1.': 'The ground factor G must be between 0 and 1.',
    'El idioma seleccionado se aplicará al hub, módulos, avisos y resúmenes generados.': 'The selected language will be applied to the hub, modules, warnings and generated summaries.',
    'El límite acústico no es válido.': 'The acoustic limit is not valid.',
    'El mapa raster se está generando en segundo plano.\n\nResolución:': 'The raster map is being generated in the background.\n\nResolution:',
    'El paso temporal de receptores debe ser mayor que 0 minutos.': 'The receiver time step must be greater than 0 minutes.',
    'El paso temporal del raster debe ser mayor que 0 minutos.': 'The raster time step must be greater than 0 minutes.',
    'El patrocinio no otorga propiedad ni control exclusivo sobre el roadmap. Las decisiones técnicas finales se mantienen en el mantenedor para preservar la calidad y la dirección open source del proyecto.': 'Sponsorship does not grant ownership or exclusive control over the roadmap. Final technical decisions remain with the maintainer to preserve the quality and open-source direction of the project.',
    'El proyecto no tiene un CRS válido.': 'The project does not have a valid CRS.',
    'El radio máximo debe ser mayor que 0.': 'The maximum radius must be greater than 0.',
    'El raster DEM/DSM seleccionado no es válido.': 'The selected DEM/DSM raster is not valid.',
    'El raster se creó pero no se pudo cargar en QGIS.': 'The raster was created but could not be loaded in QGIS.',
    'El recurso eólico describe el viento que llega al parque antes de aplicar estelas: frecuencia por dirección, velocidades y, según el formato, parámetros Weibull. Puedes usar WAsP/Surfer grids o WRG/ZIP. Si aportas varios WRG a distintas alturas, PyWake podrá interpolar por altura cuando sea necesario.': 'The wind resource describes the wind arriving at the wind farm before wake effects: directional frequency, wind speeds and, depending on the format, Weibull parameters. You can use WAsP/Surfer grids or WRG/ZIP. If you provide WRG files at several heights, PyWake can interpolate by height when needed.',
    'El rotor-average calcula el viento en varios puntos del disco del rotor, no solo en el centro. Más puntos dan más realismo cuando una estela corta parte del rotor, pero aumentan el tiempo de cálculo.': 'Rotor averaging calculates wind speed at several points of the rotor disk, not only at the centre. More points add realism when a wake cuts part of the rotor, but increase computation time.',
    'El rotor-average incrementa la fidelidad al muestrear el disco del rotor, a costa de más tiempo de cálculo.': 'Rotor averaging increases fidelity by sampling the rotor disk, at the cost of more computation time.',
    'El wake effect reduce el viento disponible detrás de cada turbina. Cada modelo aproxima esa pérdida de forma distinta.': 'The wake effect reduces the wind available behind each turbine. Each model approximates that loss differently.',
    'El/los raster(s) TI no existen:': 'The TI raster(s) do not exist:',
    'Elegir WRG/ZIP...': 'Choose WRG/ZIP...',
    'Elegir WRG/ZIP…': 'Choose WRG/ZIP…',
    'Elegir carpeta...': 'Choose folder...',
    'Elegir raster(s) TI...': 'Choose TI raster(s)...',
    'Elegir raster(s) TI…': 'Choose TI raster(s)…',
    'Elevación solar máxima [°]:': 'Max solar elevation [°]:',
    'Elevación solar máxima a incluir (90° = sin recorte de sol alto; valores menores son una hipótesis opcional de screening)': 'Maximum solar elevation to include (90° = no high-sun cut; lower values are an optional screening assumption)',
    'Elevación solar mínima [°]:': 'Min solar elevation [°]:',
    'Elevación solar:': 'Solar elevation:',
    'Elige qué capa/modelo editas con el click izquierdo/derecho del mapa. Esto permite modificar cualquiera de las capas cargadas, no solo la última creada.': 'Choose which layer/model you edit with the left/right click on the map. This lets you modify any of the loaded layers, not only the last one created.',
    'Elimina del proyecto la capa temporal del perímetro del recurso.': 'Removes the temporary resource-boundary layer from the project.',
    'En modo curva, cada grupo fuente puede usar un CSV ws/LwA distinto.': 'In curve mode, each source group can use a different ws/LwA CSV.',
    'En parques offshore grandes esto suele subestimar las pérdidas': 'In large offshore wind farms this usually underestimates losses',
    'Energía': 'Energy',
    'Energía\nAEP y wakes': 'Energy\nAEP and wakes',
    'Energía específica neta': 'Net specific yield',
    'Energía específica neta:': 'Net specific yield:',
    'Energía · AEP del parque': 'Energy · Wind farm AEP',
    'Energía: operativa · Ruido: operativo · Sombras: operativo': 'Energy: ready · Noise: ready · Shadow flicker: ready',
    'Enero': 'January',
    'Entradas del cálculo': 'Calculation inputs',
    'Error al detectar coordenadas': 'Error detecting coordinates',
    'Error al generar las gráficas:': 'Error while generating plots:',
    'Error al regenerar el raster': 'Error regenerating raster',
    'Error al regenerar el raster:': 'Error regenerating raster:',
    'Error creando turbina': 'Error creating turbine',
    'Error de raster': 'Raster error',
    'Error durante el cálculo': 'Error during calculation',
    'Error leyendo TXT/CSV': 'Error reading TXT/CSV',
    'Es robusto, pero la TI apenas cambia el déficit base.': 'It is robust, but TI barely changes the base deficit.',
    'Escenario acústico': 'Acoustic scenario',
    'Escenario acústico efectivo por grupo fuente:': 'Effective acoustic scenario by source group:',
    'Escenario acústico:': 'Acoustic scenario:',
    'Espacio': 'Space',
    'Espectro inválido:': 'Invalid spectrum:',
    'Espectro vacío': 'Empty spectrum',
    'Esta tabla replica el resumen por turbina visible en la interfaz. Para análisis GIS/Excel, usa también el CSV por turbina que genera el flujo AEP.': 'This table mirrors the per-turbine summary visible in the interface. For GIS/Excel analysis, also use the per-turbine CSV generated by the AEP workflow.',
    'Esta turbina no tiene la curva guardada en metadatos. Vuelve a pulsar «Definir…» para regenerarla.': 'This turbine has no curve stored in its metadata. Press “Define…” again to regenerate it.',
    'Estado': 'Status',
    'Estado de capas/modelos refrescado.': 'Layers/models state refreshed.',
    'Estado módulos:': 'Module status:',
    'Estadísticas de Shadow Flicker': 'Shadow Flicker Statistics',
    'Estadísticas de sombra:': 'Shadow Statistics:',
    'Estadísticos de Atenuaciones': 'Attenuation statistics',
    'Estadísticos de Atenuaciones (receptores cubiertos)': 'Attenuation statistics (covered receivers)',
    'Este campo solo se usa con': 'This field is only used with',
    'Este selector calcula turbulencia añadida por estela. No sustituye a la TI ambiente del site/WRG.': 'This selector calculates wake-added turbulence. It does not replace the site/WRG ambient TI.',
    'Estelas:': 'Wakes:',
    'Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.': 'These parameters calibrate the wake shape. For normal use, leave them as PyWake default values.',
    'Estos parámetros modifican la forma de la estela. Déjalos en «PyWake default» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.': 'These parameters modify the wake shape. Leave them as “PyWake default” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.',
    'Estos parámetros modifican la forma de la estela. Déjalos en «Valor PyWake por defecto» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.': 'These parameters modify the wake shape. Leave them as “PyWake default value” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.',
    'Evaluación de ruido generado por aerogeneradores': 'Assessment of wind turbine noise',
    'Examinar…': 'Browse…',
    'Excede': 'Exceeds',
    'Excedencias': 'Exceedances',
    'Explica por qué el rotor no debe tratarse siempre como un solo punto': 'Explains why the rotor should not always be treated as a single point',
    'Explica qué cambia cada parámetro sin asumir conocimiento previo': 'Explains what each parameter changes, without assuming prior knowledge',
    'Explica qué es el wake effect, cómo entra la TI y por qué algunos modelos son más sensibles a turbulencia': 'Explains what the wake effect is, how TI enters the calculation and why some models are more sensitive to turbulence',
    'Explica qué es la TI, cuándo usar raster de turbulencia y cómo afecta al AEP': 'Explains what TI is, when to use a turbulence raster and how it affects AEP',
    'Explica qué es una estela y qué cambia entre modelos': 'Explains what a wake is and what changes between models',
    'Explica qué pasa cuando varias estelas llegan a la misma turbina': 'Explains what happens when several wakes reach the same turbine',
    'Exporta los puntos editados de la capa activa a CSV. No sobrescribe el CSV original sin confirmar.': 'Exports the edited points of the active layer to CSV. It does not overwrite the original CSV without confirmation.',
    'Exportación correcta': 'Export Successful',
    'Exportación fallida': 'Export failed',
    'Exportados': 'Exported',
    'Exportar': 'Export',
    'Exportar CSV por turbina': 'Export per-turbine CSV',
    'Exportar CSV por turbina…': 'Export per-turbine CSV…',
    'Exportar Shadow Flicker 12×24': 'Export Shadow Flicker 12×24',
    'Exportar Top receivers CSV…': 'Export Top receivers CSV…',
    'Exportar diagnóstico MDT': 'Export DTM diagnostics',
    'Exportar excedencias': 'Export exceedances',
    'Exportar excedencias CSV…': 'Export exceedances CSV…',
    'Exportar grupos fuente': 'Export source groups',
    'Exportar grupos fuente CSV…': 'Export source groups CSV…',
    'Exportar informe AEP': 'Export AEP report',
    'Exportar informe HTML…': 'Export HTML report…',
    'Exportar informe…': 'Export report…',
    'Exportar layout editado': 'Export edited layout',
    'Exportar layout editado…': 'Export edited layout…',
    'Exportar matriz hora × mes a CSV': 'Export hour × month matrix to CSV',
    'Exportar paquete XLSX': 'Export XLSX package',
    'Exportar paquete XLSX…': 'Export XLSX package…',
    'Exportar receptores': 'Export receivers',
    'Exportar receptores CSV…': 'Export receivers CSV…',
    'Exportar resumen': 'Export summary',
    'Exportar resumen…': 'Export summary…',
    'Extensión WRG no soportada (use .wrg o .zip):': 'Unsupported WRG extension (use .wrg or .zip):',
    'Extensión WRG no soportada. Usa .wrg o .zip:': 'Unsupported WRG extension. Use .wrg or .zip:',
    'Extensión no soportada (use .wrg o .zip):': 'Unsupported extension (use .wrg or .zip):',
    'Extensión no soportada:': 'Unsupported extension:',
    'Extensión no válida:': 'Invalid extent:',
    'FC neto [%]': 'Net CF [%]',
    'Factor de capacidad (neto):': 'Capacity factor (net):',
    'Factor de capacidad neto': 'Net capacity factor',
    'Factor de suelo G:': 'Ground factor G:',
    'Falta el punto de entrada de compatibilidad del cálculo de ShadowPage.': 'ShadowPage compatibility calculation entry point is missing.',
    "Faltan las siguientes dependencias de Python para ejecutar Velantis Wind:": "The following Python dependencies are missing to run Velantis Wind:",
    'Faltan modelos': 'Missing models',
    'Fast: RotorCenter es el modo más rápido y recomendado para iteraciones rápidas o mapa interactivo.': 'Fast: RotorCenter is the fastest mode and is recommended for quick iterations or the interactive map.',
    'Febrero': 'February',
    'Fichero TXT/CSV:': 'TXT/CSV file:',
    'Fichero seleccionado:': 'Selected file:',
    'Filas de cabecera a saltar:': 'Header rows to skip:',
    'Filtrar raster por mes/hora (tras la generación)': 'Filter raster by month/hour (after generation)',
    'Filtro aplicado:': 'Applied filter:',
    'Financiación de una funcionalidad concreta si encaja con la dirección open source.': 'Fund a specific feature if it fits the open-source direction.',
    'Fracción de tiempo en que la turbina está operativa (0,97 = 97%)': 'Fraction of time the turbine is operational (0.97 = 97%)',
    'Frecuencia': 'Frequency',
    'Fuente': 'Source',
    'Física PyWake': 'PyWake physics',
    'G global de respaldo': 'Global fallback G',
    'G usado': 'G used',
    'G usado:': 'G used:',
    'G=0 suelo duro · G=1 suelo poroso': 'G=0 hard ground · G=1 porous ground',
    'GCLTurbulence — derivado Frandsen, formulación clásica. Constantes fijas.': 'GCLTurbulence — Frandsen-derived classic formulation. Fixed constants.',
    'GDAL no pudo crear el GeoTIFF del raster de ruido.': 'GDAL could not create the GeoTIFF of the noise raster.',
    'GDAL.Warp devolvió None al alinear el raster TI al grid WRG': 'GDAL.Warp returned None while aligning the TI raster to the WRG grid',
    'GDAL/OSR no disponible para leer el raster de turbulencia': 'GDAL/OSR not available to read the turbulence raster',
    'G_eff critical receiver usado': 'Critical-receiver G_eff used',
    'G_eff medio': 'Mean G_eff',
    'G_eff medio usado': 'Mean G_eff used',
    'G_eff receptor crítico': 'Critical-receiver G_eff',
    'GaussianOverlapAvgModel se ha sustituido por CGIRotorAvg(7) por compatibilidad.': 'GaussianOverlapAvgModel was replaced by CGIRotorAvg(7) for compatibility.',
    'GaussianOverlapAvgModel solo debe usarse con modelos de déficit gaussianos compatibles.': 'GaussianOverlapAvgModel should only be used with compatible Gaussian deficit models.',
    'Genera un mapa raster continuo de horas de shadow flicker': 'Generates a continuous raster map of shadow flicker hours',
    'Genera un mapa raster continuo de horas de shadow flicker\nMuestra un gradiente de color en toda el área de análisis\nAVISO: esto puede tardar varios minutos según el área': 'Generates a continuous raster map of shadow flicker hours\nDisplays a color gradient across the whole analysis area\nWARNING: this may take several minutes depending on the area',
    'Generado por Velantis Wind · Módulo Energía de QGIS · Flujo AEP basado en PyWake.': 'Generated by Velantis Wind · QGIS energy module · PyWake-based AEP workflow.',
    'Generando mapa raster de shadow flicker': 'Generating shadow flicker raster map',
    'Generando tabla por aerogenerador…': 'Generating per-turbine table…',
    'Generar capas de puntos': 'Generate point layers',
    'Generar isófonas': 'Generate isophones',
    'Generar mapa GIS de ruido (malla de cálculo)': 'Generate GIS noise map (calculation grid)',
    'Graficar con capas seleccionadas': 'Plot with selected layers',
    'Graficar estelas': 'Plot wakes',
    'Grids WAsP/Surfer:': 'WAsP/Surfer grids:',
    'Grupo': 'Group',
    'Grupo fuente': 'Source group',
    'Grupos fuente acústicos:': 'Acoustic source groups:',
    'Grupos_fuente': 'Source_groups',
    'Guarda el resumen AEP con logo Velantis Wind y formato corporativo en un archivo HTML.': 'Save the AEP summary with the Velantis Wind logo and corporate formatting as an HTML file.',
    'Guarda el resumen técnico en HTML o TXT.': 'Save the technical summary as HTML or TXT.',
    'Guarda el último cálculo AEP como escenario A para compararlo después.': 'Save the latest AEP calculation as scenario A for later comparison.',
    'Guarda el último cálculo AEP como escenario B para compararlo después.': 'Save the latest AEP calculation as scenario B for later comparison.',
    'Guarda el último cálculo como escenario A.': 'Saves the latest calculation as scenario A.',
    'Guarda el último cálculo como escenario B.': 'Saves the latest calculation as scenario B.',
    'Guarda primero un escenario A y un escenario B.': 'Save scenario A and scenario B first.',
    'Guarda primero un escenario A y un escenario B.\n\nFlujo recomendado: calcula el caso base → Guardar escenario A → cambia layout/modelo/física → calcula otra vez → Guardar escenario B → Comparar A/B.': 'Save scenario A and scenario B first.\n\nRecommended workflow: calculate the base case → Save scenario A → change layout/model/physics → calculate again → Save scenario B → Compare A/B.',
    'Guarda resumen, modelos, receptores y excedencias en un único libro Excel.': 'Save summary, models, receivers and exceedances in a single Excel workbook.',
    'Guarda solo los receptores que superan su límite acústico.': 'Save only the receivers exceeding their acoustic limit.',
    'Guarda una tabla limpia con una fila por aerogenerador: coordenadas, modelo, AEP neto/bruto y pérdidas principales.': 'Save a clean table with one row per turbine: coordinates, model, net/free-flow AEP and main losses.',
    'Guarda una tabla limpia con una fila por receptor y las columnas necesarias para consultoría.': 'Save a clean table with one row per receiver and the columns needed for consultancy.',
    'Guardar a CSV los puntos editados en el mapa interactivo.\nDefault: <original>_edited.csv (no sobrescribe el CSV original).': 'Save the points edited in the interactive map to CSV.\nDefault: <original>_edited.csv (does not overwrite the original CSV).',
    'Guardar escenario A': 'Save scenario A',
    'Guardar escenario B': 'Save scenario B',
    'Guardar resultados por turbina': 'Save per-turbine results',
    'Guía práctica muy corta': 'Very short practical guide',
    'Has definido varios hub heights pero no has seleccionado raster(s) TI. El cálculo seguirá con TI uniforme =': 'You have defined several hub heights but have not selected TI raster(s). The calculation will continue with a uniform TI =',
    'Has movido/añadido/borrado turbinas en el mapa interactivo.\nEl cálculo de AEP usa esas coordenadas vivas (no el CSV original).\nPulsa «Exportar layout editado…» si quieres persistirlas a CSV.': 'You have moved/added/deleted turbines in the interactive map.\nThe AEP calculation uses those live coordinates (not the original CSV).\nPress “Export edited layout…” if you want to persist them to CSV.',
    'Has seleccionado PropagateDownwind: PyWake no calcula bloqueo con este engine y el plugin lo ha desactivado automáticamente.': 'You selected PropagateDownwind: PyWake does not calculate blockage with this engine and the plugin has disabled it automatically.',
    'Hay WRG duplicados para la altura': 'There are duplicated WRG files for height',
    'Hay varias capas/modelos seleccionados, pero PyWake no ha podido activar type_i. Las estelas se dibujarán con el primer modelo como fallback.': 'Several layers/models are selected, but PyWake could not enable type_i. Wakes will be drawn with the first model as fallback.',
    'Hay varios hub heights pero solo una TI ambiente verticalmente uniforme; el cálculo sigue siendo válido, aunque es una aproximación en altura.': 'There are several hub heights but only a vertically uniform ambient TI; the calculation is still valid, although it is an approximation in height.',
    'Header WRG inválido (nx,ny,dx): nx=': 'Invalid WRG header (nx,ny,dx): nx=',
    "Header WRG inválido: '": "Invalid WRG header: '",
    'Hoja': 'Sheet',
    'Hora civil local (IANA/DST)': 'Local civil time (IANA/DST)',
    'Hora × Mes': 'Hour × Month',
    'Hora:': 'Hour:',
    'Horas de shadow flicker por hora del día': 'Hours of shadow flicker by hour of day',
    'Horas de shadow flicker por mes y hora del día': 'Shadow Flicker Hours by Month and Hour of Day',
    'Hub Height y Rotor Diameter deben ser números válidos.': 'Hub Height and Rotor Diameter must be valid numbers.',
    'Hub Height: altura de buje sobre el terreno [m]': 'Hub Height: hub height above ground level [m]',
    'Humedad relativa': 'Relative humidity',
    'HybridInduction — combina aproximaciones de inducción. Sin parámetros de usuario en esta versión experimental.': 'HybridInduction — combines induction approximations. No user parameters in this experimental release.',
    'IANA/DST: la tabla usa la hora civil local real del emplazamiento.\nOffset UTC fijo: usa el mismo offset UTC durante todo el año.': 'IANA/DST: the table uses the real local civil time of the site.\nFixed UTC offset: uses the same UTC offset for the full year.',
    'ID Receptor': 'Receiver ID',
    'ID de capa:': 'Layer ID:',
    'INFORME TÉCNICO DE IMPACTO ACÚSTICO': 'TECHNICAL NOISE IMPACT REPORT',
    'ISO-aligned por bandas': 'ISO-aligned by bands',
    'Idioma': 'Language',
    'Idioma del plugin:': 'Plugin language:',
    'Impacto TI [MWh]': 'TI impact [MWh]',
    'Impacto TI/turbulencia': 'TI/turbulence impact',
    'Impacto TI/turbulencia:': 'TI/turbulence impact:',
    'Importar curva acústica para el grupo fuente seleccionado…': 'Import acoustic curve for the selected source group…',
    'Incluir columna CT': 'Include CT column',
    'Informe': 'Report',
    'Informe AEP exportado correctamente:': 'AEP report exported successfully:',
    'Informe exportado': 'Report exported',
    'Informe técnico AEP': 'AEP technical report',
    'Informe técnico de ruido': 'Noise technical report',
    'Informe técnico generado desde el módulo Energía de Velantis Wind.': 'Technical report generated from the Velantis Wind Energy module.',
    'Inicializando cálculo…': 'Initializing calculation…',
    'Iniciando cálculo de shadow flicker...': 'Starting shadow-flicker calculation...',
    'Inicio': 'Home',
    'Instálalas en el entorno de Python de QGIS y vuelve a intentarlo.': 'Install them in the QGIS Python environment and try again.',
    'Interacción crítica con el motor': 'Critical interaction with the engine',
    'Interpretación física': 'Physical interpretation',
    'Introduce el EPSG en el que están las coordenadas de los tiles.\nPor ejemplo:\n  • 25829 → ETRS89 / UTM 29N (Galicia oeste)\n  • 25830 → ETRS89 / UTM 30N (gran parte de España)\n\nDeja vacío para usar': 'Enter the EPSG in which the tile coordinates are.\nFor example:\n  • 25829 → ETRS89 / UTM 29N (western Galicia)\n  • 25830 → ETRS89 / UTM 30N (most of Spain)\n\nLeave empty to use',
    'Julio': 'July',
    'Junio': 'June',
    'La GUI de QGIS/Qt no está disponible en este entorno.': 'The QGIS/Qt GUI is not available in this environment.',
    'La TI ambiente siempre viene del recurso/raster o del valor manual. Este selector añade turbulencia generada por estelas. Su impacto en AEP será mayor si el wake model usa TI de forma explícita, como Niayifar, Zong, TurboGaussian o TurboNOJ.': 'Ambient TI always comes from the resource/raster or the manual value. This selector adds wake-generated turbulence. Its AEP impact will be larger if the wake model uses TI explicitly, such as Niayifar, Zong, TurboGaussian or TurboNOJ.',
    "La TI es la intensidad de turbulencia ambiente: una medida de lo variable o 'mezclado' que llega el viento al rotor. Si no cargas raster TI, se usa el valor manual de arriba como hipótesis uniforme. La TI no siempre cambia mucho el AEP: se nota sobre todo en modelos TI-sensitive como Niayifar, Zong, TurboGaussian o TurboNOJ. Los raster(s) TI se reproyectarán al CRS del proyecto actual": 'TI is ambient turbulence intensity: a measure of how variable or mixed the wind is when it reaches the rotor. If you do not load a TI raster, the manual value above is used as a uniform assumption. TI does not always change AEP strongly: it is mainly visible in TI-sensitive models such as Niayifar, Zong, TurboGaussian or TurboNOJ. TI raster(s) will be reprojected to the current project CRS',
    'La altura de receptor no es válida.': 'The receiver height is not valid.',
    'La altura del observador no puede ser negativa.': 'Observer height cannot be negative.',
    'La capa activa no es de puntos.': 'The active layer is not a point layer.',
    'La capa activa no es vectorial.': 'The active layer is not a vector layer.',
    'La capa de puntos se creó correctamente.': 'The point layer was created successfully.',
    "La capa de receptores '": "The receiver layer '",
    'La capa de receptores está vacía.': 'The receiver layer is empty.',
    'La capa de receptores no contiene elementos válidos para el cálculo.': 'The receiver layer contains no valid features for calculation.',
    'La capa de receptores no está en el mismo CRS que el proyecto.': 'The receiver layer is not in the same CRS as the project.',
    'La capa de receptores seleccionada no es una capa vectorial.': 'The selected receiver layer is not a vector layer.',
    'La capa de receptores seleccionada no tiene elementos.': 'The selected receiver layer has no features.',
    'La capa de suelo no es vectorial válida y se ignorará.': 'The ground layer is not a valid vector layer and will be ignored.',
    'La capa de turbinas debe contener geometrías de punto.': 'The turbine layer must contain point geometries.',
    'La capa de turbinas seleccionada no es una capa vectorial.': 'The selected turbine layer is not a vector layer.',
    'La capa de turbinas seleccionada no tiene elementos.': 'The selected turbine layer has no features.',
    'La capa de uso del suelo debe ser poligonal.': 'The land-use layer must be polygonal.',
    'La capa de uso del suelo debe ser vectorial poligonal.': 'The land-use layer must be a polygonal vector layer.',
    'La capa de uso del suelo no está en el mismo CRS que el proyecto.': 'The land-use layer is not in the same CRS as the project.',
    'La capa de uso del suelo no está en el mismo CRS que el proyecto. Reproyéctala antes de calcular.': 'The land-use layer is not in the same CRS as the project. Reproject it before calculating.',
    'La capa seleccionada': 'The selected layer',
    'La carpeta WAsP/Surfer no existe.': 'The WAsP/Surfer folder does not exist.',
    'La combinación de wake TI-driven con SelfSimilarity2020 no convergió en PyWake. Se ha mantenido el bloqueo sustituyéndolo por': 'The combination of a TI-driven wake with SelfSimilarity2020 did not converge in PyWake. Blockage was kept by replacing it with',
    'La disponibilidad de turbina debe estar entre 0 y 1.': 'Turbine availability must be between 0 and 1.',
    'La distancia máxima de sombra debe ser mayor que 0 m.': 'The maximum shadow distance must be greater than 0 m.',
    'La distancia mínima debe ser mayor que 0.': 'The minimum distance must be greater than 0.',
    'La elevación solar mínima debe ser menor que la máxima.': 'Minimum sun elevation must be lower than maximum sun elevation.',
    'La entrada DEM/DSM seleccionada no es una capa raster.': 'The selected DEM/DSM input is not a raster layer.',
    'La explicación detallada': 'The detailed explanation',
    'La generación del raster fue cancelada.': 'Raster generation was cancelled.',
    'La latitud debe estar entre -90° y 90°.': 'Latitude must be between -90° and 90°.',
    'La longitud debe estar entre -180° y 180°.': 'Longitude must be between -180° and 180°.',
    'La resolución del raster debe ser mayor que 0 m.': 'The raster resolution must be greater than 0 m.',
    'La resolución del raster debe ser mayor que 0.': 'The raster resolution must be greater than 0.',
    'La simulación no pudo ejecutarse exactamente con la combinación pedida y PyWake requirió una degradación automática.': 'The simulation could not run exactly with the requested combination and PyWake required automatic degradation.',
    'La simulación principal no pudo ejecutarse con la combinación pedida y PyWake requirió la degradación automática:': 'The main simulation could not run with the requested combination and PyWake required automatic degradation:',
    'La simulación requirió degradación automática:': 'The simulation required automatic degradation:',
    'La superposición decide cómo sumar varias estelas sobre una misma turbina. SquaredSum suele ser una opción equilibrada; LinearSum acumula más pérdidas; MaxSum solo toma la estela dominante.': 'Superposition decides how to combine several wakes on the same turbine. SquaredSum is usually balanced; LinearSum accumulates more losses; MaxSum only takes the dominant wake.',
    'La superposición dice cómo sumar varias estelas cuando una turbina está detrás de más de una turbina.': 'Superposition defines how to add several wakes when one turbine is behind more than one upstream turbine.',
    "La zona '{}' no existe en la base IANA incluida.": "Time zone '{}' does not exist in the bundled IANA database.",
    'Las capas de receptores/fuentes y, si estaba activado, el mapa raster de ruido se han añadido al proyecto.': 'Receiver/source layers, and the noise raster map if enabled, have been added to the project.',
    'Las constantes internas del modelo no se exponen porque están fijadas por el paper de referencia.': 'The model internal constants are not exposed because they are fixed by the reference paper.',
    'Las extensiones de los grids no tienen intersección válida.': 'The grid extents have no valid intersection.',
    'Las plantillas son genéricas para pre-screening. Para entrega final usa la curva oficial del fabricante/cliente.': 'Templates are generic for pre-screening. For final delivery, use the official manufacturer/client curve.',
    'Latitud [°]:': 'Latitude [°]:',
    'Latitud del emplazamiento (ej. 42.465 para Logroño)': 'Site latitude (e.g. 42.465 for Logroño)',
    'Latitud:': 'Latitude:',
    'Layout activo:': 'Active layout:',
    'Lector WRG no disponible en esta instalación': 'WRG reader not available in this installation',
    'Lector WRG no disponible; no se puede dibujar el perímetro WRG.': 'WRG reader not available; the WRG boundary cannot be drawn.',
    'Lectura del receptor crítico': 'Critical-receiver reading',
    'Limitaciones y recomendaciones': 'Limitations and recommendations',
    'Limpiar': 'Clear',
    'Limpiar perímetro': 'Clear boundary',
    'LinearSum suma los déficits linealmente; suele ser conservador en parques densos.': 'LinearSum adds deficits linearly; it is usually conservative in dense wind farms.',
    'LinearSum: suma directamente varias pérdidas de velocidad. Puede aumentar las pérdidas en parques densos.': 'LinearSum: directly adds several velocity deficits. It can increase losses in dense wind farms.',
    'Lista WRG vacía': 'Empty WRG list',
    'Listas `ws` y `power_kw` deben tener la misma longitud y no estar vacías.': 'Lists `ws` and `power_kw` must have the same length and not be empty.',
    'Listo': 'Ready',
    'Logo de empresa en el README y en la web de VelantisWind.': 'Company logo on README and VelantisWind website.',
    'Logo destacado y conversación sobre prioridades del roadmap.': 'Featured logo and a conversation about roadmap priorities.',
    'Longitud [°]:': 'Longitude [°]:',
    'Longitud del emplazamiento (ej. -2.445 para Logroño)': 'Site longitude (e.g. -2.445 for Logroño)',
    'Longitud:': 'Longitude:',
    'Los raster(s) se reproyectarán al CRS del proyecto actual': 'The raster(s) will be reprojected to the current project CRS',
    'Los valores mostrados a continuación son las magnitudes de atenuación usadas por el modelo. En la ecuación principal estos términos se restan al nivel de fuente.': 'The values shown below are the attenuation magnitudes used by the model. In the main equation these terms are subtracted from the source level.',
    'LwA fijo por grupo fuente acústico': 'fixed LwA by acoustic source group',
    'LwA fuente dom. dB(A)': 'dominant source LwA dB(A)',
    'LwA fuente dominante': 'Dominant source LwA',
    'Límite': 'Limit',
    'Límite aplicable': 'Applicable limit',
    'Límite de referencia': 'Reference limit',
    'Límite día': 'Day limit',
    'Límite noche': 'Night limit',
    'Límite receptor [dB(A)]:': 'Receiver limit [dB(A)]:',
    'Límite:': 'Limit:',
    'Límites aplicados': 'Applied limits',
    'Línea WRG demasiado corta': 'WRG line too short',
    'Línea WRG incompleta: esperados >=': 'Incomplete WRG line: expected >=',
    'Línea de punto WRG inválida:': 'Invalid WRG point line:',
    'MDT / DSM (opcional):': 'DTM / DSM (optional):',
    'MWh/año': 'MWh/year',
    'Magnitud típica en AEP': 'Typical magnitude in AEP',
    'Malla uniforme · EqGridRotorAvg': 'Uniform grid · EqGridRotorAvg',
    'Mapa GIS creado:': 'GIS map created:',
    'Mapa de Estelas (': 'Wake map (',
    'Mapa interactivo': 'Interactive map',
    'Mapa raster creado correctamente.': 'Raster map created successfully.',
    'Mapa raster creado correctamente.\n\nPuntos calculados:': 'Raster map created successfully.\n\nCalculated points:',
    'Mapa raster creado correctamente.\n\nPuntos calculados: ': 'Raster map created successfully.\n\nCalculated points: ',
    'Mapa raster:': 'Raster map:',
    'Marcar todas': 'Select all',
    'Margen': 'Margin',
    'Marzo': 'March',
    'Matriz hora × mes': 'Hour × Month Matrix',
    'MaxSum considera solo el déficit más severo; útil como comprobación robusta, pero puede infraestimar pérdidas acumuladas.': 'MaxSum considers only the most severe deficit; useful as a robust check, but it may underestimate cumulative losses.',
    'MaxSum: usa solo la estela dominante. Sirve para diagnosticar, pero puede quedarse corto si varias estelas se acumulan.': 'MaxSum: uses only the dominant wake. Useful for diagnosis, but it can underestimate accumulated losses from several wakes.',
    'Mayo': 'May',
    'Media [dB]': 'Mean [dB]',
    'Media:': 'Mean:',
    'Mejoras en AEP, ruido, sombras, lectura de recursos y exportación de resultados.': 'Improvements in AEP, noise, shadow flicker, resource ingestion and result exports.',
    'Mes': 'Month',
    'Mes:': 'Month:',
    'Metodología de Cálculo': 'Calculation methodology',
    'Modelo': 'Model',
    'Modelo WT': 'WT model',
    'Modelo activo:': 'Active model:',
    'Modelo basado en LUT/RANS': 'LUT/RANS-based model',
    'Modelo base': 'Base model',
    'Modelo de PyWake': 'PyWake model',
    'Modelo de bloqueo': 'Blockage model',
    'Modelo de bloqueo (PyWake)': 'Blockage model (PyWake)',
    'Modelo de estela: pérdida de velocidad': 'Wake model: wind-speed loss',
    'Modelo de pérdida por estela (wake deficit)': 'Wake deficit model (PyWake)',
    'Modelo de turbulencia': 'Turbulence model',
    'Modelo de turbulencia (PyWake)': 'Turbulence model (PyWake)',
    'Modelo de turbulencia añadida': 'Added turbulence model',
    'Modelo dominante': 'Dominant model',
    'Modelo que estima cuánto viento pierde una turbina cuando está dentro de la estela de otra. Algunos modelos usan la turbulencia TI y otros apenas la notan.': "Model that estimates how much wind a turbine loses when it is inside another turbine's wake. Some models use TI turbulence and others barely notice it.",
    'Modelo rotor-average': 'Rotor-average model',
    'Modelo rotor-average (PyWake)': 'Rotor-average model (PyWake)',
    'Modelo simplificado LwA global, absorción fija, efecto suelo empírico': 'Simplified global LwA model, fixed absorption, empirical ground effect',
    'Modelos': 'Models',
    'Modelos WT detectados:': 'Detected WT models:',
    'Modelos de aerogenerador': 'Wind turbine models',
    'Modo suelo': 'Ground mode',
    'Modo suelo desde capa activo sin capa válida: will use G global como respaldo.': 'Ground-from-layer mode active without a valid layer: the global G will be used as fallback.',
    'Mostrar perímetro del recurso en el mapa': 'Show resource boundary on the map',
    'Motor': 'Engine',
    'Motor Rápido (actual)': 'Fast engine (current)',
    'Motor rápido: cálculo simplificado para cribado acústico\nMotor ISO: cálculo por bandas según ISO 9613-2:2024': 'Fast engine: simplified screening calculation\nISO engine: band-based calculation following ISO 9613-2:2024',
    'Motor de cálculo PyWake': 'PyWake propagation engine',
    'Motor de cálculo:': 'Calculation engine:',
    'Motor utilizado:': 'Engine used:',
    'Muy realista · CGIRotorAvg(21)': 'Very accurate · CGIRotorAvg(21)',
    'Más realista · CGIRotorAvg(7)': 'Balanced · CGIRotorAvg(7)',
    'Más realista · CGIRotorAvg(9)': 'Accurate · CGIRotorAvg(9)',
    'Máximo [dB]': 'Maximum [dB]',
    'Método ISO-aligned: ISO 9613-2 con bandas de octava (Adiv + Aatm(T,RH,P) + Agr(f))': 'ISO-aligned method: ISO 9613-2 with octave bands (Adiv + Aatm(T,RH,P) + Agr(f))',
    'Método fuente-receptor eólico (Adiv + Aatm + Aground)': 'Wind source-receiver method (Adiv + Aatm + Aground)',
    'Método usado:': 'Method used:',
    'Método:': 'Method:',
    'Métrica': 'Metric',
    'Métricas adicionales': 'Additional metrics',
    'México tiene varias zonas; verifica manualmente.': 'Mexico has several zones; check manually.',
    'Módulo Energía · Flujo PyWake': 'Energy module · PyWake workflow',
    'Módulo de ruido: conectado al layout y modelos WT del proyecto, con cálculo sobre receptores, tabla de emisión por modelo y salidas en capa. La física actual sigue un enfoque fuente-receptor propio simplificado y documentación de limitaciones para validación técnica.': 'Noise module: connected to the project layout and WT models, with per-receiver calculation, a per-model emission table and layer outputs. The current physics follows a simplified source-receiver approach with documented limitations for technical validation.',
    'Módulo de ruido: motor de screening eólico con receptores, grupos fuente, propagación ISO-aligned por bandas, mapas raster y salidas GIS.': 'Noise module: wind screening engine with receivers, source groups, ISO-aligned octave-band propagation, raster maps and GIS outputs.',
    'Módulo de cálculo de shadow flicker para aerogeneradores. Conectado al layout del proyecto, con cálculo por receptores puntuales y capas de salida QGIS con resultados detallados por receptor.': 'Shadow flicker calculation module for wind turbines. Connected to the project layout, with point-receiver calculation and QGIS output layers with detailed results by receiver.',
    'NIVEL RESULTANTE': 'RESULTING LEVEL',
    'NO calcula bloqueo': 'does NOT calculate blockage',
    'NOJ/Jensen: modelo clásico y muy rápido. Usa una expansión fija; la TI ambiente suele tener poco efecto directo en el déficit.': 'NOJ/Jensen: classic and very fast model. It uses a fixed expansion; ambient TI usually has little direct effect on the deficit.',
    'NOJDeficit usa una expansión top-hat constante: el modelo de turbulencia no cambia el AEP en la configuración estándar.': 'NOJDeficit uses a constant top-hat expansion: the turbulence model does not change AEP in the standard configuration.',
    'Neto+pérd.': 'Net+losses',
    'Niayifar: gaussiano TI-sensitive. Recomendable si quieres que el raster TI o la TI manual influyan en la recuperación de estela.': 'Niayifar: TI-sensitive Gaussian model. Useful when you want the TI raster or manual TI to influence wake recovery.',
    'Ninguno': 'None',
    'Nivel Máximo (dB(A))': 'Maximum level (dB(A))',
    'Nivel total': 'Total level',
    'Niveles isófonas [dB(A)]:': 'Isophone levels [dB(A)]:',
    'No encuentro una capa de turbinas «{Modelo} (CSV)» para editar.\n\nPulsa primero «Generar capas de puntos» o carga una capa del modelo en el proyecto.': 'No turbine layer “{Modelo} (CSV)” was found to edit.\n\nPress “Generate point layers” first, or load a model layer into the project.',
    'No había perímetro del recurso que limpiar.': 'There was no resource boundary to clear.',
    'No hay capa de fuentes para exportar.': 'There is no source layer to export.',
    'No hay capa de receptores para exportar.': 'There is no receiver layer to export.',
    'No hay capa de receptores seleccionada.': 'No receiver layer selected.',
    'No hay capa de turbinas seleccionada.': 'No turbine layer selected.',
    'No hay capas activas de receptores por categoría.': 'There are no active receiver layers by category.',
    'No hay capas de turbinas válidas con puntos para calcular.\n\nCrea una capa con «Generar capas de puntos», define su modelo de turbina y añade al menos un aerogenerador en el mapa.': 'There are no valid turbine layers with points to calculate.\n\nCreate a layer with “Generate point layers”, define its turbine model and add at least one turbine on the map.',
    'No hay configuración acústica de modelos.': 'There is no acoustic model configuration.',
    'No hay configuración de modelos disponible.\n\nRellena Hub Height y Rotor Diameter en la tabla.': 'No model configuration is available.\n\nPlease fill Hub Height and Rotor Diameter in the table.',
    'No hay coordenadas válidas en las capas/CSV proporcionados.': 'There are no valid coordinates in the provided layers/CSV.',
    'No hay datos por turbina para exportar.': 'There are no per-turbine data to export.',
    'No hay datos raster disponibles. Genera primero un raster.': 'No raster data are available. Generate a raster first.',
    'No hay diagnóstico por pares fuente-receptor disponible. Recalcula con motor ISO-aligned y fuentes dentro del radio. Este CSV es el que permite auditar cada turbina contra cada receptor.': 'No source-receiver pairwise diagnostics are available. Recalculate with the ISO-aligned engine and sources inside the radius. This CSV is the one that lets you audit each turbine against each receiver.',
    'No hay filas de Top receivers para exportar.': 'There are no Top receivers rows to export.',
    'No hay filas de receptores para exportar.': 'There are no receiver rows to export.',
    'No hay filas de screening MDT para exportar.': 'There are no DTM screening rows to export.',
    'No hay fuentes acústicas válidas para crear el raster.': 'There are no valid acoustic sources to create the raster.',
    'No hay layouts editados pendientes de exportar.': 'There are no edited layouts pending export.',
    "No hay motor WFM disponible para engine='": "No WFM engine available for engine='",
    'No hay puntos válidos para calcular': 'No valid points to calculate',
    'No hay receptores que superen el límite en este cálculo.': 'No receivers exceed the limit in this calculation.',
    'No hay turbina cerca del clic.': 'There is no turbine near the click.',
    'No hay una capa de turbinas válida activa para el mapa interactivo.': 'There is no valid active turbine layer for the interactive map.',
    'No hay wake deficit model disponible: ni BG ni NoWakeDeficit.': 'No wake deficit model available: neither BG nor NoWakeDeficit.',
    'No se detectaron modelos. Comprueba que existen capas de turbinas.': 'No models detected. Check that turbine layers exist.',
    'No se encontraron GRD válidos en los directorios dados': 'No valid GRD files were found in the given directories',
    'No se encontraron coordenadas válidas en el CSV.': 'No valid coordinates were found in the CSV.',
    'No se encontraron posiciones solares válidas': 'No valid solar positions were found',
    'No se encontraron turbinas en el layout.': 'No turbines were found in the layout.',
    'No se encontraron turbinas válidas con la configuración de modelo.': 'No valid turbines were found with the model configuration.',
    'No se encontraron turbinas válidas con la configuración de modelo.\n\nComprueba que:\n1. Hay capas de turbinas seleccionadas\n2. La tabla de modelos tiene Hub Height y Rotor Diameter configurados\n3. Los nombres de modelo coinciden con las capas': 'No valid turbines were found with the model configuration.\n\nCheck that:\n1. Turbine layers are selected\n2. The model table has Hub Height and Rotor Diameter configured\n3. Model names match the layers',
    'No se encontró la capa DEM/DSM seleccionada; actualiza el módulo de sombras.': 'The selected DEM/DSM layer was not found; refresh the shadow module.',
    'No se encontró la capa de receptores en el proyecto QGIS actual.': 'Receiver layer not found in the current QGIS project.',
    'No se encontró la capa de receptores.': 'Receiver layer not found.',
    'No se encontró la capa de turbinas en el proyecto QGIS actual.': 'Turbine layer not found in the current QGIS project.',
    'No se encontró la capa de turbinas.': 'Turbine layer not found.',
    'No se encontró ningún WRG legible en:': 'No readable WRG was found in:',
    "No se encontró un WindTurbines en 'models' y V80 no está disponible.": "No WindTurbines was found in 'models' and V80 is not available.",
    'No se encontró una PowerCtFunction válida.': 'No valid PowerCtFunction was found.',
    "No se encontró/creó una PowerCtFunction válida para el modelo '": "No valid PowerCtFunction was found/created for model '",
    'No se ha definido ningún modelo de aerogenerador.': 'No turbine model has been defined.',
    'No se ha detectado layout': 'No layout detected',
    'No se ha encontrado la factoría del módulo de energía.': 'The energy module factory was not found.',
    "No se ha podido encontrar una subcarpeta válida para PyWake en '": "Could not find a valid PyWake subfolder in '",
    'No se ha seleccionado DEM/MDT; se asume terreno plano': 'No DEM selected - flat terrain assumed',
    'No se ha seleccionado ningún WRG/ZIP.': 'No WRG/ZIP has been selected.',
    'No se ha seleccionado raster TI para WRG; se usará TI fija/fallback si el modelo lo necesita.': 'No TI raster was selected for WRG; a fixed/fallback TI will be used if the model needs it.',
    'No se han detectado turbinas/layout válidos para el cálculo acústico.': 'No valid turbines/layout were detected for the acoustic calculation.',
    'No se han encontrado grids .grd/.asc/.tif en la carpeta seleccionada.': 'No .grd/.asc/.tif grids were found in the selected folder.',
    'No se han indicado raster(s) TI válidos': 'No valid TI raster(s) were provided',
    'No se han leído datos válidos de': 'No valid data were read from',
    'No se proporcionó una capa de receptores válida.': 'No valid receiver layer was provided.',
    'No se pudieron leer datos válidos de': 'Could not read valid data from',
    'No se pudieron leer pares ws/LwA válidos de': 'Could not read valid ws/LwA pairs from',
    'No se pudieron resolver las coordenadas del layout: faltan los CSV por modelo y no existe una carpeta base válida para buscar turbines.csv.': 'The layout coordinates could not be resolved: the per-model CSVs are missing and there is no valid base folder to look for turbines.csv.',
    'No se pudo abrir el diálogo de turbina:': 'Could not open the turbine dialog:',
    'No se pudo abrir el módulo de energía.': 'Could not open the energy module.',
    'No se pudo abrir la comparación:': 'Could not open the comparison:',
    'No se pudo activar el modo interactivo:': 'Could not activate interactive mode:',
    'No se pudo activar la capa seleccionada:': 'Could not activate the selected layer:',
    'No se pudo aplicar la plantilla:': 'Could not apply the template:',
    'No se pudo añadir turbina:': 'Could not add turbine:',
    'No se pudo borrar turbina:': 'Could not delete turbine:',
    'No se pudo calcular el ruido': 'Could not calculate noise',
    'No se pudo calcular el ruido en segundo plano:': 'Could not calculate noise in the background:',
    'No se pudo calcular el ruido:': 'Could not calculate noise:',
    'No se pudo calcular la variante TI ambiente only para el modelo TI-driven; se mantiene el AEP principal. Error: ': 'The ambient-TI-only variant could not be computed for the TI-driven model; the main AEP is kept. Error: ',
    'No se pudo cargar PyWake para construir la turbina. Comprueba que py_wake está instalado en el Python de QGIS y que Windows no está bloqueando alguna DLL/.pyd de sus dependencias.': 'PyWake could not be loaded to build the turbine. Check that py_wake is installed in the QGIS Python and that Windows is not blocking a DLL/.pyd of its dependencies.',
    'No se pudo cargar el TIF filtrado': 'Could not load the filtered TIF',
    'No se pudo cargar el raster generado:': 'Could not load the generated raster:',
    'No se pudo cargar la herramienta de mapa interactivo (import fallido).': 'Could not load the interactive map tool (import failed).',
    "No se pudo cargar la zona horaria IANA '{}'. La repo incluye una base IANA local, pero esa zona no se encontró o no pudo abrirse. Prueba con una zona como 'Europe/Madrid' o usa el modo UTC offset fijo. Error zoneinfo: {}; error base incluida: {}": "Could not load the IANA time zone '{}'. The repo bundles a local IANA database, but that zone was not found or could not be opened. Try a zone like 'Europe/Madrid' or use the fixed UTC offset mode. zoneinfo error: {}; bundled database error: {}",
    'No se pudo completar el cálculo:': 'Could not complete the calculation:',
    'No se pudo construir el WindTurbines combinado para varios modelos. Se usará el primer modelo como fallback.': 'The combined WindTurbines for several models could not be built. The first model will be used as fallback.',
    'No se pudo crear el archivo raster.': 'Could not create the raster file.',
    'No se pudo crear el mapa raster:': 'Could not create raster map:',
    'No se pudo crear la capa del modelo': 'Could not create the model layer',
    'No se pudo crear la capa resumen:': 'Could not create the summary layer:',
    'No se pudo crear la capa temporal de perímetro del recurso.': 'Could not create the temporary resource-boundary layer.',
    'No se pudo detectar una zona horaria fiable sin una base mundial de polígonos. Selecciona manualmente la zona IANA en el desplegable; la repo incluye un catálogo amplio y una base TZif con reglas DST.': 'Could not detect a reliable time zone without a world polygon database. Select the IANA zone manually in the dropdown; the repo includes a broad catalogue and a TZif database with DST rules.',
    'No se pudo dibujar el perímetro del recurso:': 'Could not draw the resource boundary:',
    'No se pudo ejecutar la simulación con los modelos seleccionados. Último error:': 'The simulation could not run with the selected models. Last error:',
    'No se pudo exportar': 'Could not export',
    'No se pudo exportar el CSV': 'Could not export the CSV',
    'No se pudo exportar el CSV de diagnóstico MDT:': 'Could not export the DTM diagnostics CSV:',
    'No se pudo exportar el CSV por turbina:': 'Could not export the per-turbine CSV:',
    'No se pudo exportar el CSV:': 'Could not export the CSV:',
    'No se pudo exportar el XLSX': 'Could not export the XLSX',
    'No se pudo exportar el XLSX:': 'Could not export the XLSX:',
    'No se pudo exportar el informe': 'Could not export the report',
    'No se pudo exportar el informe AEP:': 'Could not export the AEP report:',
    'No se pudo exportar el resumen': 'Could not export the summary',
    'No se pudo exportar el resumen:': 'Could not export the summary:',
    'No se pudo generar el gráfico total:': 'Could not generate the total chart:',
    'No se pudo generar el raster:': 'Could not generate the raster:',
    'No se pudo generar la capa de puntos:': 'Could not generate the point layer:',
    'No se pudo generar la rosa de sectores:': 'Could not generate the sector rose:',
    "No se pudo importar 'graficar.py':": "Could not import 'graficar.py':",
    'No se pudo importar load_wrg_site. Revisa que exista ag_core/wrg_site.py.': 'Could not import load_wrg_site. Check that ag_core/wrg_site.py exists.',
    'No se pudo importar ningún superposition model (LinearSum/SquaredSum/WeightedSum).': 'No superposition model could be imported (LinearSum/SquaredSum/WeightedSum).',
    'No se pudo instanciar el modelo de turbulencia seleccionado; el plugin ha intentado un fallback seguro (STF2017/STF2005/GCL).': 'The selected turbulence model could not be instantiated; the plugin attempted a safe fallback (STF2017/STF2005/GCL).',
    'No se pudo invertir la geotransformación del MDT/DSM.': 'Could not invert the DTM/DSM geotransform.',
    'No se pudo lanzar el cálculo en segundo plano:': 'Could not start the background calculation:',
    'No se pudo leer la curva acústica': 'Could not read the acoustic curve',
    'No se pudo leer la extensión espacial de los grids seleccionados.': 'Could not read the spatial extent of the selected grids.',
    'No se pudo leer ningún WRG desde:': 'No WRG could be read from:',
    'No se pudo leer ningún WRG válido': 'No valid WRG could be read',
    'No se pudo leer una extensión válida desde el WRG/ZIP.': 'Could not read a valid extent from the WRG/ZIP.',
    'No se pudo limpiar el perímetro del recurso:': 'Could not clear the resource boundary:',
    'No se pudo mostrar el resumen visual de resultados:': 'Could not display the visual results summary:',
    'No se pudo mostrar el resumen visual de ruido': 'Could not display the visual noise summary',
    'No se pudo mostrar el resumen visual de ruido:': 'Could not display the visual noise summary:',
    'No se pudo obtener el map canvas de QGIS.': 'Could not access the QGIS map canvas.',
    'No se pudo preparar el cálculo en segundo plano:': 'Could not prepare the background calculation:',
    'No se pudo preparar la selección de capas:': 'Could not prepare the layer selection:',
    'No se pudo resolver el CRS destino del WRG/proyecto para el raster TI': 'Could not resolve the target WRG/project CRS for the TI raster',
    'No se pudo usar la selección de capas; se usará el flujo normal:': 'Could not use the layer selection; the normal workflow will be used:',
    'No se puede iniciar el cálculo de shadow flicker:': 'Cannot start the shadow-flicker calculation:',
    'No se puede lanzar el cálculo': 'Cannot start the calculation',
    'No se seleccionó carpeta de salida.': 'No output folder was selected.',
    'No seleccionado (fallback previsto a TI=10% en flujo WRG)': 'Not selected (expected fallback to TI=10% in WRG flow)',
    'No todos los modelos de estela usan la TI del mismo modo': 'Not all wake models use TI in the same way',
    'Nocturno': 'Night-time',
    'Noise · Mapa': 'Noise · Map',
    "Nombre de zona horaria inválido: '{}'": "Invalid time-zone name: '{}'",
    'Nombre en README y acceso a notas de actualización/actualizaciones cuando estén disponibles.': 'Name in README and experimental/update notes when available.',
    'Nombre:': 'Name:',
    'Nota': 'Note',
    'Nota sencilla: este motor calcula interacciones hacia abajo y hacia arriba. Para que PyWake no falle con algunas combinaciones, el plugin activa automáticamente use_effective_ws=True en estelas y bloqueo.': 'Simple note: this engine calculates interactions downstream and upstream. To avoid PyWake errors with some combinations, the plugin automatically sets use_effective_ws=True for wake and blockage calculations.',
    'Nota: con PropagateUpDownIterative se fuerza automáticamente use_effective_ws=True (wake y bloqueo) para evitar errores de compatibilidad.': 'Note: with PropagateUpDownIterative, use_effective_ws=True is automatically forced for wake and blockage to avoid compatibility errors.',
    'Nota: el nivel resultante incluye la suma energética multi-fuente y multi-banda; no es una resta directa de una única turbina.': 'Note: the resulting level includes multi-source and multi-band energetic summation; it is not a direct subtraction from one single turbine.',
    'Notas': 'Notes',
    'Notas:': 'Notes:',
    'Noviembre': 'November',
    'Nº aerogeneradores': 'No. turbines',
    'Nº de aerogeneradores:': 'No. of turbines:',
    'Número de modelos:': 'Number of models:',
    'Número de núcleos a usar (tu CPU tiene': 'Number of cores to use (your CPU has',
    'Número de palas usado en la longitud near-wake. Normalmente 3.': 'Number of blades used in the near-wake length. Usually 3.',
    'Octubre': 'October',
    'Ocurrió un error:': 'An error occurred:',
    'Offset UTC fijo': 'Fixed UTC offset',
    'Offset UTC fijo:': 'Fixed UTC offset:',
    'Offset fijo para modo con offset fijo. No se aplica horario de verano.': 'Fixed offset for fixed-offset mode. Daylight saving time is not applied.',
    'Offset mínimo de expansión cuando la TI tiende a cero.': 'Minimum expansion offset when TI tends to zero.',
    'Offset mínimo de expansión de la estela.': 'Minimum wake-expansion offset.',
    'Offshore con inflow relativamente limpio': 'Offshore with relatively clean inflow',
    'Opcional: altura de cada raster TI, ej. 90;120': 'Optional: height of each TI raster, e.g. 90;120',
    'Opcional: alturas TI por archivo, ej. 90;120': 'Optional: TI heights by file, e.g. 90;120',
    'Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif).': 'Optionally provide one or more ambient TI raster(s), for example .asc/.tif.',
    'Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif). Se usan solo en el flujo WRG. Si no se selecciona ninguno, el plugin avisará y usará TI=10% como fallback. Consulta el botón de información para ver cuándo conviene una sola capa, cuándo convienen varias alturas y en qué modelos de estela influye más.': 'Optionally provide one or more ambient TI raster(s), for example .asc/.tif. They are used only in the WRG flow. If none are selected, the plugin will warn and use TI=10% as fallback. Use the information button to see when a single layer is enough, when several heights are useful, and which wake models are most affected.',
    'Opciones recomendadas según el caso': 'Recommended options by case',
    'Origen espectro': 'Spectrum source',
    'Otras pérdidas': 'Other losses',
    'Otras pérdidas:': 'Other losses:',
    'Override de parámetros físicos del modelo. Si dejas «Default PyWake», se usa el valor canónico del modelo seleccionado.': "Override physical model parameters. If you leave 'Default PyWake', the canonical value for the selected model is used.",
    'Override manual de altura para cada raster TI, en el mismo orden que los archivos. Útil cuando el nombre del archivo no contiene 90m, 120m, etc.': 'Manual height override for each TI raster, in the same order as the files. Useful when the file name does not contain 90 m, 120 m, etc.',
    'P_nom [MW]': 'Rated power [MW]',
    'Para pruebas, usa Auto recomendado o SquaredSum como referencia de sensibilidad.': 'For testing, use Recommended auto or SquaredSum as the sensitivity reference.',
    'Para empresas y equipos técnicos': 'For companies and technical teams',
    'Para mover la sensibilidad del AEP a turbulencia': 'To adjust AEP sensitivity to turbulence',
    'Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.': 'For representative results, load a TI raster in the resource section or review the turbulence sensitivity.',
    'Paralelización:': 'Parallelization:',
    'Parques offshore grandes': 'Large offshore wind farms',
    'Parques onshore pequeños o muy espaciados': 'Small or widely spaced onshore wind farms',
    'Parámetro': 'Parameter',
    'Parámetro λ usado en la longitud near-wake de Vermeulen.': 'λ parameter used in the Vermeulen near-wake length.',
    'Parámetros activos:': 'Parameters active:',
    'Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)': 'Advanced model parameters (optional · leave unchanged for normal use)',
    'Parámetros avanzados del modelo (opcional)': 'Advanced model parameters (optional)',
    'Parámetros del cálculo': 'Calculation parameters',
    'Paso temporal': 'Time step',
    'Paso temporal SOLO para el raster (independiente de los receptores).': 'Time step ONLY for the raster (independent from receivers).',
    'Paso temporal SOLO para el raster (independiente de los receptores).\n1 min  = máxima precisión, MUY lento\n5 min  = recomendado (precisión >98%, 5x más rápido)\n10 min = muy rápido, precisión ~95%\n20 min = ultrarrápido para previsualizar': 'Time step ONLY for the raster (independent from receivers).\n1 min  = maximum accuracy, VERY slow\n5 min  = recommended (accuracy >98%, 5x faster)\n10 min = very fast, accuracy ~95%\n20 min = ultra-fast for previewing',
    'Paso temporal [min]:': 'Time step [min]:',
    'Paso temporal:': 'Time step:',
    'Pendiente de expansión del cono de estela top-hat. Subir k acelera la recuperación; bajarlo alarga la estela.': 'Expansion slope of the top-hat wake cone. Increasing k speeds up recovery; decreasing it lengthens the wake.',
    'Pendiente del ancho σ(x) de la estela gaussiana. Default PyWake ≈ 0.0324555.': 'Slope of the Gaussian wake width σ(x). PyWake default ≈ 0.0324555.',
    'Perímetro WAsP/Surfer dibujado: dominio común de': 'WAsP/Surfer boundary drawn: common domain of',
    'Perímetro WAsP/Surfer dibujado: envolvente de': 'WAsP/Surfer boundary drawn: envelope of',
    'Perímetro WRG dibujado:': 'WRG boundary drawn:',
    'Perímetro del recurso': 'Resource boundary',
    'Perímetro del recurso eliminado del mapa.': 'Resource boundary removed from the map.',
    'Perímetro del recurso no disponible en esta instalación.': 'Resource boundary not available in this installation.',
    'Plantilla de turbina': 'Turbine template',
    'Plantilla rápida:': 'Quick template:',
    'Plantillas de screening: rellenan D, hub, potencia nominal y una curva genérica editable. No sustituyen la curva oficial del fabricante.': 'Screening templates: they fill in D, hub height, rated power and a generic editable curve. They do not replace the official manufacturer curve.',
    'Por aerogenerador': 'By turbine',
    'Por modelo': 'By model',
    'Por modelo de aerogenerador': 'By turbine model',
    'Por qué no se exponen las constantes internas del modelo': 'Why the model internal constants are not exposed',
    'Por receptor': 'By Receptor',
    'Por sector': 'By sector',
    'Potencia (kW):': 'Power (kW):',
    'Potencia en kW, misma longitud que ws': 'Power in kW, same length as ws',
    'Potencia inst. [MW]': 'Installed power [MW]',
    'Potencia instalada': 'Installed power',
    'Potencia instalada:': 'Installed power:',
    'Potencia sonora de la turbina': 'Turbine sound power',
    'PowerCtFunctionList no está disponible para combinar varios modelos.': 'PowerCtFunctionList is not available to combine several models.',
    'Preparación del cálculo': 'Calculation preparation',
    'Preparando datos para cálculo en segundo plano…': 'Preparing data for the background calculation…',
    'Preparando fuentes, receptores y MDT para el cálculo en segundo plano…': 'Preparing sources, receivers and DTM for the background calculation…',
    'Preparando fuentes, receptores y MDT…': 'Preparing sources, receivers and DTM…',
    'Presión': 'Pressure',
    'Presión atmosférica': 'Atmospheric pressure',
    'Presión atmosférica local en kPa. Valor de referencia habitual a nivel del mar: 101.325 kPa. Usa un dato medido o ajustado por altitud si está disponible.': 'Local atmospheric pressure in kPa. Usual reference value at sea level: 101.325 kPa. Use a measured or altitude-adjusted value if available.',
    'Presión atmosférica:': 'Atmospheric pressure:',
    'Primer coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.': 'First TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.',
    'Primer coeficiente que multiplica la TI en la formulación TurboGaussian.': 'First coefficient multiplying TI in the TurboGaussian formulation.',
    'Primero lanza un cálculo AEP. Después podrás guardarlo como escenario A o B.': 'Run an AEP calculation first. Then you can save it as scenario A or B.',
    'Primero pulsa «Crear turbina» en la pestaña correspondiente.': 'First press “Create turbine” in the corresponding tab.',
    'Producción específica': 'Specific yield',
    'Promedio sobre el rotor (rotor-average)': 'Rotor-disk averaging (rotor-average)',
    'Propagación por bandas de octava según metodología ISO-aligned': 'Octave-band propagation using an ISO-aligned methodology',
    'PropagateDownwind no aplica bloqueo; el cálculo lo desactivará automáticamente.': 'PropagateDownwind does not apply blockage; the calculation will disable it automatically.',
    'Proporciona una capa de puntos válida.': 'Provide a valid point layer.',
    'Proyecto sin nombre': 'Unnamed project',
    'Proyecto:': 'Project:',
    'Pulsa el logo para volver al inicio desde los módulos preparados.': 'Click the logo to return home from the prepared modules.',
    'Punto y coma': 'Semicolon',
    'Puntos calculados:': 'Calculated points:',
    'Puntos válidos:': 'Valid points:',
    'PyWake WindTurbines no está disponible en este entorno.': 'PyWake WindTurbines is not available in this environment.',
    'PyWake permite varias formas de combinar déficits de velocidad cuando una turbina recibe varias estelas.': 'PyWake allows several ways of combining wind-speed deficits when a turbine receives several wakes.',
    'Python zoneinfo no está disponible en este entorno: {}': 'Python zoneinfo is not available in this environment: {}',
    'Página de GitHub': 'GitHub page',
    'Pérd. bloqueo [MWh]': 'Blockage loss [MWh]',
    'Pérd. estela [%]': 'Wake loss [%]',
    'Pérd. estela [MWh]': 'Wake loss [MWh]',
    'Pérd. estelas [%]': 'Wake losses [%]',
    'Pérd. estelas [MWh]': 'Wake losses [MWh]',
    'Pérd. total': 'Total loss',
    'Pérdida': 'Loss',
    'Pérdidas bloqueo': 'Blockage losses',
    'Pérdidas por bloqueo:': 'Blockage losses:',
    'Pérdidas por estela': 'Wake losses',
    'Pérdidas por estela por sector': 'Wake losses by sector',
    'Pérdidas por estelas:': 'Wake losses:',
    'Pérdidas totales': 'Total losses',
    'Pérdidas totales:': 'Total losses:',
    'Pérdidas turbulencia': 'Turbulence losses',
    'Pérdidas:': 'Losses:',
    'Quitar capa seleccionada': 'Remove selected layer',
    'Qué conviene según el parque': 'What is advisable depending on the wind farm',
    'Qué elegir en el selector de modelo de turbulencia': 'What to choose in the turbulence-model selector',
    'Qué es el bloqueo y cuándo importa': 'What blockage is and when it matters',
    'Qué significa cada parámetro físico del modelo seleccionado': 'What each physical parameter of the selected model means',
    'Qué significa la turbulencia en este plugin': 'What turbulence means in this plugin',
    'RECEPTOR CRÍTICO': 'CRITICAL RECEIVER',
    'RESUMEN DEL CÁLCULO DE SHADOW FLICKER': 'SHADOW FLICKER CALCULATION SUMMARY',
    'RESUMEN EJECUTIVO': 'EXECUTIVE SUMMARY',
    'Radio máximo': 'Maximum radius',
    'Radio máximo [m]:': 'Maximum radius [m]:',
    'Raster DEM/MDT (opcional):': 'DEM raster (optional):',
    'Raster TI no válido:': 'Invalid TI raster:',
    'Raster completado': 'Raster completed',
    'Raster de modelo digital de elevaciones (MDT) opcional.': 'Optional digital elevation model (MDT) raster.',
    'Raster de modelo digital de elevaciones (MDT) opcional.\nSi se proporciona, la elevación del terreno bajo cada turbina y receptor se\nmuestrea del raster. Así elev_diff = (buje + terreno_turb) -\n(observador + terreno_recep), usando corrección de elevación según terreno.\nSi se deja vacío, se asume terreno plano (z=0).': 'Optional digital elevation model (MDT) raster.\nIf provided, the ground elevation under each turbine and receiver is\nsampled from the raster. This makes elev_diff = (hub + ground_turb) -\n(observer + ground_recv), using terrain-aware elevation correction.\nIf left empty, flat terrain (z=0) is assumed.',
    'Raster de ruido completado.': 'Noise raster completed.',
    'Raster de ruido finalizado con aviso.': 'Noise raster finished with a warning.',
    'Raster de ruido generado y añadido al proyecto.': 'Noise raster generated and added to the project.',
    'Raster de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.': 'Noise raster launched in the background. You can see it in the QGIS task manager.',
    'Raster en progreso': 'Raster in progress',
    'Raster filtrado generado': 'Filtered raster generated',
    'Raster guardado correctamente': 'Raster saved successfully',
    'Raster(s) TI no encontrado(s):': 'TI raster(s) not found:',
    'Raster(s) TI no seleccionado(s) → fallback automático a': 'TI raster(s) not selected → automatic fallback to',
    'Raster(s) TI no seleccionado(s) → fallback automático a 10% si usas WRG.': 'No TI raster(s) selected → automatic fallback to 10% if you use WRG.',
    'Raster(s) de turbulencia TI (opcional):': 'TI turbulence raster(s) (optional):',
    'Rathmann — modelo de compatibilidad para configuraciones antiguas. Sin parámetros de usuario en esta versión experimental.': 'Rathmann — compatibility model for older settings. No user parameters in this experimental release.',
    'Receptor': 'Receiver',
    'Receptor crítico': 'Critical receiver',
    'Receptor crítico (mayor nivel sonoro)': 'Critical Receiver (Highest Sound Level)',
    'Receptores': 'Receivers',
    'Receptores Cubiertos': 'Covered receivers',
    'Receptores Evaluados': 'Evaluated receptors',
    'Recomendado por defecto': 'Recommended by default',
    'Recurso eólico': 'Wind resource',
    'Recurso eólico (WAsP grids / WRG)': 'Wind resource (WAsP grids / WRG)',
    'Recurso:': 'Resource:',
    'Refresca el estado de capas/modelos detectados en el proyecto.': 'Refresh the detected layers/models state in the project.',
    'Regenerando raster filtrado': 'Regenerating filtered raster',
    'Regenerar TIF filtrado': 'Regenerate filtered TIF',
    'Regla práctica': 'Rule of thumb',
    'Resolución [m]:': 'Resolution [m]:',
    'Resolución del grid [m] (menor = más detalle, más lento):': 'Grid resolution [m] (lower = more detail, slower):',
    'Resolución del mapa [m]:': 'Map resolution [m]:',
    'Resolución temporal del cálculo': 'Calculation time resolution',
    'Resolución temporal del cálculo\n5 min = equilibrio óptimo velocidad/precisión\n1 min = máxima precisión (muy lento)': 'Calculation time resolution\n5 min = optimal speed/accuracy balance\n1 min = maximum accuracy (very slow)',
    'Resultado acústico:': 'Acoustic result:',
    'Resultados': 'Results',
    'Resultados AEP': 'AEP results',
    'Resultados AEP – Cálculo AEP (PyWake)': 'AEP Results – AEP calculation (PyWake)',
    'Resultados por receptor calculados.': 'Per-receiver results calculated.',
    'Resumen': 'Summary',
    'Resumen AEP del parque': 'Wind farm AEP summary',
    'Resumen del análisis': 'Analysis Summary',
    'Resumen del cálculo de shadow flicker': 'Shadow Flicker Calculation Summary',
    'Resumen del proyecto': 'Project summary',
    'Resumen delta': 'Delta summary',
    'Resumen global': 'Global summary',
    'Revisa el CSV del Modelo': 'Check the CSV for Model',
    'Revisión recomendada': 'Recommended review',
    'Rosa de pérdidas por sector.': 'Sector loss rose.',
    'Rotor Diameter: diámetro de rotor [m]': 'Rotor Diameter: rotor diameter [m]',
    'Rotor-average significa promediar el viento en varios puntos del rotor en vez de mirar un único punto central. Más puntos = más realismo y más coste.': 'Rotor averaging means averaging wind speed at several rotor points instead of looking at one central point. More points = more realism and more computation.',
    'RotorCenter es rápido; CGIRotorAvg(7) es el equilibrio recomendado; CGIRotorAvg(9/21) aumenta precisión y coste computacional.': 'RotorCenter is fast; CGIRotorAvg(7) is the recommended balanced option; CGIRotorAvg(9/21) increases accuracy and computation time.',
    'RotorCenter: usa un único punto en el centro del rotor. Es rápido para pruebas y mapas, pero no representa la variación de velocidad dentro del disco.': 'RotorCenter: uses one single point at the rotor centre. It is fast for tests and maps, but it does not represent wind-speed variation inside the disk.',
    'Ruido': 'Noise',
    'Ruido · Resumen del cálculo': 'Noise · Calculation summary',
    'Rápido LwA global': 'Fast global LwA',
    'Rápido · RotorCenter': 'Fast · RotorCenter',
    'STF2005 — Frandsen original. Coeficientes empíricos fijos. Útil para comparar con configuraciones antiguas o más conservadoras.': 'STF2005 — original Frandsen model. Fixed empirical coefficients. Useful for comparison with older or more conservative settings.',
    'STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Coeficientes empíricos fijos del paper. <b>Recomendado por defecto</b>.': 'STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Fixed empirical coefficients from the paper. <b>Recommended default</b>.',
    'Salir del modo interactivo (ESC también funciona).': 'Exit interactive mode (ESC also works).',
    'Screening masivo': 'large-scale screening',
    "Se generará UNA carpeta con GRD mosaico (Sector/Altura/Variable).\nSelecciona 2 o más carpetas de tiles exportadas desde WAsP.\n• 'Sector All': se tomarán TODAS las variables presentes (mosaico por altura).\n• Además, se crea subcarpeta 'pywake_compat' estricta (sin necesidad de parches).\n• Salida en Surfer ASCII (DSAA) con formato WAsP.": "ONE folder with a GRD mosaic (Sector/Height/Variable) will be generated.\nSelect 2 or more tile folders exported from WAsP.\n• 'Sector All': ALL present variables will be taken (mosaic by height).\n• In addition, a strict 'pywake_compat' subfolder is created (no patches needed).\n• Output in Surfer ASCII (DSAA) with WAsP format.",
    'Se han seleccionado varios raster(s) TI, pero no he podido identificar al menos dos alturas distintas en sus nombres. El plugin no podrá reconstruir una TI vertical fiable; revisa nombres como TImean.90.asc o TI_120m.tif.': 'Several TI raster(s) were selected, but at least two different heights could not be identified in their names. The plugin will not be able to reconstruct a reliable vertical TI; check names such as TImean.90.asc or TI_120m.tif.',
    'Se muestran magnitudes brutas de atenuación': 'Raw attenuation magnitudes are shown',
    'Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación).': 'Raw attenuation magnitudes are shown (not the algebraic sign inside the equation).',
    'Se pasa al Site de PyWake como fracción, por ejemplo 10% = 0.10.': 'It is passed to the PyWake Site as a fraction, for example 10% = 0.10.',
    'Sector más energético': 'Most energetic sector',
    'Segundo coeficiente TI/CT en la formulación TurboGaussian.': 'Second TI/CT coefficient in the TurboGaussian formulation.',
    'Segundo coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.': 'Second TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.',
    'Selecciona CSV con columnas X,Y para este modelo': 'Select a CSV with X,Y columns for this model',
    'Selecciona CSV con coordenadas X,Y del modelo': 'Select a CSV with the model X,Y coordinates',
    'Selecciona TXT/CSV…': 'Select TXT/CSV…',
    'Selecciona al menos una capa para calcular.': 'Select at least one layer to calculate.',
    'Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos.': 'Select one or more .wrg files or .zip files containing .wrg files.',
    'Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos. Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.': 'Select one or more .wrg files or .zip files containing .wrg files. If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.',
    'Selecciona carpeta con grids WAsP': 'Select a folder with WAsP grids',
    'Selecciona carpeta de salida': 'Select output folder',
    'Selecciona cómo se combinan las estelas de varias turbinas antes de calcular la velocidad efectiva.': 'Select how wakes from several turbines are combined before calculating the effective wind speed.',
    'Selecciona el módulo de trabajo. Los tres módulos están operativos: Energía (AEP y wakes), Ruido, y Sombras (shadow flicker).': 'Select the work module. The three modules are available: Energy (AEP and wakes), Noise, and Shadow Flicker.',
    'Selecciona la capa de coordenadas de turbinas del módulo de energía': 'Select the turbine-coordinate layer from the Energy module',
    'Selecciona la capa de puntos con las ubicaciones de receptores': 'Select the point layer with receiver locations',
    'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.\nPuedes marcar varias capas para simular un parque con varios modelos de turbina.': 'Select the turbine layers you want to include in this calculation.\nYou can check several layers to simulate a wind farm with several turbine models.',
    'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.  Puedes marcar varias capas para simular un parque con varios modelos de turbina a la vez.': 'Select the turbine layers you want to include in this calculation.  You can check several layers to simulate a wind farm with several turbine models at once.',
    'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.\nMarca varias capas si el parque combina distintos modelos de turbina.': 'Select the turbine layers you want to include in the wake maps.\nCheck several layers if the wind farm combines different turbine models.',
    'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.  Marca varias capas si el parque combina distintos modelos de turbina.': 'Select the turbine layers you want to include in the wake maps.  Check several layers if the wind farm combines different turbine models.',
    'Selecciona manualmente una zona horaria IANA.': 'Select an IANA time zone manually.',
    'Selecciona qué campo de límite se aplica a cada receptor. No cambia la física del ruido, solo el criterio de evaluación.': 'Select which limit field applies to each receiver. It does not change the noise physics, only the evaluation criterion.',
    'Selecciona un archivo TXT/CSV válido.': 'Select a valid TXT/CSV file.',
    'Selecciona una capa de receptores.': 'Select a receiver layer.',
    'Selecciona una capa de turbinas.': 'Select a turbine layer.',
    'Selecciona una capa válida de receptores.': 'Select a valid receiver layer.',
    'Selecciona una carpeta WAsP válida o un WRG (.wrg/.zip).': 'Select a valid WAsP folder or a WRG (.wrg/.zip).',
    'Selecciona una carpeta WAsP válida o un WRG/ZIP antes de graficar.': 'Select a valid WAsP folder or WRG/ZIP before plotting.',
    'Selecciona una carpeta WAsP válida o un WRG/ZIP.': 'Select a valid WAsP folder or a WRG/ZIP file.',
    'Selecciona una carpeta WAsP/Surfer o un WRG/ZIP para dibujar el perímetro.': 'Select a WAsP/Surfer folder or a WRG/ZIP to draw the boundary.',
    'Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake.': 'Select a folder with WAsP/Surfer grids compatible with PyWake.',
    'Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake. Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.': 'Select a folder with WAsP/Surfer grids compatible with PyWake. If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.',
    'Selecciona una o varias capas fuente detectadas desde Energía. Si marcas varias, el cálculo de ruido usará todas ellas a la vez.': 'Select one or more source layers detected from Energy. If you select several, the noise calculation will use all of them at once.',
    'Selecciona uno o varios raster(s) .asc/.tif de TI ambiente (p.ej. de Vortex). Si no hay ninguno, se usa TI=10% como fallback.': 'Select one or more ambient-TI .asc/.tif raster(s) (e.g. from Vortex). If none is provided, TI=10% is used as fallback.',
    'Selección del usuario': 'User selection',
    'SelfSimilarityDeficit — formulación self-similarity clásica. No requiere parámetros de usuario en esta versión experimental.': 'SelfSimilarityDeficit — classic self-similarity formulation. It does not require user parameters in this experimental release.',
    'SelfSimilarityDeficit2020 — opción principal para bloqueo. No requiere parámetros de usuario en esta versión experimental.': 'SelfSimilarityDeficit2020 — main blockage option. It does not require user parameters in this experimental release.',
    'Septiembre': 'September',
    'Shadow Flicker - Resumen del cálculo': 'Shadow Flicker - Calculation Summary',
    'Si aportas `ct`, su longitud debe coincidir con `ws`.': 'If you provide `ct`, its length must match `ws`.',
    'Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.': 'If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.',
    'Si aportas varios raster(s) TI': 'If you provide several TI raster(s)',
    'Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.': 'If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.',
    'Si no es un dato medido del emplazamiento, revisa si debería estar cerca de 101.325 kPa o ajustada por altitud.': 'If it is not a measured site value, check whether it should be close to 101.325 kPa or adjusted for altitude.',
    'Si no seleccionas raster TI': 'If you do not select a TI raster',
    'Si quieres incluir bloqueo, cambia el motor a': 'To include blockage, change the engine to',
    'Si solo aportas un raster TI': 'If you only provide one TI raster',
    'Si tu wake model es TI-driven': 'If your wake model is TI-driven',
    'Si una turbina recibe varias estelas, ¿cómo se suman sus efectos?': 'If a turbine receives several wakes, how should their effects be combined?',
    'Simulación principal con PyWake…': 'Main simulation with PyWake…',
    'Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas fuente/receptor.': 'No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.',
    'Sin bloqueo:': 'No blockage:',
    'Sin capas de coordenadas generadas todavía': 'No coordinate layers generated yet',
    'Sin capas editables': 'No editable layers',
    'Sin configuración registrada': 'No configuration recorded',
    'Sin coordenadas': 'No coordinates',
    'Sin fuentes válidas en la capa': 'No valid sources in the layer',
    'Sin layout detectado en el grupo de coordenadas por modelo': 'No layout detected in the per-model coordinate group',
    'Sin parámetros tunables habituales.': 'No usual tunable parameters.',
    'Sin pasos temporales válidos': 'No valid timesteps',
    'Sin plantilla': 'No template',
    'Sin promedio de rotor: PyWake usa el viento en el centro del rotor. Es lo más simple y rápido, pero pierde detalle si una estela afecta solo a una parte del disco.': 'No rotor averaging: PyWake uses the wind speed at the rotor centre. This is the simplest and fastest option, but it loses detail if a wake affects only part of the rotor disk.',
    'Sin receptores': 'No receivers',
    'Sin recurso seleccionado todavía': 'No resource selected yet',
    'Sin rotor-average: PyWake evaluará la velocidad efectiva en el centro del rotor.': 'No rotor-average: PyWake will evaluate the effective wind speed at the rotor centre.',
    'Sin ruta de curva acústica': 'No acoustic-curve path',
    'Sin turbina': 'No turbine',
    'Sin turbinas': 'No turbines',
    'Sin turbulencia añadida:': 'No added turbulence:',
    'Solo': 'Only',
    'Solo a las': 'Only a las',
    'Sombras y\nparpadeo': 'Shadow\nflicker',
    'SquaredSum usa raíz de suma de cuadrados; es una opción habitual para arrays densos y evita sumar déficits de forma tan agresiva como LinearSum.': 'SquaredSum uses the square root of the sum of squares; it is a common option for dense arrays and avoids adding deficits as aggressively as LinearSum.',
    'SquaredSum: combina varias estelas de forma cuadrática. Suele dar un comportamiento más equilibrado cuando varias estelas se solapan.': 'SquaredSum: combines several wakes quadratically. It usually gives a more balanced behaviour when several wakes overlap.',
    'Suele ser la opción más completa': 'It is usually the most complete option',
    'Suele tener sentido en': 'It usually makes sense in',
    'Superposición': 'Superposition',
    'Sustitución del modelo de bloqueo: la combinación seleccionada con': 'Blockage-model substitution: the selected combination with',
    'Sí': 'Yes',
    'TI WRG:': 'WRG TI:',
    'TI ambiente': 'ambient TI',
    'TI ambiente constante usada cuando no hay raster TI disponible.': 'Constant ambient TI used when no TI raster is available.',
    'TI ambiente manual si no hay raster:': 'Ambient TI fallback:',
    'TI raster(s) (opcional):': 'TI raster(s) (optional):',
    'TI sí influya': 'TI really affects',
    'TIF filtrado guardado': 'Filtered TIF saved',
    'Tabla horaria: hora civil local con DST · Zona:': 'Hourly table: local civil time with DST · Zone:',
    'Tabla resumida para consultoría: resultados acústicos por receptor, cumplimiento, fuente dominante y atenuaciones principales. Los diagnósticos internos MDT por pares se conservan en memoria, pero no se muestran por defecto.': 'Summary table for consultancy: acoustic results per receiver, compliance, dominant source and main attenuations. The internal pairwise DTM diagnostics are kept in memory, but are not shown by default.',
    'Tabulación': 'Tab',
    'Tamaño de celda raster\n100 m = rápido, menos detalle\n50 m = medio\n25 m = lento, máximo detalle': 'Raster cell size\n100 m = fast, less detail\n50 m = medium\n25 m = slow, maximum detail',
    'Tamaño inicial de la estela en la base (σ₀/D). Default PyWake = 0.2.': 'Initial wake size at the base (σ₀/D). PyWake default = 0.2.',
    'Tamaño inicial de la estela. Default PyWake = 0.25.': 'Initial wake size. PyWake default = 0.25.',
    'Temperatura': 'Temperature',
    'Tiempo dedicado a validar casos reales y mejorar la estabilidad del plugin.': 'Dedicated time for real-case validation and plugin stability improvements.',
    'Tiempo transcurrido:': 'Elapsed time:',
    'Tipo': 'Type',
    'Todas las turbinas del CSV están fuera del área del grid WAsP. Revisa unidades (m vs km) y reproyección.': 'All turbines in the CSV are outside the WAsP grid area. Check units (m vs km) and reprojection.',
    'Todas las turbinas están fuera del dominio del site (': 'All turbines are outside the site domain (',
    'Todos': 'All',
    'Todos los archivos': 'All files',
    'Todos los receptores (suma)': 'All Receptors (sum)',
    'Top receptores': 'Top receivers',
    'Total sectores:': 'Sector totals:',
    'Trayectos con G distinto del global': 'Paths with G different from global',
    'Trayectos con G distinto del global:': 'Paths with G different from the global value:',
    'Turbina creada (manual). Pulsa Aceptar para usarla.': 'Turbine created (manual). Press OK to use it.',
    'Turbina creada desde TXT/CSV. Pulsa Aceptar para usarla.': 'Turbine created from TXT/CSV. Press OK to use it.',
    'Turbinas': 'Turbines',
    'Turbinas contribuyentes dentro del radio': 'Contributing turbines within radius',
    'TurboGaussian: gaussiano sensible a TI efectiva. Tiene sentido cuando la turbulencia ambiente y añadida deben influir en el AEP.': 'TurboGaussian: Gaussian model sensitive to effective TI. It makes sense when ambient and added turbulence should influence AEP.',
    'TurboNOJ: versión ligera tipo NOJ, pero con más sensibilidad a turbulencia y recuperación de estela.': 'TurboNOJ: light NOJ-like version, but with more sensitivity to turbulence and wake recovery.',
    'Turbulencia WRG': 'WRG turbulence',
    'Turbulencia opcional para WRG': 'Optional turbulence for WRG',
    'Término': 'Term',
    'Términos activos': 'Active terms',
    'Un solo raster TI': 'Single TI raster',
    'Una capa o varias capas': 'One layer or several layers',
    'Una vez generado el raster, puedes regenerar TIF filtrados por mes y/o hora\nsin recalcular toda la geometría anual.': 'Once the raster has been generated, you can regenerate filtered TIFs by month and/or hour\nwithout recalculating the full annual geometry.',
    'Unión Recurso': 'Merge resource',
    'Usa varios núcleos para acelerar el cálculo\nExperimental: puede causar errores en algunos sistemas\nRecomendado: desactivado (más estable)': 'Use multiple cores to speed up the calculation\nExperimental - may cause errors on some systems\nRecommended: disabled (more stable)',
    'Usar': 'Use',
    'Usar layout activo del proyecto': 'Use active project layout',
    'Usar varias capas de receptores por categoría': 'Use several receiver layers by category',
    'Uso del suelo': 'Land use',
    'Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo.': 'You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group.',
    'Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.': 'You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group. If you enable curve mode, the plugin will evaluate the emission at a specific wind speed or for the worst case.',
    'Validación AEP': 'AEP validation',
    'Validación avanzada no disponible; se usa flujo de compatibilidad:': 'Advanced validation not available; the compatibility workflow is used:',
    'Valor': 'Value',
    'Valor PyWake por defecto': 'Default PyWake',
    'Valor [dB]': 'Value [dB]',
    'Valor de turbulencia ambiente que se usa si no cargas un raster TI. Ejemplo: 10 % significa TI = 0.10 dentro del Site de PyWake.': 'Ambient turbulence value used if no TI raster is loaded. Example: 10% means TI = 0.10 inside the PyWake Site.',
    'Valor inválido en banda': 'Invalid value in band',
    'Valores detallados por receptor.': 'Detailed values for each receptor.',
    'Variantes: solo estela / estela+TI / estela+bloqueo…': 'Variantes: wake-only / wake+TI / wake+bloqueo…',
    'Varios raster(s) TI a distintas alturas': 'Several TI raster(s) at different heights',
    'Velantis Wind · Cálculo de ruido': 'Velantis Wind · Noise calculation',
    'Velantis Wind · Hub principal': 'Velantis Wind · Main hub',
    'Velantis Wind · Informe técnico AEP': 'Velantis Wind · AEP technical report',
    'Velantis Wind · preparando ruido': 'Velantis Wind · preparing noise',
    'VelantisWind es un plugin open source para QGIS orientado a la evaluación preliminar de parques eólicos. Si la herramienta te ahorra tiempo o aporta valor a tu flujo de trabajo, puedes apoyar su mantenimiento o solicitar soporte profesional.': 'VelantisWind is an open-source QGIS plugin for early-stage wind farm assessment. If the tool saves you time or adds value to your workflow, you can support its maintenance or request professional support.',
    'Velocidad': 'Speed',
    'Velocidad viento (m/s):': 'Wind speed (m/s):',
    'Ver curva': 'View curve',
    'Ver la curva de potencia (y Ct si está disponible) de la turbina definida.\nÚtil para detectar errores de carga (kW vs W, columnas invertidas, etc.).': 'View the power curve (and Ct if available) of the defined turbine.\nUseful to detect loading errors (kW vs W, swapped columns, etc.).',
    'Very accurate: CGIRotorAvg(21) puede ser bastante más caro; úsalo para validaciones finales.': 'Very accurate: CGIRotorAvg(21) can be much more expensive; use it for final validation.',
    'Volcando resultados a las capas QGIS…': 'Writing results to the QGIS layers…',
    'Volver al diálogo': 'Back to dialog',
    'Volver al hub': 'Back to hub',
    'VortexCylinder — modelo de inducción/bloqueo basado en cilindro de vorticidad. Sin parámetros de usuario en esta versión experimental.': 'VortexCylinder — induction/blockage model based on a vortex cylinder. No user parameters in this experimental release.',
    'VortexDipole — alternativa de inducción. Sin parámetros de usuario en esta versión experimental.': 'VortexDipole — induction alternative. No user parameters in this experimental release.',
    'Vuelve a dibujar el perímetro del recurso eólico seleccionado.': 'Redraws the boundary of the selected wind resource.',
    'Vuelve al diálogo principal de Velantis Wind sin perder la edición.': 'Return to the main Velantis Wind dialog without losing your edits.',
    'Vuelve al diálogo principal sin salir del modo interactivo.': 'Return to the main dialog without leaving interactive mode.',
    'WRG con distinta resolución/extent: no se pueden combinar por altura': 'WRG files with different resolution/extent: they cannot be combined by height',
    'WRG con distinto dx: no se pueden combinar por altura': 'WRG files with different dx: they cannot be combined by height',
    'WRG con distinto origen x0/y0: no se pueden combinar por altura': 'WRG files with different origin x0/y0: they cannot be combined by height',
    'WRG inválido o no legible:': 'Invalid or unreadable WRG:',
    'WRG sin puntos (sólo cabecera)': 'WRG with no points (header only)',
    'WRG vacío': 'Empty WRG',
    'WRG: no se ha proporcionado raster TI, así que la TI ambiente se ha fijado al valor fallback definido por el usuario como campo uniforme.': 'WRG: no TI raster was provided, so the ambient TI was set to the user-defined fallback value as a uniform field.',
    'WRG: se ha usado un único raster de TI ambiente; si mezclas varios hub heights, esa TI se replica/interpela como aproximación vertical.': 'WRG: a single ambient TI raster was used; if you mix several hub heights, that TI is replicated/interpolated as a vertical approximation.',
    'Wake deficit principal. No todos los modelos usan la turbulencia del mismo modo.': 'Main wake deficit model. Not all models use turbulence in the same way.',
    'WeightedSum (avanzado · Gaussian)': 'WeightedSum (advanced · Gaussian)',
    'WeightedSum (solo gaussianos)': 'WeightedSum (Gaussian only)',
    'WeightedSum es avanzado y se reserva para déficits gaussianos compatibles; si PyWake no acepta la combinación, el solver degradará automáticamente.': 'WeightedSum is advanced and reserved for compatible Gaussian deficits; if PyWake rejects the combination, the solver will automatically degrade it.',
    'WeightedSum: opción avanzada para gaussianos compatibles. Si PyWake rechaza la combinación, el plugin volverá a una opción segura.': 'WeightedSum: advanced option for compatible Gaussian models. If PyWake rejects the combination, the plugin will fall back to a safe option.',
    'Workers (núcleos):': 'Workers (cores):',
    'Ya existe una turbina en ese punto.': 'There is already a turbine at that point.',
    'Ya has alcanzado el máximo de': 'You have already reached the maximum of',
    'Ya hay un cálculo de shadow flicker en ejecución.': 'A shadow-flicker calculation is already running.',
    'Zona horaria IANA, por ejemplo Europe/Madrid, America/Santiago o Asia/Tokyo.\nEl plugin incluye un catálogo IANA amplio y base TZif local para aplicar DST sin timezonefinder.': 'IANA time zone, for example Europe/Madrid, America/Santiago or Asia/Tokyo.\nThe plugin includes a broad IANA catalogue and local TZif database to apply DST without timezonefinder.',
    'Zona horaria IANA:': 'IANA time zone:',
    'Zona horaria detectada:': 'Detected time zone:',
    'Zona horaria: no detectada automáticamente.': 'Time zone: not automatically detected.',
    'Zong: avanzado y sensible a TI. Útil para estudiar near wake y recuperación por turbulencia con más detalle.': 'Zong: advanced and TI-sensitive. Useful for studying near wake and turbulence-driven recovery in more detail.',
    'ZongGaussianDeficit requiere un modelo de turbulencia añadida; se ha forzado STF2017.': 'ZongGaussianDeficit requires an added-turbulence model; STF2017 was forced.',
    'a1 en k = a1·TI + a2': 'a1 in k = a1·TI + a2',
    'a2 en k = a1·TI + a2': 'a2 in k = a1·TI + a2',
    'absorción atmosférica': 'atmospheric absorption',
    'activo': 'active',
    'aguas arriba': 'upstream',
    'ajusta TI/turbulencia para mover el AEP.': 'adjust TI/turbulence to change AEP.',
    'ajusta la': 'adjust the',
    'ajuste analítico clásico': 'classic analytical fit',
    'ajuste empírico clásico. Constantes fijas del paper.': 'classic empirical fit. Fixed paper constants.',
    'alternativa clásica y sencilla': 'classic and simple alternative',
    'alternativa ligera': 'lightweight alternative',
    'altura(s) para': 'height(s) for',
    'alturas de buje distintas': 'different hub heights',
    'análisis de sensibilidad': 'sensitivity analysis',
    'apantallamiento': 'screening',
    'aplicado en todos los trayectos': 'applied to all paths',
    'aproximación vertical': 'vertical approximation',
    'autoajustada': 'auto-adjusted',
    'año': 'year',
    'banda dom. Hz': 'dominant band Hz',
    'buena opción gaussiana para onshore': 'good Gaussian option for onshore',
    'buena opción general': 'good general option',
    'buena opción general para': 'good general option for',
    'calcula la': 'calculates the',
    'calibraciones del paper de referencia': 'calibrations from the reference paper',
    'capa fuente dom.': 'dominant source layer',
    'capa(s) con': 'layer(s) with',
    'capa(s) de modelo': 'model layer(s)',
    'capa(s) de modelo ·': 'model layer(s) ·',
    'capa(s) seleccionada(s) desde el mapa interactivo.': 'layer(s) selected from the interactive map.',
    'causada por la propia turbina': 'caused by the turbine itself',
    'celdas | resolución pedida': 'cells | requested resolution',
    'celdas, resolución efectiva': 'cells, effective resolution',
    'celdas. Resolución efectiva': 'cells. Effective resolution',
    'como modelo alternativo de acoplamiento más débil con la TI efectiva. Esto preserva la partida de pérdidas por bloqueo manteniendo intactos wake, rotor-average y modelo de turbulencia.': 'as an alternative model with weaker coupling to the effective TI. This preserves the blockage-loss component while keeping wake, rotor-average and the turbulence model intact.',
    'comparación entre layouts y estudios rápidos': 'layout comparison and quick studies',
    'consulta la ayuda para elegir una combinación físicamente coherente.': 'check the help dialog to choose a physically consistent combination.',
    'conviene activar un modelo aquí.': 'it is advisable to enable a model here.',
    'coords_xy inválido para «': 'invalid coords_xy for «',
    'creada': 'created',
    'cribado rápido': 'quick screening',
    'cuando mezclas distintas alturas': 'when mixing different heights',
    'cubiertos': 'covered',
    'cumplimiento sobre receptores cubiertos': 'compliance over covered receivers',
    'curvas acústicas': 'acoustic curves',
    'curvas acústicas en peor caso': 'worst-case acoustic curves',
    'dato de entrada del recurso': 'resource input datum',
    'de cobertura': 'coverage',
    'de cumplimiento': 'compliance',
    'decide si esa TI ambiente/efectiva cambia realmente el déficit': 'decides whether ambient/effective TI actually changes the deficit',
    'default industrial post-2020': 'post-2020 industrial default',
    'deficit_cls es None (no se pudo importar un deficit model válido).': 'deficit_cls is None (a valid deficit model could not be imported).',
    'derivado Frandsen, formulación clásica. Constantes fijas.': 'Frandsen-derived, classic formulation. Fixed constants.',
    'describe la': 'describes the',
    'desde campos de receptor': 'from receiver fields',
    'desde capa': 'from layer',
    'detectarla en el nombre del archivo': 'detect it in the file name',
    'detrás de las turbinas': 'behind the turbines',
    'dirección del viento': 'wind direction',
    'dist. turbina cercana (m)': 'nearest turbine dist. (m)',
    'divergencia geométrica': 'geometrical divergence',
    'día': 'day',
    'efecto suelo': 'ground effect',
    'el cálculo no incluirá la reducción de velocidad upstream.': 'the calculation will not include upstream velocity reduction.',
    'el cálculo seguirá': 'the calculation will continue',
    'el cálculo solo usa la TI ambiente del recurso': 'the calculation only uses the resource ambient TI',
    'el plugin sigue siendo usable': 'the plugin remains usable',
    'entrega de consultoría seria': 'serious consulting deliverable',
    "error creando superposición '": "error creating superposition '",
    'escribir la altura manualmente': 'enter the height manually',
    'espectro': 'spectrum',
    'estado': 'status',
    'está en el botón de información': 'is in the information button',
    'está fuera del rango típico usado como referencia en muchos estudios preliminares': 'is outside the typical range used as a reference in many preliminary studies',
    'evaluaciones preliminares': 'preliminary assessments',
    'fallback uniforme': 'uniform fallback',
    'falló:': 'failed:',
    'filas. La exportación CSV contiene el detalle completo.</small></p>': 'rows. The CSV export contains the full detail.</small></p>',
    'frente a': 'versus',
    'genérica': 'generic',
    'grid(s) sin intersección común.': 'grid(s) with no common intersection.',
    'grupo fuente dom.': 'dominant source group',
    'grupo(s) con LwA válido.': 'group(s) with a valid LwA.',
    'grupo(s) con criterio': 'group(s) with criterion',
    'h/año': 'h/year',
    'horas/año': 'hours/year',
    'id receptor': 'receiver id',
    'impacto sobre el AEP suele ser limitado': 'impact on AEP is usually limited',
    'incluye bloqueo con propagación bidireccional': 'includes blockage with bidirectional propagation',
    'incluye bloqueo iterando todas las interacciones': 'includes blockage by iterating all interactions',
    'interpolar por altura': 'interpolate by height',
    'k (expansión de estela)': 'k (wake expansion)',
    'k se deriva de la TI local': 'k is derived from local TI',
    'k y σ se derivan de la TI': 'k and σ are derived from TI',
    'la TI no cambia el AEP de forma importante en la configuración estándar.': 'TI does not significantly change AEP in the standard configuration.',
    'la TI no cambia mucho el déficit base del wake': 'TI does not change the base wake deficit much',
    'la línea de visión entre la turbina dominante y este receptor queda despejada según el MDT': 'the line of sight between the dominant turbine and this receiver is clear according to the DTM',
    'la presión atmosférica introducida': 'the entered atmospheric pressure',
    'la tabla del apartado 2 muestra magnitudes de atenuación para trazabilidad. El nivel resultante no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.': 'the table in section 2 shows attenuation magnitudes for traceability. The resulting level should not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and contributing sources within the calculation radius.',
    'lista para editar en el mapa': 'ready to edit on the map',
    'límite dB(A)': 'limit dB(A)',
    'líneas esperadas; hay': 'expected lines; there are',
    'm\nPaso temporal:': 'm\nTime step:',
    'm\nPuedes seguir trabajando en QGIS.\n\nSe te avisará cuando termine.': 'm\nYou can continue working in QGIS.\n\nYou will be notified when it finishes.',
    'm; se aplicará a todas las alturas del WRG como aproximación.': 'm; it will be applied to all WRG heights as an approximation.',
    'margen límite dB': 'limit margin dB',
    'medio (~8x más lento que motor rápido)': 'medium (~8x slower than the fast engine)',
    'mezcla near-field (vortex) y far-field (self-similarity).': 'near-field (vortex) and far-field (self-similarity) blend.',
    'modelo dominante': 'dominant model',
    'modelo gaussiano con sensibilidad a la TI local/efectiva': 'Gaussian model with sensitivity to local/effective TI',
    'modelo no estándar; revisar disponibilidad en esta versión de PyWake.': 'non-standard model; check availability in this PyWake version.',
    'modelo(s) WT ·': 'WT model(s) ·',
    'modelo(s) de aerogenerador': 'wind turbine model(s)',
    'modelos tienen configuración válida.': 'models have valid configuration.',
    'modelos. Aumenta el límite en el código si necesitas más familias de aerogeneradores.': 'models. Increase the limit in the code if you need more turbine families.',
    'modo global': 'global mode',
    'mosaic: lista de entrada vacía': 'mosaic: empty input list',
    'muy ligera computacionalmente': 'computationally very light',
    'más conservador en near-field': 'more conservative in the near field',
    'más rápido que SS2020': 'faster than SS2020',
    'más sensibilidad a la TI': 'more sensitivity to TI',
    'más)</span>': 'more)</span>',
    'nivel total dB(A)': 'total level dB(A)',
    'no activo': 'inactive',
    'no convergió en PyWake (la iteración entre wake TI-driven y bloqueo SS2020 no siempre alcanza un punto fijo estable). Se ha mantenido el bloqueo usando': 'did not converge in PyWake (the iteration between a TI-driven wake and SS2020 blockage does not always reach a stable fixed point). Blockage was kept using',
    'no creada': 'not created',
    'no generado': 'not generated',
    'no había MDT disponible en este trayecto': 'no DTM was available on this path',
    'no perillas de sitio': 'not site-specific knobs',
    'no se detectó obstáculo topográfico relevante en el trayecto dominante': 'no relevant topographic obstacle was detected on the dominant path',
    'no se ha proporcionado un raster de TI.': 'no TI raster has been provided.',
    'no se pudo extraer un perfil MDT válido para el trayecto dominante': 'a valid DTM profile could not be extracted for the dominant path',
    'no tiene coordenadas. Añade turbinas en el mapa interactivo o selecciona un CSV X,Y válido.': 'has no coordinates. Add turbines in the interactive map or select a valid X,Y CSV.',
    'noche': 'night',
    'nombre(s) duplicados. Conviene renombrarlos para exportación y trazabilidad.': 'duplicated name(s). It is advisable to rename them for export and traceability.',
    'nombres que no casan con py_wake en:': 'names that do not match py_wake in:',
    'nº turbinas': 'no. turbines',
    'o cambia a un wake model TI-driven': 'or switch to a TI-driven wake model',
    'o selecciona un CSV X,Y válido.': 'or select a valid X,Y CSV.',
    'offshore limpio': 'clean offshore',
    'opcional': 'optional',
    'opción avanzada y sensible a TI': 'advanced TI-sensitive option',
    'opción de referencia': 'reference option',
    'opción gaussiana sensible a TI': 'TI-sensitive Gaussian option',
    'opción más avanzada dentro de la familia gaussiana': 'more advanced option within the Gaussian family',
    'openpyxl no está disponible en este entorno de QGIS. Usa las exportaciones CSV o instala openpyxl.': 'openpyxl is not available in this QGIS environment. Use CSV exports or install openpyxl.',
    'override usuario': 'user override',
    'parque': 'wind farm',
    'parque dom.': 'dominant wind farm',
    'parques con turbulencia ambiente relevante': 'wind farms with relevant ambient turbulence',
    'pero la TI añadida por estela no entrará explícitamente.': 'but wake-added TI will not explicitly enter the calculation.',
    'polígonos': 'polygons',
    'por defecto': 'default',
    'porque el motor es': 'because the engine is',
    'prima la velocidad y la robustez': 'speed and robustness are the priority',
    'puede ser sensible a la TI, pero se ha ejecutado sin modelo de turbulencia añadida; la sensibilidad queda limitada a la TI ambiente disponible.': 'can be sensitive to TI, but it ran without an added-turbulence model; the sensitivity is limited to the available ambient TI.',
    'puede usar la TI ambiente/efectiva para abrir la estela; el modelo de turbulencia activo (': 'can use ambient/effective TI to open the wake; the active turbulence model (',
    'pérdida': 'loss',
    'pérdida Aatm dB': 'Aatm loss dB',
    'pérdida Abar dB': 'Abar loss dB',
    'pérdida Adiv dB': 'Adiv loss dB',
    'pérdida Agr/Aground dB': 'Agr/Aground loss dB',
    'pérdidas': 'losses',
    'pérdidas por estela': 'wake losses',
    'rango': 'range',
    'rapidez': 'speed',
    'raster(s) de TI ambiente para reconstruir TI por altura cuando es posible.': 'ambient TI raster(s) to reconstruct TI by height when possible.',
    'receptor crítico': 'critical receiver',
    'receptor(es)': 'receiver(s)',
    'receptor(es) con G distinto del global y': 'receiver(s) with G different from the global value and',
    'receptores a:': 'receptors to:',
    'receptores cubiertos': 'covered receivers',
    'receptores dentro del radio': 'receivers within the radius',
    'receptores fuera de radio': 'receivers outside the radius',
    'receptores superan límites': 'receivers exceed limits',
    'recomendable cuando quieres un modelo gaussiano': 'recommended when you want a Gaussian model',
    'recomendado para benchmarking, estudios rápidos y muchos casos offshore limpios.': 'recommended for benchmarking, quick studies and many clean offshore cases.',
    'recomendado para screening rápido y parques grandes cuando prima la velocidad.': 'recommended for fast screening and large wind farms when speed is the priority.',
    'recuperación de la estela': 'wake recovery',
    'reducción de velocidad': 'velocity reduction',
    'rendimiento del parque': 'wind farm performance',
    'representación física del recurso mejora bastante': 'physical representation of the resource improves significantly',
    'resolución autoajustada': 'auto-adjusted resolution',
    'revisar': 'review',
    'rápido (<5 seg para 100 receptores)': 'fast (<5 s for 100 receivers)',
    'se detectó relieve, pero por debajo del umbral conservador de activación': 'relief was detected, but below the conservative activation threshold',
    'se ha usado un único raster de ambient TI': 'a single ambient TI raster was used',
    'se solicitó el raster pero no se pudo generar': 'the raster was requested but could not be generated',
    'segundos': 'seconds',
    'si el motor lo soporta': 'if the engine supports it',
    'si hace falta.': 'if needed.',
    'sin bloqueo + sin turbulencia': 'no blockage + no turbulence',
    'sin definir': 'not defined',
    'sin extensión': 'no extension',
    'sin fuentes dentro del radio': 'no sources within the radius',
    'sin nombre': 'unnamed',
    'sin pendiente k explícita.': 'no explicit k slope.',
    'sin puntos válidos': 'no valid points',
    'sin rotor-average + sin bloqueo': 'no rotor-average + no blockage',
    'sin rotor-average + sin bloqueo + sin turbulencia': 'no rotor-average + no blockage + no turbulence',
    'sin rotor-average + sin turbulencia': 'no rotor-average + no turbulence',
    'sin valor': 'no value',
    'solo propaga estelas hacia abajo, no soporta bloqueo': 'only propagates wakes downstream, it does not support blockage',
    'solución potencial tipo dipolo': 'dipole-type potential-flow solution',
    'su déficit base no cambia mucho con la TI': 'its base deficit does not change much with TI',
    'suele ser una elección robusta y estable': 'it is usually a robust and stable choice',
    'supera límite': 'exceeds limit',
    'superan el límite': 'exceed the limit',
    'sí': 'yes',
    'sí · resolución pedida ': 'yes · requested resolution ',
    'terreno rugoso': 'rough terrain',
    'tipo': 'type',
    'todas las capas del módulo de energía': 'all Energy-module layers',
    'todos los receptores han quedado fuera del radio máximo': 'all receivers are outside the maximum radius',
    'turbina(s)': 'turbine(s)',
    'turbina(s).': 'turbine(s).',
    'turbinas están fuera del dominio y se omitirán (ej.:': 'turbines are outside the domain and will be omitted (e.g.:',
    'turbinas vecinas': 'neighboring turbines',
    'turbulencia ambiente': 'ambient turbulence',
    'turbulencia añadida por estela': 'wake-added turbulence',
    'uniforme como fallback.': 'uniformly as fallback.',
    'uso del suelo / G efectivo por polígonos todavía usa intersecciones de capas QGIS': 'land use / effective G by polygons still uses QGIS layer intersections',
    'valor PyWake por defecto': 'PyWake default value',
    'valor único': 'single value',
    'varias capas TI': 'several TI layers',
    'xarray es necesario para usar WRG con PyWake': 'xarray is required to use WRG with PyWake',
    'xarray no disponible para construir TI por altura:': 'xarray not available to build TI by height:',
    'z acústica receptor m': 'receiver acoustic z m',
    'z acústica turb. dom. m': 'dominant turbine acoustic z m',
    'z acústica turb. max Abar m': 'max-Abar turbine acoustic z m',
    'z terreno receptor m': 'receiver terrain z m',
    'zona de near wake': 'near-wake region',
    '¡Cálculo completado correctamente!': 'Calculation completed successfully!',
    '· hora civil local con DST': '· local civil time with DST',
    '· inválida (': '· invalid (',
    '¿Continuar?': 'Continue?',
    '¿Cómo debe promediarse el viento dentro del disco del rotor?': 'How should wind speed be averaged across the rotor disk?',
    '¿Cómo debe propagar PyWake las estelas entre turbinas?': 'How should PyWake propagate wakes between turbines?',
    '¿Cómo quieres calcular la pérdida de viento detrás de cada turbina?': 'How do you want to calculate wind-speed loss behind each turbine?',
    '¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?': 'How should the wind-speed loss behind each turbine be calculated?',
    '¿Cómo se calculan las interacciones entre turbinas?': 'How should turbine interactions be calculated?',
    '¿Cómo se combinan las estelas de varias turbinas?': 'How are wakes from several turbines combined?',
    '¿Qué ayuda a financiar el apoyo?': 'What does the support help fund?',
    '¿Qué modelo de bloqueo quieres usar?': 'Which blockage model do you want to use?',
    '¿Qué modelo de rotor-average quieres usar?': 'Which rotor-average model do you want to use?',
    '¿Qué modelo de turbulencia quieres usar?': 'Which turbulence model do you want to use?',
    'Último cálculo': 'Last calculation',
    'Útil para comparar con configuraciones antiguas o más conservadoras.': 'Useful for comparison with older or more conservative configurations.',
    'útil si quieres mantener un modelo ligero tipo NOJ': 'useful if you want to keep a light NOJ-style model',
    'α se calcula automáticamente por banda en Motor ISO': 'α is calculated automatically per band in the ISO engine',
    'α usado:': 'α used:',
    'δw/D (longitud fuente near-wake)': 'δw/D (near-wake source length)',
    '— Selecciona capa de receptores —': '— Select receiver layer —',
    '— Selecciona capa de turbinas —': '— Select turbine layer —',
    '— Sin DEM/MDT (terreno plano) —': '— No DEM (flat terrain) —',
    '— Sin capa de uso del suelo —': '— No land-use layer —',
    '• AVISO: esta configuración usa ruta síncrona porque ': '• WARNING: this configuration uses the synchronous path because ',
    '• AVISO: se solicitó el raster pero no se pudo generar. Revisa la resolución, el CRS y el extent de las fuentes.': '• WARNING: the raster was requested but could not be generated. Check the resolution, the CRS and the source extent.',
    '• AVISO: todos los receptores han quedado fuera del radio máximo. Revisa radio, layout o ubicación de receptores.': '• WARNING: all receivers ended up outside the maximum radius. Check the radius, layout or receiver locations.',
    '• Aground representa la pérdida simplificada asociada al efecto suelo/terreno en el método acústico.': '• Aground represents the simplified loss associated with the ground/terrain effect in the acoustic method.',
    '• Altura de receptor: se toma de cada capa configurada en la tabla multi-capa.': '• Receiver height: taken from each layer configured in the multi-layer table.',
    '• Atenuación lineal α:': '• Linear attenuation α:',
    '• Auto recomendado: selección segura del plugin.': '• Recommended auto: safe plugin selection.',
    '• Capa GIS de receptores fuera de radio creada: Noise · Receivers fuera de radio (': '• GIS receivers-outside-radius layer created: Noise · Receivers fuera de radio (',
    '• Criterio de límite por capa:': '• Per-layer limit criterion:',
    '• Cálculo pesado lanzado como QgsTask. Puedes seguir usando QGIS mientras avanza.': '• Heavy calculation launched as a QgsTask. You can keep using QGIS while it runs.',
    '• Falta configurar al menos una capa de receptores en la tabla multi-capa.': '• At least one receiver layer must be configured in the multi-layer table.',
    '• Falta seleccionar la capa de receptores. Para este flujo se recomienda una capa de puntos o polígonos de viviendas/receptores.': '• The receiver layer must be selected. For this workflow, a point or polygon layer of dwellings/receivers is recommended.',
    '• Fuentes acústicas: se usará 1 capa de fuentes seleccionada manualmente en Entradas.': '• Acoustic sources: will use 1 source layer manually selected from Inputs.',
    '• Isófonas terminadas en segundo plano:': '• Isophones finished in the background:',
    '• Isófonas: se generarán al finalizar el raster en segundo plano.': '• Isophones: they will be generated once the background raster is finished.',
    '• La capa de receptores coincide con el layout de turbinas. Elige una capa distinta de viviendas/receptores.': '• The receiver layer matches the turbine layout. Choose a different dwellings/receivers layer.',
    '• LinearSum: suma lineal; conservador en parques densos.': '• LinearSum: linear sum; conservative in dense wind farms.',
    '• Límite de receptor: se toma de cada capa configurada en la tabla multi-capa.': '• Receiver limit: taken from each layer configured in the multi-layer table.',
    '• MaxSum: solo domina la estela más severa; útil para diagnóstico.': '• MaxSum: only the most severe wake dominates; useful for diagnostics.',
    '• Método actual: cálculo acústico fuente-receptor para consultoría eólica (Lp = LwA - Adiv - Aatm - Aground). Además puede crear capas GIS de fuentes, enlaces dominantes, receptores fuera de radio, mapa de ruido ráster e isófonas.': '• Current method: wind source-receiver acoustic calculation for consultancy (Lp = LwA - Adiv - Aatm - Aground). It can also create GIS layers of sources, dominant links, receivers outside the radius, a raster noise map and isophones.',
    '• No se han detectado capas de coordenadas por modelo. Genera primero el layout desde Energía.': '• No per-model coordinate layers were detected. Generate the layout from Energy first.',
    '• Parámetros de consultoría acústica: límite de receptor, isófonas y factor de suelo G para revisión rápida de cumplimiento e influencia del terreno.': '• Acoustic consultancy parameters: receiver limit, isophones and ground factor G for a quick review of compliance and terrain influence.',
    "• Raster de ruido: lanzado en segundo plano con QgsTask. Los resultados por receptor ya están disponibles; la capa 'Noise · Map' se añadirá al terminar.": "• Noise raster: launched in the background with QgsTask. Per-receiver results are already available; the 'Noise · Map' layer will be added when it finishes.",
    '• Receptores con al menos una turbina dentro del radio:': '• Receivers with at least one turbine inside the radius:',
    '• Se usó DEM/DSM para muestrear la elevación del terreno en fuentes y receptores.': '• DEM/DSM was used to sample terrain elevation at sources and receivers.',
    '• Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas de fuente/receptor.': '• No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.',
    '• Sin MDT/DSM seleccionado. El cálculo acústico arrancará en coordenadas planas con alturas relativas de fuente/receptor.': '• No DTM/DSM selected. The acoustic calculation will start in flat coordinates with relative source/receiver heights.',
    '• SquaredSum: raíz de suma de cuadrados; buena opción práctica para arrays densos.': '• SquaredSum: square root of the sum of squares; a good practical option for dense arrays.',
    "• Suelo/terreno: desde capa de uso del suelo '": "• Ground/terrain: from land-use layer '",
    '• Suelo/terreno: modo capa activado pero sin capa válida; will use G global =': '• Ground/terrain: layer mode enabled but no valid layer; the global G will be used =',
    '• WeightedSum: avanzado, pensado para modelos gaussianos compatibles y más sensible al coste/compatibilidad.': '• WeightedSum: advanced, intended for compatible Gaussian models and more sensitive to cost/compatibility.',
    '← Inicio': '← Home',
    '● editado en mapa, sin guardar': '● edited on map, unsaved',
    '♡ Apoyar VelantisWind': '♡ Support VelantisWind',
    '⚠️ La capa de turbinas está vacía.': '⚠️ The turbine layer is empty.',
    '⚠️ Ningún modelo tiene configuración válida (Hub Height y Rotor Diameter).': '⚠️ No model has valid configuration (Hub Height and Rotor Diameter).',
    '⚠️ No hay capa de receptores seleccionada.': '⚠️ No receiver layer selected.',
    '⚠️ No se detectaron modelos. Comprueba que existen capas de turbinas.': '⚠️ No models detected. Check that turbine layers exist.',
    '⚡ Rápido - LwA global simplificado': '⚡ Fast - simplified global LwA',
    '⛅ Condiciones Atmosféricas (Motor ISO)': '⛅ Atmospheric conditions (ISO engine)',
    '✅ Configuración válida. Puedes continuar con el cálculo.': '✅ Valid configuration. You can proceed with the calculation.',
    '✓ Capa de turbinas:': '✓ Turbine layer:',
    '✓ Coordenadas del emplazamiento: (': '✓ Site coordinates: (',
    '✓ Distancia máxima de sombra:': '✓ Max shadow distance:',
    '✓ Geometría de turbina que se usará en el cálculo de sombras:': '✓ Turbine geometry to be used in the shadow calculation:',
    '❌ Configuración incompleta. Revisa los elementos marcados.': '❌ Incomplete configuration. Check the flagged items.',
    '💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).': '💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).',
    '💡 Configura la altura de buje y el diámetro de rotor de cada modelo detectado. Estos parámetros son necesarios para el cálculo de shadow flicker.': '💡 Configure hub height and rotor diameter for each detected model. These parameters are required for the shadow flicker calculation.',
    '💡 Usa condiciones típicas del emplazamiento': '💡 Use typical site conditions',
    '💡 Usa condiciones típicas del emplazamiento. Si no tienes presión medida, 101.325 kPa es una referencia estándar a nivel del mar; en altitud suele ser menor.': '💡 Use typical site conditions. If you do not have measured pressure, 101.325 kPa is a standard sea-level reference; at altitude it is usually lower.',
    '📊 Exportar CSV 12×24': '📊 Export 12×24 CSV',
    '📊 ISO-aligned - ISO 9613-2 por bandas': '📊 ISO-aligned - ISO 9613-2 by bands',
}
_CANON_TO_ES = {
    '\n\n💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).': '\n\n💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).',
    "\n        \n        <div class='card'>\n            <h3>📊 Attenuation statistics (covered receivers)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Raw attenuation magnitudes are shown (not the algebraic sign inside the equation). For Abar, the maximum among the contributing turbines of each receiver is used, not only the dominant path.</i></p>\n            <table>\n                <tr>\n                    <th>Term</th>\n                    <th style='text-align: right;'>Mean [dB]</th>\n                    <th style='text-align: right;'>Maximum [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (geometrical divergence)</td>\n                    <td style='text-align: right;'>": "\n        \n        <div class='card'>\n            <h3>📊 Estadísticos de Atenuaciones (Receptores Cubiertos)</h3>\n        <p style='margin: 6px 0 10px 0; color:#495057;'><i>Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación). Para Abar se usa el máximo entre las turbinas contribuyentes de cada receptor, no solo la trayectoria dominante.</i></p>\n            <table>\n                <tr>\n                    <th>Término</th>\n                    <th style='text-align: right;'>Media [dB]</th>\n                    <th style='text-align: right;'>Máximo [dB]</th>\n                </tr>\n                <tr>\n                    <td><b>Adiv</b> (divergencia geométrica)</td>\n                    <td style='text-align: right;'>",
    "\n                </table>\n                <h4>5. Ground effect by regions</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>The ground term is split into <b>As</b> (source region), <b>Am</b> (middle region) and <b>Ar</b> (receiver region). This implementation does not use three independent ground parameters <code>Gs/Gm/Gr</code>, but a <b>single G per path</b>. Mathematically, the plugin applies:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_mid)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>where <b>h_s</b> is the characteristic source height, <b>h_r</b> the receiver height, <b>h_mid</b> the mean path height and <b>G_m≈0</b> in the current approximation for favourable propagation conditions. That single ground value can be:</p>\n                <ul>\n                    <li><b>Manual/global G</b>, if the user sets a single value.</li>\n                    <li><b>G_eff</b>, if a land-use layer exists and a length-weighted mean along the path is computed.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Physical meaning of G:</b> it represents the acoustic character of the terrain and controls how the ground influences propagation. <b>G≈0</b> indicates hard ground (urban, asphalt, rock), <b>G≈1</b> soft/porous ground (agricultural, grassland, forest) and intermediate values represent mixed terrain.</p>\n                <p><b>What “from layer” means:</b> the plugin clips the source–receiver path against the land-use layer, assigns a <b>G_i</b> value to each intercepted polygon and computes a single <b>G_eff</b> for that path. That is the value actually entering <b>Agr,b</b>; the <b>global G</b> shown in the report is kept only as a fallback.</p>\n                <p><b>Report convention:</b> <b>Agr,b</b> is shown here as a <b>positive attenuation magnitude</b>. In the main equation it is subtracted from the source level just like Adiv, Aatm and Abar.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>Base term A_ground(h)</th></tr>\n                    ": "\n                </table>\n                <h4>5. Efecto suelo por regiones</h4>\n                <div class='formula'>Agr,b = As + Am + Ar</div>\n                <p>El término de suelo se descompone en <b>As</b> (región de fuente), <b>Am</b> (región media) y <b>Ar</b> (región de receptor). En esta implementación no se usan tres parámetros de suelo independientes <code>Gs/Gm/Gr</code>, sino un <b>único G por trayecto</b>. Matemáticamente, el plugin aplica:</p>\n                <div class='formula'>As = G_eff·A_ground(h_s)</div>\n                <div class='formula'>Am = G_eff·(1 - G_m)·A_ground(h_medio)</div>\n                <div class='formula'>Ar = G_eff·A_ground(h_r)</div>\n                <p>donde <b>h_s</b> es la altura característica de la fuente, <b>h_r</b> la del receptor, <b>h_medio</b> la altura media del trayecto y <b>G_m≈0</b> en la aproximación actual para condiciones favorables de propagación. Ese valor único de suelo puede ser:</p>\n                <ul>\n                    <li><b>G manual/global</b>, si el usuario fija un único valor.</li>\n                    <li><b>G_eff</b>, si existe capa de uso del suelo y se calcula una media ponderada por longitud del trayecto.</li>\n                </ul>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <p><b>Significado físico de G:</b> representa el carácter acústico del terreno y controla cómo influye el suelo en la propagación. <b>G≈0</b> indica suelo duro (urbano, asfalto, roca), <b>G≈1</b> suelo blando/poroso (agrícola, pradera, forestal) y valores intermedios representan terreno mixto.</p>\n                <p><b>Qué significa “desde capa”:</b> el plugin corta el trayecto fuente–receptor contra la capa de uso del suelo, asigna un valor <b>G_i</b> a cada polígono interceptado y calcula un único <b>G_eff</b> para ese trayecto. Ese es el valor que entra realmente en <b>Agr,b</b>; el <b>G global</b> mostrado en el informe queda solo como respaldo.</p>\n                <p><b>Convención del informe:</b> <b>Agr,b</b> se muestra aquí como una <b>magnitud positiva de atenuación</b>. En la ecuación principal se resta al nivel de fuente igual que Adiv, Aatm y Abar.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>Término base A_ground(h)</th></tr>\n                    ",
    "\n                </table>\n                <h4>6. Topographic screening with DTM</h4>\n                <p>The DTM <b>does not change the emission</b> of the turbine nor the atmospheric absorption. Its role is to describe the <b>real path geometry</b> and feed the <b>Abar,b</b> term.</p>\n                <ol>\n                    <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with adaptive sampling.</li>\n                    <li><b>Line of sight:</b> the straight line between the effective source height and the receiver height is built. If the terrain always stays below, then <b>Abar,b = 0</b>.</li>\n                    <li><b>Dominant obstacle:</b> if a hill or ridge sticks out, its height above the line of sight is computed:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                <p>When <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                <ol start='4'>\n                    <li><b>Real obstacle geometry:</b> the plugin uses the real position of the dominant obstacle and computes <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver).</li>\n                    <li><b>Conservative activation:</b> <b>Abar</b> is not triggered by small DTM irregularities; a minimum threshold linked to the raster resolution is required.</li>\n                    <li><b>Fresnel-type diffraction:</b> from that geometry a path difference and a Fresnel number are estimated:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>That number is then turned into a frequency-dependent <b>Abar,b</b> attenuation through the plugin's current approximation:</p>\n                <div class='formula'>if C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>In the current implementation, <b>Abar</b> is additionally capped to reasonable values (upper cap) to avoid spurious over-attenuation. If there is no DTM or no relevant obstacle, then <b>Abar,b = 0</b>.</p>\n                <h4>7. A-weighting used at the end</h4>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ": "\n                </table>\n                <h4>6. Apantallamiento topográfico con MDT</h4>\n                <p>El MDT <b>no cambia la emisión</b> de la turbina ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto</b> y alimentar el término <b>Abar,b</b>.</p>\n                <ol>\n                    <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con muestreo adaptativo.</li>\n                    <li><b>Línea de visión:</b> se construye la recta entre la altura efectiva de fuente y la altura del receptor. Si el terreno queda siempre por debajo, entonces <b>Abar,b = 0</b>.</li>\n                    <li><b>Obstáculo dominante:</b> si una loma o cresta sobresale, se calcula la altura sobre la línea de visión:</li>\n                </ol>\n                <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                <p>Cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                <ol start='4'>\n                    <li><b>Geometría real del obstáculo:</b> el plugin usa la posición real del obstáculo dominante y calcula <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor).</li>\n                    <li><b>Activación conservadora:</b> no se activa <b>Abar</b> por pequeñas irregularidades del MDT; se exige un umbral mínimo ligado a la resolución del raster.</li>\n                    <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos y un número de Fresnel:</li>\n                </ol>\n                <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                <p>Ese número se transforma después en una atenuación <b>Abar,b</b> dependiente de la frecuencia mediante la aproximación actual del plugin:</p>\n                <div class='formula'>si C ≤ -2 → Abar = 0 &nbsp;&nbsp; ; &nbsp;&nbsp; -2 &lt; C ≤ 0 → Abar = 10·log10(3 + 20·C)</div>\n                <div class='formula'>0 &lt; C ≤ 3.5 → Abar = 10·log10(3 + 80·C) &nbsp;&nbsp; ; &nbsp;&nbsp; C &gt; 3.5 → Abar = 10·log10(3 + 280·C)</div>\n                <p>En la implementación actual, <b>Abar</b> se limita además a valores razonables (capado superior) para evitar sobreatenuaciones espurias. Si no hay MDT o no hay obstáculo relevante, entonces <b>Abar,b = 0</b>.</p>\n                <h4>7. Ponderación A usada al final</h4>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>A_weight [dB]</th></tr>\n                    ",
    '\n                </table>\n                <p><b>Critical-receiver reading:</b> the table in the critical-receiver section shows attenuation magnitudes for traceability. The <b>resulting level</b> must not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and over the contributing sources within the calculation radius.</p>\n            </div>\n            ': '\n                </table>\n                <p><b>Lectura del receptor crítico:</b> la tabla del apartado de receptor crítico muestra magnitudes de atenuación para trazabilidad. El <b>nivel resultante</b> no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.</p>\n            </div>\n            ',
    "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G calculation</h3>\n                    <p>In this run the ground effect was computed with a <b>single manual G</b> for the whole path:</p>\n                    <div class='formula'>G = ": "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G</h3>\n                    <p>En esta corrida el efecto suelo se ha calculado con un <b>G único manual</b> para todo el trayecto:</p>\n                    <div class='formula'>G = ",
    "\n                <div class='card'>\n                    <h3>🌱 Land-use physics and G_eff calculation</h3>\n                    <p>When the ground mode is <b>from layer</b>, the calculation does not use a single manual value for the whole wind farm. For each source–receiver path a <b>G_eff</b> is computed from the land-use layer:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>where <b>G_i</b> is the value assigned to each polygon intercepted by the path and <b>L_i</b> is the path length inside that polygon.</p>\n                    <ul>\n                        <li><b>G = 0</b>: hard ground (urban/asphalt/rock).</li>\n                        <li><b>G = 0.5</b>: mixed terrain.</li>\n                        <li><b>G = 1</b>: soft/porous ground (agricultural, grassland, forest, vegetated).</li>\n                    </ul>\n                    <p><b>Important:</b> the <b>global G</b> shown in the report is only a fallback value. When a land-use layer exists, the calculation actually uses <b>G_eff</b> per path. In this run, the effective mean value was <b>": "\n                <div class='card'>\n                    <h3>🌱 Física del uso del suelo y cálculo de G_eff</h3>\n                    <p>Cuando el modo suelo es <b>desde capa</b>, el cálculo no usa un único valor manual para todo el parque. Para cada trayecto fuente–receptor se obtiene un <b>G_eff</b> calculado desde la capa de uso del suelo:</p>\n                    <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                    <p>donde <b>G_i</b> es el valor asignado a cada polígono interceptado por el trayecto y <b>L_i</b> es la longitud del trayecto dentro de ese polígono.</p>\n                    <ul>\n                        <li><b>G = 0</b>: suelo duro (urbano/asfalto/roca).</li>\n                        <li><b>G = 0.5</b>: terreno mixto.</li>\n                        <li><b>G = 1</b>: suelo blando/poroso (agrícola, pradera, forestal, vegetado).</li>\n                    </ul>\n                    <p><b>Importante:</b> el <b>G global</b> mostrado en el informe es solo un valor de respaldo. Cuando hay capa de uso del suelo, el cálculo usa realmente <b>G_eff</b> por trayecto. En esta corrida, el valor medio efectivo fue <b>",
    "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In the ISO-aligned engine, the DTM <b>does not change the turbine emission</b> nor the atmospheric absorption. Its role is to describe the <b>real source–receiver path geometry</b> and feed the topographic screening term <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>How the DTM enters the calculation</h4>\n                    <ol>\n                        <li><b>Terrain profile:</b> the source–receiver profile is extracted from the DTM with <b>adaptive sampling</b>, adjusted to distance and raster resolution. The profile is computed <b>only once</b> per source–receiver pair and reused across the 8 bands to reduce computation time.</li>\n                        <li><b>Direct line of sight:</b> the profile is compared with the straight line joining the acoustic source at its effective height and the receiver at its evaluation height. If the terrain always stays below that line, there is no relevant topographic obstacle and <b>Abar,b = 0</b>.</li>\n                        <li><b>Dominant-obstacle detection:</b> if a hill or ridge in the DTM rises above the line of sight, the model interprets that a topographic screen exists. The key magnitude is the obstacle height above the line of sight:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terrain - z_LOS</div>\n                    <p>when <b>h_obs &gt; 0</b>, the relief cuts the direct line of sight and additional diffraction attenuation may appear.</p>\n                    <ol start='4'>\n                        <li><b>Conservative activation:</b> Abar is not triggered by small relief irregularities; a minimum threshold linked to the DTM resolution is applied.</li>\n                        <li><b>Real obstacle geometry:</b> the calculation uses the <b>real position</b> of the dominant obstacle and obtains real <b>d1</b> (source → obstacle) and <b>d2</b> (obstacle → receiver), instead of always assuming an obstacle at the midpoint.</li>\n                        <li><b>Fresnel-type diffraction:</b> from that geometry an approximate path difference is estimated and turned into a frequency-dependent attenuation:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>where <b>δ</b> is the approximate path difference, <b>f</b> the frequency and <b>c</b> the speed of sound. The number <b>C</b> is then translated into an <b>Abar,b</b> attenuation that grows the more the relief blocks the path. This is the same approximation implemented in the calculation.</p>\n                    <p><b>Physical interpretation:</b> on flat terrain or when there is no intersection with the line of sight, <b>Abar</b> is usually negligible. On complex terrain, the DTM can introduce several dB of additional attenuation and change the critical receiver.</p>\n                    <p><b>Current implementation:</b> single dominant obstacle, adaptive profile with cost limits, real obstacle geometry, conservative activation and attenuation capped to reasonable values.</p>\n                    <p><b>DTM used in this run:</b> ": "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En el motor ISO-aligned, el MDT <b>no cambia la emisión de la turbina</b> ni la absorción atmosférica. Su función es describir la <b>geometría real del trayecto fuente–receptor</b> y alimentar el término de apantallamiento topográfico <b>Abar,b</b>.</p>\n                    <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                    <h4>Cómo entra el MDT en el cálculo</h4>\n                    <ol>\n                        <li><b>Perfil del terreno:</b> se extrae el perfil fuente–receptor desde el MDT con un <b>muestreo adaptativo</b>, ajustado a la distancia y a la resolución del raster. El perfil se calcula <b>una sola vez</b> por par fuente–receptor y se reutiliza en las 8 bandas para reducir tiempo de cálculo.</li>\n                        <li><b>Línea de visión directa:</b> se compara el perfil con la recta que une la fuente acústica a su altura efectiva y el receptor a su altura de evaluación. Si el terreno queda siempre por debajo de esa recta, no hay obstáculo topográfico relevante y <b>Abar,b = 0</b>.</li>\n                        <li><b>Detección de obstáculo dominante:</b> si una loma o cresta del MDT sobresale por encima de la línea de visión, el modelo interpreta que existe pantalla topográfica. La magnitud clave es la altura del obstáculo sobre la línea de visión:</li>\n                    </ol>\n                    <div class='formula'>h_obs = z_terreno - z_LOS</div>\n                    <p>cuando <b>h_obs &gt; 0</b>, el relieve corta la visión directa y puede aparecer atenuación adicional por difracción.</p>\n                    <ol start='4'>\n                        <li><b>Activación conservadora:</b> no se activa Abar por pequeñas irregularidades del relieve; se aplica un umbral mínimo ligado a la resolución del MDT.</li>\n                        <li><b>Geometría real del obstáculo:</b> el cálculo usa la <b>posición real</b> del obstáculo dominante y obtiene <b>d1</b> (fuente → obstáculo) y <b>d2</b> (obstáculo → receptor) reales, en lugar de asumir siempre un obstáculo en el punto medio.</li>\n                        <li><b>Difracción tipo Fresnel:</b> con esa geometría se estima una diferencia de caminos aproximada y se transforma en una atenuación dependiente de la frecuencia:</li>\n                    </ol>\n                    <div class='formula'>δ ≈ 0.5·h_obs²·(1/d1 + 1/d2) &nbsp;&nbsp; ; &nbsp;&nbsp; C = (2·f·δ)/c</div>\n                    <p>donde <b>δ</b> es la diferencia de caminos aproximada, <b>f</b> la frecuencia y <b>c</b> la velocidad del sonido. El número <b>C</b> se traduce después a una atenuación <b>Abar,b</b> mayor cuanto más bloquea el relieve el trayecto. Esta es la misma aproximación implementada en el cálculo.</p>\n                    <p><b>Interpretación física:</b> en terreno plano o cuando no hay intersección con la línea de visión, <b>Abar</b> suele ser despreciable. En terreno complejo, el MDT puede introducir varios dB de atenuación adicional y cambiar el receptor crítico.</p>\n                    <p><b>Implementación actual:</b> obstáculo dominante único, perfil adaptativo con límites de coste, geometría real del obstáculo, activación conservadora y atenuación capada a valores razonables.</p>\n                    <p><b>MDT usado en esta corrida:</b> ",
    "\n                <div class='card'>\n                    <h3>🗺️ DTM physics and topographic screening</h3>\n                    <p>In this run <b>no DTM/DSM was used</b>, so the topographic screening term is set to:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>The assessment is performed without introducing topographic screens. The path geometry is resolved without a terrain profile and the calculation depends on Lw,b, Adiv, Aatm,b and Agr,b.</p>\n                </div>\n                ": "\n                <div class='card'>\n                    <h3>🗺️ Física del MDT y del apantallamiento topográfico</h3>\n                    <p>En esta corrida <b>no se ha usado MDT/DSM</b>, por lo que el término de apantallamiento topográfico se fija en:</p>\n                    <div class='formula'>Abar,b = 0</div>\n                    <p>La evaluación se realiza sin introducir pantallas topográficas. La geometría del trayecto se resuelve sin perfil del terreno y el cálculo depende de Lw,b, Adiv, Aatm,b y Agr,b.</p>\n                </div>\n                ",
    "\n                <div class='note'>\n                    <b>Correct reading of Abar:</b> the <b>dominant-path Abar</b> value corresponds only to the turbine that contributes most to the receiver and to its dominant band. The total receiver level is obtained by energetically summing all turbines and bands.\n                    <br><b>Maximum Abar among contributing turbines:</b> ": "\n                <div class='note'>\n                    <b>Lectura correcta de Abar:</b> el valor <b>Abar trayectoria dominante</b> corresponde solo a la turbina que más contribuye al receptor y a su banda dominante. El nivel total del receptor se obtiene sumando energéticamente todas las turbinas y bandas.\n                    <br><b>Abar máximo entre turbinas contribuyentes:</b> ",
    "\n                <h4>3. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance. Here <b>d</b> comes from the turbine and receiver coordinates together with their evaluation heights.</p>\n                <h4>4. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm,b = α(f, T, RH, P) · d</div>\n                <p>Atmospheric absorption is computed per band from a reference base coefficient and three correction factors. The physical dependence on temperature, relative humidity and pressure <b>is represented</b>, but through a <b>simplified plugin approximation</b>, not through the full analytical formulation of ISO 9613-1.</p>\n                <div class='formula'>α(f, T, RH, P) = α_ref(f) · corr_T · corr_RH · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_RH = 1 + 0.003·|RH - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretation of the corrections:</b> <b>T</b> is entered in °C relative to a 15 °C reference; <b>RH</b> is compared with a reference optimum humidity of 50% and the correction grows as it moves away from that value; and <b>P</b> is entered in kPa relative to a 101.325 kPa reference with an inverse correction. These factors modify only the atmospheric block <b>Aatm,b</b>: they alter neither the turbine emission, nor the ground effect, nor the DTM/screening term.</p>\n                <table>\n                    <tr><th>Band [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ": "\n                <h4>3. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor. Aquí <b>d</b> sale de las coordenadas de turbina y receptor junto con sus alturas de evaluación.</p>\n                <h4>4. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm,b = α(f, T, HR, P) · d</div>\n                <p>La absorción atmosférica se calcula por banda a partir de un coeficiente base de referencia y tres factores correctores. La dependencia física con temperatura, humedad relativa y presión <b>sí se representa</b>, pero mediante una <b>aproximación simplificada del plugin</b>, no mediante la formulación analítica completa de ISO 9613-1.</p>\n                <div class='formula'>α(f, T, HR, P) = α_ref(f) · corr_T · corr_HR · corr_P</div>\n                <div class='formula'>corr_T = 1 + 0.01·(T - 15) &nbsp;&nbsp; ; &nbsp;&nbsp; corr_HR = 1 + 0.003·|HR - 50| &nbsp;&nbsp; ; &nbsp;&nbsp; corr_P = 101.325 / P</div>\n                <p><b>Interpretación de las correcciones:</b> <b>T</b> se introduce en °C respecto a una referencia de 15 °C; <b>HR</b> se compara con una humedad óptima de referencia del 50% y la corrección crece al alejarse de ese valor; y <b>P</b> se introduce en kPa respecto a una referencia de 101.325 kPa con una corrección inversa. Estos factores modifican únicamente el bloque atmosférico <b>Aatm,b</b>: no alteran ni la emisión de la turbina, ni el efecto del suelo, ni el término de MDT/apantallamiento.</p>\n                <table>\n                    <tr><th>Banda [Hz]</th><th style='text-align:right;'>α_ref [dB/m]</th></tr>\n                    ",
    "\n                <h4>3. Simplified ground effect with land use</h4>\n                <p>In the fast engine the <b>Aground</b> term is still empirical, but the ground parameter can come from the land-use layer as a per-path <b>G_eff</b>:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>That <b>G_eff</b> is then used inside the fast engine's simplified terrain correction. The global value <b>G = ": "\n                <h4>3. Efecto suelo simplificado con uso del suelo</h4>\n                <p>En el motor rápido el término <b>Aground</b> sigue siendo empírico, pero el parámetro de suelo puede venir de la capa de uso del suelo como un <b>G_eff</b> por trayecto:</p>\n                <div class='formula'>G_eff = (Σ G_i · L_i) / (Σ L_i)</div>\n                <div class='formula'>Aground = min(6, max(0, G_eff · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>Ese <b>G_eff</b> se usa después dentro de la corrección simplificada de terreno del motor rápido. El valor global <b>G = ",
    "\n                <h4>3. Simplified ground effect</h4>\n                <p>The <b>Aground</b> term is an empirical terrain correction controlled by a single manual parameter:</p>\n                <div class='formula'>G = ": "\n                <h4>3. Efecto suelo simplificado</h4>\n                <p>El término <b>Aground</b> es una corrección empírica del terreno controlada por un único parámetro manual:</p>\n                <div class='formula'>G = ",
    '\n                <h4>4. DTM / topography</h4>\n                <p>In the fast engine the DTM does not introduce an explicit topographic screening term. Even if a relief layer exists in the project, this mode does not compute <b>Abar</b>, does not extract a line of sight and does not apply diffraction; therefore the physics relies only on <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> and the empirical terrain correction <b>Aground</b>.</p>\n            ': '\n                <h4>4. MDT / topografía</h4>\n                <p>En el motor rápido el MDT no introduce un término explícito de apantallamiento topográfico. Aunque exista una capa de relieve en el proyecto, este modo no calcula <b>Abar</b>, no extrae línea de visión ni aplica difracción; por tanto la física se basa únicamente en <b>LwA</b>, <b>Adiv</b>, <b>Aatm = α·d</b> y la corrección empírica de terreno <b>Aground</b>.</p>\n            ',
    '\n                <h4>5. What this mode does not do</h4>\n                <p>The fast engine does not work by bands, does not compute <b>Lw,b</b>, does not introduce <b>Abar</b> and does not extract a line of sight or diffraction from a DTM. It is therefore appropriate for screening and quick comparisons, but not for detailed spectral analysis.</p>\n            </div>\n            ': '\n                <h4>5. Qué no hace este modo</h4>\n                <p>El motor rápido no trabaja por bandas, no calcula <b>Lw,b</b>, no introduce <b>Abar</b> y no extrae línea de visión ni difracción desde MDT. Por tanto, es apropiado para screening y comparativas rápidas, pero no para análisis espectral detallado.</p>\n            </div>\n            ',
    "\n            <div class='card card-info'>\n                <h3>🧭 How the ISO-aligned calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows so that the per-receiver result is traceable. The final level of each receiver <b>does not come from a single simple subtraction</b>, but from computing all source–receiver contributions within the calculation radius and summing them energetically.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo ISO-aligned</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin para que el resultado por receptor sea trazable. El nivel final de cada receptor <b>no sale de una única resta simple</b>, sino de calcular todas las contribuciones fuente–receptor dentro del radio de cálculo y sumarlas energéticamente.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>",
    "\n            <div class='card card-info'>\n                <h3>🧭 How the Screening calculation was run</h3>\n                <p>This section explains the actual flow the plugin follows in the fast mode. The goal is to obtain a quick estimate for maps, comparison of alternatives and initial detection of sensitive receivers.</p>\n                <ol>\n                    <li><b>Reading GIS inputs:</b> the acoustic turbines/sources, the receivers, the receiver height, the maximum calculation radius (<b>": "\n            <div class='card card-info'>\n                <h3>🧭 Cómo se ha ejecutado el cálculo Screening</h3>\n                <p>Esta sección explica el flujo real que sigue el plugin en el modo rápido. El objetivo es obtener una estimación ágil para mapas, comparación de alternativas y detección inicial de receptores sensibles.</p>\n                <ol>\n                    <li><b>Lectura de entradas GIS:</b> se toman las turbinas/fuentes acústicas, los receptores, la altura de receptor, el radio máximo de cálculo (<b>",
    "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the ISO-aligned engine</h3>\n                <p>This engine works in <b>8 octave bands</b> (63–8000 Hz). The bands are not a calculation result, but the <b>frequency grid of the method</b>. To apply band-wise propagation, the calculation needs a per-band acoustic input <code>Lw,b</code> from the source. That input can come from a measured/imported spectrum or from a template/fallback adjusted to the global operating level.</p>\n                <p><b>Operating scenario of this run:</b> ": "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor ISO-aligned</h3>\n                <p>Este motor trabaja en <b>8 bandas de octava</b> (63–8000 Hz). Las bandas no son un resultado del cálculo, sino la <b>malla frecuencial del método</b>. Para aplicar la propagación por bandas, el cálculo necesita un <b>input acústico por banda</b> de la fuente <code>Lw,b</code>. Ese input puede venir de un espectro medido/importado o de una plantilla/fallback ajustada al nivel global operativo.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ",
    "\n            <div class='card'>\n                <h3>📘 Detailed physical development of the fast engine</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>The fast engine works with a single global level <b>LwA</b> per source group. It is intended for screening, quick maps and fast comparisons, trading spectral detail for speed. In this mode there is <b>no band-wise propagation</b> and no explicit topographic screening term.</p>\n                <p><b>Operating scenario of this run:</b> ": "\n            <div class='card'>\n                <h3>📘 Desarrollo físico detallado del motor rápido</h3>\n                <div class='formula'>Lp = LwA - Adiv - Aatm - Aground</div>\n                <p>El motor rápido trabaja con un único nivel global <b>LwA</b> por grupo fuente. Está pensado para screening, mapas ágiles y comparativas rápidas, sacrificando detalle espectral frente a velocidad. En este modo <b>no hay propagación por bandas</b> ni término explícito de apantallamiento topográfico.</p>\n                <p><b>Escenario operativo de esta corrida:</b> ",
    ' % of the wind farm net AEP)': ' % del AEP neto del parque)',
    ' %</b> uniformly as fallback. This value directly affects AEP in TI-driven wakes (Niayifar, Zong, TurboGaussian, TurboNOJ). For representative results, load a TI raster in the resource section or review the turbulence sensitivity.</p>': ' %</b> uniforme como fallback. Este valor afecta directamente al AEP en wakes TI-driven (Niayifar, Zong, TurboGaussian, TurboNOJ). Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.</p>',
    ' Activation threshold: ': ' Umbral de activación: ',
    ' Other receivers do show screening (max Abar ': ' Otros receptores sí presentan apantallamiento (Abar máx. ',
    ' dB · <b>Abar weighted by energetic contribution:</b> ': ' dB · <b>Abar ponderado por contribución energética:</b> ',
    ' dB. This shift raises or lowers the entire spectral template so that its A-weighted sum reproduces the <code>LwA_target</code> of the acoustic curve or of the fixed LwA.</p>': ' dB. Este desplazamiento eleva o reduce toda la plantilla espectral para que su suma A-ponderada reproduzca el <code>LwA_objetivo</code> de la curva acústica o del LwA fijo.</p>',
    ' dB/m</b>. In the fast engine, atmospheric absorption is summarized in a single constant coefficient, so <b>T</b>, <b>RH</b> and <b>P</b> <b>do not enter explicitly</b> in the calculation. That is one of the key simplifications compared with the ISO-aligned mode.</p>\n                ': ' dB/m</b>. En el motor rápido, la absorción atmosférica se resume en un único coeficiente constante, por lo que <b>T</b>, <b>HR</b> y <b>P</b> <b>no entran explícitamente</b> en el cálculo. Esa es una de las simplificaciones clave frente al modo ISO-aligned.</p>\n                ',
    ' m AGL · receiver acoustic height=': ' m AGL · altura acústica receptor=',
    ' m AGL · turbine acoustic height=': ' m AGL · altura acústica turbina=',
    ' m · receiver terrain=': ' m · terreno receptor=',
    ' m</b>) and the land-use layer if it exists.</li>\n                    <li><b>Acoustic state of each source group:</b> each turbine model or group works with a single <b>operating LwA</b>, defined by a fixed value or by an <code>LwA(ws)</code> curve. In this run: <b>': ' m</b>) y la capa de uso del suelo si existe.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> cada modelo o grupo de turbinas trabaja con un único <b>LwA operativo</b>, definido por un valor fijo o por una curva <code>LwA(ws)</code>. En esta corrida: <b>',
    ' m</b>), the land-use layer if it exists and the DTM/DSM if it is active.</li>\n                    <li><b>Acoustic state of each source group:</b> for each turbine model or group an <b>operating LwA</b> is obtained from a fixed value or from an <code>LwA(ws)</code> curve. In this run: <b>': ' m</b>), la capa de uso del suelo si existe y el MDT/DSM si está activo.</li>\n                    <li><b>Estado acústico de cada grupo fuente:</b> para cada modelo o grupo de turbinas se obtiene un <b>LwA operativo</b> desde un valor fijo o desde una curva <code>LwA(ws)</code>. En esta corrida: <b>',
    ' receiver(s) with at least one turbine inside the maximum radius.': ' receptor(es) con al menos una turbina dentro del radio máximo.',
    ' receivers</strong> exceed limits<br>\n                <strong>': ' receptores</strong> superan límites<br>\n                <strong>',
    ' receivers</strong> within the radius<br>\n                <strong>': ' receptores</strong> dentro del radio<br>\n                <strong>',
    '%. It is valid for a test, but it does not represent vertical variation of turbulence.': '%. Es válido para una prueba, pero no representa variación vertical de turbulencia.',
    "' does not have enough values to define bounds.": "' no tiene suficientes valores para definir bounds.",
    "' is bytes (incorrect read).": "' es bytes (lectura errónea).",
    "' is empty.": "' está vacía.",
    "' is not in the same CRS as the project. It is ignored in the noise calculation.": "' no está en el mismo CRS que el proyecto. Se ignora en el cálculo de ruido.",
    "' is str (incorrect read).": "' es str (lectura errónea).",
    "' not available in this PyWake installation. Trying fallback to SelfSimilarityDeficit2020...": "' no disponible en esta instalación de PyWake. Intentando fallback a SelfSimilarityDeficit2020...",
    "'. Check the exported WAsP structure.": "'. Revisa la estructura exportada de WAsP.",
    "'. Select the per-model coordinate CSVs in the dialog or create a turbines.csv with x,y[,model_id] columns.": "'. Selecciona los CSV de coordenadas por modelo en el diálogo o crea un turbines.csv con columnas x,y[,model_id].",
    "'; the first raster for that height is kept.": "'; se conserva el primer raster para esa altura.",
    '(more stable coupling). The rest of the selection was preserved.': '(acoplamiento más estable). El resto de la selección se ha respetado.',
    '(source/receiver terrain will be sampled in the acoustic method).': '(se muestreará terreno fuente/receptor en el método acústico).',
    ') with no identifiable height; it will be applied to all WRG heights as an approximation.': ') sin altura identificable; se aplicará a todas las alturas del WRG como aproximación.',
    '): invalid negative height.': '): altura negativa no válida.',
    '): invalid negative limits.': '): límites negativos no válidos.',
    ', no height)': ', sin altura)',
    ', read': ', leído',
    ", the turbulence breakdown is interpreted as ambient-TI only versus ambient TI + wake-added turbulence. A 'no TI' case is not run, because the PyWake Site needs a physical ambient TI.": ", el desglose de turbulencia se interpreta como TI ambiente only frente a TI ambiente + turbulencia añadida por estela. No se ejecuta un caso 'sin TI', porque el Site de PyWake necesita una TI ambiente física.",
    '- Module:': '- Módulo:',
    '-> using safe automatic selection.': '-> usando selección automática segura.',
    '. A fixed fallback will be used.': '. Se usará fallback fijo.',
    '. Solar calculation uses UTC internally and accumulates results by local month/hour.': '. El cálculo solar usa UTC internamente y acumula resultados por mes/hora local.',
    '. The plugin will apply that TI to all heights as an approximation.': '. El plugin aplicará esa TI a todas las alturas como aproximación.',
    '. Trying the standard loader…': '. Probando loader estándar…',
    '.</p>\n                    <p><b>What each column represents:</b> <code>S_b^ref</code> is the reference spectral template (if any), <code>A_weight,b</code> the A-weighting of each band and <code>Lw,b</code> the final level in dB actually used by the calculation.</p>': '.</p>\n                    <p><b>Qué representa cada columna:</b> <code>S_b^ref</code> es la plantilla espectral de referencia (si existe), <code>A_weight,b</code> la ponderación A de cada banda y <code>Lw,b</code> el nivel final en dB realmente usado por el cálculo.</p>',
    ".</p>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> a single global level <b>LwA</b> per source group.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> for the selected wind speed or worst case.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> in this mode T/RH/P are not used; absorption is summarized in a single coefficient <b>α</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> the DTM does not enter as explicit screening in this mode.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Global source input. It comes from a fixed value per group or from an <code>LwA(ws)</code> acoustic curve for the selected wind speed/worst case.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Computed with a single constant coefficient <code>α</code> multiplied by the distance.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Empirical ground-effect correction. The ground parameter can be a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</td></tr>\n                </table>\n                <h4>2. Geometrical divergence</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>It represents the geometrical spreading of the sound wave with the 3D source–receiver distance.</p>\n                <h4>3. Simplified atmospheric absorption</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>In this run <b>α = ": ".</p>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> un único nivel global <b>LwA</b> por grupo fuente.</li>\n                    <li><b>Nivel operativo global:</b> sale de un <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> para la velocidad o peor caso seleccionados.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> en este modo no se usan T/HR/P; la absorción se resume en un único coeficiente <b>α</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> el MDT no entra como apantallamiento explícito en este modo.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>LwA</b></td><td>Input global de la fuente. Sale de un valor fijo por grupo o de una curva acústica <code>LwA(ws)</code> para la velocidad/peor caso seleccionados.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm</b></td><td>Se calcula con un único coeficiente constante <code>α</code> multiplicado por la distancia.</td></tr>\n                    <tr><td><b>Aground</b></td><td>Corrección empírica del efecto suelo. El parámetro de suelo puede ser un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                </table>\n                <h4>2. Divergencia geométrica</h4>\n                <div class='formula'>Adiv = 20·log10(d) + 11</div>\n                <p>Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor.</p>\n                <h4>3. Absorción atmosférica simplificada</h4>\n                <div class='formula'>Aatm = α · d</div>\n                <p>En esta corrida se ha usado <b>α = ",
    ".</p>\n                <p><b>General per-band equation:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Final A-weighted sum:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs actually used in this run</h4>\n                <ul>\n                    <li><b>Acoustic source:</b> <code>Lw,b</code> by octave bands. If a specific spectrum of the source group exists, that is the input used. If not, the plugin uses a library/template/fallback and adjusts it to the global operating level.</li>\n                    <li><b>Global operating level:</b> it comes from a <b>fixed LwA</b> or from an <b>LwA(ws) acoustic curve</b> according to the selected scenario. That global level does not replace the bands: it sets the operating state and the spectrum provides the frequency distribution.</li>\n                    <li><b>Geometry:</b> source and receiver coordinates, receiver height, effective source height and 3D distance.</li>\n                    <li><b>Atmosphere:</b> temperature <b>T</b>, relative humidity <b>RH</b> and pressure <b>P</b>.</li>\n                    <li><b>Ground:</b> a <b>manual global G</b> or a <b>G_eff</b> derived from the land-use layer.</li>\n                    <li><b>Topography:</b> optional DTM/DSM. It only affects the calculation of <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. Where each term of the equation comes from</h4>\n                <table>\n                    <tr><th>Term</th><th>How it is obtained in this plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Per-band acoustic input. It comes from the source-group spectrum (CSV, library, template or fallback adjusted to the global level). The LwA(ws) acoustic curve or the fixed LwA define the turbine's global operating level, and the band spectrum distributes that level among the 8 bands.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Computed from the 3D source–receiver distance.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Computed per band with a base absorption table <code>α_ref(f)</code> and simplified corrections for temperature, relative humidity and pressure. The current implementation uses the plugin's exact math: <code>α = α_ref(f)·corr_T·corr_RH·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Computed as ground effect by regions. The ground parameter used is a <b>single G per path</b>: manual/global or <b>G_eff</b> derived from the land-use layer.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>It only enters if there is a DTM/DSM and topographic screening is detected. If there is no DTM or no relevant obstacle, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Source acoustic input and bands</h4>\n                <p>In this engine, the term <code>Lw,b</code> is a <b>per-band input datum</b>. The <b>octave bands</b> (63–8000 Hz) are not a result of the ISO nor a table computed by the plugin: they are the <b>frequency grid</b> on which propagation is solved.</p>\n                <p>The plugin combines two pieces:</p>\n                <ul>\n                    <li><b>Global acoustic curve LwA(ws)</b>: it sets the <b>global operating level</b> of the turbine for the selected wind speed or worst case.</li>\n                    <li><b>Band spectrum Lw,b</b>: it distributes that global level among the 8 bands and is the real input used in the band-wise equation.</li>\n                </ul>\n                <p>That spectrum can come from a specific manufacturer/user file or from a reference template. If only a global curve <code>LwA(ws)</code> is available, the plugin first sets the global operating level <code>LwA_target</code> and then builds an absolute per-band spectrum from a reference spectral shape <code>S_b^ref</code>.</p>\n                <p><b>Mathematical reconstruction of the bands when only LwA(ws) exists:</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_target - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>That is: the acoustic curve provides the <b>global operating level</b> and the template/library provides the <b>spectral shape</b>. The shift <b>Δ</b> is computed so that, when A-weighting and energetically summing the 8 bands, the reconstructed spectrum reproduces exactly the <code>LwA_target</code> of the imported curve.</p>\n                ": ".</p>\n                <p><b>Ecuación general por banda:</b></p>\n                <div class='formula'>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</div>\n                <p><b>Suma A-ponderada final:</b></p>\n                <div class='formula'>LpA,total = 10·log10(Σ 10^((Lp,b + A_weight)/10))</div>\n                <h4>0. Inputs realmente usados en esta corrida</h4>\n                <ul>\n                    <li><b>Fuente acústica:</b> <code>Lw,b</code> por bandas de octava. Si existe espectro específico del grupo fuente, ese es el input usado. Si no, el plugin usa una biblioteca/plantilla/fallback y la ajusta al nivel global operativo.</li>\n                    <li><b>Nivel operativo global:</b> viene de <b>LwA fijo</b> o de una <b>curva acústica LwA(ws)</b> según el escenario seleccionado. Ese nivel global no sustituye a las bandas: fija el estado operativo y el espectro aporta el reparto frecuencial.</li>\n                    <li><b>Geometría:</b> coordenadas de fuente y receptor, altura de receptor, altura efectiva de fuente y distancia 3D.</li>\n                    <li><b>Atmósfera:</b> temperatura <b>T</b>, humedad relativa <b>HR</b> y presión <b>P</b>.</li>\n                    <li><b>Suelo:</b> un <b>G global manual</b> o un <b>G_eff</b> derivado desde la capa de uso del suelo.</li>\n                    <li><b>Topografía:</b> MDT/DSM opcional. Solo afecta al cálculo de <b>Abar,b</b>.</li>\n                </ul>\n                <h4>1. De dónde sale cada término de la ecuación</h4>\n                <table>\n                    <tr><th>Término</th><th>Cómo se obtiene en este plugin</th></tr>\n                    <tr><td><b>Lw,b</b></td><td>Input acústico por bandas. Sale del espectro del grupo fuente (CSV, biblioteca, plantilla o fallback ajustado al nivel global). La curva acústica LwA(ws) o el LwA fijo definen el nivel global operativo de la turbina, y el espectro por bandas reparte ese nivel entre las 8 bandas.</td></tr>\n                    <tr><td><b>Adiv</b></td><td>Se calcula a partir de la distancia 3D fuente–receptor.</td></tr>\n                    <tr><td><b>Aatm,b</b></td><td>Se calcula por banda con una tabla base de absorción <code>α_ref(f)</code> y correcciones simplificadas por temperatura, humedad relativa y presión. La implementación actual usa la matemática exacta del plugin: <code>α = α_ref(f)·corr_T·corr_HR·corr_P</code>.</td></tr>\n                    <tr><td><b>Agr,b</b></td><td>Se calcula como efecto suelo por regiones. El parámetro de suelo usado es un <b>G único por trayecto</b>: manual/global o <b>G_eff</b> derivado desde la capa de uso del suelo.</td></tr>\n                    <tr><td><b>Abar,b</b></td><td>Solo entra si hay MDT/DSM y se detecta apantallamiento topográfico. Si no hay MDT o no hay obstáculo relevante, <b>Abar,b = 0</b>.</td></tr>\n                </table>\n                <h4>2. Input acústico de fuente y bandas</h4>\n                <p>En este motor, el término <code>Lw,b</code> es un <b>dato de entrada por banda</b>. Las <b>bandas de octava</b> (63–8000 Hz) no son un resultado de la ISO ni una tabla calculada por el plugin: son la <b>malla frecuencial</b> sobre la que se resuelve la propagación.</p>\n                <p>El plugin combina dos piezas:</p>\n                <ul>\n                    <li><b>Curva acústica global LwA(ws)</b>: fija el <b>nivel operativo global</b> de la turbina para la velocidad de viento o el peor caso seleccionado.</li>\n                    <li><b>Espectro por bandas Lw,b</b>: reparte ese nivel global entre las 8 bandas y es el input real usado en la ecuación por bandas.</li>\n                </ul>\n                <p>Ese espectro puede proceder de un archivo específico del fabricante/usuario o de una plantilla de referencia. Si solo se dispone de una curva global <code>LwA(ws)</code>, el plugin fija primero el nivel global operativo <code>LwA_objetivo</code> y después construye un espectro absoluto por bandas a partir de una forma espectral de referencia <code>S_b^ref</code>.</p>\n                <p><b>Reconstrucción matemática de las bandas cuando solo existe LwA(ws):</b></p>\n                <div class='formula'>Lw,b = S_b^ref + Δ</div>\n                <div class='formula'>Δ = LwA_objetivo - 10·log10(Σ 10^((S_b^ref + A_weight,b)/10))</div>\n                <p>Es decir: la curva acústica aporta el <b>nivel global operativo</b> y la plantilla/biblioteca aporta la <b>forma espectral</b>. El desplazamiento <b>Δ</b> se calcula para que, al ponderar en A y sumar energéticamente las 8 bandas, el espectro reconstruido reproduzca exactamente el <code>LwA_objetivo</code> de la curva importada.</p>\n                ",
    ': coordinates are missing. Select a CSV or add points on the map.': ': faltan coordenadas. Selecciona CSV o añade puntos en el mapa.',
    ': curve mode active but no acoustic-curve CSV.': ': modo curva activo pero sin CSV de curva acústica.',
    ': invalid fixed LwA.': ': LwA fijo no válido.',
    ': invalid hub height.': ': altura de buje no válida.',
    ': invalid receiver layer.': ': capa de receptores no válida.',
    ": the acoustic curve does not exist '": ": no existe la curva acústica '",
    ': the coordinate CSV does not exist: ': ': el CSV de coordenadas no existe: ',
    ": the layer '": ": la capa '",
    ': the layer name is missing.': ': falta el nombre de capa.',
    ': the turbine / power curve is not defined.': ': falta definir la turbina/curva de potencia.',
    ': using LinearSum (user selection).': ': usando LinearSum (selección usuario).',
    ': using MaxSum (user selection).': ': usando MaxSum (selección usuario).',
    ': using SquaredSum (auto, robust default option).': ': usando SquaredSum (auto, opción robusta por defecto).',
    ': using SquaredSum (user selection).': ': usando SquaredSum (selección usuario).',
    ': using WeightedSum (user selection, deficit=': ': usando WeightedSum (selección usuario, deficit=',
    '</b> and that of the critical receiver <b>': '</b> y el del receptor crítico <b>',
    '</b> is kept only as a fallback if the layer does not provide valid information.</p>\n                <p><b>Layer used:</b> ': '</b> queda solo como respaldo si la capa no aporta información válida.</p>\n                <p><b>Capa usada:</b> ',
    "</b>.</li>\n                    <li><b>Conversion to bands:</b> the ISO-aligned engine needs a spectrum <code>Lw,b</code> in 8 octave bands. If there is no specific spectrum, the plugin reconstructs one from a template/fallback and adjusts it to reproduce the operating LwA.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b> and do not generate a useful acoustic level.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine, the 3D distance, acoustic heights, ground <b>G</b> or <b>G_eff</b> and, if there is a DTM/DSM, the possible topographic screening of the path are computed.</li>\n                    <li><b>Per-band propagation:</b> in each band <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code> is applied. Adiv depends on distance, Aatm,b on frequency/atmosphere, Agr,b on ground and Abar,b on the DTM if there is a relevant obstacle.</li>\n                    <li><b>Per-source sum:</b> the 8 bands are A-weighted and summed energetically to obtain the A-weighted level of that turbine at the receiver.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(LpA,source/10))</div>\n                <p><b>Practical reading:</b> the critical receiver is the one with the highest total level or the worst margin against the limit. The “dominant source” column identifies the turbine/group that contributes the most, but the receiver's final result includes all sources within the radius.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the Screening mode</h3>\n                <p>The ISO-aligned mode is heavier but more traceable: it uses octave bands, a final A-weighting, frequency-dependent atmospheric absorption, ground by regions and topographic screening <b>Abar</b> when there is a DTM/DSM. It is the recommended mode for preliminary technical reports and review of sensitive receivers.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Conversión a bandas:</b> el motor ISO-aligned necesita un espectro <code>Lw,b</code> en 8 bandas de octava. Si no hay espectro específico, el plugin reconstruye uno desde una plantilla/fallback y lo ajusta para reproducir el LwA operativo.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b> y no generan nivel acústico útil.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula distancia 3D, cotas acústicas, <b>G</b> o <b>G_eff</b> del suelo y, si hay MDT/DSM, el posible apantallamiento topográfico del trayecto.</li>\n                    <li><b>Propagación por banda:</b> en cada banda se aplica <code>Lp,b = Lw,b - Adiv - Aatm,b - Agr,b - Abar,b</code>. Adiv depende de la distancia, Aatm,b de frecuencia/atmósfera, Agr,b del suelo y Abar,b del MDT si hay obstáculo relevante.</li>\n                    <li><b>Suma por fuente:</b> las 8 bandas se ponderan en A y se suman energéticamente para obtener el nivel A-ponderado de esa turbina en el receptor.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(LpA,fuente/10))</div>\n                <p><b>Lectura práctica:</b> el receptor crítico es el de mayor nivel total o el que queda con peor margen frente al límite. La columna “fuente dominante” identifica la turbina/grupo que más contribuye, pero el resultado final del receptor incluye todas las fuentes dentro del radio.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo Screening</h3>\n                <p>El modo ISO-aligned es más pesado pero más trazable: usa bandas de octava, ponderación A final, absorción atmosférica dependiente de frecuencia, suelo por regiones y apantallamiento topográfico <b>Abar</b> cuando hay MDT/DSM. Es el modo recomendado para informes técnicos preliminares y revisión de receptores sensibles.</p>\n            </div>\n            ",
    "</b>.</li>\n                    <li><b>Selection of contributors per receiver:</b> for each receiver the turbines within the maximum radius are searched. Receivers with no sources inside that radius are flagged as <b>outside the radius</b>.</li>\n                    <li><b>Per source–receiver path calculation:</b> for each contributing turbine the 3D distance, the geometrical divergence, a simplified atmospheric absorption <code>α·d</code> and an empirical ground correction are computed.</li>\n                    <li><b>Land use:</b> if there is a land-use layer, the plugin can compute a per-path <b>G_eff</b>; otherwise it uses the <b>global G</b> defined by the user.</li>\n                    <li><b>Simplified propagation:</b> <code>Lp = LwA - Adiv - Aatm - Aground</code> is applied. There are no octave bands and no explicit topographic screening <code>Abar</code>.</li>\n                    <li><b>Receiver sum:</b> all contributing turbines are summed energetically to obtain the <b>total level dB(A)</b> of the receiver.</li>\n                    <li><b>Comparison with limits:</b> the total level is compared with the limit assigned to the receiver or with the reference limit. From there come the margin, the compliance status and the exceedance table.</li>\n                </ol>\n                <div class='formula'>LpA,receiver = 10·log10(Σ_sources 10^(Lp,source/10))</div>\n                <p><b>Practical reading:</b> this mode is useful for initial screening. If a receiver appears close to the limit or in exceedance, it is advisable to recalculate it with the ISO-aligned mode and review spectra, terrain, land use and applied limits.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 What sets this mode apart from the ISO-aligned mode</h3>\n                <p>The Screening mode trades detail for speed. It does not propagate by bands, does not use T/RH/P per frequency, does not compute <b>Abar</b> from a DTM and summarizes the atmosphere with a single coefficient <b>α</b>. It should therefore be interpreted as a quick pre-assessment, not as a detailed acoustic report.</p>\n            </div>\n            ": "</b>.</li>\n                    <li><b>Selección de contribuyentes por receptor:</b> para cada receptor se buscan las turbinas dentro del radio máximo. Los receptores sin fuentes dentro de ese radio se marcan como <b>fuera de radio</b>.</li>\n                    <li><b>Cálculo por trayecto fuente–receptor:</b> para cada turbina contribuyente se calcula la distancia 3D, la divergencia geométrica, una absorción atmosférica simplificada <code>α·d</code> y una corrección empírica de suelo.</li>\n                    <li><b>Uso del suelo:</b> si hay capa de land-use, el plugin puede calcular un <b>G_eff</b> por trayecto; si no, usa el <b>G global</b> definido por el usuario.</li>\n                    <li><b>Propagación simplificada:</b> se aplica <code>Lp = LwA - Adiv - Aatm - Aground</code>. No hay bandas de octava ni apantallamiento topográfico explícito <code>Abar</code>.</li>\n                    <li><b>Suma del receptor:</b> todas las turbinas contribuyentes se suman energéticamente para obtener el <b>nivel total dB(A)</b> del receptor.</li>\n                    <li><b>Comparación con límites:</b> el nivel total se compara con el límite asignado al receptor o con el límite de referencia. De ahí salen el margen, el estado de cumplimiento y la tabla de excedencias.</li>\n                </ol>\n                <div class='formula'>LpA,receptor = 10·log10(Σ_fuentes 10^(Lp,fuente/10))</div>\n                <p><b>Lectura práctica:</b> este modo es útil para screening inicial. Si un receptor aparece cerca del límite o en excedencia, conviene recalcularlo con el modo ISO-aligned y revisar espectros, terreno, uso del suelo y límites aplicados.</p>\n            </div>\n            <div class='card'>\n                <h3>🔎 Qué diferencia este modo del modo ISO-aligned</h3>\n                <p>El modo Screening sacrifica detalle para ganar velocidad. No propaga por bandas, no usa T/HR/P por frecuencia, no calcula <b>Abar</b> desde MDT y resume la atmósfera con un único coeficiente <b>α</b>. Por eso debe interpretarse como preevaluación rápida, no como informe acústico detallado.</p>\n            </div>\n            ",
    '</div>\n                    <p>That value is applied in the ground term of the model. No G_eff was derived from a land-use layer.</p>\n                </div>\n                ': '</div>\n                    <p>Ese valor se aplica en el término de suelo del modelo. No se ha derivado un G_eff desde capa de uso del suelo.</p>\n                </div>\n                ',
    "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>In this run no G_eff was derived from a land-use layer. Here <b>d_xy</b> is the horizontal distance, <b>h_s</b> the source height and <b>h_r</b> the receiver height.</p>\n                ": "</div>\n                <div class='formula'>Aground = min(6, max(0, G · 3·log10(1 + d_xy/100) · 1/(1 + (h_s + h_r)/80)))</div>\n                <p>En esta corrida no se ha derivado un G_eff desde una capa de uso del suelo. Aquí <b>d_xy</b> es la distancia horizontal, <b>h_s</b> la altura de fuente y <b>h_r</b> la altura del receptor.</p>\n                ",
    '</p>\n        </div>\n\n        <h2>2. HOW THE RESULT WAS GENERATED</h2>\n        ': '</p>\n        </div>\n\n        <h2>2. CÓMO SE HA GENERADO EL RESULTADO</h2>\n        ',
    "</td>\n                    <td>Average weighted by the acoustic contribution of each turbine</td>\n                </tr>\n                <tr>\n                    <td>Screened paths</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Promedio ponderado por la contribución acústica de cada turbina</td>\n                </tr>\n                <tr>\n                    <td>Trayectorias apantalladas</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Maximum Abar among all turbines contributing to the receiver</td>\n                </tr>\n                <tr>\n                    <td>Energy-weighted Abar</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Máximo Abar entre todas las turbinas que contribuyen al receptor</td>\n                </tr>\n                <tr>\n                    <td>Abar ponderado por energía</td>\n                    <td style='text-align: right;'>",
    "</td>\n                    <td>Number of contributing turbines with Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>RESULTING LEVEL</td>\n                    <td style='text-align: right;'>": "</td>\n                    <td>Número de turbinas contribuyentes con Abar &gt; 0 dB</td>\n                </tr>\n                <tr style='background: #1e3a5f; color: white; font-weight: bold;'>\n                    <td>NIVEL RESULTANTE</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (maximum among contributors)</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td><b>Abar</b> (máximo entre contribuyentes)</td>\n                    <td style='text-align: right;'>",
    "</td>\n                </tr>\n                <tr>\n                    <td>Maximum contributors Abar</td>\n                    <td style='text-align: right;'>": "</td>\n                </tr>\n                <tr>\n                    <td>Abar máximo contribuyentes</td>\n                    <td style='text-align: right;'>",
    "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Compliance by category</h3>\n                <ul>": "</ul>\n            </div>\n            <div class='card'>\n                <h3>✓ Cumplimiento por Categoría</h3>\n                <ul>",
    "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitations and recommendations</strong>\n            <p><b>Fast engine:</b> Appropriate for preliminary screening and quick maps.</p>\n            <p><b>ISO-aligned engine:</b> Appropriate for preliminary technical studies, comparisons and design iteration.</p>\n            <p><b>Known simplifications:</b> simplified Aatm (tables + corrections); Agr and Abar with basic approximations; directivity Dc assumed 0 dB; long-term Cmet/meteorological correction not applied.</p>\n            <p><b>Multiple models:</b> supported through independent source layers/groups. Mixing several models within a single layer by attributes is not enabled in this experimental release.</p>\n            <p><b>ISO raster + DTM:</b> it uses the same topographic-screening logic as point receivers, but it can be expensive on large maps.</p>\n            <p><b>Recommendation:</b> For critical regulatory studies, validate with measurements or certified commercial software.</p>\n        </div>\n        ": "</ul>\n            </div>\n        </div>\n        \n        <div class='disclaimer'>\n            <strong>⚠️ Limitaciones y recomendaciones</strong>\n            <p><b>Motor Rápido:</b> Apropiado para screening preliminar y mapas ágiles.</p>\n            <p><b>Motor ISO-aligned:</b> Apropiado para estudios técnicos preliminares, comparativas e iteración de diseño.</p>\n            <p><b>Simplificaciones conocidas:</b> Aatm simplificado (tablas + correcciones); Agr y Abar con aproximaciones básicas; directividad Dc asumida 0 dB; Cmet/corrección meteorológica de largo plazo no aplicada.</p>\n            <p><b>Modelos múltiples:</b> soportados mediante capas/grupos fuente independientes. Mezclar varios modelos dentro de una sola capa por atributos no está habilitado en esta versión experimental.</p>\n            <p><b>Raster ISO + MDT:</b> usa la misma lógica de apantallamiento topográfico que los receptores puntuales, pero puede ser costoso en mapas grandes.</p>\n            <p><b>Recomendación:</b> Para estudios regulatorios críticos, validar con mediciones o software comercial certificado.</p>\n        </div>\n        ",
    '</ul>\n        </div>\n        \n        <h2>5. DETAILED PHYSICS AND CALCULATION TRACEABILITY</h2>\n        ': '</ul>\n        </div>\n        \n        <h2>5. FÍSICA DETALLADA Y TRAZABILIDAD DEL CÁLCULO</h2>\n        ',
    "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUTION BY RECEIVER TYPE</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receivers by category</h3>\n                <ul>": "</ul>\n        </div>\n        \n        <h2>7. DISTRIBUCIÓN POR TIPO DE RECEPTOR</h2>\n        <div style='display: grid; grid-template-columns: 1fr 1fr; gap: 16px;'>\n            <div class='card'>\n                <h3>📍 Receptores por Categoría</h3>\n                <ul>",
    '<b>Active model:</b>': '<b>Modelo activo:</b>',
    '<b>BastankhahGaussianDeficit</b><br><br>Gaussian model by Bastankhah & Porté-Agel (2014). The radial deficit profile is a Gaussian bell whose width σ(x) grows linearly.<br><br><b>k</b> — Growth slope of σ. σ(x)/D = k·(x/D) + cεps. PyWake default = 0.0324, a value calibrated against LES in the original paper. Increasing it = a wake that widens and recovers sooner; decreasing it = a narrower, more persistent wake.<br><br><b>cεps</b> — Initial wake size next to the rotor (σ₀/D). PyWake default = 0.2. Rarely changed; it mainly affects the near wake.<br><br><i>Note:</i> in standard BG the ambient TI does NOT enter k. It is robust for benchmarking (e.g. IEA Task 37) but not very sensitive to the TI resource.': '<b>BastankhahGaussianDeficit</b><br><br>Modelo gaussiano de Bastankhah & Porté-Agel (2014). El perfil radial del déficit es una campana de Gauss cuyo ancho σ(x) crece linealmente.<br><br><b>k</b> — Pendiente del crecimiento de σ. σ(x)/D = k·(x/D) + cεps. Default PyWake = 0.0324, valor calibrado contra LES en el paper original. Subirlo = estela que se ensancha y recupera antes; bajarlo = estela más estrecha y persistente.<br><br><b>cεps</b> — Tamaño inicial de la estela junto al rotor (σ₀/D). Default PyWake = 0.2. Casi nunca se cambia; afecta sobre todo al near-wake.<br><br><i>Nota:</i> en BG estándar la TI ambiente NO entra en k. Es robusto para benchmarking (ej. IEA Task 37) pero poco sensible al recurso TI.',
    '<b>BastankhahGaussianDeficit</b><br><br>Gaussian model by Bastankhah & Porté-Agel (2014). The radial deficit profile is a Gaussian bell whose width σ(x) grows linearly.<br><br><b>k</b> — Slope of σ growth. σ(x)/D = k·(x/D) + cεps. PyWake default = 0.0324, a value calibrated against LES in the original paper. Increasing it = wake that widens and recovers earlier; decreasing it = narrower, more persistent wake.<br><br><b>cεps</b> — Initial wake size near the rotor (σ₀/D). PyWake default = 0.2. It is rarely changed; it mainly affects the near wake.<br><br><i>Note:</i> in standard BG the ambient TI does NOT enter k. It is robust for benchmarking (e.g. IEA Task 37) but not very sensitive to the TI resource.': '<b>BastankhahGaussianDeficit</b><br><br>Modelo gaussiano de Bastankhah & Porté-Agel (2014). El perfil radial del déficit es una campana de Gauss cuyo ancho σ(x) crece linealmente.<br><br><b>k</b> — Pendiente del crecimiento de σ. σ(x)/D = k·(x/D) + cεps. Default PyWake = 0.0324, valor calibrado contra LES en el paper original. Subirlo = estela que se ensancha y recupera antes; bajarlo = estela más estrecha y persistente.<br><br><b>cεps</b> — Tamaño inicial de la estela junto al rotor (σ₀/D). Default PyWake = 0.2. Casi nunca se cambia; afecta sobre todo al near-wake.<br><br><i>Nota:</i> en BG estándar la TI ambiente NO entra en k. Es robusto para benchmarking (ej. IEA Task 37) pero poco sensible al recurso TI.',
    '<b>Blockage disabled</b> because the engine is <b>PropagateDownwind</b> (it only propagates wakes downstream and does not support blockage). To include blockage, change the engine to <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b>.': '<b>Bloqueo desactivado</b> porque el motor es <b>PropagateDownwind</b> (solo propaga estelas hacia abajo, no soporta bloqueo). Si quieres incluir bloqueo, cambia el motor a <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b>.',
    '<b>NOJDeficit (Jensen)</b><br><br>Classic top-hat model: the wake is a cylinder that widens linearly with a single slope <b>k</b>.<br><br><b>k</b> — Expansion slope. <u>Physically</u> it represents how much the wake radius grows per metre downstream: r(x) = R + k·x. The larger k is, the faster the wake recovers and the smaller the far-field deficit.<br><br><b>Reference values</b><br>• Clean offshore: k ≈ 0.04 (PyWake default)<br>• Typical onshore: k ≈ 0.075<br>• Complex onshore / high TI: k ≈ 0.10<br><br><i>Note:</i> NOJ does not use ambient TI to compute k. If your site has high TI and you need that to show in AEP, use TurboNOJ or a TI-sensitive Gaussian model.': '<b>NOJDeficit (Jensen)</b><br><br>Modelo top-hat clásico: la estela es un cilindro que se ensancha linealmente con una sola pendiente <b>k</b>.<br><br><b>k</b> — Pendiente de expansión. <u>Físicamente</u> representa cuánto crece el radio de la estela por metro avanzado aguas abajo: r(x) = R + k·x. Cuanto mayor k, más rápido se recupera la estela y más pequeño el déficit lejano.<br><br><b>Valores de referencia</b><br>• Offshore limpio: k ≈ 0.04 (default PyWake)<br>• Onshore típico: k ≈ 0.075<br>• Onshore complejo / alta TI: k ≈ 0.10<br><br><i>Nota:</i> NOJ no usa la TI ambiente para calcular k. Si tu sitio tiene TI alta y necesitas que esto se note en el AEP, usa TurboNOJ o un modelo gaussiano TI-sensible.',
    '<b>NiayifarGaussianDeficit</b><br><br>Gaussian variant where the slope k is an explicit function of the <b>effective local TI</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>cεps</b> — Initial size σ₀/D. PyWake default = 0.2.<br><br>Changing the WRG ambient TI or the added-turbulence model is still the main AEP lever. The coefficients are exposed only as an advanced adjustment.': '<b>NiayifarGaussianDeficit</b><br><br>Variante gaussiana donde la pendiente k es función explícita de la <b>TI local efectiva</b> (Niayifar &amp; Porté-Agel 2016): k ≈ a₁·TI + a₂.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>cεps</b> — Tamaño inicial σ₀/D. Default PyWake = 0.2.<br><br>Cambiar la TI ambiente del WRG o el modelo de turbulencia añadida sigue siendo la palanca principal del AEP. Los coeficientes se exponen solo como ajuste avanzado.',
    '<b>No added turbulence:</b> the calculation uses ambient TI from the resource (WRG/TI raster or manual value), but it does not add turbulence created by wakes. This is valid for models with low TI sensitivity; if you use Niayifar, Zong, TurboGaussian or TurboNOJ, added turbulence can affect AEP more.': '<b>Sin turbulencia añadida:</b> el cálculo usa la TI ambiente del recurso (WRG/raster TI o valor manual), pero no suma la turbulencia creada por las estelas. Esto es válido para modelos poco sensibles a TI; si usas Niayifar, Zong, TurboGaussian o TurboNOJ, la turbulencia añadida puede afectar más al AEP.',
    '<b>No blockage:</b> the calculation will not include upstream wind-speed reduction. In large offshore wind farms this often underestimates losses by about ~1–3% AEP.': '<b>Sin bloqueo:</b> el cálculo no incluirá la reducción de velocidad upstream. En parques offshore grandes esto suele subestimar las pérdidas en ~1–3 % AEP.',
    '<b>Sector loss rose.</b> Distribution of free-flow and net AEP by wind direction. The difference between each pair of bars represents the wake losses in that sector — useful to see which directions limit the wind farm performance the most.': '<b>Rosa de pérdidas por sector.</b> Distribución del AEP libre y neto por dirección del viento. La diferencia entre cada barra representa las pérdidas por estela en ese sector — útil para ver qué direcciones limitan más el rendimiento del parque.',
    '<b>TurboGaussianDeficit</b><br><br>TI-driven version of the Gaussian family. Both k and the width near the rotor are derived from the effective TI.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI/cεps coefficients are exposed as an advanced adjustment for controlled calibration.': '<b>TurboGaussianDeficit</b><br><br>Versión TI-driven de la familia gaussiana. Tanto k como el ancho cerca del rotor se derivan de la TI efectiva.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI/cεps se exponen como ajuste avanzado para calibración controlada.',
    '<b>TurboNOJDeficit</b><br><br>TI-sensitive variant of the Jensen model: the slope k is computed from the <b>effective turbulence</b> at each turbine.<br><br>To move AEP, adjust the <b>ambient TI</b> or the <b>added-turbulence model</b>. The A/cTI coefficients are exposed as an advanced adjustment and are only passed if your PyWake accepts them.': '<b>TurboNOJDeficit</b><br><br>Variante TI-sensible del Jensen: la pendiente k se calcula a partir de la <b>turbulencia efectiva</b> en cada turbina.<br><br>Para mover el AEP, ajusta la <b>TI ambiente</b> o el <b>modelo de turbulencia añadida</b>. Los coeficientes A/cTI se exponen como ajuste avanzado y solo se pasan si tu PyWake los acepta.',
    '<b>What TI is</b><br><br>TI stands for <b>turbulence intensity</b>. In simple terms: it measures how much the wind fluctuates around its mean speed. More TI usually means more flow mixing and a different wake recovery.<br><br><b>Two different turbulences</b><br>• <b>Ambient TI</b>: comes from the wind resource or from a TI raster. It is the wind reaching the wind farm before the turbines.<br>• <b>Wake-added TI</b>: generated by each turbine behind it. It is computed by the PyWake turbulence model, if you enable it.<br><br><b>What the plugin does</b><br>• If you load a TI raster, it is used as spatial ambient turbulence.<br>• If you do not load a TI raster, the manual value in the interface is used as a uniform assumption.<br>• If you have several rasters at different heights, you can enter 90;120;... to interpolate by hub height.<br><br><b>When it matters most</b><br>BG/NOJ are robust but not very TI-sensitive. Niayifar, Zong, TurboGaussian and TurboNOJ make TI affect wake recovery and AEP more.<br><br><b>Simple idea</b><br>If you have no turbulence data, a uniform manual TI lets you run the calculation. For more realism onshore, in complex terrain or with several hub heights, use a TI raster, ideally by height.': '<b>Qué es TI</b><br><br>TI significa <b>turbulence intensity</b>. En simple: mide cuánto fluctúa el viento alrededor de su velocidad media. Más TI suele significar más mezcla del flujo y una recuperación de estela distinta.<br><br><b>Dos turbulencias diferentes</b><br>• <b>TI ambiente</b>: viene del recurso eólico o de un raster TI. Es el viento que llega al parque antes de las turbinas.<br>• <b>TI añadida por estela</b>: la genera cada turbina detrás de sí. La calcula el modelo de turbulencia de PyWake, si lo activas.<br><br><b>Qué hace el plugin</b><br>• Si cargas raster TI, se usa como turbulencia ambiente espacial.<br>• Si no cargas raster TI, se usa el valor manual de la interfaz como hipótesis uniforme.<br>• Si tienes varios raster a distintas alturas, puedes indicar 90;120;... para interpolar por altura de buje.<br><br><b>Cuándo importa más</b><br>BG/NOJ son robustos pero poco sensibles a TI. Niayifar, Zong, TurboGaussian y TurboNOJ hacen que la TI afecte más a la recuperación de estela y al AEP.<br><br><b>Idea sencilla</b><br>Si no tienes datos de turbulencia, una TI manual uniforme permite correr el cálculo. Para más realismo en onshore, terreno complejo o varias alturas de buje, usa raster TI, idealmente por altura.',
    '<b>What blockage is</b><br><br>Besides creating a wake behind it, a turbine can also slow the wind slightly before the flow reaches the rotor. That upstream effect is called <b>blockage</b>.<br><br><b>Compatibility</b><br>• It does not depend on the WRG/GRD resource: it depends on the PyWake engine and the blockage model.<br>• With <b>PropagateDownwind</b> it is disabled, because that engine only computes downstream effects.<br>• With <b>All2AllIterative</b> or <b>PropagateUpDownIterative</b> the plugin can try to include it with the available wake models.<br><br><b>Parameters</b><br>In this experimental release no internal blockage parameters are exposed. They are model-specific PyWake calibrations; to compare sensitivity, switch model instead of touching internal constants.<br><br><b>Which option to use</b><br>SelfSimilarityDeficit2020 is the main option. VortexCylinder/VortexDipole/HybridInduction are left as alternatives if your PyWake installation supports them.': '<b>Qué es el bloqueo</b><br><br>Además de crear una estela detrás, una turbina también puede frenar ligeramente el viento antes de que el flujo llegue al rotor. Ese efecto aguas arriba se llama <b>bloqueo</b> o <i>blockage</i>.<br><br><b>Compatibilidad</b><br>• No depende del recurso WRG/GRD: depende del motor de PyWake y del modelo de bloqueo.<br>• Con <b>PropagateDownwind</b> se desactiva, porque ese motor solo calcula efectos aguas abajo.<br>• Con <b>All2AllIterative</b> o <b>PropagateUpDownIterative</b> el plugin puede intentar incluirlo con los wake models disponibles.<br><br><b>Parámetros</b><br>En esta versión experimental no se exponen parámetros internos de bloqueo. Son calibraciones propias del modelo de PyWake; para comparar sensibilidad, cambia de modelo en lugar de tocar constantes internas.<br><br><b>Qué opción usar</b><br>SelfSimilarityDeficit2020 es la opción principal. VortexCylinder/VortexDipole/HybridInduction se dejan como alternativas si tu instalación de PyWake las soporta.',
    '<b>What it is</b><br><br>A turbine does not see exactly the same wind across the whole rotor. The top, bottom, left or right of the disk can receive different wind speeds, especially when a wake crosses only part of the rotor.<br><br><b>What this option does</b><br>• <b>No averaging / RotorCenter</b>: uses only the rotor centre. It is fast, but more simplified.<br>• <b>CGIRotorAvg(7)</b>: calculates several points inside the disk. It adds more realism at a moderate cost.<br>• <b>CGIRotorAvg(9/21)</b>: uses more points. It can add more fidelity, but increases computation time.<br><br><b>Simple idea</b><br>If a wake cuts only half the turbine, looking only at the centre can be too simplified. Averaging the rotor helps to represent that effect better.': '<b>Qué es</b><br><br>Una turbina no ve exactamente el mismo viento en todo el rotor. La parte alta, baja, izquierda o derecha del disco puede recibir velocidades distintas, sobre todo si una estela solo atraviesa una parte del rotor.<br><br><b>Qué hace esta opción</b><br>• <b>Sin promedio / RotorCenter</b>: usa solo el centro del rotor. Es rápido, pero más simplificado.<br>• <b>CGIRotorAvg(7)</b>: calcula varios puntos dentro del disco. Aporta más realismo con un coste moderado.<br>• <b>CGIRotorAvg(9/21)</b>: usa más puntos. Puede aportar más fidelidad, pero aumenta el tiempo de cálculo.<br><br><b>Idea sencilla</b><br>Si una estela corta solo media turbina, mirar únicamente el centro puede ser demasiado simplificado. Promediar el rotor ayuda a representar mejor ese efecto.',
    '<b>What problem it solves</b><br><br>A turbine can receive the wake of several turbines at the same time. Superposition decides how those wind-speed losses are combined before calculating power.<br><br>• <b>Automatic</b>: the plugin chooses a safe option based on the model combination.<br>• <b>LinearSum</b>: direct sum. It can accumulate large losses in dense wind farms.<br>• <b>SquaredSum</b>: quadratic sum. It is usually a balanced option when several wakes overlap.<br>• <b>MaxSum</b>: uses only the strongest wake. Useful for diagnostics, but it can underestimate accumulated losses.<br>• <b>WeightedSum</b>: advanced option for compatible Gaussian models.<br><br><b>Simple idea</b><br>If two turbines slow the wind before it reaches a third one, you must decide whether those losses add up strongly, weakly, or whether only one of them dominates.': '<b>Qué problema resuelve</b><br><br>Una turbina puede recibir la estela de varias turbinas al mismo tiempo. La superposición decide cómo se combinan esas pérdidas de velocidad antes de calcular la potencia.<br><br>• <b>Automático</b>: el plugin escoge una opción segura según la combinación de modelos.<br>• <b>LinearSum</b>: suma directa. Puede acumular muchas pérdidas en parques densos.<br>• <b>SquaredSum</b>: suma cuadrática. Suele ser una opción equilibrada cuando varias estelas se solapan.<br>• <b>MaxSum</b>: usa solo la estela más fuerte. Útil para diagnóstico, pero puede infravalorar pérdidas acumuladas.<br>• <b>WeightedSum</b>: opción avanzada para modelos gaussianos compatibles.<br><br><b>Idea sencilla</b><br>Si dos turbinas frenan el viento antes de llegar a una tercera, hay que decidir si esas pérdidas se suman mucho, poco o solo domina una de ellas.',
    "<b>What the wake effect is, in simple terms</b><br><br>When a turbine extracts energy from the wind, a zone with lower speed and more turbulence is left behind it. That zone is the <b>wake</b>. If another turbine falls inside it, it will produce less AEP.<br><br>The <b>wake deficit model</b> decides how much speed is lost and how the wake recovers downstream.<br><br><b>How to choose without being an expert</b><br>• <b>BastankhahGaussianDeficit</b>: robust option for comparing layouts and clean offshore wind farms.<br>• <b>NiayifarGaussianDeficit</b>: similar to Bastankhah, but TI turbulence weighs more in the wake expansion.<br>• <b>ZongGaussianDeficit</b>: more advanced; useful if you want a richer representation of the near wake and TI-driven recovery.<br>• <b>TurboGaussianDeficit</b>: Gaussian model sensitive to effective turbulence; interesting when you want ambient/added TI to influence AEP.<br>• <b>NOJDeficit</b>: very fast and classic; good for large-scale screening, but more simplified.<br>• <b>TurboNOJDeficit</b>: keeps NOJ's lightness, but lets TI have more influence.<br><br><b>Simple idea</b><br>For more realism when turbulence should influence wake recovery, use <b>Niayifar/Zong/TurboGaussian</b>. For a quick calculation, use <b>NOJ/TurboNOJ</b>. For a robust comparison baseline, use <b>Bastankhah</b>.<br><br>Important: loading a TI raster does not by itself guarantee a large change in AEP. The effect really shows if the chosen wake model uses TI explicitly.": '<b>Qué es el wake effect, en simple</b><br><br>Cuando una turbina extrae energía del viento, detrás de ella queda una zona con menos velocidad y más turbulencia. Esa zona es la <b>estela</b>. Si otra turbina cae dentro, producirá menos AEP.<br><br>El <b>wake deficit model</b> decide cuánta velocidad se pierde y cómo se recupera la estela aguas abajo.<br><br><b>Cómo elegir sin ser experto</b><br>• <b>BastankhahGaussianDeficit</b>: opción robusta para comparar layouts y parques offshore limpios.<br>• <b>NiayifarGaussianDeficit</b>: parecido a Bastankhah, pero la turbulencia TI pesa más en la expansión de la estela.<br>• <b>ZongGaussianDeficit</b>: más avanzado; útil si quieres una representación más rica del near wake y de la recuperación por TI.<br>• <b>TurboGaussianDeficit</b>: gaussiano sensible a turbulencia efectiva; interesante cuando quieres que la TI ambiente/añadida influya en el AEP.<br>• <b>NOJDeficit</b>: muy rápido y clásico; bueno para screening masivo, pero más simplificado.<br>• <b>TurboNOJDeficit</b>: mantiene la ligereza de NOJ, pero permite que la TI influya más.<br><br><b>Idea sencilla</b><br>Para más realismo cuando la turbulencia debe influir en la recuperación de estela, usa <b>Niayifar/Zong/TurboGaussian</b>. Para un cálculo rápido, usa <b>NOJ/TurboNOJ</b>. Para una base robusta de comparación, usa <b>Bastankhah</b>.<br><br>Importante: cargar un raster TI no garantiza por sí solo que cambie mucho el AEP. El efecto se ve de verdad si el modelo de estela elegido usa la TI de forma explícita.',
    "<b>What turbulence means in this plugin</b><br><br>• <b>TI raster / WRG</b>: describes the site's <b>ambient turbulence</b>. It is a resource input.<br>• <b>Turbulence model</b>: computes the <b>wake-added turbulence</b> behind the turbines.<br>• <b>Wake model</b>: decides whether that ambient/effective TI actually changes the deficit and therefore the AEP.<br><br><b>Physical interpretation</b><br>• If you provide only one TI raster, the plugin uses that horizontal TI for all turbines; if there are several hub heights, that is a <b>vertical approximation</b>.<br>• If you provide several TI raster(s) at different heights, the plugin tries to reconstruct <b>TI(x,y,h)</b> and interpolate by height.<br>• If you do not provide a TI raster in WRG, the plugin warns and uses the <b>user-defined ambient TI fallback</b> as a uniform field.<br><br><b>When it matters a lot for AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> and <b>NOJDeficit</b>: TI does not change the base wake deficit much; the impact on AEP is usually limited.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> and <b>TurboNOJDeficit</b>: ambient/effective TI can influence wake opening and recovery more.<br><br><b>What is advisable depending on the wind farm</b><br>• <b>Offshore with relatively homogeneous wind</b>: a single TI layer may be enough if hub heights are similar.<br>• <b>Onshore, rough terrain or different hub heights</b>: for more realism, use TI raster(s) and a TI-sensitive wake model (Niayifar, Zong, TurboGaussian or TurboNOJ).<br>• <b>Several turbine models</b>: if you mix hub heights, a single TI layer is an approximation; several TI heights better represent the vertical variation.<br><br><b>What to choose in the turbulence-model selector</b><br>• <b>STF2017</b>: good general option and the default recommendation when you need a robust added-turbulence model.<br>• <b>STF2005</b>: classic, simple alternative if you want to compare with older or more conservative configurations.<br>• <b>CrespoHernandez</b> and <b>GCLTurbulence</b>: useful for comparisons or compatibility with certain models/studies, but the final impact depends on the wake model you choose.<br><br><b>Rule of thumb</b><br>If your goal is for turbulence to physically influence AEP, selecting a turbulence model is not enough: you also need a <b>TI-sensitive wake model</b>.": '<b>Qué significa la turbulencia en este plugin</b><br><br>• <b>TI raster / WRG</b>: describe la <b>turbulencia ambiente</b> del site. Es un dato de entrada del recurso.<br>• <b>Modelo de turbulencia</b>: calcula la <b>turbulencia añadida por estela</b> detrás de las turbinas.<br>• <b>Modelo de estela</b>: decide si esa TI ambiente/efectiva cambia realmente el déficit y, por tanto, el AEP.<br><br><b>Cómo leerlo físicamente</b><br>• Si solo aportas un raster TI, el plugin usa esa TI horizontal para todas las turbinas; si hay varias alturas de buje, eso es una <b>aproximación vertical</b>.<br>• Si aportas varios raster(s) TI a distintas alturas, el plugin intenta reconstruir <b>TI(x,y,h)</b> e interpolar por altura.<br>• Si no aportas raster TI en WRG, el plugin avisa y usa la <b>TI ambiente fallback definida por el usuario</b> como campo uniforme.<br><br><b>Cuándo importa mucho en el AEP</b><br>• <b>BastankhahGaussianDeficit (BG)</b> y <b>NOJDeficit</b>: la TI no cambia mucho el déficit base del wake; el impacto sobre el AEP suele ser limitado.<br>• <b>NiayifarGaussianDeficit</b>, <b>ZongGaussianDeficit</b>, <b>TurboGaussianDeficit</b> y <b>TurboNOJDeficit</b>: la TI ambiente/efectiva sí puede influir más en la apertura y recuperación de la estela.<br><br><b>Qué conviene según el parque</b><br>• <b>Offshore con viento relativamente homogéneo</b>: una sola capa TI puede ser suficiente si las alturas de buje son parecidas.<br>• <b>Onshore, terreno rugoso o alturas de buje distintas</b>: para más realismo, usa raster(s) TI y un wake model sensible a TI (Niayifar, Zong, TurboGaussian o TurboNOJ).<br>• <b>Varios modelos de aerogenerador</b>: si mezclas alturas de buje, una sola capa TI es una aproximación; varias alturas de TI representan mejor la variación vertical.<br><br><b>Qué elegir en el selector de modelo de turbulencia</b><br>• <b>STF2017</b>: buena opción general y la recomendación por defecto cuando necesitas un modelo de turbulencia añadida robusto.<br>• <b>STF2005</b>: alternativa clásica y sencilla si quieres comparar con configuraciones más antiguas o más conservadoras.<br>• <b>CrespoHernandez</b> y <b>GCLTurbulence</b>: útiles para comparativas o compatibilidad con ciertos modelos/estudios, pero el impacto final depende del wake model que elijas.<br><br><b>Regla práctica</b><br>Si tu objetivo es que la turbulencia influya físicamente en el AEP, no basta con seleccionar un modelo de turbulencia: también necesitas un <b>modelo de estela sensible a TI</b>.',
    '<b>ZongGaussianDeficit</b><br><br>Advanced Gaussian model (Zong &amp; Porté-Agel 2020) with an explicit near wake and a slope k that depends on local TI.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>δw/D, ε coeff, λ and B</b> — advanced near-wake parameters. The plugin forces STF2017 if you leave it as None.': '<b>ZongGaussianDeficit</b><br><br>Modelo gaussiano avanzado (Zong &amp; Porté-Agel 2020) con near-wake explícito y pendiente k dependiente de TI local.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>δw/D, ε coeff, λ y B</b> — parámetros near-wake avanzados. El plugin fuerza STF2017 si lo dejas en Ninguno.',
    '<b>ZongGaussianDeficit</b><br><br>Advanced Gaussian model (Zong &amp; Porté-Agel 2020) with explicit near wake and a slope k dependent on local TI.<br><br><b>a₁/a₂</b> — Coefficients of k = a₁·TI + a₂. PyWake default = [0.38, 0.004].<br><b>δw/D, ε coeff, λ and B</b> — advanced near-wake parameters. The plugin forces STF2017 if you leave it as None.': '<b>ZongGaussianDeficit</b><br><br>Modelo gaussiano avanzado (Zong &amp; Porté-Agel 2020) con near-wake explícito y pendiente k dependiente de TI local.<br><br><b>a₁/a₂</b> — Coeficientes de k = a₁·TI + a₂. Default PyWake = [0.38, 0.004].<br><b>δw/D, ε coeff, λ y B</b> — parámetros near-wake avanzados. El plugin fuerza STF2017 si lo dejas en Ninguno.',
    '<br><b>Dominant-path heights:</b> turbine terrain=': '<br><b>Alturas trayectoria dominante:</b> terreno turbina=',
    '<br><b>Path with maximum Abar:</b> source=': '<br><b>Trayectoria con Abar máximo:</b> fuente=',
    '<br><small>Incremental diagnostics: positive = recovers AEP versus wake-only; negative = reduces AEP. It should not always be interpreted as an accountable loss.</small></li>\n          <li><b>Blockage:</b> ': '<br><small>Diagnóstico incremental: positivo = recupera AEP frente al wake-only; negativo = reduce AEP. No se debe interpretar siempre como pérdida contable.</small></li>\n          <li><b>Bloqueo:</b> ',
    "<br><span style='color:#a85a00;'>There are no turbines in this layer yet: left click on the map to add them.</span>": "<br><span style='color:#a85a00;'>Aún no hay turbinas en esta capa: haz click izq en el mapa para añadirlas.</span>",
    "<div class='card'>\n                    <h4>2.1 Spectrum used by the source group:": "<div class='card'>\n                    <h4>2.1 Espectro usado por el grupo fuente:",
    '<h2>Shadow Flicker Calculation Summary</h2>': '<h2>Resumen del cálculo de shadow flicker</h2>',
    '<h4>Physical and compatibility notes</h4><ul>': '<h4>Notas físicas y de compatibilidad</h4><ul>',
    '<i>(value edited by user)</i>': '<i>(valor editado por usuario)</i>',
    '<i>Detailed values for each receptor. Columns are filled before sorting is enabled to avoid blank cells after sorting.</i>': '<i>Valores detallados por receptor. Las columnas se rellenan antes de habilitar la ordenación para evitar celdas vacías al ordenar.</i>',
    '<i>No specific information for this model.</i>': '<i>Sin información específica para este modelo.</i>',
    '<i>This model does not expose physical parameters manually. Press the ℹ️ button to see why.</i>': '<i>Este modelo no expone parámetros físicos manualmente. Pulsa el botón ℹ️ para ver por qué.</i>',
    '<li><b>Atmospheric α:</b>': '<li><b>α atmosférico:</b>',
    "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Blockage-model substitution:</b>": "<p style='background:#e6f4ea;border:1px solid #7fc89a;padding:8px;'><b>Sustitución de modelo de bloqueo:</b>",
    "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Ambient TI warning:</b> no TI raster has been provided. The calculation used <b>TI = ": "<p style='background:#fff8e6;border:1px solid #e0c36d;border-radius:6px;padding:8px;'><b>Aviso de TI ambiente:</b> no se ha proporcionado un raster de TI. El cálculo ha usado <b>TI = ",
    "<p style='color:#888; font-style:italic;'><small>Additional metrics not available: the rated power of the turbines could not be determined. Define the model again from “Define…” so that the power curve is stored with the metadata.</small></p>": "<p style='color:#888; font-style:italic;'><small>Métricas adicionales no disponibles: no se pudo determinar la potencia nominal de las turbinas. Vuelve a definir el modelo desde «Definir…» para que la curva de potencia se guarde con la metadata.</small></p>",
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: Abar=0 at the critical receiver does not imply that the DTM is disabled; it means that ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: Abar=0 en el receptor crítico no implica que el MDT esté desactivado; significa que ",
    "<p style='margin:8px 0 10px 0;color:#495057;'><i>DTM reading: estimated dominant obstacle ": "<p style='margin:8px 0 10px 0;color:#495057;'><i>Lectura MDT: obstáculo dominante estimado ",
    '<p><b>Interpretation:</b> in this group no visible internal template was used; the final spectrum <code>Lw,b</code> comes directly from the loaded/imported spectrum or from an external library.</p>': '<p><b>Interpretación:</b> en este grupo no se ha usado una plantilla interna visible; el espectro final <code>Lw,b</code> procede directamente del espectro cargado/importado o de una biblioteca externa.</p>',
    '<p><b>Interpretation:</b> the final spectrum <code>Lw,b</code> is the one that actually enters the band-wise equation. If <code>S_b^ref</code> exists, it corresponds to the reference template before the global adjustment <code>Δ</code>.</p>': '<p><b>Interpretación:</b> el espectro final <code>Lw,b</code> es el que entra realmente en la ecuación por bandas. Si existe <code>S_b^ref</code>, corresponde a la plantilla de referencia antes del ajuste global <code>Δ</code>.</p>',
    "<span style='color:#a85a00;'><b>No active layer</b></span> of turbines “<i>Model (CSV)</i>”.<br>Select it in the layers panel, or use “Generate point layers” in the dialog.": "<span style='color:#a85a00;'><b>Sin capa activa</b></span> de turbinas «<i>Modelo (CSV)</i>».<br>Selecciónala en el panel de capas, o usa «Generar capas de puntos» en el diálogo.",
    'A TurbOPark expansion': 'A expansión TurbOPark',
    'A TurboNOJ expansion': 'A expansión TurboNOJ',
    'A TurboPark expansion': 'A expansión TurboPark',
    'A shadow-flicker calculation is already running.': 'Ya hay un cálculo de shadow flicker en ejecución.',
    'AEP Results – AEP calculation (PyWake)': 'Resultados AEP – Cálculo AEP (PyWake)',
    'AEP by direction [MWh]': 'AEP por dirección [MWh]',
    'AEP calculation': 'Cálculo AEP',
    'AEP calculation with PyWake: define turbine models, select the wind resource (WAsP/Surfer grids or WRG), choose the physical models (wake, turbulence, blockage, rotor-average) and run the calculation. Results are automatically written to the project coordinate layers.': 'Cálculo de AEP con PyWake: define los modelos de aerogenerador, selecciona el recurso eólico (WAsP/Surfer grids o WRG), elige los modelos físicos (estela, turbulencia, bloqueo, rotor-average) y lanza el cálculo. Los resultados se vuelcan automáticamente sobre las capas de coordenadas del proyecto.',
    'AEP report exported successfully:': 'Informe AEP exportado correctamente:',
    'AEP results': 'Resultados AEP',
    'AEP scenario comparison': 'Comparador de escenarios AEP',
    'AEP technical report': 'Informe técnico AEP',
    'AEP validation': 'Validación AEP',
    'AEP with wakes:': 'AEP con estelas:',
    'AEP, losses and global capacity': 'AEP, pérdidas y capacidad global',
    'AEP, losses and overall capacity': 'AEP, pérdidas y capacidad global',
    'AEP/MW [MWh/MW·year]': 'AEP/MW [MWh/MW·año]',
    'Aatm (atmospheric)': 'Aatm (atmosférica)',
    'Aatm loss dB': 'pérdida Aatm dB',
    'Abar (screening)': 'Abar (apantallamiento)',
    'Abar loss dB': 'pérdida Abar dB',
    'Accurate · CGIRotorAvg(9)': 'Más realista · CGIRotorAvg(9)',
    'Accurate: CGIRotorAvg(9) slightly increases fidelity and computation time.': 'Accurate: CGIRotorAvg(9) aumenta algo la fidelidad y el coste computacional.',
    'Acoustic calculation completed.': 'Cálculo acústico completado.',
    'Acoustic calculation completed in the background.\n\nThe receiver/source layers, and the raster if it was enabled, have already been added to the project.': 'Cálculo acústico completado en segundo plano.\n\nLas capas de receptores/fuentes y el raster, si estaba activado, ya se han añadido al proyecto.',
    'Acoustic calculation completed.\n\nThe receiver/source layers have already been added to the project. If the raster was enabled and does not use land use, it is being generated in the background when it is safe to do so.': 'Cálculo acústico completado.\n\nLas capas de receptores/fuentes ya se han añadido al proyecto. Si el raster estaba activado y no usa uso del suelo, se está generando en segundo plano cuando es seguro hacerlo.',
    'Noise module: connected to the project layout and WT models, with per-receiver calculation, a per-model emission table and layer outputs. The current physics follows a simplified source-receiver approach with documented limitations for technical validation.': 'Módulo de ruido: conectado al layout y modelos WT del proyecto, con cálculo sobre receptores, tabla de emisión por modelo y salidas en capa. La física actual sigue un enfoque fuente-receptor propio simplificado y documentación de limitaciones para validación técnica.',
    'Noise module: wind screening engine with receivers, source groups, ISO-aligned octave-band propagation, raster maps and GIS outputs.': 'Módulo de ruido: motor de screening eólico con receptores, grupos fuente, propagación ISO-aligned por bandas, mapas raster y salidas GIS.',
    'Acoustic scenario': 'Escenario acústico',
    'Acoustic scenario:': 'Escenario acústico:',
    'Acoustic source groups:': 'Grupos fuente acústicos:',
    'Acoustic source-group configuration': 'Configuración de grupos fuente acústicos',
    'Actions': 'Acciones',
    'Activate a turbine layer first': 'Activa primero una capa de turbinas',
    'Active coefficient:': 'Coeficiente activo:',
    'Active layout:': 'Layout activo:',
    'Active model:': 'Modelo activo:',
    'Active parameters:': 'Parámetros activos:',
    'Active terms': 'Términos activos',
    'Add receiver layer': 'Añadir capa receptores',
    'Add turbines to the Model layer': 'Añade turbinas en la capa del Modelo',
    'Added turbulence model': 'Modelo de turbulencia añadida',
    'Adding result layers to QGIS…': 'Añadiendo capas de resultados a QGIS…',
    'Additional metrics': 'Métricas adicionales',
    'Additional topographic diffraction attenuation': 'Atenuación adicional por difracción topográfica',
    'Adiv (divergence)': 'Adiv (divergencia)',
    'Adiv loss dB': 'pérdida Adiv dB',
    'Adiv represents the geometrical divergence, Aatm the simplified atmospheric attenuation α·distance and Aground a simplified correction of the ground/terrain effect.': 'Adiv representa la divergencia geométrica, Aatm la atenuación atmosférica simplificada α·distancia y Aground una corrección simplificada del efecto suelo/terreno.',
    'Adiv represents the geometrical divergence. Aatm is computed per band and depends on T, RH and pressure, with a simplified formulation. Agr is applied as a ground/terrain term and Abar as basic topographic screening when there is a DTM.': 'Adiv representa la divergencia geométrica. Aatm se calcula por banda y depende de T, HR y presión, con formulación simplificada. Agr se aplica como término de suelo/terreno y Abar como apantallamiento topográfico básico cuando hay MDT.',
    'Advanced model parameters (optional · keep defaults unless calibrating)': 'Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)',
    'Advanced model parameters (optional · leave unchanged for normal use)': 'Parámetros avanzados del modelo (opcional · dejar por defecto salvo calibración)',
    'Advanced model parameters (optional)': 'Parámetros avanzados del modelo (opcional)',
    'Advanced validation not available; the compatibility workflow is used:': 'Validación avanzada no disponible; se usa flujo de compatibilidad:',
    'Agr (ground)': 'Agr (suelo)',
    'Agr/Aground loss dB': 'pérdida Agr/Aground dB',
    'Air absorption': 'Absorción en el aire',
    'All': 'Todos',
    'All Receptors (sum)': 'Todos los receptores (suma)',
    'All files': 'Todos los archivos',
    'All turbines are outside the site domain (': 'Todas las turbinas están fuera del dominio del site (',
    'All turbines in the CSV are outside the WAsP grid area. Check units (m vs km) and reprojection.': 'Todas las turbinas del CSV están fuera del área del grid WAsP. Revisa unidades (m vs km) y reproyección.',
    'Ambient TI always comes from the resource/raster or the manual value. This selector adds wake-generated turbulence. Its AEP impact will be larger if the wake model uses TI explicitly, such as Niayifar, Zong, TurboGaussian or TurboNOJ.': 'La TI ambiente siempre viene del recurso/raster o del valor manual. Este selector añade turbulencia generada por estelas. Su impacto en AEP será mayor si el wake model usa TI de forma explícita, como Niayifar, Zong, TurboGaussian o TurboNOJ.',
    'Ambient TI fallback:': 'TI ambiente manual si no hay raster:',
    'Ambient TI warning:': 'Aviso de TI ambiente:',
    'Ambient turbulence value used if no TI raster is loaded. Example: 10% means TI = 0.10 inside the PyWake Site.': 'Valor de turbulencia ambiente que se usa si no cargas un raster TI. Ejemplo: 10 % significa TI = 0.10 dentro del Site de PyWake.',
    'An error occurred:': 'Ocurrió un error:',
    'Analysis Summary': 'Resumen del análisis',
    'Analysis coverage': 'Cobertura de Análisis',
    'Analysis year:': 'Año de análisis:',
    'Applicable limit': 'Límite aplicable',
    'Applied filter:': 'Filtro aplicado:',
    'Applied limits': 'Límites aplicados',
    'Approximate auto-detection without world polygons; check the time zone in the IANA dropdown.': 'Autodetección aproximada sin polígonos mundiales; verifica la zona horaria en el desplegable IANA.',
    'April': 'Abril',
    'Assessment of wind turbine noise': 'Evaluación de ruido generado por aerogeneradores',
    'Atmospheric pressure': 'Presión atmosférica',
    'Atmospheric pressure:': 'Presión atmosférica:',
    'Attenuation breakdown': 'Desglose de Atenuaciones',
    'Attenuation statistics': 'Estadísticos de Atenuaciones',
    'Attenuation statistics (covered receivers)': 'Estadísticos de Atenuaciones (receptores cubiertos)',
    'Attenuations': 'Atenuaciones',
    'August': 'Agosto',
    'Auto recommended': 'Automático',
    'Auto-detect coordinates and time zone': 'Detectar coordenadas y zona horaria automáticamente',
    'Automatic': 'Automático',
    'Automatic: the plugin chooses a safe option based on the available model combination.': 'Automático: el plugin escoge una opción segura según la combinación de modelos disponible.',
    'B number of blades': 'B número de palas',
    'Back to dialog': 'Volver al diálogo',
    'Back to hub': 'Volver al hub',
    'Balanced · CGIRotorAvg(7)': 'Más realista · CGIRotorAvg(7)',
    'Balanced: CGIRotorAvg(7) is the recommended value for normal AEP calculations.': 'Balanced: CGIRotorAvg(7) es el valor recomendado para cálculo AEP normal.',
    'Base model': 'Modelo base',
    'Bastankhah: robust option for comparing layouts. It uses a Gaussian formulation with fixed parameters; ambient TI does not change the base deficit much.': 'Bastankhah: opción robusta para comparar layouts. Usa una formulación gaussiana con parámetros fijos; la TI ambiente no cambia mucho el déficit base.',
    'BastankhahGaussianDeficit uses a fixed k: ambient TI and the turbulence model barely change the base wake deficit.': 'BastankhahGaussianDeficit usa una k fija: la TI ambiente y el modelo de turbulencia apenas cambian el déficit base del wake.',
    'Experimental - Adiv ✓, Aatm ✓ (simplified), Agr ✓ (simplified), Abar ✓ (basic DTM/DSM)': 'Experimental - Adiv ✓, Aatm ✓ (simplificado), Agr ✓ (simplificado), Abar ✓ (MDT/DSM básico)',
    'Beyond sponsoring the project, VelantisWind can be adapted to existing workflows: internal data formats, reporting templates, validation processes, team deployment, training, automation or specific features for wind-energy projects.': 'Además de patrocinar el proyecto, VelantisWind puede adaptarse a flujos de trabajo existentes: formatos internos de datos, plantillas de informe, procesos de validación, instalación en equipos, formación, automatización o funcionalidades específicas para proyectos eólicos.',
    'Blockage ONLY enters the calculation': 'El bloqueo SOLO entra en el cálculo',
    'Blockage disabled': 'Bloqueo desactivado',
    'Blockage is the upstream velocity reduction at the rotor. It is only applied with A2A or PUD engines.': 'El bloqueo es la reducción de velocidad upstream del rotor. Solo se aplica con motores A2A o PUD.',
    'Blockage loss [MWh]': 'Pérd. bloqueo [MWh]',
    'Blockage losses': 'Pérdidas bloqueo',
    'Blockage losses:': 'Pérdidas por bloqueo:',
    'Blockage model': 'Modelo de bloqueo',
    'Blockage model (PyWake)': 'Modelo de bloqueo (PyWake)',
    'Blockage represents wind-speed reduction upstream of the rotor. It is only applied with All2AllIterative or PropagateUpDownIterative engines.': 'El bloqueo representa la reducción de velocidad aguas arriba del rotor. Solo se aplica con motores All2AllIterative o PropagateUpDownIterative.',
    'Blockage-model substitution: the selected combination with': 'Sustitución del modelo de bloqueo: la combinación seleccionada con',
    'Browse…': 'Examinar…',
    'Bug fixes and compatibility with new QGIS versions.': 'Corrección de errores y compatibilidad con nuevas versiones de QGIS.',
    'BuildVRT returned None; check the paths': 'BuildVRT devolvió None; revisa los paths',
    'By Receptor': 'Por receptor',
    'By model': 'Por modelo',
    'By sector': 'Por sector',
    'By turbine': 'Por aerogenerador',
    'By turbine model': 'Por modelo de aerogenerador',
    'CALCULATION CONFIGURATION': 'CONFIGURACIÓN DEL CÁLCULO',
    'CGIRotorAvg(21): uses many rotor points. It is heavier and mainly useful for higher-fidelity sensitivity checks.': 'CGIRotorAvg(21): usa muchos puntos del rotor. Es más pesado y solo compensa si buscas una sensibilidad de mayor fidelidad.',
    'CGIRotorAvg(7): calculates several points inside the rotor. It adds more realism for partial wakes without increasing computation too much.': 'CGIRotorAvg(7): calcula varios puntos dentro del rotor. Aporta más realismo cuando hay estelas parciales sin aumentar demasiado el coste.',
    'CGIRotorAvg(9): uses more points than CGI7. It can add some extra realism if computation time remains acceptable.': 'CGIRotorAvg(9): usa más puntos que CGI7. Puede aportar algo más de realismo si el tiempo de cálculo sigue siendo asumible.',
    'CONFIGURATION AND PARAMETERS': 'CONFIGURACIÓN Y PARÁMETROS',
    'CRITICAL RECEIVER': 'RECEPTOR CRÍTICO',
    'CRS unavailable': 'CRS no disponible',
    'CSV exported': 'CSV exportado',
    'Calculate AEP': 'Calcular AEP',
    'Calculate AEP with the active turbine layer and the selected models.': 'Calcula AEP con la capa de turbinas activa y los modelos seleccionados.',
    'Calculate AEP · Models, coordinates and wind resource': 'Calcular AEP · Modelos, coordenadas y recurso eólico',
    'Calculate Shadow Flicker': 'Calcular shadow flicker',
    'Calculate noise': 'Calcular ruido',
    'Calculate with selected layers': 'Calcular con capas seleccionadas',
    'Calculated points:': 'Puntos calculados:',
    'Calculating AEP': 'Calculando AEP',
    'Calculating noise and raster…': 'Calculando ruido y raster…',
    'Calculating noise in the background…': 'Calculando ruido en segundo plano…',
    'Calculating noise in the background… 0%': 'Calculando ruido en segundo plano… 0%',
    'Calculating per-model / cluster breakdown…': 'Calculando desglose por modelo / cluster…',
    'Calculating receivers and summary…': 'Calculando receptores y resumen…',
    'Calculating shadow flicker...': 'Calculando shadow flicker...',
    'Calculation Complete': 'Cálculo completado',
    'Calculation cancelled by the user': 'Cálculo cancelado por el usuario',
    'Calculation completed successfully!': '¡Cálculo completado correctamente!',
    'Calculation completed.': 'Cálculo completado.',
    'Calculation engine:': 'Motor de cálculo:',
    'Calculation in progress': 'Cálculo en curso',
    'Calculation inputs': 'Entradas del cálculo',
    'Calculation methodology': 'Metodología de Cálculo',
    'Calculation parameters': 'Parámetros del cálculo',
    'Calculation preparation': 'Preparación del cálculo',
    'Calculation time resolution': 'Resolución temporal del cálculo',
    'Calculation time resolution\n5 min = optimal speed/accuracy balance\n1 min = maximum accuracy (very slow)': 'Resolución temporal del cálculo\n5 min = equilibrio óptimo velocidad/precisión\n1 min = máxima precisión (muy lento)',
    'Calculation with': 'Cálculo con',
    'Calibrated against LES': 'Calibrado contra LES',
    'Canada has several zones; check manually.': 'Canadá tiene varias zonas; verifica manualmente.',
    'Cancel': 'Cancelar',
    'Cancelled': 'Cancelado',
    'Cannot start the calculation': 'No se puede lanzar el cálculo',
    'Cannot start the shadow-flicker calculation:': 'No se puede iniciar el cálculo de shadow flicker:',
    'Capacity factor (net):': 'Factor de capacidad (neto):',
    'Changing them breaks the model traceability/citability': 'Cambiarlos rompe la citabilidad del modelo',
    'Check configuration': 'Comprobar configuración',
    'Check the CSV for Model': 'Revisa el CSV del Modelo',
    'Check the attribute table for detailed monthly breakdown.': 'Consulta la tabla de atributos para el desglose mensual detallado.',
    'China uses one official zone; check whether the project uses another reference.': 'China usa una zona oficial; verifica si el proyecto usa otra referencia.',
    'Choose TI raster(s)...': 'Elegir raster(s) TI...',
    'Choose TI raster(s)…': 'Elegir raster(s) TI…',
    'Choose WRG/ZIP...': 'Elegir WRG/ZIP...',
    'Choose WRG/ZIP…': 'Elegir WRG/ZIP…',
    'Choose folder...': 'Elegir carpeta...',
    'Choose folder…': 'Elegir carpeta...',
    'Choose which layer/model you edit with the left/right click on the map. This lets you modify any of the loaded layers, not only the last one created.': 'Elige qué capa/modelo editas con el click izquierdo/derecho del mapa. Esto permite modificar cualquiera de las capas cargadas, no solo la última creada.',
    'Clear': 'Limpiar',
    'Clear all': 'Desmarcar todas',
    'Clear boundary': 'Limpiar perímetro',
    'Click the logo to return home from the prepared modules.': 'Pulsa el logo para volver al inicio desde los módulos preparados.',
    'Close': 'Cerrar',
    'Close the energy module and return to the Velantis Wind main hub without deleting project layers or loaded results.': 'Cierra el módulo de energía y vuelve al hub principal de Velantis Wind sin borrar las capas ni los resultados cargados en el proyecto.',
    'Close this window and return to the main dialog without creating the layer.': 'Cerrar esta ventana y volver al diálogo principal sin crear la capa.',
    'Coefficient that converts local/effective TI into wake expansion.': 'Coeficiente que convierte la TI local/efectiva en expansión de estela.',
    'Combination of multiple wakes (superposition)': 'Combinación de varias estelas (superposición)',
    'Comma': 'Coma',
    'Company logo on README and VelantisWind website.': 'Logo de empresa en el README y en la web de VelantisWind.',
    'Compare A/B': 'Comparar A/B',
    'Compare scenarios': 'Comparar escenarios',
    'Compare the two saved scenarios and show global and per-model deltas.': 'Compara los dos escenarios guardados y muestra delta global y por modelo.',
    'Compares the two saved scenarios and shows global and per-model deltas.': 'Compara los dos escenarios guardados y muestra delta global y por modelo.',
    'Comparison with summary:': 'Comparación con resumen:',
    'Completed successfully': 'Completado correctamente',
    'Computes latitude/longitude from the layout centroid and tries to detect the IANA time zone': 'Calcula latitud/longitud desde el centroide del layout e intenta detectar la zona horaria IANA',
    'Configuration': 'Configuración',
    'Configuration finally executed': 'Configuración finalmente ejecutada',
    'Configuration requested from solver': 'Configuración solicitada al solver',
    'Configure at least one valid layer in the receiver-category table.': 'Configura al menos una capa válida en la tabla de receptores por categoría.',
    'Configure the geometric parameters for each turbine model.': 'Configura los parámetros geométricos de cada modelo de turbina.',
    'Configure the geometric parameters for each turbine model.\nHub Height: hub height above ground level [m]\nRotor Diameter: rotor diameter [m]': 'Configura los parámetros geométricos de cada modelo de turbina.\nHub Height: altura de buje sobre el terreno [m]\nRotor Diameter: diámetro de rotor [m]',
    'Constant ambient TI used when no TI raster is available.': 'TI ambiente constante usada cuando no hay raster TI disponible.',
    'Continue?': '¿Continuar?',
    'Contributing turbines within radius': 'Turbinas contribuyentes dentro del radio',
    'Coordinates': 'Coordenadas',
    'Coordinates (CSV X,Y):': 'Coordenadas (CSV X,Y):',
    'Coordinates detected': 'Coordenadas detectadas',
    'Coordinates out of range.': 'Coordenadas fuera de rango.',
    'Corrupt coordinate CSV for «': 'CSV de coordenadas corrupto para «',
    'Could not access the QGIS map canvas.': 'No se pudo obtener el map canvas de QGIS.',
    'Could not activate interactive mode:': 'No se pudo activar el modo interactivo:',
    'Could not activate the selected layer:': 'No se pudo activar la capa seleccionada:',
    'Could not add turbine:': 'No se pudo añadir turbina:',
    'Could not apply the template:': 'No se pudo aplicar la plantilla:',
    'Could not calculate noise': 'No se pudo calcular el ruido',
    'Could not calculate noise in the background:': 'No se pudo calcular el ruido en segundo plano:',
    'Could not calculate noise:': 'No se pudo calcular el ruido:',
    'Could not clear the resource boundary:': 'No se pudo limpiar el perímetro del recurso:',
    'Could not complete the calculation:': 'No se pudo completar el cálculo:',
    'Could not create raster map:': 'No se pudo crear el mapa raster:',
    'Could not create the model layer': 'No se pudo crear la capa del modelo',
    'Could not create the raster file.': 'No se pudo crear el archivo raster.',
    'Could not create the summary layer:': 'No se pudo crear la capa resumen:',
    'Could not create the temporary resource-boundary layer.': 'No se pudo crear la capa temporal de perímetro del recurso.',
    'Could not delete turbine:': 'No se pudo borrar turbina:',
    'Could not detect a reliable time zone without a world polygon database. Select the IANA zone manually in the dropdown; the repo includes a broad catalogue and a TZif database with DST rules.': 'No se pudo detectar una zona horaria fiable sin una base mundial de polígonos. Selecciona manualmente la zona IANA en el desplegable; la repo incluye un catálogo amplio y una base TZif con reglas DST.',
    'Could not display the visual noise summary': 'No se pudo mostrar el resumen visual de ruido',
    'Could not display the visual noise summary:': 'No se pudo mostrar el resumen visual de ruido:',
    'Could not display the visual results summary:': 'No se pudo mostrar el resumen visual de resultados:',
    'Could not draw the resource boundary:': 'No se pudo dibujar el perímetro del recurso:',
    'Could not export': 'No se pudo exportar',
    'Could not export the AEP report:': 'No se pudo exportar el informe AEP:',
    'Could not export the CSV': 'No se pudo exportar el CSV',
    'Could not export the CSV:': 'No se pudo exportar el CSV:',
    'Could not export the DTM diagnostics CSV:': 'No se pudo exportar el CSV de diagnóstico MDT:',
    'Could not export the XLSX': 'No se pudo exportar el XLSX',
    'Could not export the XLSX:': 'No se pudo exportar el XLSX:',
    'Could not export the per-turbine CSV:': 'No se pudo exportar el CSV por turbina:',
    'Could not export the report': 'No se pudo exportar el informe',
    'Could not export the summary': 'No se pudo exportar el resumen',
    'Could not export the summary:': 'No se pudo exportar el resumen:',
    "Could not find a valid PyWake subfolder in '": "No se ha podido encontrar una subcarpeta válida para PyWake en '",
    'Could not generate the point layer:': 'No se pudo generar la capa de puntos:',
    'Could not generate the raster:': 'No se pudo generar el raster:',
    'Could not generate the sector rose:': 'No se pudo generar la rosa de sectores:',
    'Could not generate the total chart:': 'No se pudo generar el gráfico total:',
    "Could not import 'graficar.py':": "No se pudo importar 'graficar.py':",
    'Could not import load_wrg_site. Check that ag_core/wrg_site.py exists.': 'No se pudo importar load_wrg_site. Revisa que exista ag_core/wrg_site.py.',
    'Could not invert the DTM/DSM geotransform.': 'No se pudo invertir la geotransformación del MDT/DSM.',
    "Could not load the IANA time zone '{}'. The repo bundles a local IANA database, but that zone was not found or could not be opened. Try a zone like 'Europe/Madrid' or use the fixed UTC offset mode. zoneinfo error: {}; bundled database error: {}": "No se pudo cargar la zona horaria IANA '{}'. La repo incluye una base IANA local, pero esa zona no se encontró o no pudo abrirse. Prueba con una zona como 'Europe/Madrid' o usa el modo UTC offset fijo. Error zoneinfo: {}; error base incluida: {}",
    'Could not load the filtered TIF': 'No se pudo cargar el TIF filtrado',
    'Could not load the generated raster:': 'No se pudo cargar el raster generado:',
    'Could not load the interactive map tool (import failed).': 'No se pudo cargar la herramienta de mapa interactivo (import fallido).',
    'Could not open the comparison:': 'No se pudo abrir la comparación:',
    'Could not open the energy module.': 'No se pudo abrir el módulo de energía.',
    'Could not open the turbine dialog:': 'No se pudo abrir el diálogo de turbina:',
    'Could not prepare the background calculation:': 'No se pudo preparar el cálculo en segundo plano:',
    'Could not prepare the layer selection:': 'No se pudo preparar la selección de capas:',
    'Could not read a valid extent from the WRG/ZIP.': 'No se pudo leer una extensión válida desde el WRG/ZIP.',
    'Could not read the acoustic curve': 'No se pudo leer la curva acústica',
    'Could not read the spatial extent of the selected grids.': 'No se pudo leer la extensión espacial de los grids seleccionados.',
    'Could not read valid data from': 'No se pudieron leer datos válidos de',
    'Could not read valid ws/LwA pairs from': 'No se pudieron leer pares ws/LwA válidos de',
    'Could not resolve the target WRG/project CRS for the TI raster': 'No se pudo resolver el CRS destino del WRG/proyecto para el raster TI',
    'Could not start the background calculation:': 'No se pudo lanzar el cálculo en segundo plano:',
    'Could not use the layer selection; the normal workflow will be used:': 'No se pudo usar la selección de capas; se usará el flujo normal:',
    'Covered receivers': 'Receptores Cubiertos',
    'Create shadow flicker raster map': 'Crear mapa raster de shadow flicker',
    'Create turbine (Manual)': 'Crear turbina (Manual)',
    'Create turbine (TXT/CSV)': 'Crear turbina (TXT/CSV)',
    'Create/update the point layer for this model': 'Crear/actualizar la capa de puntos para este modelo',
    'Created layers': 'Capas creadas',
    'Creates a new TIF using the selected month/hour filter': 'Crea un nuevo TIF usando el filtro de mes/hora seleccionado',
    'Creates a per-model summary layer and lets you save a per-turbine AEP CSV.': 'Crea una capa de resumen por modelo y permite guardar un CSV con AEP por turbina.',
    'Creating raster map': 'Creando mapa raster',
    'CrespoHernandez — Crespo &amp; Hernández 1996, classic empirical fit. Fixed constants from the paper.': 'CrespoHernandez — Crespo &amp; Hernández 1996, ajuste empírico clásico. Constantes fijas del paper.',
    'Critical Receiver (Highest Sound Level)': 'Receptor crítico (mayor nivel sonoro)',
    'Critical interaction with the engine': 'Interacción crítica con el motor',
    'Critical receiver': 'Receptor crítico',
    'Critical receiver (highest sound level)': 'Receptor crítico (mayor nivel sonoro)',
    'Critical receiver unavailable.': 'Critical receiver no disponible.',
    'Critical-receiver G_eff': 'G_eff receptor crítico',
    'Critical-receiver G_eff used': 'G_eff critical receiver usado',
    'Critical-receiver reading': 'Lectura del receptor crítico',
    'DEM raster (optional):': 'Raster DEM/MDT (opcional):',
    'DEM/DSM was used to sample terrain elevation at sources and receivers.': 'DEM/DSM usado para muestrear elevación del terreno en fuentes y receptores.',
    'DTM / DSM (optional):': 'MDT / DSM (opcional):',
    'DTM attenuation on the dominant path': 'Atenuación por MDT en la trayectoria dominante',
    'Day limit': 'Límite día',
    'Daytime': 'Diurno',
    'December': 'Diciembre',
    'Dedicated time for real-case validation and plugin stability improvements.': 'Tiempo dedicado a validar casos reales y mejorar la estabilidad del plugin.',
    'Default PyWake': 'Valor PyWake por defecto',
    'Define all models before continuing.': 'Define todos los modelos antes de continuar.',
    'Define turbine': 'Definir turbina',
    'Define…': 'Definir…',
    'Delimiter:': 'Delimitador:',
    'Delta summary': 'Resumen delta',
    'Description': 'Descripción',
    'Detailed values for each receptor.': 'Valores detallados por receptor.',
    'Detected WT models:': 'Modelos WT detectados:',
    'Detected time zone:': 'Zona horaria detectada:',
    'Direction': 'Dirección',
    'Direction [°]': 'Dirección [°]',
    'Distance': 'Distancia',
    'Distribution of free-flow and net AEP by wind direction.': 'Distribución del AEP libre y neto por dirección del viento.',
    'Documentation, tutorials, examples and test datasets.': 'Documentación, tutoriales, ejemplos y datasets de prueba.',
    'Dominant band': 'Banda dominante',
    'Dominant model': 'Modelo dominante',
    'Dominant source LwA': 'LwA fuente dominante',
    'Draws 4 wake maps (N/E/S/W) using the selected models and resource (WAsP folder or WRG/ZIP).': 'Dibuja 4 mapas de estelas (N/E/S/O) usando los modelos y el recurso seleccionado (carpeta WAsP o WRG/ZIP).',
    'Draws 4 wake maps (N/E/S/W) using the selected models and wind resource (WAsP folder or WRG/ZIP).': 'Dibuja 4 mapas de estelas (N/E/S/O) usando los modelos y el recurso seleccionado (carpeta WAsP o WRG/ZIP).',
    'Draws a temporary layer with the boundaries of the WRG or the WAsP/Surfer grids. It is used to check whether the layout lies inside the resource domain.': 'Dibuja una capa temporal con los límites del WRG o de los grids WAsP/Surfer. Sirve para comprobar si el layout queda dentro del dominio del recurso.',
    'Draws wake maps (N/E/S/W) with the selected models.': 'Dibuja mapas de estelas (N/E/S/O) con los modelos seleccionados.',
    'EXCEEDS': 'EXCEDE',
    'EXECUTIVE SUMMARY': 'RESUMEN EJECUTIVO',
    'Each row represents an acoustic source group. You can rename the group and wind farm to make summaries and exports easier to read.': 'Cada fila representa un grupo fuente acústico. Puedes renombrar grupo y parque para que el resumen y las exportaciones sean más legibles.',
    'Effective acoustic scenario by source group:': 'Escenario acústico efectivo por grupo fuente:',
    'Elapsed time:': 'Tiempo transcurrido:',
    'Empty CSV.': 'CSV vacío.',
    'Empty WRG': 'WRG vacío',
    'Empty WRG list': 'Lista WRG vacía',
    'Empty acoustic curve': 'Acoustic curve vacía',
    'Empty spectrum': 'Espectro vacío',
    "Enable 'Show resource boundary on the map' to draw it.": "Activa 'Mostrar perímetro del recurso en el mapa' para dibujarlo.",
    'Enable parallel calculation': 'Activar cálculo paralelo',
    'Enables canvas editing: left click adds a turbine, right click deletes it. Edited coordinates are used directly in the AEP calculation (the memory layer is the source of truth). The original CSV is NOT modified.': 'Activa edición en el canvas: click izq añade turbina, click der borra. Las coordenadas editadas se usan directamente en el cálculo de AEP (la capa en memoria es la fuente de verdad). El CSV original NO se modifica.',
    'Energy': 'Energía',
    'Energy\nAEP and wakes': 'Energía\nAEP y wakes',
    'Energy module · PyWake workflow': 'Módulo Energía · Flujo PyWake',
    'Energy · Wind farm AEP': 'Energía · AEP del parque',
    'Energy: ready · Noise: ready · Shadow flicker: ready': 'Energía: operativa · Ruido: operativo · Sombras: operativo',
    'Engine': 'Motor',
    'Engine used:': 'Motor utilizado:',
    'Enter the EPSG in which the tile coordinates are.\nFor example:\n  • 25829 → ETRS89 / UTM 29N (western Galicia)\n  • 25830 → ETRS89 / UTM 30N (most of Spain)\n\nLeave empty to use': 'Introduce el EPSG en el que están las coordenadas de los tiles.\nPor ejemplo:\n  • 25829 → ETRS89 / UTM 29N (Galicia oeste)\n  • 25830 → ETRS89 / UTM 30N (gran parte de España)\n\nDeja vacío para usar',
    'Equation used': 'Ecuación utilizada',
    'Error creating turbine': 'Error creando turbina',
    'Error detecting coordinates': 'Error al detectar coordenadas',
    'Error during calculation': 'Error durante el cálculo',
    'Error reading TXT/CSV': 'Error leyendo TXT/CSV',
    'Error regenerating raster': 'Error al regenerar el raster',
    'Error regenerating raster:': 'Error al regenerar el raster:',
    'Error while generating plots:': 'Error al generar las gráficas:',
    'Evaluated receptors': 'Receptores Evaluados',
    'Exceedances': 'Excedencias',
    'Exceeds': 'Excede',
    'Exit interactive mode (ESC also works).': 'Salir del modo interactivo (ESC también funciona).',
    'Expansion slope of the top-hat wake cone. Increasing k speeds up recovery; decreasing it lengthens the wake.': 'Pendiente de expansión del cono de estela top-hat. Subir k acelera la recuperación; bajarlo alarga la estela.',
    'Explains what TI is, when to use a turbulence raster and how it affects AEP': 'Explica qué es la TI, cuándo usar raster de turbulencia y cómo afecta al AEP',
    'Explains what a wake is and what changes between models': 'Explica qué es una estela y qué cambia entre modelos',
    'Explains what each parameter changes, without assuming prior knowledge': 'Explica qué cambia cada parámetro sin asumir conocimiento previo',
    'Explains what happens when several wakes reach the same turbine': 'Explica qué pasa cuando varias estelas llegan a la misma turbina',
    'Explains what the wake effect is, how TI enters the calculation and why some models are more sensitive to turbulence': 'Explica qué es el wake effect, cómo entra la TI y por qué algunos modelos son más sensibles a turbulencia',
    'Explains why the rotor should not always be treated as a single point': 'Explica por qué el rotor no debe tratarse siempre como un solo punto',
    'Export': 'Exportar',
    'Export AEP report': 'Exportar informe AEP',
    'Export DTM diagnostics': 'Exportar diagnóstico MDT',
    'Export Failed': 'Exportación fallida',
    'Export HTML report…': 'Exportar informe HTML…',
    'Export Shadow Flicker 12×24': 'Exportar Shadow Flicker 12×24',
    'Export Successful': 'Exportación correcta',
    'Export Top receivers CSV…': 'Exportar Top receivers CSV…',
    'Export XLSX package': 'Exportar paquete XLSX',
    'Export XLSX package…': 'Exportar paquete XLSX…',
    'Export edited layout': 'Exportar layout editado',
    'Export edited layout...': 'Exportar layout editado…',
    'Export edited layout…': 'Exportar layout editado…',
    'Export exceedances': 'Exportar excedencias',
    'Export exceedances CSV…': 'Exportar excedencias CSV…',
    'Export failed': 'Exportación fallida',
    'Export hour × month matrix to CSV': 'Exportar matriz hora × mes a CSV',
    'Export per-turbine CSV': 'Exportar CSV por turbina',
    'Export per-turbine CSV…': 'Exportar CSV por turbina…',
    'Export receivers': 'Exportar receptores',
    'Export receivers CSV…': 'Exportar receptores CSV…',
    'Export report…': 'Exportar informe…',
    'Export source groups': 'Exportar grupos fuente',
    'Export source groups CSV…': 'Exportar grupos fuente CSV…',
    'Export summary': 'Exportar resumen',
    'Export summary…': 'Exportar resumen…',
    'Exported': 'Exportados',
    'Exports the edited points of the active layer to CSV. It does not overwrite the original CSV without confirmation.': 'Exporta los puntos editados de la capa activa a CSV. No sobrescribe el CSV original sin confirmar.',
    'Fast engine (current)': 'Motor Rápido (actual)',
    'Fast engine: simplified screening calculation\nISO engine: band-based calculation following ISO 9613-2:2024': 'Motor rápido: cálculo simplificado para cribado acústico\nMotor ISO: cálculo por bandas según ISO 9613-2:2024',
    'Fast global LwA': 'Rápido LwA global',
    'Fast · RotorCenter': 'Rápido · RotorCenter',
    'Fast: RotorCenter is the fastest mode and is recommended for quick iterations or the interactive map.': 'Fast: RotorCenter es el modo más rápido y recomendado para iteraciones rápidas o mapa interactivo.',
    'Featured logo and a conversation about roadmap priorities.': 'Logo destacado y conversación sobre prioridades del roadmap.',
    'February': 'Febrero',
    'File:': 'Archivo:',
    'Filter raster by month/hour (after generation)': 'Filtrar raster por mes/hora (tras la generación)',
    'Filtered TIF saved': 'TIF filtrado guardado',
    'Filtered raster generated': 'Raster filtrado generado',
    'First TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.': 'Primer coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.',
    'First coefficient multiplying TI in the TurboGaussian formulation.': 'Primer coeficiente que multiplica la TI en la formulación TurboGaussian.',
    'First press “Create turbine” in the corresponding tab.': 'Primero pulsa «Crear turbina» en la pestaña correspondiente.',
    'Fixed LES-calibrated constants from the paper.': 'Constantes calibradas LES fijas del paper.',
    'Fixed LwA by acoustic source group': 'LwA fijo por grupo fuente acústico',
    'Fixed UTC offset': 'Offset UTC fijo',
    'Fixed UTC offset:': 'Offset UTC fijo:',
    'Fixed blend constants.': 'Constantes de blend fijas.',
    'Fixed empirical coefficients from the paper.': 'Coeficientes empíricos fijos del paper.',
    'Fixed empirical coefficients.': 'Coeficientes empíricos fijos.',
    'Fixed offset for fixed-offset mode. Daylight saving time is not applied.': 'Offset fijo para modo con offset fijo. No se aplica horario de verano.',
    'Fixed paper constants.': 'Constantes fijas del paper.',
    'Folder with WAsP grids': 'Carpeta con grids WAsP',
    'Folder:': 'Carpeta:',
    'For testing, use Recommended auto or SquaredSum as the sensitivity reference.': 'Para pruebas, usa Auto recomendado o SquaredSum como referencia de sensibilidad.',
    'For companies and technical teams': 'Para empresas y equipos técnicos',
    'For representative results, load a TI raster in the resource section or review the turbulence sensitivity.': 'Para resultados representativos, carga un raster TI en la sección de recurso o revisa la sensibilidad de turbulencia.',
    'Fraction of time the turbine is operational (0.97 = 97%)': 'Fracción de tiempo en que la turbina está operativa (0,97 = 97%)',
    'Frandsen-derived, classic formulation. Fixed constants.': 'derivado Frandsen, formulación clásica. Constantes fijas.',
    'Free-flow AEP': 'AEP libre',
    'Frequency': 'Frecuencia',
    'From land-use layer': 'Desde capa de uso del suelo',
    'Fund a specific feature if it fits the open-source direction.': 'Financiación de una funcionalidad concreta si encaja con la dirección open source.',
    'G used': 'G usado',
    'G used:': 'G usado:',
    'G=0 hard ground · G=1 porous ground': 'G=0 suelo duro · G=1 suelo poroso',
    'GCLTurbulence — Frandsen-derived classic formulation. Fixed constants.': 'GCLTurbulence — derivado Frandsen, formulación clásica. Constantes fijas.',
    'GDAL could not create the GeoTIFF of the noise raster.': 'GDAL no pudo crear el GeoTIFF del raster de ruido.',
    'GDAL.Warp returned None while aligning the TI raster to the WRG grid': 'GDAL.Warp devolvió None al alinear el raster TI al grid WRG',
    'GDAL/OSR not available to read the turbulence raster': 'GDAL/OSR no disponible para leer el raster de turbulencia',
    'GIS map created:': 'Mapa GIS creado:',
    'Gaussian model with sensitivity to local/effective TI': 'modelo gaussiano con sensibilidad a la TI local/efectiva',
    'GaussianOverlapAvgModel should only be used with compatible Gaussian deficit models.': 'GaussianOverlapAvgModel solo debe usarse con modelos de déficit gaussianos compatibles.',
    'GaussianOverlapAvgModel was replaced by CGIRotorAvg(7) for compatibility.': 'GaussianOverlapAvgModel se ha sustituido por CGIRotorAvg(7) por compatibilidad.',
    'Generate GIS noise map (calculation grid)': 'Generar mapa GIS de ruido (malla de cálculo)',
    'Generate isophones': 'Generar isófonas',
    'Generate point layers': 'Generar capas de puntos',
    'Generated by Velantis Wind · QGIS energy module · PyWake-based AEP workflow.': 'Generado por Velantis Wind · Módulo Energía de QGIS · Flujo AEP basado en PyWake.',
    'Generates a continuous raster map of shadow flicker hours': 'Genera un mapa raster continuo de horas de shadow flicker',
    'Generates a continuous raster map of shadow flicker hours\nDisplays a color gradient across the whole analysis area\nWARNING: this may take several minutes depending on the area': 'Genera un mapa raster continuo de horas de shadow flicker\nMuestra un gradiente de color en toda el área de análisis\nAVISO: esto puede tardar varios minutos según el área',
    'Generating per-turbine table…': 'Generando tabla por aerogenerador…',
    'Generating shadow flicker raster map': 'Generando mapa raster de shadow flicker',
    'Geometrical spreading': 'Dispersión geométrica',
    'Global acoustic curve LwA(ws)': 'Curva acústica global LwA(ws)',
    'Global comparison': 'Comparación global',
    'Global fallback G': 'G global de respaldo',
    'Global summary': 'Resumen global',
    'Grid resolution [m] (lower = more detail, slower):': 'Resolución del grid [m] (menor = más detalle, más lento):',
    'Gross AEP': 'AEP bruto',
    'Gross AEP (free-stream):': 'AEP bruto (free-stream):',
    'Gross AEP [MWh]': 'AEP bruto [MWh]',
    'Ground factor G:': 'Factor de suelo G:',
    'Ground mode': 'Modo suelo',
    'Ground-effect attenuation': 'Atenuación por efecto del terreno',
    'Ground-from-layer mode active without a valid layer: the global G will be used as fallback.': 'Modo suelo desde capa activo sin capa válida: will use G global como respaldo.',
    'Group': 'Grupo',
    'Header rows to skip:': 'Filas de cabecera a saltar:',
    'Height [m]': 'Altura [m]',
    'Help about LinearSum, SquaredSum, MaxSum and WeightedSum': 'Ayuda sobre LinearSum, SquaredSum, MaxSum y WeightedSum',
    'Help keep open wind-energy tools useful, maintained and up to date.': 'Ayuda a mantener herramientas eólicas abiertas, útiles y actualizadas.',
    'Help on WRG turbulence and wake models': 'Ayuda sobre turbulencia WRG y modelos de estela',
    'Help on ambient TI, added turbulence and when it actually affects AEP': 'Ayuda sobre TI ambiente, turbulencia añadida y cuándo influye realmente en el AEP',
    'Help on ambient TI, added turbulence and when it really affects AEP': 'Ayuda sobre TI ambiente, turbulencia añadida y cuándo influye realmente en el AEP',
    'Help on when each blockage model is appropriate and why it is sometimes disabled automatically': 'Ayuda sobre cuándo conviene cada modelo de bloqueo y por qué a veces se desactiva automáticamente',
    'Help on when each blockage model makes sense and why it is sometimes disabled automatically': 'Ayuda sobre cuándo conviene cada modelo de bloqueo y por qué a veces se desactiva automáticamente',
    'Help on when each wake model is appropriate depending on the wind farm type and study goal': 'Ayuda sobre cuándo conviene cada modelo de estela según el tipo de parque y el objetivo del estudio',
    'Help · Blockage': 'Ayuda · Bloqueo',
    'Help · Model parameters (': 'Ayuda · Parámetros del modelo (',
    'Help · Rotor-average': 'Ayuda · Rotor-average',
    'Help · Rotor-disk averaging': 'Ayuda · Promedio sobre el rotor',
    'Help · TI turbulence': 'Ayuda · Turbulencia TI',
    'Help · Turbulence': 'Ayuda · Turbulencia',
    'Help · WRG turbulence': 'Ayuda · Turbulencia WRG',
    'Help · Wake effect and wake model': 'Ayuda · Wake effect y modelo de estela',
    'Help · Wake model': 'Ayuda · Modelo de estela',
    'Help · Wake superposition': 'Ayuda · Superposición de estelas',
    'Home': 'Inicio',
    'Hour × Month': 'Hora × Mes',
    'Hour × Month Matrix': 'Matriz hora × mes',
    'Hour:': 'Hora:',
    'Hourly table: local civil time with DST · Zone:': 'Tabla horaria: hora civil local con DST · Zona:',
    'Hours of shadow flicker by hour of day': 'Horas de shadow flicker por hora del día',
    'How are wakes from several turbines combined?': '¿Cómo se combinan las estelas de varias turbinas?',
    'How do you want to calculate wind-speed loss behind each turbine?': '¿Cómo quieres calcular la pérdida de viento detrás de cada turbina?',
    'How should PyWake propagate wakes between turbines?': '¿Cómo debe propagar PyWake las estelas entre turbinas?',
    'How should the wind-speed loss behind each turbine be calculated?': '¿Cómo se calcula la pérdida de velocidad detrás de cada turbina?',
    'How should turbine interactions be calculated?': '¿Cómo se calculan las interacciones entre turbinas?',
    'How should wind speed be averaged across the rotor disk?': '¿Cómo debe promediarse el viento dentro del disco del rotor?',
    'How to choose the model': 'Cómo elegir el modelo',
    'How to choose the wake model': 'Cómo elegir el modelo de estela',
    'Hub Height [m]': 'Altura de buje [m]',
    'Hub Height and Rotor Diameter must be valid numbers.': 'Hub Height y Rotor Diameter deben ser números válidos.',
    'Hub Height: hub height above ground level [m]': 'Hub Height: altura de buje sobre el terreno [m]',
    'Hub height:': 'Altura buje:',
    'HybridInduction — combines induction approximations. No user parameters in this experimental release.': 'HybridInduction — combina aproximaciones de inducción. Sin parámetros de usuario en esta versión experimental.',
    'IANA time zone, for example Europe/Madrid, America/Santiago or Asia/Tokyo.\nThe plugin includes a broad IANA catalogue and local TZif database to apply DST without timezonefinder.': 'Zona horaria IANA, por ejemplo Europe/Madrid, America/Santiago o Asia/Tokyo.\nEl plugin incluye un catálogo IANA amplio y base TZif local para aplicar DST sin timezonefinder.',
    'IANA time zone:': 'Zona horaria IANA:',
    'IANA/DST: the table uses the real local civil time of the site.\nFixed UTC offset: uses the same UTC offset for the full year.': 'IANA/DST: la tabla usa la hora civil local real del emplazamiento.\nOffset UTC fijo: usa el mismo offset UTC durante todo el año.',
    'ISO-aligned by bands': 'ISO-aligned por bandas',
    'ISO-aligned method: ISO 9613-2 with octave bands (Adiv + Aatm(T,RH,P) + Agr(f))': 'Método ISO-aligned: ISO 9613-2 con bandas de octava (Adiv + Aatm(T,RH,P) + Agr(f))',
    'If a turbine receives several wakes, how should their effects be combined?': 'Si una turbina recibe varias estelas, ¿cómo se suman sus efectos?',
    'If it is not a measured site value, check whether it should be close to 101.325 kPa or adjusted for altitude.': 'Si no es un dato medido del emplazamiento, revisa si debería estar cerca de 101.325 kPa o ajustada por altitud.',
    'If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.': 'Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.',
    'If you do not select a TI raster': 'Si no seleccionas raster TI',
    'If you only provide one TI raster': 'Si solo aportas un raster TI',
    'If you provide `ct`, its length must match `ws`.': 'Si aportas `ct`, su longitud debe coincidir con `ws`.',
    'If you provide several TI raster(s)': 'Si aportas varios raster(s) TI',
    'If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.': 'Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.',
    'If your wake model is TI-driven': 'Si tu wake model es TI-driven',
    'Import acoustic curve for the selected source group…': 'Importar curva acústica para el grupo fuente seleccionado…',
    'Improvements in AEP, noise, shadow flicker, resource ingestion and result exports.': 'Mejoras en AEP, ruido, sombras, lectura de recursos y exportación de resultados.',
    'In curve mode, each source group can use a different ws/LwA CSV.': 'En modo curva, cada grupo fuente puede usar un CSV ws/LwA distinto.',
    'In large offshore wind farms this usually underestimates losses': 'En parques offshore grandes esto suele subestimar las pérdidas',
    'Include CT column': 'Incluir columna CT',
    'Incomplete WRG line: expected >=': 'Línea WRG incompleta: esperados >=',
    'Incomplete configuration. Check the flagged items.': 'Configuración incompleta. Revisa los elementos marcados.',
    'Initial wake size at the base (σ₀/D). PyWake default = 0.2.': 'Tamaño inicial de la estela en la base (σ₀/D). Default PyWake = 0.2.',
    'Initial wake size. PyWake default = 0.25.': 'Tamaño inicial de la estela. Default PyWake = 0.25.',
    'Initializing calculation…': 'Inicializando cálculo…',
    'Install them in the QGIS Python environment and try again.': 'Instálalas en el entorno de Python de QGIS y vuelve a intentarlo.',
    'Installed power': 'Potencia instalada',
    'Installed power [MW]': 'Potencia inst. [MW]',
    'Installed power:': 'Potencia instalada:',
    'Interactive map': 'Mapa interactivo',
    'Invalid CRS': 'CRS no válido',
    'Invalid EPSG. EPSG will keep being used:': 'EPSG no válido. Se seguirá usando EPSG:',
    'Invalid TI raster:': 'Raster TI no válido:',
    'Invalid WRG header (nx,ny,dx): nx=': 'Header WRG inválido (nx,ny,dx): nx=',
    'Invalid WRG header:': 'Cabecera WRG inválida:',
    "Invalid WRG header: '": "Header WRG inválido: '",
    'Invalid WRG point line:': 'Línea de punto WRG inválida:',
    'Invalid acoustic configuration:\n- ': 'Configuración acústica inválida:\n- ',
    'Invalid configuration': 'Configuración no válida',
    'Invalid coordinate CSV for «': 'CSV de coordenadas no válido para «',
    'Invalid curve -> fixed LwA fallback (': 'Curva inválida -> fallback LwA fijo (',
    'Invalid extent:': 'Extensión no válida:',
    'Invalid or non-existent WAsP directory': 'Directorio WAsP inválido o no existente',
    'Invalid or unreadable WRG:': 'WRG inválido o no legible:',
    'Invalid shadow configuration': 'Configuración de sombras no válida',
    'Invalid spectrum:': 'Espectro inválido:',
    "Invalid time-zone name: '{}'": "Nombre de zona horaria inválido: '{}'",
    'Invalid value in band': 'Valor inválido en banda',
    'Isophone levels [dB(A)]:': 'Niveles isófonas [dB(A)]:',
    'It is passed to the PyWake Site as a fraction, for example 10% = 0.10.': 'Se pasa al Site de PyWake como fracción, por ejemplo 10% = 0.10.',
    'It is robust, but TI barely changes the base deficit.': 'Es robusto, pero la TI apenas cambia el déficit base.',
    'It is usually the most complete option': 'Suele ser la opción más completa',
    'It usually makes sense in': 'Suele tener sentido en',
    'January': 'Enero',
    'July': 'Julio',
    'June': 'Junio',
    'LUT/RANS-based model': 'Modelo basado en LUT/RANS',
    'Land use': 'Uso del suelo',
    'Land-use layer (optional):': 'Capa uso del suelo (opcional):',
    'Language': 'Idioma',
    'Large offshore wind farms': 'Parques offshore grandes',
    'Last calculation': 'Último cálculo',
    'Latitude [°]:': 'Latitud [°]:',
    'Latitude must be between -90° and 90°.': 'La latitud debe estar entre -90° y 90°.',
    'Latitude:': 'Latitud:',
    'Layer': 'Capa',
    'Layer ID:': 'ID de capa:',
    'Layer used': 'Capa usada',
    'Layers': 'Capas',
    'Layers for plotting wakes': 'Capas para graficar estelas',
    'Layers for wakes': 'Capas para estelas',
    'Layers/models state refreshed.': 'Estado de capas/modelos refrescado.',
    'Leaves the TI raster(s) list empty.': 'Deja la lista de raster(s) TI vacía.',
    'Left click: add turbine · Right click: delete · ESC: exit': 'Click izq: añadir turbina · Click der: borrar · ESC: salir',
    'Left click: add · Right click: delete · ESC: exit': 'Click izq: añadir · Click der: borrar · ESC: salir',
    'Limit': 'Límite',
    'Limit criterion:': 'Criterio de límite:',
    'Limit:': 'Límite:',
    'Limitations and recommendations': 'Limitaciones y recomendaciones',
    'Linear attenuation α [dB/m]:': 'Atenuación lineal α [dB/m]:',
    'LinearSum adds deficits linearly; it is usually conservative in dense wind farms.': 'LinearSum suma los déficits linealmente; suele ser conservador en parques densos.',
    'LinearSum: directly adds several velocity deficits. It can increase losses in dense wind farms.': 'LinearSum: suma directamente varias pérdidas de velocidad. Puede aumentar las pérdidas en parques densos.',
    'Lists `ws` and `power_kw` must have the same length and not be empty.': 'Listas `ws` y `power_kw` deben tener la misma longitud y no estar vacías.',
    'Load layer': 'Cargar capa',
    'Loading wind resource (WAsP/WRG)…': 'Cargando recurso eólico (WAsP/WRG)…',
    'Local atmospheric pressure in kPa. Usual reference value at sea level: 101.325 kPa. Use a measured or altitude-adjusted value if available.': 'Presión atmosférica local en kPa. Valor de referencia habitual a nivel del mar: 101.325 kPa. Usa un dato medido o ajustado por altitud si está disponible.',
    'Local civil time (IANA/DST)': 'Hora civil local (IANA/DST)',
    'Longitude [°]:': 'Longitud [°]:',
    'Longitude must be between -180° and 180°.': 'La longitud debe estar entre -180° y 180°.',
    'Longitude:': 'Longitud:',
    'Loss': 'Pérdida',
    'Loss breakdown': 'Desglose de pérdidas',
    'Losses:': 'Pérdidas:',
    'LwA(ws) acoustic curves': 'Curvas acústicas LwA(ws)',
    'LwA(ws) acoustic curves at': 'Curvas acústicas LwA(ws) a',
    'MWh/year': 'MWh/año',
    'Main expansion constant for TurboGaussian/TurbOPark.': 'Constante principal de expansión de TurboGaussian/TurbOPark.',
    'Main expansion constant for the TurboNOJ family when the PyWake version exposes it.': 'Constante principal de expansión para la familia TurboNOJ cuando la versión de PyWake la expone.',
    'Main simulation with PyWake…': 'Simulación principal con PyWake…',
    'Main wake deficit model. Not all models use turbulence in the same way.': 'Wake deficit principal. No todos los modelos usan la turbulencia del mismo modo.',
    'Manual ambient TI if no raster:': 'TI ambiente manual si no hay raster:',
    'Manual height for each TI raster, in the same order as the files. Useful when the file name does not clearly indicate 90 m, 120 m, etc.': 'Altura manual de cada raster TI, en el mismo orden que los archivos. Útil si el nombre del archivo no indica claramente 90 m, 120 m, etc.',
    'Manual height override for each TI raster, in the same order as the files. Useful when the file name does not contain 90 m, 120 m, etc.': 'Override manual de altura para cada raster TI, en el mismo orden que los archivos. Útil cuando el nombre del archivo no contiene 90m, 120m, etc.',
    'Map resolution [m]:': 'Resolución del mapa [m]:',
    'March': 'Marzo',
    'Margin': 'Margen',
    'Max shadow distance [m]:': 'Distancia máxima de sombra [m]:',
    'Max shadow distance:': 'Distancia máxima de sombra:',
    'Max solar elevation [°]:': 'Elevación solar máxima [°]:',
    'MaxSum considers only the most severe deficit; useful as a robust check, but it may underestimate cumulative losses.': 'MaxSum considera solo el déficit más severo; útil como comprobación robusta, pero puede infraestimar pérdidas acumuladas.',
    'MaxSum: uses only the dominant wake. Useful for diagnosis, but it can underestimate accumulated losses from several wakes.': 'MaxSum: usa solo la estela dominante. Sirve para diagnosticar, pero puede quedarse corto si varias estelas se acumulan.',
    'Maximum [dB]': 'Máximo [dB]',
    'Maximum distance from a turbine to consider shadow flicker.\nDefault: 2000 m, matching the conservative Continuum-style screening distance.\nThis value is used for receiver filtering and for the raster extent/mask.': 'Distancia máxima desde una turbina para considerar shadow flicker.\nPor defecto: 2000 m, coincidiendo con la distancia de screening conservadora tipo Continuum.\nEste valor se usa para filtrar receptores y para el extent/máscara del raster.',
    'Maximum level (dB(A))': 'Nivel Máximo (dB(A))',
    'Maximum radius': 'Radio máximo',
    'Maximum radius [m]:': 'Radio máximo [m]:',
    'Maximum solar elevation to include (90° = no high-sun cut; lower values are an optional screening assumption)': 'Elevación solar máxima a incluir (90° = sin recorte de sol alto; valores menores son una hipótesis opcional de screening)',
    'May': 'Mayo',
    'Mean G_eff': 'G_eff medio',
    'Mean G_eff used': 'G_eff medio usado',
    'Mean [dB]': 'Media [dB]',
    'Mean:': 'Media:',
    'Merge resource': 'Unión Recurso',
    'Wind source-receiver method (Adiv + Aatm + Aground)': 'Método fuente-receptor eólico (Adiv + Aatm + Aground)',
    'Method used:': 'Método usado:',
    'Method:': 'Método:',
    'Metric': 'Métrica',
    'Mexico has several zones; check manually.': 'México tiene varias zonas; verifica manualmente.',
    'Min solar elevation [°]:': 'Elevación solar mínima [°]:',
    'Minimum expansion offset when TI tends to zero.': 'Offset mínimo de expansión cuando la TI tiende a cero.',
    'Minimum sun elevation must be lower than maximum sun elevation.': 'La elevación solar mínima debe ser menor que la máxima.',
    'Minimum wake-expansion offset.': 'Offset mínimo de expansión de la estela.',
    'Missing configuration': 'Configuración incompleta',
    'Missing models': 'Faltan modelos',
    'Model': 'Modelo',
    "Model that estimates how much wind a turbine loses when it is inside another turbine's wake. Some models use TI turbulence and others barely notice it.": 'Modelo que estima cuánto viento pierde una turbina cuando está dentro de la estela de otra. Algunos modelos usan la turbulencia TI y otros apenas la notan.',
    'Models': 'Modelos',
    'Module status:': 'Estado módulos:',
    'Month': 'Mes',
    'Month:': 'Mes:',
    'Monthly Breakdown (Average across all receptors)': 'Desglose mensual (media de todos los receptores)',
    'More realistic · CGIRotorAvg(7)': 'Más realista · CGIRotorAvg(7)',
    'More realistic · CGIRotorAvg(9)': 'Más realista · CGIRotorAvg(9)',
    'Most energetic sector': 'Sector más energético',
    'NOJ/Jensen: classic and very fast model. It uses a fixed expansion; ambient TI usually has little direct effect on the deficit.': 'NOJ/Jensen: modelo clásico y muy rápido. Usa una expansión fija; la TI ambiente suele tener poco efecto directo en el déficit.',
    'NOJDeficit uses a constant top-hat expansion: the turbulence model does not change AEP in the standard configuration.': 'NOJDeficit usa una expansión top-hat constante: el modelo de turbulencia no cambia el AEP en la configuración estándar.',
    'Name in README and experimental/update notes when available.': 'Nombre en README y acceso a notas de actualización/actualizaciones cuando estén disponibles.',
    'Name:': 'Nombre:',
    'Net AEP': 'AEP neta',
    'Net AEP (operational)': 'AEP neto (operativo)',
    'Net AEP (operational):': 'AEP neto (operativo):',
    'Net AEP [MWh]': 'AEP neto [MWh]',
    'Net CF [%]': 'FC neto [%]',
    'Net capacity factor': 'Factor de capacidad neto',
    'Net specific yield': 'Energía específica neta',
    'Net specific yield:': 'Energía específica neta:',
    'Net+losses': 'Neto+pérd.',
    'Niayifar: TI-sensitive Gaussian model. Useful when you want the TI raster or manual TI to influence wake recovery.': 'Niayifar: gaussiano TI-sensitive. Recomendable si quieres que el raster TI o la TI manual influyan en la recuperación de estela.',
    'Night limit': 'Límite noche',
    'Night-time': 'Nocturno',
    'No .grd/.asc/.tif grids were found in the selected folder.': 'No se han encontrado grids .grd/.asc/.tif en la carpeta seleccionada.',
    'No DEM selected - flat terrain assumed': 'No se ha seleccionado DEM/MDT; se asume terreno plano',
    'No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.': 'Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas fuente/receptor.',
    'No TI raster was selected for WRG; a fixed/fallback TI will be used if the model needs it.': 'No se ha seleccionado raster TI para WRG; se usará TI fija/fallback si el modelo lo necesita.',
    'No TI raster(s) selected → automatic fallback to 10% if you use WRG.': 'Raster(s) TI no seleccionado(s) → fallback automático a 10% si usas WRG.',
    "No WFM engine available for engine='": "No hay motor WFM disponible para engine='",
    'No WRG could be read from:': 'No se pudo leer ningún WRG desde:',
    'No WRG/ZIP has been selected.': 'No se ha seleccionado ningún WRG/ZIP.',
    "No WindTurbines was found in 'models' and V80 is not available.": "No se encontró un WindTurbines en 'models' y V80 no está disponible.",
    'No acoustic-curve path': 'Sin ruta de curva acústica',
    'No added turbulence:': 'Sin turbulencia añadida:',
    'No blockage:': 'Sin bloqueo:',
    'No configuration recorded': 'Sin configuración registrada',
    'No coordinate layers generated yet': 'Sin capas de coordenadas generadas todavía',
    'No coordinates': 'Sin coordenadas',
    'No editable layers': 'Sin capas editables',
    'No layout detected': 'No se ha detectado layout',
    'No layout detected in the per-model coordinate group': 'Sin layout detectado en el grupo de coordenadas por modelo',
    'No model configuration is available.\n\nPlease fill Hub Height and Rotor Diameter in the table.': 'No hay configuración de modelos disponible.\n\nRellena Hub Height y Rotor Diameter en la tabla.',
    'No models detected. Check that turbine layers exist.': 'No se detectaron modelos. Comprueba que existen capas de turbinas.',
    'No output folder was selected.': 'No se seleccionó carpeta de salida.',
    'No raster data are available. Generate a raster first.': 'No hay datos raster disponibles. Genera primero un raster.',
    'No readable WRG was found in:': 'No se encontró ningún WRG legible en:',
    'No receiver layer selected.': 'No hay capa de receptores seleccionada.',
    'No receivers': 'Sin receptores',
    'No receivers exceed the limit in this calculation.': 'No hay receptores que superen el límite en este cálculo.',
    'No resource selected yet': 'Sin recurso seleccionado todavía',
    'No rotor averaging: PyWake uses the wind speed at the rotor centre. This is the simplest and fastest option, but it loses detail if a wake affects only part of the rotor disk.': 'Sin promedio de rotor: PyWake usa el viento en el centro del rotor. Es lo más simple y rápido, pero pierde detalle si una estela afecta solo a una parte del disco.',
    'No rotor-average: PyWake will evaluate the effective wind speed at the rotor centre.': 'Sin rotor-average: PyWake evaluará la velocidad efectiva en el centro del rotor.',
    'No source-receiver pairwise diagnostics are available. Recalculate with the ISO-aligned engine and sources inside the radius. This CSV is the one that lets you audit each turbine against each receiver.': 'No hay diagnóstico por pares fuente-receptor disponible. Recalcula con motor ISO-aligned y fuentes dentro del radio. Este CSV es el que permite auditar cada turbina contra cada receptor.',
    'No superposition model could be imported (LinearSum/SquaredSum/WeightedSum).': 'No se pudo importar ningún superposition model (LinearSum/SquaredSum/WeightedSum).',
    'No template': 'Sin plantilla',
    'No turbine': 'Sin turbina',
    'No turbine layer selected.': 'No hay capa de turbinas seleccionada.',
    'No turbine layer “{Modelo} (CSV)” was found to edit.\n\nPress “Generate point layers” first, or load a model layer into the project.': 'No encuentro una capa de turbinas «{Modelo} (CSV)» para editar.\n\nPulsa primero «Generar capas de puntos» o carga una capa del modelo en el proyecto.',
    'No turbine model has been defined.': 'No se ha definido ningún modelo de aerogenerador.',
    'No turbines': 'Sin turbinas',
    'No turbines were found in the layout.': 'No se encontraron turbinas en el layout.',
    'No usual tunable parameters.': 'Sin parámetros tunables habituales.',
    'No valid GRD files were found in the given directories': 'No se encontraron GRD válidos en los directorios dados',
    'No valid PowerCtFunction was found.': 'No se encontró una PowerCtFunction válida.',
    "No valid PowerCtFunction was found/created for model '": "No se encontró/creó una PowerCtFunction válida para el modelo '",
    'No valid TI raster(s) were provided': 'No se han indicado raster(s) TI válidos',
    'No valid WRG could be read': 'No se pudo leer ningún WRG válido',
    'No valid coordinates were found in the CSV.': 'No se encontraron coordenadas válidas en el CSV.',
    'No valid data were read from': 'No se han leído datos válidos de',
    'No valid points to calculate': 'No hay puntos válidos para calcular',
    'No valid receiver layer was provided.': 'No se proporcionó una capa de receptores válida.',
    'No valid solar positions were found': 'No se encontraron posiciones solares válidas',
    'No valid sources in the layer': 'Sin fuentes válidas en la capa',
    'No valid timesteps': 'Sin pasos temporales válidos',
    'No valid turbines were found with the model configuration.': 'No se encontraron turbinas válidas con la configuración de modelo.',
    'No valid turbines were found with the model configuration.\n\nCheck that:\n1. Turbine layers are selected\n2. The model table has Hub Height and Rotor Diameter configured\n3. Model names match the layers': 'No se encontraron turbinas válidas con la configuración de modelo.\n\nComprueba que:\n1. Hay capas de turbinas seleccionadas\n2. La tabla de modelos tiene Hub Height y Rotor Diameter configurados\n3. Los nombres de modelo coinciden con las capas',
    'No valid turbines/layout were detected for the acoustic calculation.': 'No se han detectado turbinas/layout válidos para el cálculo acústico.',
    'No wake deficit model available: neither BG nor NoWakeDeficit.': 'No hay wake deficit model disponible: ni BG ni NoWakeDeficit.',
    'No. of turbines:': 'Nº de aerogeneradores:',
    'No. turbines': 'Nº aerogeneradores',
    'Noise': 'Ruido',
    'Noise calculation cancelled or failed.': 'Cálculo de ruido cancelado o fallido.',
    'Noise calculation completed in the background.': 'Cálculo de ruido completado en segundo plano.',
    'Noise calculation completed.': 'Cálculo de ruido completado.',
    'Noise calculation launched in the background. You can see it in the QGIS task manager.': 'Cálculo de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.',
    'Noise raster completed.': 'Raster de ruido completado.',
    'Noise raster finished with a warning.': 'Raster de ruido finalizado con aviso.',
    'Noise raster generated and added to the project.': 'Raster de ruido generado y añadido al proyecto.',
    'Noise raster launched in the background. You can see it in the QGIS task manager.': 'Raster de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.',
    'Noise technical report': 'Informe técnico de ruido',
    'Noise · Calculation summary': 'Ruido · Resumen del cálculo',
    'Noise · Map': 'Noise · Mapa',
    'None': 'Ninguno',
    'Not all wake models use TI in the same way': 'No todos los modelos de estela usan la TI del mismo modo',
    'Not selected (expected fallback to TI=10% in WRG flow)': 'No seleccionado (fallback previsto a TI=10% en flujo WRG)',
    'Note': 'Nota',
    'Note: the resulting level includes multi-source and multi-band energetic summation; it is not a direct subtraction from one single turbine.': 'Nota: el nivel resultante incluye la suma energética multi-fuente y multi-banda; no es una resta directa de una única turbina.',
    'Note: with PropagateUpDownIterative, use_effective_ws=True is automatically forced for wake and blockage to avoid compatibility errors.': 'Nota: con PropagateUpDownIterative se fuerza automáticamente use_effective_ws=True (wake y bloqueo) para evitar errores de compatibilidad.',
    'Notes': 'Notas',
    'Notes:': 'Notas:',
    'November': 'Noviembre',
    'Number of blades used in the near-wake length. Usually 3.': 'Número de palas usado en la longitud near-wake. Normalmente 3.',
    'Number of cores to use (your CPU has': 'Número de núcleos a usar (tu CPU tiene',
    'Number of models:': 'Número de modelos:',
    'OK': 'Aceptar',
    "ONE folder with a GRD mosaic (Sector/Height/Variable) will be generated.\nSelect 2 or more tile folders exported from WAsP.\n• 'Sector All': ALL present variables will be taken (mosaic by height).\n• In addition, a strict 'pywake_compat' subfolder is created (no patches needed).\n• Output in Surfer ASCII (DSAA) with WAsP format.": "Se generará UNA carpeta con GRD mosaico (Sector/Altura/Variable).\nSelecciona 2 o más carpetas de tiles exportadas desde WAsP.\n• 'Sector All': se tomarán TODAS las variables presentes (mosaico por altura).\n• Además, se crea subcarpeta 'pywake_compat' estricta (sin necesidad de parches).\n• Salida en Surfer ASCII (DSAA) con formato WAsP.",
    'Observation point height (typical window: 2 m)': 'Altura del punto de observación (ventana típica: 2 m)',
    'Observer height [m]:': 'Altura del observador [m]:',
    'Observer height cannot be negative.': 'La altura del observador no puede ser negativa.',
    'Octave-band calculation, atmospheric absorption by conditions, ISO-type ground effect': 'Cálculo por bandas de octava, absorción atmosférica por condiciones, efecto suelo tipo ISO',
    'Octave-band propagation using an ISO-aligned methodology': 'Propagación por bandas de octava según metodología ISO-aligned',
    'October': 'Octubre',
    'Offshore with relatively clean inflow': 'Offshore con inflow relativamente limpio',
    'Once the raster has been generated, you can regenerate filtered TIFs by month and/or hour\nwithout recalculating the full annual geometry.': 'Una vez generado el raster, puedes regenerar TIF filtrados por mes y/o hora\nsin recalcular toda la geometría anual.',
    'One layer or several layers': 'Una capa o varias capas',
    'Only': 'Solo',
    'Only a las': 'Solo a las',
    'Open TXT/CSV': 'Abrir TXT/CSV',
    'Open dialog to load the power curve': 'Abrir diálogo para cargar curva de potencia',
    'Open the main Velantis Wind hub': 'Abrir el hub principal de Velantis Wind',
    'Open the official PyWake documentation, the basis for AEP and wake calculations in the Energy module.': 'Abre la documentación oficial de PyWake, base del cálculo AEP y de estelas del módulo de Energía.',
    'Optional acknowledgement on GitHub or the GitHub supporters page.': 'Agradecimiento opcional en GitHub o en la página de supporters de GitHub.',
    'Optional digital elevation model (MDT) raster.': 'Raster de modelo digital de elevaciones (MDT) opcional.',
    'Optional digital elevation model (MDT) raster.\nIf provided, the ground elevation under each turbine and receiver is\nsampled from the raster. This makes elev_diff = (hub + ground_turb) -\n(observer + ground_recv), using terrain-aware elevation correction.\nIf left empty, flat terrain (z=0) is assumed.': 'Raster de modelo digital de elevaciones (MDT) opcional.\nSi se proporciona, la elevación del terreno bajo cada turbina y receptor se\nmuestrea del raster. Así elev_diff = (buje + terreno_turb) -\n(observador + terreno_recep), usando corrección de elevación según terreno.\nSi se deja vacío, se asume terreno plano (z=0).',
    'Optional turbulence for WRG': 'Turbulencia opcional para WRG',
    'Optional: TI heights by file, e.g. 90;120': 'Opcional: alturas TI por archivo, ej. 90;120',
    'Optional: height of each TI raster, e.g. 90;120': 'Opcional: altura de cada raster TI, ej. 90;120',
    'Optionally provide one or more ambient TI raster(s), for example .asc/.tif.': 'Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif).',
    'Optionally provide one or more ambient TI raster(s), for example .asc/.tif. They are used only in the WRG flow. If none are selected, the plugin will warn and use TI=10% as fallback. Use the information button to see when a single layer is enough, when several heights are useful, and which wake models are most affected.': 'Opcionalmente puedes aportar uno o varios raster(s) de TI ambiente (por ejemplo .asc/.tif). Se usan solo en el flujo WRG. Si no se selecciona ninguno, el plugin avisará y usará TI=10% como fallback. Consulta el botón de información para ver cuándo conviene una sola capa, cuándo convienen varias alturas y en qué modelos de estela influye más.',
    'Other losses': 'Otras pérdidas',
    'Other losses:': 'Otras pérdidas:',
    "Override physical model parameters. If you leave 'Default PyWake', the canonical value for the selected model is used.": 'Override de parámetros físicos del modelo. Si dejas «Default PyWake», se usa el valor canónico del modelo seleccionado.',
    "Override physical model parameters. If you leave 'PyWake default', the canonical value for the selected model is used.": 'Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.',
    'Override physical model parameters. If you leave ‘PyWake default’, the canonical value for the selected model is used.': 'Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.',
    'PASS': 'CUMPLE',
    'Parallelization:': 'Paralelización:',
    'Parameter': 'Parámetro',
    'Parameters active:': 'Parámetros activos:',
    'Pass': 'Cumple',
    'Paths with G different from global': 'Trayectos con G distinto del global',
    'Paths with G different from the global value:': 'Trayectos con G distinto del global:',
    'Per-receiver results calculated.': 'Resultados por receptor calculados.',
    'Per-turbine CSV exported successfully:': 'CSV por turbina exportado correctamente:',
    'Physical interpretation': 'Interpretación física',
    'Please configure Hub Height and Rotor Diameter for all models.': 'Configura Hub Height y Rotor Diameter para todos los modelos.',
    'Plot wakes': 'Graficar estelas',
    'Plot with selected layers': 'Graficar con capas seleccionadas',
    'Plugin language:': 'Idioma del plugin:',
    'Power (kW):': 'Potencia (kW):',
    'Power in kW, same length as ws': 'Potencia en kW, misma longitud que ws',
    'PowerCtFunctionList is not available to combine several models.': 'PowerCtFunctionList no está disponible para combinar varios modelos.',
    'Preparing data for the background calculation…': 'Preparando datos para cálculo en segundo plano…',
    'Preparing sources, receivers and DTM for the background calculation…': 'Preparando fuentes, receptores y MDT para el cálculo en segundo plano…',
    'Preparing sources, receivers and DTM…': 'Preparando fuentes, receptores y MDT…',
    'Pressure': 'Presión',
    'Project context': 'Contexto del proyecto',
    'GitHub page': 'Página de GitHub',
    'Project summary': 'Resumen del proyecto',
    'Project:': 'Proyecto:',
    'PropagateDownwind does not apply blockage; the calculation will disable it automatically.': 'PropagateDownwind no aplica bloqueo; el cálculo lo desactivará automáticamente.',
    'Provide a valid point layer.': 'Proporciona una capa de puntos válida.',
    'PyWake WindTurbines is not available in this environment.': 'PyWake WindTurbines no está disponible en este entorno.',
    'PyWake allows several ways of combining wind-speed deficits when a turbine receives several wakes.': 'PyWake permite varias formas de combinar déficits de velocidad cuando una turbina recibe varias estelas.',
    'PyWake calculation engine': 'Motor de cálculo PyWake',
    'PyWake could not be loaded to build the turbine. Check that py_wake is installed in the QGIS Python and that Windows is not blocking a DLL/.pyd of its dependencies.': 'No se pudo cargar PyWake para construir la turbina. Comprueba que py_wake está instalado en el Python de QGIS y que Windows no está bloqueando alguna DLL/.pyd de sus dependencias.',
    'PyWake default': 'Valor PyWake por defecto',
    'PyWake default value': 'valor PyWake por defecto',
    'PyWake documentation': 'Documentación PyWake',
    'PyWake model': 'Modelo de PyWake',
    'PyWake physics': 'Física PyWake',
    'PyWake propagation engine': 'Motor de cálculo PyWake',
    'Python zoneinfo is not available in this environment: {}': 'Python zoneinfo no está disponible en este entorno: {}',
    'Quick template:': 'Plantilla rápida:',
    'RESULTING LEVEL': 'NIVEL RESULTANTE',
    'Raster cell size\n100 m = fast, less detail\n50 m = medium\n25 m = slow, maximum detail': 'Tamaño de celda raster\n100 m = rápido, menos detalle\n50 m = medio\n25 m = lento, máximo detalle',
    'Raster completed': 'Raster completado',
    'Raster error': 'Error de raster',
    'Raster generation was cancelled.': 'La generación del raster fue cancelada.',
    'Raster in progress': 'Raster en progreso',
    'Raster map created successfully.': 'Mapa raster creado correctamente.',
    'Raster map created successfully.\n\nCalculated points:': 'Mapa raster creado correctamente.\n\nPuntos calculados:',
    'Raster map created successfully.\n\nCalculated points: ': 'Mapa raster creado correctamente.\n\nPuntos calculados: ',
    'Raster map:': 'Mapa raster:',
    'Raster saved successfully': 'Raster guardado correctamente',
    'Rated power [MW]': 'P_nom [MW]',
    'Rathmann — compatibility model for older settings. No user parameters in this experimental release.': 'Rathmann — modelo de compatibilidad para configuraciones antiguas. Sin parámetros de usuario en esta versión experimental.',
    'Raw attenuation magnitudes are shown': 'Se muestran magnitudes brutas de atenuación',
    'Raw attenuation magnitudes are shown (not the algebraic sign inside the equation).': 'Se muestran magnitudes brutas de atenuación (no el signo algebraico dentro de la ecuación).',
    'Ready': 'Listo',
    'Receiver': 'Receptor',
    'Receiver ID': 'ID Receptor',
    'Receiver height': 'Altura de receptor',
    'Receiver height [m]:': 'Altura de receptor [m]:',
    'Receiver layer not found in the current QGIS project.': 'No se encontró la capa de receptores en el proyecto QGIS actual.',
    'Receiver layer not found.': 'No se encontró la capa de receptores.',
    'Receiver layer:': 'Capa de receptores:',
    'Receiver limit [dB(A)]:': 'Límite receptor [dB(A)]:',
    'Receiver/source layers, and the noise raster map if enabled, have been added to the project.': 'Las capas de receptores/fuentes y, si estaba activado, el mapa raster de ruido se han añadido al proyecto.',
    'Receivers': 'Receptores',
    'Recommended auto': 'Auto recomendado',
    'Recommended auto chooses a safe option based on wake/rotor-average. LinearSum is conservative; SquaredSum is common for dense arrays; WeightedSum is reserved for compatible Gaussian models.': 'Auto recomendado elige una opción segura según wake/rotor-average. LinearSum es conservador; SquaredSum es habitual en arrays densos; WeightedSum se reserva para modelos gaussianos compatibles.',
    'Recommended auto: the plugin chooses a safe superposition according to the active wake deficit and rotor-average model.': 'Auto recomendado: el plugin elige una superposición segura según el wake deficit y el rotor-average activo.',
    'Recommended by default': 'Recomendado por defecto',
    'Recommended options by case': 'Opciones recomendadas según el caso',
    'Recommended review': 'Revisión recomendada',
    'Redraws the boundary of the selected wind resource.': 'Vuelve a dibujar el perímetro del recurso eólico seleccionado.',
    'Reference limit': 'Límite de referencia',
    'Refresh': 'Actualizar',
    'Refresh boundary': 'Actualizar perímetro',
    'Refresh the detected layers/models state in the project.': 'Refresca el estado de capas/modelos detectados en el proyecto.',
    'Refresh the list of editable turbine layers.': 'Actualizar lista de capas de turbinas editables.',
    'Regenerate filtered TIF': 'Regenerar TIF filtrado',
    'Regenerating filtered raster': 'Regenerando raster filtrado',
    'Regulatory compliance': 'Cumplimiento Normativo',
    'Relative humidity': 'Humedad relativa',
    'Remove selected layer': 'Quitar capa seleccionada',
    'Removes the temporary resource-boundary layer from the project.': 'Elimina del proyecto la capa temporal del perímetro del recurso.',
    'Report': 'Informe',
    'Report exported': 'Informe exportado',
    'Resolution [m]:': 'Resolución [m]:',
    'Resource boundary': 'Perímetro del recurso',
    'Resource boundary not available in this installation.': 'Perímetro del recurso no disponible en esta instalación.',
    'Resource boundary removed from the map.': 'Perímetro del recurso eliminado del mapa.',
    'Resource:': 'Recurso:',
    'Results': 'Resultados',
    'Results layer created successfully.': 'Capa de resultados creada correctamente.',
    'Return to the main Velantis Wind dialog without losing your edits.': 'Vuelve al diálogo principal de Velantis Wind sin perder la edición.',
    'Return to the main dialog without leaving interactive mode.': 'Vuelve al diálogo principal sin salir del modo interactivo.',
    'Rotor Diameter [m]': 'Diámetro de rotor [m]',
    'Rotor Diameter: rotor diameter [m]': 'Rotor Diameter: diámetro de rotor [m]',
    'Rotor averaging calculates wind speed at several points of the rotor disk, not only at the centre. More points add realism when a wake cuts part of the rotor, but increase computation time.': 'El rotor-average calcula el viento en varios puntos del disco del rotor, no solo en el centro. Más puntos dan más realismo cuando una estela corta parte del rotor, pero aumentan el tiempo de cálculo.',
    'Rotor averaging increases fidelity by sampling the rotor disk, at the cost of more computation time.': 'El rotor-average incrementa la fidelidad al muestrear el disco del rotor, a costa de más tiempo de cálculo.',
    'Rotor averaging means averaging wind speed at several rotor points instead of looking at one central point. More points = more realism and more computation.': 'Rotor-average significa promediar el viento en varios puntos del rotor en vez de mirar un único punto central. Más puntos = más realismo y más coste.',
    'Rotor diameter:': 'Diámetro rotor:',
    'Rotor-average model': 'Modelo rotor-average',
    'Rotor-average model (PyWake)': 'Modelo rotor-average (PyWake)',
    'Rotor-disk averaging (rotor-average)': 'Promedio sobre el rotor (rotor-average)',
    'RotorCenter is fast; CGIRotorAvg(7) is the recommended balanced option; CGIRotorAvg(9/21) increases accuracy and computation time.': 'RotorCenter es rápido; CGIRotorAvg(7) es el equilibrio recomendado; CGIRotorAvg(9/21) aumenta precisión y coste computacional.',
    'RotorCenter: uses one single point at the rotor centre. It is fast for tests and maps, but it does not represent wind-speed variation inside the disk.': 'RotorCenter: usa un único punto en el centro del rotor. Es rápido para pruebas y mapas, pero no representa la variación de velocidad dentro del disco.',
    'Rule of thumb': 'Regla práctica',
    'Run an AEP calculation first. Then you can save it as scenario A or B.': 'Primero lanza un cálculo AEP. Después podrás guardarlo como escenario A o B.',
    'SHADOW FLICKER CALCULATION SUMMARY': 'RESUMEN DEL CÁLCULO DE SHADOW FLICKER',
    'STF2005 — original Frandsen model. Fixed empirical coefficients. Useful for comparison with older or more conservative settings.': 'STF2005 — Frandsen original. Coeficientes empíricos fijos. Útil para comparar con configuraciones antiguas o más conservadoras.',
    'STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Fixed empirical coefficients from the paper. <b>Recommended default</b>.': 'STF2017 — Frandsen 2007 / IEC 61400-1 ed.3. Coeficientes empíricos fijos del paper. <b>Recomendado por defecto</b>.',
    'Save a clean table with one row per receiver and the columns needed for consultancy.': 'Guarda una tabla limpia con una fila por receptor y las columnas necesarias para consultoría.',
    'Save a clean table with one row per turbine: coordinates, model, net/free-flow AEP and main losses.': 'Guarda una tabla limpia con una fila por aerogenerador: coordenadas, modelo, AEP neto/bruto y pérdidas principales.',
    'Save only the receivers exceeding their acoustic limit.': 'Guarda solo los receptores que superan su límite acústico.',
    'Save per-turbine results': 'Guardar resultados por turbina',
    'Save scenario A': 'Guardar escenario A',
    'Save scenario A and scenario B first.': 'Guarda primero un escenario A y un escenario B.',
    'Save scenario A and scenario B first.\n\nRecommended workflow: calculate the base case → Save scenario A → change layout/model/physics → calculate again → Save scenario B → Compare A/B.': 'Guarda primero un escenario A y un escenario B.\n\nFlujo recomendado: calcula el caso base → Guardar escenario A → cambia layout/modelo/física → calcula otra vez → Guardar escenario B → Comparar A/B.',
    'Save scenario B': 'Guardar escenario B',
    'Save summary, models, receivers and exceedances in a single Excel workbook.': 'Guarda resumen, modelos, receptores y excedencias en un único libro Excel.',
    'Save the AEP summary with the Velantis Wind logo and corporate formatting as an HTML file.': 'Guarda el resumen AEP con logo Velantis Wind y formato corporativo en un archivo HTML.',
    'Save the latest AEP calculation as scenario A for later comparison.': 'Guarda el último cálculo AEP como escenario A para compararlo después.',
    'Save the latest AEP calculation as scenario B for later comparison.': 'Guarda el último cálculo AEP como escenario B para compararlo después.',
    'Save the points edited in the interactive map to CSV.\nDefault: <original>_edited.csv (does not overwrite the original CSV).': 'Guardar a CSV los puntos editados en el mapa interactivo.\nDefault: <original>_edited.csv (no sobrescribe el CSV original).',
    'Save the technical summary as HTML or TXT.': 'Guarda el resumen técnico en HTML o TXT.',
    'Saves the latest calculation as scenario A.': 'Guarda el último cálculo como escenario A.',
    'Saves the latest calculation as scenario B.': 'Guarda el último cálculo como escenario B.',
    'Scenario comparison': 'Comparador de escenarios',
    'Screening templates: they fill in D, hub height, rated power and a generic editable curve. They do not replace the official manufacturer curve.': 'Plantillas de screening: rellenan D, hub, potencia nominal y una curva genérica editable. No sustituyen la curva oficial del fabricante.',
    'Second TI-sensitivity coefficient. It is only applied if the PyWake class accepts cTI.': 'Segundo coeficiente de sensibilidad a TI. Solo se aplicará si la clase PyWake acepta cTI.',
    'Second TI/CT coefficient in the TurboGaussian formulation.': 'Segundo coeficiente TI/CT en la formulación TurboGaussian.',
    'Sector loss rose.': 'Rosa de pérdidas por sector.',
    'Sector totals:': 'Total sectores:',
    'Select TXT/CSV…': 'Selecciona TXT/CSV…',
    'Select a CSV with X,Y columns for this model': 'Selecciona CSV con columnas X,Y para este modelo',
    'Select a CSV with the model X,Y coordinates': 'Selecciona CSV con coordenadas X,Y del modelo',
    'Select a WAsP/Surfer folder or a WRG/ZIP to draw the boundary.': 'Selecciona una carpeta WAsP/Surfer o un WRG/ZIP para dibujar el perímetro.',
    'Select a folder with WAsP grids': 'Selecciona carpeta con grids WAsP',
    'Select a folder with WAsP/Surfer grids compatible with PyWake.': 'Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake.',
    'Select a folder with WAsP/Surfer grids compatible with PyWake. If the resource is already prepared in pywake_compat format, the plugin will prioritize it automatically.': 'Selecciona una carpeta con grids WAsP/Surfer compatibles con PyWake. Si el recurso ya viene preparado en formato pywake_compat, el plugin lo priorizará automáticamente.',
    'Select a receiver layer.': 'Selecciona una capa de receptores.',
    'Select a turbine layer.': 'Selecciona una capa de turbinas.',
    'Select a valid TXT/CSV file.': 'Selecciona un archivo TXT/CSV válido.',
    'Select a valid WAsP folder or WRG/ZIP before plotting.': 'Selecciona una carpeta WAsP válida o un WRG/ZIP antes de graficar.',
    'Select a valid WAsP folder or a WRG (.wrg/.zip).': 'Selecciona una carpeta WAsP válida o un WRG (.wrg/.zip).',
    'Select a valid WAsP folder or a WRG/ZIP file.': 'Selecciona una carpeta WAsP válida o un WRG/ZIP.',
    'Select a valid receiver layer.': 'Selecciona una capa válida de receptores.',
    'Select all': 'Marcar todas',
    'Select an IANA time zone manually.': 'Selecciona manualmente una zona horaria IANA.',
    'Select at least one layer to calculate.': 'Selecciona al menos una capa para calcular.',
    'Select how wakes from several turbines are combined before calculating the effective wind speed.': 'Selecciona cómo se combinan las estelas de varias turbinas antes de calcular la velocidad efectiva.',
    'Select one or more .wrg files or .zip files containing .wrg files.': 'Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos.',
    'Select one or more .wrg files or .zip files containing .wrg files. If you provide several heights (e.g. 90 m and 100 m), PyWake will interpolate by height if needed.': 'Selecciona aquí uno o varios .wrg o .zip con archivos .wrg internos. Si aportas varias alturas (p.ej. 90m y 100m), PyWake interpolará en altura si hace falta.',
    'Select one or more ambient-TI .asc/.tif raster(s) (e.g. from Vortex). If none is provided, TI=10% is used as fallback.': 'Selecciona uno o varios raster(s) .asc/.tif de TI ambiente (p.ej. de Vortex). Si no hay ninguno, se usa TI=10% como fallback.',
    'Select one or more source layers detected from Energy. If you select several, the noise calculation will use all of them at once.': 'Selecciona una o varias capas fuente detectadas desde Energía. Si marcas varias, el cálculo de ruido usará todas ellas a la vez.',
    'Select output folder': 'Selecciona carpeta de salida',
    'Select the point layer with receiver locations': 'Selecciona la capa de puntos con las ubicaciones de receptores',
    'Select the turbine layers you want to include in the wake maps.\nCheck several layers if the wind farm combines different turbine models.': 'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.\nMarca varias capas si el parque combina distintos modelos de turbina.',
    'Select the turbine layers you want to include in the wake maps.  Check several layers if the wind farm combines different turbine models.': 'Selecciona las capas de aerogeneradores que quieres incluir en los mapas de estelas.  Marca varias capas si el parque combina distintos modelos de turbina.',
    'Select the turbine layers you want to include in this calculation.\nYou can check several layers to simulate a wind farm with several turbine models.': 'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.\nPuedes marcar varias capas para simular un parque con varios modelos de turbina.',
    'Select the turbine layers you want to include in this calculation.  You can check several layers to simulate a wind farm with several turbine models at once.': 'Selecciona las capas de aerogeneradores que quieres incluir en este cálculo.  Puedes marcar varias capas para simular un parque con varios modelos de turbina a la vez.',
    'Select the turbine-coordinate layer from the Energy module': 'Selecciona la capa de coordenadas de turbinas del módulo de energía',
    'Select the work module. The three modules are available: Energy (AEP and wakes), Noise, and Shadow Flicker.': 'Selecciona el módulo de trabajo. Los tres módulos están operativos: Energía (AEP y wakes), Ruido, y Sombras (shadow flicker).',
    'Select which limit field applies to each receiver. It does not change the noise physics, only the evaluation criterion.': 'Selecciona qué campo de límite se aplica a cada receptor. No cambia la física del ruido, solo el criterio de evaluación.',
    'Selected file:': 'Fichero seleccionado:',
    'SelfSimilarityDeficit — classic self-similarity formulation. It does not require user parameters in this experimental release.': 'SelfSimilarityDeficit — formulación self-similarity clásica. No requiere parámetros de usuario en esta versión experimental.',
    'SelfSimilarityDeficit2020 — main blockage option. It does not require user parameters in this experimental release.': 'SelfSimilarityDeficit2020 — opción principal para bloqueo. No requiere parámetros de usuario en esta versión experimental.',
    'Semicolon': 'Punto y coma',
    'September': 'Septiembre',
    'Several TI raster(s) at different heights': 'Varios raster(s) TI a distintas alturas',
    'Several TI raster(s) were selected, but at least two different heights could not be identified in their names. The plugin will not be able to reconstruct a reliable vertical TI; check names such as TImean.90.asc or TI_120m.tif.': 'Se han seleccionado varios raster(s) TI, pero no he podido identificar al menos dos alturas distintas en sus nombres. El plugin no podrá reconstruir una TI vertical fiable; revisa nombres como TImean.90.asc o TI_120m.tif.',
    'Several layers/models are selected, but PyWake could not enable type_i. Wakes will be drawn with the first model as fallback.': 'Hay varias capas/modelos seleccionados, pero PyWake no ha podido activar type_i. Las estelas se dibujarán con el primer modelo como fallback.',
    'Shadow\nflicker': 'Sombras y\nparpadeo',
    'Shadow Flicker - Calculation Summary': 'Shadow Flicker - Resumen del cálculo',
    'Shadow Flicker Calculation Summary': 'Resumen del cálculo de shadow flicker',
    'Shadow Flicker Hours by Month and Hour of Day': 'Horas de shadow flicker por mes y hora del día',
    'Shadow Flicker Statistics': 'Estadísticas de Shadow Flicker',
    'Shadow Statistics:': 'Estadísticas de sombra:',
    'Shadow calculation running': 'Cálculo de sombras en ejecución',
    'Shadow flicker calculation module for wind turbines. Connected to the project layout, with point-receiver calculation and QGIS output layers with detailed results by receiver.': 'Módulo de cálculo de shadow flicker para aerogeneradores. Conectado al layout del proyecto, con cálculo por receptores puntuales y capas de salida QGIS con resultados detallados por receptor.',
    'ShadowPage compatibility calculation entry point is missing.': 'Falta el punto de entrada de compatibilidad del cálculo de ShadowPage.',
    'Sheet': 'Hoja',
    'Show resource boundary on the map': 'Mostrar perímetro del recurso en el mapa',
    'Simple note: this engine calculates interactions downstream and upstream. To avoid PyWake errors with some combinations, the plugin automatically sets use_effective_ws=True for wake and blockage calculations.': 'Nota sencilla: este motor calcula interacciones hacia abajo y hacia arriba. Para que PyWake no falle con algunas combinaciones, el plugin activa automáticamente use_effective_ws=True en estelas y bloqueo.',
    'Simplified acoustic calculation for screening': 'Cálculo acústico simplificado para screening',
    'Simplified global LwA model, fixed absorption, empirical ground effect': 'Modelo simplificado LwA global, absorción fija, efecto suelo empírico',
    'Single TI raster': 'Un solo raster TI',
    'Site Configuration': 'Configuración del emplazamiento',
    'Site Configuration:': 'Configuración del emplazamiento:',
    'Site configuration': 'Configuración del emplazamiento',
    'Site latitude (e.g. 42.465 for Logroño)': 'Latitud del emplazamiento (ej. 42.465 para Logroño)',
    'Site longitude (e.g. -2.445 for Logroño)': 'Longitud del emplazamiento (ej. -2.445 para Logroño)',
    'Slope of the Gaussian wake width σ(x). PyWake default ≈ 0.0324555.': 'Pendiente del ancho σ(x) de la estela gaussiana. Default PyWake ≈ 0.0324555.',
    'Small or widely spaced onshore wind farms': 'Parques onshore pequeños o muy espaciados',
    'Solar elevation:': 'Elevación solar:',
    'Source': 'Fuente',
    'Source group': 'Grupo fuente',
    'Source_groups': 'Grupos_fuente',
    'Space': 'Espacio',
    'Specific yield': 'Producción específica',
    'Spectrum source': 'Origen espectro',
    'Speed': 'Velocidad',
    'Sponsorship does not grant ownership or exclusive control over the roadmap. Final technical decisions remain with the maintainer to preserve the quality and open-source direction of the project.': 'El patrocinio no otorga propiedad ni control exclusivo sobre el roadmap. Las decisiones técnicas finales se mantienen en el mantenedor para preservar la calidad y la dirección open source del proyecto.',
    'SquaredSum uses the square root of the sum of squares; it is a common option for dense arrays and avoids adding deficits as aggressively as LinearSum.': 'SquaredSum usa raíz de suma de cuadrados; es una opción habitual para arrays densos y evita sumar déficits de forma tan agresiva como LinearSum.',
    'SquaredSum: combines several wakes quadratically. It usually gives a more balanced behaviour when several wakes overlap.': 'SquaredSum: combina varias estelas de forma cuadrática. Suele dar un comportamiento más equilibrado cuando varias estelas se solapan.',
    'Starting shadow-flicker calculation...': 'Iniciando cálculo de shadow flicker...',
    'Status': 'Estado',
    'Summary': 'Resumen',
    'Summary table for consultancy: acoustic results per receiver, compliance, dominant source and main attenuations. The internal pairwise DTM diagnostics are kept in memory, but are not shown by default.': 'Tabla resumida para consultoría: resultados acústicos por receptor, cumplimiento, fuente dominante y atenuaciones principales. Los diagnósticos internos MDT por pares se conservan en memoria, pero no se muestran por defecto.',
    'Superposition': 'Superposición',
    'Superposition decides how to combine several wakes on the same turbine. SquaredSum is usually balanced; LinearSum accumulates more losses; MaxSum only takes the dominant wake.': 'La superposición decide cómo sumar varias estelas sobre una misma turbina. SquaredSum suele ser una opción equilibrada; LinearSum acumula más pérdidas; MaxSum solo toma la estela dominante.',
    'Superposition defines how to add several wakes when one turbine is behind more than one upstream turbine.': 'La superposición dice cómo sumar varias estelas cuando una turbina está detrás de más de una turbina.',
    'Support VelantisWind': 'Apoyar VelantisWind',
    'Support maintenance, documentation and future open-source development.': 'Apoya el mantenimiento, la documentación y el desarrollo open source futuro.',
    'Support maintenance, documentation, testing and future open-source development.': 'Apoya el mantenimiento, la documentación, las pruebas y el desarrollo open source futuro.',
    'TECHNICAL NOISE IMPACT REPORT': 'INFORME TÉCNICO DE IMPACTO ACÚSTICO',
    'TI does not change the base wake deficit much': 'la TI no cambia mucho el déficit base del wake',
    'TI does not significantly change AEP in the standard configuration.': 'la TI no cambia el AEP de forma importante en la configuración estándar.',
    'TI height override [m]:': 'Alturas TI override [m]:',
    'TI heights per file [m]:': 'Alturas de TI por archivo [m]:',
    'TI impact [MWh]': 'Impacto TI [MWh]',
    'TI is ambient turbulence intensity: a measure of how variable or mixed the wind is when it reaches the rotor. If you do not load a TI raster, the manual value above is used as a uniform assumption. TI does not always change AEP strongly: it is mainly visible in TI-sensitive models such as Niayifar, Zong, TurboGaussian or TurboNOJ. TI raster(s) will be reprojected to the current project CRS': "La TI es la intensidad de turbulencia ambiente: una medida de lo variable o 'mezclado' que llega el viento al rotor. Si no cargas raster TI, se usa el valor manual de arriba como hipótesis uniforme. La TI no siempre cambia mucho el AEP: se nota sobre todo en modelos TI-sensitive como Niayifar, Zong, TurboGaussian o TurboNOJ. Los raster(s) TI se reproyectarán al CRS del proyecto actual",
    'TI raster(s) (optional):': 'TI raster(s) (opcional):',
    'TI raster(s) not found:': 'Raster(s) TI no encontrado(s):',
    'TI raster(s) not selected → automatic fallback to': 'Raster(s) TI no seleccionado(s) → fallback automático a',
    'TI really affects': 'TI sí influya',
    'TI turbulence raster(s) (optional):': 'Raster(s) de turbulencia TI (opcional):',
    'TI-dependent expansion coefficient in the Zong formulation.': 'Coeficiente de expansión dependiente de TI en la formulación Zong.',
    'TI-sensitive Gaussian option': 'opción gaussiana sensible a TI',
    'TI/turbulence impact': 'Impacto TI/turbulencia',
    'TI/turbulence impact:': 'Impacto TI/turbulencia:',
    'TXT/CSV file:': 'Fichero TXT/CSV:',
    'Tab': 'Tabulación',
    'Technical report generated from the Velantis Wind Energy module.': 'Informe técnico generado desde el módulo Energía de Velantis Wind.',
    'Temperature': 'Temperatura',
    'Templates are generic for pre-screening. For final delivery, use the official manufacturer/client curve.': 'Las plantillas son genéricas para pre-screening. Para entrega final usa la curva oficial del fabricante/cliente.',
    'Term': 'Término',
    'Terrain effect': 'Efecto del terreno',
    "The CSV must have 'X' and 'Y' headers (or easting/northing).": "El CSV debe tener cabeceras 'X' y 'Y' (o easting/northing).",
    'The DTM/DSM is not in the same CRS as the project.': 'El MDT/DSM no está en el mismo CRS que el proyecto.',
    'The DTM/DSM is not in the same CRS as the project. Reproject it before calculating.': 'El MDT/DSM no está en el mismo CRS que el proyecto. Reproyéctalo antes de calcular.',
    'The QGIS/Qt GUI is not available in this environment.': 'La GUI de QGIS/Qt no está disponible en este entorno.',
    'The TI raster(s) do not exist:': 'El/los raster(s) TI no existen:',
    'The WAsP/Surfer folder does not exist.': 'La carpeta WAsP/Surfer no existe.',
    'The WRG dataset does not contain valid x/y coordinates': 'El dataset WRG no contiene coordenadas x/y válidas',
    'The WRG/ZIP file does not exist:': 'El archivo WRG/ZIP no existe:',
    'The ZIP does not contain any .wrg:': 'El ZIP no contiene ningún .wrg:',
    'The acoustic limit is not valid.': 'El límite acústico no es válido.',
    'The active layer is not a point layer.': 'La capa activa no es de puntos.',
    'The active layer is not a vector layer.': 'La capa activa no es vectorial.',
    'The ambient-TI-only variant could not be computed for the TI-driven model; the main AEP is kept. Error: ': 'No se pudo calcular la variante TI ambiente only para el modelo TI-driven; se mantiene el AEP principal. Error: ',
    'The combination of a TI-driven wake with SelfSimilarity2020 did not converge in PyWake. Blockage was kept by replacing it with': 'La combinación de wake TI-driven con SelfSimilarity2020 no convergió en PyWake. Se ha mantenido el bloqueo sustituyéndolo por',
    'The combined WindTurbines for several models could not be built. The first model will be used as fallback.': 'No se pudo construir el WindTurbines combinado para varios modelos. Se usará el primer modelo como fallback.',
    'The delta is calculated as B − A.': 'El delta se calcula como B − A.',
    'The detailed explanation': 'La explicación detallada',
    'The energy module factory was not found.': 'No se ha encontrado la factoría del módulo de energía.',
    'The file does not exist:': 'El archivo no existe:',
    "The following Python dependencies are missing to run Velantis Wind:": "Faltan las siguientes dependencias de Python para ejecutar Velantis Wind:",
    'The grid extents have no valid intersection.': 'Las extensiones de los grids no tienen intersección válida.',
    'The ground factor G must be between 0 and 1.': 'El factor de suelo G debe estar entre 0 y 1.',
    'The ground layer is not a valid vector layer and will be ignored.': 'La capa de suelo no es vectorial válida y se ignorará.',
    'The land-use layer is not in the same CRS as the project.': 'La capa de uso del suelo no está en el mismo CRS que el proyecto.',
    'The land-use layer is not in the same CRS as the project. Reproject it before calculating.': 'La capa de uso del suelo no está en el mismo CRS que el proyecto. Reproyéctala antes de calcular.',
    'The land-use layer must be a polygonal vector layer.': 'La capa de uso del suelo debe ser vectorial poligonal.',
    'The land-use layer must be polygonal.': 'La capa de uso del suelo debe ser poligonal.',
    'The layout coordinates could not be resolved: the per-model CSVs are missing and there is no valid base folder to look for turbines.csv.': 'No se pudieron resolver las coordenadas del layout: faltan los CSV por modelo y no existe una carpeta base válida para buscar turbines.csv.',
    'The main simulation could not run with the requested combination and PyWake required automatic degradation:': 'La simulación principal no pudo ejecutarse con la combinación pedida y PyWake requirió la degradación automática:',
    'The maximum radius must be greater than 0.': 'El radio máximo debe ser mayor que 0.',
    'The maximum shadow distance must be greater than 0 m.': 'La distancia máxima de sombra debe ser mayor que 0 m.',
    'The minimum distance must be greater than 0.': 'La distancia mínima debe ser mayor que 0.',
    'The model internal constants are not exposed because they are fixed by the reference paper.': 'Las constantes internas del modelo no se exponen porque están fijadas por el paper de referencia.',
    'The point layer was created successfully.': 'La capa de puntos se creó correctamente.',
    'The project does not have a valid CRS.': 'El proyecto no tiene un CRS válido.',
    'The raster map is being generated in the background.\n\nResolution:': 'El mapa raster se está generando en segundo plano.\n\nResolución:',
    'The raster resolution must be greater than 0 m.': 'La resolución del raster debe ser mayor que 0 m.',
    'The raster resolution must be greater than 0.': 'La resolución del raster debe ser mayor que 0.',
    'The raster time step must be greater than 0 minutes.': 'El paso temporal del raster debe ser mayor que 0 minutos.',
    'The raster was created but could not be loaded in QGIS.': 'El raster se creó pero no se pudo cargar en QGIS.',
    'The raster(s) will be reprojected to the current project CRS': 'Los raster(s) se reproyectarán al CRS del proyecto actual',
    'The receiver height is not valid.': 'La altura de receptor no es válida.',
    "The receiver layer '": "La capa de receptores '",
    'The receiver layer contains no valid features for calculation.': 'La capa de receptores no contiene elementos válidos para el cálculo.',
    'The receiver layer is empty.': 'La capa de receptores está vacía.',
    'The receiver layer is not in the same CRS as the project.': 'La capa de receptores no está en el mismo CRS que el proyecto.',
    'The receiver time step must be greater than 0 minutes.': 'El paso temporal de receptores debe ser mayor que 0 minutos.',
    'The selected DEM/DSM input is not a raster layer.': 'La entrada DEM/DSM seleccionada no es una capa raster.',
    'The selected DEM/DSM layer was not found; refresh the shadow module.': 'No se encontró la capa DEM/DSM seleccionada; actualiza el módulo de sombras.',
    'The selected DEM/DSM raster is not valid.': 'El raster DEM/DSM seleccionado no es válido.',
    'The selected DTM/DSM is not a valid raster and will be ignored.': 'El MDT/DSM seleccionado no es raster válido y se ignorará.',
    'The selected language will be applied to the hub, modules, warnings and generated summaries.': 'El idioma seleccionado se aplicará al hub, módulos, avisos y resúmenes generados.',
    'The selected layer': 'La capa seleccionada',
    'The selected receiver layer has no features.': 'La capa de receptores seleccionada no tiene elementos.',
    'The selected receiver layer is not a vector layer.': 'La capa de receptores seleccionada no es una capa vectorial.',
    'The selected turbine layer has no features.': 'La capa de turbinas seleccionada no tiene elementos.',
    'The selected turbine layer is not a vector layer.': 'La capa de turbinas seleccionada no es una capa vectorial.',
    'The selected turbulence model could not be instantiated; the plugin attempted a safe fallback (STF2017/STF2005/GCL).': 'No se pudo instanciar el modelo de turbulencia seleccionado; el plugin ha intentado un fallback seguro (STF2017/STF2005/GCL).',
    'The simulation could not run exactly with the requested combination and PyWake required automatic degradation.': 'La simulación no pudo ejecutarse exactamente con la combinación pedida y PyWake requirió una degradación automática.',
    'The simulation could not run with the selected models. Last error:': 'No se pudo ejecutar la simulación con los modelos seleccionados. Último error:',
    'The simulation required automatic degradation:': 'La simulación requirió degradación automática:',
    'The turbine layer must contain point geometries.': 'La capa de turbinas debe contener geometrías de punto.',
    'The values shown below are the attenuation magnitudes used by the model. In the main equation these terms are subtracted from the source level.': 'Los valores mostrados a continuación son las magnitudes de atenuación usadas por el modelo. En la ecuación principal estos términos se restan al nivel de fuente.',
    'The wake effect reduces the wind available behind each turbine. Each model approximates that loss differently.': 'El wake effect reduce el viento disponible detrás de cada turbina. Cada modelo aproxima esa pérdida de forma distinta.',
    'The wind resource describes the wind arriving at the wind farm before wake effects: directional frequency, wind speeds and, depending on the format, Weibull parameters. You can use WAsP/Surfer grids or WRG/ZIP. If you provide WRG files at several heights, PyWake can interpolate by height when needed.': 'El recurso eólico describe el viento que llega al parque antes de aplicar estelas: frecuencia por dirección, velocidades y, según el formato, parámetros Weibull. Puedes usar WAsP/Surfer grids o WRG/ZIP. Si aportas varios WRG a distintas alturas, PyWake podrá interpolar por altura cuando sea necesario.',
    'The α coefficient is not valid.': 'El coeficiente α no es válido.',
    'There are duplicated WRG files for height': 'Hay WRG duplicados para la altura',
    'There are no DTM screening rows to export.': 'No hay filas de screening MDT para exportar.',
    'There are no Top receivers rows to export.': 'No hay filas de Top receivers para exportar.',
    'There are no active receiver layers by category.': 'No hay capas activas de receptores por categoría.',
    'There are no edited layouts pending export.': 'No hay layouts editados pendientes de exportar.',
    'There are no per-turbine data to export.': 'No hay datos por turbina para exportar.',
    'There are no receiver rows to export.': 'No hay filas de receptores para exportar.',
    'There are no valid acoustic sources to create the raster.': 'No hay fuentes acústicas válidas para crear el raster.',
    'There are no valid coordinates in the provided layers/CSV.': 'No hay coordenadas válidas en las capas/CSV proporcionados.',
    'There are no valid turbine layers with points to calculate.\n\nCreate a layer with “Generate point layers”, define its turbine model and add at least one turbine on the map.': 'No hay capas de turbinas válidas con puntos para calcular.\n\nCrea una capa con «Generar capas de puntos», define su modelo de turbina y añade al menos un aerogenerador en el mapa.',
    'There are several hub heights but only a vertically uniform ambient TI; the calculation is still valid, although it is an approximation in height.': 'Hay varios hub heights pero solo una TI ambiente verticalmente uniforme; el cálculo sigue siendo válido, aunque es una aproximación en altura.',
    'There is already a turbine at that point.': 'Ya existe una turbina en ese punto.',
    'There is no acoustic model configuration.': 'No hay configuración acústica de modelos.',
    'There is no receiver layer to export.': 'No hay capa de receptores para exportar.',
    'There is no source layer to export.': 'No hay capa de fuentes para exportar.',
    'There is no turbine near the click.': 'No hay turbina cerca del clic.',
    'There is no valid active turbine layer for the interactive map.': 'No hay una capa de turbinas válida activa para el mapa interactivo.',
    'There was no resource boundary to clear.': 'No había perímetro del recurso que limpiar.',
    'These parameters calibrate the wake shape. For normal use, leave them as PyWake default values.': 'Estos parámetros calibran la forma de la estela. Para un usuario normal, déjalos en Valor PyWake por defecto.',
    'These parameters modify the wake shape. Leave them as “PyWake default value” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.': 'Estos parámetros modifican la forma de la estela. Déjalos en «Valor PyWake por defecto» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.',
    'These parameters modify the wake shape. Leave them as “PyWake default” unless you are doing sensitivity analysis, validation against another software tool or technical calibration.': 'Estos parámetros modifican la forma de la estela. Déjalos en «PyWake default» salvo que estés haciendo una sensibilidad, validación contra otro software o calibración técnica.',
    'This field is only used with': 'Este campo solo se usa con',
    'This selector calculates wake-added turbulence. It does not replace the ambient TI from the site/WRG.': 'Este selector calcula turbulencia añadida por estela. No sustituye a la TI ambiente del site/WRG.',
    'This selector calculates wake-added turbulence. It does not replace the site/WRG ambient TI.': 'Este selector calcula turbulencia añadida por estela. No sustituye a la TI ambiente del site/WRG.',
    'This table mirrors the per-turbine summary visible in the interface. For GIS/Excel analysis, also use the per-turbine CSV generated by the AEP workflow.': 'Esta tabla replica el resumen por turbina visible en la interfaz. Para análisis GIS/Excel, usa también el CSV por turbina que genera el flujo AEP.',
    'This turbine has no curve stored in its metadata. Press “Define…” again to regenerate it.': 'Esta turbina no tiene la curva guardada en metadatos. Vuelve a pulsar «Definir…» para regenerarla.',
    'Time basis:': 'Base temporal:',
    'Time step': 'Paso temporal',
    'Time step ONLY for the raster (independent from receivers).': 'Paso temporal SOLO para el raster (independiente de los receptores).',
    'Time step ONLY for the raster (independent from receivers).\n1 min  = maximum accuracy, VERY slow\n5 min  = recommended (accuracy >98%, 5x faster)\n10 min = very fast, accuracy ~95%\n20 min = ultra-fast for previewing': 'Paso temporal SOLO para el raster (independiente de los receptores).\n1 min  = máxima precisión, MUY lento\n5 min  = recomendado (precisión >98%, 5x más rápido)\n10 min = muy rápido, precisión ~95%\n20 min = ultrarrápido para previsualizar',
    'Time step [min]:': 'Paso temporal [min]:',
    'Time step:': 'Paso temporal:',
    "Time zone '{}' does not exist in the bundled IANA database.": "La zona '{}' no existe en la base IANA incluida.",
    'Time zone: not automatically detected.': 'Zona horaria: no detectada automáticamente.',
    'Tip': 'Consejo',
    'To adjust AEP sensitivity to turbulence': 'Para mover la sensibilidad del AEP a turbulencia',
    'To include blockage, change the engine to': 'Si quieres incluir bloqueo, cambia el motor a',
    'Top receivers': 'Top receptores',
    'Topographic diffraction': 'Difracción topográfica',
    'Total AEP balance': 'Balance total AEP',
    'Total balance check': 'Comprobación del balance total',
    'Total level': 'Nivel total',
    'Total loss': 'Pérd. total',
    'Total losses': 'Pérdidas totales',
    'Total losses:': 'Pérdidas totales:',
    'Turbine availability must be between 0 and 1.': 'La disponibilidad de turbina debe estar entre 0 y 1.',
    'Turbine availability:': 'Disponibilidad de turbina:',
    'Turbine centroid:': 'Centroide de turbinas:',
    'Turbine created (manual). Press OK to use it.': 'Turbina creada (manual). Pulsa Aceptar para usarla.',
    'Turbine created from TXT/CSV. Press OK to use it.': 'Turbina creada desde TXT/CSV. Pulsa Aceptar para usarla.',
    'Turbine layer not found in the current QGIS project.': 'No se encontró la capa de turbinas en el proyecto QGIS actual.',
    'Turbine layer not found.': 'No se encontró la capa de turbinas.',
    'Turbine layer:': 'Capa de turbinas:',
    'Turbine model configuration': 'Configuración de modelos de turbina',
    'Turbine sound power': 'Potencia sonora de la turbina',
    'Turbine template': 'Plantilla de turbina',
    'Turbines': 'Turbinas',
    'TurboGaussian: Gaussian model sensitive to effective TI. It makes sense when ambient and added turbulence should influence AEP.': 'TurboGaussian: gaussiano sensible a TI efectiva. Tiene sentido cuando la turbulencia ambiente y añadida deben influir en el AEP.',
    'TurboNOJ: light NOJ-like version, but with more sensitivity to turbulence and wake recovery.': 'TurboNOJ: versión ligera tipo NOJ, pero con más sensibilidad a turbulencia y recuperación de estela.',
    'Turbulence losses': 'Pérdidas turbulencia',
    'Turbulence model': 'Modelo de turbulencia',
    'Turbulence model (PyWake)': 'Modelo de turbulencia (PyWake)',
    'Type': 'Tipo',
    'Typical magnitude in AEP': 'Magnitud típica en AEP',
    'Uniform grid · EqGridRotorAvg': 'Malla uniforme · EqGridRotorAvg',
    'Unnamed project': 'Proyecto sin nombre',
    'Unrecognized superposition key:': 'Clave de superposición no reconocida:',
    'Unsupported WRG extension (use .wrg or .zip):': 'Extensión WRG no soportada (use .wrg o .zip):',
    'Unsupported WRG extension. Use .wrg or .zip:': 'Extensión WRG no soportada. Usa .wrg o .zip:',
    'Unsupported extension (use .wrg or .zip):': 'Extensión no soportada (use .wrg o .zip):',
    'Unsupported extension:': 'Extensión no soportada:',
    'Use': 'Usar',
    'Use active project layout': 'Usar layout activo del proyecto',
    'Use multiple cores to speed up the calculation\nExperimental - may cause errors on some systems\nRecommended: disabled (more stable)': 'Usa varios núcleos para acelerar el cálculo\nExperimental: puede causar errores en algunos sistemas\nRecomendado: desactivado (más estable)',
    'Use several receiver layers by category': 'Usar varias capas de receptores por categoría',
    'Useful for comparison with older or more conservative configurations.': 'Útil para comparar con configuraciones antiguas o más conservadoras.',
    'User selection': 'Selección del usuario',
    'You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group.': 'Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo.',
    'You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve per group. If you enable curve mode, the plugin will evaluate the emission at a specific wind speed or for the worst case.': 'Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.',
    'Acoustic result:': 'Resultado acústico:',
    'Valid configuration. You can proceed with the calculation.': 'Configuración válida. Puedes continuar con el cálculo.',
    'Valid points:': 'Puntos válidos:',
    'Value': 'Valor',
    'Value [dB]': 'Valor [dB]',
    'Variantes: wake-only / wake+TI / wake+bloqueo…': 'Variantes: solo estela / estela+TI / estela+bloqueo…',
    'Velantis Wind · AEP technical report': 'Velantis Wind · Informe técnico AEP',
    'Velantis Wind · Main hub': 'Velantis Wind · Hub principal',
    'Velantis Wind · Noise calculation': 'Velantis Wind · Cálculo de ruido',
    'Velantis Wind · preparing noise': 'Velantis Wind · preparando ruido',
    'VelantisWind is an open-source QGIS plugin for early-stage wind farm assessment. If the tool saves you time or adds value to your workflow, you can support its maintenance or request professional support.': 'VelantisWind es un plugin open source para QGIS orientado a la evaluación preliminar de parques eólicos. Si la herramienta te ahorra tiempo o aporta valor a tu flujo de trabajo, puedes apoyar su mantenimiento o solicitar soporte profesional.',
    'Very accurate · CGIRotorAvg(21)': 'Muy realista · CGIRotorAvg(21)',
    'Very accurate: CGIRotorAvg(21) can be much more expensive; use it for final validation.': 'Very accurate: CGIRotorAvg(21) puede ser bastante más caro; úsalo para validaciones finales.',
    'Very realistic · CGIRotorAvg(21)': 'Muy realista · CGIRotorAvg(21)',
    'Very short practical guide': 'Guía práctica muy corta',
    'View curve': 'Ver curva',
    'View the power curve (and Ct if available) of the defined turbine.\nUseful to detect loading errors (kW vs W, swapped columns, etc.).': 'Ver la curva de potencia (y Ct si está disponible) de la turbina definida.\nÚtil para detectar errores de carga (kW vs W, columnas invertidas, etc.).',
    'VortexCylinder — induction/blockage model based on a vortex cylinder. No user parameters in this experimental release.': 'VortexCylinder — modelo de inducción/bloqueo basado en cilindro de vorticidad. Sin parámetros de usuario en esta versión experimental.',
    'VortexDipole — induction alternative. No user parameters in this experimental release.': 'VortexDipole — alternativa de inducción. Sin parámetros de usuario en esta versión experimental.',
    'WARNING:': 'AVISO:',
    'WAsP/Surfer boundary drawn: common domain of': 'Perímetro WAsP/Surfer dibujado: dominio común de',
    'WAsP/Surfer boundary drawn: envelope of': 'Perímetro WAsP/Surfer dibujado: envolvente de',
    'WAsP/Surfer grids:': 'Grids WAsP/Surfer:',
    'WRG TI:': 'TI WRG:',
    'WRG boundary drawn:': 'Perímetro WRG dibujado:',
    'WRG files with different dx: they cannot be combined by height': 'WRG con distinto dx: no se pueden combinar por altura',
    'WRG files with different origin x0/y0: they cannot be combined by height': 'WRG con distinto origen x0/y0: no se pueden combinar por altura',
    'WRG files with different resolution/extent: they cannot be combined by height': 'WRG con distinta resolución/extent: no se pueden combinar por altura',
    'WRG line too short': 'Línea WRG demasiado corta',
    'WRG reader not available in this installation': 'Lector WRG no disponible en esta instalación',
    'WRG reader not available; the WRG boundary cannot be drawn.': 'Lector WRG no disponible; no se puede dibujar el perímetro WRG.',
    'WRG turbulence': 'Turbulencia WRG',
    'WRG with no points (header only)': 'WRG sin puntos (sólo cabecera)',
    'WRG/ZIP file': 'Archivo WRG/ZIP',
    'WRG: a single ambient TI raster was used; if you mix several hub heights, that TI is replicated/interpolated as a vertical approximation.': 'WRG: se ha usado un único raster de TI ambiente; si mezclas varios hub heights, esa TI se replica/interpela como aproximación vertical.',
    'WRG: no TI raster was provided, so the ambient TI was set to the user-defined fallback value as a uniform field.': 'WRG: no se ha proporcionado raster TI, así que la TI ambiente se ha fijado al valor fallback definido por el usuario como campo uniforme.',
    'WT model': 'Modelo WT',
    'WT model(s) ·': 'modelo(s) WT ·',
    'Wake deficit model (PyWake)': 'Modelo de pérdida por estela (wake deficit)',
    'Wake loss [%]': 'Pérd. estela [%]',
    'Wake loss [MWh]': 'Pérd. estela [MWh]',
    'Wake losses': 'Pérdidas por estela',
    'Wake losses [%]': 'Pérd. estelas [%]',
    'Wake losses [MWh]': 'Pérd. estelas [MWh]',
    'Wake losses by sector': 'Pérdidas por estela por sector',
    'Wake losses:': 'Pérdidas por estelas:',
    'Wake map (': 'Mapa de Estelas (',
    'Wake model: wind-speed loss': 'Modelo de estela: pérdida de velocidad',
    'Wake superposition model (PyWake)': 'Combinación de varias estelas (superposición)',
    'Wake-loss model (wake deficit)': 'Modelo de pérdida por estela (wake deficit)',
    'Wakes:': 'Estelas:',
    'Warning: the model is TI-sensitive, but added turbulence is set to None; ambient TI will be used, but wake-generated TI will not be added.': 'Aviso: el modelo es sensible a TI, pero has dejado la turbulencia añadida en Ninguno; se usará la TI ambiente, pero no se sumará TI generada por estelas.',
    'WeightedSum (Gaussian only)': 'WeightedSum (solo gaussianos)',
    'WeightedSum (advanced · Gaussian)': 'WeightedSum (avanzado · Gaussian)',
    'WeightedSum is advanced and reserved for compatible Gaussian deficits; if PyWake rejects the combination, the solver will automatically degrade it.': 'WeightedSum es avanzado y se reserva para déficits gaussianos compatibles; si PyWake no acepta la combinación, el solver degradará automáticamente.',
    'WeightedSum: advanced option for compatible Gaussian models. If PyWake rejects the combination, the plugin will fall back to a safe option.': 'WeightedSum: opción avanzada para gaussianos compatibles. Si PyWake rechaza la combinación, el plugin volverá a una opción segura.',
    'What blockage is and when it matters': 'Qué es el bloqueo y cuándo importa',
    'What does the support help fund?': '¿Qué ayuda a financiar el apoyo?',
    'What each physical parameter of the selected model means': 'Qué significa cada parámetro físico del modelo seleccionado',
    'What is advisable depending on the wind farm': 'Qué conviene según el parque',
    'What to choose in the turbulence-model selector': 'Qué elegir en el selector de modelo de turbulencia',
    'What turbulence means in this plugin': 'Qué significa la turbulencia en este plugin',
    'When it is worth providing TI': 'Cuándo merece la pena aportar TI',
    'When it matters significantly for AEP': 'Cuándo importa mucho en el AEP',
    'Which blockage model do you want to use?': '¿Qué modelo de bloqueo quieres usar?',
    'Which blockage model should be used?': '¿Qué modelo de bloqueo quieres usar?',
    'Which model do you want to use?': '¿Cómo debe propagar PyWake las estelas entre turbinas?',
    'Which rotor-average model do you want to use?': '¿Qué modelo de rotor-average quieres usar?',
    'Which turbulence model do you want to use?': '¿Qué modelo de turbulencia quieres usar?',
    'Which turbulence model should be used?': '¿Qué modelo de turbulencia quieres usar?',
    'Which wake model do you want to use?': '¿Cómo quieres calcular la pérdida de viento detrás de cada turbina?',
    'Why the model internal constants are not exposed': 'Por qué no se exponen las constantes internas del modelo',
    'Wind farm AEP summary': 'Resumen AEP del parque',
    'Wind resource': 'Recurso eólico',
    'Wind resource (WAsP grids / WRG)': 'Recurso eólico (WAsP grids / WRG)',
    'Wind speed (m/s):': 'Velocidad viento (m/s):',
    'Wind turbine models': 'Modelos de aerogenerador',
    'Wind turbines': 'Aerogeneradores',
    'Wind source-receiver model': 'Modelo fuente-receptor eólico',
    'With PUD, use_effective_ws=True is forced to avoid PyWake errors.': 'Con PUD se fuerza use_effective_ws=True para evitar errores de PyWake.',
    'With PropagateDownwind, blockage is automatically disabled.': 'Con PropagateDownwind, el bloqueo se desactiva automáticamente.',
    'With PropagateDownwind, blockage is disabled automatically.': 'Con PropagateDownwind, el bloqueo se desactiva automáticamente.',
    'Workers (cores):': 'Workers (núcleos):',
    'Worst-case LwA(ws) acoustic curves': 'Curvas acústicas LwA(ws) en peor caso',
    'Writing results to the QGIS layers…': 'Volcando resultados a las capas QGIS…',
    'Year:': 'Año:',
    'Yes': 'Sí',
    'You can now regenerate TIFs filtered by month/hour': 'Ahora puedes regenerar TIF filtrados por mes/hora',
    'You currently left the turbulence model as None': 'Ahora mismo has dejado el modelo de turbulencia en Ninguno',
    'You have already reached the maximum of': 'Ya has alcanzado el máximo de',
    'You have defined several hub heights but have not selected TI raster(s). The calculation will continue with a uniform TI =': 'Has definido varios hub heights pero no has seleccionado raster(s) TI. El cálculo seguirá con TI uniforme =',
    'You have moved/added/deleted turbines in the interactive map.\nThe AEP calculation uses those live coordinates (not the original CSV).\nPress “Export edited layout…” if you want to persist them to CSV.': 'Has movido/añadido/borrado turbinas en el mapa interactivo.\nEl cálculo de AEP usa esas coordenadas vivas (no el CSV original).\nPulsa «Exportar layout editado…» si quieres persistirlas a CSV.',
    'You selected PropagateDownwind: PyWake does not calculate blockage with this engine and the plugin has disabled it automatically.': 'Has seleccionado PropagateDownwind: PyWake no calcula bloqueo con este engine y el plugin lo ha desactivado automáticamente.',
    'Zong: advanced and TI-sensitive. Useful for studying near wake and turbulence-driven recovery in more detail.': 'Zong: avanzado y sensible a TI. Útil para estudiar near wake y recuperación por turbulencia con más detalle.',
    'ZongGaussianDeficit requires an added-turbulence model; STF2017 was forced.': 'ZongGaussianDeficit requiere un modelo de turbulencia añadida; se ha forzado STF2017.',
    'a single ambient TI raster was used': 'se ha usado un único raster de ambient TI',
    'a valid DTM profile could not be extracted for the dominant path': 'no se pudo extraer un perfil MDT válido para el trayecto dominante',
    'a1 in k = a1·TI + a2': 'a1 en k = a1·TI + a2',
    'a2 in k = a1·TI + a2': 'a2 en k = a1·TI + a2',
    'acoustic curves': 'curvas acústicas',
    'active': 'activo',
    'adjust TI/turbulence to change AEP.': 'ajusta TI/turbulencia para mover el AEP.',
    'adjust the': 'ajusta la',
    'advanced TI-sensitive option': 'opción avanzada y sensible a TI',
    'all Energy-module layers': 'todas las capas del módulo de energía',
    'all receivers are outside the maximum radius': 'todos los receptores han quedado fuera del radio máximo',
    'ambient TI': 'TI ambiente',
    'ambient TI raster(s) to reconstruct TI by height when possible.': 'raster(s) de TI ambiente para reconstruir TI por altura cuando es posible.',
    'ambient turbulence': 'turbulencia ambiente',
    'applied to all paths': 'aplicado en todos los trayectos',
    'as an alternative model with weaker coupling to the effective TI. This preserves the blockage-loss component while keeping wake, rotor-average and the turbulence model intact.': 'como modelo alternativo de acoplamiento más débil con la TI efectiva. Esto preserva la partida de pérdidas por bloqueo manteniendo intactos wake, rotor-average y modelo de turbulencia.',
    'atmospheric absorption': 'absorción atmosférica',
    'auto-adjusted': 'autoajustada',
    'auto-adjusted resolution': 'resolución autoajustada',
    'because the engine is': 'porque el motor es',
    'behind the turbines': 'detrás de las turbinas',
    'but wake-added TI will not explicitly enter the calculation.': 'pero la TI añadida por estela no entrará explícitamente.',
    'calculates the': 'calcula la',
    'calibrations from the reference paper': 'calibraciones del paper de referencia',
    'can be sensitive to TI, but it ran without an added-turbulence model; the sensitivity is limited to the available ambient TI.': 'puede ser sensible a la TI, pero se ha ejecutado sin modelo de turbulencia añadida; la sensibilidad queda limitada a la TI ambiente disponible.',
    'can use ambient/effective TI to open the wake; the active turbulence model (': 'puede usar la TI ambiente/efectiva para abrir la estela; el modelo de turbulencia activo (',
    'caused by the turbine itself': 'causada por la propia turbina',
    'cells | requested resolution': 'celdas | resolución pedida',
    'cells, effective resolution': 'celdas, resolución efectiva',
    'cells. Effective resolution': 'celdas. Resolución efectiva',
    'check the help dialog to choose a physically consistent combination.': 'consulta la ayuda para elegir una combinación físicamente coherente.',
    'classic analytical fit': 'ajuste analítico clásico',
    'classic and simple alternative': 'alternativa clásica y sencilla',
    'classic empirical fit. Fixed paper constants.': 'ajuste empírico clásico. Constantes fijas del paper.',
    'clean offshore': 'offshore limpio',
    'compliance': 'de cumplimiento',
    'compliance over covered receivers': 'cumplimiento sobre receptores cubiertos',
    'computationally very light': 'muy ligera computacionalmente',
    'coverage': 'de cobertura',
    'covered': 'cubiertos',
    'covered receivers': 'receptores cubiertos',
    'created': 'creada',
    'critical receiver': 'receptor crítico',
    'day': 'día',
    'decides whether ambient/effective TI actually changes the deficit': 'decide si esa TI ambiente/efectiva cambia realmente el déficit',
    'default': 'por defecto',
    'deficit_cls is None (a valid deficit model could not be imported).': 'deficit_cls es None (no se pudo importar un deficit model válido).',
    'describes the': 'describe la',
    'detect it in the file name': 'detectarla en el nombre del archivo',
    'did not converge in PyWake (the iteration between a TI-driven wake and SS2020 blockage does not always reach a stable fixed point). Blockage was kept using': 'no convergió en PyWake (la iteración entre wake TI-driven y bloqueo SS2020 no siempre alcanza un punto fijo estable). Se ha mantenido el bloqueo usando',
    'different hub heights': 'alturas de buje distintas',
    'dipole-type potential-flow solution': 'solución potencial tipo dipolo',
    'does NOT calculate blockage': 'NO calcula bloqueo',
    'dominant band Hz': 'banda dom. Hz',
    'dominant model': 'modelo dominante',
    'dominant source LwA dB(A)': 'LwA fuente dom. dB(A)',
    'dominant source group': 'grupo fuente dom.',
    'dominant source layer': 'capa fuente dom.',
    'dominant turbine acoustic z m': 'z acústica turb. dom. m',
    'dominant wind farm': 'parque dom.',
    'duplicated name(s). It is advisable to rename them for export and traceability.': 'nombre(s) duplicados. Conviene renombrarlos para exportación y trazabilidad.',
    'enter the height manually': 'escribir la altura manualmente',
    "error creating superposition '": "error creando superposición '",
    'exceed the limit': 'superan el límite',
    'exceeds limit': 'supera límite',
    'expected lines; there are': 'líneas esperadas; hay',
    'failed:': 'falló:',
    'fast (<5 s for 100 receivers)': 'rápido (<5 seg para 100 receptores)',
    'faster than SS2020': 'más rápido que SS2020',
    'fixed LwA by acoustic source group': 'LwA fijo por grupo fuente acústico',
    'from layer': 'desde capa',
    'from receiver fields': 'desde campos de receptor',
    'generic': 'genérica',
    'geometrical divergence': 'divergencia geométrica',
    'global mode': 'modo global',
    'good Gaussian option for onshore': 'buena opción gaussiana para onshore',
    'good general option': 'buena opción general',
    'good general option for': 'buena opción general para',
    'grid(s) with no common intersection.': 'grid(s) sin intersección común.',
    'ground effect': 'efecto suelo',
    'group(s) with a valid LwA.': 'grupo(s) con LwA válido.',
    'group(s) with criterion': 'grupo(s) con criterio',
    'h/year': 'h/año',
    'has no coordinates. Add turbines in the interactive map or select a valid X,Y CSV.': 'no tiene coordenadas. Añade turbinas en el mapa interactivo o selecciona un CSV X,Y válido.',
    'height(s) for': 'altura(s) para',
    'hours/year': 'horas/año',
    'if needed.': 'si hace falta.',
    'if the engine supports it': 'si el motor lo soporta',
    'impact on AEP is usually limited': 'impacto sobre el AEP suele ser limitado',
    'inactive': 'no activo',
    'includes blockage by iterating all interactions': 'incluye bloqueo iterando todas las interacciones',
    'includes blockage with bidirectional propagation': 'incluye bloqueo con propagación bidireccional',
    'interpolate by height': 'interpolar por altura',
    'invalid coords_xy for «': 'coords_xy inválido para «',
    'is in the information button': 'está en el botón de información',
    'is outside the typical range used as a reference in many preliminary studies': 'está fuera del rango típico usado como referencia en muchos estudios preliminares',
    'it is advisable to enable a model here.': 'conviene activar un modelo aquí.',
    'it is usually a robust and stable choice': 'suele ser una elección robusta y estable',
    'its base deficit does not change much with TI': 'su déficit base no cambia mucho con la TI',
    'k (wake expansion)': 'k (expansión de estela)',
    'k and σ are derived from TI': 'k y σ se derivan de la TI',
    'k is derived from local TI': 'k se deriva de la TI local',
    'land use / effective G by polygons still uses QGIS layer intersections': 'uso del suelo / G efectivo por polígonos todavía usa intersecciones de capas QGIS',
    'large-scale screening': 'Screening masivo',
    'layer(s) selected from the interactive map.': 'capa(s) seleccionada(s) desde el mapa interactivo.',
    'layer(s) with': 'capa(s) con',
    'layout comparison and quick studies': 'comparación entre layouts y estudios rápidos',
    'lightweight alternative': 'alternativa ligera',
    'limit dB(A)': 'límite dB(A)',
    'limit margin dB': 'margen límite dB',
    'loss': 'pérdida',
    'losses': 'pérdidas',
    'm\nTime step:': 'm\nPaso temporal:',
    'm\nYou can continue working in QGIS.\n\nYou will be notified when it finishes.': 'm\nPuedes seguir trabajando en QGIS.\n\nSe te avisará cuando termine.',
    'm; it will be applied to all WRG heights as an approximation.': 'm; se aplicará a todas las alturas del WRG como aproximación.',
    'max Abar dB': 'Abar máx. dB',
    'max-Abar turbine acoustic z m': 'z acústica turb. max Abar m',
    'medium (~8x slower than the fast engine)': 'medio (~8x más lento que motor rápido)',
    'model layer(s)': 'capa(s) de modelo',
    'model layer(s) ·': 'capa(s) de modelo ·',
    'models have valid configuration.': 'modelos tienen configuración válida.',
    'models. Increase the limit in the code if you need more turbine families.': 'modelos. Aumenta el límite en el código si necesitas más familias de aerogeneradores.',
    'more advanced option within the Gaussian family': 'opción más avanzada dentro de la familia gaussiana',
    'more conservative in the near field': 'más conservador en near-field',
    'more sensitivity to TI': 'más sensibilidad a la TI',
    'more)</span>': 'más)</span>',
    'mosaic: empty input list': 'mosaic: lista de entrada vacía',
    'names that do not match py_wake in:': 'nombres que no casan con py_wake en:',
    'near-field (vortex) and far-field (self-similarity) blend.': 'mezcla near-field (vortex) y far-field (self-similarity).',
    'near-wake region': 'zona de near wake',
    'nearest turbine dist. (m)': 'dist. turbina cercana (m)',
    'neighboring turbines': 'turbinas vecinas',
    'night': 'noche',
    'no DTM was available on this path': 'no había MDT disponible en este trayecto',
    'no TI raster has been provided.': 'no se ha proporcionado un raster de TI.',
    'no blockage + no turbulence': 'sin bloqueo + sin turbulencia',
    'no explicit k slope.': 'sin pendiente k explícita.',
    'no extension': 'sin extensión',
    'no relevant topographic obstacle was detected on the dominant path': 'no se detectó obstáculo topográfico relevante en el trayecto dominante',
    'no rotor-average + no blockage': 'sin rotor-average + sin bloqueo',
    'no rotor-average + no blockage + no turbulence': 'sin rotor-average + sin bloqueo + sin turbulencia',
    'no rotor-average + no turbulence': 'sin rotor-average + sin turbulencia',
    'no sources within the radius': 'sin fuentes dentro del radio',
    'no valid points': 'sin puntos válidos',
    'no value': 'sin valor',
    'no. turbines': 'nº turbinas',
    'non-standard model; check availability in this PyWake version.': 'modelo no estándar; revisar disponibilidad en esta versión de PyWake.',
    'not created': 'no creada',
    'not defined': 'sin definir',
    'not generated': 'no generado',
    'not site-specific knobs': 'no perillas de sitio',
    'only propagates wakes downstream, it does not support blockage': 'solo propaga estelas hacia abajo, no soporta bloqueo',
    'openpyxl is not available in this QGIS environment. Use CSV exports or install openpyxl.': 'openpyxl no está disponible en este entorno de QGIS. Usa las exportaciones CSV o instala openpyxl.',
    'optional': 'opcional',
    'or select a valid X,Y CSV.': 'o selecciona un CSV X,Y válido.',
    'or switch to a TI-driven wake model': 'o cambia a un wake model TI-driven',
    'physical representation of the resource improves significantly': 'representación física del recurso mejora bastante',
    'polygons': 'polígonos',
    'post-2020 industrial default': 'default industrial post-2020',
    'power kW column (0-based):': 'Columna potencia kW (0-based):',
    'preliminary assessments': 'evaluaciones preliminares',
    'quick screening': 'cribado rápido',
    'range': 'rango',
    'ready to edit on the map': 'lista para editar en el mapa',
    'receiver acoustic z m': 'z acústica receptor m',
    'receiver id': 'id receptor',
    'receiver terrain z m': 'z terreno receptor m',
    'receiver(s)': 'receptor(es)',
    'receiver(s) with G different from the global value and': 'receptor(es) con G distinto del global y',
    'receivers exceed limits': 'receptores superan límites',
    'receivers outside the radius': 'receptores fuera de radio',
    'receivers within the radius': 'receptores dentro del radio',
    'receptors to:': 'receptores a:',
    'recommended for benchmarking, quick studies and many clean offshore cases.': 'recomendado para benchmarking, estudios rápidos y muchos casos offshore limpios.',
    'recommended for fast screening and large wind farms when speed is the priority.': 'recomendado para screening rápido y parques grandes cuando prima la velocidad.',
    'recommended when you want a Gaussian model': 'recomendable cuando quieres un modelo gaussiano',
    'reference option': 'opción de referencia',
    'relief was detected, but below the conservative activation threshold': 'se detectó relieve, pero por debajo del umbral conservador de activación',
    'resource input datum': 'dato de entrada del recurso',
    'review': 'revisar',
    'rough terrain': 'terreno rugoso',
    'rows. The CSV export contains the full detail.</small></p>': 'filas. La exportación CSV contiene el detalle completo.</small></p>',
    'screening': 'apantallamiento',
    'seconds': 'segundos',
    'sensitivity analysis': 'análisis de sensibilidad',
    'serious consulting deliverable': 'entrega de consultoría seria',
    'several TI layers': 'varias capas TI',
    'single value': 'valor único',
    'spectrum': 'espectro',
    'speed': 'rapidez',
    'speed and robustness are the priority': 'prima la velocidad y la robustez',
    'status': 'estado',
    'the calculation only uses the resource ambient TI': 'el cálculo solo usa la TI ambiente del recurso',
    'the calculation will continue': 'el cálculo seguirá',
    'the calculation will not include upstream velocity reduction.': 'el cálculo no incluirá la reducción de velocidad upstream.',
    'the entered atmospheric pressure': 'la presión atmosférica introducida',
    'the line of sight between the dominant turbine and this receiver is clear according to the DTM': 'la línea de visión entre la turbina dominante y este receptor queda despejada según el MDT',
    'the plugin remains usable': 'el plugin sigue siendo usable',
    'the raster was requested but could not be generated': 'se solicitó el raster pero no se pudo generar',
    'the table in section 2 shows attenuation magnitudes for traceability. The resulting level should not be interpreted as a direct subtraction from a single turbine: it is obtained through energetic summation across bands and contributing sources within the calculation radius.': 'la tabla del apartado 2 muestra magnitudes de atenuación para trazabilidad. El nivel resultante no debe interpretarse como una resta directa de una sola turbina: se obtiene mediante suma energética por bandas y suma de las fuentes contribuyentes dentro del radio de cálculo.',
    'total level dB(A)': 'nivel total dB(A)',
    'turbine(s)': 'turbina(s)',
    'turbine(s).': 'turbina(s).',
    'turbines are outside the domain and will be omitted (e.g.:': 'turbinas están fuera del dominio y se omitirán (ej.:',
    'type': 'tipo',
    'uniform fallback': 'fallback uniforme',
    'uniformly as fallback.': 'uniforme como fallback.',
    'unnamed': 'sin nombre',
    'upstream': 'aguas arriba',
    'useful if you want to keep a light NOJ-style model': 'útil si quieres mantener un modelo ligero tipo NOJ',
    'user override': 'override usuario',
    'velocity reduction': 'reducción de velocidad',
    'versus': 'frente a',
    'vertical approximation': 'aproximación vertical',
    'wake losses': 'pérdidas por estela',
    'wake recovery': 'recuperación de la estela',
    'wake-added turbulence': 'turbulencia añadida por estela',
    'when mixing different heights': 'cuando mezclas distintas alturas',
    'wind direction': 'dirección del viento',
    'wind farm': 'parque',
    'wind farm performance': 'rendimiento del parque',
    'wind farms with relevant ambient turbulence': 'parques con turbulencia ambiente relevante',
    'wind turbine model(s)': 'modelo(s) de aerogenerador',
    'worst-case acoustic curves': 'curvas acústicas en peor caso',
    'ws column (0-based):': 'Columna ws (0-based):',
    'xarray is required to use WRG with PyWake': 'xarray es necesario para usar WRG con PyWake',
    'xarray not available to build TI by height:': 'xarray no disponible para construir TI por altura:',
    'year': 'año',
    'yes': 'sí',
    'yes · requested resolution ': 'sí · resolución pedida ',
    '· invalid (': '· inválida (',
    '· local civil time with DST': '· hora civil local con DST',
    'α is calculated automatically per band in the ISO engine': 'α se calcula automáticamente por banda en Motor ISO',
    'α used:': 'α usado:',
    'δw/D (near-wake source length)': 'δw/D (longitud fuente near-wake)',
    'λ parameter used in the Vermeulen near-wake length.': 'Parámetro λ usado en la longitud near-wake de Vermeulen.',
    '— No DEM (flat terrain) —': '— Sin DEM/MDT (terreno plano) —',
    '— No land-use layer —': '— Sin capa de uso del suelo —',
    '— Select receiver layer —': '— Selecciona capa de receptores —',
    '— Select turbine layer —': '— Selecciona capa de turbinas —',
    '• Acoustic sources: will use 1 source layer manually selected from Inputs.': '• Fuentes acústicas: se usará 1 capa de fuentes seleccionada manualmente en Entradas.',
    '• Aground represents the simplified loss associated with the ground/terrain effect in the acoustic method.': '• Aground representa la pérdida simplificada asociada al efecto suelo/terreno en el método acústico.',
    '• At least one receiver layer must be configured in the multi-layer table.': '• Falta configurar al menos una capa de receptores en la tabla multi-capa.',
    '• Current method: wind source-receiver acoustic calculation for consultancy (Lp = LwA - Adiv - Aatm - Aground). It can also create GIS layers of sources, dominant links, receivers outside the radius, a raster noise map and isophones.': '• Método actual: cálculo acústico fuente-receptor para consultoría eólica (Lp = LwA - Adiv - Aatm - Aground). Además puede crear capas GIS de fuentes, enlaces dominantes, receptores fuera de radio, mapa de ruido ráster e isófonas.',
    '• DEM/DSM was used to sample terrain elevation at sources and receivers.': '• Se usó DEM/DSM para muestrear la elevación del terreno en fuentes y receptores.',
    '• GIS receivers-outside-radius layer created: Noise · Receivers fuera de radio (': '• Capa GIS de receptores fuera de radio creada: Noise · Receivers fuera de radio (',
    "• Ground/terrain: from land-use layer '": "• Suelo/terreno: desde capa de uso del suelo '",
    '• Ground/terrain: layer mode enabled but no valid layer; the global G will be used =': '• Suelo/terreno: modo capa activado pero sin capa válida; will use G global =',
    '• Heavy calculation launched as a QgsTask. You can keep using QGIS while it runs.': '• Cálculo pesado lanzado como QgsTask. Puedes seguir usando QGIS mientras avanza.',
    '• Isophones finished in the background:': '• Isófonas terminadas en segundo plano:',
    '• Isophones: they will be generated once the background raster is finished.': '• Isófonas: se generarán al finalizar el raster en segundo plano.',
    '• Linear attenuation α:': '• Atenuación lineal α:',
    '• LinearSum: linear sum; conservative in dense wind farms.': '• LinearSum: suma lineal; conservador en parques densos.',
    '• MaxSum: only the most severe wake dominates; useful for diagnostics.': '• MaxSum: solo domina la estela más severa; útil para diagnóstico.',
    '• No DEM/DSM: calculation uses flat coordinates with relative source/receiver heights.': '• Sin DEM/DSM: el cálculo usa coordenadas planas con alturas relativas de fuente/receptor.',
    '• No DTM/DSM selected. The acoustic calculation will start in flat coordinates with relative source/receiver heights.': '• Sin MDT/DSM seleccionado. El cálculo acústico arrancará en coordenadas planas con alturas relativas de fuente/receptor.',
    '• No per-model coordinate layers were detected. Generate the layout from Energy first.': '• No se han detectado capas de coordenadas por modelo. Genera primero el layout desde Energía.',
    "• Noise raster: launched in the background with QgsTask. Per-receiver results are already available; the 'Noise · Map' layer will be added when it finishes.": "• Raster de ruido: lanzado en segundo plano con QgsTask. Los resultados por receptor ya están disponibles; la capa 'Noise · Map' se añadirá al terminar.",
    '• Per-layer limit criterion:': '• Criterio de límite por capa:',
    '• Receiver height: taken from each layer configured in the multi-layer table.': '• Altura de receptor: se toma de cada capa configurada en la tabla multi-capa.',
    '• Receiver limit: taken from each layer configured in the multi-layer table.': '• Límite de receptor: se toma de cada capa configurada en la tabla multi-capa.',
    '• Receivers with at least one turbine inside the radius:': '• Receptores con al menos una turbina dentro del radio:',
    '• Recommended auto: safe plugin selection.': '• Auto recomendado: selección segura del plugin.',
    '• SquaredSum: square root of the sum of squares; a good practical option for dense arrays.': '• SquaredSum: raíz de suma de cuadrados; buena opción práctica para arrays densos.',
    '• The receiver layer matches the turbine layout. Choose a different dwellings/receivers layer.': '• La capa de receptores coincide con el layout de turbinas. Elige una capa distinta de viviendas/receptores.',
    '• The receiver layer must be selected. For this workflow, a point or polygon layer of dwellings/receivers is recommended.': '• Falta seleccionar la capa de receptores. Para este flujo se recomienda una capa de puntos o polígonos de viviendas/receptores.',
    '• Acoustic consultancy parameters: receiver limit, isophones and ground factor G for a quick review of compliance and terrain influence.': '• Parámetros de consultoría acústica: límite de receptor, isófonas y factor de suelo G para revisión rápida de cumplimiento e influencia del terreno.',
    '• WARNING: all receivers ended up outside the maximum radius. Check the radius, layout or receiver locations.': '• AVISO: todos los receptores han quedado fuera del radio máximo. Revisa radio, layout o ubicación de receptores.',
    '• WARNING: the raster was requested but could not be generated. Check the resolution, the CRS and the source extent.': '• AVISO: se solicitó el raster pero no se pudo generar. Revisa la resolución, el CRS y el extent de las fuentes.',
    '• WARNING: this configuration uses the synchronous path because ': '• AVISO: esta configuración usa ruta síncrona porque ',
    '• WeightedSum: advanced, intended for compatible Gaussian models and more sensitive to cost/compatibility.': '• WeightedSum: avanzado, pensado para modelos gaussianos compatibles y más sensible al coste/compatibilidad.',
    '← Home': '← Inicio',
    '● edited on map, unsaved': '● editado en mapa, sin guardar',
    '♡ Support VelantisWind': '♡ Apoyar VelantisWind',
    '⚠️ No model has valid configuration (Hub Height and Rotor Diameter).': '⚠️ Ningún modelo tiene configuración válida (Hub Height y Rotor Diameter).',
    '⚠️ No models detected. Check that turbine layers exist.': '⚠️ No se detectaron modelos. Comprueba que existen capas de turbinas.',
    '⚠️ No receiver layer selected.': '⚠️ No hay capa de receptores seleccionada.',
    '⚠️ The turbine layer is empty.': '⚠️ La capa de turbinas está vacía.',
    '⚡ Fast - simplified global LwA': '⚡ Rápido - LwA global simplificado',
    '⛅ Atmospheric conditions (ISO engine)': '⛅ Condiciones Atmosféricas (Motor ISO)',
    '✅ Valid configuration. You can proceed with the calculation.': '✅ Configuración válida. Puedes continuar con el cálculo.',
    '✓ Max shadow distance:': '✓ Distancia máxima de sombra:',
    '✓ Site coordinates: (': '✓ Coordenadas del emplazamiento: (',
    '✓ Turbine geometry to be used in the shadow calculation:': '✓ Geometría de turbina que se usará en el cálculo de sombras:',
    '✓ Turbine layer:': '✓ Capa de turbinas:',
    '❌ Incomplete configuration. Check the flagged items.': '❌ Configuración incompleta. Revisa los elementos marcados.',
    '💡 Configure hub height and rotor diameter for each detected model. These parameters are required for the shadow flicker calculation.': '💡 Configura la altura de buje y el diámetro de rotor de cada modelo detectado. Estos parámetros son necesarios para el cálculo de shadow flicker.',
    '💡 Use typical site conditions': '💡 Usa condiciones típicas del emplazamiento',
    '💡 Use typical site conditions. If you do not have measured pressure, 101.325 kPa is a standard sea-level reference; at altitude it is usually lower.': '💡 Usa condiciones típicas del emplazamiento. Si no tienes presión medida, 101.325 kPa es una referencia estándar a nivel del mar; en altitud suele ser menor.',
    '💡 You can now regenerate TIFs filtered by month/hour\n   without recalculating (using the filter combos).': '💡 Ahora puedes regenerar TIF filtrados por mes/hora\n   sin recalcular (usando los desplegables de filtro).',
    '📊 Export 12×24 CSV': '📊 Exportar CSV 12×24',
    '📊 ISO-aligned - ISO 9613-2 by bands': '📊 ISO-aligned - ISO 9613-2 por bandas',
}
for _e in _CANON_TO_ES:        # English strings must NOT key the EN map
    _TO_EN.pop(_e, None); _FRAGMENT_TO_EN.pop(_e, None)
for _s in _CANON_TO_EN:        # Spanish strings must NOT key the ES map
    _TO_ES.pop(_s, None); _FRAGMENT_TO_ES.pop(_s, None)
_TO_EN.update(_CANON_TO_EN); _FRAGMENT_TO_EN.update(_CANON_TO_EN)
_TO_ES.update(_CANON_TO_ES); _FRAGMENT_TO_ES.update(_CANON_TO_ES)


# ---------------------------------------------------------------------------
# Iteration 11: labels used by file EXPORTS (e.g. shadow 12x24 CSV) so exported
# files follow the selected language. EXACT keys only (NOT fragment maps) to
# avoid substring collisions like 'Latitud' inside 'Latitude'.
# ---------------------------------------------------------------------------
_I18N_ITER11_TO_ES = {
    "Latitude": "Latitud",
    "Longitude": "Longitud",
    "Year": "Año",
    "Timezone": "Zona horaria",
    "Availability": "Disponibilidad",
    "Max shadow distance": "Distancia máxima de sombra",
    "Solar elevation limits": "Límites de elevación solar",
    "Number of turbines": "Número de turbinas",
    "Hub height used": "Altura de buje usada",
    "Rotor diameter used": "Diámetro de rotor usado",
    "Hour": "Hora",
    "SUMMARY": "RESUMEN",
    "Total h/year": "Total h/año",
    "Max minutes/day": "Máx minutos/día",
    "Max shadow date": "Fecha máx de sombra",
    "Days affected": "Días afectados",
    "Receiver:": "Receptor:",
}
# Exact maps only (tr_text checks these before falling back to fragments).
_TO_ES.update(_I18N_ITER11_TO_ES)
for _k, _v in _I18N_ITER11_TO_ES.items():
    if _v != _k:
        _TO_EN[_v] = _k


# ---------------------------------------------------------------------------
# Iteration 12: small leftover UI strings + visible build marker.
# ---------------------------------------------------------------------------
_I18N_ITER12_TO_ES = {
    "Sun too low → ignore (typical: 3°)": "Sol demasiado bajo → ignorar (típico: 3°)",
    "Detected WT models:": "Modelos WT detectados:",
}
_TO_ES.update(_I18N_ITER12_TO_ES); _FRAGMENT_TO_ES.update(_I18N_ITER12_TO_ES)
for _k, _v in _I18N_ITER12_TO_ES.items():
    if _v != _k:
        _TO_EN[_v] = _k; _FRAGMENT_TO_EN[_v] = _k

# Build marker so it is easy to confirm which i18n build QGIS actually loaded.


# ---------------------------------------------------------------------------
# Iteration 13: Energy module grey helper/status labels under paths and model
# options.  These are often generated dynamically, so we keep exact fragments
# short and conservative to avoid touching file paths or layer names.
# ---------------------------------------------------------------------------
_I18N_ITER13_ENERGY_GREY_TO_EN = {
    # Wind-resource metadata below selected paths
    "Malla:": "Grid:",
    "sectores=": "sectors=",
    "Raster(s) TI:": "TI raster(s):",
    "CRS destino previsto:": "expected target CRS:",
    " · CRS destino previsto:": " · expected target CRS:",
    " · Aviso:": " · Warning:",
    "altura(s) para": "height value(s) for",
    " si usas WRG.": " if you use WRG.",
    "(Lector WRG no disponible en esta instalación)": "(WRG reader not available in this installation)",
    "Lector WRG no disponible; no se puede dibujar el perímetro WRG.": "WRG reader not available; the WRG boundary cannot be drawn.",
    "Selecciona una carpeta WAsP/Surfer o un WRG/ZIP para dibujar el perímetro.": "Select a WAsP/Surfer folder or a WRG/ZIP file to draw the boundary.",
    "No se pudo dibujar el perímetro del recurso:": "Could not draw the resource boundary:",
    "WRG inválido o no legible:": "Invalid or unreadable WRG:",
    ", nombre)": ", filename)",
    " nombre)": " filename)",
    "sin extensión": "no extension",
    "sin altura": "no height",
    # Resource-boundary grey status label
    "Mostrar perímetro del recurso en el mapa": "Show resource boundary on the map",
    "Actualizar perímetro": "Refresh boundary",
    "Limpiar perímetro": "Clear boundary",
    "Activa 'Mostrar perímetro del recurso en el mapa' para dibujarlo.": "Enable 'Show resource boundary on the map' to draw it.",
    "Perímetro del recurso eliminado del mapa.": "Resource boundary removed from the map.",
    "No había perímetro del recurso que limpiar.": "There was no resource boundary to clear.",
    "Perímetro del recurso no disponible en esta instalación.": "Resource boundary not available in this installation.",
    "No se pudo limpiar el perímetro del recurso:": "Could not clear the resource boundary:",
}
_TO_EN.update(_I18N_ITER13_ENERGY_GREY_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER13_ENERGY_GREY_TO_EN)
_I18N_ITER13_ENERGY_GREY_TO_ES = {v: k for k, v in _I18N_ITER13_ENERGY_GREY_TO_EN.items() if v != k}
_TO_ES.update(_I18N_ITER13_ENERGY_GREY_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER13_ENERGY_GREY_TO_ES)

# Build marker so it is easy to confirm which i18n build QGIS actually loaded.


# ---------------------------------------------------------------------------
# Final public-release polish: Noise module UI/status/help strings.
# Keeps the Noise module fully English when the hub language is English,
# including grey helper text, status panels, progress labels and export dialogs.
# ---------------------------------------------------------------------------
_I18N_NOISE_PRERELEASE_TO_EN = {
    # Noise page hero/context
    "Módulo de ruido: conectado al layout y modelos WT del proyecto, con cálculo sobre receptores, tabla de emisión por modelo y salidas en capa. La física actual sigue un enfoque fuente-receptor propio simplificado y documentación de limitaciones para validación técnica.": "Noise module: connected to the project layout and WT models, with receiver calculations, source-group emission table and GIS layer outputs. The current physics follows a simplified source-receiver approach with documented limitations for technical validation.",
    "Modelos WT detectados:": "Detected WT models:",
    "Capa de receptores:": "Receiver layer:",
    "Contexto del proyecto": "Project context",
    "Entradas del cálculo": "Calculation inputs",
    "Proyecto sin nombre": "Unnamed project",
    "Sin layout detectado en el grupo de coordenadas por modelo": "No layout detected in the coordinate-by-model group",
    "Sin recurso seleccionado todavía": "No resource selected yet",
    "No seleccionado": "Not selected",
    "No seleccionada": "Not selected",
    "elemento(s)": "feature(s)",
    "turbina(s)": "turbine(s)",
    "modelo(s) WT": "WT model(s)",

    # Source/receiver inputs
    "Selecciona una o varias capas fuente detectadas desde Energía. Si marcas varias, el cálculo de ruido usará todas ellas a la vez.": "Select one or more source layers detected from Energy. If several are checked, the noise calculation will use all of them together.",
    "Usar varias capas de receptores por categoría": "Use multiple receiver layers by category",
    "Criterio de límite:": "Limit criterion:",
    "Manual / global": "Manual / global",
    "Diurno": "Daytime",
    "Nocturno": "Night-time",
    "Selecciona qué campo de límite se aplica a cada receptor. No cambia la física del ruido, solo el criterio de evaluación.": "Select which limit field is applied to each receiver. It does not change the noise physics, only the assessment criterion.",
    "Usar": "Use",
    "Capa": "Layer",
    "Tipo": "Type",
    "Límite día": "Day limit",
    "Límite noche": "Night limit",
    "Altura [m]": "Height [m]",
    "Añadir capa receptores": "Add receiver layer",
    "Quitar capa seleccionada": "Remove selected layer",
    "Usar layout activo del proyecto": "Use active project layout",
    "Generar mapa GIS de ruido (malla de cálculo)": "Generate GIS noise map (calculation grid)",
    "Altura de receptor [m]:": "Receiver height [m]:",
    "Radio máximo [m]:": "Maximum radius [m]:",
    "MDT / DSM (opcional):": "DEM / DSM (optional):",
    "Resolución del mapa [m]:": "Map resolution [m]:",
    "Generar isófonas": "Generate isophones",
    "Niveles isófonas [dB(A)]:": "Isophone levels [dB(A)]:",
    "Límite receptor [dB(A)]:": "Receiver limit [dB(A)]:",
    "Atenuación lineal α [dB/m]:": "Linear attenuation α [dB/m]:",
    "Factor de suelo G:": "Ground factor G:",
    "G=0 suelo duro · G=1 suelo poroso": "G=0 hard ground · G=1 porous ground",
    "Modo suelo:": "Ground mode:",
    "Global (G manual)": "Global (manual G)",
    "Desde capa de uso del suelo": "From land-use layer",
    "Capa uso del suelo (opcional):": "Land-use layer (optional):",
    "Método fuente-receptor eólico (Adiv + Aatm + Aground)": "Wind source-receiver method (Adiv + Aatm + Aground)",

    # Engine/atmosphere/acoustic scenario
    "Motor de cálculo:": "Calculation engine:",
    "⚡ Rápido - LwA global simplificado": "⚡ Fast - simplified global LwA",
    "📊 ISO-aligned - ISO 9613-2 por bandas": "📊 ISO-aligned - octave-band ISO 9613-2",
    "Motor rápido: cálculo simplificado para cribado acústico\nMotor ISO: cálculo por bandas según ISO 9613-2:2024": "Fast engine: simplified screening calculation\nISO engine: octave-band calculation according to ISO 9613-2:2024",
    "⛅ Condiciones Atmosféricas (Motor ISO)": "⛅ Atmospheric conditions (ISO engine)",
    "Temperatura:": "Temperature:",
    "Humedad relativa:": "Relative humidity:",
    "Presión atmosférica:": "Atmospheric pressure:",
    "Presión atmosférica local en kPa. Valor de referencia habitual a nivel del mar: 101.325 kPa. Usa un dato medido o ajustado por altitud si está disponible.": "Local atmospheric pressure in kPa. Usual reference value at sea level: 101.325 kPa. Use a measured or altitude-adjusted value if available.",
    "💡 Usa condiciones típicas del emplazamiento. Si no tienes presión medida, 101.325 kPa es una referencia estándar a nivel del mar; en altitud suele ser menor.": "💡 Use typical site conditions. If you do not have measured pressure, 101.325 kPa is a standard sea-level reference; at altitude it is usually lower.",
    "Escenario acústico:": "Acoustic scenario:",
    "LwA fijo por grupo fuente": "Fixed LwA by source group",
    "LwA fijo por grupo fuente acústico": "Fixed LwA by acoustic source group",
    "Velocidad eval. [m/s]:": "Evaluation speed [m/s]:",
    "Usar peor caso de la curva": "Use curve worst case",
    "En modo curva, cada grupo fuente puede usar un CSV ws/LwA distinto.": "In curve mode, each source group can use a different ws/LwA CSV.",
    "Configuración de grupos fuente acústicos": "Acoustic source-group configuration",
    "Grupo fuente": "Source group",
    "Parque": "Wind farm",
    "Modelo base": "Base model",
    "Turbinas": "Turbines",
    "LwA fijo [dB(A)]": "Fixed LwA [dB(A)]",
    "Curva CSV": "CSV curve",
    "Notas": "Notes",
    "Cada fila representa un grupo fuente acústico. Puedes renombrar grupo y parque para que el resumen y las exportaciones sean más legibles.": "Each row represents an acoustic source group. You can rename the group and wind farm to make summaries and exports easier to read.",
    "Importar curva acústica para el grupo fuente seleccionado…": "Import acoustic curve for selected source group…",
    "Limpiar curva del grupo fuente seleccionado": "Clear curve from selected source group",
    "Puedes usar un LwA fijo por grupo fuente acústico o una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.": "You can use a fixed LwA by acoustic source group or a ws/LwA acoustic curve by group. If curve mode is enabled, the plugin evaluates emission at a specific speed or in worst case.",
    "Preparación del cálculo": "Calculation preparation",
    "Listo.": "Ready.",
    "Comprobar configuración": "Check configuration",
    "Calcular ruido": "Calculate noise",
    "α se calcula automáticamente por banda en Motor ISO": "α is calculated automatically by band in the ISO engine",
    "Método ISO-aligned: ISO 9613-2 con bandas de octava (Adiv + Aatm(T,RH,P) + Agr(f))": "ISO-aligned method: ISO 9613-2 with octave bands (Adiv + Aatm(T,RH,P) + Agr(f))",
    "peor caso": "worst case",
    "inválida": "invalid",
    "sin curva -> fallback LwA fijo": "no curve -> fixed LwA fallback",
    "Detectado desde capa fuente del proyecto": "Detected from project source layer",
    "layout importado desde Energía": "layout imported from Energy",
    "Parque=": "Wind farm=",
    "Modelo=": "Model=",
    "puntos": "points",
    "polígonos": "polygons",

    # Configuration status box
    "No se han detectado capas de coordenadas por modelo. Genera primero el layout desde Energía.": "No coordinate-by-model layers were detected. Generate the layout from Energy first.",
    "Layout detectado": "Detected layout",
    "Falta configurar al menos una capa de receptores en la tabla multi-capa.": "At least one receiver layer must be configured in the multi-layer table.",
    "Receptores multi-capa": "Multi-layer receivers",
    "capa(s) activas": "active layer(s)",
    "elemento(s) totales": "total feature(s)",
    "Criterio de límite por capa": "Limit criterion by layer",
    "Falta seleccionar la capa de receptores. Para este flujo se recomienda una capa de puntos o polígonos de viviendas/receptores.": "A receiver layer must be selected. For this workflow, a point or polygon layer of dwellings/receivers is recommended.",
    "Receptores": "Receivers",
    "La capa de receptores coincide con el layout de turbinas. Elige una capa distinta de viviendas/receptores.": "The receiver layer matches the turbine layout. Choose a different dwellings/receivers layer.",
    "Grupos fuente acústicos: faltan": "Acoustic source groups: missing",
    "nombre(s) de grupo en la tabla": "group name(s) in the table",
    "Grupos fuente acústicos: hay": "Acoustic source groups: there are",
    "nombre(s) duplicados. Conviene renombrarlos para exportación y trazabilidad.": "duplicate name(s). Rename them for export and traceability.",
    "Grupos fuente acústicos": "Acoustic source groups",
    "grupo(s) con LwA válido": "group(s) with valid LwA",
    "Escenario acústico: curvas LwA(ws) en peor caso": "Acoustic scenario: worst-case LwA(ws) curves",
    "Curvas disponibles": "Available curves",
    "Escenario acústico: curvas LwA(ws) a": "Acoustic scenario: LwA(ws) curves at",
    "Escenario acústico: LwA fijo por grupo fuente acústico.": "Acoustic scenario: fixed LwA by acoustic source group.",
    "Altura de receptor: se toma de cada capa configurada en la tabla multi-capa.": "Receiver height: taken from each layer configured in the multi-layer table.",
    "Altura de receptor configurada": "Configured receiver height",
    "Radio máximo configurado": "Configured maximum radius",
    "Atenuación lineal α": "Linear attenuation α",
    "absorción atmosférica simplificada": "simplified atmospheric absorption",
    "Suelo/terreno: desde capa de uso del suelo": "Ground/terrain: from land-use layer",
    "G global de respaldo": "fallback global G",
    "Suelo/terreno: modo capa activado pero sin capa válida": "Ground/terrain: layer mode enabled but no valid layer selected",
    "will use G global como respaldo": "will use global G as fallback",
    "Factor de suelo G": "Ground factor G",
    "0=duro, 1=poroso": "0=hard, 1=porous",
    "Límite de receptor: se toma de cada capa configurada en la tabla multi-capa.": "Receiver limit: taken from each layer configured in the multi-layer table.",
    "Límite de receptor": "Receiver limit",
    "MDT/DSM seleccionado": "Selected DEM/DSM",
    "se muestreará terreno fuente/receptor en el método acústico": "source/receiver terrain will be sampled in the acoustic method",
    "Sin MDT/DSM seleccionado. El cálculo acústico arrancará en coordenadas planas con alturas relativas de fuente/receptor.": "No DEM/DSM selected. The acoustic calculation will start from flat coordinates with relative source/receiver heights.",
    "Método actual: cálculo acústico fuente-receptor para consultoría eólica (Lp = LwA - Adiv - Aatm - Aground). Además puede crear capas GIS de fuentes, enlaces dominantes, receptores fuera de radio, mapa de ruido ráster e isófonas.": "Current method: wind source-receiver acoustic calculation for consultancy (Lp = LwA - Adiv - Aatm - Aground). It can also create GIS layers for sources, dominant links, receivers outside the radius, noise raster map and isophones.",
    "Parámetros de consultoría acústica: límite de receptor, isófonas y factor de suelo G para revisión rápida de cumplimiento e influencia del terreno.": "Acoustic consultancy parameters: receiver limit, isophones and ground factor G for quick review of compliance and terrain influence.",
    "Fila": "Row",
    "capa de receptores no válida": "invalid receiver layer",
    "la capa": "the layer",
    "está vacía": "is empty",
    "No hay capas activas de receptores por categoría.": "There are no active receiver layers by category.",
    "LwA fijo inválido": "invalid fixed LwA",
    "El radio máximo debe ser mayor que 0.": "The maximum radius must be greater than 0.",
    "La resolución del raster debe ser mayor que 0.": "Raster resolution must be greater than 0.",
    "Modo suelo desde capa activo sin capa válida: will use G global como respaldo.": "Ground-from-layer mode is active without a valid layer: global G will be used as fallback.",

    # Dialog controller/progress/messages
    "Configura al menos una capa válida en la tabla de receptores por categoría.": "Configure at least one valid layer in the receiver-category table.",
    "Selecciona una capa válida de receptores.": "Select a valid receiver layer.",
    "Escenario acústico visible": "Visible acoustic scenario",
    "Método usado: Modelo fuente-receptor eólico (Adiv + Aatm + Aground) con curvas acústicas en peor caso.": "Method used: wind source-receiver model (Adiv + Aatm + Aground) with worst-case acoustic curves.",
    "Método usado: Modelo fuente-receptor eólico (Adiv + Aatm + Aground) con curvas acústicas a": "Method used: wind source-receiver model (Adiv + Aatm + Aground) with acoustic curves at",
    "Método usado: Modelo fuente-receptor eólico (Adiv + Aatm + Aground) con curvas acústicas.": "Method used: wind source-receiver model (Adiv + Aatm + Aground) with acoustic curves.",
    "Método usado: Modelo fuente-receptor eólico (Adiv + Aatm + Aground) con LwA fijo por grupo fuente acústico.": "Method used: wind source-receiver model (Adiv + Aatm + Aground) with fixed LwA by acoustic source group.",
    "Escenario acústico efectivo por grupo fuente": "Effective acoustic scenario by source group",
    "resolución autoajustada": "auto-adjusted resolution",
    "celdas | resolución pedida": "cells | requested resolution",
    "Raster de ruido: lanzado en segundo plano con QgsTask. Los resultados por receptor ya están disponibles; la capa 'Noise · Map' se añadirá al terminar.": "Noise raster: launched in the background with QgsTask. Receiver results are already available; the 'Noise · Map' layer will be added when it finishes.",
    "Isófonas: se generarán al finalizar el raster en segundo plano.": "Isophones: they will be generated when the background raster finishes.",
    "AVISO: se solicitó el raster pero no se pudo generar. Revisa la resolución, el CRS y el extent de las fuentes.": "WARNING: the raster was requested but could not be generated. Check resolution, CRS and source extent.",
    "AVISO: todos los receptores han quedado fuera del radio máximo. Revisa radio, layout o ubicación de receptores.": "WARNING: all receivers are outside the maximum radius. Check radius, layout or receiver locations.",
    "receptor(es) con G distinto del global": "receiver(s) with G different from the global value",
    "Aground representa la pérdida simplificada asociada al efecto suelo/terreno en el método acústico.": "Aground represents the simplified loss associated with ground/terrain effect in the acoustic method.",
    "Preparando datos para cálculo en segundo plano…": "Preparing data for background calculation…",
    "Velantis Wind · preparando ruido": "Velantis Wind · preparing noise",
    "Preparando fuentes, receptores y MDT para el cálculo en segundo plano…": "Preparing sources, receivers and DEM for background calculation…",
    "Preparando fuentes, receptores y MDT…": "Preparing sources, receivers and DEM…",
    "No se pudo preparar el cálculo en segundo plano": "Could not prepare the background calculation",
    "Añadiendo capas de resultados a QGIS…": "Adding result layers to QGIS…",
    "Cálculo de ruido completado en segundo plano.": "Noise calculation completed in the background.",
    "Cálculo acústico completado en segundo plano.\n\nLas capas de receptores/fuentes y el raster, si estaba activado, ya se han añadido al proyecto.": "Acoustic calculation completed in the background.\n\nReceiver/source layers and the raster, if enabled, have already been added to the project.",
    "No se pudo completar el cálculo": "Could not complete the calculation",
    "No se pudo calcular el ruido en segundo plano": "Could not calculate noise in the background",
    "Calculando ruido en segundo plano": "Calculating noise in the background",
    "Cálculo de ruido cancelado o fallido.": "Noise calculation cancelled or failed.",
    "Cálculo de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.": "Noise calculation launched in the background. You can monitor it in the QGIS task manager.",
    "Cálculo pesado lanzado como QgsTask. Puedes seguir usando QGIS mientras avanza.": "Heavy calculation launched as a QgsTask. You can keep using QGIS while it runs.",
    "No se pudo lanzar el cálculo en segundo plano": "Could not launch the background calculation",
    "Raster de ruido terminado en segundo plano": "Noise raster finished in the background",
    "celdas, resolución efectiva": "cells, effective resolution",
    "Isófonas terminadas en segundo plano": "Isophones finished in the background",
    "Raster de ruido completado.": "Noise raster completed.",
    "Raster de ruido generado y añadido al proyecto.": "Noise raster generated and added to the project.",
    "Raster de ruido finalizado con aviso.": "Noise raster finished with warning.",
    "Generando raster de ruido": "Generating noise raster",
    "Raster de ruido lanzado en segundo plano. Puedes verlo en el administrador de tareas de QGIS.": "Noise raster launched in the background. You can monitor it in the QGIS task manager.",
    "No se puede lanzar el cálculo": "Cannot start the calculation",
    "AVISO: esta configuración usa ruta síncrona porque": "WARNING: this configuration uses the synchronous path because",
    "Ruta síncrona": "Synchronous path",
    "Calculando receptores y resumen…": "Calculating receivers and summary…",
    "Calculando ruido y raster…": "Calculating noise and raster…",
    "No se pudo calcular el ruido": "Could not calculate noise",
    "Resultados por receptor calculados.": "Receiver results calculated.",
    "Cálculo de ruido completado.": "Noise calculation completed.",
    "No se pudo mostrar el resumen visual de ruido": "Could not display the visual noise summary",
    "Cálculo acústico completado.\n\nLas capas de receptores/fuentes ya se han añadido al proyecto. Si el raster estaba activado y no usa uso del suelo, se está generando en segundo plano cuando es seguro hacerlo.": "Acoustic calculation completed.\n\nReceiver/source layers have already been added to the project. If the raster was enabled and does not use land use, it is being generated in the background when safe.",

    # Validation / compute errors
    "El MDT/DSM no está en el mismo CRS que el proyecto. Reproyéctalo antes de calcular.": "The DEM/DSM is not in the same CRS as the project. Reproject it before calculating.",
    "La capa de uso del suelo debe ser vectorial poligonal.": "The land-use layer must be a polygon vector layer.",
    "La capa de uso del suelo no está en el mismo CRS que el proyecto. Reproyéctala antes de calcular.": "The land-use layer is not in the same CRS as the project. Reproject it before calculating.",
    "Sin ruta de curva acústica": "No acoustic curve path",
    "No se pudieron leer pares ws/LwA válidos de": "Could not read valid ws/LwA pairs from",
    "Acoustic curve vacía": "Empty acoustic curve",
    "Espectro vacío": "Empty spectrum",
    "Falta banda": "Missing band",
    "Valor inválido en banda": "Invalid value in band",

    # Results dialog tables/buttons/tooltips
    "Resumen": "Summary",
    "Modelos": "Models",
    "Top receptores": "Top receivers",
    "Capas creadas": "Created layers",
    "Modelo WT": "WT model",
    "Turbinas": "Turbines",
    "Notas": "Notes",
    "Tabla resumida para consultoría: resultados acústicos por receptor, cumplimiento, fuente dominante y atenuaciones principales. Los diagnósticos internos MDT por pares se conservan en memoria, pero no se muestran por defecto.": "Consultancy summary table: acoustic results by receiver, compliance, dominant source and main attenuations. Internal pairwise DEM diagnostics are kept in memory but are not shown by default.",
    "id receptor": "receiver id",
    "tipo": "type",
    "nivel total dB(A)": "total level dB(A)",
    "límite dB(A)": "limit dB(A)",
    "margen límite dB": "limit margin dB",
    "estado": "status",
    "supera límite": "exceeds limit",
    "nº turbinas": "no. turbines",
    "dist. turbina cercana (m)": "nearest turbine dist. (m)",
    "modelo dominante": "dominant model",
    "grupo fuente dom.": "dominant source group",
    "parque dom.": "dominant wind farm",
    "LwA fuente dom. dB(A)": "dominant source LwA dB(A)",
    "Abar máx. dB": "max Abar dB",
    "G suelo": "ground G",
    "modo suelo": "ground mode",
    "h receptor m": "receiver h m",
    "z terreno receptor m": "receiver ground z m",
    "z acústica receptor m": "receiver acoustic z m",
    "capa fuente dominante": "dominant source layer",
    "Capa": "Layer",
    "Estado": "Status",
    "Exportar informe…": "Export report…",
    "Guarda el resumen técnico en HTML o TXT.": "Save the technical summary as HTML or TXT.",
    "Exportar receptores CSV…": "Export receivers CSV…",
    "Guarda una tabla limpia con una fila por receptor y las columnas necesarias para consultoría.": "Save a clean table with one row per receiver and the columns needed for consultancy.",
    "Exportar excedencias CSV…": "Export exceedances CSV…",
    "Guarda solo los receptores que superan su límite acústico.": "Save only the receivers exceeding their acoustic limit.",
    "Exportar paquete XLSX…": "Export XLSX package…",
    "Guarda resumen, modelos, receptores y excedencias en un único libro Excel.": "Save summary, models, receivers and exceedances in a single Excel workbook.",
    "Cerrar": "Close",

    # Report HTML/common phrases
    "INFORME TÉCNICO DE IMPACTO ACÚSTICO": "TECHNICAL NOISE IMPACT REPORT",
    "Evaluación de ruido generado por aerogeneradores": "Assessment of wind turbine noise",
    "RESUMEN EJECUTIVO": "EXECUTIVE SUMMARY",
    "Aerogeneradores": "Wind turbines",
    "Receptores Evaluados": "Evaluated receptors",
    "Nivel Máximo (dB(A))": "Maximum level (dB(A))",
    "Cobertura de Análisis": "Analysis coverage",
    "receptores dentro del radio": "receivers within the radius",
    "receptores fuera de radio": "receivers outside the radius",
    "Cumplimiento Normativo": "Regulatory compliance",
    "receptores superan límites": "receivers exceed limits",
    "cumplimiento sobre receptores cubiertos": "compliance over covered receivers",
    "Metodología de Cálculo": "Calculation methodology",
    "Motor utilizado": "Engine used",
    "CONFIGURACIÓN Y PARÁMETROS": "CONFIGURATION AND PARAMETERS",
    "Ecuación Utilizada": "Equation used",
    "Parámetros del Cálculo": "Calculation parameters",
    "Términos Activos": "Active terms",
    "Efecto del terreno": "Ground effect",
    "Difracción topográfica": "Topographic diffraction",
    "Receptor crítico": "Critical receiver",
    "RECEPTOR CRÍTICO": "CRITICAL RECEIVER",
    "Receptor crítico (mayor nivel sonoro)": "Critical receiver (highest sound level)",
    "Desglose de atenuaciones": "Attenuation breakdown",
    "Turbinas contribuyentes dentro del radio": "Contributing turbines within radius",
    "Límites aplicados": "Applied limits",
    "desde campos de receptor": "from receiver fields",
    "valor único": "single value",
    "Límite de referencia": "Reference limit",
    "Uso del suelo": "Land use",
    "desde capa": "from layer",
    "sin nombre": "unnamed",
    "no activo": "inactive",
    "activo": "active",
    "sí": "yes",
    "no": "no",
    "Revisión recomendada": "Recommended review",
    "la presión atmosférica introducida": "the entered atmospheric pressure",
    "está fuera del rango típico usado como referencia en muchos estudios preliminares": "is outside the typical range used as a reference in many preliminary studies",
    "Si no es un dato medido del emplazamiento, revisa si debería estar cerca de 101.325 kPa o ajustada por altitud.": "If it is not a measured site value, check whether it should be close to 101.325 kPa or adjusted for altitude.",
    "Cómo se ha ejecutado el cálculo ISO-aligned": "How the ISO-aligned calculation was run",
    "Cómo se ha ejecutado el cálculo Screening": "How the screening calculation was run",
    "Lectura de entradas GIS": "GIS input reading",
    "Estado acústico de cada grupo fuente": "Acoustic state of each source group",
    "Conversión a bandas": "Band conversion",
    "Selección de contribuyentes por receptor": "Contributor selection by receiver",
    "Cálculo por trayecto fuente–receptor": "Source-receiver path calculation",
    "Suma energética": "Energetic summation",
    "fuera de radio": "outside radius",
    "Física del MDT y del apantallamiento topográfico": "DEM physics and topographic screening",
    "Física del uso del suelo y cálculo de G_eff": "Land-use physics and G_eff calculation",
    "Física del uso del suelo y cálculo de G": "Land-use physics and G calculation",
    "Desarrollo físico detallado del motor ISO-aligned": "Detailed physical development of the ISO-aligned engine",
    "Divergencia geométrica": "Geometrical divergence",
    "Absorción atmosférica simplificada": "Simplified atmospheric absorption",
    "Efecto suelo por regiones": "Ground effect by regions",
    "Apantallamiento topográfico con MDT": "Topographic screening with DEM",
    "Perfil del terreno": "Terrain profile",
    "Línea de visión": "Line of sight",
    "Obstáculo dominante": "Dominant obstacle",
    "Geometría real del obstáculo": "Actual obstacle geometry",
    "Lectura del receptor crítico": "Critical-receiver reading",
    "Fuente acústica": "Acoustic source",
    "Nivel operativo global": "Global operating level",
    "Geometría": "Geometry",
    "Input global de la fuente": "Global source input",
    "distancia 3D fuente–receptor": "3D source-receiver distance",
    "Representa la dispersión geométrica de la onda sonora con la distancia 3D fuente–receptor.": "Represents the geometrical spreading of the sound wave with 3D source-receiver distance.",
    "Receptores por Categoría": "Receivers by category",
    "Modelos múltiples": "Multiple models",
    "Raster ISO + MDT": "ISO raster + DEM",
}
_TO_EN.update(_I18N_NOISE_PRERELEASE_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_NOISE_PRERELEASE_TO_EN)
_I18N_NOISE_PRERELEASE_TO_ES = {v: k for k, v in _I18N_NOISE_PRERELEASE_TO_EN.items() if v != k}
# A few natural Spanish labels for English strings that now appear directly in the noise source code.
_I18N_NOISE_PRERELEASE_TO_ES.update({
    "Detected from project source layer": "Detectado desde capa fuente del proyecto",
    "layout imported from Energy": "layout importado desde Energía",
    "Wind farm=": "Parque=",
    "Model=": "Modelo=",
    "points": "puntos",
    "polygons": "polígonos",
    "No layout detected in the coordinate-by-model group": "Sin layout detectado en el grupo de coordenadas por modelo",
    "WT model(s)": "modelo(s) WT",
    "turbine(s)": "turbina(s)",
    "feature(s)": "elemento(s)",
    "— Select receiver layer —": "— Selecciona capa de receptores —",
    "no curve -> fixed LwA fallback": "sin curva -> fallback LwA fijo",
    "Fixed LwA by source group": "LwA fijo por grupo fuente",
    "Use curve worst case": "Usar peor caso de la curva",
    "Fast engine: simplified screening calculation\nISO engine: octave-band calculation according to ISO 9613-2:2024": "Motor rápido: cálculo simplificado para cribado acústico\nMotor ISO: cálculo por bandas según ISO 9613-2:2024",
})
_TO_ES.update(_I18N_NOISE_PRERELEASE_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_NOISE_PRERELEASE_TO_ES)

# ---------------------------------------------------------------------------
# Iteration 14: Noise module clickable help icons and helper labels.
# ---------------------------------------------------------------------------
_I18N_ITER14_NOISE_HELP_TO_EN = {
    "Fuentes / layout acústico:": "Sources / acoustic layout:",
    "Temperature, humidity and pressure used by the ISO-aligned engine.": "Temperature, humidity and pressure used by the ISO-aligned engine.",
    "Review the acoustic emission assigned to each source group before running the calculation.": "Review the acoustic emission assigned to each source group before running the calculation.",
    "Explain which turbine layers are used as acoustic sources": "Explain which turbine layers are used as acoustic sources",
    "Explain receiver layers, categories and day/night limits": "Explain receiver layers, categories and day/night limits",
    "Explain manual, day and night receiver limits": "Explain manual, day and night receiver limits",
    "Explain the GIS noise map, raster resolution and isophones": "Explain the GIS noise map, raster resolution and isophones",
    "Explain receiver height, maximum radius and DEM/DSM use": "Explain receiver height, maximum radius and DEM/DSM use",
    "Explain simplified atmospheric attenuation, ground factor G and land-use layers": "Explain simplified atmospheric attenuation, ground factor G and land-use layers",
    "Explain the fast and ISO-aligned noise engines": "Explain the fast and ISO-aligned noise engines",
    "Explain how atmospheric conditions affect ISO-band absorption": "Explain how atmospheric conditions affect ISO-band absorption",
    "Explain fixed LwA, acoustic curves and worst-case mode": "Explain fixed LwA, acoustic curves and worst-case mode",
    "Explain the acoustic source-group table": "Explain the acoustic source-group table",
}
_I18N_ITER14_NOISE_HELP_TO_ES = {
    "Sources / acoustic layout:": "Fuentes / layout acústico:",
    "Temperature, humidity and pressure used by the ISO-aligned engine.": "Temperatura, humedad y presión usadas por el motor ISO-aligned.",
    "Review the acoustic emission assigned to each source group before running the calculation.": "Revisa la emisión acústica asignada a cada grupo fuente antes de lanzar el cálculo.",
    "Explain which turbine layers are used as acoustic sources": "Explica qué capas de turbinas se usan como fuentes acústicas",
    "Explain receiver layers, categories and day/night limits": "Explica capas de receptores, categorías y límites día/noche",
    "Explain manual, day and night receiver limits": "Explica límites manuales, diurnos y nocturnos",
    "Explain the GIS noise map, raster resolution and isophones": "Explica el mapa GIS de ruido, la resolución raster y las isófonas",
    "Explain receiver height, maximum radius and DEM/DSM use": "Explica altura de receptor, radio máximo y uso de MDT/DSM",
    "Explain simplified atmospheric attenuation, ground factor G and land-use layers": "Explica atenuación atmosférica simplificada, factor de suelo G y capas land-use",
    "Explain the fast and ISO-aligned noise engines": "Explica los motores rápido e ISO-aligned de ruido",
    "Explain how atmospheric conditions affect ISO-band absorption": "Explica cómo las condiciones atmosféricas afectan a la absorción por bandas ISO",
    "Explain fixed LwA, acoustic curves and worst-case mode": "Explica LwA fijo, curvas acústicas y modo peor caso",
    "Explain the acoustic source-group table": "Explica la tabla de grupos fuente acústicos",
}
_TO_EN.update(_I18N_ITER14_NOISE_HELP_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER14_NOISE_HELP_TO_EN)
_TO_ES.update(_I18N_ITER14_NOISE_HELP_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER14_NOISE_HELP_TO_ES)


# ---------------------------------------------------------------------------
# Iteration 15: Noise source-group fixed LwA editable directly in table.
# ---------------------------------------------------------------------------
_I18N_ITER15_NOISE_LWA_TABLE_TO_EN = {
    "Puedes editar el LwA fijo directamente en la tabla de grupos fuente acústicos o importar una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.": "You can edit the fixed LwA directly in the acoustic source-group table or import a ws/LwA acoustic curve per group. If you enable curve mode, the plugin will evaluate the emission at a specific wind speed or in the worst case.",
    "Edit the fixed LwA for this acoustic source group.": "Edit the fixed LwA for this acoustic source group.",
    "Fixed acoustic emission level for this source group. Edit it here before running the calculation.": "Fixed acoustic emission level for this source group. Edit it here before running the calculation.",
    "Each row represents an acoustic source group. You can rename the group/wind farm and edit its fixed LwA directly in the table.": "Each row represents an acoustic source group. You can rename the group/wind farm and edit its fixed LwA directly in the table.",
}
_I18N_ITER15_NOISE_LWA_TABLE_TO_ES = {
    "You can edit the fixed LwA directly in the acoustic source-group table or import a ws/LwA acoustic curve per group. If you enable curve mode, the plugin will evaluate the emission at a specific wind speed or in the worst case.": "Puedes editar el LwA fijo directamente en la tabla de grupos fuente acústicos o importar una curva acústica ws/LwA por grupo. Si activas el modo curva, el plugin evaluará la emisión a una velocidad concreta o en peor caso.",
    "Edit the fixed LwA for this acoustic source group.": "Edita el LwA fijo de este grupo fuente acústico.",
    "Fixed acoustic emission level for this source group. Edit it here before running the calculation.": "Nivel de emisión acústica fija de este grupo fuente. Edítalo aquí antes de lanzar el cálculo.",
    "Each row represents an acoustic source group. You can rename the group/wind farm and edit its fixed LwA directly in the table.": "Cada fila representa un grupo fuente acústico. Puedes renombrar el grupo/parque y editar su LwA fijo directamente en la tabla.",
}
_TO_EN.update(_I18N_ITER15_NOISE_LWA_TABLE_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER15_NOISE_LWA_TABLE_TO_EN)
_TO_ES.update(_I18N_ITER15_NOISE_LWA_TABLE_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER15_NOISE_LWA_TABLE_TO_ES)


# ---------------------------------------------------------------------------
# Iteration 16: Noise summary clarity — scope banner + symbol glossary (ES/EN).
# ---------------------------------------------------------------------------
_I18N_ITER16_NOISE_SUMMARY_TO_EN = {
    # Scope banner — labels
    "⚠️ Alcance de este informe — léelo antes de usar los resultados": "⚠️ Scope of this report — read before using the results",
    "Qué es:": "What it is:",
    "Qué no es:": "What it is not:",
    "Simplificaciones aplicadas en este modo:": "Simplifications applied in this mode:",
    "Recomendación:": "Recommendation:",
    # Scope banner — ISO-aligned body
    "una evaluación acústica preliminar alineada con la metodología ISO 9613-2, pensada para diseño, comparación de alternativas y cribado de receptores sensibles.": "a preliminary acoustic assessment aligned with the ISO 9613-2 methodology, intended for design, comparison of alternatives and screening of sensitive receivers.",
    "no es un informe acústico certificado ni sustituye a un estudio regulatorio definitivo realizado con software comercial validado.": "it is not a certified acoustic report and does not replace a definitive regulatory study performed with validated commercial software.",
    "Absorción atmosférica Aatm mediante tabla de referencia con correcciones simplificadas de temperatura, humedad y presión, no la formulación analítica completa de ISO 9613-1.": "Atmospheric absorption Aatm via a reference table with simplified corrections for temperature, humidity and pressure, not the full analytical formulation of ISO 9613-1.",
    "Sin corrección meteorológica de largo plazo Cmet.": "No long-term meteorological correction Cmet.",
    "Difracción topográfica de un único obstáculo dominante: sin difracción lateral ni pantallas múltiples.": "Topographic diffraction from a single dominant obstacle: no lateral diffraction or multiple barriers.",
    "Resolución espectral en 8 bandas de octava de 63 a 8000 Hz, no en tercios de octava.": "Spectral resolution in 8 octave bands from 63 to 8000 Hz, not in one-third octaves.",
    "Directividad de fuente Dc asumida 0 dB.": "Source directivity Dc assumed to be 0 dB.",
    # Scope banner — fast/screening body
    "una estimación rápida de cribado (screening) para mapas ágiles y comparación de alternativas de implantación.": "a quick screening estimate for fast maps and comparison of layout alternatives.",
    "no es un cálculo espectral detallado ni un informe regulatorio; para receptores cercanos al límite conviene recalcular en modo ISO-aligned.": "it is not a detailed spectral calculation or a regulatory report; for receivers close to the limit it is advisable to recalculate in ISO-aligned mode.",
    "Sin propagación por bandas de octava.": "No octave-band propagation.",
    "Absorción atmosférica resumida en un único coeficiente alfa constante.": "Atmospheric absorption summarised in a single constant alpha coefficient.",
    "Sin apantallamiento topográfico Abar desde el MDT.": "No topographic screening Abar from the DEM.",
    "Efecto suelo mediante corrección empírica simplificada.": "Ground effect via a simplified empirical correction.",
    "Para decisiones regulatorias críticas, valida los resultados con mediciones de campo o software comercial certificado.": "For critical regulatory decisions, validate the results with field measurements or certified commercial software.",
    # Glossary
    "📖 Glosario de símbolos": "📖 Glossary of symbols",
    "Definición compacta de los símbolos que aparecen en las fórmulas y tablas de este informe.": "Compact definition of the symbols that appear in the formulas and tables of this report.",
    "Símbolo": "Symbol",
    "Significado": "Meaning",
    "Nivel de potencia sonora A-ponderado de la fuente, en dB(A).": "A-weighted sound power level of the source, in dB(A).",
    "Potencia sonora de la fuente por banda de octava, en dB.": "Sound power of the source per octave band, in dB.",
    "Forma espectral de referencia por banda usada como plantilla, en dB.": "Reference spectral shape per band used as a template, in dB.",
    "Ponderación A aplicada a cada banda de octava, en dB.": "A-weighting applied to each octave band, in dB.",
    "Desplazamiento global aplicado a la plantilla espectral para reproducir el LwA objetivo, en dB.": "Global shift applied to the spectral template to reproduce the target LwA, in dB.",
    "Nivel de presión sonora A-ponderado resultante en el receptor, en dB(A).": "Resulting A-weighted sound pressure level at the receiver, in dB(A).",
    "Atenuación por divergencia geométrica con la distancia, en dB.": "Attenuation due to geometric divergence with distance, in dB.",
    "Atenuación por absorción atmosférica del aire, en dB.": "Attenuation due to atmospheric absorption of the air, in dB.",
    "Atenuación por efecto del suelo, en dB.": "Attenuation due to the ground effect, in dB.",
    "Atenuación por apantallamiento topográfico, solo en modo ISO con MDT, en dB.": "Attenuation due to topographic screening, only in ISO mode with a DEM, in dB.",
    "Distancia tridimensional entre fuente y receptor, en metros.": "Three-dimensional distance between source and receiver, in metres.",
    "Factor de suelo de 0 (duro) a 1 (blando) y su valor efectivo por trayecto.": "Ground factor from 0 (hard) to 1 (soft) and its effective per-path value.",
    "Corrección meteorológica de largo plazo, no aplicada en este plugin.": "Long-term meteorological correction, not applied in this plugin.",
    "Corrección por directividad de la fuente, asumida 0 dB.": "Source directivity correction, assumed to be 0 dB.",
}
_TO_EN.update(_I18N_ITER16_NOISE_SUMMARY_TO_EN)
_FRAGMENT_TO_EN.update(_I18N_ITER16_NOISE_SUMMARY_TO_EN)
_I18N_ITER16_NOISE_SUMMARY_TO_ES = {v: k for k, v in _I18N_ITER16_NOISE_SUMMARY_TO_EN.items() if v != k}
_TO_ES.update(_I18N_ITER16_NOISE_SUMMARY_TO_ES)
_FRAGMENT_TO_ES.update(_I18N_ITER16_NOISE_SUMMARY_TO_ES)


# ---------------------------------------------------------------------------
# French language pack.
#
# Translation DATA lives in the separate module ``i18n_fr`` (Spanish->French),
# keeping this engine file free of another large dictionary and making the
# French strings easy to review and extend.  Anything not yet present there
# falls back to the Spanish source automatically.
# ---------------------------------------------------------------------------
try:
    try:
        from . import i18n_fr as _i18n_fr  # type: ignore
    except Exception:  # running as a flat module (dev / tests)
        import i18n_fr as _i18n_fr  # type: ignore
    register_language(
        LANG_FR,
        getattr(_i18n_fr, "TO_FR", {}) or {},
        getattr(_i18n_fr, "FRAGMENT_TO_FR", None),
        label=getattr(_i18n_fr, "LABEL", "Français"),
    )
except Exception:
    # French pack missing or malformed: the plugin still works, French simply
    # falls back to Spanish until the pack is available.
    pass

# Multilingual engine + French pack.


# ---------------------------------------------------------------------------
# German language pack.
# ---------------------------------------------------------------------------
try:
    try:
        from . import i18n_de as _i18n_de  # type: ignore
    except Exception:
        import i18n_de as _i18n_de  # type: ignore
    register_language(
        LANG_DE,
        getattr(_i18n_de, "TO_DE", {}) or {},
        getattr(_i18n_de, "FRAGMENT_TO_DE", None),
        label=getattr(_i18n_de, "LABEL", "Deutsch"),
    )
except Exception:
    # German pack missing or malformed: the plugin still works and falls back to Spanish.
    pass

# ---------------------------------------------------------------------------
# Layout-optimization hub strings for the public release.
# ---------------------------------------------------------------------------
_LAYOUT_OPTIMIZATION_HUB_TO_EN = {
    "Optimización de layout desde recurso y restricciones": "Layout optimization from resource and constraints",
    "Optimización de layout": "Layout optimization",
    "Muestra información de contacto y el white paper de optimización.": "Shows contact information and the optimization white paper.",
    "Optimización de layout bajo demanda": "On-demand layout optimization",
    "Podemos ayudarte a generar o mejorar alternativas de layout con el motor avanzado de VelantisWind. No necesitas un layout previo: podemos trabajar desde el recurso eólico, las restricciones y los objetivos del proyecto para buscar más producción y menores pérdidas por estela.": "We can help you generate or improve layout alternatives with VelantisWind's advanced engine. You do not need a previous layout: we can work from the wind resource, constraints and project objectives to look for higher production and lower wake losses.",
    "Contacto: info@velantiswind.com · White paper disponible en velantiswind.com": "Contact: info@velantiswind.com · White paper available at velantiswind.com",
    "Copiar contacto": "Copy contact",
    "Copia el email de contacto al portapapeles.": "Copies the contact email to the clipboard.",
    "Ver white paper": "Read white paper",
    "Abre la web de VelantisWind, donde está disponible el white paper.": "Opens the VelantisWind website, where the white paper is available.",
    "Ocultar información": "Hide information",
    "Email copiado al portapapeles:": "Email copied to clipboard:",
    "Puedes contactar en:": "You can contact us at:",
}
_TO_EN.update(_LAYOUT_OPTIMIZATION_HUB_TO_EN)
_FRAGMENT_TO_EN.update(_LAYOUT_OPTIMIZATION_HUB_TO_EN)
_TO_ES.update({v: k for k, v in _LAYOUT_OPTIMIZATION_HUB_TO_EN.items() if v != k})
_FRAGMENT_TO_ES.update({v: k for k, v in _LAYOUT_OPTIMIZATION_HUB_TO_EN.items() if v != k})
try:
    register_language(LANG_EN, _TO_EN, _FRAGMENT_TO_EN, label="English")
except Exception:
    pass
I18N_BUILD = "2026-06-22-0.1.14"

