SEO & PWA (#1)
* Initial SEO test * Canonical URL added using envs * OG & Twitter fix * Robots.txt & meta added * Localhost support for canonical url * Initial PWA version * Icons reexported & added maskable
5
.gitignore
vendored
@@ -40,3 +40,8 @@ yarn-error.log*
|
|||||||
# cypress
|
# cypress
|
||||||
cypress/videos
|
cypress/videos
|
||||||
cypress/screenshots
|
cypress/screenshots
|
||||||
|
|
||||||
|
# next-pwa
|
||||||
|
**/public/workbox-*.js*
|
||||||
|
**/public/sw.js*
|
||||||
|
**/public/worker-*.js*
|
||||||
|
|||||||
@@ -1,23 +1,46 @@
|
|||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { Flex, VStack, Button, Link } from "@chakra-ui/react";
|
import { Flex, VStack, Button, Link, useColorModeValue } from "@chakra-ui/react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import Footer from "./Footer";
|
import Footer from "./Footer";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
customTitle?: string
|
customTitle?: string,
|
||||||
|
home?: true
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
};
|
};
|
||||||
|
|
||||||
const title = "Lingva Translate";
|
const title = "Lingva Translate";
|
||||||
|
const description = "Alternative front-end for Google Translate, serving as a Free and Open Source translator with over a hundred languages available";
|
||||||
|
const siteDomain = process.env["NEXT_PUBLIC_SITE_DOMAIN"];
|
||||||
|
const url = siteDomain && (siteDomain.includes("localhost") ? "http://" : "https://") + siteDomain;
|
||||||
|
|
||||||
const Layout: FC<Props> = ({ customTitle, children }) => (
|
const Layout: FC<Props> = ({ customTitle, children, home, ...props }) => (
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
{customTitle ?? title}
|
{customTitle ?? title}
|
||||||
</title>
|
</title>
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<meta name="description" content={description} />
|
||||||
|
<meta name="robots" content={home ? "index,follow" : "noindex,nofollow"} />
|
||||||
|
{home && <link rel="canonical" href={url} />}
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||||
|
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||||
|
<link rel="manifest" href="/manifest.json" />
|
||||||
|
<meta name="theme-color" content={useColorModeValue("#bde3cb", "#005525")} />
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
<meta property="og:title" content={title} />
|
||||||
|
<meta property="og:description" content={description} />
|
||||||
|
<meta property="og:url" content={url} />
|
||||||
|
<meta property="og:locale" content="en" />
|
||||||
|
<meta property="og:image" content={`${url}/favicon-512x512.png`} />
|
||||||
|
<meta property="og:image:type" content="image/png" />
|
||||||
|
<meta property="og:image:width" content="512" />
|
||||||
|
<meta property="og:image:height" content="512" />
|
||||||
|
<meta property="og:image:alt" content={title} />
|
||||||
|
<meta property="twitter:card" content="summary" />
|
||||||
|
<meta property="twitter:creator" content="@thedaviddelta" />
|
||||||
</Head>
|
</Head>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
@@ -41,6 +64,7 @@ const Layout: FC<Props> = ({ customTitle, children }) => (
|
|||||||
id="main"
|
id="main"
|
||||||
flexGrow={1}
|
flexGrow={1}
|
||||||
w="full"
|
w="full"
|
||||||
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</Flex>
|
</Flex>
|
||||||
|
|||||||
7
next.config.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
const withPWA = require("next-pwa");
|
||||||
|
|
||||||
|
module.exports = withPWA({
|
||||||
|
pwa: {
|
||||||
|
dest: "public"
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
"cheerio": "^1.0.0-rc.5",
|
"cheerio": "^1.0.0-rc.5",
|
||||||
"framer-motion": "^3.10.3",
|
"framer-motion": "^3.10.3",
|
||||||
"next": "10.0.8",
|
"next": "10.0.8",
|
||||||
|
"next-pwa": "^5.0.6",
|
||||||
"react": "17.0.1",
|
"react": "17.0.1",
|
||||||
"react-dom": "17.0.1",
|
"react-dom": "17.0.1",
|
||||||
"react-icons": "^4.2.0"
|
"react-icons": "^4.2.0"
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const Page: FC<InferGetStaticPropsType<typeof getStaticProps>> = ({ home, transl
|
|||||||
return statusCode ? (
|
return statusCode ? (
|
||||||
<CustomError statusCode={statusCode} />
|
<CustomError statusCode={statusCode} />
|
||||||
) : (
|
) : (
|
||||||
<Layout>
|
<Layout home={home}>
|
||||||
<VStack px={[8, null, 24, 40]} w="full">
|
<VStack px={[8, null, 24, 40]} w="full">
|
||||||
<HStack px={[1, null, 3, 4]} w="full">
|
<HStack px={[1, null, 3, 4]} w="full">
|
||||||
<LangSelect
|
<LangSelect
|
||||||
|
|||||||
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 738 B |
BIN
public/favicon-192x192.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
public/favicon-512x512.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
public/favicon-maskable.png
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/favicon.ico
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
@@ -46,9 +46,9 @@
|
|||||||
borderopacity="1.0"
|
borderopacity="1.0"
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:zoom="0.98994949"
|
inkscape:zoom="0.7"
|
||||||
inkscape:cx="389.32124"
|
inkscape:cx="341.82297"
|
||||||
inkscape:cy="409.07479"
|
inkscape:cy="499.99149"
|
||||||
inkscape:document-units="mm"
|
inkscape:document-units="mm"
|
||||||
inkscape:current-layer="g1046"
|
inkscape:current-layer="g1046"
|
||||||
inkscape:document-rotation="0"
|
inkscape:document-rotation="0"
|
||||||
@@ -101,18 +101,16 @@
|
|||||||
x="85.370766"
|
x="85.370766"
|
||||||
y="21.445843"
|
y="21.445843"
|
||||||
ry="30.319227" />
|
ry="30.319227" />
|
||||||
<text
|
<g
|
||||||
xml:space="preserve"
|
aria-label="语"
|
||||||
style="font-style:normal;font-weight:normal;font-size:60.2376px;line-height:1.25;font-family:sans-serif;fill:#e4f4ea;fill-opacity:1;stroke:none;stroke-width:1.50595"
|
transform="scale(0.98945822,1.0106541)"
|
||||||
x="101.58068"
|
|
||||||
y="87.822472"
|
|
||||||
id="text892"
|
id="text892"
|
||||||
transform="scale(0.98945822,1.0106541)"><tspan
|
style="font-style:normal;font-weight:normal;font-size:60.2376px;line-height:1.25;font-family:sans-serif;fill:#e4f4ea;fill-opacity:1;stroke:none;stroke-width:1.50595">
|
||||||
sodipodi:role="line"
|
<path
|
||||||
id="tspan890"
|
d="m 119.77244,48.005419 c -1.86737,-2.469741 -6.02376,-6.324948 -9.21636,-8.975402 l -2.8914,2.710692 c 3.19259,2.891405 7.16827,6.927324 9.03564,9.457303 z M 117.00151,82.34085 V 56.197732 h -12.52942 v 4.27687 h 8.31278 v 21.806011 c 0,2.469741 -1.26499,3.794968 -2.10831,4.397344 0.72285,0.963802 1.86736,2.891405 2.22879,3.975682 0.90356,-1.144515 2.46974,-2.349267 12.46918,-9.216353 -0.42166,-0.903564 -0.9638,-2.650454 -1.20475,-3.794969 z m 33.61258,-6.927324 v 10.78253 h -20.42055 v -10.78253 z m -24.69742,17.047241 h 4.27687 V 90.1115 h 20.42055 v 2.168554 h 4.39734 V 71.498082 h -29.09476 z m 22.70958,-38.190638 c -0.24096,2.529979 -0.54214,5.541859 -0.90357,8.192314 h -12.28847 c 0.60238,-2.409504 1.26499,-5.240671 1.9276,-8.192314 z m 3.3733,8.192314 c 0.4819,-3.855207 0.9638,-8.252551 1.20475,-12.04752 l -3.19259,-0.301188 -0.66261,0.24095 h -11.14396 c 0.54214,-2.289028 1.02404,-4.638295 1.4457,-6.746611 h 17.34843 v -4.035919 h -34.03424 v 4.035919 h 12.10776 c -0.36143,2.108316 -0.78309,4.457583 -1.32523,6.746611 h -8.55374 v 3.915444 h 7.65017 c -0.66261,2.951643 -1.32522,5.78281 -1.98784,8.192314 h -9.9392 v 4.096156 h 38.1304 v -4.096156 z"
|
||||||
x="101.58068"
|
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#e4f4ea;fill-opacity:1;stroke-width:1.50595"
|
||||||
y="87.822472"
|
id="path855" />
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:sans-serif;-inkscape-font-specification:sans-serif;fill:#e4f4ea;fill-opacity:1;stroke-width:1.50595">语</tspan></text>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g
|
<g
|
||||||
id="g1031">
|
id="g1031">
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 8.2 KiB After Width: | Height: | Size: 9.2 KiB |
28
public/manifest.json
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "Lingva Translate",
|
||||||
|
"short_name": "Lingva",
|
||||||
|
"start_url": "/",
|
||||||
|
"scope": "/",
|
||||||
|
"display": "standalone",
|
||||||
|
"lang": "en",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "/favicon-192x192.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "192x192"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/favicon-512x512.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/favicon-maskable.png",
|
||||||
|
"type": "image/png",
|
||||||
|
"sizes": "512x512",
|
||||||
|
"purpose": "maskable"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"background_color": "#bde3cb",
|
||||||
|
"theme_color": "#bde3cb"
|
||||||
|
}
|
||||||
3
public/robots.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
User-Agent: *
|
||||||
|
Disallow: /
|
||||||
|
Allow: /$
|
||||||