mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-31 22:40:24 +02:00
ide, i18n, fixed clean and update translated PO files
git-svn-id: trunk@15464 -
This commit is contained in:
parent
f16a90121b
commit
dcdf28499d
@ -33,203 +33,56 @@ function AddFiles2Po(Files: TStrings; const POFilename: string): TModalResult;
|
||||
|
||||
implementation
|
||||
|
||||
function StrToPoStr(const s:string):string;
|
||||
uses Translations;
|
||||
|
||||
function FindAllTranslatedPoFiles(const Filename: string): TStringList;
|
||||
var
|
||||
SrcPos, DestPos: Integer;
|
||||
NewLength: Integer;
|
||||
Path: String;
|
||||
Name: String;
|
||||
NameOnly: String;
|
||||
Ext: String;
|
||||
FileInfo: TSearchRec;
|
||||
CurExt: String;
|
||||
begin
|
||||
NewLength:=length(s);
|
||||
for SrcPos:=1 to length(s) do
|
||||
if s[SrcPos] in ['"','\'] then inc(NewLength);
|
||||
if NewLength=length(s) then begin
|
||||
Result:=s;
|
||||
end else begin
|
||||
SetLength(Result,NewLength);
|
||||
DestPos:=1;
|
||||
for SrcPos:=1 to length(s) do begin
|
||||
case s[SrcPos] of
|
||||
'"','\':
|
||||
begin
|
||||
Result[DestPos]:='\';
|
||||
inc(DestPos);
|
||||
Result[DestPos]:=s[SrcPos];
|
||||
inc(DestPos);
|
||||
end;
|
||||
else
|
||||
Result[DestPos]:=s[SrcPos];
|
||||
inc(DestPos);
|
||||
end;
|
||||
end;
|
||||
Result:=TStringList.Create;
|
||||
Path:=ExtractFilePath(Filename);
|
||||
Name:=ExtractFilename(Filename);
|
||||
Ext:=ExtractFileExt(Filename);
|
||||
NameOnly:=LeftStr(Name,length(Name)-length(Ext));
|
||||
if SysUtils.FindFirst(Path+GetAllFilesMask,faAnyFile,FileInfo)=0 then begin
|
||||
repeat
|
||||
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='')
|
||||
or (CompareFilenames(FileInfo.Name,Name)=0) then continue;
|
||||
CurExt:=ExtractFileExt(FileInfo.Name);
|
||||
if (CompareFilenames(CurExt,'.po')<>0)
|
||||
or (CompareFilenames(LeftStr(FileInfo.Name,length(NameOnly)),NameOnly)<>0)
|
||||
then
|
||||
continue;
|
||||
Result.Add(Path+FileInfo.Name);
|
||||
until SysUtils.FindNext(FileInfo)<>0;
|
||||
end;
|
||||
SysUtils.FindClose(FileInfo);
|
||||
end;
|
||||
|
||||
function AddFiles2Po(Files: TStrings; const POFilename: string): TModalResult;
|
||||
type
|
||||
TFileType = (ftLrt, ftRst);
|
||||
var
|
||||
POValuesHash: TStringHashList;
|
||||
POFileChanged: boolean;
|
||||
POLines: TStrings;
|
||||
Identifier: string;
|
||||
List: TStringList;
|
||||
|
||||
procedure AddListPoHashEntry;
|
||||
var
|
||||
i: Integer;
|
||||
Str: String;
|
||||
begin
|
||||
|
||||
if List.Count=1 then
|
||||
Str := List[0]
|
||||
else
|
||||
Str := List.Text;
|
||||
|
||||
if POValuesHash.Find(Str) = -1 then begin
|
||||
|
||||
POFileChanged := true;
|
||||
POLines.Add('#: '+Identifier);
|
||||
|
||||
if List.Count=1 then
|
||||
POLines.Add('msgid "'+Str+'"')
|
||||
else begin
|
||||
POLines.Add('msgid ""');
|
||||
for i:= 0 to List.Count-1 do
|
||||
POLines.Add('"'+List[i]+'\n"');
|
||||
end;
|
||||
|
||||
POLines.Add('msgstr ""');
|
||||
POLines.Add('');
|
||||
POValuesHash.Add(Str);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure AddFile2PoAux(InputLines: TStringList; FileType: TFileType);
|
||||
var
|
||||
i,j,n: integer;
|
||||
p: LongInt;
|
||||
Value,Line,UStr: string;
|
||||
Multi: boolean;
|
||||
begin
|
||||
//for each string in lrt/rst list check if in PO, if not add
|
||||
for i:=0 to InputLines.Count-1 do begin
|
||||
Line:=InputLines[i];
|
||||
n := Length(Line);
|
||||
if n=0 then
|
||||
continue;
|
||||
case FileType of
|
||||
ftLrt: begin
|
||||
p:=Pos('=',Line);
|
||||
List.Clear;
|
||||
List.add(StrToPoStr( copy(Line,p+1,n-p) ));//if p=0, that's OK, all the string
|
||||
Identifier:=copy(Line,1,p-1);
|
||||
AddListPoHashEntry;
|
||||
end;
|
||||
|
||||
ftRst: begin
|
||||
if (Line[1]='#') then begin
|
||||
Value := '';
|
||||
Identifier := '';
|
||||
List.Clear;
|
||||
continue;
|
||||
end;
|
||||
|
||||
if Identifier='' then begin
|
||||
p:=Pos('=',Line);
|
||||
if P=0 then
|
||||
continue;
|
||||
Identifier := copy(Line,1,p-1);
|
||||
inc(p); // points to ' after =
|
||||
end else
|
||||
p:=1; // first char in line
|
||||
|
||||
// this will assume rst file is well formed and
|
||||
// do similar to rstconv but recognize utf-8 strings
|
||||
Multi := Line[n]='+';
|
||||
while p<=n do begin
|
||||
if Line[p]='''' then begin
|
||||
inc(p);
|
||||
j:=p;
|
||||
while (p<=n)and(Line[p]<>'''') do
|
||||
inc(p);
|
||||
Value := Value + copy(Line, j, P-j);
|
||||
inc(p);
|
||||
continue;
|
||||
end else
|
||||
if Line[p] = '#' then begin
|
||||
// collect a string with special chars
|
||||
UStr:='';
|
||||
repeat
|
||||
inc(p);
|
||||
j:=p;
|
||||
while (p<=n)and(Line[p] in ['0'..'9']) do
|
||||
inc(p);
|
||||
UStr := UStr + Chr(StrToInt(copy(Line, j, p-j)));
|
||||
until (Line[p]<>'#') or (p>=n);
|
||||
// transfer valid UTF-8 segments to result string
|
||||
// and re-encode back the rest
|
||||
while Ustr<>'' do begin
|
||||
j := UTF8CharacterLength(pchar(Ustr));
|
||||
if (j=1) and (Ustr[1] in [#0..#9,#11,#12,#14..#31,#128..#255]) then
|
||||
Value := Value + '#'+IntToStr(ord(Ustr[1]))
|
||||
else
|
||||
Value := Value + copy(Ustr, 1, j);
|
||||
Delete(UStr, 1, j);
|
||||
end;
|
||||
end else
|
||||
if Line[p]='+' then
|
||||
break;
|
||||
end;
|
||||
if not Multi then begin
|
||||
List.Text := StrToPoStr(Value);
|
||||
if List.Count>0 then
|
||||
AddListPoHashEntry;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
var
|
||||
InputLines: TStringList;
|
||||
i: Integer;
|
||||
s: String;
|
||||
Filename: string;
|
||||
BasePoFile, POFile: TPoFile;
|
||||
i: Integer;
|
||||
begin
|
||||
if (Files=nil) or (Files.Count=0) then exit(mrOk);
|
||||
|
||||
POFileChanged := false;
|
||||
POLines:=TStringList.Create;
|
||||
InputLines:=TStringList.Create;
|
||||
POValuesHash := TStringHashList.Create(true);
|
||||
List := TStringList.Create;
|
||||
|
||||
InputLines := TStringList.Create;
|
||||
try
|
||||
// Read base po items
|
||||
if FileExists(POFilename) then
|
||||
BasePOFile := TPOFile.Create(POFilename, true)
|
||||
else
|
||||
BasePOFile := TPOFile.Create;
|
||||
BasePOFile.Tag:=1;
|
||||
|
||||
//load old po file into a StringList and HashList
|
||||
POLines.Clear;
|
||||
if FileExists(POFilename) then begin
|
||||
Result:=LoadStringListFromFile(POFilename, 'PO File', POLines);
|
||||
if Result <> mrOK then Exit;
|
||||
|
||||
for i := 0 to POLines.Count-1 do begin
|
||||
s:=POLines[i];
|
||||
if LeftStr(s, 7) = 'msgid "' then begin
|
||||
s := copy(s, 8,length(s)-8);
|
||||
POValuesHash.Add(s);
|
||||
end;
|
||||
end;
|
||||
end else begin
|
||||
// To allow poedit to recognize the resulting
|
||||
// PO file as valid UTF-8 file, it needs a
|
||||
// minimal header (found by trial and error)
|
||||
POLines.Add('msgid ""');
|
||||
POLines.Add('msgstr ""');
|
||||
POLines.Add('"Content-Type: text/plain; charset=UTF-8\n"');
|
||||
POLines.Add('');
|
||||
end;
|
||||
|
||||
//merge changes of every input file
|
||||
// At the moment it only adds new strings and replaces values,
|
||||
// but does not delete unused -> ToDo
|
||||
// Update po file with lrt or/and rst files
|
||||
for i:=0 to Files.Count-1 do begin
|
||||
Filename:=Files[i];
|
||||
if (CompareFileExt(Filename,'.lrt')=0)
|
||||
@ -240,22 +93,30 @@ begin
|
||||
InputLines);
|
||||
if Result <> mrOK then Exit;
|
||||
if CompareFileExt(Filename,'.lrt')=0 then
|
||||
AddFile2PoAux(InputLines, ftLrt)
|
||||
BasePOFile.UpdateStrings(InputLines, stLrt)
|
||||
else
|
||||
AddFile2PoAux(InputLines, ftRst);
|
||||
end
|
||||
BasePOFile.UpdateStrings(InputLines, stRst);
|
||||
end;
|
||||
end;
|
||||
|
||||
//if PO file changed save it
|
||||
if POFileChanged then
|
||||
Result:=SaveStringListToFile(POFilename, 'PO File', POLines)
|
||||
else
|
||||
Result:=mrOk;
|
||||
finally
|
||||
List.Free;
|
||||
POLines.Free;
|
||||
BasePOFile.SaveToFile(POFilename);
|
||||
|
||||
// Update translated PO files
|
||||
InputLines.Free;
|
||||
POValuesHash.Free;
|
||||
InputLines := FindAllTranslatedPoFiles(POFilename);
|
||||
for i:=0 to InputLines.Count-1 do begin
|
||||
POFile := TPOFile.Create(InputLines[i], true);
|
||||
try
|
||||
POFile.Tag:=1;
|
||||
POFile.UpdateTranslation(BasePOFile);
|
||||
POFile.SaveToFile(InputLines[i]);
|
||||
finally
|
||||
POFile.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
finally
|
||||
InputLines.Free;
|
||||
BasePOFile.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
|
@ -55,11 +55,16 @@ uses
|
||||
Classes, SysUtils, LCLProc, FileUtil, StringHashList
|
||||
{$IFDEF UNIX}{$IFNDEF DisableCWString}, cwstring{$ENDIF}{$ENDIF};
|
||||
|
||||
type
|
||||
TStringsType = (stLrt, stRst);
|
||||
|
||||
type
|
||||
{ TPOFileItem }
|
||||
|
||||
TPOFileItem = class
|
||||
public
|
||||
Tag: Integer;
|
||||
Comments: string;
|
||||
Identifier: string;
|
||||
Original: string;
|
||||
Translation: string;
|
||||
@ -72,16 +77,40 @@ type
|
||||
protected
|
||||
FItems: TFPList;// list of TPOFileItem
|
||||
FIdentifierToItem: TStringHashList;
|
||||
FIdentVarToItem: TStringHashList;
|
||||
FOriginalToItem: TStringHashList;
|
||||
FCharSet: String;
|
||||
FHeader: TPOFileItem;
|
||||
FAllEntries: boolean;
|
||||
FTag: Integer;
|
||||
FModified: boolean;
|
||||
FHelperList: TStringList;
|
||||
FModuleList: TStringList;
|
||||
procedure RemoveTaggedItems(aTag: Integer);
|
||||
procedure RemoveUntaggedModules;
|
||||
public
|
||||
constructor Create(const AFilename: String);
|
||||
constructor Create(AStream: TStream);
|
||||
constructor Create;
|
||||
constructor Create(const AFilename: String; Full:boolean=false);
|
||||
constructor Create(AStream: TStream; Full:boolean=false);
|
||||
destructor Destroy; override;
|
||||
procedure ReadPOText(const s: string);
|
||||
procedure Add(const Identifier, OriginalValue, TranslatedValue: string);
|
||||
procedure Add(const Identifier, OriginalValue, TranslatedValue, Comments: string);
|
||||
function Translate(const Identifier, OriginalValue: String): String;
|
||||
Property CharSet: String read FCharSet;
|
||||
procedure Report;
|
||||
procedure CreateHeader;
|
||||
procedure UpdateStrings(InputLines:TStrings; SType: TStringsType);
|
||||
procedure SaveToFile(const AFilename: string);
|
||||
procedure UpdateItem(const Identifier: string; Original: string);
|
||||
procedure UpdateTranslation(BasePOFile: TPOFile);
|
||||
procedure ClearModuleList;
|
||||
procedure AddToModuleList(Identifier: string);
|
||||
procedure UntagAll;
|
||||
|
||||
property Tag: integer read FTag write FTag;
|
||||
property Modified: boolean read FModified;
|
||||
property Items: TFPList read FItems;
|
||||
|
||||
end;
|
||||
|
||||
EPOFileError = class(Exception);
|
||||
@ -199,29 +228,70 @@ end;
|
||||
|
||||
{ TPOFile }
|
||||
|
||||
constructor TPOFile.Create(const AFilename: String);
|
||||
procedure TPOFile.RemoveUntaggedModules;
|
||||
var
|
||||
Module: string;
|
||||
Item: TPOFileItem;
|
||||
i, p: Integer;
|
||||
begin
|
||||
if FModuleList=nil then
|
||||
exit;
|
||||
|
||||
// remove all module references that were not tagged
|
||||
for i:=FItems.Count-1 downto 0 do begin
|
||||
Item := TPOFileItem(FItems[i]);
|
||||
p := pos('.',Item.Identifier);
|
||||
if P=0 then
|
||||
continue; // module not found (?)
|
||||
|
||||
Module :=LeftStr(Item.Identifier, p-1);
|
||||
if (FModuleList.IndexOf(Module)<0) then
|
||||
continue; // module was not modified this time
|
||||
|
||||
if Item.Tag=FTag then
|
||||
continue; // PO item was updated
|
||||
|
||||
// this item is not more in updated modules, delete it
|
||||
FIdentifierToItem.Remove(Item.Identifier);
|
||||
//FOriginalToItem.Remove(Item.Original); // isn't this tricky?
|
||||
FItems.Delete(i);
|
||||
Item.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TPOFile.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
FAllEntries:=true;
|
||||
FItems:=TFPList.Create;
|
||||
FIdentifierToItem:=TStringHashList.Create(false);
|
||||
FIdentVarToItem:=TStringHashList.Create(false);
|
||||
FOriginalToItem:=TStringHashList.Create(true);
|
||||
end;
|
||||
|
||||
constructor TPOFile.Create(const AFilename: String; Full:boolean=False);
|
||||
var
|
||||
f: TStream;
|
||||
begin
|
||||
f := TFileStream.Create(AFilename, fmOpenRead);
|
||||
try
|
||||
Self.Create(f);
|
||||
Self.Create(f, Full);
|
||||
if FHeader=nil then
|
||||
CreateHeader;
|
||||
finally
|
||||
f.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
constructor TPOFile.Create(AStream: TStream);
|
||||
constructor TPOFile.Create(AStream: TStream; Full:boolean=false);
|
||||
var
|
||||
Size: Integer;
|
||||
s: string;
|
||||
begin
|
||||
inherited Create;
|
||||
|
||||
FItems:=TFPList.Create;
|
||||
FIdentifierToItem:=TStringHashList.Create(false);
|
||||
FOriginalToItem:=TStringHashList.Create(true);
|
||||
|
||||
Self.Create;
|
||||
|
||||
FAllEntries := Full;
|
||||
|
||||
Size:=AStream.Size-AStream.Position;
|
||||
if Size<=0 then exit;
|
||||
SetLength(s,Size);
|
||||
@ -233,9 +303,16 @@ destructor TPOFile.Destroy;
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if FModuleList<>nil then
|
||||
FModuleList.Free;
|
||||
if FHelperList<>nil then
|
||||
FHelperList.Free;
|
||||
if FHeader<>nil then
|
||||
FHeader.Free;
|
||||
for i:=0 to FItems.Count-1 do
|
||||
TObject(FItems[i]).Free;
|
||||
FItems.Free;
|
||||
FIdentVarToItem.Free;
|
||||
FIdentifierToItem.Free;
|
||||
FOriginalToItem.Free;
|
||||
inherited Destroy;
|
||||
@ -264,17 +341,27 @@ var
|
||||
Identifier: String;
|
||||
MsgID: String;
|
||||
Line: String;
|
||||
Comments: String;
|
||||
TextEnd: PChar;
|
||||
i: Integer;
|
||||
|
||||
procedure AddEntry;
|
||||
begin
|
||||
if Identifier<>'' then begin
|
||||
Add(Identifier,MsgID,Line);
|
||||
Add(Identifier,MsgID,Line,Comments);
|
||||
MsgId := '';
|
||||
Line := '';
|
||||
Identifier := '';
|
||||
end;
|
||||
Comments := '';
|
||||
end else
|
||||
if (Line<>'') and (FHeader=nil) then begin
|
||||
FHeader := TPOFileItem.Create('',MsgID,Line);
|
||||
FHeader.Comments:=Comments;
|
||||
MsgId := '';
|
||||
Line := '';
|
||||
Identifier := '';
|
||||
Comments := '';
|
||||
end
|
||||
end;
|
||||
|
||||
begin
|
||||
@ -284,15 +371,15 @@ begin
|
||||
LineStart:=p;
|
||||
TextEnd:=p+l;
|
||||
Identifier:='';
|
||||
Comments:='';
|
||||
Line:='';
|
||||
while LineStart<TextEnd do begin
|
||||
LineEnd:=LineStart;
|
||||
while (not (LineEnd^ in [#0,#10,#13])) do inc(LineEnd);
|
||||
LineLen:=LineEnd-LineStart;
|
||||
if LineLen>0 then begin
|
||||
if CompareMem(LineStart,sCommentIdentifier,3) then begin
|
||||
|
||||
AddEntry; // add peding entry (if exists)
|
||||
|
||||
AddEntry;
|
||||
Identifier:=copy(s,LineStart-p+4,LineLen-3);
|
||||
// the RTL creates identifier paths with point instead of colons
|
||||
// fix it:
|
||||
@ -307,11 +394,14 @@ begin
|
||||
MsgId := Line;
|
||||
// start collecting MsgStr lines
|
||||
Line:=UTF8CStringToUTF8String(LineStart+8,LineLen-9);
|
||||
end else if CompareMem(LineStart,sCharSetIdentifier,35) then begin
|
||||
FCharSet:=copy(LineStart, 35,LineLen-37);
|
||||
end else if LineStart^='"' then begin
|
||||
if Identifier<>'' then
|
||||
Line := Line + UTF8CStringToUTF8String(LineStart+1,LineLen-2);
|
||||
if CompareMem(LineStart,sCharSetIdentifier,35) then
|
||||
FCharSet:=copy(LineStart, 35,LineLen-37);
|
||||
Line := Line + UTF8CStringToUTF8String(LineStart+1,LineLen-2);
|
||||
end else if LineStart^='#' then begin
|
||||
if Comments<>'' then
|
||||
Comments := Comments + LineEnding;
|
||||
Comments := Comments + Copy(LineStart, 1, LineLen);
|
||||
end else
|
||||
AddEntry;
|
||||
end;
|
||||
@ -321,16 +411,24 @@ begin
|
||||
AddEntry;
|
||||
end;
|
||||
|
||||
procedure TPOFile.Add(const Identifier, OriginalValue, TranslatedValue: string
|
||||
);
|
||||
procedure TPOFile.Add(const Identifier, OriginalValue, TranslatedValue,
|
||||
Comments: string);
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
p: Integer;
|
||||
begin
|
||||
if (TranslatedValue='') then exit;
|
||||
if (not FAllEntries) and (TranslatedValue='') then exit;
|
||||
//debugln('TPOFile.Add Identifier="',Identifier,'" OriginalValue="',OriginalValue,'" TranslatedValue="',TranslatedValue,'"');
|
||||
Item:=TPOFileItem.Create(Identifier,OriginalValue,TranslatedValue);
|
||||
Item.Comments:=Comments;
|
||||
Item.Tag:=FTag;
|
||||
FItems.Add(Item);
|
||||
|
||||
FIdentifierToItem.Add(Identifier,Item);
|
||||
P := Pos('.', Identifier);
|
||||
if P>0 then
|
||||
FIdentVarToItem.Add(copy(Identifier, P+1, Length(IDentifier)), Item);
|
||||
|
||||
//if FIdentifierToItem.Data[UpperCase(Identifier)]=nil then raise Exception.Create('');
|
||||
FOriginalToItem.Add(OriginalValue,Item);
|
||||
//if FOriginalToItem.Data[OriginalValue]=nil then raise Exception.Create('');
|
||||
@ -350,6 +448,357 @@ begin
|
||||
Result:=OriginalValue;
|
||||
end;
|
||||
|
||||
procedure TPOFile.Report;
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
i: Integer;
|
||||
begin
|
||||
DebugLn('Header:');
|
||||
DebugLn('---------------------------------------------');
|
||||
|
||||
if FHeader=nil then
|
||||
DebugLn('No header found in po file')
|
||||
else begin
|
||||
DebugLn('Comments=',FHeader.Comments);
|
||||
DebugLn('Identifier=',FHeader.Identifier);
|
||||
DebugLn('msgid=',FHeader.Original);
|
||||
DebugLn('msgstr=', FHeader.Translation);
|
||||
end;
|
||||
DebugLn;
|
||||
|
||||
DebugLn('Entries:');
|
||||
DebugLn('---------------------------------------------');
|
||||
for i:=0 to FItems.Count-1 do begin
|
||||
DebugLn('#',dbgs(i),': ');
|
||||
Item := TPOFileItem(FItems[i]);
|
||||
DebugLn('Comments=',Item.Comments);
|
||||
DebugLn('Identifier=',Item.Identifier);
|
||||
DebugLn('msgid=',Item.Original);
|
||||
DebugLn('msgstr=', Item.Translation);
|
||||
DebugLn;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
procedure TPOFile.CreateHeader;
|
||||
begin
|
||||
if FHeader=nil then
|
||||
FHeader := TPOFileItem.Create('','','');
|
||||
FHeader.Translation:='Content-Type: text/plain; charset=UTF-8\n';
|
||||
FHeader.Comments:='';
|
||||
end;
|
||||
|
||||
function StrToPoStr(const s:string):string;
|
||||
var
|
||||
SrcPos, DestPos: Integer;
|
||||
NewLength: Integer;
|
||||
begin
|
||||
NewLength:=length(s);
|
||||
for SrcPos:=1 to length(s) do
|
||||
if s[SrcPos] in ['"','\'] then inc(NewLength);
|
||||
if NewLength=length(s) then begin
|
||||
Result:=s;
|
||||
end else begin
|
||||
SetLength(Result,NewLength);
|
||||
DestPos:=1;
|
||||
for SrcPos:=1 to length(s) do begin
|
||||
case s[SrcPos] of
|
||||
'"','\':
|
||||
begin
|
||||
Result[DestPos]:='\';
|
||||
inc(DestPos);
|
||||
Result[DestPos]:=s[SrcPos];
|
||||
inc(DestPos);
|
||||
end;
|
||||
else
|
||||
Result[DestPos]:=s[SrcPos];
|
||||
inc(DestPos);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TPOFile.UpdateStrings(InputLines: TStrings; SType: TStringsType);
|
||||
var
|
||||
i,j,n: integer;
|
||||
p: LongInt;
|
||||
Identifier, Value,Line,UStr: string;
|
||||
Multi: boolean;
|
||||
|
||||
begin
|
||||
ClearModuleList;
|
||||
UntagAll;
|
||||
// for each string in lrt/rst list check if it's already
|
||||
// in PO if not add it
|
||||
for i:=0 to InputLines.Count-1 do begin
|
||||
Line:=InputLines[i];
|
||||
n := Length(Line);
|
||||
if n=0 then
|
||||
continue;
|
||||
|
||||
if SType=stLrt then begin
|
||||
p:=Pos('=',Line);
|
||||
Value := StrToPoStr( copy(Line,p+1,n-p) );//if p=0, that's OK, all the string
|
||||
Identifier:=copy(Line,1,p-1);
|
||||
UpdateItem(Identifier, Value);
|
||||
continue;
|
||||
end;
|
||||
|
||||
if (Line[1]='#') then begin
|
||||
Value := '';
|
||||
Identifier := '';
|
||||
continue;
|
||||
end;
|
||||
|
||||
if Identifier='' then begin
|
||||
p:=Pos('=',Line);
|
||||
if P=0 then
|
||||
continue;
|
||||
Identifier := copy(Line,1,p-1);
|
||||
inc(p); // points to ' after =
|
||||
end else
|
||||
p:=1; // first char in line
|
||||
|
||||
// this will assume rst file is well formed and
|
||||
// do similar to rstconv but recognize utf-8 strings
|
||||
Multi := Line[n]='+';
|
||||
while p<=n do begin
|
||||
if Line[p]='''' then begin
|
||||
inc(p);
|
||||
j:=p;
|
||||
while (p<=n)and(Line[p]<>'''') do
|
||||
inc(p);
|
||||
Value := Value + copy(Line, j, P-j);
|
||||
inc(p);
|
||||
continue;
|
||||
end else
|
||||
if Line[p] = '#' then begin
|
||||
// collect a string with special chars
|
||||
UStr:='';
|
||||
repeat
|
||||
inc(p);
|
||||
j:=p;
|
||||
while (p<=n)and(Line[p] in ['0'..'9']) do
|
||||
inc(p);
|
||||
UStr := UStr + Chr(StrToInt(copy(Line, j, p-j)));
|
||||
until (Line[p]<>'#') or (p>=n);
|
||||
// transfer valid UTF-8 segments to result string
|
||||
// and re-encode back the rest
|
||||
while Ustr<>'' do begin
|
||||
j := UTF8CharacterLength(pchar(Ustr));
|
||||
if (j=1) and (Ustr[1] in [#0..#9,#11,#12,#14..#31,#128..#255]) then
|
||||
Value := Value + '#'+IntToStr(ord(Ustr[1]))
|
||||
else
|
||||
Value := Value + copy(Ustr, 1, j);
|
||||
Delete(UStr, 1, j);
|
||||
end;
|
||||
end else
|
||||
if Line[p]='+' then
|
||||
break
|
||||
else
|
||||
inc(p); // this is an unexpected string
|
||||
|
||||
end;
|
||||
if not Multi then begin
|
||||
Value := StrToPoStr(Value);
|
||||
if Value<>'' then
|
||||
UpdateItem(Identifier, Value);
|
||||
end;
|
||||
end;
|
||||
|
||||
RemoveUntaggedModules;
|
||||
end;
|
||||
|
||||
procedure TPOFile.RemoveTaggedItems(aTag: Integer);
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
i: Integer;
|
||||
begin
|
||||
// get rid of all entries that have Tag=aTag
|
||||
for i:=FItems.Count-1 downto 0 do begin
|
||||
Item := TPOFileItem(FItems[i]);
|
||||
if Item.Tag<>aTag then
|
||||
Continue;
|
||||
FIdentifierToItem.Remove(Item.Identifier);
|
||||
//FOriginalToItem.Remove(Item.Original); // isn't this tricky?
|
||||
FItems.Delete(i);
|
||||
Item.Free;
|
||||
end;
|
||||
end;
|
||||
|
||||
function ComparePOItems(Item1, Item2: Pointer): Integer;
|
||||
begin
|
||||
result := CompareText(TPOFileItem(Item1).Identifier,
|
||||
TPOFileItem(Item2).Identifier);
|
||||
end;
|
||||
|
||||
procedure TPOFile.SaveToFile(const AFilename: string);
|
||||
var
|
||||
OutLst: TStringList;
|
||||
j: Integer;
|
||||
|
||||
procedure WriteLst(const AProp, AValue: string );
|
||||
var
|
||||
i: Integer;
|
||||
begin
|
||||
if (AValue='') and (AProp='') then
|
||||
exit;
|
||||
|
||||
FHelperList.Text:=AValue;
|
||||
if FHelperList.Count=1 then begin
|
||||
if AProp='' then OutLst.Add(FHelperList[0])
|
||||
else OutLst.Add(AProp+' "'+FHelperList[0]+'"');
|
||||
end else begin
|
||||
if AProp<>'' then
|
||||
OutLst.Add(AProp+' ""');
|
||||
for i:=0 to FHelperList.Count-1 do
|
||||
if AProp='' then OutLst.Add(FHelperList[i])
|
||||
else OutLst.Add('"'+FHelperList[i]+'\n"');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure WriteItem(Item: TPOFileItem);
|
||||
begin
|
||||
WriteLst('',Item.Comments);
|
||||
if Item.Identifier<>'' then
|
||||
OutLst.Add('#: '+Item.Identifier);
|
||||
WriteLst('msgid', Item.Original);
|
||||
WriteLst('msgstr', Item.Translation);
|
||||
OutLst.Add('');
|
||||
end;
|
||||
|
||||
begin
|
||||
if FHeader=nil then
|
||||
CreateHeader;
|
||||
|
||||
if FHelperList=nil then
|
||||
FHelperList:=TStringList.Create;
|
||||
|
||||
OutLst := TStringList.Create;
|
||||
try
|
||||
// write header
|
||||
WriteItem(FHeader);
|
||||
|
||||
// Sort list of items by identifier
|
||||
FItems.Sort(@ComparePOItems);
|
||||
|
||||
for j:=0 to Fitems.Count-1 do
|
||||
WriteItem(TPOFileItem(FItems[j]));
|
||||
|
||||
OutLst.SaveToFile(AFilename);
|
||||
|
||||
finally
|
||||
OutLst.Free;
|
||||
end;
|
||||
|
||||
end;
|
||||
|
||||
procedure TPOFile.UpdateItem(const Identifier: string; Original: string);
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
p: Integer;
|
||||
begin
|
||||
if FHelperList=nil then
|
||||
FHelperList := TStringList.Create;
|
||||
|
||||
FHelperList.Text:=Original;
|
||||
Original := FHelperList.Text; // this should unify line endings
|
||||
|
||||
// try to find PO entry by identifier
|
||||
Item:=TPOFileItem(FIdentifierToItem.Data[Identifier]);
|
||||
if Item<>nil then begin
|
||||
// found, update item value
|
||||
AddToModuleList(IDentifier);
|
||||
FModified := FModified or (Item.Original<>Original);
|
||||
Item.Original:=Original;
|
||||
Item.Tag:=FTag;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// try to find PO entry using only variable part identifier
|
||||
p := pos('.', Identifier);
|
||||
if p>0 then begin
|
||||
Item := TPOFileItem(FIdentVarToItem.Data[RightStr(Identifier, Length(Identifier)-P)]);
|
||||
if Item<>nil then begin
|
||||
// found!, this means module name has changed
|
||||
AddToModuleList(Item.Identifier);
|
||||
// update identifier list
|
||||
FIdentifierToItem.Remove(Item.Identifier);
|
||||
FIdentifierToItem.Add(Identifier, Item);
|
||||
// update item
|
||||
FModified := true;
|
||||
Item.Identifier:=Identifier;
|
||||
Item.Original:=Original;
|
||||
Item.Tag := FTag;
|
||||
exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// try to find po entry based only on it's value
|
||||
Item := TPOFileItem(FOriginalToItem.Data[Original]);
|
||||
if Item<>nil then begin
|
||||
// found!, this would mean that both module name and variable part
|
||||
// have been changed (NOTE: this might also give false positives)
|
||||
AddToModuleList(Item.Identifier);
|
||||
|
||||
// update identifier list
|
||||
FIdentifierToItem.Remove(Item.Identifier);
|
||||
FIdentifierToItem.Add(Identifier, Item);
|
||||
FModified := true;
|
||||
Item.Identifier:=Identifier;
|
||||
Item.Tag := FTag;
|
||||
exit;
|
||||
end;
|
||||
|
||||
// this appear to be a new item
|
||||
FModified := true;
|
||||
Add(Identifier, Original, '', '');
|
||||
end;
|
||||
|
||||
procedure TPOFile.UpdateTranslation(BasePOFile: TPOFile);
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
i: Integer;
|
||||
begin
|
||||
UntagAll;
|
||||
ClearModuleList;
|
||||
for i:=0 to BasePOFile.Items.Count-1 do begin
|
||||
Item := TPOFileItem(BasePOFile.Items[i]);
|
||||
UpdateItem(Item.Identifier, Item.Original);
|
||||
end;
|
||||
RemoveTaggedItems(0); // get rid of any item not existing in BasePOFile
|
||||
end;
|
||||
|
||||
procedure TPOFile.ClearModuleList;
|
||||
begin
|
||||
if FModuleList<>nil then
|
||||
FModuleList.Clear;
|
||||
end;
|
||||
|
||||
procedure TPOFile.AddToModuleList(Identifier: string);
|
||||
var
|
||||
p: Integer;
|
||||
begin
|
||||
if FModuleList=nil then begin
|
||||
FModuleList := TStringList.Create;
|
||||
FModuleList.Duplicates:=dupIgnore;
|
||||
end;
|
||||
p := pos('.', Identifier);
|
||||
if p>0 then
|
||||
FModuleList.Add(LeftStr(Identifier, P-1));
|
||||
end;
|
||||
|
||||
procedure TPOFile.UntagAll;
|
||||
var
|
||||
Item: TPOFileItem;
|
||||
i: Integer;
|
||||
begin
|
||||
for i:=0 to Items.Count-1 do begin
|
||||
Item := TPOFileItem(Items[i]);
|
||||
Item.Tag:=0;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TPOFileItem }
|
||||
|
||||
constructor TPOFileItem.Create(const TheIdentifier, TheOriginal,
|
||||
|
Loading…
Reference in New Issue
Block a user