Commit | Line | Data |
---|---|---|
7773dd1e ML |
1 | #!/bin/bash |
2 | set -e | |
3 | ||
4 | ##################################################################### | |
5 | ## Variables | |
6 | ||
7 | ## Source code path | |
8 | SRC= | |
9 | ||
10 | ## Output path | |
11 | POTDIR= | |
12 | ||
13 | ## The component *.pot files are based roughly on CRM/Foo templates/CRM/Foo. | |
14 | COMPONENT_POTS="Admin Badge Batch Campaign Case Contribute Event Extension Financial Grant Mailing Member PCP Pledge Project Queue Report" | |
15 | ||
16 | ## The adhoc *.pot files are based on clear file list (but may have some special/less predictable rules). | |
17 | ADHOC_POTS="common-base countries drupal-civicrm install menu provinces" | |
18 | ||
19 | ## The magic *.pot files are derived from other *.pot files. | |
20 | MAGIC_POTS="common-components" | |
21 | ||
22 | ## List of chosen *.pot files | |
23 | POTS= | |
24 | ||
25 | ## Header file to prepend to any *.pot | |
26 | HEADER_TMPL=bin/header | |
27 | HEADER= | |
28 | ||
29 | ## Flags to control which actions are performed | |
30 | DO_SCAN= | |
31 | DO_DIGEST= | |
32 | DO_CLEANUP= | |
33 | FORCE= | |
34 | ||
35 | ##################################################################### | |
36 | function usage() { | |
37 | cat <<EOT | |
38 | create-pot-files.sh - builds .pot files for CiviCRM. | |
39 | ||
40 | Usage: | |
41 | ./bin/create-pot-files.sh [options] [srcdir] [destdir] [pot1...] | |
42 | ||
43 | Examples: | |
44 | ./bin/create-pot-files.sh ~/repository/civicrm/ ~/repository/l10n/po/pot/ | |
45 | ./bin/create-pot-files.sh -sd ~/repository/civicrm/ ~/repository/l10n/po/pot/ common-base Admin Contribute | |
46 | ||
47 | Options: | |
48 | -s Scan targets for strings (Pass #1) | |
49 | -d Digest/dedupe scanned strings (Pass #2) | |
50 | -c Cleanup temp files | |
51 | -a All (scan+digest+cleanup; default) | |
52 | -f Force (Ignore cached results from previous scan) | |
53 | -h Help | |
54 | ||
55 | Targets: | |
56 | $COMPONENT_POTS | |
57 | $ADHOC_POTS $MAGIC_POTS | |
58 | ||
59 | Although you should probably not call this directly. Use build-unified-pots.sh | |
60 | if you are exporting the strings to Transifex. | |
61 | http://wiki.civicrm.org/confluence/display/CRMDOC/Pushing+new+strings+to+Transifex | |
62 | ||
63 | EOT | |
64 | ||
65 | exit 1; | |
66 | } | |
67 | ||
68 | ##################################################################### | |
69 | ## Assert that CLI dependencies are met | |
70 | function check_deps() { | |
71 | for cmd in sponge civistrings msgcomm msguniq tempfile tr grep cut date sed ; do | |
72 | if ! which $cmd > /dev/null ; then | |
73 | echo "Missing required command: $cmd" | |
74 | echo | |
75 | case "$cmd" in | |
76 | sponge) | |
77 | echo 'This program uses the "sponge" command which you can get by installing the' | |
78 | echo '"moreutils" package under Debian/Ubuntu or by visting' | |
79 | echo 'https://joeyh.name/code/moreutils/' | |
80 | ;; | |
81 | civistrings) | |
82 | echo 'This program uses the "civistrings" command which is bundled with buildkit.' | |
83 | echo 'You can download it separately from https://github.com/civicrm/civistrings' | |
84 | esac | |
85 | exit 1 | |
86 | fi | |
87 | done | |
88 | } | |
89 | ||
90 | ##################################################################### | |
91 | ## civistrings wrapper with some default options | |
92 | function _civistrings() { | |
93 | civistrings --header="$HEADER" "$@" | |
94 | } | |
95 | ||
96 | ##################################################################### | |
97 | ## usage: HEADER=$(build_header "$HEADER_TMPL") | |
98 | function build_header() { | |
99 | local tmpl="$1" | |
100 | local out="$POTDIR/.header" | |
101 | local now=`date +'%F %R%z'` | |
102 | ||
103 | cat "$tmpl" \ | |
104 | | sed "s/NOW/$now/" \ | |
105 | > "$out" | |
106 | ||
107 | echo "$out" | |
108 | } | |
109 | ||
110 | ##################################################################### | |
111 | ## Build an individual POT file | |
112 | ## usage: build_raw_pot <name> | |
113 | ## example: build_raw_pot Mailing | |
114 | ## example: build_raw_pot install | |
115 | function build_raw_pot() { | |
116 | local name="$1" | |
117 | local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
118 | ||
119 | if [ -f "$filepath" -a -z "$FORCE" ]; then | |
120 | echo "[[ Found raw strings for ${name} from previous scan. ]]" | |
121 | return | |
122 | fi | |
123 | ||
124 | echo "[[ Building raw strings for ${name} ]]" | |
125 | ||
126 | case "$name" in | |
127 | ||
128 | ## Adhoc targets, sorted alphabetically | |
129 | ||
130 | common-base) | |
131 | _civistrings -o "$filepath" \ | |
132 | {CRM,templates/CRM}/{ACL,Activity,Block,common,Contact,Core} \ | |
133 | {CRM,templates/CRM}/{Custom,Dashlet,Dedupe,Export,Form,Friend} \ | |
134 | {CRM,templates/CRM}/{Group,Import,Logging,Note,Price,Profile} \ | |
135 | {CRM,templates/CRM}/{Relationship,SMS,Standalone,Tag,UF,Utils} \ | |
136 | xml/templates/civicrm_acl.tpl \ | |
137 | xml/templates/civicrm_data.tpl \ | |
138 | xml/templates/languages.tpl \ | |
139 | xml/templates/civicrm_msg_template.tpl \ | |
140 | xml/templates/message_templates/friend_* \ | |
141 | xml/templates/message_templates/uf_notify_* \ | |
25ff64b0 | 142 | ang/crm{App,Attachment,Example,Ui,Util}{,.js} \ |
c9a3a453 ML |
143 | js/Common.js \ |
144 | js/crm.ajax.js \ | |
145 | js/crm.backbone.js \ | |
146 | js/jquery/jquery.crmeditable.js \ | |
147 | js/jquery/jquery.crmProfileSelector.js \ | |
148 | js/jquery/jquery.crmRevisionLink.js \ | |
149 | js/jquery/jquery.dashboard.js \ | |
150 | js/model/crm.designer.js \ | |
151 | js/model/crm.uf.js \ | |
45799e07 | 152 | js/view/crm.designer.js \ |
25ff64b0 | 153 | packages/HTML/QuickForm |
7773dd1e ML |
154 | |
155 | ## The CRM/Upgrade folder includes *.tpl files which, for some reason, | |
156 | ## have been omitted from past pot's. Omitting these requires more | |
157 | ## precise file selection. | |
158 | find CRM/Upgrade -name '*.php' | _civistrings -ao "$filepath" - | |
159 | _civistrings -ao "$filepath" templates/CRM/Upgrade | |
160 | ;; | |
161 | ||
162 | common-components) | |
163 | ## Not yet; handled in the digest phase. That why it's in MAGIC_POTS | |
164 | return | |
165 | ;; | |
166 | ||
167 | countries) | |
168 | cat "$HEADER" > "$filepath" | |
169 | grep ^INSERT xml/templates/civicrm_country.tpl \ | |
170 | | cut -d\" -f4 \ | |
171 | | while read entry; do | |
172 | echo -e "msgctxt \"country\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
173 | done \ | |
174 | >> "$filepath" | |
175 | ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" | |
176 | ;; | |
177 | ||
178 | drupal-civicrm) | |
179 | _civistrings -o "$filepath" \ | |
180 | drupal \ | |
181 | CRM/Core/Permission.php | |
182 | ;; | |
183 | ||
184 | install) | |
185 | _civistrings -o "$filepath" \ | |
186 | install/ | |
187 | ;; | |
188 | ||
189 | menu) | |
190 | cat "$HEADER" > "$filepath" | |
191 | grep -h '<title>' CRM/*/xml/Menu/*.xml \ | |
192 | | sed 's/^.*<title>\(.*\)<\/title>.*$/\1/' \ | |
193 | | while read entry; do | |
194 | echo -e "msgctxt \"menu\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
195 | done \ | |
196 | >> "$filepath" | |
197 | _civistrings --msgctxt=menu xml/templates/civicrm_navigation.tpl -ao "$filepath" | |
198 | ;; | |
199 | ||
200 | provinces) | |
201 | cat "$HEADER" > "$filepath" | |
202 | grep '^(' xml/templates/civicrm_state_province.tpl \ | |
203 | | cut -d\" -f4 \ | |
204 | | while read entry; do | |
205 | echo -e "msgctxt \"province\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
206 | done \ | |
207 | >> "$filepath" | |
208 | ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" | |
209 | ;; | |
210 | ||
211 | ## Standard targets, sorted alphabetically | |
212 | ||
213 | Campaign) | |
214 | _civistrings -o "$filepath" \ | |
215 | {CRM,templates/CRM}/$name \ | |
216 | xml/templates/message_templates/petition_* | |
217 | ;; | |
218 | ||
219 | Case) | |
220 | _civistrings -o "$filepath" \ | |
221 | {CRM,templates/CRM}/$name \ | |
25ff64b0 | 222 | ang/crmCaseType{,.js} \ |
7773dd1e ML |
223 | xml/templates/message_templates/case_* |
224 | ;; | |
225 | ||
226 | Contribute) | |
227 | _civistrings -o "$filepath" \ | |
228 | {CRM,templates/CRM}/$name \ | |
229 | xml/templates/message_templates/contribution_* \ | |
230 | xml/templates/message_templates/test_* | |
231 | ;; | |
232 | ||
233 | Event) | |
234 | _civistrings -o "$filepath" \ | |
235 | {CRM,templates/CRM}/$name \ | |
236 | xml/templates/message_templates/event_* \ | |
237 | xml/templates/message_templates/participant_* | |
238 | ;; | |
239 | ||
c9a3a453 ML |
240 | Mailing) |
241 | _civistrings -o "$filepath" \ | |
242 | {CRM,templates/CRM}/$name \ | |
25ff64b0 | 243 | ang/crm{Mailing,MailingAB}{,.js} |
c9a3a453 ML |
244 | ;; |
245 | ||
7773dd1e ML |
246 | Member) |
247 | _civistrings -o "$filepath" \ | |
248 | {CRM,templates/CRM}/$name \ | |
249 | xml/templates/message_templates/membership_* | |
250 | ;; | |
251 | ||
252 | PCP) | |
253 | _civistrings -o "$filepath" \ | |
254 | {CRM,templates/CRM}/$name \ | |
255 | xml/templates/message_templates/pcp_* | |
256 | ;; | |
257 | ||
258 | Pledge) | |
259 | _civistrings -o "$filepath" \ | |
260 | {CRM,templates/CRM}/$name \ | |
261 | xml/templates/message_templates/pledge_* | |
262 | ;; | |
263 | ||
264 | *) | |
265 | if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then | |
266 | _civistrings -o "$filepath" {CRM,templates/CRM}/$name | |
267 | else | |
268 | echo "unrecognized pot: $name" | |
269 | fi | |
270 | ;; | |
271 | ||
272 | esac | |
273 | ||
274 | find "$filepath" ! -empty | while read f; do | |
275 | msguniq "$filepath" | sponge "$filepath" | |
276 | done | |
277 | } | |
278 | ||
279 | ##################################################################### | |
280 | ## usage: make_stat <name> | |
281 | ## example: make_stat Mailing | |
282 | function make_stat() { | |
283 | local name="$1" | |
284 | local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
285 | grep ^msgid "$filepath" | sort -u > "$filepath.msgid" | |
286 | grep '^#:' "$filepath" | sed 's/#://' | tr ' ' '\n' | sort -u > "$filepath.files" | |
287 | } | |
288 | ||
289 | ##################################################################### | |
290 | ## Scan .raw-*.pot for common strings and put them in common-components.pot | |
291 | ## usage: build_common_components | |
292 | function build_common_components() { | |
293 | echo "[[ Building common-components.pot ]]" | |
294 | local paths="" | |
295 | local has_multiple=0 | |
296 | for comp in $COMPONENT_POTS ; do | |
297 | local rawfile=".raw-"$(echo $comp | tr '[:upper:]' '[:lower:]').pot | |
298 | if [ -f "$rawfile" ]; then | |
299 | paths="$paths $rawfile" | |
300 | has_multiple=1 | |
301 | fi | |
302 | done | |
303 | if [ $has_multiple -eq 1 ]; then | |
304 | msgcomm $paths > .raw-common-components.pot | |
305 | else | |
306 | cat $HEADER > .raw-common-components.pot | |
307 | fi | |
308 | } | |
309 | ||
310 | ##################################################################### | |
311 | ## example: build_final_pot Mailing | |
312 | ## example: build_final_pot install | |
313 | function build_final_pot() { | |
314 | local name="$1" | |
315 | local rawpath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
316 | local finalpath="$POTDIR/"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
317 | local tmpfile=`tempfile` | |
318 | ||
319 | echo "[[ Building final strings for ${name} ]]" | |
320 | ||
321 | cp -f "$rawpath" "$finalpath" | |
322 | ||
323 | if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then | |
324 | msgcomm "$finalpath" .raw-common-components.pot > $tmpfile | |
325 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
326 | ||
327 | msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile | |
328 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
329 | ||
330 | elif [ "$name" == "install" ]; then | |
331 | msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile | |
332 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
333 | fi | |
334 | ||
335 | rm -f "$tmpfile" | |
336 | } | |
337 | ||
338 | ##################################################################### | |
339 | ## Delete temp files | |
340 | function do_cleanup() { | |
341 | echo "[[ Cleanup temp files ]]" | |
342 | rm .header .raw*pot -f | |
343 | } | |
344 | ||
345 | ##################################################################### | |
346 | ## Main | |
347 | ||
348 | [ "$1" == "--help" ] && usage | |
349 | [ "$1" == "-h" ] && usage | |
350 | ||
351 | check_deps | |
352 | ||
353 | FOUND_ACTION= | |
354 | while getopts "asfdc" opt; do | |
355 | case $opt in | |
356 | a) | |
357 | DO_SCAN=1 | |
358 | DO_DIGEST=1 | |
359 | DO_CLEANUP=1 | |
360 | FOUND_ACTION=1 | |
361 | ;; | |
362 | s) | |
363 | DO_SCAN=1 | |
364 | FOUND_ACTION=1 | |
365 | ;; | |
366 | d) | |
367 | DO_DIGEST=1 | |
368 | FOUND_ACTION=1 | |
369 | ;; | |
370 | c) | |
371 | DO_CLEANUP=1 | |
372 | FOUND_ACTION=1 | |
373 | ;; | |
374 | f) | |
375 | FORCE=1 | |
376 | ;; | |
377 | \?) | |
378 | echo "Invalid option: -$OPTARG" >&2 | |
379 | exit 1 | |
380 | ;; | |
381 | :) | |
382 | echo "Option -$OPTARG requires an argument." >&2 | |
383 | exit 1 | |
384 | ;; | |
385 | esac | |
386 | done | |
387 | ||
388 | if [ -z "$FOUND_ACTION" ]; then | |
389 | DO_SCAN=1 | |
390 | DO_DIGEST=1 | |
391 | DO_CLEANUP=1 | |
392 | fi | |
393 | ||
394 | shift $((OPTIND-1)) | |
395 | ||
396 | [ "$1" == "" ] && echo 'source dir missing' && usage | |
397 | test ! -e "$1" && echo 'source does not exist' && usage | |
398 | test ! -d "$1" && echo 'source not a directory' && usage | |
399 | ||
400 | [ "$2" == "" ] && echo 'target dir missing' && usage | |
401 | test ! -e "$2" && echo 'target does not exist' && usage | |
402 | test ! -d "$2" && echo 'target not a directory' && usage | |
403 | ||
404 | # use absolute paths so that we can chdir/pushd | |
405 | SRC=$(php -r 'echo realpath($argv[1]);' "$1") | |
406 | POTDIR=$(php -r 'echo realpath($argv[1]);' "$2") | |
407 | HEADER=$(build_header "$HEADER_TMPL") | |
408 | ## TODO: substitute "NOW" in HEADER | |
409 | shift 2 | |
410 | ||
411 | if [ -z "$1" ]; then | |
412 | POTS="$COMPONENT_POTS $ADHOC_POTS $MAGIC_POTS" | |
413 | else | |
414 | POTS="$@" | |
415 | fi | |
416 | ||
417 | if [ -n "$DO_SCAN" ]; then | |
418 | pushd "$SRC" >> /dev/null | |
419 | for POT in $POTS ; do | |
420 | build_raw_pot "$POT" | |
421 | done | |
422 | popd >> /dev/null | |
423 | fi | |
424 | ||
425 | if [ -n "$DO_DIGEST" ]; then | |
426 | pushd "$POTDIR" >> /dev/null | |
427 | echo "POTS : $POTS" | |
428 | build_common_components | |
429 | for POT in $POTS ; do | |
430 | echo "+++++++++ $POT" | |
431 | build_final_pot "$POT" | |
432 | done | |
433 | popd >> /dev/null | |
434 | fi | |
435 | ||
436 | if [ -n "$DO_CLEANUP" ]; then | |
437 | pushd "$POTDIR" >> /dev/null | |
438 | do_cleanup | |
439 | popd >> /dev/null | |
440 | fi |