gettext: C#

 
 15.5.4 C#
 ---------
 
 RPMs
      mono
 
 Ubuntu packages
      mono-mcs
 
 File extension
      ‘cs’
 
 String syntax
      ‘"abc"’, ‘@"abc"’
 
 gettext shorthand
      _("abc")
 
 gettext/ngettext functions
      ‘GettextResourceManager.GetString’,
      ‘GettextResourceManager.GetPluralString’
      ‘GettextResourceManager.GetParticularString’
      ‘GettextResourceManager.GetParticularPluralString’
 
 textdomain
      ‘new GettextResourceManager(domain)’
 
 bindtextdomain
      —, compiled message catalogs are located in subdirectories of the
      directory containing the executable
 
 setlocale
      automatic
 
 Prerequisite
      —
 
 Use or emulate GNU gettext
      —, uses a C# specific message catalog format
 
 Extractor
      ‘xgettext -k_’
 
 Formatting with positions
      ‘String.Format "{1} {0}"’
 
 Portability
      fully portable
 
 po-mode marking
      —
 
    Before marking strings as internationalizable, uses of the string
 concatenation operator need to be converted to ‘String.Format’
 invocations.  For example, ‘"file "+filename+" not found"’ becomes
 ‘String.Format("file {0} not found", filename)’.  Only after this is
 done, can the strings be marked and extracted.
 
    GNU gettext uses the native C#/.NET internationalization mechanism,
 namely the classes ‘ResourceManager’ and ‘ResourceSet’.  Applications
 use the ‘ResourceManager’ methods to retrieve the native language
 translation of strings.  An instance of ‘ResourceSet’ is the in-memory
 representation of a message catalog file.  The ‘ResourceManager’ loads
 and accesses ‘ResourceSet’ instances as needed to look up the
 translations.
 
    There are two formats of ‘ResourceSet’s that can be directly loaded
 by the C# runtime: ‘.resources’ files and ‘.dll’ files.
 
    • The ‘.resources’ format is a binary file usually generated through
      the ‘resgen’ or ‘monoresgen’ utility, but which doesn’t support
      plural forms.  ‘.resources’ files can also be embedded in .NET
      ‘.exe’ files.  This only affects whether a file system access is
      performed to load the message catalog; it doesn’t affect the
      contents of the message catalog.
 
    • On the other hand, the ‘.dll’ format is a binary file that is
      compiled from ‘.cs’ source code and can support plural forms
      (provided it is accessed through the GNU gettext API, see below).
 
    Note that these .NET ‘.dll’ and ‘.exe’ files are not tied to a
 particular platform; their file format and GNU gettext for C# can be
 used on any platform.
 
    To convert a PO file to a ‘.resources’ file, the ‘msgfmt’ program can
 be used with the option ‘--csharp-resources’.  To convert a ‘.resources’
 file back to a PO file, the ‘msgunfmt’ program can be used with the
 option ‘--csharp-resources’.  You can also, in some cases, use the
 ‘monoresgen’ program (from the ‘mono’/‘mcs’ package).  This program can
 also convert a ‘.resources’ file back to a PO file.  But beware: as of
 this writing (January 2004), the ‘monoresgen’ converter is quite buggy.
 
    To convert a PO file to a ‘.dll’ file, the ‘msgfmt’ program can be
 used with the option ‘--csharp’.  The result will be a ‘.dll’ file
 containing a subclass of ‘GettextResourceSet’, which itself is a
 subclass of ‘ResourceSet’.  To convert a ‘.dll’ file containing a
 ‘GettextResourceSet’ subclass back to a PO file, the ‘msgunfmt’ program
 can be used with the option ‘--csharp’.
 
    The advantages of the ‘.dll’ format over the ‘.resources’ format are:
 
   1. Freedom to localize: Users can add their own translations to an
      application after it has been built and distributed.  Whereas when
      the programmer uses a ‘ResourceManager’ constructor provided by the
      system, the set of ‘.resources’ files for an application must be
      specified when the application is built and cannot be extended
      afterwards.
 
   2. Plural handling: A message catalog in ‘.dll’ format supports the
      plural handling function ‘GetPluralString’.  Whereas ‘.resources’
      files can only contain data and only support lookups that depend on
      a single string.
 
   3. Context handling: A message catalog in ‘.dll’ format supports the
      query-with-context functions ‘GetParticularString’ and
      ‘GetParticularPluralString’.  Whereas ‘.resources’ files can only
      contain data and only support lookups that depend on a single
      string.
 
   4. The ‘GettextResourceManager’ that loads the message catalogs in
      ‘.dll’ format also provides for inheritance on a per-message basis.
      For example, in Austrian (‘de_AT’) locale, translations from the
      German (‘de’) message catalog will be used for messages not found
      in the Austrian message catalog.  This has the consequence that the
      Austrian translators need only translate those few messages for
      which the translation into Austrian differs from the German one.
      Whereas when working with ‘.resources’ files, each message catalog
      must provide the translations of all messages by itself.
 
   5. The ‘GettextResourceManager’ that loads the message catalogs in
      ‘.dll’ format also provides for a fallback: The English MSGID is
      returned when no translation can be found.  Whereas when working
      with ‘.resources’ files, a language-neutral ‘.resources’ file must
      explicitly be provided as a fallback.
 
    On the side of the programmatic APIs, the programmer can use either
 the standard ‘ResourceManager’ API and the GNU ‘GettextResourceManager’
 API. The latter is an extension of the former, because
 ‘GettextResourceManager’ is a subclass of ‘ResourceManager’.
 
   1. The ‘System.Resources.ResourceManager’ API.
 
      This API works with resources in ‘.resources’ format.
 
      The creation of the ‘ResourceManager’ is done through
             new ResourceManager(domainname, Assembly.GetExecutingAssembly())
 
      The ‘GetString’ function returns a string’s translation.  Note that
      this function returns null when a translation is missing (i.e. not
      even found in the fallback resource file).
 
   2. The ‘GNU.Gettext.GettextResourceManager’ API.
 
      This API works with resources in ‘.dll’ format.
 
      Reference documentation is in the csharpdoc directory
      (csharpdoc/index.html).
 
      The creation of the ‘ResourceManager’ is done through
             new GettextResourceManager(domainname)
 
      The ‘GetString’ function returns a string’s translation.  Note that
      when a translation is missing, the MSGID argument is returned
      unchanged.
 
      The ‘GetPluralString’ function returns a string translation with
      plural handling, like the ‘ngettext’ function in C.
 
      The ‘GetParticularString’ function returns a string’s translation,
      specific to a particular context, like the ‘pgettext’ function in
      C. Note that when a translation is missing, the MSGID argument is
      returned unchanged.
 
      The ‘GetParticularPluralString’ function returns a string
      translation, specific to a particular context, with plural
      handling, like the ‘npgettext’ function in C.
 
      To use this API, one needs the ‘GNU.Gettext.dll’ file which is part
      of the GNU gettext package and distributed under the LGPL.
 
    You can also mix both approaches: use the
 ‘GNU.Gettext.GettextResourceManager’ constructor, but otherwise use only
 the ‘ResourceManager’ type and only the ‘GetString’ method.  This is
 appropriate when you want to profit from the tools for PO files, but
 don’t want to change an existing source code that uses ‘ResourceManager’
 and don’t (yet) need the ‘GetPluralString’ method.
 
    Two examples, using the second API, are available in the ‘examples’
 directory: ‘hello-csharp’, ‘hello-csharp-forms’.
 
    Now, to make use of the API and define a shorthand for ‘GetString’,
 there are two idioms that you can choose from:
 
    • In a unique class of your project, say ‘Util’, define a static
      variable holding the ‘ResourceManager’ instance:
 
           public static GettextResourceManager MyResourceManager =
             new GettextResourceManager("domain-name");
 
      All classes containing internationalized strings then contain
 
           private static GettextResourceManager Res = Util.MyResourceManager;
           private static String _(String s) { return Res.GetString(s); }
 
      and the shorthand is used like this:
 
           Console.WriteLine(_("Operation completed."));
 
    • You add a class with a very short name, say ‘S’, containing just
      the definition of the resource manager and of the shorthand:
 
           public class S {
             public static GettextResourceManager MyResourceManager =
               new GettextResourceManager("domain-name");
             public static String _(String s) {
                return MyResourceManager.GetString(s);
             }
           }
 
      and the shorthand is used like this:
 
           Console.WriteLine(S._("Operation completed."));
 
    Which of the two idioms you choose, will depend on whether copying
 two lines of codes into every class is more acceptable in your project
 than a class with a single-letter name.