mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 00:45:32 +00:00
This was flagged years ago as a potential performance hotspot: each locale is loaded at process start for any kmcomapi-involved process, which takes quite a long time. This refactor moves the locale enumeration out of kmcomapi and into the build process, so we have a static list of locales put into locale/index.xml. This includes a minor breaking change to Keyman Engine API on Windows: `MessageFromID(id, locale)` now only allows the three ids SKUILanguageName, SKUILanguageNameWithEnglish, and SKLanguageCode, for performance reasons. At this point, only SKUILanguageNameWithEnglish is actually used anywhere in Keyman, and it is unlikely that any other consumers are requesting alternate IDs. Fixes: #14787 Build-bot: skip release:windows
118 lines
3 KiB
ObjectPascal
118 lines
3 KiB
ObjectPascal
(*
|
|
* Keyman is copyright (C) SIL Global. MIT License.
|
|
*
|
|
* Build index.xml from individual locale.xml files
|
|
*)
|
|
unit Keyman.System.DevTools.BuildLocaleIndex;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.Classes,
|
|
System.SysUtils,
|
|
|
|
Xml.XMLIntf,
|
|
Xml.XMLDoc;
|
|
|
|
type
|
|
TBuildLocaleIndex = class
|
|
private
|
|
class function ProcessFile(const SourceFile: string; LocalesNode: IXmlNode;
|
|
const BCP47: string): Boolean; static;
|
|
public
|
|
class function Run(SourcePath, Index: string): Boolean; static;
|
|
end;
|
|
|
|
implementation
|
|
|
|
uses
|
|
Xml.Win.msxmldom,
|
|
Winapi.ActiveX,
|
|
System.StrUtils,
|
|
System.TypInfo,
|
|
System.Variants;
|
|
|
|
{ TBuildSetupStringTranslations }
|
|
|
|
|
|
class function TBuildLocaleIndex.ProcessFile(const SourceFile: string; LocalesNode: IXmlNode;
|
|
const BCP47: string): Boolean;
|
|
var
|
|
doc: IXmlDocument;
|
|
node, locale: IXMLNode;
|
|
uiLanguageName, uiLanguageNameWithEnglish: string;
|
|
begin
|
|
doc := TXMLDocument.Create(nil);
|
|
doc.ParseOptions := [poResolveExternals]; // I902 - resolve externals when loading XML files
|
|
doc.LoadFromFile(SourceFile);
|
|
|
|
node := doc.DocumentElement.ChildNodes[0];
|
|
while Assigned(node) do
|
|
begin
|
|
if (node.NodeName = 'string') and (node.Attributes['name'] = 'SKUILanguageName') then
|
|
uiLanguageName := node.NodeValue
|
|
else if (node.NodeName = 'string') and (node.Attributes['name'] = 'SKUILanguageNameWithEnglish') then
|
|
uiLanguageNameWithEnglish := node.NodeValue;
|
|
node := node.NextSibling;
|
|
end;
|
|
|
|
if (uiLanguageName = '') or (uiLanguageNameWithEnglish = '') then
|
|
begin
|
|
writeln('ERROR: '+SourceFile+' is missing SKUILanguageName or SKUILanguageNameWithEnglish strings');
|
|
Exit(False);
|
|
end;
|
|
|
|
locale := LocalesNode.AddChild('locale');
|
|
locale.Attributes['SKLanguageCode'] := BCP47;
|
|
locale.Attributes['SKUILanguageName'] := uiLanguageName;
|
|
locale.Attributes['SKUILanguageNameWithEnglish'] := uiLanguageNameWithEnglish;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
class function TBuildLocaleIndex.Run(SourcePath, Index: string): Boolean;
|
|
var
|
|
f: TSearchRec;
|
|
SourceFile, BCP47: string;
|
|
OutputDoc: IXmlDocument;
|
|
LocalesNode: IXmlNode;
|
|
begin
|
|
SourcePath := IncludeTrailingPathDelimiter(SourcePath);
|
|
|
|
// Iterate through SourcePath/*/strings.xml and generate index.xml
|
|
CoInitializeEx(nil, COINIT_APARTMENTTHREADED);
|
|
try
|
|
Xml.Win.msxmldom.MSXMLDOMDocumentFactory.AddDOMProperty('ProhibitDTD', False);
|
|
|
|
OutputDoc := NewXMLDocument;
|
|
OutputDoc.Encoding := 'UTF-8';
|
|
OutputDoc.Active := True;
|
|
LocalesNode := OutputDoc.AddChild('locales');
|
|
|
|
if FindFirst(SourcePath+'*', faDirectory, f) = 0 then
|
|
begin
|
|
repeat
|
|
BCP47 := f.Name;
|
|
SourceFile := SourcePath+BCP47+'\strings.xml';
|
|
if (BCP47 <> '..') and (BCP47 <> '.') and FileExists(SourcePath+BCP47+'\strings.xml') then
|
|
begin
|
|
if not ProcessFile(SourceFile, LocalesNode, BCP47) then
|
|
Exit(False);
|
|
end;
|
|
until FindNext(f) <> 0;
|
|
FindClose(f);
|
|
end;
|
|
|
|
LocalesNode := nil;
|
|
|
|
OutputDoc.SaveToFile(Index);
|
|
OutputDoc := nil;
|
|
|
|
finally
|
|
CoUninitialize;
|
|
end;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
end.
|