使用NSLocalizedString的最佳实践

时间:2012-03-27 18:43:45

标签: objective-c ios localization nslocalizedstring

我(和所有其他人一样)使用NSLocalizedString本地化我的应用。

不幸的是,有几个“缺点”(不一定是NSLocalizedString本身的错误),包括

  • Xcode中的字符串没有自动重现。这使得工作不仅容易出错,而且还很烦人。
  • 您可能最终只是因为您不知道已经存在的等效字符串而重新定义字符串(即“请输入密码”与“先输入密码”)
  • 类似于自动完成问题,你需要“记住”/ copypaste注释字符串,否则genstring最终会有一个字符串的多个注释
  • 如果您想在已经对某些字符串进行本地化后使用genstring,则必须小心不要丢失旧的本地化。
  • 相同的字符串分散在整个项目中。例如,您在任何地方都使用了NSLocalizedString(@"Abort", @"Cancel action"),然后Code Review要求您将字符串重命名为NSLocalizedString(@"Cancel", @"Cancel action")以使代码更加一致。

我所做的事情(以及经过一些搜索,我认为很多人这样做)就是要有一个单独的strings.h文件,其中#define所有本地化代码。例如

// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(@"Cancel", nil)
// Somewhere else
NSLog(@"%@", NSLS_COMMON_CANCEL);

这实质上提供了代码完成,一个用于更改变量名称的位置(因此不再需要genstring),以及一个用于自动重构的唯一关键字。然而,这需要花费大量的#define语句,而这些语句本身并不是结构化的(比如LocString.Common.Cancel或类似的东西)。

所以,虽然这样做有点好,但我想知道你们是如何在你的项目中做到的。是否有其他方法可以简化NSLocalizedString的使用?是否有可能封装它的框架?

9 个答案:

答案 0 :(得分:93)

NSLocalizedString有一些限制,但它对Cocoa来说是如此重要,编写自定义代码来处理本地化是不合理的,这意味着你必须使用它。也就是说,一个小工具可以提供帮助,以下是我的工作方式:

更新字符串文件

genstrings会覆盖您的字符串文件,会丢弃以前的所有翻译。 我写了update_strings.py来解析旧的字符串文件,运行genstrings并填写空白,这样您就不必手动恢复现有的翻译。 该脚本尝试尽可能地匹配现有的字符串文件,以避免在更新时出现过大的差异。

命名你的字符串

如果您使用NSLocalizedString广告:

NSLocalizedString(@"Cancel or continue?", @"Cancel notice message when a download takes too long to proceed");

您可能最终在代码的另一部分中定义相同的字符串,这可能会发生冲突,因为相同的英语术语在不同的上下文中可能具有不同的含义(OKCancel会浮现在脑海中。 这就是为什么我总是使用带有模块特定前缀的无意义的全大写字符串,以及非常精确的描述:

NSLocalizedString(@"DOWNLOAD_CANCEL_OR_CONTINUE", @"Cancel notice window title when a download takes too long to proceed");

在不同的地方使用相同的字符串

如果多次使用相同的字符串,则可以像使用宏一样使用宏,也可以将其作为实例变量缓存在视图控制器或数据源中。 这样,您就不必重复可能变得陈旧的描述,并且在同一本地化的实例之间变得不一致,这总是令人困惑。 由于实例变量是符号,您可以在这些最常见的翻译中使用自动完成,并对特定的翻译使用“手动”字符串,这只会发生一次。

我希望通过这些提示,您可以更高效地使用Cocoa本地化!

答案 1 :(得分:30)

对于Xcode中字符串的自动重新组合,您可以尝试http://questbe.at/lin/

答案 2 :(得分:24)

同意ndfred,但我想补充一下:

第二个参数可以用作...默认值!!

(NSLocalizedStringWithDefaultValue与genstring无法正常工作,这就是我提出这个解决方案的原因)

这是我的Custom实现,它使用NSLocalizedString,它使用comment作为默认值:

1。在预编译的头文件(.pch文件)中,重新定义'NSLocalizedString'宏:

// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"

#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key  comment:_comment]

<强> 2。创建一个类来实现本地化处理程序

#import "LocalizationHandlerUtil.h"

@implementation LocalizationHandlerUtil

static LocalizationHandlerUtil * singleton = nil;

+ (LocalizationHandlerUtil *)singleton
{
    return singleton;
}

__attribute__((constructor))
static void staticInit_singleton()
{
    singleton = [[LocalizationHandlerUtil alloc] init];
}

- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
    // default localized string loading
    NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];

    // if (value == key) and comment is not nil -> returns comment
    if([localizedString isEqualToString:key] && comment !=nil)
        return comment;

    return localizedString;
}

