【问题标题】:Strings in a separate .pas file单独的 .pas 文件中的字符串
【发布时间】:2011-07-15 19:19:05
【问题描述】:

这可能不是这个问题的正确位置,如果不随意移动它。我标记为 Delphi/Pascal,因为这是我在 atm 中工作的,但这可能适用于我猜的所有编程。

无论如何,我正在做一些代码清理并考虑将我程序中的所有字符串移动到单独的单个 .pas 文件中。这样做有什么好处和坏处吗?这还值得吗?

澄清一下:我的意思是我将创建一个单独的文件,Strings.pas 在其中我将创建我所有的文本字符串变量。

当前代码

 Messages.Add('The voucher was NOT sent to ' + sName+
                          ' because the application is in TEST MODE.');
 Messages.Add('Voucher Saved to ' + sFullPath);
 Messages.Add('----------------------------------------------------------');

新代码类似于:

Messages.Add(sMsgText1 + '' + sName + '' + sMsgText2 + '' + sFullPath)

Strings.pas 文件将保存所有字符串数据。希望这更有意义

【问题讨论】:

  • 你为什么要这样做?你当然可以将字符串放在其他一些 .pas 文件中,然后在包含代码的文件中解析 .pas 文件并使用该字符串,尽管在这种情况下,我会给字符串文件扩展名.txt。
  • 你可以通过将文件加载到一个字符串然后解析这个字符串来做到这一点。我不知道帕斯卡,所以我不能给你一个例子。
  • @Aleksandar OP 正在考虑编写一个 .pas 文件,其中包含一长串 const 声明。
  • 我不知道pascal。但是在c中,你可以声明一个头文件,只包含常量(defines,typedefs和const vars)
  • @Ken:我认为这是一个相当普遍的问题,在 C、C# 等中具有与 Pascal 中类似的解决方案。但确实有点奇怪。

标签: delphi pascal


【解决方案1】:

将字符串移动到单独的文件中是个好主意!它将它们保持在一起,并让您在需要时轻松更改它们。您的问题并不是说您希望能够翻译它们,但集中化会有所帮助。

但是,代码如下:

Messages.Add(sMsgText1 + '' + sName + '' + sMsgText2 + '' + sFullPath)

比以下代码更好:

Messages.Add('The voucher was NOT sent to ' + sName+
                      ' because the application is in TEST MODE.');

您已经将一个杂乱但可读的函数调用变成了一个杂乱且可读的函数调用。使用旧代码(上面的第二个 sn-p),您可以阅读代码并大致了解消息要说的内容,因为其中很多都在文本中。使用新代码,您不能。

其次,移动字符串以将相关项目保持在一起并使其更容易更改的原因。如果您想更改上述消息,而不是说“路径 'bar' 中的文件 'foo'...”,而是说“文件 bar\foo 是...”,该怎么办?你不能:消息的构建方式仍然是固定的,并且分散在你的代码中。如果您想将多条消息更改为相同的格式,则需要更改许多单独的位置。

如果您的目标是翻译您的信息,这将是一个更大的问题,因为翻译通常需要重新表述信息,而不仅仅是翻译组件。 (例如,您需要更改消息中包含的子项的顺序 - 您不能仅仅假设每种语言都是逐句替换的顺序。)

进一步重构

我建议对您的消息代码进行更积极的重构。当您建议将消息​​移动到单独的文件时,您肯定是在正确的轨道上。但不要只是移动字符串:还要移动函数。不要在代码中散布大量Messages.Add('...'),而是找到您创建的消息的公共子集。许多将非常相似。创建一系列您可以调用的函数,以便所有类似的消息都使用一个函数实现,如果您需要更改它们的措辞,您可以在一个地方完成。

例如,而不是:

Messages.Add('The file ' + sFile + ' in ' + sPath + ' was not found.');
... and elsewhere:
Messages.Add('The file ' + sFileName + ' in ' + sURL + ' was not found.');

只有一个功能:

Messages.ItemNotFound(sFile, sPath);
...
Messages.ItemNotFound(sFileName, sURL);

