aboutsummaryrefslogtreecommitdiff
path: root/src/corelib/i18n/mlocale.cpp
diff options
context:
space:
mode:
authorHolger Schroeder <holger.schroeder.ext@basyskom.de>2010-05-31 11:19:09 +0200
committerMike FABIAN <mike.fabian@basyskom.de>2010-06-02 12:04:10 +0200
commit726ff87d372e0f5f724131573e37268f6b60e873 (patch)
tree138d8a07cfe6896e40bfff04805a455178f9629d /src/corelib/i18n/mlocale.cpp
parentb33ff2e9841b726bd8c2dc5581367f2143f44794 (diff)
Changes: Implement phone number grouping feature
RevBy: Mike Fabian Details: This implements the formatPhoneNumber() function in MLocale and an according unit test. All examples from the spec i have are working correctly.
Diffstat (limited to 'src/corelib/i18n/mlocale.cpp')
-rw-r--r--src/corelib/i18n/mlocale.cpp525
1 files changed, 525 insertions, 0 deletions
diff --git a/src/corelib/i18n/mlocale.cpp b/src/corelib/i18n/mlocale.cpp
index c63ad0ae..23cf4d4e 100644
--- a/src/corelib/i18n/mlocale.cpp
+++ b/src/corelib/i18n/mlocale.cpp
@@ -346,6 +346,7 @@ MLocalePrivate::MLocalePrivate()
#endif
, q_ptr(0)
{
+ loadCountryCodes();
}
// copy constructor
@@ -358,6 +359,7 @@ MLocalePrivate::MLocalePrivate(const MLocalePrivate &other)
_calendarLocale(other._calendarLocale),
_monetaryLocale(other._monetaryLocale),
_nameLocale(other._nameLocale),
+ _validCountryCodes( other._validCountryCodes ),
_calendarType(other._calendarType),
_collation(other._collation),
#ifdef HAVE_ICU
@@ -406,6 +408,7 @@ MLocalePrivate &MLocalePrivate::operator=(const MLocalePrivate &other)
_messageTranslations = other._messageTranslations;
_timeTranslations = other._timeTranslations;
_trTranslations = other._trTranslations;
+ _validCountryCodes = other._validCountryCodes;
#ifdef HAVE_ICU
delete _numberFormat;
@@ -421,6 +424,241 @@ MLocalePrivate &MLocalePrivate::operator=(const MLocalePrivate &other)
return *this;
}
+void MLocalePrivate::loadCountryCodes()
+{
+ _validCountryCodes.clear();
+
+ _validCountryCodes[ "1" ] = 1;
+ _validCountryCodes[ "20" ] = 1;
+ _validCountryCodes[ "212" ] = 1;
+ _validCountryCodes[ "213" ] = 1;
+ _validCountryCodes[ "214" ] = 1;
+ _validCountryCodes[ "215" ] = 1;
+ _validCountryCodes[ "216" ] = 1;
+ _validCountryCodes[ "218" ] = 1;
+ _validCountryCodes[ "219" ] = 1;
+ _validCountryCodes[ "220" ] = 1;
+ _validCountryCodes[ "221" ] = 1;
+ _validCountryCodes[ "222" ] = 1;
+ _validCountryCodes[ "223" ] = 1;
+ _validCountryCodes[ "224" ] = 1;
+ _validCountryCodes[ "225" ] = 1;
+ _validCountryCodes[ "226" ] = 1;
+ _validCountryCodes[ "227" ] = 1;
+ _validCountryCodes[ "228" ] = 1;
+ _validCountryCodes[ "229" ] = 1;
+ _validCountryCodes[ "230" ] = 1;
+ _validCountryCodes[ "231" ] = 1;
+ _validCountryCodes[ "232" ] = 1;
+ _validCountryCodes[ "233" ] = 1;
+ _validCountryCodes[ "234" ] = 1;
+ _validCountryCodes[ "235" ] = 1;
+ _validCountryCodes[ "236" ] = 1;
+ _validCountryCodes[ "237" ] = 1;
+ _validCountryCodes[ "238" ] = 1;
+ _validCountryCodes[ "239" ] = 1;
+ _validCountryCodes[ "240" ] = 1;
+ _validCountryCodes[ "241" ] = 1;
+ _validCountryCodes[ "242" ] = 1;
+ _validCountryCodes[ "243" ] = 1;
+ _validCountryCodes[ "244" ] = 1;
+ _validCountryCodes[ "245" ] = 1;
+ _validCountryCodes[ "246" ] = 1;
+ _validCountryCodes[ "247" ] = 1;
+ _validCountryCodes[ "248" ] = 1;
+ _validCountryCodes[ "249" ] = 1;
+ _validCountryCodes[ "250" ] = 1;
+ _validCountryCodes[ "251" ] = 1;
+ _validCountryCodes[ "252" ] = 1;
+ _validCountryCodes[ "253" ] = 1;
+ _validCountryCodes[ "254" ] = 1;
+ _validCountryCodes[ "255" ] = 1;
+ _validCountryCodes[ "256" ] = 1;
+ _validCountryCodes[ "257" ] = 1;
+ _validCountryCodes[ "258" ] = 1;
+ _validCountryCodes[ "259" ] = 1;
+ _validCountryCodes[ "260" ] = 1;
+ _validCountryCodes[ "261" ] = 1;
+ _validCountryCodes[ "262" ] = 1;
+ _validCountryCodes[ "263" ] = 1;
+ _validCountryCodes[ "264" ] = 1;
+ _validCountryCodes[ "265" ] = 1;
+ _validCountryCodes[ "266" ] = 1;
+ _validCountryCodes[ "267" ] = 1;
+ _validCountryCodes[ "268" ] = 1;
+ _validCountryCodes[ "269" ] = 1;
+ _validCountryCodes[ "27" ] = 1;
+ _validCountryCodes[ "290" ] = 1;
+ _validCountryCodes[ "291" ] = 1;
+ _validCountryCodes[ "297" ] = 1;
+ _validCountryCodes[ "298" ] = 1;
+ _validCountryCodes[ "299" ] = 1;
+ _validCountryCodes[ "30" ] = 1;
+ _validCountryCodes[ "31" ] = 1;
+ _validCountryCodes[ "32" ] = 1;
+ _validCountryCodes[ "33" ] = 1;
+ _validCountryCodes[ "34" ] = 1;
+ _validCountryCodes[ "350" ] = 1;
+ _validCountryCodes[ "351" ] = 1;
+ _validCountryCodes[ "352" ] = 1;
+ _validCountryCodes[ "353" ] = 1;
+ _validCountryCodes[ "354" ] = 1;
+ _validCountryCodes[ "355" ] = 1;
+ _validCountryCodes[ "356" ] = 1;
+ _validCountryCodes[ "357" ] = 1;
+ _validCountryCodes[ "358" ] = 1;
+ _validCountryCodes[ "359" ] = 1;
+ _validCountryCodes[ "36" ] = 1;
+ _validCountryCodes[ "370" ] = 1;
+ _validCountryCodes[ "371" ] = 1;
+ _validCountryCodes[ "372" ] = 1;
+ _validCountryCodes[ "373" ] = 1;
+ _validCountryCodes[ "374" ] = 1;
+ _validCountryCodes[ "375" ] = 1;
+ _validCountryCodes[ "376" ] = 1;
+ _validCountryCodes[ "377" ] = 1;
+ _validCountryCodes[ "378" ] = 1;
+ _validCountryCodes[ "379" ] = 1;
+ _validCountryCodes[ "380" ] = 1;
+ _validCountryCodes[ "381" ] = 1;
+ _validCountryCodes[ "382" ] = 1;
+ _validCountryCodes[ "385" ] = 1;
+ _validCountryCodes[ "386" ] = 1;
+ _validCountryCodes[ "387" ] = 1;
+ _validCountryCodes[ "388" ] = 1;
+ _validCountryCodes[ "389" ] = 1;
+ _validCountryCodes[ "39" ] = 1;
+ _validCountryCodes[ "40" ] = 1;
+ _validCountryCodes[ "41" ] = 1;
+ _validCountryCodes[ "420" ] = 1;
+ _validCountryCodes[ "421" ] = 1;
+ _validCountryCodes[ "423" ] = 1;
+ _validCountryCodes[ "43" ] = 1;
+ _validCountryCodes[ "44" ] = 1;
+ _validCountryCodes[ "45" ] = 1;
+ _validCountryCodes[ "46" ] = 1;
+ _validCountryCodes[ "47" ] = 1;
+ _validCountryCodes[ "48" ] = 1;
+ _validCountryCodes[ "49" ] = 1;
+ _validCountryCodes[ "500" ] = 1;
+ _validCountryCodes[ "501" ] = 1;
+ _validCountryCodes[ "502" ] = 1;
+ _validCountryCodes[ "503" ] = 1;
+ _validCountryCodes[ "504" ] = 1;
+ _validCountryCodes[ "505" ] = 1;
+ _validCountryCodes[ "506" ] = 1;
+ _validCountryCodes[ "507" ] = 1;
+ _validCountryCodes[ "508" ] = 1;
+ _validCountryCodes[ "509" ] = 1;
+ _validCountryCodes[ "51" ] = 1;
+ _validCountryCodes[ "52" ] = 1;
+ _validCountryCodes[ "53" ] = 1;
+ _validCountryCodes[ "54" ] = 1;
+ _validCountryCodes[ "55" ] = 1;
+ _validCountryCodes[ "56" ] = 1;
+ _validCountryCodes[ "57" ] = 1;
+ _validCountryCodes[ "58" ] = 1;
+ _validCountryCodes[ "590" ] = 1;
+ _validCountryCodes[ "591" ] = 1;
+ _validCountryCodes[ "592" ] = 1;
+ _validCountryCodes[ "593" ] = 1;
+ _validCountryCodes[ "594" ] = 1;
+ _validCountryCodes[ "595" ] = 1;
+ _validCountryCodes[ "596" ] = 1;
+ _validCountryCodes[ "597" ] = 1;
+ _validCountryCodes[ "598" ] = 1;
+ _validCountryCodes[ "599" ] = 1;
+ _validCountryCodes[ "60" ] = 1;
+ _validCountryCodes[ "61" ] = 1;
+ _validCountryCodes[ "62" ] = 1;
+ _validCountryCodes[ "63" ] = 1;
+ _validCountryCodes[ "64" ] = 1;
+ _validCountryCodes[ "65" ] = 1;
+ _validCountryCodes[ "66" ] = 1;
+ _validCountryCodes[ "670" ] = 1;
+ _validCountryCodes[ "672" ] = 1;
+ _validCountryCodes[ "673" ] = 1;
+ _validCountryCodes[ "674" ] = 1;
+ _validCountryCodes[ "675" ] = 1;
+ _validCountryCodes[ "676" ] = 1;
+ _validCountryCodes[ "677" ] = 1;
+ _validCountryCodes[ "678" ] = 1;
+ _validCountryCodes[ "679" ] = 1;
+ _validCountryCodes[ "680" ] = 1;
+ _validCountryCodes[ "681" ] = 1;
+ _validCountryCodes[ "682" ] = 1;
+ _validCountryCodes[ "683" ] = 1;
+ _validCountryCodes[ "685" ] = 1;
+ _validCountryCodes[ "686" ] = 1;
+ _validCountryCodes[ "687" ] = 1;
+ _validCountryCodes[ "688" ] = 1;
+ _validCountryCodes[ "689" ] = 1;
+ _validCountryCodes[ "690" ] = 1;
+ _validCountryCodes[ "691" ] = 1;
+ _validCountryCodes[ "692" ] = 1;
+ _validCountryCodes[ "7" ] = 1;
+ _validCountryCodes[ "800" ] = 1;
+ _validCountryCodes[ "808" ] = 1;
+ _validCountryCodes[ "81" ] = 1;
+ _validCountryCodes[ "82" ] = 1;
+ _validCountryCodes[ "84" ] = 1;
+ _validCountryCodes[ "850" ] = 1;
+ _validCountryCodes[ "852" ] = 1;
+ _validCountryCodes[ "853" ] = 1;
+ _validCountryCodes[ "855" ] = 1;
+ _validCountryCodes[ "856" ] = 1;
+ _validCountryCodes[ "86" ] = 1;
+ _validCountryCodes[ "870" ] = 1;
+ _validCountryCodes[ "871" ] = 1;
+ _validCountryCodes[ "872" ] = 1;
+ _validCountryCodes[ "873" ] = 1;
+ _validCountryCodes[ "874" ] = 1;
+ _validCountryCodes[ "878" ] = 1;
+ _validCountryCodes[ "880" ] = 1;
+ _validCountryCodes[ "881" ] = 1;
+ _validCountryCodes[ "882" ] = 1;
+ _validCountryCodes[ "883" ] = 1;
+ _validCountryCodes[ "886" ] = 1;
+ _validCountryCodes[ "888" ] = 1;
+ _validCountryCodes[ "90" ] = 1;
+ _validCountryCodes[ "91" ] = 1;
+ _validCountryCodes[ "92" ] = 1;
+ _validCountryCodes[ "93" ] = 1;
+ _validCountryCodes[ "94" ] = 1;
+ _validCountryCodes[ "95" ] = 1;
+ _validCountryCodes[ "960" ] = 1;
+ _validCountryCodes[ "961" ] = 1;
+ _validCountryCodes[ "962" ] = 1;
+ _validCountryCodes[ "963" ] = 1;
+ _validCountryCodes[ "964" ] = 1;
+ _validCountryCodes[ "965" ] = 1;
+ _validCountryCodes[ "966" ] = 1;
+ _validCountryCodes[ "967" ] = 1;
+ _validCountryCodes[ "968" ] = 1;
+ _validCountryCodes[ "970" ] = 1;
+ _validCountryCodes[ "971" ] = 1;
+ _validCountryCodes[ "972" ] = 1;
+ _validCountryCodes[ "973" ] = 1;
+ _validCountryCodes[ "974" ] = 1;
+ _validCountryCodes[ "975" ] = 1;
+ _validCountryCodes[ "976" ] = 1;
+ _validCountryCodes[ "977" ] = 1;
+ _validCountryCodes[ "979" ] = 1;
+ _validCountryCodes[ "98" ] = 1;
+ _validCountryCodes[ "991" ] = 1;
+ _validCountryCodes[ "992" ] = 1;
+ _validCountryCodes[ "993" ] = 1;
+ _validCountryCodes[ "994" ] = 1;
+ _validCountryCodes[ "995" ] = 1;
+ _validCountryCodes[ "996" ] = 1;
+ _validCountryCodes[ "998" ] = 1;
+}
+
+bool MLocalePrivate::isValidCountryCode( const QString& code ) const
+{
+ return _validCountryCodes.contains( code );
+}
+
#ifdef HAVE_ICU
// creates icu::Locale presenting a category
Locale MLocalePrivate::getCategoryLocale(MLocale::Category category) const
@@ -2065,3 +2303,290 @@ void MLocale::refreshSettings()
#endif
}
+QString MLocale::formatPhoneNumber( const QString& phoneNumber,
+ PhoneNumberGrouping grouping ) const
+{
+ Q_D(const MLocale);
+ return d->formatPhoneNumber( phoneNumber, grouping );
+}
+
+// when string starts with numbers 2 to 9
+QString groupedNormalString( const QString& phoneNumber )
+{
+ QString result;
+ QString remaining( phoneNumber );
+
+ // for remaining number length 1 (also 0) to 3 return number unchanged
+ if ( remaining.length() < 4 )
+ {
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.length() < 8 )
+ {
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( '-' );
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.length() < 11 )
+ {
+ result.append( '(' );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( ") " );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( '-' );
+ result.append( remaining );
+ return result;
+ }
+ else
+ {
+ result.append( remaining );
+ return result;
+ }
+}
+
+// when string starts with number 1
+QString groupedOneString( const QString& phoneNumber )
+{
+ QString result;
+ QString remaining( phoneNumber );
+
+ // for remaining number length 1 (also 0) to 3 return number unchanged
+ if ( remaining.length() < 2 )
+ {
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.length() < 3 )
+ {
+ result.append( remaining.left( 1 ) );
+ remaining.remove( 0, 1 );
+ result.append( " (" );
+ result.append( remaining );
+ result.append( " )" );
+ return result;
+ }
+ else if ( remaining.length() < 4 )
+ {
+ result.append( remaining.left( 1 ) );
+ remaining.remove( 0, 1 );
+ result.append( " (" );
+ result.append( remaining );
+ result.append( " )" );
+ return result;
+ }
+ else if ( remaining.length() < 5 )
+ {
+ result.append( remaining.left( 1 ) );
+ remaining.remove( 0, 1 );
+ result.append( " (" );
+ result.append( remaining );
+ result.append( ')' );
+ return result;
+ }
+ else if ( remaining.length() < 8 )
+ {
+ result.append( remaining.left( 1 ) );
+ remaining.remove( 0, 1 );
+ result.append( " (" );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( ") " );
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.length() < 12 )
+ {
+ result.append( remaining.left( 1 ) );
+ remaining.remove( 0, 1 );
+ result.append( " (" );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( ") " );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( '-' );
+ result.append( remaining );
+ return result;
+ }
+ else
+ {
+ result.append( remaining );
+ return result;
+ }
+}
+
+// when string starts with numbers 2 to 9
+QString groupedInternationalString( const QString& phoneNumber )
+{
+ QString result;
+ QString remaining( phoneNumber );
+
+ // for remaining number length 1 (also 0) to 3 return number unchanged
+ if ( remaining.length() < 4 )
+ {
+ result.append( '(' );
+ result.append( remaining );
+ result.append( ')' );
+ return result;
+ }
+ else if ( remaining.length() < 7 )
+ {
+ result.append( '(' );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( ") " );
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.length() < 11 )
+ {
+ result.append( '(' );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( ") " );
+ result.append( remaining.left( 3 ) );
+ remaining.remove( 0, 3 );
+ result.append( '-' );
+ result.append( remaining );
+ return result;
+ }
+ else
+ {
+ result.append( remaining );
+ return result;
+ }
+}
+
+QString MLocalePrivate::formatPhoneNumber( const QString& phoneNumber,
+ MLocale::PhoneNumberGrouping grouping ) const
+{
+ // first do sanity check of the input string
+ QRegExp rx( "\\+?\\d*" );
+ if ( ! rx.exactMatch( phoneNumber ) )
+ {
+ qWarning( "MLocale::formatPhoneNumber: cannot understand number: %s",
+ qPrintable( phoneNumber ) );
+ return phoneNumber;
+ }
+
+ // 00 is not a valid country calling code in north america
+ // -> do not do grouping in this case at all
+ if ( ( grouping == MLocale::NorthAmericanGrouping )
+ && phoneNumber.startsWith( "00" ) )
+ {
+ return phoneNumber;
+ }
+
+ QString remaining( phoneNumber );
+ QString result;
+
+ // if we find any error, we will return the string unchanged
+ // bool error = false;
+
+ // first extract the country code
+
+ bool foundCountryCodeIndicator = false;
+
+ // valid beginnings for a country code are "+", "00" or "011"
+ if ( remaining.startsWith( '+' ) )
+ {
+ foundCountryCodeIndicator = true;
+ result.append( '+' );
+ remaining.remove( 0, 1 );
+ }
+ else if ( remaining.startsWith( "00" ) )
+ {
+ foundCountryCodeIndicator = true;
+ result.append( "00 " );
+ remaining.remove( 0, 2 );
+ }
+ else if ( remaining.startsWith( "011" ) )
+ {
+ foundCountryCodeIndicator = true;
+ result.append( "011 " );
+ remaining.remove( 0, 3 );
+ }
+
+
+ // now check for valid country code
+ if ( foundCountryCodeIndicator )
+ {
+ int length = 1;
+ QString code;
+
+ code = remaining.left( length );
+
+ if ( isValidCountryCode( code ) )
+ {
+ result.append( code );
+ result.append( ' ' );
+ remaining.remove( 0, code.length() );
+ }
+ else
+ {
+ length = 2;
+ code = remaining.left( length );
+ if ( isValidCountryCode( code ) )
+ {
+ result.append( code );
+ result.append( ' ' );
+ remaining.remove( 0, code.length() );
+ }
+ else
+ {
+ length = 3;
+ code = remaining.left( length );
+ if ( isValidCountryCode( code ) )
+ {
+ result.append( code );
+ result.append( ' ' );
+ remaining.remove( 0, code.length() );
+ }
+ else
+ {
+ // no valid country code -> error -> return string
+ return phoneNumber;
+ }
+ }
+ }
+ } // found country code indicator
+
+ // if it exists, the country code is split off now
+ if ( grouping != MLocale::NorthAmericanGrouping )
+ {
+ result.append( remaining );
+ return result;
+ }
+ else
+ {
+ // has country code -> do not handle one special.
+ if ( foundCountryCodeIndicator )
+ {
+ result.append( groupedInternationalString( remaining ) );
+ return result;
+ }
+ // 11 is an invalid code, so disable grouping for this case
+ else if ( remaining.startsWith( "11" ) )
+ {
+ result.append( remaining );
+ return result;
+ }
+ else if ( remaining.startsWith( '1' ) )
+ {
+ result.append( groupedOneString( remaining ) );
+ return result;
+ }
+ else
+ {
+ result.append( groupedNormalString( remaining ) );
+ return result;
+ }
+ }
+
+ return phoneNumber;
+}