venerdì 10 ottobre 2025

Come tradurre le stringhe dei plugin? Ecco una soluzione definitiva

Vi è mai capitato di non riuscire a tradurre una stringa di un plugin Wordpress perchè la funzione _e() si trova all'interno di una funzione php non accessibile tramite un hook?

Sto sperimentando una soluzione molto efficace!

Ecco una classe per gestire le traduzioni dei plugin redirezionandole sul file .po del Vostro tema.

PHP
<?php
/**
 * Classe per reindirizzare le traduzioni WooCommerce al dominio del tema (myTheme).
 */
class MyTheme_Translations {

    /**
     * Dominio del tema da cui prendere le traduzioni.
     */
    private const THEME_DOMAIN = 'myTheme';

    /**
     * Domini di WooCommerce da intercettare.
     */
    private const WC_DOMAINS = [
        'woocommerce',
        'woocommerce-admin',
        'woocommerce-payments',
    ];

    /**
     * Inizializza i filtri.
     */
    public static function init(): void {
        add_filter('gettext', [__CLASS__, 'gettext'], 10, 3);
        add_filter('gettext_with_context', [__CLASS__, 'gettext_with_context'], 10, 4);
        add_filter('ngettext', [__CLASS__, 'ngettext'], 10, 5);
        add_filter('ngettext_with_context', [__CLASS__, 'ngettext_with_context'], 10, 6);
    }

    public static function gettext($translated, $text, $domain) {
        if (!in_array($domain, self::WC_DOMAINS, true)) {
            return $translated;
        }

        $alt = translate($text, self::THEME_DOMAIN);
        return ($alt !== $text) ? $alt : $translated;
    }

    public static function gettext_with_context($translated, $text, $context, $domain) {
        if (!in_array($domain, self::WC_DOMAINS, true)) {
            return $translated;
        }

        $alt = translate_with_gettext_context($text, $context, self::THEME_DOMAIN);
        return ($alt !== $text) ? $alt : $translated;
    }

    public static function ngettext($translated, $single, $plural, $number, $domain) {
        if (!in_array($domain, self::WC_DOMAINS, true)) {
            return $translated;
        }

        $alt = translate_nooped_plural([$single, $plural], $number, self::THEME_DOMAIN);
        return ($alt !== $single && $alt !== $plural) ? $alt : $translated;
    }

    public static function ngettext_with_context($translated, $single, $plural, $number, $context, $domain) {
        if (!in_array($domain, self::WC_DOMAINS, true)) {
            return $translated;
        }

        // _nx() gestisce plural + context.
        $alt = _nx($single, $plural, $number, $context, self::THEME_DOMAIN);
        return ($alt !== $single && $alt !== $plural) ? $alt : $translated;
    }
}

// Avvia la classe
MyTheme_Translations::init();

Cos'è e a cosa serve

La classe MyTheme_Translations reindirizza le traduzioni di WooCommerce al text domain del tema (myTheme). In pratica, se nel tuo tema hai file di traduzione (.po/.mo) con stringhe alternative, la classe fa in modo che WooCommerce usi quelle al posto delle sue predefinite—senza toccare i file di WooCommerce.

Come funziona

Aggancia i filtri di WordPress per la localizzazione: gettext, gettext_with_context, ngettext, ngettext_with_context.

Intercetta solo i domini: woocommerce, woocommerce-admin, woocommerce-payments (puoi configurarli in WC_DOMAINS).

Per ogni stringa intercettata:

  • prova a ritradurla usando il dominio del tema (myTheme);
  • se trova un’alternativa diversa, la restituisce;
  • altrimenti lascia la traduzione originale di WooCommerce (fallback sicuro).
  • Gestisce correttamente contesto e plurali (es. _nx()).

Perché è utile

In questo modo centralizzi gli override delle stringhe di WooCommerce (o di qualsiasi altro plugin) nel tuo tema (o child-theme), eviti di modificare o “forkare” i file di traduzione di WooCommerce e mantieni la compatibilità con aggiornamenti e supporti plurali/contesti perchè non sei costretto modificare il codice del plugin.

La costante WC_DOMAINS Non tocca stringhe di altri plugin/temi fuori dai domini elencati, naturalmente potete rinominarla come preferite e aggiungere i valori che volete.

Non cambia nulla se nel tema manca la traduzione alternativa.

Requisito fondamentale!

Il tema deve caricare il proprio text domain (es. load_theme_textdomain('myTheme', get_template_directory() . '/languages');).

Le stringhe da sovrascrivere devono essere presenti nei file /languages/myTheme-XX_YY.mo del tema.

Come usarla

Includi la classe (ad es. nel functions.php o in un piccolo plugin) e chiama MyTheme_Translations::init();.

Se usi un text domain reale diverso da myTheme, aggiorna la costante THEME_DOMAIN.

Un piccolo consiglio

Se hai problemi di performance con il tuo sito e cerchi un servizio di hosting che supporta sistemi di cache lato infrastruttura come Redis, Memcached o Varnish, ti consiglio questo servizio di hosting:

