1//
2// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3// Copyright (c) 2022-2023 Alexander Grund
4//
5// Distributed under the Boost Software License, Version 1.0.
6// https://www.boost.org/LICENSE_1_0.txt
7
8#include "boost/locale/std/std_backend.hpp"
9#include <boost/locale/gnu_gettext.hpp>
10#include <boost/locale/localization_backend.hpp>
11#include <boost/locale/util.hpp>
12#include <boost/locale/util/locale_data.hpp>
13#include <boost/assert.hpp>
14#include <boost/core/ignore_unused.hpp>
15#include <algorithm>
16#include <iterator>
17#include <vector>
18
19#if BOOST_LOCALE_USE_WIN32_API
20# ifndef NOMINMAX
21# define NOMINMAX
22# endif
23# include "boost/locale/win32/lcid.hpp"
24# include <windows.h>
25#endif
26#include "boost/locale/shared/message.hpp"
27#include "boost/locale/std/all_generator.hpp"
28#include "boost/locale/util/encoding.hpp"
29#include "boost/locale/util/gregorian.hpp"
30#include "boost/locale/util/make_std_unique.hpp"
31#include "boost/locale/util/numeric.hpp"
32
33namespace {
34struct windows_name {
35 std::string name, codepage;
36 explicit operator bool() const { return !name.empty() && !codepage.empty(); }
37};
38
39windows_name to_windows_name(const std::string& l)
40{
41#if BOOST_LOCALE_USE_WIN32_API
42 windows_name res;
43 const unsigned lcid = boost::locale::impl_win::locale_to_lcid(l);
44 char win_lang[256]{};
45 if(lcid == 0 || GetLocaleInfoA(lcid, LOCALE_SENGLANGUAGE, win_lang, sizeof(win_lang)) == 0)
46 return res;
47 res.name = win_lang;
48 char win_country[256]{};
49 if(GetLocaleInfoA(lcid, LOCALE_SENGCOUNTRY, win_country, sizeof(win_country)) != 0) {
50 res.name += "_";
51 res.name += win_country;
52 }
53
54 char win_codepage[10]{};
55 if(GetLocaleInfoA(lcid, LOCALE_IDEFAULTANSICODEPAGE, win_codepage, sizeof(win_codepage)) != 0)
56 res.codepage = win_codepage;
57 return res;
58#else
59 boost::ignore_unused(l);
60 return {};
61#endif
62}
63
64bool loadable(const std::string& name)
65{
66 try {
67 std::locale l(name);
68 return true;
69 } catch(const std::exception&) {
70 return false;
71 }
72}
73} // namespace
74
75namespace boost { namespace locale { namespace impl_std {
76
77 class std_localization_backend : public localization_backend {
78 public:
79 std_localization_backend() : invalid_(true), use_ansi_encoding_(false) {}
80 std_localization_backend(const std_localization_backend& other) :
81 localization_backend(), paths_(other.paths_), domains_(other.domains_), locale_id_(other.locale_id_),
82 invalid_(true), use_ansi_encoding_(other.use_ansi_encoding_)
83 {}
84 std_localization_backend* clone() const override { return new std_localization_backend(*this); }
85
86 void set_option(const std::string& name, const std::string& value) override
87 {
88 invalid_ = true;
89 if(name == "locale")
90 locale_id_ = value;
91 else if(name == "message_path")
92 paths_.push_back(x: value);
93 else if(name == "message_application")
94 domains_.push_back(x: value);
95 else if(name == "use_ansi_encoding")
96 use_ansi_encoding_ = value == "true";
97 }
98 void clear_options() override
99 {
100 invalid_ = true;
101 use_ansi_encoding_ = false;
102 locale_id_.clear();
103 paths_.clear();
104 domains_.clear();
105 }
106
107 void prepare_data()
108 {
109 if(!invalid_)
110 return;
111 invalid_ = false;
112 std::string lid = locale_id_;
113 if(lid.empty()) {
114 bool use_utf8 = !use_ansi_encoding_;
115 lid = util::get_system_locale(use_utf8_on_windows: use_utf8);
116 }
117 in_use_id_ = lid;
118 data_.parse(locale_name: lid);
119
120 const auto l_win = to_windows_name(l: lid);
121
122 if(!data_.is_utf8()) {
123 utf_mode_ = utf8_support::none;
124 if(loadable(name: lid))
125 name_ = lid;
126 else if(l_win && loadable(name: l_win.name)) {
127 if(util::are_encodings_equal(l: l_win.codepage, r: data_.encoding()))
128 name_ = l_win.name;
129 else {
130 int codepage_int;
131 if(util::try_to_int(s: l_win.codepage, res&: codepage_int)
132 && codepage_int == util::encoding_to_windows_codepage(data_.encoding()))
133 {
134 name_ = l_win.name;
135 } else
136 name_ = "C";
137 }
138 } else
139 name_ = "C";
140 } else {
141 if(loadable(name: lid)) {
142 name_ = lid;
143 utf_mode_ = utf8_support::native;
144 } else {
145 std::vector<std::string> alt_names;
146 if(l_win)
147 alt_names.push_back(x: l_win.name);
148 // Try different spellings
149 alt_names.push_back(x: util::locale_data(data_).encoding(new_encoding: "UTF-8").to_string());
150 alt_names.push_back(x: util::locale_data(data_).encoding(new_encoding: "utf8", uppercase: false).to_string());
151 // Without encoding, let from_wide classes handle it
152 alt_names.push_back(x: util::locale_data(data_).encoding(new_encoding: "").to_string());
153 // Final try: Classic locale, but enable Unicode (if supported)
154 alt_names.push_back(x: "C.UTF-8");
155 alt_names.push_back(x: "C.utf8");
156 // If everything fails rely on the classic locale
157 alt_names.push_back(x: "C");
158 for(const std::string& name : alt_names) {
159 if(loadable(name)) {
160 name_ = name;
161 break;
162 }
163 }
164 BOOST_ASSERT(!name_.empty());
165 utf_mode_ = utf8_support::from_wide;
166 }
167 }
168 }
169
170 std::locale install(const std::locale& base, category_t category, char_facet_t type) override
171 {
172 prepare_data();
173
174 switch(category) {
175 case category_t::convert: return create_convert(in: base, locale_name: name_, type, utf: utf_mode_);
176 case category_t::collation: return create_collate(in: base, locale_name: name_, type, utf: utf_mode_);
177 case category_t::formatting: return create_formatting(in: base, locale_name: name_, type, utf: utf_mode_);
178 case category_t::parsing: return create_parsing(in: base, locale_name: name_, type, utf: utf_mode_);
179 case category_t::codepage: return create_codecvt(in: base, locale_name: name_, type, utf: utf_mode_);
180 case category_t::calendar: return util::install_gregorian_calendar(in: base, terr: data_.country());
181 case category_t::message: return detail::install_message_facet(in: base, type, data: data_, domains: domains_, paths: paths_);
182 case category_t::information: return util::create_info(in: base, name: in_use_id_);
183 case category_t::boundary: break; // Not implemented
184 }
185 return base;
186 }
187
188 private:
189 std::vector<std::string> paths_;
190 std::vector<std::string> domains_;
191 std::string locale_id_;
192
193 util::locale_data data_;
194 std::string name_;
195 std::string in_use_id_;
196 utf8_support utf_mode_;
197 bool invalid_;
198 bool use_ansi_encoding_;
199 };
200
201 std::unique_ptr<localization_backend> create_localization_backend()
202 {
203 return make_std_unique<std_localization_backend>();
204 }
205
206}}} // namespace boost::locale::impl_std
207

source code of boost/libs/locale/src/boost/locale/std/std_backend.cpp