Resource Programming Guide (二)

字符串资源

本地化过程的一个重要部分是本地化应用程序显示的所有文本字符串。根据其性质,位于nib文件中的字符串可以随其他的nib文件内容一起容易定位。然而,嵌入在代码中的字符串必须被提取,本地化,然后重新插入到代码中。为了简化此过程,并且使代码维护变得更简单 - OS X和iOS提供了将代码分离字符串所需的基础架构,并将它们放置在可以轻松进行本地化的资源文件中。

包含可本地化字符串的资源文件被称为字符串文件,因为它们的文件扩展名是.strings。您可以根据需要手动或编程创建字符串文件。标准字符串文件格式由一个或多个键值对以及可选注释组成。给定对中的键和值是以双引号括起来并以等号分隔的文本字符串。 (您还可以使用字符串文件的属性列表格式,在这种情况下,顶级节点是字典,该字典的每个键值对都是字符串条目。)

清单2-1显示了一个简单的字符串文件,其中包含默认语言的非本地化条目。当您需要显示一个字符串时,将左侧的字符串传递给可用的字符串加载例程之一。您所得到的是包含最适合当前用户的文本转换的匹配值字符串。对于开发语言,通常对键和值使用相同的字符串,但这不是必需的。

清单2-1一个简单的字符串文件

/* Insert Element menu item */
"Insert Element" = "Insert Element";
/* Error string used for unknown error types. */
"ErrorString_1" = "An unknown error occurred.";

典型的应用程序每个定位至少有一个字符串文件,也就是每个bundle的.lproj子目录中的一个字符串文件。默认字符串文件的名称为Localizable.strings,但您可以使用您选择的任何文件名创建字符串文件。创建字符串文件将在“创建字符串资源文件”中进行更深入的讨论。

注意:建议使用UTF-8编码保存字符串文件,这是标准字符串文件的默认编码。当将字符串文件复制到产品中时,Xcode会将字符串文件自动转码为UTF-8到UTF-16。有关标准字符串文件格式的更多信息,请参阅创建字符串资源文件。有关Unicode及其文本编码的更多信息,请访问http://www.unicode.org/http://en.wikipedia.org/wiki/Unicode
字符串资源(本地化和非本地化)的加载最终依赖于在OS X和iOS中发现的捆绑和国际化支持。有关软件包的信息,请参阅捆绑编程指南。有关国际化和本地化的更多信息,请参阅国际化和本地化指南。

创建字符串资源文件

虽然您可以手动创建字符串文件,但很少需要这样做。如果您使用适当的字符串加载宏编写代码,则可以使用genstrings命令行工具来提取这些字符串并为您创建字符串文件。

以下部分介绍如何设置源文件以便于使用genstrings工具的过程。有关该工具的详细信息,请参阅genstrings手册页。

选择哪个字符串进行本地化

当涉及本地化应用程序的界面时,本地化应用程序使用的每个字符串并不总是适当的。翻译是一个昂贵的过程,翻译用户永远不会看到的字符串是浪费时间和金钱。不向用户显示的字符串,例如您的应用程序内部使用的通知名称,不需要翻译。请考虑以下示例:

