Come far funzionare Gettext con PHP su Windows 11

Come far funzionare Gettext con PHP su Windows 11

Pubblicato il: 13/12/2022
Aggiornato il: 16/01/2023
Categorie
Tag

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.

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:

Gettext abilitato su phpinfo
Gettext abilitato su phpinfo

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:

Formato regionale della lingua su Windows 11
Formato regionale della lingua su Windows 11

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:

  1. 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
  2. In alternativa, possiamo installare Poedit, un software open source che offre un'interfaccia grafica per modificare i file PO e generare i file MO
  3. Nella cartella di installazione di Poedit troveremo GettextTools\bin dove abbiamo i file eseguibili per Windows (.exe.) proprio per xgettext e msgfmt
  4. A questo punto, configuriamo il percorso nelle variabili di sistema di Windows, qui sotto ci sono gli screenshot con i passi da seguire:
    1. Cerchiamo su Windows la Modifica delle variabili di ambiente del sistema
    2. Clicchiamo poi su Variabili d'ambiente
    3. Nelle variabili di sistema, in basso, modifichiamo Path, oppure aggiungiamolo se assente
    4. 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:

Come trovare lo strumento di modifica delle Variabili di Sistema di Windows
Modifica delle Variabili di Sistema di Windows
Proprietà di sistema: Variabili d'ambiente
Proprietà di sistema: Variabili d'ambiente

Modificare la variabile di sistema PATH

Modifichiamo la variabile Path per il nostro sistema, oppure per il nostro utente se preferiamo:

Variabili di sistema: path
Variabili di sistema: Path

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:

Aggiungere una nuova variabile di ambiente per gettext
Aggiungere una nuova variabile di ambiente per 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:

Interfaccia di Poedit
Interfaccia di Poedit

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!

Se ti va, sostieni il mio lavoro. Crediti per l'immagine di anteprima: Unsplash

Leave a comment

I commenti dovranno essere approvati prima della pubblicazione. Potrebbero apparire dopo diverse ore.

Puoi usare un nome casuale, è utile per permettermi almeno di risponderti. Se scegli di lasciare la tua email, potrai ricevere una notifica quando rispondo al tuo commento.

Nessun commento è stato ancora inviato. Inizia la discussione condividendo la tua opinione!

*