diff options
author | Ulrich Drepper <drepper@cygnus.com> | 2000-06-16 07:49:23 +0000 |
---|---|---|
committer | Ulrich Drepper <drepper@cygnus.com> | 2000-06-16 07:49:23 +0000 |
commit | 60d2084de5dc3b65e6657f695f0f26df24b0f565 (patch) | |
tree | 224f21c30351570d6d7f06a385e829d5ab320f67 /misc | |
download | external_gettext-60d2084de5dc3b65e6657f695f0f26df24b0f565.zip external_gettext-60d2084de5dc3b65e6657f695f0f26df24b0f565.tar.gz external_gettext-60d2084de5dc3b65e6657f695f0f26df24b0f565.tar.bz2 |
Initial revision
Diffstat (limited to 'misc')
-rw-r--r-- | misc/Admin/alias.X11 | 202 | ||||
-rw-r--r-- | misc/ChangeLog | 699 | ||||
-rw-r--r-- | misc/Makefile.am | 31 | ||||
-rw-r--r-- | misc/Makefile.in | 307 | ||||
-rw-r--r-- | misc/README-Tcl | 52 | ||||
-rwxr-xr-x | misc/combine-sh | 141 | ||||
-rwxr-xr-x | misc/elisp-comp | 42 | ||||
-rw-r--r-- | misc/gettext-sh | 63 | ||||
-rw-r--r-- | misc/gettext.perl | 86 | ||||
-rw-r--r-- | misc/gettextize.in | 177 | ||||
-rw-r--r-- | misc/locale.alias | 52 | ||||
-rw-r--r-- | misc/magic.add | 9 | ||||
-rw-r--r-- | misc/po-mode.el | 2602 | ||||
-rw-r--r-- | misc/tcl_gettext.c | 165 |
14 files changed, 4628 insertions, 0 deletions
diff --git a/misc/Admin/alias.X11 b/misc/Admin/alias.X11 new file mode 100644 index 0000000..401d3bd --- /dev/null +++ b/misc/Admin/alias.X11 @@ -0,0 +1,202 @@ +# $XConsortium: locale.alias,v 1.9 94/08/23 18:12:18 kaleb Exp $ +# +# This file contains alias name of locale. +# Each alias name is described within one line. +# The first word is the alias name (simplified locale name) +# the second word is full locale name. +# +POSIX C +POSIX-UTF2 C +C.en C +Cextend en_US.ISO8859-1 +Cextend.en en_US.ISO8859-1 +English_United-States.437 C +# +ar ar_AA.ISO8859-6 +ar_AA ar_AA.ISO8859-6 +bg bg_BG.ISO8859-5 +bg_BG bg_BG.ISO8859-5 +cs cs_CS.ISO8859-2 +cs_CS cs_CS.ISO8859-2 +cz cz_CZ.ISO8859-2 +cz_CZ cz_CZ.ISO8859-2 +da da_DK.ISO8859-1 +da_DK da_DK.ISO8859-1 +da_DK.88591 da_DK.ISO8859-1 +da_DK.88591.en da_DK.ISO8859-1 +de de_DE.ISO8859-1 +de_DE de_DE.ISO8859-1 +De_DE de_DE.ISO8859-1 +De_DE.IBM-850 de_DE.ISO8859-1 +de_DE.88591 de_DE.ISO8859-1 +de_DE.88591.en de_DE.ISO8859-1 +GER_DE.8859 de_DE.ISO8859-1 +GER_DE.8859.in de_DE.ISO8859-1 +de_AT de_AT.ISO8859-1 +de_CH de_CH.ISO8859-1 +Du_BE du_BE.ISO8859-1 +Du_NL du_NL.ISO8859-1 +el el_GR.ISO8859-7 +el_GR el_GR.ISO8859-7 +en en_US.ISO8859-1 +en_GB en_GB.ISO8859-1 +en_GB.88591 en_GB.ISO8859-1 +en_GB.88591.en en_GB.ISO8859-1 +ENG_GB.8859 en_GB.ISO8859-1 +ENG_GB.8859.in en_GB.ISO8859-1 +en_AU en_AU.ISO8859-1 +en_CA en_CA.ISO8859-1 +en_US en_US.ISO8859-1 +En_US en_US.ISO8859-1 +en_US.88591 en_US.ISO8859-1 +en_US.88591.en en_US.ISO8859-1 +en_US.437 en_US.ISO8859-1 +en_US.646 en_US.ISO8859-1 +en_US.850 en_US.ISO8859-1 +es es_ES.ISO8859-1 +es_ES es_ES.ISO8859-1 +Es_ES es_ES.ISO8859-1 +Sp_SP es_ES.ISO8859-1 +Es_ES.IBM-850 es_ES.ISO8859-1 +es_ES.88591 es_ES.ISO8859-1 +es_ES.88591.en es_ES.ISO8859-1 +fi fi_FI.ISO8859-1 +fi_FI fi_FI.ISO8859-1 +fi_FI.88591 fi_FI.ISO8859-1 +fi_FI.88591.en fi_FI.ISO8859-1 +fr fr_FR.ISO8859-1 +fr_BE fr_BE.ISO8859-1 +fr_BE.88591 fr_BE.ISO8859-1 +fr_BE.88591.en fr_BE.ISO8859-1 +fr_CA fr_CA.ISO8859-1 +Fr_CA fr_CA.ISO8859-1 +Fr_CF fr_CF.ISO8859-1 +Fr_CA.IBM-850 fr_CA.ISO8859-1 +fr_CA.88591 fr_CA.ISO8859-1 +fr_CA.88591.en fr_CA.ISO8859-1 +fr_CH fr_CH.ISO8859-1 +fr_CH.88591 fr_CH.ISO8859-1 +fr_CH.88591.en fr_CH.ISO8859-1 +fr_FR fr_FR.ISO8859-1 +fr_FR.88591 fr_FR.ISO8859-1 +fr_FR.88591.en fr_FR.ISO8859-1 +FRE_FR.8859 fr_FR.ISO8859-1 +FRE_FR.8859.in fr_FR.ISO8859-1 +Gr_GR el_GR.ISO8859-7 +hr hr_HR.ISO8859-2 +hr_HR hr_HR.ISO8859-2 +hu hu_HU.ISO8859-2 +hu_HU hu_HU.ISO8859-2 +is is_IS.ISO8859-1 +is_IS is_IS.ISO8859-1 +it it_IT.ISO8859-1 +it_IT it_IT.ISO8859-1 +it_IT.88591 it_IT.ISO8859-1 +it_IT.88591.en it_IT.ISO8859-1 +it_CH it_CH.ISO8859-1 +iw iw_IL.ISO8859-8 +iw_IL iw_IL.ISO8859-8 +ja ja_JP.eucJP +ja_JP ja_JP.eucJP +ja_JP.ujis ja_JP.eucJP +ja_JP.eucJP ja_JP.eucJP +Ja_JP ja_JP.eucJP +Jp_JP ja_JP.eucJP +Jp_JP.IBM-932 ja_JP.eucJP +ja_JP.AJEC ja_JP.eucJP +ja_JP.EUC ja_JP.eucJP +ja_JP.ISO-2022-JP ja_JP.JIS7 +ja_JP.JIS ja_JP.JIS7 +ja_JP.jis7 ja_JP.JIS7 +ja_JP.mscode ja_JP.SJIS +ja_JP.SJIS ja_JP.SJIS +ko ko_KR.eucKR +ko_KR ko_KR.eucKR +ko_KR.EUC ko_KR.eucKR +ko_KR.euc ko_KR.eucKR +mk mk_MK.ISO8859-5 +mk_MK mk_MK.ISO8859-5 +nl nl_NL.ISO8859-1 +nl_BE nl_BE.ISO8859-1 +Nl_BE nl_BE.ISO8859-1 +Nl_BE.IBM-850 nl_BE.ISO8859-1 +nl_BE.88591 nl_BE.ISO8859-1 +nl_BE.88591.en nl_BE.ISO8859-1 +nl_NL nl_NL.ISO8859-1 +Nl_NL nl_NL.ISO8859-1 +Nl_NL.IBM-850 nl_NL.ISO8859-1 +nl_NL.88591 nl_NL.ISO8859-1 +nl_NL.88591.en nl_NL.ISO8859-1 +no no_NO.ISO8859-1 +no_NO no_NO.ISO8859-1 +no_NO.88591 no_NO.ISO8859-1 +no_NO.88591.en no_NO.ISO8859-1 +pl pl_PL.ISO8859-2 +pl_PL pl_PL.ISO8859-2 +pt pt_PT.ISO8859-1 +pt_PT pt_PT.ISO8859-1 +Pt_PT pt_PT.ISO8859-1 +Po_PO pt_PT.ISO8859-1 +Pt_PT.IBM-850 pt_PT.ISO8859-1 +pt_PT.88591 pt_PT.ISO8859-1 +pt_PT.88591.en pt_PT.ISO8859-1 +ro ro_RO.ISO8859-2 +ro_RO ro_RO.ISO8859-2 +ru ru_SU.ISO8859-5 +ru_SU ru_SU.ISO8859-5 +sh sh_YU.ISO8859-2 +sh_YU sh_YU.ISO8859-2 +sk sk_SK.ISO8859-2 +sk_SK sk_SK.ISO8859-2 +sl sl_CS.ISO8859-2 +sl_CS sl_CS.ISO8859-2 +sp sp_YU.ISO8859-5 +sp_YU sp_YU.ISO8859-5 +sv sv_SE.ISO8859-1 +sv_SE sv_SE.ISO8859-1 +Sv_SE sv_SE.ISO8859-1 +Sv_SV sv_SE.ISO8859-1 +Sv_SE.IBM-850 sv_SE.ISO8859-1 +sv_SE.88591 sv_SE.ISO8859-1 +sv_SE.88591.en sv_SE.ISO8859-1 +th_TH th_TH.TACTIS +tr tr_TR.ISO8859-9 +tr_TR tr_TR.ISO8859-9 +zh zh_CN.eucCN +zh_CN zh_CN.eucCN +zh_CN.EUC zh_CN.eucCN +zh_TW zh_TW.eucTW +zh_TW.EUC zh_TW.eucTW +arabic ar_AA.ISO8859-6 +bulgarian bg_BG.ISO8859-5 +chinese-s zh_CN.eucCN +chinese-t zh_TW.eucTW +croatian hr_HR.ISO8859-2 +czech cs_CS.ISO8859-2 +danish da_DK.ISO8859-1 +dutch nl_BE.ISO8859-1 +finnish fi_FI.ISO8859-1 +french fr_CH.ISO8859-1 +german de_CH.ISO8859-1 +greek el_GR.ISO8859-7 +hebrew iw_IL.ISO8859-8 +hungarian hu_HU.ISO8859-2 +icelandic is_IS.ISO8859-1 +iso_8859_1 en_US.ISO8859-1 +ISO8859-1 en_US.ISO8859-1 +italian it_CH.ISO8859-1 +japan ja_JP.eucJP +japanese ja_JP.eucJP +Japanese-EUC ja_JP.eucJP +korean ko_KR.eucKR +norwegian no_NO.ISO8859-1 +polish pl_PL.ISO8859-2 +portuguese pt_PT.ISO8859-1 +rumanian ro_RO.ISO8859-2 +russian ru_SU.ISO8859-5 +serbocroatian sh_YU.ISO8859-2 +slovak sk_SK.ISO8859-2 +slovene sl_CS.ISO8859-2 +spanish es_ES.ISO8859-1 +swedish sv_SE.ISO8859-1 +turkish tr_TR.ISO8859-9 diff --git a/misc/ChangeLog b/misc/ChangeLog new file mode 100644 index 0000000..649f1ff --- /dev/null +++ b/misc/ChangeLog @@ -0,0 +1,699 @@ +2000-05-06 Ulrich Drepper <drepper@redhat.com> + + * gettextize.in: Remove code to test for version of the m4 files. + + * po-mode.el: A few more changes by Fran^Áçois Pinard. + +1998-05-03 Fran^Áçois Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-create-overlay) [po-XEMACS]: No argument. + Reported by Ulrich Drepper. + +1997-10-18 Fran^Áçois Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-tags-search, po-mark-found-string): C++ as C. + (po-find-c-string): Skip C++ comments as well. + +1998-05-10 Fran^Áçois Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-mode) [po-XEMACS]: Explicitely add menus. + Reported by Hrvoje Niksic. + + * po-mode.el (po-mode-menu-layout): Give real access to auxiliary + files through the menu, instead of leaving menu entries inactive. + +1998-05-15 Ulrich Drepper <drepper@cygnus.com> + + * tcl_gettext.c (tcl_gettext): Replace illegal with invalid. + +1998-03-30 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-tags-search, po-mark-translatable, + po-select-mark-and-mark): Call interactive after the doc string, + instead of before. + Reported by Tom Tromey. + +1998-04-27 21:17 Ulrich Drepper <drepper@cygnus.com> + + * po-mode.el: Update from most recent version. + + * gettextize.in: Use aclocaldir from configuration. + +1997-09-04 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: Rename po-middle-of-entry to po-start-of-msgstr, add + variable po-start-of-msgid. Use it whenever appropriate. + + * po-mode.el (po-find-file-coding-system): New function. + Reported by Ken'ichi Handa. + + * po-mode.el: Normalise string= to string-equal. + + * po-mode.el (po-send-mail): Allow for mailing to the team. + (po-guess-team-address): New function. + +1997-09-02 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-set-comment): Unused variable buffer. + (po-edit-string): Unused variables start, middle, end and obsolete. + (po-tags-search): Unused variable find-string. + + * po-mode.el (po-check-lock): Create the work buffer as required, to + avoid diagnostics about selecting a deleted buffer, after a user + explicitly killed the work buffer instead of exiting it normally. + Reported by Hrvoje Niksic. + + * po-mode.el: New variable po-mode-menu-layout. + (po-mode): Establish a bar mode menu if possible. + Reported by Nils Naumann. + + * po-mode.el: Decide set-translation-domain before using it. + This avoids a byte-compilation warning. + + * po-mode.el (po-set-comment): Rearrange wrong conditional flow. + Translator comments were duplicated, when contents were unchanged. + Reported by Enrique Melero. + +1997-09-01 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-highlight, po-dehighlight): New functions. + New globals: po-highlight-p, po-highlight-face and po-overlay. + (po-edit-msgstr): Highlight the msgid string while editing. + (po-tags-search): Highlight found string. + (po-mark-found-string): Unhilight string before replacing it. + Reported by Jim Meyering, Michel Robitaille and Ulrich Drepper. + + * po-mode.el (po-set-field): Tells if buffer was modified. + (po-edit-msgstr): Never make fuzzy an entry which is unmodified. + + * po-mode.el: Add M command, and variables po-compose-mail-function, + po-translation-project-address and po-gzip-uuencode-command. + (po-guess-archive-name, po-send-mail): New functions. + Reported by Karl Eichwalder. + +1997-08-31 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el (po-replace-revision-date): Enforce ISO 8601 zones. + Reported by Enrique Melero Gómez, Karl Eichwalder, Max de + Mendizabal and Santiago Vila Doncel. + + * po-mode.el (po-edit-string): Expand tabs while editing + translations. Reported by Göran Uddeborg. + + * po-mode.el: Accept C-c C-k to abort recursive edits. + Reported by Göran Uddeborg and Hrvoje Niksic. + +1997-06-02 Ben Pfaff <pfaffben@pilot.msu.edu> + + * po-mode.el (po-find-c-string, po-extract-unquoted): Process ANSI + string concatenation and K&R escaped newlines. + +1997-03-02 Hrvoje Niksic <hniksic@srce.hr> + + * po-mode.el (po-help): To continue, also accept things like a + mouse press or an arrow key. + +1996-11-12 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: Alter po-font-lock-keywords to properly highlight C + formats, when using an upper case letter as formatting functor. + + * po-mode.el: If available, prefer force-mode-line-update builtin. + + * po-mode.el: Use our own buffer-substring, defining it as + buffer-substring-no-properties if available. Because of text + properties, buffer-substring does not always return a string. + + * po-mode.el (po-consider-source-path): Ensure a trailing slash. + +1996-05-13 Andreas Schwab <schwab@issan.informatik.uni-dortmund.de> + + * po-mode.el (po-eval-requoted, po-extract-unquoted): Correct + missing or spurious backslashes in some regexps. + +1997-08-01 15:49 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am (AUTOMAKE_OPTIONS): Require version 1.2. + +1997-04-12 Hrvoje Niksic <hniksic@srce.hr> + + * po-mode.el: Customize. + +1997-03-10 06:56 Ulrich Drepper <drepper@cygnus.com> + + * elisp-comp: Use EMACS environment variable is available instead + of always executing emacs. + +1996-12-03 23:24 Ulrich Drepper <drepper@cygnus.com> + + * gettextize.in: Update --help and --version texts. + +1996-11-22 04:45 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am (EXTRA_DIST): Add locale.alias. + +1996-11-21 23:11 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am: Remove rules for ELisp handling. Automake now + knows what to do. + +1996-10-28 23:09 Ulrich Drepper <drepper@cygnus.com> + + * gettextize.in: Remove -v from help message. Change format of + --version text according to last GNU coding standard. Don't print + help message for unknown option. Instead print "Try `..." + message. + +1996-10-19 17:41 1996 Ulrich Drepper <drepper@cygnus.com> + + * locale.alias: Language for czech entry must be cs. + +1996-09-18 00:29 François Pinard <pinard@progiciels-bpi.ca> + + * po-mode.el (po-font-lock-keywords): Correct highlighting of + formats like %3d. + +1996-08-19 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: Restructured to avoid all byte-compilation warnings. + Highlighting using `hilit19' is being deprecated, font lock code + should now automatically be activated whenever available. + +1996-07-15 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: po-mode.el (po-kill-msgstr): Ensure po-entry-type is + always defined before decreasing counter. + +1996-07-15 01:08 Ulrich Drepper <drepper@cygnus.com> + + * gettextize.in (gettext_dir): Remove warning that files will be + removed. It's not really necessary since the -f option is + necessary. Suggested by François Pinard. + +1996-06-26 18:40 Ulrich Drepper <drepper@cygnus.com> + + * po-mode.el: Added ELisp support. Patch by François Pinard. + +1996-06-18 15:12 Ulrich Drepper <drepper@cygnus.com> + + * gettextize.in (aclocal_version): new variable. Value determined + by configure. This finally makes the check of the aclocal.m4 + version number correct because this number need not be the same as + the version number of the package. + +1996-06-06 02:02 Ulrich Drepper <drepper@cygnus.com> + + * gettextize.in: Rewrite copying now that files are kept in + different directories and don't use funny prefixes anymore. + +1996-06-05 16:36 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am (DISTCLEANFILES): Renamed from CLEANFILES and + changed $(lisp_DATA) to $(ELCFILES). + +1996-06-03 00:46 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am (elc_DATA): Rename to elc_SCRIPTS so that + distribution wents smooth. + +1996-06-02 21:16 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.am (SUFFIXES): New variable. Automake wants it this + way to introduce new suffixes. + + * Makefile.am: Initial revision. + +1996-06-01 18:20 Ulrich Drepper <drepper@cygnus.com> + + * Makefile.in (default): Add default rule because AIX' make does + not understand multiple goals in default rule. + + * Makefile.in (DISTFILES): Rename README-TCL to README-Tcl. + + * gettextize.in: Implement test for correct aclocal.m4 version. + +1996-04-06 02:40 Ulrich Drepper <drepper@myware> + + * po-mode.el: Apply François' patch for new default values of + configuration variables and new header entry format. + +1996-04-02 18:56 Ulrich Drepper <drepper@myware> + + * Makefile.in (all-gettext): New goal. Same as all. + +1996-04-02 03:18 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: * Let po-show-source-context update + po-reference-cursor itself, and display to the minibuffer the + relative and maximum position of the shown reference in the list + of collected references. Callers adjusted accordingly. + . Do not generate tildes while reconstructing non-obsolete + comments. + . Do merge attributes on a single `#,' line. Using the non-regexp + version of the search was causing the generation of another one. + . Remove a spurious trailing comma while removing the last + attribute. + . Limit the search for source references to the msgstr line. This + does not correct any bug, but is more consistent. + +1996-03-31 23:32 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: * Small corrections to the help page. Reported by + Karl Eichwalder. + + * Change po-edit-mode-map to po-subedit-mode-map and + po-mode-edit-hook to po-subedit-mode-hook. The previous names + were not consistent. Reported by Karl Eichwalder. + + * Repair RET, completely broken for multi-lines, maybe showing a + regexp bug in Emacs(?). See the FIXME in po-extract-unquoted. + Reported by Karl Eichwalder. + +1996-03-31 22:36 Ulrich Drepper <drepper@myware> + + * gettextize.in (prefix): Define from @prefix@. Is used in + gettext_dir definition. Reported by Jim Meyering. + +1996-03-28 19:11 Karl Eichwalder <ke@ke.Central.DE> + + * gettextize.in (gettext_dir): Set to @datadir@/gettext. + +1996-03-26 21:27 Ulrich Drepper <drepper@myware> + + * Makefile.in (datadir): Initialize from @datadir@. Reported by + Karl Eichwalder. + +1996-03-25 09:52 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: * Correct documentation mistakes in short help. + + * Accept both # and #~ for obsolete comments, but use #~ when + generating them. + + * Recognize \NNN octal notation for characters in PO file strings, + however, never produce such horror. + +1996-03-25 03:51 Ulrich Drepper <drepper@myware> + + * po-mode.el: Some last minute changes by François Pinard. + +1996-03-24 18:51 Ulrich Drepper <drepper@myware> + + * po-mode.el (po-confirm-and-quit): Recognize both #, and #! but + prefer producing #,. + + * Rephrase messages about location stack contents + * Clear out message when user refuses quitting. + +1996-03-23 14:34 François Pinard <pinard@iro.umontreal.ca> + + * Implement po-default-file-header. + Reported by Karl Eichwalder. + + * Rename po-auto-select to po-auto-select-entry. + + * Revise text of all disruptive questions, try to alleviate + them, avoid them if possible. Clear the message area sometimes. + + * Correct a bug by which `#! fuzzy' was inserted before the + white line, instead of after, for PO files having no `#' line + of any kind. + + * Correct the message count updating in po-kill-msgstr and + po-yank-msgstr. + + * Allow for po-auto-edit-with-msgid to work, when + po-edit-msgstr was called on an untranslated immediately after + loading a PO file. + + * po-quit may select all kind of not fully processed entries, + rather than just untranslate ones. + + Rebound commands: + * TAB moves to LFD for po-msgid-to-msgstr. + * z moves to DEL for po-fade-out-entry. + + New commands: + * TAB is po-unfuzzy. + * SPC is po-auto-select. + * t is po-next-translated entry. + * M-t is po-previous-translated-entry. + * E is po-edit-out-full. + + Improved commands: + * DEL (po-fade-out-entry) makes the entry fuzzy as first step, + and request confirmation for some transitions. + * LFD (po-msgid-to-msgstr) requests confirmation if entry was + already translated. + * v (po-validate): Pass the -v flag to msgfmt. + * q (po-confirm-and-quit): Use milder confirmation. + * Implemented variables po-auto-edit-with-msgid, + po-auto-fuzzy-on-edit, po-auto-select-on-unfuzzy, and + po-auto-replace-revision-date. + + PO header management: + * PO-Revision-Date might be automatically updated. + Reported by Karl Eichwalder. + * A normalized PO file header is automatically created if it + not exists. Any previous PO file header is kept, obsoleted. + + Various internal cleanups: + * Revised the PO mode summary display. + * ...-hooks renamed ...-hook, per word of Richard Stallman. + * po-obsolete-flag replaced by more general po-entry-type. + * po-appropriate-counter, po-increase-appropriate-counter and + po-decrease-appropriate-counter are replaced by po-type-counter, + po-decrease-type-counter and po-increase-type-counter. + Overall counting logic revised and cleaned up. + * The concept of being after last entry disappears. Being + after last entry is equivalent to being on last entry. And + since there is at least the PO header entry, and an empty PO + file is not possible anymore: simplified code accordingly. + * po-add-attribute, po-delete-attribute: New functions. + * po-offer-validation disappears, as (buffer-modified-p) may + be used instead. + +1996-03-14 16:55 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: Inexact entries are now called fuzzy. Change + regular expression and function names. + +1996-03-14 11:38 François Pinard <pinard@iro.umontreal.ca> + + * po-mode.el: Rename po-mode-hooks to po-mode-edit-hooks. + + * po-mode.el: Suggest accepting .pot and embedded .po. as triggers + for PO editing mode, besides .po and .pox. + * PO mode help display has been revised to fit in 23 lines, + and present some more yet unimplemented commands. PO mode + initially suggests using `h' or `?' for documentation. + * Many variables and functions have been renamed for more + clarity and consistency, too numerous to be detailed here. + * Reorganization of key-bindings. This restores to M-w and M-y + their usual meaning, and free some keys for to-be-implemented + commands: the a/M-a/A/M-A series for auxiliary files, c/M-c + for compendium files, and l/M-l/L/M-L series for lexicon files. + . Commands u, v, o and q have been renamed U, V, O and Q + (po-undo, po-validate, po-other-window and po-quit). + . Command v replaces old command V (po-mode-version). + . u and SPC replace e (po-next-untranslated-entry). + M-u replaces M-e (po-previous-untranslated-entry). + . o replaces M-n and M-SPC (po-next-obsolete-entry). + M-o replaces M-p and M-DEL (po-previous-obsolete-entry). + . SPC, DEL, and M-RET are no more po-next-entry, + po-previous-entry and po-edit-comment. + . r replaces l (po-pop-location). + . s (po-cycle-source-reference) replaces c (po-cycle-reference). + M-s (po-select-source-reference) replaces M-c (po-select-reference). + S (po-consider-source-path) replaces d (po-add-path). + M-S (po-ignore-source-path) replaces M-d (po-delete-path). + . K, W and Y replace M-k, M-w and M-y (po-kill-comment, + po-kill-ring-save-comment and po-yank-comment). + * New command q (po-confirm-and-quit). + +1996-03-13 13:16 Karl Eichwalder <ke@ke.Central.DE> + + * po-mode.el (po-edit-string): run hook `po-edit-hooks'. + +1996-03-09 12:39 Ulrich Drepper <drepper@myware> + + * po-mode.el (po-keywords): Add N_. + +1996-01-15 02:58 François Pinard <pinard@iro.umontreal.ca> + + * gettextize.in: Better message about aclocal.m4 change. + +1995-12-19 22:41 Ulrich Drepper <drepper@myware> + + * README-TCL, tcl_gettext.c: Initial revision. + + * Makefile.in (DISTFILES): Add tcl_gettext and README-TCL. + +1995-12-19 22:12 Ulrich Drepper <drepper@myware> + + * Makefile.in (Makefile, gettextize): Explicitly use $(SHELL) for + running shell scripts. + +1995-12-16 15:31 Ulrich Drepper <drepper@myware> + + * gettextize.in: Implement -c option: always copy files. + Requested by Roland McGrath. + +1995-12-05 11:41 Larry Schwimmer <rosebud@cyclone.stanford.edu> + + * Makefile.in (install-data): Make sure $(localedir) exists. + +1995-11-27 02:50 Sakai Kiyotaka <ksakai@netwk.ntt-at.co.jp> + + * locale.alias: New entry for Japanese. + +1995-11-24 23:53 Ulrich Drepper <drepper@myware> + + * po-mode.el (po-quit): Always clear the message area after y-or-n-p. + +1995-11-11 16:30 Ulrich Drepper <drepper@myware> + + * po-mode.el: Implement searching of inexact entries. + + * po-mode.el: Implement po-version. + +1995-11-08 01:46 Ulrich Drepper <drepper@myware> + + * po-mode.el: + Hilit file names and line numbers in #: with function-name face. + + * po-mode.el: Add support for XEmacs' font-lock.el. + + * po-mode.el: + Patches by Franc,ois: enable hilit, handle multi-line #: lines, and + don't pass -v argument to msgfmt. + +1995-11-06 15:52 Ulrich Drepper <drepper@myware> + + * po-mode.el: msgfmt behaves now well again. Return to use + /dev/null as output file for verification. + +1995-11-05 19:39 Ulrich Drepper <drepper@myware> + + * Makefile.in (dist-gettext): Make synonym for dist. + +1995-11-05 15:40 Ulrich Drepper <drepper@myware> + + * Makefile.in (dist): Suppress error message when ln failed. + Get files from $(srcdir) explicitly. + +1995-11-01 10:39 Ulrich Drepper <drepper@myware> + + * gettextize.in: + Don't use "!" as negation; not all versions of sh support it. Patch + by Tom Tromey. + +1995-10-31 20:46 Ulrich Drepper <drepper@myware> + + * po-mode.el (po-msgfmt-program): + Variable which contains name of the msgfmt program. + (po-validate): Use above variable. + +1995-10-31 19:12 Tom Tromey <tromey@cambric.colorado.edu> + + * gettextize.in: Don't use "!" as negation; not all versions of sh + support it. + +1995-10-30 22:22 Ulrich Drepper <drepper@myware> + + * po-mode.el (po-validate): + Protect the previous value of compile-command. + Change by Franc,ois Pinard. + +1995-10-29 12:11 Ulrich Drepper <drepper@myware> + + * gettextize.in: + Change text of trailing message about aclocal.m4 changing. Suggested + by Franc,ois Pinard. + + * Makefile.in (INSTALL_PROGRAM): Not used anymore. + (INSTALL_SCRIPT): New variable, + (install-exec): Install gettextize using INSTALL_SCRIPT. + + * po-mode.el (po-eval-requoted): Add space in description. + + * Makefile.in: Remove Emacs local variable setting. + +1995-10-28 22:09 Ulrich Drepper <drepper@myware> + + * Makefile.in (install-src): Move some rules from install-data. + (install-data): Add installation of locale.alias. + + * locale.alias: Initial revision + +1995-10-28 18:08 Ulrich Drepper <drepper@myware> + + * po-mode.el: Apply latest patch by Franc,ois. + +1995-09-23 14:34 Ulrich Drepper <drepper@myware> + + * gettextize.in: + Run config.status shell script if this is exists and is selected. This + is always necessary if one updates the intl/ dir. + +1995-09-20 22:26 Ulrich Drepper <drepper@myware> + + * gettextize.in: + Try to remove files in root and po/ dir before linking. If this is + not done re-linking to the same file will cause an error. + +1995-09-07 00:21 Ulrich Drepper <drepper@myware> + + * gettext-sh: Protect IFS assignments. + +1995-08-22 22:12 Ulrich Drepper <drepper@myware> + + * gettextize.in (usage): + Rearrange help message. Now describe -f option. + Reported by Franc,ois Pinard. + +1995-08-19 23:32 Ulrich Drepper <drepper@myware> + + * gettextize.in: Add missing terminating quotes. + + * Makefile.in (INSTALL_PROGRAM): + *Do* use -m 755. Autoconf does not set any mode. + + * Makefile.in (install-src): + Make behave like install. I.e. really install + gettextize and the .elc files. + +1995-08-19 15:08 Ulrich Drepper <drepper@myware> + + * gettextize.in: + Some nicety changes of shell programming by Franc,ois Pinard. + + * po-mode.el (po-search-path): Extend by "../". + + * gettextize.in: Protect against relative source paths. + Avoid exit an second level shell. + + * gettext-sh: Better comments: By Franc,ois Pinard. + + * Makefile.in (INSTALL_PRG): Don't define mode ourself. + (uninstall) Remove gettextize. + + * gettextize.in: + intl/VERSION is now a real file and must not be generated here. + +1995-08-18 12:05 Ulrich Drepper <drepper@myware> + + * gettext-sh: Use -s option for gettext instead of --shell-script. + +1995-08-15 10:49 Ulrich Drepper <drepper@myware> + + * Makefile.in (all, check): + Add gettextize to dependencies, so that install need not + build anything. + +1995-08-15 07:13 Ulrich Drepper <drepper@myware> + + * gettextize.in: Add intl/VERSION file. + + * gettextize.in: Remove target file for root- and po- file first. + + * gettextize.in: + Files installed in top directory of package are now preceded by root-. + All other are ignore (esp aclocal.m4). + +1995-08-14 23:50 Ulrich Drepper <drepper@myware> + + * Makefile.in (exec_prefix, bindir): + Directories needed for installing gettextize. + (transform): Standard GNU program name transformation. + (INSTALL_PROGRAM): Program to install gettextize as executable. + (DISTFILES): Remove makelinks.in. Distribute gettextize.in instead. + (install-src): Install gettextize in selected binary directory. + + * gettextize.in: Initial revision + +1995-08-10 22:18 Ulrich Drepper <drepper@myware> + + * gettext-sh: Fix typos. Reported by Franc,ois Pinard. + (TEXTDOMAIN): Set value from ${PACKAGE-NAME} variable and document it. + +1995-08-08 21:45 Ulrich Drepper <drepper@myware> + + * Makefile.in (DISTFILES): Add locale.alias. + +1995-08-07 23:48 Ulrich Drepper <drepper@myware> + + * Makefile.in (DISTFILES): gettext.perl moved from intl/. + gettext-sh: New file. + + * gettext-sh: Initial revision. + + * elisp-comp: + Use `rm -fr $tempdir' instead of `rm -f $tempdir/*; rmdir $tempdir' + because some NFS implementation create .nfsxxx files which are + not caught be the *. Reported by Paul Nevai. + +1995-08-04 22:38 Ulrich Drepper <drepper@myware> + + * Makefile.in (.el.elc): + Include Franc,ois' wonderful pun to highlight warning text. + + * Makefile.in (distclean): Remove makelinks. + + * Makefile.in (dist): Remove `copying instead' message. + +1995-08-02 19:44 Ulrich Drepper <drepper@myware> + + * Makefile.in (DISTFILES): makelinks.in instead of makelinks-sh. + + * makelinks.in: First try making symbolic links. + Rename to makelinks.in because it will be configure by + config.status. + +1995-08-01 15:54 Ulrich Drepper <drepper@myware> + + * po-mode.el: Fixes to run on DEMACS. + Provided by Franc,ois Pinard. + + * Makefile.in (check): Make same as all. + +1995-08-01 10:32 Ulrich Drepper <drepper@myware> + + * makelinks-sh: Implement option handling. + When intl/ subdir exist give warning and exit unless option -f + is given. + Use ${echo} in place where translations will be necessary. + +1995-07-26 01:24 Ulrich Drepper <drepper@myware> + + * makelinks-sh: Update for correct intl_files list. + Handle existing directories. + Handle non-existing ln and/or ln -s. + Mostly by Franc,ois Pinard. + +1995-07-22 01:14 Ulrich Drepper <drepper@myware> + + * Makefile.in (prefix,datadir,lispdir): New definitions. + (SITELISPDIR): Remove variable. + (all): Now build *.elc files. + (install,uninstall): Specify complete path of files to delete. + +1995-07-20 00:03 Ulrich Drepper <drepper@myware> + + * Makefile.in (DISTFILES): elisp-comp was missing. + +1995-07-18 23:53 Ulrich Drepper <drepper@myware> + + * po-mode.el: Latest version by Franc,ois. This version allows + scanning C source code for translatable strings and interactive + construction of the .po file. The string matching heuristic is + due to Richard Stallman. + +1995-07-18 01:32 Ulrich Drepper <drepper@myware> + + * Makefile.in (DISTFILES): Add magic.add. Comes from ../. + + * po-mode.el, makelinks, combine-sh: Moved to here from ../intl/. + magic.add: Moved to here from ../. + + * Makefile.in: Initial revision diff --git a/misc/Makefile.am b/misc/Makefile.am new file mode 100644 index 0000000..aaf822d --- /dev/null +++ b/misc/Makefile.am @@ -0,0 +1,31 @@ +## Makefile for the misc subdirectory of the GNU NLS Utilities +## Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc. +## +## This 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 2, or (at your option) +## any later version. +## +## This program 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 this program; if not, write to the Free Software +## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +## Process this file with automake to produce Makefile.in. + +AUTOMAKE_OPTIONS = 1.2 gnits + +localedir = $(datadir)/locale + +EXTRA_DIST = combine-sh gettext.perl gettext-sh magic.add tcl_gettext.c \ +README-Tcl po-mode.el locale.alias +DISTCLEANFILES = gettextize +lisp_LISP = po-mode.el + +bin_SCRIPTS = gettextize + +locale_DATA = locale.alias diff --git a/misc/Makefile.in b/misc/Makefile.in new file mode 100644 index 0000000..a2ec62d --- /dev/null +++ b/misc/Makefile.in @@ -0,0 +1,307 @@ +# Makefile.in generated automatically by automake 1.4 from Makefile.am + +# Copyright (C) 1994, 1995-8, 1999 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + + +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include + +DESTDIR = + +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ + +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS) +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +transform = @program_transform_name@ + +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ +ACLOCAL_VERSION = @ACLOCAL_VERSION@ +AS = @AS@ +CATALOGS = @CATALOGS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +DATADIRNAME = @DATADIRNAME@ +DLLTOOL = @DLLTOOL@ +EMACS = @EMACS@ +GENCAT = @GENCAT@ +GMOFILES = @GMOFILES@ +GMSGFMT = @GMSGFMT@ +GT_NO = @GT_NO@ +GT_YES = @GT_YES@ +INCLUDE_LOCALE_H = @INCLUDE_LOCALE_H@ +INSTOBJEXT = @INSTOBJEXT@ +INTLDEPS = @INTLDEPS@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +LIBOBJS = @LIBOBJS@ +LIBTOOL = @LIBTOOL@ +LN_S = @LN_S@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +OBJDUMP = @OBJDUMP@ +PACKAGE = @PACKAGE@ +POFILES = @POFILES@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +YACC = @YACC@ +aclocaldir = @aclocaldir@ +l = @l@ +lispdir = @lispdir@ + +AUTOMAKE_OPTIONS = 1.2 gnits + +localedir = $(datadir)/locale + +EXTRA_DIST = combine-sh gettext.perl gettext-sh magic.add tcl_gettext.c README-Tcl po-mode.el locale.alias + +DISTCLEANFILES = gettextize +lisp_LISP = po-mode.el + +bin_SCRIPTS = gettextize + +locale_DATA = locale.alias +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = ../config.h +CONFIG_CLEAN_FILES = gettextize +SCRIPTS = $(bin_SCRIPTS) + +LISP = $(lisp_LISP) + +ELCFILES = po-mode.elc +DATA = $(locale_DATA) + +DIST_COMMON = ChangeLog Makefile.am Makefile.in elisp-comp \ +gettextize.in + + +DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST) + +TAR = tar +GZIP_ENV = --best +all: all-redirect +.SUFFIXES: +.SUFFIXES: .el .elc +$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4) + cd $(top_srcdir) && $(AUTOMAKE) --gnits misc/Makefile + +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES) + cd $(top_builddir) \ + && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +gettextize: $(top_builddir)/config.status gettextize.in + cd $(top_builddir) && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status + +install-binSCRIPTS: $(bin_SCRIPTS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(bindir) + @list='$(bin_SCRIPTS)'; for p in $$list; do \ + if test -f $$p; then \ + echo " $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + else if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`"; \ + $(INSTALL_SCRIPT) $(srcdir)/$$p $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + else :; fi; fi; \ + done + +uninstall-binSCRIPTS: + @$(NORMAL_UNINSTALL) + list='$(bin_SCRIPTS)'; for p in $$list; do \ + rm -f $(DESTDIR)$(bindir)/`echo $$p|sed '$(transform)'`; \ + done + +install-lispLISP: $(lisp_LISP) $(ELCFILES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(lispdir) + @list='$(lisp_LISP)'; for p in $$list; do \ + if test -f "$$p"; then d= ; else d="$(srcdir)/"; fi; \ + echo " $(INSTALL_DATA) $$d$$p $(DESTDIR)$(lispdir)/$$p"; \ + $(INSTALL_DATA) $$d$$p $(DESTDIR)$(lispdir)/$$p; \ + if test -f $${p}c; then \ + echo " $(INSTALL_DATA) $${p}c $(DESTDIR)$(lispdir)/$${p}c"; \ + $(INSTALL_DATA) $${p}c $(DESTDIR)$(lispdir)/$${p}c; \ + else : ; fi; \ + done + +uninstall-lispLISP: + @$(NORMAL_UNINSTALL) + list='$(lisp_LISP)'; for p in $$list; do \ + rm -f $(lispdir)/$$p $(DESTDIR)$(lispdir)/$${p}c; \ + done +.el.elc: + @echo 'WARNING: Warnings can be ignored. :-)' + if test $(EMACS) != no; then \ + EMACS=$(EMACS) $(SHELL) $(srcdir)/elisp-comp $<; \ + fi + +mostlyclean-lisp: + +clean-lisp: + -test -z "$(ELCFILES)" || rm -f $(ELCFILES) + +distclean-lisp: + +maintainer-clean-lisp: + +install-localeDATA: $(locale_DATA) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(localedir) + @list='$(locale_DATA)'; for p in $$list; do \ + if test -f $(srcdir)/$$p; then \ + echo " $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(localedir)/$$p"; \ + $(INSTALL_DATA) $(srcdir)/$$p $(DESTDIR)$(localedir)/$$p; \ + else if test -f $$p; then \ + echo " $(INSTALL_DATA) $$p $(DESTDIR)$(localedir)/$$p"; \ + $(INSTALL_DATA) $$p $(DESTDIR)$(localedir)/$$p; \ + fi; fi; \ + done + +uninstall-localeDATA: + @$(NORMAL_UNINSTALL) + list='$(locale_DATA)'; for p in $$list; do \ + rm -f $(DESTDIR)$(localedir)/$$p; \ + done +tags: TAGS +TAGS: + + +distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir) + +subdir = misc + +distdir: $(DISTFILES) + here=`cd $(top_builddir) && pwd`; \ + top_distdir=`cd $(top_distdir) && pwd`; \ + distdir=`cd $(distdir) && pwd`; \ + cd $(top_srcdir) \ + && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnits misc/Makefile + @for file in $(DISTFILES); do \ + d=$(srcdir); \ + if test -d $$d/$$file; then \ + cp -pr $$/$$file $(distdir)/$$file; \ + else \ + test -f $(distdir)/$$file \ + || ln $$d/$$file $(distdir)/$$file 2> /dev/null \ + || cp -p $$d/$$file $(distdir)/$$file || :; \ + fi; \ + done +info-am: +info: info-am +dvi-am: +dvi: dvi-am +check-am: all-am +check: check-am +installcheck-am: +installcheck: installcheck-am +install-exec-am: install-binSCRIPTS +install-exec: install-exec-am + +install-data-am: install-lispLISP install-localeDATA +install-data: install-data-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +install: install-am +uninstall-am: uninstall-binSCRIPTS uninstall-lispLISP \ + uninstall-localeDATA +uninstall: uninstall-am +all-am: Makefile $(SCRIPTS) $(LISP) $(ELCFILES) $(DATA) +all-redirect: all-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install +installdirs: + $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(lispdir) \ + $(DESTDIR)$(localedir) + + +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + -rm -f config.cache config.log stamp-h stamp-h[0-9]* + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: +mostlyclean-am: mostlyclean-lisp mostlyclean-generic + +mostlyclean: mostlyclean-am + +clean-am: clean-lisp clean-generic mostlyclean-am + +clean: clean-am + +distclean-am: distclean-lisp distclean-generic clean-am + -rm -f libtool + +distclean: distclean-am + +maintainer-clean-am: maintainer-clean-lisp maintainer-clean-generic \ + distclean-am + @echo "This command is intended for maintainers to use;" + @echo "it deletes files that may require special tools to rebuild." + +maintainer-clean: maintainer-clean-am + +.PHONY: uninstall-binSCRIPTS install-binSCRIPTS uninstall-lispLISP \ +install-lispLISP mostlyclean-lisp distclean-lisp clean-lisp \ +maintainer-clean-lisp uninstall-localeDATA install-localeDATA tags \ +distdir info-am info dvi-am dvi check check-am installcheck-am \ +installcheck install-exec-am install-exec install-data-am install-data \ +install-am install uninstall-am uninstall all-redirect all-am all \ +installdirs mostlyclean-generic distclean-generic clean-generic \ +maintainer-clean-generic clean mostlyclean distclean maintainer-clean + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/misc/README-Tcl b/misc/README-Tcl new file mode 100644 index 0000000..7b9f6f9 --- /dev/null +++ b/misc/README-Tcl @@ -0,0 +1,52 @@ + Gettext support for Tcl + ======================= + +The file `tcl_gettext.c' contains the first attempt to provide an +interface for Tcl. I know that TclX already supports `catgets', but +because GNU uses `gettext' and because I think `gettext' is the better +concept I wrote this file. + + +Unlike many other interpreter extension I haven't provided a +`tclAppInit.c' file. I have three reason: it's easy to write (see +Ousterhout's book), It will probably combined with other extensions +and so would be more or less useless. But the most important reason +is that I don't find Tcl useful without the dynamic loading introduced +in version 7.5. + +The way I think you should use this module is to build a dynamic +object and load it at runtime with the command `load'. The following +transcript shows the way I work on my Linux/ELF box. Consult other +Tcl modules or a local wizard to see how it works for your system. + + +1. Compile the file to position independent object code: + + gcc -fPIC -O3 -c tcl_gettext.c + + (The file `tcl.h' from tcl7.5 or up and `libintl.h' as installed + from GNU gettext must be found in the path.) + +2. Link the file to a shared object: + + gcc -shared -o gettext.so tcl_gettext.o -lintl + + (The system's or GNU gettext libintl.a must be found in the path + of the linker.) + +3. Now we are ready to run: + +bash> tclsh7.5 +% load ./gettext.so Gettext +% textdomain "xxx" +% gettext "some string" +The translated form +% exit +bash> + + +If you have some ideas how to improve this or you found a bug, please +let me know. + +Ulrich Drepper <drepper@gnu.ai.mit.edu>, 18. December 1995. + diff --git a/misc/combine-sh b/misc/combine-sh new file mode 100755 index 0000000..b26fb4f --- /dev/null +++ b/misc/combine-sh @@ -0,0 +1,141 @@ +#!/bin/sh + +TMPDIR=/tmp +TMPNAME=/tmp/combine1.$$.sed + +trap 'rm -fr $TMPNAME combine; exit 1' 1 2 15 + +rm -f $TMPNAME +cat > $TMPNAME <<EOF +/@@ end of prolog @@/,/@@ begin of epilog @@/!d +/@@ end of prolog @@/ { + = + d +} +/@@ begin of epilog @@/d +EOF + + +rm -fr combine +mkdir combine +cat > combine/libintl.c <<EOF +/* Combined sources of GNU gettext library + Copyright (C) 1995 Software Foundation, Inc. + +This 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 2, or (at your option) +any later version. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#define USE_COMBINED_HEADER 1 +#include "libintlP.h" + +#if HAVE_CATGETS + +#include <stdlib.h> + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef HAVE_NL_TYPES_H +# include <nl_types.h> +#endif + +EOF + +CATCFILES=cat-compat.c +GETCFILES="bindtextdom.c dcgettext.c dgettext.c finddomain.c gettext.c \ +loadmsgcat.c textdomain.c" + +for file in $CATCFILES; do + sed -f $TMPNAME < $file \ + | sed -e '1s/.*/#line & "'$file'"/' >> combine/libintl.c +done +cat >> combine/libintl.c <<EOF + +#else /* !HAVE_CATGETS */ + +#include <alloca.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifdef HAVE_MMAP +# include <sys/mman.h> +#endif + +EOF +for file in $GETCFILES; do + sed -f $TMPNAME < $file \ + | sed -e '1s/.*/#line & "'$file'"/' >> combine/libintl.c +done +cat >> combine/libintl.c <<EOF +#endif /* HAVE_CATGETS */ +EOF + +cat > combine/libintlP.h <<EOF +/* Combined header files of GNU gettext library + Copyright (C) 1995 Software Foundation, Inc. + +This 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 2, or (at your option) +any later version. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +EOF + +PUBHEAD=libgettext.h +PRIVHEAD="gettext.h gettextP.h hash-string.h" + +for file in $PUBHEAD; do + sed -f $TMPNAME < $file \ + | sed -e '1s/.*/#line & "'$file'"/' >> combine/libintlP.h +done +cat >> combine/libintlP.h <<EOF + +#ifdef USE_COMBINED_HEADER +EOF +for file in $PRIVHEAD; do + sed -f $TMPNAME < $file \ + | sed -e '1s/.*/#line & "'$file'"/' >> combine/libintlP.h +done +cat >> combine/libintlP.h <<EOF +#endif /* USE_COMBINED_HEADER */ +EOF + + +rm -f $TMPNAME +exit 0 diff --git a/misc/elisp-comp b/misc/elisp-comp new file mode 100755 index 0000000..ddd15f6 --- /dev/null +++ b/misc/elisp-comp @@ -0,0 +1,42 @@ +#!/bin/sh +# Copyright (C) 1995, 1997 Free Software Foundation, Inc. +# François Pinard <pinard@iro.umontreal.ca>, 1995. +# +# This 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 2, or (at your option) +# any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# This script byte-compiles all `.el' files which are part of its +# arguments, using GNU Emacs, and put the resulting `.elc' files into +# the current directory, so disregarding the original directories used +# in `.el' arguments. +# +# This script manages in such a way that all Emacs LISP files to +# be compiled are made visible between themselves, in the event +# they require or load-library one another. + +if test $# = 0; then + echo 1>&2 "No files given to $0" +else + tempdir=elc.$$ + mkdir $tempdir + cp $* $tempdir + cd $tempdir + + echo "(setq load-path (cons nil load-path))" > script + ${EMACS-emacs} -batch -l script -f batch-byte-compile *.el + mv *.elc .. + + cd .. + rm -fr $tempdir +fi diff --git a/misc/gettext-sh b/misc/gettext-sh new file mode 100644 index 0000000..ee15bab --- /dev/null +++ b/misc/gettext-sh @@ -0,0 +1,63 @@ +# Copyright (C) 1995 Free Software Foundation, Inc. +# Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, August 1995 +# +# This 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 2, or (at your option) +# any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Shell script excerpt to be inserted in shell scripts which are intended +# to be internationalized using GNU gettext. +# +# Precondition: +# ${PACKAGE-IDENT} is the name of one program of the package which +# understands the --print-text-domain-dir option +# ${PACKAGE-NAME} is the name of the message domain the package uses +# ${PATH} should list all directories which should be searched +# for either `gettext' or ${PACKAGE-IDENT}. +# +# Postcondition: +# ${echo} command to use like Un*x echo command which translates +# its arguments when a translation is available. +# +save_IFS="${IFS}" +IFS="${IFS}:" +gettext_dir=FAILED +locale_dir=FAILED +for dir in ${PATH} +do + if test "${gettext_dir}" = "FAILED" && test -x ${dir}/gettext \ + && (${dir}/gettext --version >/dev/null 2>&1) + then + set `${dir}/gettext --version 2>&1` + if test "$3" = "GNU" + then + gettext_dir=${dir} + fi + fi + if test "${locale_dir}" = "FAILED" && test -x ${dir}/${PACKAGE-IDENT} \ + && (${dir}/${PACKAGE-IDENT} --print-text-domain-dir >/dev/null 2>&1) + then + locale_dir=`${dir}/${PACKAGE-IDENT} --print-text-domain-dir` + fi +done +IFS="${save_IFS}" +if test "${locale_dir}" = "FAILED" || test "${gettext_dir}" = "FAILED" +then + echo=echo +else + TEXTDOMAINDIR=${locale_dir} + export TEXTDOMAINDIR + TEXTDOMAIN=${PACKAGE-NAME} + export TEXTDOMAIN + echo="${gettext_dir}/gettext -s" +fi diff --git a/misc/gettext.perl b/misc/gettext.perl new file mode 100644 index 0000000..7bd1ee8 --- /dev/null +++ b/misc/gettext.perl @@ -0,0 +1,86 @@ +# Toying at an interface between Perl and GNU gettext .mo format. +# Copyright (C) 1995 Free Software Foundation, Inc. +# François Pinard <pinard@iro.umontreal.ca>, 1995. + +textdomain ("tar"); +print &_("Try \`%s --help\' for more information.\n"); +exit 0; + +## --------------------------------------------------------------- ## +## The `&textdomain (DOMAIN_NAME)' routine reads the given domain ## +## into an associative array %_, able to later translate strings. ## +## --------------------------------------------------------------- ## + +sub textdomain +{ + local ($language, $catalog, $domain, $buffer); + local ($reverse); + local ($magic, $revision, $nstrings, $orig_tab_offset, $trans_tab_offset); + local ($orig_length, $orig_pointer, $trans_length, $trans_pointer); + + %_ = (); + + $language = $ENV{"LANG"}; + return if ! $language; + $domain = $_[0]; + $catalog = "/usr/local/share/locale/$language/LC_MESSAGES/$domain.mo"; + + open (CATALOG, $catalog) || return; + sysread (CATALOG, $buffer, (stat CATALOG)[7]); + close CATALOG; + + $magic = unpack ("I", $buffer); + if (sprintf ("%x", $magic) eq "de120495") + { + $reverse = 1; + } + elsif (sprintf ("%x", $magic) ne "950412de") + { + die "Not a catalog file\n"; + } + + $revision = &mo_format_value (4); + $nstrings = &mo_format_value (8); + $orig_tab_offset = &mo_format_value (12); + $trans_tab_offset = &mo_format_value (16); + + while ($nstrings-- > 0) + { + $orig_length = &mo_format_value ($orig_tab_offset); + $orig_pointer = &mo_format_value ($orig_tab_offset + 4); + $orig_tab_offset += 8; + + $trans_length = &mo_format_value ($trans_tab_offset); + $trans_pointer = &mo_format_value ($trans_tab_offset + 4); + $trans_tab_offset += 8; + + $_{substr ($buffer, $orig_pointer, $orig_length)} + = substr ($buffer, $trans_pointer, $trans_length); + } +} + +## ----------------------------------------------------------------- ## +## The `&mo_format_value (ADDRESS)' routine returns the value at a ## +## given address in the .mo format catalog, once read into $buffer ## +## by `&textdomain'. This is a service routine of `&textdomain', ## +## which uses $buffer and $reverse variables local in that routine. ## +## ----------------------------------------------------------------- ## + +sub mo_format_value +{ + unpack ("i", + $reverse + ? pack ("c4", reverse unpack ("c4", substr ($buffer, $_[0], 4))) + : substr ($buffer, $_[0], 4)); +} + +## ------------------------------------------------------------ ## +## The `&_(STRING)' routine translates STRING if there is some ## +## translation offered for it in the `%_' associative array, or ## +## return STRING itself, otherwize. ## +## ------------------------------------------------------------ ## + +sub _ +{ + defined $_{$_[0]} ? $_{$_[0]} : $_[0]; +} diff --git a/misc/gettextize.in b/misc/gettextize.in new file mode 100644 index 0000000..75e3c2e --- /dev/null +++ b/misc/gettextize.in @@ -0,0 +1,177 @@ +#! /bin/sh +# +# Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc. +# +# This 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 2, or (at your option) +# any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +# This file is meant for authors or maintainers which want to +# internationalize their package with the help of GNU gettext. For +# further information how to use it consult the GNU gettext manual. + +echo=echo +progname=$0 +force=0 +configstatus=0 +origdir=`pwd` +usage="\ +Usage: gettextize [OPTION]... [package-dir] + --help print this help and exit + --version print version information and exit + -c, --copy copy files instead of making symlinks + -f, --force force writing of new files even if old exist +Report bugs to <bug-gnu-utils@prep.ai.mit.edu>." +package=@PACKAGE@ +version=@VERSION@ +aclocal_version=@ACLOCAL_VERSION@ +try_ln_s=: + +while test $# -gt 0; do + case "$1" in + -c | --copy | --c* ) + shift + try_ln_s=false ;; + -f | --force | --f* ) + shift + force=1 ;; + -r | --run | --r* ) + shift + configstatus=1 ;; + --help | --h* ) + $echo "$usage"; exit 0 ;; + --version | --v* ) + echo "$progname (GNU $package) $version" + $echo "Copyright (C) 1996, 1997, 1998 Free Software Foundation, Inc. +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + $echo "Written by" "Ulrich Drepper" + exit 0 ;; + -- ) # Stop option prcessing + shift; break ;; + -* ) + $echo "gettextize: unknown option $1" + $echo "Try \`gettextize --help' for more information."; exit 1 ;; + * ) + break ;; + esac +done + +if test $# -gt 1; then + $echo "$usage" + exit 1 +fi + +# Fill in the command line options value. +if test $# -eq 1; then + srcdir=$1 + if cd $srcdir; then + srcdir=`pwd` + else + $echo "Cannot change directory to \`$srcdir'" + exit 1 + fi +else + srcdir=$origdir +fi + +# Directory where the sources are stored. +prefix=@prefix@ +gettext_dir=@datadir@/gettext + +test -f configure.in || { + $echo "Missing configure.in, please cd to your package first." + exit 1 +} + +if test -d intl && test $force -eq 0; then + $echo "\ +intl/ subdirectory exists: use option -f if you really want to delete it." + exit 1 +fi + +if test -f po/Makefile.in.in && test $force -eq 0; then + $echo "\ +po/Makefile.in.in exists: use option -f if you really want to delete it." + exit 1 +fi + +if test -f NLS && test $force -eq 0; then + $echo "NLS exists: use option -f if you really want to delete it." + exit 1 +fi + +rm -fr intl +mkdir intl || { + $echo "failed to create intl/ subdirectory" + exit 1; +} + +test -d po || mkdir po || { + $echo "failed to create po/ subdirectory" + exit 1 +} + +# For simplicity we changed to the gettext source directory. +cd $gettext_dir + +# Now copy all files. Take care for the destination directories. +for file in *; do + case $file in + intl | po | demo) + ;; + *) + rm -f $srcdir/$file + ($try_ln_s && ln -s $gettext_dir/$file $srcdir/$file) 2>/dev/null || + cp $file $srcdir/$file + ;; + esac +done + +# Copy files to intl/ subdirectory. +cd intl +for file in *; do + rm -f $srcdir/intl/$file + ($try_ln_s && ln -s $gettext_dir/intl/$file $srcdir/intl/$file) 2>/dev/null || + cp $file $srcdir/intl/$file +done + +# Copy files to po/ subdirectory. +cd ../po +for file in *; do + rm -f $srcdir/po/$file + ($try_ln_s && ln -s $gettext_dir/po/$file $srcdir/po/$file) 2>/dev/null || + cp $file $srcdir/po/$file +done + + +# Check whether we can run config.status to produce intl/Makefile.in. +cd $origdir +if test -f ./config.status; then + if test $configstatus -eq 0; then + echo "Shall I run config.status? (y/N)" + read ans + case "$ans" in + y* | Y* | 1* ) + configstatus=1 ;; + * ) + ;; + esac + fi + + test $configstatus -ne 0 && + (CONFIG_FILES=intl/Makefile CONFIG_HEADERS= ./config.status) +fi + +exit 0 diff --git a/misc/locale.alias b/misc/locale.alias new file mode 100644 index 0000000..c3666fc --- /dev/null +++ b/misc/locale.alias @@ -0,0 +1,52 @@ +# Locale name alias data base +# Copyright (C) 1995 Free Software Foundation, Inc. +# +# This 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 2, or (at your option) +# any later version. +# +# This program 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 this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +# The format of this file is the same as for the corresponding file of +# the X Window System, which normally can be found in +# /usr/lib/X11/locale/locale.alias +# A single line contains two fields: an alias and a substitution value. +# All entries are case independent. + +# Note: This file is far from being complete. If you have a value for +# your own site which you think might be useful for others too, share it +# with the rest of us. Send it to bug-gnu-utils@prep.ai.mit.edu. + +czech cs_CZ.ISO-8859-2 +danish da_DK.ISO-8859-1 +dansk da_DK.ISO-8859-1 +deutsch de_DE.ISO-8859-1 +dutch nl_NL.ISO-8859-1 +finnish fi_FI.ISO-8859-1 +français fr_FR.ISO-8859-1 +french fr_FR.ISO-8859-1 +german de_DE.ISO-8859-1 +greek el_GR.ISO-8859-7 +hebrew iw_IL.ISO-8859-8 +hungarian hu_HU.ISO-8859-2 +icelandic is_IS.ISO-8859-1 +italian it_CH.ISO-8859-1 +japanese ja_JP.EUC +norwegian no_NO.ISO-8859-1 +polish pl_PL.ISO-8859-2 +portuguese pt_PT.ISO-8859-1 +rumanian ro_RO.ISO-8859-2 +russian ru_SU.ISO-8859-5 +slovak sk_SK.ISO-8859-2 +slovene sl_CS.ISO-8859-2 +spanish es_ES.ISO-8859-1 +swedish sv_SE.ISO-8859-1 +turkish tr_TR.ISO-8859-9 diff --git a/misc/magic.add b/misc/magic.add new file mode 100644 index 0000000..85af567 --- /dev/null +++ b/misc/magic.add @@ -0,0 +1,9 @@ +# +# GNU nlsutils message catalog file format +# +0 string \336\22\4\225 GNU message catalog (little endian), +>4 lelong x revision %d, +>8 lelong x %d messages +0 string \225\4\22\336 GNU message catalog (big endian), +>4 belong x revision %d, +>8 belong x %d messages diff --git a/misc/po-mode.el b/misc/po-mode.el new file mode 100644 index 0000000..bdab3c0 --- /dev/null +++ b/misc/po-mode.el @@ -0,0 +1,2602 @@ +;;; po-mode.el -- for helping GNU gettext lovers to edit PO files. +;;; Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc. +;;; François Pinard <pinard@iro.umontreal.ca>, 1995. +;;; Helped by Greg McGary <gkm@magilla.cichlid.com>. + +;; This file is part of GNU gettext. + +;; GNU gettext 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 2, or (at your option) +;; any later version. + +;; GNU gettext 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 GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, 59 Temple Place - Suite 330, Boston, +;; MA 02111-1307, USA. + +;;; This package provides the tools meant to help editing PO files, +;;; as documented in the GNU gettext user's manual. See this manual +;;; for user documentation, which is not repeated here. + +;;; To install, merely put this file somewhere GNU Emacs will find it, +;;; then add the following lines to your .emacs file: +;;; +;;; (autoload 'po-mode "po-mode") +;;; (setq auto-mode-alist (cons '("\\.po[tx]?\\'\\|\\.po\\." . po-mode) +;;; auto-mode-alist)) +;;; +;;; To automatically use proper fonts under Emacs 20, also add: +;;; +;;; (autoload 'po-find-file-coding-system "po-mode") +;;; (modify-coding-system-alist 'file "\\.po[tx]?\\'\\|\\.po\\." +;;; 'po-find-file-coding-system) +;;; +;;; You may also adjust some variables, below, by defining them in your +;;; `.emacs' file, either directly or through command `M-x customize'. + +;;; Emacs portability matters. + +;;; Most portability matters are addressed in this page. All other cases +;;; involve one of `eval-and-compile' or `fboundp', just search for these. + +;; Identify which Emacs variety is being used. +(eval-and-compile + (cond ((string-match "Lucid\\|XEmacs" emacs-version) + (setq po-EMACS20 nil po-XEMACS t)) + ((and (string-lessp "19" emacs-version) (featurep 'faces)) + (setq po-EMACS20 t po-XEMACS nil)) + (t (setq po-EMACS20 nil po-XEMACS nil)))) + +;; Experiment with Emacs LISP message internationalisation. +(eval-and-compile + (or (fboundp 'set-translation-domain) + (defsubst set-translation-domain (string) nil)) + (or (fboundp 'translate-string) + (defsubst translate-string (string) string))) +(defsubst _ (string) (translate-string string)) +(defsubst N_ (string) string) + +;; Handle missing `customs' package. +(eval-and-compile + (condition-case () + (require 'custom) + (error nil)) + (if (and (featurep 'custom) (fboundp 'custom-declare-variable)) + nil + (defmacro defgroup (&rest args) + nil) + (defmacro defcustom (var value doc &rest args) + (` (defvar (, var) (, value) (, doc)))))) + +;; Protect string comparisons from text properties. +(eval-and-compile + (fset 'po-buffer-substring + (symbol-function (if (fboundp 'buffer-substring-no-properties) + 'buffer-substring-no-properties + 'buffer-substring)))) + +;; Handle missing `with-temp-buffer' function. +(eval-and-compile + (if nil ; FIXME: just testing... (fboundp 'with-temp-buffer) + + (fset 'po-with-temp-buffer (symbol-function 'with-temp-buffer)) + + (defmacro po-with-temp-buffer (&rest forms) + "Create a temporary buffer, and evaluate FORMS there like `progn'." + (let ((curr-buffer (make-symbol "curr-buffer")) + (temp-buffer (make-symbol "temp-buffer"))) + `(let ((,curr-buffer (current-buffer)) + (,temp-buffer (get-buffer-create + (generate-new-buffer-name " *po-temp*")))) + (unwind-protect + (progn + (set-buffer ,temp-buffer) + ,@forms) + (set-buffer ,curr-buffer) + (and (buffer-name ,temp-buffer) + (kill-buffer ,temp-buffer)))))))) + +;; Handle missing `kill-new' function. +(eval-and-compile + (if (fboundp 'kill-new) + + (fset 'po-kill-new (symbol-function 'kill-new)) + + (defun po-kill-new (string) + "Push STRING onto the kill ring, for Emacs 18 where kill-new is missing." + (po-with-temp-buffer + (insert string) + (kill-region (point-min) (point-max)))))) + +;; Handle missing `read-event' function. +(eval-and-compile + (fset 'po-read-event + (cond ((fboundp 'read-event) + ;; GNU Emacs. + 'read-event) + ((fboundp 'next-command-event) + ;; XEmacs. + 'next-command-event) + (t + ;; Older Emacses. + 'read-char)))) + +;; Handle missing `force-mode-line-update' function. +(eval-and-compile + (if (fboundp 'force-mode-line-update) + + (fset 'po-force-mode-line-update + (symbol-function 'force-mode-line-update)) + + (defun po-force-mode-line-update () + "Force the mode-line of the current buffer to be redisplayed." + (set-buffer-modified-p (buffer-modified-p))))) + +;; Handle portable highlighting. Code has been adapted (OK... stolen! :-) +;; from `ispell.el'. +(eval-and-compile + (cond + (po-EMACS20 + + (defun po-create-overlay () + "Create and return a deleted overlay structure. +The variable `po-highlight-face' selects the face to use for highlighting." + (let ((overlay (make-overlay (point) (point)))) + (overlay-put overlay 'face po-highlight-face) + ;; The fun thing is that a deleted overlay retains its face, and is + ;; movable. + (delete-overlay overlay) + overlay)) + + (defun po-highlight (overlay start end &optional buffer) + "Use OVERLAY to highlight the string from START to END. +If limits are not relative to the current buffer, use optional BUFFER." + (move-overlay overlay start end (or buffer (current-buffer)))) + + (defun po-rehighlight (overlay) + "Ensure OVERLAY is highlighted." + ;; There is nothing to do, as GNU Emacs allows multiple highlights. + nil) + + (defun po-dehighlight (overlay) + "Display normally the last string which OVERLAY highlighted. +The current buffer should be in PO mode, when this function is called." + (delete-overlay overlay))) + + (po-XEMACS + + (defun po-create-overlay () + "Create and return a deleted overlay structure." + (cons (make-marker) (make-marker))) + + (defun po-highlight (overlay start end &optional buffer) + "Use OVERLAY to highlight the string from START to END. +If limits are not relative to the current buffer, use optional BUFFER." + (if buffer + (save-excursion + (set-buffer buffer) + (isearch-highlight start end)) + (isearch-highlight start end)) + (set-marker (car overlay) start (or buffer (current-buffer))) + (set-marker (cdr overlay) end (or buffer (current-buffer)))) + + (defun po-rehighlight (overlay) + "Ensure OVERLAY is highlighted." + (let ((buffer (marker-buffer (car overlay))) + (start (marker-position (car overlay))) + (end (marker-position (cdr overlay)))) + (and buffer + (name-buffer buffer) + (po-highlight overlay start end buffer)))) + + (defun po-dehighlight (overlay) + "Display normally the last string which OVERLAY highlighted." + (isearch-dehighlight t) + (setcar overlay (make-marker)) + (setcdr overlay (make-marker)))) + + (t + + (defun po-create-overlay () + "Create and return a deleted overlay structure." + (cons (make-marker) (make-marker))) + + (defun po-highlight (overlay start end &optional buffer) + "Use OVERLAY to highlight the string from START to END. +If limits are not relative to the current buffer, use optional BUFFER. +No doubt that highlighting, when Emacs does not allow it, is a kludge." + (save-excursion + (and buffer (set-buffer buffer)) + (let ((modified (buffer-modified-p)) + (buffer-read-only nil) + (inhibit-quit t) + (buffer-undo-list t) + (text (buffer-substring start end))) + (goto-char start) + (delete-region start end) + (insert-char ? (- end start)) + (sit-for 0) + (setq inverse-video (not inverse-video)) + (delete-region start end) + (insert text) + (sit-for 0) + (setq inverse-video (not inverse-video)) + (set-buffer-modified-p modified))) + (set-marker (car overlay) start (or buffer (current-buffer))) + (set-marker (cdr overlay) end (or buffer (current-buffer)))) + + (defun po-rehighlight (overlay) + "Ensure OVERLAY is highlighted." + (let ((buffer (marker-buffer (car overlay))) + (start (marker-position (car overlay))) + (end (marker-position (cdr overlay)))) + (and buffer + (name-buffer buffer) + (po-highlight overlay start end buffer)))) + + (defun po-dehighlight (overlay) + "Display normally the last string which OVERLAY highlighted." + (let ((buffer (marker-buffer (car overlay))) + (start (marker-position (car overlay))) + (end (marker-position (cdr overlay)))) + (if buffer + (save-excursion + (set-buffer buffer) + (let ((modified (buffer-modified-p)) + (buffer-read-only nil) + (inhibit-quit t) + (buffer-undo-list t)) + (let ((text (buffer-substring start end))) + (goto-char start) + (delete-region start end) + (insert-char ? (- end start)) + (sit-for 0) + (delete-region start end) + (insert text) + (sit-for 0) + (set-buffer-modified-p modified))))) + (setcar overlay (make-marker)) + (setcdr overlay (make-marker)))) + + ))) + +;;; Customisation. + +(defgroup po nil + "Major mode for editing PO files" + :group 'i18n) + +(defcustom po-auto-edit-with-msgid nil + "*Automatically use msgid when editing untranslated entries." + :type 'boolean + :group 'po) + +(defcustom po-auto-fuzzy-on-edit nil + "*Automatically mark entries fuzzy when being edited." + :type 'boolean + :group 'po) + +(defcustom po-auto-select-on-unfuzzy nil + "*Automatically select some new entry while making an entry not fuzzy." + :type 'boolean + :group 'po) + +(defcustom po-auto-replace-revision-date 'ask + "*Automatically revise date in headers. Value is nil, t, or ask." + :type '(choice (const nil) + (const t) + (const ask)) + :group 'po) + +(defcustom po-default-file-header "\ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR Free Software Foundation, Inc. +# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. +# +#, fuzzy +msgid \"\" +msgstr \"\" +\"Project-Id-Version: PACKAGE VERSION\\n\" +\"PO-Revision-Date: YEAR-MO-DA HO:MI +ZONE\\n\" +\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\" +\"Language-Team: LANGUAGE <LL@li.org>\\n\" +\"MIME-Version: 1.0\\n\" +\"Content-Type: text/plain; charset=CHARSET\\n\" +\"Content-Transfer-Encoding: ENCODING\\n\" +" + "*Default PO file header." + :type 'string + :group 'po) + +(defcustom po-highlighting (or po-EMACS20 po-XEMACS) + "*Highlight text whenever appropriate, when non-nil. However, on older +Emacses, a yet unexplained highlighting bug causes files to get mangled." + :type 'boolean + :group 'po) + +(defcustom po-highlight-face 'highlight + "*The face used for PO mode highlighting. For Emacses with overlays. +Possible values are `highlight', `modeline', `secondary-selection', +`region', and `underline'. +This variable can be set by the user to whatever face they desire. +It's most convenient if the cursor color and highlight color are +slightly different." + :type 'face + :group 'po) + +(defcustom po-gzip-uuencode-command "gzip -9 | uuencode -m" + "*The filter to use for preparing a mail invoice of the PO file. +Normally \"gzip -9 | uuencode -m\", remove the -9 for lesser compression, +or remove the -m if you are not using the GNU version of `uuencode'." + :type 'string + :group 'po) + +;;; The following block of declarations has the main purpose of avoiding +;;; byte compiler warnings. It also introduces some documentation for +;;; each of these variables, all meant to be local to PO mode buffers. + +;; Flag telling that MODE-LINE-STRING should be displayed. See `Window' +;; page below. Exceptionally, this variable is local to *all* buffers. + +(defvar po-mode-flag) + +;; PO buffers are kept read-only to prevent random modifications. READ-ONLY +;; holds the value of the read-only flag before PO mode was entered. + +(defvar po-read-only) + +;; The current entry extends from START-OF-ENTRY to END-OF-ENTRY, it +;; includes preceding whitespace and excludes following whitespace. The +;; start of keyword lines are START-OF-MSGID and START-OF-MSGSTR. +;; ENTRY-TYPE classifies the entry. + +(defvar po-start-of-entry) +(defvar po-start-of-msgid) +(defvar po-start-of-msgstr) +(defvar po-end-of-entry) +(defvar po-entry-type) + +;; A few counters are usefully shown in the Emacs mode line. + +(defvar po-translated-counter) +(defvar po-fuzzy-counter) +(defvar po-untranslated-counter) +(defvar po-obsolete-counter) +(defvar po-mode-line-string) + +;; PO mode keeps track of fields being edited, for one given field should +;; have one editing buffer at most, and for exiting a PO buffer properly +;; should offer to close all pending edits. Variable EDITED-FIELDS holds an +;; an list of "slots" of the form: (ENTRY-MARKER EDIT-BUFFER OVERLAY-INFO). +;; To allow simultaneous edition of the comment and the msgstr of an entry, +;; ENTRY-MARKER points to the msgid line if a comment is being edited, or to +;; the msgstr line if the msgstr is being edited. EDIT-BUFFER is the +;; temporary Emacs buffer used to edit the string. OVERLAY-INFO, when not +;; nil, holds an overlay (or if overlays are not supported, a cons of two +;; markers) for this msgid string which became highlighted for the edit. + +(defvar po-edited-fields) + +;; We maintain a set of movable pointers for returning to entries. + +(defvar po-marker-stack) + +;; SEARCH path contains a list of directories where files may be found, +;; in a format suitable for read completion. Each directory includes +;; its trailing slash. PO mode starts with "./" and "../". + +(defvar po-search-path) + +;; The following variables are meaningful only when REFERENCE-CHECK +;; is identical to START-OF-ENTRY, else they should be recomputed. +;; REFERENCE-ALIST contains all known references for the current +;; entry, each list element is (PROMPT FILE LINE), where PROMPT may +;; be used for completing read, FILE is a string and LINE is a number. +;; REFERENCE-CURSOR is a cycling cursor into REFERENCE-ALIST. + +(defvar po-reference-alist) +(defvar po-reference-cursor) +(defvar po-reference-check) + +;; The following variables are for marking translatable strings in program +;; sources. KEYWORDS is the list of keywords for marking translatable +;; strings, kept in a format suitable for reading with completion. +;; NEXT-FILE-LIST is the list of source files to visit, gotten from the tags +;; table. STRING-START is the position for the beginning of the last found +;; string, or nil if the string is invalidated. STRING-END is the position +;; for the end of the string and indicates where the search should be +;; resumed, or nil for the beginning of the current file. MARKING-OVERLAY, +;; if not `nil', holds the overlay which highlight the last found string; +;; for older Emacses, it holds the cons of two markers around the +;; highlighted region. + +(defvar po-keywords) +(defvar po-next-file-list) +(defvar po-string-start) +(defvar po-string-end) +(defvar po-marking-overlay) + +;;; PO mode variables and constants (usually not to customize). + +;; The textdomain should really be "gettext", only trying it for now. +;; All this requires more thinking, we cannot just do this like that. +(set-translation-domain "po-mode") + +(defun po-mode-version () + "Show Emacs PO mode version." + (interactive) + (message (_"Emacs PO mode, version %s") (substring "$Revision: 1.43 $" 11 -2))) + +(defconst po-help-display-string + (_"\ +PO Mode Summary Next Previous Miscellaneous +*: Later, /: Docum n p Any type . Redisplay + /t /M-t Translated /v Version info +Moving around f M-f Fuzzy ?, h This help +< First if any o M-o Obsolete = Current index +> Last if any u M-u Untranslated O Other window +/SPC Auto select V Validate + Msgstr Comments M Mail officially +Modifying entries RET # Call editor U Undo +TAB Remove fuzzy mark k K Kill to E Edit out full +/DEL Fuzzy or fade out w W Copy to Q Forceful quit +LFD Init with msgid y Y Yank from q Confirm and quit + +gettext Keyword Marking Position Stack +, Find next string Compendiums m Mark and push current +M-, Mark translatable *c To compendium r Pop and return +M-. Change mark, mark *M-C Select, save x Exchange current/top + +Program Sources Auxiliary Files Lexicography +s Cycle reference a Cycle file *l Lookup translation +M-s Select reference M-a Select file *M-l Add/edit translation +S Consider path A Consider PO file *L Consider lexicon +M-S Ignore path M-A Ignore PO file *M-L Ignore lexicon +") + "Help page for PO mode.") + +(defconst po-mode-menu-layout + '("PO" + ("Moving around" + ["Auto select" po-auto-select-entry t] + "---" + "Forward" + ["Any next" po-next-entry t] + ["Next translated" po-next-translated-entry t] + ["Next fuzzy" po-next-fuzzy-entry t] + ["Next obsolete" po-next-obsolete-entry t] + ["Next untranslated" po-next-untranslated-entry t] + ["Last file entry" po-last-entry t] + "---" + "Backward" + ["Any previous" po-previous-entry t] + ["Previous translated" po-previous-translated-entry t] + ["Previous fuzzy" po-previous-fuzzy-entry t] + ["Previous obsolete" po-previous-obsolete-entry t] + ["Previous untranslated" po-previous-untranslated-entry t] + ["First file entry" po-first-entry t] + "---" + "Position stack" + ["Mark and push current" po-push-location t] + ["Pop and return" po-pop-location t] + ["Exchange current/top" po-exchange-location t] + "---" + ["Redisplay" po-current-entry t] + ["Current index" po-statistics t]) + ("Modifying entries" + ["Undo" po-undo t] + "---" + "Msgstr" + ["Edit msgstr" po-edit-msgstr t] + ["Kill msgstr" po-kill-msgstr t] + ["Save msgstr" po-kill-ring-save-msgstr t] + ["Yank msgstr" po-yank-msgstr t] + "---" + "Comments" + ["Edit comment" po-edit-comment t] + ["Kill comment" po-kill-comment t] + ["Save comment" po-kill-ring-save-comment t] + ["Yank comment" po-yank-comment t] + "---" + ["Remove fuzzy mark" po-unfuzzy t] + ["Fuzzy or fade out" po-fade-out-entry t] + ["Init with msgid" po-msgid-to-msgstr t]) + ("Other files" + ["Other window" po-other-window t] + "---" + "Program sources" + ["Cycle reference" po-cycle-source-reference t] + ["Select reference" po-select-source-reference t] + ["Consider path" po-consider-source-path t] + ["Ignore path" po-ignore-source-path t] + "---" + "Compendiums" + ["To compendium" po-save-entry nil] + ["Select, save" po-select-and-save-entry nil] + "---" + "Auxiliary files" + ["Cycle file" po-cycle-auxiliary t] + ["Select file" po-select-auxiliary t] + ["Consider file" po-consider-as-auxiliary t] + ["Ignore file" po-ignore-as-auxiliary t] + "---" + "Lexicography" + ["Lookup translation" po-lookup-lexicons nil] + ["Add/edit translation" po-edit-lexicon-entry nil] + ["Consider lexicon" po-consider-lexicon-file nil] + ["Ignore lexicon" po-ignore-lexicon-file nil]) + "---" + "Source marking" + ["Find first string" (po-tags-search '(nil)) t] + ["Prefer keyword" (po-select-mark-and-mark '(nil)) t] + ["Find next string" po-tags-search t] + ["Mark preferred" po-mark-translatable t] + ["Mark with keyword" po-select-mark-and-mark t] + "---" + ["Version info" po-mode-version t] + ["Help page" po-help t] + ["Validate" po-validate t] + ["Mail officially" po-send-mail t] + ["Edit out full" po-edit-out-full t] + "---" + ["Forceful quit" po-quit t] + ["Soft quit" po-confirm-and-quit t]) + "Menu layout for PO mode.") + +;; FIXME: subedit mode should also have its own layout. + +(defconst po-subedit-message + (_"Type `C-c C-c' once done, or `C-c C-k' to abort edit") + "Message to post in the minibuffer when an edit buffer is displayed.") + +(defconst po-content-type-charset-alist + '((euc . japanese-iso-8bit)) + "How to convert Content-Type into a Mule coding system.") + +(defvar po-auxiliary-list nil + "List of auxiliary PO files, in completing read format.") + +(defvar po-auxiliary-cursor nil + "Cursor into the `po-auxiliary-list'.") + +(defvar po-translation-project-address + "translation@iro.umontreal.ca" + "Electronic mail address of the Translation Project.") + +(defvar po-compose-mail-function + (let ((functions '(compose-mail-other-window + message-mail-other-window + compose-mail + message-mail)) + result) + (while (and (not result) functions) + (if (fboundp (car functions)) + (setq result (car functions)) + (setq functions (cdr functions)))) + (cond (result) + ((fboundp 'mail-other-window) + (function (lambda (to subject) + (mail-other-window nil to subject)))) + ((fboundp 'mail) + (function (lambda (to subject) + (mail nil to subject)))) + (t (function (lambda (to subject) + (error (_"I do not know how to mail to `%s'") to)))))) + "Function to start composing an electronic message.") + +(defvar po-any-msgid-regexp + "^\\(#~?[ \t]*\\)?msgid.*\n\\(\\(#~?[ \t]*\\)?\".*\n\\)*" + "Regexp matching a whole msgid field, whether obsolete or not.") + +(defvar po-any-msgstr-regexp + "^\\(#~?[ \t]*\\)?msgstr.*\n\\(\\(#~?[ \t]*\\)?\".*\n\\)*" + "Regexp matching a whole msgstr field, whether obsolete or not.") + +(defvar po-msgfmt-program "msgfmt" + "Path to msgfmt program from GNU gettext package.") + +;; Font lock based highlighting code. +(defconst po-font-lock-keywords + '( + ("^\\(msgid \\|msgstr \\)?\"\\|\"$" . font-lock-keyword-face) + ("\\\\.\\|%[-.0-9ul]*[a-zA-Z]" . font-lock-variable-name-face) + ("^# .*\\|^#[:,]?" . font-lock-comment-face) + ("^#:\\(.*\\)" 1 font-lock-reference-face) + ;; The following line does not work, and I wonder why. + ;;("^#,\\(.*\\)" 1 font-function-name-reference-face) + ) + "Additional expressions to highlight in PO mode.") + +;; Old activator for `font lock'. Is it still useful? I don't think so. +;; +;;(if (boundp 'font-lock-keywords) +;; (put 'po-mode 'font-lock-keywords 'po-font-lock-keywords)) + +;; `hilit19' based highlighting code has been disabled, as most probably +;; nobody really needs it (it also generates ugly byte-compiler warnings). +;; +;;(if (fboundp 'hilit-set-mode-patterns) +;; (hilit-set-mode-patterns 'po-mode +;; '(("^# .*\\|^#$" nil comment) +;; ("^#[.,:].*" nil include) +;; ("^\\(msgid\\|msgstr\\) *\"" nil keyword) +;; ("^\"\\|\"$" nil keyword)))) + +;;; Mode activation. + +(eval-and-compile + (if po-EMACS20 + + (defun po-find-file-coding-system (arg-list) + "Return a Mule (DECODING . ENCODING) pair, according to PO file charset. +Called through file-coding-system-alist, before the file is visited for real." + (and (eq (car arg-list) 'insert-file-contents) + (with-temp-buffer + (let ((coding-system-for-read 'no-conversion)) + ;; Is 4096 enough? FIXME: Retry as needed! + (insert-file-contents (nth 1 arg-list) nil 0 4096) + (if (re-search-forward + "^\"Content-Type: text/plain;[ \t]*charset=\\([^\\]+\\)" + nil t) + (let* ((charset (intern (downcase (buffer-substring + (match-beginning 1) + (match-end 1))))) + (slot (assq charset + po-content-type-charset-alist))) + (list (cond (slot (cdr slot)) + ((memq charset (coding-system-list)) charset) + (t 'no-conversion)))) + '(no-conversion)))))) + + )) + +(defvar po-mode-map nil + "Keymap for PO mode.") +(if po-mode-map + () + ;; The following line because (make-sparse-keymap) does not work on Demacs. + (setq po-mode-map (make-keymap)) + (suppress-keymap po-mode-map) + (define-key po-mode-map "\C-i" 'po-unfuzzy) + (define-key po-mode-map "\C-j" 'po-msgid-to-msgstr) + (define-key po-mode-map "\C-m" 'po-edit-msgstr) + (define-key po-mode-map " " 'po-auto-select-entry) + (define-key po-mode-map "?" 'po-help) + (define-key po-mode-map "#" 'po-edit-comment) + (define-key po-mode-map "," 'po-tags-search) + (define-key po-mode-map "." 'po-current-entry) + (define-key po-mode-map "<" 'po-first-entry) + (define-key po-mode-map "=" 'po-statistics) + (define-key po-mode-map ">" 'po-last-entry) + (define-key po-mode-map "a" 'po-cycle-auxiliary) +;;;; (define-key po-mode-map "c" 'po-save-entry) + (define-key po-mode-map "f" 'po-next-fuzzy-entry) + (define-key po-mode-map "h" 'po-help) + (define-key po-mode-map "k" 'po-kill-msgstr) +;;;; (define-key po-mode-map "l" 'po-lookup-lexicons) + (define-key po-mode-map "m" 'po-push-location) + (define-key po-mode-map "n" 'po-next-entry) + (define-key po-mode-map "o" 'po-next-obsolete-entry) + (define-key po-mode-map "p" 'po-previous-entry) + (define-key po-mode-map "q" 'po-confirm-and-quit) + (define-key po-mode-map "r" 'po-pop-location) + (define-key po-mode-map "s" 'po-cycle-source-reference) + (define-key po-mode-map "t" 'po-next-translated-entry) + (define-key po-mode-map "u" 'po-next-untranslated-entry) + (define-key po-mode-map "v" 'po-mode-version) + (define-key po-mode-map "w" 'po-kill-ring-save-msgstr) + (define-key po-mode-map "x" 'po-exchange-location) + (define-key po-mode-map "y" 'po-yank-msgstr) + (define-key po-mode-map "A" 'po-consider-as-auxiliary) + (define-key po-mode-map "E" 'po-edit-out-full) + (define-key po-mode-map "K" 'po-kill-comment) +;;;; (define-key po-mode-map "L" 'po-consider-lexicon-file) + (define-key po-mode-map "M" 'po-send-mail) + (define-key po-mode-map "O" 'po-other-window) + (define-key po-mode-map "Q" 'po-quit) + (define-key po-mode-map "S" 'po-consider-source-path) + (define-key po-mode-map "U" 'po-undo) + (define-key po-mode-map "V" 'po-validate) + (define-key po-mode-map "W" 'po-kill-ring-save-comment) + (define-key po-mode-map "Y" 'po-yank-comment) + (define-key po-mode-map "\177" 'po-fade-out-entry) + (define-key po-mode-map "\M-," 'po-mark-translatable) + (define-key po-mode-map "\M-." 'po-select-mark-and-mark) + (define-key po-mode-map "\M-a" 'po-select-auxiliary) +;;;; (define-key po-mode-map "\M-c" 'po-select-and-save-entry) + (define-key po-mode-map "\M-f" 'po-previous-fuzzy-entry) +;;;; (define-key po-mode-map "\M-l" 'po-edit-lexicon-entry) + (define-key po-mode-map "\M-o" 'po-previous-obsolete-entry) + (define-key po-mode-map "\M-t" 'po-previous-translated-entry) + (define-key po-mode-map "\M-u" 'po-previous-untranslated-entry) + (define-key po-mode-map "\M-s" 'po-select-source-reference) + (define-key po-mode-map "\M-A" 'po-ignore-as-auxiliary) +;;;; (define-key po-mode-map "\M-L" 'po-ignore-lexicon-file) + (define-key po-mode-map "\M-S" 'po-ignore-source-path) + ) + +(defun po-mode () + "Major mode for translators when they edit PO files. +Special commands:\\{po-mode-map} +Turning on PO mode calls the value of the variable `po-mode-hook', +if that value is non-nil. Behaviour may be adjusted through some variables, +all reachable through `M-x customize', in group `Emacs.Editing.I18n.Po'." + + (interactive) + (kill-all-local-variables) + (setq major-mode 'po-mode + mode-name "PO") + (use-local-map po-mode-map) + (if (fboundp 'easy-menu-define) + (progn + (easy-menu-define po-mode-menu po-mode-map "" po-mode-menu-layout) + (and po-XEMACS (easy-menu-add po-mode-menu)))) + (make-local-variable 'font-lock-defaults) + (setq font-lock-defaults '(po-font-lock-keywords t)) + + (make-local-variable 'po-read-only) + (setq po-read-only buffer-read-only + buffer-read-only t) + + (make-local-variable 'po-start-of-entry) + (make-local-variable 'po-start-of-msgid) + (make-local-variable 'po-start-of-msgstr) + (make-local-variable 'po-end-of-entry) + (make-local-variable 'po-entry-type) + + (make-local-variable 'po-translated-counter) + (make-local-variable 'po-fuzzy-counter) + (make-local-variable 'po-untranslated-counter) + (make-local-variable 'po-obsolete-counter) + (make-local-variable 'po-mode-line-string) + + (setq po-mode-flag t) + + (po-check-file-header) + (po-compute-counters nil) + + (make-local-variable 'po-edited-fields) + (setq po-edited-fields nil) + + (make-local-variable 'po-marker-stack) + (setq po-marker-stack nil) + + (make-local-variable 'po-search-path) + (setq po-search-path '(("./") ("../"))) + + (make-local-variable 'po-reference-alist) + (make-local-variable 'po-reference-cursor) + (make-local-variable 'po-reference-check) + (setq po-reference-alist nil + po-reference-cursor nil + po-reference-check 0) + + (make-local-variable 'po-keywords) + (make-local-variable 'po-next-file-list) + (make-local-variable 'po-string-start) + (make-local-variable 'po-string-end) + (make-local-variable 'po-marking-overlay) + (setq po-keywords '(("gettext") ("gettext_noop") ("_") ("N_")) + po-next-file-list nil + po-string-start nil + po-string-end nil + po-marking-overlay (po-create-overlay)) + + (message (_"You may type `h' or `?' for a short PO mode reminder.")) + (run-hooks 'po-mode-hook)) + +;;; Window management. + +(make-variable-buffer-local 'po-mode-flag) + +(defvar po-mode-line-entry '(po-mode-flag (" " po-mode-line-string)) + "Mode line format entry displaying MODE-LINE-STRING.") + +;; Insert MODE-LINE-ENTRY in mode line, but on first load only. +(or (member po-mode-line-entry mode-line-format) + (let ((entry (member 'global-mode-string mode-line-format))) + (setcdr entry (cons po-mode-line-entry (cdr entry))))) + +(defun po-update-mode-line-string () + "Compute a new statistics string to display in mode line." + (setq po-mode-line-string + (concat (format "%dt" po-translated-counter) + (if (> po-fuzzy-counter 0) + (format "+%df" po-fuzzy-counter)) + (if (> po-untranslated-counter 0) + (format "+%du" po-untranslated-counter)) + (if (> po-obsolete-counter 0) + (format "+%do" po-obsolete-counter)))) + (po-force-mode-line-update)) + +(defun po-type-counter () + "Return the symbol name of the counter appropriate for the current entry." + (cond ((eq po-entry-type 'obsolete) 'po-obsolete-counter) + ((eq po-entry-type 'fuzzy) 'po-fuzzy-counter) + ((eq po-entry-type 'translated) 'po-translated-counter) + ((eq po-entry-type 'untranslated) 'po-untranslated-counter) + (t (error (_"Unknown entry type"))))) + +(defun po-decrease-type-counter () + "Decrease the counter corresponding to the nature of the current entry." + (let ((counter (po-type-counter))) + (set counter (1- (eval counter))))) + +(defun po-increase-type-counter () + "Increase the counter corresponding to the nature of the current entry. +Then, update the mode line counters." + (let ((counter (po-type-counter))) + (set counter (1+ (eval counter)))) + (po-update-mode-line-string)) + +;; Avoid byte compiler warnings. +(defvar po-fuzzy-regexp) +(defvar po-untranslated-regexp) + +(defun po-compute-counters (flag) + "Prepare counters for mode line display. If FLAG, also echo entry position." + (and flag (po-find-span-of-entry)) + (setq po-translated-counter 0 + po-fuzzy-counter 0 + po-untranslated-counter 0 + po-obsolete-counter 0) + (let ((position 0) (total 0) here) + (save-excursion + (goto-char (point-min)) + (while (re-search-forward po-any-msgstr-regexp nil t) + (and (= (% total 20) 0) + (if flag + (message (_"Position %d/%d") position total) + (message (_"Position %d") total))) + (setq here (point)) + (goto-char (match-beginning 0)) + (setq total (1+ total)) + (and flag (eq (point) po-start-of-msgstr) (setq position total)) + (cond ((eq (following-char) ?#) + (setq po-obsolete-counter (1+ po-obsolete-counter))) + ((looking-at po-untranslated-regexp) + (setq po-untranslated-counter (1+ po-untranslated-counter))) + (t (setq po-translated-counter (1+ po-translated-counter)))) + (goto-char here)) + + ;; Make another pass just for the fuzzy entries, kind of kludgey. + ;; FIXME: Counts will be wrong if untranslated entries are fuzzy, yet + ;; this should not normally happen. + (goto-char (point-min)) + (while (re-search-forward po-fuzzy-regexp nil t) + (setq po-fuzzy-counter (1+ po-fuzzy-counter))) + (setq po-translated-counter (- po-translated-counter po-fuzzy-counter))) + + ;; Push the results out. + (if flag + (message (_"\ +Position %d/%d; %d translated, %d fuzzy, %d untranslated, %d obsolete") + position total po-translated-counter po-fuzzy-counter + po-untranslated-counter po-obsolete-counter) + (message ""))) + (po-update-mode-line-string)) + +(defun po-redisplay () + "Redisplay the current entry." + ;; FIXME: Should try to fit the whole entry on the window. If this is not + ;; possible, should try to fit the comment and the msgid. Otherwise, + ;; should try to fit the msgid. Else, the first line of the msgid should + ;; be at the top of the window. + (goto-char po-start-of-msgid)) + +(defun po-other-window () + "Get the cursor into another window, out of PO mode." + (interactive) + (if (one-window-p t) + (progn + (split-window) + (switch-to-buffer (other-buffer))) + (other-window 1))) + +;;; Processing the PO file header entry. + +(defun po-check-file-header () + "Create a missing PO mode file header, or replace an oldish one." + (save-excursion + (let ((buffer-read-only po-read-only) + insert-flag end-of-header) + (goto-char (point-min)) + (if (re-search-forward po-any-msgstr-regexp nil t) + (progn + + ;; There is at least one entry. + (goto-char (match-beginning 0)) + (previous-line 1) + (setq end-of-header (match-end 0)) + (if (looking-at "msgid \"\"\n") + + ;; There is indeed a PO file header. + (if (re-search-forward "\n\"PO-Revision-Date: " + end-of-header t) + nil + + ;; This is an oldish header. Replace it all. + (goto-char end-of-header) + (while (> (point) (point-min)) + (previous-line 1) + (insert "#~ ") + (beginning-of-line)) + (beginning-of-line) + (setq insert-flag t)) + + ;; The first entry is not a PO file header, insert one. + (setq insert-flag t))) + + ;; Not a single entry found. + (setq insert-flag t)) + + (goto-char (point-min)) + (and insert-flag (insert po-default-file-header "\n"))))) + +(defun po-replace-revision-date () + "Replace the revision date by current time in the PO file header." + (if (fboundp 'format-time-string) + (if (or (eq po-auto-replace-revision-date t) + (and (eq po-auto-replace-revision-date 'ask) + (y-or-n-p (_"May I set PO-Revision-Date? ")))) + (save-excursion + (goto-char (point-min)) + (if (re-search-forward "^\"PO-Revision-Date:.*" nil t) + (let* ((buffer-read-only po-read-only) + (time (current-time)) + (seconds (or (car (current-time-zone time)) 0)) + (minutes (/ (abs seconds) 60)) + (zone (format "%c%02d:%02d" + (if (< seconds 0) ?- ?+) + (/ minutes 60) + (% minutes 60)))) + (replace-match + (concat "\"PO-Revision-Date: " + (format-time-string "%Y-%m-%d %H:%M" time) + zone "\\n\"") + t t)))) + (message "")) + (message (_"PO-Revision-Date should be adjusted...")))) + +;;; Handling span of entry, entry type and entry attributes. + +(defun po-find-span-of-entry () + "Find the extent of the PO file entry where the cursor is. Set variables +PO-START-OF-ENTRY, PO-START-OF-MSGID, PO-START-OF-MSGSTR, PO-END-OF-ENTRY +and PO-ENTRY-TYPE to meaningful values. Decreasing priority of type +interpretation is: obsolete, fuzzy, untranslated or translated." + (let ((here (point))) + (if (re-search-backward po-any-msgstr-regexp nil t) + (progn + + ;; After a backward match, (match-end 0) will not extend + ;; beyond point, in case point was *inside* the regexp. We + ;; need a dependable (match-end 0), so we redo the match in + ;; the forward direction. + (re-search-forward po-any-msgstr-regexp) + (if (<= (match-end 0) here) + (progn + + ;; We most probably found the msgstr of the previous + ;; entry. The current entry then starts just after + ;; its end, save this information just in case. + (setq po-start-of-entry (match-end 0)) + + ;; However, it is also possible that we are located in + ;; the crumb after the last entry in the file. If + ;; yes, we know the middle and end of last PO entry. + (setq po-start-of-msgstr (match-beginning 0) + po-end-of-entry (match-end 0)) + + (if (re-search-forward po-any-msgstr-regexp nil t) + (progn + + ;; We definitely were not in the crumb. + (setq po-start-of-msgstr (match-beginning 0) + po-end-of-entry (match-end 0))) + + ;; We were in the crumb. The start of the last PO + ;; file entry is the end of the previous msgstr if + ;; any, or else, the beginning of the file. + (goto-char po-start-of-msgstr) + (setq po-start-of-entry + (if (re-search-backward po-any-msgstr-regexp nil t) + (match-end 0) + (point-min))))) + + ;; The cursor was inside msgstr of the current entry. + (setq po-start-of-msgstr (match-beginning 0) + po-end-of-entry (match-end 0)) + ;; The start of this entry is the end of the previous + ;; msgstr if any, or else, the beginning of the file. + (goto-char po-start-of-msgstr) + (setq po-start-of-entry + (if (re-search-backward po-any-msgstr-regexp nil t) + (match-end 0) + (point-min))))) + + ;; The cursor was before msgstr in the first entry in the file. + (setq po-start-of-entry (point-min)) + (goto-char po-start-of-entry) + ;; There is at least the PO file header, so this should match. + (re-search-forward po-any-msgstr-regexp) + (setq po-start-of-msgstr (match-beginning 0) + po-end-of-entry (match-end 0))) + + ;; Find start of msgid. + (goto-char po-start-of-entry) + (re-search-forward po-any-msgid-regexp) + (setq po-start-of-msgid (match-beginning 0)) + + ;; Classify the entry. + (setq po-entry-type + (if (eq (following-char) ?#) + 'obsolete + (goto-char po-start-of-entry) + (if (re-search-forward po-fuzzy-regexp po-start-of-msgid t) + 'fuzzy + (goto-char po-start-of-msgstr) + (if (looking-at po-untranslated-regexp) + 'untranslated + 'translated)))) + + ;; Put the cursor back where it was. + (goto-char here))) + +(defun po-add-attribute (name) + "Add attribute NAME to the current entry, unless it is already there." + (save-excursion + (let ((buffer-read-only po-read-only)) + (goto-char po-start-of-entry) + (if (re-search-forward "\n#[,!] .*" po-start-of-msgid t) + (save-restriction + (narrow-to-region (match-beginning 0) (match-end 0)) + (goto-char (point-min)) + (if (re-search-forward (concat "\\b" name "\\b") nil t) + nil + (goto-char (point-max)) + (insert ", " name))) + (skip-chars-forward "\n") + (while (eq (following-char) ?#) + (next-line 1)) + (insert "#, " name "\n"))))) + +(defun po-delete-attribute (name) + "Delete attribute NAME from the current entry, if any." + (save-excursion + (let ((buffer-read-only po-read-only)) + (goto-char po-start-of-entry) + (if (re-search-forward "\n#[,!] .*" po-start-of-msgid t) + (save-restriction + (narrow-to-region (match-beginning 0) (match-end 0)) + (goto-char (point-min)) + (if (re-search-forward + (concat "\\(\n#[,!] " name "$\\|, " name "$\\| " name ",\\)") + nil t) + (replace-match "" t t))))))) + +;;; Entry positionning. + +(defun po-say-location-depth () + "Tell how many entries in the entry location stack." + (let ((depth (length po-marker-stack))) + (cond ((= depth 0) (message (_"Empty location stack"))) + ((= depth 1) (message (_"One entry in location stack"))) + (t (message (_"%d entries in location stack") depth))))) + +(defun po-push-location () + "Stack the location of the current entry, for later return." + (interactive) + (po-find-span-of-entry) + (save-excursion + (goto-char po-start-of-msgid) + (setq po-marker-stack (cons (point-marker) po-marker-stack))) + (po-say-location-depth)) + +(defun po-pop-location () + "Unstack a saved location, and return to the corresponding entry." + (interactive) + (if po-marker-stack + (progn + (goto-char (car po-marker-stack)) + (setq po-marker-stack (cdr po-marker-stack)) + (po-current-entry) + (po-say-location-depth)) + (error (_"The entry location stack is empty")))) + +(defun po-exchange-location () + "Exchange the location of the current entry with the top of stack." + (interactive) + (if po-marker-stack + (progn + (po-find-span-of-entry) + (goto-char po-start-of-msgid) + (let ((location (point-marker))) + (goto-char (car po-marker-stack)) + (setq po-marker-stack (cons location (cdr po-marker-stack)))) + (po-current-entry) + (po-say-location-depth)) + (error (_"The entry location stack is empty")))) + +(defun po-current-entry () + "Display the current entry." + (interactive) + (po-find-span-of-entry) + (po-redisplay)) + +(defun po-first-entry-with-regexp (regexp) + "Display the first entry in the file which msgstr matches REGEXP." + (let ((here (point))) + (goto-char (point-min)) + (if (re-search-forward regexp nil t) + (progn + (goto-char (match-beginning 0)) + (po-current-entry)) + (goto-char here) + (error (_"There is no such entry"))))) + +(defun po-last-entry-with-regexp (regexp) + "Display the last entry in the file which msgstr matches REGEXP." + (let ((here (point))) + (goto-char (point-max)) + (if (re-search-backward regexp nil t) + (po-current-entry) + (goto-char here) + (error (_"There is no such entry"))))) + +(defun po-next-entry-with-regexp (regexp wrap) + "Display the entry following the current entry which msgstr matches REGEXP. +If WRAP is not nil, the search may wrap around the buffer." + (po-find-span-of-entry) + (let ((here (point))) + (goto-char po-end-of-entry) + (if (re-search-forward regexp nil t) + (progn + (goto-char (match-beginning 0)) + (po-current-entry)) + (if (and wrap + (progn + (goto-char (point-min)) + (re-search-forward regexp po-start-of-entry t))) + (progn + (goto-char (match-beginning 0)) + (po-current-entry) + (message (_"Wrapping around the buffer"))) + (goto-char here) + (error (_"There is no such entry")))))) + +(defun po-previous-entry-with-regexp (regexp wrap) + "Redisplay the entry preceding the current entry which msgstr matches REGEXP. +If WRAP is not nil, the search may wrap around the buffer." + (po-find-span-of-entry) + (let ((here (point))) + (goto-char po-start-of-entry) + (if (re-search-backward regexp nil t) + (po-current-entry) + (if (and wrap + (progn + (goto-char (point-max)) + (re-search-backward regexp po-end-of-entry t))) + (progn + (po-current-entry) + (message (_"Wrapping around the buffer"))) + (goto-char here) + (error (_"There is no such entry")))))) + +;; Any entries. + +(defun po-first-entry () + "Display the first entry." + (interactive) + (po-first-entry-with-regexp po-any-msgstr-regexp)) + +(defun po-last-entry () + "Display the last entry." + (interactive) + (po-last-entry-with-regexp po-any-msgstr-regexp)) + +(defun po-next-entry () + "Display the entry following the current entry." + (interactive) + (po-next-entry-with-regexp po-any-msgstr-regexp nil)) + +(defun po-previous-entry () + "Display the entry preceding the current entry." + (interactive) + (po-previous-entry-with-regexp po-any-msgstr-regexp nil)) + +;; Untranslated entries. + +(defvar po-after-entry-regexp + "\\(\\'\\|\\(#[ \t]*\\)?[^\"]\\)" + "Regexp which should be true after a full msgstr string matched.") + +(defvar po-untranslated-regexp + (concat "^msgstr[ \t]*\"\"\n" po-after-entry-regexp) + "Regexp matching a whole msgstr field, but only if active and empty.") + +(defun po-next-untranslated-entry () + "Find the next untranslated entry, wrapping around if necessary." + (interactive) + (po-next-entry-with-regexp po-untranslated-regexp t)) + +(defun po-previous-untranslated-entry () + "Find the previous untranslated entry, wrapping around if necessary." + (interactive) + (po-previous-entry-with-regexp po-untranslated-regexp t)) + +(defun po-msgid-to-msgstr () + "Use another window to edit msgstr reinitialized with msgid." + (interactive) + (po-find-span-of-entry) + (if (or (eq po-entry-type 'untranslated) + (eq po-entry-type 'obsolete) + (y-or-n-p (_"Really loose previous translation? "))) + (po-set-msgstr (po-get-msgid nil))) + (message "")) + +;; Obsolete entries. + +(defvar po-obsolete-msgstr-regexp + "^#~?[ \t]*msgstr.*\n\\(#~?[ \t]*\".*\n\\)*" + "Regexp matching a whole msgstr field of an obsolete entry.") + +(defun po-next-obsolete-entry () + "Find the next obsolete entry, wrapping around if necessary." + (interactive) + (po-next-entry-with-regexp po-obsolete-msgstr-regexp t)) + +(defun po-previous-obsolete-entry () + "Find the previous obsolete entry, wrapping around if necessary." + (interactive) + (po-previous-entry-with-regexp po-obsolete-msgstr-regexp t)) + +;; Fuzzy entries. + +(defvar po-fuzzy-regexp "^#[,!] .*fuzzy" + "Regexp matching the string inserted by msgmerge for translations +which does not match exactly.") + +(defun po-next-fuzzy-entry () + "Find the next fuzzy entry, wrapping around if necessary." + (interactive) + (po-next-entry-with-regexp po-fuzzy-regexp t)) + +(defun po-previous-fuzzy-entry () + "Find the next fuzzy entry, wrapping around if necessary." + (interactive) + (po-previous-entry-with-regexp po-fuzzy-regexp t)) + +(defun po-unfuzzy () + "Remove the fuzzy attribute for the current entry." + (interactive) + (po-find-span-of-entry) + (cond ((eq po-entry-type 'fuzzy) + (po-decrease-type-counter) + (po-delete-attribute "fuzzy") + (po-current-entry) + (po-increase-type-counter))) + (if po-auto-select-on-unfuzzy + (po-auto-select-entry)) + (po-update-mode-line-string)) + +;; Translated entries. + +(defun po-next-translated-entry () + "Find the next untranslated entry, wrapping around if necessary." + (interactive) + (if (= po-translated-counter 0) + (error (_"There is no such entry")) + (po-next-entry-with-regexp po-untranslated-regexp t) + (po-find-span-of-entry) + (while (not (eq po-entry-type 'translated)) + (po-next-entry-with-regexp po-any-msgstr-regexp t) + (po-find-span-of-entry)))) + +(defun po-previous-translated-entry () + "Find the previous untranslated entry, wrapping around if necessary." + (interactive) + (if (= po-translated-counter 0) + (error (_"There is no such entry")) + (po-previous-entry-with-regexp po-any-msgstr-regexp t) + (po-find-span-of-entry) + (while (not (eq po-entry-type 'translated)) + (po-previous-entry-with-regexp po-untranslated-regexp t) + (po-find-span-of-entry)))) + +;; Auto-selection feature. + +(defun po-auto-select-entry () + "Select the next entry having the same type as the current one. +If none, wrap from the beginning of the buffer with another type, +going from untranslated to fuzzy, and from fuzzy to obsolete. +Plain translated entries are always disregarded unless there are +no entries of the other types." + (interactive) + (po-find-span-of-entry) + (goto-char po-end-of-entry) + (if (and (= po-untranslated-counter 0) + (= po-fuzzy-counter 0) + (= po-obsolete-counter 0)) + + ;; All entries are plain translated. Next entry will do, or + ;; wrap around if there is none. + (if (re-search-forward po-any-msgstr-regexp nil t) + (goto-char (match-beginning 0)) + (goto-char (point-min))) + + ;; If over a translated entry, look for an untranslated one first. + ;; Else, look for an entry of the same type first. + (let ((goal (if (eq po-entry-type 'translated) + 'untranslated + po-entry-type))) + (while goal + + ;; Find an untranslated entry, or wrap up for a fuzzy entry. + (if (eq goal 'untranslated) + (if (and (> po-untranslated-counter 0) + (re-search-forward po-untranslated-regexp nil t)) + (progn + (goto-char (match-beginning 0)) + (setq goal nil)) + (goto-char (point-min)) + (setq goal 'fuzzy))) + + ;; Find a fuzzy entry, or wrap up for an obsolete entry. + (if (eq goal 'fuzzy) + (if (and (> po-fuzzy-counter 0) + (re-search-forward po-fuzzy-regexp nil t)) + (progn + (goto-char (match-beginning 0)) + (setq goal nil)) + (goto-char (point-min)) + (setq goal 'obsolete))) + + ;; Find an obsolete entry, or wrap up for an untranslated entry. + (if (eq goal 'obsolete) + (if (and (> po-obsolete-counter 0) + (re-search-forward po-obsolete-msgstr-regexp nil t)) + (progn + (goto-char (match-beginning 0)) + (setq goal nil)) + (goto-char (point-min)) + (setq goal 'untranslated)))))) + + ;; Display this entry nicely. + (po-current-entry)) + +;;; Killing and yanking fields. + +(defun po-extract-unquoted (buffer start end) + "Extract and return the unquoted string in BUFFER going from START to END. +Crumb preceding or following the quoted string is ignored." + (po-with-temp-buffer + (insert-buffer-substring buffer start end) + ;; Remove preceding crumb. + (goto-char (point-min)) + (search-forward "\"") + (delete-region (point-min) (point)) + ;; Remove following crumb. + (goto-char (point-max)) + (search-backward "\"") + (delete-region (point) (point-max)) + ;; Glue concatenated strings. + (goto-char (point-min)) + (while (re-search-forward "\"[ \t]*\\\\?\n\\(#~?\\)?[ \t]*\"" nil t) + (replace-match "" t t)) + ;; Remove escaped newlines. + (goto-char (point-min)) + (while (re-search-forward "\\\\[ \t]*\n" nil t) + (replace-match "" t t)) + ;; Unquote individual characters. + (goto-char (point-min)) + (while (re-search-forward "\\\\[\"abfnt\\0-7]" nil t) + (cond ((eq (preceding-char) ?\") (replace-match "\"" t t)) + ((eq (preceding-char) ?a) (replace-match "\a" t t)) + ((eq (preceding-char) ?b) (replace-match "\b" t t)) + ((eq (preceding-char) ?f) (replace-match "\f" t t)) + ((eq (preceding-char) ?n) (replace-match "\n" t t)) + ((eq (preceding-char) ?t) (replace-match "\t" t t)) + ((eq (preceding-char) ?\\) (replace-match "\\" t t)) + (t (let ((value (- (preceding-char) ?0))) + (replace-match "" t t) + (while (looking-at "[0-7]") + (setq value (+ (* 8 value) (- (following-char) ?0))) + (replace-match "" t t)) + (insert value))))) + (buffer-string))) + +(defun po-eval-requoted (form prefix obsolete) + "Eval FORM, which inserts a string, and return the string fully requoted. +If PREFIX, precede the result with its contents. If OBSOLETE, comment all +generated lines in the returned string. Evaluating FORM should insert the +wanted string in the buffer which is current at the time of evaluation. +If FORM is itself a string, then this string is used for insertion." + (po-with-temp-buffer + (if (stringp form) + (insert form) + (push-mark) + (eval form)) + (goto-char (point-min)) + (let ((multi-line (re-search-forward "[^\n]\n+[^\n]" nil t))) + (goto-char (point-min)) + (while (re-search-forward "[\"\a\b\f\n\t\\]" nil t) + (cond ((eq (preceding-char) ?\") (replace-match "\\\"" t t)) + ((eq (preceding-char) ?\a) (replace-match "\\a" t t)) + ((eq (preceding-char) ?\b) (replace-match "\\b" t t)) + ((eq (preceding-char) ?\f) (replace-match "\\f" t t)) + ((eq (preceding-char) ?\n) + (replace-match (if (or (not multi-line) (eobp)) + "\\n" + "\\n\"\n\"") + t t)) + ((eq (preceding-char) ?\t) (replace-match "\\t" t t)) + ((eq (preceding-char) ?\\) (replace-match "\\\\" t t)))) + (goto-char (point-min)) + (if prefix (insert prefix " ")) + (insert (if multi-line "\"\"\n\"" "\"")) + (goto-char (point-max)) + (insert "\"") + (if prefix (insert "\n")) + (if obsolete + (progn + (goto-char (point-min)) + (while (not (eobp)) + (or (eq (following-char) ?\n) (insert "#~ ")) + (search-forward "\n")))) + (buffer-string)))) + +(defun po-get-msgid (kill) + "Extract and return the unquoted msgid string. +If KILL, then add the unquoted string to the kill ring." + (let ((string (po-extract-unquoted (current-buffer) + po-start-of-msgid po-start-of-msgstr))) + (if kill (po-kill-new string)) + string)) + +(defun po-get-msgstr (kill) + "Extract and return the unquoted msgstr string. +If KILL, then add the unquoted string to the kill ring." + (let ((string (po-extract-unquoted (current-buffer) + po-start-of-msgstr po-end-of-entry))) + (if kill (po-kill-new string)) + string)) + +(defun po-set-msgid (form) + "Replace the current msgid, using FORM to get a string. +Evaluating FORM should insert the wanted string in the current buffer. If +FORM is itself a string, then this string is used for insertion. The string +is properly requoted before the replacement occurs. + +Returns `nil' if the buffer has not been modified, for if the new msgid +described by FORM is merely identical to the msgid already in place." + (let ((string (po-eval-requoted form "msgid" (eq po-entry-type 'obsolete)))) + (save-excursion + (goto-char po-start-of-entry) + (re-search-forward po-any-msgid-regexp po-start-of-msgstr) + (and (not (string-equal (po-buffer-substring (match-beginning 0) + (match-end 0)) + string)) + (let ((buffer-read-only po-read-only)) + (replace-match string t t) + (goto-char po-start-of-msgid) + (po-find-span-of-entry) + t))))) + +(defun po-set-msgstr (form) + "Replace the current msgstr, using FORM to get a string. +Evaluating FORM should insert the wanted string in the current buffer. If +FORM is itself a string, then this string is used for insertion. The string +is properly requoted before the replacement occurs. + +Returns `nil' if the buffer has not been modified, for if the new msgstr +described by FORM is merely identical to the msgstr already in place." + (let ((string (po-eval-requoted form "msgstr" (eq po-entry-type 'obsolete)))) + (save-excursion + (goto-char po-start-of-entry) + (re-search-forward po-any-msgstr-regexp po-end-of-entry) + (and (not (string-equal (po-buffer-substring (match-beginning 0) + (match-end 0)) + string)) + (let ((buffer-read-only po-read-only)) + (po-decrease-type-counter) + (replace-match string t t) + (goto-char po-start-of-msgid) + (po-find-span-of-entry) + (po-increase-type-counter) + t))))) + +(defun po-kill-ring-save-msgstr () + "Push the msgstr string from current entry on the kill ring." + (interactive) + (po-find-span-of-entry) + (po-get-msgstr t)) + +(defun po-kill-msgstr () + "Empty the msgstr string from current entry, pushing it on the kill ring." + (interactive) + (po-kill-ring-save-msgstr) + (po-set-msgstr "")) + +(defun po-yank-msgstr () + "Replace the current msgstr string by the top of the kill ring." + (interactive) + (po-find-span-of-entry) + (po-set-msgstr (if (eq last-command 'yank) '(yank-pop 1) '(yank))) + (setq this-command 'yank)) + +(defun po-fade-out-entry () + "Mark an active entry as fuzzy; obsolete a fuzzy or untranslated entry; +or completely delete an obsolete entry, saving its msgstr on the kill ring." + (interactive) + (po-find-span-of-entry) + + (cond ((eq po-entry-type 'translated) + (po-decrease-type-counter) + (po-add-attribute "fuzzy") + (po-current-entry) + (po-increase-type-counter)) + + ((or (eq po-entry-type 'fuzzy) + (eq po-entry-type 'untranslated)) + (if (yes-or-no-p (_"Should I really obsolete this entry? ")) + (progn + (po-decrease-type-counter) + (save-excursion + (save-restriction + (narrow-to-region po-start-of-entry po-end-of-entry) + (let ((buffer-read-only po-read-only)) + (goto-char (point-min)) + (skip-chars-forward "\n") + (while (not (eobp)) + (insert "#~ ") + (search-forward "\n"))))) + (po-current-entry) + (po-increase-type-counter))) + (message "")) + + ((and (eq po-entry-type 'obsolete) + (po-check-for-pending-edit po-start-of-msgid) + (po-check-for-pending-edit po-start-of-msgstr)) + (po-decrease-type-counter) + (po-update-mode-line-string) + (po-get-msgstr t) + (let ((buffer-read-only po-read-only)) + (delete-region po-start-of-entry po-end-of-entry)) + (goto-char po-start-of-entry) + (if (re-search-forward po-any-msgstr-regexp nil t) + (goto-char (match-beginning 0)) + (re-search-backward po-any-msgstr-regexp nil t)) + (po-current-entry) + (message "")))) + +;;; Killing and yanking comments. + +(defvar po-active-comment-regexp + "^\\(#\n\\|# .*\n\\)+" + "Regexp matching the whole editable comment part of an active entry.") + +(defvar po-obsolete-comment-regexp + "^\\(#~? #\n\\|#~? # .*\n\\)+" + "Regexp matching the whole editable comment part of an obsolete entry.") + +(defun po-get-comment (kill-flag) + "Extract and return the editable comment string, uncommented. +If KILL-FLAG, then add the unquoted comment to the kill ring." + (let ((buffer (current-buffer)) + (obsolete (eq po-entry-type 'obsolete))) + (save-excursion + (goto-char po-start-of-entry) + (if (re-search-forward (if obsolete po-obsolete-comment-regexp + po-active-comment-regexp) + po-end-of-entry t) + (po-with-temp-buffer + (insert-buffer-substring buffer (match-beginning 0) (match-end 0)) + (goto-char (point-min)) + (while (not (eobp)) + (if (looking-at (if obsolete "#~? # ?" "#~? ?")) + (replace-match "" t t)) + (forward-line 1)) + (and kill-flag (copy-region-as-kill (point-min) (point-max))) + (buffer-string)) + "")))) + +(defun po-set-comment (form) + "Using FORM to get a string, replace the current editable comment. +Evaluating FORM should insert the wanted string in the current buffer. +If FORM is itself a string, then this string is used for insertion. +The string is properly recommented before the replacement occurs." + (let ((obsolete (eq po-entry-type 'obsolete)) + string) + (po-with-temp-buffer + (if (stringp form) + (insert form) + (push-mark) + (eval form)) + (if (not (or (bobp) (= (preceding-char) ?\n))) + (insert "\n")) + (goto-char (point-min)) + (while (not (eobp)) + (insert (if (= (following-char) ?\n) + (if obsolete "#~ #" "#") + (if obsolete "#~ # " "# "))) + (search-forward "\n")) + (setq string (buffer-string))) + (goto-char po-start-of-entry) + (if (re-search-forward + (if obsolete po-obsolete-comment-regexp po-active-comment-regexp) + po-end-of-entry t) + (if (not (string-equal (po-buffer-substring (match-beginning 0) + (match-end 0)) + string)) + (let ((buffer-read-only po-read-only)) + (replace-match string t t))) + (skip-chars-forward " \t\n") + (let ((buffer-read-only po-read-only)) + (insert string)))) + (po-current-entry)) + +(defun po-kill-ring-save-comment () + "Push the msgstr string from current entry on the kill ring." + (interactive) + (po-find-span-of-entry) + (po-get-comment t)) + +(defun po-kill-comment () + "Empty the msgstr string from current entry, pushing it on the kill ring." + (interactive) + (po-kill-ring-save-comment) + (po-set-comment "") + (po-redisplay)) + +(defun po-yank-comment () + "Replace the current comment string by the top of the kill ring." + (interactive) + (po-find-span-of-entry) + (po-set-comment (if (eq last-command 'yank) '(yank-pop 1) '(yank))) + (setq this-command 'yank) + (po-redisplay)) + +;;; Editing management and submode. + +;; In a string edit buffer, BACK-POINTER points to one of the slots of the +;; list EDITED-FIELDS kept in the PO buffer. See its description elsewhere. +;; Reminder: slots have the form (ENTRY-MARKER EDIT-BUFFER OVERLAY-INFO). + +(defvar po-subedit-back-pointer) + +(defun po-clean-out-killed-edits () + "From EDITED-FIELDS, clean out any edit having a killed edit buffer." + (let ((cursor po-edited-fields)) + (while cursor + (let ((slot (car cursor))) + (setq cursor (cdr cursor)) + (if (buffer-name (nth 1 slot)) + nil + (let ((overlay (nth 2 slot))) + (and overlay (po-dehighlight overlay))) + (setq po-edited-fields (delete slot po-edited-fields))))))) + +(defun po-check-all-pending-edits () + "Resume any pending edit. Return nil if some remains." + (po-clean-out-killed-edits) + (or (null po-edited-fields) + (let ((slot (car po-edited-fields))) + (goto-char (nth 0 slot)) + (pop-to-buffer (nth 1 slot)) + (let ((overlay (nth 2 slot))) + (and overlay (po-rehighlight overlay))) + (message po-subedit-message) + nil))) + +(defun po-check-for-pending-edit (position) + "Resume any pending edit at POSITION. Return nil if such edit exists." + (po-clean-out-killed-edits) + (let ((marker (make-marker))) + (set-marker marker position) + (let ((slot (assoc marker po-edited-fields))) + (if slot + (progn + (goto-char marker) + (pop-to-buffer (nth 1 slot)) + (let ((overlay (nth 2 slot))) + (and overlay (po-rehighlight overlay))) + (message po-subedit-message))) + (not slot)))) + +(defun po-edit-out-full () + "Get out of PO mode, leaving PO file buffer in fundamental mode." + (interactive) + (if (and (po-check-all-pending-edits) + (yes-or-no-p (_"Should I let you edit the whole PO file? "))) + (progn + (setq buffer-read-only po-read-only) + (fundamental-mode) + (message (_"Type `M-x po-mode RET' once done"))))) + +(defvar po-subedit-mode-map nil + "Keymap while editing a PO mode entry (or the full PO file).") +(if po-subedit-mode-map + () + (setq po-subedit-mode-map (make-sparse-keymap)) + (define-key po-subedit-mode-map "\C-c\C-a" 'po-subedit-cycle-auxiliary) + (define-key po-subedit-mode-map "\C-c\C-c" 'po-subedit-exit) + (define-key po-subedit-mode-map "\C-c\C-k" 'po-subedit-abort)) + +(defun po-subedit-abort () + "Exit the subedit buffer, merely discarding its contents." + (interactive) + (let* ((edit-buffer (current-buffer)) + (back-pointer po-subedit-back-pointer) + (marker (nth 0 back-pointer)) + (overlay (nth 2 back-pointer)) + (buffer (marker-buffer marker))) + (if (null buffer) + (error (_"Corresponding PO buffer does not exist anymore")) + (or (one-window-p) (delete-window)) + (switch-to-buffer buffer) + (goto-char marker) + (and overlay (po-dehighlight overlay)) + (kill-buffer edit-buffer) + (setq po-edited-fields (delete back-pointer po-edited-fields))))) + +(defun po-subedit-exit () + "Exit the subedit buffer, replacing the string in the PO buffer." + (interactive) + (goto-char (point-max)) + (skip-chars-backward " \t\n") + (if (eq (preceding-char) ?<) + (delete-region (1- (point)) (point-max))) + (let ((string (buffer-string))) + (po-subedit-abort) + (po-find-span-of-entry) + (cond ((= (point) po-start-of-msgid) + (po-set-comment string) + (po-redisplay)) + ((= (point) po-start-of-msgstr) + (let ((replaced (po-set-msgstr string))) + (if (and replaced + po-auto-fuzzy-on-edit + (eq po-entry-type 'translated)) + (progn + (po-decrease-type-counter) + (po-add-attribute "fuzzy") + (po-current-entry) + (po-increase-type-counter))))) + (t (debug))))) + +(defun po-edit-string (string type expand-tabs) + "Prepare a pop up buffer for editing STRING, which is of a given TYPE. +TYPE may be 'comment or 'msgstr. If EXPAND-TABS, expand tabs to spaces. +Run functions on po-subedit-mode-hook." + (let ((marker (make-marker))) + (set-marker marker (cond ((eq type 'comment) po-start-of-msgid) + ((eq type 'msgstr) po-start-of-msgstr))) + (if (po-check-for-pending-edit marker) + (let ((edit-buffer (generate-new-buffer + (concat "*" (buffer-name) "*"))) + (buffer (current-buffer)) + overlay slot) + (if (and (eq type 'msgstr) po-highlighting) + ;; ;; Try showing all of msgid in the upper window while editing. + ;; (goto-char (1- po-start-of-msgstr)) + ;; (recenter -1) + (save-excursion + (goto-char po-start-of-entry) + (re-search-forward po-any-msgid-regexp nil t) + (let ((end (1- (match-end 0)))) + (goto-char (match-beginning 0)) + (re-search-forward "msgid +" nil t) + (setq overlay (po-create-overlay)) + (po-highlight overlay (point) end buffer)))) + (setq slot (list marker edit-buffer overlay) + po-edited-fields (cons slot po-edited-fields)) + (pop-to-buffer edit-buffer) + (make-local-variable 'po-subedit-back-pointer) + (setq po-subedit-back-pointer slot) + (erase-buffer) + (insert string "<") + (goto-char (point-min)) + (and expand-tabs (setq indent-tabs-mode nil)) + (use-local-map po-subedit-mode-map) + (run-hooks 'po-subedit-mode-hook) + (message po-subedit-message))))) + +(defun po-edit-comment () + "Use another window to edit the current translator comment." + (interactive) + (po-find-span-of-entry) + (po-edit-string (po-get-comment nil) 'comment nil)) + +(defun po-edit-msgstr () + "Use another window to edit the current msgstr." + (interactive) + (po-find-span-of-entry) + (po-edit-string (if (and po-auto-edit-with-msgid + (eq po-entry-type 'untranslated)) + (po-get-msgid nil) + (po-get-msgstr nil)) + 'msgstr + t)) + +;;; String normalization and searching. + +(defun po-normalize-old-style (explain) + "Normalize old gettext style fields using K&R C multiline string syntax. +To minibuffer messages sent while normalizing, add the EXPLAIN string." + (let ((here (point-marker)) + (counter 0) + (buffer-read-only po-read-only)) + (goto-char (point-min)) + (message (_"Normalizing %d, %s") counter explain) + (while (re-search-forward + "\\(^#?[ \t]*msg\\(id\\|str\\)[ \t]*\"\\|[^\" \t][ \t]*\\)\\\\\n" + nil t) + (if (= (% counter 10) 0) + (message (_"Normalizing %d, %s") counter explain)) + (replace-match "\\1\"\n\"" t nil) + (setq counter (1+ counter))) + (goto-char here) + (message (_"Normalizing %d...done") counter))) + +(defun po-normalize-field (field explain) + "Normalize FIELD of all entries. FIELD is either the symbol msgid or msgstr. +To minibuffer messages sent while normalizing, add the EXPLAIN string." + (let ((here (point-marker)) + (counter 0)) + (goto-char (point-min)) + (while (re-search-forward po-any-msgstr-regexp nil t) + (if (= (% counter 10) 0) + (message (_"Normalizing %d, %s") counter explain)) + (goto-char (match-beginning 0)) + (po-find-span-of-entry) + (cond ((eq field 'msgid) (po-set-msgid (po-get-msgid nil))) + ((eq field 'msgstr) (po-set-msgstr (po-get-msgstr nil)))) + (goto-char po-end-of-entry) + (setq counter (1+ counter))) + (goto-char here) + (message (_"Normalizing %d...done") counter))) + +;; Normalize, but the British way! :-) +(defsubst po-normalise () (po-normalize)) + +(defun po-normalize () + "Normalize all entries in the PO file." + (interactive) + (po-normalize-old-style (_"pass 1/3")) + (po-normalize-field t (_"pass 2/3")) + (po-normalize-field nil (_"pass 3/3")) + ;; The last PO file entry has just been processed. + (if (not (= po-end-of-entry (point-max))) + (let ((buffer-read-only po-read-only)) + (kill-region po-end-of-entry (point-max)))) + ;; A bizarre format might have fooled the counters, so recompute + ;; them to make sure their value is dependable. + (po-compute-counters nil)) + +;;; Multiple PO files. + +(defun po-show-auxiliary-list () + "Echo the current auxiliary list in the message area." + (if po-auxiliary-list + (let ((cursor po-auxiliary-cursor) + string) + (while cursor + (setq string (concat string (if string " ") (car (car cursor))) + cursor (cdr cursor))) + (setq cursor po-auxiliary-list) + (while (not (eq cursor po-auxiliary-cursor)) + (setq string (concat string (if string " ") (car (car cursor))) + cursor (cdr cursor))) + (message string)) + (message (_"No auxiliary files.")))) + +(defun po-consider-as-auxiliary () + "Add the current PO file to the list of auxiliary files." + (interactive) + (if (member (list buffer-file-name) po-auxiliary-list) + nil + (setq po-auxiliary-list + (nconc po-auxiliary-list (list (list buffer-file-name)))) + (or po-auxiliary-cursor + (setq po-auxiliary-cursor po-auxiliary-list))) + (po-show-auxiliary-list)) + +(defun po-ignore-as-auxiliary () + "Delete the current PO file from the list of auxiliary files." + (interactive) + (setq po-auxiliary-list (delete (list buffer-file-name) po-auxiliary-list) + po-auxiliary-cursor po-auxiliary-list) + (po-show-auxiliary-list)) + +(defun po-seek-equivalent-translation (name string) + "Search a PO file NAME for a `msgid' STRING having a non-empty `msgstr'. +STRING is the full quoted msgid field, including the `msgid' keyword. When +found, display the file over the current window, with the `msgstr' field +possibly highlighted, the cursor at start of msgid, then return `t'. +Otherwise, move nothing, and just return `nil'." + (let ((current (current-buffer)) + (buffer (find-file-noselect name))) + (set-buffer buffer) + (let ((start (point)) + found) + (goto-char (point-min)) + (while (and (not found) (search-forward string nil t)) + ;; Screen out longer `msgid's. + (if (looking-at "^msgstr ") + (progn + (po-find-span-of-entry) + ;; Ignore an untranslated entry. + (or (string-equal + (buffer-substring po-start-of-msgstr po-end-of-entry) + "msgstr \"\"\n") + (setq found t))))) + (if found + (progn + (switch-to-buffer buffer) + (po-find-span-of-entry) + (if po-highlighting + (progn + (goto-char po-start-of-entry) + (re-search-forward po-any-msgstr-regexp nil t) + (let ((end (1- (match-end 0)))) + (goto-char (match-beginning 0)) + (re-search-forward "msgstr +" nil t) + ;; FIXME: + (po-highlight (po-create-overlay) (point) end)))) + (goto-char po-start-of-msgid)) + (goto-char start) + (po-find-span-of-entry) + (select-buffer current)) + found))) + +(defun po-cycle-auxiliary () + "Select the next auxiliary file having an entry with same `msgid'." + (interactive) + (po-find-span-of-entry) + (if po-auxiliary-list + (let ((string (buffer-substring po-start-of-msgid po-start-of-msgstr)) + (cursor po-auxiliary-cursor) + found name) + (while (and (not found) cursor) + (setq name (car (car cursor))) + (if (and (not (string-equal buffer-file-name name)) + (po-seek-equivalent-translation name string)) + (setq found t + po-auxiliary-cursor cursor)) + (setq cursor (cdr cursor))) + (setq cursor po-auxiliary-list) + (while (and (not found) cursor) + (setq name (car (car cursor))) + (if (and (not (string-equal buffer-file-name name)) + (po-seek-equivalent-translation name string)) + (setq found t + po-auxiliary-cursor cursor)) + (setq cursor (cdr cursor))) + (or found (message (_"No other translation found"))) + found))) + +(defun po-subedit-cycle-auxiliary () + "Cycle auxiliary file, but from the translation edit buffer." + (interactive) + (if po-buffer-of-edited-entry + (let ((buffer (current-buffer))) + (pop-to-buffer po-buffer-of-edited-entry) + (po-cycle-auxiliary) + (pop-to-buffer buffer)) + (error (_"Not editing a PO file entry")))) + +(defun po-select-auxiliary () + "Select one of the available auxiliary files and locate an equivalent +entry. If an entry having the same `msgid' cannot be found, merely select +the file without moving its cursor." + (interactive) + (po-find-span-of-entry) + (if po-auxiliary-list + (let ((string (buffer-substring po-start-of-msgid po-start-of-msgstr)) + (name (car (assoc (completing-read (_"Which auxiliary file? ") + po-auxiliary-list nil t) + po-auxiliary-list)))) + (po-consider-as-auxiliary) + (or (po-seek-equivalent-translation name string) + (find-file name))))) + +;;; Original program sources as context. + +(defun po-show-source-path () + "Echo the current source search path in the message area." + (if po-search-path + (let ((cursor po-search-path) + string) + (while cursor + (setq string (concat string (if string " ") (car (car cursor))) + cursor (cdr cursor))) + (message string)) + (message (_"Empty source path.")))) + +(defun po-consider-source-path (directory) + "Add a given DIRECTORY, requested interactively, to the source search path." + (interactive "DDirectory for search path: ") + (setq po-search-path (cons (list (if (string-match "/$" directory) + directory + (concat directory "/"))) + po-search-path)) + (setq po-reference-check 0) + (po-show-source-path)) + +(defun po-ignore-source-path () + "Delete a directory, selected with completion, from the source search path." + (interactive) + (setq po-search-path + (delete (list (completing-read (_"Directory to remove? ") + po-search-path nil t)) + po-search-path)) + (setq po-reference-check 0) + (po-show-source-path)) + +(defun po-ensure-source-references () + "Extract all references into a list, with paths resolved, if necessary." + (po-find-span-of-entry) + (if (= po-start-of-entry po-reference-check) + () + (setq po-reference-alist nil) + (save-excursion + (goto-char po-start-of-entry) + (if (re-search-forward "^#:" po-start-of-msgid t) + (while (looking-at "\\(\n#:\\)? *\\([^: ]+\\):\\([0-9]+\\)") + (goto-char (match-end 0)) + (let* ((name (po-buffer-substring (match-beginning 2) + (match-end 2))) + (line (po-buffer-substring (match-beginning 3) + (match-end 3))) + (path po-search-path) + file) + (while (and (progn (setq file (concat (car (car path)) name)) + (not (file-exists-p file))) + path) + (setq path (cdr path))) + (if path + (setq po-reference-alist + (cons (list (concat file ":" line) + file + (string-to-int line)) + po-reference-alist))))))) + (setq po-reference-alist (nreverse po-reference-alist) + po-reference-cursor po-reference-alist + po-reference-check po-start-of-entry))) + +(defun po-show-source-context (triplet) + "Show the source context given a TRIPLET which is (PROMPT FILE LINE)." + (find-file-other-window (car (cdr triplet))) + (goto-line (car (cdr (cdr triplet)))) + (other-window 1) + (let ((maximum 0) + position + (cursor po-reference-alist)) + (while (not (eq triplet (car cursor))) + (setq maximum (1+ maximum) + cursor (cdr cursor))) + (setq position (1+ maximum) + po-reference-cursor cursor) + (while cursor + (setq maximum (1+ maximum) + cursor (cdr cursor))) + (message (_"Displaying %d/%d: \"%s\"") position maximum (car triplet)))) + +(defun po-cycle-source-reference () + "Display some source context for the current entry. +If the command is repeated many times in a row, cycle through contexts." + (interactive) + (po-ensure-source-references) + (if po-reference-cursor + (po-show-source-context + (car (if (eq last-command 'po-cycle-source-reference) + (or (cdr po-reference-cursor) po-reference-alist) + po-reference-cursor))) + (error (_"No resolved source references")))) + +(defun po-select-source-reference () + "Select one of the available source contexts for the current entry." + (interactive) + (po-ensure-source-references) + (if po-reference-alist + (po-show-source-context + (assoc + (completing-read (_"Which source context? ") po-reference-alist nil t) + po-reference-alist)) + (error (_"No resolved source references")))) + +;;; Program sources strings though tags table. + +;;; C mode. + +;;; A few long string cases (submitted by Ben Pfaff). + +;; #define string "This is a long string " \ +;; "that is continued across several lines " \ +;; "in a macro in order to test \\ quoting\\" \ +;; "\\ with goofy strings.\\" + +;; char *x = "This is just an ordinary string " +;; "continued across several lines without needing " +;; "to use \\ characters at end-of-line."; + +;; char *y = "Here is a string continued across \ +;; several lines in the manner that was sanctioned \ +;; in K&R C compilers and still works today, \ +;; even though the method used above is more esthetic."; + +;;; End of long string cases. + +(defun po-find-c-string (keywords) + "Find the next C string, excluding those marked by any of KEYWORDS. +Returns (START . END) for the found string, or (nil . nil) if none found." + (let (start end) + (while (and (not start) + (re-search-forward "\\([\"']\\|/\\*\\|//\\)" nil t)) + (cond ((= (preceding-char) ?*) + ;; Disregard comments. + (search-forward "*/")) + + ((= (preceding-char) ?/) + ;; Disregard C++ comments. + (end-of-line) + (forward-char 1)) + + ((= (preceding-char) ?\') + ;; Disregard character constants. + (forward-char (if (= (following-char) ?\\) 3 2))) + + ((save-excursion + (beginning-of-line) + (looking-at "^# *\\(include\\|line\\)")) + ;; Disregard lines being #include or #line directives. + (end-of-line)) + + ;; Else, find the end of the (possibly concatenated) string. + (t (setq start (1- (point)) + end nil) + (while (not end) + (cond ((= (following-char) ?\") + (if (looking-at "\"[ \t\n\\\\]*\"") + (goto-char (match-end 0)) + (forward-char 1) + (setq end (point)))) + ((= (following-char) ?\\) (forward-char 2)) + (t (skip-chars-forward "^\"\\\\")))) + + ;; Check before string for keyword and opening parenthesis. + (goto-char start) + (skip-chars-backward " \n\t") + (if (= (preceding-char) ?\() + (progn + (backward-char 1) + (skip-chars-backward " \n\t") + (let ((end-keyword (point))) + (skip-chars-backward "_A-Za-z0-9") + (if (member (list (po-buffer-substring (point) + end-keyword)) + keywords) + + ;; Disregard already marked strings. + (progn + (goto-char end) + (setq start nil + end nil))))))))) + + ;; Return the found string, if any. + (cons start end))) + +(defun po-mark-c-string (start end keyword) + "Mark the C string, from START to END, with KEYWORD. +Return the adjusted value for END." + (goto-char end) + (insert ")") + (goto-char start) + (insert keyword) + (if (not (string-equal keyword "_")) + (progn (insert " ") (setq end (1+ end)))) + (insert "(") + (+ end 2 (length keyword))) + +;;; Emacs LISP mode. + +(defun po-find-emacs-lisp-string (keywords) + "Find the next Emacs LISP string, excluding those marked by any of KEYWORDS. +Returns (START . END) for the found string, or (nil . nil) if none found." + (let (start end) + (while (and (not start) + (re-search-forward "[;\"?]" nil t)) + + (cond ((= (preceding-char) ?\;) + ;; Disregard comments. + (search-forward "\n")) + + ((= (preceding-char) ?\?) + ;; Disregard character constants. + (forward-char (if (= (following-char) ?\\) 2 1))) + + ;; Else, find the end of the string. + (t (setq start (1- (point))) + (while (not (= (following-char) ?\")) + (skip-chars-forward "^\"\\\\") + (if (= (following-char) ?\\) (forward-char 2))) + (forward-char 1) + (setq end (point)) + + ;; Check before string for keyword and opening parenthesis. + (goto-char start) + (skip-chars-backward " \n\t") + (let ((end-keyword (point))) + (skip-chars-backward "-_A-Za-z0-9") + (if (and (= (preceding-char) ?\() + (member (list (po-buffer-substring (point) + end-keyword)) + keywords)) + + ;; Disregard already marked strings. + (progn + (goto-char end) + (setq start nil + end nil))))))) + + ;; Return the found string, if any. + (cons start end))) + +(defun po-mark-emacs-lisp-string (start end keyword) + "Mark the Emacs LISP string, from START to END, with KEYWORD. +Return the adjusted value for END." + (goto-char end) + (insert ")") + (goto-char start) + (insert "(" keyword) + (if (not (string-equal keyword "_")) + (progn (insert " ") (setq end (1+ end)))) + (+ end 2 (length keyword))) + +;;; Processing generic to all programming modes. + +(eval-and-compile + (autoload 'visit-tags-table-buffer "etags")) + +(defun po-tags-search (restart) + "Find an unmarked translatable string through all files in tags table. +Disregard some simple strings which are most probably non-translatable. +With prefix argument, restart search at first file." + (interactive "P") + + ;; Take care of restarting the search if necessary. + (if restart (setq po-next-file-list nil)) + + ;; Loop doing things until an interesting string is found. + (let ((keywords po-keywords) + found buffer start + (end po-string-end)) + (while (not found) + + ;; Reinitialize the source file list if necessary. + (if (not po-next-file-list) + (progn + (setq po-next-file-list + (save-excursion + (visit-tags-table-buffer) + (copy-sequence (tags-table-files)))) + (or po-next-file-list (error (_"No files to process"))) + (setq end nil))) + + ;; Try finding a string after resuming the search position. + (message (_"Scanning %s...") (car po-next-file-list)) + (save-excursion + (setq buffer (find-file-noselect (car po-next-file-list))) + (set-buffer buffer) + (goto-char (or end (point-min))) + + (cond ((member mode-name '("C" "C++")) + (let ((pair (po-find-c-string keywords))) + (setq start (car pair) + end (cdr pair)))) + ((string-equal mode-name "Emacs-Lisp") + (let ((pair (po-find-emacs-lisp-string keywords))) + (setq start (car pair) + end (cdr pair)))) + (t (message (_"Unknown source mode for PO mode, skipping...")) + (setq start nil + end nil)))) + + ;; Advance to next file if no string was found. + (if (not start) + (progn + (setq po-next-file-list (cdr po-next-file-list)) + (if po-next-file-list + (setq end nil) + (setq po-string-end nil) + (and po-highlighting (po-dehighlight po-marking-overlay)) + (error (_"All files processed")))) + + ;; Push the string just found string into a work buffer for study. + (po-with-temp-buffer + (insert (po-extract-unquoted buffer start end)) + (goto-char (point-min)) + + ;; Do not disregard if at least three letters in a row. + (if (re-search-forward "[A-Za-z][A-Za-z][A-Za-z]" nil t) + (setq found t) + + ;; Disregard if two letters, and more punctuations than letters. + (if (re-search-forward "[A-Za-z][A-Za-z]" nil t) + (let ((total (buffer-size))) + (goto-char (point-min)) + (while (re-search-forward "[A-Za-z]+" nil t) + (replace-match "" t t)) + (if (< (* 2 (buffer-size)) total) + (setq found t)))) + + ;; Disregard if single letters or no letters at all. + )))) + + ;; Ensure the string is being displayed. + + (if (one-window-p t) (split-window) (other-window 1)) + (switch-to-buffer buffer) + (goto-char start) + (or (pos-visible-in-window-p start) (recenter '(nil))) + (if (pos-visible-in-window-p end) + (goto-char end) + (goto-char end) + (recenter -1)) + (other-window 1) + (and po-highlighting (po-highlight po-marking-overlay start end buffer)) + + ;; Save the string for later commands. + (message (_"Scanning %s...done") (car po-next-file-list)) + (setq po-string-start start + po-string-end end))) + +(defun po-mark-found-string (keyword) + "Mark last found string in program sources as translatable, using KEYWORD." + (and po-highlighting (po-dehighlight po-marking-overlay)) + (let ((buffer (find-file-noselect (car po-next-file-list))) + (start po-string-start) + (end po-string-end) + line string) + + ;; Mark string in program sources. + (setq string (po-extract-unquoted buffer start end)) + (save-excursion + (set-buffer buffer) + (setq line (count-lines (point-min) start) + end (cond ((member mode-name '("C" "C++")) + (po-mark-c-string start end keyword)) + ((string-equal mode-name "Emacs-Lisp") + (po-mark-emacs-lisp-string start end keyword)) + (t (error (_"Cannot mark in unknown source mode")))))) + (setq po-string-end end) + + ;; Add PO file entry. + (let ((buffer-read-only po-read-only)) + (goto-char (point-max)) + (insert "\n" (format "#: %s:%d\n" (car po-next-file-list) line)) + (save-excursion + (insert (po-eval-requoted string "msgid" nil) "msgstr \"\"\n")) + (setq po-untranslated-counter (1+ po-untranslated-counter)) + (po-update-mode-line-string)))) + +(defun po-mark-translatable () + "Mark last found string in program sources as translatable, using `_'." + (interactive) + (if (and po-string-start po-string-end) + (progn + (po-mark-found-string "_") + (setq po-string-start nil)) + (error (_"No such string")))) + +(defun po-select-mark-and-mark (arg) + "Mark last found string in program sources as translatable, ask for keywoard, +using completion. With prefix argument, just ask the name of a preferred +keyword for subsequent commands, also added to possible completions." + (interactive "P") + (if arg + (let ((keyword (list (read-from-minibuffer (_"Keyword: "))))) + (setq po-keywords (cons keyword (delete keyword po-keywords)))) + (if (and po-string-start po-string-end) + (let* ((default (car (car po-keywords))) + (keyword (completing-read (format (_"Mark with keywoard? [%s] ") + default) + po-keywords nil t ))) + (if (string-equal keyword "") (setq keyword default)) + (po-mark-found-string keyword) + (setq po-string-start nil)) + (error (_"No such string"))))) + +;;; Miscellaneous features. + +(defun po-help () + "Provide an help window for PO mode." + (interactive) + (po-with-temp-buffer + (insert po-help-display-string) + (goto-char (point-min)) + (save-window-excursion + (switch-to-buffer (current-buffer)) + (delete-other-windows) + (message (_"Type any character to continue")) + (po-read-event)))) + +(defun po-undo () + "Undo the last change to the PO file." + (interactive) + (let ((buffer-read-only po-read-only)) + (undo)) + (po-compute-counters nil)) + +(defun po-statistics () + "Say how many entries in each category, and the current position." + (interactive) + (po-compute-counters t)) + +(defun po-validate () + "Use `msgfmt' for validating the current PO file contents." + (interactive) + + ;; If modifications were done already, change the last revision date. + (if (buffer-modified-p) + (po-replace-revision-date)) + + ;; This `let' is for protecting the previous value of compile-command. + (let ((compile-command (concat po-msgfmt-program + " --statistics -c -v -o /dev/null " + buffer-file-name))) + (compile compile-command))) + +(defun po-guess-archive-name () + "Return the ideal file name for this PO file in the central archives." + (let (start-of-header end-of-header package version team) + (save-excursion + ;; Find the PO file header entry. + (goto-char (point-min)) + (re-search-forward po-any-msgstr-regexp) + (setq start-of-header (match-beginning 0) + end-of-header (match-end 0)) + ;; Get the package and version. + (goto-char start-of-header) + (if (re-search-forward + "\n\"Project-Id-Version:\\( GNU\\)? \\([^\n ]+\\) \\([^\n ]+\\)\\\\n\"$" + end-of-header t) + (setq package (buffer-substring (match-beginning 2) (match-end 2)) + version (buffer-substring (match-beginning 3) (match-end 3)))) + (if (or (not package) (string-equal package "PACKAGE") + (not version) (string-equal version "VERSION")) + (error (_"Project-Id-Version field does not have a proper value"))) + ;; Get the team. + (goto-char start-of-header) + (if (re-search-forward "\n\"Language-Team:.*<\\(.*\\)@li.org>\\\\n\"$" + end-of-header t) + (setq team (buffer-substring (match-beginning 1) (match-end 1)))) + (if (or (not team) (string-equal team "LL")) + (error (_"Language-Team field does not have a proper value"))) + ;; Compose the name. + (concat package "-" version "." team ".po")))) + +(defun po-guess-team-address () + "Return the team address related to this PO file." + (let (team) + (save-excursion + (goto-char (point-min)) + (re-search-forward po-any-msgstr-regexp) + (goto-char (match-beginning 0)) + (if (re-search-forward + "\n\"Language-Team: +\\(.*<\\(.*\\)@li.org>\\)\\\\n\"$" + (match-end 0) t) + (setq team (buffer-substring (match-beginning 2) (match-end 2)))) + (if (or (not team) (string-equal team "LL")) + (error (_"Language-Team field does not have a proper value"))) + (buffer-substring (match-beginning 1) (match-end 1))))) + +(defun po-send-mail () + "Start composing a letter, possibly including the current PO file." + (interactive) + (let* ((team-flag (y-or-n-p + (_"\ +Write to your team? (`n' means writing to translation project) "))) + (address (if team-flag + (po-guess-team-address) + po-translation-project-address))) + (if (not (y-or-n-p (_"Include current PO file? "))) + (apply po-compose-mail-function address + (read-string (_"Subject? ")) nil) + (if (buffer-modified-p) + (error (_"The file is not even saved, you did not validate it."))) + (if (and (y-or-n-p (_"You validated (`V') this file, didn't you? ")) + (or (zerop po-untranslated-counter) + (y-or-n-p + (format (_"%d entries are untranslated, include anyway? ") + po-untranslated-counter))) + (or (zerop po-fuzzy-counter) + (y-or-n-p + (format (_"%d entries are still fuzzy, include anyway? ") + po-fuzzy-counter))) + (or (zerop po-obsolete-counter) + (y-or-n-p + (format (_"%d entries are obsolete, include anyway? ") + po-obsolete-counter)))) + (let ((buffer (current-buffer)) + (name (po-guess-archive-name)) + (transient-mark-mode nil)) + (apply po-compose-mail-function address + (if team-flag + (read-string (_"Subject? ")) + (format "TP-Robot %s" name)) + nil) + (goto-char (point-min)) + (re-search-forward + (concat "^" (regexp-quote mail-header-separator) "\n")) + (save-excursion + (insert-buffer buffer) + (shell-command-on-region + (region-beginning) (region-end) + (concat po-gzip-uuencode-command " " name ".gz") t)))))) + (message "")) + +(defun po-confirm-and-quit () + "Confirm if quit should be attempted and then, do it. +This is a failsafe. Confirmation is asked if only the real quit would not." + (interactive) + (if (po-check-all-pending-edits) + (progn + (if (or (buffer-modified-p) + (> po-untranslated-counter 0) + (> po-fuzzy-counter 0) + (> po-obsolete-counter 0) + (y-or-n-p (_"Really quit editing this PO file? "))) + (po-quit)) + (message "")))) + +(defun po-quit () + "Save the PO file and kill buffer. However, offer validation if +appropriate and ask confirmation if untranslated strings remain." + (interactive) + (if (po-check-all-pending-edits) + (let ((quit t)) + + ;; Offer validation of newly modified entries. + (if (and (buffer-modified-p) + (not (y-or-n-p + (_"File was modified; skip validation step? ")))) + (progn + (message "") + (po-validate) + ;; If we knew that the validation was all successful, we should + ;; just quit. But since we do not know yet, as the validation + ;; might be asynchronous with PO mode commands, the safest is to + ;; stay within PO mode, even if this implies that another + ;; `po-quit' command will be later required to exit for true. + (setq quit nil))) + + ;; Offer to work on untranslated entries. + (if (and quit + (or (> po-untranslated-counter 0) + (> po-fuzzy-counter 0) + (> po-obsolete-counter 0)) + (not (y-or-n-p + (_"Unprocessed entries remain; quit anyway? ")))) + (progn + (setq quit nil) + (po-auto-select-entry))) + + ;; Clear message area. + (message "") + + ;; Or else, kill buffers and quit for true. + (if quit + (progn + (and (buffer-modified-p) (po-replace-revision-date)) + (save-buffer) + (kill-buffer (current-buffer))))))) + +;;; po-mode.el ends here diff --git a/misc/tcl_gettext.c b/misc/tcl_gettext.c new file mode 100644 index 0000000..f399ca0 --- /dev/null +++ b/misc/tcl_gettext.c @@ -0,0 +1,165 @@ +/* tcl_gettext - Module implementing gettext interface for Tcl. + Copyright (C) 1995, 1998 Free Software Foundation, Inc. + Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, December 1995. + + This 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 2, or (at your option) + any later version. + + This program 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 this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <libintl.h> +#include <locale.h> +#include <string.h> + +/* Data for Tcl interpreter interface. */ +#include "tcl.h" + +/* Prototypes for local functions. */ +static int +tcl_gettext (ClientData client_data, Tcl_Interp *interp, int argc, + char *argv[]); +static int +tcl_textdomain (ClientData client_data, Tcl_Interp *interp, int argc, + char *argv[]); +static int +tcl_bindtextdomain (ClientData client_data, Tcl_Interp *interp, int argc, + char *argv[]); + + +/* Initialization functions. Called from the tclAppInit.c/tkAppInit.c + or while the dynamic loading with Tcl7.x, x>= 5. */ +int +Gettext_Init (interp) + Tcl_Interp *interp; +{ + Tcl_CreateCommand (interp, "gettext", tcl_gettext, (ClientData) 0, + (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand (interp, "textdomain", tcl_textdomain, (ClientData) 0, + (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand (interp, "bindtextdomain", tcl_bindtextdomain, + (ClientData) 0, (Tcl_CmdDeleteProc *) NULL); + + return TCL_OK; +} + + +static int +tcl_gettext (client_data, interp, argc, argv) + ClientData client_data; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + const char *domainname = NULL; + int category = LC_MESSAGES; + const char *msgid; + + /* The pointer which is assigned in the following statement might + reference an invalid part of the address space. But we don't use + this value before we know the pointer is correct. */ + msgid = argv[1]; + + switch (argc) + { + case 4: +#ifdef LC_CTYPE + if (strcmp (argv[3], "LC_CTYPE") == 0) + category = LC_CTYPE; + else +#endif +#ifdef LC_COLLATE + if (strcmp (argv[3], "LC_COLLATE") == 0) + category = LC_COLLATE; + else +#endif +#ifdef LC_MESSAGES + if (strcmp (argv[3], "LC_MESSAGES") == 0) + category = LC_MESSAGES; + else +#endif +#ifdef LC_MONETARY + if (strcmp (argv[3], "LC_MONETARY") == 0) + category = LC_MONETARY; + else +#endif +#ifdef LC_NUMERIC + if (strcmp (argv[3], "LC_NUMERIC") == 0) + category = LC_NUMERIC; + else +#endif +#ifdef LC_TIME + if (strcmp (argv[3], "LC_TIME") == 0) + category = LC_TIME; + else +#endif + { + interp->result = gettext ("invalid third argument"); + return TCL_ERROR; + } + /* FALLTHROUGH */ + + case 3: + domainname = argv[1]; + msgid = argv[2]; + /* FALLTHROUGH */ + + case 2: + interp->result = dcgettext (domainname, msgid, category); + break; + + default: + interp->result = gettext ("wrong number of arguments"); + return TCL_ERROR; + } + + return TCL_OK; +} + + +static int +tcl_textdomain (client_data, interp, argc, argv) + ClientData client_data; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + if (argc != 2) + { + interp->result = gettext ("wrong number of arguments"); + return TCL_ERROR; + } + + interp->result = textdomain (argv[1]); + + return TCL_OK; +} + + +static int +tcl_bindtextdomain (client_data, interp, argc, argv) + ClientData client_data; + Tcl_Interp *interp; + int argc; + char *argv[]; +{ + if (argc != 3) + { + interp->result = gettext ("wrong number of arguments"); + return TCL_ERROR; + } + + return bindtextdomain (argv[1], argv[2]) == NULL ? TCL_ERROR : TCL_OK; +} |