if(CFStringHasPrefix(value,CFSTR(“ - ”)){CFArrayAppendValue(myArray,value);};

在这个例子中,字符串“ - ”在内部使用,用户永远不会看到;因此,它不需要放在字符串文件中。

以下代码显示了用户看不到的字符串的另一个示例。字符串“%d%d%s”不需要本地化,因为用户永远不会看到它,并且对用户看不到的任何内容都没有影响。

matches = sscanf(s,“%d%d%s”,&first,&last,&other);

因为nib文件是单独本地化的,所以您不需要包含已经位于nib文件内部的字符串。但是,您应该本地化的一些字符串包括以下内容:

以编程方式添加到窗口,面板,视图或控件中并随后显示给用户的字符串。这包括传入标准例程的字符串,例如显示警告框的字符串。
如果这些字符串以编程方式添加,则菜单项标题字符串。例如,如果您为“撤消”菜单项使用自定义字符串,则这些字符串应位于字符串文件中。
向用户显示的错误消息。
任何向用户显示的样板文本。
应用程序的信息属性列表(Info.plist)文件中的一些字符串;请参阅运行时配置指南。
新的文件和文档名称。
关于字符串加载宏
基础和核心基础框架定义了以下宏,使得从字符串文件加载字符串变得更容易:

CoreFoundation宏:
CFCopyLocalizedString
CFCopyLocalizedStringFromTable
CFCopyLocalizedStringFromTableInBundle
CFCopyLocalizedStringWithDefaultValue
基础宏:
NSLocalizedString
NSLocalizedStringFromTable
NSLocalizedStringFromTableInBundle
NSLocalizedStringWithDefaultValue

您在源代码中使用这些宏来从您应用程序的一个字符串文件中加载字符串。当检索到实际的字符串值时,宏会考虑用户当前的语言偏好。此外,genstrings工具搜索这些宏,并使用它们包含的信息来构建应用程序的初始字符串文件集。

有关如何使用这些宏的其他信息,请参阅将字符串资源加载到代码中。

使用genstrings工具创建字符串文件

在开发过程中的某个时刻,您需要创建代码所需的字符串文件。如果您使用Core Foundation和Foundation宏编写代码,创建字符串文件的最简单方法就是使用genstrings命令行工具。您可以使用此工具生成一组新的字符串文件,或根据源代码更新一组现有文件。

要使用genstrings工具,通常至少提供两个参数:

源文件列表

可选的输出目录

genstrings工具可以使用.c,.m或.java文件扩展名解析C,Objective-C和Java代码文件。虽然不是严格要求,但是建议指定输出目录,并且是genstrings放置结果字符串文件的位置。在大多数情况下,您需要指定包含开发语言的项目资源的目录。

以下示例显示了运行genstrings工具的简单命令。此命令使工具解析当前目录中的所有Objective-C源文件,并将生成的字符串文件放在必须已经存在的en.lproj子目录中。

genstrings -o en.lproj *.m

首次运行genstrings工具时,它会为您创建一组新的字符串文件。随后的运行将使用源代码中找到的当前字符串条目替换这些字符串文件的内容。对于后续运行,最好在运行genstrings之前保存当前字符串文件的副本。然后,您可以区分新旧版本,以确定哪些字符串添加到(或更改)您的项目。然后,您可以使用此信息来更新任何已经本地化的字符串文件的版本,而不是替换这些文件并再次本地化。

在单个字符串文件中,每个键都必须是唯一的。幸运的是,genstrings工具足够聪明,可以合并其找到的任何重复条目。当它在单个文件夹文件中发现一次使用多次的密钥字符串时,该工具将来自各个条目的注释合并到一个注释字符串中,并生成警告。 (您可以使用-q选项来禁止重复条目警告。)如果将相同的键字符串分配给不同字符串文件中的字符串,则不会生成警告。

有关使用genstrings工具的更多信息,请参阅genstrings手册页。

手动创建字符串文件

尽管genstrings工具是最方便的创建字符串文件的方法,但您也可以手动创建它们。要手动创建字符串文件,请在TextEdit(或您首选的文本编辑应用程序)中创建一个新文件,并使用Unicode UTF-8编码进行保存。 (保存文件时,TextEdit通常默认选择适当的编码)要强制执行特定编码,必须更改应用程序首选项中的保存选项。)该文件的内容包含一组键值对以及可选注释描述每个键值对的目的。键和值字符串用等号分隔,整个条目必须以分号字符终止。按照惯例,注释被包含在C风格的注释分隔符(/ /)中,并且紧接在它们描述的条目之前。

列表2-2显示了一个字符串文件的基本格式。此示例中的条目来自TextEdit应用程序的英文版本的Localizable.strings文件。每个等号左侧的字符串表示键,右侧的字符串表示该值。开发应用程序时的常见约定是使用与用于开发应用程序的语言的值相等的键名。因此,因为TextEdit是使用英文开发的,所以Localizable.strings文件的英文版具有匹配的键和值。

列表2-2为英文本地化的字符串

/* Menu item to make the current document plain text */
"Make Plain Text" = "Make Plain Text";
/* Menu item to make the current document rich text */
"Make Rich Text" = "Make Rich Text";

