ios 使用 NSLocalizedString 的最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9895621/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 17:33:26  来源:igfitidea点击:

Best practice using NSLocalizedString

objective-cioslocalizationnslocalizedstring

提问by JiaYow

I'm (like all others) using NSLocalizedStringto localize my app.

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

Unfortunately, there are several "drawbacks" (not necessarily the fault of NSLocalizedString itself), including

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

  • No autocompletition for strings in Xcode. This makes working not only error-prone but also tiresome.
  • You might end up redefining a string simply because you didn't know an equivalent string already existed (i.e. "Please enter password" vs. "Enter password first")
  • Similarily to the autocompletion-issue, you need to "remember"/copypaste the comment strings, or else genstringwill end up with multiple comments for one string
  • If you want to use genstringafter you've already localized some strings, you have to be careful to not lose your old localizations.
  • Same strings are scattered througout your whole project. For example, you used NSLocalizedString(@"Abort", @"Cancel action")everywhere, and then Code Review asks you to rename the string to NSLocalizedString(@"Cancel", @"Cancel action")to make the code more consistent.
  • Xcode 中的字符串没有自动完成功能。这使得工作不仅容易出错,而且令人厌烦。
  • 您最终可能会因为不知道等效字符串已经存在而重新定义字符串(即“请输入密码”与“首先输入密码”)
  • 与自动完成问题类似,您需要“记住”/复制粘贴注释字符串,否则genstring一个字符串会出现多个注释
  • 如果你想genstring在你已经本地化了一些字符串之后使用,你必须小心不要丢失你旧的本地化。
  • 相同的字符串分散在整个项目中。例如,你NSLocalizedString(@"Abort", @"Cancel action")到处使用,然后代码要求你将字符串重命名为 ,NSLocalizedString(@"Cancel", @"Cancel action")以使代码更加一致。

What I do (and after some searches on SO I figured many people do this) is to have a seperate strings.hfile where I #defineall the localize-code. For example

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

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

This essentially provides code-completion, a single place to change variable names (so no need for genstring anymore), and an unique keyword to auto-refactor. However, this comes at the cost of ending up with a whole bunch of #definestatements that are not inherently structured (i.e. like LocString.Common.Cancel or something like that).

这本质上提供了代码完成、更改变量名称的单一位置(因此不再需要 genstring)以及用于自动重构的唯一关键字。然而,这样做的代价是最终会产生一大堆#define没有固有结构的语句(例如 LocString.Common.Cancel 或类似的东西)。

So, while this works somewhat fine, I was wondering how you guys do it in your projects. Are there other approaches to simplify the use of NSLocalizedString? Is there maybe even a framework that encapsulates it?

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

采纳答案by ndfred

NSLocalizedStringhas a few limitations, but it is so central to Cocoa that it's unreasonable to write custom code to handle localization, meaning you will have to use it. That said, a little tooling can help, here is how I proceed:

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

Updating the strings file

更新字符串文件

genstringsoverwrites your string files, discarding all your previous translations. I wrote update_strings.pyto parse the old strings file, run genstringsand fill in the blanks so that you don't have to manually restore your existing translations. The script tries to match the existing string files as closely as possible to avoid having too big a diff when updating them.

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

Naming your strings

命名你的字符串

If you use NSLocalizedStringas advertised:

如果您NSLocalizedString按照广告使用:

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

You may end up defining the same string in another part of your code, which may conflict as the same english term may have different meaning in different contexts (OKand Cancelcome to mind). That is why I always use a meaningless all-caps string with a module-specific prefix, and a very precise description:

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

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

Using the same string in different places

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

If you use the same string multiple times, you can either use a macro as you did, or cache it as an instance variable in your view controller or your data source. This way you won't have to repeat the description which may get stale and get inconsistent among instances of the same localization, which is always confusing. As instance variables are symbols, you will be able to use auto-completion on these most common translations, and use "manual" strings for the specific ones, which would only occur once anyway.

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

I hope you'll be more productive with Cocoa localization with these tips!

我希望通过这些技巧,您在 Cocoa 本地化方面会更有效率!

回答by hiroshi

As for autocompletition for strings in Xcode, you could try http://questbe.at/lin/.

至于 Xcode 中字符串的自动补全,您可以尝试http://questbe.at/lin/

回答by Pascal

Agree with ndfred, but I would like to add this:

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

Second parameter can be use as ... default value!!

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

