Mise à jour de travail sur le portage de la version 'Ovh' vers le VPS. Nombreuses modifs à tous niveaux (donc non tracées...)
This commit is contained in:
+243
-19
@@ -8,18 +8,181 @@
|
||||
// - Les nouvelles chaînes (trouvées dans le code, absentes de en.php)
|
||||
// sont ajoutées avec une valeur vide, à traduire manuellement.
|
||||
// - Les entrées obsolètes (présentes dans en.php, plus utilisées dans le
|
||||
// code) sont déplacées en fin de fichier, en commentaire - à supprimer à
|
||||
// la main une fois vérifié qu'elles ne servent vraiment plus à rien.
|
||||
// code) sont déplacées en fin de fichier, dans un bloc de commentaire.
|
||||
// Ce bloc est RELU à chaque passage : le script n'y fait qu'AJOUTER les
|
||||
// entrées nouvellement obsolètes. Une entrée n'en sort que si vous la
|
||||
// supprimez à la main, ou si son texte est de nouveau utilisé dans le code
|
||||
// (elle retrouve alors sa traduction).
|
||||
// - Les entrées sont triées par ordre alphabétique du texte français
|
||||
// (insensible à la casse et aux accents), pour que deux textes presque
|
||||
// identiques (typiquement une coquille) se retrouvent l'un à côté de l'autre.
|
||||
// - Contrôle des espaces réservés : pour chaque traduction non vide, les jetons
|
||||
// [[Nom]], [[Email]]... et les formats %s, %d... doivent être ceux du texte
|
||||
// français (ils ne se traduisent pas). Simple avertissement à chaque passage,
|
||||
// qui ne bloque rien. Les crochets simples ([Owner]...) ne sont PAS contrôlés :
|
||||
// ce sont des libellés affichés, que l'on traduit.
|
||||
// - Chaque entrée occupe deux lignes : le texte français, puis '=>' et la
|
||||
// traduction sur la ligne suivante, précédé d'une tabulation.
|
||||
//
|
||||
// Limite connue et acceptée (même limite que qsTr()/gettext) : ne détecte
|
||||
// que les chaînes littérales simples (pas de concaténation dynamique).
|
||||
// Pour du texte avec contenu variable, utiliser un espace réservé :
|
||||
// sprintf(t("Bonjour %s"), $nom)
|
||||
// plutôt que t("Bonjour " . $nom) - la seconde forme ne sera pas détectée.
|
||||
//
|
||||
// Usage : php extraire_traductions.php [racine] (racine par défaut : /var/www)
|
||||
|
||||
$racine = "/var/www";
|
||||
$racine = $argv[1] ?? "/var/www";
|
||||
$fichier_en = "$racine/config/lang/en.php";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Clé de tri : minuscules, sans accents, sans balises HTML, espaces réduits,
|
||||
// sans ponctuation en tête. Sert UNIQUEMENT à ordonner : le texte réellement
|
||||
// stocké dans en.php reste celui du code, inchangé.
|
||||
// ----------------------------------------------------------------------------
|
||||
function cle_de_tri($texte)
|
||||
{
|
||||
static $table = [
|
||||
'à'=>'a','á'=>'a','â'=>'a','ä'=>'a','ã'=>'a','å'=>'a',
|
||||
'À'=>'a','Á'=>'a','Â'=>'a','Ä'=>'a','Ã'=>'a','Å'=>'a',
|
||||
'ç'=>'c','Ç'=>'c',
|
||||
'è'=>'e','é'=>'e','ê'=>'e','ë'=>'e',
|
||||
'È'=>'e','É'=>'e','Ê'=>'e','Ë'=>'e',
|
||||
'ì'=>'i','í'=>'i','î'=>'i','ï'=>'i',
|
||||
'Ì'=>'i','Í'=>'i','Î'=>'i','Ï'=>'i',
|
||||
'ñ'=>'n','Ñ'=>'n',
|
||||
'ò'=>'o','ó'=>'o','ô'=>'o','ö'=>'o','õ'=>'o',
|
||||
'Ò'=>'o','Ó'=>'o','Ô'=>'o','Ö'=>'o','Õ'=>'o',
|
||||
'ù'=>'u','ú'=>'u','û'=>'u','ü'=>'u',
|
||||
'Ù'=>'u','Ú'=>'u','Û'=>'u','Ü'=>'u',
|
||||
'ý'=>'y','ÿ'=>'y','Ý'=>'y',
|
||||
'œ'=>'oe','Œ'=>'oe','æ'=>'ae','Æ'=>'ae',
|
||||
'’'=>"'", '‘'=>"'", "\u{00A0}"=>' ',
|
||||
];
|
||||
$cle = strtr($texte, $table);
|
||||
$cle = strtolower($cle); // ASCII seulement : les accents sont déjà traités
|
||||
$cle = strip_tags($cle);
|
||||
$cle = preg_replace('/\s+/', ' ', $cle) ?? $cle;
|
||||
$cle = trim($cle);
|
||||
$cle = preg_replace('/^[^\p{L}\p{N}]+/u', '', $cle) ?? $cle;
|
||||
return $cle;
|
||||
}
|
||||
|
||||
// Tri par texte français ; à clé égale, ordre des octets pour rester déterministe.
|
||||
function trier_par_texte(array $tableau)
|
||||
{
|
||||
$cles = [];
|
||||
foreach ($tableau as $texte => $_) $cles[$texte] = cle_de_tri((string)$texte);
|
||||
uksort($tableau, function ($a, $b) use ($cles) {
|
||||
$c = strcmp($cles[$a], $cles[$b]);
|
||||
return $c !== 0 ? $c : strcmp((string)$a, (string)$b);
|
||||
});
|
||||
return $tableau;
|
||||
}
|
||||
|
||||
// Une entrée sur deux lignes : le texte, puis TAB + '=> traduction,'
|
||||
function formater_entree($cle, $valeur)
|
||||
{
|
||||
return " " . var_export($cle, true) . "\n\t=> " . var_export($valeur, true) . ",\n";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Contrôle des espaces réservés (jetons [[...]] et formats de sprintf)
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Jetons [[Nom]] et formats %s, %d, %1$s, %.2f... trouvés dans un texte.
|
||||
function extraire_jetons($texte)
|
||||
{
|
||||
preg_match_all('/\[\[[^\]\r\n]+\]\]/u', $texte, $m1);
|
||||
$sans_pourcent_litteral = str_replace('%%', '', $texte); // '%%' = un vrai signe %
|
||||
preg_match_all('/%(?:\d+\$)?[-+0]*\d*(?:\.\d+)?[bcdeEfFgGosuxX]/', $sans_pourcent_litteral, $m2);
|
||||
return ['reserves' => $m1[0], 'formats' => $m2[0]];
|
||||
}
|
||||
|
||||
// Compare deux listes en ignorant l'ordre. Renvoie [manquants, en trop].
|
||||
function ecart_jetons(array $attendus, array $obtenus)
|
||||
{
|
||||
$manquants = []; $en_trop = [];
|
||||
$ca = array_count_values($attendus);
|
||||
$co = array_count_values($obtenus);
|
||||
foreach ($ca as $j => $n) if (($co[$j] ?? 0) < $n) $manquants[] = $j;
|
||||
foreach ($co as $j => $n) if (($ca[$j] ?? 0) < $n) $en_trop[] = $j;
|
||||
return [$manquants, $en_trop];
|
||||
}
|
||||
|
||||
// Renvoie la liste des problèmes (chaînes lisibles) entre un texte français et sa traduction.
|
||||
function controler_jetons($francais, $traduction)
|
||||
{
|
||||
$problemes = [];
|
||||
$fr = extraire_jetons($francais);
|
||||
$en = extraire_jetons($traduction);
|
||||
|
||||
// 1) Jetons [[...]] : présence seulement, l'ordre est libre.
|
||||
[$manquants, $en_trop] = ecart_jetons($fr['reserves'], $en['reserves']);
|
||||
if ($manquants) $problemes[] = "jeton(s) absent(s), ou en nombre insuffisant, dans la traduction : " . implode(' ', $manquants);
|
||||
if ($en_trop) $problemes[] = "jeton(s) inconnu(s) dans la traduction : " . implode(' ', $en_trop);
|
||||
|
||||
// 2) Formats sprintf : on compare le type de conversion (s, d, f...) sans
|
||||
// la position ni la largeur, pour ne pas signaler un %1$s légitime.
|
||||
$types = function ($liste) {
|
||||
return array_map(function ($f) { return substr($f, -1); }, $liste);
|
||||
};
|
||||
$tf = $types($fr['formats']);
|
||||
$te = $types($en['formats']);
|
||||
[$manquants, $en_trop] = ecart_jetons($tf, $te);
|
||||
if ($manquants) $problemes[] = "format(s) sprintf absent(s), ou en nombre insuffisant, dans la traduction : %" . implode(' %', $manquants);
|
||||
if ($en_trop) $problemes[] = "format(s) sprintf en trop dans la traduction : %" . implode(' %', $en_trop);
|
||||
|
||||
// 3) Même formats mais dans un autre ordre : dangereux seulement s'ils ne
|
||||
// sont pas numérotés (%s puis %d), les valeurs seraient alors échangées.
|
||||
if (!$manquants && !$en_trop && $tf !== $te) {
|
||||
$numerotes = false;
|
||||
foreach ($en['formats'] as $f) if (strpos($f, '$') !== false) $numerotes = true;
|
||||
if (!$numerotes) {
|
||||
$problemes[] = "formats sprintf dans un autre ordre (" . implode(' ', $en['formats'])
|
||||
. " au lieu de " . implode(' ', $fr['formats'])
|
||||
. ") : numéroter avec %1\$s, %2\$s, ou remettre l'ordre du français";
|
||||
}
|
||||
}
|
||||
return $problemes;
|
||||
}
|
||||
|
||||
// Texte français raccourci pour l'affichage (coupé sur un caractère entier).
|
||||
function abreger($texte, $max = 70)
|
||||
{
|
||||
$texte = trim(preg_replace('/\s+/', ' ', $texte));
|
||||
if (preg_match('/^.{1,' . $max . '}/us', $texte, $m) && $m[0] !== $texte) return $m[0] . '…';
|
||||
return $texte;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Relit le bloc des obsolètes (commentaire en fin de fichier) et le renvoie
|
||||
// sous forme de tableau texte => traduction. Renvoie [] s'il n'y en a pas.
|
||||
// Lève une exception si le bloc a été modifié à la main de façon à ne plus
|
||||
// être du PHP valide (par exemple une entrée dont il ne reste que la moitié).
|
||||
// ----------------------------------------------------------------------------
|
||||
function lire_obsoletes($fichier)
|
||||
{
|
||||
if (!file_exists($fichier)) return [];
|
||||
$brut = file_get_contents($fichier);
|
||||
if (!preg_match('/\/\*\n\/\/ OBSOLÈTES[^\n]*\n(.*?)\*\/\s*\z/su', $brut, $m)) return [];
|
||||
|
||||
// Les '*/' des textes ont été écrits '*\/' pour ne pas fermer le commentaire :
|
||||
$entrees = str_replace('*\/', '*/', $m[1]);
|
||||
|
||||
$tmp = tempnam(sys_get_temp_dir(), 'obs');
|
||||
file_put_contents($tmp, "<?php\nreturn [\n" . $entrees . "];\n");
|
||||
try {
|
||||
$tableau = include $tmp;
|
||||
} finally {
|
||||
unlink($tmp);
|
||||
}
|
||||
return is_array($tableau) ? $tableau : [];
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Recherche des appels t("...") dans le code
|
||||
// ----------------------------------------------------------------------------
|
||||
$chaines_trouvees = [];
|
||||
$iterateur = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($racine));
|
||||
foreach ($iterateur as $fichier) {
|
||||
@@ -37,21 +200,52 @@ foreach ($iterateur as $fichier) {
|
||||
|
||||
$traductions_en = file_exists($fichier_en) ? include $fichier_en : [];
|
||||
|
||||
// --- Rapport console, comme avant ---
|
||||
echo "=== Chaînes utilisées dans le code : " . count($chaines_trouvees) . " ===\n\n";
|
||||
// Lecture du bloc des obsolètes AVANT toute écriture : si elle échoue, on
|
||||
// s'arrête sans avoir rien modifié.
|
||||
try {
|
||||
$obsoletes_anciens = lire_obsoletes($fichier_en);
|
||||
} catch (Throwable $e) {
|
||||
fwrite(STDERR, "ERREUR : le bloc des entrées obsolètes de $fichier_en n'est plus du PHP valide.\n");
|
||||
fwrite(STDERR, " (" . $e->getMessage() . ")\n");
|
||||
fwrite(STDERR, "Chaque entrée doit rester sur deux lignes : 'texte' puis, à la ligne, '=> traduction,'.\n");
|
||||
fwrite(STDERR, "Corrigez le bloc (ou supprimez-le) puis relancez. RIEN n'a été modifié.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// --- Bilan ---
|
||||
$nouvellement_obsoletes = [];
|
||||
foreach (array_diff(array_keys($traductions_en), array_keys($chaines_trouvees)) as $chaine) {
|
||||
$nouvellement_obsoletes[$chaine] = $traductions_en[$chaine];
|
||||
}
|
||||
|
||||
// Un texte de nouveau utilisé dans le code, mais présent dans le bloc des
|
||||
// obsolètes, retrouve sa traduction et sort du bloc :
|
||||
$reactivees = [];
|
||||
foreach ($chaines_trouvees as $chaine => $fichiers) {
|
||||
if (!array_key_exists($chaine, $traductions_en) && array_key_exists($chaine, $obsoletes_anciens)) {
|
||||
$reactivees[$chaine] = $obsoletes_anciens[$chaine];
|
||||
unset($obsoletes_anciens[$chaine]);
|
||||
}
|
||||
}
|
||||
|
||||
$manquantes = [];
|
||||
foreach ($chaines_trouvees as $chaine => $fichiers) {
|
||||
if (!array_key_exists($chaine, $traductions_en)) {
|
||||
if (!array_key_exists($chaine, $traductions_en) && !array_key_exists($chaine, $reactivees)) {
|
||||
$manquantes[$chaine] = $fichiers[0];
|
||||
}
|
||||
}
|
||||
|
||||
$obsoletes_total = $obsoletes_anciens + $nouvellement_obsoletes;
|
||||
|
||||
echo "=== Chaînes utilisées dans le code : " . count($chaines_trouvees) . " ===\n\n";
|
||||
echo count($manquantes) . " nouvelle(s) chaîne(s) à traduire.\n";
|
||||
echo count($nouvellement_obsoletes) . " entrée(s) devenue(s) obsolète(s), ajoutée(s) au bloc en commentaire.\n";
|
||||
echo count($obsoletes_anciens) . " entrée(s) obsolète(s) déjà en commentaire, conservée(s) (à supprimer à la main).\n";
|
||||
echo count($reactivees) . " entrée(s) obsolète(s) de nouveau utilisée(s), traduction récupérée.\n\n";
|
||||
|
||||
$obsoletes = array_diff(array_keys($traductions_en), array_keys($chaines_trouvees));
|
||||
echo count($obsoletes) . " entrée(s) obsolète(s), déplacée(s) en commentaire.\n\n";
|
||||
|
||||
// --- Régénération de lang/en.php ---
|
||||
// ----------------------------------------------------------------------------
|
||||
// Régénération de lang/en.php
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// Sauvegarde de précaution avant d'écraser :
|
||||
if (file_exists($fichier_en)) {
|
||||
@@ -59,31 +253,61 @@ if (file_exists($fichier_en)) {
|
||||
}
|
||||
|
||||
// Fusion : traductions existantes conservées pour les chaînes encore utilisées,
|
||||
// nouvelles chaînes ajoutées avec une valeur vide :
|
||||
// traductions récupérées du bloc des obsolètes, nouvelles chaînes vides :
|
||||
$nouveau_tableau = [];
|
||||
foreach ($chaines_trouvees as $chaine => $fichiers) {
|
||||
$nouveau_tableau[$chaine] = $traductions_en[$chaine] ?? "";
|
||||
$nouveau_tableau[$chaine] = $traductions_en[$chaine] ?? $reactivees[$chaine] ?? "";
|
||||
}
|
||||
$nouveau_tableau = trier_par_texte($nouveau_tableau);
|
||||
|
||||
$contenu = "<?php\n";
|
||||
$contenu .= "// Fichier régénéré automatiquement par extraire_traductions.php\n";
|
||||
$contenu .= "// le " . date("d/m/Y à H:i:s") . " - les valeurs vides sont à traduire manuellement.\n";
|
||||
$contenu .= "// Entrées triées par ordre alphabétique du texte français.\n";
|
||||
$contenu .= "return [\n";
|
||||
foreach ($nouveau_tableau as $cle => $valeur) {
|
||||
$contenu .= " " . var_export($cle, true) . " => " . var_export($valeur, true) . ",\n";
|
||||
$contenu .= formater_entree($cle, $valeur);
|
||||
}
|
||||
$contenu .= "];\n";
|
||||
|
||||
if (!empty($obsoletes)) {
|
||||
$contenu .= "\n/*\n";
|
||||
$contenu .= "// OBSOLÈTES au " . date("d/m/Y à H:i:s") . " - plus utilisées dans le code.\n";
|
||||
$contenu .= "// À supprimer manuellement une fois confirmé qu'elles ne servent vraiment plus.\n";
|
||||
foreach ($obsoletes as $chaine) {
|
||||
$contenu .= var_export($chaine, true) . " => " . var_export($traductions_en[$chaine], true) . ",\n";
|
||||
if (!empty($obsoletes_total)) {
|
||||
$bloc = "";
|
||||
foreach (trier_par_texte($obsoletes_total) as $cle => $valeur) {
|
||||
$bloc .= formater_entree($cle, $valeur);
|
||||
}
|
||||
// Une séquence '*/' dans un texte fermerait le commentaire trop tôt :
|
||||
$bloc = str_replace('*/', '*\/', $bloc);
|
||||
|
||||
$contenu .= "\n/*\n";
|
||||
$contenu .= "// OBSOLÈTES (bloc mis à jour le " . date("d/m/Y à H:i:s") . ") - plus utilisées dans le code.\n";
|
||||
$contenu .= "// Conservées d'un passage à l'autre : à supprimer à la main quand elles ne servent vraiment plus.\n";
|
||||
$contenu .= $bloc;
|
||||
$contenu .= "*/\n";
|
||||
}
|
||||
|
||||
file_put_contents($fichier_en, $contenu);
|
||||
echo "Fichier régénéré : $fichier_en\n";
|
||||
echo "(sauvegarde de l'ancienne version : $fichier_en.bak)\n";
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Contrôle des espaces réservés (affiché à CHAQUE passage tant que ce n'est
|
||||
// pas corrigé ; ne bloque rien)
|
||||
// ----------------------------------------------------------------------------
|
||||
$avertissements = [];
|
||||
foreach ($nouveau_tableau as $francais => $traduction) {
|
||||
if ($traduction === "") continue; // pas encore traduit
|
||||
$problemes = controler_jetons((string)$francais, (string)$traduction);
|
||||
if ($problemes) $avertissements[$francais] = $problemes;
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
if (empty($avertissements)) {
|
||||
echo "Espaces réservés ([[...]], %s...) : cohérents dans toutes les traductions.\n";
|
||||
} else {
|
||||
echo "ATTENTION : " . count($avertissements) . " traduction(s) dont les espaces réservés diffèrent du français.\n";
|
||||
echo "Ils ne se traduisent pas : le code cherche les jetons exacts.\n";
|
||||
foreach ($avertissements as $francais => $problemes) {
|
||||
echo "\n « " . abreger((string)$francais) . " »\n";
|
||||
foreach ($problemes as $probleme) echo " - $probleme\n";
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user