跳转到内容

翻译用户界面 ​

Pivotick 编写自己的文本:导轨、菜单、侧边栏、表格、拓展面板、历史记录、吐司以及它们后面的每个 title 工具提示和aria-label。 i18n 选项将所有内容都以您的语言显示。

js
const graph = new Pivotick(container, data, {
    i18n: { locale: 'fr' }, 
})

您交给库的文本将完全按照给定的方式绘制,并且不会查找:节点和边缘标签、面和图例标签、拓展名称、导轨模式、停靠选项卡、顶栏操作以及添加到上下文菜单的行。在传递它们之前翻译它们。

捆绑语言 ​

每个版本都包含内置 UI 的完整翻译:

语言语言环境区域标签示例
英语(默认)enen-GB, en-US
简体中文zhzh-CN, zh-Hans
法语frfr-FR, fr-CA
德语dede-DE, de-CH
葡萄牙语ptpt-BR, pt-PT

设置 i18n.locale 选择一项。区域标签使用相同的语言目录,同时保留自己的数字格式、排序和复数规则。中文目录使用简体字;没有单独的繁体中文翻译。没有捆绑目录的区域设置使用英语,除非您提供 messages 或 translate。省略 locale 保留现有的英文默认值;未检测到浏览器语言。

目录导出为 messagesEn、messagesZh、messagesFr、messagesDe、messagesPt,并一起导出为 bundledMessages,也可在浏览器版本中的 Pivotick 上使用。

选项 ​

属性类型默认值说明
localestring'en'BCP 47 标签,例如 'fr'、'de-CH' 或 'pt-BR'。它选择复数规则、数字的书写方式以及列表的排序方式。当给定时,它也被设置为图根的lang。
messagesPartial<Messages>{}通过按键覆盖。丢失的钥匙使用捆绑语言,然后是英语。
translate(key, params, fallback) => string | undefined通往您自己的翻译库的桥梁,要求提供 messages 没有的每个密钥。

语言是在构建图表时设置的。要切换它,请调用 graph.destroy() 并使用新选项再次构建图表。

如何找到消息 ​

按此顺序查找每段文本,第一个答案获胜:

  1. 该功能自己的选项,当它有一个时:UI.emptyState.render,UI.busyIndicator.label,UI.simplify.typeLabel,或传递给ctx.confirm()的title和confirmLabel。这些比任何目录都更具体。
  2. messages,你的键表。
  3. translate,你的桥梁。
  4. 捆绑语言,由locale选择。
  5. 英语,当该语言没有捆绑目录时。

支持部分覆盖:仅更改应用程序需要的术语,其余术语使用所选语言。不受支持的语言会在没有警告的情况下转为英语。

按键和消息 ​

每条消息都有一个稳定的点分键,以它出现的位置命名:'contextMenu.node.pin'、'history.menu.empty'、'sidebar.neighbors.connections'。这些键是从英文目录中键入的,因此在 TypeScript 中,拼写错误的键无法编译,您的编辑器会完成其余部分。

消息采用三种形式之一。

带有占位符的文本 ​

一个字符串,其中 {name} 为值所在位置:

js
messages: {
    'search.result.inGroup': 'dans {group}',
    'history.menu.count': '{count} sur {limit}',
}

数字的写入方式与语言环境在输入之前的写入方式相同:1,234位于'en'下,1.234位于'de'下,1 234位于'fr'下。按照写入的方式输入一个字符串。

复数形式 ​

取决于计数的消息是一个对象,该语言使用的每个复数类别都有一个条目。该类别是由浏览器的 Intl.PluralRules 从 count 参数中选择的,因此规则是 CLDR 为语言环境定义的规则。 other 是必需的,因为每种语言都有它。

js
// English and French need two forms; French counts zero as one
'count.nodes': { one: '{count} nœud', other: '{count} nœuds' },

// Polish needs four: 1 węzeł, 3 węzły, 5 węzłów, 1,5 węzła
'count.nodes': { one: '{count} węzeł', few: '{count} węzły', many: '{count} węzłów', other: '{count} węzła' },

