Перейти к основному содержимому

Генератор template

Используйте, когда нужны реалистичные «настоящие» данные — имена, даты рождения, страны — или технические идентификаторы — UUID, e-mail, IBAN, налоговые номера, — которые не хочется выдумывать руками. type="template" берёт значение из встроенного источника; атрибут value — это адрес через точку, выбирающий конкретный источник, и многие шаблоны учитывают локаль.

Неизвестный адрес — это TDC071: tdcv2 check сообщает о нём ещё до первой строки, подчёркивая ошибочное значение и подсказывая ближайший существующий адрес.

Вывод — иллюстративный

Значения на этой странице получены с фиксированным seed, поэтому они воспроизводимы, но точные строки могут отличаться между версиями ядра. Считайте их примерами формы, а не гарантией.

Зачем это, а не обычный список

С text имена приходится писать руками — короткий список, который повторяется и не локализован. template вместо этого достаёт значения из большого встроенного пула, на нужном языке и без единой строки данных в конфиге:

<sequence name="Manual"><gen type="text" value="Иван,Пётр,Анна"/></sequence>
<sequence name="Tpl"><gen type="template" value="person.male.firstName"/></sequence>
./run demo.tdc
руками=Иван   шаблон=Олег
руками=Пётр   шаблон=Владимир
руками=Анна   шаблон=Андрей
руками=Иван   шаблон=Пётр
руками=Пётр   шаблон=Сергей

Ручной список крутит одни и те же три значения; шаблон достаёт из большого встроенного пула.

Целая карточка человека

Несколько шаблонов вместе собирают согласованную запись: пол берётся первым, а имя подтягивается под него через parent. Итоговая строка собирается в блоке <data>:

<env count="6" seed="demo" local="ru">
<sequence name="Gender"><gen type="template" value="person.gender"/></sequence>
<sequence name="Man" parent="Gender.мужчина">
<gen name="First" type="template" value="person.male.firstName"/>
<gen name="Last" type="template" value="person.lastName"/>
</sequence>
<sequence name="Woman" parent="Gender.женщина">
<gen name="First" type="template" value="person.female.firstName"/>
<gen name="Last" type="template" value="person.lastName"/>
</sequence>
<sequence name="Bday">
<gen type="template" value="person.b_day" youngest="18" oldest="70" format="DD.MM.YYYY"/>
</sequence>
</env>
./run person.tdc
женщина: Юлия Батый, 28.11.1985
мужчина: Дмитрий Яблоков, 31.08.2001
женщина: Екатерина Троян, 25.04.1984
мужчина: Олег Верста, 20.05.1998
мужчина: Владимир Кравчук, 28.08.1981
женщина: Ольга Верста, 20.04.1970

Мужским строкам достаются мужские имена, женским — женские, и всё это нигде не написано руками. Дальше страница проходит по каждому семейству шаблонов с реальным выводом.

Персональные данные

АдресЧто выдаётЗависит от локали
person.male.firstNameМужское имя86
person.female.firstNameЖенское имя86
person.lastNameФамилия (мужские + общие фамилии локали)86
person.male.diagnosisМужской диагноз + общие диагнозы86
person.female.diagnosisЖенский диагноз + общие диагнозы86
person.genderСлучайный пол; ярлык берётся из локали86
person.b_dayДата рождения в заданном форматетолько формат

Все шесть есть в каждом из 86 языковых пакетов реестра — список см. в каталоге. Любая другая локаль получает TDC217 — он называет локали, где адрес есть, вместо того чтобы дать прогону гадать.

Почему lastName смешивает два пула

person.lastName склеивает мужские фамилии с общими фамилиями локали (теми, что одинаковы для обоих полов). В некоторых языках это различие важно: склоняемые фамилии имеют отдельные мужскую и женскую формы, а несклоняемые — общие. Поэтому пул собран именно так, а не строго «только мужские».

Имена — мужские и женские

Один и тот же генератор, по одному адресу на каждый пол:

