lazarus/tools/updatepofiles.pas
mattias d9f87ef6eb updatepofiles is now case sensitive,
replaced many places, where Application was needlessly Owner
updated po files, started Configure IDE Install Package dialog,
implemented removing double file package links

git-svn-id: trunk@6388 -
2004-12-18 10:20:22 +00:00

383 lines
9.8 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code 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. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Name:
updatepofiles - creates an lazarus resource file from files
Synopsis:
updatepofiles filename1.po [filename2.po ... filenameN.po]
Description:
updatepofiles deletes doubles in the po file and merges new strings into
all translated po files (filename1.po.xx)
}
program UpdatePoFiles;
{$mode objfpc}{$H+}
uses
Classes, SysUtils, FileUtil, OldAvLTree;
type
TMsgItem = record
Comment: string;
ID: string;
Str: string;
end;
PMsgItem = ^TMsgItem;
function CompareMsgItems(Data1, Data2: pointer): integer;
var
MsgItem1: PMsgItem;
MsgItem2: PMsgItem;
begin
MsgItem1:=PMsgItem(Data1);
MsgItem2:=PMsgItem(Data2);
Result:=AnsiCompareStr(MsgItem1^.ID,MsgItem2^.ID);
end;
procedure DisposeMsgTree(var Tree: TAVLTree);
var
Node: TAVLTreeNode;
MsgItem: PMsgItem;
begin
Node:=Tree.FindLowest;
while Node<>nil do begin
MsgItem:=PMsgItem(Node.Data);
Dispose(MsgItem);
Node:=Tree.FindSuccessor(Node);
end;
Tree.Free;
Tree:=nil;
end;
type
TPoFile = class
public
Tree: TAVLTree;
Header: TStringList;
constructor Create;
destructor Destroy; override;
end;
{ TPoFile }
constructor TPoFile.Create;
begin
Tree:=TAVLTree.Create(@CompareMsgItems);
Header:=TStringList.Create;
end;
destructor TPoFile.Destroy;
begin
DisposeMsgTree(Tree);
Header.Free;
inherited Destroy;
end;
//==============================================================================
var
Files: TStringList;
Prefix: string;
procedure IncPrefix;
begin
Prefix:=Prefix+' ';
end;
procedure DecPrefix;
begin
Prefix:=LeftStr(Prefix,length(Prefix)-2);
end;
function ParamsValid: boolean;
var
i: Integer;
Filename: String;
Ext: String;
Name: string;
begin
Result:=false;
if ParamCount<1 then exit;
for i:=1 to ParamCount do begin
Filename:=ParamStr(1);
Ext:=ExtractFileExt(Filename);
if not FileExists(Filename) then begin
writeln('ERROR: file not found: ',FileName);
exit;
end;
if (Ext<>'.po') then begin
writeln('ERROR: invalid extension: ',Filename);
exit;
end;
Name:=ExtractFileName(Filename);
Name:=LeftStr(Name,length(Name)-length(Ext));
if Pos('.',Name)>0 then begin
writeln('ERROR: invalid unitname: ',Name);
exit;
end;
if Files=nil then Files:=TStringList.Create;
Files.Add(Filename);
end;
Result:=true;
end;
function ReadMessageItem(SrcFile: TStringList; var Line: integer): PMsgItem;
var
s: string;
CommentStarted: boolean;
begin
New(Result);
CommentStarted:=false;
while Line<SrcFile.Count do begin
s:=SrcFile[Line];
if (LeftStr(s,7)='msgid "') then begin
Result^.ID:=copy(s,8,length(s)-8);
inc(Line);
if Line<SrcFile.Count then begin
s:=SrcFile[Line];
if LeftStr(s,8)='msgstr "' then begin
Result^.Str:=copy(s,9,length(s)-9);
inc(Line);
end;
end;
exit;
end;
if s<>'' then begin
if CommentStarted then
Result^.Comment:=Result^.Comment+#10+s
else begin
Result^.Comment:=s;
CommentStarted:=true;
end;
end;
inc(Line);
end;
end;
procedure WriteMessageItem(MsgItem: PMsgItem; DestFile: TStringList);
begin
DestFile.Add(MsgItem^.Comment);
DestFile.Add('msgid "'+MsgItem^.ID+'"');
DestFile.Add('msgstr "'+MsgItem^.Str+'"');
DestFile.Add('');
end;
function ReadPoFile(const Filename: string): TPoFile;
var
SrcFile: TStringList;
MsgItem: PMsgItem;
Line: Integer;
begin
Result:=TPoFile.Create;
// read source .po file
//writeln(Prefix,'Loading ',Filename,' ...');
SrcFile:=TStringList.Create;
SrcFile.LoadFromFile(Filename);
Line:=0;
while Line<SrcFile.Count do begin
if (SrcFile[Line]='') then begin
// empty line
inc(Line);
end
else if (length(SrcFile[Line])>=2) and (SrcFile[Line][1]='"') then begin
// header line
Result.Header.Add(SrcFile[Line]);
inc(Line);
end
else begin
// message
MsgItem:=ReadMessageItem(SrcFile,Line);
// ignore doubles
if (MsgItem^.ID='')
or (Result.Tree.FindKey(MsgItem,@CompareMsgItems)<>nil) then begin
Dispose(MsgItem);
continue;
end;
// add message
Result.Tree.Add(MsgItem);
end;
end;
SrcFile.Free;
end;
procedure WritePoFile(PoFile: TPoFile; const Filename: string);
var
DestFile: TStringList;
Node: TAVLTreeNode;
MsgItem: PMsgItem;
Save: Boolean;
OldDestFile: TStringList;
begin
//writeln(Prefix,'Saving ',Filename,' ...');
DestFile:=TStringList.Create;
if (PoFile.Header.Count>0) then begin
DestFile.Add('msgid ""');
DestFile.Add('msgstr ""');
DestFile.AddStrings(PoFile.Header);
DestFile.Add('');
end;
Node:=PoFile.Tree.FindLowest;
while Node<>nil do begin
MsgItem:=PMsgItem(Node.Data);
WriteMessageItem(MsgItem,DestFile);
Node:=PoFile.Tree.FindSuccessor(Node);
end;
Save:=true;
if FileExists(Filename) then begin
OldDestFile:=TStringList.Create;
OldDestFile.LoadFromFile(Filename);
if OldDestFile.Text=DestFile.Text then Save:=false;
OldDestFile.Free;
end;
if Save then
DestFile.SaveToFile(Filename);
DestFile.Free;
end;
function FindAllTranslatedPoFiles(const Filename: string): TStringList;
const
{$IFDEF Win32}
FindMask = '*.*';
{$ELSE}
FindMask = '*';
{$ENDIF}
var
Path: String;
Name: String;
NameOnly: String;
Ext: String;
FileInfo: TSearchRec;
CurExt: String;
begin
Result:=TStringList.Create;
Path:=ExtractFilePath(Filename);
Name:=ExtractFilename(Filename);
Ext:=ExtractFileExt(Filename);
NameOnly:=LeftStr(Name,length(Name)-length(Ext));
if SysUtils.FindFirst(Path+FindMask,faAnyFile,FileInfo)=0 then begin
repeat
if (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;
procedure MergePoTrees(SrcTree, DestTree: TAVLTree);
var
SrcNode, DestNode: TAVLTreeNode;
SrcMsgItem, DestMsgItem: PMsgItem;
OldNode: TAVLTreeNode;
begin
// add all message items from SrcTree into DestTree
SrcNode:=SrcTree.FindLowest;
while SrcNode<>nil do begin
SrcMsgItem:=PMsgItem(SrcNode.Data);
DestNode:=DestTree.FindKey(SrcMsgItem,@CompareMsgItems);
if DestNode<>nil then begin
// ID already exists -> update comment
DestMsgItem:=PMsgItem(DestNode.Data);
DestMsgItem^.Comment:=SrcMsgItem^.Comment;
end else begin
// new ID -> add new message item to DestTree
New(DestMsgItem);
DestMsgItem^.Comment:=SrcMsgItem^.Comment;
DestMsgItem^.ID:=SrcMsgItem^.ID;
DestMsgItem^.Str:=SrcMsgItem^.Str;
DestTree.Add(DestMsgItem);
end;
SrcNode:=SrcTree.FindSuccessor(SrcNode);
end;
// remove all old messages in DestTree
DestNode:=DestTree.FindLowest;
while DestNode<>nil do begin
DestMsgItem:=PMsgItem(DestNode.Data);
OldNode:=DestNode;
DestNode:=DestTree.FindSuccessor(DestNode);
if SrcTree.FindKey(DestMsgItem,@CompareMsgItems)=nil then begin
// unused message -> delete it
writeln('Deleting unused message "',DestMsgItem^.ID,
'" Comment="',DestMsgItem^.Comment,'"');
Dispose(DestMsgItem);
DestTree.Delete(OldNode);
end;
end;
end;
procedure UpdatePoFile(const Filename: string);
var
SrcFile, DestFile: TPoFile;
DestFiles: TStringList;
i: Integer;
begin
writeln('Loading ',Filename,' ...');
SrcFile:=ReadPoFile(Filename);
DestFiles:=FindAllTranslatedPoFiles(Filename);
IncPrefix;
for i:=0 to DestFiles.Count-1 do begin
writeln(Prefix,'Updating ',DestFiles[i]);
IncPrefix;
DestFile:=ReadPoFile(DestFiles[i]);
MergePoTrees(SrcFile.Tree,DestFile.Tree);
WritePoFile(DestFile,DestFiles[i]);
DestFile.Free;
DecPrefix;
end;
DecPrefix;
DestFiles.Free;
SrcFile.Free;
end;
procedure UpdateAllPoFiles;
var
i: Integer;
begin
for i:=0 to Files.Count-1 do begin
UpdatePoFile(Files[i]);
end;
end;
begin
Prefix:='';
Files:=nil;
if not ParamsValid then begin
writeln('Usage: ',ExtractFileName(ParamStr(0))
,' filename1.po [filename2.po ... filenameN.po]');
exit;
end else begin
UpdateAllPoFiles;
end;
Files.Free;
end.