【问题标题】:Is there any alternative for printf?printf有什么替代品吗?
【发布时间】:2010-10-12 09:01:34
【问题描述】:

我必须创建一个必须在多个 *nix 平台(Linux、AIX、...)上运行的软件。

我需要处理国际化,我的翻译字符串格式如下:

"Hi %1, you are %2." // English
"Vous êtes %2, bonjour %1 !" // French

这里%1 代表名字,%2 代表另一个词。我可以改变格式,这不是问题。

我尝试使用printf(),但你不能指定参数的顺序,你只需要指定它们的类型。

"Hi %s, you are %s"
"Vous êtes %s, bonjour %s !"

现在无法知道使用哪个参数替换%sprintf() 只使用第一个,然后是下一个。

printf() 有什么替代品可以解决这个问题吗?

注意:gettext() 不是一个选项。

【问题讨论】:

  • 使用其他语言? Python 会这样做,但这对 C 来说是一个有趣的问题

标签: c++ c linux translation printf


【解决方案1】:

我并不是要成为坏消息的承担者,但你的提议实际上是一个坏主意。我在一家非常重视 i18n 的公司工作,我们(痛苦地)发现你不能只将单词放入这样的句子中,因为它们通常没有意义。

我们所做的只是将错误文本与变量位完全断开,以避免这些问题。例如,我们会生成一个错误:

XYZ-E-1002 Frobozz not configured for multiple zorkmids (F22, 7).

然后,在错误的描述中,您简单地指出末尾括号中的两个值是 Frobozz 标识符和您尝试对其施加的 zorkmids 的数量。

这使得 i18n 翻译成为一项非常容易的任务,因为您在翻译时拥有所有所需的语言元素,而不必担心变量位应该是单数还是复数、阳性还是阴性,第一、第二或第三变格(不管这到底是什么意思)。

翻译团队只需转换"Frobozz not configured for multiple zorkmids" 就容易多了。


对于那些想看一个具体例子的人,我从我们的翻译机构那里得到了一些回报(有足够的东西改变了以保护有罪的人)。

在某个时候,有人提交了以下内容:

The {name} {object} is invalid

其中{name} 是对象的名称(客户、订单等),{object} 是对象类型本身(表、文件、文档、存储过程等)。

对于开发人员的主要(可能唯一)语言英语来说足够简单,但他们在翻译成德语/瑞士德语时遇到了问题。

虽然“客户文档”正确翻译(在位置上)为Kundendokument,但格式字符串在两个单词之间有空格这一事实是一个问题。这基本上是因为开发人员试图让句子听起来更自然,但不幸的是,基于他们有限的经验,这只是更自然。

更大的问题是“客户存储过程”变成了gespeichertes Verfahren der Kunden,字面意思是“客户的存储过程”。虽然德国客户可能已经忍受了Kunden dokument 中的空间,但无法成功地将gespeichertes Verfahren der Kunden 强加到{name} {object} 上。

现在你可能会说一个更聪明的格式字符串可以解决这个问题,但有几个原因会导致它不正确:

  • 这是一个非常简单的示例,可能还有其他更复杂的示例(我会尝试获取一些示例,但我们的翻译团队已经明确表示,他们有更紧迫的工作,而不是屈服于我的每一个突发奇想)。
  • 格式字符串的全部意义在于将翻译外部化。如果格式字符串本身是特定于翻译目标的,那么通过将文本外部化,您获得的收益很少。
  • 开发人员不必担心像{possible-pre-adjectives} {possible-pre-owner} {object} {possible-post-adjectives} {possible-post-owner} {possible-postowner-adjectives} 这样的格式字符串。这是翻译团队的工作,因为他们了解其中的细微差别。

请注意,引入断开连接很好地回避了这个问题:

指定的对象,类型为 ,无效。 参数 1 = {名称}。 参数 2 = {对象}。 Der sache nannte , dessen art ist, ist falsch。 参数 1 = {名称}。 参数 2 = {对象}。

最后一个翻译是我的,请不要用它来质疑我们翻译的质量。毫无疑问,更流利的德语使用者会从中获得愉快的笑声。

