feat(calendar): add i18n support and cropped abacus day numbers
This commit implements two enhancements to the calendar feature: 1. i18n Support: - Created translation files for all 7 languages (en, de, ja, hi, es, la, goh) - Updated CalendarConfigPanel to use translations for UI labels - Updated CalendarPreview to use translations for status messages - Updated calendar page to use translations for page title/subtitle - Added calendarMessages export and integrated into main messages.ts 2. Cropped Abacus Day Numbers: - Enabled cropToActiveBeads feature for monthly calendar day abacuses - Abacuses now fill calendar cells more efficiently - Extracts cropped viewBox from rendered SVG for proper scaling - Uses custom padding (top: 8, bottom: 2, left: 5, right: 5) - Dynamically scales cropped abacuses to fit cells 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b9416883b6
commit
5242f890f7
|
|
@ -1,5 +1,6 @@
|
|||
'use client'
|
||||
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
import { AbacusReact, useAbacusConfig } from '@soroban/abacus-react'
|
||||
import { AbacusDisplayDropdown } from '@/components/AbacusDisplayDropdown'
|
||||
|
|
@ -17,21 +18,6 @@ interface CalendarConfigPanelProps {
|
|||
onGenerate: () => void
|
||||
}
|
||||
|
||||
const MONTHS = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
]
|
||||
|
||||
export function CalendarConfigPanel({
|
||||
month,
|
||||
year,
|
||||
|
|
@ -44,8 +30,24 @@ export function CalendarConfigPanel({
|
|||
onPaperSizeChange,
|
||||
onGenerate,
|
||||
}: CalendarConfigPanelProps) {
|
||||
const t = useTranslations('calendar')
|
||||
const abacusConfig = useAbacusConfig()
|
||||
|
||||
const MONTHS = [
|
||||
t('months.january'),
|
||||
t('months.february'),
|
||||
t('months.march'),
|
||||
t('months.april'),
|
||||
t('months.may'),
|
||||
t('months.june'),
|
||||
t('months.july'),
|
||||
t('months.august'),
|
||||
t('months.september'),
|
||||
t('months.october'),
|
||||
t('months.november'),
|
||||
t('months.december'),
|
||||
]
|
||||
|
||||
return (
|
||||
<div
|
||||
data-component="calendar-config-panel"
|
||||
|
|
@ -75,7 +77,7 @@ export function CalendarConfigPanel({
|
|||
color: 'yellow.400',
|
||||
})}
|
||||
>
|
||||
Calendar Format
|
||||
{t('format.title')}
|
||||
</legend>
|
||||
<div
|
||||
className={css({
|
||||
|
|
@ -104,7 +106,7 @@ export function CalendarConfigPanel({
|
|||
cursor: 'pointer',
|
||||
})}
|
||||
/>
|
||||
<span>Monthly Calendar (one page per month)</span>
|
||||
<span>{t('format.monthly')}</span>
|
||||
</label>
|
||||
<label
|
||||
className={css({
|
||||
|
|
@ -126,7 +128,7 @@ export function CalendarConfigPanel({
|
|||
cursor: 'pointer',
|
||||
})}
|
||||
/>
|
||||
<span>Daily Calendar (one page per day)</span>
|
||||
<span>{t('format.daily')}</span>
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -148,7 +150,7 @@ export function CalendarConfigPanel({
|
|||
color: 'yellow.400',
|
||||
})}
|
||||
>
|
||||
Date
|
||||
{t('date.title')}
|
||||
</legend>
|
||||
<div
|
||||
className={css({
|
||||
|
|
@ -216,7 +218,7 @@ export function CalendarConfigPanel({
|
|||
color: 'yellow.400',
|
||||
})}
|
||||
>
|
||||
Paper Size
|
||||
{t('paperSize.title')}
|
||||
</legend>
|
||||
<select
|
||||
data-element="paper-size-select"
|
||||
|
|
@ -236,10 +238,10 @@ export function CalendarConfigPanel({
|
|||
_hover: { borderColor: 'gray.500' },
|
||||
})}
|
||||
>
|
||||
<option value="us-letter">US Letter (8.5" × 11")</option>
|
||||
<option value="a4">A4 (210mm × 297mm)</option>
|
||||
<option value="a3">A3 (297mm × 420mm)</option>
|
||||
<option value="tabloid">Tabloid (11" × 17")</option>
|
||||
<option value="us-letter">{t('paperSize.usLetter')}</option>
|
||||
<option value="a4">{t('paperSize.a4')}</option>
|
||||
<option value="a3">{t('paperSize.a3')}</option>
|
||||
<option value="tabloid">{t('paperSize.tabloid')}</option>
|
||||
</select>
|
||||
</fieldset>
|
||||
|
||||
|
|
@ -259,7 +261,7 @@ export function CalendarConfigPanel({
|
|||
color: 'gray.300',
|
||||
})}
|
||||
>
|
||||
Calendar abacus style preview:
|
||||
{t('styling.preview')}
|
||||
</p>
|
||||
<div
|
||||
className={css({
|
||||
|
|
@ -312,7 +314,7 @@ export function CalendarConfigPanel({
|
|||
},
|
||||
})}
|
||||
>
|
||||
{isGenerating ? 'Generating PDF...' : 'Generate PDF Calendar'}
|
||||
{isGenerating ? t('generate.generating') : t('generate.button')}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { css } from '../../../../../styled-system/css'
|
||||
|
||||
interface CalendarPreviewProps {
|
||||
|
|
@ -31,6 +32,7 @@ async function fetchTypstPreview(
|
|||
}
|
||||
|
||||
export function CalendarPreview({ month, year, format, previewSvg }: CalendarPreviewProps) {
|
||||
const t = useTranslations('calendar')
|
||||
// Use React Query to fetch Typst-generated preview (client-side only)
|
||||
const { data: typstPreviewSvg, isLoading } = useQuery({
|
||||
queryKey: ['calendar-typst-preview', month, year, format],
|
||||
|
|
@ -64,7 +66,7 @@ export function CalendarPreview({ month, year, format, previewSvg }: CalendarPre
|
|||
textAlign: 'center',
|
||||
})}
|
||||
>
|
||||
{isLoading ? 'Loading preview...' : 'No preview available'}
|
||||
{isLoading ? t('preview.loading') : t('preview.noPreview')}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -92,10 +94,10 @@ export function CalendarPreview({ month, year, format, previewSvg }: CalendarPre
|
|||
})}
|
||||
>
|
||||
{previewSvg
|
||||
? 'Generated PDF'
|
||||
? t('preview.generatedPdf')
|
||||
: format === 'daily'
|
||||
? 'Live Preview (First Day)'
|
||||
: 'Live Preview'}
|
||||
? t('preview.livePreviewFirstDay')
|
||||
: t('preview.livePreview')}
|
||||
</p>
|
||||
<div
|
||||
className={css({
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { css } from '../../../../styled-system/css'
|
||||
import { useAbacusConfig } from '@soroban/abacus-react'
|
||||
import { PageWithNav } from '@/components/PageWithNav'
|
||||
|
|
@ -8,6 +9,7 @@ import { CalendarConfigPanel } from './components/CalendarConfigPanel'
|
|||
import { CalendarPreview } from './components/CalendarPreview'
|
||||
|
||||
export default function CalendarCreatorPage() {
|
||||
const t = useTranslations('calendar')
|
||||
const currentDate = new Date()
|
||||
const abacusConfig = useAbacusConfig()
|
||||
const [month, setMonth] = useState(currentDate.getMonth() + 1) // 1-12
|
||||
|
|
@ -108,7 +110,7 @@ export default function CalendarCreatorPage() {
|
|||
color: 'yellow.400',
|
||||
})}
|
||||
>
|
||||
Create Abacus Calendar
|
||||
{t('pageTitle')}
|
||||
</h1>
|
||||
<p
|
||||
className={css({
|
||||
|
|
@ -116,7 +118,7 @@ export default function CalendarCreatorPage() {
|
|||
color: 'gray.300',
|
||||
})}
|
||||
>
|
||||
Generate printable calendars with abacus date numbers
|
||||
{t('pageSubtitle')}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "Abakus-Kalender erstellen",
|
||||
"pageSubtitle": "Druckbare Kalender mit Abakus-Datumszahlen erstellen",
|
||||
"format": {
|
||||
"title": "Kalenderformat",
|
||||
"monthly": "Monatskalender (eine Seite pro Monat)",
|
||||
"daily": "Tageskalender (eine Seite pro Tag)"
|
||||
},
|
||||
"date": {
|
||||
"title": "Datum",
|
||||
"month": "Monat",
|
||||
"year": "Jahr"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "Papiergröße",
|
||||
"usLetter": "US Letter (8,5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "Tabloid (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "Kalender-Abakus-Stil Vorschau:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "PDF-Kalender erstellen",
|
||||
"generating": "PDF wird erstellt..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "Vorschau wird geladen...",
|
||||
"noPreview": "Keine Vorschau verfügbar",
|
||||
"generatedPdf": "Erstelltes PDF",
|
||||
"livePreview": "Live-Vorschau",
|
||||
"livePreviewFirstDay": "Live-Vorschau (Erster Tag)"
|
||||
},
|
||||
"months": {
|
||||
"january": "Januar",
|
||||
"february": "Februar",
|
||||
"march": "März",
|
||||
"april": "April",
|
||||
"may": "Mai",
|
||||
"june": "Juni",
|
||||
"july": "Juli",
|
||||
"august": "August",
|
||||
"september": "September",
|
||||
"october": "Oktober",
|
||||
"november": "November",
|
||||
"december": "Dezember"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "Sonntag",
|
||||
"monday": "Montag",
|
||||
"tuesday": "Dienstag",
|
||||
"wednesday": "Mittwoch",
|
||||
"thursday": "Donnerstag",
|
||||
"friday": "Freitag",
|
||||
"saturday": "Samstag"
|
||||
},
|
||||
"notes": "Notizen:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "Create Abacus Calendar",
|
||||
"pageSubtitle": "Generate printable calendars with abacus date numbers",
|
||||
"format": {
|
||||
"title": "Calendar Format",
|
||||
"monthly": "Monthly Calendar (one page per month)",
|
||||
"daily": "Daily Calendar (one page per day)"
|
||||
},
|
||||
"date": {
|
||||
"title": "Date",
|
||||
"month": "Month",
|
||||
"year": "Year"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "Paper Size",
|
||||
"usLetter": "US Letter (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "Tabloid (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "Calendar abacus style preview:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "Generate PDF Calendar",
|
||||
"generating": "Generating PDF..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "Loading preview...",
|
||||
"noPreview": "No preview available",
|
||||
"generatedPdf": "Generated PDF",
|
||||
"livePreview": "Live Preview",
|
||||
"livePreviewFirstDay": "Live Preview (First Day)"
|
||||
},
|
||||
"months": {
|
||||
"january": "January",
|
||||
"february": "February",
|
||||
"march": "March",
|
||||
"april": "April",
|
||||
"may": "May",
|
||||
"june": "June",
|
||||
"july": "July",
|
||||
"august": "August",
|
||||
"september": "September",
|
||||
"october": "October",
|
||||
"november": "November",
|
||||
"december": "December"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "Sunday",
|
||||
"monday": "Monday",
|
||||
"tuesday": "Tuesday",
|
||||
"wednesday": "Wednesday",
|
||||
"thursday": "Thursday",
|
||||
"friday": "Friday",
|
||||
"saturday": "Saturday"
|
||||
},
|
||||
"notes": "Notes:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "Crear Calendario de Ábaco",
|
||||
"pageSubtitle": "Generar calendarios imprimibles con números de fecha de ábaco",
|
||||
"format": {
|
||||
"title": "Formato de Calendario",
|
||||
"monthly": "Calendario Mensual (una página por mes)",
|
||||
"daily": "Calendario Diario (una página por día)"
|
||||
},
|
||||
"date": {
|
||||
"title": "Fecha",
|
||||
"month": "Mes",
|
||||
"year": "Año"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "Tamaño de Papel",
|
||||
"usLetter": "Carta US (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "Tabloide (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "Vista previa del estilo de ábaco del calendario:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "Generar Calendario PDF",
|
||||
"generating": "Generando PDF..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "Cargando vista previa...",
|
||||
"noPreview": "No hay vista previa disponible",
|
||||
"generatedPdf": "PDF Generado",
|
||||
"livePreview": "Vista Previa en Vivo",
|
||||
"livePreviewFirstDay": "Vista Previa en Vivo (Primer Día)"
|
||||
},
|
||||
"months": {
|
||||
"january": "Enero",
|
||||
"february": "Febrero",
|
||||
"march": "Marzo",
|
||||
"april": "Abril",
|
||||
"may": "Mayo",
|
||||
"june": "Junio",
|
||||
"july": "Julio",
|
||||
"august": "Agosto",
|
||||
"september": "Septiembre",
|
||||
"october": "Octubre",
|
||||
"november": "Noviembre",
|
||||
"december": "Diciembre"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "Domingo",
|
||||
"monday": "Lunes",
|
||||
"tuesday": "Martes",
|
||||
"wednesday": "Miércoles",
|
||||
"thursday": "Jueves",
|
||||
"friday": "Viernes",
|
||||
"saturday": "Sábado"
|
||||
},
|
||||
"notes": "Notas:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "Abacus Kalender Giscaffan",
|
||||
"pageSubtitle": "Drucchāri kalendera mit abacus tagozalun giskaffen",
|
||||
"format": {
|
||||
"title": "Kalenderart",
|
||||
"monthly": "Mānoðkalender (ein sīta per mānoð)",
|
||||
"daily": "Tagkalender (ein sīta per tag)"
|
||||
},
|
||||
"date": {
|
||||
"title": "Tag",
|
||||
"month": "Mānoð",
|
||||
"year": "Jār"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "Papiergrōzi",
|
||||
"usLetter": "US Brief (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "Tabloid (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "Kalender abacus stil forasihti:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "PDF Kalender Giskaffen",
|
||||
"generating": "PDF wirdit giskaffan..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "Forasihti ladet...",
|
||||
"noPreview": "Nein forasihti ferfuogbar",
|
||||
"generatedPdf": "Giskaffan PDF",
|
||||
"livePreview": "Lebenti Forasihti",
|
||||
"livePreviewFirstDay": "Lebenti Forasihti (Ēristo Tag)"
|
||||
},
|
||||
"months": {
|
||||
"january": "Hartmānot",
|
||||
"february": "Hornung",
|
||||
"march": "Lentzinmānot",
|
||||
"april": "Ōstarmānot",
|
||||
"may": "Winnemānot",
|
||||
"june": "Brāhmānot",
|
||||
"july": "Hewimānot",
|
||||
"august": "Aranmānot",
|
||||
"september": "Witumanot",
|
||||
"october": "Windurmānot",
|
||||
"november": "Herbistmānot",
|
||||
"december": "Heilagmānot"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "Sunnūntag",
|
||||
"monday": "Mānetag",
|
||||
"tuesday": "Ziostag",
|
||||
"wednesday": "Mittawehha",
|
||||
"thursday": "Donarestag",
|
||||
"friday": "Frīatag",
|
||||
"saturday": "Sambaztag"
|
||||
},
|
||||
"notes": "Notiziun:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "अबेकस कैलेंडर बनाएं",
|
||||
"pageSubtitle": "अबेकस तिथि संख्याओं के साथ मुद्रण योग्य कैलेंडर उत्पन्न करें",
|
||||
"format": {
|
||||
"title": "कैलेंडर प्रारूप",
|
||||
"monthly": "मासिक कैलेंडर (प्रति माह एक पृष्ठ)",
|
||||
"daily": "दैनिक कैलेंडर (प्रति दिन एक पृष्ठ)"
|
||||
},
|
||||
"date": {
|
||||
"title": "तिथि",
|
||||
"month": "महीना",
|
||||
"year": "वर्ष"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "कागज का आकार",
|
||||
"usLetter": "यूएस लेटर (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "टैबलॉइड (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "कैलेंडर अबेकस शैली पूर्वावलोकन:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "पीडीएफ कैलेंडर उत्पन्न करें",
|
||||
"generating": "पीडीएफ उत्पन्न हो रहा है..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "पूर्वावलोकन लोड हो रहा है...",
|
||||
"noPreview": "कोई पूर्वावलोकन उपलब्ध नहीं",
|
||||
"generatedPdf": "उत्पन्न पीडीएफ",
|
||||
"livePreview": "लाइव पूर्वावलोकन",
|
||||
"livePreviewFirstDay": "लाइव पूर्वावलोकन (पहला दिन)"
|
||||
},
|
||||
"months": {
|
||||
"january": "जनवरी",
|
||||
"february": "फरवरी",
|
||||
"march": "मार्च",
|
||||
"april": "अप्रैल",
|
||||
"may": "मई",
|
||||
"june": "जून",
|
||||
"july": "जुलाई",
|
||||
"august": "अगस्त",
|
||||
"september": "सितंबर",
|
||||
"october": "अक्टूबर",
|
||||
"november": "नवंबर",
|
||||
"december": "दिसंबर"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "रविवार",
|
||||
"monday": "सोमवार",
|
||||
"tuesday": "मंगलवार",
|
||||
"wednesday": "बुधवार",
|
||||
"thursday": "गुरुवार",
|
||||
"friday": "शुक्रवार",
|
||||
"saturday": "शनिवार"
|
||||
},
|
||||
"notes": "नोट्स:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "そろばんカレンダーを作成",
|
||||
"pageSubtitle": "そろばんの日付番号付き印刷可能なカレンダーを生成",
|
||||
"format": {
|
||||
"title": "カレンダー形式",
|
||||
"monthly": "月間カレンダー(月ごとに1ページ)",
|
||||
"daily": "日めくりカレンダー(日ごとに1ページ)"
|
||||
},
|
||||
"date": {
|
||||
"title": "日付",
|
||||
"month": "月",
|
||||
"year": "年"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "用紙サイズ",
|
||||
"usLetter": "USレター (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "タブロイド (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "カレンダーそろばんスタイルプレビュー:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "PDFカレンダーを生成",
|
||||
"generating": "PDFを生成中..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "プレビューを読み込み中...",
|
||||
"noPreview": "プレビューがありません",
|
||||
"generatedPdf": "生成されたPDF",
|
||||
"livePreview": "ライブプレビュー",
|
||||
"livePreviewFirstDay": "ライブプレビュー(初日)"
|
||||
},
|
||||
"months": {
|
||||
"january": "1月",
|
||||
"february": "2月",
|
||||
"march": "3月",
|
||||
"april": "4月",
|
||||
"may": "5月",
|
||||
"june": "6月",
|
||||
"july": "7月",
|
||||
"august": "8月",
|
||||
"september": "9月",
|
||||
"october": "10月",
|
||||
"november": "11月",
|
||||
"december": "12月"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "日曜日",
|
||||
"monday": "月曜日",
|
||||
"tuesday": "火曜日",
|
||||
"wednesday": "水曜日",
|
||||
"thursday": "木曜日",
|
||||
"friday": "金曜日",
|
||||
"saturday": "土曜日"
|
||||
},
|
||||
"notes": "メモ:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"calendar": {
|
||||
"pageTitle": "Calendarium Abaci Creare",
|
||||
"pageSubtitle": "Calendaria imprimibilia cum numeris diei abaci generare",
|
||||
"format": {
|
||||
"title": "Forma Calendarii",
|
||||
"monthly": "Calendarium Mensuale (una pagina per mensem)",
|
||||
"daily": "Calendarium Diurnum (una pagina per diem)"
|
||||
},
|
||||
"date": {
|
||||
"title": "Dies",
|
||||
"month": "Mensis",
|
||||
"year": "Annus"
|
||||
},
|
||||
"paperSize": {
|
||||
"title": "Magnitudo Chartae",
|
||||
"usLetter": "US Epistula (8.5\" × 11\")",
|
||||
"a4": "A4 (210mm × 297mm)",
|
||||
"a3": "A3 (297mm × 420mm)",
|
||||
"tabloid": "Tabloid (11\" × 17\")"
|
||||
},
|
||||
"styling": {
|
||||
"preview": "Praevisio stili abaci calendarii:"
|
||||
},
|
||||
"generate": {
|
||||
"button": "Calendarium PDF Generare",
|
||||
"generating": "PDF Generatur..."
|
||||
},
|
||||
"preview": {
|
||||
"loading": "Praevisio cargatur...",
|
||||
"noPreview": "Nulla praevisio disponibilis",
|
||||
"generatedPdf": "PDF Generatum",
|
||||
"livePreview": "Praevisio Viva",
|
||||
"livePreviewFirstDay": "Praevisio Viva (Primus Dies)"
|
||||
},
|
||||
"months": {
|
||||
"january": "Ianuarius",
|
||||
"february": "Februarius",
|
||||
"march": "Martius",
|
||||
"april": "Aprilis",
|
||||
"may": "Maius",
|
||||
"june": "Iunius",
|
||||
"july": "Iulius",
|
||||
"august": "Augustus",
|
||||
"september": "September",
|
||||
"october": "October",
|
||||
"november": "November",
|
||||
"december": "December"
|
||||
},
|
||||
"weekdays": {
|
||||
"sunday": "Dies Solis",
|
||||
"monday": "Dies Lunae",
|
||||
"tuesday": "Dies Martis",
|
||||
"wednesday": "Dies Mercurii",
|
||||
"thursday": "Dies Iovis",
|
||||
"friday": "Dies Veneris",
|
||||
"saturday": "Dies Saturni"
|
||||
},
|
||||
"notes": "Notae:"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
import es from './es.json'
|
||||
import goh from './goh.json'
|
||||
import hi from './hi.json'
|
||||
import ja from './ja.json'
|
||||
import la from './la.json'
|
||||
|
||||
export const calendarMessages = {
|
||||
en: en.calendar,
|
||||
de: de.calendar,
|
||||
ja: ja.calendar,
|
||||
hi: hi.calendar,
|
||||
es: es.calendar,
|
||||
la: la.calendar,
|
||||
goh: goh.calendar,
|
||||
} as const
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import { rithmomachiaMessages } from '@/arcade-games/rithmomachia/messages'
|
||||
import { calendarMessages } from '@/i18n/locales/calendar/messages'
|
||||
import { gamesMessages } from '@/i18n/locales/games/messages'
|
||||
import { guideMessages } from '@/i18n/locales/guide/messages'
|
||||
import { homeMessages } from '@/i18n/locales/home/messages'
|
||||
|
|
@ -40,6 +41,7 @@ export async function getMessages(locale: Locale) {
|
|||
{ games: gamesMessages[locale] },
|
||||
{ guide: guideMessages[locale] },
|
||||
{ tutorial: tutorialMessages[locale] },
|
||||
{ calendar: calendarMessages[locale] },
|
||||
rithmomachiaMessages[locale]
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,6 +117,10 @@ export function generateCalendarComposite(options: CalendarCompositeOptions): st
|
|||
showNumbers={false}
|
||||
frameVisible={true}
|
||||
compact={false}
|
||||
hideInactiveBeads={true}
|
||||
cropToActiveBeads={{
|
||||
padding: { top: 8, bottom: 2, left: 5, right: 5 }
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -179,22 +183,41 @@ export function generateCalendarComposite(options: CalendarCompositeOptions): st
|
|||
const cellX = MARGIN + col * CELL_WIDTH
|
||||
const cellY = GRID_START_Y + WEEKDAY_ROW_HEIGHT + row * DAY_CELL_HEIGHT
|
||||
|
||||
// Render cropped abacus SVG
|
||||
const abacusSVG = renderAbacusSVG(day, 2, 1)
|
||||
|
||||
// Extract viewBox and dimensions from the cropped SVG
|
||||
const viewBoxMatch = abacusSVG.match(/viewBox="([^"]*)"/)
|
||||
const widthMatch = abacusSVG.match(/width="?([0-9.]+)"?/)
|
||||
const heightMatch = abacusSVG.match(/height="?([0-9.]+)"?/)
|
||||
|
||||
const croppedViewBox = viewBoxMatch ? viewBoxMatch[1] : '0 0 120 230'
|
||||
const croppedWidth = widthMatch ? parseFloat(widthMatch[1]) : ABACUS_NATURAL_WIDTH
|
||||
const croppedHeight = heightMatch ? parseFloat(heightMatch[1]) : ABACUS_NATURAL_HEIGHT
|
||||
|
||||
// Calculate scale to fit cropped abacus in cell
|
||||
const MAX_SCALE_X = (CELL_WIDTH - CELL_PADDING * 2) / croppedWidth
|
||||
const MAX_SCALE_Y = (DAY_CELL_HEIGHT - CELL_PADDING * 2) / croppedHeight
|
||||
const fitScale = Math.min(MAX_SCALE_X, MAX_SCALE_Y) * 0.95 // 95% to leave breathing room
|
||||
|
||||
const scaledWidth = croppedWidth * fitScale
|
||||
const scaledHeight = croppedHeight * fitScale
|
||||
|
||||
// Center abacus in cell
|
||||
const abacusCenterX = cellX + CELL_WIDTH / 2
|
||||
const abacusCenterY = cellY + DAY_CELL_HEIGHT / 2
|
||||
|
||||
// Offset to top-left corner of abacus (accounting for scaled size)
|
||||
const abacusX = abacusCenterX - SCALED_ABACUS_WIDTH / 2
|
||||
const abacusY = abacusCenterY - SCALED_ABACUS_HEIGHT / 2
|
||||
// Offset to top-left corner of abacus
|
||||
const abacusX = abacusCenterX - scaledWidth / 2
|
||||
const abacusY = abacusCenterY - scaledHeight / 2
|
||||
|
||||
// Render at scale=1 and let the nested SVG handle scaling via viewBox
|
||||
const abacusSVG = renderAbacusSVG(day, 2, 1)
|
||||
// Extract SVG content (remove outer <svg> tags)
|
||||
const svgContent = abacusSVG.replace(/<svg[^>]*>/, '').replace(/<\/svg>$/, '')
|
||||
|
||||
return `
|
||||
<!-- Day ${day} (row ${row}, col ${col}) -->
|
||||
<svg x="${abacusX}" y="${abacusY}" width="${SCALED_ABACUS_WIDTH}" height="${SCALED_ABACUS_HEIGHT}"
|
||||
viewBox="0 0 ${ABACUS_NATURAL_WIDTH} ${ABACUS_NATURAL_HEIGHT}">
|
||||
<svg x="${abacusX}" y="${abacusY}" width="${scaledWidth}" height="${scaledHeight}"
|
||||
viewBox="${croppedViewBox}">
|
||||
${svgContent}
|
||||
</svg>`
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue