CnHashLangStorage.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. {******************************************************************************}
  2. { CnPack For Delphi/C++Builder }
  3. { 中国人自己的开放源码第三方开发包 }
  4. { (C)Copyright 2001-2016 CnPack 开发组 }
  5. { ------------------------------------ }
  6. { }
  7. { 本开发包是开源的自由软件,您可以遵照 CnPack 的发布协议来修 }
  8. { 改和重新发布这一程序。 }
  9. { }
  10. { 发布这一开发包的目的是希望它有用,但没有任何担保。甚至没有 }
  11. { 适合特定目的而隐含的担保。更详细的情况请参阅 CnPack 发布协议。 }
  12. { }
  13. { 您应该已经和开发包一起收到一份 CnPack 发布协议的副本。如果 }
  14. { 还没有,可访问我们的网站: }
  15. { }
  16. { 网站地址:http://www.cnpack.org }
  17. { 电子邮件:master@cnpack.org }
  18. { }
  19. {******************************************************************************}
  20. unit CnHashLangStorage;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:CnPack 多语包
  24. * 单元名称:Hash 文本多语存储组件单元
  25. * 单元作者:CnPack开发组
  26. * 备 注:该单元实现了Hash TXT 多语存储组件类
  27. * 开发平台:PWin2000 + Delphi 5.0
  28. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7
  29. * 本 地 化:该单元中的字符串均符合本地化处理方式
  30. * 单元标识:$Id$
  31. * 修改记录:2004.10.23 V1.2
  32. * 修改初始化文件的处理方式
  33. * 2003.12.13 V1.1
  34. * InternalInit 中增加对 DefaultFont 的读入处理
  35. * 2003.08.20 V1.0
  36. * 创建单元,实现功能
  37. ================================================================================
  38. |</PRE>}
  39. interface
  40. {$I CnPack.inc}
  41. uses
  42. SysUtils, Classes, Windows, IniFiles, Dialogs, FileCtrl, CnCommon,
  43. CnConsts, CnLangConsts, CnHashMap, CnWideStrings, CnLangStorage, CnIniStrUtils;
  44. type
  45. TCnCustomHashLangStorage = class;
  46. TCnHashStringIterator = class(TInterfacedObject, ICnLangStringIterator)
  47. private
  48. FHashStorage: TCnCustomHashLangStorage;
  49. FEof: Boolean;
  50. FBof: Boolean;
  51. FFrontPattern: WideString;
  52. FKey: WideString;
  53. FValue: WideString;
  54. public
  55. constructor Create(AHashStorage: TCnCustomHashLangStorage);
  56. destructor Destroy; override;
  57. procedure StartIterate(const FrontPattern: WideString = '');
  58. procedure Previous;
  59. procedure Next;
  60. procedure EndIterate;
  61. procedure GetCurrentKeyValue(var Key:WideString; var Value: WideString);
  62. function GetCurrentString: WideString;
  63. function GetEof: Boolean;
  64. function GetBof: Boolean;
  65. property Eof: Boolean read GetEof;
  66. property Bof: Boolean read GetBof;
  67. end;
  68. TCnHashLangLoadFile = procedure(Sender: TObject; AFileName: WideString;
  69. AList: TCnWideStringList) of object;
  70. TCnCustomHashLangStorage = class(TCnCustomLangFileStorage)
  71. private
  72. FHashMap: TCnWideStrToWideStrHashMap;
  73. FListLength: Integer;
  74. FIncSize: Integer;
  75. FOnLoadFile: TCnHashLangLoadFile;
  76. procedure SetIncSize(const Value: Integer);
  77. procedure SetListLength(const Value: Integer);
  78. protected
  79. procedure InitHashMap;
  80. procedure AddStringToHashMap(const Key: WideString; const Value: WideString);
  81. procedure InitFromAFile(const AFileName: WideString); override;
  82. procedure CreateCurrentLanguage; override;
  83. procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
  84. procedure DoLoadFile(AFileName: WideString; AList: TCnWideStringList);
  85. property HashMap: TCnWideStrToWideStrHashMap read FHashMap;
  86. public
  87. constructor Create(AOwner: TComponent); override;
  88. destructor Destroy; override;
  89. class function GetLanguageFileExt: WideString; override;
  90. {* 返回多语言文件的扩展名.TXT }
  91. function GetString(Name: WideString; var Value: WideString): Boolean; override;
  92. {* 获得一语言条目的翻译后的字符串 }
  93. procedure GetNamesList(List: TStrings); override;
  94. {* 获得当前语言的所有语言条目名称列表 }
  95. function IsLanguageFile(const FileName: WideString): Boolean; override;
  96. {* 判断一文件是否合法的语言文件 }
  97. procedure SetString(Name, Value: WideString); override;
  98. {* 保存一语言条目,如存在则覆盖,否则新增 }
  99. function CreateIterator: ICnLangStringIterator; override;
  100. {* 获得遍历器,如果子类不支持遍历,则必须返回 nil}
  101. function LoadCurrentLanguage: Boolean; override;
  102. {* 从 TXT 文件中载入当前语言条目,为翻译字串做准备 }
  103. procedure SaveCurrentLanguage; override;
  104. {* 保存当前语言文件 }
  105. procedure ClearCurrentLanguage; override;
  106. {* 删除当前语言的所有翻译条目列表,同时也保存本语言 }
  107. published
  108. property StorageMode;
  109. {* 多语存储方式 }
  110. property LanguagePath;
  111. {* 语言文件存储的统一路径 }
  112. property FileName;
  113. {* 按文件存储时的统一多语文件名 }
  114. property Languages;
  115. {* 语言对象列表 }
  116. property ListLength: Integer read FListLength write SetListLength;
  117. {* 初始列表大小 }
  118. property IncSize: Integer read FIncSize write SetIncSize;
  119. {* 重分配时增加的大小 }
  120. property AutoDetect;
  121. {* LanguagePath 改变时是否自动检测语言 }
  122. property OnLoadFile: TCnHashLangLoadFile read FOnLoadFile write FOnLoadFile;
  123. {* 自定义加载文件事件 }
  124. end;
  125. TCnHashLangFileStorage = class(TCnCustomHashLangStorage)
  126. published
  127. property StorageMode;
  128. {* 多语存储方式 }
  129. property LanguagePath;
  130. {* 语言文件存储的统一路径 }
  131. property Languages;
  132. {* 语言对象列表 }
  133. property FileName;
  134. {* 按文件存储时的统一多语文件名 }
  135. property ListLength;
  136. {* 初始列表大小 }
  137. property IncSize;
  138. {* 重分配时增加的大小 }
  139. property AutoDetect;
  140. {* LanguagePath 改变时是否自动检测语言 }
  141. end;
  142. implementation
  143. {************************** TCnCustomHashLangStorage **************************}
  144. constructor TCnCustomHashLangStorage.Create(AOwner: TComponent);
  145. begin
  146. inherited;
  147. Self.FListLength := 1024;
  148. Self.FIncSize := 2;
  149. end;
  150. destructor TCnCustomHashLangStorage.Destroy;
  151. begin
  152. if Assigned(FHashMap) then
  153. FHashMap.Free;
  154. inherited;
  155. end;
  156. procedure TCnCustomHashLangStorage.DoLoadFile(AFileName: WideString;
  157. AList: TCnWideStringList);
  158. begin
  159. if Assigned(FOnLoadFile) then
  160. FOnLoadFile(Self, AFileName, AList)
  161. else
  162. AList.LoadFromFile(AFileName);
  163. end;
  164. procedure TCnCustomHashLangStorage.CreateCurrentLanguage;
  165. begin
  166. InitHashMap;
  167. end;
  168. function TCnCustomHashLangStorage.GetString(Name: WideString; var Value: WideString):
  169. Boolean;
  170. begin
  171. Result := False;
  172. if Assigned(FHashMap) then
  173. begin
  174. Result := FHashMap.Find(Name, Value);
  175. if Result then
  176. Value := StringReplace(Value, SCnBR, SCnCRLF, [rfReplaceAll, rfIgnoreCase])
  177. else
  178. Value := '';
  179. end;
  180. end;
  181. function TCnCustomHashLangStorage.LoadCurrentLanguage: Boolean;
  182. var
  183. List: TCnWideStringList;
  184. i, EPos: Integer;
  185. S: WideString;
  186. begin
  187. Result := True;
  188. InitHashMap;
  189. List := TCnWideStringList.Create;
  190. try
  191. // 子类也要检查设计期是否有指定路径,类似于 Ini 那个
  192. if (csDesigning in ComponentState) and (LanguagePath = '') and (DesignLangPath <> '') then
  193. S := IncludeTrailingBackslash(DesignLangPath) + GetCurrentLanguageFileName
  194. else
  195. S := IncludeTrailingBackslash(LanguagePath) + GetCurrentLanguageFileName;
  196. DoLoadFile(S, List);
  197. except
  198. Result := False;
  199. List.Free;
  200. Exit;
  201. end;
  202. for i := 0 to List.Count - 1 do
  203. begin
  204. S := List[i];
  205. EPos := Pos(DefEqual, S);
  206. if EPos > 0 then
  207. AddStringToHashMap(Copy(S, 1, EPos - 1), Copy(S, EPos + 1,
  208. Length(S) - EPos))
  209. else
  210. AddStringToHashMap(Copy(S, 1, EPos - 1), '');
  211. end;
  212. List.Free;
  213. end;
  214. procedure TCnCustomHashLangStorage.SaveCurrentLanguage;
  215. var
  216. Key, Value, aFileName: WideString;
  217. List: TCnWideStringList;
  218. begin
  219. if Assigned(FHashMap) then
  220. begin
  221. List := TCnWideStringList.Create;
  222. try
  223. FHashMap.StartEnum;
  224. while FHashMap.GetNext(Key, Value) do
  225. List.Add(Key + DefEqual + Value);
  226. List.Sort;
  227. // 设计期如果被赋值了设计期文件存储的目录,则存到此目录下
  228. if (csDesigning in ComponentState) and (LanguagePath = '') and (DesignLangPath <> '') then
  229. aFileName := IncludeTrailingBackslash(DesignLangPath) + GetCurrentLanguageFileName
  230. else
  231. aFileName := IncludeTrailingBackslash(LanguagePath) + GetCurrentLanguageFileName;
  232. if not ForceDirectories(_CnExtractFilePath(aFileName)) then
  233. raise ELanguageStorageError.Create(SCnCanNotCreateDir + _CnExtractFilePath(aFileName));
  234. List.SaveToFile(aFileName, wlfUtf8);
  235. finally
  236. List.Free;
  237. end;
  238. end;
  239. end;
  240. procedure TCnCustomHashLangStorage.SetString(Name, Value: WideString);
  241. var
  242. myValue: WideString;
  243. begin
  244. if Assigned(FHashMap) then
  245. begin
  246. if FHashMap.Find(Name, myValue) then
  247. FHashMap.Delete(Name);
  248. AddStringToHashMap(Name, StringReplace(Value, SCnCRLF, SCnBR, [rfReplaceAll, rfIgnoreCase]));
  249. end;
  250. end;
  251. procedure TCnCustomHashLangStorage.GetNamesList(List: TStrings);
  252. var
  253. Key, Value: WideString;
  254. begin
  255. if List <> nil then
  256. begin
  257. List.Clear;
  258. if Assigned(FHashMap) then
  259. begin
  260. FHashMap.StartEnum;
  261. while FHashMap.GetNext(Key, Value) do
  262. List.Add(Key);
  263. if List is TStringList then
  264. (List as TStringList).Sort;
  265. end;
  266. end;
  267. end;
  268. procedure TCnCustomHashLangStorage.ClearCurrentLanguage;
  269. begin
  270. InitHashMap;
  271. SaveCurrentLanguage;
  272. end;
  273. class function TCnCustomHashLangStorage.GetLanguageFileExt: WideString;
  274. begin
  275. Result := '.txt';
  276. end;
  277. function TCnCustomHashLangStorage.IsLanguageFile(
  278. const FileName: WideString): Boolean;
  279. var
  280. List: TCnWideStringList;
  281. begin
  282. Result := False;
  283. List := TCnWideStringList.Create;
  284. try
  285. DoLoadFile(FileName, List);
  286. if List.Count > 0 then
  287. Result := Copy(List[0], 1, Length(SystemNamePrefix + SCnLanguageID)) =
  288. SystemNamePrefix + SCnLanguageID;
  289. finally
  290. List.Free;
  291. end;
  292. end;
  293. procedure TCnCustomHashLangStorage.InitHashMap;
  294. begin
  295. if Assigned(FHashMap) then
  296. FreeAndNil(FHashMap);
  297. FHashMap := TCnWideStrToWideStrHashMap.Create(FListLength, FIncSize);
  298. end;
  299. procedure TCnCustomHashLangStorage.SetIncSize(const Value: Integer);
  300. begin
  301. if Value > 0 then
  302. FIncSize := Value;
  303. end;
  304. procedure TCnCustomHashLangStorage.SetListLength(const Value: Integer);
  305. begin
  306. if Value > 0 then
  307. FListLength := Value;
  308. end;
  309. function TCnCustomHashLangStorage.CreateIterator: ICnLangStringIterator;
  310. begin
  311. Result := TCnHashStringIterator.Create(Self);
  312. end;
  313. procedure TCnCustomHashLangStorage.InitFromAFile(const AFileName: WideString);
  314. var
  315. List: TCnWideStringList;
  316. begin
  317. List := TCnWideStringList.Create;
  318. try
  319. with Languages.Add do
  320. begin
  321. LanguageFileName := _CnExtractFileName(_CnChangeFileExt(AFileName, ''));
  322. DoLoadFile(AFileName, List);
  323. try
  324. LanguageID := StrToIntDef(List.Values[SystemNamePrefix + SCnLanguageID], 0);
  325. except
  326. LanguageID := 0;
  327. end;
  328. if LanguageID <> 0 then
  329. begin
  330. LanguageName := List.Values[SystemNamePrefix + SCnLanguageName];
  331. Author := List.Values[SystemNamePrefix + SCnAuthor];
  332. AuthorEmail := List.Values[SystemNamePrefix + SCnAuthorEmail];
  333. if List.Values[SystemNamePrefix + SCnDefaultFont] <> '' then
  334. StringToFont(List.Values[SystemNamePrefix + SCnDefaultFont], DefaultFont);
  335. end
  336. else
  337. begin
  338. Self.FCurrentLanguageIndex := -1;
  339. Self.Languages.Delete(Index);
  340. end;
  341. end;
  342. finally
  343. List.Free;
  344. end;
  345. end;
  346. procedure TCnCustomHashLangStorage.GetComponentInfo(var AName, Author,
  347. Email, Comment: string);
  348. begin
  349. AName := SCnHashLangStorageName;
  350. Author := SCnPack_LiuXiao;
  351. Email := SCnPack_LiuXiaoEmail;
  352. Comment := SCnHashLangStorageComment;
  353. end;
  354. procedure TCnCustomHashLangStorage.AddStringToHashMap(const Key,
  355. Value: WideString);
  356. begin
  357. FHashMap.Add(Key, Value);
  358. end;
  359. { TCnHashStringIterator }
  360. constructor TCnHashStringIterator.Create(AHashStorage: TCnCustomHashLangStorage);
  361. begin
  362. inherited Create;
  363. FHashStorage := AHashStorage;
  364. end;
  365. destructor TCnHashStringIterator.Destroy;
  366. begin
  367. inherited;
  368. end;
  369. procedure TCnHashStringIterator.EndIterate;
  370. begin
  371. // Do Almost NOTHING.
  372. FKey := '';
  373. FValue := '';
  374. end;
  375. function TCnHashStringIterator.GetBof: Boolean;
  376. begin
  377. Result := FBof;
  378. end;
  379. procedure TCnHashStringIterator.GetCurrentKeyValue(var Key, Value: WideString);
  380. begin
  381. Key := FKey;
  382. Value := FValue;
  383. end;
  384. function TCnHashStringIterator.GetCurrentString: WideString;
  385. begin
  386. Result := FKey + DefEqual + FValue;
  387. end;
  388. function TCnHashStringIterator.GetEof: Boolean;
  389. begin
  390. Result := FEof;
  391. end;
  392. procedure TCnHashStringIterator.Next;
  393. begin
  394. if FHashStorage.FHashMap <> nil then
  395. begin
  396. repeat
  397. FEof := not FHashStorage.FHashMap.GetNext(FKey, FValue);
  398. until FEof or (FFrontPattern = '') or (Pos(FFrontPattern, FKey) = 1);
  399. end
  400. else
  401. FEof := True;
  402. end;
  403. procedure TCnHashStringIterator.Previous;
  404. begin
  405. raise ELanguageStorageError.Create('Previous operation NOT supported.');
  406. end;
  407. procedure TCnHashStringIterator.StartIterate(const FrontPattern: WideString);
  408. begin
  409. Assert(FHashStorage <> nil);
  410. if FHashStorage.FHashMap <> nil then
  411. begin
  412. FHashStorage.FHashMap.StartEnum;
  413. FFrontPattern := FrontPattern;
  414. repeat
  415. FEof := not FHashStorage.FHashMap.GetNext(FKey, FValue);
  416. until FEof or (FFrontPattern = '') or (Pos(FFrontPattern, FKey) = 1);
  417. FBof := FEof;
  418. end;
  419. end;
  420. end.