initial commit
|
|
@ -0,0 +1,37 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
.yarn
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# next.js
|
||||
.next/
|
||||
out/
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# turbo
|
||||
.turbo
|
||||
|
||||
# build
|
||||
dist
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
.next
|
||||
dist
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"bracketSameLine": true,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "never",
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"files.trimFinalNewlines": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"cSpell.autoFormatConfigFile": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnSaveMode": "file",
|
||||
"javascript.format.enable": true,
|
||||
"json.format.enable": true,
|
||||
"eslint.format.enable": false,
|
||||
"css.format.enable": true,
|
||||
"css.format.newlineBetweenRules": true,
|
||||
"css.format.newlineBetweenSelectors": true,
|
||||
"css.format.preserveNewLines": true,
|
||||
"typescript.format.enable": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll": true
|
||||
},
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
# Turborepo starter
|
||||
|
||||
This is an official Yarn v1 starter turborepo.
|
||||
|
||||
## What's inside?
|
||||
|
||||
This turborepo uses [Yarn](https://classic.yarnpkg.com/) as a package manager. It includes the following packages/apps:
|
||||
|
||||
### Apps and Packages
|
||||
|
||||
- `docs`: a [Next.js](https://nextjs.org/) app
|
||||
- `web`: another [Next.js](https://nextjs.org/) app
|
||||
- `ui`: a stub React component library shared by both `web` and `docs` applications
|
||||
- `eslint-config-custom`: `eslint` configurations (includes `eslint-config-next` and `eslint-config-prettier`)
|
||||
- `tsconfig`: `tsconfig.json`s used throughout the monorepo
|
||||
|
||||
Each package/app is 100% [TypeScript](https://www.typescriptlang.org/).
|
||||
|
||||
### Utilities
|
||||
|
||||
This turborepo has some additional tools already setup for you:
|
||||
|
||||
- [TypeScript](https://www.typescriptlang.org/) for static type checking
|
||||
- [ESLint](https://eslint.org/) for code linting
|
||||
- [Prettier](https://prettier.io) for code formatting
|
||||
|
||||
### Build
|
||||
|
||||
To build all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
yarn run build
|
||||
```
|
||||
|
||||
### Develop
|
||||
|
||||
To develop all apps and packages, run the following command:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
yarn run dev
|
||||
```
|
||||
|
||||
### Remote Caching
|
||||
|
||||
Turborepo can use a technique known as [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching) to share cache artifacts across machines, enabling you to share build caches with your team and CI/CD pipelines.
|
||||
|
||||
By default, Turborepo will cache locally. To enable Remote Caching you will need an account with Vercel. If you don't have an account you can [create one](https://vercel.com/signup), then enter the following commands:
|
||||
|
||||
```
|
||||
cd my-turborepo
|
||||
npx turbo login
|
||||
```
|
||||
|
||||
This will authenticate the Turborepo CLI with your [Vercel account](https://vercel.com/docs/concepts/personal-accounts/overview).
|
||||
|
||||
Next, you can link your Turborepo to your Remote Cache by running the following command from the root of your turborepo:
|
||||
|
||||
```
|
||||
npx turbo link
|
||||
```
|
||||
|
||||
## Useful Links
|
||||
|
||||
Learn more about the power of Turborepo:
|
||||
|
||||
- [Tasks](https://turbo.build/repo/docs/core-concepts/monorepos/running-tasks)
|
||||
- [Caching](https://turbo.build/repo/docs/core-concepts/caching)
|
||||
- [Remote Caching](https://turbo.build/repo/docs/core-concepts/remote-caching)
|
||||
- [Filtering](https://turbo.build/repo/docs/core-concepts/monorepos/filtering)
|
||||
- [Configuration Options](https://turbo.build/repo/docs/reference/configuration)
|
||||
- [CLI Usage](https://turbo.build/repo/docs/reference/command-line-reference)
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"presets": ["next/babel"]
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'next/core-web-vitals',
|
||||
// Put gfe-core behind so that it can reset
|
||||
// some of the eslint-plugin-react configs
|
||||
// set by eslint-config-next which we don't want.
|
||||
'@gfe/eslint-config-gfe-apps',
|
||||
],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
next-env.d.ts
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
# Internationalized Routing
|
||||
|
||||
Next.js doesn't support internationalized routing in `app` directory out of the box. But you can easily implement it yourself. This example shows how to implement internationalized routing on the Edge.
|
||||
|
||||
## Deploy your own
|
||||
|
||||
Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or preview live with [StackBlitz](https://stackblitz.com/github/vercel/next.js/tree/canary/examples/app-dir-i18n-routing)
|
||||
|
||||
[](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/app-dir-i18n-routing&project-name=app-dir-i18n-routing&repository-name=app-dir-i18n-routing)
|
||||
|
||||
## How to use
|
||||
|
||||
Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:
|
||||
|
||||
```bash
|
||||
npx create-next-app --example app-dir-i18n-routing i18n-app
|
||||
# or
|
||||
yarn create next-app --example app-dir-i18n-routing i18n-app
|
||||
# or
|
||||
pnpm create next-app --example app-dir-i18n-routing i18n-app
|
||||
```
|
||||
|
||||
Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
project_id: "572367"
|
||||
api_token: eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiJ3UUVxdmhVM3ZMT2EyWGljbVV5VCIsImp0aSI6ImJjN2QzOGYxZGY4MTg2NGE1ZWYyNjFhN2RlN2MwNzJkNmU1ZDEzZDBmMDRhZDQ5ODBmZDY0ZWIyZGQ2YWY1ZWU5ZWI4NzhkNDJlY2IyNWM3IiwiaWF0IjoxNjc4MDk1NDYyLjkwODg3NiwibmJmIjoxNjc4MDk1NDYyLjkwODg4NCwiZXhwIjoxNjgwNjgzODYyLjgwMDA2NSwic3ViIjoiMTMyNjY4NTUiLCJzY29wZXMiOlsicHJvamVjdCJdLCJkb21haW4iOm51bGwsImFzc29jaWF0aW9ucyI6WyIqIl0sInNlc3Npb24iOjB9.LUt9qwqGbYURAn4mkpRRIPKujv0-2Q93LRPm_fmlh8H6nARFqGegKKcXyOYxccSpeYWulA4BdCMFvgebmIWtU3hG7VJMymrizIpaBgEF0r_qLolVkRUFoGXCV26Dbk0YCF50dWb_6rh9jT6kSh48kKYkfh0Acb8Helm_q3tQwH9XHhLvCC6HXucXwUOnzr6DAuH3j7GRzciBxUfuPNu4s7NoqwV4G5gg4U0s4niso83_JM5JlUKCWgv4youmVqm3FCLoXNSu9cibb5AnUtV80XHtW6OSJbmb4moFIx5djS3iE6UEoZ8MPWsFitserZAcqj_kL_aRClJ-gM-dd4KHW85aoV_cyzYDJFzcAwRw26N4NacLjHkfi9-VJumBpkujweJj-LiwTxeMYeJ8j5DKfjgaTfZ-hMHCHdSPi-5EeScawfGW0keL3cuhr1-b_6MGOjvvuaP59lJ6bESOM4ZINhr0cYV5QLB4hKjrMAdgTkal9HspA37avW0mgUed0N1cr4VUpusDQHQZ0uP-Ltfcz4r65BOn330L4h7xixiNKwYQehidsO5fw_j_w8w32HAIwcMkVNyE0b3TelIIiKfBWFa6GvYu3n-1WnFKo8te0Ys8VODSe-_UbsmKuag0bZ94FnNpR6ptchgJuleaCVqomUN1pn2D-fseNwlhYv89djQ
|
||||
base_path: .
|
||||
base_url: https://api.crowdin.com
|
||||
preserve_hierarchy: true
|
||||
files:
|
||||
# JSON translation files
|
||||
- source: /src/lang/en.json
|
||||
translation: /src/lang/%locale%.json
|
||||
# Docs Markdown files
|
||||
- source: /src/**/en.mdx
|
||||
translation: /%original_path%/%locale%.mdx
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/* eslint-disable spaced-comment */
|
||||
/// <reference types="next-i18nostic/config" />
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
const config = {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en', 'de', 'zh'],
|
||||
localeHrefLangs: {
|
||||
en: 'en-US',
|
||||
de: 'de-DE',
|
||||
zh: 'zh-CN',
|
||||
},
|
||||
trailingSlash: false,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
// @ts-check
|
||||
|
||||
import nextI18nostic from './nextI18nostic.mjs';
|
||||
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
**/
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
appDir: true,
|
||||
serverComponentsExternalPackages: ['mdx-bundler'],
|
||||
},
|
||||
transpilePackages: ['next-i18nostic'],
|
||||
pageExtensions: ['ts', 'tsx'],
|
||||
};
|
||||
|
||||
export default nextI18nostic()(nextConfig);
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
// import { NextConfig, Metadata } from "next";
|
||||
|
||||
// export type NextI18nosticHrefLang = keyof NonNullable<
|
||||
// NonNullable<Metadata["alternates"]>["languages"]
|
||||
// >;
|
||||
|
||||
// export type WithI18nostic = (config: NextConfig) => NextConfig;
|
||||
|
||||
export default function nextI18nostic() {
|
||||
return (nextConfig) => {
|
||||
return Object.assign({}, nextConfig, {
|
||||
// TODO: Derive from NextJsWebpackConfig.
|
||||
webpack(config, options) {
|
||||
config.resolve.alias['next-i18nostic/config'] = [
|
||||
'private-next-root-dir/next-i18nostic.config',
|
||||
];
|
||||
|
||||
if (typeof nextConfig.webpack === 'function') {
|
||||
return nextConfig.webpack(config, options);
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "i18n-example",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"tsc": "tsc",
|
||||
"i18n:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/lang/en.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"esbuild": "^0.17.11",
|
||||
"mdx-bundler": "^9.2.1",
|
||||
"next": "13.2.3",
|
||||
"next-i18nostic": "*",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intl": "^6.2.8",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"server-only": "0.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gfe/eslint-config-gfe-apps": "*",
|
||||
"@formatjs/cli": "^6.0.1",
|
||||
"@types/node": "^18.11.5",
|
||||
"@types/react": "^18.0.23",
|
||||
"@types/react-dom": "^18.0.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"eslint": "8.34.0",
|
||||
"eslint-config-next": "13.2.3",
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 9.6 KiB |
|
|
@ -0,0 +1,26 @@
|
|||
'use client';
|
||||
|
||||
import { I18nProvider } from 'next-i18nostic';
|
||||
import nextI18nosticConfig from 'next-i18nostic/config';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
|
||||
import type { IntlMessages } from '~/intl';
|
||||
|
||||
type Props = Readonly<{
|
||||
children: React.ReactNode;
|
||||
intlMessages: IntlMessages;
|
||||
locale: string;
|
||||
}>;
|
||||
|
||||
export function Providers({ children, intlMessages, locale }: Props) {
|
||||
return (
|
||||
<I18nProvider locale={locale}>
|
||||
<IntlProvider
|
||||
defaultLocale={nextI18nosticConfig.defaultLocale}
|
||||
locale={locale}
|
||||
messages={intlMessages}>
|
||||
{children}
|
||||
</IntlProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
'use client';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import Counter from './components/Counter';
|
||||
|
||||
export default function IndexPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
defaultMessage="Welcome"
|
||||
description="Welcome message"
|
||||
id="iyaPkC"
|
||||
/>
|
||||
</h1>
|
||||
<Counter />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default function Counter() {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button type="button" onClick={() => setCount((n) => n - 1)}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Decrement"
|
||||
description="Decrease"
|
||||
id="ADeXOi"
|
||||
/>
|
||||
</button>{' '}
|
||||
{count}{' '}
|
||||
<button type="button" onClick={() => setCount((n) => n + 1)}>
|
||||
<FormattedMessage
|
||||
defaultMessage="Increment"
|
||||
description="Increase"
|
||||
id="MCH3lP"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
'use client';
|
||||
|
||||
import { I18nLink, useI18nPathname } from 'next-i18nostic';
|
||||
import nextI18nosticConfig from 'next-i18nostic/config';
|
||||
|
||||
export default function LocaleSwitcher() {
|
||||
const { pathname } = useI18nPathname();
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex' }}>
|
||||
<p>Language:</p>
|
||||
<ul style={{ columnGap: 10, display: 'flex', listStyleType: 'none' }}>
|
||||
{nextI18nosticConfig.locales.map((locale) => (
|
||||
<li key={locale}>
|
||||
<I18nLink href={pathname!} locale={locale}>
|
||||
{locale}
|
||||
</I18nLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
'use client';
|
||||
|
||||
import { I18nLink } from 'next-i18nostic';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default function Navbar() {
|
||||
return (
|
||||
<nav style={{ display: 'flex' }}>
|
||||
<ul
|
||||
style={{
|
||||
columnGap: 10,
|
||||
display: 'flex',
|
||||
listStyleType: 'none',
|
||||
paddingLeft: 0,
|
||||
}}>
|
||||
<li>
|
||||
<I18nLink href="/">
|
||||
<FormattedMessage
|
||||
defaultMessage="Home"
|
||||
description="Link to home page"
|
||||
id="OmkkJd"
|
||||
/>
|
||||
</I18nLink>
|
||||
</li>
|
||||
<li>
|
||||
<I18nLink href="/profile">
|
||||
<FormattedMessage
|
||||
defaultMessage="Profile"
|
||||
description="Profile page title"
|
||||
id="81i+95"
|
||||
/>
|
||||
</I18nLink>
|
||||
</li>
|
||||
<li>
|
||||
<I18nLink href="/docs">
|
||||
<FormattedMessage
|
||||
defaultMessage="Docs"
|
||||
description="Docs page"
|
||||
id="/x1uyJ"
|
||||
/>
|
||||
</I18nLink>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Overview
|
||||
|
||||
[De] Lorem ipsum dolor sit amet.
|
||||
|
||||
| Tabellen | Ar | Kalt |
|
||||
| -------- | :-----------: | ----: |
|
||||
| col 1 is | left-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| col 3 is | right-aligned | $1 |
|
||||
|
||||
The translated content should be downloaded in `i18n/fr`.
|
||||
|
||||
```jsx
|
||||
Hello World
|
||||
```
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Overview
|
||||
|
||||
Lorem ipsum dolor sit amet.
|
||||
|
||||
| Tables | Are | Cool |
|
||||
| -------- | :-----------: | ----: |
|
||||
| col 1 is | left-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| col 3 is | right-aligned | $1 |
|
||||
|
||||
The translated content should be downloaded in `i18n/fr`.
|
||||
|
||||
```jsx
|
||||
Hello World
|
||||
```
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
import { bundleMDX } from 'mdx-bundler';
|
||||
import { getMDXComponent } from 'mdx-bundler/client';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
export default async function Page({
|
||||
params: { locale },
|
||||
}: Readonly<{ params: { locale: string } }>) {
|
||||
const mdxSource = fs
|
||||
.readFileSync(
|
||||
path.join(fileURLToPath(path.dirname(import.meta.url)), `${locale}.mdx`),
|
||||
)
|
||||
.toString();
|
||||
|
||||
const { code } = await bundleMDX({
|
||||
mdxOptions(options) {
|
||||
options.remarkPlugins = [...(options.remarkPlugins ?? []), remarkGfm];
|
||||
|
||||
return options;
|
||||
},
|
||||
source: mdxSource,
|
||||
});
|
||||
|
||||
const Markdown = getMDXComponent(code);
|
||||
|
||||
return <Markdown />;
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
# Overview
|
||||
|
||||
[Zh] Lorem ipsum dolor sit amet.
|
||||
|
||||
| 表 | Are | 冷色 |
|
||||
| -------- | :-----------: | ----: |
|
||||
| col 1 is | left-aligned | $1600 |
|
||||
| col 2 is | centered | $12 |
|
||||
| col 3 is | right-aligned | $1 |
|
||||
|
||||
The translated content should be downloaded in `i18n/fr`.
|
||||
|
||||
```jsx
|
||||
Hello World
|
||||
```
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { i18nMetadata } from 'next-i18nostic';
|
||||
import nextI18nosticConfig from 'next-i18nostic/config';
|
||||
|
||||
import { getIntlServerOnly, getLocaleMessages } from '~/intl';
|
||||
|
||||
import LocaleSwitcher from './components/LocaleSwitcher';
|
||||
import Navbar from './components/Navbar';
|
||||
import { Providers } from '../Providers';
|
||||
|
||||
import '~/styles/globals.css';
|
||||
|
||||
export async function generateStaticParams() {
|
||||
return nextI18nosticConfig.locales.map((locale) => ({ locale }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params: { locale },
|
||||
}: Props): Promise<Metadata> {
|
||||
const intl = await getIntlServerOnly(locale);
|
||||
|
||||
return i18nMetadata({
|
||||
alternates: {
|
||||
canonical: '/',
|
||||
},
|
||||
title: intl.formatMessage({
|
||||
defaultMessage: 'Home',
|
||||
description: 'Link to home page',
|
||||
id: 'OmkkJd',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
type Props = Readonly<{
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}>;
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params: { locale },
|
||||
}: Props) {
|
||||
const localeMessages = await getLocaleMessages(locale);
|
||||
|
||||
return (
|
||||
<html lang={locale}>
|
||||
<body>
|
||||
<Providers intlMessages={localeMessages} locale={locale}>
|
||||
<div style={{ margin: '0 auto', maxWidth: 600 }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
}}>
|
||||
<Navbar />
|
||||
<LocaleSwitcher />
|
||||
</div>
|
||||
<p>Current locale: {locale}</p>
|
||||
<hr />
|
||||
{children}
|
||||
</div>
|
||||
</Providers>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { i18nMetadata } from 'next-i18nostic';
|
||||
|
||||
import { getIntlServerOnly } from '~/intl';
|
||||
|
||||
import IndexPage from './IndexPage';
|
||||
|
||||
export async function generateMetadata({
|
||||
params: { locale },
|
||||
}: Readonly<{ params: { locale: string } }>): Promise<Metadata> {
|
||||
const intl = await getIntlServerOnly(locale);
|
||||
|
||||
return i18nMetadata({
|
||||
alternates: {
|
||||
canonical: '/',
|
||||
},
|
||||
title: intl.formatMessage({
|
||||
defaultMessage: 'Welcome',
|
||||
description: 'Welcome message',
|
||||
id: 'iyaPkC',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export default function Page() {
|
||||
return <IndexPage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
'use client';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
export default function ProfilePage() {
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
<FormattedMessage
|
||||
defaultMessage="Profile Contents"
|
||||
description="Profile page contents"
|
||||
id="v0BzAj"
|
||||
/>
|
||||
</h1>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
defaultMessage="Body content"
|
||||
description="Some content"
|
||||
id="SMjxq8"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
import type { Metadata } from 'next';
|
||||
import { i18nMetadata } from 'next-i18nostic';
|
||||
|
||||
import { getIntlServerOnly } from '~/intl';
|
||||
|
||||
import ProfilePage from './ProfilePage';
|
||||
|
||||
export async function generateMetadata({
|
||||
params: { locale },
|
||||
}: Readonly<{ params: { locale: string } }>): Promise<Metadata> {
|
||||
const intl = await getIntlServerOnly(locale);
|
||||
|
||||
return i18nMetadata({
|
||||
alternates: {
|
||||
canonical: '/profile',
|
||||
},
|
||||
title: intl.formatMessage({
|
||||
defaultMessage: 'Profile',
|
||||
description: 'Profile page title',
|
||||
id: '81i+95',
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
export default async function Page() {
|
||||
return <ProfilePage />;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import type { ResolvedIntlConfig } from 'react-intl';
|
||||
|
||||
import { createIntl,createIntlCache } from '@formatjs/intl';
|
||||
|
||||
export type IntlMessages = ResolvedIntlConfig['messages'];
|
||||
|
||||
// We enumerate all dictionaries here for better linting and typescript support
|
||||
// We also get the default import for cleaner types
|
||||
const locales = {
|
||||
de: () => import('../lang/de.json').then((module) => module.default),
|
||||
en: () => import('../lang/en.json').then((module) => module.default),
|
||||
zh: () => import('../lang/zh.json').then((module) => module.default),
|
||||
};
|
||||
|
||||
export async function getLocaleMessages(locale: string): Promise<IntlMessages> {
|
||||
const i18nStrings = await locales[locale as keyof typeof locales]();
|
||||
const strings: IntlMessages = {};
|
||||
|
||||
Object.entries(i18nStrings).map(([messageId, value]) => {
|
||||
strings[messageId] = value.defaultMessage;
|
||||
});
|
||||
|
||||
return strings;
|
||||
}
|
||||
|
||||
export async function getIntlServerOnly(locale: string) {
|
||||
const cache = createIntlCache();
|
||||
const messages = await getLocaleMessages(locale);
|
||||
|
||||
return createIntl(
|
||||
{
|
||||
locale,
|
||||
messages,
|
||||
},
|
||||
cache,
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"/x1uyJ": {
|
||||
"defaultMessage": "Dokumentation",
|
||||
"description": "Docs page"
|
||||
},
|
||||
"81i+95": {
|
||||
"defaultMessage": "Profil",
|
||||
"description": "Profile page title"
|
||||
},
|
||||
"ADeXOi": {
|
||||
"defaultMessage": "Reduzieren",
|
||||
"description": "Decrease"
|
||||
},
|
||||
"MCH3lP": {
|
||||
"defaultMessage": "Schrittweite",
|
||||
"description": "Increase"
|
||||
},
|
||||
"OmkkJd": {
|
||||
"defaultMessage": "Zurück",
|
||||
"description": "Link to home page"
|
||||
},
|
||||
"SMjxq8": {
|
||||
"defaultMessage": "Body Inhalt",
|
||||
"description": "Some content"
|
||||
},
|
||||
"iyaPkC": {
|
||||
"defaultMessage": "Willkommen",
|
||||
"description": "Welcome message"
|
||||
},
|
||||
"v0BzAj": {
|
||||
"defaultMessage": "Schulprofil Inhalt",
|
||||
"description": "Profile page contents"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"/x1uyJ": {
|
||||
"defaultMessage": "Docs",
|
||||
"description": "Docs page"
|
||||
},
|
||||
"81i+95": {
|
||||
"defaultMessage": "Profile",
|
||||
"description": "Profile page title"
|
||||
},
|
||||
"ADeXOi": {
|
||||
"defaultMessage": "Decrement",
|
||||
"description": "Decrease"
|
||||
},
|
||||
"MCH3lP": {
|
||||
"defaultMessage": "Increment",
|
||||
"description": "Increase"
|
||||
},
|
||||
"OmkkJd": {
|
||||
"defaultMessage": "Home",
|
||||
"description": "Link to home page"
|
||||
},
|
||||
"SMjxq8": {
|
||||
"defaultMessage": "Body content",
|
||||
"description": "Some content"
|
||||
},
|
||||
"iyaPkC": {
|
||||
"defaultMessage": "Welcome",
|
||||
"description": "Welcome message"
|
||||
},
|
||||
"v0BzAj": {
|
||||
"defaultMessage": "Profile Contents",
|
||||
"description": "Profile page contents"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"/x1uyJ": {
|
||||
"defaultMessage": "文件",
|
||||
"description": "Docs page"
|
||||
},
|
||||
"81i+95": {
|
||||
"defaultMessage": "资料",
|
||||
"description": "Profile page title"
|
||||
},
|
||||
"ADeXOi": {
|
||||
"defaultMessage": "递减",
|
||||
"description": "Decrease"
|
||||
},
|
||||
"MCH3lP": {
|
||||
"defaultMessage": "递增",
|
||||
"description": "Increase"
|
||||
},
|
||||
"OmkkJd": {
|
||||
"defaultMessage": "主页",
|
||||
"description": "Link to home page"
|
||||
},
|
||||
"SMjxq8": {
|
||||
"defaultMessage": "正文内容",
|
||||
"description": "Some content"
|
||||
},
|
||||
"iyaPkC": {
|
||||
"defaultMessage": "欢迎",
|
||||
"description": "Welcome message"
|
||||
},
|
||||
"v0BzAj": {
|
||||
"defaultMessage": "个人资料内容",
|
||||
"description": "Profile page contents"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export function useMDXComponents(components) {
|
||||
return components;
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import type { NextRequest } from 'next/server';
|
||||
import { NextResponse } from 'next/server';
|
||||
import { i18nMiddleware } from 'next-i18nostic';
|
||||
|
||||
export function middleware(req: NextRequest) {
|
||||
// // `/_next/` and `/api/` are ignored by the watcher, but we need to ignore files in `public` manually.
|
||||
// // If you have one
|
||||
// if (
|
||||
// [
|
||||
// '/manifest.json',
|
||||
// '/favicon.ico',
|
||||
// // Your other files in `public`
|
||||
// ].includes(pathname)
|
||||
// )
|
||||
// return
|
||||
|
||||
return i18nMiddleware(req) ?? NextResponse.next();
|
||||
}
|
||||
|
||||
export const config = {
|
||||
// Matcher ignoring `/_next/` and `/api/`
|
||||
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
||||
};
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
html {
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"extends": "@gfe/tsconfig/nextjs.json",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"~/*": ["*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
".next/types/**/*.ts",
|
||||
"next-i18nostic-env.d.ts",
|
||||
"src/mdx-components.jsx"
|
||||
],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"presets": [
|
||||
"next/babel"
|
||||
],
|
||||
"plugins": []
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
# Update these with your Supabase details from your project settings > API
|
||||
SUPABASE_SERVICE_ROLE_KEY=
|
||||
NEXT_PUBLIC_SUPABASE_URL=https://SUPABASE_REFERENCE_ID.supabase.co
|
||||
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY=pk_something
|
||||
STRIPE_SECRET_KEY=sk_something
|
||||
STRIPE_SIGNING_SECRET=whsec_something
|
||||
STRIPE_MAIN_PRODUCT_ID=prod_MCPDYV6lv5QS5z
|
||||
API_ROUTE_SECRET=some_generated_hash
|
||||
CLIENT_URL=http://localhost:3000
|
||||
DATABASE_URL=postgresql://postgres:[YOUR-PASSWORD]@db.[SUPABASE_REFERENCE_ID].supabase.co:5432/postgres
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
extends: [
|
||||
'next/core-web-vitals',
|
||||
// Put gfe-core behind so that it can reset
|
||||
// some of the eslint-plugin-react configs
|
||||
// set by eslint-config-next which we don't want.
|
||||
'@gfe/eslint-config-gfe-apps',
|
||||
],
|
||||
ignorePatterns: ['src/supabase/database.types.ts'],
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
.yarn/*
|
||||
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# next.js
|
||||
/.next/
|
||||
/out/
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
*.pem
|
||||
|
||||
# debug
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# local env files
|
||||
.env
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
tsconfig.tsbuildinfo
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
src/__generated__
|
||||
src/supabase/database.types.ts
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
## Getting Started
|
||||
|
||||
First, run the development server:
|
||||
|
||||
```bash
|
||||
yarn dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||
|
||||
You can start editing the page by modifying `pages/index.js`. The page auto-updates as you edit the file.
|
||||
|
||||
[API routes](https://nextjs.org/docs/api-routes/introduction) can be accessed on [http://localhost:3000/api/hello](http://localhost:3000/api/hello). This endpoint can be edited in `pages/api/hello.js`.
|
||||
|
||||
The `pages/api` directory is mapped to `/api/*`. Files in this directory are treated as [API routes](https://nextjs.org/docs/api-routes/introduction) instead of React pages.
|
||||
|
||||
## Learn More
|
||||
|
||||
To learn more about Next.js, take a look at the following resources:
|
||||
|
||||
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
|
||||
- [Learn Next.js](https://nextjs.org/learn/foundations/about-nextjs) - an interactive Next.js tutorial.
|
||||
|
||||
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
|
||||
|
||||
## Deploy on Vercel
|
||||
|
||||
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_source=github.com&utm_medium=referral&utm_campaign=turborepo-readme) from the creators of Next.js.
|
||||
|
||||
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
# i18n
|
||||
|
||||
## Setting Up
|
||||
|
||||
We use AST Explorer as a tool to help convert the strings in the TypeScript files with `react-intl` APIs.
|
||||
|
||||
1. Go to <https://astexplorer.net>.
|
||||
1. Select "typescript" for both the parser option and the transform option.
|
||||
1. Find the most updated version of the codemod in `~/transforms/intlify.ts`.
|
||||
1. Copy and paste the code into the bottom left window of <https://astexplorer.net>.
|
||||
1. Replace the top left window with your contents.
|
||||
|
||||
The codemod handles the following basic cases:
|
||||
|
||||
1. Object `StringLiteral` values used within functions, which are mostly React components/hooks.
|
||||
|
||||
```diff
|
||||
function Foo() {
|
||||
const items = {
|
||||
- key: 'Some text',
|
||||
+ key: intl.formatMessage({
|
||||
+ defaultMessage: 'Some text'
|
||||
+ }),
|
||||
};
|
||||
|
||||
return <div>{items.key}</div>;
|
||||
}
|
||||
```
|
||||
|
||||
2. Plain `JSXText` nodes.
|
||||
|
||||
```diff
|
||||
function Foo() {
|
||||
- return <div>Some text</div>;
|
||||
+ return <div><Format defaultMessage="Some text" /></div>;
|
||||
}
|
||||
```
|
||||
|
||||
3. JSX attributes which are `StringLiteral`s.
|
||||
|
||||
```diff
|
||||
function Foo() {
|
||||
- return <div label="Some text" />;
|
||||
+ return <div label={intl.formatMessage({ defaultMessage: "Some text" })} />;
|
||||
}
|
||||
```
|
||||
|
||||
## Converting Strings Not Within Functions
|
||||
|
||||
Because strings need to read from React context, strings have to be wrapped within functions.
|
||||
|
||||
```jsx
|
||||
const tabs = [
|
||||
{
|
||||
label: 'JavaScript',
|
||||
value: 'javascript',
|
||||
},
|
||||
{
|
||||
label: 'System Design',
|
||||
value: 'system-design',
|
||||
},
|
||||
];
|
||||
|
||||
function Page() {
|
||||
return (
|
||||
<ul>
|
||||
{tabs.map((tab) => (
|
||||
<li key={tab.value}>{tab.label}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Wrap in React Hooks (preferred)
|
||||
|
||||
Wrap a function around the object and return it. The function has to start with `use`.
|
||||
|
||||
```jsx
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
function useTabs() {
|
||||
// Now that it's in a React hook, you can use intl.formatMessage
|
||||
const intl = useIntl();
|
||||
return [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
/* ... */
|
||||
}),
|
||||
value: 'javascript',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
/* ... */
|
||||
}),
|
||||
value: 'system-design',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function Page() {
|
||||
const tabs = useTabs(); // Obtain via the hook.
|
||||
return (
|
||||
<ul>
|
||||
{tabs.map((tab) => (
|
||||
<li key={tab.value}>{tab.label}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Shift into React Component
|
||||
|
||||
You can shift the contents into a React component as well, but this makes the translated less reusable and cannot be used in other components.
|
||||
|
||||
```jsx
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
function Page() {
|
||||
const intl = useIntl();
|
||||
const tabs [
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
/* ... */
|
||||
}),
|
||||
value: 'javascript',
|
||||
},
|
||||
{
|
||||
label: intl.formatMessage({
|
||||
/* ... */
|
||||
}),
|
||||
value: 'system-design',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ul>
|
||||
{tabs.map((tab) => (
|
||||
<li key={tab.value}>{tab.label}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Rich Text Formatting
|
||||
|
||||
If a string contains some markup, use the [Rich Text Formatting](https://formatjs.io/docs/react-intl/components#rich-text-formatting) syntax.
|
||||
|
||||
```jsx
|
||||
// Before
|
||||
<Heading>
|
||||
All the essentials for front end{' '}
|
||||
<span className="font-bold">interviews and more</span>
|
||||
</Heading>
|
||||
```
|
||||
|
||||
```jsx
|
||||
// After
|
||||
<Heading>
|
||||
<FormattedMessage
|
||||
defaultMessage="All the essentials for front end <span>interviews and more</span>"
|
||||
values={{
|
||||
span: chunks => <span className="font-bold">{chunks}<span>
|
||||
}}
|
||||
/>
|
||||
</Heading>
|
||||
```
|
||||
|
||||
### Nested Rich Text Formatting
|
||||
|
||||
Rich text can also be nested like typical HTML tags. Just pass the children into the mapped elements and it'll be fine.
|
||||
|
||||
```jsx
|
||||
// Before
|
||||
<Heading>
|
||||
<span>All the essentials for front end</span>{' '}
|
||||
<span className="inline-block">
|
||||
interviews <span className="font-bold">and more</span>
|
||||
</span>
|
||||
</Heading>
|
||||
```
|
||||
|
||||
```jsx
|
||||
// After
|
||||
<Heading>
|
||||
<FormattedMessage
|
||||
defaultMessage="All the essentials for front end <span>interviews <bold>and more</bold></span>"
|
||||
description="foo"
|
||||
id="QULf19"
|
||||
values={{
|
||||
span: (chunks) => <span className="inline-block">{chunks}</span>,
|
||||
bold: (chunks) => <span className="font-bold">{chunks}</span>,
|
||||
}}
|
||||
/>
|
||||
</Heading>
|
||||
```
|
||||
|
||||
### Strings which Contain Unicode
|
||||
|
||||
Unicode within JSX text are mostly for decorative purposes, such as `&arr;`, `·`. Regardless, they shouldn't be translated and have to remain within JSX otherwise they will not be converted by the browser into a symbol.
|
||||
|
||||
### Take them out of the translatable content
|
||||
|
||||
```jsx
|
||||
// Before
|
||||
<p>Front End Engineer · Web Developer · Full Stack Engineer</p>
|
||||
|
||||
// After
|
||||
<p><span>Front End Engineer</span> · <span>Web Developer</span> · <span>Full Stack Engineer</span></p>
|
||||
// Then transform this instead.
|
||||
```
|
||||
|
||||
```jsx
|
||||
// Before
|
||||
<button>Click here now →</button>
|
||||
|
||||
// After
|
||||
<button><FormattedMessage defaultMessage="Click here now"> →</button>
|
||||
```
|
||||
|
||||
#### Use Rich Text
|
||||
|
||||
We can use the rich text syntax as well and leave the contents to be empty.
|
||||
|
||||
```jsx
|
||||
// Before
|
||||
<button>Click here now →</button>
|
||||
|
||||
// After
|
||||
<button>
|
||||
<FormattedMessage
|
||||
defaultMessage="Click here now <arrow></arrow>"
|
||||
values={{arrow: () => <span>→</span>}}
|
||||
/>
|
||||
</button>
|
||||
```
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import MDXComponents from './src/components/mdx/MDXComponents';
|
||||
|
||||
export function useMDXComponents() {
|
||||
return MDXComponents;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
/// <reference types="next/navigation-types/compat/navigation" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
/* eslint-disable spaced-comment */
|
||||
/// <reference types="next-i18nostic/config" />
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
const config = {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
localeHrefLangs: {
|
||||
en: 'en-US',
|
||||
},
|
||||
trailingSlash: false,
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
// @ts-check
|
||||
|
||||
import codingQuestionsList from './src/__generated__/questions/CodingQuestionsList.json' assert { type: 'json' };
|
||||
|
||||
const priority = 0.7;
|
||||
const changefreq = 'daily';
|
||||
|
||||
/** @type {import('next-sitemap').IConfig} */
|
||||
export default {
|
||||
siteUrl: process.env.SITE_URL || 'https://www.greatfrontend.com',
|
||||
exclude: [
|
||||
'/dev__/*',
|
||||
'/logout',
|
||||
'/password/reset',
|
||||
'/payment/success',
|
||||
'/profile',
|
||||
'/profile/*',
|
||||
],
|
||||
priority,
|
||||
changefreq,
|
||||
sitemapSize: 5000,
|
||||
generateIndexSitemap: false,
|
||||
additionalPaths: async (config) =>
|
||||
await Promise.all(
|
||||
codingQuestionsList.map(
|
||||
async ({ href }) => await config.transform(config, href),
|
||||
),
|
||||
),
|
||||
};
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// @ts-check
|
||||
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
import remarkMdxFrontmatter from 'remark-mdx-frontmatter';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkSlug from 'remark-slug';
|
||||
import remarkExtractToc from '@stefanprobst/remark-extract-toc';
|
||||
import remarkExtractTocExport from '@stefanprobst/remark-extract-toc/mdx';
|
||||
import nextMDX from '@next/mdx';
|
||||
|
||||
import nextI18nostic from './nextI18nostic.mjs';
|
||||
|
||||
const withMDX = nextMDX({
|
||||
extension: /\.mdx?$/,
|
||||
options: {
|
||||
format: 'mdx',
|
||||
remarkPlugins: [
|
||||
remarkGfm,
|
||||
remarkFrontmatter,
|
||||
remarkMdxFrontmatter,
|
||||
remarkSlug,
|
||||
remarkExtractToc,
|
||||
remarkExtractTocExport, // Adds toc as exported const for MDX files.
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const withNextI18nostic = nextI18nostic();
|
||||
|
||||
/**
|
||||
* @type {import('next').NextConfig}
|
||||
**/
|
||||
const nextConfig = {
|
||||
pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx'],
|
||||
reactStrictMode: true,
|
||||
experimental: {
|
||||
appDir: true,
|
||||
serverComponentsExternalPackages: ['mdx-bundler'],
|
||||
},
|
||||
transpilePackages: ['next-i18nostic'],
|
||||
async redirects() {
|
||||
return [
|
||||
{
|
||||
source: '/questions/javascript',
|
||||
destination: '/questions/js',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/coding',
|
||||
destination: '/prepare/coding',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/quiz',
|
||||
destination: '/prepare/quiz',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/system-design',
|
||||
destination: '/prepare/system-design',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/quiz/css/:path*',
|
||||
destination: '/questions/quiz/:path*',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/quiz/html/:path*',
|
||||
destination: '/questions/quiz/:path*',
|
||||
permanent: false,
|
||||
},
|
||||
{
|
||||
source: '/questions/quiz/javascript/:path*',
|
||||
destination: '/questions/quiz/:path*',
|
||||
permanent: false,
|
||||
},
|
||||
];
|
||||
},
|
||||
};
|
||||
|
||||
export default withNextI18nostic(withMDX(nextConfig));
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
export default function nextI18nostic() {
|
||||
return (nextConfig) => {
|
||||
return Object.assign({}, nextConfig, {
|
||||
// TODO: Derive from NextJsWebpackConfig.
|
||||
webpack(config, options) {
|
||||
config.resolve.alias['next-i18nostic/config'] = [
|
||||
'private-next-root-dir/next-i18nostic.config',
|
||||
];
|
||||
|
||||
if (typeof nextConfig.webpack === 'function') {
|
||||
return nextConfig.webpack(config, options);
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
{
|
||||
"name": "greatfrontend",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"ci": "yarn tsc && next lint",
|
||||
"dev": "next dev",
|
||||
"dev:gen": "concurrently -n gfe,next -c green,blue -k \"yarn gen:watch\" \"next dev\"",
|
||||
"build": "yarn gen:all && next build",
|
||||
"sitemap": "next-sitemap",
|
||||
"start": "yarn gen:all && next start",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "eslint src questions --fix",
|
||||
"test": "jest --env=jsdom",
|
||||
"tsc": "tsc",
|
||||
"gen": "NODE_ENV=production ts-node --esm --experimentalSpecifierResolution node src/scripts/questions-gen-cli.ts",
|
||||
"gen:watch": "yarn gen:all && ts-node --esm --experimentalSpecifierResolution node src/scripts/questions-watcher.ts",
|
||||
"gen:all": "yarn gen:clean && yarn gen",
|
||||
"gen:clean": "rm -rf src/__generated__",
|
||||
"postinstall": "prisma generate",
|
||||
"i18n:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file src/locales/en.json",
|
||||
"ppp": "ts-node --esm --experimentalSpecifierResolution node ./src/scripts/ppp-update.ts",
|
||||
"supabase": "supabase gen types typescript --project-id vaqybtnqyonvlwtskzmv --schema public > src/supabase/database.types.ts",
|
||||
"intl:js": "ts-node --esm --experimentalSpecifierResolution node ./src/scripts/questions-javascript-intl.ts",
|
||||
"prisma": "prisma"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codesandbox/sandpack-react": "^1.20.9",
|
||||
"@headlessui/react": "^1.7.11",
|
||||
"@heroicons/react": "2.0.13",
|
||||
"@mdx-js/react": "^2.3.0",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@next/mdx": "^13.2.3",
|
||||
"@prisma/client": "^4.11.0",
|
||||
"@stefanprobst/remark-extract-toc": "^2.2.0",
|
||||
"@stripe/stripe-js": "^1.46.0",
|
||||
"@supabase/auth-helpers-nextjs": "^0.5.4",
|
||||
"@supabase/auth-helpers-react": "^0.3.1",
|
||||
"@supabase/supabase-js": "^2.10.0",
|
||||
"@tanstack/react-query": "^4.2.3",
|
||||
"@vercel/analytics": "^0.1.10",
|
||||
"axios": "^0.27.2",
|
||||
"clsx": "^1.2.1",
|
||||
"console-feed": "3.3.0",
|
||||
"cookie": "^0.5.0",
|
||||
"copy-text-to-clipboard": "^3.0.1",
|
||||
"cors": "^2.8.5",
|
||||
"esbuild": "^0.17.8",
|
||||
"framer-motion": "^9.0.4",
|
||||
"gray-matter": "^4.0.3",
|
||||
"js-cookie": "^3.0.1",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mdx-bundler": "^9.2.1",
|
||||
"micro": "^9.4.1",
|
||||
"mitt": "^3.0.0",
|
||||
"monaco-themes": "^0.4.3",
|
||||
"next": "13.2.3",
|
||||
"next-absolute-url": "^1.2.2",
|
||||
"next-i18nostic": "*",
|
||||
"next-seo": "^5.15.0",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"radix-ui": "^1.0.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-intl": "^6.2.8",
|
||||
"remark-frontmatter": "^4.0.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-mdx-frontmatter": "^2.1.1",
|
||||
"remark-slug": "^7.0.1",
|
||||
"server-only": "^0.0.1",
|
||||
"stripe": "^11.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@formatjs/cli": "^6.0.1",
|
||||
"@gfe/eslint-config-gfe-apps": "*",
|
||||
"@mdx-js/loader": "^2.3.0",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/cors": "^2.8.13",
|
||||
"@types/glob": "^8.0.1",
|
||||
"@types/gtag.js": "^0.0.12",
|
||||
"@types/jest": "^29.4.1",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"@types/lodash-es": "^4.17.6",
|
||||
"@types/mdx": "^2.0.3",
|
||||
"@types/node": "^18.14.0",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/react": "18.0.28",
|
||||
"@types/react-dom": "18.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.53.0",
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"chalk": "^5.2.0",
|
||||
"chokidar": "^3.5.3",
|
||||
"concurrently": "^7.6.0",
|
||||
"encoding": "^0.1.13",
|
||||
"eslint": "8.34.0",
|
||||
"eslint-config-next": "13.2.3",
|
||||
"glob": "^8.1.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next-sitemap": "^3.1.52",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.4",
|
||||
"prettier-plugin-tailwindcss": "^0.2.3",
|
||||
"prisma": "^4.11.0",
|
||||
"slugify": "^1.6.5",
|
||||
"supabase": "^1.41.1",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
-- Manually added, so that uuid_generate_v4() can be used.
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "EmailSubscriber" (
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"email" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "EmailSubscribers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Event" (
|
||||
"id" BIGSERIAL NOT NULL,
|
||||
"userId" UUID NOT NULL,
|
||||
"action" TEXT NOT NULL,
|
||||
"payload" JSON,
|
||||
"createdAt" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"referer" VARCHAR,
|
||||
"country" TEXT,
|
||||
|
||||
CONSTRAINT "Event_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "FeedbackMessage" (
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"message" VARCHAR NOT NULL,
|
||||
"email" TEXT,
|
||||
"createdAt" TIMESTAMP(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"resolved" BOOLEAN NOT NULL DEFAULT false,
|
||||
"owner" TEXT DEFAULT 'yangshun',
|
||||
"userEmail" TEXT,
|
||||
"comments" VARCHAR,
|
||||
"metadata" JSON,
|
||||
|
||||
CONSTRAINT "FeedbackMessage_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Profile" (
|
||||
"id" UUID NOT NULL,
|
||||
"premium" BOOLEAN NOT NULL DEFAULT false,
|
||||
"plan" TEXT,
|
||||
"stripeCustomer" TEXT,
|
||||
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "profile_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "QuestionProgress" (
|
||||
"format" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"status" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMPTZ(6) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"userId" UUID NOT NULL,
|
||||
|
||||
CONSTRAINT "question_progress_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "SitePerformance" (
|
||||
"createdAt" TIMESTAMPTZ(6) DEFAULT CURRENT_TIMESTAMP,
|
||||
"duration" DOUBLE PRECISION,
|
||||
"country" TEXT,
|
||||
"userEmail" TEXT,
|
||||
"event" TEXT,
|
||||
"id" UUID NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"url" TEXT,
|
||||
"referrer" TEXT,
|
||||
|
||||
CONSTRAINT "SitePerformance_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "EmailSubscribers_email_key" ON "EmailSubscriber"("email");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Event" ADD CONSTRAINT "Event_userId_fkey" FOREIGN KEY ("userId") REFERENCES "Profile"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "QuestionProgress" ADD CONSTRAINT "QuestionProgress_userId_fkey" FOREIGN KEY ("userId") REFERENCES "Profile"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
model EmailSubscriber {
|
||||
id String @id(map: "EmailSubscribers_pkey") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
email String @unique(map: "EmailSubscribers_email_key")
|
||||
createdAt DateTime? @default(now()) @db.Timestamp(6)
|
||||
}
|
||||
|
||||
model Event {
|
||||
id BigInt @id @default(autoincrement())
|
||||
userId String @db.Uuid
|
||||
action String
|
||||
payload Json? @db.Json
|
||||
createdAt DateTime? @default(now()) @db.Timestamptz(6)
|
||||
referer String? @db.VarChar
|
||||
country String?
|
||||
Profile Profile @relation(fields: [userId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||
}
|
||||
|
||||
model FeedbackMessage {
|
||||
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
message String @db.VarChar
|
||||
email String?
|
||||
createdAt DateTime? @default(now()) @db.Timestamp(6)
|
||||
resolved Boolean @default(false)
|
||||
owner String? @default("yangshun")
|
||||
userEmail String?
|
||||
comments String? @db.VarChar
|
||||
metadata Json? @db.Json
|
||||
}
|
||||
|
||||
model Profile {
|
||||
id String @id(map: "profile_pkey") @db.Uuid
|
||||
premium Boolean @default(false)
|
||||
plan String?
|
||||
stripeCustomer String?
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
Event Event[]
|
||||
QuestionProgress QuestionProgress[]
|
||||
}
|
||||
|
||||
model QuestionProgress {
|
||||
format String
|
||||
slug String
|
||||
id String @id(map: "question_progress_pkey") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
status String
|
||||
createdAt DateTime @default(now()) @db.Timestamptz(6)
|
||||
userId String @db.Uuid
|
||||
Profile Profile @relation(fields: [userId], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||
}
|
||||
|
||||
model SitePerformance {
|
||||
createdAt DateTime? @default(now()) @db.Timestamptz(6)
|
||||
duration Float?
|
||||
country String?
|
||||
userEmail String?
|
||||
event String?
|
||||
id String @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
|
||||
url String?
|
||||
referrer String?
|
||||
}
|
||||
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
|
@ -0,0 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg viewBox="0 0 24 24" width="24" height="24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g transform="matrix(1, 0, 0, 1, 27.009001, -39.238998)">
|
||||
<path fill="#4285F4" d="M -3.264 51.509 C -3.264 50.719 -3.334 49.969 -3.454 49.239 L -14.754 49.239 L -14.754 53.749 L -8.284 53.749 C -8.574 55.229 -9.424 56.479 -10.684 57.329 L -10.684 60.329 L -6.824 60.329 C -4.564 58.239 -3.264 55.159 -3.264 51.509 Z"/>
|
||||
<path fill="#34A853" d="M -14.754 63.239 C -11.514 63.239 -8.804 62.159 -6.824 60.329 L -10.684 57.329 C -11.764 58.049 -13.134 58.489 -14.754 58.489 C -17.884 58.489 -20.534 56.379 -21.484 53.529 L -25.464 53.529 L -25.464 56.619 C -23.494 60.539 -19.444 63.239 -14.754 63.239 Z"/>
|
||||
<path fill="#FBBC05" d="M -21.484 53.529 C -21.734 52.809 -21.864 52.039 -21.864 51.239 C -21.864 50.439 -21.724 49.669 -21.484 48.949 L -21.484 45.859 L -25.464 45.859 C -26.284 47.479 -26.754 49.299 -26.754 51.239 C -26.754 53.179 -26.284 54.999 -25.464 56.619 L -21.484 53.529 Z"/>
|
||||
<path fill="#EA4335" d="M -14.754 43.989 C -12.984 43.989 -11.404 44.599 -10.154 45.789 L -6.734 42.369 C -8.804 40.429 -11.514 39.239 -14.754 39.239 C -19.444 39.239 -23.494 41.939 -25.464 45.859 L -21.484 48.949 C -20.534 46.099 -17.884 43.989 -14.754 43.989 Z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
version="1.1"
|
||||
width="603"
|
||||
height="182"
|
||||
style="fill:#000">
|
||||
<path
|
||||
d="m 374.00642,142.18404 c -34.99948,25.79739 -85.72909,39.56123 -129.40634,39.56123 -61.24255,0 -116.37656,-22.65135 -158.08757,-60.32496 -3.2771,-2.96252 -0.34083,-6.9999 3.59171,-4.69283 45.01431,26.19064 100.67269,41.94697 158.16623,41.94697 38.774689,0 81.4295,-8.02237 120.6499,-24.67006 5.92501,-2.51683 10.87999,3.88009 5.08607,8.17965"
|
||||
id="path8"
|
||||
style="fill:#000" />
|
||||
<path
|
||||
d="m 388.55678,125.53635 c -4.45688,-5.71527 -29.57261,-2.70033 -40.84585,-1.36327 -3.43442,0.41947 -3.95874,-2.56925 -0.86517,-4.71905 20.00346,-14.07844 52.82696,-10.01483 56.65462,-5.2958 3.82764,4.74526 -0.99624,37.64741 -19.79373,53.35128 -2.88385,2.41195 -5.63662,1.12734 -4.35198,-2.07113 4.2209,-10.53917 13.68519,-34.16054 9.20211,-39.90203"
|
||||
id="path10"
|
||||
style="fill:#000" />
|
||||
<path
|
||||
d="M 348.49744,20.06598 V 6.38079 c 0,-2.07113 1.57301,-3.46062 3.46062,-3.46062 h 61.26875 c 1.96628,0 3.53929,1.41571 3.53929,3.46062 v 11.71893 c -0.0262,1.96626 -1.67788,4.53551 -4.61418,8.59912 l -31.74859,45.32893 c 11.79759,-0.28837 24.25059,1.46814 34.94706,7.49802 2.41195,1.36327 3.06737,3.35575 3.25089,5.32203 V 99.4506 c 0,1.99248 -2.20222,4.32576 -4.5093,3.1198 -18.84992,-9.88376 -43.887,-10.95865 -64.72939,0.10487 -2.12356,1.15354 -4.35199,-1.15354 -4.35199,-3.14602 V 85.66054 c 0,-2.22843 0.0262,-6.02989 2.25463,-9.41186 l 36.78224,-52.74829 h -32.01076 c -1.96626,0 -3.53927,-1.38948 -3.53927,-3.43441" />
|
||||
<path
|
||||
d="m 124.99883,105.45424 h -18.64017 c -1.78273,-0.13107 -3.19845,-1.46813 -3.32954,-3.17224 V 6.61676 c 0,-1.91383 1.59923,-3.43442 3.59171,-3.43442 h 17.38176 c 1.80898,0.0786 3.25089,1.46814 3.38199,3.19845 v 12.50545 h 0.34082 c 4.53551,-12.08598 13.05597,-17.7226 24.53896,-17.7226 11.66649,0 18.95477,5.63662 24.19814,17.7226 4.5093,-12.08598 14.76008,-17.7226 25.74495,-17.7226 7.81262,0 16.35931,3.22467 21.57646,10.46052 5.89879,8.04857 4.69281,19.74128 4.69281,29.99208 l -0.0262,60.37739 c 0,1.91383 -1.59923,3.46061 -3.59171,3.46061 h -18.61397 c -1.86138,-0.13107 -3.35574,-1.62543 -3.35574,-3.46061 V 51.29025 c 0,-4.03739 0.36702,-14.10466 -0.52434,-17.93233 -1.38949,-6.42311 -5.55797,-8.23209 -10.95865,-8.23209 -4.5093,0 -9.22833,3.01494 -11.14216,7.83885 -1.91383,4.8239 -1.73031,12.89867 -1.73031,18.32557 v 50.70338 c 0,1.91383 -1.59923,3.46061 -3.59171,3.46061 h -18.61395 c -1.88761,-0.13107 -3.35576,-1.62543 -3.35576,-3.46061 L 152.946,51.29025 c 0,-10.67025 1.75651,-26.37415 -11.48298,-26.37415 -13.39682,0 -12.87248,15.31063 -12.87248,26.37415 v 50.70338 c 0,1.91383 -1.59923,3.46061 -3.59171,3.46061" />
|
||||
<path
|
||||
d="m 469.51439,1.16364 c 27.65877,0 42.62858,23.75246 42.62858,53.95427 0,29.17934 -16.54284,52.32881 -42.62858,52.32881 -27.16066,0 -41.94697,-23.75246 -41.94697,-53.35127 0,-29.78234 14.96983,-52.93181 41.94697,-52.93181 m 0.15729,19.53156 c -13.73761,0 -14.60278,18.71881 -14.60278,30.38532 0,11.69271 -0.18352,36.65114 14.44549,36.65114 14.44548,0 15.12712,-20.13452 15.12712,-32.40403 0,-8.07477 -0.34082,-17.72257 -2.779,-25.3779 -2.09735,-6.65906 -6.26581,-9.25453 -12.19083,-9.25453" />
|
||||
<path
|
||||
d="M 548.00762,105.45424 H 529.4461 c -1.86141,-0.13107 -3.35577,-1.62543 -3.35577,-3.46061 l -0.0262,-95.69149 c 0.1573,-1.75653 1.7041,-3.1198 3.59171,-3.1198 h 17.27691 c 1.62543,0.0786 2.96249,1.17976 3.32954,2.67412 v 14.62899 h 0.3408 c 5.21717,-13.0822 12.53165,-19.32181 25.40412,-19.32181 8.36317,0 16.51662,3.01494 21.75999,11.27324 4.87633,7.65532 4.87633,20.5278 4.87633,29.78233 v 60.22011 c -0.20973,1.67786 -1.75653,3.01492 -3.59169,3.01492 h -18.69262 c -1.70411,-0.13107 -3.11982,-1.38948 -3.30332,-3.01492 V 50.47753 c 0,-10.46052 1.20597,-25.77117 -11.66651,-25.77117 -4.5355,0 -8.70399,3.04117 -10.77512,7.65532 -2.62167,5.84637 -2.96249,11.66651 -2.96249,18.11585 v 51.5161 c -0.0262,1.91383 -1.65166,3.46061 -3.64414,3.46061" />
|
||||
<use
|
||||
xlink:href="#path30"
|
||||
transform="translate(244.36719)" />
|
||||
<path
|
||||
d="M 55.288261,59.75829 V 55.7209 c -13.475471,0 -27.711211,2.88385 -27.711211,18.77125 0,8.04857 4.16847,13.50169 11.32567,13.50169 5.24337,0 9.93618,-3.22467 12.8987,-8.46805 3.670341,-6.44935 3.486841,-12.50544 3.486841,-19.7675 m 18.79747,45.43378 c -1.23219,1.10111 -3.01495,1.17976 -4.40444,0.4457 -6.18716,-5.1385 -7.28828,-7.52423 -10.69647,-12.42678 -10.224571,10.4343 -17.460401,13.55409 -30.726141,13.55409 -15.67768,0 -27.89471,-9.67401 -27.89471,-29.04824 0,-15.12713 8.20587,-25.43035 19.87236,-30.46398 10.1197,-4.45688 24.25058,-5.24337 35.051931,-6.47556 v -2.41195 c 0,-4.43066 0.34082,-9.67403 -2.25465,-13.50167 -2.280881,-3.43442 -6.632861,-4.85013 -10.460531,-4.85013 -7.10475,0 -13.44924,3.64414 -14.99603,11.19459 -0.31461,1.67789 -1.5468,3.32955 -3.22467,3.4082 L 6.26276,32.67628 C 4.74218,32.33548 3.0643,31.10327 3.48377,28.76999 7.65225,6.85271 27.44596,0.24605 45.16856,0.24605 c 9.071011,0 20.921021,2.41195 28.078221,9.28076 9.07104,8.46804 8.20587,19.7675 8.20587,32.06321 v 29.04826 c 0,8.73022 3.61794,12.55786 7.02613,17.27691 1.20597,1.67786 1.46814,3.69656 -0.05244,4.95497 -3.80144,3.17225 -10.56538,9.07104 -14.28819,12.37436 l -0.05242,-0.0525"
|
||||
id="path30" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 272 92" width="272" height="92"><path fill="#000" d="M115.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18C71.25 34.32 81.24 25 93.5 25s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44S80.99 39.2 80.99 47.18c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/><path fill="#000" d="M163.75 47.18c0 12.77-9.99 22.18-22.25 22.18s-22.25-9.41-22.25-22.18c0-12.85 9.99-22.18 22.25-22.18s22.25 9.32 22.25 22.18zm-9.74 0c0-7.98-5.79-13.44-12.51-13.44s-12.51 5.46-12.51 13.44c0 7.9 5.79 13.44 12.51 13.44s12.51-5.55 12.51-13.44z"/><path fill="#000" d="M209.75 26.34v39.82c0 16.38-9.66 23.07-21.08 23.07-10.75 0-17.22-7.19-19.66-13.07l8.48-3.53c1.51 3.61 5.21 7.87 11.17 7.87 7.31 0 11.84-4.51 11.84-13v-3.19h-.34c-2.18 2.69-6.38 5.04-11.68 5.04-11.09 0-21.25-9.66-21.25-22.09 0-12.52 10.16-22.26 21.25-22.26 5.29 0 9.49 2.35 11.68 4.96h.34v-3.61h9.25zm-8.56 20.92c0-7.81-5.21-13.52-11.84-13.52-6.72 0-12.35 5.71-12.35 13.52 0 7.73 5.63 13.36 12.35 13.36 6.63 0 11.84-5.63 11.84-13.36z"/><path fill="#000" d="M225 3v65h-9.5V3h9.5z"/><path fill="#000" d="M262.02 54.48l7.56 5.04c-2.44 3.61-8.32 9.83-18.48 9.83-12.6 0-22.01-9.74-22.01-22.18 0-13.19 9.49-22.18 20.92-22.18 11.51 0 17.14 9.16 18.98 14.11l1.01 2.52-29.65 12.28c2.27 4.45 5.8 6.72 10.75 6.72 4.96 0 8.4-2.44 10.92-6.14zm-23.27-7.98l19.82-8.23c-1.09-2.77-4.37-4.7-8.23-4.7-4.95 0-11.84 4.37-11.59 12.93z"/><path fill="#000" d="M35.29 41.41V32H67c.31 1.64.47 3.58.47 5.68 0 7.06-1.93 15.79-8.15 22.01-6.05 6.3-13.78 9.66-24.02 9.66C16.32 69.35.36 53.89.36 34.91.36 15.93 16.32.47 35.3.47c10.5 0 17.98 4.12 23.6 9.49l-6.64 6.64c-4.03-3.78-9.49-6.72-16.97-6.72-13.86 0-24.7 11.17-24.7 25.03 0 13.86 10.84 25.03 24.7 25.03 8.99 0 14.11-3.61 17.39-6.89 2.66-2.66 4.41-6.46 5.1-11.65l-22.49.01z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:cc="http://creativecommons.org/ns#" width="948" height="191">
|
||||
<path id="Logo0" style="fill:#000" d="m31.06,125.96c0,10.98 2.41,19.41 5.56,24.51 4.13,6.68 10.29,9.51 16.57,9.51 8.1,0 15.51-2.01 29.79-21.76 11.44-15.83 24.92-38.05 33.99-51.98l15.36-23.6c10.67-16.39 23.02-34.61 37.18-46.96 11.56-10.08 24.03-15.68 36.58-15.68 21.07,0 41.14,12.21 56.5,35.11 16.81,25.08 24.97,56.67 24.97,89.27 0,19.38-3.82,33.62-10.32,44.87-6.28,10.88-18.52,21.75-39.11,21.75l0-31.02c17.63,0 22.03-16.2 22.03-34.74 0-26.42-6.16-55.74-19.73-76.69-9.63-14.86-22.11-23.94-35.84-23.94-14.85,0-26.8,11.2-40.23,31.17-7.14,10.61-14.47,23.54-22.7,38.13l-9.06,16.05c-18.2,32.27-22.81,39.62-31.91,51.75-15.95,21.24-29.57,29.29-47.5,29.29-21.27,0-34.72-9.21-43.05-23.09-6.8-11.31-10.14-26.15-10.14-43.06z"/>
|
||||
<path id="Logo1" style="fill:#000" d="m24.49,37.3c14.24-21.95 34.79-37.3 58.36-37.3 13.65,0 27.22,4.04 41.39,15.61 15.5,12.65 32.02,33.48 52.63,67.81l7.39,12.32c17.84,29.72 27.99,45.01 33.93,52.22 7.64,9.26 12.99,12.02 19.94,12.02 17.63,0 22.03-16.2 22.03-34.74l27.4-.86c0,19.38-3.82,33.62-10.32,44.87-6.28,10.88-18.52,21.75-39.11,21.75-12.8,0-24.14-2.78-36.68-14.61-9.64-9.08-20.91-25.21-29.58-39.71l-25.79-43.08c-12.94-21.62-24.81-37.74-31.68-45.04-7.39-7.85-16.89-17.33-32.05-17.33-12.27,0-22.69,8.61-31.41,21.78z"/>
|
||||
<path id="Logo2" style="fill:#000" d="m82.35,31.23c-12.27,0-22.69,8.61-31.41,21.78-12.33,18.61-19.88,46.33-19.88,72.95 0,10.98 2.41,19.41 5.56,24.51l-26.48,17.44c-6.8-11.31-10.14-26.15-10.14-43.06 0-30.75 8.44-62.8 24.49-87.55 14.24-21.95 34.79-37.3 58.36-37.3z"/>
|
||||
<path id="Text" style="fill:#000" d="m347.94,6.04h35.93l61.09,110.52 61.1-110.52h35.15v181.6h-29.31v-139.18l-53.58,96.38h-27.5l-53.57-96.38v139.18h-29.31z
|
||||
m285.11,67.71c-21.02,0-33.68,15.82-36.71,35.41h71.34c-1.47-20.18-13.11-35.41-34.63-35.41z
|
||||
m-65.77,46.57c0-41.22 26.64-71.22 66.28-71.22 38.99,0 62.27,29.62 62.27,73.42v8.05h-99.49c3.53,21.31 17.67,35.67 40.47,35.67 18.19,0 29.56-5.55 40.34-15.7l15.57,19.07c-14.67,13.49-33.33,21.27-56.95,21.27-42.91,0-68.49-31.29-68.49-70.56z
|
||||
m164.09-43.97h-26.98v-24h26.98v-39.69h28.28v39.69h40.99v24h-40.99v60.83c0,20.77 6.64,28.15 22.96,28.15 7.45,0 11.72-.64 18.03-1.69v23.74c-7.86,2.22-15.36,3.24-23.48,3.24-30.53,0-45.79-16.68-45.79-50.07z
|
||||
m188.35,23.34c-5.68-14.34-18.35-24.9-36.97-24.9-24.2,0-39.69,17.17-39.69,45.14 0,27.27 14.26,45.27 38.53,45.27 19.08,0 32.7-11.1 38.13-24.91z
|
||||
m28.28,87.95h-27.76v-18.94c-7.76,11.15-21.88,22.18-44.75,22.18-36.78,0-61.36-30.79-61.36-70.95 0-40.54 25.17-70.83 62.92-70.83 18.66,0 33.3,7.46 43.19,20.63v-17.38h27.76z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 133 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 141 KiB |
|
After Width: | Height: | Size: 181 KiB |
|
After Width: | Height: | Size: 166 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 61 KiB |
|
After Width: | Height: | Size: 60 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 584 KiB |
|
After Width: | Height: | Size: 481 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 208 KiB |
|
After Width: | Height: | Size: 104 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 210 KiB |