@jjlmoya/utils-books 1.7.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +7 -1
- package/src/index.ts +1 -0
- package/src/tests/locale_completeness.test.ts +1 -1
- package/src/tests/tool_validation.test.ts +1 -1
- package/src/tool/book-index-page-budget-calculator/bibliography.astro +6 -0
- package/src/tool/book-index-page-budget-calculator/bibliography.ts +17 -0
- package/src/tool/book-index-page-budget-calculator/book-index-page-budget-calculator.css +478 -0
- package/src/tool/book-index-page-budget-calculator/component.astro +134 -0
- package/src/tool/book-index-page-budget-calculator/controller.ts +114 -0
- package/src/tool/book-index-page-budget-calculator/dom-views.ts +89 -0
- package/src/tool/book-index-page-budget-calculator/entry.ts +39 -0
- package/src/tool/book-index-page-budget-calculator/evaluator.ts +26 -0
- package/src/tool/book-index-page-budget-calculator/i18n/de.ts +35 -0
- package/src/tool/book-index-page-budget-calculator/i18n/en.ts +113 -0
- package/src/tool/book-index-page-budget-calculator/i18n/es.ts +35 -0
- package/src/tool/book-index-page-budget-calculator/i18n/fr.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/id.ts +35 -0
- package/src/tool/book-index-page-budget-calculator/i18n/it.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/ja.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/ko.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/nl.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/pl.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/pt.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/ru.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/sv.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/tr.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/i18n/zh.ts +8 -0
- package/src/tool/book-index-page-budget-calculator/index.ts +11 -0
- package/src/tool/book-index-page-budget-calculator/logic.test.ts +52 -0
- package/src/tool/book-index-page-budget-calculator/logic.ts +86 -0
- package/src/tool/book-index-page-budget-calculator/seo.astro +15 -0
- package/src/tool/book-index-page-budget-calculator/storage.ts +17 -0
- package/src/tool/book-index-page-budget-calculator/ui.ts +41 -0
- package/src/tool/book-reading-time-deadline-planner/bibliography.astro +16 -0
- package/src/tool/book-reading-time-deadline-planner/bibliography.ts +31 -0
- package/src/tool/book-reading-time-deadline-planner/book-reading-time-deadline-planner.css +449 -0
- package/src/tool/book-reading-time-deadline-planner/calendar.test.ts +18 -0
- package/src/tool/book-reading-time-deadline-planner/calendar.ts +56 -0
- package/src/tool/book-reading-time-deadline-planner/component.astro +91 -0
- package/src/tool/book-reading-time-deadline-planner/controller.ts +253 -0
- package/src/tool/book-reading-time-deadline-planner/dom-views.ts +60 -0
- package/src/tool/book-reading-time-deadline-planner/entry.ts +27 -0
- package/src/tool/book-reading-time-deadline-planner/evaluator.ts +17 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/de.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/en.ts +51 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/es.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/fr.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/id.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/it.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/ja.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/ko.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/nl.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/pl.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/pt.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/ru.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/sv.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/tr.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/i18n/zh.ts +45 -0
- package/src/tool/book-reading-time-deadline-planner/index.ts +11 -0
- package/src/tool/book-reading-time-deadline-planner/logic.test.ts +61 -0
- package/src/tool/book-reading-time-deadline-planner/logic.ts +129 -0
- package/src/tool/book-reading-time-deadline-planner/seo.astro +15 -0
- package/src/tool/book-reading-time-deadline-planner/speed-test.test.ts +15 -0
- package/src/tool/book-reading-time-deadline-planner/speed-test.ts +9 -0
- package/src/tool/book-reading-time-deadline-planner/storage.ts +24 -0
- package/src/tool/book-reading-time-deadline-planner/ui.ts +76 -0
- package/src/tool/book-reading-time-deadline-planner/validation.ts +19 -0
- package/src/tools.ts +3 -1
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: 'Liczba słów rękopisu', wordsHint: 'Tekst główny przed dodatkami', trimWidthLabel: 'Szerokość po obcięciu', trimHeightLabel: 'Wysokość po obcięciu', trimHint: 'Gotowy format strony', unitLabel: 'Jednostki strony', metricButtonLabel: 'Metryczne cm', imperialButtonLabel: 'Calowe', densityLabel: 'Gęstość typografii', densityHint: 'Steruje liczbą słów i haseł indeksu na stronie', airyLabel: 'Luźna', standardLabel: 'Standardowa', compactLabel: 'Zwarta', indexEntriesLabel: 'Przewidywane hasła indeksu', indexEntriesHint: 'Tematy, nazwiska, miejsca i odsyłacze', targetIndexPagesLabel: 'Zarezerwowane strony indeksu', targetIndexPagesHint: 'Limit produkcyjny na indeks', textPagesLabel: 'Strony tekstu', estimatedIndexPagesLabel: 'Szacowany indeks', totalPagesLabel: 'Szacowana liczba stron książki', targetTotalPagesLabel: 'Z docelowym indeksem', entriesPerPageLabel: 'Haseł na stronę docelową', indexShareLabel: 'Udział indeksu w książce', statusBalanced: 'Cel odpowiada szacunkom', statusTight: 'Cel jest zbyt ciasny', statusRoomy: 'Cel zostawia zapas', statusBalancedHint: 'Zaplanowany limit odpowiada modelowi gęstości.', statusTightHint: 'Dodaj strony lub zmniejsz liczbę haseł przed składem.', statusRoomyHint: 'Zapas może pomieścić późniejsze dodatki redakcyjne.', sceneLabel: 'Budżet stron', sceneBodyLabel: 'strony tekstu', sceneIndexLabel: 'strony indeksu', sceneCaption: 'Szacowana książka zajmuje', sceneTargetCaption: 'Przy zaplanowanym limicie będzie mieć', sourceNote: 'To model planowania redakcyjnego. Ostateczna paginacja zależy od składu, korekt, stylu indeksu oraz wytycznych wydawcy lub drukarni.', summaryLabel: 'Sygnał budżetu', summaryPagesOver: 'stron ponad cel', summaryPagesSpare: 'stron zapasu', summaryOnTarget: 'Zgodnie z celem', };
|
|
5
|
+
const faq = [{ question: 'Co wpisać jako hasła indeksu?', answer: 'Wpisz tematy, nazwiska, miejsca, podhasła i odsyłacze, które mają znaleźć się w gotowym indeksie.' }, { question: 'Dlaczego strony tekstu i indeksu liczą się osobno?', answer: 'Tekst zależy od liczby słów i projektu strony, a indeks od liczby haseł i jego gęstości.' }, { question: 'Co oznacza zbyt ciasny cel?', answer: 'Limit wymagałby upchnięcia na stronie większej liczby haseł, niż wygodnie mieści wybrany profil.' }, { question: 'Czy wynik zastępuje końcową korektę indeksu?', answer: 'Nie. To szacunek do planowania. Odsyłacze i paginację trzeba sprawdzić na złożonej korekcie.' }];
|
|
6
|
+
const howTo = [{ name: 'Wpisz objętość rękopisu', text: 'Podaj liczbę słów tekstu głównego.' }, { name: 'Dopasuj projekt strony', text: 'Wybierz format końcowy i gęstość typografii.' }, { name: 'Opisz indeks', text: 'Oszacuj hasła i zarezerwuj strony.' }, { name: 'Zareaguj na różnicę', text: 'Zwiększ limit lub uporządkuj hasła, gdy cel jest zbyt ciasny.' }];
|
|
7
|
+
const seo = [{ type: 'title', text: 'Wcześnie zaplanuj budżet stron indeksu książki', level: 2 }, { type: 'paragraph', html: 'Indeks analityczny dodaje rzeczywiste strony do końcowej części drukowanej książki. To narzędzie łączy słowa rękopisu, format, gęstość typografii, przewidywane hasła i rezerwę redakcyjną.' }, { type: 'title', text: 'Jak czytać szacunek', level: 2 }, { type: 'paragraph', html: 'Strony tekstu wynikają ze słów i gęstości. Strony indeksu wynikają z liczby haseł i pojemności strony. Porównaj szacunek z rezerwą przed zamknięciem składu.' }, { type: 'list', items: ['Licz przewidywane hasła, a nie wszystkie słowa rękopisu.', 'Używaj formatu i gęstości planowanego wnętrza.', 'Zarezerwuj więcej miejsca przy licznych odsyłaczach i podhasłach.', 'Przelicz ponownie, gdy korekta stron będzie stabilna.'] }, { type: 'title', text: 'Ciasny cel jest sygnałem redakcyjnym', level: 2 }, { type: 'paragraph', html: 'Zbyt wiele haseł na kilku stronach obniża czytelność indeksu. Uporządkuj słabe odsyłacze lub zwiększ przestrzeń, zanim jeszcze bardziej ściśniesz typografię.' }, { type: 'tip', title: 'To nie jest ostateczna wartość dla drukarni', html: 'Krój pisma, skład, korekty i styl indeksu zmienią końcowy wynik. Potwierdź go z wydawcą, drukarnią lub profesjonalnym indeksatorem.' }];
|
|
8
|
+
export const content = { ...base, slug: 'kalkulator-budzetu-stron-indeksu-ksiazki', title: 'Kalkulator budżetu stron indeksu książki', description: 'Oszacuj strony dodane przez indeks analityczny i porównaj je z redakcyjnym budżetem książki.', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: 'Palavras do manuscrito', wordsHint: 'Texto principal antes dos anexos', trimWidthLabel: 'Largura de corte', trimHeightLabel: 'Altura de corte', trimHint: 'Formato final da página', unitLabel: 'Unidades da página', metricButtonLabel: 'Métrico cm', imperialButtonLabel: 'Imperial polegadas', densityLabel: 'Densidade tipográfica', densityHint: 'Controla palavras e entradas por página', airyLabel: 'Arejada', standardLabel: 'Padrão', compactLabel: 'Compacta', indexEntriesLabel: 'Entradas de índice previstas', indexEntriesHint: 'Temas, nomes, lugares e referências cruzadas', targetIndexPagesLabel: 'Páginas de índice reservadas', targetIndexPagesHint: 'A sua margem de produção para o índice', textPagesLabel: 'Páginas de texto', estimatedIndexPagesLabel: 'Índice estimado', totalPagesLabel: 'Total estimado do livro', targetTotalPagesLabel: 'Com o índice reservado', entriesPerPageLabel: 'Entradas por página-alvo', indexShareLabel: 'Parte do índice no livro', statusBalanced: 'O alvo corresponde à estimativa', statusTight: 'O alvo é apertado demais', statusRoomy: 'O alvo deixa margem', statusBalancedHint: 'A reserva prevista corresponde ao modelo de densidade.', statusTightHint: 'Reserve mais páginas ou reduza entradas antes da composição.', statusRoomyHint: 'A margem pode absorver acréscimos editoriais tardios.', sceneLabel: 'O orçamento de páginas', sceneBodyLabel: 'páginas de texto', sceneIndexLabel: 'páginas de índice', sceneCaption: 'O livro estimado ocupa', sceneTargetCaption: 'Com a sua reserva, o total seria', sourceNote: 'Este é um modelo de planeamento editorial. A paginação final depende da composição, das correções, do estilo do índice e das instruções da editora ou gráfica.', summaryLabel: 'Sinal do orçamento', summaryPagesOver: 'páginas acima do alvo', summaryPagesSpare: 'páginas de margem', summaryOnTarget: 'Dentro do alvo', };
|
|
5
|
+
const faq = [{ question: 'O que devo inserir como entradas do índice?', answer: 'Insira os temas, nomes, lugares, subentradas e referências cruzadas que espera incluir no índice final.' }, { question: 'Por que o texto e o índice são calculados separadamente?', answer: 'O texto depende das palavras e do design da página; o índice depende das entradas e da sua densidade.' }, { question: 'O que significa um alvo apertado demais?', answer: 'A reserva exigiria mais entradas por página do que o perfil escolhido consegue acomodar confortavelmente.' }, { question: 'Isto substitui a prova final do índice?', answer: 'Não. É uma estimativa de planeamento. As referências e a paginação devem ser conferidas na prova composta.' }];
|
|
6
|
+
const howTo = [{ name: 'Introduza o manuscrito', text: 'Indique as palavras do texto principal.' }, { name: 'Ajuste o design da página', text: 'Escolha o formato final e a densidade tipográfica.' }, { name: 'Descreva o índice', text: 'Estime as entradas e reserve páginas.' }, { name: 'Aja sobre a comparação', text: 'Aumente a reserva ou reduza entradas quando o alvo for apertado.' }];
|
|
7
|
+
const seo = [{ type: 'title', text: 'Planeie cedo o orçamento de páginas do índice', level: 2 }, { type: 'paragraph', html: 'Um índice analítico acrescenta páginas reais à parte final de um livro impresso. Esta ferramenta combina palavras, formato, densidade tipográfica, entradas previstas e reserva editorial.' }, { type: 'title', text: 'Como ler a estimativa', level: 2 }, { type: 'paragraph', html: 'As páginas de texto dependem das palavras e da densidade. As páginas de índice dependem das entradas e da capacidade por página. Compare a estimativa com a reserva antes de fechar a composição.' }, { type: 'list', items: ['Conte entradas previstas, não todas as palavras do manuscrito.', 'Use o formato e a densidade do design real.', 'Reserve espaço extra quando houver muitas referências cruzadas e subentradas.', 'Volte a calcular quando a prova de páginas estiver estável.'] }, { type: 'title', text: 'Um alvo apertado exige uma decisão editorial', level: 2 }, { type: 'paragraph', html: 'Colocar entradas demais em poucas páginas dificulta a consulta. Limpe referências fracas ou aumente o espaço antes de comprimir ainda mais a tipografia.' }, { type: 'tip', title: 'Não é um valor final para a gráfica', html: 'Fonte, composição, correções e estilo do índice alteram o resultado final. Confirme-o com a editora, a gráfica ou um indexador profissional.' }];
|
|
8
|
+
export const content = { ...base, slug: 'calculadora-orcamento-paginas-indice-livro', title: 'Calculadora do orçamento de páginas do índice', description: 'Estime as páginas acrescentadas por um índice analítico e compare-as com a reserva editorial do livro.', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: 'Слов в рукописи', wordsHint: 'Основной текст до приложений', trimWidthLabel: 'Обрезная ширина', trimHeightLabel: 'Обрезная высота', trimHint: 'Готовый размер страницы', unitLabel: 'Единицы страницы', metricButtonLabel: 'Метрические см', imperialButtonLabel: 'Дюймы', densityLabel: 'Типографская плотность', densityHint: 'Настраивает число слов и записей указателя на странице', airyLabel: 'Свободная', standardLabel: 'Стандартная', compactLabel: 'Плотная', indexEntriesLabel: 'Ожидаемые записи указателя', indexEntriesHint: 'Темы, имена, места и перекрёстные ссылки', targetIndexPagesLabel: 'Зарезервированные страницы указателя', targetIndexPagesHint: 'Производственный лимит указателя', textPagesLabel: 'Страницы текста', estimatedIndexPagesLabel: 'Расчёт указателя', totalPagesLabel: 'Расчётный объём книги', targetTotalPagesLabel: 'С целевым указателем', entriesPerPageLabel: 'Записей на целевую страницу', indexShareLabel: 'Доля указателя в книге', statusBalanced: 'Цель соответствует расчёту', statusTight: 'Цель слишком мала', statusRoomy: 'Цель оставляет запас', statusBalancedHint: 'Запланированный объём соответствует модели плотности.', statusTightHint: 'Добавьте страницы или сократите записи до начала вёрстки.', statusRoomyHint: 'Запас может вместить поздние редакторские добавления.', sceneLabel: 'Бюджет страниц', sceneBodyLabel: 'страницы текста', sceneIndexLabel: 'страницы указателя', sceneCaption: 'Расчётный объём книги -', sceneTargetCaption: 'С целевым объёмом указателя -', sourceNote: 'Это редакционная модель планирования. Итоговая пагинация зависит от вёрстки, корректур, стиля указателя и требований издателя или типографии.', summaryLabel: 'Сигнал бюджета', summaryPagesOver: 'стр. сверх цели', summaryPagesSpare: 'стр. запаса', summaryOnTarget: 'В пределах цели', };
|
|
5
|
+
const faq = [{ question: 'Что вводить в поле записей указателя?', answer: 'Введите темы, имена, места, подзаписи и перекрёстные ссылки, которые войдут в готовый указатель.' }, { question: 'Почему текст и указатель считаются отдельно?', answer: 'Текст зависит от числа слов и дизайна страницы, а указатель - от записей и плотности указателя.' }, { question: 'Что означает слишком маленькая цель?', answer: 'Чтобы уложиться в резерв, пришлось бы разместить на странице больше записей, чем позволяет выбранный профиль.' }, { question: 'Заменяет ли результат финальную корректуру?', answer: 'Нет. Это планировочная оценка. Ссылки и пагинацию нужно проверить на свёрстанной корректуре.' }];
|
|
6
|
+
const howTo = [{ name: 'Введите объём рукописи', text: 'Укажите число слов основного текста.' }, { name: 'Настройте дизайн страницы', text: 'Выберите готовый размер и типографскую плотность.' }, { name: 'Опишите указатель', text: 'Оцените записи и зарезервируйте страницы.' }, { name: 'Используйте сравнение', text: 'Увеличьте резерв или сократите записи, если цель слишком мала.' }];
|
|
7
|
+
const seo = [{ type: 'title', text: 'Заранее рассчитайте бюджет страниц указателя книги', level: 2 }, { type: 'paragraph', html: 'Предметный указатель добавляет реальные страницы в конец печатной книги. Инструмент объединяет объём рукописи, формат, типографскую плотность, ожидаемые записи и редакционный резерв.' }, { type: 'title', text: 'Как читать расчёт', level: 2 }, { type: 'paragraph', html: 'Страницы текста зависят от слов и плотности. Страницы указателя зависят от числа записей и вместимости страницы. Сравните расчёт с резервом до завершения вёрстки.' }, { type: 'list', items: ['Считайте записи, а не все слова рукописи.', 'Используйте формат и плотность настоящего макета.', 'Зарезервируйте больше места при большом числе ссылок и подзаписей.', 'Пересчитайте результат после стабилизации корректуры.'] }, { type: 'title', text: 'Маленькая цель требует редакторского решения', level: 2 }, { type: 'paragraph', html: 'Слишком плотный указатель сложнее просматривать. Сначала уберите слабые ссылки или увеличьте резерв, а уже потом уплотняйте типографику.' }, { type: 'tip', title: 'Это не финальная цифра для типографии', html: 'Шрифт, вёрстка, корректуры и стиль указателя меняют результат. Подтвердите его с издателем, типографией или профессиональным составителем указателя.' }];
|
|
8
|
+
export const content = { ...base, slug: 'raschet-byudzheta-stranits-ukazatelya-knigi', title: 'Калькулятор бюджета страниц указателя книги', description: 'Рассчитайте дополнительные страницы предметного указателя и сравните их с редакционным резервом книги.', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: 'Ord i manuskriptet', wordsHint: 'Brödtext före eftertext', trimWidthLabel: 'Skärbredd', trimHeightLabel: 'Skärhöjd', trimHint: 'Färdigt sidformat', unitLabel: 'Sidenheter', metricButtonLabel: 'Metriskt cm', imperialButtonLabel: 'Tum', densityLabel: 'Typografisk täthet', densityHint: 'Styr ord och indexposter per sida', airyLabel: 'Luftig', standardLabel: 'Standard', compactLabel: 'Tät', indexEntriesLabel: 'Förväntade indexposter', indexEntriesHint: 'Ämnen, namn, platser och korshänvisningar', targetIndexPagesLabel: 'Reserverade indexsidor', targetIndexPagesHint: 'Produktionsutrymme för indexet', textPagesLabel: 'Textsidor', estimatedIndexPagesLabel: 'Beräknat index', totalPagesLabel: 'Beräknat antal boksidor', targetTotalPagesLabel: 'Med indexmålet', entriesPerPageLabel: 'Poster per målsida', indexShareLabel: 'Indexets andel av boken', statusBalanced: 'Målet motsvarar uppskattningen', statusTight: 'Målet är för snävt', statusRoomy: 'Målet lämnar utrymme', statusBalancedHint: 'Den planerade reserven stämmer med täthetsmodellen.', statusTightHint: 'Lägg till sidor eller minska poster före sättningen.', statusRoomyHint: 'Utrymmet kan ta emot sena redaktionella tillägg.', sceneLabel: 'Sidbudgeten', sceneBodyLabel: 'textsidor', sceneIndexLabel: 'indexsidor', sceneCaption: 'Den beräknade boken omfattar', sceneTargetCaption: 'Med din reserv blir totalen', sourceNote: 'Detta är en redaktionell planeringsmodell. Den slutliga pagineringen beror på sättning, korrektur, indexstil och instruktioner från förlag eller tryckeri.', summaryLabel: 'Budgetsignal', summaryPagesOver: 'sidor över målet', summaryPagesSpare: 'sidor tillgodo', summaryOnTarget: 'På målet', };
|
|
5
|
+
const faq = [{ question: 'Vad ska jag ange som indexposter?', answer: 'Ange ämnen, namn, platser, underposter och korshänvisningar som ska finnas i det färdiga indexet.' }, { question: 'Varför beräknas text och index separat?', answer: 'Texten styrs av ord och sidlayout, medan indexet styrs av poster och indextäthet.' }, { question: 'Vad betyder ett för snävt mål?', answer: 'Reserven skulle kräva fler poster per sida än den valda profilen rimligen rymmer.' }, { question: 'Ersätter detta den slutliga indexkorrekturen?', answer: 'Nej. Det är en planeringsuppskattning. Hänvisningar och paginering måste kontrolleras i det satta korrekturet.' }];
|
|
6
|
+
const howTo = [{ name: 'Ange manuskriptet', text: 'Skriv in antalet ord i brödtexten.' }, { name: 'Anpassa sidlayouten', text: 'Välj färdigt format och typografisk täthet.' }, { name: 'Beskriv indexet', text: 'Uppskatta poster och reservera sidor.' }, { name: 'Agera på skillnaden', text: 'Öka reserven eller minska poster när målet är för snävt.' }];
|
|
7
|
+
const seo = [{ type: 'title', text: 'Planera bokens indexbudget tidigt', level: 2 }, { type: 'paragraph', html: 'Ett analytiskt index lägger till verkliga sidor i slutet av en tryckt bok. Verktyget kombinerar manuskriptord, format, typografisk täthet, förväntade poster och redaktionell reserv.' }, { type: 'title', text: 'Så läser du uppskattningen', level: 2 }, { type: 'paragraph', html: 'Textsidor beräknas från ord och täthet. Indexsidor beräknas från poster och kapacitet per sida. Jämför uppskattningen med reserven innan sättningen låses.' }, { type: 'list', items: ['Räkna förväntade poster, inte alla manuskriptord.', 'Använd format och täthet från den riktiga inredningen.', 'Reservera extra utrymme vid många korshänvisningar och underposter.', 'Räkna om när sidkorrekturet har stabiliserats.'] }, { type: 'title', text: 'Ett snävt mål är en redaktionell signal', level: 2 }, { type: 'paragraph', html: 'För många poster på få sidor gör indexet svårare att använda. Rensa svaga hänvisningar eller öka utrymmet innan du pressar ihop typografin ytterligare.' }, { type: 'tip', title: 'Inte ett slutligt tryckvärde', html: 'Typsnitt, sättning, korrektur och indexstil ändrar slutresultatet. Bekräfta det med förlag, tryckeri eller professionell indexerare.' }];
|
|
8
|
+
export const content = { ...base, slug: 'budget-for-bokens-indexsidor', title: 'Kalkylator för bokens indexbudget', description: 'Uppskatta indexets extra sidor och jämför dem med bokens redaktionella sidbudget.', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: 'El yazması kelimeleri', wordsHint: 'Eklerden önceki ana metin', trimWidthLabel: 'Kesim genişliği', trimHeightLabel: 'Kesim yüksekliği', trimHint: 'Bitmiş sayfa boyutu', unitLabel: 'Sayfa birimleri', metricButtonLabel: 'Metrik cm', imperialButtonLabel: 'İnç', densityLabel: 'Tipografik yoğunluk', densityHint: 'Sayfa başına kelime ve dizin maddelerini ayarlar', airyLabel: 'Seyrek', standardLabel: 'Standart', compactLabel: 'Sıkı', indexEntriesLabel: 'Beklenen dizin maddeleri', indexEntriesHint: 'Konular, adlar, yerler ve çapraz göndermeler', targetIndexPagesLabel: 'Ayrılan dizin sayfaları', targetIndexPagesHint: 'Dizin için üretim payı', textPagesLabel: 'Metin sayfaları', estimatedIndexPagesLabel: 'Tahmini dizin', totalPagesLabel: 'Tahmini kitap toplamı', targetTotalPagesLabel: 'Hedef dizinle', entriesPerPageLabel: 'Hedef sayfa başına madde', indexShareLabel: 'Dizinin kitaptaki payı', statusBalanced: 'Hedef tahminle uyumlu', statusTight: 'Hedef çok sıkı', statusRoomy: 'Hedef alan bırakıyor', statusBalancedHint: 'Planlanan pay yoğunluk modeliyle uyumlu.', statusTightHint: 'Dizgi öncesinde sayfa ekleyin veya madde sayısını azaltın.', statusRoomyHint: 'Pay, geç eklenen editoryal içerikleri karşılayabilir.', sceneLabel: 'Sayfa bütçesi', sceneBodyLabel: 'metin sayfası', sceneIndexLabel: 'dizin sayfası', sceneCaption: 'Tahmini kitap', sceneTargetCaption: 'Hedef payla toplam', sourceNote: 'Bu bir editoryal planlama modelidir. Son sayfa sayısı dizgiye, düzeltmelere, dizin stiline ve yayınevi ya da matbaa talimatlarına bağlıdır.', summaryLabel: 'Bütçe sinyali', summaryPagesOver: 'sayfa hedef üstü', summaryPagesSpare: 'sayfa boşluk', summaryOnTarget: 'Hedefte', };
|
|
5
|
+
const faq = [{ question: 'Dizin maddesi olarak ne girmeliyim?', answer: 'Son dizinde yer alacak konu, ad, yer, alt madde ve çapraz göndermeleri girin.' }, { question: 'Metin ve dizin sayfaları neden ayrı hesaplanır?', answer: 'Metin kelime sayısı ve sayfa tasarımına, dizin ise madde sayısı ve yoğunluğuna bağlıdır.' }, { question: 'Hedefin çok sıkı olması ne demektir?', answer: 'Seçilen profilin rahatça taşıyabileceğinden daha fazla maddeyi bir sayfaya sığdırmanız gerekir.' }, { question: 'Bu sonuç son dizin provasının yerini tutar mı?', answer: 'Hayır. Bu bir planlama tahminidir. Göndermeler ve sayfalar dizgili prova üzerinde kontrol edilmelidir.' }];
|
|
6
|
+
const howTo = [{ name: 'El yazmasını girin', text: 'Ana metnin kelime sayısını yazın.' }, { name: 'Sayfa tasarımını eşleştirin', text: 'Bitmiş boyutu ve tipografik yoğunluğu seçin.' }, { name: 'Dizini tanımlayın', text: 'Madde sayısını tahmin edip sayfa ayırın.' }, { name: 'Karşılaştırmayı kullanın', text: 'Hedef sıkıysa payı artırın veya maddeleri azaltın.' }];
|
|
7
|
+
const seo = [{ type: 'title', text: 'Kitap dizininin sayfa bütçesini erkenden planlayın', level: 2 }, { type: 'paragraph', html: 'Analitik dizin, basılı kitabın sonunda gerçek sayfalar oluşturur. Bu araç el yazması kelimelerini, boyutu, tipografik yoğunluğu, beklenen maddeleri ve editoryal payı tek bir planlamada birleştirir.' }, { type: 'title', text: 'Tahmini nasıl okuyabilirsiniz?', level: 2 }, { type: 'paragraph', html: 'Metin sayfaları kelime sayısı ve yoğunluktan, dizin sayfaları madde sayısı ve sayfa kapasitesinden tahmin edilir. Dizgiyi tamamlamadan önce tahmini ayrılan sayfalarla karşılaştırın.' }, { type: 'list', items: ['El yazmasının tüm kelimelerini değil, beklenen dizin maddelerini sayın.', 'Gerçek iç tasarıma yakın boyut ve yoğunluğu kullanın.', 'Çapraz gönderme ve alt maddeler fazlaysa ek sayfa ayırın.', 'Sayfa provası sabitlendiğinde yeniden hesaplayın.'] }, { type: 'title', text: 'Sıkı hedef editoryal bir sinyaldir', level: 2 }, { type: 'paragraph', html: 'Az sayfaya çok madde sığdırmak dizinin taranabilirliğini azaltır. Tipografiyi daha da sıkıştırmadan önce zayıf göndermeleri temizleyin veya alanı artırın.' }, { type: 'tip', title: 'Matbaa için kesin değer değildir', html: 'Yazı tipi, dizgi, düzeltmeler ve dizin stili sonucu değiştirir. Yayınevi, matbaa veya profesyonel dizin hazırlayıcısıyla doğrulayın.' }];
|
|
8
|
+
export const content = { ...base, slug: 'kitap-dizin-sayfa-butcesi-hesaplayici', title: 'Kitap dizini sayfa bütçesi hesaplayıcısı', description: 'Analitik dizinin ekleyeceği sayfaları tahmin edin ve kitabın editoryal sayfa payıyla karşılaştırın.', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { content as base } from './en';
|
|
2
|
+
import type { BookIndexUI } from '../ui';
|
|
3
|
+
|
|
4
|
+
const ui: BookIndexUI = { ...base.ui, wordsLabel: '稿件字数', wordsHint: '正文,不含书后部分', trimWidthLabel: '成品宽度', trimHeightLabel: '成品高度', trimHint: '页面成品尺寸', unitLabel: '页面单位', metricButtonLabel: '公制 cm', imperialButtonLabel: '英制 英寸', densityLabel: '排版密度', densityHint: '调整每页正文词数和索引条目数', airyLabel: '宽松', standardLabel: '标准', compactLabel: '紧凑', indexEntriesLabel: '预计索引条目', indexEntriesHint: '主题、姓名、地点和交叉引用', targetIndexPagesLabel: '预留索引页数', targetIndexPagesHint: '为索引预留的制作空间', textPagesLabel: '正文页', estimatedIndexPagesLabel: '预计索引页', totalPagesLabel: '预计全书页数', targetTotalPagesLabel: '按目标索引计算', entriesPerPageLabel: '每个目标页的条目数', indexShareLabel: '索引占全书比例', statusBalanced: '目标与估算一致', statusTight: '目标过于紧张', statusRoomy: '目标留有余量', statusBalancedHint: '预留页数与当前密度模型一致。', statusTightHint: '排版前增加页数或减少索引条目。', statusRoomyHint: '余量可以容纳后期的编辑增补。', sceneLabel: '页面预算', sceneBodyLabel: '正文页', sceneIndexLabel: '索引页', sceneCaption: '预计全书为', sceneTargetCaption: '按目标预留则为', sourceNote: '这是编辑计划模型。最终页数会受排版、校样修改、索引样式以及出版社或印刷厂要求影响。', summaryLabel: '预算信号', summaryPagesOver: '页超出目标', summaryPagesSpare: '页余量', summaryOnTarget: '符合目标', };
|
|
5
|
+
const faq = [{ question: '索引条目应该填什么?', answer: '填写预计出现在最终索引中的主题、姓名、地点、子条目和交叉引用。' }, { question: '为什么正文页和索引页要分开计算?', answer: '正文取决于字数和页面设计,索引则取决于条目数量和索引密度。' }, { question: '目标过于紧张是什么意思?', answer: '这表示需要在每页放入超过所选密度模型合理承载量的条目。' }, { question: '它能替代最终索引校样吗?', answer: '不能。这是计划阶段的估算,页码引用和分页必须在排版校样中核对。' }];
|
|
6
|
+
const howTo = [{ name: '输入稿件规模', text: '输入不含书后部分的正文词数。' }, { name: '匹配页面设计', text: '选择成品尺寸和排版密度。' }, { name: '描述索引', text: '估计条目数量并预留页数。' }, { name: '根据差额行动', text: '目标紧张时增加页数或整理条目。' }];
|
|
7
|
+
const seo = [{ type: 'title', text: '尽早规划书籍索引的页面预算', level: 2 }, { type: 'paragraph', html: '分析索引会为印刷书籍的后半部分增加实际页面。本工具将稿件字数、成品尺寸、排版密度、预计条目和编辑预算放在同一份计划中。' }, { type: 'title', text: '如何阅读估算结果', level: 2 }, { type: 'paragraph', html: '正文页根据字数和密度估算,索引页根据条目数和每页容量估算。在确定排版前,将预计页数与预留页数进行比较。' }, { type: 'list', items: ['统计预计索引条目,而不是稿件中的全部字词。', '使用接近实际内页设计的尺寸和密度。', '交叉引用和子条目较多时预留额外页面。', '页面校样稳定后重新计算。'] }, { type: 'title', text: '紧张的目标是编辑决策信号', level: 2 }, { type: 'paragraph', html: '把过多条目塞进少量页面会降低索引的可读性。在进一步压缩排版前,先清理低价值引用或增加页面空间。' }, { type: 'tip', title: '不是印刷最终值', html: '字体、排版、校对和索引样式都会改变最终结果。请与出版社、印刷厂或专业索引编制人员确认。' }];
|
|
8
|
+
export const content = { ...base, slug: 'book-index-page-budget-calculator', title: '书籍索引页面预算计算器', description: '估算分析索引会增加的页面,并与书籍的编辑页面预算进行比较。', ui, seo, faq, howTo };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ToolDefinition } from '../../types';
|
|
2
|
+
import { bookIndexPageBudgetCalculator } from './entry';
|
|
3
|
+
|
|
4
|
+
export * from './entry';
|
|
5
|
+
|
|
6
|
+
export const BOOK_INDEX_PAGE_BUDGET_CALCULATOR_TOOL: ToolDefinition = {
|
|
7
|
+
entry: bookIndexPageBudgetCalculator,
|
|
8
|
+
Component: () => import('./component.astro'),
|
|
9
|
+
SEOComponent: () => import('./seo.astro'),
|
|
10
|
+
BibliographyComponent: () => import('./bibliography.astro'),
|
|
11
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import {
|
|
3
|
+
calculateIndexBudget,
|
|
4
|
+
centimetersToInches,
|
|
5
|
+
clampNumber,
|
|
6
|
+
inchesToCentimeters,
|
|
7
|
+
DEFAULT_INDEX_INPUTS,
|
|
8
|
+
} from './logic';
|
|
9
|
+
|
|
10
|
+
describe('book index page budget logic', () => {
|
|
11
|
+
it('estimates body pages, index pages, and totals for the default brief', () => {
|
|
12
|
+
const result = calculateIndexBudget(DEFAULT_INDEX_INPUTS);
|
|
13
|
+
|
|
14
|
+
expect(result.textPages).toBe(188);
|
|
15
|
+
expect(result.estimatedIndexPages).toBe(18);
|
|
16
|
+
expect(result.totalPages).toBe(206);
|
|
17
|
+
expect(result.targetTotalPages).toBe(197);
|
|
18
|
+
expect(result.entriesPerPage).toBe(46);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('responds to airy, compact, and large trim profiles', () => {
|
|
22
|
+
const airy = calculateIndexBudget({ ...DEFAULT_INDEX_INPUTS, density: 'airy' });
|
|
23
|
+
const compact = calculateIndexBudget({ ...DEFAULT_INDEX_INPUTS, density: 'compact' });
|
|
24
|
+
const large = calculateIndexBudget({ ...DEFAULT_INDEX_INPUTS, trimWidthInches: 8, trimHeightInches: 10 });
|
|
25
|
+
|
|
26
|
+
expect(airy.wordsPerPage).toBe(320);
|
|
27
|
+
expect(compact.entriesPerPage).toBe(60);
|
|
28
|
+
expect(large.textPages).toBeLessThan(DEFAULT_INDEX_INPUTS.manuscriptWords / 400);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('protects calculations from empty and invalid values', () => {
|
|
32
|
+
const result = calculateIndexBudget({
|
|
33
|
+
...DEFAULT_INDEX_INPUTS,
|
|
34
|
+
manuscriptWords: 0,
|
|
35
|
+
trimWidthInches: Number.NaN,
|
|
36
|
+
density: 'unknown' as 'standard',
|
|
37
|
+
indexEntries: 0,
|
|
38
|
+
targetIndexPages: 0,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
expect(result.textPages).toBe(1);
|
|
42
|
+
expect(result.estimatedIndexPages).toBe(1);
|
|
43
|
+
expect(result.targetIndexPages).toBe(1);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('clamps numeric input and converts dimensions', () => {
|
|
47
|
+
expect(clampNumber(Number.NaN, 1, 10, 5)).toBe(5);
|
|
48
|
+
expect(clampNumber(20, 1, 10, 5)).toBe(10);
|
|
49
|
+
expect(clampNumber(-2, 1, 10, 5)).toBe(1);
|
|
50
|
+
expect(centimetersToInches(inchesToCentimeters(6))).toBeCloseTo(6);
|
|
51
|
+
});
|
|
52
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type Density = 'airy' | 'standard' | 'compact';
|
|
2
|
+
|
|
3
|
+
export interface BookIndexInputs {
|
|
4
|
+
manuscriptWords: number;
|
|
5
|
+
trimWidthInches: number;
|
|
6
|
+
trimHeightInches: number;
|
|
7
|
+
density: Density;
|
|
8
|
+
indexEntries: number;
|
|
9
|
+
targetIndexPages: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface BookIndexBudget {
|
|
13
|
+
textPages: number;
|
|
14
|
+
estimatedIndexPages: number;
|
|
15
|
+
targetIndexPages: number;
|
|
16
|
+
totalPages: number;
|
|
17
|
+
targetTotalPages: number;
|
|
18
|
+
wordsPerPage: number;
|
|
19
|
+
entriesPerPage: number;
|
|
20
|
+
targetEntriesPerPage: number;
|
|
21
|
+
indexSharePercent: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const DEFAULT_INDEX_INPUTS: BookIndexInputs = {
|
|
25
|
+
manuscriptWords: 75000,
|
|
26
|
+
trimWidthInches: 6,
|
|
27
|
+
trimHeightInches: 9,
|
|
28
|
+
density: 'standard',
|
|
29
|
+
indexEntries: 800,
|
|
30
|
+
targetIndexPages: 9,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const DENSITY_PROFILES: Record<Density, { wordsPerPage: number; entriesPerPage: number }> = {
|
|
34
|
+
airy: { wordsPerPage: 320, entriesPerPage: 34 },
|
|
35
|
+
standard: { wordsPerPage: 400, entriesPerPage: 46 },
|
|
36
|
+
compact: { wordsPerPage: 480, entriesPerPage: 60 },
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function positive(value: number, fallback: number): number {
|
|
40
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function areaFactor(inputs: BookIndexInputs): number {
|
|
44
|
+
const area = positive(inputs.trimWidthInches, 6) * positive(inputs.trimHeightInches, 9);
|
|
45
|
+
return Math.min(1.6, Math.max(0.65, area / 54));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function roundPages(value: number): number {
|
|
49
|
+
return Math.max(1, Math.ceil(value));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function calculateIndexBudget(inputs: BookIndexInputs): BookIndexBudget {
|
|
53
|
+
const profile = DENSITY_PROFILES[inputs.density] ?? DENSITY_PROFILES.standard;
|
|
54
|
+
const wordsPerPage = Math.round(profile.wordsPerPage * areaFactor(inputs));
|
|
55
|
+
const textPages = roundPages(positive(inputs.manuscriptWords, 1) / wordsPerPage);
|
|
56
|
+
const indexEntries = positive(inputs.indexEntries, 1);
|
|
57
|
+
const estimatedIndexPages = roundPages(indexEntries / profile.entriesPerPage);
|
|
58
|
+
const targetIndexPages = roundPages(inputs.targetIndexPages);
|
|
59
|
+
const totalPages = textPages + estimatedIndexPages;
|
|
60
|
+
const targetTotalPages = textPages + targetIndexPages;
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
textPages,
|
|
64
|
+
estimatedIndexPages,
|
|
65
|
+
targetIndexPages,
|
|
66
|
+
totalPages,
|
|
67
|
+
targetTotalPages,
|
|
68
|
+
wordsPerPage,
|
|
69
|
+
entriesPerPage: profile.entriesPerPage,
|
|
70
|
+
targetEntriesPerPage: indexEntries / targetIndexPages,
|
|
71
|
+
indexSharePercent: (estimatedIndexPages / totalPages) * 100,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function clampNumber(value: number, min: number, max: number, fallback: number): number {
|
|
76
|
+
const safeValue = Number.isFinite(value) ? value : fallback;
|
|
77
|
+
return Math.min(max, Math.max(min, safeValue));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function inchesToCentimeters(value: number): number {
|
|
81
|
+
return value * 2.54;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function centimetersToInches(value: number): number {
|
|
85
|
+
return value / 2.54;
|
|
86
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { bookIndexPageBudgetCalculator } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props as Props;
|
|
11
|
+
const loader = bookIndexPageBudgetCalculator.i18n[locale] || bookIndexPageBudgetCalculator.i18n.en;
|
|
12
|
+
const content = await loader?.();
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content ? <SEORenderer content={{ locale, sections: content.seo }} /> : null}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type UnitSystem = 'metric' | 'imperial';
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = 'jjlmoya-books-index-budget-unit';
|
|
4
|
+
|
|
5
|
+
export function loadUnitSystem(): UnitSystem {
|
|
6
|
+
try {
|
|
7
|
+
return localStorage.getItem(STORAGE_KEY) === 'metric' ? 'metric' : 'imperial';
|
|
8
|
+
} catch {
|
|
9
|
+
return 'imperial';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function saveUnitSystem(unitSystem: UnitSystem): void {
|
|
14
|
+
try {
|
|
15
|
+
localStorage.setItem(STORAGE_KEY, unitSystem);
|
|
16
|
+
} catch {}
|
|
17
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export interface BookIndexUI extends Record<string, string> {
|
|
2
|
+
wordsLabel: string;
|
|
3
|
+
wordsHint: string;
|
|
4
|
+
trimWidthLabel: string;
|
|
5
|
+
trimHeightLabel: string;
|
|
6
|
+
trimHint: string;
|
|
7
|
+
unitLabel: string;
|
|
8
|
+
metricButtonLabel: string;
|
|
9
|
+
imperialButtonLabel: string;
|
|
10
|
+
densityLabel: string;
|
|
11
|
+
densityHint: string;
|
|
12
|
+
airyLabel: string;
|
|
13
|
+
standardLabel: string;
|
|
14
|
+
compactLabel: string;
|
|
15
|
+
indexEntriesLabel: string;
|
|
16
|
+
indexEntriesHint: string;
|
|
17
|
+
targetIndexPagesLabel: string;
|
|
18
|
+
targetIndexPagesHint: string;
|
|
19
|
+
textPagesLabel: string;
|
|
20
|
+
estimatedIndexPagesLabel: string;
|
|
21
|
+
totalPagesLabel: string;
|
|
22
|
+
targetTotalPagesLabel: string;
|
|
23
|
+
entriesPerPageLabel: string;
|
|
24
|
+
indexShareLabel: string;
|
|
25
|
+
statusBalanced: string;
|
|
26
|
+
statusTight: string;
|
|
27
|
+
statusRoomy: string;
|
|
28
|
+
statusBalancedHint: string;
|
|
29
|
+
statusTightHint: string;
|
|
30
|
+
statusRoomyHint: string;
|
|
31
|
+
sceneLabel: string;
|
|
32
|
+
sceneBodyLabel: string;
|
|
33
|
+
sceneIndexLabel: string;
|
|
34
|
+
sceneCaption: string;
|
|
35
|
+
sceneTargetCaption: string;
|
|
36
|
+
sourceNote: string;
|
|
37
|
+
summaryLabel: string;
|
|
38
|
+
summaryPagesOver: string;
|
|
39
|
+
summaryPagesSpare: string;
|
|
40
|
+
summaryOnTarget: string;
|
|
41
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { bookReadingTimeDeadlinePlanner } from './index';
|
|
4
|
+
import { bibliography } from './bibliography';
|
|
5
|
+
import type { KnownLocale } from '../../types';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
locale?: KnownLocale;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale = 'en' } = Astro.props as Props;
|
|
12
|
+
const loader = bookReadingTimeDeadlinePlanner.i18n[locale] || bookReadingTimeDeadlinePlanner.i18n.en;
|
|
13
|
+
const content = await loader?.();
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<SharedBibliography links={content?.bibliography || bibliography} />
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
interface TracedBibliographyEntry extends BibliographyEntry {
|
|
4
|
+
region: string;
|
|
5
|
+
originalLanguage: string;
|
|
6
|
+
supports: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const bibliography: TracedBibliographyEntry[] = [
|
|
10
|
+
{
|
|
11
|
+
name: 'Allegheny College. College Level Reading',
|
|
12
|
+
url: 'https://sites.allegheny.edu/academic-success/academic-resources/reading/',
|
|
13
|
+
region: 'United States',
|
|
14
|
+
originalLanguage: 'English',
|
|
15
|
+
supports: 'Measure an individual reading rate in words per minute and pair pacing with comprehension.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
name: 'Ministerio de Educación y Formación Profesional. Marco teórico de lectura PISA 2018',
|
|
19
|
+
url: 'https://www.educacionfpydeportes.gob.es/dam/jcr%3A2f1081a1-c1e4-4799-8a49-9bc589724ca4/marco%20teorico%20lectura%202018_esp_ESP.pdf',
|
|
20
|
+
region: 'Spain and OECD',
|
|
21
|
+
originalLanguage: 'Spanish',
|
|
22
|
+
supports: 'Reading fluency combines accuracy, automaticity and meaning, so a speed target is not a comprehension guarantee.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'Rayner, Schotter, Masson, Potter and Treiman. So much to read, so little time',
|
|
26
|
+
url: 'https://profiles.wustl.edu/en/publications/so-much-to-read-so-little-time-how-do-we-read-and-can-speed-readi/',
|
|
27
|
+
region: 'International research',
|
|
28
|
+
originalLanguage: 'English',
|
|
29
|
+
supports: 'Reading speed and comprehension can trade off, so the planner treats speed as a personal estimate rather than a promise.',
|
|
30
|
+
},
|
|
31
|
+
];
|