<sequence name="M"><gen type="template" value="person.male.firstName"/></sequence>
<sequence name="F"><gen type="template" value="person.female.firstName"/></sequence>
./run names.tdc (local=ru)
муж=Дмитрий    жен=Анна
муж=Андрей     жен=Мария
муж=Максим     жен=Екатерина
муж=Олег       жен=Ольга
муж=Сергей     жен=Юлия
муж=Владимир   жен=Ирина

Два отдельных адреса нужны, когда пол строки уже задан (как в примере с согласованной карточкой выше). Если же пол хочется выбрать случайно, сначала берут один жребий по person.gender (см. раздел ниже).

Фамилии и диагнозы

person.lastName и гендерные адреса person.*.diagnosis работают так же — выбираете адрес, получаете значение из пула:

<sequence name="L"><gen type="template" value="person.lastName"/></sequence>
<sequence name="D"><gen type="template" value="person.male.diagnosis"/></sequence>
./run patient.tdc (local=ru)
фамилия=Яблоков    диагноз=Гипертония
фамилия=Кравчук    диагноз=Сахарный диабет 2 типа
фамилия=Верста     диагноз=Бронхиальная астма
фамилия=Строяков   диагноз=Хронический гастрит
фамилия=Салогуб    диагноз=Мигрень
фамилия=Долгих     диагноз=Гипертония

Пулы диагнозов разделены по полу для правдоподобия — person.female.diagnosis берёт из женского списка, смешанного с общими состояниями, — поэтому у них тот же раздел male / female, что и у имён. Используйте их для синтетических медицинских фикстур, где ярлык должен просто выглядеть правдоподобно, а не быть клинически точным.

person.gender — ярлык, зависящий от локали

person.gender — это не фиксированная строка Male / Female, а ярлык из списка текущей локали (примерно 50/50). Именно эти строки вы передаёте ключом в parent, так что смена локали меняет и ключ, на который вы сопоставляетесь:

<sequence name="Gender"><gen type="template" value="person.gender"/></sequence>
./run gender.tdc (локализация: en и ru)
local="en"     local="ru"
Male           мужчина
Male           мужчина
Female         женщина
Male           мужчина
Female         женщина
Male           мужчина

Под local="en" ключи — Male / Female; под local="ru" тот же жребий даёт мужчина / женщина. В первом случае сопоставляйтесь через parent="Gender.Male", во втором — parent="Gender.мужчина".

Локализация — один адрес, два языка

Адрес не меняется — меняется только local у <env>. Вот person.male.firstName + person.lastName, отрендеренные один раз по-английски и один раз по-русски, чтобы показать, как один и тот же конфиг даёт локализованный вывод:

./run names.tdc (локализация: en и ru)
local="en"           local="ru"
Ahmed Spangler       Пётр Строяков
Griffin Richey       Дмитрий Строяков
Zavier Fong          Михаил Строяков
Emilio Halstead      Дмитрий Салогуб
Cullen Bristol       Андрей Долгих
Titan Bryant         Андрей Белкин

Русская колонка — демонстрация локализации: суть в том, что один адрес ложится на тот пакет данных, который выбирает локаль. Под local="en" по умолчанию берётся английский.

Местоположение

АдресЧто выдаётЗависит от локали
location.countryНазвание странывсе 86 пакетов
<sequence name="C"><gen type="template" value="location.country"/></sequence>
./run country.tdc (local=en)
Libya
Japan
Wallis and Futuna Islands
Colombia
Lesotho
Iceland

Список локализован: под local="ru" выходят русские названия (Словения, Латвия, Лаос), под local="es" — испанские.

Во всех локалях, у которых есть пакет

location.country есть во всех 86 языковых пакетах реестра. Списки при этом разной длины: пакет называет те страны, для которых в его языке есть названия, поэтому длина идёт от 23 до 247, посередине 206, в английском 237. У локали без пакета списка нет: адрес выдаёт ошибку, а не откатывается на английский.

Адреса location.city не существует. Города, регионы и почтовые индексы живут под geo.* — это следующий раздел, — потому что приходят из странового пакета, а не из языкового.

У восьми из этих стран в названии запятая

