1
0
Fork 0

feat(org): Added translation status page

This commit is contained in:
Joost De Cock 2023-07-05 20:58:30 +02:00
parent 7a4f47e50f
commit a2b689e69a
8 changed files with 271 additions and 0 deletions

View file

@ -319,6 +319,8 @@ org:
'algoliasearch': *algoliasearch 'algoliasearch': *algoliasearch
'react-copy-to-clipboard': 5.1.0 'react-copy-to-clipboard': 5.1.0
'daisyui': *daisyui 'daisyui': *daisyui
'echarts': 5.4.2
'echarts-for-react': 3.0.2
'jotai': &jotai '2.2.1' 'jotai': &jotai '2.2.1'
'jotai-location': &jotai-location '0.5.1' 'jotai-location': &jotai-location '0.5.1'
'lodash.get': *_get 'lodash.get': *_get

View file

@ -0,0 +1,103 @@
import { useState, useEffect } from 'react'
import { siteConfig } from 'site/site.config.mjs'
import { Spinner } from 'shared/components/spinner.mjs'
import { ChartWrapper } from 'shared/components/wrappers/chart.mjs'
import orderBy from 'lodash.orderby'
/*
* This method load the translation status from the Crowdin API
*/
const loadTranslationStatus = async (setter) => {
let response
try {
response = await fetch(
`https://api.crowdin.com/api/v2/projects/${siteConfig.crowdin.projectId}/languages/progress`,
{ headers: { Authorization: `Bearer ${siteConfig.crowdin.token}` } }
)
} catch (err) {
console.log(err)
setter(err)
}
if (response) {
const data = await response.json()
setter(data)
}
}
const Status = ({ done, approved, language }) => {
const option = {
series: [
{
name: 'Translation Status',
type: 'pie',
radius: ['40%', '70%'],
label: {
show: false,
},
emphasis: {
disabled: true,
},
data: [
{
value: approved,
name: 'Translated & Approved',
itemStyle: {
color: 'currentColor',
opacity: 1,
},
},
{
value: done,
name: 'Translated & Approved',
itemStyle: {
color: 'currentColor',
opacity: 0.7,
},
},
{
value: 100 - (done + approved),
name: 'Untranslated',
itemStyle: {
color: 'currentColor',
opacity: 0.3,
},
},
],
},
],
}
return <ChartWrapper option={option} h={150} />
}
export const TranslationStatus = () => {
const [status, setStatus] = useState(false)
useEffect(() => {
if (!status) loadTranslationStatus(setStatus)
}, [status])
return status ? (
<>
<div className="text-primary flex flex-row flex-wrap items-center space-around w-full">
{status.data &&
orderBy(status.data, ['data.approvalProgress'], ['desc']).map((entry) => (
<div className="text-center" key={entry.data.languageId} style={{ width: '150px' }}>
<h5>
{entry.data.languageId.indexOf('-') === -1
? entry.data.languageId.toUpperCase()
: entry.data.languageId.split('-')[0].toUpperCase()}
: {entry.data.approvalProgress}%
</h5>
<Status
done={entry.data.translationProgress - entry.data.approvalProgress}
approved={entry.data.approvalProgress}
/>
</div>
))}
</div>
</>
) : (
<Spinner className="w-24 h-24 m-auto text-primary animate-spin" />
)
}

View file

