Обычный словарь

Формат словаря для импорта 辞書形式

od-dict/1 · контракт импорта пользовательских словарей

Приложение «Обычный японский словарь» умеет подключать ваши собственные словари. Один словарь — это один JSON-файл в формате od-dict/1. Вы готовите такой файл, конвертируя существующий словарь (например, словарь Yomitan) — вручную или с помощью LLM, — а приложение строит из него свою базу.

Формат — это надмножество jmdict-simplified (scriptin/jmdict-simplified): корректное слово из jmdict-simplified уже валидно здесь, так что при таком источнике конвертация почти не нужна.

Эта страница — удобочитаемая выжимка. Полный, сверенный с кодом контракт лежит в репозитории: docs/od-dict-format.md.

1. Файл целиком

{
  "format": "od-dict/1",          // обязательно, точная строка
  "source": "custom",             // обязательно; id этого словаря (§2)
  "metadata": {                   // необязательно; только для отображения/происхождения
    "title": "My Dictionary",
    "license": "CC BY-SA 4.0",
    "url": "https://…"
  },
  "tags": { "v1": "Ichidan verb" }, // необязательно; НЕ используется (§6)
  "words": [
    {
      "id": "1259420",            // обязательно; УНИКАЛЕН в пределах файла (§3)
      "kanji": ["食べる"],         // необязательно; опустите для слов только из каны
      "kana":  ["たべる"],         // хотя бы одна форма (kanji или kana) обязана быть
      "sense": [                  // обязательно; ≥1
        {
          "partOfSpeech": ["v1"], // необязательно; ТОЛЬКО коды POS, не текст (§4)
          "misc": ["transitive"], // необязательно; свободные подписи, показываются как есть
          "gloss": [              // переводы
            { "lang": "ru", "text": "есть; кушать" },
            "пожирать"            // голая строка = { lang: "ru", origin: "original" }
          ],
          "examples": [ ["ご飯を食べる", "есть рис"] ],  // необязательно; пары [японский, перевод]
          "references": []        // необязательно; перекрёстные ссылки (§5)
        }
      ]
    }
  ]
}

2. Верхний уровень

полеобяз.значение
formatдаРовно "od-dict/1".
sourceдаСтрока-id всего словаря. Стабильный слаг ("custom", "jitendex"). Это пространство имён для id и перекрёстных ссылок. Неизвестный источник просто ранжируется после встроенных словарей — никогда не отвергается.
metadataнетСвободный объект (title, license, url). Хранится только для отображения; сборщик его не читает.
tagsнетКарта код → подпись для совместимости с jmdict-simplified. Не используется (§6). Нужные подписи кладите прямо в misc.
wordsдаМассив статей.

Безопасность: задумано принудительно выставлять source = "custom" при импорте, чтобы файл не выдал себя за "warodai"/"jmdict". Это пока не реализовано — считайте source рекомендательным до появления проверяющего импортёра.

3. Слова и идентичность

{
  "id": "1259420",
  "homograph": "II",          // необязательно; различитель, если head+чтение совпадают
  "label": "colloquial",      // необязательно; тег уровня статьи, показывается на карточке
  "isExpression": true,       // необязательно; фраза из нескольких слов
  "common": true,             // необязательно; высокочастотная статья
  "kanji": ["食べる", "喰べる"],  // 0…n письменных форм
  "kana":  ["たべる"],          // 0…n чтений
  "sense": [ /* … */ ]
}

Идентичность — это (source, id), и она UNIQUE.

  • id обязан быть уникальным в файле. Таблица пишется обычным INSERT с ограничением UNIQUE(source, source_entry_id), поэтому дубликат id роняет и прерывает весь батч импорта — не молча. Берите собственный id источника; если его нет — синтезируйте стабильный (счётчик или сам заголовок).
  • Элементы kanji/kana — голая строка (норма) или объект jmdict-simplified { "text": "…" }; читается только text, остальное принимается, но отбрасывается. Голая строка предпочтительнее.
  • У слова обязана быть хотя бы одна форма. Для японского давайте kana; для слов только из каны опускайте kanji.

Группировка. Приложение показывает одну карточку на группу (заголовок + чтение), объединяя словари. В своём файле выдавайте одно слово на статью — все письменные формы в kanji, все чтения в kana, все значения в sense. Если источник разбивает статью на строки (Yomitan — через общий sequence), сначала слейте их в одно слово, иначе получите несколько карточек на одну статью.

4. partOfSpeech — единственное поле, тихо ломающее поиск

Это единственное поле, где неверное значение убирает слово из поиска без всякой ошибки. POS не влияет на поиск по словарной форме или чтению — 食べる и たべる находятся всегда. POS используется ровно в одном месте: ворота деинфлексии — нахождение слова по словоформе, которую набрал пользователь (食べた, 静かな).

POS статьивердиктэффект
отсутствует / не класс спряженияweakвсё равно находится, чуть ниже в ранге
класс спряжения, который совпалconfirmedнаходится, обычный ранг
класс спряжения, который не совпалrejectedвыбрасывается — не находится из этой словоформы

