145

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

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

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

我所做的(在对 SO 进行了一些搜索之后,我发现很多人都这样做)是有一个单独的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 的使用吗?甚至可能有一个封装它的框架吗?

4

9 回答 9

101

NSLocalizedString有一些限制,但它对 Cocoa 来说是如此重要,以至于编写自定义代码来处理本地化是不合理的,这意味着您将不得不使用它。也就是说,一些工具可以提供帮助,这就是我如何进行的:

更新字符串文件

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

命名你的字符串

如果您NSLocalizedString按照宣传使用:

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

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

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

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

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

我希望您能通过这些技巧提高 Cocoa 本地化的工作效率!

于 2012-04-17T17:49:45.823 回答
31

至于 Xcode 中字符串的自动补全,你可以试试https://github.com/questbeat/Lin

于 2013-09-28T02:22:32.963 回答
25

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

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

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

这是我使用 NSLocalizedString 的自定义实现,它使用注释作为默认值:

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 Phases 添加一个 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 == value,自定义LocalizedStringHandler将返回注释,即'Settings'

瞧 :-)

于 2012-05-09T09:27:13.983 回答
3

在斯威夫特我使用以下,例如在这种情况下按钮“是”:

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 被理解为添加新的翻译文本,这可能并不总是需要的。

于 2017-08-31T23:48:44.473 回答
2

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

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

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

我希望你们中的一些人觉得它有用。

于 2013-09-29T02:56:18.870 回答
2

如果有人在寻找 Swift 解决方案。您可能想查看我在这里整理的解决方案:SwiftyLocalization

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

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

此外,它还生成 Localizables.swift,这是一个类似于为您检索和解码密钥的接口的结构(尽管您必须手动指定一种从密钥解码字符串的方法)。

为什么这么棒?

  1. 您不再需要到处都有一个纯字符串形式的密钥。
  2. 在编译时检测到错误的键。
  3. Xcode 可以自动完成。

虽然有一些工具可以自动完成您的可本地化密钥。对真实变量的引用将确保它始终是有效的键,否则将无法编译。

// 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 Script 来转换 Sheets --> CSV 和 Python 脚本来转换 CSV 文件 --> Localizable.strings 您可以快速查看此示例表以了解可能的情况。

于 2016-10-13T06:00:34.130 回答
1

对于 iOS 7 和 Xcode 5,您应该避免使用 'Localization.strings' 方法,并使用新的 'base localisation' 方法。如果您在谷歌上搜索“基本本地化”,则有一些教程

Apple doc :基础本地化

于 2014-02-02T16:36:46.260 回答
0
#define PBLocalizedString(key, val) \

[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
于 2013-06-27T09:05:30.460 回答
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"

当然可以抛光更多,但我想我会分享。

于 2016-04-07T14:59:18.490 回答