类别为 zero、one、two、few、many 和 other。语言不使用的形式永远不会被选择,缺少的形式会回退到 other。

Fallback English 在任何语言环境下都保留英语复数规则。例如,将零视为单数的不受支持的语言仍然会从英语后备中获得“0 个节点”。

一个功能 ​

对于模板无法表达的任何内容,消息可以是原始参数和图形的 I18n 的函数:

js
'simplify.typeLabel': ({ count, type }, i18n) =>
    `${i18n.formatNumber(Number(count))} ${type === 'ip' ? 'adresses IP' : type}`,

无论其形式如何,消息都是文本。它作为文本插入,从未解析为 HTML,因此 <b> 显示为书面形式。

编辑 PO 翻译文件 ​

存储库的 locales/ 目录包含 gettext 模板、pivotick.pot 以及完整的 zh.po、fr.po、de.po 和 pt.po 文件。这些文件也包含在 npm 包中。在 Poedit 或任何 UTF-8 文本编辑器中打开 .po 文件。每个msgctxt是稳定的消息密钥; msgid是英文原文,msgstr是翻译。保持{placeholders}完好无损。对于复数条目,翻译每个msgstr[n]槽:中文只有other,法语、德语和葡萄牙语有one然后是other。运行时使用 Intl.PluralRules,many 回落到 other,而不是将 gettext 标头评估为 JavaScript。

从已完成 npm ci 的源签出:

sh
# Refresh the template and merge English changes into the PO files
npm run i18n:extract

# Edit locales/*.po, finish empty entries and review any fuzzy translations

# Compile PO files into the TypeScript catalogues used by every build
npm run i18n:compile

# Check completeness, source text, placeholders, plurals and generated files
npm run i18n:check
npm run test:i18n