你得到:

  • 集中消息字符串
  • 集中消息功能
  • 减少代码重复
  • 更简洁的代码(函数调用中没有字符串的组合,只有参数)
  • 更容易翻译 - 提供函数的替代实现(不要忘记,仅翻译子字符串可能还不够,您通常需要能够大幅改变措辞。)
  • 在函数名中明确说明消息是什么,如ItemNotFount(item, path),这会导致
  • 阅读时更清晰的代码

对我来说听起来不错:)

【讨论】:

  • 虽然这个过程是在我为改变而考虑改变的时候出现的,但我也清楚地看到了利弊。 @David M,我接受你的回答作为答案,因为你给了我一个很好的方向。
  • @JamesW - 我发现使用 formatStrings 对这类工作非常有帮助。定义一个字符串常量,例如:'const eMsg='file %s was not found'。然后调用格式化常量的中心函数: Messages.ItemNotFound(format(eMsg,['missing_file']))
【解决方案2】:

我认为将所有字符串常量移动到一个单元是很有意义的。它使更改文本变得更加容易,特别是如果您想翻译成其他(人类)语言。

但是你为什么不做我通常做的事情而不是字符串,即使用资源字符串。这样,其他人可以使用资源编辑器更改您的字符串,而无需重新编译。

unit Strings;

resourcestring
  strMsgText1 = 'The voucher was NOT sent to ';

etc...

但是这样的字符串可能会更好:

resourcestring
  strVoucherNotSent = 
    'The voucher was NOT sent to %s because the application is in TEST MODE.';
  strVoucherForNHasValueOf =
    'The voucher for %s has a value of $%.2f'; 

这样做的好处是,在某些语言中,这种替换的位置和顺序是不同的。这样,翻译人员可以将论点放在任何需要的地方。当然,应用程序必须使用 Format() 来处理字符串:

Messages.Add(Format(strVoucherNotSent, [sName]));
Messages.Add(Format(strVoucherSavedTo, [sFullPath]));
Messages.Add(Format(strVoucherForNHasValueOf, [sName, dblValue]));

【讨论】:

  • 您没有提到将所有字符串、常量等移动到单独的文件中的缺点。然后它们会从引用它们的代码中脱位,这会使维护变得困难。一个很好的例子是答案中的最后一行代码。我认为这是您有相互竞争的关注点的问题之一,这些关注点朝着相反的方向发展。每种方法都有优点和缺点。
  • 我认为它使维护更容易,或者至少一点也不难。他应该使用有意义的常量名,而不是使用文字字符串。文本的维护要容易得多。这是 IMO 每个编写重要程序的人都应该做的事情。
  • 我的答案中的最后一行代码有什么问题?常量的名称非常清楚地说明了文本应该传达的内容,尽管它没有给出确切的文本。对于翻译,值可能应该放在名称之前,这是唯一有用的解决方案。在这种情况下,格式字符串当然应该使用格式来指示哪个参数在哪里使用。
  • 一旦输出逻辑变得复杂,如果您的文本隐藏在不同的位置,就会变得非常混乱。
  • @David:我完全不同意。我认为这根本不会令人困惑。很清楚所有文本的位置:在那个单一的单元中。在单独的单元中,它们可以按目的或单元分组,并以空行和注释分隔。并且它也使得在拼写、语法等方面更容易保持一致。IMO,这是一个很好的编程实践。但是,让我们同意我们不同意。人们应该做他们想做的事。我只是想展示它是如何实现的,并且有些人,像我一样,认为它很有意义。你注意到 RTL 和 VCL 也这样做了吗?
【解决方案3】:

如果您想将 UI 翻译成不同的语言,那么您可能会受益于将所有文本放在一个文件中,或者可能是多个专用于声明字符串常量的文件。

但是,如果您在没有如此强烈的动机的情况下进行此更改,那么您可能只会使您的代码难以阅读。

一般来说,你必须问这样一个重大重构的好处是什么,如果它们不是不言而喻的,那么你很可能只是为了改变而改变。

