// Zostaví slovník platných pokusov pre Slovo dňa z Hunspell slovníkov (.aff + .dic). // node scripts/build-dict.mjs (zdroje z dict-src/) // node scripts/build-dict.mjs [ ...] // Súbory môžu byť aj .gz. Pri viacerých dvojiciach sa tvary zjednotia (EN = en_US + en_GB-ise). // Rozvinie korene do tvarov (predpony, prípony, dvojúrovňové prípony, cirkumfix), nechá len slová // s malým začiatočným písmenom, dĺžky 5 až 7, zložené len z písmen abecedy daného jazyka. // Vynechá korene označené ako zakázané alebo tabu (FORBIDDENWORD, NOSUGGEST) a vulgarizmy a nadávky // zo zoznamov nižšie (kontrolujú sa korene aj hotové tvary). // Výstup: src/dict/.ts s textom v kódovaní „spoločná predpona + zvyšok“ (pozri src/dictionary.ts) // a s licenčnou poznámkou NOTICE, ktorú hra drží v pamäti, aby ostala aj v minifikovanom JS. import { readFileSync, writeFileSync, mkdirSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { gunzipSync, gzipSync } from 'node:zlib'; const here = dirname(fileURLToPath(import.meta.url)); const src = (name) => join(here, '..', 'dict-src', name); const [lang, ...paths] = process.argv.slice(2); const DEFAULT_SOURCES = { sk: [[src('sk_SK.aff.gz'), src('sk_SK.dic.gz')]], cs: [[src('cs_CZ.aff.gz'), src('cs_CZ.dic.gz')]], en: [[src('en_US.aff.gz'), src('en_US.dic.gz')], [src('en_GB-ise.aff.gz'), src('en_GB-ise.dic.gz')]], }; if (!lang || !DEFAULT_SOURCES[lang] || paths.length % 2 !== 0) { console.error('použitie: node scripts/build-dict.mjs [ ...]'); process.exit(2); } const sources = paths.length ? paths.reduce((acc, p, i) => (i % 2 ? acc[acc.length - 1].push(p) : acc.push([p]), acc), []) : DEFAULT_SOURCES[lang]; const ALPHABET = { sk: 'aáäbcčdďeéfghiíjklĺľmnňoóôpqrŕsštťuúvwxyýzž', cs: 'aábcčdďeéěfghiíjklmnňoópqrřsštťuúůvwxyýzž', en: 'abcdefghijklmnopqrstuvwxyz', }[lang]; const LENGTHS = [5, 6, 7]; /* ---------- Vulgarizmy a nadávky ---------- */ // Vzory nad koreňom aj nad hotovým tvarom (predpony ako vy-, na-, z- zachytia aj odvodené tvary). const PATTERNS = { sk: [ /^(vy|na|z|zo|po|za|roz|pre|od|do|u|pri|o|ob|s|ná)?jeb/, /^(z|na|vy|po|roz|s|pre|o)?kurv/, /^p[ií]č/, /^chuj/, /^kokot/, /^buzer/, /^buzn/, /^(vy|po|z|za|na|ob|o|u|pre|ná)?sra[ťtnl]/, /^(vy|po|z|za|na|o|u)?sr[aá][čc]/, /^cig[áa][nň]/, /^negr/, /^debil/, /^kret[ée]n/, /^hovn/, /^prdel/, /^z?mrd/, /^(vy|po|na)?šuk/, /^sex(om|u|y|i)?$/, /^č[uú]r[aá]k/, /^hajzl/, /^kund/, /^teplou[šs]/, /^riť/, /^pind/, /^onan/, /^porno/, /^dild/, ], cs: [ /^(vy|na|z|po|za|roz|pře|od|do|u|při|o|ob|s)?jeb/, /^(z|na|vy|po|roz|s|pře|o)?kurv/, /^p[ií]č/, /^chuj/, /^kokot/, /^buzer/, /^buzn/, /^(vy|po|z|za|na|o|u|pře|ná)?sr[aá][tnl]/, /^(vy|po|z|za|na|o|u)?sr[aá][čc]/, /^cik[áa]n/, /^negr/, /^debil/, /^kret[ée]n/, /^hovn/, /^prdel/, /^z?mrd/, /^(vy|po|na|o)?šuk/, /^č[uů]r[aá]k/, /^hajzl/, /^kund/, /^teplou[šs]/, /^(vy|z|na|o|po|pře)?šoust/, /^prc[aá]/, /^(vy|na|po|o)?chc[aá]/, /^porno/, /^dild/, /^onan/, ], en: [ /^(mother)?fuck/, /^shit/, /^cunt/, /^nigg/, /^bitch/, /^whore/, /^slut/, /^wank/, /^twat/, /^piss/, /^crap/, /^fag/, /^retard/, /^bastard/, /^pussy/, /^pussies$/, /^dild/, /^jizz/, /^porn/, /^asshole/, /^arsehole/, /^dickhead/, /^cocksuck/, /^blowjob/, /^handjob/, /^cum(s|med|ming|shot|shots)?$/, /^tit(s|ty|ties)$/, /^boob(s|ies|y)?$/, /^horny$/, /^rapist/, /^nazi(s)?$/, /^wetback/, /^gook/, /^chink(s)?$/, /^kike(s)?$/, /^spic(s)?$/, /^dyke(s)?$/, /^coon(s)?$/, /^homo(s)?$/, /^queer/, /^trann/, /^kraut(s)?$/, /^bollock/, /^bugger/, /^turd/, /^skank/, /^pimp(s|ed|ing)?$/, /^douchebag/, /^orgasm/, /^erotic/, /^incest/, /^molest/, /^cock(s)?$/, /^dick(s)?$/, /^penis/, /^vagina/, /^arse(s)?$/, ], }[lang]; // Presné slová (koreň aj tvar). EN a CS: List of Dirty, Naughty, Obscene, and Otherwise Bad Words // (https://github.com/LDNOOBW/List-of-Dirty-Naughty-Obscene-and-Otherwise-Bad-Words, CC BY 4.0), // len jednoslovné položky, bez bežných slov, ktoré zoznam berie príliš široko (vymenované v ASSETS.md). // SK: vlastný zoznam. const WORDS = { sk: ['penis', 'vagína', 'neger', 'hajzel', 'sex', 'sexy', 'kunda', 'zmrd', 'onania', 'prostitútka', 'buzerant'], cs: [ 'buzna', 'čurák', 'debil', 'hajzl', 'hovno', 'chcanky', 'chuj', 'jebat', 'kokot', 'kokotina', 'koňomrd', 'kunda', 'kurva', 'mamrd', 'mrdat', 'mrdka', 'mrdník', 'oslošoust', 'piča', 'píčus', 'pizda', 'prcat', 'prdel', 'prdelka', 'sračka', 'srát', 'šoustat', 'šulin', 'vypíčenec', 'zkurvit', 'zkurvysyn', 'zmrd', 'flundra', 'držka', 'seru', 'sereš', 'serou', 'sere', 'sereme', 'serete', 'penis', 'vagína', 'sex', 'sexy', 'negr', 'prostitutka', ], en: [ 'acrotomophilia', 'anal', 'anilingus', 'anus', 'apeshit', 'arsehole', 'ass', 'asshole', 'assmunch', 'autoerotic', 'beaner', 'beaners', 'beastiality', 'bestiality', 'bimbos', 'bitch', 'bitches', 'blowjob', 'bollocks', 'bondage', 'boner', 'boob', 'boobs', 'bukkake', 'bulldyke', 'bullshit', 'bunghole', 'busty', 'buttcheeks', 'butthole', 'camgirl', 'camslut', 'camwhore', 'carpetmuncher', 'circlejerk', 'clit', 'clitoris', 'clusterfuck', 'cock', 'cocks', 'coon', 'coons', 'creampie', 'cum', 'cumming', 'cumshot', 'cumshots', 'cunnilingus', 'cunt', 'darkie', 'daterape', 'deepthroat', 'dick', 'dildo', 'dingleberry', 'dingleberries', 'doggiestyle', 'doggystyle', 'dominatrix', 'ejaculation', 'erotic', 'erotism', 'fag', 'faggot', 'fecal', 'felch', 'fellatio', 'feltch', 'femdom', 'fisting', 'footjob', 'frotting', 'fuck', 'fuckin', 'fucking', 'fucktards', 'fudgepacker', 'gangbang', 'genitals', 'grope', 'handjob', 'hentai', 'homoerotic', 'honkey', 'hooker', 'horny', 'humping', 'incest', 'intercourse', 'jailbait', 'jigaboo', 'jiggaboo', 'jiggerboo', 'jizz', 'juggs', 'kike', 'kinkster', 'livesex', 'lolita', 'lovemaking', 'masturbate', 'masturbating', 'masturbation', 'milf', 'mong', 'motherfucker', 'negro', 'neonazi', 'nigga', 'nigger', 'nimphomania', 'nipple', 'nipples', 'nude', 'nudity', 'nympho', 'nymphomania', 'orgasm', 'orgy', 'paedophile', 'paki', 'panties', 'panty', 'pedophile', 'pegging', 'penis', 'pikey', 'pissing', 'playboy', 'poof', 'poon', 'poontang', 'punany', 'porn', 'porno', 'pornography', 'pubes', 'pussy', 'queaf', 'queef', 'quim', 'raghead', 'rape', 'raping', 'rapist', 'rectum', 'rimjob', 'rimming', 'sadism', 'schlong', 'semen', 'sex', 'sexo', 'sexy', 'sexual', 'sexually', 'sexuality', 'goddamn', 'goddammit', 'goddamned', 'shemale', 'shit', 'shitty', 'skeet', 'slanteye', 'slut', 'smut', 'sodomize', 'sodomy', 'spastic', 'spic', 'splooge', 'spooge', 'spunk', 'strapon', 'swastika', 'threesome', 'tit', 'tits', 'titties', 'titty', 'topless', 'tosser', 'towelhead', 'tranny', 'tribadism', 'tushy', 'twat', 'twink', 'upskirt', 'vagina', 'vibrator', 'voyeur', 'vulva', 'wank', 'wetback', 'whore', 'zoophilia', ], }[lang]; const WORD_SET = new Set(WORDS); const isBlocked = (w) => WORD_SET.has(w) || PATTERNS.some((re) => re.test(w)); /* ---------- Načítanie ---------- */ const readText = (p) => (p.endsWith('.gz') ? gunzipSync(readFileSync(p)) : readFileSync(p)).toString('utf8'); function condRegex(cond, kind) { if (cond === '.' || cond === '') return null; // Hunspell podmienka: písmená, [abc], [^abc], „.“ const s = cond.replace(/[$()*+?{}|\\]/g, (c) => '\\' + c); return new RegExp(kind === 'SFX' ? `(?:${s})$` : `^(?:${s})`, 'u'); } function parseAff(text) { const aff = text.split(/\r?\n/); const affixes = { PFX: new Map(), SFX: new Map() }; const meta = { circumfix: null, forbiddenFlags: new Set(), needAffix: null, onlyInCompound: null }; for (let i = 0; i < aff.length; i++) { const line = aff[i].replace(/#.*$/, '').trim(); if (!line) continue; const p = line.split(/\s+/); if (p[0] === 'CIRCUMFIX') meta.circumfix = p[1]; else if (p[0] === 'FORBIDDENWORD' || p[0] === 'NOSUGGEST') meta.forbiddenFlags.add(p[1]); else if (p[0] === 'NEEDAFFIX') meta.needAffix = p[1]; else if (p[0] === 'ONLYINCOMPOUND') meta.onlyInCompound = p[1]; else if ((p[0] === 'PFX' || p[0] === 'SFX') && p.length === 4 && /^\d+$/.test(p[3])) { const kind = p[0]; const flag = p[1]; const group = { cross: p[2] === 'Y', rules: [] }; const n = Number(p[3]); for (let k = 0; k < n; k++) { const r = aff[++i].trim().split(/\s+/); if (r[0] !== kind || r[1] !== flag) throw new Error(`zlý riadok ${i + 1}: ${aff[i]}`); const strip = r[2] === '0' ? '' : r[2]; let [add, cont = ''] = (r[3] ?? '').split('/'); if (add === '0') add = ''; const cond = r[4] ?? '.'; group.rules.push({ strip, add, cont: [...cont], re: condRegex(cond, kind) }); } const existing = affixes[kind].get(flag); if (existing) existing.rules.push(...group.rules); else affixes[kind].set(flag, group); } } return { affixes, ...meta }; } function applySfx(word, r) { if (r.strip && !word.endsWith(r.strip)) return null; if (r.re && !r.re.test(word)) return null; return word.slice(0, word.length - r.strip.length) + r.add; } function applyPfx(word, r) { if (r.strip && !word.startsWith(r.strip)) return null; if (r.re && !r.re.test(word)) return null; return r.add + word.slice(r.strip.length); } /* ---------- Rozvinutie ---------- */ const alpha = new Set([...ALPHABET]); const out = new Map(LENGTHS.map((l) => [l, new Set()])); let roots = 0; let blockedRoots = 0; let blockedForms = 0; function keep(form) { const chars = [...form]; if (!LENGTHS.includes(chars.length)) return; if (form !== form.toLowerCase()) return; for (const c of chars) if (!alpha.has(c)) return; if (isBlocked(form)) { blockedForms++; return; } out.get(chars.length).add(form); } function expandPair(affPath, dicPath) { const { affixes, circumfix, forbiddenFlags, needAffix, onlyInCompound } = parseAff(readText(affPath)); const isCirc = (r) => circumfix && r.cont.includes(circumfix); function expand(word, flags) { if (!(needAffix && flags.includes(needAffix))) keep(word); const pfxFlags = flags.filter((f) => affixes.PFX.has(f)); const sfxFlags = flags.filter((f) => affixes.SFX.has(f)); const prefixed = (base, allowed, needCirc, crossOnly) => { for (const pf of allowed) { const g = affixes.PFX.get(pf); if (crossOnly && !g.cross) continue; for (const pr of g.rules) { if (needCirc !== isCirc(pr)) continue; const w = applyPfx(base, pr); if (w) keep(w); } } }; for (const sf of sfxFlags) { const g = affixes.SFX.get(sf); for (const r of g.rules) { const w1 = applySfx(word, r); if (!w1) continue; const circ = isCirc(r); if (!circ) keep(w1); // predpony: krížové z koreňa a predpony povolené pokračovaním prípony if (g.cross) prefixed(w1, pfxFlags, circ, true); const contPfx = r.cont.filter((f) => affixes.PFX.has(f)); prefixed(w1, contPfx, circ, false); // druhá úroveň prípon for (const sf2 of r.cont.filter((f) => affixes.SFX.has(f))) { for (const r2 of affixes.SFX.get(sf2).rules) { const w2 = applySfx(w1, r2); if (!w2) continue; const circ2 = circ || isCirc(r2); if (!circ2) keep(w2); if (g.cross) prefixed(w2, pfxFlags, circ2, true); } } } } for (const pf of pfxFlags) { for (const pr of affixes.PFX.get(pf).rules) { if (isCirc(pr)) continue; const w = applyPfx(word, pr); if (!w) continue; keep(w); for (const sf of pr.cont.filter((f) => affixes.SFX.has(f))) { for (const r of affixes.SFX.get(sf).rules) { const w2 = applySfx(w, r); if (w2) keep(w2); } } } } } const dic = readText(dicPath).split(/\r?\n/); for (let i = 1; i < dic.length; i++) { const raw = dic[i].split('\t')[0].trim().split(/\s+/)[0]; if (!raw) continue; const slash = raw.search(/(? forbiddenFlags.has(f))) { blockedRoots++; continue; } if (onlyInCompound && flags.includes(onlyInCompound)) continue; if (word !== word.toLowerCase()) continue; if (isBlocked(word)) { blockedRoots++; continue; } roots++; expand(word, flags); } } for (const [aff, dic] of sources) expandPair(aff, dic); /* ---------- kódovanie ---------- */ // Písmeno s poradím i v abecede jazyka = znak CODE[i] (čisté ASCII, bundler ho neprepíše na \uXXXX). // Pre každú dĺžku L: zakódované slová zoradené, každé ako číslica p (spoločná predpona s predošlým slovom) // a zvyšných L - p znakov. Dĺžky oddelené znakom „|“ v poradí 5, 6, 7. Dekóduje src/dictionary.ts. const CODE = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; const encodeWord = (w) => [...w].map((c) => CODE[[...ALPHABET].indexOf(c)]).join(''); const parts = []; let total = 0; for (const L of LENGTHS) { const words = [...out.get(L)].map(encodeWord).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0)); total += words.length; let prev = ''; let s = ''; for (const w of words) { let p = 0; while (p < L - 1 && w[p] === prev[p]) p++; s += String(p) + w.slice(p); prev = w; } parts.push(s); console.log(`dĺžka ${L}: ${words.length} slov`); } const data = parts.join('|'); /* ---------- licenčná poznámka ---------- */ // Plné znenia licencií a zdroje sú v licenses/ a dict-src/ (pozri ASSETS.md). Text NOTICE ide do JS chunku // slovníka ako reťazec (komentáre minifikácia odstráni) a hra ho drží v objekte slovníka. const LO = 'https://github.com/LibreOffice/dictionaries'; const NOTICE = { sk: [ 'Word list of the game Slovo dna (HoyHop), derived from the Slovak dictionary sk_SK 2.4.8 (2024-08-29) for Hunspell,', `project sk-spell (Zdenko Podobny and contributors), https://github.com/sk-spell/hunspell-sk, copy at ${LO}/tree/master/sk_SK.`, 'The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); you may not use this', 'file except in compliance with the License. You may obtain a copy of the License at https://www.mozilla.org/MPL/1.1/', 'Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express', 'or implied. See the License for the specific language governing rights and limitations under the License.', 'The Original Code is the Slovak dictionary sk_SK for Hunspell, version 2.4.8.', 'The Initial Developer of the Original Code is the sk-spell project.', 'Contributor(s): HoyHop, 2026: word forms of 5 to 7 letters expanded from sk_SK.aff and sk_SK.dic, offensive words removed, re-encoded.', 'Alternatively, the contents of this file may be used under the terms of the GNU General Public License Version 2 or the', 'GNU Lesser General Public License Version 2.1, in which case the provisions of that license are applicable instead of those above.', 'The Source Code Form of this file (sk_SK.aff, sk_SK.dic and the script build-dict.mjs) is published together with the HoyHop licence notices.', ], cs: [ 'Word list of the game Slovo dne (HoyHop), derived from the Czech dictionary cs_CZ for Hunspell (Petr Kolar and', `contributors, ispell version of 2006-10-29, updated by Miroslav Posta in 2021), ${LO}/tree/master/cs_CZ.`, 'This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License', 'version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but', 'WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.', 'See the GNU General Public License for more details: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html', 'Modified by HoyHop in 2026: word forms of 5 to 7 letters expanded from cs_CZ.aff and cs_CZ.dic, offensive words removed, re-encoded.', 'The complete corresponding source (cs_CZ.aff, cs_CZ.dic and the script build-dict.mjs) is published together with the HoyHop licence notices.', ], en: [ 'Word list of the game Daily Word (HoyHop), derived from the Hunspell dictionaries en_US and en_GB-ise 2020.12.07 made from', `SCOWL, http://wordlist.aspell.net (en_US: ${LO}/tree/master/en). Modified version: word forms of 5 to 7 letters`, 'expanded from both dictionaries, offensive words removed, re-encoded (HoyHop, 2026).', 'Copyright 2000-2018 by Kevin Atkinson. Permission to use, copy, modify, distribute and sell these word lists, the associated', 'scripts, the output created from the scripts, and its documentation for any purpose is hereby granted without fee, provided', 'that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear', 'in supporting documentation. Kevin Atkinson makes no representations about the suitability of this array for any purpose.', 'It is provided "as is" without express or implied warranty.', 'VarCon: Copyright 2000-2016 by Kevin Atkinson; Copyright 2016 by Benjamin Titze. Permission to use, copy, modify, distribute', 'and sell this array, the associated software, and its documentation for any purpose is hereby granted without fee, provided', 'that the above copyright notice appears in all copies and that both that copyright notice and this permission notice appear', 'in supporting documentation. Kevin Atkinson and Benjamin Titze make no representations about the suitability of this array', 'for any purpose. It is provided "as is" without express or implied warranty.', 'Copyright 1993, Geoff Kuenning, Granada Hills, CA. All rights reserved. Redistribution and use in source and binary forms,', 'with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source', 'code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in', 'binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the', 'documentation and/or other materials provided with the distribution. 3. All modifications to the source code must be clearly', 'marked as such. Binary redistributions based on modified source code must be clearly marked as modified versions in the', 'documentation and/or other materials provided with the distribution. (clause 4 removed with permission from Geoff Kuenning)', '5. The name of Geoff Kuenning may not be used to endorse or promote products derived from this software without specific', "prior written permission. THIS SOFTWARE IS PROVIDED BY GEOFF KUENNING AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED", 'WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE', 'DISCLAIMED. IN NO EVENT SHALL GEOFF KUENNING OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,', 'EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,', 'DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT', 'LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF', 'THE POSSIBILITY OF SUCH DAMAGE.', 'WordNet 1.6 Copyright 1997 by Princeton University. All rights reserved. Permission to use, copy, modify and distribute this', 'software and database and its documentation for any purpose and without fee or royalty is hereby granted, provided that you', 'agree to comply with the following copyright notice and statements, including the disclaimer, and that the same appear on ALL', 'copies of the software, database and documentation, including modifications that you make for internal use or for', 'distribution. THIS SOFTWARE AND DATABASE IS PROVIDED "AS IS" AND PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES,', 'EXPRESS OR IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PRINCETON UNIVERSITY MAKES NO REPRESENTATIONS OR WARRANTIES OF', 'MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE, DATABASE OR DOCUMENTATION WILL', 'NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. The name of Princeton University or Princeton may', 'not be used in advertising or publicity pertaining to distribution of the software and/or database. Title to copyright in this', 'software, database and any associated documentation shall at all times remain with Princeton University and LICENSEE agrees', 'to preserve same.', ], }[lang].join('\n'); const outDir = join(here, '..', 'src', 'dict'); mkdirSync(outDir, { recursive: true }); const header = { sk: '// Odvodené zo sk_SK Hunspell (sk-spell), MPL 1.1 (alebo GPL 2 / LGPL 2.1). Plné znenie poznámky je v NOTICE.\n', cs: '// Odvodené z cs_CZ Hunspell (Petr Kolář, Miroslav Pošta a kol.), GNU GPL 2. Plné znenie poznámky je v NOTICE.\n', en: '// Odvodené z en_US a en_GB-ise Hunspell (SCOWL, Kevin Atkinson a kol.). Plné znenie poznámky je v NOTICE.\n', }[lang]; const file = header + '// Vygenerované skriptom scripts/build-dict.mjs. Nemeniť ručne.\n' + `export const NOTICE = ${JSON.stringify(NOTICE)};\n` + `export const ALPHABET = ${JSON.stringify(ALPHABET)};\n` + `export const DATA = ${JSON.stringify(data)};\n`; writeFileSync(join(outDir, `${lang}.ts`), file); const bytes = Buffer.byteLength(file); console.log(`${lang}: koreňov ${roots}, vynechaných koreňov ${blockedRoots}, vynechaných tvarov ${blockedForms}, tvarov ${total}, súbor ${(bytes / 1024).toFixed(0)} kB, gzip ${(gzipSync(file, { level: 9 }).length / 1024).toFixed(0)} kB`);