#!/usr/bin/env bash
# Script permettant de corriger et de signaler les erreurs typographiques dans un fichier PO
#
# Copyright (C) 2010 Nicolas Delvaux <nicolas.delvaux@gmx.com>
# Ce script est publié sous licence BSD, cf. http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
# Version 1.4

RT=/tmp/francisator      # Répertoire de travail
FIC=$RT/fichier.po       # Fichier de travail
RES=$RT/res.po           # Fichier où le résultat va apparaitre petit à petit
CORR=$RT/corr.po         # Fichier où sera isolés la chaine traduite courante (bloc msgstr) devant être vérifiés/corrigés
TMP=$RT/tmp.po           # Fichier temporaire
TMP2=$RT/tmp2.po         # Autre fichier temporaire
VERIF=$RT/erreurs.txt    # Fichier où seront notifiés les erreurs typographiques *potentielle* détectées
ORTHO=$RT/orthographe.po # Fichier où seront rassemblées toutes les chaines (pour utiliser le correcteur d'orthographique)


# Fonction permettant de corriger les erreurs typographiques (non ambigüe) du fichier $CORR
function corriger {

	sed -i -e 's/[  ]!/ !/g'       \
	       -e 's/[  ]?/ ?/g'       \
	       -e 's/[  ]»/ »/g'       \
	       -e 's/«[  ]/« /g'       \
	       -e 's/[  ]:"/ :"/g'     \
	       -e 's/[  ];"/ ;"/g'     \
	       -e 's/[  ]: / : /g'     \
	       -e 's/[  ]; / ; /g'     \
	       -e 's/[  ]:<\// :<\//g' \
	       -e 's/[  ];<\// ;<\//g' \
	       -e 's/[  ]:\\n/ :\\n/g' \
	       -e 's/[  ];\\n/ ;\\n/g' $CORR

# Risque de conflit avec des smileys pour : et ;, on est donc plus précis en discernant les cas suivants :
# en milieu de ligne ou devant un \n littéral ou devant un « " » ou devant une balise fermante.

}


# Fonction permettant de vérifier et de signaler la présence de certaines erreurs typographiques *potentielles* du fichier $CORR
function verifier {

	nbLignesAbs="`awk 'END {print NR}' $RES`"
	nbLignesRel="`awk 'END {print NR}' $CORR`"

	# Les balises XML sont-elles valides ? 

	# Commande miraculeuse donnée par chopinhauer du forum ubuntu-fr : http://forum.ubuntu-fr.org/viewtopic.php?pid=3859298
	# Elle supprime les balises correctement fermées (marche aussi quand les balises sont imbriquées !)
	sed -n -e 's/^\(msgid\)\?[[:space:]]*"\(.*\)"[[:space:]]*$/\2/;H' \
	-e '${g;s/[\n]//g;:a;s/<\([[:alpha:]]\+\)\( [^>]*\)\?>[^<]*<\/\1>\|<[^>]\+\/>//g;ta;p}' $CORR > $TMP

	# Il suffit de regarder s'il reste des balises. (on exclue les adresses mail)
	if [[ -n "`grep "<.\+>" $TMP`" && -z "`grep "<.\+@[^\.]\+\..\+>" $TMP`" ]]; then
		echo "Bloc commençant à la ligne $((nbLignesAbs + 1)) : problème avec les balises (fermeture, coquille...) ?" >> $VERIF
		obtenir_contexte
	fi

	# Y a-t-il des espaces en double/triple ? (on prépare la détection ici, on regardera ligne par ligne plus tard)
	sed -e ':z;N;s/[   ]\"\n\"[   ]/\nESPACES_EN_TROP/g;bz' \
	    -e 's/[   ][   ]\+[^\*   ]/ESPACES_EN_TROP/g' $CORR > $TMP


	# La ponctuation finale de la traduction est-elle la même que celle de la chaine originale ?

	local ponct="`tail -1 $CORR`" # Dernière ligne de la chaine traduite
	
	if [ "$ponct" != 'msgstr ""' ]; then # On ne tient pas compte des chaines non traduites

		ponct="`echo ${ponct: -2}`"   # Deux derniers caractères de la chaine traduite (avec le « " »)
		ponct="`echo ${ponct::1}`"    # Dernier caractère

		local ponct2="`tail -1 $RES`" # On regarde maintenant la chaine originale
		ponct2="`echo ${ponct2: -2}`"
		ponct2="`echo ${ponct2::1}`"

		if [[ "$ponct" != "$ponct2" && (( -n "`echo $ponct | grep "[,;:?\!   …]"`" || -n "`echo $ponct2 | grep "[,;:?\!   …]"`" )) ]]; then

			echo "Ligne $((nbLignesAbs + nbLignesRel)) : La chaine et sa traduction ne se terminent pas par le même signe de ponctuation." >> $VERIF
			obtenir_contexte $nbLignesRel

		fi
	fi


	local i=1
	while [ $i -le $nbLignesRel ]; do

		local ligne="`head -$i $TMP | tail -1`" # On avance ligne par ligne pour identifier celle-ci précisément (plus pratique)

		# Un signe de ponctuation double non précédé par une espace fine insécable ?
		if [ -n "`echo $ligne | grep "[^ ][!?;:»]\|«[^ ]" | grep -v "\(http://\|[0-9HMd]:[0-9%]\|&lt;\|&gt;\|&amp;\|&quot;\|&#[0-9][0-9][0-9];\|\(.png\|.gif\|.jpg\)';\)"`" ]; then
			echo "Ligne $((i + nbLignesAbs)) : oublie d'une espace fine insécable ?" >> $VERIF
			obtenir_contexte $i
		fi

		# Utilisation de « B » au lieu de « o » ?
		if [ -n "`echo $ligne | grep "kB\|[^S]MB\|[^R]GB\|TB\|PB\|kiB\|MiB\|Gib\|TiB\|PiB"`" ]; then
			echo "Ligne $((i + nbLignesAbs)) : emploi de B au lieu de o ? (b = bit, B = byte = octet)" >> $VERIF
			obtenir_contexte $i
		fi

		# Présence d'une espace insécable ? (il ne devrait pas en rester beaucoup, les vérifier manuellement peut être utile)
		if [ -n "`echo $ligne | grep " "`" ]; then
			echo "Ligne $((i + nbLignesAbs)) : espace insécable détectée. Est-elle bien employée ? (nom composé, mesure angulaire...)" >> $VERIF
			obtenir_contexte $i
		fi

		# On a détecté des espaces en trop
		if [ -n "`echo $ligne | grep "ESPACES_EN_TROP"`" ]; then
			echo "Ligne $((i + nbLignesAbs)) : espaces en double/triple ?" >> $VERIF
			obtenir_contexte $i
		fi

	((i++))
	done
}


# Prend le numéro de ligne relatif dans $CORR en paramètre. Sinon, affiche le bloc en entier.
function obtenir_contexte {

	local numLigne=$1
	local i=2
	local continuer=1
	> $TMP2 # On vide $TMP2

	echo "" >> $VERIF

	if [[ "$nbLignesRel" -ge "4" && "$#" != "0" ]]; then

		if [ "$numLigne" == "$nbLignesRel" ]; then # Si l'erreur est sur la dernière ligne, ne pas essayer d'afficher la suivante
			local j=1
		else
			local j=0
		fi

		while [ "$i" -ge "$j" ]; do
			echo "      `tail -$((nbLignesRel - numLigne + i)) $RES | head -1`" >> $VERIF
			((i--))
		done

		echo "      -----" >> $VERIF

		echo "      `head -$((numLigne - 1)) $CORR | tail -1`" >> $VERIF
		echo " ---> `head -$((numLigne))     $CORR | tail -1`" >> $VERIF

		if [ "$j" == "0" ]; then # Si l'erreur est sur la dernière ligne, ne pas essayer d'afficher la suivante
			echo "      `head -$((numLigne + 1)) $CORR | tail -1`" >> $VERIF
		fi


	else # On affiche tout le bloc.

		i=1
		continuer=1

		while [ "$continuer" == "1" ]; do
			local ligne="`tail -$i $RES | head -1`"
			echo "      $ligne" >> $TMP2
			if [ -n "`echo $ligne | grep "^msgid \|^#~ msgid"`" ]; then
				continuer=0
			fi
			((i++))
		done

		local j=1
		while [ $j -le $((i - 1)) ]; do
			tail -$j $TMP2 | head -1 >> $VERIF
			((j++))
		done

		echo "      -----" >> $VERIF

		i=1
		while [ $i -le $nbLignesRel ]; do
			if [[ $# != "0" && $i == $1 ]]; then
				echo " ---> `head -$i $CORR | tail -1`" >> $VERIF
			else
				echo "      `head -$i $CORR | tail -1`" >> $VERIF
			fi
			((i++))
		done
	fi
	echo "" >> $VERIF

}


# Fonction permettant d'isoler le premier bloc msgstr de $FIC dans le fichier $CORR
function isoler_blocs {

	local i="`grep -onm 1 "^msgstr\|^#~ msgstr" $FIC | grep -om 1 "^[0-9]\+"`"
	((i--))

# i contient maintenant le numéro de la ligne qui précède le début du bloc msgstr.
# Puisqu'on ne doit pas toucher à la partie supérieure, on l'envoie dans le résultat

	local nbLignes="`awk 'END {print NR}' $FIC`"
	head -$i $FIC >> $RES
	tail -$((nbLignes - i)) $FIC > $TMP

# Le début de $TMP correspond maintenant au début d'un bloc msgstr
# Reste à trouver la fin du bloc...
# La ligne appartient au bloc si elle commence par " ou par msgstr (les formes plurielles sont prises en compte)

	((nbLignes-=$i))
	i="`grep -vnm 1 '^msgstr\|^#~ msgstr\|^\"\|#~ \"' $TMP | grep -om 1 "^[0-9]\+"`"
	((i--))

	if [ "$i" == "-1" ]; then # Si on est sur la dernière ligne de $FIC...
		cat $TMP > $CORR
		> $FIC # On vide $FIC (on a traité sa dernière ligne)
	else
		head -$i $TMP > $CORR
		tail -$((nbLignes - i)) $TMP > $FIC
	fi

# Voilà, le bloc msgstr a été isolé dans $CORR : il est prêt à être corrigé/vérifié !
# $FIC est également prêt pour l'itération suivante !

}


################# DÉBUT DU SCRIPT #################

if [[ "$#" == "0" || "$#" -gt "2" ]]; then
	echo "Vous ne pouvez passer en paramètre que « --ortho » (facultatif) et le nom du fichier PO à vérifier/corriger."
	exit
elif [[ "$#" == "1" && ! -e $1 ]]; then
	echo "le fichier $1 n'existe pas."
	exit
elif [[ "$#" == "2" && ! -e $2 ]]; then
	echo "le fichier $2 n'existe pas."
	exit
fi

ortho=0
orig=$1

if [[ "$#" == "2" && "$1" != "--ortho" ]]; then
	echo "Argument $1 inconnu."
	echo "Vous ne pouvez passer en paramètre que « --ortho » (facultatif) et le nom du fichier PO à vérifier/corriger."
	exit
elif [[ "$#" == "2" && "$1" == "--ortho" ]]; then
	ortho=1
	orig=$2
fi

total="$((`grep -c "\(^msgid \|^\#~ msgid \)" $orig` - 1))" # Nombre total de chaines

rm -rf $RT # Nettoyage
mkdir -p $RT

cat $orig > $FIC # Copie de l'original dans le répertoire de travail

isoler_blocs
cat $CORR >> $RES # On ignore le premier bloc (méta-données)

iter=1 # Compteurs de chaines traitées
continuer=1

while [ "$continuer" == "1" ]; do

	clear
	echo $iter/$total
	((iter++))
	isoler_blocs
	corriger
	verifier

	if [ "$ortho" == "1" ]; then
		echo "** Ligne $((`awk 'END {print NR}' $RES` + 1)) : **" >> $ORTHO
		cat $CORR >> $ORTHO
		echo "" >> $ORTHO
	fi

	cat $CORR >> $RES

	if [ -z "`grep --max-count=1 "\(^msgid \|^\#~ msgid \)" $FIC`" ]; then # S'il n'y a plus de chaines à traiter...
		continuer=0
		cat $FIC >> $RES
	fi
done

cat $RES > corrigé.$orig
echo "C'est fini !"
echo "Le fichier corrigé est « corrigé.$orig » dans le répertoire courant."
echo ""

if [ -e $VERIF ]; then
	cat $VERIF > erreurs_potentielles.txt
	echo "Des erreurs *potentielles* ont été détectées."
	echo "Plus de détails dans le fichier « erreurs_potentielles.txt »."
fi
echo ""

if [ -e $ORTHO ]; then
	sed -e 's/msgstr //g' \
	    -e 's/# ~ msgstr //g' \
	    -e 's/"//g'       \
	    -e 's/_//g' $ORTHO > orthographe.txt
	echo "Vous pouvez aussi utiliser un correcteur orthographique sur le fichier « orthographe.txt »."
	echo ""
fi

