views:

475

answers:

4

I would like to have an easy to use way to write code like:

#include <iostream>
int main (){
    std::cout << "hello, world!\n";
}

but that supports i18n. Here is an example using gettext():

#include <libintl.h>
#include <iostream>
int main (){
    std::cout << gettext("hello, world!\n");
}

This can then be processed by xgettext to produce a message catalog file that can be used by translators to create various versions. These extra files can be handled on target systems to allow the user to interact in a preferred language.

I would like to write the code something like this instead:

#include <i18n-iostream>
int main (){
    i18n::cout << "hello, world!\n";
}

At build time the quoted strings would be examined by a program like xgettext to produce the base message catalog file. << operator with argument i18n::cout would take a string literal as the key to lookup the run-time text to use from a message catalog.

Does it exist somewhere?

A: 

You mean you just want another API? You could write a small wrapper, shouldn't be too hard and it would give you the possibility to use the best API you can think of :)

soulmerge
It looks like gettext() hides a lot of details by passing an English text to gettext() which looks up the run-time value while making to code readable. This appears to have been created for a C world. In C++ it seems like the << operator should be able to be overloaded to handle the text as a key rather than string to output.
C.W.Holeman II
+1  A: 

The short answer is "No" :)

Seriously, which aspects of internationalization are you interested in? ICU provides pretty much everything but does not feel like standard C++. There are other libraries smaller in scope that provide some i18n functionalities, i.e. UTF-CPP for handling UTF-8 encoded strings.

Nemanja Trifunovic
http://site.icu-project.org/
rasjani
+2  A: 

At build time the quoted strings would be examined by a program like xgettext to produce the base message catalog file. << operator with argument i18n::cout would take a string literal as the key to lookup the run-time text to use from a message catalog.

You try to convert a string like a single instance, but it isn't/

The point, you don't want something like this. Think of:

if(n=1)
    i18n::cout << "I need one apple"
else
    i18n::cout << "I need " << n << " apples" ;

So why this is would not work, because "n=1" or "n!=1" works only for English, many other languages have more then one plural form, also it requires translation of "I need X apples" as signle instance.

I suggest you just to learn to deal with gettext, it is quite simple and powerful, many people had thought about it.

Another point, you are usually do not call gettext but

#include <libintl.h>
#include <iostream>
#define _(x) gettext(x)

int main (){
    std::cout << _("hello, world!\n");
}

This makes the code much cleaner, also it is quite a "standard" feature to use "_" as gettext alias.

Just learn how to use it, before you try to make "nicer" API. Just to mention, gettext API is quite de-facto standard for many languages, not only C.

Artyom
You've not used boost placeholders I take it...?
Geoff
+1  A: 

Personally I would go with this answer, but it might be possible to use a bit of streambuf magic to do this as the text is written to the stream. If you're really interested in doing this though, please take a look at Standard C++ IOStreams and Locales by Langer and Kreft, it's the bible of iostreams.

The following assumes that everything written to the buffer is to be translated, and that each full line can be translated completely:

std::string xgettext (std::string const & s)
{
  return s;
}

The following transbuf class overrides the "overflow" function and translates the buffer every time it sees a newline.

class transbuf : public std::streambuf {
public:
  transbuf (std::streambuf * realsb) : std::streambuf (), m_realsb (realsb)
    , m_buf () {}

  ~transbuf () {
    // ... flush  m_buf if necessary
  }

  virtual std::streambuf::int_type overflow (std::streambuf::int_type c) {
    m_buf.push_back (c);
    if (c == '\n') {
      // We have a complete line, translate it and write it to our stream:
      std::string transtext = xgettext (m_buf);
      for (std::string::const_iterator i = transtext.begin ()
        ; i != transtext.end ()
        ; ++i) {
        m_realsb->sputc (*i);
        // ... check that overflow returned the correct value...
      }
      m_buf = "";
    }
    return c;
  }    

  std::streambuf * get () { return m_realsb; }

  // data
private:
  std::streambuf * m_realsb;
  std::string m_buf;
};

And here's an example of how that might be used:

int main ()
{
  transbuf * buf = new transbuf (std::cout.rdbuf ());
  std::ostream trans (buf);

  trans << "Hello";  // Added to m_buf
  trans << " World"; // Added to m_buf
  trans << "\n";     // Causes m_buf to be written

  trans << "Added to buffer\neach new line causes\n"
           "the string to be translated\nand written" << std::endl;

  delete buf;
}
Richard Corden