PUFF-J文件格式
PUFF-J
PUFF-J是一种基于JSON的文件格式,对本地化资源使用UTF-8字符编码。资源是提交给语言专家进行翻译的字符串。
这种格式让您能够以映射的形式提供资源:
资源对象可以用其他一些属性来装饰,这些属性可以提供:
- 资源的描述,包括在其中使用资源的上下文。
- 有关在资源生命周期内如何处理资源的更多信息 - 可以是在翻译过程中的处理,也可以是在消耗资源的运行时的处理。
该格式旨在面向开发者、让人类易于阅读且易于编辑。它在构建时转换为更易于在运行时使用的内部格式。
PUFF-J文件的扩展名必须为puff.json。
资源级别属性
资源标识符
被提交以供翻译的每种资源均使用其唯一标识符进行标识。此资源ID作为资源对象的属性名称提供。尽管JSON几乎允许此字符串为任何Unicode字符,但PUFF-J将字符集限制为:
^(\\w[\\w-:]*)$`.
\w等效于[a-zA-Z_0-9]。
资源值
资源值是提交给语言专家进行翻译的实际值。通常在value属性中指定它,并且它可以是三种类型之一:
可选属性
资源对象可以具有其他属性。这些属性提供了有关在翻译过程中将如何处理资源的更多信息:
- translate- 一种标记,指示是否应翻译字符串。如果将其省略或其值等于true,则应翻译字符串。这意味着字符串将包含在翻译请求中。
 
- note- 强烈建议使用上下文注释。
- 每个注释的长度限制为3,000个字符。
- 注释就是开发者的注释,这些注释提供字符串和占位符的更广泛上下文信息,为翻译者提供帮助。
 
示例:
  {
    "dir" : "ltr",
    "resources": {
      "messageXml": {
        "note" : "{0} 是客户订单的追踪编码。",
        "value": "您的订单号是·{0}."
      },
      "messageXml2": {
        "translate": false,
        "note" : "此变量不需要由翻译人员翻译。它会由MessageFormat本地化",
        "value": "{numItems,number,integer}"
      }
    }
  }
资源类型
如之前所述,发送给语言专家进行翻译的资源值可以是以下三种类型之一:
该部分提供了有关这些类型的更多信息,以及有关如何在PUFF-J文件中正确表示它们的最佳实践。
要点
- 使用结构化资源值类型来减轻编辑和阅读复杂的MessageFormat复数和选择模式的困难。
- 
    
    通常会倾向于用复杂的参数(例如复数)仅涵盖消息字符串的很小部分。但是,这会对翻译者造成困难,原因有二: 1.翻译者可能难以理解参数子消息中的句子片段如何与句子的其余部分相互作用,以及;2.翻译者无从知道自己能否以及如何缩小或扩大参数内句子部分的范围,以使整个信息适用于他们的语言。 建议: 如果可能,使用复杂的参数作为消息的最外层结构,并在其子消息中写出完整的句子。如果您有嵌套的选择和复数参数,可将选择参数(及其固定的选项集)置于外部,并将复数参数(最好不超过一个)嵌套在内部。 
- 添加上下文注释。这些注释代表开发者的注释,为翻译者提供字符串和占位符的更广泛上下文信息。它们还提供信息帮助翻译者理解可能模棱两可的字词或概念(例如“window”一词可指代计算机上的视窗与房屋中的窗户)。
让我们更仔细地看一下不同的资源类型、用例和建议。
字符串类型
简单字符串表示为JSON字符串。这些字符串以MessageFormat模式编写,对于在运行时格式化和注入的参数(字符串、数字、日期、时间等),带或不带占位符。
字符串可以包含任何Unicode字符,但被转义的控制字符除外。参见转义语法。
尽管它是有效的JSON,但打字机撇号U+0027 (')(美国英语键盘上的默认单引号)是个例外,因为它是MessageFormat模式中的特殊字符。最好在源字符串中避免使用此字符;应改为使用右单引号U+2019 (’) 字符表示单引号。
即使源字符串不包含任何单引号,翻译后的字符串也可能需要包含单引号。例如,这在法语翻译中尤为常见。在这种情况下,指示翻译者优先使用右单引号U+2019 (’) 字符。对于开发者来说,在上下文注释中就这一点提醒翻译者也是不错的做法。
如果必须在MessageFormat模式中特别包含字面打字机撇号字符,则可以通过使用第二个打字机撇号对其进行转义来实现。例如,在MessageFormat模式中,'' 呈现为 '。
花括号是另一个例外。它也是MessageFormat模式中的一个特殊字符。必须将打字机撇号作为转义字符使用的一种情况是,在MessageFormat模式中包含一个字面花括号字符。例如,将MessageFormat模式中的 '{''}' 呈现为 {'}。对于这种用法,最好为翻译者提供上下文注释。此外,应仔细审阅返回的翻译,因为语法不直观。
带和不带占位符(速记表示)的简单字符串示例:
"stringId1": "This is a window.",
"stringId2": "Hello {user}, this is a window."
第一个示例显示了没有占位符的简单字符串。第二个示例显示了带有字符串占位符的字符串。
值结构示例(详细表示):
"stringId1": {
    "value": "This is a window.",
    "note": "这是指计算机中的视窗。"
},
"stringId2": {
    "value": "Hello {user}, this is a window.",
    "note": "这是指计算机中的视窗。"
},
第一个示例显示了没有占位符的字符串。第二个示例显示了带有字符串占位符的字符串。添加上下文注释可以向翻译者阐明window是代表计算机中的视窗还是房屋中的实体窗户。
建议: 强烈建议使用带有上下文注释的值结构来帮助翻译者。应包括上下文注释,尤其是当字符串有占位符时。
结构化资源值类型
结构化资源值类型可减轻编辑和阅读复杂的MessageFormat复数和选择模式的困难。可以将包含基于参数的分支的子模式作为映射提供,其中将参数值作为键。映射中的项目表示子模式的不同分支。
复数类型
建议: 编写复杂的MessageFormat复数模式既复杂又容易出错。因此,为了提高资源的可读性,强烈建议开发者构造具有复数结构化资源值类型的复数形式,这样有利于每个复数项目使用完整的可翻译句子,并避免使用复杂的MessageFormat构造。这样做有助于确保高质量的翻译。
在向字符串中插入数值时,一个常见的问题是确保生成的句子在语法上正确。例如,您可能有一个MessageFormat模式,例如:
"You have {0,number,integer} **items** in your cart"
但是,如果项目数为1,则这个句子应改为:
"You have 1 **item** in your cart"
为了处理这种情况,创建两个字符串可能是不错的做法:
"itemsInCartPattern_singular"
以及
"itemsInCartPattern_plural"
使用“中性”字符串来避免这个问题也可能是不错的做法,例如:
"You have {0,number,integer} item(s) in your cart"
但是,这两种解决方案都忽略了其他语言的语法要求,这些要求并不总是能明确分为两类。
MessageFormat有一种用于处理复数的模式语法,但是很难使用。改为使用复数结构化资源值类型语法可以更轻松地为任何语言编写语法正确的复数。
要构造复数资源,需要两个参数:
- param: 用于选择消息的变量的名称
- pluralItems: 包含可供选择的相关规则的字符串列表
根据参数值选择相应的复数形态。相关参数ID与param属性一起提供。既可以对MessageFormat中的参数命名也可以对其进行编号,因而此属性可以是字符串或数字。
复数形态包含在pluralItems属性中,该属性是复数参数值和复数形态的映射。
允许的参数名称为:
[^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
[:Pattern_Syntax:]和[:Pattern_White_Space:]为ICU UnicodeSet。
参数名称不能包含空格。
允许的复数参数值为:
- zero
- one
- two
- few
- many
- ^(=[0-9]+)$
- other(必需;- other必须始终出现,因为它是参数值与其他规则不匹配时会触发的规则)
注意: 复数参数值集因语言而异。英语使用=0、one和other。对于two、few、many、^(=[0-9]+)$,它没有不同的语法用例。如果源字符串是用英语创作的,则后面的值不应出现在源复数结构化资源值中。
规则分为两类:枚举规则和数值规则。枚举规则为zero、one、two、few、many和other。尽管名称具有启发性,但规则并不总是“义如其文”。对于何时触发规则,每个区域设置都有不同的逻辑。例如,在英语中,one规则触发的值为1。但是,对于没有语法复数的语言(例如日语或中文),则不会触发此规则,也不会触发不同值,例如在其他区域设置中以一结尾的数字(21、31等)。
数值规则匹配特定值。在示例中,=0规则与值0相匹配。数值规则优先于枚举规则。当您想将特定消息绑定到某个值时,您应该编写一个数值规则。例如,如果您要显示消息“You have one chance remaining before your account is locked”(在账户被锁定之前您还有一次机会),则应使用规则=1而不是one规则,以确保始终且仅在参数值等于1时才显示消息。
用英语创作资源时,请务必编写示例中使用的规则:=0、one和other。在翻译过程中,本地化人员应负责生成其他区域设置要求的任何规则。根据需要添加数值规则,例如上文所提=1的情况。
复数结构化资源值示例:
{
"resources": {
  "message1": {
    "value": {
     "param": "appleCount",
     "pluralItems": {
        "=0": "You have no apples in a basket",
        "one": "You have {appleCount,number,integer} apple in a basket",
        "other": "You have {appleCount,number,integer} apples in a basket"
      }
    }
  }
}
还有带有编号参数的相同示例:
{
  "resources": {
    "message1": {
      "value": {
        "param": 0,
        "pluralItems": {
          "=0": "You have no apples in a basket",
          "one": "You have {0,number,integer} apple in a basket",
          "other": "You have {0,number,integer} apples in a basket"
        }
      }
    }
  }
}
在前面的示例中,参数用于分支,也出现在分支句子中。
何时使用复数
在句子中插入数字或价格时使用复数。例如:
"You have 2 items in your cart"
或
"You will be charged $5 per item."
即使英语字符串在语法上对所有值都正确,也要使用复数。对于其他语言中的所有值,这样做在语法上可能不正确。
何时不使用复数
如果数字或值不是句子的一部分,则不要使用复数。例如:
"Number of items: 2"
或
"Price: $3.27"
复数类型用例示例
下面是一个用例示例。假设您想在字符串中插入一个数值,例如用于显示客户购物车中的商品数量。例如:
"You have no items in your cart."
"You have 1 item in your cart."
"You have 2 items in your cart."
...
在此示例中,您应该构造一个复数结构化资源值,这样客户就可以获得与其语言相对应的语法正确结果,并且避免了编辑和阅读复杂的MessageFormat模式。
建议做法: 强烈建议使用以下格式。请注意,复数结构化资源值类型对每个复数项目都有完整的可翻译句子。这样对于翻译者而言就提高了可读性,并为他们提供了字符串的完整上下文。此外,对于完整句子,可以更轻松地重新排列翻译中的字词顺序以遵循自然语言流程。以下示例还包括上下文注释,为翻译者提供更多信息:
{
  "resources": {
    "number-of-items-in-cart": {
      "note": "cartItems是指示购物车中物品数量的占位符",
      "value": {
        "param": "cartItems",
        "pluralItems": {
          "=0": "You have zero items in your cart.",
          "one": "You have {cartItems,number,integer} item in your cart.",
          "other": "You have {cartItems,number,integer} items in your cart."
        }
      }
    }
  }
}
不建议的做法: 以下格式表达了一个不完整的想法。这可能会导致翻译中的字词顺序出现问题,并且翻译者很难理解上下文:
{
  "resources": {
    "plural-message-format": "You have {cartItems,plural,=0 {zero items} one {{cartItems,number,integer} item} other {{cartItems,number,integer} items}} in your cart."
  }
}
选择类型
在字符串中插入名称或名词时,它们的语法性别属性会影响字符串中的其他关联字词。选择MessageFormat模式旨在帮助处理基于语法性别的变化。有关更多信息,请参阅ICU SelectFormat文档(仅提供英文版)。
选择结构化资源值类型简化了选择MessageFormat模式的使用。此类型表示基于参数的分支,该参数具有任意值。选择结构化资源值具有两个必需的属性:
- param: 用于选择消息的变量的名称
- selectItems: 包含可供选择的相关参数值的字符串列表
选择参数ID通过param属性提供。既可以对MessageFormat模式中的参数命名也可以对其进行编号,因而此属性可以是字符串或数字。
在selectItems属性中,这些分支被指定为项目。除了对JSON中的字符串类型施加的限制外,选择项目键没有任何限制。
允许的参数名称为:
[^[[:Pattern_Syntax:][:Pattern_White_Space:]]]+
[:Pattern_Syntax:]和[:Pattern_White_Space:]为ICU UnicodeSet。
参数名称不能包含空格。
selectItems需要在列表中包含other规则。other必须始终出现,因为它是参数值与其他项目键不匹配时会触发的规则。
选择结构化资源值示例:
{
  "resources": {
    "message2": {
      "note" : "简单选择示例",
      "value": {
        "param": "gender",
        "selectItems": {
          "female": "She has invited us for supper.",
          "male": "He has invited us for supper.",
          "other": "They have invited us for supper."
        }
      }
    }
  }
}
还有带有编号参数的相同示例:
{
  "resources": {
    "message2": {
      "note" : "简单选择示例",
      "value": {
        "param": 0,
        "selectItems": {
          "female": "She has invited us for supper.",
          "male": "He has invited us for supper.",
          "other": "They have invited us for supper."
        }
      }
    }
  }
}
在前面的示例中,参数仅用于分支,不出现在任何分支句子中。
选择类型用例示例
下面是一个用例示例。假设您想根据主持人的性别显示以下字符串之一:
"{hostName} invites you to her party."
"{hostName} invites you to his party."
"{hostName} invites you to their party."
要实现此目的,可指定一个选择结构化资源值,并将字符串作为参数来操作选择。请注意,selectItems需要将other规则指定为项目:
{
  "resources": {
    "party-invitation": {
      "note" : "hostGender是主持人性别的字符串参数。hostName是主持人名字的字符串占位符。",
      "value": {
        "param": "hostGender",
        "selectItems": {
          "female": "{hostName} invites you to her party.",
          "male": "{hostName} invites you to his party.",
          "other": "{hostName} invites you to their party.",
        }
      }
    }
  }
}
嵌套
句子可能因多个复数和/或选择参数而有所不同:
There are 3 warnings and 1 error.
尽管在某些情况下可以避免这种情况,但在有些情况下,确实需要支持多个参数和基于这些参数的分支。这是通过嵌套结构化资源值来实现的。
可以嵌套复数和选择结构化资源值,因为每个复数或选择项目可以是数字、字符串、复数或选择类型。在最深的嵌套层面,复杂形式的项目代表完整的翻译句子。
嵌套结构化资源值的示例:
{
  "resources": {
    "party-invitation-status": {
      "note" : "guestCount是受邀嘉宾数量的数字参数。hostGender是主持人性别的字符串参数。hostName是主持人名字的字符串占位符。",
      "value": {
        "param": "guestCount",
        "pluralItems": {
          "=0": {
            "param": "hostGender",
            "selectItems": {
                "female": "{hostName} did not invite any guests to her party.",
                "male": "{hostName} did not invite any guests to his party.",
                "other": "{hostName} did not invite any guests to their party."
            }
          },
          "one": {
            "param": "hostGender",
            "selectItems": {
                "female": "{hostName} invited one guest to her party.",
                "male": "{hostName} invited one guest to his party.",
                "other": "{hostName} invited one guest to their party."
            }
          },
          "other": {
            "param": "hostGender",
            "selectItems": {
                "female": "{hostName} invited {guestCount,number,integer} guests to her party.",
                "male": "{hostName} invited {guestCount,number,integer} guests to his party.",
                "other": "{hostName} invited {guestCount,number,integer} guests to their party."
            }
          }
        }
      }
    }
  }
}
上面的示例显示了如何通过嵌套结构化资源值来定义依赖于两个参数(一个是复数参数,另一个是选择参数)的本地化消息分支。添加了第三个参数{hostName}是为了展示在复数和选择分支中包含其他参数的可能性,此类参数与复数和选择项目的选择无关。
转义语法
PUFF-J遵循字符串的标准JSON定义:
字符串是由零个或更多个Unicode字符组成的序列,括在双引号中,使用反斜杠转义符。字符表示为单个字符串。字符串和C语言或Java字符串很相似。
多行字符串
JSON根据自身定义,不支持多行字符串。任何换行符都必须替换为\n。
Last updated: 2025年9月30日