【讨论】:

  • 我认为任何重要的程序将其常量(尤其是字符串)放在单独的文件中总是一个好主意。有时必须更改它们,然后在单个文件中找到它们比在应用程序的所有源文件中找到它们要容易得多。不仅用于翻译,而且当我检查所有字符串是否一致使用单词、逗号等时。使用 Delphi IDE 中当前的重构功能非常简单。
  • @James:如果您为常量选择可读的名称(我的意思是比 sMsgText1 更有意义的名称),IMO 将字符串分开是很有意义的。你知道优点。我没有看到任何缺点,只是需要一些时间。但是 IDE 可以帮助您重构这些字符串。 Semy 的其他回复也是:它使更改、拼写和语法检查、一致性检查等变得容易得多。
  • 我认为这很容易。您几乎可以按字面意思复制文本,就像 IDE 在重构此类字符串时所做的那样,或者您给出使用它的意图。对于具有多个单元的程序或专家,我总是这样做。我认为我的源文件不会因此变得不可读。相反:我的常量名称反映了字符串的意图和目的,而不是文字文本。我认为它使源代码非常可读,尤其是在文本很长的情况下。长文本会分散实际代码的注意力。
  • 即使没有翻译计划,有时仍然需要更改常量和字符串文字,例如更正拼写,或使它们都一致地使用相同的术语等。将它们放在一个地方是 IMO 实现这一目标的最佳方式。所以可能没有直接的好处,但很可能有长期的好处,如果这是一个不平凡的计划。尤其是你超过 6 个月没有看过的代码,很难再去摸索了,把你的东西放在指定的地方会让这些事情变得容易得多,IMO。
  • @david - “改变的好处值得吗?” - 绝对,原因解释。它通常很容易完成并且非常值得。充满硬编码字符串消息的代码尖叫“业余”。
【解决方案4】:

如果您想翻译您的应用程序,请考虑使用 Gnu GetText for delphi,也称为 dxGetText。这比将字符串放入单独的 .pas 文件要好,因为它允许您无需任何特殊工具、任何重新编译,甚至最终用户都可以启用翻译。

【讨论】:

  • 这确实是一个不错的选择。我已经为某人的程序翻译了一些这样的文件到荷兰语或德语。我从来没有看过源代码,只有英文的原始文本文件,我把翻译后的文件发回了。
【解决方案5】:

嗯,这里的共识似乎倾向于专业的一方,我完全同意这一点。错误地过度使用字符串文字可能会导致您的来源获得stringly typed

我仍然缺少的一个好处是字符串的可重用性。虽然这不是从运送到另一个单元的直接好处,它是从将字符串文字移动到常量。

但一个相当大的缺点是创建这个单独的源文件需要时间。您可能会考虑在下一个项目中实施这个良好的编程实践。这完全取决于您是否有足够的时间(例如爱好项目)或截止日期,是否有下一个项目(例如学生),或者只是想做一些练习。就像 David H answers 和 cmets 一样,您做出的所有决定都必须权衡收益。

除了可以提供一些自动帮助的各种花哨的重构工具之外,请意识到将字符串文字单独移动到另一个单元并不能完成工作。就像 RudyDavid M 已经回答一样,您必须部分重写您的源代码。此外,找到易读、简短且适用的常量名称确实需要时间。正如许多 cmets 已经指出,控制拼写一致性很重要,我认为同样的论点也适用于替换常量本身。

关于翻译答案,无论是否适用于 OP,将所有源字符串移动到单独的单元只是翻译解决方案的一部分。您还必须注意设计器字符串(即标题)和 GUI 兼容性:更长的翻译仍然必须适合您的标签。

如果您有奢侈或需要:去吧。但我会把它带到下一个项目中。

【讨论】:

  • 我看不出分组字符串文字与字符串类型有什么关系。
  • @Marco 好吧,一个例子是 “消息传递而不使用类型化消息”。但是由于 stringly typed 是一个虚构的术语,我可能没有正确理解它的定义。
【解决方案6】:

我确实在旧版本的框架中使用资源字符串对所有文字进行了分组。我回来了,因为在框架中你可能不会使用框架中的所有字符串(例如,因为某些单元或单元组没有被使用,但是扫描公共目录会在你的翻译工具中显示所有字符串)。

现在我再次将它们分配给单位。我最初开始将它们分组以避免重复,但回想起来这是较小的问题

(*) 在“通用”目录上使用 dxgettext。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-30
    • 1970-01-01
    • 2012-05-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多