wip(org): Added translation support and locale picker
This commit is contained in:
parent
0ee9b36451
commit
f1c291491f
11 changed files with 281 additions and 27 deletions
10
packages/freesewing.org/components/designs/teaser.js
Normal file
10
packages/freesewing.org/components/designs/teaser.js
Normal file
|
@ -0,0 +1,10 @@
|
|||
const DesignTeaser = ({ design=false }) => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>Tease {design} here</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default DesignTeaser
|
|
@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
|
|||
import Logo from 'shared/components/logos/freesewing.js'
|
||||
import Link from 'next/link'
|
||||
import ThemePicker from 'shared/components/theme-picker.js'
|
||||
import LocalePicker from 'shared/components/locale-picker.js'
|
||||
import CloseIcon from 'shared/components/icons/close.js'
|
||||
import MenuIcon from 'shared/components/icons/menu.js'
|
||||
import SearchIcon from 'shared/components/icons/search.js'
|
||||
|
@ -114,6 +115,7 @@ const Header = ({ app, setSearch }) => {
|
|||
</div>
|
||||
<div className="hidden md:flex flex-row items-center">
|
||||
<ThemePicker app={app} />
|
||||
<LocalePicker app={app} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
11
packages/freesewing.org/components/mdx/index.js
Normal file
11
packages/freesewing.org/components/mdx/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import PatternDocs from './pattern-docs.js'
|
||||
import PatternOptions from './pattern-options.js'
|
||||
import PatternMeasurements from './pattern-measurements.js'
|
||||
|
||||
const components = {
|
||||
PatternDocs,
|
||||
PatternOptions,
|
||||
PatternMeasurements,
|
||||
}
|
||||
|
||||
export default components
|
138
packages/freesewing.org/components/mdx/pattern-docs.js
Normal file
138
packages/freesewing.org/components/mdx/pattern-docs.js
Normal file
|
@ -0,0 +1,138 @@
|
|||
import React from 'react'
|
||||
import { capitalize } from 'shared/utils.js'
|
||||
import Link from 'next/link'
|
||||
import { getConfig } from 'shared/designs/index.js'
|
||||
import Popout from 'shared/components/popout.js'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import DesignTeaser from 'site/components/designs/teaser.js'
|
||||
//import PatternOptions from './pattern-options'
|
||||
//import PatternMeasurements from './pattern-measurements'
|
||||
|
||||
const PatternDocs = ({ pattern=false }) => {
|
||||
const { t } = useTranslation(['docs'])
|
||||
|
||||
if (!pattern) return <p>Please specify a pattern prop when using the PatternDocs component</p>
|
||||
|
||||
const config = getConfig(pattern)
|
||||
console.log({pattern, config})
|
||||
|
||||
return (
|
||||
<>
|
||||
{config.deprecated && (
|
||||
<Popout note>
|
||||
<h5>{t('thingIsDeprecated', { thing: capitalize(pattern)})}</h5>
|
||||
<p>
|
||||
{t('weRecommendThingInstead', { thing: capitalize(config.deprecated)})}
|
||||
</p>
|
||||
<DesignTeaser design={pattern} />
|
||||
</Popout>
|
||||
)}
|
||||
<pre>{JSON.stringify(config, null ,2)}</pre>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
{config.deprecated && (
|
||||
<Popout note>
|
||||
<h5>{capitalize(pattern)} is deprecated</h5>
|
||||
<p>
|
||||
We recommend{' '}
|
||||
<Link to={`/designs/${info[props.pattern].deprecated}/`}>
|
||||
{capitalize(info[props.pattern].deprecated)}
|
||||
</Link>{' '}
|
||||
instead.
|
||||
</p>
|
||||
</Popout>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: '1rem',
|
||||
}}
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage id={'patterns.' + props.pattern + '.description'} />
|
||||
</p>
|
||||
<div>
|
||||
<Button
|
||||
style={{ marginRight: '1rem' }}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
size="large"
|
||||
href={'/create/' + props.pattern + '/'}
|
||||
>
|
||||
<PlayIcon style={{ marginRight: '1rem' }} />
|
||||
<FormattedMessage
|
||||
id="app.newThing"
|
||||
values={{
|
||||
thing: [capitalize(props.pattern), ' ', <FormattedMessage id={`app.pattern`} />],
|
||||
}}
|
||||
/>
|
||||
</Button>
|
||||
<p>
|
||||
<Hashtag
|
||||
tag={`FreeSewing${capitalize(props.pattern)}`}
|
||||
title={`${capitalize(props.pattern)} Hashtag`}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<h2>
|
||||
<FormattedMessage id="app.patternInstructions" />
|
||||
</h2>
|
||||
<ul className="links">
|
||||
<li>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/cutting/'}>
|
||||
{capitalize(props.pattern)} » <FormattedMessage id="app.cutting" />
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/fabric/'}>
|
||||
{capitalize(props.pattern)} » <FormattedMessage id="app.fabricOptions" />
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/instructions/'}>
|
||||
{capitalize(props.pattern)} » <FormattedMessage id="app.instructions" />
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/needs/'}>
|
||||
{capitalize(props.pattern)} » <FormattedMessage id="app.whatYouNeed" />
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
<h2>
|
||||
<FormattedMessage id="app.patternOptions" />
|
||||
</h2>
|
||||
<PatternOptions pattern={props.pattern} />
|
||||
{measurements[props.pattern].length > 0 ? (
|
||||
<>
|
||||
<h2>
|
||||
<FormattedMessage id="app.requiredMeasurements" />
|
||||
</h2>
|
||||
<PatternMeasurements pattern={props.pattern} app={props.app} />
|
||||
</>
|
||||
) : null}
|
||||
<h2>
|
||||
<FormattedMessage id="app.examples" />
|
||||
</h2>
|
||||
<p>
|
||||
<FormattedMessage id="intro.txt-showcase" />:
|
||||
</p>
|
||||
<ul className="links">
|
||||
<li>
|
||||
<Link to={'/showcase/designs/' + props.pattern}>
|
||||
<FormattedMessage id="app.showcase" /> / {capitalize(props.pattern)}
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default PatternDocs
|
|
@ -0,0 +1,33 @@
|
|||
|
||||
const PatternMeasurements = (props) => {
|
||||
return null
|
||||
const intl = useIntl()
|
||||
const sortMeasurements = (measurements) => {
|
||||
if (typeof measurements === 'undefined') return []
|
||||
let sorted = []
|
||||
let translated = {}
|
||||
for (let m of measurements) {
|
||||
let translation = intl.messages['measurements.' + m] || m
|
||||
translated[translation] = m
|
||||
}
|
||||
let order = Object.keys(translated)
|
||||
order.sort()
|
||||
for (let m of order) sorted.push(translated[m])
|
||||
|
||||
return sorted
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="links">
|
||||
{sortMeasurements(measurements[props.pattern]).map((m) => (
|
||||
<li key={m}>
|
||||
<Link to={'/docs/measurements/' + m.toLowerCase()}>
|
||||
<FormattedMessage id={'measurements.' + m} />
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export default PatternMeasurements
|
57
packages/freesewing.org/components/mdx/pattern-options.js
Normal file
57
packages/freesewing.org/components/mdx/pattern-options.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
|
||||
const PatternOptions = (props) => {
|
||||
return null
|
||||
const renderOptions = () => {
|
||||
const groups = optionGroups[props.pattern]
|
||||
const list = []
|
||||
for (let l1 in groups) {
|
||||
let children = []
|
||||
for (let l2 of groups[l1]) {
|
||||
if (typeof l2 === 'string') {
|
||||
children.push(
|
||||
<li key={props.pattern + l2}>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/options/' + l2.toLowerCase()}>
|
||||
<FormattedMessage id={'options.' + props.pattern + '.' + l2 + '.title'} />
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
} else {
|
||||
for (let l3 in l2) {
|
||||
let grandchildren = []
|
||||
for (let l4 of l2[l3]) {
|
||||
grandchildren.push(
|
||||
<li key={props.pattern + l4}>
|
||||
<Link to={'/docs/patterns/' + props.pattern + '/options/' + l4.toLowerCase()}>
|
||||
<FormattedMessage id={'options.' + props.pattern + '.' + l4 + '.title'} />
|
||||
</Link>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
children.push(
|
||||
<li key={props.pattern + l3}>
|
||||
<b>
|
||||
<FormattedMessage id={'optiongroups.' + l3} />
|
||||
</b>
|
||||
<ul className="links">{grandchildren}</ul>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
list.push(
|
||||
<li key={props.pattern + l1}>
|
||||
<b>
|
||||
<FormattedMessage id={'optiongroups.' + l1} />
|
||||
</b>
|
||||
<ul className="links">{children}</ul>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
return <ul className="links">{list}</ul>
|
||||
}
|
||||
|
||||
return renderOptions()
|
||||
}
|
||||
|
||||
export default PatternOptions
|
|
@ -5,6 +5,7 @@ import useLocalStorage from 'shared/hooks/useLocalStorage.js'
|
|||
// Prebuild navigation
|
||||
import prebuildNavigation from 'site/prebuild/navigation.js'
|
||||
// Translation
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
/*
|
||||
|
@ -53,6 +54,7 @@ const buildNavigation = (lang, t) => {
|
|||
function useApp(full = true) {
|
||||
|
||||
// Load translation method
|
||||
const locale = useRouter().locale
|
||||
const { t } = useTranslation()
|
||||
|
||||
// User color scheme preference
|
||||
|
@ -63,11 +65,10 @@ function useApp(full = true) {
|
|||
// Persistent state
|
||||
const [account, setAccount] = useLocalStorage('account', { username: false })
|
||||
const [theme, setTheme] = useLocalStorage('theme', prefersDarkMode ? 'dark' : 'light')
|
||||
const [language, setLanguage] = useLocalStorage('language', 'en')
|
||||
|
||||
// React State
|
||||
const [primaryMenu, setPrimaryMenu] = useState(false)
|
||||
const [navigation, setNavigation] = useState(buildNavigation(language, t))
|
||||
const [navigation, setNavigation] = useState(buildNavigation(locale, t))
|
||||
const [slug, setSlug] = useState('/')
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
|
@ -88,10 +89,12 @@ function useApp(full = true) {
|
|||
|
||||
return {
|
||||
// Static vars
|
||||
site: 'dev',
|
||||
site: 'org',
|
||||
|
||||
// i18n
|
||||
locale,
|
||||
|
||||
// State
|
||||
language,
|
||||
loading,
|
||||
navigation,
|
||||
primaryMenu,
|
||||
|
@ -99,7 +102,6 @@ function useApp(full = true) {
|
|||
theme,
|
||||
|
||||
// State setters
|
||||
setLanguage,
|
||||
setLoading,
|
||||
setNavigation,
|
||||
setPrimaryMenu,
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
import i18n from '../freesewing.shared/config/i18n.config.mjs'
|
||||
|
||||
export default i18n()
|
|
@ -7,25 +7,25 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import Layout from 'site/components/layouts/bare'
|
||||
import Navigation, { Icons } from 'shared/components/navigation/primary'
|
||||
import DownIcon from 'shared/components/icons/down.js'
|
||||
|
||||
const HomePage = (props) => {
|
||||
const app = useApp()
|
||||
const { t, i18n } = useTranslation(['ograph'])
|
||||
const { language } = i18n
|
||||
const { t } = useTranslation(['homepage', 'ograph'])
|
||||
|
||||
return (
|
||||
<Page app={app} title="Welcome to FreeSewing.org" layout={Layout}>
|
||||
<Head>
|
||||
<meta property="og:title" content="FreeSewing.org" key="title" />
|
||||
<meta property="og:type" content="article" key='type' />
|
||||
<meta property="og:description" content={t('og:orgDesc')} key='description' />
|
||||
<meta property="og:description" content={t('ograph:orgDesc')} key='description' />
|
||||
<meta property="og:article:author" content='Joost De Cock' key='author' />
|
||||
<meta property="og:image" content={`https://canary.backend.freesewing.org/og-img/${language}/org/`} key='image' />
|
||||
<meta property="og:image" content={`https://canary.backend.freesewing.org/og-img/${app.locale}/org/`} key='image' />
|
||||
<meta property="og:image:type" content="image/png" />
|
||||
<meta property="og:image:width" content="1200" />
|
||||
<meta property="og:image:height" content="630" />
|
||||
<meta property="og:url" content="https://freesewing.org/" key='url' />
|
||||
<meta property="og:locale" content={language} key='locale' />
|
||||
<meta property="og:locale" content={app.locale} key='locale' />
|
||||
<meta property="og:site_name" content="freesewing.org" key='site' />
|
||||
</Head>
|
||||
<section
|
||||
|
@ -36,8 +36,8 @@ const HomePage = (props) => {
|
|||
}}
|
||||
className="m-0 p-0 shadow drop-shadow-lg w-full mb-8"
|
||||
>
|
||||
<div className="mx-auto px-8 flex flex-col items-center justify-around min-h-screen">
|
||||
<span>test</span>
|
||||
<div className="mx-auto px-8 flex flex-col items-center justify-between min-h-screen">
|
||||
<span> </span>
|
||||
<div>
|
||||
<div className="flex flex-col items-end max-w-4xl">
|
||||
<h1
|
||||
|
@ -60,7 +60,7 @@ const HomePage = (props) => {
|
|||
md:text-4xl
|
||||
lg:max-w-1/2 lg:text-4xl xl:pr-0 `}
|
||||
style={{ textShadow: '1px 1px 3px #000', color: 'white' }}
|
||||
dangerouslySetInnerHTML={{ __html: t('orgDescription')}}
|
||||
dangerouslySetInnerHTML={{ __html: t('ograph:orgDescription')}}
|
||||
/>
|
||||
</div>
|
||||
<Icons app={app} active='/'
|
||||
|
@ -72,14 +72,10 @@ const HomePage = (props) => {
|
|||
flex flex-col items-center capitalize`}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-neutral-content text-center mt-8">
|
||||
To learn more about FreeSewing and try our platform
|
||||
go to <a
|
||||
href="https://freesewing.org/"
|
||||
title="Go to FreeSewing.org"
|
||||
className="text-secondary font-bold"
|
||||
>freesewing.org</a>
|
||||
</p>
|
||||
<div className="text-neutral-content text-center mt-8 text-center">
|
||||
{t('scrollDownToLearnMore')}
|
||||
<DownIcon className="w-24 h-24 animate-bounce w-full m-auto mt-8"/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<div>
|
||||
|
@ -126,7 +122,7 @@ export default HomePage
|
|||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations('en')),
|
||||
...(await serverSideTranslations(locale)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
8
packages/freesewing.shared/components/icons/down.js
Normal file
8
packages/freesewing.shared/components/icons/down.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
const Down = ({ className="h-6 w-6" }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
)
|
||||
|
||||
export default Down
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
/* Sourced from heroicons.com - Thanks guys! */
|
||||
const Left = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
const Left = ({ className="h-6 w-6" }) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className={className} fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
|
||||
</svg>
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue