NSString 子类或包装器 Class 或类别

NSString Subclass or Wrapper Class or Category

我目前正在帮助一位客户,由于某些政府指南(医疗与健康措辞),他们需要更改其应用程序中的语言。他们的应用程序很大,所有字符串都包含在代码中,即 (stringWithFormat/hardcoded),其中 none 在外部 table 中。这意味着这将是一项巨大的手动任务。

在未来某个不确定的时间点,客户认为他们将获得 return 当前措辞的批准,并希望将字符串切换回来。大多数更改实际上是将一个有问题的词转换为一个问题较少的词。

我想也许如果我可以根据 bool 开关在 运行 时间更改字符串,它可能会消除涉及的手动工作,并且可以让我在需要时切换回语言。

第一次尝试:

+ (instancetype)stringWithFormat:(NSString *)format, ...
{
   va_list args;
   va_start(args,format);
   //todo check flag if we're changing the language
   //todo replace problematic word from 'format'
   NSString *result = [NSString stringWithFormat:format,args];

   return result;
}

我首先快速编写了一个类别来覆盖 stringWithFormat 以替换有问题的单词。我忘了我会丢失 stringWithFormat 的原始实现。这导致了无休止的递归。

下一次尝试(subclass):

我开始尝试 subclass NSString 但遇到了 Whosebug post 说如果我的解决方案是 subclass 一个 class 集群那么我没有'不明白我的问题,因为子 class 一个 class 集群几乎从未完成。

最终选项(包装):

我最后的尝试是编写一个包装器 class,但这违背了最初的目的,即避免在应用程序中手动查找每个字符串。

我不太确定如何解决这个问题了。如果我需要 add/override 核心 class 之一的功能,我该怎么办。

有一个更简单的解决方案似乎更合适。使用 NSLocalizedString,带键而不是实际字符串:

displayString *NSString = NSLocalizedString(@"displayString", nil);
cancelButtonTitle *NSString = NSLocalizedString(@"cancelButtonTitle", nil);

然后在您的应用中创建一个 Localizable.strings 文件,并定义应显示的实际值:

"displayString" = "The string to display in English"
"cancelButtonTitle" = "Cancel"

您将 Localizable.strings 文件放入应用程序包中,应用程序使用它在运行时进行字符串替换。

您还可以为不同的语言定义不同版本的 Localizable.strings,因此,例如,如果用户将他们的语言设置为西班牙语,则对 NSLocalizedString() 的调用会为您提供西班牙语版本。

(正如您所提到的,NSString 是一个 class 集群。这意味着它是一个 public 到各种不同私有子 class 的接口es. 你不能确定当你创建一个 NSString 时你得到的是什么 private subclass ,这使得尝试 subclass 它成为一个坏主意。)

对于硬编码字符串,您别无选择,只能通过将其分配给某种字符串转换器 class 来手动修改它们。所以那些:

yourmom.text = @"Hi Mom";
yourdad.text = [NSString stringWithFormat:@"%@ and Dad!",yourmom.text];

您需要将此类分配更改为类似

yourmom.text = [StringConverter string:@"Hi Mom"];
yourdad.text = [StringConverter string:@"%@ and Dad!" placeHolder:yourmom.text];

至于故事板或xibs中的字符串,您可以通过viewdidload中的迭代循环来更改它们。祝你好运。

你第一次尝试的想法没有错,只是在实施过程中出现了一点错误。变化:

NSString *result = [NSString stringWithFormat:format,args];

至:

NSString *result = [NSString alloc] initWithFormat:format arguments:args];

这是stringWithFormat:的扩展,拦截就可以了。

关于 class 集群的想法在这种特殊情况下是一个转移注意力的问题,前面的 class 集群 (NSString) 必须提供 class 方法的实现 ( +stringWithFormat:), 所以你可以用一个简单的分类来拦截它们。

不过,拦截了+stringWithFormat:小心。一个简单的测试将向您展示它被框架大量使用并且您不希望破坏它们 - 正如我的第一个简单测试所做的那样,只需将 "d" 更改为 "c",这会更改 "window" 到 "wincow",这反过来又破坏了 Xcode 的默认应用程序的绑定设置,该应用程序绑定了 属性 "window"...

如果您正在更改与健康相关的词,您可能没问题,整个字符串会更好。

更好的方法可能是简单地编写一个 RE 来匹配代码中的所有文字字符串,并用 function(string) 替换您编写的用于执行转换的某些函数(而不是方法)。

HTH