+commit 187fcbe585560f128c7436e66f6b8e3a789a73b0
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Sun Jun 26 12:28:10 2011 -0400
+
+ mallard.its: Set msgctxt on info titles
+
+ its/mallard.its | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+commit b57dfa4327da4d889dbfc5d26d824482fd7b2f54
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Sun Jun 26 12:27:46 2011 -0400
+
+ Don't bomb if a locNotePointer returns a string
+
+ itstool.in | 22 +++++++++++++---------
+ 1 files changed, 13 insertions(+), 9 deletions(-)
+
+commit 8d97758ac43f2079d252fedf2a9666b33a751fd0
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Sat Jun 25 14:49:53 2011 -0400
+
+ Added itst:context to specify a msgctxt for a node
+
+ itstool.in | 35 ++++++++++++++++++++++++++++++++++-
+ tests/Context.ll.po | 35 +++++++++++++++++++++++++++++++++++
+ tests/Context.ll.xml | 15 +++++++++++++++
+ tests/Context.pot | 35 +++++++++++++++++++++++++++++++++++
+ tests/Context.xml | 14 ++++++++++++++
+ tests/run_tests.py | 3 +++
+ 6 files changed, 136 insertions(+), 1 deletions(-)
+
+commit e84f296dd21de75b9244896be9d2acc6aeed4dea
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Fri Jun 24 12:25:17 2011 -0400
+
+ Fixes for Python 3
+
+ itstool.in | 2 +-
+ tests/run_tests.py | 2 +-
+ 2 files changed, 2 insertions(+), 2 deletions(-)
+
+commit b13436f2a75ffded500e5f26028237d337510394
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Fri Jun 24 11:21:38 2011 -0400
+
+ Use #!/usr/bin/python -s for shebang, RH bug #702989
+
+ itstool.in | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+commit d8b399ae628e822ee9a9762657969361c5aebc93
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Fri Jun 24 10:19:54 2011 -0400
+
+ Make itst:drop work on non-inline nodes
+
+ itstool.in | 15 ++++++++++++++-
+ tests/Droprule.xml | 2 ++
+ 2 files changed, 16 insertions(+), 1 deletions(-)
+
+commit 56cd8382ae979fab1a4a4a25f052d93d1070fc6d
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Thu Jun 23 17:51:40 2011 -0400
+
+ Made dropRule take a drop attribute, like other rules
+
+ itstool.in | 6 +++---
+ tests/Droprule.ll.xml | 2 +-
+ tests/Droprule.xml | 2 +-
+ 3 files changed, 5 insertions(+), 5 deletions(-)
+
+commit 333218d4978a7b203252d35e7979f64cfb3b0c1c
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Tue May 31 22:46:29 2011 +0200
+
+ Add itst drop rule
+
+ The itst Droprule is a rule allowing to ignore completely a tag from
+ the translation, including its content. The resulting xml will not
+ have the tag either.
+
+ itstool.in | 7 ++++++-
+ tests/Droprule.ll.po | 15 +++++++++++++++
+ tests/Droprule.ll.xml | 11 +++++++++++
+ tests/Droprule.pot | 15 +++++++++++++++
+ tests/Droprule.xml | 10 ++++++++++
+ tests/run_tests.py | 4 ++++
+ 6 files changed, 61 insertions(+), 1 deletions(-)
+
+commit 7cda8e16e2281e0e6c42f1e47bfc98ee5e4fe4ae
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Thu Jun 23 17:45:16 2011 -0400
+
+ Renamed attribute test files
+
+ tests/Attributes1.ll.po | 19 +++++++++++++++++++
+ tests/Attributes1.ll.xml | 10 ++++++++++
+ tests/Attributes1.pot | 19 +++++++++++++++++++
+ tests/Attributes1.xml | 10 ++++++++++
+ tests/README | 2 --
+ tests/run_tests.py | 4 ++--
+ tests/x-attr1.ll.po | 19 -------------------
+ tests/x-attr1.ll.xml | 10 ----------
+ tests/x-attr1.pot | 19 -------------------
+ tests/x-attr1.xml | 10 ----------
+ 10 files changed, 60 insertions(+), 62 deletions(-)
+
+commit 4334f863ae6ab636b3e7c905e15693a188e4bee1
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Thu Jun 23 16:11:42 2011 -0400
+
+ Handled translatable attributes in non-translatable elements
+
+ itstool.in | 25 +++++++++++++++----------
+ tests/README | 4 +++-
+ tests/run_tests.py | 3 +++
+ tests/x-attr1.ll.po | 19 +++++++++++++++++++
+ tests/x-attr1.ll.xml | 10 ++++++++++
+ tests/x-attr1.pot | 19 +++++++++++++++++++
+ tests/x-attr1.xml | 10 ++++++++++
+ 7 files changed, 79 insertions(+), 11 deletions(-)
+
+commit 7ecd70b27d5cc22f8d6b35fc68aa9fe9cd3a7def
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Sun Jun 12 21:40:36 2011 +0200
+
+ Extract and translate node attributes
+
+ itstool.in | 41 ++++++++++++++++++++++++++++++++++++-----
+ tests/Translate1.ll.po | 4 ++++
+ tests/Translate1.ll.xml | 2 +-
+ tests/Translate1.pot | 4 ++++
+ tests/Translate2.ll.po | 4 ++++
+ tests/Translate2.ll.xml | 2 +-
+ tests/Translate2.pot | 4 ++++
+ tests/TranslateGlobal.ll.po | 23 +++++++++++++++++++++++
+ tests/TranslateGlobal.ll.xml | 13 +++++++++++++
+ tests/TranslateGlobal.pot | 23 +++++++++++++++++++++++
+ tests/run_tests.py | 7 +++----
+ 11 files changed, 116 insertions(+), 11 deletions(-)
+
+commit fadb43fc37dbe18d9b1204bb13fb5ccea492b649
+Merge: 43a518a 1ebcc2a
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Wed Jun 1 09:22:03 2011 -0400
+
+ Merge branch 'testsuite'
+
+commit 1ebcc2af7fa4d6e12687199e173720b44eb823a3
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Wed Jun 1 15:11:51 2011 +0200
+
+ Fix LocNote2 test and add README in tests
+
+ tests/LocNote2.pot | 25 +++++++++++++++++++++++++
+ tests/README | 5 +++++
+ tests/run_tests.py | 4 ++--
+ 3 files changed, 32 insertions(+), 2 deletions(-)
+
+commit 43a518a2da4eb11f78b23cbca6291f9cd7c2f4ab
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Wed Jun 1 08:59:26 2011 -0400
+
+ Adding copyright and license info to itstool
+
+ itstool.in | 17 +++++++++++++++++
+ 1 files changed, 17 insertions(+), 0 deletions(-)
+
+commit 480a2531c9b8ba0249f9326e8d043de309486e5b
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Wed Jun 1 13:45:51 2011 +0200
+
+ Add remaining tests
+
+ itstool.in | 5 ++-
+ tests/EX-locNotePointer-attribute-1.pot | 21 +++++++++++
+ tests/EX-locNoteRefPointer-attribute-1.pot | 21 +++++++++++
+ tests/LocNote1.pot | 25 +++++++++++++
+ tests/LocNote3.pot | 29 ++++++++++++++++
+ tests/LocNote4.pot | 30 ++++++++++++++++
+ tests/WithinText1.ll.po | 23 ++++++++++++
+ tests/WithinText1.ll.xml | 14 ++++++++
+ tests/WithinText1.pot | 23 ++++++++++++
+ tests/WithinText2.ll.po | 51 ++++++++++++++++++++++++++++
+ tests/WithinText2.ll.xml | 21 +++++++++++
+ tests/WithinText2.pot | 51 ++++++++++++++++++++++++++++
+ tests/run_tests.py | 40 ++++++++++++++++++++-
+ 13 files changed, 350 insertions(+), 4 deletions(-)
+
+commit 66f3fdd0d678bfffefef4b26e0c6812e3a9b8bb8
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Wed Jun 1 08:07:42 2011 -0400
+
+ itstool: Allow both XLink and child rules on its:rules
+
+ We weren't handling tests/WithinText2.xml correctly
+
+ itstool.in | 3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+commit 2ffe1d30ea1d5925a75955bc9b88c3d4c14b7b46
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Tue May 31 22:05:00 2011 +0200
+
+ Add tests for the Translate* series
+
+ tests/Translate2.ll.po | 15 +++++++++++++++
+ tests/Translate2.ll.xml | 10 ++++++++++
+ tests/Translate2.pot | 15 +++++++++++++++
+ tests/Translate3.ll.po | 19 +++++++++++++++++++
+ tests/Translate3.ll.xml | 10 ++++++++++
+ tests/Translate4.ll.po | 19 +++++++++++++++++++
+ tests/Translate4.ll.xml | 10 ++++++++++
+ tests/Translate5.ll.po | 19 +++++++++++++++++++
+ tests/Translate5.ll.xml | 19 +++++++++++++++++++
+ tests/Translate5.pot | 19 +++++++++++++++++++
+ tests/Translate6.ll.po | 31 +++++++++++++++++++++++++++++++
+ tests/Translate6.ll.xml | 19 +++++++++++++++++++
+ tests/Translate7.ll.po | 19 +++++++++++++++++++
+ tests/Translate7.ll.xml | 29 +++++++++++++++++++++++++++++
+ tests/Translate7.pot | 19 +++++++++++++++++++
+ tests/run_tests.py | 41 +++++++++++++++++++++++++++++++++++------
+ 16 files changed, 307 insertions(+), 6 deletions(-)
+
+commit d3177444660bac67265ba9cf17d586fd1b0b2907
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Tue May 31 20:41:13 2011 +0200
+
+ Fix xml iteration when constructing translated subnodes
+
+ itstool.in | 3 ++-
+ tests/Translate1.ll.xml | 2 +-
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+commit 6c7b6bb9db8e77ec142fac2c9b514ec1769df65b
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Tue May 31 16:33:48 2011 +0200
+
+ Use unicode strings inside of Message class
+
+ itstool.in | 40 +++++++++++++++++++++-------------------
+ 1 files changed, 21 insertions(+), 19 deletions(-)
+
+commit f4635d084d93212af73221de5165c3fa92f834cd
+Author: Claude Paroz <claude@2xlibre.net>
+Date: Mon May 30 21:23:15 2011 +0200
+
+ Initial test infrastructure
+
+ tests/Translate1.ll.po | 35 +++++++++++++++++++++++++
+ tests/Translate1.ll.xml | 39 +++++++++++++++++++++++++++
+ tests/Translate1.pot | 35 +++++++++++++++++++++++++
+ tests/run_tests.py | 66 +++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 175 insertions(+), 0 deletions(-)
+
+commit 00a8df54545a66aa70c20ceecb19709ca0b811a7
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Fri May 27 12:09:10 2011 -0400
+
+ Include installation dir in search path if XDG_DATA_DIRS not set
+
+ configure.ac | 9 +++++++++
+ itstool.in | 5 ++++-
+ 2 files changed, 13 insertions(+), 1 deletions(-)
+
+commit abd3afc9bd7066658d295de3a39c5c0b287d0644
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Mon May 9 21:26:42 2011 -0400
+
+ itstool: Allow localization notes to be space-preserving
+
+ itstool.in | 32 ++++++++++++++++++++++----------
+ 1 files changed, 22 insertions(+), 10 deletions(-)
+
+commit 04a706ae1057d0a9ed4b36c7e20292d44e97ad80
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Mon May 9 16:32:25 2011 -0400
+
+ itstool.1: Added a man page
+
+ .gitignore | 1 +
+ Makefile.am | 10 +++++++-
+ configure.ac | 1 +
+ itstool.1.in | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 4 files changed, 88 insertions(+), 1 deletions(-)
+
+commit 69db1ded268af460d31b6ab2d86172873468890f
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Mon May 9 11:15:02 2011 -0400
+
+ Catch XPath exceptions and warn
+
+ itstool.in | 30 +++++++++++++++++++-----------
+ 1 files changed, 19 insertions(+), 11 deletions(-)
+
+commit 6fdaea81bf70023612202cadcd7d9995e571f23e
+Author: Shaun McCance <shaunm@gnome.org>
+Date: Fri May 6 17:02:13 2011 -0400
+
+ Version 1.0.1
+
+ NEWS | 7 +++++++
+ configure.ac | 2 +-
+ 2 files changed, 8 insertions(+), 1 deletions(-)
+
commit e9344508508112ecd177232ba49b5664e860bc9f
Author: Shaun McCance <shaunm@gnome.org>
Date: Tue May 3 12:48:53 2011 -0400
bin_SCRIPTS = itstool
-EXTRA_DIST = ChangeLog COPYING.GPL3 $(bin_SCRIPTS) itstool.in
+man_MANS = itstool.1
+
+EXTRA_DIST = \
+ ChangeLog \
+ COPYING.GPL3 \
+ $(bin_SCRIPTS) \
+ itstool.in \
+ $(man_MANS) \
+ itstool.1.in
ChangeLog:
@if test -f $(top_srcdir)/.git/HEAD; then \
POST_UNINSTALL = :
subdir = .
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(srcdir)/itstool.in \
- $(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
- install-sh missing
+ $(srcdir)/Makefile.in $(srcdir)/itstool.1.in \
+ $(srcdir)/itstool.in $(top_srcdir)/configure AUTHORS COPYING \
+ ChangeLog INSTALL NEWS install-sh missing
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
-CONFIG_CLEAN_FILES = itstool
+CONFIG_CLEAN_FILES = itstool itstool.1
CONFIG_CLEAN_VPATH_FILES =
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-am__installdirs = "$(DESTDIR)$(bindir)"
+am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"
SCRIPTS = $(bin_SCRIPTS)
SOURCES =
DIST_SOURCES =
install-pdf-recursive install-ps-recursive install-recursive \
installcheck-recursive installdirs-recursive pdf-recursive \
ps-recursive uninstall-recursive
+man1dir = $(mandir)/man1
+NROFF = nroff
+MANS = $(man_MANS)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CYGPATH_W = @CYGPATH_W@
+DATADIR = @DATADIR@
DEFS = @DEFS@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
top_srcdir = @top_srcdir@
SUBDIRS = its
bin_SCRIPTS = itstool
-EXTRA_DIST = ChangeLog COPYING.GPL3 $(bin_SCRIPTS) itstool.in
+man_MANS = itstool.1
+EXTRA_DIST = \
+ ChangeLog \
+ COPYING.GPL3 \
+ $(bin_SCRIPTS) \
+ itstool.in \
+ $(man_MANS) \
+ itstool.1.in
+
all: all-recursive
.SUFFIXES:
$(am__aclocal_m4_deps):
itstool: $(top_builddir)/config.status $(srcdir)/itstool.in
cd $(top_builddir) && $(SHELL) ./config.status $@
+itstool.1: $(top_builddir)/config.status $(srcdir)/itstool.1.in
+ cd $(top_builddir) && $(SHELL) ./config.status $@
install-binSCRIPTS: $(bin_SCRIPTS)
@$(NORMAL_INSTALL)
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
+install-man1: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ test -z "$(man1dir)" || $(MKDIR_P) "$(DESTDIR)$(man1dir)"
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ { for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man1:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man1dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
+ sed -n '/\.1[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ test -z "$$files" || { \
+ echo " ( cd '$(DESTDIR)$(man1dir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(man1dir)" && rm -f $$files; }
# This directory's subdirectories are mostly independent; you can cd
# into them and run `make' without going through this Makefile.
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
+ @list='$(MANS)'; if test -n "$$list"; then \
+ list=`for p in $$list; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
+ if test -n "$$list" && \
+ grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
+ echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
+ grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
+ echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
+ echo " typically \`make maintainer-clean' will remove them" >&2; \
+ exit 1; \
+ else :; fi; \
+ else :; fi
$(am__remove_distdir)
test -d "$(distdir)" || mkdir "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
exit 1; } >&2
check-am: all-am
check: check-recursive
-all-am: Makefile $(SCRIPTS)
+all-am: Makefile $(SCRIPTS) $(MANS)
installdirs: installdirs-recursive
installdirs-am:
- for dir in "$(DESTDIR)$(bindir)"; do \
+ for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
info-am:
-install-data-am:
+install-data-am: install-man
install-dvi: install-dvi-recursive
install-info-am:
-install-man:
+install-man: install-man1
install-pdf: install-pdf-recursive
ps-am:
-uninstall-am: uninstall-binSCRIPTS
+uninstall-am: uninstall-binSCRIPTS uninstall-man
+
+uninstall-man: uninstall-man1
.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \
install-am install-strip tags-recursive
info-am install install-am install-binSCRIPTS install-data \
install-data-am install-dvi install-dvi-am install-exec \
install-exec-am install-html install-html-am install-info \
- install-info-am install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs installdirs-am maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-generic pdf \
- pdf-am ps ps-am tags tags-recursive uninstall uninstall-am \
- uninstall-binSCRIPTS
+ install-info-am install-man install-man1 install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic pdf pdf-am ps ps-am tags tags-recursive \
+ uninstall uninstall-am uninstall-binSCRIPTS uninstall-man \
+ uninstall-man1
ChangeLog:
+1.1.0
+=====
+* Added itst:context to set msgctxt
+* Added itst:drop to drop context from translations
+* Allow XML attribute to be translated
+* Allow locNotePointer to return a string
+* Allow localization notes to be space-preserving
+* Allow both XLink and child rules on its:rules
+* Fixed Unicode encoding/decoding errors
+* Added automated test suite
+* Added a man page
+* Python 3 fixes
+* Commits by Shaun McCance, Claude Paroz
+
1.0.1
=====
* Convert POSIX-style locales to BCP47
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.66 for itstool 1.0.1.
+# Generated by GNU Autoconf 2.66 for itstool 1.1.0.
#
#
# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
# Identity of this package.
PACKAGE_NAME='itstool'
PACKAGE_TARNAME='itstool'
-PACKAGE_VERSION='1.0.1'
-PACKAGE_STRING='itstool 1.0.1'
+PACKAGE_VERSION='1.1.0'
+PACKAGE_STRING='itstool 1.1.0'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
ac_subst_vars='LTLIBOBJS
LIBOBJS
+DATADIR
am__untar
am__tar
AMTAR
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures itstool 1.0.1 to adapt to many kinds of systems.
+\`configure' configures itstool 1.1.0 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of itstool 1.0.1:";;
+ short | recursive ) echo "Configuration of itstool 1.1.0:";;
esac
cat <<\_ACEOF
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-itstool configure 1.0.1
+itstool configure 1.1.0
generated by GNU Autoconf 2.66
Copyright (C) 2010 Free Software Foundation, Inc.
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by itstool $as_me 1.0.1, which was
+It was created by itstool $as_me 1.1.0, which was
generated by GNU Autoconf 2.66. Invocation command line was
$ $0 $@
# Define the identity of the package.
PACKAGE='itstool'
- VERSION='1.0.1'
+ VERSION='1.1.0'
cat >>confdefs.h <<_ACEOF
-ac_config_files="$ac_config_files Makefile itstool its/Makefile"
+DATADIR=`(
+ case $prefix in
+ NONE) prefix=$ac_default_prefix ;;
+ *) ;;
+ esac
+ eval echo $(eval echo $datadir)
+)`
+
+
+ac_config_files="$ac_config_files Makefile itstool itstool.1 its/Makefile"
cat >confcache <<\_ACEOF
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by itstool $as_me 1.0.1, which was
+This file was extended by itstool $as_me 1.1.0, which was
generated by GNU Autoconf 2.66. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-itstool config.status 1.0.1
+itstool config.status 1.1.0
configured by $0, generated by GNU Autoconf 2.66,
with options \\"\$ac_cs_config\\"
case $ac_config_target in
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"itstool") CONFIG_FILES="$CONFIG_FILES itstool" ;;
+ "itstool.1") CONFIG_FILES="$CONFIG_FILES itstool.1" ;;
"its/Makefile") CONFIG_FILES="$CONFIG_FILES its/Makefile" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
-AC_INIT([itstool], [1.0.1], [])
+AC_INIT([itstool], [1.1.0], [])
AM_INIT_AUTOMAKE([1.9 no-dist-gzip dist-bzip2])
+DATADIR=`(
+ case $prefix in
+ NONE) prefix=$ac_default_prefix ;;
+ *) ;;
+ esac
+ eval echo $(eval echo $datadir)
+)`
+AC_SUBST([DATADIR])
+
AC_CONFIG_FILES([
Makefile
itstool
+itstool.1
its/Makefile
])
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
CYGPATH_W = @CYGPATH_W@
+DATADIR = @DATADIR@
DEFS = @DEFS@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
<its:translateRule translate="no" selector="//mal:credit/mal:email"/>
<itst:externalRefRule selector="//mal:media" refPointer="@src"/>
+
+ <itst:contextRule selector="//mal:info/mal:title[@type]" contextPointer="@type"/>
+ <itst:contextRule selector="//mal:info/mal:title[@type and @role]" contextPointer="concat(@type, ':', @role)"/>
</its:rules>
-#!/usr/bin/env python
-
-VERSION="1.0.1"
+#!/usr/bin/python -s
+#
+# Copyright (c) 2010-2011 Shaun McCance <shaunm@gnome.org>
+#
+# ITS Tool program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# ITS Tool is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with ITS Tool; if not, write to the Free Software Foundation, 59 Temple
+# Place, Suite 330, Boston, MA 0211-1307 USA.
+#
+
+VERSION="1.1.0"
+DATADIR="/usr/local/share"
import gettext
import hashlib
out.write('"Content-Transfer-Encoding: 8bit\\n"\n')
out.write('\n')
for msg in msgs:
- out.write(msg.format())
+ out.write(msg.format().encode('utf-8'))
out.write('\n')
self._comments = []
self._preserve = False
+ def __repr__(self):
+ if self._empty:
+ return "Empty message"
+ return self.get_string()
+
class Placeholder (object):
def __init__ (self, node):
self.node = node
- self.name = node.name
+ self.name = unicode(node.name, 'utf-8')
def escape (self, text):
return text.replace('\\','\\\\').replace('"', "\\\"").replace("\n","\\n").replace("\t","\\t")
def add_text (self, text):
if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
self._message.append('')
+ if not isinstance(text, unicode):
+ text = unicode(text, 'utf-8')
self._message[-1] += text.replace('&', '&').replace('<', '<').replace('>', '>')
if re.sub('\s+', ' ', text).strip() != '':
self._empty = False
def get_placeholder (self, name):
placeholder = 1
for holder in self._placeholders:
- holdername = '%s-%i' % (holder.name, placeholder)
- if holdername == name:
+ holdername = u'%s-%i' % (holder.name, placeholder)
+ if holdername == unicode(name, 'utf-8'):
return holder
placeholder += 1
if node.children is not None:
if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
self._message.append('')
- self._message[-1] += ('</%s>' % node.name)
+ self._message[-1] += (u'</%s>' % unicode(node.name, 'utf-8'))
def is_empty (self):
return self._empty
self._ctxt = ctxt
def add_source (self, source):
+ if not isinstance(source, unicode):
+ source = unicode(source, 'utf-8')
self._sources.append(source)
def get_sources (self):
return self._sources
def add_comment (self, comment):
- self._comments.append(comment)
+ if comment is not None:
+ self._comments.append(comment)
def get_comments (self):
return self._comments
def get_string (self):
- message = ''
+ message = u''
placeholder = 1
for msg in self._message:
if isinstance(msg, basestring):
message += msg
elif isinstance(msg, Message.Placeholder):
- message += '<_:%s-%i/>' % (msg.name, placeholder)
+ message += u'<_:%s-%i/>' % (msg.name, placeholder)
placeholder += 1
if not self._preserve:
message = re.sub('\s+', ' ', message).strip()
self._preserve = preserve
def format (self):
- ret = ''
+ ret = u''
for i in range(len(self._comments)):
if i != 0:
ret += '#.\n'
comment = self._comments[i]
- while len(comment) > 72:
- j = comment.rfind(' ', 0, 72)
- if j == -1:
- j = comment.find(' ')
- if j == -1:
- break
- ret += '#. %s\n' % comment[:j]
- comment = comment[j+1:]
- ret += '#. %s\n' % comment
+ if '\n' in comment:
+ doadd = False
+ for line in comment.split('\n'):
+ if line != '':
+ doadd = True
+ if not doadd:
+ continue
+ ret += u'#. %s\n' % line
+ else:
+ while len(comment) > 72:
+ j = comment.rfind(' ', 0, 72)
+ if j == -1:
+ j = comment.find(' ')
+ if j == -1:
+ break
+ ret += u'#. %s\n' % comment[:j]
+ comment = comment[j+1:]
+ ret += '#. %s\n' % comment
for source in self._sources:
- ret += '#: %s\n' % source
+ ret += u'#: %s\n' % source
if self._preserve:
- ret += '#, no-wrap\n'
+ ret += u'#, no-wrap\n'
if self._ctxt is not None:
- ret += 'msgctxt "%s"\n' % self._ctxt
+ ret += u'msgctxt "%s"\n' % self._ctxt
message = self.get_string()
if self._preserve:
- ret += 'msgid ""\n'
+ ret += u'msgid ""\n'
lines = message.split('\n')
for line, no in zip(lines, range(len(lines))):
if no == len(lines) - 1:
- ret += '"%s"\n' % self.escape(line)
+ ret += u'"%s"\n' % self.escape(line)
else:
- ret += '"%s\\n"\n' % self.escape(line)
+ ret += u'"%s\\n"\n' % self.escape(line)
else:
- ret += 'msgid "%s"\n' % self.escape(message)
- ret += 'msgstr ""\n'
+ ret += u'msgid "%s"\n' % self.escape(message)
+ ret += u'msgstr ""\n'
return ret
yield child
child = child.next
+def xml_attr_iter (node):
+ attr = node.get_properties()
+ while attr is not None:
+ yield attr
+ attr = attr.next
+
def xml_is_ns_name (node, ns, name):
if node.type != 'element':
return False
hctxt.replaceEntities(1)
hctxt.parseDocument()
self._localrules.append(hctxt.doc().getRootElement())
- else:
- self._localrules.append(child)
+ self._localrules.append(child)
pre_process(child)
pre_process(self._doc)
self._msgs = messages
self._its_within_text_nodes = {}
self._its_loc_notes = {}
self._itst_preserve_space_nodes = {}
+ self._itst_drop_nodes = {}
+ self._itst_contexts = {}
self._its_lang = {}
self._itst_lang_attr = {}
self._itst_credits = None
return
if xml_is_ns_name(rule, NS_ITS, 'translateRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._its_translate_nodes[node] = rule.prop('translate')
elif xml_is_ns_name(rule, NS_ITS, 'withinTextRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._its_within_text_nodes[node] = rule.prop('withinText')
elif xml_is_ns_name(rule, NS_ITST, 'preserveSpaceRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._itst_preserve_space_nodes[node] = rule.prop('preserveSpace')
+ elif xml_is_ns_name(rule, NS_ITST, 'dropRule'):
+ if rule.prop('selector') is not None:
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
+ self._itst_drop_nodes[node] = rule.prop('drop')
+ elif xml_is_ns_name(rule, NS_ITST, 'contextRule'):
+ if rule.prop('selector') is not None:
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
+ if rule.hasProp('context'):
+ self._itst_contexts[node] = rule.prop('context')
+ elif rule.hasProp('contextPointer'):
+ try:
+ oldnode = xpath.contextNode()
+ except:
+ oldnode = None
+ xpath.setContextNode(node)
+ ctxt = self._try_xpath_eval(xpath, rule.prop('contextPointer'))
+ if isinstance(ctxt, basestring):
+ self._itst_contexts[node] = ctxt
+ else:
+ for ctxt in ctxt:
+ self._itst_contexts[node] = ctxt.content
+ break
+ xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITS, 'locNoteRule'):
locnote = None
for child in xml_child_iter(rule):
if rule.hasProp('locNoteRef'):
locnote = 'SEE: ' + re.sub('\s+', ' ', rule.prop('locNoteRef')).strip()
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
if locnote is not None:
self._its_loc_notes[node] = locnote
else:
except:
oldnode = None
xpath.setContextNode(node)
- for note in xpath.xpathEval(sel):
- cont = re.sub('\s+', ' ', note.content).strip()
- if ref:
- cont = 'SEE: ' + cont
- self._its_loc_notes[node] = cont
- break
+ note = self._try_xpath_eval(xpath, sel)
+ if isinstance(note, basestring):
+ self._its_loc_notes[node] = note
+ else:
+ for note in note:
+ if self.get_preserve_space(note):
+ cont = note.content
+ else:
+ cont = re.sub('\s+', ' ', note.content).strip()
+ if ref:
+ cont = 'SEE: ' + cont
+ self._its_loc_notes[node] = cont
+ break
xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITS, 'langRule'):
if rule.prop('selector') is not None and rule.prop('langPointer') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
try:
oldnode = xpath.contextNode()
except:
oldnode = None
xpath.setContextNode(node)
- res = xpath.xpathEval(rule.prop('langPointer'))
+ res = self._try_xpath_eval(xpath, rule.prop('langPointer'))
if len(res) > 0:
self._its_lang[node] = res[0].content
# We need to construct language attributes, not just read
xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITST, 'credits'):
if rule.prop('appendTo') is not None:
- for node in xpath.xpathEval(rule.prop('appendTo')):
+ for node in self._try_xpath_eval(xpath, rule.prop('appendTo')):
self._itst_credits = (node, rule)
break
elif xml_is_ns_name(rule, NS_ITST, 'externalRefRule'):
if rule.prop('selector') is not None and rule.prop('refPointer') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
try:
oldnode = xpath.contextNode()
except:
oldnode = None
xpath.setContextNode(node)
- res = xpath.xpathEval(rule.prop('refPointer'))
+ res = self._try_xpath_eval(xpath, rule.prop('refPointer'))
if len(res) > 0:
self._itst_externals.append((node, res[0].content))
xpath.setContextNode(oldnode)
dirs.append(ddir)
ddir = os.getenv('XDG_DATA_DIRS', '')
if ddir == '':
- ddir = '/usr/local/share:/usr/share'
+ if DATADIR not in ('/usr/local/share', '/usr/share'):
+ ddir += DATADIR + ':'
+ ddir += '/usr/local/share:/usr/share'
dirs.extend(ddir.split(':'))
ddone = {}
for ddir in dirs:
nsdef = nsdef.next
par = par.parent
if match.hasProp('selector'):
- if len(xpath.xpathEval(match.prop('selector'))) > 0:
+ if len(self._try_xpath_eval(xpath, match.prop('selector'))) > 0:
matched = True
break
if matched == False:
xpath = self._doc.xpathNewContext()
reg_ns(xpath, rules)
for rule in xml_child_iter(rules):
+ if rule.type != 'element':
+ continue
if rule.nsDefs() is not None:
rule_xpath = self._doc.xpathNewContent()
reg_ns(rule_xpath, rule)
node = self._doc.getRootElement()
if node is None or node.type != 'element':
return
+ if ((node.hasNsProp('drop', NS_ITST) and node.nsProp('drop', NS_ITST) == 'yes') or
+ self._itst_drop_nodes.get(node, 'no') == 'yes'):
+ prev = node.prev
+ node.unlinkNode()
+ node.freeNode()
+ if prev.isBlankNode():
+ prev.unlinkNode()
+ prev.freeNode()
+ return
if is_root:
self.merge_credits(translations, language, node)
msg = self._msgs.get_message_by_node(node)
if msg is None:
+ self.translate_attrs(node, node)
children = [child for child in xml_child_iter(node)]
for child in children:
self.merge_translations(translations, language, node=child)
else:
newnode = self.get_translated(node, translations)
if newnode != node:
+ self.translate_attrs(node, newnode)
node.replaceNode(newnode)
if is_root:
# Apply language attributes to untranslated nodes. We don't do
fix_node_ns(child, childnsdefs)
fix_node_ns(node, {})
+ def translate_attrs(self, oldnode, newnode):
+ trans_attrs = [attr for attr in xml_attr_iter(oldnode) if self._its_translate_nodes.get(attr, 'no') == 'yes']
+ for attr in trans_attrs:
+ newcontent = translations.ugettext(attr.get_content())
+ if newcontent:
+ newnode.setProp(attr.name, translations.ugettext(attr.get_content()))
+
def get_translated (self, node, translations):
msg = self._msgs.get_message_by_node(node)
if msg is None:
return node
- trans = translations.ugettext(msg.get_string())
+ msgstr = msg.get_string()
+ # Dear Python, please implement pgettext.
+ # http://bugs.python.org/issue2504
+ # Sincerely, Shaun
+ if msg.get_context() is not None:
+ msgstr = msg.get_context() + '\x04' + msgstr
+ trans = translations.ugettext(msgstr)
if trans is None:
return node
nss = {}
ctxt.parseDocument()
trnode = ctxt.doc().getRootElement()
def scan_node(node):
- for child in xml_child_iter(node):
+ children = [child for child in xml_child_iter(node)]
+ for child in children:
if child.type != 'element':
continue
if child.ns() is not None and child.ns().content == NS_BLANK:
return
if node.type != 'element':
return
+ if node.hasNsProp('drop', NS_ITST) and node.nsProp('drop', NS_ITST) == 'yes':
+ return
+ if self._itst_drop_nodes.get(node, 'no') == 'yes':
+ return
translate = self.get_its_translate(node)
if translate is None:
if self._in_translatable:
translate = 'yes'
else:
translate = 'no'
+ withinText = False
if translate == 'no':
if msg is not None:
msg.add_placeholder(node)
if msg is not None:
msg.add_placeholder(node)
msg = Message()
+ ctxt = None
+ if node.hasNsProp('context', NS_ITST):
+ ctxt = node.nsProp('context', NS_ITST)
+ if ctxt is None:
+ ctxt = self._itst_contexts.get(node)
+ if ctxt is not None:
+ msg.set_context(ctxt)
if self.get_preserve_space(node):
msg.set_preserve_space()
msg.add_source('%s:%i(%s/%s)' % (self._doc.name, node.lineNo(), node.parent.name, node.name))
else:
+ withinText = True
msg.add_start_tag(node)
+ if not withinText:
+ # Add msg for translatable node attributes
+ for attr in xml_attr_iter(node):
+ if self._its_translate_nodes.get(attr, 'no') == 'yes':
+ attr_msg = Message()
+ attr_msg.add_source('%s:%i(%s/%s@%s)' % (
+ self._doc.name, node.lineNo(), node.parent.name, node.name, attr.name))
+ attr_msg.add_text(attr.content)
+ if comments:
+ attr_msg.add_comment(self.get_its_loc_note(attr))
+ self._msgs.add_message(attr_msg, attr)
+
+
if comments and msg is not None:
- comment = self.get_its_loc_note(node)
- if comment is not None:
- msg.add_comment(comment)
+ msg.add_comment(self.get_its_loc_note(node))
in_translatable = self._in_translatable
self._in_translatable = (translate == 'yes')
return 'SEE: ' + re.sub('\s+', ' ', node.prop('locNoteRef')).strip()
return self._its_loc_notes.get(node, None)
+ @staticmethod
+ def _try_xpath_eval (xpath, expr):
+ try:
+ return xpath.xpathEval(expr)
+ except:
+ sys.stderr.write('Warning: Invalid XPath: %s\n' % expr)
+ return []
+
_locale_pattern = re.compile('([a-zA-Z0-9-]+)(_[A-Za-z0-9]+)?(@[A-Za-z0-9]+)?(\.[A-Za-z0-9]+)?')
def convert_locale (locale):
(opts, args) = options.parse_args(sys.argv)
if opts.version:
- print 'itstool ' + VERSION
+ print('itstool %s' % VERSION)
sys.exit(0)
if opts.merge is None:
--- /dev/null
+.TH ITSTOOL "1" "May 2011" "itstool 1.1.0"
+.SH NAME
+itstool \- convert between XML and PO using ITS
+
+.SH SYNOPSIS
+itstool [OPTIONS] [XMLFILES]
+.br
+itstool \fB\-m\fR <MOFILE> [OPTIONS] [XMLFILES]
+
+.SH DESCRIPTION
+.BR itstool
+extracts messages from XML files and outputs PO template files, then merges
+translations from MO files to create translated XML files. It determines what
+to translate and how to chunk it into messages using the W3C Internationalization
+Tag Set (ITS).
+
+To extract messages from XML files \fBFILES\fR and output them to \fBOUT.pot\fR:
+
+\fBitstool -o OUT.pot FILES\fR
+
+After merging with existing translations or translating strings, generate an
+MO file with \fBmsgfmt(1)\fR, then output translated files to the directory
+\fBDIR\fR:
+
+\fBitstool -m OUT.mo -o DIR FILES\fR
+
+ITS definitions are loaded from the built-in rules, rules embedded in the source
+XML files, files passed with the \fB-i\fR option, and ITS attributes in the source
+XML files. Later definitions take precedence.
+
+.SH OPTIONS
+.SS "Extracting"
+.IP "\fB\-o \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-out=\fIFILE \fR" 4
+.PD
+output PO template to the file
+.BR OUT
+.SS "Merging"
+.IP "\fB\-m \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-merge=\fIFILE \fR" 4
+.PD
+merge from an MO file
+.BR FILE
+and output XML files
+.TP
+.IP "\fB\-l \fILANG\fR" 4
+.PD 0
+.IP "\fB\-\-lang=\fILANG \fR" 4
+.PD
+explicitly set the language code output to XML
+.TP
+.IP "\fB\-o \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-out=\fIFILE \fR" 4
+.PD
+output XML files in the directory
+.BR OUT
+.SS "Common"
+.IP "\fB\-i \fIITS\fR" 4
+.PD 0
+.IP "\fB\-\-its=\fIITS \fR" 4
+.PD
+load the ITS rules in the file ITS (can specify
+multiple times)
+
+.SH AUTHOR
+Shaun McCance <shaunm@gnome.org>
+
+.SH "SEE ALSO"
+More documentation for
+.B itstool
+is maintained online. For more information, see:
+.IP
+.B http://itstool.org/documentation/
+.PP
--- /dev/null
+.TH ITSTOOL "1" "May 2011" "itstool @VERSION@"
+.SH NAME
+itstool \- convert between XML and PO using ITS
+
+.SH SYNOPSIS
+itstool [OPTIONS] [XMLFILES]
+.br
+itstool \fB\-m\fR <MOFILE> [OPTIONS] [XMLFILES]
+
+.SH DESCRIPTION
+.BR itstool
+extracts messages from XML files and outputs PO template files, then merges
+translations from MO files to create translated XML files. It determines what
+to translate and how to chunk it into messages using the W3C Internationalization
+Tag Set (ITS).
+
+To extract messages from XML files \fBFILES\fR and output them to \fBOUT.pot\fR:
+
+\fBitstool -o OUT.pot FILES\fR
+
+After merging with existing translations or translating strings, generate an
+MO file with \fBmsgfmt(1)\fR, then output translated files to the directory
+\fBDIR\fR:
+
+\fBitstool -m OUT.mo -o DIR FILES\fR
+
+ITS definitions are loaded from the built-in rules, rules embedded in the source
+XML files, files passed with the \fB-i\fR option, and ITS attributes in the source
+XML files. Later definitions take precedence.
+
+.SH OPTIONS
+.SS "Extracting"
+.IP "\fB\-o \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-out=\fIFILE \fR" 4
+.PD
+output PO template to the file
+.BR OUT
+.SS "Merging"
+.IP "\fB\-m \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-merge=\fIFILE \fR" 4
+.PD
+merge from an MO file
+.BR FILE
+and output XML files
+.TP
+.IP "\fB\-l \fILANG\fR" 4
+.PD 0
+.IP "\fB\-\-lang=\fILANG \fR" 4
+.PD
+explicitly set the language code output to XML
+.TP
+.IP "\fB\-o \fIFILE\fR" 4
+.PD 0
+.IP "\fB\-\-out=\fIFILE \fR" 4
+.PD
+output XML files in the directory
+.BR OUT
+.SS "Common"
+.IP "\fB\-i \fIITS\fR" 4
+.PD 0
+.IP "\fB\-\-its=\fIITS \fR" 4
+.PD
+load the ITS rules in the file ITS (can specify
+multiple times)
+
+.SH AUTHOR
+Shaun McCance <shaunm@gnome.org>
+
+.SH "SEE ALSO"
+More documentation for
+.B itstool
+is maintained online. For more information, see:
+.IP
+.B http://itstool.org/documentation/
+.PP
-#!/usr/bin/env python
+#!/usr/bin/python -s
+#
+# Copyright (c) 2010-2011 Shaun McCance <shaunm@gnome.org>
+#
+# ITS Tool program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by the
+# Free Software Foundation, either version 3 of the License, or (at your
+# option) any later version.
+#
+# ITS Tool is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with ITS Tool; if not, write to the Free Software Foundation, 59 Temple
+# Place, Suite 330, Boston, MA 0211-1307 USA.
+#
VERSION="@VERSION@"
+DATADIR="@DATADIR@"
import gettext
import hashlib
out.write('"Content-Transfer-Encoding: 8bit\\n"\n')
out.write('\n')
for msg in msgs:
- out.write(msg.format())
+ out.write(msg.format().encode('utf-8'))
out.write('\n')
self._comments = []
self._preserve = False
+ def __repr__(self):
+ if self._empty:
+ return "Empty message"
+ return self.get_string()
+
class Placeholder (object):
def __init__ (self, node):
self.node = node
- self.name = node.name
+ self.name = unicode(node.name, 'utf-8')
def escape (self, text):
return text.replace('\\','\\\\').replace('"', "\\\"").replace("\n","\\n").replace("\t","\\t")
def add_text (self, text):
if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
self._message.append('')
+ if not isinstance(text, unicode):
+ text = unicode(text, 'utf-8')
self._message[-1] += text.replace('&', '&').replace('<', '<').replace('>', '>')
if re.sub('\s+', ' ', text).strip() != '':
self._empty = False
def get_placeholder (self, name):
placeholder = 1
for holder in self._placeholders:
- holdername = '%s-%i' % (holder.name, placeholder)
- if holdername == name:
+ holdername = u'%s-%i' % (holder.name, placeholder)
+ if holdername == unicode(name, 'utf-8'):
return holder
placeholder += 1
if node.children is not None:
if len(self._message) == 0 or not(isinstance(self._message[-1], basestring)):
self._message.append('')
- self._message[-1] += ('</%s>' % node.name)
+ self._message[-1] += (u'</%s>' % unicode(node.name, 'utf-8'))
def is_empty (self):
return self._empty
self._ctxt = ctxt
def add_source (self, source):
+ if not isinstance(source, unicode):
+ source = unicode(source, 'utf-8')
self._sources.append(source)
def get_sources (self):
return self._sources
def add_comment (self, comment):
- self._comments.append(comment)
+ if comment is not None:
+ self._comments.append(comment)
def get_comments (self):
return self._comments
def get_string (self):
- message = ''
+ message = u''
placeholder = 1
for msg in self._message:
if isinstance(msg, basestring):
message += msg
elif isinstance(msg, Message.Placeholder):
- message += '<_:%s-%i/>' % (msg.name, placeholder)
+ message += u'<_:%s-%i/>' % (msg.name, placeholder)
placeholder += 1
if not self._preserve:
message = re.sub('\s+', ' ', message).strip()
self._preserve = preserve
def format (self):
- ret = ''
+ ret = u''
for i in range(len(self._comments)):
if i != 0:
ret += '#.\n'
comment = self._comments[i]
- while len(comment) > 72:
- j = comment.rfind(' ', 0, 72)
- if j == -1:
- j = comment.find(' ')
- if j == -1:
- break
- ret += '#. %s\n' % comment[:j]
- comment = comment[j+1:]
- ret += '#. %s\n' % comment
+ if '\n' in comment:
+ doadd = False
+ for line in comment.split('\n'):
+ if line != '':
+ doadd = True
+ if not doadd:
+ continue
+ ret += u'#. %s\n' % line
+ else:
+ while len(comment) > 72:
+ j = comment.rfind(' ', 0, 72)
+ if j == -1:
+ j = comment.find(' ')
+ if j == -1:
+ break
+ ret += u'#. %s\n' % comment[:j]
+ comment = comment[j+1:]
+ ret += '#. %s\n' % comment
for source in self._sources:
- ret += '#: %s\n' % source
+ ret += u'#: %s\n' % source
if self._preserve:
- ret += '#, no-wrap\n'
+ ret += u'#, no-wrap\n'
if self._ctxt is not None:
- ret += 'msgctxt "%s"\n' % self._ctxt
+ ret += u'msgctxt "%s"\n' % self._ctxt
message = self.get_string()
if self._preserve:
- ret += 'msgid ""\n'
+ ret += u'msgid ""\n'
lines = message.split('\n')
for line, no in zip(lines, range(len(lines))):
if no == len(lines) - 1:
- ret += '"%s"\n' % self.escape(line)
+ ret += u'"%s"\n' % self.escape(line)
else:
- ret += '"%s\\n"\n' % self.escape(line)
+ ret += u'"%s\\n"\n' % self.escape(line)
else:
- ret += 'msgid "%s"\n' % self.escape(message)
- ret += 'msgstr ""\n'
+ ret += u'msgid "%s"\n' % self.escape(message)
+ ret += u'msgstr ""\n'
return ret
yield child
child = child.next
+def xml_attr_iter (node):
+ attr = node.get_properties()
+ while attr is not None:
+ yield attr
+ attr = attr.next
+
def xml_is_ns_name (node, ns, name):
if node.type != 'element':
return False
hctxt.replaceEntities(1)
hctxt.parseDocument()
self._localrules.append(hctxt.doc().getRootElement())
- else:
- self._localrules.append(child)
+ self._localrules.append(child)
pre_process(child)
pre_process(self._doc)
self._msgs = messages
self._its_within_text_nodes = {}
self._its_loc_notes = {}
self._itst_preserve_space_nodes = {}
+ self._itst_drop_nodes = {}
+ self._itst_contexts = {}
self._its_lang = {}
self._itst_lang_attr = {}
self._itst_credits = None
return
if xml_is_ns_name(rule, NS_ITS, 'translateRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._its_translate_nodes[node] = rule.prop('translate')
elif xml_is_ns_name(rule, NS_ITS, 'withinTextRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._its_within_text_nodes[node] = rule.prop('withinText')
elif xml_is_ns_name(rule, NS_ITST, 'preserveSpaceRule'):
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
self._itst_preserve_space_nodes[node] = rule.prop('preserveSpace')
+ elif xml_is_ns_name(rule, NS_ITST, 'dropRule'):
+ if rule.prop('selector') is not None:
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
+ self._itst_drop_nodes[node] = rule.prop('drop')
+ elif xml_is_ns_name(rule, NS_ITST, 'contextRule'):
+ if rule.prop('selector') is not None:
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
+ if rule.hasProp('context'):
+ self._itst_contexts[node] = rule.prop('context')
+ elif rule.hasProp('contextPointer'):
+ try:
+ oldnode = xpath.contextNode()
+ except:
+ oldnode = None
+ xpath.setContextNode(node)
+ ctxt = self._try_xpath_eval(xpath, rule.prop('contextPointer'))
+ if isinstance(ctxt, basestring):
+ self._itst_contexts[node] = ctxt
+ else:
+ for ctxt in ctxt:
+ self._itst_contexts[node] = ctxt.content
+ break
+ xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITS, 'locNoteRule'):
locnote = None
for child in xml_child_iter(rule):
if rule.hasProp('locNoteRef'):
locnote = 'SEE: ' + re.sub('\s+', ' ', rule.prop('locNoteRef')).strip()
if rule.prop('selector') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
if locnote is not None:
self._its_loc_notes[node] = locnote
else:
except:
oldnode = None
xpath.setContextNode(node)
- for note in xpath.xpathEval(sel):
- cont = re.sub('\s+', ' ', note.content).strip()
- if ref:
- cont = 'SEE: ' + cont
- self._its_loc_notes[node] = cont
- break
+ note = self._try_xpath_eval(xpath, sel)
+ if isinstance(note, basestring):
+ self._its_loc_notes[node] = note
+ else:
+ for note in note:
+ if self.get_preserve_space(note):
+ cont = note.content
+ else:
+ cont = re.sub('\s+', ' ', note.content).strip()
+ if ref:
+ cont = 'SEE: ' + cont
+ self._its_loc_notes[node] = cont
+ break
xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITS, 'langRule'):
if rule.prop('selector') is not None and rule.prop('langPointer') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
try:
oldnode = xpath.contextNode()
except:
oldnode = None
xpath.setContextNode(node)
- res = xpath.xpathEval(rule.prop('langPointer'))
+ res = self._try_xpath_eval(xpath, rule.prop('langPointer'))
if len(res) > 0:
self._its_lang[node] = res[0].content
# We need to construct language attributes, not just read
xpath.setContextNode(oldnode)
elif xml_is_ns_name(rule, NS_ITST, 'credits'):
if rule.prop('appendTo') is not None:
- for node in xpath.xpathEval(rule.prop('appendTo')):
+ for node in self._try_xpath_eval(xpath, rule.prop('appendTo')):
self._itst_credits = (node, rule)
break
elif xml_is_ns_name(rule, NS_ITST, 'externalRefRule'):
if rule.prop('selector') is not None and rule.prop('refPointer') is not None:
- for node in xpath.xpathEval(rule.prop('selector')):
+ for node in self._try_xpath_eval(xpath, rule.prop('selector')):
try:
oldnode = xpath.contextNode()
except:
oldnode = None
xpath.setContextNode(node)
- res = xpath.xpathEval(rule.prop('refPointer'))
+ res = self._try_xpath_eval(xpath, rule.prop('refPointer'))
if len(res) > 0:
self._itst_externals.append((node, res[0].content))
xpath.setContextNode(oldnode)
dirs.append(ddir)
ddir = os.getenv('XDG_DATA_DIRS', '')
if ddir == '':
- ddir = '/usr/local/share:/usr/share'
+ if DATADIR not in ('/usr/local/share', '/usr/share'):
+ ddir += DATADIR + ':'
+ ddir += '/usr/local/share:/usr/share'
dirs.extend(ddir.split(':'))
ddone = {}
for ddir in dirs:
nsdef = nsdef.next
par = par.parent
if match.hasProp('selector'):
- if len(xpath.xpathEval(match.prop('selector'))) > 0:
+ if len(self._try_xpath_eval(xpath, match.prop('selector'))) > 0:
matched = True
break
if matched == False:
xpath = self._doc.xpathNewContext()
reg_ns(xpath, rules)
for rule in xml_child_iter(rules):
+ if rule.type != 'element':
+ continue
if rule.nsDefs() is not None:
rule_xpath = self._doc.xpathNewContent()
reg_ns(rule_xpath, rule)
node = self._doc.getRootElement()
if node is None or node.type != 'element':
return
+ if ((node.hasNsProp('drop', NS_ITST) and node.nsProp('drop', NS_ITST) == 'yes') or
+ self._itst_drop_nodes.get(node, 'no') == 'yes'):
+ prev = node.prev
+ node.unlinkNode()
+ node.freeNode()
+ if prev.isBlankNode():
+ prev.unlinkNode()
+ prev.freeNode()
+ return
if is_root:
self.merge_credits(translations, language, node)
msg = self._msgs.get_message_by_node(node)
if msg is None:
+ self.translate_attrs(node, node)
children = [child for child in xml_child_iter(node)]
for child in children:
self.merge_translations(translations, language, node=child)
else:
newnode = self.get_translated(node, translations)
if newnode != node:
+ self.translate_attrs(node, newnode)
node.replaceNode(newnode)
if is_root:
# Apply language attributes to untranslated nodes. We don't do
fix_node_ns(child, childnsdefs)
fix_node_ns(node, {})
+ def translate_attrs(self, oldnode, newnode):
+ trans_attrs = [attr for attr in xml_attr_iter(oldnode) if self._its_translate_nodes.get(attr, 'no') == 'yes']
+ for attr in trans_attrs:
+ newcontent = translations.ugettext(attr.get_content())
+ if newcontent:
+ newnode.setProp(attr.name, translations.ugettext(attr.get_content()))
+
def get_translated (self, node, translations):
msg = self._msgs.get_message_by_node(node)
if msg is None:
return node
- trans = translations.ugettext(msg.get_string())
+ msgstr = msg.get_string()
+ # Dear Python, please implement pgettext.
+ # http://bugs.python.org/issue2504
+ # Sincerely, Shaun
+ if msg.get_context() is not None:
+ msgstr = msg.get_context() + '\x04' + msgstr
+ trans = translations.ugettext(msgstr)
if trans is None:
return node
nss = {}
ctxt.parseDocument()
trnode = ctxt.doc().getRootElement()
def scan_node(node):
- for child in xml_child_iter(node):
+ children = [child for child in xml_child_iter(node)]
+ for child in children:
if child.type != 'element':
continue
if child.ns() is not None and child.ns().content == NS_BLANK:
return
if node.type != 'element':
return
+ if node.hasNsProp('drop', NS_ITST) and node.nsProp('drop', NS_ITST) == 'yes':
+ return
+ if self._itst_drop_nodes.get(node, 'no') == 'yes':
+ return
translate = self.get_its_translate(node)
if translate is None:
if self._in_translatable:
translate = 'yes'
else:
translate = 'no'
+ withinText = False
if translate == 'no':
if msg is not None:
msg.add_placeholder(node)
if msg is not None:
msg.add_placeholder(node)
msg = Message()
+ ctxt = None
+ if node.hasNsProp('context', NS_ITST):
+ ctxt = node.nsProp('context', NS_ITST)
+ if ctxt is None:
+ ctxt = self._itst_contexts.get(node)
+ if ctxt is not None:
+ msg.set_context(ctxt)
if self.get_preserve_space(node):
msg.set_preserve_space()
msg.add_source('%s:%i(%s/%s)' % (self._doc.name, node.lineNo(), node.parent.name, node.name))
else:
+ withinText = True
msg.add_start_tag(node)
+ if not withinText:
+ # Add msg for translatable node attributes
+ for attr in xml_attr_iter(node):
+ if self._its_translate_nodes.get(attr, 'no') == 'yes':
+ attr_msg = Message()
+ attr_msg.add_source('%s:%i(%s/%s@%s)' % (
+ self._doc.name, node.lineNo(), node.parent.name, node.name, attr.name))
+ attr_msg.add_text(attr.content)
+ if comments:
+ attr_msg.add_comment(self.get_its_loc_note(attr))
+ self._msgs.add_message(attr_msg, attr)
+
+
if comments and msg is not None:
- comment = self.get_its_loc_note(node)
- if comment is not None:
- msg.add_comment(comment)
+ msg.add_comment(self.get_its_loc_note(node))
in_translatable = self._in_translatable
self._in_translatable = (translate == 'yes')
return 'SEE: ' + re.sub('\s+', ' ', node.prop('locNoteRef')).strip()
return self._its_loc_notes.get(node, None)
+ @staticmethod
+ def _try_xpath_eval (xpath, expr):
+ try:
+ return xpath.xpathEval(expr)
+ except:
+ sys.stderr.write('Warning: Invalid XPath: %s\n' % expr)
+ return []
+
_locale_pattern = re.compile('([a-zA-Z0-9-]+)(_[A-Za-z0-9]+)?(@[A-Za-z0-9]+)?(\.[A-Za-z0-9]+)?')
def convert_locale (locale):
(opts, args) = options.parse_args(sys.argv)
if opts.version:
- print 'itstool ' + VERSION
+ print('itstool %s' % VERSION)
sys.exit(0)
if opts.merge is None: