Delphi的TStringList查找方法无法找到项目。

3
我正在编写自己的类来管理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;
< p > TLocalization 类负责所有工作。如您所见,我创建变量 a,然后将语言添加到该类中(这是使用字典/字符串列表在内部管理的)。

可以使用 Locale[Index: string] 属性访问已添加的语言,该属性返回一个 TLanguage,我使用它来表示单个语言。最后通过 localize 方法获得我的翻译。


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

enter image description here

< p > FLocaleList 有项目,但我已经测试过了,我猜我在第71行(我使用Find函数的地方)做错了什么。也许我传递的索引是错误的吗?


似乎你应该尝试使用.IndexOf而不是.Find... - John Easley
查找适用于已排序列表 - 我没有看到您正在执行此操作。请使用IndexOf或先对列表进行排序。 - Jason
不在此问题的范围内——你似乎在类中混淆了两个术语。Locale和language code(或ID)。你所谓的locale通常被称为language code(或ID),例如 enit 等,而locale是语言的一个特定子类(用于世界上的特定地区),例如对于英语,它可以是英式英语(en-gb)、美式英语(en-us)等。因此,在翻译文本时,首先检查是否存在特定方言(locale),如果没有,则使用该语言的通用方言。 - Victoria
1个回答

11

你的代码逻辑是反过来的。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() 文档 写道:

Note: 只有在已排序的列表中使用 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;

3
在这种情况下,我根本不会使用TStringList。如果出现问题,很容易使FLanguagesListFLocaleList不同步。我要么1)向TLanguage添加LocaleID成员,然后循环遍历FLanguagesList寻找该ID,要么2)将FLanguagesList更改为TDictionary<string, TLanguage>而不是TObjectList - Remy Lebeau
谢谢你的意见,我想你的想法比我的更合理。 - Raffaele Rossi
我真的很喜欢你的1)想法,我打算编写代码,因为我的代码到目前为止都运行良好,但我假设字符串列表和对象之间存在1:1的对应关系。正如你所说,这可能会出问题,有风险。我认为持有区域设置的成员变量更好;) - Raffaele Rossi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接