Load WordPress Sites in as fast as 37ms!

sabato 27 settembre 2025

Siti multilingua in Wordpress e performance

Wordpress non gestisce nativamente la funzionalità multilingua ma è possibile realizzare il sito vs sito in più lingue utilizzando vari approcci differenti:

Approcci possibili

  1. Multisite (una installazione per lingua)
    Crei un network WordPress e un “sito” per ogni lingua (es. example.com/itexample.com/en).
    Pro: isolamento pulito dei contenuti, performance prevedibili, permessi separati, plugin/tema configurabili per lingua.
    Contro: manutenzione più articolata (backup, utenti, plugin), relazioni tra contenuti manuali se non supportate da plugin dedicati.

  2. Plugin multilingua su singolo sito
    Tutte le lingue convivono nella stessa installazione. Opzioni più diffuse:

    • Polylang: leggero, ottimo per blog/siti corporate; estensione a pagamento per WooCommerce.

    • WPML: completo, include traduzione stringhe, media, workflow; più “pesante”.

    • TranslatePress: traduzione visuale dal front-end, supporto MT (da rivedere/redigere).
      Pro: una sola installazione da gestire, linking tra traduzioni semplice.
      Contro: lock-in da plugin, possibili impatti su performance in siti molto grandi.

  3. Multisite + plugin di collegamento (es. MultilingualPress)
    Ibrido: benefici di Multisite con collegamento “nativo” tra versioni tradotte.
    Pro: scalabilità, nessun contenuto duplicato nel DB di un solo sito.
    Contro: costo/licenza, setup più tecnico.

  4. Headless/Jamstack
    WordPress solo come CMS; front-end (Next.js/Nuxt) gestisce i18n.
    Pro: massima libertà su UX/performance, routing i18n su misura.
    Contro: richiede team con competenze da sviluppo front-end moderno e DevOps.


Come scegliere (decision quick-guide)

  • Sito piccolo/medio senza e-commerce: Polylang o TranslatePress.

  • Sito complesso o con molto flusso redazionale: WPML (workflow, TM, stringhe).

  • Portale enterprise o network di brand/paesi: Multisite (con o senza MultilingualPress).

  • Team con skill React e focus performance/SEO avanzata: Headless.


