1 /* Converts Uniforum style .po files to binary .mo files
2 Copyright (C) 1995-1998, 2000-2007, 2009-2010, 2012 Free Software Foundation, Inc.
3 Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, April 1995.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>. */
34 #include "error-progname.h"
36 #include "relocatable.h"
39 #include "xvasprintf.h"
43 #include "write-java.h"
44 #include "write-csharp.h"
45 #include "write-resources.h"
46 #include "write-tcl.h"
48 #include "propername.h"
50 #include "open-catalog.h"
51 #include "read-catalog.h"
53 #include "read-properties.h"
54 #include "read-stringtable.h"
55 #include "po-charset.h"
56 #include "msgl-check.h"
59 #define _(str) gettext (str)
61 /* Contains exit status for case in which no premature exit occurs. */
62 static int exit_status;
64 /* If true include even fuzzy translations in output file. */
65 static bool include_fuzzies = false;
67 /* If true include even untranslated messages in output file. */
68 static bool include_untranslated = false;
70 /* Specifies name of the output file. */
71 static const char *output_file_name;
73 /* Java mode output file specification. */
74 static bool java_mode;
75 static bool assume_java2;
76 static const char *java_resource_name;
77 static const char *java_locale_name;
78 static const char *java_class_directory;
80 /* C# mode output file specification. */
81 static bool csharp_mode;
82 static const char *csharp_resource_name;
83 static const char *csharp_locale_name;
84 static const char *csharp_base_directory;
86 /* C# resources mode output file specification. */
87 static bool csharp_resources_mode;
89 /* Tcl mode output file specification. */
91 static const char *tcl_locale_name;
92 static const char *tcl_base_directory;
94 /* Qt mode output file specification. */
97 /* We may have more than one input file. Domains with same names in
98 different files have to merged. So we need a list of tables for
102 /* List for mapping message IDs to message strings. */
103 message_list_ty *mlp;
104 /* Name of domain these ID/String pairs are part of. */
105 const char *domain_name;
106 /* Output file name. */
107 const char *file_name;
108 /* Link to the next domain. */
109 struct msg_domain *next;
111 static struct msg_domain *domain_list;
112 static struct msg_domain *current_domain;
114 /* Be more verbose. Use only 'fprintf' and 'multiline_warning' but not
115 'error' or 'multiline_error' to emit verbosity messages, because 'error'
116 and 'multiline_error' during PO file parsing cause the program to exit
117 with EXIT_FAILURE. See function lex_end(). */
120 /* If true check strings according to format string rules for the
122 static bool check_format_strings = false;
124 /* If true check the header entry is present and complete. */
125 static bool check_header = false;
127 /* Check that domain directives can be satisfied. */
128 static bool check_domain = false;
130 /* Check that msgfmt's behaviour is semantically compatible with
131 X/Open msgfmt or XView msgfmt. */
132 static bool check_compatibility = false;
134 /* If true, consider that strings containing an '&' are menu items and
135 the '&' designates a keyboard accelerator, and verify that the translations
136 also have a keyboard accelerator. */
137 static bool check_accelerators = false;
138 static char accelerator_char = '&';
140 /* Counters for statistics on translations for the processed files. */
141 static int msgs_translated;
142 static int msgs_untranslated;
143 static int msgs_fuzzy;
145 /* If not zero print statistics about translation at the end. */
146 static int do_statistics;
149 static const struct option long_options[] =
151 { "alignment", required_argument, NULL, 'a' },
152 { "check", no_argument, NULL, 'c' },
153 { "check-accelerators", optional_argument, NULL, CHAR_MAX + 1 },
154 { "check-compatibility", no_argument, NULL, 'C' },
155 { "check-domain", no_argument, NULL, CHAR_MAX + 2 },
156 { "check-format", no_argument, NULL, CHAR_MAX + 3 },
157 { "check-header", no_argument, NULL, CHAR_MAX + 4 },
158 { "csharp", no_argument, NULL, CHAR_MAX + 10 },
159 { "csharp-resources", no_argument, NULL, CHAR_MAX + 11 },
160 { "directory", required_argument, NULL, 'D' },
161 { "endianness", required_argument, NULL, CHAR_MAX + 13 },
162 { "help", no_argument, NULL, 'h' },
163 { "java", no_argument, NULL, 'j' },
164 { "java2", no_argument, NULL, CHAR_MAX + 5 },
165 { "locale", required_argument, NULL, 'l' },
166 { "no-hash", no_argument, NULL, CHAR_MAX + 6 },
167 { "output-file", required_argument, NULL, 'o' },
168 { "properties-input", no_argument, NULL, 'P' },
169 { "qt", no_argument, NULL, CHAR_MAX + 9 },
170 { "resource", required_argument, NULL, 'r' },
171 { "statistics", no_argument, &do_statistics, 1 },
172 { "strict", no_argument, NULL, 'S' },
173 { "stringtable-input", no_argument, NULL, CHAR_MAX + 8 },
174 { "tcl", no_argument, NULL, CHAR_MAX + 7 },
175 { "use-fuzzy", no_argument, NULL, 'f' },
176 { "use-untranslated", no_argument, NULL, CHAR_MAX + 12 },
177 { "verbose", no_argument, NULL, 'v' },
178 { "version", no_argument, NULL, 'V' },
183 /* Forward declaration of local functions. */
184 static void usage (int status)
185 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
186 __attribute__ ((noreturn))
189 static const char *add_mo_suffix (const char *);
190 static struct msg_domain *new_domain (const char *name, const char *file_name);
191 static bool is_nonobsolete (const message_ty *mp);
192 static void read_catalog_file_msgfmt (char *filename,
193 catalog_input_format_ty input_syntax);
197 main (int argc, char *argv[])
200 bool do_help = false;
201 bool do_version = false;
202 bool strict_uniforum = false;
203 catalog_input_format_ty input_syntax = &input_format_po;
205 const char *canon_encoding;
206 struct msg_domain *domain;
208 /* Set default value for global variables. */
209 alignment = DEFAULT_OUTPUT_ALIGNMENT;
211 /* Set program name for messages. */
212 set_program_name (argv[0]);
213 error_print_progname = maybe_print_progname;
214 error_one_per_line = 1;
215 exit_status = EXIT_SUCCESS;
217 #ifdef HAVE_SETLOCALE
218 /* Set locale via LC_ALL. */
219 setlocale (LC_ALL, "");
222 /* Set the text message domain. */
223 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
224 bindtextdomain ("bison-runtime", relocate (BISON_LOCALEDIR));
225 textdomain (PACKAGE);
227 /* Ensure that write errors on stdout are detected. */
228 atexit (close_stdout);
230 while ((opt = getopt_long (argc, argv, "a:cCd:D:fhjl:o:Pr:vV", long_options,
235 case '\0': /* Long option. */
240 size_t new_align = strtoul (optarg, &endp, 0);
243 alignment = new_align;
248 check_format_strings = true;
252 check_compatibility = true;
255 java_class_directory = optarg;
256 csharp_base_directory = optarg;
257 tcl_base_directory = optarg;
260 dir_list_append (optarg);
263 include_fuzzies = true;
272 java_locale_name = optarg;
273 csharp_locale_name = optarg;
274 tcl_locale_name = optarg;
277 output_file_name = optarg;
280 input_syntax = &input_format_properties;
283 java_resource_name = optarg;
284 csharp_resource_name = optarg;
287 strict_uniforum = true;
295 case CHAR_MAX + 1: /* --check-accelerators */
296 check_accelerators = true;
299 if (optarg[0] != '\0' && ispunct ((unsigned char) optarg[0])
300 && optarg[1] == '\0')
301 accelerator_char = optarg[0];
303 error (EXIT_FAILURE, 0,
304 _("the argument to %s should be a single punctuation character"),
305 "--check-accelerators");
308 case CHAR_MAX + 2: /* --check-domain */
311 case CHAR_MAX + 3: /* --check-format */
312 check_format_strings = true;
314 case CHAR_MAX + 4: /* --check-header */
317 case CHAR_MAX + 5: /* --java2 */
321 case CHAR_MAX + 6: /* --no-hash */
322 no_hash_table = true;
324 case CHAR_MAX + 7: /* --tcl */
327 case CHAR_MAX + 8: /* --stringtable-input */
328 input_syntax = &input_format_stringtable;
330 case CHAR_MAX + 9: /* --qt */
333 case CHAR_MAX + 10: /* --csharp */
336 case CHAR_MAX + 11: /* --csharp-resources */
337 csharp_resources_mode = true;
339 case CHAR_MAX + 12: /* --use-untranslated (undocumented) */
340 include_untranslated = true;
342 case CHAR_MAX + 13: /* --endianness={big|little} */
346 if (strcmp (optarg, "big") == 0)
348 else if (strcmp (optarg, "little") == 0)
351 error (EXIT_FAILURE, 0, _("invalid endianness: %s"), optarg);
353 byteswap = endianness ^ ENDIANNESS;
357 usage (EXIT_FAILURE);
361 /* Version information is requested. */
364 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
365 /* xgettext: no-wrap */
366 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
367 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
368 This is free software: you are free to change and redistribute it.\n\
369 There is NO WARRANTY, to the extent permitted by law.\n\
371 "1995-1998, 2000-2010");
372 printf (_("Written by %s.\n"), proper_name ("Ulrich Drepper"));
376 /* Help is requested. */
378 usage (EXIT_SUCCESS);
380 /* Test whether we have a .po file name as argument. */
383 error (EXIT_SUCCESS, 0, _("no input file given"));
384 usage (EXIT_FAILURE);
387 /* Check for contradicting options. */
391 | (csharp_mode ? 2 : 0)
392 | (csharp_resources_mode ? 4 : 0)
394 | (qt_mode ? 16 : 0);
395 static const char *mode_options[] =
396 { "--java", "--csharp", "--csharp-resources", "--tcl", "--qt" };
397 /* More than one bit set? */
398 if (modes & (modes - 1))
400 const char *first_option;
401 const char *second_option;
404 if (modes & (1 << i))
406 first_option = mode_options[i];
407 for (i = i + 1; ; i++)
408 if (modes & (1 << i))
410 second_option = mode_options[i];
411 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
412 first_option, second_option);
417 if (output_file_name != NULL)
419 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
420 "--java", "--output-file");
422 if (java_class_directory == NULL)
424 error (EXIT_SUCCESS, 0,
425 _("%s requires a \"-d directory\" specification"),
427 usage (EXIT_FAILURE);
430 else if (csharp_mode)
432 if (output_file_name != NULL)
434 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
435 "--csharp", "--output-file");
437 if (csharp_locale_name == NULL)
439 error (EXIT_SUCCESS, 0,
440 _("%s requires a \"-l locale\" specification"),
442 usage (EXIT_FAILURE);
444 if (csharp_base_directory == NULL)
446 error (EXIT_SUCCESS, 0,
447 _("%s requires a \"-d directory\" specification"),
449 usage (EXIT_FAILURE);
454 if (output_file_name != NULL)
456 error (EXIT_FAILURE, 0, _("%s and %s are mutually exclusive"),
457 "--tcl", "--output-file");
459 if (tcl_locale_name == NULL)
461 error (EXIT_SUCCESS, 0,
462 _("%s requires a \"-l locale\" specification"),
464 usage (EXIT_FAILURE);
466 if (tcl_base_directory == NULL)
468 error (EXIT_SUCCESS, 0,
469 _("%s requires a \"-d directory\" specification"),
471 usage (EXIT_FAILURE);
476 if (java_resource_name != NULL)
478 error (EXIT_SUCCESS, 0, _("%s is only valid with %s or %s"),
479 "--resource", "--java", "--csharp");
480 usage (EXIT_FAILURE);
482 if (java_locale_name != NULL)
484 error (EXIT_SUCCESS, 0, _("%s is only valid with %s, %s or %s"),
485 "--locale", "--java", "--csharp", "--tcl");
486 usage (EXIT_FAILURE);
488 if (java_class_directory != NULL)
490 error (EXIT_SUCCESS, 0, _("%s is only valid with %s, %s or %s"),
491 "-d", "--java", "--csharp", "--tcl");
492 usage (EXIT_FAILURE);
496 /* The -o option determines the name of the domain and therefore
498 if (output_file_name != NULL)
500 new_domain (output_file_name,
501 strict_uniforum && !csharp_resources_mode && !qt_mode
502 ? add_mo_suffix (output_file_name)
505 /* Process all given .po files. */
506 for (arg_i = optind; arg_i < argc; arg_i++)
508 /* Remember that we currently have not specified any domain. This
509 is of course not true when we saw the -o option. */
510 if (output_file_name == NULL)
511 current_domain = NULL;
513 /* And process the input file. */
514 read_catalog_file_msgfmt (argv[arg_i], input_syntax);
517 /* We know a priori that some input_syntax->parse() functions convert
519 canon_encoding = (input_syntax->produces_utf8 ? po_charset_utf8 : NULL);
521 /* Remove obsolete messages. They were only needed for duplicate
523 for (domain = domain_list; domain != NULL; domain = domain->next)
524 message_list_remove_if_not (domain->mlp, is_nonobsolete);
526 /* Perform all kinds of checks: plural expressions, format strings, ... */
530 for (domain = domain_list; domain != NULL; domain = domain->next)
532 check_message_list (domain->mlp,
533 /* Untranslated and fuzzy messages have already
534 been dealt with during parsing, see below in
535 msgfmt_frob_new_message. */
537 1, check_format_strings, check_header,
539 check_accelerators, accelerator_char);
541 /* Exit with status 1 on any error. */
545 ngettext ("found %d fatal error", "found %d fatal errors",
548 exit_status = EXIT_FAILURE;
552 /* Now write out all domains. */
553 for (domain = domain_list; domain != NULL; domain = domain->next)
557 if (msgdomain_write_java (domain->mlp, canon_encoding,
558 java_resource_name, java_locale_name,
559 java_class_directory, assume_java2))
560 exit_status = EXIT_FAILURE;
562 else if (csharp_mode)
564 if (msgdomain_write_csharp (domain->mlp, canon_encoding,
565 csharp_resource_name, csharp_locale_name,
566 csharp_base_directory))
567 exit_status = EXIT_FAILURE;
569 else if (csharp_resources_mode)
571 if (msgdomain_write_csharp_resources (domain->mlp, canon_encoding,
574 exit_status = EXIT_FAILURE;
578 if (msgdomain_write_tcl (domain->mlp, canon_encoding,
579 tcl_locale_name, tcl_base_directory))
580 exit_status = EXIT_FAILURE;
584 if (msgdomain_write_qt (domain->mlp, canon_encoding,
585 domain->domain_name, domain->file_name))
586 exit_status = EXIT_FAILURE;
590 if (msgdomain_write_mo (domain->mlp, domain->domain_name,
592 exit_status = EXIT_FAILURE;
595 /* List is not used anymore. */
596 message_list_free (domain->mlp, 0);
599 /* Print statistics if requested. */
600 if (verbose || do_statistics)
602 if (do_statistics + verbose >= 2 && optind < argc)
604 /* Print the input file name(s) in front of the statistics line. */
605 char *all_input_file_names;
608 string_list_ty input_file_names;
610 string_list_init (&input_file_names);;
611 for (arg_i = optind; arg_i < argc; arg_i++)
612 string_list_append (&input_file_names, argv[arg_i]);
613 all_input_file_names =
614 string_list_join (&input_file_names, ", ", '\0', false);
615 string_list_destroy (&input_file_names);
618 /* TRANSLATORS: The prefix before a statistics message. The argument
619 is a file name or a comma separated list of file names. */
620 fprintf (stderr, _("%s: "), all_input_file_names);
621 free (all_input_file_names);
624 ngettext ("%d translated message", "%d translated messages",
629 ngettext (", %d fuzzy translation", ", %d fuzzy translations",
632 if (msgs_untranslated > 0)
634 ngettext (", %d untranslated message",
635 ", %d untranslated messages",
638 fputs (".\n", stderr);
645 /* Display usage information and exit. */
649 if (status != EXIT_SUCCESS)
650 fprintf (stderr, _("Try '%s --help' for more information.\n"),
655 Usage: %s [OPTION] filename.po ...\n\
659 Generate binary message catalog from textual translation description.\n\
662 /* xgettext: no-wrap */
664 Mandatory arguments to long options are mandatory for short options too.\n\
665 Similarly for optional arguments.\n\
669 Input file location:\n"));
671 filename.po ... input files\n"));
673 -D, --directory=DIRECTORY add DIRECTORY to list for input files search\n"));
675 If input file is -, standard input is read.\n"));
678 Operation mode:\n"));
680 -j, --java Java mode: generate a Java ResourceBundle class\n"));
682 --java2 like --java, and assume Java2 (JDK 1.2 or higher)\n"));
684 --csharp C# mode: generate a .NET .dll file\n"));
686 --csharp-resources C# resources mode: generate a .NET .resources file\n"));
688 --tcl Tcl mode: generate a tcl/msgcat .msg file\n"));
690 --qt Qt mode: generate a Qt .qm file\n"));
693 Output file location:\n"));
695 -o, --output-file=FILE write output to specified file\n"));
697 --strict enable strict Uniforum mode\n"));
699 If output file is -, output is written to standard output.\n"));
702 Output file location in Java mode:\n"));
704 -r, --resource=RESOURCE resource name\n"));
706 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
708 -d DIRECTORY base directory of classes directory hierarchy\n"));
710 The class name is determined by appending the locale name to the resource name,\n\
711 separated with an underscore. The -d option is mandatory. The class is\n\
712 written under the specified directory.\n\
716 Output file location in C# mode:\n"));
718 -r, --resource=RESOURCE resource name\n"));
720 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
722 -d DIRECTORY base directory for locale dependent .dll files\n"));
724 The -l and -d options are mandatory. The .dll file is written in a\n\
725 subdirectory of the specified directory whose name depends on the locale.\n"));
728 Output file location in Tcl mode:\n"));
730 -l, --locale=LOCALE locale name, either language or language_COUNTRY\n"));
732 -d DIRECTORY base directory of .msg message catalogs\n"));
734 The -l and -d options are mandatory. The .msg file is written in the\n\
735 specified directory.\n"));
738 Input file syntax:\n"));
740 -P, --properties-input input files are in Java .properties syntax\n"));
742 --stringtable-input input files are in NeXTstep/GNUstep .strings\n\
746 Input file interpretation:\n"));
748 -c, --check perform all the checks implied by\n\
749 --check-format, --check-header, --check-domain\n"));
751 --check-format check language dependent format strings\n"));
753 --check-header verify presence and contents of the header entry\n"));
755 --check-domain check for conflicts between domain directives\n\
756 and the --output-file option\n"));
758 -C, --check-compatibility check that GNU msgfmt behaves like X/Open msgfmt\n"));
760 --check-accelerators[=CHAR] check presence of keyboard accelerators for\n\
763 -f, --use-fuzzy use fuzzy entries in output\n"));
766 Output details:\n"));
768 -a, --alignment=NUMBER align strings to NUMBER bytes (default: %d)\n"), DEFAULT_OUTPUT_ALIGNMENT);
770 --endianness=BYTEORDER write out 32-bit numbers in the given byte order\n\
771 (big or little, default depends on platform)\n"));
773 --no-hash binary file will not include the hash table\n"));
776 Informative output:\n"));
778 -h, --help display this help and exit\n"));
780 -V, --version output version information and exit\n"));
782 --statistics print statistics about translations\n"));
784 -v, --verbose increase verbosity level\n"));
786 /* TRANSLATORS: The placeholder indicates the bug-reporting address
787 for this package. Please add _another line_ saying
788 "Report translation bugs to <...>\n" with the address for translation
789 bugs (typically your translation team's web or email address). */
790 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
798 add_mo_suffix (const char *fname)
803 len = strlen (fname);
804 if (len > 3 && memcmp (fname + len - 3, ".mo", 3) == 0)
806 if (len > 4 && memcmp (fname + len - 4, ".gmo", 4) == 0)
808 result = XNMALLOC (len + 4, char);
809 stpcpy (stpcpy (result, fname), ".mo");
814 static struct msg_domain *
815 new_domain (const char *name, const char *file_name)
817 struct msg_domain **p_dom = &domain_list;
819 while (*p_dom != NULL && strcmp (name, (*p_dom)->domain_name) != 0)
820 p_dom = &(*p_dom)->next;
824 struct msg_domain *domain;
826 domain = XMALLOC (struct msg_domain);
827 domain->mlp = message_list_alloc (true);
828 domain->domain_name = name;
829 domain->file_name = file_name;
839 is_nonobsolete (const message_ty *mp)
841 return !mp->obsolete;
845 /* The rest of the file defines a subclass msgfmt_catalog_reader_ty of
846 default_catalog_reader_ty. Its particularities are:
847 - The header entry check is performed on-the-fly.
848 - Comments are not stored, they are discarded right away.
849 (This is achieved by setting handle_comments = false and
850 handle_filepos_comments = false.)
851 - The multi-domain handling is adapted to our domain_list.
855 /* This structure defines a derived class of the default_catalog_reader_ty
856 class. (See read-catalog-abstract.h for an explanation.) */
857 typedef struct msgfmt_catalog_reader_ty msgfmt_catalog_reader_ty;
858 struct msgfmt_catalog_reader_ty
860 /* inherited instance variables, etc */
861 DEFAULT_CATALOG_READER_TY
863 bool has_header_entry;
864 bool has_nonfuzzy_header_entry;
868 /* Prepare for first message. */
870 msgfmt_constructor (abstract_catalog_reader_ty *that)
872 msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
874 /* Invoke superclass constructor. */
875 default_constructor (that);
877 this->has_header_entry = false;
878 this->has_nonfuzzy_header_entry = false;
882 /* Some checks after whole file is read. */
884 msgfmt_parse_debrief (abstract_catalog_reader_ty *that)
886 msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
888 /* Invoke superclass method. */
889 default_parse_debrief (that);
891 /* Test whether header entry was found. */
894 if (!this->has_header_entry)
896 multiline_error (xasprintf ("%s: ", this->file_name),
898 warning: PO file header missing or invalid\n")));
899 multiline_error (NULL,
901 warning: charset conversion will not work\n")));
903 else if (!this->has_nonfuzzy_header_entry)
905 /* Has only a fuzzy header entry. Since the versions 0.10.xx
906 ignore a fuzzy header entry and even give an error on it, we
907 give a warning, to increase operability with these older
908 msgfmt versions. This warning can go away in January 2003. */
909 multiline_warning (xasprintf ("%s: ", this->file_name),
910 xasprintf (_("warning: PO file header fuzzy\n")));
911 multiline_warning (NULL,
913 warning: older versions of msgfmt will give an error on this\n")));
919 /* Set 'domain' directive when seen in .po file. */
921 msgfmt_set_domain (default_catalog_reader_ty *this, char *name)
923 /* If no output file was given, we change it with each 'domain'
925 if (!java_mode && !csharp_mode && !csharp_resources_mode && !tcl_mode
926 && !qt_mode && output_file_name == NULL)
930 correct = strcspn (name, INVALID_PATH_CHAR);
931 if (name[correct] != '\0')
933 exit_status = EXIT_FAILURE;
937 domain name \"%s\" not suitable as file name"), name);
942 domain name \"%s\" not suitable as file name: will use prefix"), name);
943 name[correct] = '\0';
946 /* Set new domain. */
947 current_domain = new_domain (name, add_mo_suffix (name));
948 this->domain = current_domain->domain_name;
949 this->mlp = current_domain->mlp;
954 po_gram_error_at_line (&gram_pos,
955 _("'domain %s' directive ignored"), name);
957 /* NAME was allocated in po-gram-gen.y but is not used anywhere. */
964 msgfmt_add_message (default_catalog_reader_ty *this,
967 lex_pos_ty *msgid_pos,
969 char *msgstr, size_t msgstr_len,
970 lex_pos_ty *msgstr_pos,
973 char *prev_msgid_plural,
974 bool force_fuzzy, bool obsolete)
976 /* Check whether already a domain is specified. If not, use default
978 if (current_domain == NULL)
980 current_domain = new_domain (MESSAGE_DOMAIN_DEFAULT,
981 add_mo_suffix (MESSAGE_DOMAIN_DEFAULT));
982 /* Keep current_domain and this->domain synchronized. */
983 this->domain = current_domain->domain_name;
984 this->mlp = current_domain->mlp;
987 /* Invoke superclass method. */
988 default_add_message (this, msgctxt, msgid, msgid_pos, msgid_plural,
989 msgstr, msgstr_len, msgstr_pos,
990 prev_msgctxt, prev_msgid, prev_msgid_plural,
991 force_fuzzy, obsolete);
996 msgfmt_frob_new_message (default_catalog_reader_ty *that, message_ty *mp,
997 const lex_pos_ty *msgid_pos,
998 const lex_pos_ty *msgstr_pos)
1000 msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
1004 /* Don't emit untranslated entries.
1005 Also don't emit fuzzy entries, unless --use-fuzzy was specified.
1006 But ignore fuzziness of the header entry. */
1007 if ((!include_untranslated && mp->msgstr[0] == '\0')
1008 || (!include_fuzzies && mp->is_fuzzy && !is_header (mp)))
1010 if (check_compatibility)
1012 error_with_progname = false;
1013 error_at_line (0, 0, mp->pos.file_name, mp->pos.line_number,
1014 (mp->msgstr[0] == '\0'
1015 ? _("empty 'msgstr' entry ignored")
1016 : _("fuzzy 'msgstr' entry ignored")));
1017 error_with_progname = true;
1020 /* Increment counter for fuzzy/untranslated messages. */
1021 if (mp->msgstr[0] == '\0')
1022 ++msgs_untranslated;
1026 mp->obsolete = true;
1030 /* Test for header entry. */
1033 this->has_header_entry = true;
1035 this->has_nonfuzzy_header_entry = true;
1038 /* We don't count the header entry in the statistic so place
1039 the counter incrementation here. */
1049 /* Test for '#, fuzzy' comments and warn. */
1051 msgfmt_comment_special (abstract_catalog_reader_ty *that, const char *s)
1053 msgfmt_catalog_reader_ty *this = (msgfmt_catalog_reader_ty *) that;
1055 /* Invoke superclass method. */
1056 default_comment_special (that, s);
1060 static bool warned = false;
1062 if (!include_fuzzies && check_compatibility && !warned)
1066 %s: warning: source file contains fuzzy translation"),
1067 gram_pos.file_name);
1073 /* So that the one parser can be used for multiple programs, and also
1074 use good data hiding and encapsulation practices, an object
1075 oriented approach has been taken. An object instance is allocated,
1076 and all actions resulting from the parse will be through
1077 invocations of method functions of that object. */
1079 static default_catalog_reader_class_ty msgfmt_methods =
1082 sizeof (msgfmt_catalog_reader_ty),
1085 default_parse_brief,
1086 msgfmt_parse_debrief,
1087 default_directive_domain,
1088 default_directive_message,
1090 default_comment_dot,
1091 default_comment_filepos,
1092 msgfmt_comment_special
1094 msgfmt_set_domain, /* set_domain */
1095 msgfmt_add_message, /* add_message */
1096 msgfmt_frob_new_message /* frob_new_message */
1100 /* Read .po file FILENAME and store translation pairs. */
1102 read_catalog_file_msgfmt (char *filename, catalog_input_format_ty input_syntax)
1104 char *real_filename;
1105 FILE *fp = open_catalog_file (filename, &real_filename, true);
1106 default_catalog_reader_ty *pop;
1108 pop = default_catalog_reader_alloc (&msgfmt_methods);
1109 pop->handle_comments = false;
1110 pop->handle_filepos_comments = false;
1111 pop->allow_domain_directives = true;
1112 pop->allow_duplicates = false;
1113 pop->allow_duplicates_if_same_msgstr = false;
1114 pop->file_name = real_filename;
1117 if (current_domain != NULL)
1119 /* Keep current_domain and this->domain synchronized. */
1120 pop->domain = current_domain->domain_name;
1121 pop->mlp = current_domain->mlp;
1123 po_lex_pass_obsolete_entries (true);
1124 catalog_reader_parse ((abstract_catalog_reader_ty *) pop, fp, real_filename,
1125 filename, input_syntax);
1126 catalog_reader_free ((abstract_catalog_reader_ty *) pop);