Gettext con PHP su Windows 11
Ebbene sì, ce l'ho fatta, dopo solo 8 ore di tentativi e di debug, ho finalmente configurato gettext su Windows nel mio ambiente PHP locale!
Premessa importante: Nel mio caso ho usato Laragon e PHP 8.1.10 con Apache come Web Server. Potrebbe funzionare diversamente con altri ambienti come XAMP o WAMP, oppure con Nginx o altre versioni di PHP.
Tabella dei contenuti
- Preparare l'ambiente PHP
- Verificare le impostazioni di lingua di Windows: formato regionale
- Come funziona gettext con PHP su Windows
- Ottenere la lingua del browser dell'utente
- Tradurre automaticamente i file PHP con xgettext e msgfmt
- Modificare le variabili di ambiente del sistema di Windows
- Modificare la variabile di sistema PATH
- Aggiungere una nuova variabile con il percorso degli eseguibili di gettext
- Eseguire i comandi per compilare e aggiornare i file PO automaticamente
- Tradurre tutti i file PHP in un colpo solo
- Rimuovere le stringhe obsolete che non servono più
- Come tradurre manualmente con PoEdit
- Modificare i file PO con editor di testo
- Conclusioni
- Leggi anche
Preparare l'ambiente PHP
Anzitutto, è necessario abilitare l'estensione gettext di PHP, modificando php.ini:
# php.ini
extension=gettext
E poi verificare che sia attiva usando phpinfo:
// index.php
phpinfo();
L'output deve evidenziare che gettext è abilitato:
Potrebbe interessarti anche: Come usare il tracciamento lato server di Matomo
Verificare le impostazioni di lingua di Windows: formato regionale
So che sembrerà incredibile, ma l'uso di gettext dipende dal Formato Regionale della lingua che abbiamo impostato su Windows. Mi ci sono volute ore per arrivare a questa conclusione ma, almeno nel mio caso, è esattamente così.
Verifichiamo quale lingua abbiamo impostato:
Possiamo verificare i codici delle lingue nella documentazione Microsoft:
In genere, è sufficiente usare il codice a due lettere, "it" per Italiano, "en" per Inglese, e via dicendo. Tuttavia è possibile che sia necessario usare un codice più specifico, come "it-IT" oppure "en-US".
È utile notare che Windows usa il trattino, quindi ad esempio il francese è "fr-FR", mentre su Unix è "fr_FR".
Questo andrà usato per definire il locale in php tramite setlocale e, come vedremo, per il textdomain.
Come funziona gettext con PHP su Windows
In poche parole: Supponiamo di avere "Italiano" come Lingua Regionale (vedi sopra). Gettext cercherà i file MO all'interno della cartella "it" nel percorso che specificheremo in PHP con le funzioni sul textdomain:
$language = 'fr'; // Francesce, potrei anche impostare fr-FR
$domain = 'mydomain_' . $language; // Il dominio sarà mydomain_fr e quindi PHP cercherà il file mydomain_fr.mo
setlocale(LC_ALL, $language); // Imposto il locale a "fr"
bindtextdomain($domain, 'C:\laragon\usr\locale'); // PHP cercherà in questo percorso la cartella "it" basata sul codice della lingua del Formato Regionale di sistema
textdomain($domain);
bind_textdomain_codeset($domain, 'UTF-8');
# Tutti i miei file .mo dovranno quindi essere dentro a C:\laragon\usr\locale\it
È bene notare che su Windows usiamo LC_ALL, non abbiamo LC_MESSAGES e LC_TIME che sono per Unix. LC_ALL vale per tutti i locale, come indicato nella documentazione php di setlocale.
Ottenere la lingua del browser dell'utente
Se vogliamo ottenere la lingua dal browser dell'utente possiamo fare così:
$language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'], 0, 2);
Ricordando che questo è solo un header inviato dal browser, potrebbe non essere presente perciò è bene definire un default, ad esempio "en".
Con questa configurazione, dovremo avere tutti i file .mo seguendo questa struttura:
C:\laragon\
- usr
- locale
- it (codice della lingua per il formato regionale di sistema)
.\mydomain_fr.mo ---> questo è il file che verrà caricato perché ho impostato setlocale() a "fr"
.\mydomain_it.mo
.\mydomain_es.mo
Possiamo modificare il $domain per usare un file MO per un altro progetto (myotherdomain_fr.mo) oppure modificare $language per usare il file MO per lo stesso progetto ma per una lingua diversa (mydomain_es.mo).
Tradurre automaticamente i file PHP con xgettext e msgfmt
Questo passaggio è opzionale, utile solo se abbiamo bisogno di compilare e aggiornare automaticamente i file PO sulla base delle nuove stringhe che inseriamo nel nostro sito o applicativo PHP.
Anche su Windows possiamo fare le stesse operazioni che facciamo con i sistemi Unix usando xgettext e msgfmt.
Tutte le stringhe usate con la funzione php gettext() oppure con il suo alias _() saranno configurate in un file PO per poter essere tradotte, questo in modo automatico che ci permetterà di risparmiare molto tempo!
Esempio di stringhe traducili automaticamente:
# index.php
_('Titolo della pagina')
_('Il mio testo')
gettext('Altro testo')
Vediamo come fare:
- Scarichiamo gettext per Windows dal repository Github di Michele Locali, che ringraziamo. Se il download non dovesse funzionare, ho caricato l'ultima release di gettext qui: gettext 0.21 Windows
- In alternativa, possiamo installare Poedit, un software open source che offre un'interfaccia grafica per modificare i file PO e generare i file MO
- Nella cartella di installazione di Poedit troveremo GettextTools\bin dove abbiamo i file eseguibili per Windows (.exe.) proprio per xgettext e msgfmt
- A questo punto, configuriamo il percorso nelle variabili di sistema di Windows, qui sotto ci sono gli screenshot con i passi da seguire:
- Cerchiamo su Windows la Modifica delle variabili di ambiente del sistema
- Clicchiamo poi su Variabili d'ambiente
- Nelle variabili di sistema, in basso, modifichiamo Path, oppure aggiungiamolo se assente
- Inseriamo una nuova variabile con il percorso che porta a Poedit\GettextTools\bin
In pratica, le variabili Path servono per rendere eseguibili da terminale, da qualsiasi punto, determinati comandi come appunto xgettext e msgfmt, oppure ad esempio cwebp se usiamo le librerie WebP, o ffmpeg se vogliamo convertire un video.
Modificare le variabili di ambiente del sistema di Windows
Usando la ricerca di Windows possiamo aprire lo strumento di modifica delle variabili di sistema:
Modificare la variabile di sistema PATH
Modifichiamo la variabile Path per il nostro sistema, oppure per il nostro utente se preferiamo:
Aggiungere una nuova variabile con il percorso degli eseguibili di gettext
Ora aggiungiamo una nuova variabile per il Path, inserendo il percorso dove si trovano gli eseguibili di gettext:
Devo dare credito all'utente Desintegr da StackOverflow grazie al quale ho scoperto che gli eseguibili Windows erano nella cartella GettextTools di Poedit.
Eseguire i comandi per compilare e aggiornare i file PO automaticamente
Dopo aver fatto queste operazioni per impostare a livello globale xgettext e msgfmt, passiamo al passo successivo. In realtà potremmo anche lanciare gli eseguibili direttamente dalla cartella di Poedit, ma è molto più comodo impostare le variabili d'ambiente.
Se abbiamo optato per PoEdit, l'eseguibile msgattrib non è presente nei GettextTools. Per poterlo usare, dobbiamo scaricare gettext per Windows dal repository Github di Michele Locali come già indicato. In questo caso, la variabile d'ambiente per Path dovrà puntare al percorso dove abbiamo messo gettext, ad esempio C:\Programs\gettext
REM Per tradurre un singolo file, in questo caso un file PHP
xgettext -d it -p \path\to\project\locale\it -L PHP --from-code=UTF-8 --no-location -j \path\to\project\index.php
REM Per compilare gli MO
msgfmt -D \path\to\project\locale\it it.po -o \path\to\project\locale\it\LC_MESSAGES\mydomain_it.mo
Nota bene: per essere rilevate da gettext, le stringhe devono essere usate con la funzione gettext() oppure il suo alias _(). Vedi la documentazione di gettext.
Se appare un errore "No such file or directory" per il file PO, probabilmente è perché dobbiamo prima creare il file PO, non viene creato da gettext, deve già esistere. Possiamo crearlo come un semplice file di testo oppure usare PoEdit.
Tradurre tutti i file PHP in un colpo solo
Possiamo modificare questo codice secondo le nostre esigenze e salvarlo come file .bat:
@echo off
REM qui possiamo omettere count e pause se non ci servono
SET count=1
REM chiedo la lingua all'utente, tipo it o fr
SET /p language=Scegli lingua:
FOR /f "tokens=*" %%G IN ('dir *.php /b /s') DO (call :subroutine "%%G")
pause
GOTO :eof
:subroutine
REM qui possiamo omettere anche i comandi echo e set
echo %count%:%1
REM aggiungiamo --no-location se vogliamo rimuovere dal file PO il percorso dove è stata trovata la traduzione
xgettext.exe -j %1 -d %language% -p \path\to\locale\it -L PHP --from-code=UTF-8
set /a count+=1
GOTO :eof
Questo file BAT salverà in \path\to\locale\it il file it.po con tutte le stringhe pre-compilate per tutti i file PHP trovati. Possiamo anche omettere "pause" se vogliamo che il terminale si chiuda in automatico quando ha terminato.
Nota bene: in questo script viene eseguita una ricerca su tutti i file PHP nella cartella e nelle sottocartelle, a partire dal percorso dove è presente il file BAT. Se vuoi quindi ampliare o restringere la ricerca, puoi spostare il file BAT di livello.
Ringrazio la documentazione del ciclo FOR per CMD su SS64 perché non riuscivo a far funzionare correttamente il loop, poi ho trovato questa pagina e ho capito che dovevo usare le subroutine.
Il count è solo un'utilità per avere un output più ordinato nel prompt dei comandi.
Aggiungiamo --no-location se vogliamo rimuovere dal file PO il percorso dove è stata trovata la traduzione.
Il seguente eventuale errore può essere ignorato, è probabilmente causato dall'uso dell'attributo -j all'interno del ciclo for.
Charset "CHARSET" is not a portable encoding name. Message conversion to user's charset might not work.
Compilare i file MO in automatico
Se non vogliamo usare PoEdit per creare il MO e preferiamo farlo automaticamente, possiamo salvare questo codice in un file BAT, dopo averlo regolato sulle nostre esigenze:
@echo off
REM chiedo all'utente la lingua, tipo en o fr, e il dominio, ad esempio "my_project"
SET /p language=Scegli la lingua:
SET /p domain=Scegli il dominio:
msgfmt -D \path\to\locale\%language% %language%.po -o \path\to\locale\%language%\LC_MESSAGES\mydomain_%language%.mo
Possiamo rimuovere gli input per la lingua e il dominio se vogliamo. Ringrazio Instantsoup su StackOverflow per l'idea.
Rimuovere le stringhe obsolete che non servono più
REM Rimuoviamo le stringhe non più necessarie
REM 1) Creiamo il PO
SET /p sourcelanguage=Inserisci la lingua di riferimento:
SET /p destinationlanguage=Inserisci la lingua di destinazione:
xgettext -d %sourcelanguage% -p \path\to\project\locale\%sourcelanguage% -L PHP --from-code=UTF-8 --no-location -j file.php
REM 2) Make a diff between it.po and en.po, then mark as obsolete the extra strings that are not inside it.po
msgattrib --set-obsolete --ignore-file=\path\to\project\locale\%sourcelanguage%.po -o \path\to\project\locale\%destinationlanguage%.po \path\to\project\locale\%destinationlanguage%.po
REM 3) Remove obsolete strings
msgattrib --no-obsolete -o \path\to\project\locale\%destinationlanguage%.po \path\to\project\locale\%destinationlanguage%.po
Salviamo il codice come file .bat dopo averlo sistemato sulle nostre esigenze.
In pratica, dopo aver creato il file PO, lanciamo msgattrib che troverà le stringhe che sono presenti su (ad esempio) en.po ma non su it.po, marchiamo quelle in più come obsolete, e poi le rimuoviamo.
Teniamo presente che se msgattrib trova delle stringhe non tradotte, cioè dove msgstr è vuoto, le rimuoverà.
Come tradurre manualmente con PoEdit
È chiaro che prima o poi dovremo anche tradurre i testi, oppure consegnarli ai traduttori.
I file PO (Portable Object) sono "human-readable" cioè possiamo aprirli con qualsiasi editor di testo e modificarli. I file MO (Machine Object) sono invece leggibili solo dal computer, sono file binari. Inoltre, i file POT (Portable Object Template) sono dei modelli di partenza per creare i file PO, ma sono facoltativi.
Ecco un esempio di file PO:
# it.po
msgid "This is a title"
msgstr "Questo è un titolo"
msgid "Some other text"
msgstr "Dell'altro testo"
Quindi abbiamo il msgid che indica la stringa da tradurre, e il msgstr che indica la traduzione effettiva nella lingua che ci interessa.
Possiamo modificare questi PO via editor oppure con PoEdit: attenzione che su PoEdit possiamo solo modificare le traduzioni finali, non le stringhe originali:
In alto abbiamo le stringhe presenti nel PO, in basso abbiamo il testo sorgente (msgid) e la traduzione (msgstr).
A destra abbiamo anche le traduzioni suggerite, che sono però una funzionalità a pagamento.
Salviamo il file PO e automaticamente sarà generato il file MO nella stessa cartella. Possiamo anche generare il solo file MO andando su File > Compila MO
Modificare i file PO con editor di testo
Come accennato, possiamo modificare i file PO con qualsiasi editor. Io ad esempio uso VSCodium e ho installato anche l'estensione gettext per evidenziare la sintassi dei file PO. Potremmo usare anche Notepad++ o il buon vecchio Blocco Note.
Dopo aver fatto le modifiche, possiamo salvare il file PO sovrascrivendo il precedente. Per convertirlo in MO però dovremo usare PoEdit oppure lanciare il comando msgfmt.
Conclusioni
L'importante è che tutti i file MO siano dentro la stessa cartella, definita dalla lingua regionale di Windows!
Se questo articolo ti è stato di aiuto, seguimi su Facebook e Youtube! Lascia un commento qui sotto per farmi sapere cosa ne pensi o se hai avuto qualche difficoltà nella configurazione di gettext!
Leggi anche
- Come convertire i file WEM in audio riproducibili OGG e MP3
- Come unire più video in modo facile e veloce con FFMPEG
Se ti va, sostieni il mio lavoro. Crediti per l'immagine di anteprima: Unsplash