#!/bin/bash
#
# gettext-singularity - gettext wrapper customized to Endgame: Singularity
#
#    Copyright (C) 2012 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program. See <http://www.gnu.org/licenses/gpl.html>
#
# Uses GNU's gettext tools to parse source code files for translatable
# strings, generate a template .POT file in data/messages.pot, update
# existing data/messages_ll_CC.po files, check them for syntax errors,
# and create a new one if requested.
# It is smart enough to properly find the source code files and output dir
# regardless of the current dir on invocation, provided this script is in
# tools/ dir of E:S tree source
#
# All gettext options are already properly set for the Endgame: Singularity
# project, so this script can be invoked without any options.

usage() {
cat <<- USAGE
	Usage: $myname [options] [--new LANG | --check FILE]
USAGE
if [[ "$1" ]] ; then
	cat <<- USAGE
		Try '$myname --help' for more information.
	USAGE
	exit 1
fi
cat <<-USAGE

	gettext wrapper customized to Endgame: Singularity project

	Options:
	-h|--help     - show this page
	-q|--quiet    - suppress non-error, informative messages

	--new LANG    - create a new PO file. LANG must be in ll or
	                ll_CC format. Examples: en, en_US, pt, pt_BR

	--check FILE  - test FILE for syntax errors in the PO format and
	                print translation statistics (regardless of -q)
	                If used, does not perform any other actions.

	--dir         - The root directory of E:S where data file and i18n
                    are found. Default: __file__/../..

	--catalog     - The catalog to update (messages/data_str). Default: messages


	By default this command reside in i18n/utils/ dir of E:S source tree,
	so it can automatically find the source code files and output dir.

	Uses the following GNU's gettext tools:
	- xgettext : to create or update data/messages.pot template
	- msgmerge : to update existing data/messages_ll_CC.po files
	- msgfmt   : to check existing PO files for syntax errors
	- msginit  : to create a new data/messages_ll_CC.po file

	Copyright (C) 2012 Rodrigo Silva (MestreLion) <linux@rodrigosilva.com>
	License: GPLv3 or later. See <http://www.gnu.org/licenses/gpl.html>
USAGE
exit 0
}
fatal()   { printf "%s: error: %s\n" "$myname" "$1" >&2 ; exit "${2:-1}" ; }
argerr()  { printf "%s: %s\n" "$myname" "${1:-error}" >&2 ; usage 1 ; }
invalid() { argerr "invalid option: $1" ; }
missing() { argerr "missing ${2:+$2 }operand${1:+ in $1}." ; }
message() { ((verbose)) && printf "%s\n" "$1"; }
rel()     { python -c "import os.path; print os.path.relpath('$1','$dir')"; }

check() {
	local stats=()
	(($1)) && stats=( --statistics )
	[[ "$2" ]] || missing "--check" "FILE"
	msgfmt --check-accelerators    \
	       --output-file=/dev/null \
	       --check                 \
	       "${stats[@]}"           \
	       "$2"
}


PYTHON=${PYTHON:-python3}
myname="${0##*/}"
dir="$PWD"
mydir=$(dirname "$(readlink -f "$0")")
esdir="${mydir}/.."
codedir="${esdir}/singularity/code"
datadir="${esdir}/singularity/data"
i18ndir="${esdir}/singularity/i18n"
catalog="messages"
verbose=1
new=0
quiet=()

# Loop options
while (( $# )); do
	case "$1" in
	-h|--help      ) usage                          ;;
	-q|--quiet     ) verbose=0 ; quiet=( -q )       ;;
	   --new=*     ) new=1 ; lang="${1#*=}"         ;;
	   --new       ) new=1 ; shift ; lang="$1"      ;;
	   --check=*   ) check 1 "${1#*=}" ; exit       ;;
	   --check     ) shift ; check 1 "$1"; exit     ;;
	   --dir=*     ) esdir="${1#*=}"                ;;
	   --dir       ) shift ; esdir="$1"             ;;
       --catalog=* ) catalog="${1#*=}"              ;;
       --catalog   ) shift ; catalog="$1"           ;;
	*              ) invalid "$1"                   ;;
	esac
	shift
done

((new)) && {
	[[ "$lang" ]] || missing "--new" "LANG"
	[[ "$lang" =~ ^[[:lower:]][[:lower:]](_[[:upper:]][[:upper:]])?$ ]] ||
		invalid "LANG format must be 'll' or 'll_CC'"
}