Struttura URL e SEO internazionale

  • Struttura consigliata: sottocartelle per lingua (/it//en/, …). In alternativa sottodomini (it.example.com) o domini ccTLD. Mantieni una sola strategia ovunque.

  • hreflang: obbligatorio per evitare contenuti duplicati tra lingue e far capire a Google la variante corretta. I plugin citati lo gestiscono; verifica anche la versione “x-default”.

  • Sitemap per lingua: meglio separare ed elencarle in robots.txt.

  • Slug, title e meta tradotti: non lasciare slugs inglesi su pagine italiane e viceversa.

  • Redirect iniziale per lingua del browser: se lo usi, fallo solo alla prima visita, salva preferenza (cookie) e non indicizzare la pagina di redirect.

  • Contenuti davvero localizzati: evita traduzioni “letterali”; adatta esempi, misure, valute, date.


Note per WooCommerce

  • Lingua ≠ valuta: valuta, tasse e spedizioni possono variare per paese, non solo per lingua. Valuta e tasse vanno gestite con plugin specifici (multi-currency, geolocalizzazione).

  • Prodotti e attributi: traduci titoli, descrizioni, attributi e termini (paio/pack, taglie, materiali).

  • URL prodotto e categorie: traduci slug e ricontrolla i 301 se cambi struttura.

  • Email transazionali: predisponi template per lingua (ordine, spedizione, resi).

  • Ricerca e filtri: assicurati che l’indice (es. Elastic/Algolia) sia separato per lingua o includa il campo lingua nei documenti.


Workflow di traduzione

  • Glossario e guida di stile per coerenza terminologica.

  • Processo a step: bozza → traduzione → revisione → QA (link, moduli, formati numeri/date).

  • Stringhe di tema/plugin: usa .po/.mo (Loco Translate) o il modulo “String Translation” del tuo plugin i18n.

  • Media: valuta se duplicare o riutilizzare; in molti casi le stesse immagini bastano, ma cura i testi ALT per lingua.


Performance e caching

  • Cache per lingua: varia la cache per path (/it//en/) e per cookie se usi redirect alla prima visita.

  • CDN: regole di purge e invalidation consapevoli della lingua.

  • Ricorda il Vary: evita di variare su Accept-Language lato server quando non serve, per non esplodere la cache.


Checklist di avvio rapido

  1. Scegli struttura URL e plugin/architettura.

  2. Definisci lingue, glossario e stile.

  3. Imposta hreflang, sitemap per lingua e meta.

  4. Traduci menu, widget, footer, form, email.

  5. Traduci slugs e controlla i 301.

  6. Configura cache/CDN per lingua.

  7. QA completo (contenuti, moduli, pagamenti, ricerca).

  8. Monitora Search Console per ogni lingua/property.


Errori comuni da evitare

  • Mescolare sottocartelle e sottodomini senza motivo.

  • Lasciare meta/slug non tradotti.

  • Redirect forzato ad ogni visita (esperienza pessima + problemi di indicizzazione).

  • Non separare la cache per lingua.

  • Affidarsi solo alla MT senza revisione umana.


Performance

Un unico sito multilingua rispetto ad un'installazione network può creare problemi di performance, soprattutto se sono installati altri plugin (Woocommerce, Advanced Custom Fields, etc..) per questo consiglio di valutare bene prima di effettuare una scelta o si dovrà ritornare sui propri passi.

Nel caso il sito diventi molto lento sarà opportuno, se non è possibile ottimizzare il codice, ricorrere a dei sistemi di cache (Memcached, Redis, Varnish su tutti).

Un servizio di hosting che supporta tutti questi servizi elencati sopra è Cloudways, vivamente consigliato!

Load WordPress Sites in as fast as 37ms!

Usare i file pot/po per tradurre le stringhe del sito

La traduzione delle stringhe del vostro sito web Worpress è basata su un formato noto come gettext, il codice dichiara una stringa e un text domain; a runtime WordPress cerca la versione tradotta nei file .mo per la locale attiva (es. it_IT).

Ciclo di vita di una stringa

  1. Marcatura nel codice → usi le funzioni gettext.
  2. Estrazione → generi un file .pot (template) con tutte le stringhe.
  3. Traduzione → crei i .po per ogni lingua a partire dal .pot.
  4. Compilazione → i .po diventano .mo (binari veloci).
  5. Caricamento → WordPress carica i .mo del tuo text domain.
Queste stringhe vengono tradotte tramite delle funzioni php.

Marcatura nel codice (PHP)

Le funzioni principali:

__( 'Testo', 'mio-dominio' );              // restituisce la stringa
_e( 'Testo', 'mio-dominio' );              // echo diretto
_x( 'Testo', 'contesto', 'mio-dominio' );  // con contesto
_n( '1 articolo', '%s articoli', $n, 'mio-dominio' );     // plurali
_nx( 'file', 'file', $n, 'contesto', 'mio-dominio' );     // plurali + contesto

// Sempre con escaping adeguato:
esc_html__( 'Testo', 'mio-dominio' );
esc_attr__( 'Testo', 'mio-dominio' );

Come generare i file

Poedit

Per generare/gestire questi file è necessario utilizzare dei software appositi, uno di questi è Poedit, questo software ha una interfaccia visuale tramite cui gestire 



Loco Translate 

Esiste anche un utile plugin, Loco Translate (per tradurre le stringhe, non i contenuti)

Loco Translate non è un plugin multilingua: serve a tradurre le stringhe di tema e plugin (menu, etichette, messaggi, email) tramite file .po/.mo e .json per gli script. È ideale per coprire testi dell’interfaccia non gestiti dai plugin i18n o per rifinire la terminologia.



La mia scelta: WP-CLI e un po' di BASH

Personalmente preferisco un approccio programmatico tramite WP-CLI e bash scripting.

Ecco un esempio di uno script che sto usando in questi giorni per un tema personalizzato:

#!/usr/bin/env bash
set -e  # interrompe lo script se un comando fallisce

source ~/.bashrc
LANG_PATH="$MYTHEME_THEME_PATH/languages" #cartella con i file delle traduzioni
POT_FILE="$LANG_PATH/mytheme.pot" # file del template generato 
PO_FILE="$LANG_PATH/it_IT.po" #file con le traduzioni da inserire

# 1. Genera il template .pot dal tema
wp i18n make-pot "$MYTHEME_THEME_PATH" "$POT_FILE" --exclude=node_modules,vendor,tests --domain=mytheme

# 2. Se non esiste già il .po, inizializzalo
if [ ! -f "$PO_FILE" ]; then
  msginit --no-translator --locale=it_IT --input="$POT_FILE" --output-file="$PO_FILE"
fi

# 3. Aggiorna il .po con le nuove stringhe dal .pot
msgmerge --update "$PO_FILE" "$POT_FILE"

# 4. Compila i .mo
wp i18n make-mo "$LANG_PATH"

Lo script scansionerà quindi la vostra cartella verificando la presenza di stringhe relative al vostro tema e genererà i file dove potrete inserire le traduzioni.
Lanciandolo nuovamnete sarà anche generato il file mo. (quello che viene effettivamente letto da Wordpress per la traduzione) basato sulle traduzioni inserite nel .po.


Soluzioni Multilingua

In questa pagina ho scritto un articolo sui possibili approcci di un sito multilingua e dei relativi problemi di performance. Se vi dovessero capitare vi consiglio di rivedere il codice del Vs. sito ed i plugin che state usando. Se proprio non potete cambiare nulla potete appoggiarvi ad un hosting che supporti efficaci servizi di hosting, io uso questo:


Load WordPress Sites in as fast as 37ms!