(NSLocalizedStringWithDefaultValue does not work properly with genstring, that's why I proposed this solution)

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

Here is my Custom implementation that use NSLocalizedString that use comment as default value:

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

1 . In your pre compiled header (.pch file) , redefine the 'NSLocalizedString' macro:

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. create a class to implement the localization handler

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. Use it!

3. 使用它!

Make sure you add a Run script in your App Build Phases so you Localizable.strings file will be updated at each build, i.e., new localized string will be added in your Localized.strings file:

确保在应用程序构建阶段添加运行脚本,以便在每次构建时更新 Localizable.strings 文件,即,将在 Localized.strings 文件中添加新的本地化字符串:

My build phase Script is a shell script:

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

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

So when you add this new line in your code:

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

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

Then perform a build, your ./Localizable.scripts file will contain this new line:

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

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

And since key == value for 'view_settings_title', the custom LocalizedStringHandler will returns the comment, i.e. 'Settings"

由于'view_settings_title'的key == value,自定义LocalizedStringHandler将返回评论,即'Settings'

Voilà :-)

瞧:-)

回答by petrsyn

In Swift I'm using the following, e.g. for button "Yes" in this case:

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

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

Note usage of the value:for the default text value. The first parameter serves as the translation ID. The advantage of using the value:parameter is that the default text can be changed later but the translation ID remains the same. The Localizable.strings file will contain "btn_yes" = "Yes";

注意value:默认文本值的用法。第一个参数用作翻译 ID。使用该value:参数的好处是可以稍后更改默认文本,但翻译 ID 保持不变。Localizable.strings 文件将包含"btn_yes" = "Yes";

If the value:parameter was not used then the first parameter would be used for both: for the translation ID and also for the default text value. The Localizable.strings file would contain "Yes" = "Yes";. This kind of managing localization files seems to be strange. Especially if the translated text is long then the ID is long as well. Whenever any character of the default text value is changed, then the translation ID gets changed as well. This leads to issues when external translation systems are used. Changing of the translation ID is understood as adding new translation text, which may not be always desired.

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

回答by hiroshi

I wrote a script to help maintaining Localizable.strings in multiple languages. While it doesn't help in autocompletion it helps to merge .strings files using command:

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

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

For more info see: https://github.com/hiroshi/merge_strings

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

Some of you find it useful I hope.

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

回答by Ronny Webers

with iOS 7 & Xcode 5, you should avoid using the 'Localization.strings' method, and use the new 'base localisation' method. There are some tutorials around if you google for 'base localization'

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

Apple doc : Base localization

Apple 文档:基础本地化

回答by aunnnn

If anyone looking for a Swift solution. You may want to check out my solution I put together here: SwiftyLocalization

如果有人正在寻找 Swift 解决方案。您可能想查看我在此处汇总的解决方案:SwiftyLocalization

With few steps to setup, you will have a very flexible localization in Google Spreadsheet (comment, custom color, highlight, font, multiple sheets, and more).

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

In short, steps are: Google Spreadsheet --> CSV files --> Localizable.strings

简而言之,步骤是:Google Spreadsheet --> CSV files --> Localizable.strings

Moreover, it also generates Localizables.swift, a struct that acts like interfaces to a key retrieval & decoding for you (You have to manually specify a way to decode String from key though).

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

Why is this great?

为什么这很棒?

  1. You no longer need have a key as a plain string all over the places.
  2. Wrong keys are detected at compile time.
  3. Xcode can do autocomplete.
  1. 您不再需要在所有地方都将密钥作为普通字符串。
  2. 在编译时检测到错误的键。
  3. Xcode 可以自动完成。

While there're tools that can autocomplete your localizable key. Reference to a real variable will ensure that it's always a valid key, else it won't compile.

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

// 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)

The project uses Google App Script to convert Sheets --> CSV , and Python script to convert CSV files --> Localizable.strings You can have a quick look at this example sheet to know what's possible.

该项目使用 Google App Script 转换 Sheets --> CSV 和 Python 脚本转换 CSV 文件 --> Localizable.strings 您可以快速查看此示例工作表以了解可能的情况。

回答by Stanislav Dvoychenko

Myself, I'm often carried away with coding, forgetting to put the entries into .strings files. Thus I have helper scripts to find what do I owe to put back into .strings files and translate.

我自己经常被编码冲昏头脑,忘记将条目放入 .strings 文件中。因此,我有帮助脚本来查找我应该放回 .strings 文件并翻译的内容。

As I use my own macro over NSLocalizedString, please review and update the script before usingas I assumed for simplicity that nilis used as a second param to NSLocalizedString. The part you'd want to change is

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

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

Just replace it with something that matches your macro and NSLocalizedString usage.

只是一些符合您的宏观和NSLocalizedString使用替换它。

Here comes the script, you only need Part 3 indeed. The rest is to see easier where it all comes from:

脚本来了,您确实只需要第 3 部分。剩下的就是更容易地看到这一切的来源:

// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "\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 "\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 "\n" if /NSLocalizedString\(@(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt

The output file contains keys that were found in the code, but not in the Localizable.strings file. Here is a sample:

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

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

Certainly can be polished more, but thought I'd share.

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

回答by baozhifei

#define PBLocalizedString(key, val) \

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