清单2-3显示了相同条目的德文翻译。 这些条目也位于名为Localizable.strings的文件中,但该文件的该版本位于TextEdit应用程序的德语项目目录中。 请注意,这些键仍然是英文,但分配给这些键的值是德语。 这是因为最终用户永远不会看到关键字符串。 代码使用它们来检索相应的值字符串,在这种情况下,它是德文。

列表2-3为德语本地化的字符串

/* Menu item to make the current document plain text */
"Make Plain Text" = "In reinen Text umwandeln";
/* Menu item to make the current document rich text */
"Make Rich Text" = "In formatierten Text umwandeln";

检测非本地化字符串

基于AppKit的应用程序可以利用内置的支持来检测不需要本地化的字符串,而不需要本地化的字符串。要使用此内置支持,请在运行应用程序时设置用户默认值或添加启动参数。指定布尔值以指示是否启用或禁用用户默认值。可用的用户默认值如下所示:

NSShowNonLocalizableStrings用户默认标识不可本地化的字符串。字符串大写记录到shell。此选项偶尔会产生一些误报,但总体上仍然有用。
NSShowNonLocalizedStrings用户默认定位旨在本地化但在应用程序的现有字符串文件中找不到的字符串。您可以使用此用户默认值来捕获过期本地化的问题。
例如,要使用带有TextEdit应用程序的NSShowNonLocalizedStrings用户默认值,请在终端中输入以下内容:

/Applications/TextEdit.app/Contents/MacOS/TextEdit -NSShowNonLocalizedStrings YES

将字符串资源加载到代码中

Core Foundation和Foundation框架提供了用于检索存储在字符串文件中的本地化和非本地化字符串的宏。 尽管这些宏的主要目的是在运行时加载字符串,但它们也可以作为生成器工具可用来定位应用程序字符串资源的标记来进行次要目的。 这是第二个目的,它解释了为什么许多宏让您指定比通常需要加载字符串更多的信息。 genstrings工具使用您提供的信息自动创建或更新应用程序的字符串文件。 表2-1列出了可以为这些例程指定的信息类型,并介绍了genstrings工具如何使用该信息。

表2-1字符串加载例程中的常见参数

参数
描述

字符串用来查找相应的值。此字符串不能包含扩展ASCII字符集中包含任何字符,其中包括ASCII字符的重音版本。如果希望初始值字符串包含扩展ASCII字符,请使用允许您指定默认值参数的例程。 (有关扩展ASCII字符集的信息,请参阅相应的维基百科条目。)
表名
指定键所在的字符串文件的名称。 genstrings工具将此参数解释为字符串应放置在其中的字符串文件的名称。如果没有提供表名,该字符串将放置在默认的Localizable.strings文件中。 (当指定此参数的值时,包括不带.strings扩展名的文件名。)
其名称以.nocache结尾的.strings文件(例如ErrorNames.nocache.strings)将不会被NSBundle缓存其内容。
默认值
与给定键关联的默认值。如果未指定默认值,则genstrings工具将使用键字符串作为初始值。默认值字符串可能包含扩展ASCII字符。
评论
翻译注释包含与字符串。您可以使用意见为翻译团队提供有关如何使用给定字符串的线索。 genstrings工具将这些注释放在字符串文件中,并将它们包围在相关条目上方的C风格的注释分隔符(/ /)中。

对应于包含字符串文件的包的NSBundle对象或CFBundleRef类型。您可以使用它从除应用程序的主包之外的捆绑包加载字符串。例如,您可以使用它从框架或插件加载本地化字符串。

当您从字符串文件请求字符串时,返回的字符串取决于可用的本地化(如果有)。 Cocoa和Core Foundation宏使用内置的捆绑包国际化支持来检索其本地化与用户当前语言偏好相匹配的字符串。只要将本地化的资源文件放在适当的特定于语言的项目目录中,使用这些宏加载字符串就会自动生成相应的字符串。如果没有找到适当的本地化字符串资源,则该bundle的加载代码会自动选择适当的非本地化字符串。

