#!/bin/bash set -e ##################################################################### ## Variables ## Source code path SRC= ## Output path POTDIR= ## The component *.pot files are based roughly on CRM/Foo templates/CRM/Foo. COMPONENT_POTS="Admin Badge Batch Campaign Case Contribute Event Extension Financial Grant Mailing Member PCP Pledge Project Queue Report" ## The adhoc *.pot files are based on clear file list (but may have some special/less predictable rules). ADHOC_POTS="common-base countries drupal-civicrm install menu provinces" ## The magic *.pot files are derived from other *.pot files. MAGIC_POTS="common-components" ## List of chosen *.pot files POTS= ## Header file to prepend to any *.pot HEADER_TMPL=bin/header HEADER= ## Flags to control which actions are performed DO_SCAN= DO_DIGEST= DO_CLEANUP= FORCE= ##################################################################### function usage() { cat < /dev/null ; then echo "Missing required command: $cmd" echo case "$cmd" in sponge) echo 'This program uses the "sponge" command which you can get by installing the' echo '"moreutils" package under Debian/Ubuntu or by visting' echo 'https://joeyh.name/code/moreutils/' ;; civistrings) echo 'This program uses the "civistrings" command which is bundled with buildkit.' echo 'You can download it separately from https://github.com/civicrm/civistrings' esac exit 1 fi done } ##################################################################### ## civistrings wrapper with some default options function _civistrings() { civistrings --header="$HEADER" "$@" } ##################################################################### ## usage: HEADER=$(build_header "$HEADER_TMPL") function build_header() { local tmpl="$1" local out="$POTDIR/.header" local now=`date +'%F %R%z'` cat "$tmpl" \ | sed "s/NOW/$now/" \ > "$out" echo "$out" } ##################################################################### ## Build an individual POT file ## usage: build_raw_pot ## example: build_raw_pot Mailing ## example: build_raw_pot install function build_raw_pot() { local name="$1" local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot if [ -f "$filepath" -a -z "$FORCE" ]; then echo "[[ Found raw strings for ${name} from previous scan. ]]" return fi echo "[[ Building raw strings for ${name} ]]" case "$name" in ## Adhoc targets, sorted alphabetically common-base) _civistrings -o "$filepath" \ {CRM,templates/CRM}/{ACL,Activity,Block,common,Contact,Core} \ {CRM,templates/CRM}/{Custom,Dashlet,Dedupe,Export,Form,Friend} \ {CRM,templates/CRM}/{Group,Import,Logging,Note,Price,Profile} \ {CRM,templates/CRM}/{Relationship,SMS,Standalone,Tag,UF,Utils} \ xml/templates/civicrm_acl.tpl \ xml/templates/civicrm_data.tpl \ xml/templates/languages.tpl \ xml/templates/civicrm_msg_template.tpl \ xml/templates/message_templates/friend_* \ xml/templates/message_templates/uf_notify_* \ ang/crm{App,Attachment,Example,Ui,Util}{,.js} \ js/Common.js \ js/crm.ajax.js \ js/crm.backbone.js \ js/jquery/jquery.crmeditable.js \ js/jquery/jquery.crmProfileSelector.js \ js/jquery/jquery.crmRevisionLink.js \ js/jquery/jquery.dashboard.js \ js/model/crm.designer.js \ js/model/crm.uf.js \ js/view/crm.designer.js \ packages/HTML/QuickForm ## The CRM/Upgrade folder includes *.tpl files which, for some reason, ## have been omitted from past pot's. Omitting these requires more ## precise file selection. find CRM/Upgrade -name '*.php' | _civistrings -ao "$filepath" - _civistrings -ao "$filepath" templates/CRM/Upgrade ;; common-components) ## Not yet; handled in the digest phase. That why it's in MAGIC_POTS return ;; countries) cat "$HEADER" > "$filepath" grep ^INSERT xml/templates/civicrm_country.tpl \ | cut -d\" -f4 \ | while read entry; do echo -e "msgctxt \"country\"\nmsgid \"$entry\"\nmsgstr \"\"\n" done \ >> "$filepath" ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" ;; drupal-civicrm) _civistrings -o "$filepath" \ drupal \ CRM/Core/Permission.php ;; install) _civistrings -o "$filepath" \ install/ ;; menu) cat "$HEADER" > "$filepath" grep -h '' CRM/*/xml/Menu/*.xml \ | sed 's/^.*<title>\(.*\)<\/title>.*$/\1/' \ | while read entry; do echo -e "msgctxt \"menu\"\nmsgid \"$entry\"\nmsgstr \"\"\n" done \ >> "$filepath" _civistrings --msgctxt=menu xml/templates/civicrm_navigation.tpl -ao "$filepath" ;; provinces) cat "$HEADER" > "$filepath" grep '^(' xml/templates/civicrm_state_province.tpl \ | cut -d\" -f4 \ | while read entry; do echo -e "msgctxt \"province\"\nmsgid \"$entry\"\nmsgstr \"\"\n" done \ >> "$filepath" ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" ;; ## Standard targets, sorted alphabetically Campaign) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/petition_* ;; Case) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ ang/crmCaseType{,.js} \ xml/templates/message_templates/case_* ;; Contribute) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/contribution_* \ xml/templates/message_templates/test_* ;; Event) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/event_* \ xml/templates/message_templates/participant_* ;; Mailing) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ ang/crm{Mailing,MailingAB}{,.js} ;; Member) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/membership_* ;; PCP) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/pcp_* ;; Pledge) _civistrings -o "$filepath" \ {CRM,templates/CRM}/$name \ xml/templates/message_templates/pledge_* ;; *) if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then _civistrings -o "$filepath" {CRM,templates/CRM}/$name else echo "unrecognized pot: $name" fi ;; esac find "$filepath" ! -empty | while read f; do msguniq "$filepath" | sponge "$filepath" done } ##################################################################### ## usage: make_stat <name> ## example: make_stat Mailing function make_stat() { local name="$1" local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot grep ^msgid "$filepath" | sort -u > "$filepath.msgid" grep '^#:' "$filepath" | sed 's/#://' | tr ' ' '\n' | sort -u > "$filepath.files" } ##################################################################### ## Scan .raw-*.pot for common strings and put them in common-components.pot ## usage: build_common_components function build_common_components() { echo "[[ Building common-components.pot ]]" local paths="" local has_multiple=0 for comp in $COMPONENT_POTS ; do local rawfile=".raw-"$(echo $comp | tr '[:upper:]' '[:lower:]').pot if [ -f "$rawfile" ]; then paths="$paths $rawfile" has_multiple=1 fi done if [ $has_multiple -eq 1 ]; then msgcomm $paths > .raw-common-components.pot else cat $HEADER > .raw-common-components.pot fi } ##################################################################### ## example: build_final_pot Mailing ## example: build_final_pot install function build_final_pot() { local name="$1" local rawpath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot local finalpath="$POTDIR/"$(echo $name | tr '[:upper:]' '[:lower:]').pot local tmpfile=`tempfile` echo "[[ Building final strings for ${name} ]]" cp -f "$rawpath" "$finalpath" if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then msgcomm "$finalpath" .raw-common-components.pot > $tmpfile msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" elif [ "$name" == "install" ]; then msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" fi rm -f "$tmpfile" } ##################################################################### ## Delete temp files function do_cleanup() { echo "[[ Cleanup temp files ]]" rm .header .raw*pot -f } ##################################################################### ## Main [ "$1" == "--help" ] && usage [ "$1" == "-h" ] && usage check_deps FOUND_ACTION= while getopts "asfdc" opt; do case $opt in a) DO_SCAN=1 DO_DIGEST=1 DO_CLEANUP=1 FOUND_ACTION=1 ;; s) DO_SCAN=1 FOUND_ACTION=1 ;; d) DO_DIGEST=1 FOUND_ACTION=1 ;; c) DO_CLEANUP=1 FOUND_ACTION=1 ;; f) FORCE=1 ;; \?) echo "Invalid option: -$OPTARG" >&2 exit 1 ;; :) echo "Option -$OPTARG requires an argument." >&2 exit 1 ;; esac done if [ -z "$FOUND_ACTION" ]; then DO_SCAN=1 DO_DIGEST=1 DO_CLEANUP=1 fi shift $((OPTIND-1)) [ "$1" == "" ] && echo 'source dir missing' && usage test ! -e "$1" && echo 'source does not exist' && usage test ! -d "$1" && echo 'source not a directory' && usage [ "$2" == "" ] && echo 'target dir missing' && usage test ! -e "$2" && echo 'target does not exist' && usage test ! -d "$2" && echo 'target not a directory' && usage # use absolute paths so that we can chdir/pushd SRC=$(php -r 'echo realpath($argv[1]);' "$1") POTDIR=$(php -r 'echo realpath($argv[1]);' "$2") HEADER=$(build_header "$HEADER_TMPL") ## TODO: substitute "NOW" in HEADER shift 2 if [ -z "$1" ]; then POTS="$COMPONENT_POTS $ADHOC_POTS $MAGIC_POTS" else POTS="$@" fi if [ -n "$DO_SCAN" ]; then pushd "$SRC" >> /dev/null for POT in $POTS ; do build_raw_pot "$POT" done popd >> /dev/null fi if [ -n "$DO_DIGEST" ]; then pushd "$POTDIR" >> /dev/null echo "POTS : $POTS" build_common_components for POT in $POTS ; do echo "+++++++++ $POT" build_final_pot "$POT" done popd >> /dev/null fi if [ -n "$DO_CLEANUP" ]; then pushd "$POTDIR" >> /dev/null do_cleanup popd >> /dev/null fi