xref: /freebsd-src/contrib/libxo/doc/howto.rst (revision 983afe3373c427a080f06dccec820b20891be186)
1*983afe33SPhil Shafer
2*983afe33SPhil ShaferHowtos: Focused Directions
3*983afe33SPhil Shafer==========================
4*983afe33SPhil Shafer
5*983afe33SPhil ShaferThis section provides task-oriented instructions for selected tasks.
6*983afe33SPhil ShaferIf you have a task that needs instructions, please open a request as
7*983afe33SPhil Shaferan enhancement issue on github.
8*983afe33SPhil Shafer
9*983afe33SPhil ShaferHowto: Report bugs
10*983afe33SPhil Shafer------------------
11*983afe33SPhil Shafer
12*983afe33SPhil Shaferlibxo uses github to track bugs or request enhancements.  Please use
13*983afe33SPhil Shaferthe following URL:
14*983afe33SPhil Shafer
15*983afe33SPhil Shafer  https://github.com/Juniper/libxo/issues
16*983afe33SPhil Shafer
17*983afe33SPhil ShaferHowto: Install libxo
18*983afe33SPhil Shafer--------------------
19*983afe33SPhil Shafer
20*983afe33SPhil Shaferlibxo is open source, under a new BSD license.  Source code is
21*983afe33SPhil Shaferavailable on github, as are recent releases.  To get the most
22*983afe33SPhil Shafercurrent release, please visit:
23*983afe33SPhil Shafer
24*983afe33SPhil Shafer  https://github.com/Juniper/libxo/releases
25*983afe33SPhil Shafer
26*983afe33SPhil ShaferAfter downloading and untarring the source code, building involves the
27*983afe33SPhil Shaferfollowing steps::
28*983afe33SPhil Shafer
29*983afe33SPhil Shafer    sh bin/setup.sh
30*983afe33SPhil Shafer    cd build
31*983afe33SPhil Shafer    ../configure
32*983afe33SPhil Shafer    make
33*983afe33SPhil Shafer    make test
34*983afe33SPhil Shafer    sudo make install
35*983afe33SPhil Shafer
36*983afe33SPhil Shaferlibxo uses a distinct "*build*" directory to keep generated files
37*983afe33SPhil Shaferseparated from source files.
38*983afe33SPhil Shafer
39*983afe33SPhil Shafer.. index:: configure
40*983afe33SPhil Shafer
41*983afe33SPhil ShaferUse "`../configure --help`" to display available configuration
42*983afe33SPhil Shaferoptions, which include the following::
43*983afe33SPhil Shafer
44*983afe33SPhil Shafer  --enable-warnings      Turn on compiler warnings
45*983afe33SPhil Shafer  --enable-debug         Turn on debugging
46*983afe33SPhil Shafer  --enable-text-only     Turn on text-only rendering
47*983afe33SPhil Shafer  --enable-printflike    Enable use of GCC __printflike attribute
48*983afe33SPhil Shafer  --disable-libxo-options  Turn off support for LIBXO_OPTIONS
49*983afe33SPhil Shafer  --with-gettext=PFX     Specify location of gettext installation
50*983afe33SPhil Shafer  --with-libslax-prefix=PFX  Specify location of libslax config
51*983afe33SPhil Shafer
52*983afe33SPhil ShaferCompiler warnings are a very good thing, but recent compiler version
53*983afe33SPhil Shaferhave added some very pedantic checks.  While every attempt is made to
54*983afe33SPhil Shaferkeep libxo code warning-free, warnings are now optional.  If you are
55*983afe33SPhil Shaferdoing development work on libxo, it is required that you
56*983afe33SPhil Shaferuse --enable-warnings to keep the code warning free, but most users
57*983afe33SPhil Shaferneed not use this option.
58*983afe33SPhil Shafer
59*983afe33SPhil Shafer.. index:: --enable-text-only
60*983afe33SPhil Shafer
61*983afe33SPhil Shaferlibxo provides the `--enable-text-only` option to reduce the
62*983afe33SPhil Shaferfootprint of the library for smaller installations.  XML, JSON, and
63*983afe33SPhil ShaferHTML rendering logic is removed.
64*983afe33SPhil Shafer
65*983afe33SPhil Shafer.. index:: --with-gettext
66*983afe33SPhil Shafer
67*983afe33SPhil ShaferThe gettext library does not provide a simple means of learning its
68*983afe33SPhil Shaferlocation, but libxo will look for it in /usr and /opt/local.  If
69*983afe33SPhil Shaferinstalled elsewhere, the installer will need to provide this
70*983afe33SPhil Shaferinformation using the "`--with-gettext=/dir/path`" option.
71*983afe33SPhil Shafer
72*983afe33SPhil Shafer.. index:: libslax
73*983afe33SPhil Shafer
74*983afe33SPhil Shaferlibslax is not required by libxo; it contains the "oxtradoc" program
75*983afe33SPhil Shaferused to format documentation.
76*983afe33SPhil Shafer
77*983afe33SPhil ShaferFor additional information, see :ref:`building`.
78*983afe33SPhil Shafer
79*983afe33SPhil ShaferHowto: Convert command line applications
80*983afe33SPhil Shafer----------------------------------------
81*983afe33SPhil Shafer
82*983afe33SPhil ShaferCommon question: How do I convert an existing command line application?
83*983afe33SPhil Shafer
84*983afe33SPhil ShaferThere are four basic steps for converting command line application to
85*983afe33SPhil Shaferuse libxo::
86*983afe33SPhil Shafer
87*983afe33SPhil Shafer- Setting up the context
88*983afe33SPhil Shafer- Converting printf calls
89*983afe33SPhil Shafer- Creating hierarchy
90*983afe33SPhil Shafer- Converting error functions
91*983afe33SPhil Shafer
92*983afe33SPhil ShaferSetting up the context
93*983afe33SPhil Shafer~~~~~~~~~~~~~~~~~~~~~~
94*983afe33SPhil Shafer
95*983afe33SPhil ShaferTo use libxo, you'll need to include the "xo.h" header file in your
96*983afe33SPhil Shafersource code files::
97*983afe33SPhil Shafer
98*983afe33SPhil Shafer    #include <libxo/xo.h>
99*983afe33SPhil Shafer
100*983afe33SPhil ShaferIn your main() function, you'll need to call xo_parse_args to handling
101*983afe33SPhil Shaferargument parsing (:ref:`xo_parse_args`).  This function removes
102*983afe33SPhil Shaferlibxo-specific arguments the program's argv and returns either the
103*983afe33SPhil Shafernumber of remaining arguments or -1 to indicate an error::
104*983afe33SPhil Shafer
105*983afe33SPhil Shafer    int
106*983afe33SPhil Shafer    main (int argc, char **argv)
107*983afe33SPhil Shafer    {
108*983afe33SPhil Shafer        argc = xo_parse_args(argc, argv);
109*983afe33SPhil Shafer        if (argc < 0)
110*983afe33SPhil Shafer            return argc;
111*983afe33SPhil Shafer        ....
112*983afe33SPhil Shafer    }
113*983afe33SPhil Shafer
114*983afe33SPhil Shafer.. index:: atexit
115*983afe33SPhil Shafer.. index:: xo_finish_atexit
116*983afe33SPhil Shafer
117*983afe33SPhil ShaferAt the bottom of your main(), you'll need to call xo_finish() to
118*983afe33SPhil Shafercomplete output processing for the default handle (:ref:`handles`).  This
119*983afe33SPhil Shaferis required to flush internal information buffers.  libxo provides the
120*983afe33SPhil Shaferxo_finish_atexit function that is suitable for use with the
121*983afe33SPhil Shafer:manpage:`atexit(3)` function::
122*983afe33SPhil Shafer
123*983afe33SPhil Shafer    atexit(xo_finish_atexit);
124*983afe33SPhil Shafer
125*983afe33SPhil ShaferConverting printf Calls
126*983afe33SPhil Shafer~~~~~~~~~~~~~~~~~~~~~~~
127*983afe33SPhil Shafer
128*983afe33SPhil ShaferThe second task is inspecting code for :manpage:`printf(3)` calls and
129*983afe33SPhil Shaferreplacing them with xo_emit() calls.  The format strings are similar
130*983afe33SPhil Shaferin task, but libxo format strings wrap output fields in braces.  The
131*983afe33SPhil Shaferfollowing two calls produce identical text output::
132*983afe33SPhil Shafer
133*983afe33SPhil Shafer  OLD::
134*983afe33SPhil Shafer    printf("There are %d %s events\n", count, etype);
135*983afe33SPhil Shafer
136*983afe33SPhil Shafer  NEW::
137*983afe33SPhil Shafer    xo_emit("There are {:count/%d} {:event} events\n", count, etype);
138*983afe33SPhil Shafer
139*983afe33SPhil Shafer"count" and "event" are used as names for JSON and XML output.  The
140*983afe33SPhil Shafer"count" field uses the format "%d" and "event" uses the default "%s"
141*983afe33SPhil Shaferformat.  Both are "value" roles, which is the default role.
142*983afe33SPhil Shafer
143*983afe33SPhil ShaferSince text outside of output fields is passed verbatim, other roles
144*983afe33SPhil Shaferare less important, but their proper use can help make output more
145*983afe33SPhil Shaferuseful.  The "note" and "label" roles allow HTML output to recognize
146*983afe33SPhil Shaferthe relationship between text and the associated values, allowing
147*983afe33SPhil Shaferappropriate "hover" and "onclick" behavior.  Using the "units" role
148*983afe33SPhil Shaferallows the presentation layer to perform conversions when needed.  The
149*983afe33SPhil Shafer"warning" and "error" roles allows use of color and font to draw
150*983afe33SPhil Shaferattention to warnings.  The "padding" role makes the use of vital
151*983afe33SPhil Shaferwhitespace more clear (:ref:`padding-role`).
152*983afe33SPhil Shafer
153*983afe33SPhil ShaferThe "*title*" role indicates the headings of table and sections.  This
154*983afe33SPhil Shaferallows HTML output to use CSS to make this relationship more obvious::
155*983afe33SPhil Shafer
156*983afe33SPhil Shafer  OLD::
157*983afe33SPhil Shafer    printf("Statistics:\n");
158*983afe33SPhil Shafer
159*983afe33SPhil Shafer  NEW::
160*983afe33SPhil Shafer    xo_emit("{T:Statistics}:\n");
161*983afe33SPhil Shafer
162*983afe33SPhil ShaferThe "*color*" roles controls foreground and background colors, as well
163*983afe33SPhil Shaferas effects like bold and underline (see :ref:`color-role`)::
164*983afe33SPhil Shafer
165*983afe33SPhil Shafer  NEW::
166*983afe33SPhil Shafer    xo_emit("{C:bold}required{C:}\n");
167*983afe33SPhil Shafer
168*983afe33SPhil ShaferFinally, the start- and stop-anchor roles allow justification and
169*983afe33SPhil Shaferpadding over multiple fields (see :ref:`anchor-role`)::
170*983afe33SPhil Shafer
171*983afe33SPhil Shafer  OLD::
172*983afe33SPhil Shafer    snprintf(buf, sizeof(buf), "(%u/%u/%u)", min, ave, max);
173*983afe33SPhil Shafer    printf("%30s", buf);
174*983afe33SPhil Shafer
175*983afe33SPhil Shafer  NEW::
176*983afe33SPhil Shafer    xo_emit("{[:30}({:minimum/%u}/{:average/%u}/{:maximum/%u}{]:}",
177*983afe33SPhil Shafer            min, ave, max);
178*983afe33SPhil Shafer
179*983afe33SPhil ShaferCreating Hierarchy
180*983afe33SPhil Shafer~~~~~~~~~~~~~~~~~~
181*983afe33SPhil Shafer
182*983afe33SPhil ShaferText output doesn't have any sort of hierarchy, but XML and JSON
183*983afe33SPhil Shaferrequire this.  Typically applications use indentation to represent
184*983afe33SPhil Shaferthese relationship::
185*983afe33SPhil Shafer
186*983afe33SPhil Shafer  OLD::
187*983afe33SPhil Shafer    printf("table %d\n", tnum);
188*983afe33SPhil Shafer    for (i = 0; i < tmax; i++) {
189*983afe33SPhil Shafer        printf("    %s %d\n", table[i].name, table[i].size);
190*983afe33SPhil Shafer    }
191*983afe33SPhil Shafer
192*983afe33SPhil Shafer  NEW::
193*983afe33SPhil Shafer    xo_emit("{T:/table %d}\n", tnum);
194*983afe33SPhil Shafer    xo_open_list("table");
195*983afe33SPhil Shafer    for (i = 0; i < tmax; i++) {
196*983afe33SPhil Shafer        xo_open_instance("table");
197*983afe33SPhil Shafer        xo_emit("{P:    }{k:name} {:size/%d}\n",
198*983afe33SPhil Shafer                table[i].name, table[i].size);
199*983afe33SPhil Shafer        xo_close_instance("table");
200*983afe33SPhil Shafer    }
201*983afe33SPhil Shafer    xo_close_list("table");
202*983afe33SPhil Shafer
203*983afe33SPhil ShaferThe open and close list functions are used before and after the list,
204*983afe33SPhil Shaferand the open and close instance functions are used before and after
205*983afe33SPhil Shafereach instance with in the list.
206*983afe33SPhil Shafer
207*983afe33SPhil ShaferTypically these developer looks for a "for" loop as an indication of
208*983afe33SPhil Shaferwhere to put these calls.
209*983afe33SPhil Shafer
210*983afe33SPhil ShaferIn addition, the open and close container functions allow for
211*983afe33SPhil Shaferorganization levels of hierarchy::
212*983afe33SPhil Shafer
213*983afe33SPhil Shafer  OLD::
214*983afe33SPhil Shafer    printf("Paging information:\n");
215*983afe33SPhil Shafer    printf("    Free:      %lu\n", free);
216*983afe33SPhil Shafer    printf("    Active:    %lu\n", active);
217*983afe33SPhil Shafer    printf("    Inactive:  %lu\n", inactive);
218*983afe33SPhil Shafer
219*983afe33SPhil Shafer  NEW::
220*983afe33SPhil Shafer    xo_open_container("paging-information");
221*983afe33SPhil Shafer    xo_emit("{P:    }{L:Free:      }{:free/%lu}\n", free);
222*983afe33SPhil Shafer    xo_emit("{P:    }{L:Active:    }{:active/%lu}\n", active);
223*983afe33SPhil Shafer    xo_emit("{P:    }{L:Inactive:  }{:inactive/%lu}\n", inactive);
224*983afe33SPhil Shafer    xo_close_container("paging-information");
225*983afe33SPhil Shafer
226*983afe33SPhil ShaferConverting Error Functions
227*983afe33SPhil Shafer~~~~~~~~~~~~~~~~~~~~~~~~~~
228*983afe33SPhil Shafer
229*983afe33SPhil Shaferlibxo provides variants of the standard error and warning functions,
230*983afe33SPhil Shafer:manpage:`err(3)` and :manpage:`warn(3)`.  There are two variants, one
231*983afe33SPhil Shaferfor putting the errors on standard error, and the other writes the
232*983afe33SPhil Shafererrors and warnings to the handle using the appropriate encoding
233*983afe33SPhil Shaferstyle::
234*983afe33SPhil Shafer
235*983afe33SPhil Shafer  OLD::
236*983afe33SPhil Shafer    err(1, "cannot open output file: %s", file);
237*983afe33SPhil Shafer
238*983afe33SPhil Shafer  NEW::
239*983afe33SPhil Shafer    xo_err(1, "cannot open output file: %s", file);
240*983afe33SPhil Shafer    xo_emit_err(1, "cannot open output file: {:filename}", file);
241*983afe33SPhil Shafer
242*983afe33SPhil Shafer.. index:: xo_finish
243*983afe33SPhil Shafer
244*983afe33SPhil ShaferCall xo_finish
245*983afe33SPhil Shafer~~~~~~~~~~~~~~
246*983afe33SPhil Shafer
247*983afe33SPhil ShaferOne important item: call `xo_finish` at the end of your program so
248*983afe33SPhil Shaferensure that all buffered data is written out.  You can call it
249*983afe33SPhil Shaferexplicitly call it, or use :manpage:`atexit(3)` to have
250*983afe33SPhil Shafer`xo_finish_atexit` called implicitly on exit::
251*983afe33SPhil Shafer
252*983afe33SPhil Shafer  OLD::
253*983afe33SPhil Shafer    exit(0);
254*983afe33SPhil Shafer
255*983afe33SPhil Shafer  NEW::
256*983afe33SPhil Shafer    xo_finish();
257*983afe33SPhil Shafer    exit(0);
258*983afe33SPhil Shafer
259*983afe33SPhil ShaferHowto: Use "xo" in Shell Scripts
260*983afe33SPhil Shafer--------------------------------
261*983afe33SPhil Shafer
262*983afe33SPhil Shafer.. admonition:: Needed
263*983afe33SPhil Shafer
264*983afe33SPhil Shafer  Documentation is needed for this area.
265*983afe33SPhil Shafer
266*983afe33SPhil Shafer.. index:: Internationalization (i18n)
267*983afe33SPhil Shafer.. index:: gettext
268*983afe33SPhil Shafer.. index:: xopo
269*983afe33SPhil Shafer
270*983afe33SPhil Shafer.. _i18n:
271*983afe33SPhil Shafer
272*983afe33SPhil ShaferHowto: Internationalization (i18n)
273*983afe33SPhil Shafer-----------------------------------------------
274*983afe33SPhil Shafer
275*983afe33SPhil Shafer    How do I use libxo to support internationalization?
276*983afe33SPhil Shafer
277*983afe33SPhil Shaferlibxo allows format and field strings to be used a keys into message
278*983afe33SPhil Shafercatalogs to enable translation into a user's native language by
279*983afe33SPhil Shaferinvoking the standard :manpage:`gettext(3)` functions.
280*983afe33SPhil Shafer
281*983afe33SPhil Shafergettext setup is a bit complicated: text strings are extracted from
282*983afe33SPhil Shafersource files into "*portable object template*" (.pot) files using the
283*983afe33SPhil Shafer`xgettext` command.  For each language, this template file is used as
284*983afe33SPhil Shaferthe source for a message catalog in the "*portable object*" (.po)
285*983afe33SPhil Shaferformat, which are translated by hand and compiled into "*machine
286*983afe33SPhil Shaferobject*" (.mo) files using the `msgfmt` command.  The .mo files are
287*983afe33SPhil Shaferthen typically installed in the /usr/share/locale or
288*983afe33SPhil Shafer/opt/local/share/locale directories.  At run time, the user's language
289*983afe33SPhil Shafersettings are used to select a .mo file which is searched for matching
290*983afe33SPhil Shafermessages.  Text strings in the source code are used as keys to look up
291*983afe33SPhil Shaferthe native language strings in the .mo file.
292*983afe33SPhil Shafer
293*983afe33SPhil ShaferSince the xo_emit format string is used as the key into the message
294*983afe33SPhil Shafercatalog, libxo removes unimportant field formatting and modifiers from
295*983afe33SPhil Shaferthe format string before use so that minor formatting changes will not
296*983afe33SPhil Shaferimpact the expensive translation process.  We don't want a developer
297*983afe33SPhil Shaferchange such as changing "/%06d" to "/%08d" to force hand inspection of
298*983afe33SPhil Shaferall .po files.  The simplified version can be generated for a single
299*983afe33SPhil Shafermessage using the `xopo -s $text` command, or an entire .pot can be
300*983afe33SPhil Shafertranslated using the `xopo -f $input -o $output` command::
301*983afe33SPhil Shafer
302*983afe33SPhil Shafer    EXAMPLE:
303*983afe33SPhil Shafer        % xopo -s "There are {:count/%u} {:event/%.6s} events\n"
304*983afe33SPhil Shafer        There are {:count} {:event} events\n
305*983afe33SPhil Shafer
306*983afe33SPhil Shafer    Recommended workflow:
307*983afe33SPhil Shafer        # Extract text messages
308*983afe33SPhil Shafer	xgettext --default-domain=foo --no-wrap \
309*983afe33SPhil Shafer	    --add-comments --keyword=xo_emit --keyword=xo_emit_h \
310*983afe33SPhil Shafer	    --keyword=xo_emit_warn -C -E -n --foreign-user \
311*983afe33SPhil Shafer	    -o foo.pot.raw foo.c
312*983afe33SPhil Shafer
313*983afe33SPhil Shafer        # Simplify format strings for libxo
314*983afe33SPhil Shafer        xopo -f foo.pot.raw -o foo.pot
315*983afe33SPhil Shafer
316*983afe33SPhil Shafer        # For a new language, just copy the file
317*983afe33SPhil Shafer        cp foo.pot po/LC/my_lang/foo.po
318*983afe33SPhil Shafer
319*983afe33SPhil Shafer        # For an existing language:
320*983afe33SPhil Shafer        msgmerge --no-wrap po/LC/my_lang/foo.po \
321*983afe33SPhil Shafer                foo.pot -o po/LC/my_lang/foo.po.new
322*983afe33SPhil Shafer
323*983afe33SPhil Shafer        # Now the hard part: translate foo.po using tools
324*983afe33SPhil Shafer        # like poedit or emacs' po-mode
325*983afe33SPhil Shafer
326*983afe33SPhil Shafer        # Compile the finished file; Use of msgfmt's "-v" option is
327*983afe33SPhil Shafer        # strongly encouraged, so that "fuzzy" entries are reported.
328*983afe33SPhil Shafer        msgfmt -v -o po/my_lang/LC_MESSAGES/foo.mo po/my_lang/foo.po
329*983afe33SPhil Shafer
330*983afe33SPhil Shafer        # Install the .mo file
331*983afe33SPhil Shafer        sudo cp po/my_lang/LC_MESSAGES/foo.mo \
332*983afe33SPhil Shafer                /opt/local/share/locale/my_lang/LC_MESSAGE/
333*983afe33SPhil Shafer
334*983afe33SPhil ShaferOnce these steps are complete, you can use the `gettext` command to
335*983afe33SPhil Shafertest the message catalog::
336*983afe33SPhil Shafer
337*983afe33SPhil Shafer    gettext -d foo -e "some text"
338*983afe33SPhil Shafer
339*983afe33SPhil Shaferi18n and xo_emit
340*983afe33SPhil Shafer~~~~~~~~~~~~~~~~
341*983afe33SPhil Shafer
342*983afe33SPhil ShaferThere are three features used in libxo used to support i18n:
343*983afe33SPhil Shafer
344*983afe33SPhil Shafer- The "{G:}" role looks for a translation of the format string.
345*983afe33SPhil Shafer- The "{g:}" modifier looks for a translation of the field.
346*983afe33SPhil Shafer- The "{p:}" modifier looks for a pluralized version of the field.
347*983afe33SPhil Shafer
348*983afe33SPhil ShaferTogether these three flags allows a single function call to give
349*983afe33SPhil Shafernative language support, as well as libxo's normal XML, JSON, and HTML
350*983afe33SPhil Shafersupport::
351*983afe33SPhil Shafer
352*983afe33SPhil Shafer    printf(gettext("Received %zu %s from {g:server} server\n"),
353*983afe33SPhil Shafer           counter, ngettext("byte", "bytes", counter),
354*983afe33SPhil Shafer           gettext("web"));
355*983afe33SPhil Shafer
356*983afe33SPhil Shafer    xo_emit("{G:}Received {:received/%zu} {Ngp:byte,bytes} "
357*983afe33SPhil Shafer            "from {g:server} server\n", counter, "web");
358*983afe33SPhil Shafer
359*983afe33SPhil Shaferlibxo will see the "{G:}" role and will first simplify the format
360*983afe33SPhil Shaferstring, removing field formats and modifiers::
361*983afe33SPhil Shafer
362*983afe33SPhil Shafer    "Received {:received} {N:byte,bytes} from {:server} server\n"
363*983afe33SPhil Shafer
364*983afe33SPhil Shaferlibxo calls :manpage:`gettext(3)` with that string to get a localized
365*983afe33SPhil Shaferversion.  If your language were *Pig Latin*, the result might look
366*983afe33SPhil Shaferlike::
367*983afe33SPhil Shafer
368*983afe33SPhil Shafer    "Eceivedray {:received} {N:byte,bytes} omfray "
369*983afe33SPhil Shafer               "{:server} erversay\n"
370*983afe33SPhil Shafer
371*983afe33SPhil ShaferNote the field names do not change and they should not be translated.
372*983afe33SPhil ShaferThe contents of the note ("byte,bytes") should also not be translated,
373*983afe33SPhil Shafersince the "g" modifier will need the untranslated value as the key for
374*983afe33SPhil Shaferthe message catalog.
375*983afe33SPhil Shafer
376*983afe33SPhil ShaferThe field "{g:server}" requests the rendered value of the field be
377*983afe33SPhil Shafertranslated using :manpage:`gettext(3)`.  In this example, "web" would
378*983afe33SPhil Shaferbe used.
379*983afe33SPhil Shafer
380*983afe33SPhil ShaferThe field "{Ngp:byte,bytes}" shows an example of plural form using the
381*983afe33SPhil Shafer"{p:}" modifier with the "{g:}" modifier.  The base singular and plural
382*983afe33SPhil Shaferforms appear inside the field, separated by a comma.  At run time,
383*983afe33SPhil Shaferlibxo uses the previous field's numeric value to decide which form to
384*983afe33SPhil Shaferuse by calling :manpage:`ngettext(3)`.
385*983afe33SPhil Shafer
386*983afe33SPhil ShaferIf a domain name is needed, it can be supplied as the content of the
387*983afe33SPhil Shafer{G:} role.  Domain names remain in use throughout the format string
388*983afe33SPhil Shaferuntil cleared with another domain name::
389*983afe33SPhil Shafer
390*983afe33SPhil Shafer    printf(dgettext("dns", "Host %s not found: %d(%s)\n"),
391*983afe33SPhil Shafer        name, errno, dgettext("strerror", strerror(errno)));
392*983afe33SPhil Shafer
393*983afe33SPhil Shafer    xo_emit("{G:dns}Host {:hostname} not found: "
394*983afe33SPhil Shafer            "%d({G:strerror}{g:%m})\n", name, errno);
395