25 Localization library [localization]

25.4 Standard locale categories [locale.categories]

25.4.8 Program-defined facets [facets.examples]

A C++ program may define facets to be added to a locale and used identically as the built-in facets. To create a new facet interface, C++ programs simply derive from locale​::​facet a class containing a static member: static locale​::​id id.

[Note: The locale member function templates verify its type and storage class. end note]

[Example: Traditional global localization is still easy:

#include <iostream>
#include <locale>
int main(int argc, char** argv) {
  using namespace std;
  locale::global(locale(""));           // set the global locale
                                        // imbue it on all the std streams
  cin.imbue(locale());
  cout.imbue(locale());
  cerr.imbue(locale());
  wcin.imbue(locale());
  wcout.imbue(locale());
  wcerr.imbue(locale());

  return MyObject(argc, argv).doit();
}

end example]

[Example: Greater flexibility is possible:

#include <iostream>
#include <locale>
int main() {
  using namespace std;
  cin.imbue(locale(""));        // the user's preferred locale
  cout.imbue(locale::classic());
  double f;
  while (cin >> f) cout << f << endl;
  return (cin.fail() != 0);
}

In a European locale, with input 3.456,78, output is 3456.78. end example]

This can be important even for simple programs, which may need to write a data file in a fixed format, regardless of a user's preference.

[Example: Here is an example of the use of locales in a library interface.

// file: Date.h
#include <iosfwd>
#include <string>
#include <locale>

class Date {
public:
  Date(unsigned day, unsigned month, unsigned year);
  std::string asString(const std::locale& = std::locale());
};

std::istream& operator>>(std::istream& s, Date& d);
std::ostream& operator<<(std::ostream& s, Date d);

This example illustrates two architectural uses of class locale.

The first is as a default argument in Date​::​asString(), where the default is the global (presumably user-preferred) locale.

The second is in the operators << and >>, where a locale “hitchhikes” on another object, in this case a stream, to the point where it is needed.

// file: Date.C
#include "Date"                 // includes <ctime>
#include <sstream>
std::string Date::asString(const std::locale& l) {
  using namespace std;
  ostringstream s; s.imbue(l);
  s << *this; return s.str();
}

std::istream& operator>>(std::istream& s, Date& d) {
  using namespace std;
  istream::sentry cerberos(s);
  if (cerberos) {
    ios_base::iostate err = goodbit;
    struct tm t;
    use_facet<time_get<char>>(s.getloc()).get_date(s, 0, s, err, &t);
    if (!err) d = Date(t.tm_day, t.tm_mon + 1, t.tm_year + 1900);
    s.setstate(err);
  }
  return s;
}

end example]

A locale object may be extended with a new facet simply by constructing it with an instance of a class derived from locale​::​facet. The only member a C++ program must define is the static member id, which identifies your class interface as a new facet.

[Example: Classifying Japanese characters:

// file: <jctype>
#include <locale>
namespace My {
  using namespace std;
  class JCtype : public locale::facet {
  public:
    static locale::id id;       // required for use as a new locale facet
    bool is_kanji (wchar_t c) const;
    JCtype() { }
  protected:
    ~JCtype() { }
  };
}

// file: filt.C
#include <iostream>
#include <locale>
#include "jctype"               // above
std::locale::id My::JCtype::id; // the static JCtype member declared above.

int main() {
  using namespace std;
  using wctype = ctype<wchar_t>;
  locale loc(locale(""),        // the user's preferred locale ...
         new My::JCtype);       // and a new feature ...
  wchar_t c = use_facet<wctype>(loc).widen('!');
  if (!use_facet<My::JCtype>(loc).is_kanji(c))
    cout << "no it isn't!" << endl;
}

The new facet is used exactly like the built-in facets. end example]

[Example: Replacing an existing facet is even easier. The code does not define a member id because it is reusing the numpunct<charT> facet interface:

// file: my_­bool.C
#include <iostream>
#include <locale>
#include <string>
namespace My {
  using namespace std;
  using cnumpunct = numpunct_byname<char>;
  class BoolNames : public cnumpunct {
  protected:
    string do_truename()  const { return "Oui Oui!"; }
    string do_falsename() const { return "Mais Non!"; }
    ~BoolNames() { }
  public:
    BoolNames(const char* name) : cnumpunct(name) { }
  };
}

int main(int argc, char** argv) {
  using namespace std;
  // make the user's preferred locale, except for...
  locale loc(locale(""), new My::BoolNames(""));
  cout.imbue(loc);
  cout << boolalpha << "Any arguments today? " << (argc > 1) << endl;
}

end example]