📄 gnugettext.pas
字号:
isopen := False;
end;
moexists := True;
finally
moCS.EndWrite;
end;
end;
constructor TDomain.Create;
begin
moCS := TMultiReadExclusiveWriteSynchronizer.Create;
isOpen := False;
moexists := True;
end;
destructor TDomain.Destroy;
begin
CloseMoFile;
FreeAndNil(moCS);
inherited;
end;
function TDomain.gettextbyid(id: cardinal): ansistring;
var
offset, size: cardinal;
begin
offset:=CardinalInMem (momemory,O+8*id+4);
size:=CardinalInMem (momemory,O+8*id);
SetString (Result,momemory+offset,size);
end;
function TDomain.getdsttextbyid(id: cardinal): ansistring;
var
offset, size: cardinal;
begin
offset:=CardinalInMem (momemory,T+8*id+4);
size:=CardinalInMem (momemory,T+8*id);
SetString (Result,momemory+offset,size);
end;
function TDomain.gettext(msgid: ansistring): ansistring;
var
i, nn, step: cardinal;
s: string;
begin
if (not isopen) and moexists then
OpenMoFile;
if not isopen then begin
Result := msgid;
exit;
end;
// Calculate start conditions for a binary search
nn := N;
i := 1;
while nn <> 0 do begin
nn := nn shr 1;
i := i shl 1;
end;
i := i shr 1;
step := i shr 1;
// Do binary search
while true do begin
// Get string for index i
s := gettextbyid(i-1);
if msgid = s then begin
// Found the msgid
Result := getdsttextbyid(i-1);
break;
end;
if step = 0 then begin
// Not found
Result := msgid;
break;
end;
if msgid < s then begin
if i < 1+step then
i := 1
else
i := i - step;
step := step shr 1;
end else
if msgid > s then begin
i := i + step;
if i > N then
i := N;
step := step shr 1;
end;
end;
end;
{$ifdef mswindows}
function GetLastWinError:string;
var
errcode:Cardinal;
begin
SetLength (Result,2000);
errcode:=GetLastError();
Windows.FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,nil,errcode,0,PChar(Result),2000,nil);
Result:=StrPas(PChar(Result));
end;
{$endif}
procedure TDomain.OpenMoFile;
var
i: cardinal;
filename: string;
offset,size:Int64;
{$ifdef linux}
mofile:TFileStream;
{$endif}
begin
moCS.BeginWrite;
try
// Check if it is already open
if isopen then
exit;
// Check if it has been attempted to open the file before
if not moexists then
exit;
if sizeof(i) <> 4 then
raise Exception.Create('TDomain in gnugettext is written for an architecture that has 32 bit integers.');
filename := Directory + curlang + PathDelim + 'LC_MESSAGES' + PathDelim + domain + '.mo';
if (not AssemblyAnalyzer.FileExists(filename)) and (not fileexists(filename)) then
filename := Directory + copy(curlang, 1, 2) + PathDelim + 'LC_MESSAGES' + PathDelim + domain + '.mo';
if (not AssemblyAnalyzer.FileExists(filename)) and (not fileexists(filename)) then begin
moexists := False;
exit;
end;
AssemblyAnalyzer.GetFileInfo(filename,filename,offset,size);
FileOffset:=offset;
{$ifdef mswindows}
// The next two lines are necessary because otherwise MapViewOfFile fails
size:=0;
offset:=0;
// Map the mo file into memory and let the operating system decide how to cache
mo:=createfile (PChar(filename),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,0,0);
if mo=INVALID_HANDLE_VALUE then
raise Exception.Create ('Cannot open file '+filename);
momapping:=CreateFileMapping (mo, nil, PAGE_READONLY, 0, 0, nil);
if momapping=0 then
raise Exception.Create ('Cannot create memory map on file '+filename);
momemoryHandle:=MapViewOfFile (momapping,FILE_MAP_READ,offset shr 32,offset and $FFFFFFFF,size);
if momemoryHandle=nil then begin
raise Exception.Create ('Cannot map file '+filename+' into memory. Reason: '+GetLastWinError);
end;
momemory:=momemoryHandle+FileOffset;
{$endif}
{$ifdef linux}
// Read the whole file into memory
mofile:=TFileStream.Create (filename, fmOpenRead or fmShareDenyNone);
try
if size=0 then
size:=mofile.Size;
Getmem (momemoryHandle,size);
momemory:=momemoryHandle;
mofile.Seek(FileOffset,soFromBeginning);
mofile.ReadBuffer(momemory^,size);
finally
FreeAndNil (mofile);
end;
{$endif}
isOpen := True;
// Check the magic number
doswap:=False;
i:=CardinalInMem(momemory,0);
if (i <> $950412DE) and (i <> $DE120495) then
raise Exception.Create('This file is not a valid GNU gettext mo file: ' + filename);
doswap := (i = $DE120495);
CardinalInMem(momemory,4); // Read the version number, but don't use it for anything.
N:=CardinalInMem(momemory,8); // Get string count
O:=CardinalInMem(momemory,12); // Get offset of original strings
T:=CardinalInMem(momemory,16); // Get offset of translated strings
finally
moCS.EndWrite;
end;
end;
procedure TDomain.setDirectory(dir: string);
begin
vDirectory := IncludeTrailingPathDelimiter(dir);
CloseMoFile;
end;
function LoadDLLifPossible (dllname:string='gnu_gettext.dll'):boolean;
begin
{$ifdef MSWINDOWS}
if not DLLisLoaded then begin
dllmodule := LoadLibraryEx(PChar(dllname), 0, 0);
DLLisLoaded := (dllmodule <> 0);
if DLLisLoaded then begin
pgettext := tpgettext(GetProcAddress(dllmodule, 'gettext'));
pdgettext := tpdgettext(GetProcAddress(dllmodule, 'dgettext'));
ptextdomain := tptextdomain(GetProcAddress(dllmodule, 'textdomain'));
pbindtextdomain := tpbindtextdomain(GetProcAddress(dllmodule, 'bindtextdomain'));
pgettext_putenv := tpgettext_putenv(GetProcAddress(dllmodule, 'gettext_putenv'));
end;
end;
{$endif}
{$ifdef LINUX}
// On Linux, gettext is always there as part of the Libc library.
// But default is not to use it, but to use the internal implementation instead.
DLLisLoaded := False;
{$endif}
Result:=DLLisLoaded;
end;
procedure AddDomainForResourceString (domain:string);
begin
TPDomainListCS.BeginWrite;
try
TPDomainList.Add (domain);
finally
TPDomainListCS.EndWrite;
end;
end;
procedure TDomain.SetLanguageCode(langcode: string);
begin
CloseMoFile;
curlang:=langcode;
end;
function GetPluralForm2EN(Number: Integer): Integer;
begin
Number:=abs(Number);
if Number=1 then Result:=0 else Result:=1;
end;
function GetPluralForm1(Number: Integer): Integer;
begin
Result:=0;
end;
function GetPluralForm2FR(Number: Integer): Integer;
begin
Number:=abs(Number);
if (Number=1) or (Number=0) then Result:=0 else Result:=1;
end;
function GetPluralForm3LV(Number: Integer): Integer;
begin
Number:=abs(Number);
if (Number mod 10=1) and (Number mod 100<>11) then
Result:=0
else
if Number<>0 then Result:=1
else Result:=2;
end;
function GetPluralForm3GA(Number: Integer): Integer;
begin
Number:=abs(Number);
if Number=1 then Result:=0
else if Number=2 then Result:=1
else Result:=2;
end;
function GetPluralForm3LT(Number: Integer): Integer;
var
n1,n2:byte;
begin
Number:=abs(Number);
n1:=Number mod 10;
n2:=Number mod 100;
if (n1=1) and (n2<>11) then
Result:=0
else
if (n1>=2) and ((n2<10) or (n2>=20)) then Result:=1
else Result:=2;
end;
function GetPluralForm3PL(Number: Integer): Integer;
var
n1,n2:byte;
begin
Number:=abs(Number);
n1:=Number mod 10;
n2:=Number mod 100;
if n1=1 then Result:=0
else if (n1>=2) and (n1<=4) and ((n2<10) or (n2>=20)) then Result:=1
else Result:=2;
end;
function GetPluralForm3RU(Number: Integer): Integer;
var
n1,n2:byte;
begin
Number:=abs(Number);
n1:=Number mod 10;
n2:=Number mod 100;
if (n1=1) and (n2<>11) then
Result:=0
else
if (n1>=2) and (n1<=4) and ((n2<10) or (n2>=20)) then Result:=1
else Result:=2;
end;
function GetPluralForm4SL(Number: Integer): Integer;
var
n2:byte;
begin
Number:=abs(Number);
n2:=Number mod 100;
if n2=1 then Result:=0
else
if n2=2 then Result:=1
else
if (n2=3) or (n2=4) then Result:=2
else
Result:=3;
end;
{ TGnuGettextInstance }
procedure TGnuGettextInstance.bindtextdomain(const szDomain,
szDirectory: string);
var
dir:string;
begin
dir:=IncludeTrailingPathDelimiter(szDirectory);
getdomain(domainlist,szDomain,DefaultDomainDirectory,CurLang).Directory := dir;
{$ifdef LINUX}
dir:=ExcludeTrailingPathDelimiter(szDirectory);
Libc.bindtextdomain(PChar(szDomain), PChar(dir));
{$endif}
{$ifdef MSWINDOWS}
if DLLisLoaded then
pbindtextdomain(PChar(szDomain), PChar(dir));
{$endif}
end;
constructor TGnuGettextInstance.Create;
var
lang: string;
begin
curGetPluralForm:=GetPluralForm2EN;
Enabled:=True;
curmsgdomain:=DefaultTextDomain;
savefileCS := TMultiReadExclusiveWriteSynchronizer.Create;
domainlist := TStringList.Create;
TP_IgnoreList:=TStringList.Create;
TP_IgnoreList.Sorted:=True;
TP_ClassHandling:=TList.Create;
// Set some settings
DefaultDomainDirectory := IncludeTrailingPathDelimiter(extractfilepath(ExecutableFilename))+'locale';
UseLanguage(lang);
bindtextdomain(DefaultTextDomain, DefaultDomainDirectory);
textdomain(DefaultTextDomain);
{$ifdef LINUX}
bind_textdomain_codeset(DefaultTextDomain,'utf-8');
{$endif}
// Add default properties to ignore
TP_GlobalIgnoreClassProperty(TComponent,'Name');
TP_GlobalIgnoreClassProperty(TCollection,'PropName');
end;
destructor TGnuGettextInstance.Destroy;
begin
if savememory <> nil then begin
savefileCS.BeginWrite;
try
CloseFile(savefile);
finally
savefileCS.EndWrite;
end;
FreeAndNil(savememory);
end;
FreeAndNil (savefileCS);
FreeAndNil (TP_IgnoreList);
while TP_ClassHandling.Count<>0 do begin
TObject(TP_ClassHandling.Items[0]).Free;
TP_ClassHandling.Delete(0);
end;
FreeAndNil (TP_ClassHandling);
while domainlist.Count <> 0 do begin
domainlist.Objects[0].Free;
domainlist.Delete(0);
end;
FreeAndNil(domainlist);
inherited;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -