Compare commits
1 Commits
abacus-rea
...
codex/inte
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02e49e9601 |
@@ -3,6 +3,7 @@
|
||||
import { useAbacusConfig } from '@soroban/abacus-react'
|
||||
import { useForm } from '@tanstack/react-form'
|
||||
import { useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { ConfigurationFormWithoutGenerate } from '@/components/ConfigurationFormWithoutGenerate'
|
||||
import { GenerationProgress } from '@/components/GenerationProgress'
|
||||
import { LivePreview } from '@/components/LivePreview'
|
||||
@@ -107,6 +108,7 @@ export default function CreatePage() {
|
||||
const [generationStatus, setGenerationStatus] = useState<GenerationStatus>('idle')
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const globalConfig = useAbacusConfig()
|
||||
const t = useTranslations('create.page')
|
||||
|
||||
const form = useForm<FlashcardFormState>({
|
||||
defaultValues: {
|
||||
@@ -184,7 +186,7 @@ export default function CreatePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<PageWithNav navTitle="Create Flashcards" navEmoji="✨">
|
||||
<PageWithNav navTitle={t('navTitle')} navEmoji="✨">
|
||||
<div className={css({ minHeight: '100vh', bg: 'gray.50' })}>
|
||||
{/* Main Content */}
|
||||
<div className={container({ maxW: '7xl', px: '4', py: '8' })}>
|
||||
@@ -197,7 +199,7 @@ export default function CreatePage() {
|
||||
color: 'gray.900',
|
||||
})}
|
||||
>
|
||||
Create Your Flashcards
|
||||
{t('hero.title')}
|
||||
</h1>
|
||||
<p
|
||||
className={css({
|
||||
@@ -205,7 +207,7 @@ export default function CreatePage() {
|
||||
color: 'gray.600',
|
||||
})}
|
||||
>
|
||||
Configure content and style, preview instantly, then generate your flashcards
|
||||
{t('hero.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -248,7 +250,7 @@ export default function CreatePage() {
|
||||
color: 'gray.900',
|
||||
})}
|
||||
>
|
||||
🎨 Visual Style
|
||||
{t('style.title')}
|
||||
</h3>
|
||||
<p
|
||||
className={css({
|
||||
@@ -256,7 +258,7 @@ export default function CreatePage() {
|
||||
color: 'gray.600',
|
||||
})}
|
||||
>
|
||||
See changes instantly in the preview
|
||||
{t('style.subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -337,12 +339,12 @@ export default function CreatePage() {
|
||||
animation: 'spin 1s linear infinite',
|
||||
})}
|
||||
/>
|
||||
Generating Your Flashcards...
|
||||
{t('generateButton.loading')}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className={css({ fontSize: 'xl' })}>✨</div>
|
||||
Generate Flashcards
|
||||
{t('generateButton.cta')}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
@@ -374,7 +376,7 @@ export default function CreatePage() {
|
||||
color: 'red.800',
|
||||
})}
|
||||
>
|
||||
Generation Failed
|
||||
{t('error.title')}
|
||||
</h3>
|
||||
</div>
|
||||
<p
|
||||
@@ -399,7 +401,7 @@ export default function CreatePage() {
|
||||
_hover: { bg: 'red.700' },
|
||||
})}
|
||||
>
|
||||
Try Again
|
||||
{t('error.retry')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,6 +8,7 @@ import * as Switch from '@radix-ui/react-switch'
|
||||
import * as Tabs from '@radix-ui/react-tabs'
|
||||
import type { FormApi } from '@tanstack/react-form'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import type { FlashcardFormState } from '@/app/create/page'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { grid, hstack, stack } from '../../styled-system/patterns'
|
||||
@@ -18,6 +19,8 @@ interface ConfigurationFormProps {
|
||||
}
|
||||
|
||||
export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProps) {
|
||||
const t = useTranslations('create.form')
|
||||
|
||||
return (
|
||||
<div className={stack({ gap: '6' })}>
|
||||
<div className={stack({ gap: '2' })}>
|
||||
@@ -28,14 +31,14 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
color: 'gray.900',
|
||||
})}
|
||||
>
|
||||
Configuration
|
||||
{t('title')}
|
||||
</h2>
|
||||
<p
|
||||
className={css({
|
||||
color: 'gray.600',
|
||||
})}
|
||||
>
|
||||
Content, layout, and output settings
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -57,8 +60,8 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
})}
|
||||
>
|
||||
{[
|
||||
{ value: 'content', label: '📝 Content', icon: '🔢' },
|
||||
{ value: 'output', label: '💾 Output', icon: '💾' },
|
||||
{ value: 'content', label: t('tabs.content'), icon: '🔢' },
|
||||
{ value: 'output', label: t('tabs.output'), icon: '💾' },
|
||||
].map((tab) => (
|
||||
<Tabs.Trigger
|
||||
key={tab.value}
|
||||
@@ -90,15 +93,15 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
<Tabs.Content value="content" className={css({ mt: '6' })}>
|
||||
<div className={stack({ gap: '6' })}>
|
||||
<FormField
|
||||
label="Number Range"
|
||||
description="Define which numbers to include (e.g., '0-99' or '1,2,5,10')"
|
||||
label={t('content.range.label')}
|
||||
description={t('content.range.description')}
|
||||
>
|
||||
<form.Field name="range">
|
||||
{(field) => (
|
||||
<input
|
||||
value={field.state.value || ''}
|
||||
onChange={(e) => field.handleChange(e.target.value)}
|
||||
placeholder="0-99"
|
||||
placeholder={t('content.range.placeholder')}
|
||||
className={inputStyles}
|
||||
/>
|
||||
)}
|
||||
@@ -106,7 +109,7 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
</FormField>
|
||||
|
||||
<div className={grid({ columns: 2, gap: '4' })}>
|
||||
<FormField label="Step Size" description="For ranges, increment by this amount">
|
||||
<FormField label={t('content.step.label')} description={t('content.step.description')}>
|
||||
<form.Field name="step">
|
||||
{(field) => (
|
||||
<input
|
||||
@@ -120,7 +123,7 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
</form.Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Shuffle Cards" description="Randomize the order">
|
||||
<FormField label={t('content.shuffle.label')} description={t('content.shuffle.description')}>
|
||||
<form.Field name="shuffle">
|
||||
{(field) => (
|
||||
<SwitchField
|
||||
@@ -137,7 +140,10 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
{/* Output Tab */}
|
||||
<Tabs.Content value="output" className={css({ mt: '6' })}>
|
||||
<div className={stack({ gap: '6' })}>
|
||||
<FormField label="Output Format" description="Choose your preferred file format">
|
||||
<FormField
|
||||
label={t('output.format.label')}
|
||||
description={t('output.format.description')}
|
||||
>
|
||||
<form.Field name="format">
|
||||
{(field) => (
|
||||
<FormatSelectField
|
||||
@@ -173,7 +179,7 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
color: 'blue.800',
|
||||
})}
|
||||
>
|
||||
📄 PDF Layout Options
|
||||
{t('output.pdf.sectionTitle')}
|
||||
</h3>
|
||||
<p
|
||||
className={css({
|
||||
@@ -181,14 +187,14 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
color: 'blue.700',
|
||||
})}
|
||||
>
|
||||
Configure page layout and printing options for your PDF
|
||||
{t('output.pdf.sectionDescription')}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className={grid({ columns: 2, gap: '4' })}>
|
||||
<FormField
|
||||
label="Cards Per Page"
|
||||
description="Number of flashcards on each page"
|
||||
label={t('output.pdf.cardsPerPage.label')}
|
||||
description={t('output.pdf.cardsPerPage.description')}
|
||||
>
|
||||
<form.Field name="cardsPerPage">
|
||||
{(field) => (
|
||||
@@ -198,13 +204,18 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
min={1}
|
||||
max={12}
|
||||
step={1}
|
||||
formatValue={(value) => `${value} cards`}
|
||||
formatValue={(value) =>
|
||||
t('output.pdf.cardsPerPage.value', { count: value })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Paper Size" description="Output paper dimensions">
|
||||
<FormField
|
||||
label={t('output.pdf.paperSize.label')}
|
||||
description={t('output.pdf.paperSize.description')}
|
||||
>
|
||||
<form.Field name="paperSize">
|
||||
{(field) => (
|
||||
<SelectField
|
||||
@@ -213,28 +224,32 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
options={[
|
||||
{
|
||||
value: 'us-letter',
|
||||
label: 'US Letter (8.5×11")',
|
||||
label: t('output.pdf.paperSize.options.us-letter'),
|
||||
},
|
||||
{
|
||||
value: 'a4',
|
||||
label: 'A4 (210×297mm)',
|
||||
label: t('output.pdf.paperSize.options.a4'),
|
||||
},
|
||||
{
|
||||
value: 'a3',
|
||||
label: 'A3 (297×420mm)',
|
||||
label: t('output.pdf.paperSize.options.a3'),
|
||||
},
|
||||
{
|
||||
value: 'a5',
|
||||
label: 'A5 (148×210mm)',
|
||||
label: t('output.pdf.paperSize.options.a5'),
|
||||
},
|
||||
]}
|
||||
placeholder={t('shared.selectPlaceholder')}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
</FormField>
|
||||
</div>
|
||||
|
||||
<FormField label="Orientation" description="Page layout direction">
|
||||
<FormField
|
||||
label={t('output.pdf.orientation.label')}
|
||||
description={t('output.pdf.orientation.description')}
|
||||
>
|
||||
<form.Field name="orientation">
|
||||
{(field) => (
|
||||
<RadioGroupField
|
||||
@@ -243,13 +258,17 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
options={[
|
||||
{
|
||||
value: 'portrait',
|
||||
label: '📄 Portrait',
|
||||
desc: 'Taller than wide',
|
||||
label: t('output.pdf.orientation.options.portrait.label'),
|
||||
desc: t(
|
||||
'output.pdf.orientation.options.portrait.description'
|
||||
),
|
||||
},
|
||||
{
|
||||
value: 'landscape',
|
||||
label: '📃 Landscape',
|
||||
desc: 'Wider than tall',
|
||||
label: t('output.pdf.orientation.options.landscape.label'),
|
||||
desc: t(
|
||||
'output.pdf.orientation.options.landscape.description'
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -259,8 +278,8 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
|
||||
<div className={grid({ columns: 2, gap: '4' })}>
|
||||
<FormField
|
||||
label="Show Cut Marks"
|
||||
description="Add guides for cutting cards"
|
||||
label={t('output.pdf.showCutMarks.label')}
|
||||
description={t('output.pdf.showCutMarks.description')}
|
||||
>
|
||||
<form.Field name="showCutMarks">
|
||||
{(field) => (
|
||||
@@ -273,8 +292,8 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
</FormField>
|
||||
|
||||
<FormField
|
||||
label="Registration Marks"
|
||||
description="Alignment guides for duplex printing"
|
||||
label={t('output.pdf.showRegistration.label')}
|
||||
description={t('output.pdf.showRegistration.description')}
|
||||
>
|
||||
<form.Field name="showRegistration">
|
||||
{(field) => (
|
||||
@@ -294,8 +313,8 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
</form.Field>
|
||||
|
||||
<FormField
|
||||
label="Scale Factor"
|
||||
description="Adjust the overall size of flashcards"
|
||||
label={t('output.scaleFactor.label')}
|
||||
description={t('output.scaleFactor.description')}
|
||||
>
|
||||
<form.Field name="scaleFactor">
|
||||
{(field) => (
|
||||
@@ -305,7 +324,11 @@ export function ConfigurationFormWithoutGenerate({ form }: ConfigurationFormProp
|
||||
min={0.5}
|
||||
max={1.0}
|
||||
step={0.05}
|
||||
formatValue={(value) => `${Math.round(value * 100)}%`}
|
||||
formatValue={(value) =>
|
||||
t('output.scaleFactor.value', {
|
||||
percent: Math.round(value * 100),
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</form.Field>
|
||||
|
||||
@@ -4,48 +4,30 @@ import * as Select from '@radix-ui/react-select'
|
||||
import { ChevronDown } from 'lucide-react'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { hstack, stack } from '../../styled-system/patterns'
|
||||
|
||||
interface FormatOption {
|
||||
value: string
|
||||
label: string
|
||||
icon: string
|
||||
description: string
|
||||
}
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
interface FormatSelectFieldProps {
|
||||
value: string
|
||||
onValueChange: (value: string) => void
|
||||
}
|
||||
|
||||
const formatOptions: FormatOption[] = [
|
||||
{
|
||||
value: 'pdf',
|
||||
label: 'PDF',
|
||||
icon: '📄',
|
||||
description: 'Print-ready vector document with layout options',
|
||||
},
|
||||
{
|
||||
value: 'html',
|
||||
label: 'HTML',
|
||||
icon: '🌐',
|
||||
description: 'Interactive web flashcards',
|
||||
},
|
||||
{
|
||||
value: 'svg',
|
||||
label: 'SVG',
|
||||
icon: '🖼️',
|
||||
description: 'Scalable vector images',
|
||||
},
|
||||
{
|
||||
value: 'png',
|
||||
label: 'PNG',
|
||||
icon: '📷',
|
||||
description: 'High-resolution images',
|
||||
},
|
||||
]
|
||||
|
||||
export function FormatSelectField({ value, onValueChange }: FormatSelectFieldProps) {
|
||||
const selectedOption = formatOptions.find((option) => option.value === value) || formatOptions[0]
|
||||
const t = useTranslations('create.form.formatOptions')
|
||||
const formatOptions = (['pdf', 'html', 'svg', 'png'] as const).map((option) => ({
|
||||
value: option,
|
||||
icon:
|
||||
{
|
||||
pdf: '📄',
|
||||
html: '🌐',
|
||||
svg: '🖼️',
|
||||
png: '📷',
|
||||
}[option],
|
||||
label: t(`${option}.label`),
|
||||
description: t(`${option}.description`),
|
||||
}))
|
||||
|
||||
const selectedOption =
|
||||
formatOptions.find((option) => option.value === value) || formatOptions[0]
|
||||
|
||||
return (
|
||||
<Select.Root value={value} onValueChange={onValueChange}>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
import * as Progress from '@radix-ui/react-progress'
|
||||
import { CheckCircle, Sparkles, Zap } from 'lucide-react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import type { FlashcardFormState } from '@/app/create/page'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { hstack, stack } from '../../styled-system/patterns'
|
||||
@@ -23,35 +24,40 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
const [progress, setProgress] = useState(0)
|
||||
const [currentStep, setCurrentStep] = useState(0)
|
||||
const [steps, setSteps] = useState<ProgressStep[]>([])
|
||||
const t = useTranslations('create.progress')
|
||||
|
||||
useEffect(() => {
|
||||
// Initialize steps based on config
|
||||
const generationSteps: ProgressStep[] = [
|
||||
{
|
||||
id: 'validate',
|
||||
label: 'Validating Configuration',
|
||||
description: 'Checking parameters and dependencies',
|
||||
label: t('steps.validate.label'),
|
||||
description: t('steps.validate.description'),
|
||||
icon: <CheckCircle size={20} />,
|
||||
status: 'pending',
|
||||
},
|
||||
{
|
||||
id: 'generate',
|
||||
label: 'Generating Soroban Patterns',
|
||||
description: `Creating ${getEstimatedCardCount(config)} flashcard patterns`,
|
||||
label: t('steps.generate.label'),
|
||||
description: t('steps.generate.description', {
|
||||
count: getEstimatedCardCount(config),
|
||||
}),
|
||||
icon: <Sparkles size={20} />,
|
||||
status: 'pending',
|
||||
},
|
||||
{
|
||||
id: 'render',
|
||||
label: `Rendering ${config.format?.toUpperCase() || 'PDF'}`,
|
||||
description: 'Converting to your chosen format',
|
||||
label: t('steps.render.label', {
|
||||
format: config.format?.toUpperCase() || t('steps.render.defaultFormat'),
|
||||
}),
|
||||
description: t('steps.render.description'),
|
||||
icon: <Zap size={20} />,
|
||||
status: 'pending',
|
||||
},
|
||||
{
|
||||
id: 'finalize',
|
||||
label: 'Finalizing Download',
|
||||
description: 'Preparing your flashcards for download',
|
||||
label: t('steps.finalize.label'),
|
||||
description: t('steps.finalize.description'),
|
||||
icon: <CheckCircle size={20} />,
|
||||
status: 'pending',
|
||||
},
|
||||
@@ -73,7 +79,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
}, 500)
|
||||
|
||||
return () => clearInterval(progressInterval)
|
||||
}, [config])
|
||||
}, [config, t])
|
||||
|
||||
useEffect(() => {
|
||||
// Update step statuses based on current step
|
||||
@@ -87,6 +93,8 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
|
||||
const estimatedTime = getEstimatedTime(config)
|
||||
const currentStepData = steps[currentStep]
|
||||
const funFacts = (t.raw('funFacts') as string[]) || []
|
||||
const funFact = useMemo(() => getFunFact(funFacts), [funFacts])
|
||||
|
||||
return (
|
||||
<div className={stack({ gap: '6' })}>
|
||||
@@ -99,7 +107,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
color: 'gray.900',
|
||||
})}
|
||||
>
|
||||
Generating Your Flashcards
|
||||
{t('title')}
|
||||
</h3>
|
||||
<div
|
||||
className={css({
|
||||
@@ -108,7 +116,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
fontWeight: 'medium',
|
||||
})}
|
||||
>
|
||||
~{estimatedTime} seconds
|
||||
{t('estimatedTime', { seconds: estimatedTime })}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -181,7 +189,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
color: 'gray.600',
|
||||
})}
|
||||
>
|
||||
{Math.round(progress)}% complete
|
||||
{t('percentComplete', { percent: Math.round(progress) })}
|
||||
</span>
|
||||
<span
|
||||
className={css({
|
||||
@@ -190,7 +198,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
color: 'brand.600',
|
||||
})}
|
||||
>
|
||||
Step {currentStep + 1} of {steps.length}
|
||||
{t('stepCount', { current: currentStep + 1, total: steps.length })}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -308,7 +316,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
mb: '2',
|
||||
})}
|
||||
>
|
||||
💡 Did you know?
|
||||
{t('funFactTitle')}
|
||||
</h4>
|
||||
<p
|
||||
className={css({
|
||||
@@ -317,7 +325,7 @@ export function GenerationProgress({ config }: GenerationProgressProps) {
|
||||
lineHeight: 'relaxed',
|
||||
})}
|
||||
>
|
||||
{getFunFact(config)}
|
||||
{funFact}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -355,17 +363,10 @@ function getEstimatedTime(config: FlashcardFormState): number {
|
||||
return Math.round((baseTime + cardTime) * formatMultiplier)
|
||||
}
|
||||
|
||||
function getFunFact(_config: FlashcardFormState): string {
|
||||
const facts = [
|
||||
'The soroban is a Japanese counting tool that dates back over 400 years!',
|
||||
'Master soroban users can calculate faster than electronic calculators.',
|
||||
'Each bead position on a soroban represents a specific numeric value.',
|
||||
'The word "soroban" comes from ancient Chinese "suanpan" (counting board).',
|
||||
'Soroban training improves mathematical intuition and mental calculation speed.',
|
||||
'Modern soroban competitions feature lightning-fast calculations.',
|
||||
'The soroban method strengthens both logical and creative thinking.',
|
||||
'Japanese students often learn soroban alongside traditional mathematics.',
|
||||
]
|
||||
function getFunFact(facts: string[]): string {
|
||||
if (!Array.isArray(facts) || facts.length === 0) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return facts[Math.floor(Math.random() * facts.length)]
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { AbacusReact } from '@soroban/abacus-react'
|
||||
import { Eye } from 'lucide-react'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import type { FlashcardFormState } from '@/app/create/page'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { grid, hstack, stack } from '../../styled-system/patterns'
|
||||
@@ -12,12 +13,14 @@ interface LivePreviewProps {
|
||||
}
|
||||
|
||||
export function LivePreview({ config }: LivePreviewProps) {
|
||||
const t = useTranslations('create.preview')
|
||||
// Generate preview numbers directly from config
|
||||
const previewNumbers = useMemo(() => {
|
||||
return getPreviewNumbers(config.range || '1-10')
|
||||
}, [config.range])
|
||||
|
||||
const previewCount = previewNumbers.length
|
||||
const formatLabel = config.format?.toUpperCase() || t('summary.format.default')
|
||||
|
||||
return (
|
||||
<div className={stack({ gap: '6' })}>
|
||||
@@ -30,7 +33,7 @@ export function LivePreview({ config }: LivePreviewProps) {
|
||||
color: 'gray.900',
|
||||
})}
|
||||
>
|
||||
Live Preview
|
||||
{t('title')}
|
||||
</h3>
|
||||
<p
|
||||
className={css({
|
||||
@@ -38,7 +41,7 @@ export function LivePreview({ config }: LivePreviewProps) {
|
||||
color: 'gray.600',
|
||||
})}
|
||||
>
|
||||
See how your flashcards will look
|
||||
{t('subtitle')}
|
||||
</p>
|
||||
</div>
|
||||
<div className={hstack({ gap: '3', alignItems: 'center' })}>
|
||||
@@ -53,7 +56,7 @@ export function LivePreview({ config }: LivePreviewProps) {
|
||||
rounded: 'full',
|
||||
})}
|
||||
>
|
||||
{previewCount} cards • {config.format?.toUpperCase()}
|
||||
{t('badge', { count: previewCount, format: formatLabel })}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -88,13 +91,25 @@ export function LivePreview({ config }: LivePreviewProps) {
|
||||
mb: '2',
|
||||
})}
|
||||
>
|
||||
Configuration Summary
|
||||
{t('summary.title')}
|
||||
</h4>
|
||||
<div className={grid({ columns: { base: 1, md: 2 }, gap: '3' })}>
|
||||
<ConfigItem label="Range" value={config.range || 'Not set'} />
|
||||
<ConfigItem label="Format" value={config.format?.toUpperCase() || 'PDF'} />
|
||||
<ConfigItem label="Cards per page" value={config.cardsPerPage?.toString() || '6'} />
|
||||
<ConfigItem label="Paper size" value={config.paperSize?.toUpperCase() || 'US-LETTER'} />
|
||||
<ConfigItem
|
||||
label={t('summary.range.label')}
|
||||
value={config.range || t('summary.range.empty')}
|
||||
/>
|
||||
<ConfigItem
|
||||
label={t('summary.format.label')}
|
||||
value={config.format?.toUpperCase() || t('summary.format.default')}
|
||||
/>
|
||||
<ConfigItem
|
||||
label={t('summary.cardsPerPage.label')}
|
||||
value={config.cardsPerPage?.toString() || t('summary.cardsPerPage.default')}
|
||||
/>
|
||||
<ConfigItem
|
||||
label={t('summary.paperSize.label')}
|
||||
value={config.paperSize?.toUpperCase() || t('summary.paperSize.default')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as Switch from '@radix-ui/react-switch'
|
||||
import { useAbacusDisplay } from '@soroban/abacus-react'
|
||||
import type { FormApi } from '@tanstack/react-form'
|
||||
import { useEffect } from 'react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import type { FlashcardFormState } from '@/app/create/page'
|
||||
import { css } from '../../styled-system/css'
|
||||
import { grid, hstack, stack } from '../../styled-system/patterns'
|
||||
@@ -16,6 +17,7 @@ interface StyleControlsProps {
|
||||
|
||||
export function StyleControls({ form }: StyleControlsProps) {
|
||||
const { config, updateConfig } = useAbacusDisplay()
|
||||
const t = useTranslations('create.styleControls')
|
||||
|
||||
// Sync form values with global context
|
||||
useEffect(() => {
|
||||
@@ -26,7 +28,10 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
}, [config, form])
|
||||
return (
|
||||
<div className={stack({ gap: '4' })}>
|
||||
<FormField label="Color Scheme" description="Choose how colors are applied to beads">
|
||||
<FormField
|
||||
label={t('colorScheme.label')}
|
||||
description={t('colorScheme.description')}
|
||||
>
|
||||
<form.Field name="colorScheme">
|
||||
{(field) => (
|
||||
<RadioGroupField
|
||||
@@ -38,23 +43,23 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
options={[
|
||||
{
|
||||
value: 'monochrome',
|
||||
label: 'Monochrome',
|
||||
desc: 'Classic black and white',
|
||||
label: t('colorScheme.options.monochrome.label'),
|
||||
desc: t('colorScheme.options.monochrome.description'),
|
||||
},
|
||||
{
|
||||
value: 'place-value',
|
||||
label: 'Place Value',
|
||||
desc: 'Colors by digit position',
|
||||
label: t('colorScheme.options.place-value.label'),
|
||||
desc: t('colorScheme.options.place-value.description'),
|
||||
},
|
||||
{
|
||||
value: 'heaven-earth',
|
||||
label: 'Heaven-Earth',
|
||||
desc: 'Different colors for 5s and 1s',
|
||||
label: t('colorScheme.options.heaven-earth.label'),
|
||||
desc: t('colorScheme.options.heaven-earth.description'),
|
||||
},
|
||||
{
|
||||
value: 'alternating',
|
||||
label: 'Alternating',
|
||||
desc: 'Alternating column colors',
|
||||
label: t('colorScheme.options.alternating.label'),
|
||||
desc: t('colorScheme.options.alternating.description'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -62,7 +67,7 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
</form.Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Bead Shape" description="Choose the visual style of the beads">
|
||||
<FormField label={t('beadShape.label')} description={t('beadShape.description')}>
|
||||
<form.Field name="beadShape">
|
||||
{(field) => (
|
||||
<RadioGroupField
|
||||
@@ -74,18 +79,18 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
options={[
|
||||
{
|
||||
value: 'diamond',
|
||||
label: '💎 Diamond',
|
||||
desc: 'Realistic 3D appearance',
|
||||
label: t('beadShape.options.diamond.label'),
|
||||
desc: t('beadShape.options.diamond.description'),
|
||||
},
|
||||
{
|
||||
value: 'circle',
|
||||
label: '⭕ Circle',
|
||||
desc: 'Traditional round beads',
|
||||
label: t('beadShape.options.circle.label'),
|
||||
desc: t('beadShape.options.circle.description'),
|
||||
},
|
||||
{
|
||||
value: 'square',
|
||||
label: '⬜ Square',
|
||||
desc: 'Modern geometric style',
|
||||
label: t('beadShape.options.square.label'),
|
||||
desc: t('beadShape.options.square.description'),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
@@ -94,7 +99,10 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
</FormField>
|
||||
|
||||
<div className={grid({ columns: 1, gap: '4' })}>
|
||||
<FormField label="Colored Numerals" description="Match numeral colors to bead colors">
|
||||
<FormField
|
||||
label={t('coloredNumerals.label')}
|
||||
description={t('coloredNumerals.description')}
|
||||
>
|
||||
<form.Field name="coloredNumerals">
|
||||
{(field) => (
|
||||
<SwitchField
|
||||
@@ -108,7 +116,10 @@ export function StyleControls({ form }: StyleControlsProps) {
|
||||
</form.Field>
|
||||
</FormField>
|
||||
|
||||
<FormField label="Hide Inactive Beads" description="Show only active beads for clarity">
|
||||
<FormField
|
||||
label={t('hideInactiveBeads.label')}
|
||||
description={t('hideInactiveBeads.description')}
|
||||
>
|
||||
<form.Field name="hideInactiveBeads">
|
||||
{(field) => (
|
||||
<SwitchField
|
||||
|
||||
229
apps/web/src/i18n/locales/create/de.json
Normal file
229
apps/web/src/i18n/locales/create/de.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "Karteikarten erstellen",
|
||||
"hero": {
|
||||
"title": "Erstelle deine Karteikarten",
|
||||
"subtitle": "Konfiguriere Inhalt und Stil, sieh dir sofort eine Vorschau an und generiere dann deine Karteikarten"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 Visueller Stil",
|
||||
"subtitle": "Sieh Änderungen sofort in der Vorschau"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "Karteikarten werden erstellt...",
|
||||
"cta": "Karteikarten generieren"
|
||||
},
|
||||
"error": {
|
||||
"title": "Erstellung fehlgeschlagen",
|
||||
"retry": "Erneut versuchen"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "Konfiguration",
|
||||
"subtitle": "Einstellungen für Inhalt, Layout und Ausgabe",
|
||||
"tabs": {
|
||||
"content": "📝 Inhalt",
|
||||
"output": "💾 Ausgabe"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "Zahlenbereich",
|
||||
"description": "Lege fest, welche Zahlen enthalten sind (z. B. '0-99' oder '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "Schrittweite",
|
||||
"description": "Bei Bereichen wird um diesen Wert erhöht"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "Karten mischen",
|
||||
"description": "Reihenfolge zufällig anordnen"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "Ausgabeformat",
|
||||
"description": "Wähle dein bevorzugtes Dateiformat"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 PDF-Layout-Optionen",
|
||||
"sectionDescription": "Konfiguriere Seitenlayout und Druckoptionen für dein PDF",
|
||||
"cardsPerPage": {
|
||||
"label": "Karten pro Seite",
|
||||
"description": "Anzahl der Karteikarten je Seite",
|
||||
"value": "{count, plural, one {{count} Karte} other {{count} Karten}}"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Papierformat",
|
||||
"description": "Ausgabemaße des Papiers",
|
||||
"options": {
|
||||
"us-letter": "US Letter (8.5×11\")",
|
||||
"a4": "A4 (210×297 mm)",
|
||||
"a3": "A3 (297×420 mm)",
|
||||
"a5": "A5 (148×210 mm)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "Ausrichtung",
|
||||
"description": "Ausrichtung des Seitenlayouts",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 Hochformat",
|
||||
"description": "Höher als breit"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 Querformat",
|
||||
"description": "Breiter als hoch"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "Schnittmarken anzeigen",
|
||||
"description": "Hilfslinien zum Zuschneiden hinzufügen"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "Passkreuze",
|
||||
"description": "Ausrichtungshilfen für Duplexdruck"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "Skalierungsfaktor",
|
||||
"description": "Gesamtgröße der Karteikarten anpassen",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "Auswählen..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "Druckfertiges Vektordokument mit Layoutoptionen"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "Interaktive Web-Karteikarten"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "Skalierbare Vektorgrafiken"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "Hochauflösende Bilder"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "Farbschema",
|
||||
"description": "Lege fest, wie Farben auf die Perlen angewendet werden",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "Monochrom",
|
||||
"description": "Klassisches Schwarz-Weiß"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "Stellenwert",
|
||||
"description": "Farben nach Stellenwert"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "Himmel-Erde",
|
||||
"description": "Unterschiedliche Farben für Fünfer und Einer"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "Alternierend",
|
||||
"description": "Abwechselnde Spaltenfarben"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "Perlenform",
|
||||
"description": "Bestimme den visuellen Stil der Perlen",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 Diamant",
|
||||
"description": "Realistische 3D-Anmutung"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ Kreis",
|
||||
"description": "Traditionelle runde Perlen"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ Quadrat",
|
||||
"description": "Moderner geometrischer Stil"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "Farbige Ziffern",
|
||||
"description": "Ziffernfarben an die Perlenfarben anpassen"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "Inaktive Perlen ausblenden",
|
||||
"description": "Nur aktive Perlen für bessere Übersicht zeigen"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "Karteikarten werden erstellt",
|
||||
"estimatedTime": "~{seconds} Sekunden",
|
||||
"percentComplete": "{percent}% abgeschlossen",
|
||||
"stepCount": "Schritt {current} von {total}",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "Konfiguration wird geprüft",
|
||||
"description": "Parameter und Abhängigkeiten werden kontrolliert"
|
||||
},
|
||||
"generate": {
|
||||
"label": "Soroban-Muster werden erzeugt",
|
||||
"description": "{count} Kartenmuster werden erstellt"
|
||||
},
|
||||
"render": {
|
||||
"label": "{format} wird gerendert",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "Umwandlung in dein ausgewähltes Format"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "Download wird vorbereitet",
|
||||
"description": "Karteikarten für den Download bereitstellen"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 Schon gewusst?",
|
||||
"funFacts": [
|
||||
"Das Soroban ist ein japanisches Rechenbrett, das über 400 Jahre alt ist!",
|
||||
"Geübte Soroban-Anwender rechnen schneller als elektronische Taschenrechner.",
|
||||
"Jede Perlenposition auf dem Soroban steht für einen bestimmten Zahlenwert.",
|
||||
"Das Wort \"Soroban\" stammt vom chinesischen \"Suanpan\" (Rechenbrett).",
|
||||
"Soroban-Training verbessert Intuition und Geschwindigkeit beim Kopfrechnen.",
|
||||
"Moderne Soroban-Wettbewerbe zeigen blitzschnelle Rechenkünstler.",
|
||||
"Die Soroban-Methode stärkt logisches und kreatives Denken gleichermaßen.",
|
||||
"In Japan lernen Kinder Soroban oft parallel zum Mathematikunterricht."
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "Live-Vorschau",
|
||||
"subtitle": "So werden deine Karteikarten aussehen",
|
||||
"badge": "{count} {count, plural, one {Karte} other {Karten}} • {format}",
|
||||
"summary": {
|
||||
"title": "Konfigurationsübersicht",
|
||||
"range": {
|
||||
"label": "Bereich",
|
||||
"empty": "Nicht festgelegt"
|
||||
},
|
||||
"format": {
|
||||
"label": "Format",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "Karten pro Seite",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Papierformat",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
apps/web/src/i18n/locales/create/en.json
Normal file
229
apps/web/src/i18n/locales/create/en.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "Create Flashcards",
|
||||
"hero": {
|
||||
"title": "Create Your Flashcards",
|
||||
"subtitle": "Configure content and style, preview instantly, then generate your flashcards"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 Visual Style",
|
||||
"subtitle": "See changes instantly in the preview"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "Generating Your Flashcards...",
|
||||
"cta": "Generate Flashcards"
|
||||
},
|
||||
"error": {
|
||||
"title": "Generation Failed",
|
||||
"retry": "Try Again"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "Configuration",
|
||||
"subtitle": "Content, layout, and output settings",
|
||||
"tabs": {
|
||||
"content": "📝 Content",
|
||||
"output": "💾 Output"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "Number Range",
|
||||
"description": "Define which numbers to include (e.g., '0-99' or '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "Step Size",
|
||||
"description": "For ranges, increment by this amount"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "Shuffle Cards",
|
||||
"description": "Randomize the order"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "Output Format",
|
||||
"description": "Choose your preferred file format"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 PDF Layout Options",
|
||||
"sectionDescription": "Configure page layout and printing options for your PDF",
|
||||
"cardsPerPage": {
|
||||
"label": "Cards Per Page",
|
||||
"description": "Number of flashcards on each page",
|
||||
"value": "{count, plural, one {{count} card} other {{count} cards}}"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Paper Size",
|
||||
"description": "Output paper dimensions",
|
||||
"options": {
|
||||
"us-letter": "US Letter (8.5×11\")",
|
||||
"a4": "A4 (210×297mm)",
|
||||
"a3": "A3 (297×420mm)",
|
||||
"a5": "A5 (148×210mm)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "Orientation",
|
||||
"description": "Page layout direction",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 Portrait",
|
||||
"description": "Taller than wide"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 Landscape",
|
||||
"description": "Wider than tall"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "Show Cut Marks",
|
||||
"description": "Add guides for cutting cards"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "Registration Marks",
|
||||
"description": "Alignment guides for duplex printing"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "Scale Factor",
|
||||
"description": "Adjust the overall size of flashcards",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "Select..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "Print-ready vector document with layout options"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "Interactive web flashcards"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "Scalable vector images"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "High-resolution images"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "Color Scheme",
|
||||
"description": "Choose how colors are applied to beads",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "Monochrome",
|
||||
"description": "Classic black and white"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "Place Value",
|
||||
"description": "Colors by digit position"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "Heaven-Earth",
|
||||
"description": "Different colors for 5s and 1s"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "Alternating",
|
||||
"description": "Alternating column colors"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "Bead Shape",
|
||||
"description": "Choose the visual style of the beads",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 Diamond",
|
||||
"description": "Realistic 3D appearance"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ Circle",
|
||||
"description": "Traditional round beads"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ Square",
|
||||
"description": "Modern geometric style"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "Colored Numerals",
|
||||
"description": "Match numeral colors to bead colors"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "Hide Inactive Beads",
|
||||
"description": "Show only active beads for clarity"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "Generating Your Flashcards",
|
||||
"estimatedTime": "~{seconds} seconds",
|
||||
"percentComplete": "{percent}% complete",
|
||||
"stepCount": "Step {current} of {total}",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "Validating Configuration",
|
||||
"description": "Checking parameters and dependencies"
|
||||
},
|
||||
"generate": {
|
||||
"label": "Generating Soroban Patterns",
|
||||
"description": "Creating {count} flashcard patterns"
|
||||
},
|
||||
"render": {
|
||||
"label": "Rendering {format}",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "Converting to your chosen format"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "Finalizing Download",
|
||||
"description": "Preparing your flashcards for download"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 Did you know?",
|
||||
"funFacts": [
|
||||
"The soroban is a Japanese counting tool that dates back over 400 years!",
|
||||
"Master soroban users can calculate faster than electronic calculators.",
|
||||
"Each bead position on a soroban represents a specific numeric value.",
|
||||
"The word \"soroban\" comes from ancient Chinese \"suanpan\" (counting board).",
|
||||
"Soroban training improves mathematical intuition and mental calculation speed.",
|
||||
"Modern soroban competitions feature lightning-fast calculations.",
|
||||
"The soroban method strengthens both logical and creative thinking.",
|
||||
"Japanese students often learn soroban alongside traditional mathematics."
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "Live Preview",
|
||||
"subtitle": "See how your flashcards will look",
|
||||
"badge": "{count} {count, plural, one {card} other {cards}} • {format}",
|
||||
"summary": {
|
||||
"title": "Configuration Summary",
|
||||
"range": {
|
||||
"label": "Range",
|
||||
"empty": "Not set"
|
||||
},
|
||||
"format": {
|
||||
"label": "Format",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "Cards per page",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Paper size",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
apps/web/src/i18n/locales/create/es.json
Normal file
229
apps/web/src/i18n/locales/create/es.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "Crear tarjetas didácticas",
|
||||
"hero": {
|
||||
"title": "Crea tus tarjetas didácticas",
|
||||
"subtitle": "Configura el contenido y el estilo, observa la vista previa al instante y luego genera tus tarjetas"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 Estilo visual",
|
||||
"subtitle": "Observa los cambios al instante en la vista previa"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "Generando tus tarjetas...",
|
||||
"cta": "Generar tarjetas"
|
||||
},
|
||||
"error": {
|
||||
"title": "La generación falló",
|
||||
"retry": "Intentar de nuevo"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "Configuración",
|
||||
"subtitle": "Ajustes de contenido, diseño y salida",
|
||||
"tabs": {
|
||||
"content": "📝 Contenido",
|
||||
"output": "💾 Salida"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "Rango numérico",
|
||||
"description": "Define qué números se incluirán (p. ej., '0-99' o '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "Tamaño de paso",
|
||||
"description": "Para rangos, aumenta en esta cantidad"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "Mezclar tarjetas",
|
||||
"description": "Aleatorizar el orden"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "Formato de salida",
|
||||
"description": "Elige tu formato de archivo preferido"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 Opciones de diseño PDF",
|
||||
"sectionDescription": "Configura el diseño de página y las opciones de impresión para tu PDF",
|
||||
"cardsPerPage": {
|
||||
"label": "Tarjetas por página",
|
||||
"description": "Cantidad de tarjetas en cada página",
|
||||
"value": "{count, plural, one {{count} tarjeta} other {{count} tarjetas}}"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Tamaño de papel",
|
||||
"description": "Dimensiones del papel de salida",
|
||||
"options": {
|
||||
"us-letter": "US Letter (8.5×11\")",
|
||||
"a4": "A4 (210×297 mm)",
|
||||
"a3": "A3 (297×420 mm)",
|
||||
"a5": "A5 (148×210 mm)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "Orientación",
|
||||
"description": "Dirección del diseño de página",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 Vertical",
|
||||
"description": "Más alto que ancho"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 Horizontal",
|
||||
"description": "Más ancho que alto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "Mostrar guías de corte",
|
||||
"description": "Agregar guías para recortar las tarjetas"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "Marcas de registro",
|
||||
"description": "Guías de alineación para impresión a doble cara"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "Factor de escala",
|
||||
"description": "Ajusta el tamaño general de las tarjetas",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "Seleccionar..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "Documento vectorial listo para imprimir con opciones de diseño"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "Tarjetas web interactivas"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "Imágenes vectoriales escalables"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "Imágenes de alta resolución"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "Esquema de color",
|
||||
"description": "Elige cómo se aplican los colores a las cuentas",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "Monocromático",
|
||||
"description": "Clásico blanco y negro"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "Valor posicional",
|
||||
"description": "Colores según la posición de cada dígito"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "Cielo-Tierra",
|
||||
"description": "Colores distintos para cuentas de cinco y de uno"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "Alternado",
|
||||
"description": "Colores alternados por columna"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "Forma de las cuentas",
|
||||
"description": "Elige el estilo visual de las cuentas",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 Diamante",
|
||||
"description": "Apariencia realista en 3D"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ Círculo",
|
||||
"description": "Cuentas redondas tradicionales"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ Cuadrado",
|
||||
"description": "Estilo geométrico moderno"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "Números coloreados",
|
||||
"description": "Igualar el color de los números con el de las cuentas"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "Ocultar cuentas inactivas",
|
||||
"description": "Mostrar solo las cuentas activas para mayor claridad"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "Generando tus tarjetas",
|
||||
"estimatedTime": "~{seconds} segundos",
|
||||
"percentComplete": "{percent}% completado",
|
||||
"stepCount": "Paso {current} de {total}",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "Validando la configuración",
|
||||
"description": "Comprobando parámetros y dependencias"
|
||||
},
|
||||
"generate": {
|
||||
"label": "Generando patrones de soroban",
|
||||
"description": "Creando {count} patrones de tarjetas"
|
||||
},
|
||||
"render": {
|
||||
"label": "Renderizando {format}",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "Convirtiendo al formato elegido"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "Finalizando la descarga",
|
||||
"description": "Preparando tus tarjetas para descargar"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 ¿Sabías que...?",
|
||||
"funFacts": [
|
||||
"El soroban es una herramienta japonesa de cálculo con más de 400 años de historia.",
|
||||
"Los expertos del soroban pueden calcular más rápido que las calculadoras electrónicas.",
|
||||
"Cada posición de cuenta en el soroban representa un valor numérico específico.",
|
||||
"La palabra \"soroban\" proviene del antiguo chino \"suanpan\" (tablero de conteo).",
|
||||
"El entrenamiento con soroban mejora la intuición matemática y la velocidad mental.",
|
||||
"Las competencias modernas de soroban muestran cálculos a gran velocidad.",
|
||||
"El método soroban fortalece tanto el pensamiento lógico como el creativo.",
|
||||
"En Japón, el soroban suele aprenderse junto con las matemáticas tradicionales."
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "Vista previa en vivo",
|
||||
"subtitle": "Observa cómo se verán tus tarjetas",
|
||||
"badge": "{count} {count, plural, one {tarjeta} other {tarjetas}} • {format}",
|
||||
"summary": {
|
||||
"title": "Resumen de configuración",
|
||||
"range": {
|
||||
"label": "Rango",
|
||||
"empty": "Sin definir"
|
||||
},
|
||||
"format": {
|
||||
"label": "Formato",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "Tarjetas por página",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Tamaño de papel",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
apps/web/src/i18n/locales/create/hi.json
Normal file
229
apps/web/src/i18n/locales/create/hi.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "फ्लैश कार्ड बनाएं",
|
||||
"hero": {
|
||||
"title": "अपने फ्लैश कार्ड बनाएं",
|
||||
"subtitle": "सामग्री और शैली कॉन्फ़िगर करें, तुरंत पूर्वावलोकन देखें और फिर अपने फ्लैश कार्ड जनरेट करें"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 दृश्य शैली",
|
||||
"subtitle": "पूर्वावलोकन में बदलाव तुरंत देखें"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "आपके फ्लैश कार्ड बन रहे हैं...",
|
||||
"cta": "फ्लैश कार्ड जनरेट करें"
|
||||
},
|
||||
"error": {
|
||||
"title": "जनरेशन विफल",
|
||||
"retry": "फिर से प्रयास करें"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "कॉन्फ़िगरेशन",
|
||||
"subtitle": "सामग्री, लेआउट और आउटपुट सेटिंग्स",
|
||||
"tabs": {
|
||||
"content": "📝 सामग्री",
|
||||
"output": "💾 आउटपुट"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "संख्या सीमा",
|
||||
"description": "किन संख्याओं को शामिल करना है (जैसे '0-99' या '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "चरण अंतर",
|
||||
"description": "सीमा के लिए, इतनी मात्रा से वृद्धि करें"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "कार्ड मिलाएँ",
|
||||
"description": "क्रम को यादृच्छिक करें"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "आउटपुट फ़ॉर्मेट",
|
||||
"description": "अपना पसंदीदा फ़ाइल फ़ॉर्मेट चुनें"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 PDF लेआउट विकल्प",
|
||||
"sectionDescription": "अपने PDF के लिए पेज लेआउट और प्रिंट विकल्प कॉन्फ़िगर करें",
|
||||
"cardsPerPage": {
|
||||
"label": "प्रति पृष्ठ कार्ड",
|
||||
"description": "प्रत्येक पृष्ठ पर फ्लैश कार्ड की संख्या",
|
||||
"value": "{count, plural, one {{count} कार्ड} other {{count} कार्ड}}"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "पेपर आकार",
|
||||
"description": "आउटपुट पेपर के आयाम",
|
||||
"options": {
|
||||
"us-letter": "US Letter (8.5×11\")",
|
||||
"a4": "A4 (210×297 मिमी)",
|
||||
"a3": "A3 (297×420 मिमी)",
|
||||
"a5": "A5 (148×210 मिमी)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "अभिविन्यास",
|
||||
"description": "पेज लेआउट की दिशा",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 लंबवत",
|
||||
"description": "ऊँचाई चौड़ाई से अधिक"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 क्षैतिज",
|
||||
"description": "चौड़ाई ऊँचाई से अधिक"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "कट मार्क दिखाएँ",
|
||||
"description": "कार्ड काटने के लिए गाइड जोड़ें"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "रजिस्ट्रेशन मार्क",
|
||||
"description": "दोनों तरफ प्रिंट के लिए संरेखण गाइड"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "स्केल फ़ैक्टर",
|
||||
"description": "फ्लैश कार्ड का समग्र आकार समायोजित करें",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "चुनें..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "लेआउट विकल्पों वाला प्रिंट-रेडी वेक्टर दस्तावेज़"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "इंटरैक्टिव वेब फ्लैश कार्ड"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "स्केलेबल वेक्टर चित्र"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "उच्च-रिज़ॉल्यूशन छवियाँ"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "रंग योजना",
|
||||
"description": "निर्धारित करें कि रंग मोतियों पर कैसे लागू हों",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "मोनोक्रोम",
|
||||
"description": "क्लासिक काला और सफेद"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "स्थान-मूल्य",
|
||||
"description": "प्रत्येक अंक की स्थिति के अनुसार रंग"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "स्वर्ग-पृथ्वी",
|
||||
"description": "पाँच और एक की मोतियों के लिए अलग रंग"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "बारी-बारी",
|
||||
"description": "हर स्तंभ में वैकल्पिक रंग"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "मोती का आकार",
|
||||
"description": "मोती की दृश्य शैली चुनें",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 हीरा",
|
||||
"description": "यथार्थवादी 3D रूप"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ वृत्त",
|
||||
"description": "पारंपरिक गोल मोती"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ वर्ग",
|
||||
"description": "आधुनिक ज्यामितीय शैली"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "रंगीन अंक",
|
||||
"description": "अंकों के रंग को मोतियों से मिलाएँ"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "निष्क्रिय मोती छिपाएँ",
|
||||
"description": "स्पष्टता के लिए केवल सक्रिय मोती दिखाएँ"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "आपके फ्लैश कार्ड तैयार हो रहे हैं",
|
||||
"estimatedTime": "~{seconds} सेकंड",
|
||||
"percentComplete": "{percent}% पूर्ण",
|
||||
"stepCount": "चरण {current} में से {total}",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "कॉन्फ़िगरेशन जाँची जा रही है",
|
||||
"description": "पैरामीटर और निर्भरताएँ सत्यापित की जा रही हैं"
|
||||
},
|
||||
"generate": {
|
||||
"label": "सोरबन पैटर्न बनाए जा रहे हैं",
|
||||
"description": "{count} कार्ड पैटर्न तैयार किए जा रहे हैं"
|
||||
},
|
||||
"render": {
|
||||
"label": "{format} रेंडर हो रहा है",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "आपके चुने हुए फ़ॉर्मेट में परिवर्तित किया जा रहा है"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "डाउनलोड अंतिम रूप में",
|
||||
"description": "डाउनलोड के लिए आपके कार्ड तैयार किए जा रहे हैं"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 क्या आप जानते हैं?",
|
||||
"funFacts": [
|
||||
"सोरबन एक जापानी गणना उपकरण है जो 400 से अधिक वर्ष पुराना है।",
|
||||
"महारथी सोरबन उपयोगकर्ता इलेक्ट्रॉनिक कैलकुलेटर से भी तेज़ गणना कर सकते हैं।",
|
||||
"सोरबन पर प्रत्येक मोती की स्थिति एक विशिष्ट संख्यात्मक मान दर्शाती है।",
|
||||
"\"सोरबन\" शब्द प्राचीन चीनी \"सुआनपान\" (गणना पट्ट) से आया है।",
|
||||
"सोरबन का अभ्यास गणितीय अंतर्ज्ञान और मानसिक गति को बढ़ाता है।",
|
||||
"आधुनिक सोरबन प्रतियोगिताओं में बिजली की गति से गणना होती है।",
|
||||
"सोरबन विधि तार्किक और रचनात्मक दोनों सोच को मजबूत करती है।",
|
||||
"जापान में छात्र अक्सर पारंपरिक गणित के साथ-साथ सोरबन भी सीखते हैं।"
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "लाइव पूर्वावलोकन",
|
||||
"subtitle": "देखें कि आपके फ्लैश कार्ड कैसे दिखेंगे",
|
||||
"badge": "{count} {count, plural, one {कार्ड} other {कार्ड}} • {format}",
|
||||
"summary": {
|
||||
"title": "कॉन्फ़िगरेशन सारांश",
|
||||
"range": {
|
||||
"label": "सीमा",
|
||||
"empty": "निर्धारित नहीं"
|
||||
},
|
||||
"format": {
|
||||
"label": "फ़ॉर्मेट",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "प्रति पृष्ठ कार्ड",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "पेपर आकार",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
apps/web/src/i18n/locales/create/ja.json
Normal file
229
apps/web/src/i18n/locales/create/ja.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "フラッシュカードを作成",
|
||||
"hero": {
|
||||
"title": "フラッシュカードを作りましょう",
|
||||
"subtitle": "内容とスタイルを設定し、すぐにプレビューしてからフラッシュカードを生成しましょう"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 ビジュアルスタイル",
|
||||
"subtitle": "プレビューで変更をすぐに確認"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "フラッシュカードを生成しています...",
|
||||
"cta": "フラッシュカードを生成"
|
||||
},
|
||||
"error": {
|
||||
"title": "生成に失敗しました",
|
||||
"retry": "もう一度試す"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "設定",
|
||||
"subtitle": "コンテンツ・レイアウト・出力の設定",
|
||||
"tabs": {
|
||||
"content": "📝 コンテンツ",
|
||||
"output": "💾 出力"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "数値範囲",
|
||||
"description": "含める数字を指定します(例:'0-99' や '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "刻み幅",
|
||||
"description": "範囲の場合はこの値ずつ増加します"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "カードをシャッフル",
|
||||
"description": "順番をランダムに並べ替えます"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "出力形式",
|
||||
"description": "お好みのファイル形式を選択"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 PDF レイアウトオプション",
|
||||
"sectionDescription": "PDF のページレイアウトと印刷オプションを設定します",
|
||||
"cardsPerPage": {
|
||||
"label": "1ページあたりのカード数",
|
||||
"description": "各ページに配置するカードの数",
|
||||
"value": "{count}枚のカード"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "用紙サイズ",
|
||||
"description": "出力する用紙の寸法",
|
||||
"options": {
|
||||
"us-letter": "USレター (8.5×11\")",
|
||||
"a4": "A4 (210×297mm)",
|
||||
"a3": "A3 (297×420mm)",
|
||||
"a5": "A5 (148×210mm)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "向き",
|
||||
"description": "ページレイアウトの向き",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 縦向き",
|
||||
"description": "高さが幅より長い"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 横向き",
|
||||
"description": "幅が高さより長い"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "カットマークを表示",
|
||||
"description": "カードを切り分けるためのガイドを追加"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "見当合わせのマーク",
|
||||
"description": "両面印刷のための位置合わせガイド"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "縮尺",
|
||||
"description": "カード全体のサイズを調整",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "選択..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "レイアウト設定が可能な印刷向けベクター文書"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "インタラクティブなウェブカード"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "スケーラブルなベクター画像"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "高解像度の画像"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "配色",
|
||||
"description": "ビーズに色を適用する方法を選択",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "モノクロ",
|
||||
"description": "クラシックな白黒"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "位取り",
|
||||
"description": "桁の位置ごとに色分け"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "天地",
|
||||
"description": "五玉と一玉で別の色"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "交互",
|
||||
"description": "列ごとに交互の色"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "ビーズの形",
|
||||
"description": "ビーズの見た目のスタイルを選択",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 ダイヤ",
|
||||
"description": "リアルな立体感"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ 円",
|
||||
"description": "伝統的な丸いビーズ"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ 四角",
|
||||
"description": "モダンな幾何学スタイル"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "数字の色を付ける",
|
||||
"description": "数字の色をビーズの色に合わせる"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "非アクティブなビーズを隠す",
|
||||
"description": "見やすさのためにアクティブなビーズのみ表示"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "フラッシュカードを生成しています",
|
||||
"estimatedTime": "~{seconds}秒",
|
||||
"percentComplete": "{percent}% 完了",
|
||||
"stepCount": "全{total}ステップ中 {current} ステップ目",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "設定を検証しています",
|
||||
"description": "パラメーターと依存関係を確認中"
|
||||
},
|
||||
"generate": {
|
||||
"label": "そろばんパターンを生成",
|
||||
"description": "{count} 件のカードパターンを作成中"
|
||||
},
|
||||
"render": {
|
||||
"label": "{format} をレンダリング",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "選択した形式に変換中"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "ダウンロードを最終準備",
|
||||
"description": "カードをダウンロード用に整えています"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 豆知識",
|
||||
"funFacts": [
|
||||
"そろばんは400年以上の歴史を持つ日本の計算器です。",
|
||||
"熟練したそろばん使いは電子計算機よりも速く計算できます。",
|
||||
"そろばんの各ビーズの位置は特定の数値を表します。",
|
||||
"「そろばん」という言葉は中国古来の「算盤」から来ています。",
|
||||
"そろばんの練習は数学的直感と暗算の速度を高めます。",
|
||||
"現代のそろばん大会では驚異的な速さの計算が披露されます。",
|
||||
"そろばんの学習は論理的思考と創造的思考の両方を鍛えます。",
|
||||
"日本では学校で数学と一緒にそろばんを学ぶことがよくあります。"
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "ライブプレビュー",
|
||||
"subtitle": "フラッシュカードの仕上がりを確認しましょう",
|
||||
"badge": "{count}枚のカード • {format}",
|
||||
"summary": {
|
||||
"title": "設定のサマリー",
|
||||
"range": {
|
||||
"label": "範囲",
|
||||
"empty": "未設定"
|
||||
},
|
||||
"format": {
|
||||
"label": "形式",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "1ページあたりのカード数",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "用紙サイズ",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
229
apps/web/src/i18n/locales/create/la.json
Normal file
229
apps/web/src/i18n/locales/create/la.json
Normal file
@@ -0,0 +1,229 @@
|
||||
{
|
||||
"create": {
|
||||
"page": {
|
||||
"navTitle": "Chartas memoriales crea",
|
||||
"hero": {
|
||||
"title": "Chartas tuas memoriales para",
|
||||
"subtitle": "Argumentum et formam dispone, statim praenotam aspice, deinde chartas tuas gignito"
|
||||
},
|
||||
"style": {
|
||||
"title": "🎨 Stilus visualis",
|
||||
"subtitle": "Mutationes in praenota continuo vide"
|
||||
},
|
||||
"generateButton": {
|
||||
"loading": "Chartae tuae gignuntur...",
|
||||
"cta": "Chartas gignere"
|
||||
},
|
||||
"error": {
|
||||
"title": "Generatio defecit",
|
||||
"retry": "Iterum conare"
|
||||
}
|
||||
},
|
||||
"form": {
|
||||
"title": "Configuratio",
|
||||
"subtitle": "Praecepta de argumento, dispositione et exitu",
|
||||
"tabs": {
|
||||
"content": "📝 Argumentum",
|
||||
"output": "💾 Exitus"
|
||||
},
|
||||
"content": {
|
||||
"range": {
|
||||
"label": "Intervallum numerorum",
|
||||
"description": "Quos numeros includas determina (exempli gratia '0-99' vel '1,2,5,10')",
|
||||
"placeholder": "0-99"
|
||||
},
|
||||
"step": {
|
||||
"label": "Incrementum gradus",
|
||||
"description": "In intervallis hoc quantulum augetur"
|
||||
},
|
||||
"shuffle": {
|
||||
"label": "Chartas misce",
|
||||
"description": "Ordinem fortuito verte"
|
||||
}
|
||||
},
|
||||
"output": {
|
||||
"format": {
|
||||
"label": "Forma exitus",
|
||||
"description": "Formam tabellae quam mavis selige"
|
||||
},
|
||||
"pdf": {
|
||||
"sectionTitle": "📄 Optiones dispositionis PDF",
|
||||
"sectionDescription": "Paginam et optiones imprimendi pro PDF dispone",
|
||||
"cardsPerPage": {
|
||||
"label": "Chartae per paginam",
|
||||
"description": "Quot chartae singulae paginae habeant",
|
||||
"value": "{count, plural, one {{count} charta} other {{count} chartae}}"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Magnitudo chartae",
|
||||
"description": "Dimensiones chartae exitus",
|
||||
"options": {
|
||||
"us-letter": "US Letter (8.5×11\")",
|
||||
"a4": "A4 (210×297 mm)",
|
||||
"a3": "A3 (297×420 mm)",
|
||||
"a5": "A5 (148×210 mm)"
|
||||
}
|
||||
},
|
||||
"orientation": {
|
||||
"label": "Directio",
|
||||
"description": "Quo modo pagina vertatur",
|
||||
"options": {
|
||||
"portrait": {
|
||||
"label": "📄 Verticalis",
|
||||
"description": "Altior quam lata"
|
||||
},
|
||||
"landscape": {
|
||||
"label": "📃 Horizontali",
|
||||
"description": "Latior quam alta"
|
||||
}
|
||||
}
|
||||
},
|
||||
"showCutMarks": {
|
||||
"label": "Signa sectionis ostende",
|
||||
"description": "Lineas ad chartas secandas adde"
|
||||
},
|
||||
"showRegistration": {
|
||||
"label": "Signa directionis",
|
||||
"description": "Ducet alignmenti pro impressione duplici"
|
||||
}
|
||||
},
|
||||
"scaleFactor": {
|
||||
"label": "Factor scalae",
|
||||
"description": "Magnitudinem totam chartarum tempera",
|
||||
"value": "{percent}%"
|
||||
}
|
||||
},
|
||||
"shared": {
|
||||
"selectPlaceholder": "Elige..."
|
||||
},
|
||||
"formatOptions": {
|
||||
"pdf": {
|
||||
"label": "PDF",
|
||||
"description": "Documentum vectorium ad imprimendum paratum cum optionibus dispositionis"
|
||||
},
|
||||
"html": {
|
||||
"label": "HTML",
|
||||
"description": "Chartae interretiales interactivae"
|
||||
},
|
||||
"svg": {
|
||||
"label": "SVG",
|
||||
"description": "Imagines vectoriae scalabiles"
|
||||
},
|
||||
"png": {
|
||||
"label": "PNG",
|
||||
"description": "Imagines altae resolutionis"
|
||||
}
|
||||
}
|
||||
},
|
||||
"styleControls": {
|
||||
"colorScheme": {
|
||||
"label": "Schema colorum",
|
||||
"description": "Defini quomodo colores in globulos applicantur",
|
||||
"options": {
|
||||
"monochrome": {
|
||||
"label": "Monochromum",
|
||||
"description": "Classica nigro-alba"
|
||||
},
|
||||
"place-value": {
|
||||
"label": "Valor locorum",
|
||||
"description": "Colores secundum ordinem digitorum"
|
||||
},
|
||||
"heaven-earth": {
|
||||
"label": "Caelum-Terra",
|
||||
"description": "Diversi colores globulis quinariis et singularibus"
|
||||
},
|
||||
"alternating": {
|
||||
"label": "Alternans",
|
||||
"description": "Columnae alterno colore"
|
||||
}
|
||||
}
|
||||
},
|
||||
"beadShape": {
|
||||
"label": "Figura globulorum",
|
||||
"description": "Genus aspectus globulorum elige",
|
||||
"options": {
|
||||
"diamond": {
|
||||
"label": "💎 Adamans",
|
||||
"description": "Species quasi tridimensionalis"
|
||||
},
|
||||
"circle": {
|
||||
"label": "⭕ Orbis",
|
||||
"description": "Globuli rotundi traditi"
|
||||
},
|
||||
"square": {
|
||||
"label": "⬜ Quadratum",
|
||||
"description": "Moderna forma geometrica"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coloredNumerals": {
|
||||
"label": "Numeri colorati",
|
||||
"description": "Numerorum colores cum globulis compone"
|
||||
},
|
||||
"hideInactiveBeads": {
|
||||
"label": "Globulos inactivos occulta",
|
||||
"description": "Ut clarius sit, tantum globulos activos ostende"
|
||||
}
|
||||
},
|
||||
"progress": {
|
||||
"title": "Chartae tuae generantur",
|
||||
"estimatedTime": "~{seconds} secundae",
|
||||
"percentComplete": "{percent}% perfectum",
|
||||
"stepCount": "Gradus {current} ex {total}",
|
||||
"steps": {
|
||||
"validate": {
|
||||
"label": "Configuratio probatur",
|
||||
"description": "Parametri et dependentiae recognoscuntur"
|
||||
},
|
||||
"generate": {
|
||||
"label": "Forma soroban paratur",
|
||||
"description": "{count} schemata chartarum creantur"
|
||||
},
|
||||
"render": {
|
||||
"label": "{format} redditur",
|
||||
"defaultFormat": "PDF",
|
||||
"description": "In formam electam convertitur"
|
||||
},
|
||||
"finalize": {
|
||||
"label": "Download paratur",
|
||||
"description": "Chartae ad detrahendum componuntur"
|
||||
}
|
||||
},
|
||||
"funFactTitle": "💡 Scisne?",
|
||||
"funFacts": [
|
||||
"Soroban instrumentum Iaponicum numerandi est quod plus quam annos quadringentos superest.",
|
||||
"Periti soroban citius quam calculatrices electronicae rationes conficiunt.",
|
||||
"Quisque locus globuli in soroban certum numerum repraesentat.",
|
||||
"Vocabulum \"soroban\" ex antiquo Sinico \"suanpan\" (tabula computatoria) derivatur.",
|
||||
"Exercitatio soroban sensum mathematicum et velocitatem mentis auget.",
|
||||
"Certamina soroban hodierna fulmineas computationes ostendunt.",
|
||||
"Methodus soroban tam cogitationem logicam quam creatricem firmat.",
|
||||
"Pueri Iaponici saepe soroban una cum mathematica tradita discunt."
|
||||
]
|
||||
},
|
||||
"preview": {
|
||||
"title": "Praenotio viva",
|
||||
"subtitle": "Vide quomodo chartae tuae apparebunt",
|
||||
"badge": "{count} {count, plural, one {charta} other {chartae}} • {format}",
|
||||
"summary": {
|
||||
"title": "Summarium configurationis",
|
||||
"range": {
|
||||
"label": "Intervallum",
|
||||
"empty": "Nondum definitum"
|
||||
},
|
||||
"format": {
|
||||
"label": "Forma",
|
||||
"default": "PDF"
|
||||
},
|
||||
"cardsPerPage": {
|
||||
"label": "Chartae per paginam",
|
||||
"default": "6"
|
||||
},
|
||||
"paperSize": {
|
||||
"label": "Magnitudo chartae",
|
||||
"default": "US-LETTER"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
apps/web/src/i18n/locales/create/messages.ts
Normal file
15
apps/web/src/i18n/locales/create/messages.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import de from './de.json'
|
||||
import en from './en.json'
|
||||
import es from './es.json'
|
||||
import hi from './hi.json'
|
||||
import ja from './ja.json'
|
||||
import la from './la.json'
|
||||
|
||||
export const createMessages = {
|
||||
en: en.create,
|
||||
de: de.create,
|
||||
ja: ja.create,
|
||||
hi: hi.create,
|
||||
es: es.create,
|
||||
la: la.create,
|
||||
} as const
|
||||
@@ -1,4 +1,5 @@
|
||||
import { rithmomachiaMessages } from '@/arcade-games/rithmomachia/messages'
|
||||
import { createMessages } from '@/i18n/locales/create/messages'
|
||||
import { homeMessages } from '@/i18n/locales/home/messages'
|
||||
import { tutorialMessages } from '@/i18n/locales/tutorial/messages'
|
||||
|
||||
@@ -35,6 +36,7 @@ export async function getMessages(locale: Locale) {
|
||||
return mergeMessages(
|
||||
common,
|
||||
{ home: homeMessages[locale] },
|
||||
{ create: createMessages[locale] },
|
||||
{ tutorial: tutorialMessages[locale] },
|
||||
rithmomachiaMessages[locale]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user