Congo, Republic of (Brazzaville), Korea, Republic of (South Korea), Micronesia, Federal States of — восемь из 237 английских записей записаны так, как их пишет список ISO: уточнение после запятой. Положите такую запись прямо в строку CSV — и строка разъедется:

<line><data>${{Id}},${{Country}}</data></line>
./run csv.tdc — во второй строке теперь три поля, а не два
1,Mongolia
2,Korea, Republic of (South Korea)
3,India

Ответ у движка уже есть. Фильтр csv берёт значение в кавычки, причём всегда, а не когда сочтёт нужным: читатель вывода видит одно правило, применённое ко всем строкам, вместо того чтобы гадать, какие значения оказались особенными.

<line><data>${{Id}},${{Country|csv}}</data></line>
./run csv.tdc — те же три строки, и вторая снова одно поле
1,"Mongolia"
2,"Korea, Republic of (South Korea)"
3,"India"

Запятая может оказаться в любом значении из пакета — в названии компании, в улице, в должности. csv — ответ для всех них, а колонка со странами просто первое место, где вы с этим сталкиваетесь.

geo.* — география из страновых пакетов

location.* — это голос языкового пакета: названия стран на языке читателя — одни и те же 233 в каждой локали, кроме английской, где их 237. geo.* — голос странового пакета: места внутри одной страны и список стран, взвешенный по числу жителей.

АдресОткудаЧто даёт
geo.countryязыковой пакетНазвание страны, взвешенное по населению
geo.capitalByCountryязыковой пакетСтолица, по ключу-стране
geo.currencyByCountryязыковой пакетНазвание валюты, по ключу-стране
geo.currencyCodeByCountryязыковой пакетКод валюты ISO, по ключу-стране
geo.directionязыковой пакетnorth, south-east, …
<страна>.geo.cityстрановой пакетГород этой страны
<страна>.geo.<единица>страновой пакетАдминистративная единица — словом этой страны
<страна>.geo.<индекс>страновой пакетПочтовый индекс, тоже словом этой страны
<страна>.geo.streetNameстрановой пакетНазвание улицы

Пять строк языкового пакета сегодня есть в en и ru; попросите geo.country при local="es" — прогон остановится с TDC217 и назовёт локали, где эти данные есть. Страновые строки — то место, где таблица вынуждена быть расплывчатой, и причина в самом предмете: страновой пакет называет свои единицы так, как их называет эта страна.

ЛистВ скольких страновых пакетах
geo.city147 из 152
geo.postalCode76
geo.region68
geo.streetName45 (плюс 38 с geo.streetNamed)
geo.province35
geo.municipality21
geo.district21
geo.zip16

Какая-нибудь административная единица есть в 137 страновых пакетах из 152, почтовый индекс — в 92, под именами вроде department, canton, governorate, voivodeship, prefecture, zip, postalCode, eircode, cep, cap, cpa. Поэтому usa.geo.province — не адрес: у США есть usa.geo.state и usa.geo.zip.

Угадывать — это и есть предусмотренный способ узнать. Напишите тот лист, которого ждёте, и запустите check — диагностика перечислит то, что есть на самом деле:

tdcv2 check geo.tdc
error[TDC071]: unknown template path "usa.geo.province"
--> geo.tdc:4:35
|
4 |       <gen type="template" value="usa.geo.province"/>
|                                   ^^^^^^^^^^^^^^^^
|
note: Beside it: usa.geo.city, usa.geo.county, usa.geo.state, usa.geo.stateAbbr, usa.geo.streetName, usa.geo.streetNamed, … (1 more).

aborted: 1 error

Что несёт конкретная страна, смотрите в Пакетах данных.

location.country и geo.country — разные списки

Оба настоящие, и отвечают они на разные вопросы:

<sequence name="Flat"><gen type="template" value="location.country"/></sequence>
<sequence name="Weighted"><gen type="template" value="geo.country"/></sequence>
<sequence name="City"><gen type="template" value="usa.geo.city"/></sequence>
./run geo.tdc (count=5, local=en)
Burundi | Indonesia | Los Angeles
Singapore | China | New York
Congo, Republic of (Brazzaville) | India | Phoenix
Estonia | China | Chicago
Cyprus | United States | Houston