Не уверены в классе спряжения — опускайте partOfSpeech целиком. Отсутствие безопасно (weak), ошибка фатальна (rejected). Опустить лучше, чем угадать.

Правила:

  • Только коды, никогда свободный текст. Читаемые подписи («verb», «honorific») кладите в misc — там они показываются дословно.
  • Значимы только классы спряжения. Прочие коды (n, exp, vt, adv …) поиском игнорируются — безвредны, но бесполезны.
  • Ловушка Yomitan: никогда не копируйте схлопнутый v5. У движка нет класса v5 — нужен конкретный (v5k, v5r …). Голый "v5" не совпадает ни с чем → rejected, то есть хуже, чем без тега. Берите конкретный код из definitionTags, либо разворачивайте по последней кане словарной формы, либо опускайте.

Классы спряжения, которые понимает движок:

v1                              ичидан (-ru):        食べる, 見る
v5u v5k v5g v5s v5t v5n v5b v5m v5r   годан, по финальной кане словарной формы
vk                              来る (kuru)
vs vs-i vs-s                    する-глаголы
adj-i                           い-прилагательные:    高い, 良い
adj-na                          な-прилагательные:    静か, 綺麗

5. Глоссы, языки и перекрёстные ссылки

"гулять"                                  // голая строка = { "lang": "ru", "origin": "original" }
{ "lang": "en", "text": "to take a walk" }
{ "lang": "ru", "text": "то же, что {0}", "origin": "original" }
  • Голая строка — это русский (lang: "ru"). Для любого другого языка нужен объект с lang.
  • origin"original" (по умолчанию) или "machine" (создано ИИ); только отображение.
  • Как lang влияет на поиск: глосс с lang: "ru" попадает в русский поисковый канал, любой другой lang — в английский. Канал выбирается по письменности запроса: кириллица → ru, латиница → en. Поэтому для японско-русского словаря глоссы должны быть lang: "ru", иначе кириллический поиск их не найдёт. Одноязычный японский глосс (lang: "ja") попадает в en-канал и фактически становится только отображаемым — слово всё ещё находится по заголовку и чтению, но не по тексту определения.

Перекрёстные ссылки — ICU-плейсхолдеры {0}, {1} … в тексте глосса, разрешаемые через массив references:

{
  "gloss": [ { "lang": "ru", "text": "вежливая форма {0}" } ],
  "references": [ { "label": "", "to": "1234567", "text": "言う" } ]
}
  • {0} заменяется на месте на references[0].text как нажимаемая ссылка.
  • to — это id целевого слова в этом же файле; ссылка открывает { source: <этот словарь>, id: to }. Несуществующий to просто ничего не открывает.
  • label — маркер связи ("see", "cf."), для ссылок без {n} (рендерятся после глосса). Литеральная { в тексте пишется как '{' (ICU-экранирование).

6. Чего формат НЕ несёт

Эти данные приложение добавляет само, по поверхности/чтению, чтобы все словари были согласованы — их не нужно задавать: тоновое ударение, фуригана, ромадзи/Поливанов, частотность/«common», аудио/TTS, уровни JLPT/WaniKani, разбор кандзи. Также не используются (хотя принимаются ради совместимости): верхнеуровневый tags и пер-форменные/пер-глоссовые extras. Карточка рендерит misc/field/dialect как свободные строки дословно — раскрытия кодов нет, кладите туда читаемые подписи.

7. Минимальные примеры

Самое маленькое слово — только кана и один русский глосс:

{ "id": "w1", "kana": ["ありがとう"], "sense": [ { "gloss": ["спасибо"] } ] }
// (a) JA→EN годан. POS — КОНКРЕТНЫЙ класс v5k, а не "v5" из Yomitan.
{ "id": "1578850", "kanji": ["書く"], "kana": ["かく"],
  "sense": [ { "partOfSpeech": ["v5k", "vt"],
    "gloss": [ { "lang": "en", "text": "to write; to compose; to pen" } ] } ] }

// (b) JA→RU с внутренней перекрёстной ссылкой.
{ "id": "敷衍", "kanji": ["敷衍", "敷延"], "kana": ["ふえん"],
  "sense": [ { "gloss": [ { "lang": "ru", "text": "развёртывание мысли (ср. {0})" } ],
    "references": [ { "label": "", "to": "演繹", "text": "演繹" } ] } ] }

// (c) な-прилагательное только из каны. adj-na — класс деинфлексии, тег уместен.
{ "id": "1000230", "kana": ["きれい"],
  "sense": [ { "partOfSpeech": ["adj-na"], "gloss": ["красивый; чистый; опрятный"] } ] }

8. POS-allowlist

Помещайте код в partOfSpeech, только если он один из этих (или подкласс jmdict, начинающийся с одного из них). Всё прочее — человеческую подпись в misc, а здесь опустить.

v1
v5u v5k v5g v5s v5t v5n v5b v5m v5r
vk
vs vs-i vs-s
adj-i
adj-na

Схлопнутый Yomitan "v5" → развернуть по финальной кане: う→v5u く→v5k ぐ→v5g す→v5s つ→v5t ぬ→v5n ぶ→v5b む→v5m る→v5r. Не уверены — опускайте.

← На главную