有关一般国际化和如何创建特定于语言的项目目录的信息,请参阅国际化和本地化指南。有关捆绑包结构以及如何从捆绑包目录中选择资源文件的信息,请参阅捆绑编程指南。

使用Core Foundation框架

Core Foundation框架定义了单个函数和几个宏,用于从应用程序包中加载本地化字符串。 CFBundleCopyLocalizedString函数提供了检索字符串的基本实现。但是,建议您使用以下宏:

* CFCopyLocalizedString(key, comment)

* CFCopyLocalizedStringFromTable(key, tableName, comment)

* CFCopyLocalizedStringFromTableInBundle(key, tableName, bundle, comment)

* CFCopyLocalizedStringWithDefaultValue(key, tableName, bundle, value, comment)

使用宏而不是CFBundleCopyLocalizedString函数有几个原因。 首先,对于某些常见的情况,宏更容易使用。 其次,宏让您将注释字符串与字符串条目相关联。 第三,这个宏由genstrings工具识别,但CFBundleCopyLocalizedString函数不是。

有关上述宏的语法的信息,请参阅CFBundle参考。

使用基础框架

基础框架定义了单个方法和几个用于加载字符串资源的宏。 NSBundle类的localizedStringForKey:value:table:方法从驻留在当前包中的字符串文件加载指定的字符串资源。 Cocoa还定义了以下用于获取本地化字符串的宏:

* NSLocalizedString(key, comment)

* NSLocalizedStringFromTable(key, tableName, comment)

* NSLocalizedStringFromTableInBundle(key, tableName, bundle, comment)

* NSLocalizedStringWithDefaultValue(key, tableName, bundle, value, comment)

与Core Foundation一样,Apple建议您使用Cocoa方便宏来加载字符串。 这些宏的主要优点是它们可以由genstrings工具解析,用于创建应用程序的字符串文件。 它们也更简单易用,让您将翻译注释与每个条目相关联。

有关上述宏的语法的信息,请参阅Foundation函数参考。 NSBundle类参考中还定义了加载字符串的其他方法。

获取字符串的示例

以下示例演示了使用Foundation和Core Foundation宏检索字符串的基本技术。 每个示例假定当前的包包含一个名为Custom.strings的字符串文件,该文件已被翻译成法语。 此翻译文件包含以下字符串:

/* A comment */
"Yes" = "Oui";
"The same text in English" = "Le même texte en anglais";

使用Foundation框架,您可以使用NSLocalizedStringFromTable宏获取“是”字符串的值,如以下示例所示:

NSString* theString;
theString = NSLocalizedStringFromTable (@"Yes", @"Custom", @"A comment");

使用Core Foundation框架,您可以使用CFCopyLocalizedStringFromTable宏获取相同的字符串,如此示例所示:

CFStringRef theString;
theString = CFCopyLocalizedStringFromTable(CFSTR("Yes"), CFSTR("Custom"), "A comment");

在这两个示例中,代码指定要检索的键,即字符串“是”。他们还指定要在其中查找密钥的字符串文件(或表),在这种情况下是Custom.strings文件。在字符串检索期间,忽略注释字符串。

高级字符串文件提示

以下部分提供了一些额外的使用字符串文件和字符串资源的提示。

用genstrings搜索自定义函数
genstrings工具默认搜索Core Foundation和Foundation字符串宏。它使用这些宏中的信息在项目的字符串文件中创建字符串条目。您还可以引导genstrings在代码中查找自定义字符串加载函数,并使用除标准宏之外的这些函数。您可以使用自定义函数来包装内置的字符串加载例程并执行一些额外的处理,或者您可以使用自己的自定义模型替换默认的字符串处理行为。

如果要使用genstrings与您自己的自定义函数,您的函数必须使用基础宏使用的命名和格式化约定。您的函数的参数必须匹配相应宏的参数。当您调用genstrings时,您可以指定-s选项,然后指定与NSLocalizedString宏对应的函数的名称。您的其他函数名称应该从此基本名称生成。例如,如果您指定了函数名MyStringFunction,其他函数名称应为MyStringFunctionFromTable,MyStringFunctionFromTableInBundle和MyStringFunctionWithDefaultValue。 genstrings工具查找这些函数并使用它们来构建相应的字符串文件。

