【问题标题】:Delphi TStringList Find method cannot find itemDelphi TStringList Find 方法找不到项目
【发布时间】:2017-06-06 22:46:39
【问题描述】:

我正在编写自己的类来管理 android/ios 应用程序的翻译,并且我已经生成了这段代码。我将解释下面的代码,但它非常简单和简短。

unit Localization;

interface

uses
 System.Classes, System.SysUtils, Generics.Collections, Vcl.Dialogs;

//this class represents a single language (Italian, French, German...)
type
 TLanguage = class
  private
   FTranslationList: TDictionary<string, string>;
   function localize(const aWordId: string): string;
  public
   constructor Create;
   destructor Destroy; override;
   //methods
   procedure addWord(const aIndex, aWord: string);
   procedure removeWord(const aIndex: string);
 end;

//this is a "container", it gathers all the languages in one place
type
 TLocalization = class
  private
   FLanguagesList: TObjectList<TLanguage>;
   FLocaleList: TStringList;
   function getLang(Index: string): TLanguage;
  public
   constructor Create;
   destructor Destroy; override;
   //methods
   function localize(const aLocaleId: string; const aIndex: string): string;
   procedure addLanguage(const localeId: string);
   //property to manage the languages
   property Locale[Index: string]: TLanguage read getLang;
   property langCount: integer read getCount;
 end;

implementation

{ TLocalization }

{ add a new language to the class. }
{the localeId is a symbol like 'it' that represents the Italian language}
procedure TLocalization.addLanguage(const localeId: string);
begin
 //add the language to the languages container
 FLanguagesList.Add(TLanguage.Create);
 //add the representation of the language. 
 FLocaleList.Add(localeId);
end;

constructor TLocalization.Create;
begin
 FLanguagesList := TObjectList<TLanguage>.Create;
 FLocaleList := TStringList.Create;
end;

destructor TLocalization.Destroy;
begin
 FLanguagesList.Free;
 FLocaleList.Free;
 inherited;
end;

//ERROR HERE   
function TLocalization.getLang(Index: string): TLanguage;
var i: integer;
begin

 { I search the locale id (for example 'it') if it's in the list. }
 { if it's in the list, I return the respective TLanguage object}
 if not( FLocaleList.Find(Index, i) ) then
  Result := FLanguagesList.Items[i]
 else
  raise Exception.Create('Locale not found');

end;

function TLocalization.localize(const aLocaleId, aIndex: string): string;
var k: integer;
begin

 k := 0;

 if not( FLocaleList.Find(aLocaleId, k) ) then
  raise Exception.Create('Locale not found.');

 //ho trovato il locale, adesso basta cercare la parola
 Result := FLanguagesList.Items[k].localize(aIndex);

end;

{ TLanguage }

procedure TLanguage.addWord(const aIndex, aWord: string);
begin
 FTranslationList.Add(aIndex, aWord);
end;

constructor TLanguage.Create;
begin
 FTranslationList := TDictionary<string, string>.Create;
end;

destructor TLanguage.Destroy;
begin
 FTranslationList.Free;
 inherited;
end;

function TLanguage.localize(const aWordId: string): string;
begin

 try
  Result := FTranslationList.Items[aWordId];
 except
  Result := 'Not found.';
 end;

end;

procedure TLanguage.removeWord(const aIndex: string);
begin
 FTranslationList.Remove(aIndex);
end;

end.

上面的代码使用如下:

var a: TLocalization;
begin
  a := TLocalization.Create;

  a.addLanguage('it');
  a.addLanguage('cse');
  a.Locale['it'].addWord('test', 'Ciao mondo!');
  a.Locale['cse'].addWord('test', 'fadfa ea!');

  ButtonSomething.Text := a.localize('it', test);

end;

TLocalization 类完成所有工作。如您所见,我创建了变量a,然后向类添加了一种语言(这是在内部使用字典/字符串列表进行管理的)。

我可以使用Locale[Index: string] 属性访问我添加的语言,该属性返回一个TLanguage,我用一个类来指示单个语言。最后使用 localize 方法,我得到了我的翻译。


奇怪的是,我总是收到错误'Locale not found'。任何的想法?使用调试器我发现了这一点:

FLocaleList 有项目,但我已经对此进行了测试,我想我在第 71 行(我使用 Find 函数)做错了什么。我是否错误地传递了索引?

【问题讨论】:

  • 似乎不是 .Find 你应该试试 .IndexOf ...
  • 查找适用于排序列表 - 我看不到您正在这样做。使用 IndexOf 或先对列表进行排序。
  • 超出了这个问题的范围 - 您似乎在课堂上混合了两个术语。区域设置和语言代码(或 ID)。您所说的区域设置通常称为语言代码(或 ID),例如enit 等,而 locale 是一种语言的特定子类(用于世界的特定区域),例如对于英语,它可以是英式英语 (en-gb)、美式英语 (en-us) 等。因此,在翻译文本时,首先检查文本是否仅针对特定方言(区域设置)存在,如果不存在, 使用该语言常用的一种。

标签: delphi firemonkey


【解决方案1】:

您的代码逻辑是倒退的。 Find() 如果找到匹配项,则返回 True,否则返回 False。如果 Find() 返回 False,则您正在访问 Items[],如果返回 True,则引发异常。您需要在 if 语句中删除 not

function TLocalization.getLang(Index: string): TLanguage;
var
  i: integer;
begin
  { I search the locale id (for example 'it') if it's in the list. }
  { if it's in the list, I return the respective TLanguage object}
  if FLocaleList.Find(Index, i) then // <-- HERE
    Result := FLanguagesList.Items[i]
  else
    raise Exception.Create('Locale not found');
end;

但是,更重要的是,Find() documentation 说:

注意:仅将Find 用于排序列表。对于未排序的列表,请改用 IndexOf 方法。

您的列表未排序,因为 Sorted 属性默认为 false。所以请改用IndexOf()

function TLocalization.getLang(Index: string): TLanguage;
var
  i: integer;
begin
  { I search the locale id (for example 'it') if it's in the list. }
  { if it's in the list, I return the respective TLanguage object}
  i := FLocaleList.IndexOf(Index);
  if i <> -1 then
    Result := FLanguagesList.Items[i]
  else
    raise Exception.Create('Locale not found');
end;

【讨论】:

  • 在这种情况下,我根本不会使用TStringList。如果出现问题,很容易让FLanguagesListFLocaleList 不同步。我要么 1) 将 LocaleID 成员添加到 TLanguage,然后通过 FLanguagesList 循环查找该 ID,或者 2) 将 FLanguagesList 更改为 TDictionary&lt;string, TLanguage&gt; 而不是 TObjectList
  • 感谢您的意见,我想您的想法比我的更有意义。
  • 我真的很喜欢你的 1) 想法,我将编写代码,因为我的代码到目前为止有效,但我假设我与字符串列表和对象有 1:1 的对应关系。正如你所说,它可能会破裂并且有风险。我想保存语言环境的成员会更好;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-09-07
  • 2012-12-05
  • 1970-01-01
  • 1970-01-01
  • 2016-04-02
  • 2014-10-06
  • 1970-01-01
相关资源
最近更新 更多