【讨论】:

  • @Konrad Rudolph:语法对象在一种语言中分成两部分但在另一种语言中没有的任何情况。以printf("I %s know", iKnow ? "" : "don't"); 为例,“我知道”和“我不知道”的法语对应词是“Je sais”和“Je ne sais pas”。否定不适合适用于英语的模板。
  • @JeremyP:但这更多是草率的结果,以及为什么人们不喜欢用这种方式注入单词而不是使用完整的句子/短语。
  • @pax:非技术用户如何回复诸如“对不起,您订购的其中一件商品缺货。商品和时间应该可用,在此消息的末尾。(Widget23,星期五)“? ;-) 并不是说​​您的想法是一个坏的想法,只是针对实际阅读手册的人的错误消息并不是特别困难的情况,就像 i18n 那样。
  • @Konrad:一个更常见的问题是,一个天真的英语使用者会创建模板"%d %s %s hanging on the wall",其预期值包括绿色/蓝色/粉红色和瓶子/大象。但是您发现,为了本地化第二个参数,您需要知道第三个参数的性别。两者都是“单个真实单词”,但它们的独立性不足以单独插入,即使在英语中插入的单词也取决于%d 的值。有时只能在句子中插入一个“东西”,翻译需要比printf更聪明。
  • @pax: 变格表示名词根据它们出现的情况而改变的方式(这通常取决于一些介词)。英语中的例子很少,但代词仍然下降:有 I/me,she/her,如果你是正式的,宾格中的“谁”变成“谁”——“我应该联系谁?”。拉丁语有 6 个格和 5 个“变格”。也就是说,一个名词可以属于 5 个不同的组,一旦你知道了名词的词干、它的变格和它的性别,就会告诉你这个名词在 6 种情况下是如何转换的。 printf 处理不好 ;-)
【解决方案2】:

POSIX printf() 支持位置参数。

printf("Hi %1$s, you are %2$s.", name, status);
printf("Vous êtes %2$s, bonjour %1$s !", name, status);

【讨论】:

  • 酷!我不知道!有参考吗?
  • 哦。但我必须说我不完全理解联机帮助页。 printf("%*d", width, num);和 printf("%2$*1$d", width, num);如果参数从左到右编号是否等价?
  • @Amigable,如果您考虑一下,这确实是有道理的。这是 one 正在打印的东西。 %2$*1$d 分解为 (%2$(*1$)d),其中括号内的位指定 param1 将用作 param2 给定参数的宽度。这相当于 %*d 分解为 (%(*)d) 并按顺序分配 param1 和 param2。
【解决方案3】:

boost.format 支持这种方式,就像在 python 中一样,但是这是针对 C++ 的

【讨论】:

  • boost::format 是最好的方法,因为它也是类型安全的。然而,该实现并未优化,并且在幕后调用 printf(或者我上次查看时做过),因此比直接使用 C printf 慢 2-3 倍。
  • 根据boost.org/doc/libs/1_70_0/libs/format/doc/format.html 2019 年的说法,boost 格式仍然比 printf 慢得多。
【解决方案4】:

您需要大多数 Unix 系统通用的 %n$s 扩展名。

"Hi %1$s, you are %2$s."

见底部的德语示例printf

问候 戴夫F

【讨论】:

  • 哇 - 几秒钟后发布是 6 票和 0 票之间的差异。
  • @David Allan Finch:我赞成公平。但它总是发生在我身上;)
  • 我相信这也是一个声誉问题。获得金徽章和 1000+ 声望后,我的投票率更高。
  • 以及什么本质上是评论(很有趣,但评论得到了不错的 +9 ...)
  • 确实,这个世界是不公平的。所以我赞成尝试让它变得更加如此。 :) 虽然我确实认为 paxdiablo 有更重要的答案(“不要这样做,这不是你真正想要的。”)
猜你喜欢
  • 1970-01-01
  • 2020-06-20
  • 1970-01-01
  • 2023-03-07
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 2010-12-13
相关资源
最近更新 更多