CnLangMgr.pas 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596
  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 CnLangMgr;
  21. {* |<PRE>
  22. ================================================================================
  23. * 软件名称:CnPack 多语包
  24. * 单元名称:多语管理器基础类单元
  25. * 单元作者:CnPack开发组 刘啸 (liuxiao@cnpack.org)
  26. * 备 注:该单元定义了多语管理器基础类
  27. * 开发平台:PWin2000 + Delphi 5.0
  28. * 兼容测试:PWin9X/2000/XP + Delphi 5/6/7
  29. * 本 地 化:该单元中的字符串均符合本地化处理方式
  30. * 单元标识:$Id$
  31. * 修改记录:2009.08.18 V1.5
  32. * 将字符串常量注册机制与多语管理器独立出来
  33. * 2009.07.15 V1.5
  34. * 修改资源字符串常量存储机制,直接保存在 PResStringRec的Identifier
  35. * 中,由翻译时统一改动,不挂接以减少问题。
  36. * 2009.07.11 V1.4
  37. * 增加字符串常量的注册机制,注册了的字符串能在改变语言时被自动翻译
  38. * 而无需在事件中手工调用 TranslateStr,资源字符串的自动翻译也已通
  39. * 过挂接 LoadResString 完成。
  40. * 2008.05.30 V1.3
  41. * 不处理只读的 string 属性,加入某 Tag 值不翻译的机制
  42. * 2007.09.18 V1.10
  43. * 增加翻译事件以便让用户控制是否翻译某些对象和属性。
  44. * 2006.08.21 V1.9
  45. * 修正手工创建多语管理器时未释放的问题。
  46. * 2006.08.19 V1.8
  47. * 修改为允许多实例,但全局函数只返回第一个实例。
  48. * 2006.08.17 V1.7
  49. * 增加字符串数组翻译函数。
  50. * 2005.04.02 V1.6
  51. * 根据崔东伟的建议,合并 AList 的使用以避免循环引用。
  52. * 2004.10.25 V1.5
  53. * 增加基于字符串进行搜索的翻译模式。
  54. * 2004.07.16 V1.4
  55. * 增加单独翻译某一 Component 的功能;改做设计期不进行翻译,
  56. * (翻译整个 IDE 窗口总是不好)
  57. * 2004.07.12 V1.3
  58. * 进行初步的性能测试,确定 List 的使用未增加显著的开销
  59. * 2004.06.01 V1.2
  60. * 修改对 Form 的处理,不区分 Form 的 Parent 是否为 nil 了
  61. * 2003.12.10 V1.1
  62. * 增加对字体的额外处理
  63. * 2003.08.20 V1.0
  64. * 创建单元,实现功能
  65. ================================================================================
  66. |</PRE>}
  67. interface
  68. {$I CnPack.inc}
  69. {$UNDEF DEBUG}
  70. uses
  71. SysUtils, Classes, Graphics, TypInfo, Windows, Forms, ComCtrls, ActnList,
  72. Dialogs, ExtCtrls, Controls, Contnrs, {$IFDEF COMPILER6_UP}Variants, {$ENDIF}
  73. CnConsts, CnClasses, CnCommon, CnLangStorage, CnIniStrUtils;
  74. const
  75. CN_MULTI_LANG_TAG_NOT_TRANSLATE = 2001;
  76. {* 组件 Tag 值为此值时,不翻译}
  77. type
  78. ECnLanguageManagerError = class(Exception);
  79. PCnLangChangedNotifierRecord = ^TCnLangChangedNotifierRecord;
  80. TCnLangChangedNotifierRecord = record
  81. Notifier: TMethod;
  82. end;
  83. TCnAutoTransOption = (atApplication, atForms, atDataModules);
  84. TCnAutoTransOptions = set of TCnAutoTransOption;
  85. TCnTranslationMode = (tmByComponents, tmByStrings);
  86. {* 翻译模式,根据窗体和控件等遍历还是根据翻译字符串内容遍历 }
  87. TCnStringObj = class
  88. {* 描述一自动翻译的字符串}
  89. private
  90. FStringAddr: Pointer;
  91. FStringName: WideString;
  92. FIsWide: Boolean;
  93. public
  94. property StringAddr: Pointer read FStringAddr write FStringAddr;
  95. property StringName: WideString read FStringName write FStringName;
  96. property IsWide: Boolean read FIsWide write FIsWide;
  97. end;
  98. TCnResourceStringObj = class
  99. {* 描述一自动翻译的资源字符串}
  100. private
  101. FStringRecAddr: Pointer;
  102. FStringName: WideString;
  103. FDstStr: string;
  104. public
  105. property StringRecAddr: Pointer read FStringRecAddr write FStringRecAddr;
  106. property StringName: WideString read FStringName write FStringName;
  107. end;
  108. TTranslateStringEvent = procedure (Sender: TObject; const Src: WideString;
  109. var Dst: WideString) of object;
  110. {* 翻译字符串事件,可用于统一检查或修改目标字符串 }
  111. TCnBaseLangManager = class(TCnComponent)
  112. {* 多语言管理器类 }
  113. private
  114. FDefaultLanguageIndex: Integer;
  115. FCurrentLanguageIndex: Integer;
  116. FOnStorageChanged: TNotifyEvent;
  117. FOnLanguageChanged: TNotifyEvent;
  118. FOnTranslateString: TTranslateStringEvent;
  119. FAutoTranslateStrings: Boolean;
  120. procedure SetLanguageStorage(Value: TCnCustomLangStorage);
  121. procedure AdjustNewLanguage(AID: LongWord);
  122. function GetCurrentLanguageIndex: Integer;
  123. protected
  124. FLanguageStorage: TCnCustomLangStorage;
  125. procedure Notification(AComponent: TComponent; Operation: TOperation);
  126. override;
  127. procedure DoStorageChanged; virtual;
  128. procedure DoLanguageChanged; virtual;
  129. procedure SetCurrentLanguageIndex(const Value: Integer); virtual;
  130. procedure GetComponentInfo(var AName, Author, Email, Comment: string); override;
  131. public
  132. constructor Create(AOwner: TComponent); override;
  133. {* 构造方法 }
  134. destructor Destroy; override;
  135. {* 销毁方法 }
  136. function Translate(Src: WideString): WideString;
  137. {* 根据当前语言获得翻译的字符串 }
  138. function TranslateString(Src: WideString): WideString;
  139. {* 根据当前语言获得翻译的字符串,无则返回空 }
  140. function TranslateStrFmt(Src: WideString; Args: array of const): WideString;
  141. {* 根据当前语言获得格式化的翻译字符串 }
  142. property AutoTranslateStrings: Boolean read FAutoTranslateStrings
  143. write FAutoTranslateStrings default True;
  144. {* 是否在语言改变时自动翻译已经注册了的字符串与资源字符串,默认为 True}
  145. property LanguageStorage: TCnCustomLangStorage read FLanguageStorage
  146. write SetLanguageStorage;
  147. {* 多语言存储元件引用 }
  148. property CurrentLanguageIndex: Integer read GetCurrentLanguageIndex
  149. write SetCurrentLanguageIndex default -1;
  150. {* 当前语言号,影响到整个程序的语言设置。语言号含义由存储元件条目内容决定 }
  151. property OnStorageChanged: TNotifyEvent read FOnStorageChanged
  152. write FOnStorageChanged;
  153. {* 存储元件引用改变时触发 }
  154. property OnLanguageChanged: TNotifyEvent read FOnLanguageChanged
  155. write FOnLanguageChanged;
  156. {* 当前语言发生改变时触发 }
  157. property OnTranslateString: TTranslateStringEvent read FOnTranslateString
  158. write FOnTranslateString;
  159. {* 当翻译字符串时触发 }
  160. end;
  161. TCnTranslateObjectEvent = procedure (AObject: TObject; var Translate: Boolean) of object;
  162. {* 翻译一对象时的事件原型 }
  163. TCnTranslateObjectPropertyEvent = procedure (AObject: TObject; const PropName: string;
  164. var Translate: Boolean) of object;
  165. {* 翻译一对象及其某个属性时的事件原型 }
  166. TCnCustomLangManager = class(TCnBaseLangManager)
  167. {* 具有翻译窗体能力的多语言管理器 }
  168. private
  169. FNotifier: TList;
  170. FAutoTranslate: Boolean;
  171. FTranslateTreeNode: Boolean;
  172. FTranslateListItem: Boolean;
  173. FUseDefaultFont: Boolean;
  174. FTranslateOtherFont: Boolean;
  175. FAutoTransOptions: TCnAutoTransOptions;
  176. FTranslationMode: TCnTranslationMode;
  177. FOldTransForms: TList;
  178. FOldTransDMs: TList;
  179. FOldFormPrefix: WideString;
  180. FOldDMPrefix: WideString;
  181. FIgnoreAction: Boolean;
  182. FOnTranslateObjectProperty: TCnTranslateObjectPropertyEvent;
  183. FOnTranslateObject: TCnTranslateObjectEvent;
  184. procedure SetTranslationMode(const Value: TCnTranslationMode);
  185. protected
  186. procedure TranslateRecurComponent(AComponent: TComponent;
  187. AList: TList; const BaseName: WideString); virtual;
  188. {* 递归翻译一 Component 和其 Children }
  189. procedure TranslateRecurObject(AObject: TObject; AList: TList;
  190. const BaseName: WideString = ''); virtual;
  191. {* 递归翻译一 Object 和其属性中的 Object }
  192. function GetRecurOwner(AComponent: TComponent): WideString;
  193. {* 回溯获得一 Component 的祖先标识字符串 }
  194. procedure TranslateKeyToValue(const Key, Value: WideString);
  195. {* 翻译级联的字符串 }
  196. procedure SetCurrentLanguageIndex(const Value: Integer); override;
  197. procedure DoLanguageChanged; override;
  198. function DoTranslateObject(AObject: TObject): Boolean; virtual;
  199. function DoTranslateObjectProperty(AObject: TObject;
  200. const PropName: WideString): Boolean; virtual;
  201. public
  202. constructor Create(AOwner: TComponent); override;
  203. destructor Destroy; override;
  204. procedure AddChangeNotifier(Notify: TNotifyEvent);
  205. {* 增加语言改变时的事件通知 }
  206. procedure RemoveChangeNotifier(Notify: TNotifyEvent);
  207. {* 删除语言改变时的事件通知 }
  208. procedure TranslateComponent(AComponent: TComponent; const BaseName: WideString = '');
  209. {* 翻译一个元件及其子对象和子属性 }
  210. procedure TranslateForm(AForm: TCustomForm);
  211. {* 翻译一个 Form 及其子对象和子属性 }
  212. procedure TranslateObject(AObject: TObject; const BaseName: WideString = '');
  213. {* 翻译一个对象及其子对象和子属性 }
  214. property AutoTranslate: Boolean read FAutoTranslate
  215. write FAutoTranslate default True;
  216. {* 是否在当前语言号改变后自动翻译所有已经存在的窗体和其他内容 }
  217. property TranslationMode: TCnTranslationMode read FTranslationMode
  218. write SetTranslationMode;
  219. {* 翻译模式,默认根据窗体和控件等遍历 }
  220. property AutoTransOptions: TCnAutoTransOptions read FAutoTransOptions
  221. write FAutoTransOptions;
  222. {* 自动翻译选项,控制是否翻译窗体、数据模块和 Application 实例 }
  223. property TranslateListItem: Boolean read FTranslateListItem
  224. write FTranslateListItem default False;
  225. {* 是否翻译 ListView 中的 ListItem }
  226. property TranslateTreeNode: Boolean read FTranslateTreeNode
  227. write FTranslateTreeNode default False;
  228. {* 是否翻译 TreeView 中的 TreeNode }
  229. property UseDefaultFont: Boolean read FUseDefaultFont
  230. write FUseDefaultFont default True;
  231. {* 是否翻译完窗体后使用 DefaultFont 来设置窗体字体 }
  232. property TranslateOtherFont: Boolean read FTranslateOtherFont
  233. write FTranslateOtherFont default True;
  234. {* 是否将其他的 Font 属性翻译成字符串 }
  235. property IgnoreAction: Boolean read FIgnoreAction
  236. write FIgnoreAction default True;
  237. {* 是否翻译 Action 属性不为空的控件的 Caption 和 Hint 属性}
  238. property OnTranslateObject: TCnTranslateObjectEvent read FOnTranslateObject
  239. write FOnTranslateObject;
  240. {* 翻译一对象时的事件 }
  241. property OnTranslateObjectProperty: TCnTranslateObjectPropertyEvent
  242. read FOnTranslateObjectProperty write FOnTranslateObjectProperty;
  243. {* 翻译一对象的某个属性时的事件 }
  244. end;
  245. TCnLangManager = class(TCnCustomLangManager)
  246. {* 具有窗体翻译能力的多语言管理器 }
  247. published
  248. property LanguageStorage;
  249. {* 多语言存储元件引用 }
  250. property CurrentLanguageIndex;
  251. {* 当前语言号,影响到整个程序的语言设置。语言号含义由存储元件条目内容决定 }
  252. property AutoTranslate;
  253. {* 是否在当前语言号改变后自动翻译已经存在的窗体和其他内容 }
  254. property AutoTranslateStrings;
  255. {* 是否在语言改变时自动翻译已经注册了的字符串与资源字符串,默认为 True}
  256. property TranslationMode;
  257. {* 翻译模式,默认根据窗体和控件等遍历 }
  258. property AutoTransOptions;
  259. {* 自动翻译选项,控制是否翻译窗体、数据模块和 Application 实例 }
  260. property TranslateListItem;
  261. {* 是否翻译 ListView 中的 ListItem }
  262. property TranslateTreeNode;
  263. {* 是否翻译 TreeView 中的 TreeNode }
  264. property UseDefaultFont;
  265. {* 是否翻译完窗体后使用 DefaultFont 来设置窗体字体 }
  266. property TranslateOtherFont;
  267. {* 是否将其他的 Font 属性翻译成字符串 }
  268. property IgnoreAction;
  269. {* 是否翻译 Action 属性不为空的控件的 Caption 和 Hint 属性}
  270. property OnStorageChanged;
  271. {* 存储元件引用改变时触发 }
  272. property OnLanguageChanged;
  273. {* 当前语言发生改变时触发 }
  274. property OnTranslateObject;
  275. {* 翻译一对象时的事件 }
  276. property OnTranslateObjectProperty;
  277. {* 翻译一对象的某个属性时的事件 }
  278. end;
  279. function CnLanguageManager: TCnCustomLangManager;
  280. {* 全局函数,用于返回多语言管理器的实例 }
  281. procedure CreateLanguageManager(AOwner: TComponent = nil);
  282. {* 创建多语言管理器,用于非可视化或手工创建多语言管理器的场合 }
  283. function GetPropName(Instance: TObject; Index: Integer): WideString;
  284. {* 获得某对象的第 n 个 published 的属性名 }
  285. function GetValueByTransName(Owner: TComponent; const Name: WideString): WideString;
  286. {* 获得级联字符串的属性值 }
  287. procedure SetValueByTransName(Owner: TComponent; const Name, Value: WideString);
  288. {* 设置级联字符串的属性值 }
  289. procedure TranslateStr(var SrcStr: string; const IDStr: string); overload;
  290. procedure TranslateStr(var SrcStr: WideString; const IDStr: WideString); overload;
  291. {* 翻译某个字符串,如无翻译管理器或不存在翻译后的条目,则 SrcStr 保持不变 }
  292. procedure TranslateStrArray(var StrArray: array of string; const IDStr: string);
  293. procedure TranslateWideStrArray(var StrArray: array of WideString; const IDStr: WideString);
  294. {* 翻译某个字符串数组 }
  295. procedure RegisterTranslateString(const StringAddr: PAnsiString; const IDStr: WideString); overload;
  296. procedure RegisterTranslateStringW(const StringAddr: PWideString; const IDStr: WideString); overload;
  297. {* 注册一字符串,传入地址与名称,可在语言改变时被自动翻译,无需手工调 Translate}
  298. procedure RegisterTranslateResourceString(const ResStringAddr: Pointer; const IDStr: WideString);
  299. {* 注册一资源字符串,传入地址与名称,可在语言改变时被自动翻译}
  300. procedure TranslateReggedStrings;
  301. {* 翻译注册了的字符串与资源字符串,多语管理器的语言改变后会自动调用
  302. 这里开放出来给需要手工调用的场合,如初始化了多语管理器但未改变语言的场合}
  303. implementation
  304. uses
  305. {$IFDEF DEBUG}
  306. CnDebug,
  307. {$ENDIF DEBUG}
  308. CnLangConsts;
  309. type
  310. TCnIterateByTransName = (itGet, itSet);
  311. var
  312. FLangMgrList: TList = nil;
  313. FRegStrings: TObjectList;
  314. FRegResStrings: TObjectList;
  315. // 使用所有多语管理器实例中的第一个作为全局返回的实例
  316. function CnLanguageManager: TCnCustomLangManager;
  317. var
  318. I: Integer;
  319. begin
  320. Result := nil;
  321. if (FLangMgrList <> nil) and (FLangMgrList.Count > 0) then
  322. for I := 0 to FLangMgrList.Count - 1 do
  323. if TObject(FLangMgrList.Items[I]) is TCnCustomLangManager then
  324. begin
  325. Result := TObject(FLangMgrList.Items[I]) as TCnCustomLangManager;
  326. Exit;
  327. end;
  328. end;
  329. procedure CreateLanguageManager(AOwner: TComponent);
  330. begin
  331. if CnLanguageManager = nil then
  332. TCnLangManager.Create(AOwner);
  333. end;
  334. procedure TranslateStr(var SrcStr: string; const IDStr: string);
  335. var
  336. DstStr: WideString;
  337. begin
  338. if CnLanguageManager <> nil then
  339. begin
  340. DstStr := CnLanguageManager.Translate(IDStr);
  341. if DstStr <> '' then
  342. SrcStr := DstStr;
  343. end;
  344. end;
  345. procedure TranslateStr(var SrcStr: WideString; const IDStr: WideString);
  346. var
  347. DstStr: WideString;
  348. begin
  349. if CnLanguageManager <> nil then
  350. begin
  351. DstStr := CnLanguageManager.Translate(IDStr);
  352. if DstStr <> '' then
  353. SrcStr := DstStr;
  354. end;
  355. end;
  356. procedure TranslateStrArray(var StrArray: array of string; const IDStr: string);
  357. var
  358. I: Integer;
  359. DstStr: WideString;
  360. begin
  361. if CnLanguageManager <> nil then
  362. begin
  363. for I := Low(StrArray) to High(StrArray) do
  364. begin
  365. DstStr := CnLanguageManager.Translate(IDStr + IntToStr(I));
  366. if DstStr <> '' then
  367. StrArray[I] := DstStr;
  368. end;
  369. end;
  370. end;
  371. procedure TranslateWideStrArray(var StrArray: array of WideString; const IDStr: WideString);
  372. var
  373. I: Integer;
  374. DstStr: WideString;
  375. begin
  376. if CnLanguageManager <> nil then
  377. begin
  378. for I := Low(StrArray) to High(StrArray) do
  379. begin
  380. DstStr := CnLanguageManager.Translate(IDStr + IntToStr(I));
  381. if DstStr <> '' then
  382. StrArray[I] := DstStr;
  383. end;
  384. end;
  385. end;
  386. //==============================================================================
  387. // TCnBaseLangManager
  388. //==============================================================================
  389. procedure TCnBaseLangManager.AdjustNewLanguage(AID: LongWord);
  390. var
  391. i: Integer;
  392. begin
  393. if AID = 0 then
  394. AID := GetSystemDefaultLangID;
  395. if Assigned(FLanguageStorage) then
  396. for i := 0 to FLanguageStorage.LanguageCount - 1 do
  397. if FLanguageStorage.Languages.Items[i].LanguageID = AID then
  398. begin
  399. CurrentLanguageIndex := i;
  400. Exit;
  401. end;
  402. end;
  403. constructor TCnBaseLangManager.Create(AOwner: TComponent);
  404. var
  405. i: Integer;
  406. begin
  407. inherited;
  408. if FLangMgrList = nil then
  409. FLangMgrList := TList.Create;
  410. FLangMgrList.Add(Self);
  411. FDefaultLanguageIndex := -1;
  412. FCurrentLanguageIndex := -1;
  413. FAutoTranslateStrings := True;
  414. if (csDesigning in ComponentState) then
  415. for I := 0 to AOwner.ComponentCount - 1 do
  416. if AOwner.Components[i] is TCnCustomLangFileStorage then
  417. begin
  418. LanguageStorage := AOwner.Components[i] as TCnCustomLangFileStorage;
  419. Exit;
  420. end;
  421. end;
  422. destructor TCnBaseLangManager.Destroy;
  423. begin
  424. FLangMgrList.Remove(Self);
  425. inherited;
  426. end;
  427. procedure TCnBaseLangManager.DoLanguageChanged;
  428. begin
  429. if FAutoTranslateStrings then
  430. TranslateReggedStrings;
  431. if Assigned(FOnLanguageChanged) then
  432. FOnLanguageChanged(Self);
  433. end;
  434. procedure TCnBaseLangManager.DoStorageChanged;
  435. begin
  436. if Assigned(FOnStorageChanged) then
  437. FOnStorageChanged(Self);
  438. end;
  439. function TCnBaseLangManager.GetCurrentLanguageIndex: Integer;
  440. begin
  441. Result := FCurrentLanguageIndex;
  442. end;
  443. procedure TCnBaseLangManager.Notification(AComponent: TComponent;
  444. Operation: TOperation);
  445. begin
  446. inherited Notification(aComponent, Operation);
  447. if (Operation = opRemove) and (AComponent = FLanguageStorage) then
  448. FLanguageStorage := nil;
  449. end;
  450. procedure TCnBaseLangManager.SetCurrentLanguageIndex(
  451. const Value: Integer);
  452. begin
  453. FCurrentLanguageIndex := Value;
  454. if Assigned(FLanguageStorage) then
  455. if (Value >= 0) and (Value < FLanguageStorage.LanguageCount) then
  456. begin
  457. FLanguageStorage.CurrentLanguageIndex := Value;
  458. DoLanguageChanged;
  459. end;
  460. end;
  461. procedure TCnBaseLangManager.SetLanguageStorage(Value:
  462. TCnCustomLangStorage);
  463. var
  464. AID: LongWord;
  465. begin
  466. if Value <> FLanguageStorage then
  467. begin
  468. if Assigned(FLanguageStorage) then
  469. FLanguageStorage.RemoveFreeNotification(Self);
  470. FLanguageStorage := Value;
  471. if (Value <> nil) and (FCurrentLanguageIndex <> -1) then
  472. if FCurrentLanguageIndex <> FLanguageStorage.CurrentLanguageIndex then
  473. FLanguageStorage.CurrentLanguageIndex := FCurrentLanguageIndex;
  474. if Assigned(Value) then
  475. Value.FreeNotification(Self);
  476. if FLanguageStorage.CurrentLanguage <> nil then
  477. begin
  478. AID := FLanguageStorage.CurrentLanguage.LanguageID;
  479. AdjustNewLanguage(AID);
  480. end;
  481. DoStorageChanged;
  482. end;
  483. end;
  484. function TCnBaseLangManager.Translate(Src: WideString): WideString;
  485. begin
  486. Result := TranslateString(Src);
  487. end;
  488. function TCnBaseLangManager.TranslateString(Src: WideString): WideString;
  489. begin
  490. if FLanguageStorage <> nil then
  491. begin
  492. if CurrentLanguageIndex <> FLanguageStorage.CurrentLanguageIndex then
  493. FLanguageStorage.CurrentLanguageIndex := CurrentLanguageIndex;
  494. FLanguageStorage.GetString(Src, Result);
  495. if Assigned(FOnTranslateString) then
  496. FOnTranslateString(Self, Src, Result);
  497. end
  498. else
  499. Result := '';
  500. end;
  501. function TCnBaseLangManager.TranslateStrFmt(Src: WideString; Args:
  502. array of const): WideString;
  503. begin
  504. {$IFDEF COMPILER6_UP}
  505. Result := WideFormat(Translate(Src), Args);
  506. {$ELSE}
  507. // todo: D5 doesn't support WideFormat
  508. Result := Format(Translate(Src), Args);
  509. {$ENDIF}
  510. end;
  511. function GetPropName(Instance: TObject; Index: Integer): WideString;
  512. var
  513. PropList: PPropList;
  514. PropInfo: PPropInfo;
  515. Data: PTypeData;
  516. begin
  517. Result := '';
  518. Data := GetTypeData(Instance.Classinfo);
  519. GetMem(PropList, Data^.PropCount * Sizeof(PPropInfo));
  520. try
  521. GetPropInfos(Instance.ClassInfo, PropList);
  522. PropInfo := PropList^[Index];
  523. Result := PropInfoName(PropInfo);
  524. finally
  525. FreeMem(PropList, Data^.PropCount * Sizeof(PPropInfo));
  526. end;
  527. end;
  528. function IterateTransName(Owner: TComponent; const Name, Value: WideString;
  529. Mode: TCnIterateByTransName): WideString;
  530. var
  531. S, R, P, Q, Prefix, SubFix: WideString;
  532. OutS: string;
  533. I, J, K, OutN: Integer;
  534. AObject: TObject;
  535. begin
  536. Result := '';
  537. if Owner = nil then Exit;
  538. I := Pos(DefDelimeter, Name);
  539. if I > 0 then // I 是第一点位置
  540. begin
  541. S := Copy(Name, 1, I - 1);
  542. if S = Owner.ClassName then
  543. begin
  544. R := Copy(Name, I + 1, Length(Name) - I); // R 是第一点后的字串
  545. J := Pos(DefDelimeter, R);
  546. if J > 0 then // J 是第二点位置
  547. begin
  548. P := Copy(R, 1, J - 1); // P 此时是第一和第二点中间的字串
  549. if Owner.FindComponent(P) <> nil then // 子控件的属性优先
  550. begin
  551. Result := VartoStr(GetPropValueIncludeSub(Owner.FindComponent(P),
  552. Copy(R, J + 1, Length(R) - J)));
  553. if Mode = itSet then
  554. SetPropValueIncludeSub(Owner.FindComponent(P), Copy(R, J + 1, Length(R) - J), Value);
  555. end // 然后才是属性的属性
  556. else
  557. begin
  558. Result := VartoStr(GetPropValueIncludeSub(Owner, Copy(Name, I + 1, Length(Name) - I)));
  559. if Mode = itSet then
  560. SetPropValueIncludeSub(Owner, Copy(Name, I + 1, Length(Name) - I), Value);
  561. end;
  562. if Result = '' then
  563. begin
  564. // 处理 Item0 之类的情况。
  565. K := 1;
  566. while (CharPosWithCounter(DefDelimeter, R, K) <> 0) and
  567. (CharPosWithCounter(DefDelimeter, R, K + 1) <> 0) do
  568. begin
  569. Q := Copy(R, CharPosWithCounter(DefDelimeter, R, K) + 1,
  570. CharPosWithCounter(DefDelimeter, R, K + 1) - CharPosWithCounter(DefDelimeter, R, K) - 1);
  571. SeparateStrAndNum(Q, OutS, OutN);
  572. if (OutN = -1) or ((OutS <> 'Item') and (OutS <> 'ListItem')
  573. and (OutS <> 'TreeNode')) then
  574. begin
  575. Inc(K);
  576. Continue;
  577. end;
  578. Prefix := Copy(R, 1, CharPosWithCounter(DefDelimeter, R, K) - 1);
  579. Subfix := Copy(R, CharPosWithCounter(DefDelimeter, R, K + 1) + 1,
  580. Length(R) - CharPosWithCounter(DefDelimeter, R, K + 1));
  581. // Prefix 是 Listview1 形式的字符串
  582. AObject := Owner.FindComponent(P); // 先找到子控件,可以直接是 ListView1
  583. try
  584. if Prefix <> P then // 说明 Prefix 层数多
  585. AObject := TObject(Integer(GetPropValueIncludeSub(AObject,
  586. Copy(Prefix, CharPosWithCounter(DefDelimeter, R) + 1,
  587. Length(Prefix) - CharPosWithCounter(DefDelimeter, R)))));
  588. except
  589. Inc(K);
  590. Continue;
  591. end;
  592. if AObject = nil then // 找到待处理Item0的该对象
  593. begin
  594. Inc(K);
  595. Continue;
  596. end;
  597. if (AObject is TCollection) and (OutS = 'Item') then
  598. begin
  599. if OutN < (AObject as TCollection).Count then
  600. begin
  601. if Mode = itGet then
  602. Result := VartoStr(GetPropValueIncludeSub((AObject as TCollection).
  603. Items[OutN], Subfix));
  604. if Mode = itSet then
  605. SetPropValueIncludeSub((AObject as TCollection).Items[OutN],
  606. Subfix, Value);
  607. end;
  608. end
  609. else if (AObject is TListView) and (OutS = 'ListItem') then
  610. begin
  611. if OutN < (AObject as TListView).Items.Count then
  612. begin
  613. if Subfix = 'Caption' then // ListItem 的 Caption 属性并非 published
  614. begin
  615. if Mode = itGet then
  616. Result := (AObject as TListView).Items[OutN].Caption;
  617. if Mode = itSet then
  618. (AObject as TListView).Items[OutN].Caption := Value;
  619. end
  620. else // 可无必要,因为 TListItem 无 published 属性
  621. begin
  622. if Mode = itGet then
  623. Result := VartoStr(GetPropValueIncludeSub((AObject as TListView).
  624. Items[OutN], Subfix));
  625. if Mode = itSet then
  626. SetPropValueIncludeSub((AObject as TListView).Items[OutN],
  627. Subfix, Value);
  628. end;
  629. end;
  630. end
  631. else if (AObject is TTreeView) and (OutS = 'TreeNode') then
  632. begin
  633. if OutN < (AObject as TTreeView).Items.Count then
  634. begin
  635. if (Subfix = 'Text') then // TreeNode 的 Text 属性并非 published
  636. begin
  637. if Mode = itGet then
  638. Result := (AObject as TTreeView).Items[OutN].Text;
  639. if Mode = itSet then
  640. (AObject as TTreeView).Items[OutN].Text := Value;
  641. end
  642. else // 可无必要,因为 TTreeNode 无 published 属性
  643. begin
  644. if Mode = itGet then
  645. Result := VartoStr(GetPropValueIncludeSub((AObject as TTreeView).
  646. Items[OutN], Subfix));
  647. if Mode = itSet then
  648. SetPropValueIncludeSub((AObject as TTreeView).Items[OutN],
  649. Subfix, Value);
  650. end;
  651. end;
  652. end;
  653. Inc(K);
  654. end;
  655. end;
  656. end
  657. else // 直接是属性
  658. begin
  659. if Mode = itGet then
  660. Result := VartoStr(GetPropValueIncludeSub(Owner, Copy(Name, I + 1, Length(Name) - I)));
  661. if Mode = itSet then
  662. SetPropValueIncludeSub(Owner, Copy(Name, I + 1, Length(Name) - I), Value);
  663. end;
  664. end
  665. else if (S = 'Application') and (Owner = Application) then
  666. begin
  667. if Mode = itGet then
  668. Result := VartoStr(GetPropValueIncludeSub(Application,
  669. Copy(Name, I + 1, Length(Name) - I)));
  670. if Mode = itSet then
  671. SetPropValueIncludeSub(Application, Copy(Name, I + 1, Length(Name) - I), Value);
  672. end
  673. else
  674. begin
  675. if Mode = itGet then
  676. Result := VartoStr(GetPropValueIncludeSub(Owner.FindComponent(S),
  677. Copy(Name, I + 1, Length(Name) - I)));
  678. if Mode = itSet then
  679. SetPropValueIncludeSub(Owner.FindComponent(S), Copy(Name, I + 1, Length(Name) - I), Value);
  680. end;
  681. end;
  682. end;
  683. function GetValueByTransName(Owner: TComponent; const Name: WideString): WideString;
  684. begin
  685. Result := IterateTransName(Owner, Name, '', itGet);
  686. end;
  687. procedure SetValueByTransName(Owner: TComponent; const Name, Value: WideString);
  688. begin
  689. IterateTransName(Owner, Name, Value, itSet);
  690. end;
  691. //==============================================================================
  692. // TCnCustomLangManager
  693. //==============================================================================
  694. constructor TCnCustomLangManager.Create;
  695. begin
  696. inherited;
  697. FNotifier := TList.Create;
  698. FAutoTranslate := True;
  699. FAutoTransOptions := [atApplication, atForms, atDataModules];
  700. FUseDefaultFont := True;
  701. FTranslateOtherFont := True;
  702. FTranslateListItem := False;
  703. FTranslateTreeNode := False;
  704. FIgnoreAction := True;
  705. end;
  706. destructor TCnCustomLangManager.Destroy;
  707. var
  708. i: Integer;
  709. P: Pointer;
  710. begin
  711. for i := 0 to FNotifier.Count - 1 do
  712. begin
  713. P := FNotifier[i];
  714. Dispose(P);
  715. end;
  716. FreeAndNil(FNotifier);
  717. FreeAndNil(FOldTransForms);
  718. FreeAndNil(FOldTransDMs);
  719. inherited Destroy;
  720. end;
  721. procedure TCnCustomLangManager.TranslateComponent(AComponent: TComponent;
  722. const BaseName: WideString);
  723. var
  724. List: TList;
  725. ABaseName, Prefix: WideString;
  726. Iterator: ICnLangStringIterator;
  727. AKey, AValue: WideString;
  728. APos: Integer;
  729. begin
  730. if (AComponent <> nil) and (AComponent.Tag = CN_MULTI_LANG_TAG_NOT_TRANSLATE) then
  731. Exit;
  732. ABaseName := BaseName;
  733. if ABaseName = '' then
  734. ABaseName := GetRecurOwner(AComponent);
  735. if FTranslationMode = tmByComponents then
  736. begin
  737. List := TList.Create;
  738. List.Add(AComponent); // 必须加入自身,防止被子控件引用而重复翻译
  739. try
  740. if AComponent.ComponentCount > 0 then
  741. TranslateRecurComponent(AComponent, List, ABaseName)
  742. else
  743. TranslateRecurObject(AComponent, List, ABaseName);
  744. finally
  745. List.Free;
  746. end;
  747. end
  748. else
  749. begin
  750. Iterator := FLanguageStorage.CreateIterator;
  751. if Iterator <> nil then
  752. begin
  753. APos := Pos(DefDelimeter, ABaseName);
  754. if APos > 0 then
  755. Prefix := Copy(ABaseName, 1, APos - 1)
  756. else
  757. Prefix := ABaseName;
  758. Iterator.StartIterate(Prefix);
  759. try
  760. while not Iterator.Eof do
  761. begin
  762. Iterator.GetCurrentKeyValue(AKey, AValue);
  763. TranslateKeyToValue(AKey, AValue);
  764. Iterator.Next;
  765. end;
  766. finally
  767. Iterator.EndIterate;
  768. end;
  769. end;
  770. end;
  771. end;
  772. procedure TCnCustomLangManager.TranslateRecurComponent(
  773. AComponent: TComponent; AList: TList; const BaseName: WideString);
  774. var
  775. I: Integer;
  776. T: TComponent;
  777. IsInList, IsApplication: Boolean;
  778. begin
  779. {$IFDEF DEBUG}
  780. CnDebugger.LogEnter('TranslateRecurComponent: ' + BaseName + ' ' + AComponent.Name);
  781. {$ENDIF DEBUG}
  782. IsApplication := AComponent is TApplication;
  783. if AComponent <> nil then
  784. begin
  785. if AComponent.Tag = CN_MULTI_LANG_TAG_NOT_TRANSLATE then
  786. Exit;
  787. TranslateObject(AComponent, BaseName);
  788. // 使用 AList 避免子属性和父 Component 重复
  789. for I := 0 to AComponent.ComponentCount - 1 do
  790. begin
  791. T := AComponent.Components[I];
  792. if IsApplication and (T is TCustomForm) then
  793. Continue; // 不翻译 Application 的下属 Form,留给 TranslateForm 等来处理
  794. if T.Tag = CN_MULTI_LANG_TAG_NOT_TRANSLATE then
  795. Continue;
  796. IsInList := AList <> nil;
  797. if IsInList and (AList.IndexOf(T) = -1) then
  798. begin
  799. IsInList := False;
  800. AList.Add(T);
  801. end; // 列表为 nil 时不判断,不为 nil 时检测是否已包含
  802. if not IsInList then // 不处理某一 Form 有 Parent 的情况。2004.06.01 by Passion
  803. begin
  804. if (AComponent is TCustomForm) {and ((AComponent as TCustomForm).Parent = nil)} then
  805. TranslateRecurComponent(T, AList, BaseName)
  806. else
  807. TranslateRecurComponent(T, AList, BaseName + DefDelimeter + AComponent.Name);
  808. end;
  809. end;
  810. end;
  811. {$IFDEF DEBUG}
  812. CnDebugger.LogLeave('TranslateRecurComponent: ' + BaseName + ' ' + AComponent.Name);
  813. {$ENDIF DEBUG}
  814. end;
  815. procedure TCnCustomLangManager.TranslateForm(AForm: TCustomForm);
  816. begin
  817. LockWindowUpdate(AForm.Handle);
  818. try
  819. if FUseDefaultFont and Assigned(FLanguageStorage) then
  820. begin
  821. with FLanguageStorage do
  822. begin
  823. if FontInited then
  824. begin
  825. {$IFDEF DEBUG}
  826. CnDebugger.LogMsg('LangManager: FontInited. ');
  827. {$ENDIF DEBUG}
  828. if CurrentLanguageIndex <> -1 then
  829. begin
  830. AForm.Font.Name := DefaultFont.Name;
  831. AForm.Font.Size := DefaultFont.Size;
  832. AForm.Font.Charset := DefaultFont.Charset;
  833. end;
  834. end;
  835. end;
  836. end;
  837. TranslateComponent(AForm, AForm.ClassName);
  838. finally
  839. LockWindowUpdate(0);
  840. end;
  841. end;
  842. procedure TCnCustomLangManager.TranslateObject(AObject: TObject;
  843. const BaseName: WideString = '');
  844. var
  845. AList: TList;
  846. begin
  847. {$IFDEF DEBUG}
  848. CnDebugger.LogEnter('TranslateObject: ' + BaseName + ' ' + AObject.ClassName);
  849. {$ENDIF DEBUG}
  850. AList := TList.Create;
  851. AList.Add(AObject); // 必须加入自身来防止被子属性引用而重复翻译
  852. try
  853. if DoTranslateObject(AObject) then
  854. TranslateRecurObject(AObject, AList, BaseName);
  855. finally
  856. AList.Free;
  857. end;
  858. {$IFDEF DEBUG}
  859. CnDebugger.LogLeave('TranslateObject: ' + BaseName + ' ' + AObject.ClassName);
  860. {$ENDIF DEBUG}
  861. end;
  862. procedure TCnCustomLangManager.TranslateRecurObject(AObject: TObject;
  863. AList: TList; const BaseName: WideString);
  864. var
  865. i: Integer;
  866. APropName, APropValue, TransStr, AStr: WideString;
  867. APropType: TTypeKind;
  868. Data: PTypeData;
  869. ActionObj, SubObj: TObject;
  870. AItem: TCollectionItem;
  871. AListItem: TListItem;
  872. ATreeNode: TTreeNode;
  873. IsForm, IsInList: Boolean;
  874. NeedIgnoreAction: Boolean;
  875. ActionCaption, ActionHint: WideString;
  876. Info: PPropInfo;
  877. begin
  878. if (AObject <> nil) {and (AList <> nil)} and Assigned(FLanguageStorage) then
  879. begin
  880. // 避免传入一些野了的 AObject 导致死循环,曾在 IDE 内部出现过
  881. try
  882. if AObject.ClassType = AObject.ClassParent then
  883. Exit;
  884. if (AObject.ClassParent <> nil) and (AObject.ClassParent.ClassParent = AObject.ClassType) then
  885. Exit;
  886. except
  887. Exit;
  888. end;
  889. if (AObject is TCnCustomLangStorage) or (AObject is TCnCustomLangStorage)
  890. or ((AObject is TComponent) and ((AObject as TComponent).Name = '')) then
  891. Exit;
  892. if (AObject is TStrings) then // Strings的对象直接翻译 Text 属性。
  893. begin
  894. AStr := 'Text';
  895. // 调用翻译某属性前的事件
  896. if not DoTranslateObjectProperty(AObject, AStr) then
  897. Exit;
  898. if BaseName <> '' then
  899. AStr := BaseName + DefDelimeter + AStr;
  900. TransStr := TranslateString(AStr);
  901. if TransStr <> '' then
  902. (AObject as TStrings).Text := TransStr;
  903. Exit;
  904. end
  905. else if (AObject is TCollection) then // TCollection 对象遍历其 Item
  906. begin
  907. for i := 0 to (AObject as TCollection).Count - 1 do
  908. begin
  909. AItem := (AObject as TCollection).Items[i];
  910. IsInList := AList <> nil;
  911. if IsInList and (AList.IndexOf(AItem) = -1) then
  912. begin
  913. IsInList := False;
  914. AList.Add(AItem);
  915. end;
  916. if not IsInList then
  917. begin
  918. if BaseName <> '' then
  919. TranslateRecurObject(AItem, AList, BaseName + DefDelimeter +
  920. 'Item' + InttoStr(i))
  921. else
  922. TranslateRecurObject(AItem, AList, 'Item' + InttoStr(i));
  923. end;
  924. end;
  925. end
  926. // ListView 在需要时遍历其 Item
  927. else if FTranslateListItem and (AObject is TListView) then
  928. begin
  929. for i := 0 to (AObject as TListView).Items.Count - 1 do
  930. begin
  931. AListItem := (AObject as TListView).Items[i];
  932. IsInList := AList <> nil;
  933. if IsInList and (AList.IndexOf(AListItem) = -1) then
  934. begin
  935. IsInList := False;
  936. AList.Add(AListItem);
  937. end;
  938. if not IsInList then
  939. begin
  940. if BaseName <> '' then
  941. TranslateRecurObject(AListItem, AList, BaseName + DefDelimeter +
  942. TComponent(AObject).Name + DefDelimeter + 'ListItem' + InttoStr(i))
  943. else
  944. TranslateRecurObject(AListItem, AList, TComponent(AObject).Name +
  945. DefDelimeter + 'ListItem' + InttoStr(i));
  946. end;
  947. end;
  948. end
  949. // ListItem 翻译其 Caption 属性和 SubItems 属性
  950. else if FTranslateListItem and (AObject is TListItem) then
  951. begin
  952. AStr := 'Caption';
  953. // 调用翻译某属性前的事件
  954. if DoTranslateObjectProperty(AObject, AStr) then
  955. begin
  956. if BaseName <> '' then
  957. AStr := BaseName + DefDelimeter + AStr;
  958. TransStr := TranslateString(AStr);
  959. if TransStr <> '' then
  960. (AObject as TListItem).Caption := TransStr;
  961. end;
  962. AStr := 'SubItems.Text';
  963. if BaseName <> '' then
  964. AStr := BaseName + DefDelimeter + AStr;
  965. TransStr := TranslateString(AStr);
  966. if TransStr <> '' then
  967. (AObject as TListItem).SubItems.Text := TransStr;
  968. Exit;
  969. end
  970. // TreeView 在需要时遍历其 Item
  971. else if FTranslateTreeNode and (AObject is TTreeView) then
  972. begin
  973. for i := 0 to (AObject as TTreeView).Items.Count - 1 do
  974. begin
  975. ATreeNode := (AObject as TTreeView).Items[i];
  976. IsInList := AList <> nil;
  977. if IsInList and (AList.IndexOf(ATreeNode) = -1) then
  978. begin
  979. IsInList := False;
  980. AList.Add(ATreeNode);
  981. end;
  982. if not IsInList then
  983. begin
  984. if BaseName <> '' then
  985. TranslateRecurObject(ATreeNode, AList, BaseName + DefDelimeter +
  986. TComponent(AObject).Name + DefDelimeter + 'TreeNode' + InttoStr(i))
  987. else
  988. TranslateRecurObject(ATreeNode, AList, TComponent(AObject).Name +
  989. DefDelimeter + 'TreeNode' + InttoStr(i));
  990. end;
  991. end;
  992. end
  993. // TreeNode 翻译其 Text 属性。
  994. else if FTranslateTreeNode and (AObject is TTreeNode) then
  995. begin
  996. AStr := 'Text';
  997. // 调用翻译某属性前的事件
  998. if not DoTranslateObjectProperty(AObject, AStr) then
  999. Exit;
  1000. if BaseName <> '' then
  1001. AStr := BaseName + DefDelimeter + AStr;
  1002. TransStr := TranslateString(AStr);
  1003. if TransStr <> '' then
  1004. (AObject as TTreeNode).Text := TransStr;
  1005. Exit;
  1006. end;
  1007. IsForm := (AObject is TCustomForm) or (AObject is TCustomFrame);
  1008. try
  1009. Data := GetTypeData(AObject.Classinfo);
  1010. except
  1011. Exit; // TChartSeriesList 会在此处出错,不得不抓住屏蔽
  1012. end;
  1013. NeedIgnoreAction := False;
  1014. if FIgnoreAction then
  1015. begin
  1016. // 查找是否有 Action 属性,看是否 nil
  1017. for I := 0 to Data^.PropCount - 1 do
  1018. begin
  1019. APropName := GetPropName(AObject, I);
  1020. if (PropType(AObject, APropName) = tkClass) and (APropName = 'Action') then
  1021. begin
  1022. // 存在 Action 属性,为tkClass
  1023. ActionObj := GetObjectProp(AObject, APropName);
  1024. if (ActionObj <> nil) and (ActionObj is TCustomAction)then
  1025. begin
  1026. // 有 Action 属性不为 nil 的,需要忽略 Caption 和 Hint
  1027. NeedIgnoreAction := True;
  1028. ActionCaption := (ActionObj as TCustomAction).Caption;
  1029. ActionHint := (ActionObj as TCustomAction).Hint;
  1030. Break;
  1031. end;
  1032. end;
  1033. end;
  1034. end;
  1035. for I := 0 to Data^.PropCount - 1 do
  1036. begin
  1037. APropName := GetPropName(AObject, I);
  1038. // 不翻译 TComponent 的 Name 属性
  1039. if (AObject is TComponent) and (APropName = 'Name') then
  1040. Continue;
  1041. // 不翻译 TCnComponent 的 About 属性
  1042. if (AObject is TCnComponent) and (APropName = 'About') then
  1043. Continue;
  1044. APropType := PropType(AObject, APropName);
  1045. if (APropType in [tkString, tkLString, tkWString //, tkWChar
  1046. {$IFDEF UNICODE_STRING}, tkUString{$ENDIF}]) then
  1047. begin
  1048. if NeedIgnoreAction then
  1049. begin
  1050. APropValue := VartoStr(GetPropValue(AObject, APropName));
  1051. if (APropName = 'Caption') and (ActionCaption = APropValue) then
  1052. Continue
  1053. else if (APropName = 'Hint') and (ActionHint = APropValue) then
  1054. Continue;
  1055. end;
  1056. Info := GetPropInfo(AObject, APropName);
  1057. if (Info <> nil) and (Info^.SetProc = nil) then // 只读不能写的,躲开
  1058. Continue;
  1059. // 调用翻译某属性前的事件
  1060. if not DoTranslateObjectProperty(AObject, APropName) then
  1061. Continue;
  1062. if IsForm then
  1063. AStr := AObject.ClassName + DefDelimeter + APropName
  1064. else if AObject is TComponent then
  1065. AStr := TComponent(AObject).Name + DefDelimeter + APropName
  1066. else
  1067. AStr := APropName;
  1068. if (BaseName <> '') and not IsForm then
  1069. AStr := BaseName + DefDelimeter + AStr;
  1070. TransStr := TranslateString(AStr);
  1071. if TransStr <> '' then
  1072. SetPropValue(AObject, APropName, TransStr);
  1073. end
  1074. else if APropType = tkClass then
  1075. begin
  1076. SubObj := GetObjectProp(AObject, APropName);
  1077. if SubObj = nil then
  1078. Continue;
  1079. IsInList := AList <> nil;
  1080. if IsInList and (AList.IndexOf(SubObj) = -1) then
  1081. begin
  1082. IsInList := False;
  1083. AList.Add(SubObj);
  1084. end;
  1085. // 调用翻译某属性前的事件
  1086. if not DoTranslateObjectProperty(AObject, APropName) then
  1087. Continue;
  1088. if AObject is TComponent then // 是 Component 则进行复杂的处理
  1089. begin
  1090. if not IsInList then
  1091. begin
  1092. {* 是子对象不是引用或 Owner 不是任何控件的控件,因 Owner 循环的方式访问不到,
  1093. 便只有在此处以主控件名.属性名的形式访问。但如果出现这样的情况:一个控件
  1094. 有两个属性,连接到两个子控件,这俩子控件的 Owner 都是 nil,但由父控件负
  1095. 责创建释放。这两个子控件都有一个属性指向对方,这样就会出现循环引用,所以
  1096. 还是得通过 List 方式来避免死循环。 }
  1097. if (AObject is TControl) and (SubObj is TFont) and (APropName = 'Font') then
  1098. begin
  1099. if not IsParentFont(AObject as TControl) then // 不使用 ParentFont 时存字体
  1100. begin
  1101. if not IsForm then
  1102. AStr := TComponent(AObject).Name + DefDelimeter + SCnControlFont
  1103. else
  1104. AStr := SCnControlFont;
  1105. if BaseName <> '' then
  1106. AStr := BaseName + DefDelimeter + AStr;
  1107. TransStr := TranslateString(AStr);
  1108. if TransStr <> '' then
  1109. StringToFontEx(TransStr, TCnFontControl(AObject).Font,
  1110. GetParentFont(AObject as TComponent));
  1111. end;
  1112. end // 不按常规处理 TControl 的字体
  1113. else if FTranslateOtherFont and (SubObj is TFont) then
  1114. begin
  1115. if not IsForm then
  1116. AStr := TComponent(AObject).Name + DefDelimeter +
  1117. SystemNamePrefix + APropName
  1118. else
  1119. AStr := SystemNamePrefix + APropName;
  1120. if BaseName <> '' then
  1121. AStr := BaseName + DefDelimeter + AStr;
  1122. TransStr := TranslateString(AStr);
  1123. try
  1124. if TransStr <> '' then
  1125. StringToFontEx(TransStr, SubObj as TFont,
  1126. GetParentFont(AObject as TComponent));
  1127. except
  1128. ; // 屏蔽万一碰上的异常
  1129. end;
  1130. end // 处理其他 Font。
  1131. else if (not (SubObj is TComponent)) or // 如果 SubObj 不是 TComponent 则只能在此通过属性遍历
  1132. ((SubObj as TComponent).Owner = nil) then // 如果 SubObj 的 Owner 不为 nil,则等它的 Owner 遍历下来再说,此处不处理。
  1133. begin
  1134. if IsForm then
  1135. TranslateRecurObject(SubObj, AList, TComponent(AObject).ClassName
  1136. + DefDelimeter + APropName)
  1137. else if (InheritsFromClassName(AObject, 'TNotebook') or InheritsFromClassName(AObject, 'TTabbedNotebook'))
  1138. and (APropName = 'Pages') then
  1139. // 不翻译 TNotebook/TTabbedNotebook 的 Pages 属性以免出现页面内容丢失。
  1140. else if InheritsFromClassName(AObject, 'TJvWizard') and (APropName = 'Pages') then
  1141. // 不翻译 JVcl Wizards 的 Pages 属性以免 Crash
  1142. else if not (SubObj is TComponent) then
  1143. // 此处应该判断 SubObj 是否是 TComponet 然后决定是否调用 TranslateRecurComponent
  1144. TranslateRecurObject(SubObj, AList, BaseName + DefDelimeter +
  1145. TComponent(AObject).Name + DefDelimeter + APropName)
  1146. else
  1147. TranslateRecurComponent((SubObj as TComponent), AList, BaseName + DefDelimeter +
  1148. TComponent(AObject).Name + DefDelimeter + APropName)
  1149. end;
  1150. end;
  1151. end
  1152. else // AObject 不是 Component 则直接翻译它和它的属性
  1153. begin
  1154. if not IsInList then
  1155. TranslateRecurObject(SubObj, AList, BaseName + DefDelimeter + APropName);
  1156. end;
  1157. end;
  1158. end;
  1159. end;
  1160. end;
  1161. procedure TCnCustomLangManager.SetCurrentLanguageIndex(
  1162. const Value: Integer);
  1163. var
  1164. I: Integer;
  1165. Iterator: ICnLangStringIterator;
  1166. AKey, AValue: WideString;
  1167. begin
  1168. inherited;
  1169. // 设计期不进行翻译
  1170. if not (csDesigning in ComponentState) and FAutoTranslate
  1171. and (LanguageStorage <> nil) then
  1172. begin
  1173. if FTranslationMode = tmByComponents then
  1174. begin
  1175. if atForms in FAutoTransOptions then
  1176. for I := 0 to Screen.CustomFormCount - 1 do
  1177. TranslateForm(Screen.CustomForms[I]);
  1178. if atDataModules in FAutoTransOptions then
  1179. for I := 0 to Screen.DataModuleCount - 1 do
  1180. TranslateComponent(Screen.DataModules[I]);
  1181. if atApplication in FAutoTransOptions then
  1182. TranslateComponent(Application);
  1183. end
  1184. else // 基于翻译条目
  1185. begin
  1186. Iterator := FLanguageStorage.CreateIterator;
  1187. if Iterator <> nil then
  1188. begin
  1189. Iterator.StartIterate;
  1190. try
  1191. while not Iterator.Eof do
  1192. begin
  1193. Iterator.GetCurrentKeyValue(AKey, AValue);
  1194. TranslateKeyToValue(AKey, AValue);
  1195. Iterator.Next;
  1196. end;
  1197. finally
  1198. Iterator.EndIterate;
  1199. end;
  1200. end;
  1201. end;
  1202. end;
  1203. end;
  1204. procedure TCnCustomLangManager.AddChangeNotifier(Notify: TNotifyEvent);
  1205. var
  1206. P: PCnLangChangedNotifierRecord;
  1207. I: Integer;
  1208. Found: Boolean;
  1209. begin
  1210. Found := False;
  1211. for I := 0 to FNotifier.Count - 1 do
  1212. if SameMethod(TMethod(PCnLangChangedNotifierRecord(FNotifier[I])^.Notifier),
  1213. TMethod(Notify)) then
  1214. begin
  1215. Found := True;
  1216. Break;
  1217. end;
  1218. if not Found then
  1219. begin
  1220. New(P);
  1221. P^.Notifier := TMethod(Notify);
  1222. FNotifier.Add(P);
  1223. end;
  1224. end;
  1225. procedure TCnCustomLangManager.RemoveChangeNotifier(Notify: TNotifyEvent);
  1226. var
  1227. P: PCnLangChangedNotifierRecord;
  1228. Idx, I: Integer;
  1229. begin
  1230. Idx := -1;
  1231. for I := 0 to FNotifier.Count - 1 do
  1232. if SameMethod(TMethod(PCnLangChangedNotifierRecord(FNotifier[I])^.Notifier),
  1233. TMethod(Notify)) then
  1234. begin
  1235. Idx := I;
  1236. Break;
  1237. end;
  1238. if Idx >= 0 then
  1239. begin
  1240. P := FNotifier[Idx];
  1241. Dispose(P);
  1242. FNotifier.Delete(Idx);
  1243. end;
  1244. end;
  1245. procedure TCnCustomLangManager.DoLanguageChanged;
  1246. var
  1247. I: Integer;
  1248. begin
  1249. inherited; // 先响应父类的语言改变事件,再实施通知。
  1250. for I := 0 to FNotifier.Count - 1 do
  1251. TNotifyEvent(PCnLangChangedNotifierRecord(FNotifier.Items[I])^.Notifier)(Self);
  1252. end;
  1253. function TCnCustomLangManager.DoTranslateObject(AObject: TObject): Boolean;
  1254. begin
  1255. Result := True;
  1256. if Assigned(FOnTranslateObject) then
  1257. FOnTranslateObject(AObject, Result);
  1258. end;
  1259. function TCnCustomLangManager.DoTranslateObjectProperty(AObject: TObject;
  1260. const PropName: WideString): Boolean;
  1261. begin
  1262. Result := True;
  1263. if Assigned(FOnTranslateObjectProperty) then
  1264. FOnTranslateObjectProperty(AObject, PropName, Result);
  1265. end;
  1266. function TCnCustomLangManager.GetRecurOwner(AComponent: TComponent): WideString;
  1267. begin
  1268. if (AComponent is TCustomForm) or (AComponent is TDataModule) then
  1269. Result := AComponent.ClassName
  1270. else if AComponent.Owner <> nil then
  1271. begin
  1272. if AComponent.Owner is TCustomForm then
  1273. Result := AComponent.Owner.ClassName
  1274. else
  1275. Result := GetRecurOwner(AComponent.Owner) + DefDelimeter + AComponent.Owner.Name;
  1276. end;
  1277. end;
  1278. procedure TCnCustomLangManager.SetTranslationMode(
  1279. const Value: TCnTranslationMode);
  1280. begin
  1281. FTranslationMode := Value;
  1282. end;
  1283. procedure TCnCustomLangManager.TranslateKeyToValue(const Key,
  1284. Value: WideString);
  1285. var
  1286. I, APos: Integer;
  1287. Prefix: WideString;
  1288. begin
  1289. if Key = '' then
  1290. Exit;
  1291. APos := Pos(DefDelimeter, Key);
  1292. if APos = 0 then // 不带点号的不在此翻译
  1293. Exit;
  1294. Prefix := Copy(Key, 1, APos - 1);
  1295. if atForms in FAutoTransOptions then
  1296. begin
  1297. if (Prefix <> FOldFormPrefix) or not Assigned(FOldTransForms) then
  1298. begin
  1299. if not Assigned(FOldTransForms) then
  1300. FOldTransForms := TList.Create
  1301. else
  1302. FOldTransForms.Clear;
  1303. for I := 0 to Screen.CustomFormCount - 1 do
  1304. if Screen.CustomForms[I].ClassNameIs(Prefix) then
  1305. FOldTransForms.Add(Screen.CustomForms[I]);
  1306. end;
  1307. for I := 0 to FOldTransForms.Count - 1 do
  1308. SetValueByTransName(TComponent(FOldTransForms.Items[I]), Key, Value);
  1309. end;
  1310. if atDataModules in FAutoTransOptions then
  1311. begin
  1312. if (Prefix <> FOldDMPrefix) or not Assigned(FOldTransDMs) then
  1313. begin
  1314. if not Assigned(FOldTransDMs) then
  1315. FOldTransDMs := TList.Create
  1316. else
  1317. FOldTransDMs.Clear;
  1318. for I := 0 to Screen.DataModuleCount - 1 do
  1319. if Screen.DataModules[I].ClassNameIs(Prefix) then
  1320. FOldTransDMs.Add(Screen.DataModules[I]);
  1321. end;
  1322. for I := 0 to FOldTransDMs.Count - 1 do
  1323. SetValueByTransName(TComponent(FOldTransDMs.Items[I]), Key, Value);
  1324. end;
  1325. if atApplication in FAutoTransOptions then
  1326. if Prefix = 'Application' then
  1327. SetValueByTransName(Application, Key, Value);
  1328. end;
  1329. procedure FreeLanguageManagers;
  1330. var
  1331. I: Integer;
  1332. begin
  1333. if Assigned(FLangMgrList) then
  1334. begin
  1335. if FLangMgrList.Count > 0 then
  1336. for I := FLangMgrList.Count - 1 downto 0 do
  1337. if FLangMgrList.Items[I] <> nil then
  1338. TObject(FLangMgrList.Items[I]).Free;
  1339. FreeAndNil(FLangMgrList);
  1340. end;
  1341. end;
  1342. procedure TCnBaseLangManager.GetComponentInfo(var AName, Author, Email,
  1343. Comment: string);
  1344. begin
  1345. AName := SCnLangMgrName;
  1346. Author := SCnPack_LiuXiao;
  1347. Email := SCnPack_LiuXiaoEmail;
  1348. Comment := SCnLangMgrComment;
  1349. end;
  1350. procedure RegisterTranslateResourceString(
  1351. const ResStringAddr: Pointer; const IDStr: WideString);
  1352. var
  1353. AObj: TCnResourceStringObj;
  1354. begin
  1355. if ResStringAddr <> nil then
  1356. begin
  1357. AObj := TCnResourceStringObj.Create;
  1358. AObj.StringRecAddr := ResStringAddr;
  1359. AObj.StringName := IDStr;
  1360. FRegResStrings.Add(AObj);
  1361. end;
  1362. end;
  1363. procedure RegisterTranslateString(const StringAddr: PAnsiString; const IDStr: WideString);
  1364. var
  1365. AObj: TCnStringObj;
  1366. begin
  1367. if StringAddr <> nil then
  1368. begin
  1369. AObj := TCnStringObj.Create;
  1370. AObj.StringAddr := StringAddr;
  1371. AObj.StringName := IDStr;
  1372. AObj.IsWide := False;
  1373. FRegStrings.Add(AObj);
  1374. end;
  1375. end;
  1376. procedure RegisterTranslateStringW(const StringAddr: PWideString; const IDStr: WideString);
  1377. var
  1378. AObj: TCnStringObj;
  1379. begin
  1380. if StringAddr <> nil then
  1381. begin
  1382. AObj := TCnStringObj.Create;
  1383. AObj.StringAddr := StringAddr;
  1384. AObj.StringName := IDStr;
  1385. AObj.IsWide := True;
  1386. FRegStrings.Add(AObj);
  1387. end;
  1388. end;
  1389. procedure TranslateReggedStrings;
  1390. var
  1391. I: Integer;
  1392. AObj: TCnStringObj;
  1393. BObj: TCnResourceStringObj;
  1394. DstStr: WideString;
  1395. OldProtect: Cardinal;
  1396. begin
  1397. if CnLanguageManager = nil then
  1398. raise Exception.Create('Language Manager NOT initialized.');
  1399. for I := 0 to FRegStrings.Count - 1 do
  1400. begin
  1401. AObj := TCnStringObj(FRegStrings[I]);
  1402. DstStr := CnLanguageManager.TranslateString(AObj.StringName);
  1403. if DstStr <> '' then
  1404. begin
  1405. if AObj.IsWide then
  1406. PWideString(AObj.FStringAddr)^ := DstStr
  1407. else
  1408. PAnsiString(AObj.FStringAddr)^ := AnsiString(DstStr);
  1409. end;
  1410. end;
  1411. for I := 0 to FRegResStrings.Count - 1 do
  1412. begin
  1413. BObj := TCnResourceStringObj(FRegResStrings[I]);
  1414. DstStr := CnLanguageManager.TranslateString(BObj.StringName);
  1415. if DstStr <> '' then
  1416. begin
  1417. BObj.FDstStr := DstStr; // 保存一份字符串引用
  1418. VirtualProtect(BObj.StringRecAddr, SizeOf(TResStringRec), PAGE_EXECUTE_READWRITE, @OldProtect);
  1419. {$IFDEF WIN64}
  1420. PResStringRec(BObj.StringRecAddr)^.Identifier := NativeUint(BObj.FDstStr);
  1421. {$ELSE}
  1422. PResStringRec(BObj.StringRecAddr)^.Identifier := Integer(BObj.FDstStr);
  1423. {$ENDIF}
  1424. VirtualProtect(BObj.StringRecAddr, SizeOf(TResStringRec), OldProtect, nil);
  1425. end;
  1426. end;
  1427. end;
  1428. initialization
  1429. FRegStrings := TObjectList.Create(True);
  1430. FRegResStrings := TObjectList.Create(True);
  1431. finalization
  1432. FreeLanguageManagers;
  1433. FRegStrings.Free;
  1434. FRegResStrings.Free;
  1435. end.