123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- {
- This file is part of the Free Pascal run time library.
- Copyright (c) 1999-2001 by the Free Pascal development team
- Gettext interface to resourcestrings.
- See the file COPYING.FPC, included in this distribution,
- for details about the copyright.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- **********************************************************************}
- {$MODE objfpc}
- {$H+}
- unit gettext;
- interface
- uses SysUtils, Classes;
- const
- MOFileHeaderMagic = $950412de;
- type
- TMOFileHeader = packed record
- magic: LongWord; // MOFileHeaderMagic
- revision: LongWord; // 0
- nstrings: LongWord; // Number of string pairs
- OrigTabOffset: LongWord; // Offset of original string offset table
- TransTabOffset: LongWord; // Offset of translated string offset table
- HashTabSize: LongWord; // Size of hashing table
- HashTabOffset: LongWord; // Offset of first hashing table entry
- end;
- TMOStringInfo = packed record
- length: LongWord;
- offset: LongWord;
- end;
- TMOStringTable = array[0..(1 shl 30) div SizeOf(TMOStringInfo)] of TMOStringInfo;
- PMOStringTable = ^TMOStringTable;
- TLongWordArray = array[0..(1 shl 30) div SizeOf(LongWord)] of LongWord;
- PLongWordArray = ^TLongWordArray;
- TPCharArray = array[0..(1 shl 30) div SizeOf(PChar)] of PChar;
- PPCharArray = ^TPCharArray;
- TMOFile = class
- protected
- StringCount, HashTableSize: LongWord;
- HashTable: PLongWordArray;
- OrigTable, TranslTable: PMOStringTable;
- OrigStrings, TranslStrings: PPCharArray;
- public
- constructor Create(const AFilename: String);
- constructor Create(AStream: TStream);
- destructor Destroy; override;
- function Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
- function Translate(AOrig: String; AHash: LongWord): String;
- function Translate(AOrig: String): String;
- end;
- EMOFileError = class(Exception);
- procedure GetLanguageIDs(var Lang, FallbackLang: string);
- procedure TranslateResourceStrings(AFile: TMOFile);
- procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
- procedure TranslateResourceStrings(const AFilename: String);
- procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
- implementation
- uses {$ifdef win32} windows, {$endif}dos;
- constructor TMOFile.Create(AStream: TStream);
- var
- header: TMOFileHeader;
- i: Integer;
- begin
- inherited Create;
- AStream.Read(header, Sizeof(header));
- if header.magic <> MOFileHeaderMagic then
- raise EMOFileError.Create('Invalid magic - not a MO file?');
- GetMem(OrigTable, header.nstrings * SizeOf(TMOStringInfo));
- GetMem(TranslTable, header.nstrings * SizeOf(TMOStringInfo));
- GetMem(OrigStrings, header.nstrings * SizeOf(PChar));
- GetMem(TranslStrings, header.nstrings * SizeOf(PChar));
- AStream.Position := header.OrigTabOffset;
- AStream.Read(OrigTable^, header.nstrings * SizeOf(TMOStringInfo));
- AStream.Position := header.TransTabOffset;
- AStream.Read(TranslTable^, header.nstrings * SizeOf(TMOStringInfo));
- StringCount := header.nstrings;
- // Read strings
- for i := 0 to StringCount - 1 do
- begin
- AStream.Position := OrigTable^[i].offset;
- { SetLength(s, OrigTable^[i].length);
- AStream.Read(s[1], OrigTable^[i].length);
- OrigStrings^[i] := StrNew(PChar(s));}
- GetMem(OrigStrings^[i], OrigTable^[i].length + 1);
- AStream.Read(OrigStrings^[i]^, OrigTable^[i].length);
- OrigStrings^[i][OrigTable^[i].length] := #0;
- end;
- for i := 0 to StringCount - 1 do
- begin
- AStream.Position := TranslTable^[i].offset;
- { SetLength(s, TranslTable^[i].length);
- AStream.Read(s[1], TranslTable^[i].length);
- TranslStrings^[i] := StrNew(PChar(s));}
- GetMem(TranslStrings^[i], TranslTable^[i].length+1);
- AStream.Read(TranslStrings^[i]^, TranslTable^[i].length);
- TranslStrings^[i][TranslTable^[i].length] := #0;
- end;
- // Read hashing table
- HashTableSize := header.HashTabSize;
- GetMem(HashTable, 4 * HashTableSize);
- AStream.Position := header.HashTabOffset;
- AStream.Read(HashTable^, 4 * HashTableSize);
- end;
- constructor TMOFile.Create(const AFilename: String);
- var
- f: TStream;
- begin
- f := TFileStream.Create(AFilename, fmOpenRead);
- try
- Self.Create(f);
- finally
- f.Free;
- end;
- end;
- destructor TMOFile.Destroy;
- var
- i: Integer;
- begin
- for i := 0 to StringCount - 1 do
- begin
- Dispose(OrigStrings^[i]);
- Dispose(TranslStrings^[i]);
- end;
- Dispose(OrigTable);
- Dispose(TranslTable);
- Dispose(OrigStrings);
- Dispose(TranslStrings);
- Dispose(HashTable);
- inherited Destroy;
- end;
- function TMOFile.Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
- var
- idx, incr, nstr: LongWord;
- begin
- idx := AHash mod HashTableSize;
- incr := 1 + (AHash mod (HashTableSize - 2));
- while True do
- begin
- nstr := HashTable^[idx];
- if nstr = 0 then
- begin
- Result := '';
- exit;
- end;
- if (OrigTable^[nstr - 1].length = LongWord(ALen)) and
- (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then
- begin
- Result := TranslStrings^[nstr - 1];
- exit;
- end;
- if idx >= HashTableSize - incr then
- Dec(idx, HashTableSize - incr)
- else
- Inc(idx, incr);
- end;
- end;
- function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
- begin
- Result := Translate(PChar(AOrig), Length(AOrig), AHash);
- end;
- function TMOFile.Translate(AOrig: String): String;
- begin
- Result := Translate(AOrig, Hash(AOrig));
- end;
- // -------------------------------------------------------
- // Resourcestring translation procedures
- // -------------------------------------------------------
- function Translate (Name,Value : AnsiString; Hash : Longint; arg:pointer) : AnsiString;
- begin
- Result:=TMOFile(arg).Translate(Value,Hash);
- end;
- procedure TranslateResourceStrings(AFile: TMOFile);
- begin
- SetResourceStrings(@Translate,AFile);
- end;
- procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
- begin
- SetUnitResourceStrings(AUnitName,@Translate,AFile);
- end;
- {$ifdef win32}
- procedure GetLanguageIDs(var Lang, FallbackLang: string);
- var
- Buffer: array[1..4] of char;
- Country: string;
- UserLCID: LCID;
- begin
- //defaults
- Lang := '';
- FallbackLang:='';
- UserLCID := GetUserDefaultLCID;
- if GetLocaleInfo(UserLCID, LOCALE_SABBREVLANGNAME, @Buffer[1], 4)<>0 then
- FallbackLang := lowercase(copy(Buffer,1,2));
- if GetLocaleInfo(UserLCID, LOCALE_SABBREVCTRYNAME, @Buffer[1], 4)<>0 then begin
- Country := copy(Buffer,1,2);
- // some 2 letter codes are not the first two letters of the 3 letter code
- // there are probably more, but first let us see if there are translations
- if (Buffer='PRT') then Country:='PT';
- Lang := FallbackLang+'_'+Country;
- end;
- end;
- {$else}
- procedure GetLanguageIDs(var Lang, FallbackLang: string);
- begin
- lang := GetEnv('LC_ALL');
- if Length(lang) = 0 then
- begin
- lang := GetEnv('LC_MESSAGES');
- if Length(lang) = 0 then
- begin
- lang := GetEnv('LANG');
- if Length(lang) = 0 then
- exit; // no language defined via environment variables
- end;
- end;
- FallbackLang := Copy(lang, 1, 2);
- end;
- {$endif}
- procedure TranslateResourceStrings(const AFilename: String);
- var
- mo: TMOFile;
- lang, FallbackLang: String;
- begin
- GetLanguageIDs(Lang, FallbackLang);
- try
- mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
- try
- TranslateResourceStrings(mo);
- finally
- mo.Free;
- end;
- except
- on e: Exception do;
- end;
- lang := Copy(lang, 1, 5);
- try
- mo := TMOFile.Create(Format(AFilename, [lang]));
- try
- TranslateResourceStrings(mo);
- finally
- mo.Free;
- end;
- except
- on e: Exception do;
- end;
- end;
- procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
- var
- mo: TMOFile;
- lang, FallbackLang: String;
- begin
- GetLanguageIDs(Lang, FallbackLang);
- try
- mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
- try
- TranslateUnitResourceStrings(AUnitName,mo);
- finally
- mo.Free;
- end;
- except
- on e: Exception do;
- end;
- lang := Copy(lang, 1, 5);
- try
- mo := TMOFile.Create(Format(AFilename, [lang]));
- try
- TranslateUnitResourceStrings(AUnitName,mo);
- finally
- mo.Free;
- end;
- except
- on e: Exception do;
- end;
- end;
- end.
|