Imported Upstream version 17.23.5
[platform/upstream/libzypp.git] / zypp / Locale.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/Locale.cc
10  *
11 */
12 #include <iostream>
13 #include <map>
14
15 #include <zypp/Locale.h>
16 #include <zypp/ZConfig.h>
17
18 using std::endl;
19
20 ///////////////////////////////////////////////////////////////////
21 namespace zypp
22 {
23   /** Wrap static codemap data. */
24   struct CodeMaps
25   {
26    /** Return IdString without trailing garbage. */
27     static IdString withoutTrash( IdString code_r )
28     {
29        boost::string_ref::size_type sep = trashStart( code_r );
30        if ( sep != boost::string_ref::npos )
31          code_r = IdString( code_r.c_str(), sep );
32        return code_r;
33     }
34
35     /** Return IdString without trailing garbage. */
36     static IdString withoutTrash( const std::string & code_r )
37     { return withoutTrash( boost::string_ref(code_r) ); }
38
39     /** Return IdString without trailing garbage. */
40     static IdString withoutTrash( const char * code_r )
41     { return( code_r ? withoutTrash( boost::string_ref(code_r) ) : IdString::Null ); }
42
43     /** Return IdString from language/country codes. */
44     static IdString combineLC( LanguageCode language_r, CountryCode country_r )
45     {
46       IdString ret;
47       if ( language_r )
48       {
49         if ( country_r )
50           ret = IdString( std::string(language_r) + "_" + country_r.c_str() );
51         else
52           ret = IdString(language_r);
53       }
54       else
55       {
56         if ( country_r )
57           ret = IdString( "_" + std::string(country_r) );
58         else if ( ! ( IdString(language_r) || IdString(country_r) ) )
59           ret = IdString::Null;
60         // else IdString::Empty
61       }
62       return ret;
63     }
64
65     /** The singleton */
66     static CodeMaps & instance()
67     {
68       static CodeMaps _instance;
69       return _instance;
70     }
71
72     LanguageCode language( IdString index_r )
73     { return getIndex( index_r )._l; }
74
75     CountryCode country( IdString index_r )
76     { return  getIndex( index_r )._c; }
77
78     std::string name( IdString index_r )
79     {
80       const LC & lc( getIndex( index_r ) );
81       std::string ret( lc._l.name() );
82       if ( lc._c )
83       {
84         ret += " (";
85         ret += lc._c.name();
86         ret += ")";
87       }
88       return ret;
89     }
90
91     Locale fallback( IdString index_r )
92     {
93       static const IdString special( "pt_BR" );
94       Locale ret;
95       if ( index_r == special ) // "pt_BR"->"en" - by now the only fallback exception
96         ret = Locale::enCode;
97       else
98       {
99         const LC & lc( getIndex( index_r ) );
100         if ( lc._c )
101           ret = lc._l;
102         else if ( lc._l && lc._l != LanguageCode::enCode )
103           ret = Locale::enCode;
104       }
105       return ret;
106     }
107
108   private:
109     static IdString withoutTrash( boost::string_ref code_r )
110     {
111       boost::string_ref::size_type sep = trashStart( code_r );
112       if ( sep != boost::string_ref::npos )
113         code_r = code_r.substr( 0, sep );
114       return IdString( code_r );
115     }
116
117     static boost::string_ref::size_type trashStart( boost::string_ref code_r )
118     { return code_r.find_first_of( "@." ); }
119
120     static boost::string_ref::size_type trashStart( IdString code_r )
121     { return trashStart( boost::string_ref(code_r.c_str()) ); }
122
123   private:
124     struct LC {
125       LC()                                      {}
126       LC( LanguageCode l_r )                    : _l( l_r ) {}
127       LC( LanguageCode l_r, CountryCode c_r )   : _l( l_r ), _c( c_r ) {}
128       LanguageCode _l;
129       CountryCode  _c;
130     };
131     typedef std::unordered_map<IdString,LC> CodeMap;
132
133     /** Ctor initializes the code maps. */
134     CodeMaps()
135     : _codeMap( { { IdString::Null,  LC( LanguageCode(IdString::Null),  CountryCode(IdString::Null) )  }
136                 , { IdString::Empty, LC( LanguageCode(IdString::Empty), CountryCode(IdString::Empty) ) } } )
137     {}
138
139     /** Return \ref LC for \a index_r, creating it if necessary. */
140     const LC & getIndex( IdString index_r )
141     {
142       auto it = _codeMap.find( index_r );
143       if ( it == _codeMap.end() )
144       {
145         CodeMap::value_type newval( index_r, LC() );
146
147         boost::string_ref str( index_r.c_str() );
148         boost::string_ref::size_type sep = str.find( '_' );
149         if ( sep == boost::string_ref::npos )
150           newval.second._l = LanguageCode( index_r );
151         else
152         {
153           // bsc#1064999: dup! Creating a new IdString may invalidate the IdString.c_str() stored in str.
154           std::string dup( str );
155           str = dup;
156           newval.second._l = LanguageCode( IdString(str.substr( 0, sep )) );
157           newval.second._c = CountryCode( IdString(str.substr( sep+1 )) );
158         }
159
160         it = _codeMap.insert( std::move(newval) ).first;
161       }
162       return it->second;
163     }
164
165   private:
166     CodeMap _codeMap;
167   };
168
169   ///////////////////////////////////////////////////////////////////
170   // class Locale
171   ///////////////////////////////////////////////////////////////////
172
173   const Locale Locale::noCode;
174   const LanguageCode LanguageCode::enCode("en");        // from in LanguageCode.cc as Locale::enCode depends on it
175   const Locale Locale::enCode( LanguageCode::enCode );
176
177   Locale::Locale()
178   {}
179
180   Locale::Locale( IdString str_r )
181   : _str( CodeMaps::withoutTrash( str_r ) )
182   {}
183
184   Locale::Locale( const std::string & str_r )
185   : _str( CodeMaps::withoutTrash( str_r ) )
186   {}
187
188   Locale::Locale( const char * str_r )
189   : _str( CodeMaps::withoutTrash( str_r ) )
190   {}
191
192   Locale::Locale( LanguageCode language_r, CountryCode country_r )
193   : _str( CodeMaps::combineLC( language_r, country_r ) )
194   {}
195
196   Locale::~Locale()
197   {}
198
199   LanguageCode Locale::language() const
200   { return CodeMaps::instance().language( _str ); }
201
202   CountryCode Locale::country() const
203   { return CodeMaps::instance().country( _str ); }
204
205   std::string Locale::name() const
206   { return CodeMaps::instance().name( _str ); }
207
208   Locale Locale::fallback() const
209   { return CodeMaps::instance().fallback( _str ); }
210
211   ///////////////////////////////////////////////////////////////////
212
213   Locale Locale::bestMatch( const LocaleSet & avLocales_r, Locale requested_r )
214   {
215     if ( ! avLocales_r.empty() )
216     {
217       if ( ! requested_r )
218         requested_r = ZConfig::instance().textLocale();
219       for ( ; requested_r; requested_r = requested_r.fallback() )
220       {
221         if ( avLocales_r.count( requested_r ) )
222           return requested_r;
223       }
224     }
225     return Locale();
226   }
227
228 } // namespace zypp
229 ///////////////////////////////////////////////////////////////////