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_* \ | |
c9a3a453 ML |
142 | js/angular-crmApp.js \ |
143 | js/angular-crmAttachment.js \ | |
144 | js/angular-crm-ui.js \ | |
145 | js/Common.js \ | |
146 | js/crm.ajax.js \ | |
147 | js/crm.backbone.js \ | |
148 | js/jquery/jquery.crmeditable.js \ | |
149 | js/jquery/jquery.crmProfileSelector.js \ | |
150 | js/jquery/jquery.crmRevisionLink.js \ | |
151 | js/jquery/jquery.dashboard.js \ | |
152 | js/model/crm.designer.js \ | |
153 | js/model/crm.uf.js \ | |
154 | js/view/crm.designer.js | |
7773dd1e | 155 | packages/HTML/QuickForm \ |
c9a3a453 ML |
156 | partials/crmAttachment \ |
157 | partials/crmExample \ | |
158 | partials/crmUi | |
159 | ||
7773dd1e ML |
160 | |
161 | ## The CRM/Upgrade folder includes *.tpl files which, for some reason, | |
162 | ## have been omitted from past pot's. Omitting these requires more | |
163 | ## precise file selection. | |
164 | find CRM/Upgrade -name '*.php' | _civistrings -ao "$filepath" - | |
165 | _civistrings -ao "$filepath" templates/CRM/Upgrade | |
166 | ;; | |
167 | ||
168 | common-components) | |
169 | ## Not yet; handled in the digest phase. That why it's in MAGIC_POTS | |
170 | return | |
171 | ;; | |
172 | ||
173 | countries) | |
174 | cat "$HEADER" > "$filepath" | |
175 | grep ^INSERT xml/templates/civicrm_country.tpl \ | |
176 | | cut -d\" -f4 \ | |
177 | | while read entry; do | |
178 | echo -e "msgctxt \"country\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
179 | done \ | |
180 | >> "$filepath" | |
181 | ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" | |
182 | ;; | |
183 | ||
184 | drupal-civicrm) | |
185 | _civistrings -o "$filepath" \ | |
186 | drupal \ | |
187 | CRM/Core/Permission.php | |
188 | ;; | |
189 | ||
190 | install) | |
191 | _civistrings -o "$filepath" \ | |
192 | install/ | |
193 | ;; | |
194 | ||
195 | menu) | |
196 | cat "$HEADER" > "$filepath" | |
197 | grep -h '<title>' CRM/*/xml/Menu/*.xml \ | |
198 | | sed 's/^.*<title>\(.*\)<\/title>.*$/\1/' \ | |
199 | | while read entry; do | |
200 | echo -e "msgctxt \"menu\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
201 | done \ | |
202 | >> "$filepath" | |
203 | _civistrings --msgctxt=menu xml/templates/civicrm_navigation.tpl -ao "$filepath" | |
204 | ;; | |
205 | ||
206 | provinces) | |
207 | cat "$HEADER" > "$filepath" | |
208 | grep '^(' xml/templates/civicrm_state_province.tpl \ | |
209 | | cut -d\" -f4 \ | |
210 | | while read entry; do | |
211 | echo -e "msgctxt \"province\"\nmsgid \"$entry\"\nmsgstr \"\"\n" | |
212 | done \ | |
213 | >> "$filepath" | |
214 | ## Hmm, if civicrm_country.tpl used {ts}, then we could just call "civistrings --msgctxt=country" | |
215 | ;; | |
216 | ||
217 | ## Standard targets, sorted alphabetically | |
218 | ||
219 | Campaign) | |
220 | _civistrings -o "$filepath" \ | |
221 | {CRM,templates/CRM}/$name \ | |
222 | xml/templates/message_templates/petition_* | |
223 | ;; | |
224 | ||
225 | Case) | |
226 | _civistrings -o "$filepath" \ | |
227 | {CRM,templates/CRM}/$name \ | |
c9a3a453 | 228 | partials/crmCaseType \ |
7773dd1e ML |
229 | xml/templates/message_templates/case_* |
230 | ;; | |
231 | ||
232 | Contribute) | |
233 | _civistrings -o "$filepath" \ | |
234 | {CRM,templates/CRM}/$name \ | |
235 | xml/templates/message_templates/contribution_* \ | |
236 | xml/templates/message_templates/test_* | |
237 | ;; | |
238 | ||
239 | Event) | |
240 | _civistrings -o "$filepath" \ | |
241 | {CRM,templates/CRM}/$name \ | |
242 | xml/templates/message_templates/event_* \ | |
243 | xml/templates/message_templates/participant_* | |
244 | ;; | |
245 | ||
c9a3a453 ML |
246 | Mailing) |
247 | _civistrings -o "$filepath" \ | |
248 | {CRM,templates/CRM}/$name \ | |
249 | js/angular-crmCaseType.js \ | |
250 | js/angular-crmMailingAB.js \ | |
251 | js/angular-crmMailingAB/services.js \ | |
252 | js/angular-crmMailing/directives.js \ | |
253 | js/angular-crmMailing.js \ | |
254 | js/angular-crmMailing/services.js \ | |
255 | partials/crmMailing \ | |
256 | partials/crmMailingAB | |
257 | ;; | |
258 | ||
7773dd1e ML |
259 | Member) |
260 | _civistrings -o "$filepath" \ | |
261 | {CRM,templates/CRM}/$name \ | |
262 | xml/templates/message_templates/membership_* | |
263 | ;; | |
264 | ||
265 | PCP) | |
266 | _civistrings -o "$filepath" \ | |
267 | {CRM,templates/CRM}/$name \ | |
268 | xml/templates/message_templates/pcp_* | |
269 | ;; | |
270 | ||
271 | Pledge) | |
272 | _civistrings -o "$filepath" \ | |
273 | {CRM,templates/CRM}/$name \ | |
274 | xml/templates/message_templates/pledge_* | |
275 | ;; | |
276 | ||
277 | *) | |
278 | if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then | |
279 | _civistrings -o "$filepath" {CRM,templates/CRM}/$name | |
280 | else | |
281 | echo "unrecognized pot: $name" | |
282 | fi | |
283 | ;; | |
284 | ||
285 | esac | |
286 | ||
287 | find "$filepath" ! -empty | while read f; do | |
288 | msguniq "$filepath" | sponge "$filepath" | |
289 | done | |
290 | } | |
291 | ||
292 | ##################################################################### | |
293 | ## usage: make_stat <name> | |
294 | ## example: make_stat Mailing | |
295 | function make_stat() { | |
296 | local name="$1" | |
297 | local filepath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
298 | grep ^msgid "$filepath" | sort -u > "$filepath.msgid" | |
299 | grep '^#:' "$filepath" | sed 's/#://' | tr ' ' '\n' | sort -u > "$filepath.files" | |
300 | } | |
301 | ||
302 | ##################################################################### | |
303 | ## Scan .raw-*.pot for common strings and put them in common-components.pot | |
304 | ## usage: build_common_components | |
305 | function build_common_components() { | |
306 | echo "[[ Building common-components.pot ]]" | |
307 | local paths="" | |
308 | local has_multiple=0 | |
309 | for comp in $COMPONENT_POTS ; do | |
310 | local rawfile=".raw-"$(echo $comp | tr '[:upper:]' '[:lower:]').pot | |
311 | if [ -f "$rawfile" ]; then | |
312 | paths="$paths $rawfile" | |
313 | has_multiple=1 | |
314 | fi | |
315 | done | |
316 | if [ $has_multiple -eq 1 ]; then | |
317 | msgcomm $paths > .raw-common-components.pot | |
318 | else | |
319 | cat $HEADER > .raw-common-components.pot | |
320 | fi | |
321 | } | |
322 | ||
323 | ##################################################################### | |
324 | ## example: build_final_pot Mailing | |
325 | ## example: build_final_pot install | |
326 | function build_final_pot() { | |
327 | local name="$1" | |
328 | local rawpath="$POTDIR/.raw-"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
329 | local finalpath="$POTDIR/"$(echo $name | tr '[:upper:]' '[:lower:]').pot | |
330 | local tmpfile=`tempfile` | |
331 | ||
332 | echo "[[ Building final strings for ${name} ]]" | |
333 | ||
334 | cp -f "$rawpath" "$finalpath" | |
335 | ||
336 | if echo " $COMPONENT_POTS " | grep -q " $name " > /dev/null ; then | |
337 | msgcomm "$finalpath" .raw-common-components.pot > $tmpfile | |
338 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
339 | ||
340 | msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile | |
341 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
342 | ||
343 | elif [ "$name" == "install" ]; then | |
344 | msgcomm "$finalpath" .raw-common-base.pot | sponge $tmpfile | |
345 | msgcomm --unique "$finalpath" $tmpfile | sponge "$finalpath" | |
346 | fi | |
347 | ||
348 | rm -f "$tmpfile" | |
349 | } | |
350 | ||
351 | ##################################################################### | |
352 | ## Delete temp files | |
353 | function do_cleanup() { | |
354 | echo "[[ Cleanup temp files ]]" | |
355 | rm .header .raw*pot -f | |
356 | } | |
357 | ||
358 | ##################################################################### | |
359 | ## Main | |
360 | ||
361 | [ "$1" == "--help" ] && usage | |
362 | [ "$1" == "-h" ] && usage | |
363 | ||
364 | check_deps | |
365 | ||
366 | FOUND_ACTION= | |
367 | while getopts "asfdc" opt; do | |
368 | case $opt in | |
369 | a) | |
370 | DO_SCAN=1 | |
371 | DO_DIGEST=1 | |
372 | DO_CLEANUP=1 | |
373 | FOUND_ACTION=1 | |
374 | ;; | |
375 | s) | |
376 | DO_SCAN=1 | |
377 | FOUND_ACTION=1 | |
378 | ;; | |
379 | d) | |
380 | DO_DIGEST=1 | |
381 | FOUND_ACTION=1 | |
382 | ;; | |
383 | c) | |
384 | DO_CLEANUP=1 | |
385 | FOUND_ACTION=1 | |
386 | ;; | |
387 | f) | |
388 | FORCE=1 | |
389 | ;; | |
390 | \?) | |
391 | echo "Invalid option: -$OPTARG" >&2 | |
392 | exit 1 | |
393 | ;; | |
394 | :) | |
395 | echo "Option -$OPTARG requires an argument." >&2 | |
396 | exit 1 | |
397 | ;; | |
398 | esac | |
399 | done | |
400 | ||
401 | if [ -z "$FOUND_ACTION" ]; then | |
402 | DO_SCAN=1 | |
403 | DO_DIGEST=1 | |
404 | DO_CLEANUP=1 | |
405 | fi | |
406 | ||
407 | shift $((OPTIND-1)) | |
408 | ||
409 | [ "$1" == "" ] && echo 'source dir missing' && usage | |
410 | test ! -e "$1" && echo 'source does not exist' && usage | |
411 | test ! -d "$1" && echo 'source not a directory' && usage | |
412 | ||
413 | [ "$2" == "" ] && echo 'target dir missing' && usage | |
414 | test ! -e "$2" && echo 'target does not exist' && usage | |
415 | test ! -d "$2" && echo 'target not a directory' && usage | |
416 | ||
417 | # use absolute paths so that we can chdir/pushd | |
418 | SRC=$(php -r 'echo realpath($argv[1]);' "$1") | |
419 | POTDIR=$(php -r 'echo realpath($argv[1]);' "$2") | |
420 | HEADER=$(build_header "$HEADER_TMPL") | |
421 | ## TODO: substitute "NOW" in HEADER | |
422 | shift 2 | |
423 | ||
424 | if [ -z "$1" ]; then | |
425 | POTS="$COMPONENT_POTS $ADHOC_POTS $MAGIC_POTS" | |
426 | else | |
427 | POTS="$@" | |
428 | fi | |
429 | ||
430 | if [ -n "$DO_SCAN" ]; then | |
431 | pushd "$SRC" >> /dev/null | |
432 | for POT in $POTS ; do | |
433 | build_raw_pot "$POT" | |
434 | done | |
435 | popd >> /dev/null | |
436 | fi | |
437 | ||
438 | if [ -n "$DO_DIGEST" ]; then | |
439 | pushd "$POTDIR" >> /dev/null | |
440 | echo "POTS : $POTS" | |
441 | build_common_components | |
442 | for POT in $POTS ; do | |
443 | echo "+++++++++ $POT" | |
444 | build_final_pot "$POT" | |
445 | done | |
446 | popd >> /dev/null | |
447 | fi | |
448 | ||
449 | if [ -n "$DO_CLEANUP" ]; then | |
450 | pushd "$POTDIR" >> /dev/null | |
451 | do_cleanup | |
452 | popd >> /dev/null | |
453 | fi |