#!/usr/bin/env bash
define() { IFS='\n' read -r -d '' ${1} || true ; }
myname="${0##*/}"
define pod <<"=cut"
=head1 NAME
xlate - translate CLI front-end for App::Greple::xlate module
=head1 SYNOPSIS
xlate [ options ] -t lang file [ greple options ]
-h help
-v show version
-d debug
-n dry-run
-a use API
-c just check translation area
-r refresh cache
-s silent mode
-e # translation engine (default "deepl")
-p # pattern to determine translation area
-w # wrap line by # width
-o # output format (default "xtxt", or "cm", "ifdef")
-f # from lang (ignored)
-t # to lang (required, no default)
-m # max length per API call
-l # show library files (XLATE.mk, xlate.el)
-- terminate option parsing
Make options
-M run make
-n dry-run
Docker options
-G mount git top-level directory
-B run in non-interactive (batch) mode
-R mount read-only
-E # specify environment variable to be inherited
-I # specify altanative docker image (default: tecolicom/xlate:version)
# -T # specify docker image version
# -L use the latest version
# -V # specify directory to be mounted (default: current directory)
-D * run xlate on the container with the rest parameters
-C * run following command on the container, or run shell
N.B. -D/-C terminates option parsing
Control Files:
*.LANG translation languates
*.FORMAT translation foramt (xtxt, cm, ifdef)
*.ENGINE translation engine (deepl or gpt3)
# marked as # options are experimental
=head1 VERSION
Version 0.3201
=cut
my_version=$(awk '$1=="Version"{print $2; exit}' <<< "$pod")
git_topdir() {
[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == true ] || return
local dir="$(git rev-parse --show-superproject-working-tree)"
[ "$dir" ] || dir="$(git rev-parse --show-toplevel)"
echo -n $dir
}
env_pattern() {
local name
while (( $# > 0 ))
do
name+="${name:+\\|}$1"
shift
done
echo -n ${name:+"^\\($name\\)="}
}
get_ip() {
local ip=$(ifconfig 2>/dev/null | awk '/inet /{print $2}' | tail -1)
echo -n $ip
}
repository=tecolicom/xlate
hostname=xlate
interactive=yes
version=
workdir=/work
mount_mode=rw
read_only=
display=
[ -e ${localtime:=/etc/localtime} ] || localtime=
DEFAULT_ENV="$(sed 's/ */\n/g' <<-EOF
LANG TZ
LESS=-cR LESSANSIENDCHARS=mK
HTTP_PROXY HTTPS_PROXY
http_proxy https_proxy
TERM_PROGRAM TERM_BGCOLOR COLORTERM
DEEPL_AUTH_KEY
OPENAI_API_KEY
EOF
)"
declare -a ENV
while getopts :${EXOPT:=dBI:V:T:LRE:GCD} OPT
do
case $OPT in
d) debug=yes ; set -x ;;
B) interactive= ;;
I) container="$OPTARG" ;;
V) volume="$OPTARG" ;;
T) version=:"$OPTARG" ;;
L) version=:latest ;;
R) mount_mode=ro ;;
E)
DEFAULT_ENV+=$'\n'"$OPTARG" ;;
G)
[ "$XLATE_RUNNING_ON_DOCKER" ] && break
if [ "${topdir:=$(git_topdir)}" ]
then
if [ "$topdir" != "${PWD:=$(pwd)}" ]
then
echo "Mount $topdir to $workdir" >&2
volume=$topdir
cd_path=${PWD#$topdir}
fi
fi
;;
C|D)
[ "$XLATE_RUNNING_ON_DOCKER" ] && break
if [ "$DISPLAY" ] && [ "${ip:=$(get_ip)}" ]
then
display="$ip:0"
fi
if [ ! "$version" ]
then
[[ "$my_version" =~ ^[0-9]+\.[0-9_]+$ ]] && version=":$my_version"
fi
declare -a command
case "$OPT" in
C) shift $((OPTIND - 1)) ;;
D) command=(xlate) ;;
esac
command+=(${1+"$@"})
[ ${#ENV[@]} -gt 0 ] && ENV_PATTERN="$(env_pattern ${ENV[@]})"
exec docker run --rm \
-e XLATE_RUNNING_ON_DOCKER=1 \
${interactive:+-it} \
${read_only:+--read-only} \
${hostname:+--hostname "${hostname}"} \
${localtime:+-v ${localtime}:/etc/localtime:ro} \
-v "${volume:-$(pwd)}:${workdir}:${mount_mode}" \
-w "${workdir}${cd_path}" \
${display:+-e DISPLAY="$display"} \
${DEFAULT_ENV:+--env-file <(echo "$DEFAULT_ENV")} \
${ENV_PATTERN:+--env-file <(env | grep -e "$ENV_PATTERN")} \
${container:-${repository}${version}} \
"${command[@]}"
exit;
;;
esac
done
OPTIND=1
mod="App-Greple-xlate"
share=$(perl -MFile::Share=:all -E "say dist_dir '$mod'")
declare -a pattern
while getopts "${EXOPT}"'Macrsne:w:o:f:t:m:p:l:hv' OPT
do
case $OPT in
[$EXOPT]) : ignore ;;
M) run_make=yes ;;
a) use_api=yes ;;
c) check=yes ;;
r) refresh=yes ;;
s) silent=yes ;;
n) dryrun=yes ;;
e) engine="$OPTARG" ;;
w) wrap="$OPTARG" ;;
o) format="$OPTARG" ;;
f) from_lang="$OPTARG" ;;
t) to_lang="$OPTARG" ;;
m) max="$OPTARG" ;;
p)
pattern+=(--re "$OPTARG")
;;
l)
file="$share/$OPTARG"
if [ -f "$file" ]
then
cat $file
else
echo $share
ls -1 $share | sed 's/^/\t/'
fi
exit
;;
h)
sed -r \
-e '/^$/N' \
-e '/^\n*#/d' \
-e 's/^(\n*)=[a-z]+[0-9]* */\1/' \
-e '/Version/q' \
<<< "$pod"
exit
;;
v)
echo $my_version
exit
;;
esac
done
shift $((OPTIND - 1))
: run make
if [ "$run_make" == yes ]
then
declare -a opt
for m in "$@"
do
if [ -f "$m" ]
then
# GNU Make behaves differently in different versions with
# respect to double-quoted strings and spaces within them.
files="${files:+$files|||}$m"
else
opt+=("$m")
fi
done
unset MAKELEVEL
exec make -f $share/XLATE.mk \
${dryrun:+-n} \
XLATE_LANG=\""$to_lang"\" \
XLATE_DEBUG=$debug \
XLATE_MAXLEN=$max \
XLATE_USEAPI=$use_api \
${engine:+XLATE_ENGINE=\""$engine"\"} \
${format:+XLATE_FORMAT=\""$format"\"} \
${files:+XLATE_FILES=\""$files"\"} \
${opt[@]}
exit 1
fi
if [ ! "$to_lang" ]
then
echo "-t option is required."
exit 1
fi
: ${format:=xtxt}
: ${engine:=deepl}
if [[ "$format" =~ ^(.+)-fold$ ]]
then
format=${BASH_REMATCH[1]}
: ${wrap:=76}
fi
declare -a module option
module+=(-Mxlate)
option+=(--xlate-engine="$engine")
option+=(--xlate-to="$to_lang" --xlate-format="$format" --xlate-cache=yes)
option+=(--all)
[ "$use_api" == yes ] || use_clipboard=yes
[ "$check" == yes ] || option+=(--xlate${use_clipboard:+-labor})
[ "$wrap" != "" ] && option+=(--xlate-fold-line --xlate-fold-width=$wrap)
[ "$debug" == yes ] && option+=(-dmo)
[ "$refresh" == yes ] && option+=(--cache-clear)
[ "$silent" == yes ] && option+=(--no-xlate-progress)
[ "$max" != "" ] && option+=(--xlate-maxlen="$max")
declare -a area
case $1 in
*.txt)
area=(--re '^(.+\n)+')
;;
*.md)
area=(--re '(?x) ^[-+#].*\n | ^\h+\K.*\n | ^(.+\n)+ ')
;;
*.pm|*.pod)
module+=(-Mperl)
option+=(--pod)
option+=(--exclude '^=head\d +(VERSION|AUTHOR|LICENSE|COPYRIGHT|SEE.?ALSO).*\n(?s:.*?)(?=^=|\z)')
area=(--re '^(\w.+\n)+')
;;
*.doc|*.docx|*.pptx|*.xlsx)
module+=(-Mmsdoc)
;&
*.stxt)
option+=(--exclude '^\[.*\b(doc|docx|pptx|xlsx)\b.*\]\n')
area=(--re '^.+\n')
;;
*)
area=(--re '^(.+\n)+')
;;
esac
if (( ${#pattern[@]} > 0))
then
option+=("${pattern[@]}")
else
option+=("${area[@]}")
fi
exec=(greple "${module[@]}" "${option[@]}" ${1+"$@"})
if [ "$dryrun" ]
then
echo "${exec[@]}"
else
"${exec[@]}"
fi