Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert ISO 3166-1 alpha-2 Country Code to localized Country Name

Is there a way to convert the two-letter Country Codes into their readable counterparts without using external ressources?

e.g. DE -> Germany, AD -> Andorra

It would be great if I could select the target language or it's using the system language, because I'd like to have them in German.

like image 733
Florian Koch Avatar asked Aug 14 '14 06:08

Florian Koch


People also ask

Should I use alpha-2 or Alpha 3 country codes?

The country codes can be represented either as a two-letter code (alpha-2) which is recommended as the general-purpose code, a three-letter code (alpha-3) which is more closely related to the country name and a three-digit numeric code (numeric-3) which can be useful if you need to avoid using Latin script.

What is an A2 country?

The A1 (Anonymous proxy), AP (Asian Pacific region), EU (Europe) and A2 (Satellite providers) codes refer to IP addresses of which the country of origin is uncertain. EU, for example, means that the precise IP country is uncertain but it belongs to Europe.

What is my 2 character country code?

MY is the two-letter country abbreviation for Malaysia.


2 Answers

As @Uwe mentioned in his comment, you can use the EnumSystemGeoID and GetGeoInfo functions. The principle is that with EnumSystemGeoID function you'll enumerate geographical location identifiers and by the GetGeoInfo function query if the enumerated identifier's ISO 2-letter country / region code (info type GEO_ISO2) equals to the one of your interest. If so, then you can query for this identifier with the same function either a friendly name (info type GEO_FRIENDLYNAME), or the official name (info type GEO_OFFICIALNAME), return the result and stop the enumeration.

Here is an example code, which might do that (unfortunately, the enumeration function does not support passing custom data, so I've used a global record variable for passing values):

type
  TEnumData = record
    GeoCode: string;
    GeoName: string;
    Success: Boolean;
  end;

  GEOID = type LONG;
  GEOTYPE = type DWORD;
  GEOCLASS = type DWORD;

  SYSGEOTYPE = (
    GEO_NATION = $0001,
    GEO_LATITUDE = $0002,
    GEO_LONGITUDE = $0003,
    GEO_ISO2 = $0004,
    GEO_ISO3 = $0005,
    GEO_RFC1766 = $0006,
    GEO_LCID = $0007,
    GEO_FRIENDLYNAME= $0008,
    GEO_OFFICIALNAME= $0009,
    GEO_TIMEZONES = $000A,
    GEO_OFFICIALLANGUAGES = $000B,
    GEO_ISO_UN_NUMBER = $000C,
    GEO_PARENT = $000D
  );

  SYSGEOCLASS = (
    GEOCLASS_NATION = 16,
    GEOCLASS_REGION = 14,
    GEOCLASS_ALL = 0
  );

  GEO_ENUMPROC = function(GeoId: GEOID): BOOL; stdcall;

  function EnumSystemGeoID(GeoClass: GEOCLASS;
    ParentGeoId: GEOID; lpGeoEnumProc: GEO_ENUMPROC): BOOL; stdcall;
    external kernel32 name 'EnumSystemGeoID';
  function GetGeoInfo(Location: GEOID; GeoType: GEOTYPE;
    lpGeoData: LPTSTR; cchData: Integer; LangId: LANGID): Integer; stdcall;
    external kernel32 name {$IFDEF UNICODE}'GetGeoInfoW'{$ELSE}'GetGeoInfoA'{$ENDIF};

implementation

var
  // I have used this global variable due to a lack of user data parameter for the callback function
  EnumData: TEnumData;

function TryGetGeoInfo(GeoId: GEOID; GeoType: GEOTYPE; out Value: string): Boolean;
var
  Buffer: string;
  BufferLen: Integer;
begin
  Result := False;
  BufferLen := GetGeoInfo(GeoId, GeoType, LPTSTR(Buffer), 0, 0);
  if BufferLen <> 0 then
  begin
    SetLength(Buffer, BufferLen);
    Result := GetGeoInfo(GeoId, GeoType, LPTSTR(Buffer), BufferLen, 0) <> 0;
    if Result then
      Value := Trim(Buffer);
  end;
end;

function EnumGeoInfoProc(GeoId: GEOID): BOOL; stdcall;
var
  S: string;
begin
  Result := TryGetGeoInfo(GeoId, GEOTYPE(GEO_ISO2), S);
  if Result and (S = EnumData.GeoCode) then
  begin
    // stop the enumeration since we've found the country by its ISO code
    Result := False;
    // return the success flag and try to return the friendly name of the country to the
    // EnumData.GeoName record field; you can optionally query the GEO_OFFICIALNAME
    EnumData.Success := TryGetGeoInfo(GeoId, GEOTYPE(GEO_FRIENDLYNAME), EnumData.GeoName);
  end;
end;

function TryGetCountryNameByISO2(const Code: string; out Name: string): Boolean;
begin
  // here is the brainless part using global record variable (because the function used
  // here with its callback does not support passing user data); no, you cannot tune it
  // up by making the callback function nested
  EnumData.GeoCode := Code;
  EnumData.Success := False;

  if not EnumSystemGeoID(GEOCLASS(GEOCLASS_NATION), 0, EnumGeoInfoProc) then
    RaiseLastOSError;

  Result := EnumData.Success;
  if Result then
    Name := EnumData.GeoName;
end;

And a possible usage:

var
  S: string;
begin
  if TryGetCountryNameByISO2('DE', S) then
    ShowMessage(S);
end;
like image 140
TLama Avatar answered Nov 15 '22 00:11

TLama


You can iterate Languages (from Sysutils) and check the Ext property. The corresponding Name property will give you the localized language name.

  for I := 0 to Languages.Count - 1 do begin
    Writeln(Languages.Ext[I], '=', Languages.Name[I]);
  end;
like image 22
Uwe Raabe Avatar answered Nov 14 '22 22:11

Uwe Raabe