#!/bin/bash
# -*- coding:utf-8 -*-
# file KLEIDER/web/src/localization/localize
# Lokalisierung der Website http://kleider.herbaer.de
# 2015-07-02 Herbert Schiemann <h.schiemann@herbaer.de>

# Zunächst Funktionen,
# die an die konkrete Anwendung anzupassen sind.

# Zähler, Variable, Aktionen
declare_vars ()
{
   # Ein Leerzeichen als Wert bedeutet, dass Positionsargumente verarbeitet werden
   _argv=" ";

   # Suchpfad für rc-Dateien, : - getrennte Liste von Verzeichnispfaden
   # Falls leer, wird die Option --rc nicht speziell behandelt
   g_configpath= ;

   # Zähler
   g_counters="  \
      verbose    \
      overwrite  \
      story      \
      zipfiles   "

   # Variable
   g_variables=" \
      srclang    \
      basedir    \
      docroot    \
      locdir     \
      trtoutdir  \
      trtindir   \
      srcdir     \
      stylesrc   \
      trname     \
      trlang     \
      storyptn   \
      ftpmode    \
      runonce    ";

   # Aktionen
   g_actions="   \
      reminter   \
      skl        \
      xliff      \
      mtt        \
      trt        \
      mtrans     \
      merge      \
      finish     \
      upload     \
      once       ";
} # declare_vars

# setzt Vorgabe-Werte
set_defaults ()
{
   local b=$(realpath $0);
   b=${b%/src/localization/localize};
   [[ -n "$no_trt"        ]] || no_trt=1 ;
   [[ -n "$no_once"       ]] || no_once=1 ;
   [[ -n "$verbose"       ]] || verbose=1 ;
   [[ -n "$overwrite"     ]] || overwrite=0 ;
   [[ -n "$story"         ]] || story=0 ;
   [[ -n "$zipfiles"      ]] || zipfiles=1 ;
   [[ -n "$srclang"       ]] || srclang="de" ;
   [[ -n "$basedir"       ]] || basedir="$b" ;
   [[ -n "$docroot"       ]] || docroot="$basedir/docroot" ;
   [[ -n "$locdir"        ]] || locdir="$basedir/local" ;
   [[ -n "$trtoutdir"     ]] || trtoutdir="$locdir/trtout" ;
   [[ -n "$trtindir"      ]] || trtindir="$locdir/trtin" ;
   [[ -n "$srcdir"        ]] || srcdir="$basedir/src/localization" ;
   [[ -n "$stylesrc"      ]] || stylesrc="$basedir/src/style" ;
   # pipe_nscnr_google normale Anwendung
   # pipe_nsbnr_google gepuffertes Lernen
   # pipe_nslnr_google hartes Lernen
   [[ -n "$trname"        ]] || trname="pipe_nscnr_google" ;
   [[ -n "$trlang"        ]] || set_default_trlang ;
   [[ -n "$trlang"        ]] || trlang=" \
     eu ca zh hr cs da nl en eo et fi fr gl el hu is ga it ja ko la lv lt  ms mt \
     no pl pt ro sk sl es sv th vi ";
   [[ -n "$storyptn"      ]] || storyptn="s\${sid}/story.xml.\${srclang}.";
   [[ "$ftpmode" == "put" ]] || [[ "$ftpmode" == "putnotex" ]] || ftpmode="putnewer" ;
   [[ -n "$runonce"       ]] || runonce="$srcdir/runonce";
   [[ $runonce =~ ^/      ]] || runonce="$srcdir/$runonce";
# Basque        eu
# Catalan       ca
# Chinese Simplified    zh
# Croatian      hr
# Czech         cs
# Danish        da
# Dutch         nl
# English       en
# Esperanto     eo
# Estonian      et
# Finnish       fi
# French        fr
# Galician      gl
# Greek         el
# Hungarian     hu
# Icelandic     is
# Irish         ga
# Italian       it
# Japanese      ja
# Korean        ko
# Latin         la
# Latvian       lv
# Lithuanian    lt
# Malay         ms
# Maltese       mt
# Norwegian     no
# Polish        pl
# Portuguese    pt
# Romanian      ro
# Slovak        sk
# Slovenian     sl
# Spanish       es
# Swedish       sv
# Thai  th
# Vietnamese    vi
} # set_defaults

# setzt die Default-Sprachen
set_default_trlang () {
   trlang="" ;
   local f;
   local l;
   for f in $docroot/local/local.xml.*.; do
      l=${f#$docroot/local/local.xml.};
      l=${l%%.*};
      [[ $l == $srclang ]] || trlang="$trlang $l";
   done;
} # set_default_trlang

# Zeigt eine kurze Hilfe an
show_help ()
{
   local cmd=${0#*/} ;
   set_defaults ;
   cat << .HELP ;
$cmd --version
$cmd --help
$cmd ([Aktion] | [Option])*  FILE ..

Aktionen
--reminter            Zwischendateien löschen
--skl                 Gerüstdateien erzeugen
--xliff               XLIFF-Dateien erzeugen
--mtt                 Übersetzungstexte für die maschinelle Übersetzung
--trt                 Textdateien zur menschlichen Übersetzung
--mtrans              Texte maschinell übersetzen
--merge               Übersetzten Text in XLIFF-Dateien einfügen
--finish              Übersetzte XLIFF und Skeleton zum Dokument zusammenfügen
--upload              Übersetzte Dateien hochladen
--once                ein Skript für jede zu übersetzende Datei ausführen

FILE                  Rel. Pfad einer zu übersetzenden Datei ohne Sprachsuffix
                      ($_argv)
Optionen
--[no_]verbose        Erhöht den Umfang der Ausgabe des Scripts ($verbose)
--[no_]overwrite      Existierende Dateien überschreiben ($overwrite)
--[no_]story          Positionsargumente als Bildergeschichten ($story)
--[no_]zipfiles       Dateien gzip-komprimieren ($zipfiles)
--srclang SRCLANG     Kürzel der Ausgangssprache ($srclang)
--basedir BASEDIR     Basisverzeichnis
                      ($basedir)
--docroot DOCROOT     Document Root des Servers
                      ($docroot)
--locdir LOCDIR       Verzeichnis für Lokalisierungs-Zwischendateien
                      ($locdir)
--trtoutdir TRTOUTDIR Verzeichnis der Dateien für die menschliche Übersetzung
                      ($trtoutdir)
--trtindir TRTINDIR   Verzeichnis der übersetzten Dateien zur Weiterverarbeitung
                      ($trtindir)
--srcdir SRCDIR       Verzeichnis der Skripte
                      ($srcdir)
--stylesrc STYLESRC   Verzeichnis der Stil-Quelldateien
                      ($stylesrc)
--trname TRNAME       Name der Übersetzungmaschine ($trname)
--trlang TRLANG       Zielsprachen ($trlang)
--storyptn STORYPTN   Muster eines Bildergeschichten-Pfads ($storyptn)
--ftpmode FTPMODE     putnewer, putnotex oder put ($ftpmode)         
--runonce RUNONCE     Skript, das für jede Datei ausgeführt wird
                      ($runonce)
.HELP
} # show_help

# Zeigt die Version an
show_version ()
{
   cat << .VERSION ;
web/src/localization/localize
Lokalisierung der Website http://kleider.herbaer.de
2015-01-09, Herbert Schiemann, h.schiemann@herbaer.de
GPL Version 2 oder neuer
.VERSION
} # show_version

# Variable und Zähler initialisieren
init_vars () {
   local v;
   declare_vars ;
   for v in $g_counters $g_variables $g_actions; do
      eval "$v=" ;
   done;
} # init_vars

# is_secure path/to/file
# Ist der Dateipfad sicher, d.h
# - Ist die Datei eine gewöhnliche Datei, lesbar und nicht leer?
# - Ist die Datei nicht im Wurzelverzeichnis?
# - Hat nur der Besitzer mehr als nur Leserecht für die Datei?
# - Hat nur der Besitzer Schreibrecht für das Verzeichnis?
# - Ist der Besitzer der Datei auch der Besitzer des Verzeichnisses?
is_secure ()
{
   local chk ;
   (( verbose )) && echo "prüfe Sicherheit $1" ;
   [[ -f "$1" && -r "$1" && -s "$1" ]] || return 1 ;
   [[ $(stat --format=%A "$1") =~ ^.{4}[r-]{6}$ ]] || return 1 ;
   chk=${1%/*};
   [[ -n "$chk" ]] || return 1;
   [[ "$chk" != "$1" ]] || chk=$(pwd);
   [[ -d "$chk" ]] || return 1 ;
   [[ $(stat --format=%u "$chk") == $(stat --format=%u "$1") ]] || return 1;
   if [[ $(stat --format=%A "$chk") =~ ^.{4}[rx-]{6}$ ]]; then
      (( verbose )) && echo "Datei $1 scheint sicher";
      return 0;
   fi;
   return 1;
} # is_secure

# read_configuration file
# file: Dateiname ("rc"-Datei) ohne Endung ".rc"
read_configuration ()
{
   local path=$g_configpath ;
   local file=$1.rc ;
   local dir;
   while [[ -n "$path" ]]; do
      dir=${path%%:*} ;
      if is_secure "$dir/$file"; then
         source "$dir/$file";
         return 0;
      fi;
      path=${path#*:} ;
      [[ "$dir" == "$path" ]] && break;
   done;
   return 1;
} # read_configuration

# Liste der existierenden Konfigurationen
list_configurations ()
{
   local path=$g_configpath ;
   local cfg=" ";
   local dir;
   local f;
   while [[ -n "$path" ]]; do
      dir=${path%%:*} ;
      for f in $dir/*.rc; do
         [[ -f $f && -r $f && -s $f ]] || continue ;
         f=${f##*/};
         f=${f%.rc};
         [[ -n $f ]] || continue ;
         [[ "$cfg" == "${cfg#* $f }" ]] && cfg="$cfg $f " ;
      done ;
      path=${path#*:} ;
      [[ "$dir" == "$path" ]] && break;
   done;
   cfg=${cfg# };
   echo ${cfg% };
} # list_configurations

# Argumente verarbeiten
read_args ()
{
   local wd ;
   local lastwd ;
   local var ;
   local ok ;

   has_actions=0 ;
   for wd in "$@"; do
      if [[ "$lastwd" = "--" ]]; then
          _argv="$_argv $wd";
      elif [[ -n "$lastwd" ]]; then
         if [[ "$wd" =~ ^[\ a-zA-Z0-9./_#-]+$ ]]; then
            if [[ "$lastwd" == "rc" && -n "$g_configpath" ]]; then
               if ! read_configuration $wd; then
                  (( verbose )) && echo "Kann Konfiguration $wd nicht lesen" ;
                  exit 10 ;
               fi ;
            else
               ok=0 ;
               for var in $g_variables; do
                  if [[ "$var" == "$lastwd" ]]; then
                     (( ++ok )) ;
                     eval "$var=\"$wd\"" ;
                     break ;
                  fi ;
               done ;
               if (( ! ok )); then
                  (( verbose )) && echo "Unbekannte Option --$lastwd $wd" ;
                  exit 11 ;
               fi ;
            fi ;
         else
            (( verbose )) && echo "Ungültiger Optionswert --$lastwd $wd" ;
            exit 12;
         fi;
         lastwd= ;
      else
         case "$wd" in
            --version )
               show_version ;
               exit 0 ;
               ;;
            --help )
               show_version ;
               show_help ;
               exit 0 ;
               ;;
            -- )
               if [[ -n "$_argv" ]]; then
                  lastwd=--;
                  continue;
               else
                  (( verbose )) && echo "Ungültige Option $wd" ;
                  exit 13 ;
               fi ;
               ;;
            --* )
               if [[ "$wd" =~ ^--[a-z][a-z0-9_]*$ ]]; then
                  lastwd=${wd#--} ;
                  ok=0 ;
                  for var in $g_counters ; do
                     if   [[ "$lastwd" == $var    ]]; then
                        eval "(( ++$lastwd ))" ;
                     elif [[ "$lastwd" == "no_$var" ]]; then
                        eval "${lastwd#no_}=0" ;
                     else
                        continue;
                     fi;
                     (( ++ok )) ;
                     break ;
                  done;
                  if (( !ok )); then
                     for var in $g_actions; do
                        if [[ "$lastwd" == "$var" ]]; then
                           eval "(( ++$var ))" ;
                           (( ++ok ));
                           has_actions=1;
                           break;
                        elif [[ "$lastwd" == "no_$var" ]]; then
                           eval "(( ++no_$var ))" ;
                           (( ++ok ));
                           break;
                        fi;
                     done;
                  fi;
                  (( ok )) && lastwd=;
               else
                  (( verbose )) && echo "Ungültige Option $wd" ;
                  exit 14 ;
               fi ;
               ;;
            * )
               if [[ -n $_argv ]]; then
                   _argv="$_argv $wd";
               else
                   (( verbose )) && echo "Ungültige Option $wd" ;
                   exit 15 ;
               fi;
               ;;
         esac ;
      fi ;
   done ;
   if [[ -n $lastwd && "$lastwd" != "--" ]]; then
      (( verbose )) && echo "Unverarbeitete Option --$lastwd";
      exit 16 ;
   fi ;
   [[ "$_argv" =~ ^[[:space:]]+$ ]] && _argv="" ;
} # read_args

# Aktionen ausführen
run_actions ()
{
   local act ;
   for act in $g_actions; do
     eval "(( ! has_actions && ! no_$act || $act )) && process_$act";
   done;
} # run_actions

# show_variables VARNAME1 VARNAME2
# Werte der Variablen anzeigen
show_variables ()
{
   local v ;
   for v in $g_counters $g_variables $g_actions $1; do
      eval "echo \"$v = \$$v\"" ;
   done;
} # show_variables

# Können die Eingabedateien gelesen werden?
# check_infiles first/path/to/file path/to/second_file ;
check_infiles ()
{
   local f ;
   for f in "$@"; do
      if [[ ! -f "$f" ]]; then
         (( verbose )) && echo "\"$f\" ist keine gewöhnliche Datei";
         return 1;
      fi;
      if [[ ! -s "$f" ]]; then
         (( verbose )) && echo "\"$f\" ist leer";
         return 1;
      fi;
      if [[ ! -r "$f" ]]; then
         (( verbose )) && echo "Kann Datei \"$f\" nicht lesen";
         return 1;
      fi;
   done;
   return 0;
} # check_infiles

# Können die Ausgabedateien erstellt werden?
# erstellt fehlende Verzeichnisse und löscht existierende Dateien
# nach Maßgabe der Variablen overwrite
# check_outfiles first/path/to/file path/to/second_file ;
check_outfiles ()
{
   local fp;
   local dir;
   local verb;
   (( verbose )) && verb=--verbose ;
   for fp in "$@"; do
      if [[ ! -e $fp ]]; then
         dir=${fp%/*};
         if [[ -n $dir && ! -e $dir ]]; then
            mkdir -p $verb $dir ;
            if [[ ! -d $dir ]]; then
               (( verbose )) && echo "$dir ist kein Verzeichnis";
               return 1;
            fi;
         fi;
      elif [[ -d $fp ]]; then
         (( verbose )) && echo "$fp ist ein Verzeichnis";
         return 1;
      elif (( overwrite )); then
         (( verbose )) && echo "lösche $fp";
         rm $fp;
      else
         (( verbose )) && echo "$fp existiert";
         return 1;
      fi;
      (( verbose )) && echo "$fp";
   done;
   return 0;
} # check_outfiles

# Sind die Dateien ausführbar?
# check_executeable first/path/to/script path/to/second_srcipt ;
check_executeable ()
{
   local f ;
   for f in "$@"; do
      if [[ ! -f "$f" ]]; then
         (( verbose )) && echo "$f\" ist keine gewöhnliche Datei";
         return 1;
      fi;
      if [[ ! -x "$f" ]]; then
         (( verbose )) && echo "$f\" ist keine ausführbare Datei";
         return 1;
      fi;
   done;
   return 0;
} # check_executeable

# Hilfsfunktion: Zwischendateien zu einer Quelldatei löschen
# procfile_reminter srcfile
procfile_reminter ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_reminter $q" ;
   local verb= ;
   (( verbose )) && verb=--verbose ;
   [[ -e $q ]] || q=$q. ;
   check_infiles $q || return;
   q=${q%.} ;
   [[ $q =~ \.$srclang$ ]] || return ;   # Kennung der Quellsprache?
   local s=$locdir/${q#$docroot/} ;      # Lokalisierungsverzeichnis statt docroot
   [[ -e $s.skl ]] && rm $verb $s.skl ;
   [[ -e $s.xlf ]] && rm $verb $s.xlf ;
   [[ -e $s.mtt ]] && rm $verb $s.mtt ;
   s=${s%.$srclang};
   local l;
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      [[ -e $s.$l.rtr ]] && rm $verb $s.$l.rtr ;
      [[ -e $s.$l.xlf ]] && rm $verb $s.$l.xlf ;
   done;
} # procfile_reminter

# Hilfsfunktion: eine einzelne Gerüstdatei erzeugen
# procfile_skl srcfile
procfile_skl ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_skl $q" ;
   [[ -e $q ]] || q=$q. ;
   check_infiles $q || return;
   local s=$locdir/${q#$docroot/} ;      # Lokalisierungsverzeichnis statt docroot
   local ns ;                            # Namensraum des Wurzelelements der Quelldatei
   local ft ;                            # Kennung des Dateityps
   local sn ;                            # Name des Stylesheets
   s=${s%.}.skl ;                        # Suffix .skl für Gerüstdateien
   [[ $s =~ \.$srclang\. ]] || return ;  # Kennung der Quellsprache?
   check_outfiles $s        || return ;  # Kann die Gerüstdatei erstellt werden?
   ns=$(xsltproc $srcdir/xmlnsss.xslt $q) ;
   sn=${ns#*|};
   ns=${ns%|*};
   if [[ $ns == "http://www.w3.org/1999/xhtml" ]]; then
      ft=xhtml ;
   elif [[ $ns == "http://herbaer.de/xmlns/20141210/localization" ]]; then
      ft=local ;
   elif [[ $ns == "http://herbaer.de/xmlns/20080705/imgshow" ]]; then
      ft=imgshow ;
   else
      (( verbose )) && echo "Kann XML-Namensraum $ns nicht übersetzen";
      return ;
   fi;
   local tpx=$srcdir/${sn}_pre.xslt ;    # Vor-Transformation abhängig vom Stylesheet
   local tpf=$srcdir/${ft}_pre.xslt ;    # Vor-Transformation abhängig vom Namensraum
   local t=$srcdir/${ft}_clfy.xslt ;     # 1. Schritt zur Erzeugung der Gerüst-Datei
   local t2=$srcdir/skeleton_2.xslt ;    # 2. Schritt
   local c="cat $q";
   [[ -f $tpx ]] && c="$c | xsltproc $tpx -" ;
   [[ -f $tpf ]] && c="$c | xsltproc $tpf -" ;
   eval "$c | xsltproc $t - | xsltproc $t2 - > $s" ;
} # procfile_skl 

# Hilfsfunktion: eine einzelne XLIFF-Datei erzeugen
# procfile_xliff srcfile
procfile_xliff ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_xliff $q" ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local ow=$overwrite ;
   local s=$locdir/$b.skl ;              # Gerüstdatei
   local x=$locdir/$b.xlf ;              # XLIFF-Datei
   check_outfiles $x || return ;
   if ! check_infiles $s; then
      overwrite=0 ;
      procfile_skl $q ;
      overwrite=$ow;
      check_infiles $s || return 1 ;
   fi;
   local tx=$srcdir/skeleton_xliff.xslt ;
   local tc=$srcdir/xliff_clean.xslt ;
   xsltproc --stringparam p_file "$b" $tx $s | xsltproc -o $x $tc - ;
} # procfile_xliff

# Hilfsfunktion:
# eine einzelne Übersetzungstext-Datei für die automatische Übersetzung
# procfile_mtt srcfile
procfile_mtt ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_mtt $q" ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local ow=$overwrite ;
   local x=$locdir/$b.xlf ;              # XLIFF-Datei
   local m=$locdir/$b.mtt ;              # Übersetzungstext-Datei
   check_outfiles $m || return ;
   if ! check_infiles $x; then
      overwrite=0 ;
      procfile_xliff $q ;
      overwrite=$ow;
      check_infiles $x || return 1 ;
   fi;
   local tt=$srcdir/xliff_txt.xslt ;
   xsltproc -o $m $tt $x ;
} # procfile_mtt

# Hilfsfunktion:
# Übersetzungstext-Dateien für die menschliche Übersetzung
# zu einer einzelnen Quelldatei
# procfile_trt srcfile
procfile_trt ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_trt $q" ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local ow=$overwrite ;
   local x=$locdir/$b.xlf ;              # XLIFF-Datei
   if ! check_infiles $x; then
      overwrite=0 ;
      procfile_xliff $q ;
      overwrite=$ow;
      check_infiles $x || return 1 ;
   fi;
   local o=$trtoutdir/${b%.$srclang} ;   # Ausgabedatei ohne Sprach/Typ-Suffix
   local l ;                             # Zielsprache
   local o2;                             # Ausgabedatei
   local tt=$srcdir/xliff_txt.xslt ;
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      o2=$o.$l.txt ;
      check_outfiles $o2 || continue ;
      xsltproc --stringparam p_targetlang $l $tt $x > $o2 ;
   done;
} # procfile_trt

# Hilfsfunktion:
# maschinelle Übersetzungen zu einer einzelnen Quelldatei
# procfile_mtrans srcfile
procfile_mtrans ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_mtrans $q" ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local ow=$overwrite ;
   local m=$locdir/$b.mtt ;              # XLIFF-Datei
   if ! check_infiles $m; then
      overwrite=0 ;
      procfile_mtt $q ;
      overwrite=$ow;
      check_infiles $m || return 1 ;
   fi;
   local verb;
   (( verbose )) && verb=--verbose ;
   local o=$locdir/${b%.$srclang} ;      # Ausgabedatei ohne Sprach/Typ-Suffix
   local p1=$srcdir/mttext.pl ;
   local p2=$srcdir/resstruct.pl ;
   local l ;                             # Zielsprache
   local o2 ;                            # Ausgabedatei
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      o2=$o.$l.rtr ;
      check_outfiles $o2 || continue ;
      $p1 $verb --in $m --srclang $srclang --tgtlang $l --trname $trname \
      | $p2 $verb --tgtlang $l > $o2                                     ;
   done;
} # procfile_mtrans

# Hilfsfunktion:
# rtr-Datei und XLIFF-Datei zusammenfügen
# procfile_merge srcfile
procfile_merge ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_merge $q" ;
   local verb ;
   (( verbose )) && verb=--verbose ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local bb=${b%.$srclang} ;             # relativer Dateipfad ohne Sprach/Typ-Suffix
   local ow=$overwrite ;
   local tr=$srcdir/xliff_merge_rtr.xslt ;
   local l ;                             # Zielsprache
   local rt ;                            # übersetzte Übersetzungstext-Datei
   local x=$locdir/$b.xlf ;              # XLIFF - Datei der Quellsprache
   local o ;                             # Ausgabe: übersetzte XLIFF-Datei
   if ! check_infiles $x ; then
      overwrite=0 ;
      procfile_xliff $q ;
      overwrite=$ow;
      check_infiles $x || return ;
   fi;
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      o=$locdir/$bb.$l.xlf ;
      check_outfiles $o || continue ;
      if check_infiles $trtindir/$bb.$l.rtr ; then
         rt=$trtindir/$bb.$l.rtr ;
      elif check_infiles $trtindir/$bb.$l.txt ; then
         rt=$trtindir/$bb.$l.rtr ;
         if check_outfiles $rt ; then
            $srcdir/resstruct.pl $verb --in $trtindir/$bb.$l.txt --tgtlang $l > $rt ;
         else
            rt=$locdir/$bb.$l.rtr ;
         fi ;
      else
         rt=$locdir/$bb.$l.rtr ;
      fi;
      if ! check_infiles $rt ; then
         overwrite=0 ;
         procfile_mtrans $q ;
         overwrite=$ow;
         check_infiles $rt || continue ;
      fi;
      xsltproc --stringparam p_rtrfile $rt $tr $x > $o ;
   done;
} # procfile_merge

# Hilfsfunktion:
# Skeleton und übersetzte XLIFF-Datei zum übersetzten Dokument zusammenfügen
# procfile_finish srcfile
procfile_finish ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_finish $q" ;
   local b=${q#$docroot/} ;              # relativer Dateipfad ohne Punkt am Ende
   b=${b%.} ;
   local bb=${b%.$srclang} ;             # relativer Dateipfad ohne Sprach/Typ-Suffix
   local ow=$overwrite ;
   local tr=$srcdir/skeleton_merge_xliff.xslt ;
   local s=$locdir/$b.skl ;              # Skeleton-Datei
   local x;                              # übersetzte XLIFF-Datei
   local o;                              # übersetztes Dokument

   local tp ;                            # Kennung für nachgeordnete Transformation

   local ns ;                            # XML-Namensraum
   local sn ;                            # Stylesheet-Basisname
   local tf ;                            # Kennung des Dateityps
   if ! check_infiles $s; then
      overwrite=0 ;
      procfile_skl $q;
      overwrite=$ow;
      check_infiles $s || return ;
   fi;

   ns=$(xsltproc $srcdir/xmlnsss.xslt $q) ;
   sn=${ns#*|};
   ns=${ns%|*};
   if [[ $ns == "http://www.w3.org/1999/xhtml" ]]; then
      ft=xhtml ;
   elif [[ $ns == "http://herbaer.de/xmlns/20141210/localization" ]]; then
      ft=local ;
   elif [[ $ns == "http://herbaer.de/xmlns/20080705/imgshow" ]]; then
      ft=imgshow ;
   else
      ft=none ;
      return ;
   fi;
   local tpx=$srcdir/${sn}_post.xslt ;  # Nach-Transformation abhängig vom Stylesheet
   local tpf=$srcdir/${ft}_post.xslt ;  # Nach-Transformation abhängig vom Namensraum
   # Befehl zur Transformation
   local c="xsltproc             \
     --stringparam p_xliff   \$x \
     --stringparam p_trglang \$l \
     $tr $s                      ";
   # Parameter für die Nach-Transformation
   local pparm="                                            \
     --stringparam p_localsrc $stylesrc/local.xml.$srclang  \
     --stringparam p_localweb $docroot/local/local.xml.\$l. "
   [[ -f $tpf ]] && c="$c | xsltproc $pparm $tpf -" ;
   [[ -f $tpx ]] && c="$c | xsltproc $pparm $tpx -" ;
   if check_executeable $stylesrc/rmxmlns.pl ;
   then
      c="$c | $stylesrc/rmxmlns.pl" ;
      (( verbose )) && c="$c --verbose";
   fi;
   c="$c > \$o.";
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      x=$locdir/$bb.$l.xlf ;
      if ! check_infiles $x ; then
         overwrite=0 ;
         procfile_merge $q ;
         overwrite=$ow;
         check_infiles $x || continue ;
      fi;
      o=$docroot/$bb.$l ;
      check_outfiles $o. || continue ;
      eval $c;
      if (( zipfiles )); then
         [[ -f $o.gz ]] && rm $o.gz ;
         gzip --best --stdout $o. > $o.gz ;
      fi;
      continue;
   done;
} # procfile_finish

procfile_upload ()
{
   local q=$1 ;              # Quelldatei im Dokument-Verzeichnis
   local b=${q#$docroot/} ;  # relativer Dateipfad
   b=${b%.} ;                # ohne Punkt
   b=${b%.$srclang} ;        # ohne Sprach-Suffix
   local o;                  # übersetztes Dokument
   for l in $trlang; do
      [[ $l == $srclang ]] && continue;
      o=$docroot/$b.$l ;
      [[ -f $o.   ]] && echo "$ftpmode $b.$l.";
      [[ -f $o.gz ]] && echo "$ftpmode $b.$l.gz";
   done;   
} # upload

# Hilfsfunktion:
# Einmal-Programm aufrufen
# procfile_once srcfile
procfile_once ()
{
   local q=$1 ;                          # Quelldatei im Dokument-Verzeichnis
   (( verbose )) && echo "procfile_once $q" ;
   local verb;
   (( verbose )) && verb=--verbose ;
   local ow ;
   if [[ -n $overwrite ]] ;
   then
      if (( overwrite )) ;
      then
         ow=--overwrite ;
      else
         ow=--no_overwrite ;
      fi;
   fi;
   $runonce $verb $ow --trlang "$trlang" --srclang $srclang --srcdir $srcdir $q;
} # procfile_once

# Hilfsfunktion: die Verarbeitungsschleife
# procloop STEP $docroot/subdir
# STEP: skl ...
procloop ()
{
   local s=$1
   local dir=$2 ;
   (( verbose )) && echo "procloop $s $dir";
   local f  ; # Dateipfad im Verzeichnis
   local n  ; # Dateiname
   for f in $dir/* ; do
      if [[ -d $f ]]; then
         n=${f##*/};
         [[ $n == images   ]] && continue;
         [[ $n == smallimg ]] && continue;
         [[ $n == thumbs   ]] && continue;
         [[ $n == montage  ]] && continue;
         procloop $s $f;
         continue;
      elif [[ $f =~ \.$srclang\.$ ]]; then
         procfile_$s $f;
      fi;
   done;
} # procloop

# Hilfsfunktion: einzelne Datei verarbeiten oder Schleife
# Falls (( story )), werden alle Bildergeschichten verarbeitet
# proctry STEP
proctry ()
{
   local s=$1 ;
   local f ;
   local sid ;
   local ss ;
   (( verbose )) && echo "proctry $s";
   if [[ -n "$_argv" ]]; then
      if (( story )); then
         for sid in $_argv; do
            eval f=$docroot/$storyptn;
            procfile_$s $f ;
         done;
      else
         for f in $_argv; do
            if [[ -d "$docroot/$f" ]]; then
               procloop $s $docroot/$f ;
            elif [[ -f $docroot/$f.xhtml.$srclang. ]]; then
               procfile_$s $docroot/$f.xhtml.$srclang. ;
            else
               procfile_$s $docroot/$f.$srclang. ;
            fi;
         done;
      fi;
   elif (( story )); then
      sid='*';
      eval ss=$storyptn;
      for f in $docroot/$ss; do
         procfile_$s $f ;
      done;
   else 
      procloop $s $docroot ;
   fi;
} # proctry

# Zwischendateien löschen
process_reminter () 
{
   (( verbose )) && echo "process_reminter" ;
   proctry reminter ;
}

# Gerüstdateien erzeugen
process_skl ()
{
   (( verbose )) && echo "process_skl" ;
   if check_infiles               \
      $srcdir/imgshow_clfy.xslt   \
      $srcdir/local_clfy.xslt     \
      $srcdir/xhtml_clfy.xslt     \
      $srcdir/skeleton_2.xslt     ;
   then
      proctry skl ;
   else
      (( verbose )) && echo "Kann skl-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_skl

# XLIFF-Dateien erzeugen
process_xliff ()
{
   (( verbose )) && echo "process_xliff" ;
   if check_infiles               \
      $srcdir/imgshow_clfy.xslt   \
      $srcdir/local_clfy.xslt     \
      $srcdir/xhtml_clfy.xslt     \
      $srcdir/skeleton_2.xslt     \
      $srcdir/skeleton_xliff.xslt \
      $srcdir/xliff_clean.xslt    ;
   then
      proctry xliff ;
   else
      (( verbose )) && echo "Kann XLIFF-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_xliff

# Übersetzungstext-Dateien für die maschinelle Übersetzung
process_mtt ()
{
   (( verbose )) && echo "process_mtt" ;
   if check_infiles               \
      $srcdir/imgshow_clfy.xslt   \
      $srcdir/local_clfy.xslt     \
      $srcdir/xhtml_clfy.xslt     \
      $srcdir/skeleton_2.xslt     \
      $srcdir/skeleton_xliff.xslt \
      $srcdir/xliff_clean.xslt    \
      $srcdir/xliff_txt.xslt      ;
   then
      proctry mtt ;
   else
      (( verbose )) && echo "Kann mtt-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_mtt

# Übersetzungstext-Dateien für die menschliche Übersetzung
process_trt ()
{
   (( verbose )) && echo "process_mtt" ;
   if check_infiles               \
      $srcdir/imgshow_clfy.xslt   \
      $srcdir/local_clfy.xslt     \
      $srcdir/xhtml_clfy.xslt     \
      $srcdir/skeleton_2.xslt     \
      $srcdir/skeleton_xliff.xslt \
      $srcdir/xliff_clean.xslt    \
      $srcdir/xliff_txt.xslt      ;
   then
      proctry trt ;
   else
      (( verbose )) && echo "Kann trt-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_trt

# maschinelle Übersetzung
process_mtrans ()
{
   (( verbose )) && echo "process_mtrans" ;
   if check_executeable     \
      $srcdir/mttext.pl     \
      $srcdir/resstruct.pl  ;
   then
      local verb;
      (( verbose )) && verb=--verbose ;
      [[ $trname =~ "pipe" ]] && $srcdir/pipe_srv.pl $verb &
      proctry mtrans ;
      [[ $trname =~ "pipe" ]] && $srcdir/pipe_srv_stop.pl $verb ;
   else
      (( verbose )) && echo "Kann rtr-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_mtrans

# rtr-Dateien und XLIFF-Dokumente zusammenfügen
process_merge ()
{
   (( verbose )) && echo "process_merge" ;
   if check_infiles $srcdir/xliff_merge_rtr.xslt; then
      proctry merge ;
   else
      (( verbose )) && echo "Kann übersetzte XLIFF-Dateien nicht erzeugen";
      return 1;
   fi;
} # process_merge

# Übersetzte XLIFF und Skeleton zum Dokument zusammenfügen
process_finish ()
{
   (( verbose )) && echo "process_finish" ;
   if check_infiles $srcdir/skeleton_merge_xliff.xslt; then
      proctry finish ;
   else
      (( verbose )) && echo "Kann Skeleton- und XLIFF-Dateien nicht zusammenfügen";
      return 1;
   fi;
}

# Übersetzte Dateien hochladen
process_upload ()
{
   (( verbose )) && echo "process_upload" ;
   if check_executeable \
      $srcdir/ftp.pl    ;
   then
      local verb;
      (( verbose )) && verb=--verbose
      local v=$verbose;
      verbose=0 ;
      { proctry upload; }                         \
      | $srcdir/ftp.pl $verb --putbase $docroot   ;
      verbose=$v ;
   fi;
} # process_upload

# Einmal-Programm ausführen
process_once ()
{
   (( verbose )) && echo "process_once" ;
   if check_executeable $runonce ;
   then
      proctry once ;
   fi;
} # process_mtrans

# Sicherheit
export PATH=/bin:/usr/bin ;
IFS=$' \t\n' ;

init_vars ;

set -o noclobber ;   # existierende Dateien werden nicht überschrieben
shopt -s extglob nullglob ;

read_args "$@" ;
set_defaults ;

(( verbose > 1 )) && show_variables;

run_actions ;
exit 0;
# end of file KLEIDER/web/src/localization/localize
