mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 16:35:33 +00:00
198 lines
5.6 KiB
ObjectPascal
198 lines
5.6 KiB
ObjectPascal
unit Keyman.System.DevTools.BuildSetupStringTranslations;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
System.SysUtils,
|
|
|
|
Xml.XMLIntf,
|
|
Xml.XMLDoc;
|
|
|
|
type
|
|
TBuildSetupStringTranslations = class
|
|
private
|
|
class function ProcessFile(const SourceFile, DestinationFile, BCP47, BCP47ID: string): Boolean; static;
|
|
class function PascalifyString(s: string): string; static;
|
|
public
|
|
class function Run(SourcePath, DestinationPath: string): Boolean; static;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Xml.Win.msxmldom,
|
|
Winapi.ActiveX,
|
|
System.Generics.Collections,
|
|
System.StrUtils,
|
|
System.TypInfo,
|
|
System.Variants,
|
|
|
|
|
|
Keyman.System.AndroidStringToKeymanLocaleString,
|
|
SetupStrings; // From Desktop/Setup
|
|
|
|
{ TBuildSetupStringTranslations }
|
|
|
|
class function TBuildSetupStringTranslations.PascalifyString(s: string): string;
|
|
var
|
|
i: Integer;
|
|
ch: Integer;
|
|
InQuotes: Boolean;
|
|
begin
|
|
InQuotes := False;
|
|
Result := '';
|
|
for i := 1 to Length(s) do
|
|
begin
|
|
ch := Ord(s[i]);
|
|
if (ch < 32) or (ch > 126) or (ch = Ord('''')) then
|
|
begin
|
|
if InQuotes then
|
|
Result := Result + '''';
|
|
InQuotes := False;
|
|
// Coming from UTF-8 XML, we may have only #10 so convert to #13#10 if so
|
|
if (ch = 10) and (i > 1) and (Ord(s[i-1]) <> 13)
|
|
then Result := Result + '#13#10'
|
|
else Result := Result + '#'+IntToStr(ch);
|
|
end
|
|
else
|
|
begin
|
|
if not InQuotes then
|
|
Result := Result + '''';
|
|
InQuotes := True;
|
|
Result := Result + Char(ch);
|
|
end;
|
|
|
|
// If string is too long, Delphi doesn't like it
|
|
if (i mod 64) = 0 then
|
|
if InQuotes
|
|
then Result := Result + '''+'''
|
|
else Result := Result + '+';
|
|
end;
|
|
if InQuotes then
|
|
Result := Result + '''';
|
|
end;
|
|
|
|
class function TBuildSetupStringTranslations.ProcessFile(const SourceFile,
|
|
DestinationFile, BCP47, BCP47ID: string): Boolean;
|
|
var
|
|
n: Integer;
|
|
iit: TInstallInfoText;
|
|
doc: IXmlDocument;
|
|
f: TextFile;
|
|
sconst: string;
|
|
node: IXMLNode;
|
|
HasPreviousNode: Boolean;
|
|
strings: TDictionary<TInstallInfoText, string>;
|
|
begin
|
|
doc := TXMLDocument.Create(nil);
|
|
doc.ParseOptions := [poResolveExternals]; // I902 - resolve externals when loading XML files
|
|
doc.LoadFromFile(SourceFile);
|
|
|
|
strings := TDictionary<TInstallInfoText, string>.Create;
|
|
try
|
|
node := doc.DocumentElement.ChildNodes[0];
|
|
while Assigned(node) do
|
|
begin
|
|
if node.NodeName = 'string' then
|
|
begin
|
|
n := GetEnumValue(TypeInfo(TInstallInfoText), VarToStr(node.Attributes['name']));
|
|
if (n >= Ord(Low(TInstallInfoText))) and (n <= Ord(High(TInstallInfoText))) then
|
|
strings.Add(TInstallInfoText(n), PascalifyString(TAndroidStringToKeymanLocaleString.Transform(VarToStr(node.NodeValue))));
|
|
end;
|
|
node := node.NextSibling;
|
|
end;
|
|
|
|
AssignFile(f, DestinationFile);
|
|
Rewrite(f);
|
|
writeln(f, '// Generated file - generated by devtools -buildsetupstrings at '+FormatDateTime('dd/mm/yyyy hh:nn:ss', Now));
|
|
writeln(f, 'unit '+ChangeFileExt(ExtractFileName(DestinationFile), '')+';'#13#10'interface'#13#10);
|
|
writeln(f, 'uses Keyman.Setup.System.SetupUILanguageManager, SetupStrings;'#13#10);
|
|
|
|
sconst := 'const FLocaleStrings_'+BCP47ID+': TSetupUILanguageManager.TLocaleArray = ('#13#10;
|
|
|
|
HasPreviousNode := False;
|
|
for iit := Low(TInstallInfoText) to High(TInstallInfoText) do
|
|
begin
|
|
if HasPreviousNode then
|
|
sconst := sconst + ','#13#10;
|
|
|
|
if strings.ContainsKey(iit)
|
|
then sconst := sconst + ' '+strings[iit]
|
|
else sconst := sconst + ' ''''';
|
|
HasPreviousNode := True;
|
|
end;
|
|
finally
|
|
strings.Free;
|
|
end;
|
|
|
|
sconst := sconst + #13#10');';
|
|
|
|
writeln(f, sconst);
|
|
writeln(f);
|
|
writeln(f, 'implementation');
|
|
writeln(f);
|
|
writeln(f, 'initialization');
|
|
writeln(f,' TSetupUILanguageManager.RegisterSetupStrings('''+BCP47+''', FLocaleStrings_'+BCP47ID+');');
|
|
writeln(f, 'end.');
|
|
|
|
CloseFile(f);
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
class function TBuildSetupStringTranslations.Run(SourcePath,
|
|
DestinationPath: string): Boolean;
|
|
var
|
|
f: TSearchRec;
|
|
outfile: TextFile;
|
|
SourceFile, DestFile, BCP47, BCP47ID: string;
|
|
HasPreviousNode: Boolean;
|
|
begin
|
|
SourcePath := IncludeTrailingPathDelimiter(SourcePath);
|
|
DestinationPath := IncludeTrailingPathDelimiter(DestinationPath);
|
|
|
|
// Iterate through SourcePath/*/strings.xml and generate DestinationPath/Keyman.Setup.System.Locale.*.pas file for each
|
|
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
|
|
try
|
|
Xml.Win.msxmldom.MSXMLDOMDocumentFactory.AddDOMProperty('ProhibitDTD', False);
|
|
|
|
AssignFile(outfile, DestinationPath+'Keyman.Setup.System.Locales.pas');
|
|
Rewrite(outfile);
|
|
writeln(outfile, 'unit Keyman.Setup.System.Locales;');
|
|
writeln(outfile, 'interface');
|
|
writeln(outfile, 'implementation');
|
|
writeln(outfile, 'uses');
|
|
|
|
HasPreviousNode := False;
|
|
if FindFirst(SourcePath+'*', faDirectory, f) = 0 then
|
|
begin
|
|
repeat
|
|
BCP47 := f.Name;
|
|
BCP47ID := ReplaceText(BCP47, '-', '_');
|
|
SourceFile := SourcePath+BCP47+'\strings.xml';
|
|
DestFile := DestinationPath+'Keyman.Setup.System.Locale.'+BCP47ID+'.pas';
|
|
if (BCP47 <> '..') and (BCP47 <> '.') and FileExists(SourcePath+BCP47+'\strings.xml') then
|
|
begin
|
|
if not ProcessFile(SourceFile, DestFile, BCP47, BCP47ID) then
|
|
Exit(False);
|
|
if HasPreviousNode then
|
|
writeln(outfile, ',');
|
|
write(outfile, ' Keyman.Setup.System.Locale.'+BCP47ID);
|
|
HasPreviousNode := True;
|
|
end;
|
|
until FindNext(f) <> 0;
|
|
FindClose(f);
|
|
end;
|
|
|
|
writeln(outfile, ';');
|
|
writeln(outfile, 'end.');
|
|
CloseFile(outfile);
|
|
finally
|
|
CoUninitialize;
|
|
end;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
end.
|