if [ -d "$esdir" ]; then
	message "E:S root dir: $(rel "$esdir")"
else
	invalid "E:S root dir $(rel "$esdir") does not exist"
fi

tempfile=$(mktemp) || fatal "could not create temporary file"
trap 'rm -f -- "$tempfile"' EXIT

# Create or update the POT template from source code
if [[ $catalog == "messages" ]]; then
    potfile="${i18ndir}/messages.pot"
    pofilename="messages.po"
    message "updating template $(rel "$potfile") from sources in $(rel "$esdir")/code"

    cd "${codedir}" || fatal "could not access source code dir '${esdir}/code'"
    xgettext --language=Python \
             --from-code=UTF-8 \
             --force-po        \
             --add-comments    \
             --package-name=singularity \
             --package-version=$(awk -F'['\'\"']' '/^version *= */{print $2}' g.py) \
             --msgid-bugs-address="endgame-singularity-dev@googlegroups.com" \
             --output="$tempfile" \
             --files-from=<(find -type f -name "*.py")
    cd "$dir"
elif [[ $catalog == "data_str" ]] || [[ $catalog == "story" ]]; then
    potfile="${i18ndir}/${catalog}.pot"
    pofilename="${catalog}.po"
    message "updating template $(rel "$potfile") from string files in $(rel "$esdir")/data"

    cd "${codedir}" || fatal "could not access source code dir '${esdir}/data'"
    PYTHONPATH="${esdir}" "${PYTHON}" "${mydir}/data-translations.py" --catalog "${catalog}" -o $tempfile
    cd "$dir"
else
    invalid "Catalog $(rel "$catalog") does not exist"
fi

# Fix POT, as --from-code is currently ignored for Python
sed -i '/^"Content-Type:/s/CHARSET/UTF-8/;/^#, fuzzy$/d' "$tempfile"

# Compare new POT with existing one. Did only date change?
if [[ -f "$potfile" ]] &&
   diff -I '^"POT-Creation-Date:' "$tempfile" "$potfile" > /dev/null
then
	message "template was already up-to-date"
else
	mv "$tempfile" "$potfile" || fatal "could update POT file"
fi

# Update existing PO files with new entries
pofiles=( "$i18ndir"/lang_*/"$pofilename" )
if [[ ${pofiles[0]} != "$i18ndir/lang_"'*'"/$pofilename" ]]; then
  for pofile in "${pofiles[@]}"; do
    message "updating translation $(rel "$pofile")"
    msgmerge --no-fuzzy-matching \
             --backup=off        \
             --previous          \
             --update            \
             "${quiet[@]}"       \
             "$pofile"           \
             "$potfile"
    check $verbose "$(rel "$pofile")"
  done
fi

# Create a new PO file, if requested
[[ "$lang" ]] && {
	output_dir="${i18ndir}/lang_${lang}"
	output="$output_dir/$pofilename"
	[[ -f "$output" ]] && fatal "translation $(rel "$output") already exists!"
	[[ -d "$output_dir" ]] || mkdir -p "$output_dir"
	message "creating blank translation $(rel "$output")"
	msginit --input="$potfile"               \
		--output-file="$(rel "$output")" \
		--locale="${lang}.UTF-8"

	# Ask user and add Language-Native-Name header
	# So much work for so little gain...
	name=$(awk -F': |"|\\\\| <' '$2=="Language-Team"{print $3}' "$output")
	tempfile=$(mktemp) || fatal "could not create temporary file"
	trap 'rm -f -- "$tempfile"' EXIT
	awk -v name="$name" -F': |"|\\\\| <' '$2=="Language-Team"{
		print "\"Language-Name: " name  "\\n\""
		print "\"Language-Native-Name: LANGUAGE\\n\""
		} 1' "$output" > "$tempfile"
	mv "$tempfile" "$output" || fatal "could update PO file"
	native=""
	echo
	while [[ -z "$native" ]]; do
		read -r -p "What is this language name in ${name}? " native
	done
	tempfile=$(mktemp) || fatal "could not create temporary file"
	trap 'rm -f -- "$tempfile"' EXIT
	awk -v native="$native" -F': |"|\\\\' '$2=="Language-Native-Name"{
		sub(/LANGUAGE/,native)} 1' "$output" > "$tempfile"
	mv "$tempfile" "$output" || fatal "could update PO file"
	message "$(rel "$output") created and ready to be translated"
}
