The_Fox
The_Fox

Reputation: 7062

How to convert ISO 639-1 code to language id

Is there a way to get the primary language id from an language ISO code using the Windows API? I want to use it for my GetDateFormatInt function and I am curious if there is a way without using the constants. Although the function doesn't use the default sublang for english, I'm interested in the primary language id only.

function GetDateFormatInt(const aLanguageISOCode: string): string;
const
  C_ISO_CODES: array[0..3] of string = (
    'nl', 'en', 'de', 'fr'
  );
  C_LCIDS: array[0..3] of Cardinal = (
    ((SUBLANG_DUTCH shl 10) or LANG_DUTCH) or (SORT_DEFAULT shl 16),
    ((SUBLANG_ENGLISH_UK shl 10) or LANG_ENGLISH) or (SORT_DEFAULT shl 16),
    ((SUBLANG_GERMAN shl 10) or LANG_GERMAN) or (SORT_DEFAULT shl 16),
    ((SUBLANG_FRENCH shl 10) or LANG_FRENCH) or (SORT_DEFAULT shl 16)
  );
var
  i: Integer;
  lLCID: Cardinal;
  lBuffer: array[0..512] of Char;
begin
  i := AnsiIndexText(aLanguageISOCode, C_ISO_CODES);
  if i > -1 then
    lLCID := C_LCIDS[i]
  else
    lLCID := LOCALE_USER_DEFAULT;

  i := GetDateFormat(lLCID, DATE_LONGDATE, nil, nil, lBuffer, 512);
  SetString(Result, lBuffer, i - 1);
end;

Upvotes: 1

Views: 2417

Answers (1)

Ondrej Kelle
Ondrej Kelle

Reputation: 37221

Disclaimer: I wrote the following solely based on the existence of the LOCALE_SISO639LANGNAME constant. I have no idea if it actually works or is useful in any way. I haven't tested it at all.

unit Iso639;

interface

uses
  Windows;

function Iso639ToPrimaryLangID(const S: string): LANGID;

implementation

uses
  SysUtils, Classes;

var
  Iso639Languages: TStringList = nil;

function GetLocaleDataW(ID: LCID; Flag: DWORD): WideString;
var
  Buffer: array[0..1023] of WideChar;
begin
  Buffer[0] := #0;
  SetString(Result, Buffer, GetLocaleInfoW(ID, Flag, Buffer, SizeOf(Buffer) div 2));
end;

function LangIDFromLcID(ID: LCID): LANGID;
begin
  Result := LANGID(ID);
end;

function PrimaryLangID(LangID: LANGID): LANGID;
begin
  Result := LangID and $3FF;
end;

procedure InitializeIso639Languages;
var
  I: Integer;
  ALocaleID: LCID;
  ALangID: LANGID;
  S: string;
begin
  Iso639Languages := TStringList.Create;
  try
    Iso639Languages.Sorted := True;
    for I := 0 to Languages.Count - 1 do
    begin
      ALocaleID := Languages.LocaleID[I];
      ALangID := PrimaryLangID(LangIDFromLcID(ALocaleID));
      if Iso639Languages.IndexOfObject(TObject(ALangID)) = -1 then
      begin
        S := GetLocaleDataW(ALocaleID, LOCALE_SISO639LANGNAME);
        Iso639Languages.AddObject(S, TObject(ALangID));
      end;
    end;
  except
    FreeAndNil(Iso639Languages);
    raise;
  end;
end;

function Iso639ToPrimaryLangID(const S: string): LANGID;
var
  I: Integer;
begin
  Result := 0;

  if not Assigned(Iso639Languages) then
    InitializeIso639Languages;

  I := Iso639Languages.IndexOf(S);
  if I <> -1 then
    Result := LANGID(Iso639Languages.Objects[I]);
end;

initialization

finalization
  FreeAndNil(Iso639Languages);

end.

Warning for Delphi 7 users:

TLanguages.Create has an issue with DEP and throws an Access Violation when DEP is enabled. So don't use Languages[I].LocaleID and get the LocaleID yourself:

function EnumLocalesProc(aLocaleString: PChar): Integer; stdcall;
var
  lLocaleID: LCID;
begin
  lLocaleID := StrToInt('$' + Copy(aLocaleString, 5, 4));
  Result := 1;
end;

EnumSystemLocales(@EnumLocalesProc, LCID_SUPPORTED);

Upvotes: 6

Related Questions