diff options
author | Mathias Gumz <akira@fluxbox.org> | 2015-01-31 20:37:44 (GMT) |
---|---|---|
committer | Mathias Gumz <akira@fluxbox.org> | 2015-01-31 20:37:44 (GMT) |
commit | fff0abad765714d967614cfa871e37efca956194 (patch) | |
tree | b4ee31f5611dcf0ffbef0b1ccc950d9f07e4d83e | |
parent | 57f21b64ca27cf29212cd0d5184823cf2b0d694e (diff) | |
download | fluxbox-fff0abad765714d967614cfa871e37efca956194.zip fluxbox-fff0abad765714d967614cfa871e37efca956194.tar.bz2 |
Improve I18n support
Among the first steps to produce better i18n support is to test the created
translations adhoc without running "make install". To achieve this, fluxbox
now honors several environment variables:
- NLSPATH: fluxbox won't create the absolute path to the catalog and thus
catopen() is free to use NLSPATH as described in the manpage. Example
given: "/tmp/%N" will pick "/tmp/fluxbox.cat". %N refers to FLUXBOX_CATFILE.
- FLUXBOX_CATFILE: By setting FLUXBOX_CATFILE the users can make fluxbox to
use a different name for the catalog file. Default: "fluxbox.cat"
- FLUXBOX_CATDIR: Per default fluxbox tries to find FLUXBOX_CATFILE at several
places. Setting this environment variable allows to point fluxbox to a
different search path for the catalog files.
Then, fluxbox tries catopen() first without changing the deduced catalog file
name. After that it applies some heuristics to get a good catalog file name.
-rw-r--r-- | src/FbTk/I18n.cc | 165 | ||||
-rw-r--r-- | src/cli_info.cc | 13 | ||||
-rw-r--r-- | src/cli_options.cc | 18 | ||||
-rw-r--r-- | src/main.cc | 2 | ||||
-rw-r--r-- | util/fbsetroot.cc | 2 | ||||
-rw-r--r-- | util/fluxbox-update_configs.cc | 2 |
6 files changed, 134 insertions, 68 deletions
diff --git a/src/FbTk/I18n.cc b/src/FbTk/I18n.cc index f53e5ed..18eb67c 100644 --- a/src/FbTk/I18n.cc +++ b/src/FbTk/I18n.cc | |||
@@ -37,6 +37,7 @@ | |||
37 | 37 | ||
38 | #include <cstdlib> | 38 | #include <cstdlib> |
39 | #include <cstring> | 39 | #include <cstring> |
40 | #include <cstdarg> | ||
40 | #include <iostream> | 41 | #include <iostream> |
41 | 42 | ||
42 | #ifdef HAVE_LOCALE_H | 43 | #ifdef HAVE_LOCALE_H |
@@ -65,15 +66,54 @@ using std::string; | |||
65 | 66 | ||
66 | namespace { | 67 | namespace { |
67 | 68 | ||
68 | const nl_catd INVALID_CATALOG = ((nl_catd)(-1)); | 69 | const char UTF8_SUFFIX[] = "-UTF-8.cat"; |
69 | nl_catd s_catalog_fd = INVALID_CATALOG; | 70 | const size_t UTF8_SUFFIX_LEN = sizeof(UTF8_SUFFIX)-1; // without \0 |
71 | const char DEFAULT_CATFILE[] = "fluxbox.cat"; | ||
72 | const char ENV_CATFILE[] = "FLUXBOX_CATFILE"; | ||
73 | const char ENV_CATDIR[] = "FLUXBOX_CATDIR"; | ||
74 | |||
75 | const nl_catd INVALID_CATALOG = (nl_catd)(-1); | ||
76 | nl_catd s_catalog_fd = INVALID_CATALOG; | ||
77 | |||
78 | |||
79 | const char* getCatalogDir() { | ||
80 | const char* cat_dir = getenv(ENV_CATDIR); | ||
81 | if (cat_dir) { | ||
82 | return cat_dir; | ||
83 | } | ||
84 | return LOCALEPATH; | ||
85 | } | ||
86 | |||
87 | |||
88 | std::string join_str(size_t n, ...) { | ||
89 | std::string s; | ||
90 | va_list args; | ||
91 | va_start(args, n); | ||
92 | for (; n > 0; n--) { | ||
93 | s.append(va_arg(args, const char*)); | ||
94 | } | ||
95 | return s; | ||
96 | } | ||
70 | 97 | ||
71 | } | 98 | } |
72 | 99 | ||
73 | 100 | ||
74 | namespace FbTk { | 101 | namespace FbTk { |
75 | 102 | ||
103 | |||
104 | // initialize the i18n-system be opening the catalog-file | ||
105 | // named by 'catalog'. per default we expect 'catalog' to | ||
106 | // be 0/NULL, the code picks a sane default then: | ||
107 | // | ||
108 | // - environment variable FLUXBOX_CATFILE is set? use it | ||
109 | // - DEFAULT_CATFILE ("fluxbox.cat") | ||
110 | // - the utf8 encoded translation for the current locale | ||
111 | // | ||
112 | // handling things this was allows us to test catalog files | ||
113 | // without putting them into the install path | ||
114 | // $PREFIX/share/fluxbox/nls/XYZ/ | ||
76 | void I18n::init(const char* catalog) { | 115 | void I18n::init(const char* catalog) { |
116 | |||
77 | static bool init = false; | 117 | static bool init = false; |
78 | if (init) { | 118 | if (init) { |
79 | return; | 119 | return; |
@@ -81,50 +121,87 @@ void I18n::init(const char* catalog) { | |||
81 | 121 | ||
82 | #if defined(NLS) && defined(HAVE_CATOPEN) | 122 | #if defined(NLS) && defined(HAVE_CATOPEN) |
83 | 123 | ||
124 | if (!catalog) { | ||
125 | const char* c = getenv(ENV_CATFILE); | ||
126 | if (!c) { | ||
127 | c = DEFAULT_CATFILE; | ||
128 | } | ||
129 | catalog = c; | ||
130 | } | ||
131 | |||
84 | FbStringUtil::init(); | 132 | FbStringUtil::init(); |
85 | 133 | ||
134 | int flag; | ||
135 | |||
86 | I18n& i18n = I18n::instance(); | 136 | I18n& i18n = I18n::instance(); |
137 | const string dir = getCatalogDir(); | ||
138 | const string locale = i18n.m_locale; | ||
139 | string clean_locale = locale; | ||
140 | size_t i; | ||
87 | 141 | ||
88 | string filename = LOCALEPATH; | 142 | // clean the locale, we have to append something later on |
89 | filename += '/'; | 143 | i = clean_locale.find('.'); |
90 | filename += i18n.m_locale; | 144 | if (i != string::npos) |
91 | filename += '/'; | 145 | clean_locale.erase(i); |
92 | filename += catalog; | ||
93 | 146 | ||
94 | if (!FileUtil::isRegularFile(filename.c_str()) && i18n.m_locale != "C" && FbStringUtil::haveUTF8()) { | 147 | #ifdef MCLoadBySet |
95 | // try the UTF-8 catalog, this also picks up situations where | 148 | flag = MCLoadBySet; |
96 | // the codeset somehow isn't specified | 149 | #else |
150 | flag = NL_CAT_LOCALE; | ||
151 | #endif | ||
97 | 152 | ||
98 | // remove everything after @ | 153 | struct { std::string catalog; std::string locale; bool utf8; } _catalog[] = { |
99 | string::size_type index = i18n.m_locale.find('.'); | ||
100 | // erase all characters starting at index | ||
101 | if (index != string::npos) | ||
102 | i18n.m_locale.erase(index); | ||
103 | 154 | ||
104 | i18n.m_locale.append(".UTF-8"); | 155 | // first try pure 'catalog'. catopen() will use NLSPATH if it's |
105 | i18n.m_utf8_translate = true; | 156 | // set and replaces '%N' by 'catalog'. eg: with catalog="fluxbox.cat" |
157 | // "/usr/share/fluxbox/nls/C/%N" becomes "/usr/share/fluxbox/nls/C/fluxbox.cat" | ||
158 | { string(catalog), locale, false }, | ||
106 | 159 | ||
107 | filename = LOCALEPATH; | 160 | // try full-path to 'catalog' |
108 | filename += '/'; | 161 | { join_str(5, dir.c_str(), "/", locale.c_str(), "/", catalog), locale, false }, |
109 | filename += i18n.m_locale; | ||
110 | filename += '/'; | ||
111 | filename += catalog; | ||
112 | } | ||
113 | 162 | ||
114 | #ifdef MCLoadBySet | 163 | // try the UTF-8 catalog, this also picks up situations where |
115 | s_catalog_fd = catopen(filename.c_str(), MCLoadBySet); | 164 | // the codeset somehow isn't specified |
116 | #else // !MCLoadBySet | 165 | { join_str(5, dir.c_str(), "/", clean_locale.c_str(), ".UTF-8/", catalog), |
117 | s_catalog_fd = catopen(filename.c_str(), NL_CAT_LOCALE); | 166 | join_str(2, clean_locale.c_str(), ".UTF8"), true}, |
118 | #endif // MCLoadBySet | 167 | |
168 | }; | ||
169 | |||
170 | for (i = 0; i < sizeof(_catalog)/sizeof(_catalog[0]); i++) { | ||
171 | |||
172 | if (_catalog[i].utf8 && locale == "C") { | ||
173 | continue; | ||
174 | } | ||
175 | |||
176 | const char* fname = _catalog[i].catalog.c_str(); | ||
177 | |||
178 | s_catalog_fd = catopen(fname, flag); | ||
179 | if (s_catalog_fd == INVALID_CATALOG) { | ||
180 | continue; | ||
181 | } | ||
182 | |||
183 | i18n.m_locale = _catalog[i].locale; | ||
184 | if (FbStringUtil::haveUTF8()) { | ||
185 | if (_catalog[i].utf8) { | ||
186 | i18n.m_utf8_translate = true; | ||
187 | } else { | ||
188 | size_t n = _catalog[i].catalog.rfind(UTF8_SUFFIX); | ||
189 | if (n != std::string::npos && (n + UTF8_SUFFIX_LEN) == _catalog[i].catalog.size()) { | ||
190 | i18n.m_utf8_translate = true; | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | break; | ||
195 | } | ||
119 | 196 | ||
120 | if (s_catalog_fd == INVALID_CATALOG) { | 197 | if (s_catalog_fd == INVALID_CATALOG) { |
121 | cerr<<"Warning: Failed to open file("<<filename<<")"<<endl | 198 | cerr<<"Warning: Failed to open file("<< catalog <<")"<<endl |
122 | <<"for translation, using default messages."<<endl; | 199 | <<"for translation, using default messages."<<endl; |
123 | } | 200 | } |
124 | #endif // HAVE_CATOPEN | 201 | #endif // HAVE_CATOPEN |
125 | } | 202 | } |
126 | 203 | ||
127 | I18n::I18n():m_multibyte(false), m_utf8_translate(false) { | 204 | I18n::I18n() : m_multibyte(false), m_utf8_translate(false) { |
128 | #if defined(HAVE_SETLOCALE) && defined(NLS) | 205 | #if defined(HAVE_SETLOCALE) && defined(NLS) |
129 | //make sure we don't get 0 to m_locale string | 206 | //make sure we don't get 0 to m_locale string |
130 | char *temp = setlocale(LC_MESSAGES, ""); | 207 | char *temp = setlocale(LC_MESSAGES, ""); |
@@ -146,14 +223,13 @@ I18n::I18n():m_multibyte(false), m_utf8_translate(false) { | |||
146 | 223 | ||
147 | // truncate any encoding off the end of the locale | 224 | // truncate any encoding off the end of the locale |
148 | 225 | ||
149 | // remove everything after @ | ||
150 | string::size_type index = m_locale.find('@'); | 226 | string::size_type index = m_locale.find('@'); |
151 | if (index != string::npos) | 227 | if (index != string::npos) |
152 | m_locale.erase(index); //erase all characters starting at index | 228 | m_locale.erase(index); |
153 | // remove everything before = | 229 | |
154 | index = m_locale.find('='); | 230 | index = m_locale.find('='); |
155 | if (index != string::npos) | 231 | if (index != string::npos) |
156 | m_locale.erase(0,index+1); //erase all characters starting up to index | 232 | m_locale.erase(0, index+1); |
157 | } | 233 | } |
158 | #endif // defined(HAVE_SETLOCALE) && defined(NLS) | 234 | #endif // defined(HAVE_SETLOCALE) && defined(NLS) |
159 | } | 235 | } |
@@ -162,7 +238,7 @@ I18n::I18n():m_multibyte(false), m_utf8_translate(false) { | |||
162 | I18n::~I18n() { | 238 | I18n::~I18n() { |
163 | 239 | ||
164 | #if defined(NLS) && defined(HAVE_CATCLOSE) | 240 | #if defined(NLS) && defined(HAVE_CATCLOSE) |
165 | if (s_catalog_fd != (nl_catd)-1) | 241 | if (s_catalog_fd != INVALID_CATALOG) |
166 | catclose(s_catalog_fd); | 242 | catclose(s_catalog_fd); |
167 | #endif // HAVE_CATCLOSE | 243 | #endif // HAVE_CATCLOSE |
168 | } | 244 | } |
@@ -178,27 +254,28 @@ I18n& I18n::instance() { | |||
178 | FbString I18n::getMessage(int set_number, int message_number, | 254 | FbString I18n::getMessage(int set_number, int message_number, |
179 | const char *default_message, bool translate_fb) const { | 255 | const char *default_message, bool translate_fb) const { |
180 | 256 | ||
257 | FbString msg(default_message); | ||
258 | |||
181 | #if defined(NLS) && defined(HAVE_CATGETS) | 259 | #if defined(NLS) && defined(HAVE_CATGETS) |
182 | if (s_catalog_fd != INVALID_CATALOG) { | 260 | if (s_catalog_fd != INVALID_CATALOG) { |
183 | const char *ret = catgets(s_catalog_fd, set_number, message_number, default_message); | 261 | const char *ret = catgets(s_catalog_fd, set_number, message_number, default_message); |
184 | // can't translate, leave it in raw ascii (utf-8 compatible) | ||
185 | if (ret == default_message || ret == NULL) | ||
186 | return default_message; | ||
187 | 262 | ||
188 | if (!m_utf8_translate && translate_fb) | 263 | if (ret == default_message || ret == NULL) { |
264 | // can't translate, leave it in raw ascii (utf-8 compatible) | ||
265 | } else if (!m_utf8_translate && translate_fb) | ||
189 | // Local input, UTF-8 output | 266 | // Local input, UTF-8 output |
190 | return FbStringUtil::LocaleStrToFb(ret); | 267 | msg = FbStringUtil::LocaleStrToFb(ret); |
191 | else if (m_utf8_translate && !translate_fb) | 268 | else if (m_utf8_translate && !translate_fb) |
192 | // UTF-8 input, local output | 269 | // UTF-8 input, local output |
193 | return FbStringUtil::FbStrToLocale(ret); | 270 | msg = FbStringUtil::FbStrToLocale(ret); |
194 | else | 271 | else |
195 | // UTF-8 input, UTF-8 output OR | 272 | // UTF-8 input, UTF-8 output OR |
196 | // local input, local output | 273 | // local input, local output |
197 | return ret; | 274 | msg = ret; |
198 | } | 275 | } |
199 | else | 276 | |
200 | #endif // NLS && HAVE_CATGETS | 277 | #endif // NLS && HAVE_CATGETS |
201 | return default_message; | 278 | return msg; |
202 | } | 279 | } |
203 | 280 | ||
204 | } // end namespace FbTk | 281 | } // end namespace FbTk |
diff --git a/src/cli_info.cc b/src/cli_info.cc index 6156d56..dab6029 100644 --- a/src/cli_info.cc +++ b/src/cli_info.cc | |||
@@ -72,22 +72,25 @@ void FluxboxCli::showInfo(ostream &ostr) { | |||
72 | <<_FB_CONSOLETEXT(Common, Defaults, "Defaults", "Default values compiled in") | 72 | <<_FB_CONSOLETEXT(Common, Defaults, "Defaults", "Default values compiled in") |
73 | << ": " << endl; | 73 | << ": " << endl; |
74 | 74 | ||
75 | ostr <<_FB_CONSOLETEXT(Common, DefaultMenuFile, " menu", "default menu file (right aligned - make sure same width as other default values)") | 75 | ostr <<_FB_CONSOLETEXT(Common, DefaultMenuFile, " menu", "default menu file (right aligned - make sure same width as other default values)") |
76 | << ": " | 76 | << ": " |
77 | << FbTk::StringUtil::expandFilename(DEFAULTMENU) << endl; | 77 | << FbTk::StringUtil::expandFilename(DEFAULTMENU) << endl; |
78 | ostr << _FB_CONSOLETEXT(Common, DefaultStyle, " style", "default style (right aligned - make sure same width as other default values)") | 78 | ostr <<_FB_CONSOLETEXT(Common, DefaultWindowMenuFile, " windowmenu", "default windowmenu file (right aligned - make sure same width as other default values)") |
79 | << ": " | ||
80 | << FbTk::StringUtil::expandFilename(DEFAULT_WINDOWMENU) << endl; | ||
81 | ostr << _FB_CONSOLETEXT(Common, DefaultStyle, " style", "default style (right aligned - make sure same width as other default values)") | ||
79 | << ": " | 82 | << ": " |
80 | << FbTk::StringUtil::expandFilename(DEFAULTSTYLE) << endl; | 83 | << FbTk::StringUtil::expandFilename(DEFAULTSTYLE) << endl; |
81 | 84 | ||
82 | ostr << _FB_CONSOLETEXT(Common, DefaultKeyFile, " keys", "default key file (right aligned - make sure same width as other default values)") | 85 | ostr << _FB_CONSOLETEXT(Common, DefaultKeyFile, " keys", "default key file (right aligned - make sure same width as other default values)") |
83 | << ": " | 86 | << ": " |
84 | << FbTk::StringUtil::expandFilename(DEFAULTKEYSFILE) << endl; | 87 | << FbTk::StringUtil::expandFilename(DEFAULTKEYSFILE) << endl; |
85 | ostr << _FB_CONSOLETEXT(Common, DefaultInitFile, " init", "default init file (right aligned - make sure same width as other default values)") | 88 | ostr << _FB_CONSOLETEXT(Common, DefaultInitFile, " init", "default init file (right aligned - make sure same width as other default values)") |
86 | << ": " | 89 | << ": " |
87 | << FbTk::StringUtil::expandFilename(DEFAULT_INITFILE) << endl; | 90 | << FbTk::StringUtil::expandFilename(DEFAULT_INITFILE) << endl; |
88 | 91 | ||
89 | #ifdef NLS | 92 | #ifdef NLS |
90 | ostr << _FB_CONSOLETEXT(Common, DefaultLocalePath, " nls", "location for localization files (right aligned - make sure same width as other default values)") | 93 | ostr << _FB_CONSOLETEXT(Common, DefaultLocalePath, " nls", "location for localization files (right aligned - make sure same width as other default values)") |
91 | << ": " | 94 | << ": " |
92 | << FbTk::StringUtil::expandFilename(LOCALEPATH) << endl; | 95 | << FbTk::StringUtil::expandFilename(LOCALEPATH) << endl; |
93 | #endif | 96 | #endif |
diff --git a/src/cli_options.cc b/src/cli_options.cc index 9e30af7..4440e83 100644 --- a/src/cli_options.cc +++ b/src/cli_options.cc | |||
@@ -32,22 +32,8 @@ | |||
32 | #include "FbTk/Command.hh" | 32 | #include "FbTk/Command.hh" |
33 | #include "FbTk/CommandParser.hh" | 33 | #include "FbTk/CommandParser.hh" |
34 | 34 | ||
35 | #ifdef HAVE_CONFIG_H | 35 | #include <cstdlib> |
36 | #include "config.h" | 36 | #include <cstring> |
37 | #endif // HAVE_CONFIG_H | ||
38 | |||
39 | #ifdef HAVE_CSTDLIB | ||
40 | #include <cstdlib> | ||
41 | #else | ||
42 | #include <stdlib.h> | ||
43 | #endif | ||
44 | |||
45 | #ifdef HAVE_CSTRING | ||
46 | #include <cstring> | ||
47 | #else | ||
48 | #include <string.h> | ||
49 | #endif | ||
50 | |||
51 | #include <iostream> | 37 | #include <iostream> |
52 | 38 | ||
53 | using std::cerr; | 39 | using std::cerr; |
diff --git a/src/main.cc b/src/main.cc index d1b3046..3cc728d 100644 --- a/src/main.cc +++ b/src/main.cc | |||
@@ -158,7 +158,7 @@ void setupSignalHandling() { | |||
158 | 158 | ||
159 | int main(int argc, char **argv) { | 159 | int main(int argc, char **argv) { |
160 | 160 | ||
161 | FbTk::I18n::init("fluxbox.cat"); | 161 | FbTk::I18n::init(0); |
162 | 162 | ||
163 | FluxboxCli::Options opts; | 163 | FluxboxCli::Options opts; |
164 | int exitcode = opts.parse(argc, argv); | 164 | int exitcode = opts.parse(argc, argv); |
diff --git a/util/fbsetroot.cc b/util/fbsetroot.cc index 28ad26e..07f6b68 100644 --- a/util/fbsetroot.cc +++ b/util/fbsetroot.cc | |||
@@ -370,7 +370,7 @@ int main(int argc, char **argv) { | |||
370 | char *display_name = (char *) 0; | 370 | char *display_name = (char *) 0; |
371 | int i = 1; | 371 | int i = 1; |
372 | 372 | ||
373 | FbTk::I18n::init("fluxbox.cat"); | 373 | FbTk::I18n::init(0); |
374 | 374 | ||
375 | for (; i < argc; i++) { | 375 | for (; i < argc; i++) { |
376 | if (!strcmp(argv[i], "-display") || !strcmp(argv[i], "--display")) { | 376 | if (!strcmp(argv[i], "-display") || !strcmp(argv[i], "--display")) { |
diff --git a/util/fluxbox-update_configs.cc b/util/fluxbox-update_configs.cc index a7d3d4a..4065f97 100644 --- a/util/fluxbox-update_configs.cc +++ b/util/fluxbox-update_configs.cc | |||
@@ -565,7 +565,7 @@ int main(int argc, char **argv) { | |||
565 | bool check = 0; | 565 | bool check = 0; |
566 | pid_t fb_pid = 0; | 566 | pid_t fb_pid = 0; |
567 | 567 | ||
568 | FbTk::I18n::init("fluxbox.cat"); | 568 | FbTk::I18n::init(0); |
569 | _FB_USES_NLS; | 569 | _FB_USES_NLS; |
570 | 570 | ||
571 | for (; i < argc; i++) { | 571 | for (; i < argc; i++) { |