@ -89,6 +89,11 @@ const sitePages = (t = false, control = 99) => {
s: 'profile', s: 'profile',
h: 1, h: 1,
}, },
translation: {
t: t('translation'),
s: 'translation',
h: 1,
},
sitemap: { sitemap: {
t: t('sitemap'), t: t('sitemap'),
s: 'sitemap', s: 'sitemap',

View file

@ -39,6 +39,8 @@
"algoliasearch": "4.18.0", "algoliasearch": "4.18.0",
"react-copy-to-clipboard": "5.1.0", "react-copy-to-clipboard": "5.1.0",
"daisyui": "3.1.7", "daisyui": "3.1.7",
"echarts": "5.4.2",
"echarts-for-react": "3.0.2",
"jotai": "2.2.1", "jotai": "2.2.1",
"jotai-location": "0.5.1", "jotai-location": "0.5.1",
"lodash.get": "4.4.2", "lodash.get": "4.4.2",

View file

@ -0,0 +1,102 @@
// Dependencies
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
// Components
import { PageWrapper, ns as pageNs } from 'shared/components/wrappers/page.mjs'
import { BareLayout as Layout } from 'site/components/layouts/bare.mjs'
import { TranslationStatus } from 'site/components/crowdin/status.mjs'
import { Popout } from 'shared/components/popout.mjs'
import { Breadcrumbs } from 'shared/components/breadcrumbs.mjs'
import { WebLink } from 'shared/components/web-link.mjs'
// Translation namespaces used on this page
const namespaces = [...new Set(pageNs)]
// FIXME: This page (ironically) lacks translation
//
const TranslationPage = ({ page }) => (
<PageWrapper {...page} layout={Layout}>
<div className="max-w-6xl mx-auto p-4 mt-4">
<Breadcrumbs crumbs={[{ s: 'translation', t: 'Translation' }]} title="Translation" />
<h1>Translation</h1>
<p>
Thanks to the translation volunteers in our community FreeSewing is proudly multilingual.
</p>
<h2>Get involved</h2>
<p>
Translation is a team effort, and getting involved is not hard at all. Refer to{' '}
<WebLink href="https://freesewing.dev/guides/translation" txt="the translation guide" /> for
all details.
</p>
<h2>Translation Status</h2>
<TranslationStatus />
<b className="ml-10">Legend</b>
<ul className="list list-inside ml-4">
<li className="flex flex-row items-center gap-2">
<span className="inline-block w-4 h-4 rounded-full bg-primary"></span>
<span>Translated & Approved</span>
</li>
<li className="flex flex-row items-center gap-2">
<span className="inline-block w-4 h-4 rounded-full bg-primary bg-opacity-70"></span>
<span>Translated but not (yet) approved</span>
</li>
<li className="flex flex-row items-center gap-2">
<span className="inline-block w-4 h-4 rounded-full bg-primary bg-opacity-30"></span>
<span>Not translated (yet)</span>
</li>
</ul>
<h2>Supported Languages</h2>
<p>We currently support the following five languages:</p>
<ul className="list list-inside list-disc ml-4">
<li>
<b>English</b>{' '}
<small>
(This is our source language and the working language of the FreeSewing project)
</small>
</li>
<li>
<b>Dutch</b>
</li>
<li>
<b>German</b>
</li>
<li>
<b>French</b>
</li>
<li>
<b>Spanish</b>
</li>
</ul>
<p>In addition, comminity members have started initiatives to add the following langauges:</p>
<ul className="list list-inside list-disc ml-4">
<li>
<b>Ukranian</b>
</li>
</ul>
<Popout tip>
<h5>Looking to add a language?</h5>
<p>
We would love to make FreeSewing available in more langauges.
<br />
If you are interested in starting a new translation effort, please reach out.
</p>
</Popout>
</div>
</PageWrapper>
)
export default TranslationPage
export async function getStaticProps({ locale }) {
return {
props: {
...(await serverSideTranslations(locale, namespaces)),
page: {
locale,
path: ['translation'],
},
},
}
}

View file

@ -7,11 +7,16 @@ export const siteConfig = {
bugsnag: { bugsnag: {
key: '1b3a900d6ebbfd071975e39b534e1ff5', key: '1b3a900d6ebbfd071975e39b534e1ff5',
}, },
crowdin: {
projectId: 356411,
token: '77cf8abfabef66e1275cd4ddfe0c857d45db5e51e8349877dc50a7a740d28fb573e8a4543eca616b', // Translation status (Read-only)
},
sanity: { sanity: {
project: process.env.SANITY_PROJECT || 'hl5bw8cj', project: process.env.SANITY_PROJECT || 'hl5bw8cj',
dataset: 'site-content', dataset: 'site-content',
apiVersion: '2023-06-17', apiVersion: '2023-06-17',
}, },
languages: ['en', 'es', 'de', 'fr', 'nl'], languages: ['en', 'es', 'de', 'fr', 'nl'],
languagesWip: ['uk'],
site: 'FreeSewing.org', site: 'FreeSewing.org',
} }

View file

@ -0,0 +1,19 @@
import { useState } from 'react'
import * as echarts from 'echarts'
import ReactECharts from 'echarts-for-react'
import { Popout } from 'shared/components/popout.mjs'
echarts.registerTheme('light', {
backgroundColor: 'transparent',
})
echarts.registerTheme('dark', {
backgroundColor: 'transparent',
})
export const ChartWrapper = ({ option = false, theme = 'light', h = 400 }) => {
return option ? (
<ReactECharts option={option} className="class_2" theme={theme} style={{ height: h }} />
) : (
<Popout loading>Loading chart...</Popout>
)
}

View file

@ -7894,6 +7894,22 @@ ecdsa-sig-formatter@1.0.11:
dependencies: dependencies:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
echarts-for-react@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/echarts-for-react/-/echarts-for-react-3.0.2.tgz#ac5859157048a1066d4553e34b328abb24f2b7c1"
integrity sha512-DRwIiTzx8JfwPOVgGttDytBqdp5VzCSyMRIxubgU/g2n9y3VLUmF2FK7Icmg/sNVkv4+rktmrLN9w22U2yy3fA==
dependencies:
fast-deep-equal "^3.1.3"
size-sensor "^1.0.1"
echarts@^5.4.2:
version "5.4.2"
resolved "https://registry.yarnpkg.com/echarts/-/echarts-5.4.2.tgz#9f38781c9c6ae323e896956178f6956952c77a48"
integrity sha512-2W3vw3oI2tWJdyAz+b8DuWS0nfXtSDqlDmqgin/lfzbkB01cuMEN66KWBlmur3YMp5nEDEEt5s23pllnAzB4EA==
dependencies:
tslib "2.3.0"
zrender "5.4.3"
editorconfig@^0.15.3: editorconfig@^0.15.3:
version "0.15.3" version "0.15.3"
resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
@ -18126,6 +18142,11 @@ sisteransi@^1.0.5:
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
size-sensor@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/size-sensor/-/size-sensor-1.0.1.tgz#f84e46206d3e259faff1d548e4b3beca93219dbb"
integrity sha512-QTy7MnuugCFXIedXRpUSk9gUnyNiaxIdxGfUjr8xxXOqIB3QvBUYP9+b51oCg2C4dnhaeNk/h57TxjbvoJrJUA==
slash@3.0.0, slash@^3.0.0: slash@3.0.0, slash@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
@ -19392,6 +19413,11 @@ tsconfig-paths@^4.1.2:
minimist "^1.2.6" minimist "^1.2.6"
strip-bom "^3.0.0" strip-bom "^3.0.0"
tslib@2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0: tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0:
version "1.14.1" version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
@ -20723,6 +20749,13 @@ zod@3.21.4:
resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db"
integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw== integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==
zrender@5.4.3:
version "5.4.3"
resolved "https://registry.yarnpkg.com/zrender/-/zrender-5.4.3.tgz#41ffaf835f3a3210224abd9d6964b48ff01e79f5"
integrity sha512-DRUM4ZLnoaT0PBVvGBDO9oWIDBKFdAVieNWxWwK0niYzJCMwGchRk21/hsE+RKkIveH3XHCyvXcJDkgLVvfizQ==
dependencies:
tslib "2.3.0"
zwitch@^1.0.0: zwitch@^1.0.0:
version "1.0.5" version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"