location.country выбирает равномерно из 237 названий, поэтому Бурунди выпадает так же часто, как Китай. Это то, что нужно, когда колонка означает «любая страна» — когда проверке всё равно, какая именно.

geo.country выбирает из 36 стран, взвешенных по населению, поэтому Китай и Индия попадаются часто, а хвост — редко. Это то, что нужно, когда колонка должна выглядеть как клиентская база. Равномерные страны в таблице клиентов — первейший признак того, что данные сгенерированы.

Ни один из двух не «правильнее». Берите тот, чей вопрос совпадает с вашей колонкой.

Даты

Оба шаблона дат используют те же токены формата (и зависящие от локали L / LL), что и генератор date.

person.b_day — дата рождения

АтрибутПо умолчаниюОписание
oldest80Максимальный возраст (лет)
youngest10Минимальный возраст (лет)
formatLФормат вывода (TDC date-format)
localиз <env>Локаль для локализованных форматов (L, LL)
precisionmillisecondday, second или millisecond

Используйте, когда записи нужна дата рождения с ограничением по возрасту — окно youngest / oldest держит всех внутри правдоподобного возрастного диапазона.

Одна и та же дата рождения, два умолчания

person.b_day и <gen type="date" value="birth"> считают одно и то же из одного возрастного окна — и умолчания у них разные: шаблон берёт миллисекунду, генератор дат — день. При формате без времени разницы не видно. Попросите время — и разница окажется всем значением:

person.b_day 1976-07-06 11:28:39.539
<gen type="date" value="birth"> 1999-01-21 00:00:00.000

Пишите precision="day" на шаблоне, когда дата рождения должна быть датой и ничем больше. Это не только косметика: округление до дня может сдвинуть значение в соседнюю дату, поэтому две точности не всегда дают один и тот же день.

<gen type="template" value="person.b_day" youngest="18" oldest="65" format="YYYY-MM-DD"/>
./run bday.tdc
1997-07-03
1988-10-22
2000-09-12
1987-08-06
1972-10-18
1984-06-09

Локализованные названия месяцев через LL

Формат LL пишет месяц словом на языке локали — сама дата не меняется, меняется только её написание:

./run bday.tdc (format=LL, локализация: en и ru)
local="en"            local="ru"
November 18, 1999     18 ноября 1999 г.
February 22, 1973     22 февраля 1973 г.
April 15, 1999        15 апреля 1999 г.
April 30, 1971        30 апреля 1971 г.
June 17, 1986         17 июня 1986 г.
September 17, 1988    17 сентября 1988 г.

date.range — дата из диапазона

АтрибутПо умолчаниюОписание
rangeОбязательный. "YYYY.MM.DD - YYYY.MM.DD"
formatLФормат вывода
localиз <env>Локаль для локализованных форматов
precisiondayday, second или millisecond

range принимает только такое написание: две даты через точки, разделённые дефисом. Написание "2020-01-01..2020-12-31", которое использует <gen type="date">, получает отказ TDC073, а не читается неправильно молча.

Используйте для любой даты, кроме дня рождения — дата заказа, регистрации, события — везде, где нужен равномерный жребий между двумя явными границами.

<gen type="template" value="date.range" range="2020.01.01 - 2025.12.31" format="DD.MM.YYYY"/>
./run event.tdc
29.08.2024
20.07.2023
25.01.2025
25.05.2023
04.07.2021
29.12.2022

Та же локализация LL работает и здесь — поменяйте на format="LL", и месяц напечатается словом на активной локали (December 8, 2023 под en, 8 декабря 2023 г. под ru).

Отметка времени вместо даты

По умолчанию жребий прилипает к дню, поэтому формат, запрашивающий время, каждую строку печатает полночь. precision="second" разыгрывает и время суток:

<gen type="template" value="date.range" range="2020.01.01 - 2025.12.31"
precision="second" format="YYYY-MM-DD HH:mm:ss"/>
./run event.tdc (precision=second)
2024-08-10 07:59:57
2021-01-22 23:48:52
2021-06-06 17:44:00
2025-12-17 04:31:37

