GT JSX 数据格式

General Translation JSX 精简数据格式参考

GT JSX 数据格式是 General Translation 库使用的一种精简数据格式,用于在你的 React 应用中表示翻译后的 UI。

介绍:JSX 树

React 将 JSX 树表示为具有以下结构的对象:

type Element = {
    type: string;
    props: {
        children: JSXTree[] | JSXTree;
        // ...其他 props
    };
    // ...其他属性
};
type JSXTree = Element | string;

GT JSX 是该 JSX 树结构的压缩表示,General Translation 的库使用它在你的 React 应用中呈现已翻译的 UI。

参考

type Element = {
    t?: string; // 标签名
    c?: (Element | Variable | string)[]; // 子节点
    i?: number; // 元素的 GT ID
    d?: {
        b?: Record<string, Element | Variable | string>; // 分支
        t?: "p" | "b"; // 分支转换类型(复数或分支)
        pl?: string; // 占位符
        ti?: string; // 标题
        alt?: string; // 替代文本
        arl?: string; // aria-label
        arb?: string; // aria-labelledby
        ard?: string; // aria-describedby
        s?: Record<string, string>; // 样式
    };
}
type Variable = {
    k: string; // 键
    v?: "v" | "n" | "c" | "d"; // 类型
    i?: number; // GT ID
}
type GTJSXTree = Element | Variable | string | (Element | Variable | string)[];

GT JSX:字符串

GT JSX 最简单的形式是字符串,表示一段静态文本。

例如:

<T>你好,世界!</T>

在 GT 的 JSX 中表示为:

"你好,世界!"

字符串数组也可作为有效的 GT JSX:

["你好,", "世界!"]

GT JSX:元素

GT 可以通过两种方式表示 JSX 的 Element 类型。

变量

第一种是变量,它是一个包含键和可选类型的简单对象。用于表示可在运行时变化的变量。

type Variable = {
    k: string; // `k` 表示键,即变量的名称
    v?: ( // 表示变量的类型,如果省略则默认为 `v`
        "v" | // `v`,通用变量
        "n" | // `n`,数值变量
        "c" | // `c`,货币变量
        "d" // `d`,日期时间变量
    );
    i?: number; // 变量的 GT ID
}

示例 1:Var

<T>你好,<Var>{name}</Var>!</T>

在 GT JSX 中表示为:

["你好,", { k: "_gt_var_1", i: 1 }, "!"]

未包含 name 属性的变量会根据其 GT ID 被分配唯一的内部名称

示例 2:Num

<T>计数为 <Num>{count}</Num></T>

在 GT 的 JSX 中表示为:

["计数为 ", { k: "count", v: "n", i: 1 }]

示例 3:使用 name 属性

<T>此产品售价为 <Currency name="cost">{amount}</Currency></T>

在 GT JSX 中表示为:

["此产品售价 ", { k: "cost", v: "c", i: 1 }]

元素

非变量元素使用以下数据结构表示:

请注意,这些属性均为可选。 一个空对象表示在与其原始对应项相同的位置有一个已翻译的元素,且其后代中不包含任何可翻译内容。 在实际使用中,i 始终会被包含。

type Element = {
    t?: string; // 标签名称
    c?: GTJSXTree | GTJSXTree[]; // 子元素
    i?: number; // 元素的 GT ID
    d?: { // data-_gt 属性
        b?: Record<string, GTJSXTree | GTJSXTree[]>; // 分支
        t?: "p" | "b"; // 分支转换类型(复数或分支)
        pl?: string; // 占位符
        ti?: string; // 标题
        alt?: string; // alt 属性
        arl?: string; // aria-label 属性
        arb?: string; // aria-labelledby 属性
        ard?: string; // aria-describedby 属性
        s?: Record<string, string>; // 样式
    }
}

示例 1:基础标签

<T>你好,<b>世界</b>!</T>

在 GT 的 JSX 中表示为:

["你好,", { c: "world", i: 1 }, "!"]

示例 2:嵌套,带变量

<T><b>你好</b>,我叫<i><Var>{name}</Var></i></T>

可在 GT JSX 中表示为:

[
    { t: "b", c: "你好", i: 1 },
    ",我叫 ",
    { 
        t: "i",
        c: { k: "_gt_var_3", i: 3 }, 
        i: 2 
    }
]

示例 3:带复数

<T>
    <Plural 
        n={count} 
        one={<>我有 <Num>{count}</Num> 项</>} 
        other={<>我有 <Num>{count}</Num> 项</>}
    />
</T>

在 GT JSX 中可表示为:

{ 
    i: 1,
    d: {
        t: "p",
        b: {
            one: {
                c: ["我有", { k: "_gt_num_4", v: "n", i: 3 }, "个项目"],
                i: 2 
            },
            other: {
                c: ["我有", { k: "_gt_num_4", v: "n", i: 3 }, "个项目"],
                i: 2 // 注意:并行分支使用相同的 ID
            }
        }
    }
}

GTJSX 类型

type GTJSXTree = Element | Variable | string | (Element | Variable | string)[];

GT ID

GT ID 会按照深度优先、顺序遍历的方式,从 1 开始依次分配给 JSX 树中的元素和变量。

当存在 <Branch><Plural> 等分支组件时,并行分支会被分配相同的 GT ID。这样即使某种语言的分支数多于另一种语言(例如某些语言有更多复数形式),也能创建具有正确 props 和逻辑的元素。

GT JSX JSON 文件

每棵 JSX 树都表示一个 <T> 组件的 children。 这些组件被集中存放在翻译用的 JSON 文件中。 这些文件中存放的 JSON 对象类型对应于:

type GTJSXFile = {
    [key: string]: GTJSXTree;
}

其中,key 可以是用户自定义的,也可以是原始语言 GTJSXTree 的哈希值;GTJSXTree 指的是上文所述的 GT JSX 树的类型。

完整示例

所编写的 JSX:

<T>
    <b>Alice 的</b>满意<i>客户</i>
</T>

将表示为如下 JSX 树:

[
    {
        type: "b",
        "props": {
            "children": "Alice的"
        }
    },
    " 满意的 ",
    {
        type: "i",
        "props": {
            "children": "客户"
        }
    }
]

这将被最小化为 GT JSX:

[{ t: "b", c: "Alice 的", i: 1 }, " 开心的 ", { t: "i", c: "顾客", i: 2 }]

翻译成西班牙语后,GT JSX 将为:

[{ c: "客户", i: 2 }, " 开心 ", { c: "Alice 的", i: 1 }]

它会被存储在一个如下所示的文件中:

{ "abc123": [{ "c": "El cliente", "i": 2 }, " feliz ", { "c": "de Alice", "i": 1 }] }
abc123 是原始英文 GT JSX 树的哈希,而非西班牙语译文的哈希。

当西班牙语译文与原始 JSX 树对齐后,将得到如下结果:

[
    {
        type: "i",
        "props": {
            "children": "客户"
        }
    },
    "快乐的",
    {
        type: "b",
        "props": {
            "children": "Alice的"
        }
    }
]

随后即可按预期的界面呈现:

<><i>客户</i> 快乐 <b>属于 Alice</b></>

哈希

哈希算法、哈希长度待定

在运行时避免使用哈希

为避免在运行时计算哈希,这些库提供了可选的 fallback 机制,允许用户指定一个 ID。

<T id="example">
    你好,世界
</T>

如果翻译 JSON 文件中包含该 ID,将优先使用该 ID,而不是哈希值。

{ "example": "你好,世界" }

本指南如何?