Merge branch 'blender2.7'
[blender.git] / intern / locale / boost_locale_wrapper.cpp
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2012, Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Bastien Montagne.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  *
27  */
28
29 #include <stdio.h>
30 #include <boost/locale.hpp>
31
32 #include "boost_locale_wrapper.h"
33
34 static std::string messages_path;
35 static std::string default_domain;
36 static std::string locale_str;
37
38 /* Note: We cannot use short stuff like boost::locale::gettext, because those return
39  * std::basic_string objects, which c_ptr()-returned char* is no more valid
40  * once deleted (which happens as soons they are out of scope of this func). */
41 typedef boost::locale::message_format<char> char_message_facet;
42 static std::locale locale_global;
43 static char_message_facet const *facet_global = NULL;
44
45 static void bl_locale_global_cache()
46 {
47         /* Cache facet in global variable. Not only is it better for performance,
48          * it also fixes crashes on macOS when doing translation from threads other
49          * than main. Likely because of some internal thread local variables. */
50         try {
51                 /* facet_global reference is valid as long as local_global exists,
52                  * so we store both. */
53                 locale_global = std::locale();
54                 facet_global = &std::use_facet<char_message_facet>(locale_global);
55         }
56         catch(const std::bad_cast &e) { /* if std::has_facet<char_message_facet>(l) == false, LC_ALL = "C" case */
57 #ifndef NDEBUG
58                 std::cout << "bl_locale_global_cache:" << e.what() << " \n";
59 #endif
60                 (void)e;
61                 facet_global = NULL;
62         }
63         catch(const std::exception &e) {
64 #ifndef NDEBUG
65                 std::cout << "bl_locale_global_cache:" << e.what() << " \n";
66 #endif
67                 (void)e;
68                 facet_global = NULL;
69         }
70 }
71
72 void bl_locale_init(const char *_messages_path, const char *_default_domain)
73 {
74         // Avoid using ICU backend, we do not need its power and it's rather heavy!
75         boost::locale::localization_backend_manager lman = boost::locale::localization_backend_manager::global(); 
76 #if defined (_WIN32)
77         lman.select("winapi");
78 #else
79         lman.select("posix");
80 #endif
81         boost::locale::localization_backend_manager::global(lman);
82
83         messages_path = _messages_path;
84         default_domain = _default_domain;
85 }
86
87 void bl_locale_set(const char *locale)
88 {
89         boost::locale::generator gen;
90         std::locale _locale;
91         // Specify location of dictionaries.
92         gen.add_messages_path(messages_path);
93         gen.add_messages_domain(default_domain);
94         //gen.set_default_messages_domain(default_domain);
95
96         try {
97                 if (locale && locale[0]) {
98                         _locale = gen(locale);
99                 }
100                 else {
101 #if defined(__APPLE__) && !defined(WITH_HEADLESS) && !defined(WITH_GHOST_SDL)
102                         std::string locale_osx = osx_user_locale() + std::string(".UTF-8");
103                         _locale = gen(locale_osx.c_str());
104 #else
105                         _locale = gen("");
106 #endif
107                 }
108                 std::locale::global(_locale);
109                 // Note: boost always uses "C" LC_NUMERIC by default!
110
111                 bl_locale_global_cache();
112
113                 // Generate the locale string (useful to know which locale we are actually using in case of "default" one).
114 #define LOCALE_INFO std::use_facet<boost::locale::info>(_locale)
115
116                 locale_str = LOCALE_INFO.language();
117                 if (LOCALE_INFO.country() != "") {
118                         locale_str += "_" + LOCALE_INFO.country();
119                 }
120                 if (LOCALE_INFO.variant() != "") {
121                         locale_str += "@" + LOCALE_INFO.variant();
122                 }
123
124 #undef LOCALE_INFO
125
126         }
127         catch(std::exception const &e) {
128                 std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n";
129         }
130 }
131
132 const char *bl_locale_get(void)
133 {
134         return locale_str.c_str();
135 }
136
137 const char *bl_locale_pgettext(const char *msgctxt, const char *msgid)
138 {
139         if (facet_global) {
140                 char const *r = facet_global->get(0, msgctxt, msgid);
141                 if (r) {
142                         return r;
143                 }
144         }
145
146         return msgid;
147 }