@end

第3。使用它!

确保在App Build阶段添加Run脚本,以便在每次构建时更新Localizable.strings文件,即新的本地化字符串将添加到Localized.strings文件中:

我的构建阶段脚本是一个shell脚本:

Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder

因此,当您在代码中添加此新行时:

self.title = NSLocalizedString(@"view_settings_title", @"Settings");

然后执行构建,您的./Localizable.scripts文件将包含以下新行:

/* Settings */
"view_settings_title" = "view_settings_title";

由于'view_settings_title'的key ==值,自定义LocalizedStringHandler将返回注释,即'Settings >>

Voilà: - )

答案 3 :(得分:3)

在Swift中,我使用以下内容,例如:对于这种情况下按钮“是”:

NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")

请注意value:对默认文本值的使用。第一个参数用作翻译ID。使用value:参数的优点是可以在以后更改默认文本,但转换ID保持不变。 Localizable.strings文件将包含"btn_yes" = "Yes";

如果未使用value:参数,则第一个参数将同时用于:转换ID和默认文本值。 Localizable.strings文件将包含"Yes" = "Yes";。这种管理本地化文件似乎很奇怪。特别是如果翻译的文本很长,那么ID也很长。每当更改默认文本值的任何字符时,转换ID也会更改。当使用外部翻译系统时,这会导致问题。更改翻译ID被理解为添加新的翻译文本,这可能并不总是需要。

答案 4 :(得分:2)

我编写了一个脚本来帮助维护多种语言的Localizable.strings。虽然它在自动完成方面没有帮助,但它有助于使用命令合并.strings文件:

merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings

有关详细信息,请参阅: https://github.com/hiroshi/merge_strings

你们中的一些人发现我希望它有用。

答案 5 :(得分:0)

#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]

答案 6 :(得分:0)

iOS 7&amp; Xcode 5,你应该避免使用'Localization.strings'方法,并使用新的'基本本地化'方法。如果你谷歌的'基础本地化',有一些教程

Apple doc:Base localization

答案 7 :(得分:0)

我自己,我经常带着编码,忘记将条目放入.strings文件中。因此,我有帮助脚本来查找我要归还的.strings文件和翻译。

当我在NSLocalizedString上使用我自己的宏时,请在使用之前查看并更新脚本,因为我假设为了简单起见, nil 被用作NSLocalizedString的第二个参数。你想要改变的部分是

NSLocalizedString\(@(".*?")\s*,\s*nil\) 

只需将其替换为与您的宏和NSLocalizedString用法相匹配的内容。

这是脚本,你只需要第3部分。剩下的就是更容易看到它来自哪里:

// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings

// Part 2. Get keys from the source code
grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/'

// Part 3. Get Part 1 and 2 together.

comm -2 -3 <(grep -n -h -Eo -r  'NSLocalizedString\(@(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

输出文件包含在代码中找到的键,但不包含在Localizable.strings文件中。这是一个示例:

"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"

当然可以更多地打磨,但我想我会分享。

答案 8 :(得分:0)

如果有人在寻找Swift解决方案。你可能想看看我在这里放在一起的解决方案:SwiftyLocalization

只需几步设置,您就可以在Google电子表格中进行非常灵活的本地化(评论,自定义颜色,突出显示,字体,多张表格等)。

简而言之,步骤是:Google电子表格 - &gt; CSV文件 - &gt; Localizable.strings

此外,它还生成了Localizables.swift,这个结构就像一个密钥检索和接口的接口。为你解码(你必须手动指定一种从密钥解码String的方法)。

为什么这很棒?

  1. 您不再需要在整个地方都使用普通字符串作为字符串。
  2. 在编译时检测到错误的密钥。
  3. Xcode可以自动完成。
  4. 虽然有些工具可以自动填充您的可本地化密钥。对真实变量的引用将确保它始终是有效的密钥,否则它将无法编译。

    // It's defined as computed static var, so it's up-to-date every time you call. 
    // You can also have your custom retrieval method there.
    
    button.setTitle(Localizables.login.button_title_login, forState: .Normal)
    

    该项目使用Google App脚本转换表格 - &gt;用于转换CSV文件的CSV和Python脚本 - &gt; Localizable.strings您可以快速查看此示例表以了解可能的内容。