#include <iostream>
#include <map>
-#include "zypp/Locale.h"
+#include <zypp/Locale.h>
+#include <zypp/ZConfig.h>
using std::endl;
///////////////////////////////////////////////////////////////////
namespace zypp
-{ /////////////////////////////////////////////////////////////////
+{
+ /** Wrap static codemap data. */
+ struct CodeMaps
+ {
+ /** Return IdString without trailing garbage. */
+ static IdString withoutTrash( IdString code_r )
+ {
+ boost::string_ref::size_type sep = trashStart( code_r );
+ if ( sep != boost::string_ref::npos )
+ code_r = IdString( code_r.c_str(), sep );
+ return code_r;
+ }
- typedef std::map<std::string, std::string> OtherDefaultLanguage;
- static OtherDefaultLanguage otherDefaultLanguage;
+ /** Return IdString without trailing garbage. */
+ static IdString withoutTrash( const std::string & code_r )
+ { return withoutTrash( boost::string_ref(code_r) ); }
- ///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : Locale::Impl
- //
- /** Locale implementation. */
- struct Locale::Impl
- {
- Impl()
- {}
+ /** Return IdString without trailing garbage. */
+ static IdString withoutTrash( const char * code_r )
+ { return( code_r ? withoutTrash( boost::string_ref(code_r) ) : IdString::Null ); }
- Impl( const std::string & code_r )
+ /** Return IdString from language/country codes. */
+ static IdString combineLC( LanguageCode language_r, CountryCode country_r )
{
- std::string t;
- std::string::size_type sep = code_r.find_first_of( "@." );
- if ( sep == std::string::npos ) {
- t = code_r;
- } else {
- t = code_r.substr( 0, sep );
+ IdString ret;
+ if ( language_r )
+ {
+ if ( country_r )
+ ret = IdString( std::string(language_r) + "_" + country_r.c_str() );
+ else
+ ret = IdString(language_r);
}
-
- sep = t.find( '_' );
- if ( sep == std::string::npos ) {
- _language = LanguageCode( t );
- } else {
- _language = LanguageCode( t.substr( 0, sep ) );
- _country = CountryCode( t.substr( sep+1 ) );
+ else
+ {
+ if ( country_r )
+ ret = IdString( "_" + std::string(country_r) );
+ else if ( ! ( IdString(language_r) || IdString(country_r) ) )
+ ret = IdString::Null;
+ // else IdString::Empty
}
+ return ret;
}
- Impl( const LanguageCode & language_r,
- const CountryCode & country_r )
- : _language( language_r )
- , _country( country_r )
- {}
+ /** The singleton */
+ static CodeMaps & instance()
+ {
+ static CodeMaps _instance;
+ return _instance;
+ }
- const LanguageCode & language() const
- { return _language; }
+ LanguageCode language( IdString index_r )
+ { return getIndex( index_r )._l; }
- const CountryCode & country() const
- { return _country; }
+ CountryCode country( IdString index_r )
+ { return getIndex( index_r )._c; }
- std::string code() const
+ std::string name( IdString index_r )
{
- std::string ret( _language.code() );
- if ( _country.hasCode() )
- ret += "_" + _country.code();
+ const LC & lc( getIndex( index_r ) );
+ std::string ret( lc._l.name() );
+ if ( lc._c )
+ {
+ ret += " (";
+ ret += lc._c.name();
+ ret += ")";
+ }
return ret;
}
- std::string name() const
+ Locale fallback( IdString index_r )
{
- std::string ret( _language.name() );
- if ( _country.hasCode() )
- ret += " (" + _country.name() + ")";
+ static const IdString special( "pt_BR" );
+ Locale ret;
+ if ( index_r == special ) // "pt_BR"->"en" - by now the only fallback exception
+ ret = Locale::enCode;
+ else
+ {
+ const LC & lc( getIndex( index_r ) );
+ if ( lc._c )
+ ret = lc._l;
+ else if ( lc._l && lc._l != LanguageCode::enCode )
+ ret = Locale::enCode;
+ }
return ret;
}
- Locale fallback() const
+ private:
+ static IdString withoutTrash( boost::string_ref code_r )
{
- if (otherDefaultLanguage.size() == 0) {
- // initial inserting map
- otherDefaultLanguage["pt_BR"] = "en";
- }
-
- if (otherDefaultLanguage.find(code()) != otherDefaultLanguage.end())
- return LanguageCode(otherDefaultLanguage[code()]);
-
- if ( _country.hasCode() )
- return _language;
+ boost::string_ref::size_type sep = trashStart( code_r );
+ if ( sep != boost::string_ref::npos )
+ code_r = code_r.substr( 0, sep );
+ return IdString( code_r );
+ }
- if ( _language.hasCode() && _language != LanguageCode("en") )
- return LanguageCode("en");
+ static boost::string_ref::size_type trashStart( boost::string_ref code_r )
+ { return code_r.find_first_of( "@." ); }
- return Locale();
- }
+ static boost::string_ref::size_type trashStart( IdString code_r )
+ { return trashStart( boost::string_ref(code_r.c_str()) ); }
private:
+ struct LC {
+ LC() {}
+ LC( LanguageCode l_r ) : _l( l_r ) {}
+ LC( LanguageCode l_r, CountryCode c_r ) : _l( l_r ), _c( c_r ) {}
+ LanguageCode _l;
+ CountryCode _c;
+ };
+ typedef std::unordered_map<IdString,LC> CodeMap;
+
+ /** Ctor initializes the code maps. */
+ CodeMaps()
+ : _codeMap( { { IdString::Null, LC( LanguageCode(IdString::Null), CountryCode(IdString::Null) ) }
+ , { IdString::Empty, LC( LanguageCode(IdString::Empty), CountryCode(IdString::Empty) ) } } )
+ {}
- LanguageCode _language;
- CountryCode _country;
-
- public:
- /** Offer default Impl. */
- static shared_ptr<Impl> nullimpl()
+ /** Return \ref LC for \a index_r, creating it if necessary. */
+ const LC & getIndex( IdString index_r )
{
- static shared_ptr<Impl> _nullimpl( new Impl );
- return _nullimpl;
+ auto it = _codeMap.find( index_r );
+ if ( it == _codeMap.end() )
+ {
+ CodeMap::value_type newval( index_r, LC() );
+
+ boost::string_ref str( index_r.c_str() );
+ boost::string_ref::size_type sep = str.find( '_' );
+ if ( sep == boost::string_ref::npos )
+ newval.second._l = LanguageCode( index_r );
+ else
+ {
+ // bsc#1064999: dup! Creating a new IdString may invalidate the IdString.c_str() stored in str.
+ std::string dup( str );
+ str = dup;
+ newval.second._l = LanguageCode( IdString(str.substr( 0, sep )) );
+ newval.second._c = CountryCode( IdString(str.substr( sep+1 )) );
+ }
+
+ it = _codeMap.insert( std::move(newval) ).first;
+ }
+ return it->second;
}
- };
- ///////////////////////////////////////////////////////////////////
- /** \relates Locale::Impl Stream output */
- inline std::ostream & operator<<( std::ostream & str, const Locale::Impl & obj )
- {
- return str << "Locale::Impl";
- }
+ private:
+ CodeMap _codeMap;
+ };
///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : Locale
- //
+ // class Locale
///////////////////////////////////////////////////////////////////
const Locale Locale::noCode;
+ const LanguageCode LanguageCode::enCode("en"); // from in LanguageCode.cc as Locale::enCode depends on it
+ const Locale Locale::enCode( LanguageCode::enCode );
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::Locale
- // METHOD TYPE : Ctor
- //
Locale::Locale()
- : _pimpl( Impl::nullimpl() )
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::Locale
- // METHOD TYPE : Ctor
- //
- Locale::Locale( IdString code_r )
- : _pimpl( new Impl( code_r.asString() ) )
+ Locale::Locale( IdString str_r )
+ : _str( CodeMaps::withoutTrash( str_r ) )
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::Locale
- // METHOD TYPE : Ctor
- //
- Locale::Locale( const std::string & code_r )
- : _pimpl( new Impl( code_r ) )
+ Locale::Locale( const std::string & str_r )
+ : _str( CodeMaps::withoutTrash( str_r ) )
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::Locale
- // METHOD TYPE : Ctor
- //
- Locale::Locale( const char * code_r )
- : _pimpl( new Impl( C_Str(code_r).c_str() ) )
+ Locale::Locale( const char * str_r )
+ : _str( CodeMaps::withoutTrash( str_r ) )
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::Locale
- // METHOD TYPE : Ctor
- //
- Locale::Locale( const LanguageCode & language_r,
- const CountryCode & country_r )
- : _pimpl( new Impl( language_r, country_r ) )
+ Locale::Locale( LanguageCode language_r, CountryCode country_r )
+ : _str( CodeMaps::combineLC( language_r, country_r ) )
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::~Locale
- // METHOD TYPE : Dtor
- //
Locale::~Locale()
{}
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::
- // METHOD TYPE :
- //
- const LanguageCode & Locale::language() const
- { return _pimpl->language(); }
-
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::
- // METHOD TYPE :
- //
- const CountryCode & Locale::country() const
- { return _pimpl->country(); }
+ LanguageCode Locale::language() const
+ { return CodeMaps::instance().language( _str ); }
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::
- // METHOD TYPE :
- //
- std::string Locale::code() const
- { return _pimpl->code(); }
+ CountryCode Locale::country() const
+ { return CodeMaps::instance().country( _str ); }
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::
- // METHOD TYPE :
- //
std::string Locale::name() const
- { return _pimpl->name(); }
+ { return CodeMaps::instance().name( _str ); }
- ///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Locale::
- // METHOD TYPE :
- //
Locale Locale::fallback() const
- { return _pimpl->fallback(); }
- /////////////////////////////////////////////////////////////////
+ { return CodeMaps::instance().fallback( _str ); }
+
+ ///////////////////////////////////////////////////////////////////
+
+ Locale Locale::bestMatch( const LocaleSet & avLocales_r, Locale requested_r )
+ {
+ if ( ! avLocales_r.empty() )
+ {
+ if ( ! requested_r )
+ requested_r = ZConfig::instance().textLocale();
+ for ( ; requested_r; requested_r = requested_r.fallback() )
+ {
+ if ( avLocales_r.count( requested_r ) )
+ return requested_r;
+ }
+ }
+ return Locale();
+ }
+
} // namespace zypp
///////////////////////////////////////////////////////////////////