Plugin Directory

Changeset 3645531


Ignore:
Timestamp:
08/13/2026 02:03:58 PM (2 months ago)
Author:
opcion5
Message:

1.1.0: plugin now translatable, shipping Spanish, French, German, Italian and Portuguese translations; dates follow the WordPress format setting

Location:
opcion5-sports-event-registration/trunk
Files:
11 added
35 edited

Legend:

Unmodified
Added
Removed
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-add-participant.php

    r3642215 r3645531  
    1313        add_submenu_page(
    1414            null,
    15             'Añadir participante',
    16             'Añadir participante',
     15            __( 'Add entrant', 'opcion5-sports-event-registration' ),
     16            __( 'Add entrant', 'opcion5-sports-event-registration' ),
    1717            'manage_options',
    1818            'wpcr-add-participant',
    … …  
    2727        // Solo lectura para saber qué evento mostrar; el alta verifica su propio nonce.
    2828        // phpcs:disable WordPress.Security.NonceVerification.Recommended
    29         if(empty($_GET['event'])) wp_die("Falta parámetro event");
     29        if(empty($_GET['event'])) wp_die( esc_html__( 'Missing event parameter', 'opcion5-sports-event-registration' ) );
    3030
    3131        $this->public_id = sanitize_text_field( wp_unslash( $_GET['event'] ) );
    … …  
    4545   
    4646    <h1>➕ Nuevo participante</h1>
    47     <p><b>Evento ID público:</b> <?php echo esc_html($this->public_id); ?></p>
     47    <p><b><?php esc_html_e( 'Public event ID:', 'opcion5-sports-event-registration' ); ?></b> <?php echo esc_html($this->public_id); ?></p>
    4848
    4949    <?php
    … …  
    8282
    8383            <h2>🆕 Participante registrado</h2>
    84             <p>¿Cómo deseas continuar?</p><br>
     84            <p><?php esc_html_e( 'How do you want to continue?', 'opcion5-sports-event-registration' ); ?></p><br>
    8585
    8686            <button id="wpcr-pay-manual" class="button button-primary" style="width:100%;margin-bottom:10px;font-weight:bold;">
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-admin-assets.php

    r3642215 r3645531  
    3232// Nonce compartido para las acciones AJAX del editor de eventos
    3333// (invitar, recalcular categorías, guardar resultados…)
     34/*
     35 * Los textos de los .js del editor van aquí: un fichero .js no lo alcanza el
     36 * .mo. Se traducen en PHP y el navegador los recibe ya traducidos. 'wpcr-tabs'
     37 * se encola antes que el resto, así que wpcrAdmin existe para todos ellos.
     38 */
    3439wp_localize_script('wpcr-tabs', 'wpcrAdmin', [
    3540    'nonce' => wp_create_nonce('wpcr_admin_ajax'),
     41    'i18n'  => [
     42        'recalcCategories' => __( 'Recalculate categories', 'opcion5-sports-event-registration' ),
     43        'deleteEntrant'    => __( 'Delete this entrant?', 'opcion5-sports-event-registration' ),
     44        'invalidAmount'    => __( 'Enter a valid amount.', 'opcion5-sports-event-registration' ),
     45        'processing'       => __( 'Processing…', 'opcion5-sports-event-registration' ),
     46        'saving'           => __( 'Saving…', 'opcion5-sports-event-registration' ),
     47        'decimalPoint'     => __( 'The "Race distance (km)" field must use a POINT as the decimal separator (for example 20.5), not a comma. Fix the value before saving.', 'opcion5-sports-event-registration' ),
     48        'alreadyEntered'   => __( "This entrant is already registered for this event.\n\nYou cannot add them again.", 'opcion5-sports-event-registration' ),
     49    ],
    3650]);
    3751
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-admin-menu.php

    r3642215 r3645531  
    142142            'url'   => admin_url('admin-ajax.php'),
    143143            'nonce' => wp_create_nonce('wpcr_dni_lookup'),
     144            // Los textos del .js se traducen aquí: el .mo no llega a un fichero .js.
     145            'i18n'  => [
     146                'alreadyEntered' => __( "This entrant is already registered for this event.\n\nYou cannot add them again.", 'opcion5-sports-event-registration' ),
     147            ],
    144148        ]);
    145149    }
    … …  
    167171    add_submenu_page(
    168172        'wpcr-competition',
    169         __('Eventos','opcion5-sports-event-registration'),
    170         __('Eventos','opcion5-sports-event-registration'),
     173        __('Events','opcion5-sports-event-registration'),
     174        __('Events','opcion5-sports-event-registration'),
    171175        $cap,
    172176        'wpcr-competition',
    … …  
    196200    add_submenu_page(
    197201        'wpcr-competition',
    198         __('Categorías','opcion5-sports-event-registration'),
    199         __('Categorías','opcion5-sports-event-registration'),
     202        __('Categories','opcion5-sports-event-registration'),
     203        __('Categories','opcion5-sports-event-registration'),
    200204        $cap,
    201205        'wpcr-category-settings',
    … …  
    205209    add_submenu_page(
    206210        'wpcr-competition',
    207         __('Formularios','opcion5-sports-event-registration'),
    208         __('Formularios','opcion5-sports-event-registration'),
     211        __('Forms','opcion5-sports-event-registration'),
     212        __('Forms','opcion5-sports-event-registration'),
    209213        $cap,
    210214        'wpcr-form-settings',
    … …  
    245249    add_submenu_page(
    246250        null,
    247         __('Editar evento','opcion5-sports-event-registration'),
    248         __('Editar evento','opcion5-sports-event-registration'),
     251        __('Edit event','opcion5-sports-event-registration'),
     252        __('Edit event','opcion5-sports-event-registration'),
    249253        $cap,
    250254        'wpcr-edit-event',
    … …  
    254258    add_submenu_page(
    255259        null,
    256         __('Añadir nuevo evento','opcion5-sports-event-registration'),
    257         __('Añadir nuevo evento','opcion5-sports-event-registration'),
     260        __('Add new event','opcion5-sports-event-registration'),
     261        __('Add new event','opcion5-sports-event-registration'),
    258262        $cap,
    259263        'wpcr-add-event',
    … …  
    276280    public function render_participants_page_fallback() {
    277281
    278         echo '<div class="wrap"><h1>Participantes</h1>';
     282        echo '<div class="wrap"><h1>' . esc_html__( 'Entrants', 'opcion5-sports-event-registration' ) . '</h1>';
    279283
    280284        if ( class_exists( 'WPCR_Participants_List' ) ) {
    … …  
    394398           LISTADO DE EVENTOS (sin límite en el número de eventos)
    395399        ========================================================= */
    396         echo '<div class="wrap"><h1>Eventos / Pruebas</h1>';
     400        echo '<div class="wrap"><h1>' . esc_html__( 'Events', 'opcion5-sports-event-registration' ) . '</h1>';
    397401
    398402        /**
    … …  
    403407            <strong>Significado de los estados:</strong>
    404408            <div style="display:flex; align-items:center; flex-wrap:wrap; gap:20px; margin-top:6px;">
    405                 <span>🟢 <strong>Abierto:</strong> las inscripciones están disponibles.</span>
     409                <span>🟢 <strong>' . esc_html__( 'Open:', 'opcion5-sports-event-registration' ) . '</strong> las inscripciones están disponibles.</span>
    406410                <span>🟡 <strong>Pendiente:</strong> las inscripciones aún no han comenzado.</span>
    407                 <span>🔴 <strong>Cerrado:</strong> las inscripciones han finalizado.</span>
     411                <span>🔴 <strong>' . esc_html__( 'Closed:', 'opcion5-sports-event-registration' ) . '</strong> las inscripciones han finalizado.</span>
    408412            </div>
    409413        </div>';
    … …  
    445449                <th class="wpcr-id">ID</th>
    446450                <th class="wpcr-title">Título</th>
    447                 <th>Fecha</th>
    448                 <th>Apertura</th>
    449                 <th>Cierre</th>
     451                <th>' . esc_html__( 'Date', 'opcion5-sports-event-registration' ) . '</th>
     452                <th>' . esc_html__( 'Opens', 'opcion5-sports-event-registration' ) . '</th>
     453                <th>' . esc_html__( 'Closes', 'opcion5-sports-event-registration' ) . '</th>
    450454                <th>Inscritos</th>
    451                 <th>Cupo</th>
     455                <th>' . esc_html__( 'Quota', 'opcion5-sports-event-registration' ) . '</th>
    452456                <th>Estado</th>
    453457                <th>Formulario</th>
    … …  
    588592                $close_ts = $e->reg_close ? strtotime($e->reg_close) : 0;
    589593
    590                 $event_date = $event_ts ? date_i18n('d/m/Y',$event_ts) : '—';
    591                 $open_date  = $open_ts  ? date_i18n('d/m/Y H:i',$open_ts) : '—';
    592                 $close_date = $close_ts ? date_i18n('d/m/Y H:i',$close_ts) : '—';
     594                $event_date = wpcr_format_date($event_ts, '—');
     595                $open_date  = wpcr_format_datetime($open_ts, '—');
     596                $close_date = wpcr_format_datetime($close_ts, '—');
    593597
    594598                if ( $open_ts && $open_ts > $now ) {
    … …  
    948952        echo '<div class="wrap">';
    949953        echo '<h1>¿Qué tipo de evento quieres crear?</h1>';
    950         echo '<p style="color:#555;max-width:760px;">Elige cómo se organiza tu prueba. Lo configuras todo después; esto solo prepara la estructura adecuada.</p>';
     954        echo '<p style="color:#555;max-width:760px;">' . esc_html__( 'Choose how your event is organised. You configure everything afterwards; this only sets up the right structure.', 'opcion5-sports-event-registration' ) . '</p>';
    951955
    952956        echo '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px;max-width:1040px;margin-top:18px;">';
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-category-settings.php

    r3642215 r3645531  
    8686
    8787        private function render_category_sets_list_content() {
    88                 echo '<h1>' . esc_html__( 'Configuración de Categorías', 'opcion5-sports-event-registration' ) .
     88                echo '<h1>' . esc_html__( 'Category settings', 'opcion5-sports-event-registration' ) .
    8989                     ' <a href="' . esc_url( admin_url( 'admin.php?page=wpcr-category-settings&action=add' ) ) . '" class="page-title-action">' .
    90                      esc_html__( 'Añadir nuevo conjunto', 'opcion5-sports-event-registration' ) . '</a></h1>';
     90                     esc_html__( 'Add new set', 'opcion5-sports-event-registration' ) . '</a></h1>';
    9191
    9292                $sets = WPCR_Data_Model::get_all_category_sets();
    … …  
    9999        <thead>
    100100            <tr>
    101                 <th><?php esc_html_e( 'Nombre del Conjunto', 'opcion5-sports-event-registration' ); ?></th>
    102                 <th><?php esc_html_e( 'Tipo', 'opcion5-sports-event-registration' ); ?></th>
    103                 <th style="text-align:center;"><?php esc_html_e( 'Total Categorías', 'opcion5-sports-event-registration' ); ?></th>
    104                 <th style="text-align:center;"><?php esc_html_e( 'Masc.', 'opcion5-sports-event-registration' ); ?></th>
    105                 <th style="text-align:center;"><?php esc_html_e( 'Fem.', 'opcion5-sports-event-registration' ); ?></th>
    106                 <th style="text-align:center;"><?php esc_html_e( 'Mixtas', 'opcion5-sports-event-registration' ); ?></th>
    107                 <th><?php esc_html_e( 'Acciones', 'opcion5-sports-event-registration' ); ?></th>
     101                <th><?php esc_html_e( 'Set name', 'opcion5-sports-event-registration' ); ?></th>
     102                <th><?php esc_html_e( 'Type', 'opcion5-sports-event-registration' ); ?></th>
     103                <th style="text-align:center;"><?php esc_html_e( 'Total categories', 'opcion5-sports-event-registration' ); ?></th>
     104                <th style="text-align:center;"><?php esc_html_e( 'Men', 'opcion5-sports-event-registration' ); ?></th>
     105                <th style="text-align:center;"><?php esc_html_e( 'Women', 'opcion5-sports-event-registration' ); ?></th>
     106                <th style="text-align:center;"><?php esc_html_e( 'Mixed', 'opcion5-sports-event-registration' ); ?></th>
     107                <th><?php esc_html_e( 'Actions', 'opcion5-sports-event-registration' ); ?></th>
    108108            </tr>
    109109        </thead>
    … …  
    112112            <?php if ( empty( $sets ) ) : ?>
    113113                <tr>
    114                     <td colspan="7" style="text-align:center;"><?php esc_html_e( 'No hay conjuntos de categorías creados.', 'opcion5-sports-event-registration' ); ?></td>
     114                    <td colspan="7" style="text-align:center;"><?php esc_html_e( 'No category sets have been created yet.', 'opcion5-sports-event-registration' ); ?></td>
    115115                </tr>
    116116            <?php else : ?>
    … …  
    164164                                '<a href="%s">%s</a>',
    165165                                esc_url( wp_nonce_url( admin_url( 'admin.php?page=wpcr-category-settings&action=edit&id=' . $id ), 'wpcr_edit_set_' . $id ) ),
    166                                 esc_html__( 'Editar', 'opcion5-sports-event-registration' )
     166                                esc_html__( 'Edit', 'opcion5-sports-event-registration' )
    167167                        ),
    168168                        sprintf(
    169169                                '<a href="%s" data-wpcr-confirm="%s">%s</a>',
    170170                                esc_url( $base . '&action=delete' ),
    171                                 esc_attr__( '¿Estás seguro de que quieres borrar este conjunto?', 'opcion5-sports-event-registration' ),
    172                                 esc_html__( 'Borrar', 'opcion5-sports-event-registration' )
     171                                esc_attr__( 'Are you sure you want to delete this set?', 'opcion5-sports-event-registration' ),
     172                                esc_html__( 'Delete', 'opcion5-sports-event-registration' )
    173173                        )
    174174                ] );
    … …  
    234234
    235235                ?>
    236                 <h1><?php echo $is_edit ? esc_html__( 'Editar conjunto de categorías', 'opcion5-sports-event-registration' ) : esc_html__( 'Añadir conjunto de categorías', 'opcion5-sports-event-registration' ); ?></h1>
     236                <h1><?php echo $is_edit ? esc_html__( 'Edit category set', 'opcion5-sports-event-registration' ) : esc_html__( 'Add category set', 'opcion5-sports-event-registration' ); ?></h1>
    237237
    238238                <form method="post" id="wpcr-set-and-categories-form">
    … …  
    243243                        <table class="form-table">
    244244                                <tr>
    245                                         <th><label for="set_name"><?php esc_html_e( 'Nombre del conjunto', 'opcion5-sports-event-registration' ); ?></label></th>
     245                                        <th><label for="set_name"><?php esc_html_e( 'Set name', 'opcion5-sports-event-registration' ); ?></label></th>
    246246                                        <td>
    247247                                                <input type="text" id="set_name" name="set_name" class="regular-text" required value="<?php echo esc_attr( $set['name'] ); ?>">
    248                                                 <p class="description"><?php esc_html_e( 'Nombre del conjunto que identifica a este grupo de categorías.', 'opcion5-sports-event-registration' ); ?></p>
     248                                                <p class="description"><?php esc_html_e( 'Name that identifies this group of categories.', 'opcion5-sports-event-registration' ); ?></p>
    249249                                        </td>
    250250                                </tr>
    251251                                <tr>
    252                                         <th><?php esc_html_e( 'Tipo de categorización', 'opcion5-sports-event-registration' ); ?></th>
     252                                        <th><?php esc_html_e( 'Category type', 'opcion5-sports-event-registration' ); ?></th>
    253253                                        <td>
    254254                                                <label style="margin-right:20px;">
    255255                                                        <input type="radio" name="set_type" value="age" <?php checked( $set['type'] ?? 'age', 'age' ); ?>>
    256                                                         <?php esc_html_e( 'Por edad (ej: de 6 a 8 años)', 'opcion5-sports-event-registration' ); ?>
     256                                                        <?php esc_html_e( 'By age (e.g. from 6 to 8 years old)', 'opcion5-sports-event-registration' ); ?>
    257257                                                </label>
    258258                                                <label>
    259259                                                        <input type="radio" name="set_type" value="birth_year" <?php checked( $set['type'] ?? 'age', 'birth_year' ); ?>>
    260                                                         <?php esc_html_e( 'Por año de nacimiento (ej: nacidos entre 2016 y 2018)', 'opcion5-sports-event-registration' ); ?>
     260                                                        <?php esc_html_e( 'By year of birth (e.g. born between 2016 and 2018)', 'opcion5-sports-event-registration' ); ?>
    261261                                                </label>
    262                                                 <p class="description"><?php esc_html_e( 'Esta opción afecta a cómo se asignan los participantes a cada categoría en el momento de la inscripción.', 'opcion5-sports-event-registration' ); ?></p>
     262                                                <p class="description"><?php esc_html_e( 'This setting affects how entrants are assigned to each category when they register.', 'opcion5-sports-event-registration' ); ?></p>
    263263                                        </td>
    264264                                </tr>
    265265                        </table>
    266266
    267                         <h2><?php esc_html_e( 'Categorías internas', 'opcion5-sports-event-registration' ); ?></h2>
     267                        <h2><?php esc_html_e( 'Categories in this set', 'opcion5-sports-event-registration' ); ?></h2>
    268268
    269269                        <?php $set_type = $set['type'] ?? 'age'; ?>
    … …  
    271271                                <thead>
    272272                                        <tr>
    273                                                 <th width="35%"><?php esc_html_e( 'Nombre de la Categoría', 'opcion5-sports-event-registration' ); ?></th>
    274                                                 <th width="15%" class="wpcr-col-age" <?php echo $set_type === 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Edad mín.', 'opcion5-sports-event-registration' ); ?></th>
    275                                                 <th width="15%" class="wpcr-col-age" <?php echo $set_type === 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Edad máx.', 'opcion5-sports-event-registration' ); ?></th>
    276                                                 <th width="15%" class="wpcr-col-year" <?php echo $set_type !== 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Año desde', 'opcion5-sports-event-registration' ); ?></th>
    277                                                 <th width="15%" class="wpcr-col-year" <?php echo $set_type !== 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Año hasta', 'opcion5-sports-event-registration' ); ?></th>
    278                                                 <th width="10%"><?php esc_html_e( 'Género', 'opcion5-sports-event-registration' ); ?></th>
    279                                                 <th width="10%"><?php esc_html_e( 'Acciones', 'opcion5-sports-event-registration' ); ?></th>
     273                                                <th width="35%"><?php esc_html_e( 'Category name', 'opcion5-sports-event-registration' ); ?></th>
     274                                                <th width="15%" class="wpcr-col-age" <?php echo $set_type === 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Min. age', 'opcion5-sports-event-registration' ); ?></th>
     275                                                <th width="15%" class="wpcr-col-age" <?php echo $set_type === 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Max. age', 'opcion5-sports-event-registration' ); ?></th>
     276                                                <th width="15%" class="wpcr-col-year" <?php echo $set_type !== 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Year from', 'opcion5-sports-event-registration' ); ?></th>
     277                                                <th width="15%" class="wpcr-col-year" <?php echo $set_type !== 'birth_year' ? 'style="display:none"' : ''; ?>><?php esc_html_e( 'Year to', 'opcion5-sports-event-registration' ); ?></th>
     278                                                <th width="10%"><?php esc_html_e( 'Gender', 'opcion5-sports-event-registration' ); ?></th>
     279                                                <th width="10%"><?php esc_html_e( 'Actions', 'opcion5-sports-event-registration' ); ?></th>
    280280                                        </tr>
    281281                                </thead>
    … …  
    293293                        </table>
    294294
    295                         <p><button type="button" id="add-category-row" class="button"><?php esc_html_e( 'Añadir Categoría', 'opcion5-sports-event-registration' ); ?></button></p>
     295                        <p><button type="button" id="add-category-row" class="button"><?php esc_html_e( 'Add category', 'opcion5-sports-event-registration' ); ?></button></p>
    296296
    297297                        <p class="submit">
    298                                 <button type="submit" class="button button-primary"><?php echo $is_edit ? esc_html__( 'Guardar cambios', 'opcion5-sports-event-registration' ) : esc_html__( 'Crear conjunto', 'opcion5-sports-event-registration' ); ?></button>
    299                                 <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings' ) ); ?>"><?php esc_html_e( 'Cancelar', 'opcion5-sports-event-registration' ); ?></a>
     298                                <button type="submit" class="button button-primary"><?php echo $is_edit ? esc_html__( 'Save changes', 'opcion5-sports-event-registration' ) : esc_html__( 'Create set', 'opcion5-sports-event-registration' ); ?></button>
     299                                <a class="button" href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings' ) ); ?>"><?php esc_html_e( 'Cancel', 'opcion5-sports-event-registration' ); ?></a>
    300300                                <?php if ( $is_edit ) : ?>
    301                                         <a class="button" style="margin-left:10px;" href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings&action=delete&id=' . intval( $set_id ) . '&_wpnonce=' . wp_create_nonce( 'wpcr_category_set_action_' . $set_id ) ) ); ?>" data-wpcr-confirm="<?php echo esc_attr__( '¿Estás seguro de que quieres borrar este conjunto?', 'opcion5-sports-event-registration' ); ?>"><?php esc_html_e( 'Borrar conjunto', 'opcion5-sports-event-registration' ); ?></a>
     301                                        <a class="button" style="margin-left:10px;" href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings&action=delete&id=' . intval( $set_id ) . '&_wpnonce=' . wp_create_nonce( 'wpcr_category_set_action_' . $set_id ) ) ); ?>" data-wpcr-confirm="<?php echo esc_attr__( 'Are you sure you want to delete this set?', 'opcion5-sports-event-registration' ); ?>"><?php esc_html_e( 'Delete set', 'opcion5-sports-event-registration' ); ?></a>
    302302                                <?php endif; ?>
    303303                        </p>
    … …  
    327327                <h1><?php
    328328                        /* translators: %s: nombre del conjunto de categorías */
    329                         printf( esc_html__( 'Gestionar Categorías: %s', 'opcion5-sports-event-registration' ), esc_html( $set_data['name'] ) );
     329                        printf( esc_html__( 'Manage categories: %s', 'opcion5-sports-event-registration' ), esc_html( $set_data['name'] ) );
    330330                ?></h1>
    331                 <p><a href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings' ) ); ?>">&laquo; <?php esc_html_e( 'Volver al listado', 'opcion5-sports-event-registration' ); ?></a></p>
     331                <p><a href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-category-settings' ) ); ?>">&laquo; <?php esc_html_e( 'Back to the list', 'opcion5-sports-event-registration' ); ?></a></p>
    332332
    333333                <form method="post" id="wpcr-categories-form">
    … …  
    338338                                <thead>
    339339                                        <tr>
    340                                                 <th width="25%"><?php esc_html_e( 'Nombre de la Categoría', 'opcion5-sports-event-registration' ); ?></th>
    341                                                 <th width="15%"><?php esc_html_e( 'Edad Mínima', 'opcion5-sports-event-registration' ); ?></th>
    342                                                 <th width="15%"><?php esc_html_e( 'Edad Máxima', 'opcion5-sports-event-registration' ); ?></th>
    343                                                 <th width="15%"><?php esc_html_e( 'Género', 'opcion5-sports-event-registration' ); ?></th>
    344                                                 <th width="20%"><?php esc_html_e( 'Acciones', 'opcion5-sports-event-registration' ); ?></th>
     340                                                <th width="25%"><?php esc_html_e( 'Category name', 'opcion5-sports-event-registration' ); ?></th>
     341                                                <th width="15%"><?php esc_html_e( 'Minimum age', 'opcion5-sports-event-registration' ); ?></th>
     342                                                <th width="15%"><?php esc_html_e( 'Maximum age', 'opcion5-sports-event-registration' ); ?></th>
     343                                                <th width="15%"><?php esc_html_e( 'Gender', 'opcion5-sports-event-registration' ); ?></th>
     344                                                <th width="20%"><?php esc_html_e( 'Actions', 'opcion5-sports-event-registration' ); ?></th>
    345345                                        </tr>
    346346                                </thead>
    … …  
    356356                        </table>
    357357
    358                         <p><button type="button" id="add-category-row" class="button"><?php esc_html_e( 'Añadir Categoría', 'opcion5-sports-event-registration' ); ?></button></p>
    359                         <?php submit_button( esc_html__( 'Guardar Categorías', 'opcion5-sports-event-registration' ), 'primary', 'wpcr_save_categories' ); ?>
     358                        <p><button type="button" id="add-category-row" class="button"><?php esc_html_e( 'Add category', 'opcion5-sports-event-registration' ); ?></button></p>
     359                        <?php submit_button( esc_html__( 'Save categories', 'opcion5-sports-event-registration' ), 'primary', 'wpcr_save_categories' ); ?>
    360360                </form>
    361361
    … …  
    395395                                </select>
    396396                        </td>
    397                         <td><button type="button" class="button button-secondary delete-category-row" data-row-id="<?php echo esc_attr( $row_id ); ?>"><?php esc_html_e( 'Borrar', 'opcion5-sports-event-registration' ); ?></button></td>
     397                        <td><button type="button" class="button button-secondary delete-category-row" data-row-id="<?php echo esc_attr( $row_id ); ?>"><?php esc_html_e( 'Delete', 'opcion5-sports-event-registration' ); ?></button></td>
    398398                </tr>
    399399                <?php
    … …  
    448448        private function handle_save_set_and_categories() {
    449449                if ( ! current_user_can( 'manage_options' ) ) {
    450                         wp_die( esc_html__( 'No autorizado.', 'opcion5-sports-event-registration' ) );
     450                        wp_die( esc_html__( 'Not allowed.', 'opcion5-sports-event-registration' ) );
    451451                }
    452452
    453453                if ( empty( $_POST['wpcr_save_set_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wpcr_save_set_nonce'] ) ), 'wpcr_save_set_nonce' ) ) {
    454                         wp_die( esc_html__( 'Verificación de seguridad fallida.', 'opcion5-sports-event-registration' ) );
     454                        wp_die( esc_html__( 'Security check failed.', 'opcion5-sports-event-registration' ) );
    455455                }
    456456
    … …  
    461461
    462462                if ( empty( $set_name ) ) {
    463                         wp_die( esc_html__( 'El nombre del conjunto no puede estar vacío.', 'opcion5-sports-event-registration' ) );
     463                        wp_die( esc_html__( 'The set name cannot be empty.', 'opcion5-sports-event-registration' ) );
    464464                }
    465465
    … …  
    467467
    468468                if ( ! $saved_set_id ) {
    469                         wp_die( esc_html__( 'No se pudo guardar el conjunto.', 'opcion5-sports-event-registration' ) );
     469                        wp_die( esc_html__( 'The set could not be saved.', 'opcion5-sports-event-registration' ) );
    470470                }
    471471
    … …  
    510510        private function handle_save_categories( $set_id ) {
    511511                if ( ! isset( $_POST['wpcr_categories_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wpcr_categories_nonce'] ) ), 'wpcr_save_categories_nonce' ) ) {
    512                         wp_die( esc_html__( 'Verificación de seguridad fallida.', 'opcion5-sports-event-registration' ) );
     512                        wp_die( esc_html__( 'Security check failed.', 'opcion5-sports-event-registration' ) );
    513513                }
    514514
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-edit-event.php

    r3642215 r3645531  
    140140            $wpcr_new_type = ( $is_new && isset( $_GET['wpcr_type'] ) ) ? sanitize_key( wp_unslash( $_GET['wpcr_type'] ) ) : '';
    141141            $wpcr_type_notice = [
    142                 'multidistancia' => 'Estás creando una <strong>carrera con multidistancias</strong>. Rellena los datos generales y <strong>guarda</strong>; después añade las distancias en la pestaña <strong>🏁 Distancias</strong>.',
    143                 'stages'         => 'Estás creando una <strong>carrera por etapas</strong>. Rellena los datos generales (y el precio del <em>pack</em> en Precios y Pagos) y <strong>guarda</strong>; después añade las etapas en la pestaña <strong>🏔 Etapas</strong>.',
     142                'multidistancia' => 'Estás creando una <strong>' . esc_html__( 'race with several distances', 'opcion5-sports-event-registration' ) . '</strong>. Rellena los datos generales y <strong>' . esc_html__( 'save', 'opcion5-sports-event-registration' ) . '</strong>; después añade las distancias en la pestaña <strong>🏁 ' . esc_html__( 'Distances', 'opcion5-sports-event-registration' ) . '</strong>.',
     143                'stages'         => 'Estás creando una <strong>' . esc_html__( 'stage race', 'opcion5-sports-event-registration' ) . '</strong>. Rellena los datos generales (y el precio del <em>pack</em> en Precios y Pagos) y <strong>' . esc_html__( 'save', 'opcion5-sports-event-registration' ) . '</strong>; después añade las etapas en la pestaña <strong>🏔 Etapas</strong>.',
    144144            ];
    145145            if ( isset( $wpcr_type_notice[ $wpcr_new_type ] ) ) :
    … …  
    181181                };
    182182                $wpcr_extension_tabs = [
    183                     'distances' => [ 'label' => '🏁 Distancias',       'on' => $distances_eligible && $wpcr_ext_on('wpcr-multidistancia') ],
     183                    'distances' => [ 'label' => '🏁 ' . __( 'Distances', 'opcion5-sports-event-registration' ),       'on' => $distances_eligible && $wpcr_ext_on('wpcr-multidistancia') ],
    184184                    'stages'    => [ 'label' => '🏔 Etapas',           'on' => $stages_eligible && $wpcr_ext_on('wpcr-stages') ],
    185185                    'aid'       => [ 'label' => '🥤 Avituallamientos', 'on' => $wpcr_ext_on('wpcr-aid-stations') ],
    … …  
    187187                    'cartel'    => [ 'label' => '🖼 Cartel',           'on' => $wpcr_ext_on('wpcr-cartel-designer') ],
    188188                    'checkin'   => [ 'label' => '🎫 Entrega',          'on' => $wpcr_ext_on('wpcr-checkin') ],
    189                     'timing'    => [ 'label' => '⏱ Cronometraje',     'on' => $wpcr_ext_on('wpcr-timing') ],
    190                     'crono'     => [ 'label' => '⏱ Contrarreloj',     'on' => $wpcr_ext_on('wpcr-crono') ],
     189                    'timing'    => [ 'label' => '⏱ ' . __( 'Timing', 'opcion5-sports-event-registration' ),     'on' => $wpcr_ext_on('wpcr-timing') ],
     190                    'crono'     => [ 'label' => '⏱ ' . __( 'Time trial', 'opcion5-sports-event-registration' ),     'on' => $wpcr_ext_on('wpcr-crono') ],
    191191                    'rfid'      => [ 'label' => '🔖 RFID',             'on' => $wpcr_ext_on('wpcr-rfid') ],
    192                     'stats'     => [ 'label' => '📊 Estadísticas',     'on' => $wpcr_ext_on('wpcr-stats') ],
    193                     'results'   => [ 'label' => '🏁 Clasificaciones',  'on' => $wpcr_ext_on('wpcr-results') ],
    194                     'import'    => [ 'label' => '📂 Importar CSV',     'on' => $wpcr_ext_on('wpcr-import') ],
     192                    'stats'     => [ 'label' => '📊 ' . __( 'Statistics', 'opcion5-sports-event-registration' ),     'on' => $wpcr_ext_on('wpcr-stats') ],
     193                    'results'   => [ 'label' => '🏁 ' . __( 'Standings', 'opcion5-sports-event-registration' ),  'on' => $wpcr_ext_on('wpcr-results') ],
     194                    'import'    => [ 'label' => '📂 ' . __( 'Import CSV', 'opcion5-sports-event-registration' ),     'on' => $wpcr_ext_on('wpcr-import') ],
    195195                ];
    196196                ?>
    … …  
    201201                <div class="wpcr-tab-bar">
    202202                                        <div class="wpcr-tab active" data-open="wpcr-tab-general">General</div>
    203                                         <div class="wpcr-tab" data-open="wpcr-tab-tech">📁 Info técnica</div>
    204                                         <div class="wpcr-tab" data-open="wpcr-tab-options">Opciones</div>
    205                                         <div class="wpcr-tab" data-open="wpcr-tab-prices">Precios y Pagos</div>
     203                                        <div class="wpcr-tab" data-open="wpcr-tab-tech">📁 <?php esc_html_e( 'Race info', 'opcion5-sports-event-registration' ); ?></div>
     204                                        <div class="wpcr-tab" data-open="wpcr-tab-options"><?php esc_html_e( 'Options', 'opcion5-sports-event-registration' ); ?></div>
     205                                        <div class="wpcr-tab" data-open="wpcr-tab-prices"><?php esc_html_e( 'Prices and payments', 'opcion5-sports-event-registration' ); ?></div>
    206206                                        <div class="wpcr-tab" data-open="wpcr-tab-emails">Emails</div>
    207                                         <div class="wpcr-tab" data-open="wpcr-tab-participants">Participantes</div>
    208                                         <div class="wpcr-tab" data-open="wpcr-tab-payments">💳 Cobros</div>
     207                                        <div class="wpcr-tab" data-open="wpcr-tab-participants"><?php esc_html_e( 'Entrants', 'opcion5-sports-event-registration' ); ?></div>
     208                                        <div class="wpcr-tab" data-open="wpcr-tab-payments">💳 <?php esc_html_e( 'Payments', 'opcion5-sports-event-registration' ); ?></div>
    209209                                        <?php foreach ( $wpcr_extension_tabs as $wpcr_tab_key => $wpcr_tab ) : ?>
    210210                                                <?php if ( $wpcr_tab['on'] ) : ?>
    … …  
    577577
    578578        if ( ! $row ) {
    579             $msg = "<h2 style='color:red;'>Evento no encontrado</h2>";
     579            $msg = "<h2 style='color:red;'>" . esc_html__( 'Event not found', 'opcion5-sports-event-registration' ) . "</h2>";
    580580            if ( $is_ajax ) {
    581581                wp_send_json_error( ['message' => 'Evento no encontrado'] );
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-edit-participant.php

    r3642215 r3645531  
    301301        // Render formulario
    302302        // ----------------------------
    303         echo '<div class="wrap"><h1>' . ( $participant_id ? esc_html__( 'Editar participante', 'opcion5-sports-event-registration' ) : esc_html__( 'Añadir participante', 'opcion5-sports-event-registration' ) ) . '</h1>';
     303        echo '<div class="wrap"><h1>' . ( $participant_id ? esc_html__( 'Edit entrant', 'opcion5-sports-event-registration' ) : esc_html__( 'Add entrant', 'opcion5-sports-event-registration' ) ) . '</h1>';
    304304
    305305        echo '<form method="post" action="' . esc_url( admin_url( 'admin-post.php' ) ) . '">';
    … …  
    346346
    347347        // Convierte YYYY-MM-DD → DD/MM/YYYY solo para visualizar
    348         $dob_display = ($val && $val !== '0000-00-00') ? date_i18n("d/m/Y", strtotime($val)) : '';
     348        $dob_display = wpcr_format_date($val);
    349349
    350350        echo '<input type="text"
    … …  
    459459        // ----------------------------
    460460        if ( ! empty( $template_clean ) ) {
    461             echo '<tr><th colspan="2"><strong>' . esc_html__( 'Campos adicionales', 'opcion5-sports-event-registration' ) . '</strong></th></tr>';
     461            echo '<tr><th colspan="2"><strong>' . esc_html__( 'Extra fields', 'opcion5-sports-event-registration' ) . '</strong></th></tr>';
    462462        }
    463463
    … …  
    615615        // botón guardar + volver
    616616        echo '<p class="submit">';
    617         submit_button( esc_html__( 'Guardar cambios', 'opcion5-sports-event-registration' ), 'primary', 'submit', false );
     617        submit_button( esc_html__( 'Save changes', 'opcion5-sports-event-registration' ), 'primary', 'submit', false );
    618618// Volver siempre al listado general (ignorar evento)
    619619$back_url = admin_url("admin.php?page=wpcr-participants");
    … …  
    716716            echo '<!DOCTYPE html><html><head><meta charset="utf-8"></head>'
    717717               . '<body style="font-family:-apple-system,Segoe UI,sans-serif;color:#1d2327;padding:24px;">'
    718                . esc_html__( 'Guardado. Cerrando…', 'opcion5-sports-event-registration' );
     718               . esc_html__( 'Saved. Closing…', 'opcion5-sports-event-registration' );
    719719            wp_print_inline_script_tag(
    720720                'if(window.parent&&window.parent!==window){window.parent.postMessage({wpcr:"participant-saved",id:' . (int) $id . '},"*");}'
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-form-settings.php

    r3642215 r3645531  
    3939        ?>
    4040        <div class="wrap">
    41             <h1 class="wp-heading-inline"><?php esc_html_e( 'Formularios personalizados', 'opcion5-sports-event-registration' ); ?></h1>
    42             <a href="?page=wpcr-form-settings&action=add" class="page-title-action"><?php esc_html_e( 'Añadir nuevo', 'opcion5-sports-event-registration' ); ?></a>
     41            <h1 class="wp-heading-inline"><?php esc_html_e( 'Custom forms', 'opcion5-sports-event-registration' ); ?></h1>
     42            <a href="?page=wpcr-form-settings&action=add" class="page-title-action"><?php esc_html_e( 'Add new', 'opcion5-sports-event-registration' ); ?></a>
    4343            <hr class="wp-header-end">
    4444
    … …  
    4747                    <tr>
    4848                        <th><?php esc_html_e( 'ID', 'opcion5-sports-event-registration' ); ?></th>
    49                         <th><?php esc_html_e( 'Nombre', 'opcion5-sports-event-registration' ); ?></th>
    50                         <th><?php esc_html_e( 'Descripción', 'opcion5-sports-event-registration' ); ?></th>
    51                         <th><?php esc_html_e( 'Acciones', 'opcion5-sports-event-registration' ); ?></th>
     49                        <th><?php esc_html_e( 'Name', 'opcion5-sports-event-registration' ); ?></th>
     50                        <th><?php esc_html_e( 'Description', 'opcion5-sports-event-registration' ); ?></th>
     51                        <th><?php esc_html_e( 'Actions', 'opcion5-sports-event-registration' ); ?></th>
    5252                    </tr>
    5353                </thead>
    5454                <tbody>
    5555                    <?php if ( empty( $forms ) ) : ?>
    56                         <tr><td colspan="4"><?php esc_html_e( 'No hay formularios creados.', 'opcion5-sports-event-registration' ); ?></td></tr>
     56                        <tr><td colspan="4"><?php esc_html_e( 'No forms have been created yet.', 'opcion5-sports-event-registration' ); ?></td></tr>
    5757                    <?php else : ?>
    5858                        <?php foreach ( $forms as $f ) : ?>
    … …  
    6262                                <td><?php echo esc_html( $f['description'] ); ?></td>
    6363                                <td>
    64                                     <a href="?page=wpcr-form-settings&action=edit&id=<?php echo esc_attr( $f['id'] ); ?>" class="button"><?php esc_html_e( 'Editar', 'opcion5-sports-event-registration' ); ?></a>
     64                                    <a href="?page=wpcr-form-settings&action=edit&id=<?php echo esc_attr( $f['id'] ); ?>" class="button"><?php esc_html_e( 'Edit', 'opcion5-sports-event-registration' ); ?></a>
    6565                                </td>
    6666                            </tr>
    … …  
    168168                <?php
    169169                echo $id
    170                     ? esc_html__( 'Editar formulario', 'opcion5-sports-event-registration' )
    171                     : esc_html__( 'Nuevo formulario', 'opcion5-sports-event-registration' );
     170                    ? esc_html__( 'Edit form', 'opcion5-sports-event-registration' )
     171                    : esc_html__( 'New form', 'opcion5-sports-event-registration' );
    172172                ?>
    173173            </h1>
    … …  
    180180                <table class="form-table" role="presentation">
    181181                    <tr>
    182                         <th><label for="name"><?php esc_html_e( 'Nombre del formulario', 'opcion5-sports-event-registration' ); ?></label></th>
     182                        <th><label for="name"><?php esc_html_e( 'Form name', 'opcion5-sports-event-registration' ); ?></label></th>
    183183                        <td><input name="name" id="name" type="text" value="<?php echo esc_attr( $form['name'] ); ?>" class="regular-text" required></td>
    184184                    </tr>
    185185                    <tr>
    186                         <th><label for="description"><?php esc_html_e( 'Descripción', 'opcion5-sports-event-registration' ); ?></label></th>
     186                        <th><label for="description"><?php esc_html_e( 'Description', 'opcion5-sports-event-registration' ); ?></label></th>
    187187                        <td><textarea name="description" id="description" rows="3" class="large-text"><?php echo esc_textarea( $form['description'] ); ?></textarea></td>
    188188                    </tr>
    189189                </table>
    190190
    191                 <h2><?php esc_html_e( 'Constructor de campos', 'opcion5-sports-event-registration' ); ?></h2>
     191                <h2><?php esc_html_e( 'Field builder', 'opcion5-sports-event-registration' ); ?></h2>
    192192
    193193                <div style="background:#f9f9f9;border:1px solid #ccd0d4;border-left:4px solid #2271b1;padding:12px 15px;border-radius:4px;margin-bottom:20px;">
    194                     <p><strong><?php esc_html_e( 'Campos obligatorios del sistema (no editables aquí):', 'opcion5-sports-event-registration' ); ?></strong></p>
     194                    <p><strong><?php esc_html_e( 'Required system fields (not editable here):', 'opcion5-sports-event-registration' ); ?></strong></p>
    195195                    <ul style="margin-left:20px;list-style:disc;">
    196                         <li><?php esc_html_e( 'DNI / Nacionalidad — usados para identificación y lógica de documento.', 'opcion5-sports-event-registration' ); ?></li>
    197                         <li><?php esc_html_e( 'Fecha de nacimiento — usada para calcular edad y categoría.', 'opcion5-sports-event-registration' ); ?></li>
    198                         <li><?php esc_html_e( 'Género — usado para calcular la categoría por edad y sexo.', 'opcion5-sports-event-registration' ); ?></li>
    199                         <li><?php esc_html_e( 'Política de privacidad — campo legal obligatorio.', 'opcion5-sports-event-registration' ); ?></li>
     196                        <li><?php esc_html_e( 'ID number / Nationality — used for identification and document checks.', 'opcion5-sports-event-registration' ); ?></li>
     197                        <li><?php esc_html_e( 'Date of birth — used to work out age and category.', 'opcion5-sports-event-registration' ); ?></li>
     198                        <li><?php esc_html_e( 'Gender — used to work out the category by age and gender.', 'opcion5-sports-event-registration' ); ?></li>
     199                        <li><?php esc_html_e( 'Privacy policy — required legal field.', 'opcion5-sports-event-registration' ); ?></li>
    200200                    </ul>
    201                     <p style="margin-top:8px;"><?php esc_html_e( 'En este constructor solo verás y editarás los campos extra específicos de la prueba.', 'opcion5-sports-event-registration' ); ?></p>
     201                    <p style="margin-top:8px;"><?php esc_html_e( 'In this builder you only see and edit the extra fields specific to the event.', 'opcion5-sports-event-registration' ); ?></p>
    202202                </div>
    203203
    … …  
    205205
    206206                <p class="submit" style="margin-top:20px;">
    207                     <button type="submit" class="button-primary"><?php esc_html_e( 'Guardar formulario', 'opcion5-sports-event-registration' ); ?></button>
    208                     <a href="?page=wpcr-form-settings" class="button"><?php esc_html_e( 'Cancelar', 'opcion5-sports-event-registration' ); ?></a>
     207                    <button type="submit" class="button-primary"><?php esc_html_e( 'Save form', 'opcion5-sports-event-registration' ); ?></button>
     208                    <a href="?page=wpcr-form-settings" class="button"><?php esc_html_e( 'Cancel', 'opcion5-sports-event-registration' ); ?></a>
    209209                </p>
    210210
    … …  
    456456    public function handle_save_form() {
    457457        if ( empty( $_POST['wpcr_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wpcr_nonce'] ) ), 'wpcr_save_form' ) ) {
    458             wp_die( esc_html__( 'Nonce no válido.', 'opcion5-sports-event-registration' ) );
     458            wp_die( esc_html__( 'Invalid nonce.', 'opcion5-sports-event-registration' ) );
    459459        }
    460460
    461461        if ( ! current_user_can( 'manage_options' ) ) {
    462             wp_die( esc_html__( 'No autorizado.', 'opcion5-sports-event-registration' ) );
     462            wp_die( esc_html__( 'Not allowed.', 'opcion5-sports-event-registration' ) );
    463463        }
    464464
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-modalidades.php

    r3642215 r3645531  
    77
    88        if ( ! current_user_can('manage_options') ) {
    9             wp_die( esc_html__('No autorizado', 'opcion5-sports-event-registration') );
     9            wp_die( esc_html__('Not allowed', 'opcion5-sports-event-registration') );
    1010        }
    1111
    … …  
    3232                $wpdb->update($table, ['image'=>null], ['id'=>$id]);
    3333
    34                 echo "<div class='updated'><p>🗑 Imagen eliminada correctamente.</p></div>";
     34                echo "<div class='updated'><p>🗑 " . esc_html__( 'Image deleted successfully.', 'opcion5-sports-event-registration' ) . "</p></div>";
    3535            }
    3636        }
    … …  
    5858            $wpdb->delete($table, ['id'=>$id]);
    5959
    60             echo "<div class='updated'><p>🗑 Modalidad eliminada junto con su imagen.</p></div>";
     60            echo "<div class='updated'><p>🗑 " . esc_html__( 'Discipline deleted along with its image.', 'opcion5-sports-event-registration' ) . "</p></div>";
    6161        }
    6262
    … …  
    7575            $wpdb->insert($table, ['name'=>$name, 'slug'=>$slug]);
    7676
    77             echo "<div class='updated'><p>✔ Modalidad añadida correctamente</p></div>";
     77            echo "<div class='updated'><p>✔ " . esc_html__( 'Discipline added successfully', 'opcion5-sports-event-registration' ) . "</p></div>";
    7878        }
    7979
    … …  
    137137            $wpdb->update($table, $update_data, ['id'=>$id]);
    138138
    139             echo "<div class='updated'><p>✏ Modalidad actualizada con éxito</p></div>";
     139            echo "<div class='updated'><p>✏ " . esc_html__( 'Discipline updated successfully', 'opcion5-sports-event-registration' ) . "</p></div>";
    140140        }
    141141
    … …  
    154154        echo "  <input type='text' name='wpcr_new_mod' placeholder='Nueva modalidad (MTB, Trail...)'
    155155                    required style='width:260px;padding:6px;'>
    156                 <button class='button button-primary'>Añadir</button>
     156                <button class='button button-primary'>" . esc_html__( 'Add', 'opcion5-sports-event-registration' ) . "</button>
    157157              </form>";
    158158
    … …  
    185185                        <a href="<?php echo esc_url($url_edit); ?>" class="button button-small">✏ Editar</a>
    186186                        <a href="<?php echo esc_url($url_delimg); ?>" class="button button-small"
    187                            data-wpcr-confirm="¿Eliminar SOLO la imagen?">🖼 Eliminar imagen</a>
     187                           data-wpcr-confirm="' . esc_attr__( 'Delete ONLY the image?', 'opcion5-sports-event-registration' ) . '">🖼 ' . esc_html__( 'Delete image', 'opcion5-sports-event-registration' ) . '</a>
    188188                        <a href="<?php echo esc_url($url_del); ?>" class="button button-small" style="color:#c00"
    189                            data-wpcr-confirm="¿Eliminar modalidad y su imagen?">🗑 Eliminar</a>
     189                           data-wpcr-confirm="' . esc_attr__( 'Delete the discipline and its image?', 'opcion5-sports-event-registration' ) . '">🗑 ' . esc_html__( 'Delete', 'opcion5-sports-event-registration' ) . '</a>
    190190                    </td>
    191191                </tr>
  • opcion5-sports-event-registration/trunk/admin/class-wpcr-participants-list.php

    r3642215 r3645531  
    271271            // plugin ($pair_cell y get_row_actions_html) se filtra con wp_kses_post().
    272272            echo '<tr data-search="' . esc_attr( $search_val ) . '">'
    273                 . '<td><a href="' . esc_url( $edit_url ) . '" title="' . esc_attr__( 'Editar ficha del participante', 'opcion5-sports-event-registration' ) . '">' . esc_html( $p['first_name'] ) . '</a></td>'
     273                . '<td><a href="' . esc_url( $edit_url ) . '" title="' . esc_attr__( 'Edit entrant details', 'opcion5-sports-event-registration' ) . '">' . esc_html( $p['first_name'] ) . '</a></td>'
    274274                . '<td>' . esc_html( $ap ) . '</td>'
    275275                . '<td>' . esc_html( $p['dni'] ) . '</td>'
    … …  
    281281                . '<td><b>' . esc_html( $estado ) . '</b></td>'
    282282                . '<td class="wpcr-actions-compact">'
    283                 . '<a href="' . esc_url( $edit_url ) . '" class="wpcr-ico" data-tip="' . esc_attr__( 'Editar participante', 'opcion5-sports-event-registration' ) . '">&#9998;</a>'
     283                . '<a href="' . esc_url( $edit_url ) . '" class="wpcr-ico" data-tip="' . esc_attr__( 'Edit entrant', 'opcion5-sports-event-registration' ) . '">&#9998;</a>'
    284284                . '<button class="wpcr-icon-btn wpcr-pay"'
    285285                . ' data-id="' . esc_attr( $p['id'] ) . '"'
    286286                . ' data-name="' . esc_attr( $full_name ) . '"'
    287287                . ' data-email="' . esc_attr( $p['email'] ) . '"'
    288                 . ' data-tip="' . esc_attr__( 'Pagos', 'opcion5-sports-event-registration' ) . '">&#128179;</button>'
    289                 . '<button class="wpcr-ico wpcr-invite" data-id="' . esc_attr( $p['id'] ) . '" data-tip="' . esc_attr__( 'Invitar', 'opcion5-sports-event-registration' ) . '">&#128994;</button>'
     288                . ' data-tip="' . esc_attr__( 'Payments', 'opcion5-sports-event-registration' ) . '">&#128179;</button>'
     289                . '<button class="wpcr-ico wpcr-invite" data-id="' . esc_attr( $p['id'] ) . '" data-tip="' . esc_attr__( 'Invite', 'opcion5-sports-event-registration' ) . '">&#128994;</button>'
    290290                . wp_kses_post( self::get_row_actions_html( $p ) )
    291                 . '<button class="wpcr-ico wpcr-delete" data-id="' . esc_attr( $p['id'] ) . '" data-tip="' . esc_attr__( 'Eliminar participante', 'opcion5-sports-event-registration' ) . '" style="color:#d33">&#128465;</button>'
     291                . '<button class="wpcr-ico wpcr-delete" data-id="' . esc_attr( $p['id'] ) . '" data-tip="' . esc_attr__( 'Delete entrant', 'opcion5-sports-event-registration' ) . '" style="color:#d33">&#128465;</button>'
    292292                . '</td>'
    293293                . '</tr>';
  • opcion5-sports-event-registration/trunk/admin/events/tabs/emails.php

    r3642215 r3645531  
    1111                                <!-- Email inscripción recibida -->
    1212                                <tr>
    13                                     <th>Email inscripción recibida</th>
     13                                    <th><?php esc_html_e( 'Registration received email', 'opcion5-sports-event-registration' ); ?></th>
    1414                                    <td>
    1515                                        <?php $ins_enabled = $event["wpcr_mail_ins_enabled"] ?? 0; ?>
    … …  
    2020                                        </label>
    2121
    22                                         <p><strong>Asunto email</strong><br>
     22                                        <p><strong><?php esc_html_e( 'Email subject', 'opcion5-sports-event-registration' ); ?></strong><br>
    2323                                            <input type="text" name="wpcr_mail_ins_subject" class="regular-text"
    2424                                                   value="<?php echo esc_attr( $event['wpcr_mail_ins_subject'] ?? 'Inscripción recibida - {nombre_evento}' ); ?>">
    2525                                        </p>
    2626
    27                                         <p><strong>Contenido del email</strong></p>
     27                                        <p><strong><?php esc_html_e( 'Email content', 'opcion5-sports-event-registration' ); ?></strong></p>
    2828                                        <?php
    2929                                        $body = $event['wpcr_mail_ins_body'] ??
    … …  
    4747                                <!-- Email de resultado provisional en meta -->
    4848                                <tr>
    49                                     <th>Email de resultado en meta</th>
     49                                    <th><?php esc_html_e( 'Finish line result email', 'opcion5-sports-event-registration' ); ?></th>
    5050                                    <td>
    5151                                        <?php $res_enabled = $event['result_mail_enabled'] ?? 0; ?>
    … …  
    6767                                        <?php endif; ?>
    6868
    69                                         <p><strong>Asunto email</strong><br>
     69                                        <p><strong><?php esc_html_e( 'Email subject', 'opcion5-sports-event-registration' ); ?></strong><br>
    7070                                            <input type="text" name="result_mail_subject" class="regular-text"
    7171                                                   value="<?php echo esc_attr( $event['result_mail_subject'] ?? '🏁 Tu resultado provisional - {nombre_evento}' ); ?>">
    7272                                        </p>
    7373
    74                                         <p><strong>Contenido del email</strong></p>
     74                                        <p><strong><?php esc_html_e( 'Email content', 'opcion5-sports-event-registration' ); ?></strong></p>
    7575                                        <?php
    7676                                        $res_body = $event['result_mail_body'] ?:
    … …  
    107107                                        </label>
    108108
    109                                         <p><strong>Días después del registro para avisar:</strong><br>
     109                                        <p><strong><?php esc_html_e( 'Days after registration to send the reminder:', 'opcion5-sports-event-registration' ); ?></strong><br>
    110110                                            <input type="number" name="wpcr_mail_reminder_days" min="0"
    111111                                                   style="width:80px"
    … …  
    114114                                        </p>
    115115
    116                                         <p><strong>Plantilla recordatorio</strong></p>
     116                                        <p><strong><?php esc_html_e( 'Reminder template', 'opcion5-sports-event-registration' ); ?></strong></p>
    117117                                        <?php
    118118                                        $rem = $event['wpcr_mail_reminder_body'] ??
    … …  
    145145                                        <br><br>
    146146
    147                                         <label>Días después de registrarse:</label>
     147                                        <label><?php esc_html_e( 'Days after registering:', 'opcion5-sports-event-registration' ); ?></label>
    148148                                        <input type="number" name="reminder_days"
    149149                                               value="<?php echo esc_attr( $event["reminder_days"] ?? 2 ); ?>"
    … …  
    151151                                        <p><small>Se enviará un email si el usuario no ha realizado el pago.</small></p>
    152152
    153                                         <p><strong>Contenido del email</strong></p>
     153                                        <p><strong><?php esc_html_e( 'Email content', 'opcion5-sports-event-registration' ); ?></strong></p>
    154154                                        <?php
    155155                                        wp_editor(
    … …  
    182182                                        <br><br>
    183183
    184                                         <label>Días antes del cierre del cupo:</label>
     184                                        <label><?php esc_html_e( 'Days before the quota closes:', 'opcion5-sports-event-registration' ); ?></label>
    185185                                        <input type="number" name="last_call_days"
    186186                                               value="<?php echo esc_attr( $event["last_call_days"] ?? 1 ); ?>"
    … …  
    188188                                        <p><small>Se enviará como aviso final de urgencia.</small></p>
    189189
    190                                         <p><strong>Contenido del email</strong></p>
     190                                        <p><strong><?php esc_html_e( 'Email content', 'opcion5-sports-event-registration' ); ?></strong></p>
    191191                                        <?php
    192192                                        wp_editor(
    … …  
    217217
    218218                            <ul class="wpcr-side-list">
    219                                 <li><strong>{nombre_evento}</strong> - nombre del evento</li>
    220                                 <li><strong>{fecha_evento}</strong> - fecha del evento</li>
    221                                 <li><strong>{precio}</strong> - precio actual con IVA</li>
    222                                 <li><strong>{nombre}</strong> - nombre del participante</li>
    223                                 <li><strong>{apellidos}</strong> - apellidos del participante</li>
    224                                 <li><strong>{dni}</strong> - documento identificativo</li>
    225                                 <li><strong>{fecha_inscripcion}</strong> - fecha y hora de registro</li>
    226                                 <li><strong>{url_pago}</strong> - enlace directo al pago</li>
     219                                <li><strong>{nombre_evento}</strong> <?php esc_html_e( '- event name', 'opcion5-sports-event-registration' ); ?></li>
     220                                <li><strong>{fecha_evento}</strong> <?php esc_html_e( '- event date', 'opcion5-sports-event-registration' ); ?></li>
     221                                <li><strong>{precio}</strong> <?php esc_html_e( '- current price including VAT', 'opcion5-sports-event-registration' ); ?></li>
     222                                <li><strong>{nombre}</strong> <?php esc_html_e( '- entrant first name', 'opcion5-sports-event-registration' ); ?></li>
     223                                <li><strong>{apellidos}</strong> <?php esc_html_e( '- entrant surname', 'opcion5-sports-event-registration' ); ?></li>
     224                                <li><strong>{dni}</strong> <?php esc_html_e( '- ID number', 'opcion5-sports-event-registration' ); ?></li>
     225                                <li><strong>{fecha_inscripcion}</strong> <?php esc_html_e( '- registration date and time', 'opcion5-sports-event-registration' ); ?></li>
     226                                <li><strong>{url_pago}</strong> <?php esc_html_e( '- direct payment link', 'opcion5-sports-event-registration' ); ?></li>
    227227                                <?php if ( wpcr_is_addon_active( 'wpcr-checkin' ) ) : ?>
    228228                                <li><strong>{qr_entrega}</strong> - QR para recoger el dorsal (addon Entrega de dorsales). Si no lo colocas, se añade solo al final del email de confirmación.</li>
    … …  
    232232                            <h3 class="wpcr-side-title" style="margin-top:18px;">Solo email de resultado</h3>
    233233                            <ul class="wpcr-side-list">
    234                                 <li><strong>{tiempo}</strong> - tiempo empleado</li>
    235                                 <li><strong>{dorsal}</strong> - dorsal del corredor</li>
    236                                 <li><strong>{pos_general}</strong> - posición general provisional</li>
    237                                 <li><strong>{pos_categoria}</strong> - posición en su categoría</li>
    238                                 <li><strong>{pos_genero}</strong> - posición por género</li>
    239                                 <li><strong>{categoria}</strong> - nombre de la categoría</li>
    240                                 <li><strong>{url_clasificaciones}</strong> - enlace a la pestaña de clasificaciones de la página pública del evento</li>
     234                                <li><strong>{tiempo}</strong> <?php esc_html_e( '- finishing time', 'opcion5-sports-event-registration' ); ?></li>
     235                                <li><strong>{dorsal}</strong> <?php esc_html_e( '- entrant bib number', 'opcion5-sports-event-registration' ); ?></li>
     236                                <li><strong>{pos_general}</strong> <?php esc_html_e( '- provisional overall position', 'opcion5-sports-event-registration' ); ?></li>
     237                                <li><strong>{pos_categoria}</strong> <?php esc_html_e( '- position in their category', 'opcion5-sports-event-registration' ); ?></li>
     238                                <li><strong>{pos_genero}</strong> <?php esc_html_e( '- position by gender', 'opcion5-sports-event-registration' ); ?></li>
     239                                <li><strong>{categoria}</strong> <?php esc_html_e( '- category name', 'opcion5-sports-event-registration' ); ?></li>
     240                                <li><strong>{url_clasificaciones}</strong> <?php esc_html_e( '- link to the standings tab of the public event page', 'opcion5-sports-event-registration' ); ?></li>
    241241                                <li><strong>{clasificacion_local}</strong> - línea con la posición en la clasificación local (solo se rellena si el corredor es local y el evento tiene activada esa clasificación; si no, queda vacío)</li>
    242242                            </ul>
  • opcion5-sports-event-registration/trunk/admin/events/tabs/general.php

    r3642215 r3645531  
    1111           <table class="form-table">
    1212<tr>
    13     <th colspan="2"><h3 style="margin:0;">⚙️ Datos principales del evento</h3></th>
     13    <th colspan="2"><h3 style="margin:0;">⚙️ <?php esc_html_e( 'Main event details', 'opcion5-sports-event-registration' ); ?></h3></th>
    1414</tr>
    1515<tr>
    … …  
    4444
    4545<tr>
    46     <th><label for="wpcr-description">Descripción</label></th>
     46    <th><label for="wpcr-description"><?php esc_html_e( 'Description', 'opcion5-sports-event-registration' ); ?></label></th>
    4747    <td>
    4848        <textarea id="wpcr-description" name="description" rows="3" class="large-text"><?php
    … …  
    6161    <td>
    6262        <select id="wpcr-nationality" name="nationality" style="width:260px;">
    63             <option value="ES"   <?php selected($currentNat,'ES'); ?>>España</option>
     63            <option value="ES"   <?php selected($currentNat,'ES'); ?>><?php esc_html_e( 'Spain', 'opcion5-sports-event-registration' ); ?></option>
    6464            <option value="OTRA" <?php echo $isOther?'selected':''; ?>>Otra (indicar)</option>
    6565        </select>
    … …  
    7474<!-- ░░░ PROVINCIA ░░░ -->
    7575<tr id="row-provincia">
    76     <th><label for="wpcr-provincia">Provincia</label></th>
     76    <th><label for="wpcr-provincia"><?php esc_html_e( 'Province', 'opcion5-sports-event-registration' ); ?></label></th>
    7777    <td>
    7878        <select id="wpcr-provincia" name="provincia" style="width:260px;">
    … …  
    127127<!-- LOCALIDAD -->
    128128<tr>
    129     <th><label for="wpcr-localidad">Localidad</label></th>
     129    <th><label for="wpcr-localidad"><?php esc_html_e( 'Town', 'opcion5-sports-event-registration' ); ?></label></th>
    130130    <td>
    131131        <input type="text" id="wpcr-localidad" name="localidad"
    … …  
    165165
    166166<tr>
    167     <th>Estado</th>
     167    <th><?php esc_html_e( 'Status', 'opcion5-sports-event-registration' ); ?></th>
    168168    <td><label><input type="checkbox" name="status" value="1"
    169169        <?php checked($event["status"],1); ?>> Activo</label></td>
    … …  
    278278<div class="wpcr-poster-box">
    279279
    280     <h3 class="wpcr-poster-title">📌 Cartel del evento</h3>
    281     <p class="wpcr-poster-desc">Formatos recomendados: JPG o PNG — Máx 1MB</p>
     280    <h3 class="wpcr-poster-title">📌 <?php esc_html_e( 'Event poster', 'opcion5-sports-event-registration' ); ?></h3>
     281    <p class="wpcr-poster-desc"><?php esc_html_e( 'Recommended formats: JPG or PNG — max. 1MB', 'opcion5-sports-event-registration' ); ?></p>
    282282
    283283    <!-- PREVIEW -->
    … …  
    294294
    295295        <label class="wpcr-upload-btn">
    296             <span>📁 Seleccionar archivo</span>
     296            <span>📁 <?php esc_html_e( 'Choose file', 'opcion5-sports-event-registration' ); ?></span>
    297297            <input type="file" id="poster_file" name="poster_file" accept="image/*">
    298298        </label>
  • opcion5-sports-event-registration/trunk/admin/events/tabs/options.php

    r3642215 r3645531  
    33        TAB OPCIONES
    44=============================== -->
    5     <h3>🛠 Opciones del evento</h3>
     5    <h3>🛠 <?php esc_html_e( 'Event options', 'opcion5-sports-event-registration' ); ?></h3>
    66
    77    <table class="form-table">
    … …  
    1111        ================================ -->
    1212        <tr>
    13             <th>Distancia de la prueba (km)</th>
     13            <th><?php esc_html_e( 'Race distance (km)', 'opcion5-sports-event-registration' ); ?></th>
    1414            <td>
    1515                <input type="number" name="distance_km" min="0" step="0.01"
    … …  
    3030        ================================ -->
    3131        <tr>
    32             <th>Límite de participantes</th>
     32            <th><?php esc_html_e( 'Entrant limit', 'opcion5-sports-event-registration' ); ?></th>
    3333            <td>
    3434                <label>
    … …  
    4848        ================================ -->
    4949        <tr>
    50             <th>Modalidad parejas</th>
     50            <th><?php esc_html_e( 'Pair registration', 'opcion5-sports-event-registration' ); ?></th>
    5151            <td>
    5252                <label>
    … …  
    6363                            <?php echo esc_attr( empty( $event['pairs_enabled'] ) ? 'display:none;' : '' ); ?>">
    6464
    65                     <p style="margin:0 0 6px;"><strong>Modalidades de pareja disponibles:</strong></p>
     65                    <p style="margin:0 0 6px;"><strong><?php esc_html_e( 'Pair categories available:', 'opcion5-sports-event-registration' ); ?></strong></p>
    6666                    <label style="display:inline-block;margin-right:16px;">
    6767                        <input type="checkbox" name="pair_categories[]" value="male"
    … …  
    8080                    </label>
    8181
    82                     <p style="margin:14px 0 4px;"><strong>Cupo de inscripciones para parejas</strong> (número de parejas, cada una son 2 dorsales; 0 = sin límite):</p>
     82                    <p style="margin:14px 0 4px;"><strong><?php esc_html_e( 'Pair registration quota', 'opcion5-sports-event-registration' ); ?></strong> (número de parejas, cada una son 2 dorsales; 0 = sin límite):</p>
    8383                    <input type="number" name="pairs_max_couples" min="0"
    8484                           value="<?php echo (int) ( $event['pairs_max_couples'] ?? 0 ); ?>"
    … …  
    113113        ================================ -->
    114114        <tr>
    115             <th>Bloqueo / lista de espera</th>
     115            <th><?php esc_html_e( 'Cut-off / waiting list', 'opcion5-sports-event-registration' ); ?></th>
    116116            <td>
    117117
    … …  
    147147               
    148148                           <tr>
    149     <th colspan="3"><h3 style="margin:0;">📝 Personalizaciones</h3></th>
     149    <th colspan="3"><h3 style="margin:0;">📝 <?php esc_html_e( 'Customisations', 'opcion5-sports-event-registration' ); ?></h3></th>
    150150</tr>
    151151<?php if ( wpcr_is_addon_active( 'wpcr-participant-area' ) ) : ?>
    152152<tr>
    153     <th>Fondo del diploma</th>
     153    <th><?php esc_html_e( 'Certificate background', 'opcion5-sports-event-registration' ); ?></th>
    154154    <td>
    155155        <?php
    … …  
    161161            <img src="<?php echo esc_url( $diploma_bg_url ); ?>" style="max-width:320px;border:1px solid #ddd;border-radius:6px;display:block;">
    162162        </div>
    163         <button type="button" class="button" id="wpcr-diploma-bg-select">🖼 Elegir imagen de fondo</button>
    164         <button type="button" class="button" id="wpcr-diploma-bg-remove" style="<?php echo $diploma_bg_url ? '' : 'display:none;'; ?>">🗑 Quitar</button>
     163        <button type="button" class="button" id="wpcr-diploma-bg-select">🖼 <?php esc_html_e( 'Choose background image', 'opcion5-sports-event-registration' ); ?></button>
     164        <button type="button" class="button" id="wpcr-diploma-bg-remove" style="<?php echo $diploma_bg_url ? '' : 'display:none;'; ?>">🗑 <?php esc_html_e( 'Remove', 'opcion5-sports-event-registration' ); ?></button>
    165165        <p style="margin-top:8px;padding:8px 12px;background:#f0f4ff;border-left:3px solid #0073aa;font-size:13px;color:#333;">
    166166            Se usa como fondo del diploma de participación descargable desde el área privada del corredor.
    167             <strong>Medida recomendada: 1754 × 1240 px</strong> (horizontal, formato JPG o PNG). Si no subes
     167            <strong><?php esc_html_e( 'Recommended size: 1754 × 1240 px', 'opcion5-sports-event-registration' ); ?></strong> (horizontal, formato JPG o PNG). Si no subes
    168168            ninguna, se usa un diseño por defecto.
    169169        </p>
    … …  
    196196<?php endif; ?>
    197197<tr>
    198     <th>Formulario inscripción</th>
     198    <th><?php esc_html_e( 'Registration form', 'opcion5-sports-event-registration' ); ?></th>
    199199    <td>
    200200        <select name="form_template_id">
    201             <option value="0">Formulario estándar</option>
     201            <option value="0"><?php esc_html_e( 'Standard form', 'opcion5-sports-event-registration' ); ?></option>
    202202            <?php foreach ($forms as $f): ?>
    203203                <option value="<?php echo (int)$f["id"]; ?>"
    … …  
    218218    <td>
    219219        <select name="category_set_id">
    220             <option value="0">— Sin asignar —</option>
     220            <option value="0"><?php esc_html_e( '— Not assigned —', 'opcion5-sports-event-registration' ); ?></option>
    221221            <?php foreach ($categories as $c): ?>
    222222                <option value="<?php echo (int)$c["id"]; ?>"
    … …  
    238238        ================================ -->
    239239        <tr>
    240             <th>Seleccionar talla</th>
     240            <th><?php esc_html_e( 'Select size', 'opcion5-sports-event-registration' ); ?></th>
    241241            <td>
    242242
    … …  
    255255                            <?php echo esc_attr( empty($event['tallas_enabled']) ? 'display:none;' : '' ); ?>">
    256256
    257                     <label><strong>Etiqueta campo:</strong></label><br>
     257                    <label><strong><?php esc_html_e( 'Field label:', 'opcion5-sports-event-registration' ); ?></strong></label><br>
    258258                    <input type="text" name="tallas_label"
    259259                           value="<?php echo esc_attr($event['tallas_label'] ?? 'Selecciona tu talla'); ?>"
    260260                           style="width:260px;margin-bottom:8px;">
    261261
    262                     <p><strong>Tallas disponibles:</strong></p>
     262                    <p><strong><?php esc_html_e( 'Sizes available:', 'opcion5-sports-event-registration' ); ?></strong></p>
    263263
    264264                    <?php
    … …  
    308308        <tr>
    309309            <th colspan="2" style="padding-top:25px;">
    310                 <h3 style="margin:0;">Validación de DNI</h3>
     310                <h3 style="margin:0;"><?php esc_html_e( 'ID number validation', 'opcion5-sports-event-registration' ); ?></h3>
    311311            </th>
    312312        </tr>
    313313
    314314        <tr>
    315             <th>Validar formato DNI/NIE español</th>
     315            <th><?php esc_html_e( 'Check the Spanish ID/NIE format', 'opcion5-sports-event-registration' ); ?></th>
    316316            <td>
    317317                <label>
    … …  
    336336        <tr>
    337337            <th colspan="2" style="padding-top:25px;">
    338                 <h3 style="margin:0;">Privacidad del evento</h3>
     338                <h3 style="margin:0;"><?php esc_html_e( 'Event privacy', 'opcion5-sports-event-registration' ); ?></h3>
    339339            </th>
    340340        </tr>
    … …  
    356356        ?>
    357357        <tr>
    358             <th>Activar política de privacidad</th>
     358            <th><?php esc_html_e( 'Enable privacy policy', 'opcion5-sports-event-registration' ); ?></th>
    359359            <td>
    360360                <label>
    361361                    <input type="checkbox" id="wpcr-privacy-enabled" name="privacy_enabled" value="1"
    362362                        <?php checked( $event['privacy_enabled'] ?? 0 , 1 ); ?>>
    363                     Activar política de privacidad
     363                    <?php esc_html_e( 'Enable privacy policy', 'opcion5-sports-event-registration' ); ?>
    364364                </label>
    365365                <p style="font-size:13px;color:#777;margin-top:6px;">
    … …  
    371371
    372372        <tr class="wpcr-privacy-row">
    373             <th>Texto de la política<br><small>(HTML permitido)</small></th>
     373            <th><?php esc_html_e( 'Policy text', 'opcion5-sports-event-registration' ); ?><br><small><?php esc_html_e( '(HTML allowed)', 'opcion5-sports-event-registration' ); ?></small></th>
    374374            <td>
    375375                <?php if ( $org_has ) : ?>
    … …  
    381381                <?php else : ?>
    382382                    <p style="font-size:13px;color:#b45309;margin:0 0 6px;">
    383                         ⚠ No hay datos del organizador en la pestaña <strong>Info técnica</strong>.
     383                        ⚠ No hay datos del organizador en la pestaña <strong><?php esc_html_e( 'Race info', 'opcion5-sports-event-registration' ); ?></strong>.
    384384                        Complétalos allí y pulsa “Regenerar” para que aparezcan como responsable del tratamiento.
    385385                    </p>
    … …  
    392392
    393393                <p style="margin-top:8px;">
    394                     <button type="button" class="button" id="wpcr-privacy-regen">↻ Regenerar con datos del organizador</button>
     394                    <button type="button" class="button" id="wpcr-privacy-regen">↻ <?php esc_html_e( 'Regenerate with the organizer details', 'opcion5-sports-event-registration' ); ?></button>
    395395                    <span style="font-size:12px;color:#777;margin-left:8px;">
    396396                        Sustituye el texto por el modelo por defecto (perderás los cambios manuales).
    … …  
    432432
    433433        <tr class="wpcr-privacy-row">
    434             <th>URL documento completo<br><small>(opcional)</small></th>
     434            <th><?php esc_html_e( 'Full document URL', 'opcion5-sports-event-registration' ); ?><br><small><?php esc_html_e( '(optional)', 'opcion5-sports-event-registration' ); ?></small></th>
    435435            <td>
    436436                <input type="url"
    … …  
    451451        ================================ -->
    452452        <tr>
    453             <th>Privacidad en el listado de inscritos público</th>
     453            <th><?php esc_html_e( 'Privacy in the public entrants list', 'opcion5-sports-event-registration' ); ?></th>
    454454            <td>
    455455                <label>
  • opcion5-sports-event-registration/trunk/admin/events/tabs/participants.php

    r3642215 r3645531  
    33     TAB PARTICIPANTES
    44=============================== -->
    5 <h2 style="margin-top:0;">👥 Participantes del evento</h2>
     5<h2 style="margin-top:0;">👥 <?php esc_html_e( 'Event entrants', 'opcion5-sports-event-registration' ); ?></h2>
    66
    77<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px;">
    88    <a id="wpcr-add-user" class="button button-primary"
    99       href="<?php echo esc_url( admin_url( 'admin.php?page=wpcr-add-participant&event=' . rawurlencode( (string) ( $event['public_id'] ?? '' ) ) ) ); ?>">
    10         + Añadir participante
     10        + <?php esc_html_e( 'Add entrant', 'opcion5-sports-event-registration' ); ?>
    1111    </a>
    12     <button type="button" id="wpcr-recalc-categories" class="button" style="background:#f0f0f0;">🔄 Recalcular categorías</button>
     12    <button type="button" id="wpcr-recalc-categories" class="button" style="background:#f0f0f0;">🔄 <?php esc_html_e( 'Recalculate categories', 'opcion5-sports-event-registration' ); ?></button>
    1313    <span id="wpcr-recalc-status" style="font-size:13px;font-weight:600;"></span>
    1414
  • opcion5-sports-event-registration/trunk/admin/events/tabs/prices.php

    r3642215 r3645531  
    1212?>
    1313
    14 <h2 style="margin-top:0;">💶 Configuración de precios</h2>
    15 <p>Elige el modo de precio y configura las tarifas.</p>
     14<h2 style="margin-top:0;">💶 <?php esc_html_e( 'Price settings', 'opcion5-sports-event-registration' ); ?></h2>
     15<p><?php esc_html_e( 'Choose the pricing mode and set the fees.', 'opcion5-sports-event-registration' ); ?></p>
    1616
    1717<table class="form-table" style="max-width:900px;">
    … …  
    2121    ================================ -->
    2222    <tr>
    23         <th>Modo de precio</th>
     23        <th><?php esc_html_e( 'Pricing mode', 'opcion5-sports-event-registration' ); ?></th>
    2424        <td>
    2525            <?php
    … …  
    3131                <input type="radio" name="price_mode" value="fixed"
    3232                    <?php checked($price_mode,'fixed'); ?>>
    33                 Precio fijo
     33                <?php esc_html_e( 'Fixed price', 'opcion5-sports-event-registration' ); ?>
    3434            </label><br>
    3535
    … …  
    3737                <input type="radio" name="price_mode" value="ranges"
    3838                    <?php checked($price_mode,'ranges'); ?>>
    39                 Precio por franjas
     39                <?php esc_html_e( 'Price by date', 'opcion5-sports-event-registration' ); ?>
    4040            </label><br>
    4141
    … …  
    4343                <input type="radio" name="price_mode" value="free"
    4444                    <?php checked($price_mode,'free'); ?>>
    45                 Gratis
     45                <?php esc_html_e( 'Free', 'opcion5-sports-event-registration' ); ?>
    4646            </label>
    4747
    4848            <div id="wpcr-price-fixed"
    4949                 style="margin-top:10px; <?php echo esc_attr( ($price_mode==='fixed')?'':'display:none;' ); ?>">
    50                 <label>Importe (€):</label>
     50                <label><?php esc_html_e( 'Amount (€):', 'opcion5-sports-event-registration' ); ?></label>
    5151                <input type="number" step="0.01" name="fixed_price"
    5252                       value="<?php echo esc_attr($fixed_price); ?>"
    … …  
    5454
    5555                <p style="font-size:12px;color:#666;margin-top:6px;">
    56                     Si seleccionas Gratis, este importe se ignorará.
     56                    <?php esc_html_e( 'If you choose Free, this amount is ignored.', 'opcion5-sports-event-registration' ); ?>
    5757                </p>
    5858            </div>
    … …  
    6565    <tr id="wpcr-prices-ranges-row"
    6666        style="<?php echo esc_attr( ($price_mode==='ranges')?'':'display:none;' ); ?>">
    67         <th>Franjas de precio</th>
     67        <th><?php esc_html_e( 'Price periods', 'opcion5-sports-event-registration' ); ?></th>
    6868        <td>
    6969
    70             <p>Configura diferentes precios según el rango de fechas.</p>
     70            <p><?php esc_html_e( 'Set different prices depending on the date range.', 'opcion5-sports-event-registration' ); ?></p>
    7171
    7272            <table class="widefat" style="max-width:750px;">
    7373                <thead>
    7474                    <tr>
    75                         <th>Desde</th>
    76                         <th>Hasta</th>
    77                         <th>Precio (€)</th>
     75                        <th><?php esc_html_e( 'From', 'opcion5-sports-event-registration' ); ?></th>
     76                        <th><?php esc_html_e( 'To', 'opcion5-sports-event-registration' ); ?></th>
     77                        <th><?php esc_html_e( 'Price (€)', 'opcion5-sports-event-registration' ); ?></th>
    7878                        <th></th>
    7979                    </tr>
    … …  
    101101                                <button type="button"
    102102                                        class="button wpcr-remove">
    103                                     Eliminar
     103                                    <?php esc_html_e( 'Remove', 'opcion5-sports-event-registration' ); ?>
    104104                                </button>
    105105                            </td>
    … …  
    113113                    class="button button-primary"
    114114                    style="margin-top:8px;">
    115                 + Añadir franja
     115                <?php esc_html_e( '+ Add period', 'opcion5-sports-event-registration' ); ?>
    116116            </button>
    117117
    … …  
    123123    ================================ -->
    124124    <tr>
    125         <th>Extras / Opciones adicionales</th>
     125        <th><?php esc_html_e( 'Extras / Optional add-ons', 'opcion5-sports-event-registration' ); ?></th>
    126126        <td>
    127127
    128             <p>Añade suplementos o descuentos opcionales. El precio puede ser negativo.</p>
     128            <p><?php esc_html_e( 'Add optional extras or discounts. The price can be negative.', 'opcion5-sports-event-registration' ); ?></p>
    129129
    130130            <table class="widefat" style="max-width:750px;margin-top:10px;">
    131131                <thead>
    132132                    <tr>
    133                         <th>Título</th>
    134                         <th>Precio (€)</th>
     133                        <th><?php esc_html_e( 'Title', 'opcion5-sports-event-registration' ); ?></th>
     134                        <th><?php esc_html_e( 'Price (€)', 'opcion5-sports-event-registration' ); ?></th>
    135135                        <th>Tipo</th>
    136136                        <th></th>
    … …  
    161161                                <button type="button"
    162162                                        class="button wpcr-remove-extra">
    163                                     Eliminar
     163                                    <?php esc_html_e( 'Remove', 'opcion5-sports-event-registration' ); ?>
    164164                                </button>
    165165                            </td>
    … …  
    172172                    id="wpcr-add-extra"
    173173                    class="button">
    174                 + Añadir extra
     174                <?php esc_html_e( '+ Add extra', 'opcion5-sports-event-registration' ); ?>
    175175            </button>
    176176
  • opcion5-sports-event-registration/trunk/admin/events/tabs/tech.php

    r3642215 r3645531  
    77     DATOS DEL ORGANIZADOR
    88=============================== -->
    9 <h2 style="margin-top:0;">👤 Datos del organizador</h2>
     9<h2 style="margin-top:0;">👤 <?php esc_html_e( 'Organizer details', 'opcion5-sports-event-registration' ); ?></h2>
    1010<p class="description">
    11     Información de contacto del organizador del evento
     11    <?php esc_html_e( 'Contact details of the event organizer', 'opcion5-sports-event-registration' ); ?>
    1212</p>
    1313
    … …  
    3030                       value="1"
    3131                       <?php checked($org_name_pub); ?>>
    32                 Mostrar en la página pública
     32                <?php esc_html_e( 'Show on the public page', 'opcion5-sports-event-registration' ); ?>
    3333            </label>
    3434        </p>
    … …  
    5252                       value="1"
    5353                       <?php checked($org_email_pub); ?>>
    54                 Mostrar en la página pública
     54                <?php esc_html_e( 'Show on the public page', 'opcion5-sports-event-registration' ); ?>
    5555            </label>
    5656        </p>
    … …  
    5959
    6060<tr>
    61     <th><label for="wpcr-organizer-phone">Teléfono de contacto</label></th>
     61    <th><label for="wpcr-organizer-phone"><?php esc_html_e( 'Contact phone', 'opcion5-sports-event-registration' ); ?></label></th>
    6262    <td>
    6363        <input type="text"
    … …  
    7474                       value="1"
    7575                       <?php checked($org_phone_pub); ?>>
    76                 Mostrar en la página pública
     76                <?php esc_html_e( 'Show on the public page', 'opcion5-sports-event-registration' ); ?>
    7777            </label>
    7878        </p>
    … …  
    9797];
    9898?>
    99 <h2 style="margin-top:30px;">📢 Canal de comunicación</h2>
     99<h2 style="margin-top:30px;">📢 <?php esc_html_e( 'Communication channel', 'opcion5-sports-event-registration' ); ?></h2>
    100100<p class="description">
    101101    Enlaces a las redes sociales o web donde se publican las comunicaciones del evento.
    … …  
    135135    </tr>
    136136    <tr>
    137         <th>Apertura de inscripciones</th>
    138         <td><?php echo ! empty( $event['reg_open'] ) && $event['reg_open'] !== '0000-00-00 00:00:00' ? esc_html( date_i18n( 'd/m/Y H:i', strtotime( $event['reg_open'] ) ) ) : '<em style="color:#999;">Sin definir</em>'; ?></td>
    139     </tr>
    140     <tr>
    141         <th>Cierre de inscripciones</th>
    142         <td><?php echo ! empty( $event['reg_close'] ) && $event['reg_close'] !== '0000-00-00 00:00:00' ? esc_html( date_i18n( 'd/m/Y H:i', strtotime( $event['reg_close'] ) ) ) : '<em style="color:#999;">Sin definir</em>'; ?></td>
     137        <th><?php esc_html_e( 'Registration opens', 'opcion5-sports-event-registration' ); ?></th>
     138        <td><?php echo ! empty( $event['reg_open'] ) && $event['reg_open'] !== '0000-00-00 00:00:00' ? esc_html( wpcr_format_datetime( $event['reg_open'] ) ) : '<em style="color:#999;">Sin definir</em>'; ?></td>
     139    </tr>
     140    <tr>
     141        <th><?php esc_html_e( 'Registration closes', 'opcion5-sports-event-registration' ); ?></th>
     142        <td><?php echo ! empty( $event['reg_close'] ) && $event['reg_close'] !== '0000-00-00 00:00:00' ? esc_html( wpcr_format_datetime( $event['reg_close'] ) ) : '<em style="color:#999;">Sin definir</em>'; ?></td>
    143143    </tr>
    144144    <tr>
    … …  
    189189     OBSERVACIONES
    190190=============================== -->
    191 <h2 style="margin-top:30px;">📝 Observaciones</h2>
     191<h2 style="margin-top:30px;">📝 <?php esc_html_e( 'Notes', 'opcion5-sports-event-registration' ); ?></h2>
    192192<p class="description">Texto libre para cualquier información adicional que quieras mostrar en la ficha técnica pública (normativa, avituallamientos, recomendaciones, etc.).</p>
    193193<textarea name="tech_observations" rows="5" style="width:100%;max-width:900px;"><?php echo esc_textarea( $event['tech_observations'] ?? '' ); ?></textarea>
  • opcion5-sports-event-registration/trunk/admin/js/wpcr-admin.js

    r3642215 r3645531  
    5858            //-------------------------------------------
    5959            if (res.data && res.data.already_in_event) {
    60                 alert("⚠️ Este participante YA está inscrito en este evento.\n\nNo puedes añadirlo de nuevo.");
     60                alert("⚠️ " + wpcr_ajax.i18n.alreadyEntered);
    6161                return;
    6262            }
  • opcion5-sports-event-registration/trunk/asset/admin/js/shared/save-event.js

    r3642215 r3645531  
    1212
    1313    function warnInvalidDistanceComma(){
    14         alert('⚠ El campo "Distancia de la prueba (km)" debe usar un PUNTO como separador decimal (ejemplo: 20.5), no una coma. Corrige el valor antes de guardar.');
     14        alert('⚠ ' + wpcrAdmin.i18n.decimalPoint);
    1515    }
    1616
    … …  
    2626
    2727            var btn = $(this);
    28             btn.prop('disabled', true).text('Guardando... ⏳');
     28            btn.prop('disabled', true).text(wpcrAdmin.i18n.saving + ' ⏳');
    2929
    3030            var form = document.getElementById('wpcr-event-form');
    … …  
    8282
    8383            var btn = $(this);
    84             btn.prop('disabled', true).text('Guardando... ⏳');
     84            btn.prop('disabled', true).text(wpcrAdmin.i18n.saving + ' ⏳');
    8585
    8686            var form = document.getElementById('wpcr-event-form');
  • opcion5-sports-event-registration/trunk/asset/admin/js/tabs/cobros.js

    r3642215 r3645531  
    2121
    2222            if (!id || !amount) {
    23                 alert('Indica un importe válido.');
     23                alert(wpcrAdmin.i18n.invalidAmount);
    2424                return;
    2525            }
    2626
    2727            // 🔒 Feedback inmediato
    28             btn.prop('disabled', true).text('Procesando... ⏳');
     28            btn.prop('disabled', true).text(wpcrAdmin.i18n.processing + ' ⏳');
    2929
    3030            jQuery.post(
    … …  
    6767
    6868            // 🔒 Feedback inmediato
    69             btn.prop('disabled', true).text('Procesando... ⏳');
     69            btn.prop('disabled', true).text(wpcrAdmin.i18n.processing + ' ⏳');
    7070
    7171            jQuery.post(
  • opcion5-sports-event-registration/trunk/asset/admin/js/tabs/participants.js

    r3642215 r3645531  
    8181        })
    8282        .always(function(){
    83             $btn.prop('disabled', false).text('🔄 Recalcular categorías');
     83            $btn.prop('disabled', false).text('🔄 ' + wpcrAdmin.i18n.recalcCategories);
    8484            setTimeout(function(){ $status.text(''); }, 6000);
    8585        });
    … …  
    141141        jQuery('.wpcr-delete').off('click').on('click', function(e){
    142142            e.preventDefault();
    143             if (!confirm('¿Eliminar inscrito?')) return;
     143            if (!confirm(wpcrAdmin.i18n.deleteEntrant)) return;
    144144            const id = jQuery(this).data('id');
    145145            jQuery.post(ajaxurl, { action: 'wpcr_delete_user', id, nonce: wpcrAdmin.nonce }, loadParticipants);
  • opcion5-sports-event-registration/trunk/asset/wpcr-frontend.js

    r3642215 r3645531  
    8585        var validarDNIActivo = (window.wpcr_ajax && wpcr_ajax.validate_dni !== false);
    8686        if (validarDNIActivo && nationality === "ES" && !validarDNI(dni)) {
    87             feedback.textContent = "⚠️ DNI/NIE no válido. Comprueba el número y la letra.";
     87            feedback.textContent = "⚠️ " + wpcr_ajax.i18n.invalidDni;
    8888            feedback.style.color = "red";
    8989            return;
    … …  
    114114            try { res = JSON.parse(raw); }
    115115            catch (e) {
    116                 feedback.textContent = "Error al comprobar el DNI. Inténtalo de nuevo.";
     116                feedback.textContent = wpcr_ajax.i18n.checkError;
    117117                feedback.style.color = "red";
    118118                console.error("WPCR DNI: respuesta no válida del servidor:", raw);
    … …  
    124124                var errMsg = (res && res.data && res.data.msg)
    125125                    ? res.data.msg
    126                     : "Error al comprobar el DNI. Inténtalo de nuevo.";
     126                    : wpcr_ajax.i18n.checkError;
    127127                feedback.textContent = "⚠️ " + errMsg;
    128128                feedback.style.color = "red";
    … …  
    132132            // Ya está inscrito en este evento
    133133            if (res.data && res.data.already_in_event) {
    134                 feedback.textContent = "⚠️ Este DNI ya está inscrito en esta prueba.";
     134                feedback.textContent = "⚠️ " + wpcr_ajax.i18n.alreadyIn;
    135135                feedback.style.color = "red";
    136136                return;
    … …  
    169169                var resumen =
    170170                    "<div style='background:#f0fdf4;border:1px solid #86efac;border-radius:8px;padding:12px 14px;margin:10px 0;text-align:left;'>" +
    171                     "<div style='font-weight:bold;color:#166534;margin-bottom:6px;'>✓ Te hemos reconocido de inscripciones anteriores</div>" +
     171                    "<div style='font-weight:bold;color:#166534;margin-bottom:6px;'>✓ " + wpcr_ajax.i18n.recognised + "</div>" +
    172172                    "<div style='font-size:13px;color:#334155;line-height:1.7;'>" +
    173                         (masked.first_name ? "Nombre: <b>" + masked.first_name + " " + (masked.last_name || "") + "</b><br>" : "") +
    174                         (masked.email      ? "Email: <b>"  + masked.email + "</b><br>" : "") +
    175                         (masked.phone      ? "Teléfono: <b>" + masked.phone + "</b><br>" : "") +
    176                         (masked.localidad  ? "Localidad: <b>" + masked.localidad + "</b>" : "") +
     173                        (masked.first_name ? wpcr_ajax.i18n.labelName + " <b>" + masked.first_name + " " + (masked.last_name || "") + "</b><br>" : "") +
     174                        (masked.email      ? wpcr_ajax.i18n.labelEmail + " <b>"  + masked.email + "</b><br>" : "") +
     175                        (masked.phone      ? wpcr_ajax.i18n.labelPhone + " <b>" + masked.phone + "</b><br>" : "") +
     176                        (masked.localidad  ? wpcr_ajax.i18n.labelTown + " <b>" + masked.localidad + "</b>" : "") +
    177177                    "</div></div>";
    178178
    179179                feedback.innerHTML =
    180180                    resumen +
    181                     "<button type='button' id='wpcr-load-data' style='padding:8px 14px;margin-right:10px;'>Son correctos, usar mis datos</button>" +
    182                     "<button type='button' id='wpcr-reset-data' style='padding:8px 14px;'>Han cambiado, actualizarlos</button>";
     181                    "<button type='button' id='wpcr-load-data' style='padding:8px 14px;margin-right:10px;'>" + wpcr_ajax.i18n.useMyData + "</button>" +
     182                    "<button type='button' id='wpcr-reset-data' style='padding:8px 14px;'>" + wpcr_ajax.i18n.dataChanged + "</button>";
    183183
    184184                var btnLoad  = document.getElementById("wpcr-load-data");
    … …  
    197197
    198198                        feedback.innerHTML =
    199                             "<span style='color:green;font-weight:bold;'>✓ Usaremos tus datos guardados. Completa lo que falte (talla, etc.) y pulsa Enviar.</span>";
     199                            "<span style='color:green;font-weight:bold;'>✓ " + wpcr_ajax.i18n.willUseSaved + "</span>";
    200200                    };
    201201                }
  • opcion5-sports-event-registration/trunk/cron/wpcr-cron-pairs.php

    r3642215 r3645531  
    5555            if ( ! is_email( $p['email'] ) ) continue;
    5656
    57             $subject = 'Tu pareja no completó la inscripción - ' . $ev['name'];
     57            $subject = sprintf(
     58                /* translators: %s: event name */
     59                __( 'Your partner did not complete the registration - %s', 'opcion5-sports-event-registration' ),
     60                $ev['name']
     61            );
    5862            $body = '
    5963<div style="font-family:Arial,Helvetica,sans-serif;line-height:1.6;color:#0a2540;max-width:600px;margin:0 auto;">
    60     <h2 style="color:#0d47a1;">👥 Inscripción por parejas — ' . esc_html( $ev['name'] ) . '</h2>
    61     <p>Hola <strong>' . esc_html( $p['first_name'] ) . '</strong>,</p>
    62     <p>El plazo de inscripción de <strong>' . esc_html( $ev['name'] ) . '</strong> ha finalizado y tu pareja
    63        (código <strong>' . esc_html( $p['pair_code'] ) . '</strong>) no llegó a completar su inscripción.</p>
    64     <p><strong>Tu plaza sigue siendo válida:</strong> pasas a participar en la modalidad
    65        <strong>individual</strong>.</p>
    66     <p>Si crees que se trata de un error, contacta con la organización del evento.</p>'
     64    <h2 style="color:#0d47a1;">👥 ' . sprintf(
     65        /* translators: %s: event name */
     66        esc_html__( 'Pair registration — %s', 'opcion5-sports-event-registration' ),
     67        esc_html( $ev['name'] )
     68    ) . '</h2>
     69    <p>' . sprintf(
     70        /* translators: %s: participant first name */
     71        esc_html__( 'Hi %s,', 'opcion5-sports-event-registration' ),
     72        '<strong>' . esc_html( $p['first_name'] ) . '</strong>'
     73    ) . '</p>
     74    <p>' . sprintf(
     75        /* translators: 1: event name, 2: pair code */
     76        esc_html__( 'Registration for %1$s has closed and your partner (code %2$s) did not complete theirs.', 'opcion5-sports-event-registration' ),
     77        '<strong>' . esc_html( $ev['name'] ) . '</strong>',
     78        '<strong>' . esc_html( $p['pair_code'] ) . '</strong>'
     79    ) . '</p>
     80    <p>' . sprintf(
     81        /* translators: 1: "Your place is still valid" in bold, 2: the word "individual" in bold */
     82        esc_html__( '%1$s you now take part in the %2$s category.', 'opcion5-sports-event-registration' ),
     83        '<strong>' . esc_html__( 'Your place is still valid:', 'opcion5-sports-event-registration' ) . '</strong>',
     84        '<strong>' . esc_html__( 'individual', 'opcion5-sports-event-registration' ) . '</strong>'
     85    ) . '</p>
     86    <p>' . esc_html__( 'If you think this is a mistake, please contact the event organizer.', 'opcion5-sports-event-registration' ) . '</p>'
    6787    . ( function_exists( 'wpcr_email_footer' ) ? wpcr_email_footer() : '' ) . '
    6888</div>';
  • opcion5-sports-event-registration/trunk/includes/class-wpcr-demo-data.php

    r3642215 r3645531  
    7474        ] );
    7575        $price_extras = wp_json_encode( [
    76             [ 'label' => 'Camiseta técnica', 'price' => 5,  'type' => 'checkbox' ],
    77             [ 'label' => 'Maillot oficial',  'price' => 25, 'type' => 'checkbox' ],
     76            [ 'label' => __( 'Technical T-shirt', 'opcion5-sports-event-registration' ), 'price' => 5,  'type' => 'checkbox' ],
     77            [ 'label' => __( 'Official jersey', 'opcion5-sports-event-registration' ),  'price' => 25, 'type' => 'checkbox' ],
    7878        ] );
    7979
    … …  
    8686        // 6) Crear el evento demo (salida a las 09:00 del día de la prueba, cupo de 100 plazas)
    8787        $wpdb->insert( $events_table, [
    88             'name'             => '🧪 Evento de ejemplo',
     88            'name'             => '🧪 ' . __( 'Sample event', 'opcion5-sports-event-registration' ),
    8989            'description'      => 'Este es un evento de ejemplo con inscritos, modalidad, categorías y precios ya rellenos para que pruebes el plugin: formulario de inscripción, gestión de participantes, precios y correos. Puedes borrarlo cuando quieras desde "Resetear datos demo".',
    9090            'event_date'       => $event_date,
    … …  
    117117        $event_id = (int) $wpdb->insert_id;
    118118        if ( ! $event_id ) {
    119             return [ 'ok' => false, 'msg' => 'No se pudo crear el evento de ejemplo.' ];
     119            return [ 'ok' => false, 'msg' => __( 'The sample event could not be created.', 'opcion5-sports-event-registration' ) ];
    120120        }
    121121
    … …  
    143143        update_option( 'wpcr_demo_category_set_id', $category_set_id, false );
    144144
    145         return [ 'ok' => true, 'msg' => 'Datos de ejemplo instalados correctamente.', 'event_id' => $event_id ];
     145        return [ 'ok' => true, 'msg' => __( 'Sample data installed successfully.', 'opcion5-sports-event-registration' ), 'event_id' => $event_id ];
    146146    }
    147147
    … …  
    154154            delete_option( self::OPTION_EVENT_ID );
    155155            self::cleanup_orphan_demo_category_set();
    156             return [ 'ok' => true, 'msg' => 'No había datos de ejemplo instalados.' ];
     156            return [ 'ok' => true, 'msg' => __( 'There was no sample data installed.', 'opcion5-sports-event-registration' ) ];
    157157        }
    158158
    … …  
    164164        self::cleanup_orphan_demo_modality();
    165165
    166         return [ 'ok' => true, 'msg' => 'Datos de ejemplo eliminados.' ];
     166        return [ 'ok' => true, 'msg' => __( 'Sample data removed.', 'opcion5-sports-event-registration' ) ];
    167167    }
    168168
    … …  
    221221
    222222        $wpdb->insert( $sets_table, [
    223             'name' => 'Categorías de ejemplo (por edad)',
     223            'name' => __( 'Sample categories (by age)', 'opcion5-sports-event-registration' ),
    224224            'type' => 'age',
    225225        ] );
  • opcion5-sports-event-registration/trunk/includes/class-wpcr-install.php

    r3642215 r3645531  
    356356            [
    357357                'option'    => 'wpcr_event_list_page_id',
    358                 'title'     => __( 'Eventos', 'opcion5-sports-event-registration' ),
     358                'title'     => __( 'Events', 'opcion5-sports-event-registration' ),
    359359                'shortcode' => 'wpcr_event_list',
    360360            ],
    361361            [
    362362                'option'    => 'wpcr_event_page_id',
    363                 'title'     => __( 'Inscripción', 'opcion5-sports-event-registration' ),
     363                'title'     => __( 'Registration', 'opcion5-sports-event-registration' ),
    364364                'shortcode' => 'wpcr_event',
    365365            ],
  • opcion5-sports-event-registration/trunk/includes/emails/class-wpcr-payment-emails.php

    r3642215 r3645531  
    7979        $subject = apply_filters(
    8080            'wpcr_payment_email_subject',
    81             '✅ Pago confirmado – ' . $event_name,
     81            '✅ ' . sprintf(
     82                /* translators: %s: event name */
     83                __( 'Payment confirmed – %s', 'opcion5-sports-event-registration' ),
     84                $event_name
     85            ),
    8286            $payment,
    8387            $event,
    … …  
    9599<tr><td align='center'>
    96100
    97 <h2 style='color:#003a99;margin-top:0;'>🎉 Pago confirmado</h2>
     101<h2 style='color:#003a99;margin-top:0;'>🎉 " . esc_html__( 'Payment confirmed', 'opcion5-sports-event-registration' ) . "</h2>
    98102
    99103<p style='font-size:16px;'>
    100 Hola <strong>{$name}</strong>,
     104" . sprintf(
     105    /* translators: %s: participant first name */
     106    esc_html__( 'Hi %s,', 'opcion5-sports-event-registration' ),
     107    '<strong>' . $name . '</strong>'
     108) . "
    101109</p>
    102110
    103111<p style='font-size:15px;'>
    104 Tu inscripción en el evento:
     112" . esc_html__( 'Your registration for the event:', 'opcion5-sports-event-registration' ) . "
    105113</p>
    106114
    … …  
    108116
    109117<p style='font-size:15px;'>
    110 <strong>Importe pagado:</strong> {$amount} €<br>
    111 <strong>Método de pago:</strong> {$method}
     118<strong>" . esc_html__( 'Amount paid:', 'opcion5-sports-event-registration' ) . "</strong> {$amount} €<br>
     119<strong>" . esc_html__( 'Payment method:', 'opcion5-sports-event-registration' ) . "</strong> {$method}
    112120</p>
    113121
    … …  
    115123
    116124<p style='font-size:14px;color:#555;'>
    117 ✅ Tu plaza ha quedado <strong>confirmada</strong>.<br>
    118 Te esperamos el día del evento.
     125✅ " . sprintf(
     126    /* translators: %s: the word "confirmed", in bold */
     127    esc_html__( 'Your place is now %s.', 'opcion5-sports-event-registration' ),
     128    '<strong>' . esc_html__( 'confirmed', 'opcion5-sports-event-registration' ) . '</strong>'
     129) . "<br>
     130" . esc_html__( 'See you on the day of the event.', 'opcion5-sports-event-registration' ) . "
    119131</p>
    120132
    121133<p style='font-size:13px;color:#777;margin-top:25px;'>
    122 Este email es automático, no respondas a este mensaje.
     134" . esc_html__( 'This is an automatic email, please do not reply to it.', 'opcion5-sports-event-registration' ) . "
    123135</p>
    124136
    … …  
    160172            wpcr_mail(
    161173                $organizer_email,
    162                 '💳 Pago recibido – ' . $event_name,
    163                 "Se ha confirmado un pago de {$amount} € ({$method}) para el evento {$event_name}.",
     174                '💳 ' . sprintf(
     175                    /* translators: %s: event name */
     176                    __( 'Payment received – %s', 'opcion5-sports-event-registration' ),
     177                    $event_name
     178                ),
     179                sprintf(
     180                    /* translators: 1: amount with currency, 2: payment method, 3: event name */
     181                    __( 'A payment of %1$s € (%2$s) has been confirmed for the event %3$s.', 'opcion5-sports-event-registration' ),
     182                    $amount,
     183                    $method,
     184                    $event_name
     185                ),
    164186                $headers,
    165187                $event_name
  • opcion5-sports-event-registration/trunk/includes/emails/class-wpcr-result-email.php

    r3642215 r3645531  
    105105
    106106                    if ( $pos_local > 0 ) {
    107                         $gender_label = $gender === 'M' ? 'masculina' : 'femenina';
    108                         $local_line = '🏠 Posición en la clasificación local ' . $gender_label . ': <b>' . $pos_local . '</b><br><br>';
     107                        $gender_label = $gender === 'M'
     108                            ? __( "men's", 'opcion5-sports-event-registration' )
     109                            : __( "women's", 'opcion5-sports-event-registration' );
     110                        $local_line = '🏠 ' . sprintf(
     111                            /* translators: 1: gender of the local standings, 2: position */
     112                            __( 'Position in the %1$s local standings: <b>%2$s</b>', 'opcion5-sports-event-registration' ),
     113                            $gender_label,
     114                            $pos_local
     115                        ) . '<br><br>';
    109116                    }
    110117                }
    … …  
    112119        }
    113120
    114         $subject = $event['result_mail_subject'] ?: '🏁 Tu resultado provisional - {nombre_evento}';
     121        $subject = $event['result_mail_subject'] ?: '🏁 ' . __( 'Your provisional result - {nombre_evento}', 'opcion5-sports-event-registration' );
    115122        $body    = $event['result_mail_body'] ?: self::default_body();
    116123
    … …  
    128135            '{apellidos}'     => trim( ( $participant['middle_name'] ?? '' ) . ' ' . ( $participant['last_name'] ?? '' ) ),
    129136            '{nombre_evento}' => $event['name'],
    130             '{fecha_evento}'  => ! empty( $event['event_date'] ) ? date_i18n( 'd/m/Y', strtotime( $event['event_date'] ) ) : '',
     137            '{fecha_evento}'  => wpcr_format_date( $event['event_date'] ?? '' ),
    131138            '{dorsal}'        => $result['dorsal'] ?: ( $participant['dorsal'] ?? '' ),
    132139            '{tiempo}'        => $result['tiempo'],
    … …  
    146153            $area_url = $results_base . '#wpcr-area-privada';
    147154            $diploma_notice = '<div style="margin-top:18px;padding:14px 16px;background:#f0f4ff;border:1px solid #c0d0f0;border-radius:8px;text-align:center;">'
    148                 . '<p style="margin:0 0 10px;font-size:14px;">📄 ¿Ya tienes tu cuenta? Descarga tu <b>diploma de participación</b> desde tu área privada.</p>'
    149                 . '<a href="' . esc_url( $area_url ) . '" target="_blank" style="display:inline-block;padding:10px 20px;background:#166534;color:#ffffff;border-radius:6px;text-decoration:none;font-weight:bold;font-size:14px;">🎓 Ir a mi área privada</a>'
     155                . '<p style="margin:0 0 10px;font-size:14px;">📄 ' . sprintf(
     156                    /* translators: %s: the words "participation certificate", in bold */
     157                    esc_html__( 'Do you already have an account? Download your %s from your private area.', 'opcion5-sports-event-registration' ),
     158                    '<b>' . esc_html__( 'participation certificate', 'opcion5-sports-event-registration' ) . '</b>'
     159                ) . '</p>'
     160                . '<a href="' . esc_url( $area_url ) . '" target="_blank" style="display:inline-block;padding:10px 20px;background:#166534;color:#ffffff;border-radius:6px;text-decoration:none;font-weight:bold;font-size:14px;">🎓 ' . esc_html__( 'Go to my private area', 'opcion5-sports-event-registration' ) . '</a>'
    150161                . '</div>';
    151162        }
    … …  
    154165              . wpautop( $body )
    155166              . $diploma_notice
    156               . '<p style="font-size:12px;color:#888;margin-top:18px;">Resultado provisional, pendiente de revisión oficial por la organización.</p>'
     167              . '<p style="font-size:12px;color:#888;margin-top:18px;">' . esc_html__( 'Provisional result, pending official review by the organization.', 'opcion5-sports-event-registration' ) . '</p>'
    157168              . ( function_exists( 'wpcr_email_footer' ) ? wpcr_email_footer() : '' )
    158169              . '</div>';
    … …  
    172183    }
    173184
     185    /**
     186     * Cuerpo por defecto del email de resultados.
     187     *
     188     * Los marcadores entre llaves los sustituye strtr() más arriba y son los
     189     * mismos que el organizador puede usar en su propia plantilla: no se
     190     * traducen, o las plantillas ya guardadas dejarían de rellenar los datos.
     191     */
    174192    private static function default_body() {
    175         return 'Hola {nombre}, ¡acabas de cruzar la meta de <b>{nombre_evento}</b>! 🎉<br><br>'
    176              . 'Este es tu resultado <b>provisional</b>:<br><br>'
    177              . '⏱ Tiempo: <b>{tiempo}</b><br>'
    178              . '🏆 Posición general: <b>{pos_general}</b><br>'
    179              . '🏅 Posición en tu categoría ({categoria}): <b>{pos_categoria}</b><br>'
    180              . '🚻 Posición por género: <b>{pos_genero}</b><br><br>'
     193        return sprintf(
     194            /* translators: 1: the {nombre} placeholder, 2: the {nombre_evento} placeholder in bold */
     195            __( 'Hi %1$s, you have just crossed the finish line at %2$s!', 'opcion5-sports-event-registration' ),
     196            '{nombre}',
     197            '<b>{nombre_evento}</b>'
     198        ) . ' 🎉<br><br>'
     199             . sprintf(
     200                 /* translators: %s: the word "provisional", in bold */
     201                 __( 'This is your %s result:', 'opcion5-sports-event-registration' ),
     202                 '<b>' . __( 'provisional', 'opcion5-sports-event-registration' ) . '</b>'
     203             ) . '<br><br>'
     204             . '⏱ ' . __( 'Time:', 'opcion5-sports-event-registration' ) . ' <b>{tiempo}</b><br>'
     205             . '🏆 ' . __( 'Overall position:', 'opcion5-sports-event-registration' ) . ' <b>{pos_general}</b><br>'
     206             . '🏅 ' . __( 'Position in your category', 'opcion5-sports-event-registration' ) . ' ({categoria}): <b>{pos_categoria}</b><br>'
     207             . '🚻 ' . __( 'Position by gender:', 'opcion5-sports-event-registration' ) . ' <b>{pos_genero}</b><br><br>'
    181208             . '{clasificacion_local}'
    182209             . '<a href="{url_clasificaciones}" target="_blank" '
    183210             . 'style="display:inline-block;padding:12px 24px;background:#0d47a1;color:#ffffff;'
    184211             . 'border-radius:6px;text-decoration:none;font-weight:bold;font-size:15px;">'
    185              . '📱 Ver clasificaciones completas</a><br><br>'
    186              . '¡Enhorabuena por completar el recorrido!';
     212             . '📱 ' . __( 'See the full standings', 'opcion5-sports-event-registration' ) . '</a><br><br>'
     213             . __( 'Congratulations on finishing the course!', 'opcion5-sports-event-registration' );
    187214    }
    188215}
  • opcion5-sports-event-registration/trunk/modules/addons/AddonsPage.php

    r3642631 r3645531  
    1515                'url'         => 'https://wpcompetitionregister.com/estadisticas-del-evento/',
    1616                'icon'        => '📊',
    17                 'title'       => 'Estadísticas + PDF',
    18                 'description' => 'Informes profesionales con KPIs, gráficos, análisis por talla, provincia y más. Exporta en PDF con un clic.',
    19                 'features'    => ['Estadísticas automáticas del evento', 'Informe PDF descargable', 'KPIs, gráficos y métricas', 'Análisis por categoría y género'],
     17                'title'       => __( 'Statistics + PDF', 'opcion5-sports-event-registration' ),
     18                'description' => __( 'Professional reports with KPIs, charts and breakdowns by size, province and more. Export to PDF in one click.', 'opcion5-sports-event-registration' ),
     19                'features'    => [__( 'Automatic event statistics', 'opcion5-sports-event-registration' ), __( 'Downloadable PDF report', 'opcion5-sports-event-registration' ), __( 'KPIs, charts and metrics', 'opcion5-sports-event-registration' ), __( 'Breakdown by category and gender', 'opcion5-sports-event-registration' )],
    2020                'plans'       => ['BASIC', 'PRO'],
    2121            ],
    … …  
    2424                'url'         => 'https://wpcompetitionregister.com/ficha-tecnica/',
    2525                'icon'        => '📖',
    26                 'title'       => 'Documentación técnica',
    27                 'description' => 'Adjunta documentos ilimitados (PDF, ZIP, imágenes) en la pestaña de Información Técnica de tu evento.',
    28                 'features'    => ['PDF de normativa y reglamento', 'Tracks en archivos GPX/ZIP', 'Imágenes del recorrido', 'Actualizaciones continuas'],
     26                'title'       => __( 'Event documents', 'opcion5-sports-event-registration' ),
     27                'description' => __( 'Attach unlimited documents (PDF, ZIP, images) in the Race info tab of your event.', 'opcion5-sports-event-registration' ),
     28                'features'    => [__( 'PDF of rules and regulations', 'opcion5-sports-event-registration' ), __( 'Tracks in GPX/ZIP files', 'opcion5-sports-event-registration' ), __( 'Route images', 'opcion5-sports-event-registration' ), __( 'Ongoing updates', 'opcion5-sports-event-registration' )],
    2929                'plans'       => ['BASIC', 'PRO'],
    3030            ],
    … …  
    3333                'url'         => 'https://wpcompetitionregister.com/cobros-por-stripe/',
    3434                'icon'        => '💳',
    35                 'title'       => 'Pagos con Stripe',
    36                 'description' => 'Cobra las inscripciones con tarjeta de crédito/débito mediante Stripe Checkout. Confirmación automática por email.',
    37                 'features'    => ['Pago con tarjeta seguro (Stripe)', 'Confirmación automática', 'Compatible con precios por franjas', 'Registro de ingresos por evento'],
     35                'title'       => __( 'Stripe payments', 'opcion5-sports-event-registration' ),
     36                'description' => __( 'Take registration payments by credit or debit card through Stripe Checkout. Automatic confirmation by email.', 'opcion5-sports-event-registration' ),
     37                'features'    => [__( 'Secure card payment (Stripe)', 'opcion5-sports-event-registration' ), __( 'Automatic confirmation', 'opcion5-sports-event-registration' ), __( 'Works with date-based pricing', 'opcion5-sports-event-registration' ), __( 'Income record per event', 'opcion5-sports-event-registration' )],
    3838                'plans'       => ['PRO'],
    3939            ],
    … …  
    4242                'url'         => 'https://wpcompetitionregister.com/cobros-por-paypal/',
    4343                'icon'        => '🅿',
    44                 'title'       => 'Pagos con PayPal',
    45                 'description' => 'Permite a los participantes pagar con su cuenta PayPal o con tarjeta a través de PayPal.',
    46                 'features'    => ['Pago con PayPal o tarjeta', 'Confirmación automática', 'Reconocido internacionalmente', 'Registro de ingresos por evento'],
     44                'title'       => __( 'PayPal payments', 'opcion5-sports-event-registration' ),
     45                'description' => __( 'Let entrants pay with their PayPal account or by card through PayPal.', 'opcion5-sports-event-registration' ),
     46                'features'    => [__( 'Pay with PayPal or by card', 'opcion5-sports-event-registration' ), __( 'Automatic confirmation', 'opcion5-sports-event-registration' ), __( 'Recognised worldwide', 'opcion5-sports-event-registration' ), __( 'Income record per event', 'opcion5-sports-event-registration' )],
    4747                'plans'       => ['PRO'],
    4848            ],
    … …  
    5151                'url'         => 'https://wpcompetitionregister.com/conector-con-woocommerce/',
    5252                'icon'        => '🛒',
    53                 'title'       => 'Conexión WooCommerce',
    54                 'description' => 'Integra el checkout de WooCommerce para cobrar con cualquier pasarela instalada: Redsys, Bizum, TPV, etc.',
    55                 'features'    => ['Usa cualquier pasarela de WooCommerce', 'Producto por evento creado automáticamente', 'Precio dinámico por franjas', 'Compatible con Redsys, Bizum y más'],
     53                'title'       => __( 'WooCommerce connection', 'opcion5-sports-event-registration' ),
     54                'description' => __( 'Use the WooCommerce checkout to charge with any gateway you have installed: Redsys, Bizum, card terminals and so on.', 'opcion5-sports-event-registration' ),
     55                'features'    => [__( 'Use any WooCommerce gateway', 'opcion5-sports-event-registration' ), __( 'A product per event, created automatically', 'opcion5-sports-event-registration' ), __( 'Dynamic pricing by date', 'opcion5-sports-event-registration' ), __( 'Works with Redsys, Bizum and more', 'opcion5-sports-event-registration' )],
    5656                'plans'       => ['PRO'],
    5757            ],
    … …  
    6060                'url'         => 'https://wpcompetitionregister.com/asignacion-de-dorsales/',
    6161                'icon'        => '🎽',
    62                 'title'       => 'Dorsales automáticos',
    63                 'description' => 'Asigna dorsales únicos automáticamente según el orden de inscripción, con prefijos y rangos configurables.',
    64                 'features'    => ['Asignación automática al pagar', 'Prefijo y rango configurables', 'Sin duplicados ni conflictos', 'Exportación de listado de dorsales'],
     62                'title'       => __( 'Automatic bib numbers', 'opcion5-sports-event-registration' ),
     63                'description' => __( 'Assign unique bib numbers automatically in registration order, with configurable prefixes and ranges.', 'opcion5-sports-event-registration' ),
     64                'features'    => [__( 'Assigned automatically on payment', 'opcion5-sports-event-registration' ), __( 'Configurable prefix and range', 'opcion5-sports-event-registration' ), __( 'No duplicates or clashes', 'opcion5-sports-event-registration' ), __( 'Export the bib number list', 'opcion5-sports-event-registration' )],
    6565                'plans'       => ['PRO'],
    6666            ],
    … …  
    6969                'url'         => 'https://wpcompetitionregister.com/asignacion-de-dorsales-disenador/',
    7070                'icon'        => '🖨',
    71                 'title'       => 'Dorsales + Diseñador',
    72                 'description' => 'Todo lo del addon de Dorsales más el diseñador de dorsales físicos: PDF listo para imprenta con una página por corredor, a medida real.',
    73                 'features'    => ['Incluye toda la asignación de dorsales', 'PDF a medida real (200×150 / 210×150 mm)', 'Fondo con tu foto, colores por categoría', 'Talla, controles y teléfono de emergencias'],
     71                'title'       => __( 'Bib numbers + Designer', 'opcion5-sports-event-registration' ),
     72                'description' => __( 'Everything in the Bib numbers extension plus the physical bib designer: a print-ready PDF with one page per entrant, at real size.', 'opcion5-sports-event-registration' ),
     73                'features'    => [__( 'Includes all the bib assignment', 'opcion5-sports-event-registration' ), __( 'PDF at real size (200×150 / 210×150 mm)', 'opcion5-sports-event-registration' ), __( 'Your photo as background, colours by category', 'opcion5-sports-event-registration' ), __( 'Size, checkpoints and emergency phone', 'opcion5-sports-event-registration' )],
    7474                'plans'       => ['PRO'],
    7575            ],
    … …  
    7878                'url'         => 'https://wpcompetitionregister.com/disenador-de-carteles/',
    7979                'icon'        => '🖼',
    80                 'title'       => 'Diseñador de Carteles',
    81                 'description' => 'Diseña el cartel del evento en A3 o A2 vertical con plantillas prediseñadas totalmente editables, vista previa en vivo y PDF listo para imprenta.',
    82                 'features'    => ['A3/A2 vertical a medida física exacta', '4 plantillas editables (textos, colores, fuentes)', 'Datos del evento prerrellenados', 'Tu foto de fondo con capa de legibilidad'],
     80                'title'       => __( 'Poster designer', 'opcion5-sports-event-registration' ),
     81                'description' => __( 'Design your event poster in A3 or A2 portrait with fully editable templates, live preview and a print-ready PDF.', 'opcion5-sports-event-registration' ),
     82                'features'    => [__( 'A3/A2 portrait at exact physical size', 'opcion5-sports-event-registration' ), __( '4 editable templates (text, colours, fonts)', 'opcion5-sports-event-registration' ), __( 'Event details pre-filled', 'opcion5-sports-event-registration' ), __( 'Your background photo with a readability layer', 'opcion5-sports-event-registration' )],
    8383                'plans'       => ['PRO'],
    8484            ],
    … …  
    8787                'url'         => 'https://wpcompetitionregister.com/cronometraje/',
    8888                'icon'        => '⏱',
    89                 'title'       => 'Cronometraje en meta',
    90                 'description' => 'Toma los tiempos en meta y en puntos intermedios desde el móvil, con captura de llegadas en grupo y clasificación en vivo por punto.',
    91                 'features'    => ['Pantalla móvil tipo teclado', 'Puntos de control con enlace y QR únicos', 'Captura de grupos (tiempo congelado)', 'Parciales y clasificación en vivo', 'Funciona sin conexión y sincroniza'],
     89                'title'       => __( 'Finish line timing', 'opcion5-sports-event-registration' ),
     90                'description' => __( 'Take times at the finish line and at intermediate points from your phone, with group finish capture and live standings per checkpoint.', 'opcion5-sports-event-registration' ),
     91                'features'    => [__( 'Keypad-style mobile screen', 'opcion5-sports-event-registration' ), __( 'Checkpoints with their own link and QR', 'opcion5-sports-event-registration' ), __( 'Group capture (frozen time)', 'opcion5-sports-event-registration' ), __( 'Splits and live standings', 'opcion5-sports-event-registration' ), __( 'Works offline and syncs', 'opcion5-sports-event-registration' )],
    9292                'plans'       => ['PRO'],
    9393            ],
    … …  
    9696                'url'         => 'https://wpcompetitionregister.com/lector-con-chip/',
    9797                'icon'        => '🔖',
    98                 'title'       => 'Lectura RFID por puntos de control',
    99                 'description' => 'Cada dorsal lleva un chip RFID: en cada punto de control (intermedio o meta), el lector lo detecta automáticamente y registra el tiempo sin intervención manual.',
    100                 'features'    => ['Puntos de control ilimitados (salida, intermedios y meta)', 'Asociación de chip a dorsal (individual o por listado)', 'Un dorsal no se lee dos veces en el mismo punto, pero sí en puntos distintos', 'Tiempo oficial o tiempo neto (desde el chip de salida) configurable', 'Pantalla pública de seguimiento en vivo por punto'],
     98                'title'       => __( 'RFID reading at checkpoints', 'opcion5-sports-event-registration' ),
     99                'description' => __( 'Each bib carries an RFID chip: at every checkpoint (intermediate or finish) the reader detects it automatically and records the time with no manual input.', 'opcion5-sports-event-registration' ),
     100                'features'    => [__( 'Unlimited checkpoints (start, intermediate and finish)', 'opcion5-sports-event-registration' ), __( 'Chip-to-bib pairing (one by one or from a list)', 'opcion5-sports-event-registration' ), __( 'A bib is not read twice at the same point, but it is at different points', 'opcion5-sports-event-registration' ), __( 'Official time or net time (from the start chip), configurable', 'opcion5-sports-event-registration' ), __( 'Public live tracking screen per checkpoint', 'opcion5-sports-event-registration' )],
    101101                'plans'       => ['PRO'],
    102102            ],
    … …  
    105105                'url'         => 'https://wpcompetitionregister.com/multidistancia/',
    106106                'icon'        => '🏁',
    107                 'title'       => 'Multidistancia / Sub-pruebas',
    108                 'description' => 'Agrupa varias distancias (10K, 21K, 42K...) bajo un mismo evento, cada una con su propia inscripción, precio, cupo, dorsales y clasificación.',
    109                 'features'    => ['Selector de distancia en la página pública', 'Cada distancia con su precio y cupo propios', 'Reutiliza dorsales, clasificaciones y cronometraje sin cambios', 'Creación de nuevas distancias con un clic', 'Un evento no puede agruparse dos veces'],
     107                'title'       => __( 'Multiple distances', 'opcion5-sports-event-registration' ),
     108                'description' => __( 'Group several distances (10K, 21K, 42K…) under one event, each with its own registration, price, quota, bib numbers and standings.', 'opcion5-sports-event-registration' ),
     109                'features'    => [__( 'Distance selector on the public page', 'opcion5-sports-event-registration' ), __( 'Each distance with its own price and quota', 'opcion5-sports-event-registration' ), __( 'Reuses bib numbers, standings and timing unchanged', 'opcion5-sports-event-registration' ), __( 'Create new distances in one click', 'opcion5-sports-event-registration' ), __( 'An event cannot be grouped twice', 'opcion5-sports-event-registration' )],
    110110                'plans'       => ['PRO'],
    111111            ],
    … …  
    114114                'url'         => 'https://wpcompetitionregister.com/area-privada/',
    115115                'icon'        => '🔐',
    116                 'title'       => 'Área privada de participantes',
    117                 'description' => 'Cada participante accede con usuario y contraseña a su historial de pruebas, clasificaciones, velocidad media comparada entre eventos y diploma descargable.',
    118                 'features'    => ['Registro y login seguros con contraseña', 'Historial de pruebas por DNI', 'Velocidad media comparada entre eventos', 'Diploma de participación en PDF', 'Inscripción rápida en nuevos eventos'],
     116                'title'       => __( 'Entrant private area', 'opcion5-sports-event-registration' ),
     117                'description' => __( 'Each entrant signs in with a username and password to see their race history, standings, average speed compared across events and a downloadable certificate.', 'opcion5-sports-event-registration' ),
     118                'features'    => [__( 'Secure sign-up and login with a password', 'opcion5-sports-event-registration' ), __( 'Race history by ID number', 'opcion5-sports-event-registration' ), __( 'Average speed compared across events', 'opcion5-sports-event-registration' ), __( 'Participation certificate in PDF', 'opcion5-sports-event-registration' ), __( 'Quick registration for new events', 'opcion5-sports-event-registration' )],
    119119                'plans'       => ['PRO'],
    120120            ],
    … …  
    123123                'url'         => 'https://wpcompetitionregister.com/contrarreloj/',
    124124                'icon'        => '⏱',
    125                 'title'       => 'Contrarreloj individual',
    126                 'description' => 'Salidas de uno en uno con intervalo configurable: pantalla móvil de salida con cuenta atrás y START por dorsal, puntos intermedios y meta con el tiempo individual de cada corredor.',
    127                 'features'    => ['Salidas individuales con intervalo configurable', 'Cuenta atrás por corredor (pausable)', 'START por dorsal: fija el tiempo real de cada uno', 'Puntos intermedios y meta desde el móvil', 'Clasificación automática por menor tiempo empleado'],
     125                'title'       => __( 'Individual time trial', 'opcion5-sports-event-registration' ),
     126                'description' => __( 'One-by-one starts with a configurable interval: mobile start screen with countdown and START per bib, intermediate points and finish with each entrant\'s individual time.', 'opcion5-sports-event-registration' ),
     127                'features'    => [__( 'Individual starts with a configurable interval', 'opcion5-sports-event-registration' ), __( 'Countdown per entrant (pausable)', 'opcion5-sports-event-registration' ), __( 'START per bib: sets each entrant\'s real time', 'opcion5-sports-event-registration' ), __( 'Intermediate points and finish from your phone', 'opcion5-sports-event-registration' ), __( 'Automatic standings by shortest time', 'opcion5-sports-event-registration' )],
    128128                'plans'       => ['PRO'],
    129129            ],
    … …  
    132132                'url'         => 'https://wpcompetitionregister.com/cupones-de-descuento/',
    133133                'icon'        => '🎟',
    134                 'title'       => 'Cupones de descuento',
    135                 'description' => 'Crea códigos de descuento por importe fijo o porcentaje para las inscripciones, con límite de usos, fechas de validez e importe mínimo.',
    136                 'features'    => ['Descuento fijo (€) o porcentual (%)', 'Límite de usos totales y por participante', 'Válido para todos los eventos o solo algunos', 'Fechas de validez e importe mínimo', 'Validación en vivo en el formulario público'],
     134                'title'       => __( 'Discount coupons', 'opcion5-sports-event-registration' ),
     135                'description' => __( 'Create discount codes by fixed amount or percentage for registrations, with a usage limit, validity dates and a minimum amount.', 'opcion5-sports-event-registration' ),
     136                'features'    => [__( 'Fixed (€) or percentage (%) discount', 'opcion5-sports-event-registration' ), __( 'Limit on total uses and uses per entrant', 'opcion5-sports-event-registration' ), __( 'Valid for every event or only some', 'opcion5-sports-event-registration' ), __( 'Validity dates and minimum amount', 'opcion5-sports-event-registration' ), __( 'Live validation in the public form', 'opcion5-sports-event-registration' )],
    137137                'plans'       => ['PRO'],
    138138            ],
    … …  
    141141                'url'         => 'https://wpcompetitionregister.com/clasificaciones-en-vivo/',
    142142                'icon'        => '🏆',
    143                 'title'       => 'Clasificaciones y resultados',
    144                 'description' => 'Publica los resultados del evento: importa desde CSV o introduce manualmente posiciones por categoría y género.',
    145                 'features'    => ['Resultados visibles en página pública', 'Importación desde CSV', 'Posición general, categoría y género', 'Activación/ocultación por evento'],
     143                'title'       => __( 'Standings and results', 'opcion5-sports-event-registration' ),
     144                'description' => __( 'Publish the event results: import them from CSV or enter positions by category and gender manually.', 'opcion5-sports-event-registration' ),
     145                'features'    => [__( 'Results visible on the public page', 'opcion5-sports-event-registration' ), __( 'Import from CSV', 'opcion5-sports-event-registration' ), __( 'Overall, category and gender positions', 'opcion5-sports-event-registration' ), __( 'Show or hide per event', 'opcion5-sports-event-registration' )],
    146146                'plans'       => ['PRO'],
    147147            ],
    … …  
    150150                'url'         => 'https://wpcompetitionregister.com/exportador-de-participantes-a-csv/',
    151151                'icon'        => '📤',
    152                 'title'       => 'Exportar participantes CSV',
    153                 'description' => 'Descarga un CSV con todos los datos de participantes: contacto, categoría, talla, estado de pago y dorsal.',
    154                 'features'    => ['Exportación masiva en un clic', 'Incluye todos los campos del inscrito', 'Formato compatible con Excel', 'Filtro por estado de pago'],
     152                'title'       => __( 'Export entrants to CSV', 'opcion5-sports-event-registration' ),
     153                'description' => __( 'Download a CSV with all the entrant data: contact details, category, size, payment status and bib number.', 'opcion5-sports-event-registration' ),
     154                'features'    => [__( 'Bulk export in one click', 'opcion5-sports-event-registration' ), __( 'Includes every entrant field', 'opcion5-sports-event-registration' ), __( 'Excel-compatible format', 'opcion5-sports-event-registration' ), __( 'Filter by payment status', 'opcion5-sports-event-registration' )],
    155155                'plans'       => ['BASIC', 'PRO'],
    156156            ],
    … …  
    159159                'url'         => 'https://wpcompetitionregister.com/importador-de-participantes-csv-xls/',
    160160                'icon'        => '📂',
    161                 'title'       => 'Importar CSV / XLS',
    162                 'description' => 'Carga participantes de forma masiva desde Excel, CSV u ODS. Asistente de mapeo de columnas incluido.',
    163                 'features'    => ['Compatible con XLS, XLSX, CSV, ODS', 'Asistente de mapeo de columnas', 'Validación antes de importar', 'Informe de resultados detallado'],
     161                'title'       => __( 'Import CSV / XLS', 'opcion5-sports-event-registration' ),
     162                'description' => __( 'Bulk load entrants from Excel, CSV or ODS. Column mapping wizard included.', 'opcion5-sports-event-registration' ),
     163                'features'    => [__( 'Works with XLS, XLSX, CSV and ODS', 'opcion5-sports-event-registration' ), __( 'Column mapping wizard', 'opcion5-sports-event-registration' ), __( 'Validation before importing', 'opcion5-sports-event-registration' ), __( 'Detailed results report', 'opcion5-sports-event-registration' )],
    164164                'plans'       => ['PRO'],
    165165            ],
    … …  
    168168                'url'         => 'https://wpcompetitionregister.com/avituallamientos-inteligentes/',
    169169                'icon'        => '🥤',
    170                 'title'       => 'Avituallamientos inteligentes',
    171                 'description' => 'Calcula cuánta agua, isotónico y fruta necesitas en cada avituallamiento según los inscritos, la temperatura, la humedad y la lluvia prevista. Con lista de la compra y PDF por punto.',
    172                 'features'    => ['Cálculo por inscritos reales o cupo completo', 'Meteorología: temperatura, humedad y lluvia', 'Perfiles MTB, trail y ruta (vasos o botellines)', 'Margen de seguridad ajustable', 'Lista de la compra y hoja PDF por punto'],
     170                'title'       => __( 'Smart aid stations', 'opcion5-sports-event-registration' ),
     171                'description' => __( 'Work out how much water, sports drink and fruit you need at each aid station based on the entrants, the temperature, the humidity and the forecast rain. With a shopping list and a PDF per station.', 'opcion5-sports-event-registration' ),
     172                'features'    => [__( 'Calculated on actual entrants or full quota', 'opcion5-sports-event-registration' ), __( 'Weather: temperature, humidity and rain', 'opcion5-sports-event-registration' ), __( 'MTB, trail and road profiles (cups or bottles)', 'opcion5-sports-event-registration' ), __( 'Adjustable safety margin', 'opcion5-sports-event-registration' ), __( 'Shopping list and a PDF sheet per station', 'opcion5-sports-event-registration' )],
    173173                'plans'       => ['PRO'],
    174174            ],
    … …  
    177177                'url'         => 'https://wpcompetitionregister.com/carreras-por-etapas/',
    178178                'icon'        => '🏔',
    179                 'title'       => 'Carreras por etapas',
    180                 'description' => 'Agrupa varias etapas (días y distancias distintas) bajo una misma carrera y genera la clasificación general por suma de tiempos. Compatible con categorías, parejas, locales y multidistancia.',
    181                 'features'    => ['General por suma de tiempos de todas las etapas', 'Tiempo de cada etapa + diferencias al líder', 'Compatible con categorías, parejas y locales', 'Varios niveles/recorridos (corto, largo…)', 'Solo clasifican quienes completan todas las etapas'],
     179                'title'       => __( 'Stage races', 'opcion5-sports-event-registration' ),
     180                'description' => __( 'Group several stages (different days and distances) under one race and produce the overall standings by total time. Works with categories, pairs, local entrants and multiple distances.', 'opcion5-sports-event-registration' ),
     181                'features'    => [__( 'Overall standings by total time across all stages', 'opcion5-sports-event-registration' ), __( 'Time for each stage plus gaps to the leader', 'opcion5-sports-event-registration' ), __( 'Works with categories, pairs and local entrants', 'opcion5-sports-event-registration' ), __( 'Several levels or routes (short, long…)', 'opcion5-sports-event-registration' ), __( 'Only those who complete every stage are classified', 'opcion5-sports-event-registration' )],
    182182                'plans'       => ['PRO'],
    183183            ],
    … …  
    186186                'url'         => 'https://wpcompetitionregister.com/ranking-o-campeonatos/',
    187187                'icon'        => '🏆',
    188                 'title'       => 'Rankings y Campeonatos',
    189                 'description' => 'Agrupa varias pruebas distintas en un ranking o campeonato y saca la clasificación general por puntos o por tiempo, cruzando a los corredores por su DNI.',
    190                 'features'    => ['Clasificación general por puntos o por tiempo', 'Tabla de puntos configurable (o descendente)', 'Descartes: cuentan las N mejores pruebas', 'General también por categoría', 'Se publica con un shortcode en cualquier página'],
     188                'title'       => __( 'Rankings and championships', 'opcion5-sports-event-registration' ),
     189                'description' => __( 'Group several different events into a ranking or championship and produce the overall standings by points or by time, matching entrants by their ID number.', 'opcion5-sports-event-registration' ),
     190                'features'    => [__( 'Overall standings by points or by time', 'opcion5-sports-event-registration' ), __( 'Configurable points table (or descending)', 'opcion5-sports-event-registration' ), __( 'Drop scores: the best N events count', 'opcion5-sports-event-registration' ), __( 'Overall standings by category too', 'opcion5-sports-event-registration' ), __( 'Published with a shortcode on any page', 'opcion5-sports-event-registration' )],
    191191                'plans'       => ['PRO'],
    192192            ],
    … …  
    195195                'url'         => 'https://wpcompetitionregister.com/entrega-de-dorsales/',
    196196                'icon'        => '🎫',
    197                 'title'       => 'Entrega de dorsales',
    198                 'description' => 'Cada inscrito recibe un QR en su email. El día de la prueba tus voluntarios leen el QR desde el móvil (o buscan por apellido) y confirman la entrega, con contador de dorsales pendientes y listado de no recogidos.',
    199                 'features'    => ['QR de entrega en el email de confirmación', 'Pantalla móvil para voluntarios (sin usuario WP)', 'Lectura de QR + búsqueda por apellido/DNI/dorsal', 'Aviso de pago pendiente antes de entregar', 'Contador en vivo y listado de no recogidos (CSV)'],
     197                'title'       => __( 'Bib collection', 'opcion5-sports-event-registration' ),
     198                'description' => __( 'Every entrant gets a QR code by email. On race day your volunteers scan the QR from their phone (or search by surname) and confirm the handover, with a counter of pending bibs and a list of those not collected.', 'opcion5-sports-event-registration' ),
     199                'features'    => [__( 'Collection QR in the confirmation email', 'opcion5-sports-event-registration' ), __( 'Mobile screen for volunteers (no WordPress account needed)', 'opcion5-sports-event-registration' ), __( 'QR scanning plus search by surname, ID or bib', 'opcion5-sports-event-registration' ), __( 'Warning about pending payment before handing over', 'opcion5-sports-event-registration' ), __( 'Live counter and list of bibs not collected (CSV)', 'opcion5-sports-event-registration' )],
    200200                'plans'       => ['PRO'],
    201201            ],
    … …  
    219219        <div class="wpcr-ap-header">
    220220            <div>
    221                 <h1 class="wpcr-ap-title">🔌 Extensiones</h1>
    222                 <p class="wpcr-ap-subtitle">Este plugin es gratuito y funciona por completo tal cual. Estas extensiones son plugins independientes, desarrollados y distribuidos aparte por el autor, que añaden funciones especializadas. Su código no forma parte de este plugin.</p>
     221                <h1 class="wpcr-ap-title">🔌 <?php esc_html_e( 'Extensions', 'opcion5-sports-event-registration' ); ?></h1>
     222                <p class="wpcr-ap-subtitle"><?php esc_html_e( 'This plugin is free and fully functional on its own. These extensions are separate plugins, developed and distributed apart by the author, that add specialised features. Their code is not part of this plugin.', 'opcion5-sports-event-registration' ); ?></p>
    223223            </div>
    224224        </div>
    225225
    226226        <!-- ═══ Addons grid ═══ -->
    227         <div class="wpcr-ap-section-title">🧩 Addons disponibles</div>
     227        <div class="wpcr-ap-section-title">🧩 <?php esc_html_e( 'Available extensions', 'opcion5-sports-event-registration' ); ?></div>
    228228
    229229        <div class="wpcr-addons-grid">
    … …  
    239239            <div class="wpcr-addon-state-pill">
    240240                <?php if ($installed): ?>
    241                     <span class="pill-active">✔ Instalado</span>
     241                    <span class="pill-active">✔ <?php esc_html_e( 'Installed', 'opcion5-sports-event-registration' ); ?></span>
    242242                <?php else: ?>
    243243                    <span class="pill-buy">No instalado</span>
    … …  
    268268                   href="<?php echo esc_url( $addon['url'] ); ?>"
    269269                   target="_blank" rel="noopener">
    270                     Más información
     270                    <?php esc_html_e( 'More information', 'opcion5-sports-event-registration' ); ?>
    271271                </a>
    272272            </div>
  • opcion5-sports-event-registration/trunk/opcion5-sports-event-registration.php

    r3642631 r3645531  
    44Plugin URI: https://wpcompetitionregister.com
    55Description: Manage registrations, participants and payments for sports events (races, MTB, trail, triathlon) directly from your WordPress site.
    6 Version: 1.0.1
     6Version: 1.1.0
    77Requires at least: 6.2
    88Tested up to: 7.0
    … …  
    1313License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1414Text Domain: opcion5-sports-event-registration
     15Domain Path: /languages
    1516*/
    1617
    … …  
    2930define('WPCR_PLUGIN_URL', plugin_dir_url(__FILE__));
    3031define('WPCR_PLUGIN_FILE', __FILE__);
    31 define('WPCR_VERSION', '1.0.1');
     32define('WPCR_VERSION', '1.1.0');
     33
     34/**
     35 * Carga las traducciones del plugin.
     36 *
     37 * Se engancha a 'init' porque a partir de WordPress 6.7 cargar un dominio de
     38 * texto antes de ese momento provoca el aviso de "just in time translation
     39 * loading". WordPress busca primero en wp-content/languages/plugins (lo que
     40 * llega desde translate.wordpress.org) y, si no lo encuentra, usa el .mo que
     41 * viaja en la carpeta languages de este plugin.
     42 */
     43add_action( 'init', function () {
     44    load_plugin_textdomain(
     45        'opcion5-sports-event-registration',
     46        false,
     47        dirname( plugin_basename( __FILE__ ) ) . '/languages'
     48    );
     49} );
    3250
    3351// Este plugin es gratuito y totalmente funcional: gestiona todos los eventos que
    … …  
    4967            error_log( '[WPCR] ' . $message );
    5068        }
     69    }
     70}
     71
     72/**
     73 * Fechas y horas con el formato que el administrador tenga configurado en
     74 * Ajustes → Generales, en lugar de un formato fijo.
     75 *
     76 * Acepta una fecha de MySQL, un texto que entienda strtotime o un timestamp, y
     77 * descarta las fechas cero que guarda MySQL cuando el campo está vacío.
     78 * Devuelve $fallback si no hay una fecha utilizable.
     79 */
     80if ( ! function_exists( 'wpcr_timestamp' ) ) {
     81    function wpcr_timestamp( $value ) {
     82        if ( empty( $value ) ) {
     83            return null;
     84        }
     85        if ( is_numeric( $value ) ) {
     86            return (int) $value;
     87        }
     88        $value = trim( (string) $value );
     89        if ( $value === '0000-00-00' || $value === '0000-00-00 00:00:00' ) {
     90            return null;
     91        }
     92        $ts = strtotime( $value );
     93        return $ts ? $ts : null;
     94    }
     95}
     96
     97if ( ! function_exists( 'wpcr_format_date' ) ) {
     98    function wpcr_format_date( $value, $fallback = '' ) {
     99        $ts = wpcr_timestamp( $value );
     100        return $ts ? date_i18n( get_option( 'date_format' ), $ts ) : $fallback;
     101    }
     102}
     103
     104if ( ! function_exists( 'wpcr_format_time' ) ) {
     105    function wpcr_format_time( $value, $fallback = '' ) {
     106        $ts = wpcr_timestamp( $value );
     107        return $ts ? date_i18n( get_option( 'time_format' ), $ts ) : $fallback;
     108    }
     109}
     110
     111if ( ! function_exists( 'wpcr_format_datetime' ) ) {
     112    function wpcr_format_datetime( $value, $fallback = '' ) {
     113        $ts = wpcr_timestamp( $value );
     114        if ( ! $ts ) {
     115            return $fallback;
     116        }
     117        return date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $ts );
    51118    }
    52119}
    … …  
    311378        // 1) Responsable del tratamiento (con datos del organizador si existen)
    312379        if ( $name !== '' ) {
    313             $responsable = "El responsable del tratamiento de los datos es {$name}, organizador del evento";
     380            $responsable = sprintf(
     381                /* translators: %s: organizer name */
     382                __( 'The data controller is %s, organizer of the event', 'opcion5-sports-event-registration' ),
     383                $name
     384            );
    314385        } else {
    315             $responsable = "El responsable del tratamiento de los datos es el organizador del evento";
     386            $responsable = __( 'The data controller is the organizer of the event', 'opcion5-sports-event-registration' );
    316387        }
    317388        $contacto = [];
    318         if ( $email !== '' ) { $contacto[] = "correo electrónico {$email}"; }
    319         if ( $phone !== '' ) { $contacto[] = "teléfono {$phone}"; }
     389        if ( $email !== '' ) {
     390            /* translators: %s: contact email address */
     391            $contacto[] = sprintf( __( 'email address %s', 'opcion5-sports-event-registration' ), $email );
     392        }
     393        if ( $phone !== '' ) {
     394            /* translators: %s: contact phone number */
     395            $contacto[] = sprintf( __( 'phone number %s', 'opcion5-sports-event-registration' ), $phone );
     396        }
    320397        if ( $contacto ) {
    321             $responsable .= ", con los siguientes datos de contacto: " . implode( ' y ', $contacto ) . ".";
     398            $responsable .= sprintf(
     399                /* translators: %s: list of contact details joined with "and" */
     400                __( ', with the following contact details: %s.', 'opcion5-sports-event-registration' ),
     401                implode( ' ' . __( 'and', 'opcion5-sports-event-registration' ) . ' ', $contacto )
     402            );
    322403        } else {
    323             $responsable .= ", cuyos datos de contacto figuran en la información oficial del mismo.";
    324         }
    325         $responsable .= " En caso de gestionarse a través de plataformas externas, estas actuarán como encargadas del tratamiento.";
    326 
    327         $text = "En cumplimiento del Reglamento (UE) 2016/679 General de Protección de Datos (RGPD) y de la Ley Orgánica 3/2018 (LOPDGDD), le informamos sobre el tratamiento de sus datos personales al realizar una inscripción en este evento.
    328 
    329 1. Responsable del Tratamiento
     404            $responsable .= __( ', whose contact details appear in the official information for the event.', 'opcion5-sports-event-registration' );
     405        }
     406        $responsable .= ' ' . __( 'Where the event is managed through external platforms, these act as data processors.', 'opcion5-sports-event-registration' );
     407
     408        /*
     409         * Plantilla por defecto de la política de privacidad. Mantiene las
     410         * referencias a la normativa española (RGPD + LOPDGDD) por decisión del
     411         * autor. El organizador puede sustituirla entera desde la pestaña
     412         * Opciones del evento.
     413         */
     414        $text = __( 'In accordance with Regulation (EU) 2016/679 (GDPR) and Spanish Organic Law 3/2018 (LOPDGDD), we inform you about the processing of your personal data when you register for this event.', 'opcion5-sports-event-registration' ) . "
     415
     416" . __( '1. Data Controller', 'opcion5-sports-event-registration' ) . "
    330417{$responsable}
    331418
    332 2. Finalidad del Tratamiento
    333 Sus datos serán tratados para:
    334 - Gestionar su inscripción y participación.
    335 - Verificar identidad y evitar inscripciones duplicadas.
    336 - Comunicar avisos, cambios, horarios u otra información relevante.
    337 - Gestionar pagos y facturación.
    338 - Emitir listados, dorsales, clasificaciones y resultados deportivos.
    339 - Gestionar seguros deportivos cuando proceda.
    340 - Enviar certificados de participación o resultados.
    341 
    342 3. Legitimación
    343 El tratamiento se basa en la ejecución de un contrato (gestión de inscripción), el consentimiento explícito del participante, obligaciones legales y el interés legítimo del organizador en publicar listados o resultados.
    344 
    345 4. Datos Tratados
    346 Datos identificativos (nombre, apellidos, DNI, fecha nacimiento), contacto (email, teléfono, localidad), datos deportivos (club, categoría, talla), datos económicos y datos vinculados a la participación (tiempos, clasificación, dorsal).
    347 
    348 5. Publicación de Listados y Resultados
    349 Podrán publicarse listados de inscritos, dorsales, resultados y clasificaciones. El usuario puede solicitar el enmascaramiento de ciertos datos si el organizador lo permite.
    350 
    351 6. Cesiones de Datos
    352 Los datos podrán comunicarse a empresas de cronometraje, aseguradoras, plataformas de inscripción, autoridades competentes o proveedores necesarios para el evento. No se realizarán transferencias internacionales no autorizadas.
    353 
    354 7. Conservación
    355 Los datos se conservarán durante la gestión del evento y el plazo legal aplicable. Los resultados pueden conservarse con fines históricos.
    356 
    357 8. Derechos
    358 El usuario puede ejercer sus derechos de acceso, rectificación, supresión, oposición, limitación, portabilidad y revocación contactando con el responsable del evento" . ( $email !== '' ? " en {$email}" : '' ) . ".
    359 
    360 9. Seguridad
    361 El organizador aplicará medidas técnicas y organizativas adecuadas para proteger sus datos.
    362 
    363 10. Aceptación
    364 Al inscribirse, el usuario declara haber leído y aceptado esta política de privacidad.";
     419" . __( '2. Purpose of the Processing', 'opcion5-sports-event-registration' ) . "
     420" . __( 'Your data will be processed in order to:', 'opcion5-sports-event-registration' ) . "
     421- " . __( 'Manage your registration and participation.', 'opcion5-sports-event-registration' ) . "
     422- " . __( 'Verify your identity and prevent duplicate registrations.', 'opcion5-sports-event-registration' ) . "
     423- " . __( 'Send notices, changes, schedules and other relevant information.', 'opcion5-sports-event-registration' ) . "
     424- " . __( 'Manage payments and invoicing.', 'opcion5-sports-event-registration' ) . "
     425- " . __( 'Produce entrant lists, bib numbers, standings and sports results.', 'opcion5-sports-event-registration' ) . "
     426- " . __( 'Manage sports insurance where applicable.', 'opcion5-sports-event-registration' ) . "
     427- " . __( 'Send participation certificates or results.', 'opcion5-sports-event-registration' ) . "
     428
     429" . __( '3. Legal Basis', 'opcion5-sports-event-registration' ) . "
     430" . __( 'The processing is based on the performance of a contract (managing your registration), the explicit consent of the entrant, legal obligations and the legitimate interest of the organizer in publishing lists or results.', 'opcion5-sports-event-registration' ) . "
     431
     432" . __( '4. Data Processed', 'opcion5-sports-event-registration' ) . "
     433" . __( 'Identification data (first name, surname, ID number, date of birth), contact data (email, phone, town), sports data (club, category, size), financial data and data linked to your participation (times, standings, bib number).', 'opcion5-sports-event-registration' ) . "
     434
     435" . __( '5. Publication of Lists and Results', 'opcion5-sports-event-registration' ) . "
     436" . __( 'Entrant lists, bib numbers, results and standings may be published. You may ask for certain data to be masked if the organizer allows it.', 'opcion5-sports-event-registration' ) . "
     437
     438" . __( '6. Data Disclosure', 'opcion5-sports-event-registration' ) . "
     439" . __( 'Your data may be disclosed to timing companies, insurers, registration platforms, the competent authorities or suppliers needed for the event. No unauthorised international transfers will be made.', 'opcion5-sports-event-registration' ) . "
     440
     441" . __( '7. Retention', 'opcion5-sports-event-registration' ) . "
     442" . __( 'Data will be kept for as long as the event is being managed and for the applicable legal period. Results may be kept for historical purposes.', 'opcion5-sports-event-registration' ) . "
     443
     444" . __( '8. Your Rights', 'opcion5-sports-event-registration' ) . "
     445" . __( 'You may exercise your rights of access, rectification, erasure, objection, restriction, portability and withdrawal of consent by contacting the person responsible for the event', 'opcion5-sports-event-registration' )
     446    . ( $email !== '' ? ' ' . sprintf( /* translators: %s: contact email address */ __( 'at %s', 'opcion5-sports-event-registration' ), $email ) : '' ) . ".
     447
     448" . __( '9. Security', 'opcion5-sports-event-registration' ) . "
     449" . __( 'The organizer will apply appropriate technical and organisational measures to protect your data.', 'opcion5-sports-event-registration' ) . "
     450
     451" . __( '10. Acceptance', 'opcion5-sports-event-registration' ) . "
     452" . __( 'By registering, you declare that you have read and accepted this privacy policy.', 'opcion5-sports-event-registration' );
    365453
    366454        return $text;
  • opcion5-sports-event-registration/trunk/public/ajax-wpcr-dni.php

    r3642215 r3645531  
    9292     */
    9393    if ( ! check_ajax_referer( 'wpcr_dni_lookup', 'nonce', false ) ) {
    94         wp_send_json_error( [ 'msg' => 'Sesión caducada. Recarga la página e inténtalo de nuevo.' ] );
     94        wp_send_json_error( [ 'msg' => __( 'Your session has expired. Reload the page and try again.', 'opcion5-sports-event-registration' ) ] );
    9595    }
    9696
    … …  
    102102
    103103    if ($dni === '') {
    104         wp_send_json_error(['msg' => 'DNI vacío']);
     104        wp_send_json_error(['msg' => __( 'Empty ID number', 'opcion5-sports-event-registration' )]);
    105105    }
    106106
    … …  
    133133        if ( ! $valid ) {
    134134            wp_send_json_error([
    135                 'msg'         => 'DNI/NIE no válido. Comprueba el número y la letra.',
     135                'msg'         => __( 'Invalid ID number. Check the digits and the letter.', 'opcion5-sports-event-registration' ),
    136136                'invalid_dni' => true,
    137137            ]);
    … …  
    191191    }
    192192    if (!check_ajax_referer('wpcr_dni_lookup', 'nonce', false)) {
    193         wp_send_json_error('Token de seguridad no válido.', 403);
     193        wp_send_json_error( __( 'Invalid security token.', 'opcion5-sports-event-registration' ), 403 );
    194194    }
    195195
    … …  
    197197
    198198    if ($dni === '') {
    199         wp_send_json_error('DNI vacío');
     199        wp_send_json_error( __( 'Empty ID number', 'opcion5-sports-event-registration' ) );
    200200    }
    201201
  • opcion5-sports-event-registration/trunk/public/class-wpcr-payment-mails.php

    r3642215 r3645531  
    2626
    2727        $subject = $event['wpcr_mail_paid_subject']
    28             ?? 'Pago confirmado - ' . $event['name'];
     28            ?? sprintf(
     29                /* translators: %s: event name */
     30                __( 'Payment confirmed - %s', 'opcion5-sports-event-registration' ),
     31                $event['name']
     32            );
    2933
     34        /*
     35         * Plantilla por defecto. Los marcadores entre llaves ({nombre},
     36         * {nombre_evento}, {precio}) son los que sustituye WPCR_Mailer y los que
     37         * el organizador escribe en sus propias plantillas: no se traducen, o las
     38         * plantillas ya guardadas dejarían de rellenar los datos.
     39         */
    3040        $template = $event['wpcr_mail_paid_body']
    31             ?? "
    32             <h2>✅ Pago confirmado</h2>
    33             <p>Hola {nombre},</p>
    34             <p>Tu inscripción en <b>{nombre_evento}</b> ha sido confirmada.</p>
    35             <p><b>Importe pagado:</b> {precio} €</p>
    36             <p>¡Te esperamos!</p>
    37             ";
     41            ?? '
     42            <h2>✅ ' . __( 'Payment confirmed', 'opcion5-sports-event-registration' ) . '</h2>
     43            <p>' . __( 'Hi', 'opcion5-sports-event-registration' ) . ' {nombre},</p>
     44            <p>' . sprintf(
     45                /* translators: %s: the {nombre_evento} placeholder, replaced with the event name */
     46                __( 'Your registration for %s has been confirmed.', 'opcion5-sports-event-registration' ),
     47                '<b>{nombre_evento}</b>'
     48            ) . '</p>
     49            <p><b>' . __( 'Amount paid:', 'opcion5-sports-event-registration' ) . '</b> {precio} €</p>
     50            <p>' . __( 'See you there!', 'opcion5-sports-event-registration' ) . '</p>
     51            ';
    3852
    3953        $mailer->send_mail(
  • opcion5-sports-event-registration/trunk/public/class-wpcr-payment-page.php

    r3642215 r3645531  
    6161        // phpcs:disable WordPress.Security.NonceVerification.Recommended
    6262        if (empty($_GET['token'])) {
    63             wp_die('Token no válido.');
     63            wp_die( esc_html__( 'Invalid token.', 'opcion5-sports-event-registration' ) );
    6464        }
    6565
    … …  
    105105
    106106        if (!$participant || !$event) {
    107             wp_die('Datos de inscripción no válidos.');
     107            wp_die( esc_html__( 'Invalid registration data.', 'opcion5-sports-event-registration' ) );
    108108        }
    109109               
    … …  
    121121    // phpcs:disable WordPress.Security.NonceVerification.Recommended
    122122    if (empty($_GET['token'])) {
    123         wp_die('Token de pago no válido.');
     123        wp_die( esc_html__( 'Invalid payment token.', 'opcion5-sports-event-registration' ) );
    124124    }
    125125
    … …  
    210210
    211211            default:
    212                 wp_die('Método de pago no soportado.');
     212                wp_die( esc_html__( 'Unsupported payment method.', 'opcion5-sports-event-registration' ) );
    213213        }
    214214
  • opcion5-sports-event-registration/trunk/public/class-wpcr-public-list.php

    r3642215 r3645531  
    141141                <a href="<?php echo esc_url( $url_prox ); ?>"
    142142                   style="padding:10px 22px;text-decoration:none;font-weight:600;border-bottom:3px solid <?php echo esc_attr($view==='proximos'?'#2271b1':'transparent'); ?>;color:<?php echo esc_attr($view==='proximos'?'#2271b1':'#666'); ?>;">
    143                     Próximos eventos
     143                    <?php esc_html_e( 'Upcoming events', 'opcion5-sports-event-registration' ); ?>
    144144                </a>
    145145                <a href="<?php echo esc_url( $url_pas ); ?>"
    146146                   style="padding:10px 22px;text-decoration:none;font-weight:600;border-bottom:3px solid <?php echo esc_attr($view==='pasados'?'#2271b1':'transparent'); ?>;color:<?php echo esc_attr($view==='pasados'?'#2271b1':'#666'); ?>;">
    147                     Eventos pasados
     147                    <?php esc_html_e( 'Past events', 'opcion5-sports-event-registration' ); ?>
    148148                </a>
    149149            </div>
    … …  
    153153
    154154                <select name="modality">
    155                     <option value="">Modalidad</option>
     155                    <option value=""><?php esc_html_e( 'Discipline', 'opcion5-sports-event-registration' ); ?></option>
    156156                    <option value="mtb" <?php selected($mod,'mtb'); ?>>MTB</option>
    157                     <option value="carrera" <?php selected($mod,'carrera'); ?>>Carrera</option>
     157                    <option value="carrera" <?php selected($mod,'carrera'); ?>><?php esc_html_e( 'Running', 'opcion5-sports-event-registration' ); ?></option>
    158158                    <option value="trail" <?php selected($mod,'trail'); ?>>Trail</option>
    159159                </select>
    160160
    161161                <select name="provincia">
    162                     <option value="">Provincia</option>
     162                    <option value=""><?php esc_html_e( 'Province', 'opcion5-sports-event-registration' ); ?></option>
    163163                    <?php foreach (WPCR_Data_Model::get_provincias() as $pv): ?>
    164164                        <option value="<?php echo esc_attr($pv); ?>" <?php selected($prov, $pv); ?>>
    … …  
    169169
    170170                <select name="mes">
    171                     <option value="">Mes</option>
     171                    <option value=""><?php esc_html_e( 'Month', 'opcion5-sports-event-registration' ); ?></option>
    172172                    <?php foreach ($meses as $m): ?>
    173173                        <option value="<?php echo esc_attr($m['valor']); ?>"
    … …  
    178178                </select>
    179179
    180                 <button type="submit">Filtrar</button>
     180                <button type="submit"><?php esc_html_e( 'Filter', 'opcion5-sports-event-registration' ); ?></button>
    181181            </form>
    182182
    … …  
    184184            <div class="wpcr-event-grid">
    185185                <?php if (empty($events)): ?>
    186                     <p><em>No hay eventos próximos que coincidan con los filtros.</em></p>
     186                    <p><em><?php esc_html_e( 'There are no events matching these filters.', 'opcion5-sports-event-registration' ); ?></em></p>
    187187                <?php endif; ?>
    188188
    … …  
    217217                    $start_time_raw = $e['start_time'] ?? '';
    218218                    $start_time = ($start_time_raw && $start_time_raw !== "00:00:00")
    219                         ? date_i18n("H:i", strtotime($start_time_raw)) . "h"
     219                        ? wpcr_format_time($start_time_raw, "-")
    220220                        : "-";
    221221
    … …  
    223223                    $reg_close_raw = $e['reg_close'] ?? '';
    224224
    225                     $reg_open = ($reg_open_raw && $reg_open_raw !== '0000-00-00 00:00:00')
    226                         ? date_i18n("d/m/Y H:i", strtotime($reg_open_raw)) . "h"
    227                         : "-";
    228 
    229                     $reg_close = ($reg_close_raw && $reg_close_raw !== '0000-00-00 00:00:00')
    230                         ? date_i18n("d/m/Y H:i", strtotime($reg_close_raw)) . "h"
    231                         : "-";
     225                    $reg_open  = wpcr_format_datetime($reg_open_raw, "-");
     226                    $reg_close = wpcr_format_datetime($reg_close_raw, "-");
    232227
    233228                    // ---------------------------------------------------------
    … …  
    247242                    if ($insc_open_ts && $now < $insc_open_ts) {
    248243
    249                         $btn = "<div class='wpcr-event-btn upcoming'>PRÓXIMAMENTE…</div>";
     244                        $btn = "<div class='wpcr-event-btn upcoming'>" . esc_html__( 'COMING SOON…', 'opcion5-sports-event-registration' ) . "</div>";
    250245
    251246                    } elseif ($insc_open_ts && $insc_close_ts && $now >= $insc_open_ts && $now <= $insc_close_ts) {
    252247
    253248                        if ($full) {
    254                             $btn = "<div class='wpcr-event-btn full'>SIN PLAZAS DISPONIBLES</div>";
     249                            $btn = "<div class='wpcr-event-btn full'>" . esc_html__( 'NO PLACES LEFT', 'opcion5-sports-event-registration' ) . "</div>";
    255250                        } else {
    256                             $btn = "<a href='" . esc_url($url_evento) . "' class='wpcr-event-btn active'>INSCRIPCIONES ABIERTAS</a>";
     251                            $btn = "<a href='" . esc_url($url_evento) . "' class='wpcr-event-btn active'>" . esc_html__( 'REGISTRATION OPEN', 'opcion5-sports-event-registration' ) . "</a>";
    257252                        }
    258253
    259254                    } else {
    260255
    261                         $btn = "<div class='wpcr-event-btn closed'>INSCRIPCIONES CERRADAS</div>";
     256                        $btn = "<div class='wpcr-event-btn closed'>" . esc_html__( 'REGISTRATION CLOSED', 'opcion5-sports-event-registration' ) . "</div>";
    262257                    }
    263258                    ?>
    … …  
    282277                            ?>
    283278                                <a href="<?php echo esc_url( $url_evento ); ?>" class="wpcr-live-badge" style="text-decoration:none;margin:2px 0 6px;">
    284                                     <span class="wpcr-live-dot"></span> En directo
     279                                    <span class="wpcr-live-dot"></span> <?php esc_html_e( 'Live', 'opcion5-sports-event-registration' ); ?>
    285280                                </a>
    286281                            <?php endif; ?>
    … …  
    316311                                $mod_label = ! empty( $mod_name ) ? $mod_name : ( $e['event_type'] ?? '' );
    317312                                ?>
    318                                 <strong>Modalidad:</strong> <?php echo esc_html( $mod_label ); ?><br>
    319                                 <strong>Fecha del evento:</strong> <?php echo esc_html( date_i18n('d/m/Y', strtotime($e['event_date'])) ); ?><br>
    320                                 <strong>Localidad:</strong> <?php echo esc_html($e['localidad']); ?><br>
    321                                 <strong>Provincia:</strong> <?php echo esc_html($e['provincia']); ?><br>
    322                                 <strong>Hora de salida:</strong> <?php echo esc_html($start_time); ?><br>
    323                                 <strong>Apertura de inscrip.:</strong> <?php echo esc_html($reg_open); ?><br>
    324                                 <strong>Cierre de inscrip.:</strong> <?php echo esc_html($reg_close); ?><br>
    325                                 <strong>Inscritos / Plazas:</strong> <?php echo (int) $total . " / " . esc_html( $plazas_label ); ?><br>
     313                                <strong><?php esc_html_e( 'Discipline:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html( $mod_label ); ?><br>
     314                                <strong><?php esc_html_e( 'Event date:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html( wpcr_format_date( $e['event_date'] ) ); ?><br>
     315                                <strong><?php esc_html_e( 'Town:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html($e['localidad']); ?><br>
     316                                <strong><?php esc_html_e( 'Province:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html($e['provincia']); ?><br>
     317                                <strong><?php esc_html_e( 'Start time:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html($start_time); ?><br>
     318                                <strong><?php esc_html_e( 'Registration opens:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html($reg_open); ?><br>
     319                                <strong><?php esc_html_e( 'Registration closes:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo esc_html($reg_close); ?><br>
     320                                <strong><?php esc_html_e( 'Entrants / Places:', 'opcion5-sports-event-registration' ); ?></strong> <?php echo (int) $total . " / " . esc_html( $plazas_label ); ?><br>
    326321                            </p>
    327322                        </div>
    … …  
    348343
    349344        if (empty($public_id)) {
    350             return '<p>No se ha especificado un evento válido.</p>';
     345            return '<p>' . esc_html__( 'No valid event was specified.', 'opcion5-sports-event-registration' ) . '</p>';
    351346        }
    352347
    … …  
    361356
    362357        if (!$event) {
    363             return '<p>Evento no encontrado.</p>';
     358            return '<p>' . esc_html__( 'Event not found.', 'opcion5-sports-event-registration' ) . '</p>';
    364359        }
    365360
    … …  
    385380
    386381        if ( empty( $participants ) ) {
    387             return '<p>No hay participantes inscritos aún.</p>';
     382            return '<p>' . esc_html__( 'There are no entrants yet.', 'opcion5-sports-event-registration' ) . '</p>';
    388383        }
    389384
    … …  
    439434        foreach($participants as $p){
    440435            $cat = trim($p['category_name'] ?? '');
    441             if($cat == '') $cat = "Sin categoría";
     436            if($cat == '') $cat = __( 'No category', 'opcion5-sports-event-registration' );
    442437
    443438            if(!isset($counts[$cat])) $counts[$cat] = 0;
    … …  
    523518
    524519        <div class="wpcr-summary-item wpcr-summary-total" data-cat="">
    525             Total: <?php echo (int) $total; ?>
     520            <?php esc_html_e( 'Total:', 'opcion5-sports-event-registration' ); ?> <?php echo (int) $total; ?>
    526521        </div>
    527522
    … …  
    537532    <!-- 🔍 COLUMNA DERECHA — BUSCADOR -->
    538533    <div class="wpcr-search-box">
    539         <input type="text" id="wpcr-search" placeholder="Buscar nombre o club…">
     534        <input type="text" id="wpcr-search" placeholder="<?php echo esc_attr__( 'Search name or club…', 'opcion5-sports-event-registration' ); ?>">
    540535    </div>
    541536
    … …  
    545540        <thead>
    546541            <tr class="wpcr-row">
    547                 <th data-sort="name">Nombre</th>
    548                 <th data-sort="surname">Apellidos</th>
    549                 <th data-sort="category">Categoría</th>
    550                 <th data-sort="club">Club</th>
     542                <th data-sort="name"><?php esc_html_e( 'Name', 'opcion5-sports-event-registration' ); ?></th>
     543                <th data-sort="surname"><?php esc_html_e( 'Surname', 'opcion5-sports-event-registration' ); ?></th>
     544                <th data-sort="category"><?php esc_html_e( 'Category', 'opcion5-sports-event-registration' ); ?></th>
     545                <th data-sort="club"><?php esc_html_e( 'Club', 'opcion5-sports-event-registration' ); ?></th>
    551546<?php if ($show_localidad): ?>
    552     <th data-sort="localidad">Localidad</th>
     547    <th data-sort="localidad"><?php esc_html_e( 'Town', 'opcion5-sports-event-registration' ); ?></th>
    553548<?php endif; ?>
    554549
    555 <th>Estado</th>
    556 <th>Acción</th>
     550<th><?php esc_html_e( 'Status', 'opcion5-sports-event-registration' ); ?></th>
     551<th><?php esc_html_e( 'Action', 'opcion5-sports-event-registration' ); ?></th>
    557552
    558553            </tr>
    … …  
    618613        if ( in_array( $payment_type, ['invited','manual','woocommerce'], true )
    619614             || $pay_status === 'paid' ) {
    620             echo "<span style='color:#007700;font-weight:bold;'>✔ Inscrito</span>";
     615            echo "<span style='color:#007700;font-weight:bold;'>✔ " . esc_html__( 'Registered', 'opcion5-sports-event-registration' ) . "</span>";
    621616        }
    622617        /* 🕐 Transferencia bancaria pendiente de confirmar → Preinscrito */
    623618        elseif ( $pay_method === 'bank_transfer' && $pay_status === 'pending' ) {
    624             echo "<span style='color:#2271b1;font-weight:bold;'>🕐 Preinscrito</span>";
     619            echo "<span style='color:#2271b1;font-weight:bold;'>🕐 " . esc_html__( 'Pre-registered', 'opcion5-sports-event-registration' ) . "</span>";
    625620        }
    626621        /* 🔒 Lista de espera */
    627622        elseif ( $waitlist == 1 ) {
    628             echo "<span style='color:#555;'>🔒 Reserva</span>";
     623            echo "<span style='color:#555;'>🔒 " . esc_html__( 'Reserve', 'opcion5-sports-event-registration' ) . "</span>";
    629624        }
    630625        /* ⏳ Pendiente de pago */
    631626        else {
    632             echo "<span style='color:#c40000;font-weight:bold;'>⏳ Pendiente de pago</span>";
     627            echo "<span style='color:#c40000;font-weight:bold;'>⏳ " . esc_html__( 'Payment pending', 'opcion5-sports-event-registration' ) . "</span>";
    633628        }
    634629    ?>
    … …  
    640635        if ( in_array( $payment_type, ['invited','manual','woocommerce'], true )
    641636             || $pay_status === 'paid' ) {
    642             echo "<span style='color:#007700;font-weight:bold;'>🟢 Plaza confirmada</span>";
     637            echo "<span style='color:#007700;font-weight:bold;'>🟢 " . esc_html__( 'Place confirmed', 'opcion5-sports-event-registration' ) . "</span>";
    643638        }
    644639        elseif ( $pay_method === 'bank_transfer' && $pay_status === 'pending' ) {
    645640            // Transferencia pendiente: no hay acción pública, la confirma el administrador
    646             echo "<span style='color:#777;font-style:italic;'>Pendiente de confirmación</span>";
     641            echo "<span style='color:#777;font-style:italic;'>" . esc_html__( 'Awaiting confirmation', 'opcion5-sports-event-registration' ) . "</span>";
    647642        }
    648643        elseif ( $waitlist == 1 ) {
    649             echo "<span style='color:#555;font-style:italic;'>En lista de espera</span>";
     644            echo "<span style='color:#555;font-style:italic;'>" . esc_html__( 'On the waiting list', 'opcion5-sports-event-registration' ) . "</span>";
    650645        }
    651646        else {
    652647            if ( $reg_is_closed ) {
    653                 echo "<span style='color:#999;font-style:italic;'>Plazo cerrado</span>";
     648                echo "<span style='color:#999;font-style:italic;'>" . esc_html__( 'Registration closed', 'opcion5-sports-event-registration' ) . "</span>";
    654649            } else {
    655650                $pay_url = wpcr_endpoint_url( 'pago', [ 'token' => $pay_token ] );
    656                 echo "<a href='" . esc_url( $pay_url ) . "' style='color:#007bff;font-weight:bold;text-decoration:underline;'>💳 Pagar ahora</a>";
     651                echo "<a href='" . esc_url( $pay_url ) . "' style='color:#007bff;font-weight:bold;text-decoration:underline;'>💳 " . esc_html__( 'Pay now', 'opcion5-sports-event-registration' ) . "</a>";
    657652            }
    658653        }
  • opcion5-sports-event-registration/trunk/public/class-wpcr-public.php

    r3642215 r3645531  
    7272            true
    7373        );
     74        /*
     75         * Los textos del JS viajan aquí: un fichero .js no lo alcanza el .mo, así
     76         * que se traducen en PHP y se pasan ya traducidos al navegador.
     77         */
    7478        wp_localize_script('wpcr-frontend','wpcr_ajax',[
    7579            'url'=>admin_url('admin-ajax.php'),
    76             'nonce'=>wp_create_nonce('wpcr_dni_lookup')
     80            'nonce'=>wp_create_nonce('wpcr_dni_lookup'),
     81            'i18n'=>[
     82                'invalidDni'   => __( 'Invalid ID number. Check the digits and the letter.', 'opcion5-sports-event-registration' ),
     83                'checkError'   => __( 'Something went wrong checking the ID number. Please try again.', 'opcion5-sports-event-registration' ),
     84                'alreadyIn'    => __( 'This ID number is already registered for this event.', 'opcion5-sports-event-registration' ),
     85                'recognised'   => __( 'We recognised you from previous registrations', 'opcion5-sports-event-registration' ),
     86                'labelName'    => __( 'Name:', 'opcion5-sports-event-registration' ),
     87                'labelEmail'   => __( 'Email:', 'opcion5-sports-event-registration' ),
     88                'labelPhone'   => __( 'Phone:', 'opcion5-sports-event-registration' ),
     89                'labelTown'    => __( 'Town:', 'opcion5-sports-event-registration' ),
     90                'useMyData'    => __( 'That is me, use my details', 'opcion5-sports-event-registration' ),
     91                'dataChanged'  => __( 'They have changed, update them', 'opcion5-sports-event-registration' ),
     92                'willUseSaved' => __( 'We will use your saved details. Fill in what is missing (size, etc.) and press Send.', 'opcion5-sports-event-registration' ),
     93            ],
    7794        ]);
    7895    }
    … …  
    94111        $event=$this->get_event_data($public_id);
    95112
    96         if(!$event) return "<div class='wpcr-message wpcr-error'>No hay eventos disponibles.</div>";
     113        if(!$event) return "<div class='wpcr-message wpcr-error'>" . esc_html__( 'No events available.', 'opcion5-sports-event-registration' ) . "</div>";
    97114
    98115        $event_id=intval($event['id']);
    … …  
    119136        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
    120137        if(isset($_GET['wpcr_message']) && sanitize_text_field(wp_unslash($_GET['wpcr_message']))=='success'){
    121             return "<div class='wpcr-message wpcr-success'>Registro completado en <b>" . esc_html($event_name) . "</b></div>";
     138            return "<div class='wpcr-message wpcr-success'>" . sprintf(
     139                /* translators: %s: event name */
     140                esc_html__( 'Registration completed for %s', 'opcion5-sports-event-registration' ),
     141                '<b>' . esc_html($event_name) . '</b>'
     142            ) . "</div>";
    122143        }
    123144
    … …  
    131152
    132153    <h2 class="wpcr-card-title"><?php echo esc_html($event_name); ?></h2>
    133     <p class="wpcr-card-subtitle">Completa tu identificación para comenzar la inscripción.</p>
    134 
    135     <h3 class="wpcr-step-title">Paso 1: Identificación</h3>
     154    <p class="wpcr-card-subtitle"><?php esc_html_e( 'Enter your ID to start the registration.', 'opcion5-sports-event-registration' ); ?></p>
     155
     156    <h3 class="wpcr-step-title"><?php esc_html_e( 'Step 1: Identification', 'opcion5-sports-event-registration' ); ?></h3>
    136157
    137158
    … …  
    144165<div id="wpcr-step-1" class="wpcr-box">
    145166
    146 <p><label>Nacionalidad *</label>
     167<p><label><?php esc_html_e( 'Nationality', 'opcion5-sports-event-registration' ); ?> *</label>
    147168<select id="wpcr_nationality" name="nationality" required>
    148 <option value="ES">España</option>
    149 <option value="OTRO">Otro</option>
     169<option value="ES"><?php esc_html_e( 'Spain', 'opcion5-sports-event-registration' ); ?></option>
     170<option value="OTRO"><?php esc_html_e( 'Other', 'opcion5-sports-event-registration' ); ?></option>
    150171</select></p>
    151172
    152 <p><label>DNI *</label>
     173<p><label><?php esc_html_e( 'ID number', 'opcion5-sports-event-registration' ); ?> *</label>
    153174<input type="text" id="dni" name="dni" required></p>
    154175
    155 <button type="button" id="wpcr-check-dni-btn">Comprobar</button>
     176<button type="button" id="wpcr-check-dni-btn"><?php esc_html_e( 'Check', 'opcion5-sports-event-registration' ); ?></button>
    156177<div id="wpcr-dni-feedback"></div>
    157178</div>
    158179
    159180<div id="wpcr-step-2" class="wpcr-box" style="display:none;margin-top:20px;padding:20px;border-radius:8px;border:1px solid #ccc;">
    160 <h3>Completa tus datos</h3>
    161 
    162 <p><label>Nombre *</label><input type="text" name="first_name" required></p>
    163 <p><label>1er Apellido *</label><input type="text" name="middle_name" required></p>
    164 <p><label>2º Apellido</label><input type="text" name="last_name"></p>
    165 <p><label>Fecha nacimiento *</label><input type="date" name="dob" required></p>
    166 
    167 <p><label>Género *</label><br>
    168 <label><input type="radio" name="gender" value="M" required> Masculino</label>
    169 <label><input type="radio" name="gender" value="F" required> Femenino</label>
     181<h3><?php esc_html_e( 'Complete your details', 'opcion5-sports-event-registration' ); ?></h3>
     182
     183<p><label><?php esc_html_e( 'First name', 'opcion5-sports-event-registration' ); ?> *</label><input type="text" name="first_name" required></p>
     184<p><label><?php esc_html_e( 'First surname', 'opcion5-sports-event-registration' ); ?> *</label><input type="text" name="middle_name" required></p>
     185<p><label><?php esc_html_e( 'Second surname', 'opcion5-sports-event-registration' ); ?></label><input type="text" name="last_name"></p>
     186<p><label><?php esc_html_e( 'Date of birth', 'opcion5-sports-event-registration' ); ?> *</label><input type="date" name="dob" required></p>
     187
     188<p><label><?php esc_html_e( 'Gender', 'opcion5-sports-event-registration' ); ?> *</label><br>
     189<label><input type="radio" name="gender" value="M" required> <?php esc_html_e( 'Male', 'opcion5-sports-event-registration' ); ?></label>
     190<label><input type="radio" name="gender" value="F" required> <?php esc_html_e( 'Female', 'opcion5-sports-event-registration' ); ?></label>
    170191</p>
    171192
    172 <p><label>Localidad *</label><input type="text" name="localidad" required></p>
    173 <p><label>Provincia *</label><input type="text" name="provincia" required></p>
    174 
    175 <p><label>Teléfono *</label><input type="tel" name="phone" required></p>
    176 <p><label>Email *</label><input type="email" name="email" required></p>
    177 <p><label>Club / Equipo</label><input type="text" name="club_team" value="independiente"></p>
     193<p><label><?php esc_html_e( 'Town', 'opcion5-sports-event-registration' ); ?> *</label><input type="text" name="localidad" required></p>
     194<p><label><?php esc_html_e( 'Province', 'opcion5-sports-event-registration' ); ?> *</label><input type="text" name="provincia" required></p>
     195
     196<p><label><?php esc_html_e( 'Phone', 'opcion5-sports-event-registration' ); ?> *</label><input type="tel" name="phone" required></p>
     197<p><label><?php esc_html_e( 'Email', 'opcion5-sports-event-registration' ); ?> *</label><input type="email" name="email" required></p>
     198<p><label><?php esc_html_e( 'Club / Team', 'opcion5-sports-event-registration' ); ?></label><input type="text" name="club_team" value="<?php echo esc_attr__( 'independent', 'opcion5-sports-event-registration' ); ?>"></p>
    178199<?php
    179200// =====================================
    … …  
    191212     && !empty($event_data['tallas_options']) ):
    192213
    193     $tallas_label    = $event_data['tallas_label'] ?: "Selecciona tu talla";
     214    $tallas_label    = $event_data['tallas_label'] ?: __( 'Select your size', 'opcion5-sports-event-registration' );
    194215    $tallas_opciones = array_filter(array_map('trim', explode(",", $event_data['tallas_options'])));
    195216?>
    … …  
    197218        <label><?php echo esc_html($tallas_label); ?> *</label><br>
    198219        <select name="talla" required>
    199             <option value="" disabled selected>Seleccione una talla</option>
     220            <option value="" disabled selected><?php esc_html_e( 'Select a size', 'opcion5-sports-event-registration' ); ?></option>
    200221            <?php foreach($tallas_opciones as $t): ?>
    201222                <option value="<?php echo esc_attr($t); ?>"><?php echo esc_html($t); ?></option>
    … …  
    211232$pairs_enabled_evt = ! empty( $event['pairs_enabled'] );
    212233$pair_cats_evt     = array_filter( explode( ',', $event['pair_categories'] ?? '' ) );
    213 $pair_cat_labels   = [ 'male' => 'Pareja masculina', 'mixed' => 'Pareja mixta', 'female' => 'Pareja femenina' ];
     234$pair_cat_labels   = [
     235    'male'   => __( "Men's pair", 'opcion5-sports-event-registration' ),
     236    'mixed'  => __( 'Mixed pair', 'opcion5-sports-event-registration' ),
     237    'female' => __( "Women's pair", 'opcion5-sports-event-registration' ),
     238];
    214239
    215240// Código de pareja precargado desde el enlace compartido (?pair=XXXX)
    … …  
    229254?>
    230255<div class="wpcr-pairs-box" style="margin:16px 0;padding:14px;background:#f0f4ff;border:1px solid #c0d0f0;border-radius:8px;">
    231     <p style="margin:0 0 8px;font-weight:600;">👥 Modalidad de inscripción</p>
     256    <p style="margin:0 0 8px;font-weight:600;">👥 <?php esc_html_e( 'Registration type', 'opcion5-sports-event-registration' ); ?></p>
    232257
    233258    <?php if ( $prefill_pair_row ): ?>
    234259        <p style="margin:0 0 8px;padding:8px 12px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:6px;font-size:14px;">
    235             ✔ Te estás inscribiendo como pareja de
    236             <strong><?php echo esc_html( trim( $prefill_pair_row['first_name'] . ' ' . $prefill_pair_row['middle_name'] ) ); ?></strong>
    237             (<?php echo esc_html( $pair_cat_labels[ $prefill_pair_row['pair_category'] ] ?? 'Pareja' ); ?>).
     260            ✔ <?php printf(
     261                /* translators: 1: partner name, 2: pair category, for example "Mixed pair" */
     262                esc_html__( 'You are registering as the partner of %1$s (%2$s).', 'opcion5-sports-event-registration' ),
     263                '<strong>' . esc_html( trim( $prefill_pair_row['first_name'] . ' ' . $prefill_pair_row['middle_name'] ) ) . '</strong>',
     264                esc_html( $pair_cat_labels[ $prefill_pair_row['pair_category'] ] ?? __( 'Pair', 'opcion5-sports-event-registration' ) )
     265            ); ?>
    238266        </p>
    239267        <input type="hidden" name="wpcr_pair_mode" value="pair">
    … …  
    242270
    243271        <label style="margin-right:16px;">
    244             <input type="radio" name="wpcr_pair_mode" value="individual" checked> Individual
     272            <input type="radio" name="wpcr_pair_mode" value="individual" checked> <?php esc_html_e( 'Individual', 'opcion5-sports-event-registration' ); ?>
    245273        </label>
    246274        <label>
    247             <input type="radio" name="wpcr_pair_mode" value="pair"> Por parejas
     275            <input type="radio" name="wpcr_pair_mode" value="pair"> <?php esc_html_e( 'As a pair', 'opcion5-sports-event-registration' ); ?>
    248276        </label>
    249277
    250278        <div id="wpcr-pair-options" style="display:none;margin-top:12px;">
    251             <p style="margin:0 0 4px;"><label>Modalidad de pareja *</label><br>
     279            <p style="margin:0 0 4px;"><label><?php esc_html_e( 'Pair category', 'opcion5-sports-event-registration' ); ?> *</label><br>
    252280                <select name="wpcr_pair_category">
    253281                    <?php foreach ( $pair_cats_evt as $pc ): if ( ! isset( $pair_cat_labels[ $pc ] ) ) continue; ?>
    … …  
    256284                </select>
    257285            </p>
    258             <p style="margin:10px 0 4px;"><label>¿Tu pareja ya se ha inscrito? Introduce su código de pareja (opcional)</label><br>
    259                 <input type="text" name="wpcr_pair_join_code" placeholder="Ej: PAR-XXXXXX" style="text-transform:uppercase;">
     286            <p style="margin:10px 0 4px;"><label><?php esc_html_e( 'Has your partner already registered? Enter their pair code (optional)', 'opcion5-sports-event-registration' ); ?></label><br>
     287                <input type="text" name="wpcr_pair_join_code" placeholder="<?php echo esc_attr__( 'e.g. PAR-XXXXXX', 'opcion5-sports-event-registration' ); ?>" style="text-transform:uppercase;">
    260288            </p>
    261289            <p style="margin:8px 0 0;font-size:13px;color:#555;">
    262                 Si eres el primero de la pareja, deja el código vacío: al terminar tu inscripción recibirás
    263                 un código y un enlace para que tu pareja complete la suya. Cada uno paga su propia inscripción.
     290                <?php esc_html_e( 'If you are the first of the pair, leave the code empty: once you finish you will get a code and a link so your partner can complete theirs. Each of you pays for your own registration.', 'opcion5-sports-event-registration' ); ?>
    264291            </p>
    265292        </div>
    … …  
    363390echo '<div class="wpcr-price-box">';
    364391
    365 echo "<p><strong>Precio base:</strong> " . number_format($base_price, 2, ',', '.') . " €</p>";
     392echo "<p><strong>" . esc_html__( 'Base price:', 'opcion5-sports-event-registration' ) . "</strong> " . number_format($base_price, 2, ',', '.') . " €</p>";
    366393
    367394/* =====================================================
    … …  
    384411if (!empty($price_extras)):
    385412
    386     echo "<p><strong>Extras opcionales:</strong></p>";
     413    echo "<p><strong>" . esc_html__( 'Optional extras:', 'opcion5-sports-event-registration' ) . "</strong></p>";
    387414    echo '<div class="wpcr-extras-box">';
    388415
    … …  
    407434                esc_html( $label ),
    408435                esc_attr( $name ),
    409                 esc_html__( 'Ninguno', 'opcion5-sports-event-registration' ),
     436                esc_html__( 'None', 'opcion5-sports-event-registration' ),
    410437                esc_attr( $p ),
    411                 esc_html( 'Añadir (+' . $price_txt . ')' )
     438                esc_html( sprintf(
     439                    /* translators: %s: extra price, already formatted with the currency */
     440                    __( 'Add (+%s)', 'opcion5-sports-event-registration' ),
     441                    $price_txt
     442                ) )
    412443            );
    413444        else:
    … …  
    448479
    449480<div id="wpcr-total-box" style="margin-top:20px;font-size:18px;font-weight:bold;">
    450     Total inscripción: <span id="wpcr-total-amount">
     481    <?php esc_html_e( 'Registration total:', 'opcion5-sports-event-registration' ); ?> <span id="wpcr-total-amount">
    451482        <?php echo number_format($base_price, 2, ',', '.'); ?> €
    452483    </span>
    … …  
    487518
    488519    <span>
    489         He leído y acepto la
    490         <a href="#" id="wpcr-privacy-link" style="color:#0073aa; text-decoration:underline;">
    491             política de privacidad
    492         </a>.
     520        <?php printf(
     521            /* translators: %s: link with the text "privacy policy" */
     522            esc_html__( 'I have read and accept the %s.', 'opcion5-sports-event-registration' ),
     523            '<a href="#" id="wpcr-privacy-link" style="color:#0073aa; text-decoration:underline;">'
     524                . esc_html__( 'privacy policy', 'opcion5-sports-event-registration' )
     525                . '</a>'
     526        ); ?>
    493527    </span>
    494528</label>
    495     <p id="wpcr-privacy-error" 
     529    <p id="wpcr-privacy-error"
    496530       style="color:#d63638; font-size:13px; margin-top:6px; display:none;">
    497        Debes aceptar la política de privacidad para continuar.
     531       <?php esc_html_e( 'You must accept the privacy policy to continue.', 'opcion5-sports-event-registration' ); ?>
    498532    </p>
    499533
    … …  
    519553        </span>
    520554
    521         <h2 style="margin-top:0;">Política de privacidad</h2>
    522         <div id="wpcr-modal-content" 
     555        <h2 style="margin-top:0;"><?php esc_html_e( 'Privacy policy', 'opcion5-sports-event-registration' ); ?></h2>
     556        <div id="wpcr-modal-content"
    523557             style="font-size:15px; line-height:1.5; color:#333;">
    524             Cargando…
     558            <?php esc_html_e( 'Loading…', 'opcion5-sports-event-registration' ); ?>
    525559        </div>
    526560    </div>
    … …  
    598632<p style="text-align:center;margin-top:2px;">
    599633    <button type="submit" name="wpcr_register_submit" class="wpcr-submit-btn">
    600         Finalizar inscripción
     634        <?php esc_html_e( 'Complete registration', 'opcion5-sports-event-registration' ); ?>
    601635    </button>
    602636</p>
    … …  
    641675
    642676        if(!$public_id){
    643             return "<p style='color:red;'>Debes indicar un public_id → [wpcr_list event=\"PUBLIC_ID\"]</p>";
     677            return "<p style='color:red;'>" . esc_html__( 'You must provide a public_id → [wpcr_list event="PUBLIC_ID"]', 'opcion5-sports-event-registration' ) . "</p>";
    644678        }
    645679
    … …  
    663697    // 3️⃣ Validación
    664698    if(empty($public_id)){
    665         return "<p style='color:red;'>⚠ No se ha especificado ningún public_id.</p>";
     699        return "<p style='color:red;'>⚠ " . esc_html__( 'No public_id was specified.', 'opcion5-sports-event-registration' ) . "</p>";
    666700    }
    667701
    … …  
    673707
    674708    if(!$event){
    675         return "<p>⚠ Evento no encontrado.</p>";
     709        return "<p>⚠ " . esc_html__( 'Event not found.', 'opcion5-sports-event-registration' ) . "</p>";
    676710    }
    677711
    … …  
    695729    // Datos básicos del evento
    696730    $event_name  = esc_html($event['name']);
    697     $description = !empty($event['description']) ? wp_kses_post(wpautop($event['description'])) : "<i>Descripción no disponible.</i>";
     731    $description = !empty($event['description']) ? wp_kses_post(wpautop($event['description'])) : "<i>" . esc_html__( 'Description not available.', 'opcion5-sports-event-registration' ) . "</i>";
    698732
    699733    // Obtener el nombre de la modalidad tal como se introdujo en la administración
    … …  
    710744    $lugar_salida = $event['start_location']   ?? "";
    711745    $fecha_evento_raw = $event['event_date'] ?? "";
    712     $fecha_evento = $fecha_evento_raw && $fecha_evento_raw !== "0000-00-00"
    713         ? date_i18n("d/m/Y", strtotime($fecha_evento_raw))
    714         : "";
     746    $fecha_evento = wpcr_format_date( $fecha_evento_raw );
    715747
    716748    $start_time_raw = $event['start_time'] ?? "";
    717     $start_time = $start_time_raw ? date_i18n("H:i", strtotime($start_time_raw))."h" : "-";
     749    $start_time = wpcr_format_time( $start_time_raw, "-" );
    718750
    719751    $reg_open_raw     = $event['reg_open']   ?? "";
    720752    $reg_close_raw    = $event['reg_close']  ?? "";
    721753
    722     $reg_open     = $reg_open_raw    ? date_i18n("d/m/Y H:i", strtotime($reg_open_raw))."h"     : "-";
    723     $reg_close    = $reg_close_raw   ? date_i18n("d/m/Y H:i", strtotime($reg_close_raw))."h"    : "-";
     754    $reg_open     = wpcr_format_datetime( $reg_open_raw, "-" );
     755    $reg_close    = wpcr_format_datetime( $reg_close_raw, "-" );
    724756
    725757    // Estado inscripción (necesario antes de renderizar el botón)
    … …  
    765797                $price = isset($r['price']) ? floatval($r['price']) : null;
    766798                if (!$price) continue;
    767                 $from_fmt = $from ? date_i18n('d/m/Y', strtotime($from)) : '';
    768                 $to_fmt   = $to   ? date_i18n('d/m/Y', strtotime($to))   : '';
    769                 if ($from_fmt && $to_fmt)      $label = "Del {$from_fmt} al {$to_fmt}";
    770                 elseif ($from_fmt)             $label = "Desde {$from_fmt}";
    771                 elseif ($to_fmt)               $label = "Hasta {$to_fmt}";
    772                 else                           $label = '';
     799                $from_fmt = wpcr_format_date( $from );
     800                $to_fmt   = wpcr_format_date( $to );
     801                if ($from_fmt && $to_fmt) {
     802                    /* translators: 1: start date, 2: end date */
     803                    $label = sprintf( __( 'From %1$s to %2$s', 'opcion5-sports-event-registration' ), $from_fmt, $to_fmt );
     804                } elseif ($from_fmt) {
     805                    /* translators: %s: start date */
     806                    $label = sprintf( __( 'From %s', 'opcion5-sports-event-registration' ), $from_fmt );
     807                } elseif ($to_fmt) {
     808                    /* translators: %s: end date */
     809                    $label = sprintf( __( 'Until %s', 'opcion5-sports-event-registration' ), $to_fmt );
     810                } else {
     811                    $label = '';
     812                }
    773813                $rows[] = ['label' => $label, 'price' => number_format($price, 2, ',', '.') . ' €'];
    774814            }
    … …  
    816856    <!--  PESTAÑAS - TABS -->
    817857    <div class="wpcr-tabs">
    818         <button class="wpcr-tab-btn active" data-tab="info">ℹ DETALLES</button>
    819                 <button class="wpcr-tab-btn" data-tab="tech">📁 FICHA TÉCNICA</button>
    820         <button class="wpcr-tab-btn" data-tab="form">📝 INSCRIPCIÓN</button>
    821         <button class="wpcr-tab-btn" data-tab="list">📋 INSCRITOS</button>
     858        <button class="wpcr-tab-btn active" data-tab="info">ℹ <?php esc_html_e( 'DETAILS', 'opcion5-sports-event-registration' ); ?></button>
     859                <button class="wpcr-tab-btn" data-tab="tech">📁 <?php esc_html_e( 'RACE INFO', 'opcion5-sports-event-registration' ); ?></button>
     860        <button class="wpcr-tab-btn" data-tab="form">📝 <?php esc_html_e( 'REGISTRATION', 'opcion5-sports-event-registration' ); ?></button>
     861        <button class="wpcr-tab-btn" data-tab="list">📋 <?php esc_html_e( 'ENTRANTS', 'opcion5-sports-event-registration' ); ?></button>
    822862        <?php if ( ( function_exists('wpcr_is_addon_active') && wpcr_is_addon_active('wpcr-results') ) || $wpcr_is_stage_event ) : ?>
    823         <button class="wpcr-tab-btn" data-tab="results">🏁 <?php echo $wpcr_is_stage_event ? 'CLASIFICACIONES' : 'RESULTADOS'; ?><?php if ( $wpcr_live ) : ?> <span class="wpcr-live-dot" title="En directo"></span><?php endif; ?></button>
     863        <button class="wpcr-tab-btn" data-tab="results">🏁 <?php echo $wpcr_is_stage_event ? esc_html__( 'STANDINGS', 'opcion5-sports-event-registration' ) : esc_html__( 'RESULTS', 'opcion5-sports-event-registration' ); ?><?php if ( $wpcr_live ) : ?> <span class="wpcr-live-dot" title="<?php echo esc_attr__( 'Live', 'opcion5-sports-event-registration' ); ?>"></span><?php endif; ?></button>
    824864        <?php endif; ?>
    825865        <?php if ( ( function_exists('wpcr_is_addon_active') && wpcr_is_addon_active('wpcr-participant-area') ) ) : ?>
    826         <button class="wpcr-tab-btn" data-tab="area-privada">🔐 ACCESO PARTICIPANTES</button>
     866        <button class="wpcr-tab-btn" data-tab="area-privada">🔐 <?php esc_html_e( 'PARTICIPANT ACCESS', 'opcion5-sports-event-registration' ); ?></button>
    827867        <?php endif; ?>
    828868    </div>
    … …  
    835875                    <h2 class="wpcr-event-title"><?php echo esc_html($event_name); ?>
    836876                        <?php if ( $wpcr_live ) : ?>
    837                             <a href="#" class="wpcr-live-badge wpcr-live-goto" style="text-decoration:none;margin-left:8px;"><span class="wpcr-live-dot"></span> En directo</a>
     877                            <a href="#" class="wpcr-live-badge wpcr-live-goto" style="text-decoration:none;margin-left:8px;"><span class="wpcr-live-dot"></span> <?php esc_html_e( 'Live', 'opcion5-sports-event-registration' ); ?></a>
    838878                        <?php endif; ?>
    839879                    </h2>
    … …  
    849889                    ?>
    850890
    851                     <p><span class="wpcr-meta">Modalidad:</span> <?php echo esc_html($modalidad); ?></p>
     891                    <p><span class="wpcr-meta"><?php esc_html_e( 'Discipline:', 'opcion5-sports-event-registration' ); ?></span> <?php echo esc_html($modalidad); ?></p>
    852892
    853893                    <?php
    854894                    if (!empty($localidad)) {
    855                         echo "<p><span class='wpcr-meta'>Ubicación:</span> " . esc_html($localidad);
     895                        echo "<p><span class='wpcr-meta'>" . esc_html__( 'Location:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($localidad);
    856896                        if (!empty($provincia)) echo " (" . esc_html($provincia) . ")";
    857897                        echo "</p>";
    858898                    }
    859                     if (!empty($lugar_salida)) echo "<p><span class='wpcr-meta'>Lugar de salida:</span> " . esc_html($lugar_salida) . "</p>";
    860                     if (!empty($fecha_evento)) echo "<p><span class='wpcr-meta'>Fecha de celebración:</span> " . esc_html($fecha_evento) . "</p>";
    861                     if (!empty($start_time_raw) && $start_time_raw !== "00:00:00") echo "<p><span class='wpcr-meta'>Hora de salida:</span> " . esc_html($start_time) . "</p>";
    862                     if (!empty($reg_open_raw) && $reg_open_raw !== "0000-00-00 00:00:00") echo "<p><span class='wpcr-meta'>Apertura de inscrip.:</span> " . esc_html($reg_open) . "</p>";
    863                     if (!empty($reg_close_raw) && $reg_close_raw !== "0000-00-00 00:00:00") echo "<p><span class='wpcr-meta'>Cierre de inscrip.:</span> " . esc_html($reg_close) . "</p>";
     899                    if (!empty($lugar_salida)) echo "<p><span class='wpcr-meta'>" . esc_html__( 'Start location:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($lugar_salida) . "</p>";
     900                    if (!empty($fecha_evento)) echo "<p><span class='wpcr-meta'>" . esc_html__( 'Event date:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($fecha_evento) . "</p>";
     901                    if (!empty($start_time_raw) && $start_time_raw !== "00:00:00") echo "<p><span class='wpcr-meta'>" . esc_html__( 'Start time:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($start_time) . "</p>";
     902                    if (!empty($reg_open_raw) && $reg_open_raw !== "0000-00-00 00:00:00") echo "<p><span class='wpcr-meta'>" . esc_html__( 'Registration opens:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($reg_open) . "</p>";
     903                    if (!empty($reg_close_raw) && $reg_close_raw !== "0000-00-00 00:00:00") echo "<p><span class='wpcr-meta'>" . esc_html__( 'Registration closes:', 'opcion5-sports-event-registration' ) . "</span> " . esc_html($reg_close) . "</p>";
    864904                    ?>
    865905
    … …  
    900940                    <div class="wpcr-price-section">
    901941
    902                         <p><span class="wpcr-meta">Precio base:</span>
     942                        <p><span class="wpcr-meta"><?php esc_html_e( 'Base price:', 'opcion5-sports-event-registration' ); ?></span>
    903943                            <strong><?php echo number_format($base_price, 2, ',', '.'); ?> €</strong>
    904944                        </p>
    … …  
    909949                    if ( $show_results_btn && ( ( function_exists('wpcr_is_addon_active') && wpcr_is_addon_active('wpcr-results') ) || $wpcr_is_stage_event ) ) : ?>
    910950                    <a href="javascript:void(0);" class="wpcr-btn-insc" data-goto-tab="results"
    911                        style="background:#0059D1;">🏁 CLASIFICACIONES</a>
     951                       style="background:#0059D1;">🏁 <?php esc_html_e( 'STANDINGS', 'opcion5-sports-event-registration' ); ?></a>
    912952                    <?php elseif ( ! $show_results_btn ) : ?>
    913                     <a href="javascript:void(0);" class="wpcr-btn-insc" data-goto-tab="form">INSCRIBIRME</a>
     953                    <a href="javascript:void(0);" class="wpcr-btn-insc" data-goto-tab="form"><?php esc_html_e( 'REGISTER', 'opcion5-sports-event-registration' ); ?></a>
    914954                    <?php endif; ?>
    915955
    … …  
    930970                        <div id="wpcr-countdown-title"
    931971                             style="font-size:22px; font-weight:700; color:#002e77; margin-bottom:8px;">
    932                             Quedan...
     972                            <?php esc_html_e( 'Time left...', 'opcion5-sports-event-registration' ); ?>
    933973                        </div>
    934974                        <div id="wpcr-countdown"
    935975                             style="font-size:24px; font-weight:bold; color:#003d99;">
    936                             Cargando cuenta atrás…
     976                            <?php esc_html_e( 'Loading countdown…', 'opcion5-sports-event-registration' ); ?>
    937977                        </div>
    938978                    </div>
    939979
    940                     <?php wpcr_inline_js( 'var wpcrCountdownStart = ' . wp_json_encode( (string) $start_datetime_js ) . ";\n" . <<<'JS'
     980                    <?php wpcr_inline_js( 'var wpcrCountdownStart = ' . wp_json_encode( (string) $start_datetime_js ) . ";\n"
     981                    // Los textos viajan en una variable serializada: dentro del nowdoc
     982                    // no se pueden traducir porque PHP no procesa nada ahí dentro.
     983                    . 'var wpcrCountdownI18n = ' . wp_json_encode( [
     984                        'noDate'   => __( 'Date not available', 'opcion5-sports-event-registration' ),
     985                        'finished' => __( 'The event has finished.', 'opcion5-sports-event-registration' ),
     986                        'started'  => __( 'The event has started!', 'opcion5-sports-event-registration' ),
     987                        'timeLeft' => __( 'Time left...', 'opcion5-sports-event-registration' ),
     988                    ] ) . ";\n" . <<<'JS'
    941989                    document.addEventListener("DOMContentLoaded", function() {
    942990                        // cuenta atrás - defensiva: si no hay fecha, no romper
    … …  
    950998                                const now = new Date().getTime();
    951999                                if (isNaN(eventStart)) {
    952                                     countdownEl.innerHTML = "Fecha no disponible";
     1000                                    countdownEl.textContent = wpcrCountdownI18n.noDate;
    9531001                                    return;
    9541002                                }
    9551003                                if (now > eventEnd) {
    956                                     titleEl.innerHTML = "";
    957                                     countdownEl.innerHTML = "El evento ha finalizado.";
     1004                                    titleEl.textContent = "";
     1005                                    countdownEl.textContent = wpcrCountdownI18n.finished;
    9581006                                    return;
    9591007                                }
    9601008                                if (now >= eventStart && now <= eventEnd) {
    961                                     titleEl.innerHTML = "";
    962                                     countdownEl.innerHTML = "¡¡ El evento ha comenzado !!";
     1009                                    titleEl.textContent = "";
     1010                                    countdownEl.textContent = wpcrCountdownI18n.started;
    9631011                                    return;
    9641012                                }
    … …  
    9681016                                const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
    9691017                                const seconds = Math.floor((distance % (1000 * 60)) / 1000);
    970                                 titleEl.innerHTML = "Quedan...";
    971                                 countdownEl.innerHTML = days + "d " + hours + "h " + minutes + "m " + seconds + "s";
     1018                                titleEl.textContent = wpcrCountdownI18n.timeLeft;
     1019                                countdownEl.textContent = days + "d " + hours + "h " + minutes + "m " + seconds + "s";
    9721020                            }
    9731021                            updateCountdown();
    … …  
    9841032                <div class="wpcr-right" style="text-align:center;">
    9851033                    <?php if($poster_url): ?>
    986                         <img src="<?php echo esc_url($poster_url); ?>" class="wpcr-poster" alt="Cartel del evento" />
     1034                        <img src="<?php echo esc_url($poster_url); ?>" class="wpcr-poster" alt="<?php echo esc_attr__( 'Event poster', 'opcion5-sports-event-registration' ); ?>" />
    9871035                    <?php endif; ?>
    9881036
    … …  
    10051053                            'x'         => [ 'label' => 'X (Twitter)', 'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.24 2.25h3.31l-7.23 8.26 8.5 11.24h-6.66l-5.21-6.82-5.97 6.82H1.67l7.73-8.84L1.25 2.25h6.83l4.71 6.23 5.45-6.23Zm-1.16 17.52h1.83L7.08 4.13H5.12l11.96 15.64Z"/></svg>' ],
    10061054                            'instagram' => [ 'label' => 'Instagram',  'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2.16c3.2 0 3.58.01 4.85.07 1.17.05 1.8.25 2.23.41.56.22.96.48 1.38.9.42.42.68.82.9 1.38.16.42.36 1.06.41 2.23.06 1.27.07 1.65.07 4.85s-.01 3.58-.07 4.85c-.05 1.17-.25 1.8-.41 2.23a3.7 3.7 0 0 1-.9 1.38c-.42.42-.82.68-1.38.9-.42.16-1.06.36-2.23.41-1.27.06-1.65.07-4.85.07s-3.58-.01-4.85-.07c-1.17-.05-1.8-.25-2.23-.41a3.72 3.72 0 0 1-1.38-.9 3.72 3.72 0 0 1-.9-1.38c-.16-.42-.36-1.06-.41-2.23-.06-1.27-.07-1.65-.07-4.85s.01-3.58.07-4.85c.05-1.17.25-1.8.41-2.23.22-.56.48-.96.9-1.38.42-.42.82-.68 1.38-.9.42-.16 1.06-.36 2.23-.41 1.27-.06 1.65-.07 4.85-.07M12 0C8.74 0 8.33.01 7.05.07 5.78.13 4.9.33 4.14.63a5.88 5.88 0 0 0-2.13 1.38A5.88 5.88 0 0 0 .63 4.14C.33 4.9.13 5.78.07 7.05.01 8.33 0 8.74 0 12s.01 3.67.07 4.95c.06 1.27.26 2.15.56 2.91.31.8.72 1.47 1.38 2.13a5.88 5.88 0 0 0 2.13 1.38c.76.3 1.64.5 2.91.56C8.33 23.99 8.74 24 12 24s3.67-.01 4.95-.07c1.27-.06 2.15-.26 2.91-.56a5.88 5.88 0 0 0 2.13-1.38 5.88 5.88 0 0 0 1.38-2.13c.3-.76.5-1.64.56-2.91.06-1.28.07-1.69.07-4.95s-.01-3.67-.07-4.95c-.06-1.27-.26-2.15-.56-2.91a5.88 5.88 0 0 0-1.38-2.13A5.88 5.88 0 0 0 19.86.63c-.76-.3-1.64-.5-2.91-.56C15.67.01 15.26 0 12 0Zm0 5.84A6.16 6.16 0 1 0 18.16 12 6.16 6.16 0 0 0 12 5.84Zm0 10.15A4 4 0 1 1 16 12a4 4 0 0 1-4 3.99Zm7.84-10.4a1.44 1.44 0 1 1-1.44-1.44 1.44 1.44 0 0 1 1.44 1.44Z"/></svg>' ],
    1007                             'web'       => [ 'label' => 'Página web', 'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2Zm7.93 9h-3.02a15.65 15.65 0 0 0-1.27-5.75A8.03 8.03 0 0 1 19.93 11ZM12 4.04A13.87 13.87 0 0 1 14.9 11H9.1A13.87 13.87 0 0 1 12 4.04ZM4.07 13h3.02a15.65 15.65 0 0 0 1.27 5.75A8.03 8.03 0 0 1 4.07 13Zm3.02-2H4.07a8.03 8.03 0 0 1 4.29-5.75A15.65 15.65 0 0 0 7.09 11ZM12 19.96A13.87 13.87 0 0 1 9.1 13h5.8a13.87 13.87 0 0 1-2.9 6.96Zm3.64-1.21A15.65 15.65 0 0 0 16.91 13h3.02a8.03 8.03 0 0 1-4.29 5.75Z"/></svg>' ],
    1008                             'otros'     => [ 'label' => 'Otros canales', 'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M10.59 13.41a1 1 0 0 1 0-1.41l2.83-2.83a1 1 0 0 1 1.41 1.41l-2.83 2.83a1 1 0 0 1-1.41 0Zm-2.12 4.95a3 3 0 0 1-4.24-4.24l2.83-2.83a3 3 0 0 1 4.24 0 1 1 0 0 1-1.41 1.41 1 1 0 0 0-1.41 0l-2.83 2.83a1 1 0 0 0 1.41 1.41l1.41-1.41a1 1 0 0 1 1.42 1.42Zm11.31-7.07-2.83 2.83a3 3 0 0 1-4.24 0 1 1 0 0 1 1.41-1.41 1 1 0 0 0 1.41 0l2.83-2.83a1 1 0 1 0-1.41-1.41l-1.41 1.41a1 1 0 0 1-1.42-1.42l1.42-1.41a3 3 0 0 1 4.24 4.24Z"/></svg>' ],
     1055                            'web'       => [ 'label' => __( 'Website', 'opcion5-sports-event-registration' ), 'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2Zm7.93 9h-3.02a15.65 15.65 0 0 0-1.27-5.75A8.03 8.03 0 0 1 19.93 11ZM12 4.04A13.87 13.87 0 0 1 14.9 11H9.1A13.87 13.87 0 0 1 12 4.04ZM4.07 13h3.02a15.65 15.65 0 0 0 1.27 5.75A8.03 8.03 0 0 1 4.07 13Zm3.02-2H4.07a8.03 8.03 0 0 1 4.29-5.75A15.65 15.65 0 0 0 7.09 11ZM12 19.96A13.87 13.87 0 0 1 9.1 13h5.8a13.87 13.87 0 0 1-2.9 6.96Zm3.64-1.21A15.65 15.65 0 0 0 16.91 13h3.02a8.03 8.03 0 0 1-4.29 5.75Z"/></svg>' ],
     1056                            'otros'     => [ 'label' => __( 'Other channels', 'opcion5-sports-event-registration' ), 'svg' => '<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M10.59 13.41a1 1 0 0 1 0-1.41l2.83-2.83a1 1 0 0 1 1.41 1.41l-2.83 2.83a1 1 0 0 1-1.41 0Zm-2.12 4.95a3 3 0 0 1-4.24-4.24l2.83-2.83a3 3 0 0 1 4.24 0 1 1 0 0 1-1.41 1.41 1 1 0 0 0-1.41 0l-2.83 2.83a1 1 0 0 0 1.41 1.41l1.41-1.41a1 1 0 0 1 1.42 1.42Zm11.31-7.07-2.83 2.83a3 3 0 0 1-4.24 0 1 1 0 0 1 1.41-1.41 1 1 0 0 0 1.41 0l2.83-2.83a1 1 0 1 0-1.41-1.41l-1.41 1.41a1 1 0 0 1-1.42-1.42l1.42-1.41a3 3 0 0 1 4.24 4.24Z"/></svg>' ],
    10091057                        ];
    10101058                    ?>
    10111059                    <div class="wpcr-comm-channels">
    1012                         <h4 class="wpcr-comm-title"><?php echo count( $comm_channels ) === 1 ? 'Canal de comunicación del evento' : 'Canales de comunicación del evento'; ?></h4>
     1060                        <h4 class="wpcr-comm-title"><?php echo esc_html( _n(
     1061                            'Event communication channel',
     1062                            'Event communication channels',
     1063                            count( $comm_channels ),
     1064                            'opcion5-sports-event-registration'
     1065                        ) ); ?></h4>
    10131066                        <div class="wpcr-comm-icons">
    10141067                            <?php foreach ( $comm_icons as $key => $icon ) :
    … …  
    10431096
    10441097if (!empty($event['organizer_name_public']) && !empty($event['organizer_name'])) {
    1045     $org_rows['Organizador'] = esc_html($event['organizer_name']);
     1098    $org_rows[ __( 'Organizer', 'opcion5-sports-event-registration' ) ] = esc_html($event['organizer_name']);
    10461099}
    10471100
    10481101if (!empty($event['organizer_email_public']) && !empty($event['organizer_email'])) {
    1049     $org_rows['Email'] = '<a href="mailto:' . esc_attr($event['organizer_email']) . '">' .
     1102    $org_rows[ __( 'Email', 'opcion5-sports-event-registration' ) ] = '<a href="mailto:' . esc_attr($event['organizer_email']) . '">' .
    10501103                         esc_html($event['organizer_email']) . '</a>';
    10511104}
    10521105
    10531106if (!empty($event['organizer_phone_public']) && !empty($event['organizer_phone'])) {
    1054     $org_rows['Teléfono'] = esc_html($event['organizer_phone']);
     1107    $org_rows[ __( 'Phone', 'opcion5-sports-event-registration' ) ] = esc_html($event['organizer_phone']);
    10551108}
    10561109?>
    10571110
    10581111<?php if (!empty($org_rows)): ?>
    1059     <h3 style="margin-top:0;">Datos del organizador</h3>
     1112    <h3 style="margin-top:0;"><?php esc_html_e( 'Organizer details', 'opcion5-sports-event-registration' ); ?></h3>
    10601113    <table class="wpcr-tech-table">
    10611114        <?php foreach ($org_rows as $label => $value): ?>
    … …  
    10761129
    10771130if ( ! empty( $event['start_time'] ) ) {
    1078     $ficha_rows['Hora de salida'] = esc_html( substr( $event['start_time'], 0, 5 ) ) . 'h';
     1131    $ficha_rows[ __( 'Start time', 'opcion5-sports-event-registration' ) ] = esc_html( wpcr_format_time( $event['start_time'] ) );
    10791132}
    10801133if ( ! empty( $event['reg_open'] ) && $event['reg_open'] !== '0000-00-00 00:00:00' ) {
    1081     $ficha_rows['Apertura de inscripciones'] = esc_html( date_i18n( 'd/m/Y H:i', strtotime( $event['reg_open'] ) ) );
     1134    $ficha_rows[ __( 'Registration opens', 'opcion5-sports-event-registration' ) ] = esc_html( wpcr_format_datetime( $event['reg_open'] ) );
    10821135}
    10831136if ( ! empty( $event['reg_close'] ) && $event['reg_close'] !== '0000-00-00 00:00:00' ) {
    1084     $ficha_rows['Cierre de inscripciones'] = esc_html( date_i18n( 'd/m/Y H:i', strtotime( $event['reg_close'] ) ) );
     1137    $ficha_rows[ __( 'Registration closes', 'opcion5-sports-event-registration' ) ] = esc_html( wpcr_format_datetime( $event['reg_close'] ) );
    10851138}
    10861139if ( ! empty( $event['start_location'] ) ) {
    1087     $ficha_rows['Lugar de salida'] = esc_html( $event['start_location'] );
     1140    $ficha_rows[ __( 'Start location', 'opcion5-sports-event-registration' ) ] = esc_html( $event['start_location'] );
    10881141}
    10891142
    … …  
    10951148    ) );
    10961149    $disponibles = max( 0, (int) $event['max_participants'] - $inscritos_count );
    1097     $ficha_rows['Plazas disponibles'] = $disponibles . ' de ' . (int) $event['max_participants'];
     1150    $ficha_rows[ __( 'Places available', 'opcion5-sports-event-registration' ) ] = sprintf(
     1151        /* translators: 1: places still free, 2: total places */
     1152        __( '%1$d of %2$d', 'opcion5-sports-event-registration' ),
     1153        $disponibles,
     1154        (int) $event['max_participants']
     1155    );
    10981156}
    10991157
    … …  
    11051163    ) );
    11061164    $pairs_disponibles = max( 0, (int) $event['pairs_max_couples'] - $started_pairs );
    1107     $ficha_rows['Plazas de parejas'] = $pairs_disponibles . ' de ' . (int) $event['pairs_max_couples'];
     1165    $ficha_rows[ __( 'Pair places', 'opcion5-sports-event-registration' ) ] = sprintf(
     1166        /* translators: 1: pair places still free, 2: total pair places */
     1167        __( '%1$d of %2$d', 'opcion5-sports-event-registration' ),
     1168        $pairs_disponibles,
     1169        (int) $event['pairs_max_couples']
     1170    );
    11081171}
    11091172
    … …  
    11151178    ) );
    11161179    if ( $ficha_cats ) {
    1117         $ficha_rows['Categorías'] = esc_html( implode( ', ', $ficha_cats ) );
     1180        $ficha_rows[ __( 'Categories', 'opcion5-sports-event-registration' ) ] = esc_html( implode( ', ', $ficha_cats ) );
    11181181    }
    11191182}
    … …  
    11231186
    11241187if ( $ficha_price_mode === 'free' ) {
    1125     $ficha_rows['Precio'] = 'Gratuito';
     1188    $ficha_rows[ __( 'Price', 'opcion5-sports-event-registration' ) ] = __( 'Free', 'opcion5-sports-event-registration' );
    11261189} elseif ( $ficha_price_mode === 'fixed' && (float) ( $event['fixed_price'] ?? 0 ) > 0 ) {
    1127     $ficha_rows['Precio'] = number_format( (float) $event['fixed_price'], 2, ',', '.' ) . ' €';
     1190    $ficha_rows[ __( 'Price', 'opcion5-sports-event-registration' ) ] = number_format( (float) $event['fixed_price'], 2, ',', '.' ) . ' €';
    11281191} elseif ( $ficha_price_mode === 'ranges' ) {
    11291192    $ficha_ranges = json_decode( $event['price_ranges'] ?? '[]', true );
    … …  
    11361199            $from     = $r['start'] ?? ( $r['from'] ?? '' );
    11371200            $to       = $r['end']   ?? ( $r['to']   ?? '' );
    1138             $from_fmt = $from ? date_i18n( 'd/m/Y', strtotime( $from ) ) : '';
    1139             $to_fmt   = $to   ? date_i18n( 'd/m/Y', strtotime( $to ) )   : '';
     1201            $from_fmt = wpcr_format_date( $from );
     1202            $to_fmt   = wpcr_format_date( $to );
    11401203
    11411204            if ( $from_fmt && $to_fmt ) {
    1142                 $label = "Del {$from_fmt} al {$to_fmt}";
     1205                /* translators: 1: start date, 2: end date */
     1206                $label = sprintf( __( 'From %1$s to %2$s', 'opcion5-sports-event-registration' ), $from_fmt, $to_fmt );
    11431207            } elseif ( $from_fmt ) {
    1144                 $label = "Desde {$from_fmt}";
     1208                /* translators: %s: start date */
     1209                $label = sprintf( __( 'From %s', 'opcion5-sports-event-registration' ), $from_fmt );
    11451210            } elseif ( $to_fmt ) {
    1146                 $label = "Hasta {$to_fmt}";
     1211                /* translators: %s: end date */
     1212                $label = sprintf( __( 'Until %s', 'opcion5-sports-event-registration' ), $to_fmt );
    11471213            } else {
    11481214                continue;
    … …  
    11531219    }
    11541220    if ( $range_lines ) {
    1155         $ficha_rows['Precio por franjas'] = implode( '<br>', $range_lines );
     1221        $ficha_rows[ __( 'Price by date', 'opcion5-sports-event-registration' ) ] = implode( '<br>', $range_lines );
    11561222    }
    11571223}
    … …  
    11691235}
    11701236if ( $extra_lines ) {
    1171     $ficha_rows['Extras opcionales'] = implode( '<br>', $extra_lines );
     1237    $ficha_rows[ __( 'Optional extras', 'opcion5-sports-event-registration' ) ] = implode( '<br>', $extra_lines );
    11721238}
    11731239?>
    11741240
    11751241<?php if ( ! empty( $ficha_rows ) ) : ?>
    1176     <h3 style="margin-top:30px;">Ficha técnica</h3>
     1242    <h3 style="margin-top:30px;"><?php esc_html_e( 'Race info', 'opcion5-sports-event-registration' ); ?></h3>
    11771243    <table class="wpcr-tech-table">
    11781244        <?php foreach ( $ficha_rows as $label => $value ) : ?>
    … …  
    11981264
    11991265<?php if ( $ficha_distances_html !== '' ) : ?>
    1200     <h3 style="margin-top:30px;">Distancias y precios</h3>
     1266    <h3 style="margin-top:30px;"><?php esc_html_e( 'Distances and prices', 'opcion5-sports-event-registration' ); ?></h3>
    12011267    <?php echo wp_kses( $ficha_distances_html, wpcr_kses_allowed_html() ); ?>
    12021268<?php endif; ?>
    … …  
    12121278<?php if ( ! empty( $event['tech_observations'] ) ) : ?>
    12131279    <div style="margin:16px 0;padding:14px 16px;background:#f8fafc;border-left:3px solid #0077cc;border-radius:6px;">
    1214         <strong style="display:block;margin-bottom:6px;">Observaciones</strong>
     1280        <strong style="display:block;margin-bottom:6px;"><?php esc_html_e( 'Notes', 'opcion5-sports-event-registration' ); ?></strong>
    12151281        <div style="white-space:pre-line;color:#333;font-size:14px;line-height:1.5;"><?php echo wp_kses_post( $event['tech_observations'] ); ?></div>
    12161282    </div>
    … …  
    12341300
    12351301<?php if (!empty($docs)): ?>
    1236     <h3 style="margin-top:30px;">Documentación técnica</h3>
     1302    <h3 style="margin-top:30px;"><?php esc_html_e( 'Event documents', 'opcion5-sports-event-registration' ); ?></h3>
    12371303
    12381304    <table class="wpcr-tech-docs">
    12391305        <thead>
    12401306            <tr>
    1241                 <th style="width:8%;text-align:center;">Tipo</th>
    1242                 <th style="width:20%;">Título</th>
    1243                 <th style="width:52%;">Descripción</th>
    1244                 <th style="width:20%;">Documento</th>
     1307                <th style="width:8%;text-align:center;"><?php esc_html_e( 'Type', 'opcion5-sports-event-registration' ); ?></th>
     1308                <th style="width:20%;"><?php esc_html_e( 'Title', 'opcion5-sports-event-registration' ); ?></th>
     1309                <th style="width:52%;"><?php esc_html_e( 'Description', 'opcion5-sports-event-registration' ); ?></th>
     1310                <th style="width:20%;"><?php esc_html_e( 'Document', 'opcion5-sports-event-registration' ); ?></th>
    12451311            </tr>
    12461312        </thead>
    … …  
    12751341   download
    12761342   class="wpcr-download-btn">
    1277    Descargar
     1343   <?php esc_html_e( 'Download', 'opcion5-sports-event-registration' ); ?>
    12781344</a>
    12791345                </td>
    … …  
    13071373            <div style="text-align:center;padding:35px 20px;background:#f0f7ff;border:1px solid #b3d4ff;border-radius:8px;margin-top:15px;">
    13081374                <p style="font-size:22px;margin:0 0 6px;">🗓️</p>
    1309                 <p style="font-size:17px;font-weight:600;margin:0 0 4px;">Las inscripciones aún no están abiertas</p>
    1310                 <p style="color:#555;margin:0 0 16px;">Abrirán el <strong><?php echo esc_html($reg_open); ?></strong></p>
     1375                <p style="font-size:17px;font-weight:600;margin:0 0 4px;"><?php esc_html_e( 'Registration is not open yet', 'opcion5-sports-event-registration' ); ?></p>
     1376                <p style="color:#555;margin:0 0 16px;"><?php printf(
     1377                    /* translators: %s: date and time when registration opens */
     1378                    esc_html__( 'It opens on %s', 'opcion5-sports-event-registration' ),
     1379                    '<strong>' . esc_html($reg_open) . '</strong>'
     1380                ); ?></p>
    13111381                <div id="wpcr-reg-countdown" style="font-size:26px;font-weight:700;color:#0077cc;letter-spacing:2px;">--</div>
    1312                 <p style="color:#888;font-size:12px;margin:6px 0 0;">días · horas · minutos · segundos</p>
     1382                <p style="color:#888;font-size:12px;margin:6px 0 0;"><?php esc_html_e( 'days · hours · minutes · seconds', 'opcion5-sports-event-registration' ); ?></p>
    13131383            </div>
    1314             <?php wpcr_inline_js( 'var wpcrRegOpenTs = ' . (int) $open_ts . ";\n" . <<<'JS'
     1384            <?php wpcr_inline_js( 'var wpcrRegOpenTs = ' . (int) $open_ts . ";\n"
     1385            . 'var wpcrRegOpenMsg = ' . wp_json_encode( __( 'Now open!', 'opcion5-sports-event-registration' ) ) . ";\n" . <<<'JS'
    13151386            (function(){
    13161387                var target = wpcrRegOpenTs * 1000;
    … …  
    13181389                function tick(){
    13191390                    var diff = target - Date.now();
    1320                     if(diff <= 0){ el.textContent = '¡Ya abiertas!'; return; }
     1391                    if(diff <= 0){ el.textContent = wpcrRegOpenMsg; return; }
    13211392                    var d = Math.floor(diff/86400000);
    13221393                    var h = Math.floor((diff%86400000)/3600000);
    … …  
    13341405            <div style="text-align:center;padding:35px 20px;background:#fff3f3;border:1px solid #ffb3b3;border-radius:8px;margin-top:15px;">
    13351406                <p style="font-size:22px;margin:0 0 6px;">🔒</p>
    1336                 <p style="font-size:17px;font-weight:600;margin:0 0 6px;">Inscripciones cerradas</p>
     1407                <p style="font-size:17px;font-weight:600;margin:0 0 6px;"><?php esc_html_e( 'Registration closed', 'opcion5-sports-event-registration' ); ?></p>
    13371408                <?php if ( $close_ts && $now > $close_ts ) : ?>
    1338                     <p style="color:#555;margin:0;">El plazo de inscripción finalizó el <strong><?php echo esc_html($reg_close); ?></strong>.</p>
     1409                    <p style="color:#555;margin:0;"><?php printf(
     1410                        /* translators: %s: date and time when registration closed */
     1411                        esc_html__( 'Registration closed on %s.', 'opcion5-sports-event-registration' ),
     1412                        '<strong>' . esc_html($reg_close) . '</strong>'
     1413                    ); ?></p>
    13391414                <?php elseif ( ( function_exists('wpcr_is_addon_active') && wpcr_is_addon_active('wpcr-results') ) || $wpcr_is_stage_event ) : ?>
    1340                     <p style="color:#555;margin:0;">Este evento ya se ha celebrado. Consulta las clasificaciones en la pestaña <strong>🏁 Resultados</strong>.</p>
     1415                    <p style="color:#555;margin:0;"><?php printf(
     1416                        /* translators: %s: name of the results tab, in bold */
     1417                        esc_html__( 'This event has already taken place. See the standings in the %s tab.', 'opcion5-sports-event-registration' ),
     1418                        '<strong>🏁 ' . esc_html__( 'RESULTS', 'opcion5-sports-event-registration' ) . '</strong>'
     1419                    ); ?></p>
    13411420                <?php else : ?>
    1342                     <p style="color:#555;margin:0;">Este evento ya se ha celebrado.</p>
     1421                    <p style="color:#555;margin:0;"><?php esc_html_e( 'This event has already taken place.', 'opcion5-sports-event-registration' ); ?></p>
    13431422                <?php endif; ?>
    13441423            </div>
    … …  
    13481427            <?php if ( $close_ts ) : ?>
    13491428                <div style="background:#fff8e1;border:1px solid #ffe082;border-radius:6px;padding:10px 16px;margin-bottom:14px;font-size:14px;display:flex;align-items:center;gap:8px;">
    1350                     ⚠️ <span>Las inscripciones cierran el <strong><?php echo esc_html($reg_close); ?></strong></span>
     1429                    ⚠️ <span><?php printf(
     1430                        /* translators: %s: date and time when registration closes */
     1431                        esc_html__( 'Registration closes on %s', 'opcion5-sports-event-registration' ),
     1432                        '<strong>' . esc_html($reg_close) . '</strong>'
     1433                    ); ?></span>
    13511434                </div>
    13521435            <?php endif; ?>
    … …  
    13711454        <div id="wpcr-live-box" style="border:2px solid #fca5a5;border-radius:10px;padding:16px 18px;margin:15px 0;background:#fff;">
    13721455            <div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:12px;">
    1373                 <span class="wpcr-live-badge"><span class="wpcr-live-dot"></span> En directo</span>
    1374                 <strong style="font-size:16px;">Seguimiento de la carrera</strong>
     1456                <span class="wpcr-live-badge"><span class="wpcr-live-dot"></span> <?php esc_html_e( 'Live', 'opcion5-sports-event-registration' ); ?></span>
     1457                <strong style="font-size:16px;"><?php esc_html_e( 'Race tracking', 'opcion5-sports-event-registration' ); ?></strong>
    13751458                <span id="wpcr-live-updated" style="font-size:12px;color:#888;margin-left:auto;"></span>
    13761459            </div>
    13771460            <div id="wpcr-live-points" style="display:flex;gap:8px;flex-wrap:wrap;margin-bottom:12px;"></div>
    1378             <div id="wpcr-live-table" style="overflow-x:auto;"><p style="color:#888;">Cargando tiempos…</p></div>
    1379             <p style="margin:10px 0 0;font-size:12px;color:#888;">Tiempos provisionales por punto de control, actualizados automáticamente. La clasificación oficial se publica al finalizar la carrera.</p>
     1461            <div id="wpcr-live-table" style="overflow-x:auto;"><p style="color:#888;"><?php esc_html_e( 'Loading times…', 'opcion5-sports-event-registration' ); ?></p></div>
     1462            <p style="margin:10px 0 0;font-size:12px;color:#888;"><?php esc_html_e( 'Provisional times by checkpoint, updated automatically. The official standings are published once the race is over.', 'opcion5-sports-event-registration' ); ?></p>
    13801463        </div>
    13811464        <?php
    … …  
    13831466            'ajax' => admin_url( 'admin-ajax.php' ),
    13841467            'ev'   => $public_id,
     1468            'i18n' => [
     1469                'noTimes'  => __( 'No times recorded yet. As soon as the timekeepers start marking, they will show up here.', 'opcion5-sports-event-registration' ),
     1470                'noPoint'  => __( 'No times at this checkpoint yet.', 'opcion5-sports-event-registration' ),
     1471                'pos'      => __( 'Pos.', 'opcion5-sports-event-registration' ),
     1472                'bib'      => __( 'Bib', 'opcion5-sports-event-registration' ),
     1473                'name'     => __( 'Name', 'opcion5-sports-event-registration' ),
     1474                'category' => __( 'Category', 'opcion5-sports-event-registration' ),
     1475                'time'     => __( 'Time', 'opcion5-sports-event-registration' ),
     1476                'retired'  => __( 'RETIRED', 'opcion5-sports-event-registration' ),
     1477                'raceOver' => __( 'The race has finished. See the official standings below.', 'opcion5-sports-event-registration' ),
     1478            ],
    13851479        ] ) . ";\n" . <<<'JS'
    13861480        (function(){
    … …  
    14061500                var points = data.points || [];
    14071501                if (!points.length){
    1408                     document.getElementById('wpcr-live-table').innerHTML = '<p style="color:#888;">Todavía no hay tiempos registrados. En cuanto los cronometradores empiecen a marcar, aparecerán aquí.</p>';
     1502                    document.getElementById('wpcr-live-table').innerHTML = '<p style="color:#888;">' + esc(wpcrLiveCfg.i18n.noTimes) + '</p>';
    14091503                    return;
    14101504                }
    … …  
    14191513                if (!pt) return;
    14201514                if (!pt.ranking.length){
    1421                     document.getElementById('wpcr-live-table').innerHTML = '<p style="color:#888;">Sin tiempos en este punto todavía.</p>';
     1515                    document.getElementById('wpcr-live-table').innerHTML = '<p style="color:#888;">' + esc(wpcrLiveCfg.i18n.noPoint) + '</p>';
    14221516                    return;
    14231517                }
    14241518                var html = '<table style="width:100%;border-collapse:collapse;font-size:14px;"><thead><tr style="background:#f5f5f5;text-align:left;">'
    1425                     + '<th style="padding:8px 10px;width:60px;">Pos.</th><th style="padding:8px 10px;width:70px;">Dorsal</th>'
    1426                     + '<th style="padding:8px 10px;">Nombre</th><th style="padding:8px 10px;">Categoría</th><th style="padding:8px 10px;width:100px;">Tiempo</th></tr></thead><tbody>';
     1519                    + '<th style="padding:8px 10px;width:60px;">' + esc(wpcrLiveCfg.i18n.pos) + '</th><th style="padding:8px 10px;width:70px;">' + esc(wpcrLiveCfg.i18n.bib) + '</th>'
     1520                    + '<th style="padding:8px 10px;">' + esc(wpcrLiveCfg.i18n.name) + '</th><th style="padding:8px 10px;">' + esc(wpcrLiveCfg.i18n.category) + '</th><th style="padding:8px 10px;width:100px;">' + esc(wpcrLiveCfg.i18n.time) + '</th></tr></thead><tbody>';
    14271521                pt.ranking.forEach(function(r){
    14281522                    var ret = !!r.retired;
    … …  
    14301524                        + '<td style="padding:7px 10px;font-weight:700;">' + esc(r.pos) + '</td>'
    14311525                        + '<td style="padding:7px 10px;font-weight:700;">' + esc(r.dorsal) + '</td>'
    1432                         + '<td style="padding:7px 10px;">' + esc(r.name) + (ret ? ' · RETIRADO' : '') + '</td>'
     1526                        + '<td style="padding:7px 10px;">' + esc(r.name) + (ret ? ' · ' + esc(wpcrLiveCfg.i18n.retired) : '') + '</td>'
    14331527                        + '<td style="padding:7px 10px;">' + esc(r.cat) + '</td>'
    14341528                        + '<td style="padding:7px 10px;font-variant-numeric:tabular-nums;' + (ret ? 'text-decoration:line-through;' : '') + '">' + esc(r.tiempo) + '</td></tr>';
    … …  
    14561550                        if (res.data.finished){
    14571551                            stopped = true;
    1458                             box.innerHTML = '<p style="margin:0;font-weight:600;color:#166534;">🏁 La carrera ha finalizado. Consulta las clasificaciones oficiales más abajo.</p>';
     1552                            box.innerHTML = '<p style="margin:0;font-weight:600;color:#166534;">🏁 ' + esc(wpcrLiveCfg.i18n.raceOver) + '</p>';
    14591553                            return;
    14601554                        }
    … …  
    17051799    if ( ! $valid_dni ) {
    17061800        wp_die(
    1707             '<h2 style="color:red;">❌ DNI/NIE no válido</h2>'
    1708             . '<p>El DNI o NIE introducido (<strong>' . esc_html( $dni_raw ) . '</strong>) no tiene un formato o letra de control correctos.</p>'
    1709             . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1710             'DNI no válido',
     1801            '<h2 style="color:red;">❌ ' . esc_html__( 'Invalid ID number', 'opcion5-sports-event-registration' ) . '</h2>'
     1802            . '<p>' . sprintf(
     1803                /* translators: %s: the ID number entered by the user */
     1804                esc_html__( 'The ID number entered (%s) does not have a valid format or check letter.', 'opcion5-sports-event-registration' ),
     1805                '<strong>' . esc_html( $dni_raw ) . '</strong>'
     1806            ) . '</p>'
     1807            . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1808            esc_html__( 'Invalid ID number', 'opcion5-sports-event-registration' ),
    17111809            [ 'response' => 400 ]
    17121810        );
    … …  
    17251823    if ( $ya_inscrito ) {
    17261824        wp_die(
    1727             '<h2 style="color:red;">❌ Ya estás inscrito</h2>'
    1728             . '<p>El DNI <strong>' . esc_html( $dni_raw ) . '</strong> ya figura como inscrito en este evento.</p>'
    1729             . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1730             'Ya inscrito',
     1825            '<h2 style="color:red;">❌ ' . esc_html__( 'You are already registered', 'opcion5-sports-event-registration' ) . '</h2>'
     1826            . '<p>' . sprintf(
     1827                /* translators: %s: the ID number entered by the user */
     1828                esc_html__( 'The ID number %s is already registered for this event.', 'opcion5-sports-event-registration' ),
     1829                '<strong>' . esc_html( $dni_raw ) . '</strong>'
     1830            ) . '</p>'
     1831            . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1832            esc_html__( 'You are already registered', 'opcion5-sports-event-registration' ),
    17311833            [ 'response' => 409 ]
    17321834        );
    … …  
    17631865        if ( ! $pair_join_partner ) {
    17641866            wp_die(
    1765                 '<h2 style="color:red;">❌ Código de pareja no válido</h2>'
    1766                 . '<p>El código introducido no existe, ya está completo o no pertenece a este evento.</p>'
    1767                 . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1768                 'Código de pareja no válido', [ 'response' => 400 ]
     1867                '<h2 style="color:red;">❌ ' . esc_html__( 'Invalid pair code', 'opcion5-sports-event-registration' ) . '</h2>'
     1868                . '<p>' . esc_html__( 'The code you entered does not exist, is already complete or does not belong to this event.', 'opcion5-sports-event-registration' ) . '</p>'
     1869                . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1870                esc_html__( 'Invalid pair code', 'opcion5-sports-event-registration' ), [ 'response' => 400 ]
    17691871            );
    17701872        }
    … …  
    17781880
    17791881        if ( ! $coherente ) {
    1780             $labels = [ 'male' => 'masculina', 'female' => 'femenina', 'mixed' => 'mixta' ];
     1882            $labels = [
     1883                'male'   => __( "men's", 'opcion5-sports-event-registration' ),
     1884                'female' => __( "women's", 'opcion5-sports-event-registration' ),
     1885                'mixed'  => __( 'mixed', 'opcion5-sports-event-registration' ),
     1886            ];
    17811887            wp_die(
    1782                 '<h2 style="color:red;">❌ Género no compatible con la modalidad</h2>'
    1783                 . '<p>Esta pareja es de modalidad <strong>' . esc_html( $labels[ $pcat ] ?? $pcat ) . '</strong> y tu género seleccionado no es compatible.</p>'
    1784                 . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1785                 'Modalidad de pareja no compatible', [ 'response' => 400 ]
     1888                '<h2 style="color:red;">❌ ' . esc_html__( 'Gender not compatible with this pair category', 'opcion5-sports-event-registration' ) . '</h2>'
     1889                . '<p>' . sprintf(
     1890                    /* translators: %s: pair category, for example "mixed" */
     1891                    esc_html__( 'This is a %s pair and the gender you selected is not compatible.', 'opcion5-sports-event-registration' ),
     1892                    '<strong>' . esc_html( $labels[ $pcat ] ?? $pcat ) . '</strong>'
     1893                ) . '</p>'
     1894                . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1895                esc_html__( 'Gender not compatible with this pair category', 'opcion5-sports-event-registration' ), [ 'response' => 400 ]
    17861896            );
    17871897        }
    … …  
    17981908        if ( ! in_array( $pair_category, $allowed_pair_cats, true ) ) {
    17991909            wp_die(
    1800                 '<h2 style="color:red;">❌ Modalidad de pareja no válida</h2>'
    1801                 . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1802                 'Modalidad no válida', [ 'response' => 400 ]
     1910                '<h2 style="color:red;">❌ ' . esc_html__( 'Invalid pair category', 'opcion5-sports-event-registration' ) . '</h2>'
     1911                . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1912                esc_html__( 'Invalid pair category', 'opcion5-sports-event-registration' ), [ 'response' => 400 ]
    18031913            );
    18041914        }
    … …  
    18071917        if ( ( $pair_category === 'male' && $my_gender !== 'M' ) || ( $pair_category === 'female' && $my_gender !== 'F' ) ) {
    18081918            wp_die(
    1809                 '<h2 style="color:red;">❌ Género no compatible con la modalidad de pareja elegida</h2>'
    1810                 . '<p><a href="javascript:history.back()">← Volver al formulario</a></p>',
    1811                 'Modalidad de pareja no compatible', [ 'response' => 400 ]
     1919                '<h2 style="color:red;">❌ ' . esc_html__( 'Gender not compatible with the pair category you chose', 'opcion5-sports-event-registration' ) . '</h2>'
     1920                . '<p><a href="javascript:history.back()">← ' . esc_html__( 'Back to the form', 'opcion5-sports-event-registration' ) . '</a></p>',
     1921                esc_html__( 'Gender not compatible with this pair category', 'opcion5-sports-event-registration' ), [ 'response' => 400 ]
    18121922            );
    18131923        }
    … …  
    18221932            if ( $started_pairs >= (int) $evt_pairs['pairs_max_couples'] ) {
    18231933                wp_die(
    1824                     '<h2 style="color:red;">El cupo de parejas está completo</h2>'
    1825                     . '<p>No se aceptan más inscripciones por parejas para este evento.</p>',
    1826                     'Cupo de parejas completo', [ 'response' => 409 ]
     1934                    '<h2 style="color:red;">' . esc_html__( 'The pair quota is full', 'opcion5-sports-event-registration' ) . '</h2>'
     1935                    . '<p>' . esc_html__( 'No more pair registrations are being accepted for this event.', 'opcion5-sports-event-registration' ) . '</p>',
     1936                    esc_html__( 'The pair quota is full', 'opcion5-sports-event-registration' ), [ 'response' => 409 ]
    18271937                );
    18281938            }
    … …  
    18982008        if ( $current >= intval($event['max_participants']) ) {
    18992009
    1900             wp_die("
    1901                 <h2 style='color:red;'>El cupo está completo</h2>
    1902                 <p>No se aceptan más inscripciones para este evento.</p>
    1903             ");
     2010            wp_die(
     2011                "<h2 style='color:red;'>" . esc_html__( 'The event is full', 'opcion5-sports-event-registration' ) . "</h2>"
     2012                . "<p>" . esc_html__( 'No more registrations are being accepted for this event.', 'opcion5-sports-event-registration' ) . "</p>"
     2013            );
    19042014        }
    19052015    }
    … …  
    19242034if (!$participant_id) {
    19252035    wpcr_debug_log('WPCR insert error: ' . $wpdb->last_error);
    1926     wp_die( esc_html__('No se pudo completar tu inscripción. Por favor, inténtalo de nuevo o contacta con el organizador.', 'opcion5-sports-event-registration') );
     2036    wp_die( esc_html__( 'Your registration could not be completed. Please try again or contact the organizer.', 'opcion5-sports-event-registration' ) );
    19272037}
    19282038
    … …  
    19732083
    19742084if (!$payment || empty($payment['payment_token'])) {
    1975     wp_die('❌ No se pudo crear el pago asociado a la inscripción.');
     2085    wp_die( '❌ ' . esc_html__( 'The payment for this registration could not be created.', 'opcion5-sports-event-registration' ) );
    19762086}
    19772087
    … …  
    20042114// Asunto
    20052115$subject = get_post_meta($event_id, 'wpcr_mail_ins_subject', true)
    2006     ?: 'Inscripción recibida - ' . $event['name'];
     2116    ?: sprintf(
     2117        /* translators: %s: event name */
     2118        __( 'Registration received - %s', 'opcion5-sports-event-registration' ),
     2119        $event['name']
     2120    );
    20072121
    20082122// Plantilla
    … …  
    20142128
    20152129<h2 style='color:#0d47a1;margin-bottom:5px;font-size:24px;font-weight:800;'>
    2016 ⏱ ¡Inscripción realizada!
     2130⏱ " . esc_html__( 'Registration received!', 'opcion5-sports-event-registration' ) . "
    20172131</h2>
    20182132
    20192133<p style='font-size:16px;margin-top:5px;color:#0a2540;'>
    2020 Hola <b>{nombre}</b>, ya estás pre-inscrito en el evento:
     2134" . sprintf(
     2135    /* translators: %s: the {nombre} placeholder, replaced with the participant name */
     2136    esc_html__( 'Hi %s, you are now pre-registered for the event:', 'opcion5-sports-event-registration' ),
     2137    '<b>{nombre}</b>'
     2138) . "
    20212139</p>
    20222140
    … …  
    20282146
    20292147<p style='font-size:15px;'>
    2030 <b>💶 Precio actual:</b>
     2148<b>💶 " . esc_html__( 'Current price:', 'opcion5-sports-event-registration' ) . "</b>
    20312149<span style='font-size:17px;color:#004cff;'><b>{precio} €</b></span>
    20322150</p>
    20332151
    20342152<p style='font-size:15px;margin-top:10px;'>
    2035 Para confirmar tu plaza debes completar el pago.
     2153" . esc_html__( 'To confirm your place you need to complete the payment.', 'opcion5-sports-event-registration' ) . "
    20362154</p>
    20372155
    … …  
    20412159style='font-size:18px;font-weight:bold;padding:14px 28px;display:inline-block;
    20422160text-decoration:none;color:#ffffff;font-family:Arial,sans-serif;'>
    2043 🏁 Pagar inscripción ahora
     2161🏁 " . esc_html__( 'Pay for my registration now', 'opcion5-sports-event-registration' ) . "
    20442162</a>
    20452163</td></tr></table>
    … …  
    20522170if ( ! empty( $save['pair_code'] ) && ( $save['pair_status'] ?? '' ) === 'open' ) {
    20532171
    2054     $pair_labels = [ 'male' => 'Pareja masculina', 'mixed' => 'Pareja mixta', 'female' => 'Pareja femenina' ];
    2055     $pair_label  = $pair_labels[ $save['pair_category'] ] ?? 'Pareja';
     2172    $pair_labels = [
     2173        'male'   => __( "Men's pair", 'opcion5-sports-event-registration' ),
     2174        'mixed'  => __( 'Mixed pair', 'opcion5-sports-event-registration' ),
     2175        'female' => __( "Women's pair", 'opcion5-sports-event-registration' ),
     2176    ];
     2177    $pair_label  = $pair_labels[ $save['pair_category'] ] ?? __( 'Pair', 'opcion5-sports-event-registration' );
    20562178
    20572179    $event_page_url = function_exists( 'wpcr_get_event_page_url' ) ? wpcr_get_event_page_url() : home_url( '/' );
    … …  
    20662188padding:20px;border-radius:10px;border:2px solid #0d47a1;margin-top:15px;'>
    20672189<tr><td align='center'>
    2068 <h3 style='color:#0d47a1;margin:0 0 8px;'>👥 Inscripción por parejas — " . esc_html( $pair_label ) . "</h3>
     2190<h3 style='color:#0d47a1;margin:0 0 8px;'>👥 " . sprintf(
     2191    /* translators: %s: pair category, for example "Mixed pair" */
     2192    esc_html__( 'Pair registration — %s', 'opcion5-sports-event-registration' ),
     2193    esc_html( $pair_label )
     2194) . "</h3>
    20692195<p style='font-size:15px;margin:0 0 10px;'>
    2070 Falta que tu pareja complete su inscripción. Comparte con él/ella este código:
     2196" . esc_html__( 'Your partner still has to complete their registration. Share this code with them:', 'opcion5-sports-event-registration' ) . "
    20712197</p>
    20722198<p style='font-size:26px;font-weight:800;letter-spacing:2px;color:#0d47a1;margin:0 0 12px;'>
    20732199" . esc_html( $save['pair_code'] ) . "
    20742200</p>
    2075 <p style='font-size:14px;margin:0 0 14px;'>O envíale directamente este enlace para inscribirse ya vinculado contigo:</p>
     2201<p style='font-size:14px;margin:0 0 14px;'>" . esc_html__( 'Or send them this link so they register already linked to you:', 'opcion5-sports-event-registration' ) . "</p>
    20762202<table role='presentation' cellspacing='0' cellpadding='0' border='0' align='center'>
    20772203<tr><td bgcolor='#0d47a1' style='border-radius:6px;'>
    … …  
    20792205style='font-size:15px;font-weight:bold;padding:11px 22px;display:inline-block;
    20802206text-decoration:none;color:#ffffff;font-family:Arial,sans-serif;'>
    2081 👥 Completar inscripción de mi pareja
     2207👥 " . esc_html__( 'Complete my partner registration', 'opcion5-sports-event-registration' ) . "
    20822208</a>
    20832209</td></tr></table>
    20842210<p style='font-size:12px;color:#555;margin:12px 0 0;'>
    2085 Cada corredor paga su propia inscripción. La pareja quedará confirmada cuando ambos hayáis completado el proceso.
     2211" . esc_html__( 'Each entrant pays for their own registration. The pair is confirmed once you have both completed the process.', 'opcion5-sports-event-registration' ) . "
    20862212</p>
    20872213</td></tr></table>
  • opcion5-sports-event-registration/trunk/public/views/payment-bank-transfer.php

    r3642215 r3645531  
    1414?>
    1515<!DOCTYPE html>
    16 <html lang="es">
     16<html lang="<?php echo esc_attr( str_replace( '_', '-', get_bloginfo( 'language' ) ) ); ?>">
    1717<head>
    1818<meta charset="utf-8">
    1919<meta name="viewport" content="width=device-width, initial-scale=1">
    2020
    21 <title>Pago por transferencia</title>
     21<title><?php esc_html_e( 'Bank transfer payment', 'opcion5-sports-event-registration' ); ?></title>
    2222
    2323<?php
    … …  
    3535<div class="wpcr-box">
    3636
    37     <h1>Pago por transferencia bancaria</h1>
     37    <h1><?php esc_html_e( 'Bank transfer payment', 'opcion5-sports-event-registration' ); ?></h1>
    3838
    3939    <p>
    40         Hola <b><?php echo esc_html($participant['first_name']); ?></b>, 
    41         para confirmar tu inscripción en:
     40        <?php printf(
     41            /* translators: %s: participant first name */
     42            esc_html__( 'Hi %s, to confirm your registration for:', 'opcion5-sports-event-registration' ),
     43            '<b>' . esc_html( $participant['first_name'] ) . '</b>'
     44        ); ?>
    4245    </p>
    4346
    … …  
    4548
    4649    <p>
    47         <b>Importe a pagar:</b> <?php echo esc_html($amount); ?> €
     50        <b><?php esc_html_e( 'Amount to pay:', 'opcion5-sports-event-registration' ); ?></b> <?php echo esc_html($amount); ?> €
    4851    </p>
    4952
    5053    <hr style="margin:30px 0;border-color:#ccd9ff;">
    5154
    52     <p>Realiza la transferencia con los siguientes datos:</p>
     55    <p><?php esc_html_e( 'Make the transfer using these details:', 'opcion5-sports-event-registration' ); ?></p>
    5356
    5457    <ul class="wpcr-bank-list">
    … …  
    5861        </li>
    5962        <li>
    60             <strong>Beneficiario:</strong>
    61             <?php echo esc_html($event['bank_beneficiary'] ?? 'Organización del evento'); ?>
     63            <strong><?php esc_html_e( 'Payee:', 'opcion5-sports-event-registration' ); ?></strong>
     64            <?php echo esc_html($event['bank_beneficiary'] ?? __( 'Event organizer', 'opcion5-sports-event-registration' )); ?>
    6265        </li>
    6366        <li>
    64             <strong>Concepto:</strong>
     67            <strong><?php esc_html_e( 'Reference:', 'opcion5-sports-event-registration' ); ?></strong>
    6568            <?php echo esc_html($reference); ?>
    6669        </li>
    … …  
    6871
    6972    <div class="wpcr-payment-warning">
    70         ⚠️ La plaza no se confirma hasta que confirmemos la transferencia.
     73        ⚠️ <?php esc_html_e( 'Your place is not confirmed until we have received the transfer.', 'opcion5-sports-event-registration' ); ?>
    7174    </div>
    7275
    7376    <p class="wpcr-muted" style="margin-top:18px;">
    74         Puedes cerrar esta página. Te avisaremos cuando el pago sea validado.
     77        <?php esc_html_e( 'You can close this page. We will let you know once the payment has been validated.', 'opcion5-sports-event-registration' ); ?>
    7578    </p>
    7679
  • opcion5-sports-event-registration/trunk/public/wpcr-inscripcion-confirmada.php

    r3642215 r3645531  
    4040        <div class="wpcr-confirm-box wpcr-confirm-paid">
    4141            <div class="wpcr-confirm-icon">✅</div>
    42             <h1><?php esc_html_e( '¡Pago completado!', 'opcion5-sports-event-registration' ); ?></h1>
    43             <p><?php printf(
    44                 /* translators: %s nombre del participante */
    45                 esc_html__( 'Hola %s, tu inscripción está confirmada y el pago recibido.', 'opcion5-sports-event-registration' ),
     42            <h1><?php esc_html_e( 'Payment complete!', 'opcion5-sports-event-registration' ); ?></h1>
     43            <p><?php printf(
     44                /* translators: %s: participant first name */
     45                esc_html__( 'Hi %s, your registration is confirmed and your payment has been received.', 'opcion5-sports-event-registration' ),
    4646                '<strong>' . esc_html( $participant['first_name'] ) . '</strong>'
    4747            ); ?></p>
    … …  
    4949            <p class="wpcr-confirm-amount">
    5050                <?php echo esc_html( number_format( $amount, 2, ',', '.' ) ); ?> €
    51                 <span class="wpcr-confirm-badge wpcr-badge-paid"><?php esc_html_e( 'Pagado', 'opcion5-sports-event-registration' ); ?></span>
     51                <span class="wpcr-confirm-badge wpcr-badge-paid"><?php esc_html_e( 'Paid', 'opcion5-sports-event-registration' ); ?></span>
    5252            </p>
    5353        </div>
    … …  
    5858        <div class="wpcr-confirm-box">
    5959            <div class="wpcr-confirm-icon">🎉</div>
    60             <h1><?php esc_html_e( '¡Inscripción registrada!', 'opcion5-sports-event-registration' ); ?></h1>
    61             <p><?php printf(
    62                 /* translators: %s: nombre del participante */
    63                 esc_html__( 'Hola %s, tu inscripción en el evento está pendiente de pago.', 'opcion5-sports-event-registration' ),
     60            <h1><?php esc_html_e( 'Registration received!', 'opcion5-sports-event-registration' ); ?></h1>
     61            <p><?php printf(
     62                /* translators: %s: participant first name */
     63                esc_html__( 'Hi %s, your registration is awaiting payment.', 'opcion5-sports-event-registration' ),
    6464                '<strong>' . esc_html( $participant['first_name'] ) . '</strong>'
    6565            ); ?></p>
    6666            <h2 class="wpcr-confirm-event"><?php echo esc_html( $event['name'] ); ?></h2>
    6767            <p class="wpcr-confirm-amount">
    68                 <?php esc_html_e( 'Importe a abonar:', 'opcion5-sports-event-registration' ); ?>
     68                <?php esc_html_e( 'Amount to pay:', 'opcion5-sports-event-registration' ); ?>
    6969                <strong><?php echo esc_html( number_format( $amount, 2, ',', '.' ) ); ?> €</strong>
    7070            </p>
    … …  
    7272            <!-- DATOS BANCARIOS -->
    7373            <div class="wpcr-bank-box">
    74                 <h3>🏦 <?php esc_html_e( 'Datos para la transferencia', 'opcion5-sports-event-registration' ); ?></h3>
     74                <h3>🏦 <?php esc_html_e( 'Bank transfer details', 'opcion5-sports-event-registration' ); ?></h3>
    7575                <table class="wpcr-bank-table">
    7676                    <?php if ( $bank_name ) : ?>
    7777                    <tr>
    78                         <th><?php esc_html_e( 'Banco', 'opcion5-sports-event-registration' ); ?></th>
     78                        <th><?php esc_html_e( 'Bank', 'opcion5-sports-event-registration' ); ?></th>
    7979                        <td><?php echo esc_html( $bank_name ); ?></td>
    8080                    </tr>
    … …  
    8282                    <?php if ( $account_name ) : ?>
    8383                    <tr>
    84                         <th><?php esc_html_e( 'Titular', 'opcion5-sports-event-registration' ); ?></th>
     84                        <th><?php esc_html_e( 'Account holder', 'opcion5-sports-event-registration' ); ?></th>
    8585                        <td><?php echo esc_html( $account_name ); ?></td>
    8686                    </tr>
    … …  
    9999                    <?php endif; ?>
    100100                    <tr>
    101                         <th><?php esc_html_e( 'Concepto / Referencia', 'opcion5-sports-event-registration' ); ?></th>
     101                        <th><?php esc_html_e( 'Payment reference', 'opcion5-sports-event-registration' ); ?></th>
    102102                        <td class="wpcr-bank-ref"><?php echo esc_html( $reference ); ?></td>
    103103                    </tr>
    104104                    <tr>
    105                         <th><?php esc_html_e( 'Importe', 'opcion5-sports-event-registration' ); ?></th>
     105                        <th><?php esc_html_e( 'Amount', 'opcion5-sports-event-registration' ); ?></th>
    106106                        <td><strong><?php echo esc_html( number_format( $amount, 2, ',', '.' ) ); ?> €</strong></td>
    107107                    </tr>
    … …  
    109109
    110110                <div class="wpcr-bank-notice">
    111                     ⚠️ <?php esc_html_e( 'Indica siempre el concepto/referencia en la transferencia. Tu plaza quedará reservada durante 48 horas hasta confirmar el pago.', 'opcion5-sports-event-registration' ); ?>
     111                    ⚠️ <?php esc_html_e( 'Always include the payment reference in your transfer. Your place is held for 48 hours until the payment is confirmed.', 'opcion5-sports-event-registration' ); ?>
    112112                </div>
    113113            </div>
    … …  
    120120        <div class="wpcr-confirm-box">
    121121            <div class="wpcr-confirm-icon">🎉</div>
    122             <h1><?php esc_html_e( '¡Inscripción registrada!', 'opcion5-sports-event-registration' ); ?></h1>
    123             <p><?php printf(
    124                 /* translators: %s: nombre del participante */
    125                 esc_html__( 'Hola %s, completa el pago para confirmar tu plaza.', 'opcion5-sports-event-registration' ),
     122            <h1><?php esc_html_e( 'Registration received!', 'opcion5-sports-event-registration' ); ?></h1>
     123            <p><?php printf(
     124                /* translators: %s: participant first name */
     125                esc_html__( 'Hi %s, complete the payment to confirm your place.', 'opcion5-sports-event-registration' ),
    126126                '<strong>' . esc_html( $participant['first_name'] ) . '</strong>'
    127127            ); ?></p>
    128128            <h2 class="wpcr-confirm-event"><?php echo esc_html( $event['name'] ); ?></h2>
    129129            <p class="wpcr-confirm-amount">
    130                 <?php esc_html_e( 'Importe:', 'opcion5-sports-event-registration' ); ?>
     130                <?php esc_html_e( 'Amount:', 'opcion5-sports-event-registration' ); ?>
    131131                <strong><?php echo esc_html( number_format( $amount, 2, ',', '.' ) ); ?> €</strong>
    132132            </p>
    133133            <a class="wpcr-confirm-btn" href="<?php echo esc_url( $payment_url ); ?>">
    134                 💳 <?php esc_html_e( 'Pagar ahora', 'opcion5-sports-event-registration' ); ?>
     134                💳 <?php esc_html_e( 'Pay now', 'opcion5-sports-event-registration' ); ?>
    135135            </a>
    136136            <p class="wpcr-confirm-note">
    137                 <?php esc_html_e( 'La plaza no se confirma hasta completar el pago.', 'opcion5-sports-event-registration' ); ?>
     137                <?php esc_html_e( 'Your place is not confirmed until the payment is complete.', 'opcion5-sports-event-registration' ); ?>
    138138            </p>
    139139        </div>
    … …  
    145145    if ( ! empty( $participant['pair_code'] ) && ( $participant['pair_status'] ?? '' ) === 'open' ) :
    146146
    147         $pair_labels = [ 'male' => 'Pareja masculina', 'mixed' => 'Pareja mixta', 'female' => 'Pareja femenina' ];
    148         $pair_label  = $pair_labels[ $participant['pair_category'] ?? '' ] ?? 'Pareja';
     147        $pair_labels = [
     148            'male'   => __( "Men's pair", 'opcion5-sports-event-registration' ),
     149            'mixed'  => __( 'Mixed pair', 'opcion5-sports-event-registration' ),
     150            'female' => __( "Women's pair", 'opcion5-sports-event-registration' ),
     151        ];
     152        $pair_label = $pair_labels[ $participant['pair_category'] ?? '' ] ?? __( 'Pair', 'opcion5-sports-event-registration' );
    149153
    150154        $event_page_url = function_exists( 'wpcr_get_event_page_url' ) ? wpcr_get_event_page_url() : home_url( '/' );
    … …  
    153157            $event_page_url
    154158        );
    155         $pair_share_text = rawurlencode( '¡Nos hemos apuntado juntos a ' . $event['name'] . '! Completa tu inscripción como mi pareja aquí: ' . $pair_link );
     159        $pair_share_text = rawurlencode(
     160            sprintf(
     161                /* translators: 1: event name, 2: registration link */
     162                __( 'We have signed up together for %1$s! Complete your registration as my partner here: %2$s', 'opcion5-sports-event-registration' ),
     163                $event['name'],
     164                $pair_link
     165            )
     166        );
    156167    ?>
    157168        <div class="wpcr-confirm-box" style="margin-top:20px;border-top-color:#0d47a1;">
    158169            <div class="wpcr-confirm-icon">👥</div>
    159             <h1 style="font-size:22px !important;">Inscripción por parejas — <?php echo esc_html( $pair_label ); ?></h1>
    160             <p>Falta que tu pareja complete su inscripción. Comparte con él/ella este código:</p>
     170            <h1 style="font-size:22px !important;"><?php printf(
     171                /* translators: %s: pair category, for example "Mixed pair" */
     172                esc_html__( 'Pair registration — %s', 'opcion5-sports-event-registration' ),
     173                esc_html( $pair_label )
     174            ); ?></h1>
     175            <p><?php esc_html_e( 'Your partner still has to complete their registration. Share this code with them:', 'opcion5-sports-event-registration' ); ?></p>
    161176            <p style="font-size:30px;font-weight:800;letter-spacing:3px;color:#0d47a1;margin:10px 0;">
    162177                <?php echo esc_html( $participant['pair_code'] ); ?>
    163178            </p>
    164             <p>O envíale directamente el enlace de inscripción:</p>
     179            <p><?php esc_html_e( 'Or send them the registration link directly:', 'opcion5-sports-event-registration' ); ?></p>
    165180            <p style="word-break:break-all;font-size:13px;background:#f0f4ff;padding:10px;border-radius:6px;">
    166181                <?php echo esc_url( $pair_link ); ?>
    … …  
    168183            <a class="wpcr-confirm-btn" style="background:#25D366;"
    169184               href="https://wa.me/?text=<?php echo esc_attr( $pair_share_text ); ?>" target="_blank" rel="noopener">
    170                 📲 Enviar por WhatsApp
     185                📲 <?php esc_html_e( 'Send by WhatsApp', 'opcion5-sports-event-registration' ); ?>
    171186            </a>
    172187            <p class="wpcr-confirm-note">
    173                 Cada corredor paga su propia inscripción. La pareja quedará confirmada cuando ambos completéis el proceso.
    174                 También te hemos enviado el código por email.
     188                <?php esc_html_e( 'Each entrant pays for their own registration. The pair is confirmed once you have both completed the process. We have also emailed you the code.', 'opcion5-sports-event-registration' ); ?>
    175189            </p>
    176190        </div>
    … …  
    178192        <div class="wpcr-confirm-box" style="margin-top:20px;border-top-color:#1a9e3f;">
    179193            <div class="wpcr-confirm-icon">👥</div>
    180             <h1 style="font-size:22px !important;">¡Pareja completa!</h1>
    181             <p>Tu inscripción ha quedado vinculada correctamente con la de tu pareja
    182                (código <strong><?php echo esc_html( $participant['pair_code'] ); ?></strong>).
    183                Recordad que la plaza de cada uno se confirma con su propio pago.</p>
     194            <h1 style="font-size:22px !important;"><?php esc_html_e( 'Pair complete!', 'opcion5-sports-event-registration' ); ?></h1>
     195            <p><?php printf(
     196                /* translators: %s: pair code */
     197                esc_html__( 'Your registration is now linked to your partner\'s (code %s). Remember that each place is confirmed by its own payment.', 'opcion5-sports-event-registration' ),
     198                '<strong>' . esc_html( $participant['pair_code'] ) . '</strong>'
     199            ); ?></p>
    184200        </div>
    185201    <?php endif; ?>
  • opcion5-sports-event-registration/trunk/readme.txt

    r3642631 r3645531  
    55Tested up to: 7.0
    66Requires PHP: 7.4
    7 Stable tag: 1.0.1
     7Stable tag: 1.1.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    3333
    3434Every feature listed above is included and works with this plugin alone. There are no license keys, usage limits, trial periods or locked features of any kind.
     35
     36**Available in 6 languages**
     37
     38The plugin is written in English and ships with translations for Spanish, French, German, Italian and Portuguese. It picks the language configured in your WordPress site automatically, so both you and your entrants see it in your own language. The privacy policy template is intentionally left in its original wording, since it refers to Spanish data protection law and you should adapt it to your own country.
    3539
    3640**Shortcodes**
    … …  
    7983== Changelog ==
    8084
     85= 1.1.0 =
     86* The plugin is now translatable. Its interface is written in English and includes translations for Spanish, French, German, Italian and Portuguese, chosen automatically from your WordPress language setting.
     87* Dates and times now follow the format you set in Settings → General, instead of a fixed one.
     88* The privacy policy template is kept in its original wording on purpose: it refers to Spanish data protection law, so adapt it to your own country from the event Options tab.
     89
    8190= 1.0.1 =
    8291* Plugin renamed to "Sports Event Registration by CronoMeta".
    … …  
    8897== Upgrade Notice ==
    8998
     99= 1.1.0 =
     100The plugin is now available in six languages and follows your WordPress date format.
     101
    90102= 1.0.1 =
    91103Cosmetic release: new plugin name and corrected links in the extensions catalogue.
Note: See TracChangeset for help on using the changeset viewer.