mirror of
https://https.git.savannah.gnu.org/git/gettext.git
synced 2026-01-26 15:39:11 +00:00
msginit: Produce a merged PO file instead of failing.
* gettext-tools/src/msginit.c: Include <omp.h>, msgl-merge.h, backupfile.h, copy-file.h. (catalogname): Remove variable. (main): When the PO file already exists, create a backup file, then merge the two files. (usage): Say what happens if the output file already exists. (struct header_entry_field): New type. (fresh_fields): Renamed from fields. (NFIELDS): Remove macro. (FRESH_FIELDS_LAST_TRANSLATOR): Renamed from FIELD_LAST_TRANSLATOR. (update_fields): New variable. (UPDATE_FIELDS_LAST_TRANSLATOR): New macro. (fill_header): Add 'fresh' parameter. Allocate field_value array on the heap. * gettext-tools/src/Makefile.am (msginit_SOURCES): Add msgl-fsearch.c, msgl-merge.c. (msginit_CFLAGS, msginit_CXXFLAGS): Link with the OpenMP flags. * gettext-tools/doc/msginit.texi: Say what happens if the output file already exists. * gettext-tools/doc/gettext.texi (Creating): Change title. Mention that msginit can also be used when continuing an existing translation. * NEWS: Mention the improvement.
This commit is contained in:
parent
e6bff68d9d
commit
092e5329e6
6
NEWS
6
NEWS
@ -1,5 +1,11 @@
|
||||
Version 0.27 - October 2025
|
||||
|
||||
# Improvements for translators:
|
||||
* msginit:
|
||||
- When the PO file already exists, 'msginit' now updates it w.r.t. the
|
||||
POT file, like 'msgmerge' would do. Previously, 'msginit' failed with
|
||||
an error message in this situation.
|
||||
|
||||
# Programming languages support:
|
||||
* OCaml:
|
||||
- xgettext now supports OCaml.
|
||||
|
||||
@ -75,7 +75,7 @@
|
||||
* msgfilter: (gettext)msgfilter Invocation. Pipe a PO file through a filter.
|
||||
* msgfmt: (gettext)msgfmt Invocation. Make MO files out of PO files.
|
||||
* msggrep: (gettext)msggrep Invocation. Select part of a PO file.
|
||||
* msginit: (gettext)msginit Invocation. Create a fresh PO file.
|
||||
* msginit: (gettext)msginit Invocation. Start translating a PO file.
|
||||
* msgmerge: (gettext)msgmerge Invocation. Update a PO file from template.
|
||||
* msgunfmt: (gettext)msgunfmt Invocation. Uncompile MO file into PO file.
|
||||
* msguniq: (gettext)msguniq Invocation. Unify duplicates for PO file.
|
||||
@ -3746,15 +3746,23 @@ This is because @code{msgcat} generally is meant to produce PO files that
|
||||
are to be reviewed and edited by a translator; this is not desired here.
|
||||
|
||||
@node Creating
|
||||
@chapter Creating a New PO File
|
||||
@chapter Creating or preparing for translating a PO file
|
||||
@cindex creating a new PO file
|
||||
@cindex preparing for translating a PO file
|
||||
|
||||
When starting a new translation, the translator creates a file called
|
||||
@file{@var{LANG}.po}, as a copy of the @file{@var{package}.pot} template
|
||||
file with modifications in the initial comments (at the beginning of the file)
|
||||
and in the header entry (the first entry, near the beginning of the file).
|
||||
|
||||
The easiest way to do so is by use of the @samp{msginit} program.
|
||||
Before continuing an existing translation,
|
||||
after a new release of the package @var{package} was made,
|
||||
the translator updates the file called @file{@var{LANG}.po},
|
||||
with respect to the new @file{@var{package}.pot} template file,
|
||||
and adds her name in the @code{Last-Translator} field of the header entry.
|
||||
|
||||
The easiest way to do so, in either case,
|
||||
is by use of the @samp{msginit} program.
|
||||
For example:
|
||||
|
||||
@example
|
||||
@ -3763,10 +3771,20 @@ $ cd po
|
||||
$ msginit
|
||||
@end example
|
||||
|
||||
The alternative way is to do the copy and modifications by hand.
|
||||
To do so, the translator copies @file{@var{package}.pot} to
|
||||
@file{@var{LANG}.po}. Then she modifies the initial comments and
|
||||
the header entry of this file.
|
||||
The alternative way, without @code{msginit}, is as follows:
|
||||
@itemize @bullet
|
||||
@item
|
||||
When starting a new translation,
|
||||
the translator does the copy and modifications by hand.
|
||||
She copies @file{@var{package}.pot} to @file{@var{LANG}.po}.
|
||||
Then she modifies the initial comments and the header entry of this file.
|
||||
|
||||
@item
|
||||
When continuing an existing translation,
|
||||
the translator invokes
|
||||
@code{msgmerge --previous -o @var{lang}.new.po @var{lang}.po @var{package}.pot}
|
||||
and @code{mv @var{lang}.new.po @var{lang}.po}.
|
||||
@end itemize
|
||||
|
||||
@menu
|
||||
* msginit Invocation:: Invoking the @code{msginit} Program
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
@c This file is part of the GNU gettext manual.
|
||||
@c Copyright (C) 1995-2019 Free Software Foundation, Inc.
|
||||
@c Copyright (C) 1995-2025 Free Software Foundation, Inc.
|
||||
@c See the file gettext.texi for copying conditions.
|
||||
|
||||
@pindex msginit
|
||||
@ -77,8 +77,12 @@ Write output to specified PO file.
|
||||
@end table
|
||||
|
||||
If no output file is given, it depends on the @samp{--locale} option or the
|
||||
user's locale setting. If it is @samp{-}, the results are written to
|
||||
standard output.
|
||||
user's locale setting.
|
||||
|
||||
If the output file already exists, it is merged with the input file,
|
||||
as if through @command{msgmerge}.
|
||||
|
||||
If it is @samp{-}, the results are written to standard output.
|
||||
|
||||
@subsection Input file syntax
|
||||
|
||||
|
||||
@ -397,7 +397,7 @@ else
|
||||
msggrep_SOURCES = ../woe32dll/c++msggrep.cc
|
||||
endif
|
||||
msginit_SOURCES = msginit.c
|
||||
msginit_SOURCES += lang-table.c plural-count.c
|
||||
msginit_SOURCES += msgl-fsearch.c msgl-merge.c lang-table.c plural-count.c
|
||||
msginit_SOURCES += ../../gettext-runtime/intl/localealias.c
|
||||
if !WOE32DLL
|
||||
msguniq_SOURCES = msguniq.c
|
||||
@ -491,6 +491,8 @@ endif
|
||||
# Compile-time flags for particular source files.
|
||||
msgmerge_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS)
|
||||
msgmerge_CXXFLAGS = $(AM_CXXFLAGS) $(OPENMP_CFLAGS)
|
||||
msginit_CFLAGS = $(AM_CFLAGS) $(OPENMP_CFLAGS)
|
||||
msginit_CXXFLAGS = $(AM_CXXFLAGS) $(OPENMP_CFLAGS)
|
||||
# On mingw, the compiler option '-fno-threadsafe-statics' avoids requiring
|
||||
# the symbols __cxa_guard_acquire and __cxa_guard_release, which in turn
|
||||
# avoids a dependency towards libstdc++.
|
||||
|
||||
@ -35,6 +35,9 @@
|
||||
#if HAVE_PWD_H
|
||||
# include <pwd.h>
|
||||
#endif
|
||||
#ifdef _OPENMP
|
||||
# include <omp.h>
|
||||
#endif
|
||||
|
||||
#include <textstyle.h>
|
||||
|
||||
@ -49,6 +52,7 @@
|
||||
#include "c-strstr.h"
|
||||
#include "c-strcase.h"
|
||||
#include "message.h"
|
||||
#include "msgl-merge.h"
|
||||
#include "read-catalog-file.h"
|
||||
#include "read-po.h"
|
||||
#include "read-properties.h"
|
||||
@ -74,6 +78,8 @@
|
||||
#include "plural-count.h"
|
||||
#include "spawn-pipe.h"
|
||||
#include "wait-process.h"
|
||||
#include "backupfile.h"
|
||||
#include "copy-file.h"
|
||||
#include "xsetenv.h"
|
||||
#include "xstriconv.h"
|
||||
#include "str-list.h"
|
||||
@ -95,9 +101,6 @@ extern const char * _nl_expand_alias (const char *name);
|
||||
/* Locale name. */
|
||||
static const char *locale;
|
||||
|
||||
/* Language (ISO-639 code) and optional territory (ISO-3166 code). */
|
||||
static const char *catalogname;
|
||||
|
||||
/* Language (ISO-639 code). */
|
||||
static const char *language;
|
||||
|
||||
@ -110,7 +113,7 @@ static const char *find_pot (void);
|
||||
static const char *catalogname_for_locale (const char *locale);
|
||||
static const char *language_of_locale (const char *locale);
|
||||
static char *get_field (const char *header, const char *field);
|
||||
static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp);
|
||||
static msgdomain_list_ty *fill_header (msgdomain_list_ty *mdlp, bool fresh);
|
||||
static msgdomain_list_ty *update_msgstr_plurals (msgdomain_list_ty *mdlp);
|
||||
|
||||
|
||||
@ -306,34 +309,54 @@ This is necessary so you can test your translations.\n"),
|
||||
|
||||
/* Default output file name is CATALOGNAME.po. */
|
||||
if (output_file == NULL)
|
||||
output_file = xasprintf ("%s.po", catalogname);
|
||||
|
||||
if (strcmp (output_file, "-") != 0
|
||||
&& access (output_file, F_OK) == 0)
|
||||
{
|
||||
output_file = xasprintf ("%s.po", catalogname);
|
||||
/* The output PO file already exists. Assume the translator wants to
|
||||
continue, based on these translations. */
|
||||
|
||||
/* But don't overwrite existing PO files. */
|
||||
if (access (output_file, F_OK) == 0)
|
||||
{
|
||||
multiline_error (xstrdup (""),
|
||||
xasprintf (_("\
|
||||
Output file %s already exists.\n\
|
||||
Please specify the locale through the --locale option or\n\
|
||||
the output .po file through the --output-file option.\n"),
|
||||
output_file));
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
/* First, create a backup file. */
|
||||
{
|
||||
const char *backup_suffix_string = getenv ("SIMPLE_BACKUP_SUFFIX");
|
||||
if (backup_suffix_string != NULL && backup_suffix_string[0] != '\0')
|
||||
simple_backup_suffix = backup_suffix_string;
|
||||
}
|
||||
{
|
||||
char *backup_file = find_backup_file_name (output_file, simple);
|
||||
xcopy_file_preserving (output_file, backup_file);
|
||||
}
|
||||
|
||||
/* Initialize OpenMP. */
|
||||
#ifdef _OPENMP
|
||||
openmp_init ();
|
||||
#endif
|
||||
|
||||
/* Read both files and merge them. */
|
||||
quiet = true;
|
||||
keep_previous = true;
|
||||
msgdomain_list_ty *def;
|
||||
result = merge (output_file, input_file, input_syntax, &def);
|
||||
|
||||
/* Update the header entry. */
|
||||
result = fill_header (result, false);
|
||||
}
|
||||
|
||||
/* Read input file. */
|
||||
result = read_catalog_file (input_file, input_syntax);
|
||||
check_pot_charset (result, input_file);
|
||||
|
||||
/* Fill the header entry. */
|
||||
result = fill_header (result);
|
||||
|
||||
/* Initialize translations. */
|
||||
if (strcmp (language, "en") == 0)
|
||||
result = msgdomain_list_english (result);
|
||||
else
|
||||
result = update_msgstr_plurals (result);
|
||||
{
|
||||
/* Read input file. */
|
||||
result = read_catalog_file (input_file, input_syntax);
|
||||
check_pot_charset (result, input_file);
|
||||
|
||||
/* Fill the header entry. */
|
||||
result = fill_header (result, true);
|
||||
|
||||
/* Initialize translations. */
|
||||
if (strcmp (language, "en") == 0)
|
||||
result = msgdomain_list_english (result);
|
||||
else
|
||||
result = update_msgstr_plurals (result);
|
||||
}
|
||||
|
||||
/* Write the modified message list out. */
|
||||
msgdomain_list_print (result, output_file, output_syntax,
|
||||
@ -383,7 +406,11 @@ Output file location:\n"));
|
||||
-o, --output-file=FILE write output to specified PO file\n"));
|
||||
printf (_("\
|
||||
If no output file is given, it depends on the --locale option or the user's\n\
|
||||
locale setting. If it is -, the results are written to standard output.\n"));
|
||||
locale setting.\n\
|
||||
If the output file already exists, it is merged with the input file,\n\
|
||||
as if through '%s'.\n\
|
||||
If it is -, the results are written to standard output.\n"),
|
||||
"msgmerge");
|
||||
printf ("\n");
|
||||
printf (_("\
|
||||
Input file syntax:\n"));
|
||||
@ -1491,13 +1518,14 @@ plural_forms ()
|
||||
}
|
||||
|
||||
|
||||
static struct
|
||||
struct header_entry_field
|
||||
{
|
||||
const char *name;
|
||||
const char * (*getter0) (void);
|
||||
const char * (*getter1) (const char *header);
|
||||
}
|
||||
fields[] =
|
||||
};
|
||||
|
||||
static struct header_entry_field fresh_fields[] =
|
||||
{
|
||||
{ "Project-Id-Version", NULL, project_id_version },
|
||||
{ "PO-Revision-Date", NULL, po_revision_date },
|
||||
@ -1509,9 +1537,13 @@ fields[] =
|
||||
{ "Content-Transfer-Encoding", content_transfer_encoding, NULL },
|
||||
{ "Plural-Forms", plural_forms, NULL }
|
||||
};
|
||||
#define FRESH_FIELDS_LAST_TRANSLATOR 2
|
||||
|
||||
#define NFIELDS SIZEOF (fields)
|
||||
#define FIELD_LAST_TRANSLATOR 2
|
||||
static struct header_entry_field update_fields[] =
|
||||
{
|
||||
{ "Last-Translator", last_translator, NULL }
|
||||
};
|
||||
#define UPDATE_FIELDS_LAST_TRANSLATOR 0
|
||||
|
||||
|
||||
/* Retrieve a freshly allocated copy of a field's value. */
|
||||
@ -1763,7 +1795,7 @@ subst_string_list (string_list_ty *slp,
|
||||
|
||||
/* Fill the templates in all fields of the header entry. */
|
||||
static msgdomain_list_ty *
|
||||
fill_header (msgdomain_list_ty *mdlp)
|
||||
fill_header (msgdomain_list_ty *mdlp, bool fresh)
|
||||
{
|
||||
/* Determine the desired encoding to the PO file.
|
||||
If the POT file contains charset=UTF-8, it means that the POT file
|
||||
@ -1813,10 +1845,25 @@ fill_header (msgdomain_list_ty *mdlp)
|
||||
|
||||
/* Cache the strings filled in, for use when there are multiple domains
|
||||
and a header entry for each domain. */
|
||||
const char *field_value[NFIELDS];
|
||||
struct header_entry_field *fields;
|
||||
size_t nfields;
|
||||
size_t field_last_translator;
|
||||
if (fresh)
|
||||
{
|
||||
fields = fresh_fields;
|
||||
nfields = SIZEOF (fresh_fields);
|
||||
field_last_translator = FRESH_FIELDS_LAST_TRANSLATOR;
|
||||
}
|
||||
else
|
||||
{
|
||||
fields = update_fields;
|
||||
nfields = SIZEOF (update_fields);
|
||||
field_last_translator = UPDATE_FIELDS_LAST_TRANSLATOR;
|
||||
}
|
||||
const char **field_value = XNMALLOC (nfields, const char *);
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < NFIELDS; i++)
|
||||
for (i = 0; i < nfields; i++)
|
||||
field_value[i] = NULL;
|
||||
|
||||
for (k = 0; k < mdlp->nitems; k++)
|
||||
@ -1848,7 +1895,7 @@ fill_header (msgdomain_list_ty *mdlp)
|
||||
header = xstrdup (header_mp->msgstr);
|
||||
|
||||
/* Fill in the fields. */
|
||||
for (i = 0; i < NFIELDS; i++)
|
||||
for (i = 0; i < nfields; i++)
|
||||
{
|
||||
if (field_value[i] == NULL)
|
||||
field_value[i] =
|
||||
@ -1881,7 +1928,7 @@ fill_header (msgdomain_list_ty *mdlp)
|
||||
subst[1][0] = "PACKAGE";
|
||||
subst[1][1] = id;
|
||||
subst[2][0] = "FIRST AUTHOR <EMAIL@ADDRESS>";
|
||||
subst[2][1] = field_value[FIELD_LAST_TRANSLATOR];
|
||||
subst[2][1] = field_value[field_last_translator];
|
||||
subst[3][0] = "YEAR";
|
||||
subst[3][1] =
|
||||
xasprintf ("%d",
|
||||
@ -1894,6 +1941,8 @@ fill_header (msgdomain_list_ty *mdlp)
|
||||
}
|
||||
}
|
||||
|
||||
free (field_value);
|
||||
|
||||
return mdlp;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user