格式化字符串资源

对于某些字符串,您可能不想(或能够)对字符串资源中的整个字符串进行编码,因为字符串的一部分可能在运行时更改。例如,如果字符串包含用户文档的名称,则需要能够将该文档名称动态插入到该字符串中。在创建字符串资源时,您可以使用通常用于在Foundation和Core Foundation框架中处理字符串替换的格式化字符。清单2-4显示了使用基本格式化字符的几个字符串资源:

清单2-4带有格式字符的字符串

"Windows must have at least %d columns and %d rows." =
"Les fenêtres doivent être composes au minimum de %d colonnes et %d lignes.";
"File %@ not found." = "Le fichier %@ n’existe pas.";

要使用实际值替换格式化字符,可以使用字符串资源作为格式字符串,使用NSString的stringWithFormat:方法或CFStringCreateWithFormat函数。 Foundation和Core Foundation支持printf语句中使用的大多数标准格式化字符。此外,您可以使用上述示例中所示的%@说明符来插入与任意Objective-C对象关联的描述性文本。有关说明符的完整列表,请参阅字符串编程指南中的格式化字符串对象。

在翻译过程中经常出现的一个问题是,翻译者可能需要重新排序翻译字符串中的参数,以解释源语言和目标语言的差异。如果一个字符串包含多个参数,则转换器可以在格式化字符之间插入格式为n $(其中n指定原始参数的位置)的特殊标签。这些标记让翻译器重新排序出现在原始字符串中的参数。以下示例显示了一个在翻译的字符串中两个参数相反的字符串:

/* Message in alert dialog when something fails */
"%@ Error! %@ failed!" = "%2$@ blah blah, %1$@ blah!";

在字符串资源中使用特殊字符
与C一样,一些字符必须带有反斜杠前缀,然后才能将它们包含在字符串中。 这些字符包括双引号,反斜杠字符本身以及特殊控制字符,如换行符(\ n)和回车符(\ r)。

"File \"%@\" cannot be opened" = " ... ";
"Type \"OK\" when done" = " ... ";

您可以通过指定\ U,然后立即加上最多四个十六进制数字,将任意Unicode字符包含在值字符串中。四位数表示所需Unicode字符的条目;例如,空格字符由十六进制20表示,因此在指定为Unicode字符时为\ U0020。如果字符串必须包含由于某些原因无法键入的Unicode字符,则此选项很有用。如果使用此选项,还必须将-u选项传递给genstrings,以便在生成的字符串文件中正确解释十六进制数字。 genstrings工具假设您的字符串默认为低ASCII,只有指定-u选项时才解释反斜杠序列。

注意:如果您自己生成字符串文件,例如使用genstrings,请确保这些字符串文件最终以UTF-8编码,然后再将其添加到项目中。

调试字符串文件

如果您在测试过程中遇到的问题,并找到检索字符串的函数和宏总是返回相同的密钥(而不是翻译值),运行在你的字符串的文件在/ usr / bin中/ plutil工具。字符串文件本质上是以特殊方式格式化的属性列表文件。使用-lint选项运行plutil可以发现隐藏的字符或其他正在阻止字符串正确检索的错误。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 74,704评论 12 116
  • 介绍 捆绑是macOS和iOS中用于封装代码和资源的基础技术。软件包通过为所需资源提供已知位置来简化开发人员体验,...
    nicedayCoco阅读 343评论 0 0
  • 框架捆绑 框架是封装动态共享库和支持该库所需的资源文件的分层目录。框架比典型的动态共享库提供了一些优势,因为它们为...
    nicedayCoco阅读 446评论 0 1
  • 关于资源 适用于计算机程序的资源是与程序可执行代码相关的数据文件。资源可以通过将代码之外的复杂数据集或图形内容创建...
    nicedayCoco阅读 110评论 0 0
  • 关于首选项和设置 首选项是您持久存储的信息,并用于配置您的应用程序。应用程序通常会向用户公开偏好设置,以便他们自定...
    nicedayCoco阅读 258评论 0 0