Это зеркало примечания про person.b_day выше: день рождения по умолчанию — миллисекунда, а диапазон — день, так что каждому нужна поправка в свою сторону, если нужно поведение другого.

Технические идентификаторы

Тот же type="template" строит и алгоритмические идентификаторы — UUID, e-mail, IBAN, номера карт, налоговые и документные номера с контрольной суммой. Два правила именования:

  • Глобальные идентификаторы несут префикс common.common.id.uuid, common.finance.iban, common.payment.card.pan, common.phone.e164.
  • Привязанные к стране начинаются с названия страны — russia.tax.inn_org, russia.docs.snils, brazil.tax.cpf, poland.docs.pesel.

Почему это не просто случайные цифры

У большинства «номеров-идентификаторов» есть контрольная цифра, посчитанная из остального номера (Луна, mod-11, ISO 7064, …). Десять случайных цифр забракует первая же проверка формата, так что тесты на них бесполезны. Эти шаблоны выдают значения, которые проходят свою контрольную сумму, но при этом заведомо ненастоящие — зарезервированные тестовые диапазоны, вымышленные префиксы, — поэтому их безопасно класть в демо, фикстуры и CI.

Идентификаторы и интернет

<gen type="template" value="common.id.uuid"/>
<gen type="template" value="common.id.ulid"/>
<gen type="template" value="common.internet.email"/>
<gen type="template" value="common.internet.ipv4"/>
<gen type="template" value="common.system.semver"/>
./run ids.tdc
common.id.uuid          b04b0159-d6a6-441f-b3cb-8941d2742bd0
common.id.ulid          609Q13BKAVCMD292YSS7RQ1HK9
common.internet.email   uak1benwm6@fixture-odkd82.test
common.internet.ipv4    192.168.102.101
common.system.semver    7.0.7

E-mail и домены используют зарезервированные IANA домены верхнего уровня (.test, .invalid, .example), а IP — частные диапазоны, так что ничего здесь не может столкнуться с реальным адресом. Также доступны: common.id.nanoid, common.id.object_id, common.internet.url, common.internet.mac, common.internet.slug, common.internet.username.

Финансы и платежи

<gen type="template" value="common.finance.iban"/>
<gen type="template" value="common.finance.bic"/>
<gen type="template" value="common.payment.card.pan"/>
<gen type="template" value="russia.bank.account"/>
./run finance.tdc
common.finance.iban       DE68702701363846402097
common.finance.bic        SAHTDENW5OW
common.payment.card.pan   4242420270136385
russia.bank.account       40802810546402097727

IBAN несёт корректную контрольную сумму ISO 7064 mod-97, номер карты (PAN) — корректную проверку Луна в тестовом диапазоне Visa (4242…), а российский счёт — корректный межполевой ключ mod-10; каждый проходит проверку формата, оставаясь ненастоящим.

Товары и устройства

<gen type="template" value="common.book.isbn13"/>
<gen type="template" value="common.product.ean13"/>
<gen type="template" value="common.device.imei"/>
<gen type="template" value="common.vehicle.vin"/>
./run products.tdc
common.book.isbn13     9790270136387
common.product.ean13   7027013638467
common.device.imei     702701363846407
common.vehicle.vin     0AK1BDNX8L5640209

Также доступны: common.book.isbn10, common.product.gtin14, common.product.upc_a, common.periodical.issn, common.device.iccid.

Безопасность и хеши

<gen type="template" value="common.security.api_key"/>
<gen type="template" value="common.security.otp"/>
<gen type="template" value="common.security.sha256"/>
<gen type="template" value="common.git.sha"/>
./run security.tdc
common.security.api_key   tdc_i1Hk26NbKrPdP5H5xnmFlk3XbH5rBSI9
common.security.otp       702701
common.security.sha256    b04b01595d6a6141fcc3cb08941d2742bd0800d71700...
common.git.sha            b04b01595d6a6141fcc3cb08941d2742bd0800d7

Также доступны: common.security.jwt, common.security.md5, common.security.sha1, common.security.totp_secret.

Телефоны

common.phone.e164 выбирает случайную страну; у каждой страны есть и свой адрес. Все выдают форму E.164 и используют вымышленные диапазоны, зарезервированные для кино/тестов (российские мобильные +7 9XX, британский Ofcom 07700 900xxx, …):

<gen type="template" value="russia.phone"/>
<gen type="template" value="common.phone.e164"/>
./run phones.tdc
russia.phone        +79702701363
russia.phone        +79682926087
common.phone.e164   +447700900829
common.phone.e164   +33670270136

Национальные налоговые и документные номера

У каждой страны есть своё семейство номеров с контрольной суммой. Одного российского набора хватает для большинства нужд:

<gen type="template" value="russia.docs.snils"/>
<gen type="template" value="russia.tax.inn_org"/>
<gen type="template" value="russia.tax.inn_person"/>
<gen type="template" value="russia.tax.ogrn"/>
./run ru-ids.tdc
russia.docs.snils       70270136346
russia.tax.inn_org      7027136386
russia.tax.inn_person   702713638455
russia.tax.ogrn         1707038464021

Десятки других стран доступны по тому же принципу — usa.docs.ssn, brazil.tax.cpf, poland.docs.pesel, germany.tax.vat, france.tax.siren и многие другие. Полный каталог по странам — в Справочнике.

<gen type="template" value="brazil.tax.cpf"/>
<gen type="template" value="poland.docs.pesel"/>
<gen type="template" value="germany.tax.vat"/>
<gen type="template" value="france.tax.siren"/>
./run world-ids.tdc
brazil.tax.cpf       64173916477
poland.docs.pesel    18302310570
germany.tax.vat      DE153363548
france.tax.siren     860356302

Четыре страны, четыре схемы, один тег: у CPF пара контрольных цифр по mod-11, у PESEL внутри дата рождения и цифра по mod-10, у немецкого VAT свой mod-11, у SIREN проверка по Луну. Все проходят проверку формата и при этом никому не принадлежат.

Параметры

Многие идентификаторы принимают параметры — передавайте их обычными атрибутами на <gen>. Любой параметр, который вы опустили, берётся случайно; заданный — закрепляется во всех строках. Например, зафиксируем домен e-mail:

<gen type="template" value="common.internet.email" domain="example.test"/>
./run email.tdc
u99o89qpeo@example.test
pk9p3g482c@example.test
vyjs5yc5n2@example.test
oau8cd92kv@example.test
g2z4nh4999@example.test

Страновые генераторы принимают свои параметры — код налоговой (tax_office), пол (sex), префикс, — и контрольная цифра всегда пересчитывается, чтобы остаться верной. Какие параметры принимает конкретный адрес, определяет стоящий за ним пакет данных: каждая локальная <sequence name="…"> в пакете — это один параметр. Неверный параметр — понятная ошибка (TDC072), никогда не тихая: TDC сообщает, что адрес на самом деле принимает.

Упрощённые варианты

При переезде этих генераторов в редактируемые пакеты часть редких параметров и «форматированных» вариантов (со скобками или дефисами) были сведены к основному виду. Контрольные суммы и основной формат сохранены; убрана только косметическая обёртка.

Как устроены контрольные цифры

Логика контрольной суммы не спрятана в скомпилированном коде — каждый пакет считает свою контрольную цифру декларативно тегом <compute>, прямо рядом с данными. Поменялись правила в стране — вы правите текстовый файл пакета, а не движок. Как устроен пакет — см. Пакеты данных.

Где хранятся данные шаблонов

Сейчас пулы шаблонов поставляются вместе с библиотекой и доступны через встроенные адреса, перечисленные выше. В планах — загружать любой файл с данными «декларативно» (с метаданными: что это, чем разделено, какой класс это разбирает), чтобы вы могли регистрировать свои пулы так же, как зарегистрированы встроенные. Пока же доступны ровно те встроенные шаблоны, что описаны здесь.

См. также

  • Дата — токены формата, которые используют эти шаблоны дат.
  • <compute> — как определяются контрольные суммы.
  • Справочник: генераторы — полный каталог идентификаторов.
  • Пакеты данных — откуда берутся данные шаблонов и как добавить свои.