将 PO 文件、模板和生成的 src/i18n/locales/*.ts 文件一起提交。不要直接编辑生成的文件。提取会保留现有翻译和译者注释,将更改的英文文本标记为 fuzzy,并将新条目留空。在编译之前解决这些条目。删除的密钥将从活动目录中删除。 npm run build 运行 i18n:check,因此不完整的翻译或过时的生成目录会使构建失败。这些命令使用现有的 Node 依赖项;无需安装系统 gettext。

要捆绑另一种语言,请将其语言环境和复数插槽映射添加到scripts/i18n.mjs,提取并翻译新的 PO 文件,编译它,然后从src/i18n/locales/index.ts导出生成的目录并将其添加到bundledMessages。对于仅限应用程序的语言,将目录传递为 i18n.messages 仍然有效,无需更改 Pivotick。

从 messagesEn {#catalogue} 开始目录 ​

英文目录在 ES 版本中导出为 messagesEn,在浏览器版本中导出为 Pivotick.messagesEn。它包含每个密钥及其英文文本,并且是纯数据,因此可以将其写为翻译人员开始的文件:

js
import { messagesEn } from 'pivotick'

console.log(JSON.stringify(messagesEn, null, 4))

将翻译后的文件加载回 messages。在 TypeScript 中,使用 satisfies Messages 声明的目录必须命名每个键,因此在后续版本中添加的键会显示为编译错误而不是杂散的英文字符串:

ts
import type { Messages } from 'pivotick'

export const messagesFr = {
    'count.nodes': { one: '{count} nœud', other: '{count} nœuds' },
    // …every other key
} satisfies Messages

使用 Partial<Messages> 来进行翻译。

连接您自己的翻译库 ​

如果您的应用程序已经通过库进行翻译,则 translate 保留一种事实来源和一种语言切换。为每个 messages 没有的键调用它,其中:

  • key:消息键,如'count.nodes';
  • params:原始值,数字未格式化,因此您的库自己的复数和数字规则适用;
  • fallback:英文文本,已填写。

返回一个字符串以使用它,或返回undefined以使用捆绑语言,然后使用英语。这里是 i18next,它的 count 参数已经驱动了它的复数:

js
const graph = new Pivotick(container, data, {
    i18n: {
        locale: i18next.language,
        translate: (key, params, fallback) => {
            const found = i18next.exists(`pivotick.${key}`)
            return found ? i18next.t(`pivotick.${key}`, params) : undefined
        },
    },
})

然后,您的资源文件以您的库自己的语法(i18next 中的"{{count}} nœuds")在相同的键下写入消息。

抛出的 translate 被捕获:控制台显示哪个键,并显示捆绑的语言(或不受支持的语言的英语)。该钩子仍然接收英语作为其 fallback 参数,保留桥接合同。当messages和translate都有密钥时,messages获胜并且translate不被询问。

数字、列表、排序和 lang ​

locale 涵盖的不仅仅是文字。在其下:

  • Counts 用 Intl.NumberFormat 书写:英语为“1,234 个节点”,德语为“1.234 Knoten”。后备英语还使用所选区域设置的数字格式。
  • 一个句子中的列表与Intl.ListFormat连接:“a、b和c”、“a、b et c”。
  • 表中的排序,邻居列表和拓展分类窗格使用Intl.Collator,因此“Émile”排在“Emma”旁边,瑞典语将“ä”放在“z”之后,“项目 10”排在“项目 9”之后。

当您声明 locale 时,图表的根元素将其获取为 lang,库在根外部安装的部分(工具提示、下拉列表、建议列表)也是如此。然后屏幕阅读器以正确的声音朗读界面。如果没有 locale,则不会设置 lang:库的文本是英语,但图表中的数据采用页面语言,并且页面自己的 lang 对其进行了最好的描述。

不支持从右到左布局:没有 dir 处理,并且界面在每种语言中都是从左到右布局的。

每个图一种语言 ​

每个图表都有自己的语言。一页上的两个图表可以使用两种语言,互不影响。

图内的图讲述其父图:簇内绘制的子图以及侧栏中的小邻居图。

库写入数据的文本以图形语言写入一次:历史条目的标签(“隐藏 3 个节点”)和新注释开头的正文。用另一种语言重建图表会使它们保持原样。

在代码中:graph.i18n和插件 ​

graph.i18n 是图的语言,与库编写的对象相同:

会员它的作用
locale正在使用的标签:您的,或'en'。
t(key, params?)内置消息的文本,通过上面的查找。
formatNumber(value, options?)区域设置数字中的数字。采用 Intl.NumberFormat 选项。
formatList(items, options?)项目作为句子连接起来。采用 Intl.ListFormat 选项。
compare(a, b)按照区域设置顺序的比较器,可以安全地按原样传递到 sort。
collator(options?)compare后面的Intl.Collator,默认为数字。

插件读取与ctx.i18n相同的对象。插件自己编写的文本是插件要翻译的文本,按 ctx.i18n.locale 键;通过 ctx.i18n 格式化其计数并对其列表进行排序,使它们与图表的其余部分保持一致。

js
const nodeCounter = {
    name: 'node-counter',
    install(ctx) {
        const words = { fr: 'Total', de: 'Gesamt' }
        const label = words[ctx.i18n.locale.split('-')[0]] ?? 'Total'
        const count = ctx.i18n.formatNumber(ctx.graph.getNodes().length)
        // …draw `${label}: ${count}`
    },
}

保持原样的东西 ​

  • 您的数据。 节点和边标签、属性名称和值按原样绘制。没有声明标签的属性名称按其键的标题大小写(first_seen 读取“First Seen”);声明 nodePropertiesMap 或 edgePropertiesMap 以您的语言命名。属性列表在您自己的键上方添加的 id 行被写入为键本身。
  • 开发人员文本。控制台警告和引发的错误仍为英文。
  • 按键字形。⌃ ⇧ ⌥ ⌘按原样显示。键名是消息(keys.ctrlCombo、keys.escape),因此德语界面可以说Strg+Z。
  • 节点边缘上的徽章计数(2k、99+),必须适合三个字符。