t12s 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. #!/bin/sh
  2. # translate using t12s (technosaurus) method
  3. # 2011-11-14 L18L
  4. #
  5. # developer: never ever change a message, use a blank ID instead
  6. # example: if you want to change
  7. # _M5_:-...failed
  8. # then use
  9. # _M_:-...oops this failed
  10. # that is because original translation is stored nowhere
  11. # note, simple quote (') inside message is not allowed, use (´) instead
  12. # translate to dummy language xxx thus you can test completeness and design
  13. #
  14. #20111115 enabling variables in message, example: '${1}' not found
  15. # not yet tested!!!!!!!!! but anyhow: without variables it is OK
  16. #20111116 bugfix for empty language in 'Select code of language' dialog
  17. version='0.4.1' # bug fix for breaking script in automatic numbering
  18. version='0.4.2' # added check for selected script
  19. version='0.4.3' # bugfix and code clean up
  20. version='0.4.4' # bugfix slash inside message, delimiter for sed is backtick
  21. version='0.4.5' # bugfixes:utf8, enabled using \"
  22. version='0.4.5.1' # bugfix:xxx
  23. RESTART=$0
  24. myfile=$1
  25. OUTPUT_CHARSET=UTF-8 # needed?
  26. T=/usr/share/locales # TEXTDOMAINDIR
  27. title="t12s -v $version "
  28. [ "`whoami`" != "root" ] && exec sudo -A ${0} ${@} # write to TEXTDOMAINDIR
  29. delim='`' # delimiter for sed, no backtick in messages !
  30. # make sure UTF-8 is enabled
  31. if [ "${LANG#*.}" = "$LANG" ]; then
  32. yad --text=" <big>Enable <span color='red'>UTF-8</span> in the following dialog
  33. then restart X server and start again please</big>"
  34. [[ $? -eq 1 || $? -eq 252 ]] && exit 0
  35. /usr/sbin/chooselocale
  36. ln -s "`pwd`/$RESTART" ${HOME}/Startup/t12s
  37. exit 0
  38. else
  39. rm ${HOME}/Startup/t12s 2>/dev/null
  40. fi
  41. # the following texts will hopefully give you the idea of this script
  42. text0="script is not yet
  43. internationalized by method t12s, sorry."
  44. text1=" Select a script which uses t12s method for internationalisation.
  45. Translatable text strings are marked by <span color='red'>_M_</span> in such scripts.
  46. (Developers: klick ´Developer´ button for info how to Internationalize shell scripts using this method)"
  47. text2=" Select code of language
  48. which you want to translate to.
  49. Choose from <span color='darkgreen'>existing</span> translations
  50. or
  51. enter <span color='red'>new</span> language code
  52. Inofficial languages (example: slv for Slavio) possible.
  53. Regional variants (dialect) appreciated!
  54. examples: pt_BR or en_AUS
  55. "
  56. text3=" Select <span color='red'>one message</span> which you want to translate.
  57. The original English message will be shown
  58. after klicking <span color='darkgreen'>OK</span> button.
  59. Or <span color='red'>Undo</span> translation of last message!"
  60. available_lang_list=/dev/shm/available_languages # temporary list
  61. my_LANG=${LANG%_*} # wild guess: user wants to translate to his language
  62. # select shell script which has this method of i18n
  63. [ -z $myfile ] && while [ 1=1 ]; do # main window ===========================
  64. ### some docu added here !
  65. # select shell script myfile which has this method of i18n
  66. myfile=$(yad --title="$title Select 1 Script" --text="$text1" \
  67. --width="500" --height="400" \
  68. --file-selection --filename=/usr/sbin/ \
  69. --button="Developer:4" \
  70. --button="Translator:3" \
  71. --button="gtk-about:2" \
  72. --button="gtk-quit:1" \
  73. --button="gtk-ok:0" \
  74. )
  75. choice=$?
  76. case $choice in
  77. 0) this_LANG=${this_LANG%|*} # cut -d '|' -f 1 `
  78. [ "`grep _M_ $myfile`" ] && [ -x "$myfile" ] && break # this_LANG preselected for translation
  79. text=" $myfile $text0"
  80. [ ! -x "$myfile" ] && text="Sorry, $myfile is not executable"
  81. read LINE < $myfile ; [ ${LINE:0:3} != "#!/" ] && text="Sorry, $myfile is no script"
  82. yad --text="$text" ; [[ $? -eq 1 || $? -eq 252 ]] && exit 0
  83. ;;
  84. 1|252) exit 0 ;;
  85. 2) yad --width="450" --title="About t12s : translate 1 script to several languages" \
  86. --text=" GUI for fast and easy l10n (localization) of shell scripts
  87. There is just a language file containing translations
  88. from original messages created from source at run time.
  89. core algorithm by Technosaurus
  90. <small>http://bashismal.blogspot.com/2011/10/
  91. localizing-shell-scripts-without.html</small>
  92. written by L18L 2011, licence is GPL v3" \
  93. --button="gtk-ok"
  94. ;;
  95. 3) yad --title="t12s: some Notes for Translators" \
  96. --text="
  97. You can translate to <span color='darkgreen'>any</span> language
  98. without needing their locale (though it might be better).
  99. Your will find your translation of <span color='darkred'>script</span> in this file:
  100. ${T}/&lt;your language code&gt;/<span color='darkgreen'>script</span>
  101. Just <b>select a script that is internationalized using this method</b>.
  102. " --button="gtk-ok"
  103. ;;
  104. 4) yad --title="t12s: some Notes for Developpers" \
  105. --text=" Some simple rules for i18n (internationalization) of shell script:
  106. ###################### copy this block into any other script that... ##########
  107. app=\`basename \$0\`
  108. T=/usr/share/locales # TEXTDOMAINDIR
  109. # a one-liner to find existing translation file LOCALES allowing 'dialects'
  110. [ ! -f \${T}/\${LANG%.*}/\${app} ] &amp;&amp; LOCALES=\${T}/\${LANG%_*}/\${app} || LOCALES=\${T}/\${LANG%.*}/\${app}
  111. [ -f \$LOCALES ] &amp;&amp; . \$LOCALES # (re)load translation file (when using variables)
  112. ###################### ... uses this _ M _ method of internationalization #####
  113. - mark messages by _M_, no { } for variables, example
  114. change:
  115. echo \"Hello \${myOS} world\"
  116. to:
  117. [ -f \$LOCALES ] &amp;&amp; . \$LOCALES
  118. echo \"<span color='darkgreen'>\${_M_:-</span>Hello \$myOS world<span color='darkgreen'>}</span>\"
  119. - using square or round brackets [ ( then
  120. alway 1 space after and 1 space before: ) ]
  121. - use \" only if really needed. Try instead ' or ´ (single quote or apostrophe)
  122. - 1 message per code line only
  123. You can start preparing just some messages and after having chosen the script in the main window
  124. if you are asked for the language to translate to you can type in Language code <b>xxx</b>.
  125. Then your internationalized script will run in a dummy language, that is just your original messages
  126. embedded in +++ (Other languages tend to be larger than English messages)
  127. This way you can check completeness of i18n and possible (hopefully not) break of your screen design." \
  128. --button="gtk-ok"
  129. ;;
  130. esac
  131. done # /main window ==========================================================
  132. app="`basename $myfile`"
  133. echo app=$app
  134. if [ "`grep -E '^[^#].*_M_:-' $myfile`" ]; then # id numbering of messages
  135. # find last highest number i to skip
  136. echo find last highest number i to skip
  137. sed -e "s${delim}.*{_M_${delim}${delim};s${delim}:-}.*${delim}${delim}" $myfile | grep -E '^[0-9]' | sort -unr > /dev/shm/t12s_i
  138. read LINE < /dev/shm/t12s_i # head -n 1
  139. i=${LINE%%:*} # cut -d ':' -f 1
  140. [ $i ] || i=0 # found last highest number i to skip
  141. echo i=$i is highest existing number
  142. #sort uniq just the (new added) message text
  143. grep -E '^[^#].*_M_:' $myfile | cut -d "{" -f 2- | rev | cut -d "}" -f 2- | rev \
  144. | sed -r "s${delim}_M_:-${delim}${delim}g" | sort -u > /dev/shm/t12s_${app}_u
  145. #echo "last one dummy" >> /dev/shm/t12s_${app}_u # without this last one is missing
  146. echo "
  147. new unique numbered messages will be line numbers in /dev/shm/t12s_${app}_u added to $i
  148. press ENTER to continue
  149. automatic addition of identifiers to _M_ in
  150. $myfile
  151. a backup of $myfile
  152. will be made as
  153. ${myfile}_bak."
  154. #read x
  155. cp ${myfile} ${myfile}_bak
  156. # put id numbers into myfile
  157. while read LINE ; do # just 1 second, sed ///g needed for multiple use, example: please wait...
  158. i=$(($i + 1))
  159. [[ $LINE =~ "[" ]] && LINE=`echo $LINE | sed -e 's/\[/\\\[/g;s/\]/\\\]/g'` # escape square [brackets]
  160. [[ $LINE =~ "(" ]] && LINE=`echo $LINE | sed -e 's/\(/\\\[/g;s/\)/\\\)/g'` # escape square [brackets]
  161. [[ $LINE =~ "<" ]] && LINE=`echo $LINE | sed -e 's/</\</g;s/>/\>/g'` # escape <brackets>
  162. [[ $LINE =~ "\'" ]] && LINE=`echo $LINE | sed -e "s/\'/\\\'/g;"` # escape single quote
  163. [[ $LINE =~ '\"' ]] && LINE=`echo $LINE | sed -e 's/\"/\\\"/g;'` # escape double quote
  164. # not working for \"
  165. sed -e "s${delim}_M_:-${LINE}${delim}_M_${i}:-${LINE}${delim}g" ${myfile} > ${myfile}_$i
  166. # need to test file sizes, success if different
  167. if [[ `ls -l ${myfile}_$i | cut -d ' ' -f 5` -gt `ls -l ${myfile} | cut -d ' ' -f 5` ]]; then
  168. mv ${myfile}_$i ${myfile}
  169. else #always if \" was used
  170. chmod +x ${myfile}
  171. yad --text="Sorry, could not write number $i for
  172. $LINE
  173. You have to add unique number $i manually to _M_ in
  174. $myfile
  175. now then press OK button to restart t12s" --button="gtk-ok:0"
  176. sync; $RESTART $myfile && exit 1
  177. fi
  178. done < /dev/shm/t12s_${app}_u
  179. chmod +x ${myfile}
  180. sync
  181. fi # /id numbering of messages
  182. # start with user's language, English have to type in
  183. [ "${LANG%_*}" != "en" ] || this_LANG=${LANG%_*}
  184. # endless loop for translating myfile to several languages ##############
  185. while [ 1=1 ]; do
  186. # existing_translations for app in TEXTDOMAINDIR, one of them could be new
  187. cd ${T}
  188. existing_translations="`ls */${app} 2>/dev/null | tr -s ' ' | cut -d '/' -f 1 `"
  189. echo -n > $available_lang_list
  190. for x in $existing_translations; do echo $x >> $available_lang_list; done
  191. cd - 1>/dev/null
  192. sync # ? needed
  193. # choose language $this_LANG
  194. this_LANG=''
  195. while [ "`echo $this_LANG | sed -r 's/[a-z]{2,3}(_[A-Z]{2,3}){0,1}/OK/'`" != "OK" ]; do #20111116
  196. this_LANG=$(yad --title="$title translate $app" --text="$text2" \
  197. --width="80" --height="240" \
  198. --form \
  199. --item-separator="\n" \
  200. --field="Language code:CBE" "`echo ${my_LANG} ; cat $available_lang_list`" \
  201. --item-separator="\n" \
  202. --button="gtk-quit:1" \
  203. --button="gtk-ok:0" \
  204. )
  205. choice=$?
  206. case $choice in
  207. 0) this_LANG=${this_LANG%|*};; # cut -d '|' -f 1 `
  208. 1|252) exit 0 ;;
  209. esac
  210. done
  211. echo $this_LANG
  212. [ ! -d "${T}/${this_LANG}" ] && mkdir ${T}/${this_LANG}
  213. # extract English messages from script: create temporary English language file
  214. grep _M_ $myfile | cut -d "{" -f 2- | rev | cut -d "}" -f 2- | rev \
  215. | sed -r "s${delim}:-${delim}=\"${delim}g;s${delim}\$${delim}\"${delim}g" | sort -u > /dev/shm/t12s_${app}_en
  216. # dialect starts with copy of base language, ex: fr_CA copied from fr
  217. if [ ${#this_LANG} -gt 4 -a ! -f ${T}/${this_LANG}/${app} ]; then
  218. cp ${T}/${this_LANG%_*}/${app} ${T}/${this_LANG}
  219. [ $! ] || existing_translations="$existing_translations ${this_LANG}"
  220. fi
  221. # merge existing translation
  222. if [[ `grep -E "^${this_LANG}$" $available_lang_list` ]]; then # merge
  223. mv ${T}/${this_LANG}/${app} ${T}/${this_LANG}/${app}_tmp
  224. while read LINE; do #comment for no more existing ones, but once only
  225. [ "`grep -E ${LINE%=*} /dev/shm/t12s_${app}_en`" ] && comment="" || comment="#"
  226. [[ "${LINE:0:1}" = "#" ]] && comment=""
  227. echo ${comment}${LINE} >> ${T}/${this_LANG}/${app}
  228. done < ${T}/${this_LANG}/${app}_tmp
  229. rm ${T}/${this_LANG}/${app}_tmp
  230. while read LINE; do #append new ones
  231. [ "`grep -E ${LINE%=*} ${T}/${this_LANG}/${app}`" ] \
  232. || echo $LINE >> ${T}/${this_LANG}/${app}
  233. done < /dev/shm/t12s_${app}_en
  234. else
  235. if [ "$this_LANG" = "xxx" ]; then # dummy language
  236. # emulating translation (messages just embedded in +++)
  237. sed -e "s${delim}=\"${delim}=\"+++${delim}g;s${delim}\"${delim}+++\"${delim}g" /dev/shm/t12s_${app}_en > ${T}/xxx/${app}
  238. LANG=xxx
  239. exec $myfile &
  240. break
  241. elif [ 1 = 2 ] ; then #automated translation available...
  242. FROM=/C/
  243. [ ! -d /usr/share/locales/C/ ] && mkdir /usr/share/locales/C/
  244. for x in $myfile ; do
  245. TEMPLATE=/usr/share/locales/C/${app}.txt
  246. while read -d$ A || [ "$A" ]; do
  247. case "$A" in
  248. *{_M_*)B=_M${A##*_M};B=${B%%\}*};echo ${B//:-/=};;
  249. esac
  250. done < $x >${TEMPLATE}
  251. mv ${TEMPLATE} ${TEMPLATE}m; sort -u ${TEMPLATE}m>${TEMPLATE}; rm ${TEMPLATE}m
  252. for x in $this_LANG ; do
  253. TO=/${x//-/_}/
  254. [ ! -d ${TEMPLATE%%/C/*}/${x//-/_} ] && mkdir ${TEMPLATE%%/C/*}/${x//-/_}
  255. curl -A firefox \
  256. -F "file=@$TEMPLATE;type=text/plain" \
  257. -F "sl=en" \
  258. -F "tl=$x" \
  259. http://translate.googleusercontent.com/translate_f | \
  260. while read A || [ "$A" ]; do
  261. case $A in
  262. "<meta"*)A="";;
  263. *"<pre>"*)A=${A##*"<pre>"};;
  264. *"</pre>"*)A=${A%%"</pre>"*};;
  265. esac
  266. [ "$A" ] && echo ${A// = /=\"}\"
  267. done > ${TEMPLATE//$FROM/$TO}
  268. mv ${T}/${x}/${app}.txt ${T}/${x}/${app}
  269. #
  270. #curl: (6) Couldn't resolve host 'translate.googleusercontent.com' : empty file
  271. #
  272. # file is not utf-8 but iso-8859
  273. #
  274. done
  275. rm $TEMPLATE
  276. done #automated translation available...
  277. else # copy English to this_LANG
  278. cp /dev/shm/t12s_${app}_en ${T}/${this_LANG}/${app}
  279. fi
  280. fi
  281. # loop translate 1 item -------------------------------------------------------
  282. # while [ 2=2 ]; do
  283. [ "$this_LANG" = "xxx" ] && $myfile || while [ 2=2 ]; do
  284. sed -r "s${delim}=\"${delim}\n${delim}g;s${delim}\"\$${delim}${delim}g" ${T}/${this_LANG}/${app} > /dev/shm/t # just for inspection
  285. message=$(cat /dev/shm/t \
  286. | yad --title="$title translate $app to $this_LANG" \
  287. --height="400" --text="$text3" \
  288. --list \
  289. --column="ID" \
  290. --column="Actual Message Translated To ${this_LANG//_/_ }:TEXT" \
  291. --button="Change Language:3" \
  292. --button="gtk-execute:2" \
  293. --button="gtk-quit:1" \
  294. --button="gtk-undo:9" \
  295. --button="gtk-ok:0" \
  296. )
  297. choice=$?
  298. case $choice in
  299. 1|252) exit 0 ;;
  300. 3) break ;;
  301. 2)
  302. LANG="${this_LANG}" # Gtk-WARNING **: Locale not supported by C library.
  303. # but sufficient for just language
  304. [ -x $myfile ] || yad --text="sorry, chosen $myfile is not executable" \
  305. && exec $myfile & ;;
  306. 0) if [ "$message" ]; then
  307. msgID="`echo $message | cut -d '|' -f 1`" # ${message%|*} no go
  308. msgtr="`echo $message | cut -d '|' -f 2`"
  309. msgEN="`grep -F ${msgID}= /dev/shm/t12s_${app}_en | cut -d '\"' -f 2`"
  310. if [ -z "$msgEN" ];then
  311. yad --text="not in use any more"
  312. else
  313. msgEN="$msgEN"
  314. new_msgtr=$(yad --title="translate" --width="600" --text="$msgEN" \
  315. --entry --entry-text="$msgtr" \
  316. --button="gtk-ok:0" )
  317. [ "$new_msgtr" = "$msgtr" ] && continue
  318. # update translation file
  319. mv ${T}/${this_LANG}/${app} ${T}/${this_LANG}/${app}_old
  320. while read LINE; do
  321. [ "${LINE%=*}" != "$msgID" ] && out="$LINE" || out="${msgID}=\"${new_msgtr}\""
  322. echo $out >> ${T}/${this_LANG}/${app}
  323. done < ${T}/${this_LANG}/${app}_old
  324. fi
  325. else
  326. yad --title="translate" \
  327. --text="Select a line before OK button please" \
  328. --button="gtk-ok:0"
  329. fi ;;
  330. 9) cp ${T}/${this_LANG}/${app}_old ${T}/${this_LANG}/${app} ;;
  331. esac
  332. done # /loop translate item --------------------------------------------------
  333. done # /endless loop for translating myfile to several languages ##############
  334. #end of script