feat(org): Added various translation pages
This commit is contained in:
parent
2299cc6ade
commit
b822e35fbe
11 changed files with 548 additions and 17 deletions
|
@ -19,16 +19,6 @@ We currently support the following five languages:
|
|||
- **es** : Spanish
|
||||
- **fr** : French
|
||||
- **nl** : Dutch
|
||||
|
||||
## Incubator Languages
|
||||
|
||||
For the following languages, our community has started an effort, but that
|
||||
effort has not yet reached the level of maturity that to make it a supported
|
||||
language.
|
||||
|
||||
In other words, **these are the languages where we are most in need of extra
|
||||
translators**:
|
||||
|
||||
- **uk** : Ukranian
|
||||
|
||||
## Become a FreeSewing translator
|
||||
|
@ -53,7 +43,7 @@ Discord](https://discord.freesewing.org) for any questions that may remain.
|
|||
|
||||
## Adding a new language
|
||||
|
||||
We would love to make FreeSewing available in more langauges. If you are
|
||||
We would love to make FreeSewing available in more langauges. If you are
|
||||
interested in starting a new translation effort, that is great.
|
||||
|
||||
We ask that you familiarize yourself with this translation guide to understand
|
||||
|
@ -62,7 +52,7 @@ a new language with the link below.
|
|||
|
||||
<Link compact>
|
||||
|
||||
###### [Request to setup a new FreeSewing language](https://next.freesewing.org/translation/add-language)
|
||||
###### [Suggest a new FreeSewing language](https://next.freesewing.org/translation/suggest-language)
|
||||
</Link>
|
||||
|
||||
<Fixme compact>
|
||||
|
|
221
sites/org/components/crowdin/suggest-language.mjs
Normal file
221
sites/org/components/crowdin/suggest-language.mjs
Normal file
|
@ -0,0 +1,221 @@
|
|||
// Dependencies
|
||||
import { siteConfig } from 'site/site.config.mjs'
|
||||
import translators from 'site/prebuild/translators.json'
|
||||
// Context
|
||||
import { LoadingContext } from 'shared/context/loading-context.mjs'
|
||||
// Hooks
|
||||
import { useAccount } from 'shared/hooks/use-account.mjs'
|
||||
import { useBackend } from 'shared/hooks/use-backend.mjs'
|
||||
import { useToast } from 'shared/hooks/use-toast.mjs'
|
||||
import { useState, useContext } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Components
|
||||
import { ChoiceButton } from 'shared/components/choice-button.mjs'
|
||||
import { I18nIcon } from 'shared/components/icons.mjs'
|
||||
import { Popout } from 'shared/components/popout.mjs'
|
||||
import { WebLink } from 'shared/components/web-link.mjs'
|
||||
|
||||
export const ns = ['translation', 'locales']
|
||||
|
||||
/*
|
||||
* Note that this is not a list of all languages.
|
||||
* Instead it is a list of languages that are supported by DeepL
|
||||
* and not yet available (Our Crowdin is integrated with DeepL.
|
||||
*/
|
||||
const languages = [
|
||||
'Bulgarian',
|
||||
'Chinese (simplified)',
|
||||
'Czech',
|
||||
'Danish',
|
||||
'Estonian',
|
||||
'Finnish',
|
||||
'Greek',
|
||||
'Hungarian',
|
||||
'Indonesian',
|
||||
'Italian',
|
||||
'Japanese',
|
||||
'Korean',
|
||||
'Latvian',
|
||||
'Lithuanian',
|
||||
'Norwegian',
|
||||
'Polish',
|
||||
'Portuguese',
|
||||
'Romanian',
|
||||
'Russian',
|
||||
'Slovak',
|
||||
'Slovenian',
|
||||
'Swedish',
|
||||
'Turkish',
|
||||
]
|
||||
|
||||
const TeamList = ({ language, t }) => {
|
||||
const count = Object.keys(translators[language]).length
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(translators[language])
|
||||
.sort()
|
||||
.map((name, i) => (
|
||||
<span key={name}>
|
||||
<b>{name}</b>
|
||||
{i < count - 2 ? ', ' : i < count - 1 ? ' & ' : ' '}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const SuggestLanguageForm = () => {
|
||||
// Context
|
||||
const { loading, startLoading, stopLoading } = useContext(LoadingContext)
|
||||
|
||||
// Hooks
|
||||
const { t } = useTranslation(ns)
|
||||
const { account, setAccount, token } = useAccount()
|
||||
const backend = useBackend(token)
|
||||
const toast = useToast()
|
||||
|
||||
// State
|
||||
const [language, setLanguage] = useState(false)
|
||||
const [sent, setSent] = useState(false)
|
||||
const [help, setHelp] = useState(0)
|
||||
const [friends, setFriends] = useState(0)
|
||||
const [comments, setComments] = useState('')
|
||||
|
||||
const sendSuggestion = async () => {
|
||||
startLoading()
|
||||
const result = await backend.sendLanguageSuggestion({ language, help, friends, comments })
|
||||
if (result.success) {
|
||||
setSent(true)
|
||||
stopLoading()
|
||||
toast.success('Suggestion submitted')
|
||||
} else {
|
||||
toast.for.backendError()
|
||||
}
|
||||
}
|
||||
|
||||
if (sent)
|
||||
return (
|
||||
<>
|
||||
<Popout note>
|
||||
<h5>Suggestion submitted</h5>
|
||||
<p>
|
||||
We will get back to you shortly. Thank you for taking an interest in bringing FreeSewing
|
||||
to more people, specifically the {language} community.
|
||||
</p>
|
||||
</Popout>
|
||||
</>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<h5 className="mt-4">In what language would you like make FreeSewing available?</h5>
|
||||
<select
|
||||
className="select select-bordered w-full border-neutral"
|
||||
onChange={(evt) => setLanguage(evt.target.value)}
|
||||
>
|
||||
<option value="">Please choose a language</option>
|
||||
{languages.map((l) => (
|
||||
<option value={l} key={l}>
|
||||
{l}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
{language ? (
|
||||
<>
|
||||
<h5 className="mt-4">
|
||||
Do you plan to (help) translate FreeSewing to this language yourself?
|
||||
</h5>
|
||||
<div className="flex flex-col gap-2 lg:grid lg:grid-cols-2">
|
||||
<ChoiceButton noMargin title="Yes, I do" onClick={() => setHelp(1)} active={help === 1}>
|
||||
<span>I am able and willing to help out with translation</span>
|
||||
</ChoiceButton>
|
||||
<ChoiceButton
|
||||
noMargin
|
||||
title="No, I do not"
|
||||
onClick={() => setHelp(-1)}
|
||||
active={help === -1}
|
||||
>
|
||||
<span>I do not intent to help out with translation</span>
|
||||
</ChoiceButton>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<Popout tip>
|
||||
<h5>Are you looking to suggest a language that is not in the list?</h5>
|
||||
<p>
|
||||
The list of languages above does obviously not include <em>all</em> languages. Instead,
|
||||
it is limimted to the list of langauges that are supported by{' '}
|
||||
<WebLink href="https://www.deepl.com/" txt="DeepL" />, a machine-learning tool that can
|
||||
help translators with suggestions that make for an efficient translation experience.
|
||||
</p>
|
||||
<p>
|
||||
It is always possible to translate to another language by translating everything by
|
||||
hand. However, we estimate that the amount of people out there who are willing to take
|
||||
on such a task is a rounding error.
|
||||
</p>
|
||||
<p>
|
||||
If you are committed to translating FreeSewing to a language not in the list above,
|
||||
please <WebLink href="https://discord.freesewing.org/" txt="please readh out to us" />.
|
||||
</p>
|
||||
</Popout>
|
||||
)}
|
||||
{language && help < 0 && (
|
||||
<Popout note>
|
||||
<h5>Thank you for your suggestion</h5>
|
||||
<p>
|
||||
We appreciate that you would like to see FreeSewing translated to {language}.
|
||||
<br />
|
||||
However, since you are unable to make a commitment yourself, we will not process your
|
||||
suggestion.
|
||||
</p>
|
||||
<p>
|
||||
We rely on the community to provide translation. We encourage you to find people in the{' '}
|
||||
{language} maker/sewing community who may want to take an active part in translating
|
||||
FreeSewing into {language}.
|
||||
</p>
|
||||
<p>You can then send those people to this same page to suggest {language} again.</p>
|
||||
</Popout>
|
||||
)}
|
||||
{language && help > 0 && (
|
||||
<>
|
||||
<h5 className="mt-4">Do you have friends who can help?</h5>
|
||||
<div className="flex flex-col gap-2 lg:grid lg:grid-cols-2">
|
||||
<ChoiceButton
|
||||
noMargin
|
||||
title="Hell yeah I do"
|
||||
onClick={() => setFriends(1)}
|
||||
active={friends === 1}
|
||||
>
|
||||
<span>And they want to help out too!</span>
|
||||
</ChoiceButton>
|
||||
<ChoiceButton
|
||||
noMargin
|
||||
title="Not really"
|
||||
onClick={() => setFriends(-1)}
|
||||
active={friends === -1}
|
||||
>
|
||||
<span>But maybe others will join my effort?</span>
|
||||
</ChoiceButton>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{language && help > 0 && friends !== 0 && (
|
||||
<>
|
||||
<h5 className="mt-4">Any comments you would like to add?</h5>
|
||||
<textarea
|
||||
value={comments}
|
||||
placeholder="Type your commnents here"
|
||||
className="textarea textarea-bordered w-full border border-neutral"
|
||||
onChange={(evt) => setComments(evt.target.value)}
|
||||
/>
|
||||
<p className="mt-8 text-center">
|
||||
<button className="btn btn-primary" onClick={sendSuggestion}>
|
||||
Submit Suggestion
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
126
sites/org/components/crowdin/translator-invite.mjs
Normal file
126
sites/org/components/crowdin/translator-invite.mjs
Normal file
|
@ -0,0 +1,126 @@
|
|||
// Dependencies
|
||||
import { siteConfig } from 'site/site.config.mjs'
|
||||
import translators from 'site/prebuild/translators.json'
|
||||
// Context
|
||||
import { LoadingContext } from 'shared/context/loading-context.mjs'
|
||||
// Hooks
|
||||
import { useAccount } from 'shared/hooks/use-account.mjs'
|
||||
import { useBackend } from 'shared/hooks/use-backend.mjs'
|
||||
import { useToast } from 'shared/hooks/use-toast.mjs'
|
||||
import { useState, useContext } from 'react'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Components
|
||||
import { ChoiceButton } from 'shared/components/choice-button.mjs'
|
||||
import { I18nIcon } from 'shared/components/icons.mjs'
|
||||
import { Popout } from 'shared/components/popout.mjs'
|
||||
import { WebLink } from 'shared/components/web-link.mjs'
|
||||
|
||||
export const ns = ['translation', 'locales']
|
||||
|
||||
const languages = [
|
||||
...siteConfig.languages.filter((lang) => lang !== 'en'),
|
||||
...siteConfig.languagesWip,
|
||||
].sort()
|
||||
|
||||
const TeamList = ({ language, t }) => {
|
||||
const count = Object.keys(translators[language]).length
|
||||
|
||||
return (
|
||||
<>
|
||||
{Object.keys(translators[language])
|
||||
.sort()
|
||||
.map((name, i) => (
|
||||
<span key={name}>
|
||||
<b>{name}</b>
|
||||
{i < count - 2 ? ', ' : i < count - 1 ? ' & ' : ' '}
|
||||
</span>
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const TranslatorInvite = () => {
|
||||
// Context
|
||||
const { loading, startLoading, stopLoading } = useContext(LoadingContext)
|
||||
|
||||
// Hooks
|
||||
const { t } = useTranslation(ns)
|
||||
const { account, setAccount, token } = useAccount()
|
||||
const backend = useBackend(token)
|
||||
const toast = useToast()
|
||||
|
||||
// State
|
||||
const [team, setTeam] = useState(false)
|
||||
const [sent, setSent] = useState(false)
|
||||
|
||||
const sendInvite = async () => {
|
||||
startLoading()
|
||||
const result = await backend.sendTranslatorInvite(team)
|
||||
if (result.success) {
|
||||
setSent(true)
|
||||
stopLoading()
|
||||
toast.success(t('translation:inviteSent'))
|
||||
} else {
|
||||
toast.for.backendError()
|
||||
}
|
||||
}
|
||||
|
||||
if (sent)
|
||||
return (
|
||||
<>
|
||||
<Popout tip>
|
||||
<h5>{t('translation:inviteSent')}</h5>
|
||||
<p>{t('translation:successNote')}</p>
|
||||
</Popout>
|
||||
<Popout link compact>
|
||||
<WebLink
|
||||
href="https://freesewing.dev/guides/translation"
|
||||
txt={t('translation:seeTranslationGuide')}
|
||||
/>
|
||||
</Popout>
|
||||
</>
|
||||
)
|
||||
|
||||
return team ? (
|
||||
<>
|
||||
<p>
|
||||
<button className="btn btn-primary mr-2" onClick={sendInvite}>
|
||||
{t(`locales:sendMeAnInvite`)}: {t(`locales:${team}`)}
|
||||
</button>
|
||||
<button className="btn btn-primary btn-outline" onClick={() => setTeam(false)}>
|
||||
Join a different team
|
||||
</button>
|
||||
</p>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<p>{t('translation:pleaseChooseTeam')}</p>
|
||||
<h5>{t('translation:whatTeam')}</h5>
|
||||
<div className="flex flex-col gap-2 lg:grid lg:grid-cols-2 gap-2 mt- mb-82">
|
||||
{languages.map((language) => {
|
||||
const count = Object.keys(translators[language]).length
|
||||
return (
|
||||
<ChoiceButton
|
||||
noMargin
|
||||
key={language}
|
||||
icon={<I18nIcon />}
|
||||
title={t('translation:languageTeam', { language: t('locales:' + language) })}
|
||||
onClick={() => setTeam(language)}
|
||||
>
|
||||
<div className="text-sm flex flex-row flex-wrap gap-1">
|
||||
{Object.keys(translators[language]).map((name, i) => (
|
||||
<span
|
||||
key={i}
|
||||
className="bg-secondary bg-opacity-10 rounded px-2 text-sm border border-secondary"
|
||||
>
|
||||
{name}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</ChoiceButton>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
|
@ -93,6 +93,16 @@ const sitePages = (t = false, control = 99) => {
|
|||
t: t('translation'),
|
||||
s: 'translation',
|
||||
h: 1,
|
||||
join: {
|
||||
t: t('translation:joinATranslationTeam'),
|
||||
s: 'translation',
|
||||
h: 1,
|
||||
},
|
||||
'suggest-language': {
|
||||
t: t('translation:suggestLanguage'),
|
||||
s: 'translation',
|
||||
h: 1,
|
||||
},
|
||||
},
|
||||
sitemap: {
|
||||
t: t('sitemap'),
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Translators } from 'site/components/crowdin/translators.mjs'
|
|||
import { Popout } from 'shared/components/popout.mjs'
|
||||
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
|
||||
import { WebLink } from 'shared/components/web-link.mjs'
|
||||
import Link from 'next/link'
|
||||
|
||||
// Translation namespaces used on this page
|
||||
const namespaces = [...new Set(pageNs), 'translation', 'locales']
|
||||
|
@ -30,7 +31,13 @@ const TranslationPage = ({ page }) => {
|
|||
<Popout tip>
|
||||
<h5>{t('translation:getInvolved')}</h5>
|
||||
<p>{t('translation:teamEffort')}</p>
|
||||
<a href="https://freesewing.dev/guides/translation" className="btn btn-accent">
|
||||
<Link href="/translation/join" className="btn btn-accent mr-2">
|
||||
{t('translation:joinTheTeam')}
|
||||
</Link>
|
||||
<a
|
||||
href="https://freesewing.dev/guides/translation"
|
||||
className="btn btn-accent btn-outline"
|
||||
>
|
||||
{t('translation:seeTranslationGuide')}
|
||||
</a>
|
||||
</Popout>
|
||||
|
@ -92,7 +99,13 @@ const TranslationPage = ({ page }) => {
|
|||
<br />
|
||||
{t('translation:addLanguage3')}
|
||||
</p>
|
||||
<a href="https://freesewing.dev/guides/translation" className="btn btn-accent">
|
||||
<Link href="/translation/suggest-language" className="btn btn-accent mr-2">
|
||||
{t('translation:suggestLanguage')}
|
||||
</Link>
|
||||
<a
|
||||
href="https://freesewing.dev/guides/translation"
|
||||
className="btn btn-accent btn-outline"
|
||||
>
|
||||
{t('translation:seeTranslationGuide')}
|
||||
</a>
|
||||
</Popout>
|
||||
|
|
70
sites/org/pages/translation/join.mjs
Normal file
70
sites/org/pages/translation/join.mjs
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Dependencies
|
||||
import dynamic from 'next/dynamic'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
// Hooks
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Components
|
||||
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
|
||||
import { BareLayout as Layout } from 'site/components/layouts/bare.mjs'
|
||||
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
|
||||
|
||||
// Translation namespaces used on this page
|
||||
const namespaces = [...new Set(pageNs), 'translation', 'locales']
|
||||
|
||||
/*
|
||||
* Some things should never generated as SSR
|
||||
* So for these, we run a dynamic import and disable SSR rendering
|
||||
*/
|
||||
const DynamicAuthWrapper = dynamic(
|
||||
() => import('shared/components/wrappers/auth/index.mjs').then((mod) => mod.AuthWrapper),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const DynamicForm = dynamic(
|
||||
() => import('site/components/crowdin/translator-invite.mjs').then((mod) => mod.TranslatorInvite),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const TranslationJoinPage = ({ page }) => {
|
||||
const { t } = useTranslation(namespaces)
|
||||
|
||||
const title = t('translation:joinATranslationTeam')
|
||||
|
||||
return (
|
||||
<PageWrapper {...page} layout={Layout}>
|
||||
<div className="max-w-4xl mx-auto p-4 mt-4">
|
||||
<Breadcrumbs
|
||||
crumbs={[
|
||||
{ s: 'translation', t: t('translation:translation') },
|
||||
{ s: 'translation/join', t: title },
|
||||
]}
|
||||
title={title}
|
||||
/>
|
||||
|
||||
<h1>{title}</h1>
|
||||
<p>
|
||||
{t('translation:joinIntro')}
|
||||
<br />
|
||||
{t('translation:thatIsAwesome')} {t('translation:thanksSoMuch')}
|
||||
</p>
|
||||
<DynamicAuthWrapper>
|
||||
<DynamicForm />
|
||||
</DynamicAuthWrapper>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default TranslationJoinPage
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, namespaces)),
|
||||
page: {
|
||||
locale,
|
||||
path: ['translation/join'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
71
sites/org/pages/translation/suggest-language.mjs
Normal file
71
sites/org/pages/translation/suggest-language.mjs
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Dependencies
|
||||
import dynamic from 'next/dynamic'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
// Hooks
|
||||
import { useTranslation } from 'next-i18next'
|
||||
// Components
|
||||
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
|
||||
import { BareLayout as Layout } from 'site/components/layouts/bare.mjs'
|
||||
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
|
||||
|
||||
// Translation namespaces used on this page
|
||||
const namespaces = [...new Set(pageNs), 'translation', 'locales']
|
||||
|
||||
/*
|
||||
* Some things should never generated as SSR
|
||||
* So for these, we run a dynamic import and disable SSR rendering
|
||||
*/
|
||||
const DynamicAuthWrapper = dynamic(
|
||||
() => import('shared/components/wrappers/auth/index.mjs').then((mod) => mod.AuthWrapper),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const DynamicForm = dynamic(
|
||||
() =>
|
||||
import('site/components/crowdin/suggest-language.mjs').then((mod) => mod.SuggestLanguageForm),
|
||||
{ ssr: false }
|
||||
)
|
||||
|
||||
const SuggestLanguagePage = ({ page }) => {
|
||||
const { t } = useTranslation(namespaces)
|
||||
|
||||
const title = t('translation:suggestLanguage')
|
||||
|
||||
return (
|
||||
<PageWrapper {...page} layout={Layout}>
|
||||
<div className="max-w-4xl mx-auto p-4 mt-4">
|
||||
<Breadcrumbs
|
||||
crumbs={[
|
||||
{ s: 'translation', t: t('translation:translation') },
|
||||
{ s: 'translation/join', t: title },
|
||||
]}
|
||||
title={title}
|
||||
/>
|
||||
|
||||
<h1>{title}</h1>
|
||||
<p>
|
||||
{t('translation:suggestIntro')}
|
||||
<br />
|
||||
{t('translation:thatIsAwesome')} {t('translation:thanksSoMuch')}
|
||||
</p>
|
||||
<DynamicAuthWrapper>
|
||||
<DynamicForm />
|
||||
</DynamicAuthWrapper>
|
||||
</div>
|
||||
</PageWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export default SuggestLanguagePage
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, namespaces)),
|
||||
page: {
|
||||
locale,
|
||||
path: ['translation/suggest-language'],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -14,5 +14,18 @@ globalRanking: Global ranking
|
|||
groupByLanguage: Group by language
|
||||
translator: Translator
|
||||
words: Words
|
||||
joinTheTeam: Join the team
|
||||
joinATranslationTeam: Join a translation team
|
||||
languageTeam: "{language} Team"
|
||||
whatTeam: What language team are you joining?
|
||||
sendMeAnInvite: Send me an invite
|
||||
pleaseChooseTeam: Please choose a language below so we can send you the correct invite.
|
||||
successNote: Please check your inbox. You will get an email with an invite code that grants you access to the translation on Crowdin, the online translation platform that we use to translate FreeSewing into multiple languages.
|
||||
suggestLanguage: Suggest a new language
|
||||
joinIntro: Looking to join a FreeSewing translation team?
|
||||
thatIsAwesome: That is awesome.
|
||||
thanksSoMuch: Thanks so much.
|
||||
suggestIntro: Looking to add a new language to FreeSewing?
|
||||
pleaseMotivate: Please complete the form below so we can review your suggestion.
|
||||
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ export const siteConfig = {
|
|||
dataset: 'site-content',
|
||||
apiVersion: '2023-06-17',
|
||||
},
|
||||
languages: ['en', 'es', 'de', 'fr', 'nl'],
|
||||
languagesWip: ['uk'],
|
||||
languages: ['en', 'es', 'de', 'fr', 'nl', 'uk'],
|
||||
languagesWip: [],
|
||||
site: 'FreeSewing.org',
|
||||
}
|
||||
|
|
|
@ -5,11 +5,14 @@ export const ChoiceButton = ({
|
|||
icon = null,
|
||||
color = 'secondary',
|
||||
active = false,
|
||||
noMargin = false,
|
||||
}) => (
|
||||
<button
|
||||
onClick={onClick}
|
||||
className={`
|
||||
flex flex-col flex-nowrap items-start justify-start gap-2 pt-2 pb-4 h-auto w-full mt-3
|
||||
flex flex-col flex-nowrap items-start justify-start gap-2 pt-2 pb-4 h-auto w-full ${
|
||||
noMargin ? '' : 'mt-3'
|
||||
}
|
||||
btn btn-${color} btn-ghost border border-${color}
|
||||
hover:bg-opacity-20 hover:bg-${color} hover:border hover:border-${color}
|
||||
${active ? 'bg-' + color + ' bg-opacity-20 border border-' + color : ''}
|
||||
|
|
|
@ -287,6 +287,20 @@ Backend.prototype.createIssue = async function (data) {
|
|||
return responseHandler(await api.post(`/issues`, data), 201)
|
||||
}
|
||||
|
||||
/*
|
||||
* Send translation invite
|
||||
*/
|
||||
Backend.prototype.sendTranslatorInvite = async function (language) {
|
||||
return responseHandler(await api.post(`/flows/translator-invite/jwt`, { language }, this.auth))
|
||||
}
|
||||
|
||||
/*
|
||||
* Send language suggestion
|
||||
*/
|
||||
Backend.prototype.sendLanguageSuggestion = async function (data) {
|
||||
return responseHandler(await api.post(`/flows/language-suggestion/jwt`, data, this.auth))
|
||||
}
|
||||
|
||||
export function useBackend(token = false) {
|
||||
/*
|
||||
* This backend object is what we'll end up returning
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue