[Koha-cvs] CVS: koha/misc/translator opac.fr,NONE,1.1 text-extract.pl,NONE,1.1 tmpl_process.pl,NONE,1.1 translator_doc.txt,NONE,1.1

Paul POULAIN tipaul at users.sourceforge.net
Fri Dec 5 17:53:48 CET 2003


Update of /cvsroot/koha/koha/misc/translator
In directory sc8-pr-cvs1:/tmp/cvs-serv16892/misc/translator

Added Files:
	opac.fr text-extract.pl tmpl_process.pl translator_doc.txt 
Log Message:
translator tool. see translator_doc.txt

--- NEW FILE ---
1	
10	
11	
12	
13	
14	
15	
16	
17	
18	
19	
2	
20	
2002	
2003	
2004	
2005	
2006	
21	
22	
23	
24	
25	
26	
27	
28	
29	
3	
30	
31	
4	
5	
6	
7	
8	
9	
Account for	Compte de
Additional Author:	Autres auteurs
Advanced Search, More Options	Recherche avancée
AMOUNT	COUT
An Error has Occurred	Une erreur s'est produite
Any	Tout
Apr	Avril
Are any of our records incorrect? Have you moved recently, got a new phone number or e-mail address?	Un changement à nous faire connaitre ?
ARE OUR RECORDS CORRECT?	Est-ce correct
Aug	Aout
Author	Auteur
Author:	Auteur
Available	Disponible
BARCODE	CODE BARRE
Barcode	Code barre
bgcolor='#ffffcc' align=center>	
bgcolor='#ffffcc'>	
Biblio number:	Notice biblio :
BIBLIO RECORD	Notice bibliographique
Biblionumber:	Numéro notice :
Cancelled:	Annulé
Cannot be Reserved	Non réservable
Cardnumber:	N° de carte
Catalogue Search	Recherche catalogue
Charges	Charges
City	Ville
Class	Classe
Classification:	
Collection:	
Compact Disc	
Copies	Copies
Current Branch:	Annexe
Current Loan	Prêt
Date Due	Date retour
Date:	
Day	Jour
Dec	
Dewey:	
Due date	Date retour
Easy / Picture Books	
Easy Reader	Lecture facile
Email	Mail
End reserve on this date:	Annuler réservation à cette date
Exact	
Feb	Fev
Fiction	
FINES & CHARGES	Dettes
Firstname	Prénom
GROUP -	GROUPE
Group Number:	Numéro groupe :
Hi	Bonjour,
Home	Accueil
Home Branch:	Annexe propriétaire
Home phone	Téléphone maison
Illus:	
Illustrator	Illustrateur
in	dans
input{font-size:16px}	
ISBN:	
Item Count	Nombre d'ouvrages
Item lost:	Ouvrage perdu
Item Type	Type d'ouvrage
Item Type:	Type d'ouvrage :
items currently issued.	Ouvrage en prêt actuellement
items currently reserved.	Ouvrages réservés actuellement
Itemtype	Type d'ouvrage
Jan	Jan
Join	Rejoindre
Jul	Juill
Jun	Juin
Junior Fiction	
Junior Non-Fiction	
Keywords	Mot-clefs
KOHA	
Koha Login	Identifiant Koha
KOHA: Become a Member	KOHA : s'abonner
KOHA: Catalogue Search	Recherche catalogue
Koha: Horowhenua Library Trust Catalogue and Member Services	Koha, catalogue et outils abonnés
KOHA: Members Area	Zone abonnés
KOHA: OPAC Catalogue Search	Recherche OPAC
Last borrowed:	Dernier prêt
Last Borrower 1:	Dernier emprunteur 1
Last Borrower 2:	Dernier emprunteur 2
Last Seen	Vu
Last seen:	Vu :
Library Card:	Carte lecteur
Loan Length:	Durée prêt
Location	Localisation
Log In	S'identifier
Log In to Koha	S'identifier
Log Out	Déconnecter
Logged in as:	Connecté en tant que :
Logged in as:  	Connecté en tant que: 
Logged in as:  [	
Logout	Déconnecter
Mar	Mars
May	Mai
Members Home	Accueil lecteur
Month	Mois
Next Records	Enregistrement suivant
No	Non
No. of Items:	N° ouvrage
Non-Fiction	
Normal	Normal
Not Reservable	Non réservable
Note that if you enter a value in Keyword and a value somewhere else, only keyword will be used	Notez que le mot clef est prioritaire
Note there will be a reserve charge of	Il y aura des frais de réservation de
Notes:	Notes
Nov	
Oct	
of	de
OK Start Search	
on issue bit	En prêt ?
Online	En ligne
OR :	OU
OR one or more from :	OU un ou plus de :
Pages:	
Paid for:	Payé pour :
Password:	Mot de passe :
pick up at:	Prendre à :
Place:	
Please confirm that you wish to request an item of these types:	Confirmez que vous souhaitez réserver un ouvrage :
Please select the branch from which you want to collect the item:	Sélectionnez l'annexe ou vous prendrez l'ouvrage
Please select which item types are ok for you. The first of these item that matches one of these types that becomes available will be set kept for you.	Sélectionnez les types d'ouvrages souhaités. Le premier disponible sera gardé pour vous.
Please, change what's needed. An email will be sent to the library.	 Changer le nécessaire
Previous Records	Précédents
Published by :	Publié par
Publisher:	Editions
records.	lignes
Reference Books	
Renewals:	Renouvellements
Rental Charge:	Coût du prêt
Replacement Price:	Coût de remplacement
Request	Demande
Reserve date	Date réservation
Reserve on this date:	Réserver à cette date :
Reserve page for item:	Réservation de l'ouvrage :
reserves already on this item.	Réservations déjà faites sur cet ouvrage
reserves.	Réservations
Results	Résultats
Results  through  of  records.	
results found	Réponses trouvées
Search	IGNORE
search	IGNORE
Search the Catalogue	Chercher dans le catalogue
Sep	Sep
Serial:	Périodique
Size:	Taille
Something new ?	quelque chose à modifier ?
Sorry, KOHA doesnt think you have permission for this page	INTERDIT !
Sorry, KOHA doesnt think you have permission for this page.	INTERDIT
Sorry, there were no results	Désolé, aucun résultat !
Sorry, you cannot make more than	Impossible de faire plus de
Sorry, you cannot make more than  reserves.	Désolé, vous ne pouvez pas faire plus de réservations
Sorry, you cannot make reserves because you owe	Vous ne pouvez réserver parce que vous devez
Sorry, you cannot make reserves because you owe .	DESOLE
Sorry, your session has timed out, please login again.	Déconnecté. Identifiez vous à nouveau.
STILL OWING	EN ATTENTE
Street address	Adresse postale
Subject	Sujet
Subject:	Sujet :
Subtitle:	Sous-titre
Surname	Nom
Teacher Reference	Ref. prof.
text/html; charset=iso8859-1	
There are	Il y a
through	à
Title	Titre
to Koha	à Koha
Total Due	Total dû
Total Number of Items:	Nombre total d'exemplaires
Try another	Essayez un autre
Type in the box and press the enter key.	Saisissez et validez avec Entrée
Unititle:	Titre unifié
Update Record	Mettre à jour
URL:	URL
View Accounts	Voir compte
Volume:	
Website	Site web
WELCOME TO THE  KOHA OPAC	Bienvenue dans l'OPAC de Koha
WELCOME TO THE KOHA OPAC	
Which is to be picked up from	Qui sera retiré à
Work/Fax phone	Tel bureau
Year	Année
Year :	Année :
Yes	Oui
YesNo	
You already have a reserve placed on this item.	Vous avez déjà placé une réservation sur cet exemplaire
You are accessing koha from a different ip address! please login again.	Vous avez changé d'adresse IP. Identifiez vous à nouveau
You did not specify any seach criteria	Vous n'avez pas spécifié de critère
You entered an incorrect username or password, please try again.	Identifiant ou mot de passe erroné. Essayez encore.
You have	Vous avez
You have  items currently issued.	Vous avez exemplaires en prêt
You have  items currently reserved.	Vous avez exemplaires réservés
You have a credit of	Vous avez un crédit de
You have no items on issue.	Vous n'avez pas d'exemplaire en prêt
You have outstanding charges and fines of	Vous avez des dettes de
You have reserved items waiting:	Vouas avez des réservations en attente
You must select a branch for pickup!	Sélectionnez une annexe ou retirer l'ouvrage
You must select at least on item type!	Vous devez sélectionner au moins 1 type d'exemplaire
You searched on	Recherche effectuée sur
Young Adult Fiction	
Your changes won't appear until the library has validated them.	Vos changement n'apparaitront que lorsque la bibliothèque les aura validé

--- NEW FILE ---
#!/usr/bin/perl
use HTML::Tree;
use Getopt::Std;
getopt("f:");
	my $tree = HTML::TreeBuilder->new; # empty tree

	$tree->parse_file($opt_f);
	sub give_id {
		my $x = $_[0];
		foreach my $c ($x->content_list) {
			next if (ref($c) && $c->tag() eq "~comment");
			print "$c\n" unless ref($c);
			if (ref($c) && $c->attr('alt')) {
				print $c->attr('alt')."\n";
			}
			if (ref($c) && $c->tag() eq 'meta') {
				print $c->attr('content')."\n ";
			}
			give_id($c) if ref $c; # ignore text nodes
		}
	};
	give_id($tree);
	$tree = $tree->delete;

--- NEW FILE ---
#!/usr/bin/perl

use strict;
use Getopt::Long;

my (@in_files, $str_file, $split_char, $recursive, $type, $out_dir, $in_dir, @excludes, $filter);
my $help;
my $exclude_regex;

$split_char = '	';

GetOptions(
	'input|i=s'	=> \@in_files,
	'outputdir|o=s' => \$out_dir,
	'str-file|s=s' => \$str_file,
	'recursive|r' => \$recursive,
	'filter=s' => \$filter,
	'type=s' => \$type,
	'exclude=s' => \@excludes,
	'sep=s' => \$split_char,
	'help'	=> \$help);

help() if $help;

# utiliser glob() pour tous les fichiers d'un repertoire

my $action = shift or usage();
my %strhash = ();

# Checks for missing input and string list arguments

if( !defined(@in_files) || !defined($str_file) )
{
	usage("You must at least specify input and string list filenames.");
}

# Type match defaults to *.tmpl if not specified
$type = "tmpl|inc" if !defined($type);

$filter = "./text-extract.pl -f" if !defined($filter);
# Input is not a file nor a directory
if( !(-d $in_files[0]) && !(-f $in_files[0]))
{
	usage("Unknow input. Input must a file or a directory. (Symbolic links are not supported for the moment).");
}
elsif( -d $in_files[0] )
{
	# input is a directory, generates list of files to process
	$in_dir = $in_files[0];
	$in_dir =~ s/\/$//; # strips the trailing / if any

	print "Generating list of files to process...\n";
	
	@in_files = ();
	@in_files = &listfiles(\@in_files, $in_dir, $type, $recursive);
	
	if(scalar(@in_files) == 0)
	{
		warn "Nothing to process in $in_dir matching *.$type.";
		exit -1;
	}
}

# Generates the global exclude regular expression
$exclude_regex =  "(".join("|", @excludes).")" if @excludes;

if( $action eq "create" )
{
	# updates the list. As the list is empty, every entry will be added
	%strhash = &update_strhash(\%strhash, \@in_files, $exclude_regex, $filter);
	# saves the list to the file
	write_strhash(\%strhash, $str_file, "\t");
}
elsif( $action eq "update" )
{
	# restores the string list from file
	%strhash = &restore_strhash(\%strhash, $str_file, $split_char);
	# updates the list, adding new entries if any
	%strhash = &update_strhash(\%strhash, \@in_files, $exclude_regex, $filter);
	# saves the list to the file
	write_strhash(\%strhash, $str_file, $split_char);
}
elsif( $action eq "install" )
{
	if(!defined($out_dir))
	{
		usage("You must specify an output directory when using the install method.");
	}
	
	if( $in_dir eq $out_dir )
	{
		warn "You must specify a different input and output directory.\n";
		exit -1;
	}

	# restores the string list from file
	%strhash = &restore_strhash(\%strhash, $str_file, $split_char);
	# creates the new tmpl file using the new translation
	&install_strhash(\%strhash, \@in_files, $in_dir, $out_dir);
}
else
{
	usage("Unknown action specified.");
}

exit 0;

##########################################################
# Creates the new template files in the output directory #
##########################################################

sub install_strhash
{
	my($strhash, $in_files, $in_dir, $out_dir) = @_;

	my $fh_in; my $fh_out; # handles for input and output files
	my $tmp_dir; # temporary directory name (used to create destination dir)

	$out_dir =~ s/\/$//; # chops the trailing / if any.

	# Processes every entry found.
	foreach my $file (@{$in_files})
	{
		if( !open($fh_in, "< $file") )
		{
			warn "Can't open $file : $!\n";
			next;
		}

		# generates the name of the output file
		my $out_file = $file;

		if(!defined $in_dir)
		{
			# processing single files not an entire directory
			$out_file = "$out_dir/$file";
		}
		else
		{
			$out_file =~ s/^$in_dir/$out_dir/;
		}

		my $slash = rindex($out_file, "\/");
		$tmp_dir = substr($out_file, 0, $slash); #gets the directory where the file will be saved

		# the file doesn't exist
		if( !(-f $tmp_dir) && !(-l $tmp_dir) && !(-e $tmp_dir) )
		{
			if(!mkdir($tmp_dir,0775)) # creates with rwxrwxr-x permissions
			{
				warn("Make directory $tmp_dir : $!");
				close($fh_in);
				exit(1);
			}
		}
		elsif((-f $tmp_dir) || (-l $tmp_dir))
		{
			warn("Unable to create directory $tmp_dir.\n A file or symbolic link with the same name already exists.");
			close($fh_in);
			exit(1);
		}
		
		# opens handle for output
		if( !open($fh_out, "> $out_file") )
		{
			warn "Can't write $out_file : $!\n";
			close($fh_in);
			next;
		}

		print "Generating $out_file...\n";

		while(my $line = <$fh_in>)
		{
			foreach my $text (sort  {length($b) <=> length($a)} keys %{$strhash})
			{
				# Test if the key has been translated
				if( %{$strhash}->{$text} != 1 )
				{
					# Does the line contains text that needs to be changed ?
					if( $line =~ /$text/ && %{$strhash}->{$text} ne "IGNORE")
					{
						# changing text
						my $subst = %{$strhash}->{$text};
						$line =~ s/(\W)$text(\W)/$1$subst$2/g;
					}
				}
			}

			# Writing the modified (or not) line to output
			printf($fh_out "%s", $line);
		}

		close($fh_in);
		close($fh_out);
	}
}

########################################################
# Updates the string list hash with the new components #
########################################################

sub update_strhash
{
	my($strhash, $in_files, $exclude, $filter)= @_;

	my $fh;

	# Processes every file entries
	foreach my $in (@{$in_files})
	{

		print "Processing $in...\n";

		# Creates a filehandle containing all the strings returned by
		# the plain text program extractor
		open($fh, "$filter $in |") or print "$filter $in : $!";
		next $in if !defined $fh;

		# Processes every string returned
		while(my $str = <$fh>)
		{
			$str =~ s/[\n\r\f]+$//; # chomps the trailing \n (or <cr><lf> if file was edited with Windows)
			$str =~ s/^\s+//; # remove trailing blanks
			$str =~ s/\s+$//;

			# the line begins with letter(s) followed by optional words and/or spaces
			if($str =~ /^[ ]*[\w]+[ \w]*/)
			{
				# the line is to be excluded ?
				if( !(defined($exclude) && ($str =~ /$exclude/o) && $str>0) )
				{
					if( !defined(%{$strhash}->{$str}) )
					{
						# the line is not already in the list so add it
						%{$strhash}->{$str} = 1;
					}
				}
			}
		}

		close($fh);
	}
	
	return %{$strhash};
}

#####################################################
# Reads the input file and returns a generated hash #
#####################################################

sub restore_strhash
{
	my($strhash, $str_file, $split_char) = @_;
	
	my $fh;
	
	open($fh, "< $str_file") or die "$str_file : $!";
	
	print "Restoring string list from $str_file...\n";
	
	while( my $line = <$fh> )
	{
		chomp $line;
		
		# extracts the two fields
		my ($original, $translated) = split(/$split_char/, $line, 2);

		if($translated ne "")
		{
			# the key has been translated
			%{$strhash}->{$original} = $translated;
		}
		else
		{
			# the key exist but has no translation.
			%{$strhash}->{$original} = 1;
		}
		
	}
	
	close($fh);
	
	return %{$strhash};
}

#########################################
# Writes the string hashtable to a file #
#########################################

sub write_strhash
{
	my($strhash, $str_file, $split_char) = @_;

	my $fh;

	# Opens a handle for saving the list
	open($fh, "> $str_file") or die "$str_file : $!";

	print "Writing string list to $str_file...\n";

	foreach my $str(sort {uc($a) cmp uc($b) || length($a) <=> length($b)} keys %{$strhash})
	{
		if(%{$strhash}->{$str} != 1)
		{
			printf($fh "%s%s%s\n", $str, $split_char, %{$strhash}->{$str});
		}
		else
		{
			printf($fh "%s%s\n", $str, $split_char);
		}
	}

	close($fh);
}

########################################################
# List the contents of dir matching the pattern *.type #
########################################################

sub listfiles
{
	my($in_files, $dir, $type, $recursive) = @_;

	my $dir_h;
#	my @types = split(/ /,$type);
	opendir($dir_h, $dir) or warn("Can't open $dir : $!\n");

	my @tmp_list = grep(!/^\.\.?$/, readdir($dir_h));

	closedir($dir_h);

	foreach my $tmp_file (@tmp_list)
	{

		if( $recursive && (-d "$dir/$tmp_file") ) # entry is a directory
		{
			@{$in_files} = listfiles($in_files, "$dir/$tmp_file", $type);
		}
		elsif( $tmp_file =~ /\.$type$/ )
		{
			push(@{$in_files}, "$dir/$tmp_file");
		}
	}
	return @{$in_files};
}

######################################
# DEBUG ROUTINE                      #
# Prints the contents of a hashtable #
######################################

sub print_strhash
{
	my($strhash, $split_char) = @_;
	
	foreach my $str(sort keys %{$strhash})
	{
		if(%{$strhash}->{$str} != 1)
		{
			printf("%s%s%s\n", $str, $split_char, %{$strhash}->{$str});
		}
		else
		{
			printf("%s%s\n", $str, $split_char);
		}
	}
}	

#########################################
# Short help messsage printing function #
#########################################

sub usage
{
	warn join(" ", @_)."\n" if @_;
	warn <<EOF;

Usage : $0 method -i input.tmpl|/input/dir -s strlist.file
        [-o /output/dir] [options]

where method can be :
  * create : creates the string list from scratch using the input files.
  * update : updates an existing string list, adding the new strings to
             the list, leaving the others alone.
  * install : creates the new .tmpl files using the string list config file
              (--outputdir must be used to specify the output directory).

Use $0 --help for a complete listing of options.
EOF
	exit(1);
}

##############################################
# Long help message describing every options #
##############################################

sub help
{
	warn <<EOF;
Usage : $0 method [options]
        
where method can be :
  * create : creates the string list from scratch using the input files.
  * update : updates an existing string list, adding the new strings to
             the list, leaving the others alone.
  * install : creates the new .tmpl files using the string list config file
              (-o must be used to specify the output directory).

options can be :

  -i or --input=
     Specify the input to process. Input can be a file or a directory.
     When input is a directory, files matching the --type option will be
     processed.
     When using files, the parameter can be repeated to process a list
     of files.
   
  Example: $0 create -i foo.tmpl --input=bar.tmpl -s foobar.txt

  -s or --str-file=
     Specify the file where the different strings will be stored.

  -o or --outputdir=
     Specify the output directory to use when generating the translated
     input files.

  -r or --recursive=
     Use the recursive mode to process every entry in subdirectories.
     Note: Symbolic links used to link directories are not supported.

  --type=
     Defines the type of files to match when input is a directory.
     By default --type=tmpl

  --exclude=regex
     Use this option to exclude some entries extracted by the program.
     This option can be repeated to exclude many types of strings.

  Example: $0 create -i foo.tmpl -s foo.txt --exclude=^\[0-9\]+\$
   will create a list from foo.tmpl called foo.txt where lines
   composed of numbers only are excluded. Special characters need to
   be escaped.

  --filter=
     Specify the program to use to extract plain text from files.
     Default is str-extract which means str-extract must be in the path
     in order to use it.

  --sep=char
     Use this option to specify the char to be used to separate entries
     in the string list file.

  --help
     This help message.
EOF
	exit(0);
}

--- NEW FILE ---
TRANSLATION TOOL :
============
This transation tool should greatly help Koha translators.
It's composed of 2 script :
* test-extract.pl, that extracts the texts in a template. It is called by
* tmpl_process.
tmpl process can do 3 things :
- create a file with all sentences all files in a directory (& it's subdirectories)
- update an existing translation file.
- rebuild translated templates from english & translation file.

Call tmpl_process --help to get full explanations.

HOW TO TRANSLATE Koha :
============
1- create your translation file :
./tmpl_process.pl update -i /home/paul/koha.dev/koha/koha-tmpl/opac-tmpl/default/en/ -s opac.fr -r
2- translate your opac.fr file (english & french being separated by a tab
3- create your translated version :
./tmpl_process.pl install -i /home/paul/koha.dev/koha/koha-tmpl/opac-tmpl/default/en/ -o /home/paul/koha.dev/koha/koha-tmpl/opac-tmpl/default/fr2/ -s opac.fr -r
4- copy images in your new directory (as they are NOT moved by tmpl_process.pl install)

If something changes in english version, go back to pt1 & repeat.

WEAKNESSES
=======
I've found some weaknesses (some solvable probably, but maybe some unsolvable)
- HTML::Templates pb :
 if there is only a <TMPL* > tag between 2 strings, text-extract sometimes merges both strings, and so can't replace them in the translated template. I've solved all those cases with a <br/> or another HTML statement.

- SHORT STATEMENTS
 short statements (like "in") can be replaced stupidly (like in cgi-bin => gives cgi-bdans in french). the workaround has been to replace only complete words (
line 185 of tmpl_process :
$line =~ s/(\W)$text(\W)/$1$subst$2/g;
)
Don't seem to have any side effect.

- WORD exist in FILENAME
words that are in the templates and in a perl script name are replaced !
For example : Search is replace by Recherche in french, thus,giving <a href="cgi-bin/opac-Recherche.pl">, which is wrong...
The only way I've found to solve this is to ignore any translation whose translation string is IGNORE
(search is the only cas i've found in french opac)

- MISSING STRINGS
Maybe some strings that should be translated are missing.
You can add whatever you want in text-extract.pl.
For instance, it extracts :
- standard text
- text in alt="SOMETEXT"
- meta html encoding (8859-1) to enable non european translations.

COPYRIGHT
======
@ paul poulain (paul.poulain _ at _ free.fr) & Jerome Vizcaino vizcainj _ at _ esiee.fr





More information about the Koha-cvs mailing list