Формат данных GT JSX

Справочник по минифицированному формату данных JSX от General Translation

Минифицированный формат данных GT JSX используется библиотеками General Translation для представления переведённого интерфейса в вашем приложении React.

Введение: деревья JSX

React представляет деревья JSX в виде объектов со следующей структурой:

type Element = {
    type: string;
    props: {
        children: JSXTree[] | JSXTree;
        // ...other props
    };
    // ...other attributes
};
type JSXTree = Element | string;

GT JSX — это компактное представление дерева JSX, которое библиотеки General Translation используют для отображения переведённого UI в вашем приложении React.

Справочные материалы

type Element = {
    t?: string; // tag name
    c?: (Element | Variable | string)[]; // children
    i?: number; // GT ID of the element
    d?: {
        b?: Record<string, Element | Variable | string>; // branches
        t?: "p" | "b"; // branch transformation type (plural or branch)
        pl?: string; // placeholder
        ti?: string; // title
        alt?: string; // alt
        arl?: string; // aria-label
        arb?: string; // aria-labelledby
        ard?: string; // aria-describedby
        s?: Record<string, string>; // style
    };
}
type Variable = {
    k: string; // key
    v?: "v" | "n" | "c" | "d"; // type
    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: "мир", 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 // note the same ID is used for parallel branches
            }
        }
    }
}

Тип GTJSX

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

GT IDs

GT IDs назначаются элементам и переменным в дереве JSX в порядке обхода в глубину и последовательно, начиная с 1.

Когда используются разветвляющие компоненты, такие как <Branch> или <Plural>, одинаковые GT IDs назначаются параллельным ветвям. Это нужно для того, чтобы даже если в одном языке ветвей больше, чем в другом (например, в языках с большим количеством форм множественного числа), можно было создать элементы с корректными пропсами и логикой.

JSON‑файлы GT JSX

Каждое дерево JSX представляет собой дочерние элементы компонента <T>. Компоненты хранятся вместе в JSON‑файлах переводов. Тип объекта JSON, сохраняемого в этих файлах, соответствует:

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

Где key — это либо заданное пользователем значение, либо хеш исходного языкового GTJSXTree, а GTJSXTree — это тип дерева GT JSX, описанного выше.

Полный пример

Исходный JSX:

<T>
    довольный <i>клиент</i> <b>Элис</b>
</T>

Будет представлено следующим деревом JSX:

[
    {
        type: "b",
        "props": {
            "children": "Элис"
        }
    },
    " довольна ",
    {
        type: "i",
        "props": {
            "children": "клиентка"
        }
    }
]

Это будет минифицировано в GT JSX следующим образом:

[{ c: "клиент", i: 2 }, " счастлив ", { c: "Элис", i: 1 }]

При переводе на испанский GT JSX будет таким:

[{ c: "Клиент", i: 2 }, " доволен ", { c: "Алисы", i: 1 }]

Он будет храниться в файле следующего вида:

{ "abc123": [{ "c": "Клиент", "i": 2 }, " доволен ", { "c": "Алисы", "i": 1 }] }
abc123 — это хеш исходного английского дерева GT JSX, а не испанского перевода.

После согласования испанского перевода с исходным деревом JSX будет получен следующий результат:

[
    {
        type: "i",
        "props": {
            "children": "Клиент"
        }
    },
    " счастлив ",
    {
        type: "b",
        "props": {
            "children": "Алисы"
        }
    }
]

Который затем можно отобразить как задуманный интерфейс:

<><i>Счастливый клиент</i> <b>Алисы</b></>

Хеши

Алгоритм хеширования и длина хеша будут определены позже

Избежание вычисления хешей во время выполнения

Чтобы не вычислять хеши на этапе выполнения, в библиотеках предусмотрен необязательный резервный механизм, позволяющий пользователю указать идентификатор.

<T id="example">
    Привет, мир!
</T>

Если ID присутствует в JSON‑файле перевода, он будет использован вместо хэша.

{ "example": "Привет, мир!" }

Насколько полезно это руководство?

Формат данных GT JSX