diff --git a/LangPath.json b/LangPath.json new file mode 100644 index 00000000..fe47441f --- /dev/null +++ b/LangPath.json @@ -0,0 +1 @@ +{"en":["/book/cs/_meta.en.json","/book/cs/from-func.en.mdx","/book/cs/from-solidity.en.mdx","/book/guides/getting-started/_meta.en.json","/book/guides/getting-started/deploy.en.mdx","/book/guides/getting-started/first.en.mdx","/book/guides/getting-started/test.en.mdx","/book/guides/_meta.en.json","/book/guides/getting-started.en.mdx","/book/_meta.en.json","/book/bounced.en.mdx","/book/cells.en.mdx","/book/config.en.mdx","/book/constants.en.mdx","/book/contracts.en.mdx","/book/debug.en.mdx","/book/deploy.en.mdx","/book/exit-codes.en.mdx","/book/expressions.en.mdx","/book/external.en.mdx","/book/func.en.mdx","/book/functions.en.mdx","/book/import.en.mdx","/book/index.en.mdx","/book/integers.en.mdx","/book/lifecycle.en.mdx","/book/maps.en.mdx","/book/masterchain.en.mdx","/book/message-mode.en.mdx","/book/operators.en.mdx","/book/optionals.en.mdx","/book/programmatic.en.mdx","/book/receive.en.mdx","/book/send.en.mdx","/book/statements.en.mdx","/book/structs-and-messages.en.mdx","/book/types.en.mdx","/book/upgrades.en.mdx","/cookbook/dexes/_meta.en.json","/cookbook/dexes/dedust.en.mdx","/cookbook/dexes/stonfi.en.mdx","/cookbook/_meta.en.json","/cookbook/access.en.mdx","/cookbook/algo.en.mdx","/cookbook/data-structures.en.mdx","/cookbook/index.en.mdx","/cookbook/jettons.en.mdx","/cookbook/misc.en.mdx","/cookbook/multi-communication.en.mdx","/cookbook/nfts.en.mdx","/cookbook/random.en.mdx","/cookbook/single-communication.en.mdx","/cookbook/time.en.mdx","/cookbook/type-conversion.en.mdx","/ecosystem/tools/_meta.en.json","/ecosystem/tools/jetbrains.en.mdx","/ecosystem/tools/overview.en.mdx","/ecosystem/tools/typescript.en.mdx","/ecosystem/tools/vscode.en.mdx","/ecosystem/_meta.en.json","/ecosystem/index.en.mdx","/ref/evolution/OTP-001.en.mdx","/ref/evolution/OTP-002.en.mdx","/ref/evolution/OTP-003.en.mdx","/ref/evolution/OTP-004.en.mdx","/ref/evolution/OTP-005.en.mdx","/ref/evolution/OTP-006.en.mdx","/ref/evolution/_meta.en.json","/ref/evolution/overview.en.mdx","/ref/_meta.en.json","/ref/core-advanced.en.mdx","/ref/core-base.en.mdx","/ref/core-cells.en.mdx","/ref/core-common.en.mdx","/ref/core-comptime.en.mdx","/ref/core-debug.en.mdx","/ref/core-math.en.mdx","/ref/core-random.en.mdx","/ref/core-strings.en.mdx","/ref/index.en.mdx","/ref/spec.en.mdx","/ref/standard-libraries.en.mdx","/ref/stdlib-config.en.mdx","/ref/stdlib-content.en.mdx","/ref/stdlib-deploy.en.mdx","/ref/stdlib-dns.en.mdx","/ref/stdlib-ownable.en.mdx","/ref/stdlib-stoppable.en.mdx","/_meta.en.json","/index.en.mdx"],"zh-CN":["/_meta.zh-CN.json","/index.zh-CN.mdx"]} \ No newline at end of file diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..f44df925 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,7 @@ +project_id: "674258" +api_token_env: CROWDIN_PERSONAL_TOKEN +preserve_hierarchy: true +files: + - source: /pages/**/*.en.* + translation: /pages/**/%file_name%.%two_letters_code%.%file_extension% + "translation_replace": { ".en": "" } diff --git a/locales.ts b/locales.ts new file mode 100644 index 00000000..9e893705 --- /dev/null +++ b/locales.ts @@ -0,0 +1,133 @@ +import { addBasePath } from "next/dist/client/add-base-path"; +import { addLocale } from "next/dist/client/add-locale"; +import { hasBasePath } from "next/dist/client/has-base-path"; +import { removeBasePath } from "next/dist/client/remove-base-path"; +import { removeLocale } from "next/dist/client/remove-locale"; +import type { NextRequest } from "next/server"; +import { NextResponse } from "next/server"; + +import LangPath from "./LangPath.json"; + +type langKeys = "en" | "zh-CN"; + +type LegacyMiddlewareCookies = Record; +type StableMiddlewareCookies = Map; +type Next13MiddlewareCookies = NextRequest["cookies"]; + +function getCookie( + cookies: + | LegacyMiddlewareCookies + | StableMiddlewareCookies + | Next13MiddlewareCookies, + key: string +): string | undefined { + if (typeof cookies.get === "function") { + const cookie = cookies.get(key); + if (cookie && typeof cookie === "object") { + return cookie.value; + } + return cookie as any; + } + return (cookies as LegacyMiddlewareCookies)[key]; +} + +export function locales(request: NextRequest) { + const { nextUrl } = request; + + if (/\/_meta(\.[a-z]{2}-[A-Z]{2})?$/.test(nextUrl.pathname)) { + const url = nextUrl.clone(); + url.pathname = `/404`; + + return NextResponse.rewrite(url); + } + + const rootDirArray = ["/book", "/ecosystem", "/cookbook", "/ref"]; + + const shouldHandleLocale = + !/^\/(api|_next)\//.test(nextUrl.pathname) && + !/\.(jpe?g|svg|png|webmanifest|xml|ico|txt|mp4)$/.test(nextUrl.pathname) && + nextUrl.locale !== "" && + // not Server-Side Error page + nextUrl.pathname !== "/500"; + + if (!shouldHandleLocale) return; + + // The locale code prefixed in the current URL, which can be empty. + const locale = nextUrl.locale === nextUrl.defaultLocale ? "" : nextUrl.locale; + + // pathname for default locale doesn't contain basePath and locale segment + nextUrl.pathname = hasBasePath(nextUrl.pathname) + ? removeLocale(removeBasePath(nextUrl.pathname), nextUrl.locale) + : nextUrl.pathname; + + let finalLocale; + + if (locale) { + // If a locale is explicitly set, we don't do any modifications. + finalLocale = locale; + } else { + // If there is a locale cookie, we try to use it. If it doesn't exist, or + // it's invalid, `nextUrl.locale` will be automatically figured out by Next + // via the `accept-languages` header. + const clientLocale = getCookie(request.cookies, "NEXT_LOCALE"); + if (clientLocale) { + try { + nextUrl.locale = clientLocale; + } catch { + // The locale from the cookie isn't valid. + // https://github.com/vercel/next.js/blob/e5dee17f776dcc79ebb269f7b7341fa6e2b6c3f1/packages/next/server/web/next-url.ts#L122-L129 + } + } + finalLocale = nextUrl.locale; + + // Now we want to display the locale. If it's not the default one, we have + // to prefix the URL with that locale since it's missing. Only the default + // locale can be missing from there for consistency. + if (finalLocale !== nextUrl.defaultLocale) { + const url = addBasePath( + addLocale( + `${nextUrl.pathname}${nextUrl.search}`, + finalLocale, + nextUrl.defaultLocale + ) + ); + + return NextResponse.redirect(new URL(url, request.url)); + } + } + let pathname = nextUrl.pathname || "/"; + if (pathname === "/") pathname += "index"; + else if (pathname.endsWith("/")) pathname = pathname.slice(0, -1); + + const isValidRoute = LangPath[finalLocale].includes( + `${ + rootDirArray?.includes(pathname) ? pathname + "/index" : pathname + }.${finalLocale}.mdx` + ); + + // If we are not showing the correct localed page, rewrite the current request. + if (!pathname.endsWith("." + finalLocale)) { + let url = addBasePath( + addLocale( + `${ + rootDirArray?.includes(pathname) ? pathname + "/index" : pathname + }.${finalLocale}${nextUrl.search}`, + finalLocale, + nextUrl.defaultLocale + ) + ); + + if (!isValidRoute) + url = url + .replace(`/${finalLocale}`, "") + .replace(`${finalLocale}`, nextUrl.defaultLocale); + + return NextResponse.rewrite(new URL(url, request.url)); + } +} + +export function withLocales(middleware: any) { + return (...args: any[]) => { + return locales(args[0]) || middleware(...args); + }; +} diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 00000000..ee79168f --- /dev/null +++ b/middleware.ts @@ -0,0 +1 @@ +export { locales as middleware } from "./locales"; diff --git a/next.config.js b/next.config.js index 7d0bcca8..ddea0117 100644 --- a/next.config.js +++ b/next.config.js @@ -55,10 +55,14 @@ const withNextra = nextra({ * @type {import('next').NextConfig} */ export default withNextra({ - output: 'export', images: { unoptimized: true }, + i18n: { + locales: ["en", "zh-CN"], + defaultLocale: "en", + localeDetection: false, + }, // i18n: { // // locales: ['default', 'en', 'zh'], // locales: ['default', 'en'], diff --git a/package.json b/package.json index 34024bb7..6eb0e7a3 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ "scripts": { "clean": "rm -fr .next out", "deps": "yarn install --frozen-lockfile", - "dev": "yarn deps && yarn clean && next", - "build": "yarn deps && yarn clean && next build", + "dev": "node ./scripts/I18nPathJson-generate.js && yarn deps && yarn clean && next", + "build": "node ./scripts/I18nPathJson-generate.js && yarn deps && yarn clean && next build", "post-build": "echo 'spell checking, link checking, formatting'", - "build-pages": "yarn build && node ./scripts/redirects-generate.js", + "build-pages": "node ./scripts/I18nPathJson-generate.js && yarn build && next export && node ./scripts/redirects-generate.js", "next": "next", "spell": "cspell \"**\"" }, diff --git a/pages/_meta.en.json b/pages/_meta.en.json new file mode 100644 index 00000000..8a848465 --- /dev/null +++ b/pages/_meta.en.json @@ -0,0 +1,26 @@ +{ + "index": { + "title": "Tact Documentation", + "type": "page", + "display": "hidden", + "theme": { + "typesetting": "article" + } + }, + "book": { + "title": "Book", + "type": "page" + }, + "cookbook": { + "title": "Cookbook", + "type": "page" + }, + "ref": { + "title": "Reference", + "type": "page" + }, + "ecosystem": { + "title": "Ecosystem", + "type": "page" + } +} diff --git a/pages/_meta.js b/pages/_meta.js deleted file mode 100644 index 330d4313..00000000 --- a/pages/_meta.js +++ /dev/null @@ -1,26 +0,0 @@ -export default { - index: { - title: 'Tact Documentation', - type: 'page', - display: 'hidden', - theme: { - typesetting: 'article' - } - }, - book: { - title: "Book", - type: 'page', - }, - cookbook: { - title: "Cookbook", - type: 'page', - }, - ref: { - title: "Reference", - type: 'page', - }, - ecosystem: { - title: "Ecosystem", - type: 'page', - }, -} diff --git a/pages/_meta.zh-CN.json b/pages/_meta.zh-CN.json new file mode 100644 index 00000000..8a848465 --- /dev/null +++ b/pages/_meta.zh-CN.json @@ -0,0 +1,26 @@ +{ + "index": { + "title": "Tact Documentation", + "type": "page", + "display": "hidden", + "theme": { + "typesetting": "article" + } + }, + "book": { + "title": "Book", + "type": "page" + }, + "cookbook": { + "title": "Cookbook", + "type": "page" + }, + "ref": { + "title": "Reference", + "type": "page" + }, + "ecosystem": { + "title": "Ecosystem", + "type": "page" + } +} diff --git a/pages/book/_meta.en.json b/pages/book/_meta.en.json new file mode 100644 index 00000000..e278cffe --- /dev/null +++ b/pages/book/_meta.en.json @@ -0,0 +1,61 @@ +{ + "index": "Overview", + "guides": "Guides", + "cs": "Cheatsheets", + "-- 1": { + "type": "separator", + "title": "Fundamentals of Tact" + }, + "types": "Type system overview", + "integers": "Integers", + "cells": "Cells, Builders and Slices", + "maps": "Maps", + "structs-and-messages": "Structs and Messages", + "optionals": "Optionals", + "contracts": "Contracts", + "exit-codes": "Exit codes", + "-- 2": { + "type": "separator", + "title": "Expressiveness" + }, + "operators": "Operators", + "expressions": "Expressions", + "statements": "Statements", + "constants": "Constants", + "functions": "Functions", + "-- 3": { + "type": "separator", + "title": "Communication" + }, + "receive": "Receive messages", + "bounced": "Bounced messages", + "external": "External messages", + "lifecycle": "Message lifecycle", + "send": "Sending messages", + "message-mode": "Message mode", + "-- 4": { + "type": "separator", + "title": "Going places" + }, + "deploy": "Deployment", + "debug": "Debugging", + "upgrades": "Contract upgrades", + "import": "Importing code", + "config": "Configuration", + "masterchain": "Masterchain", + "func": "Compatibility with FunC", + "programmatic": "Programmatic API", + "-- Community": { + "type": "separator" + }, + "telegram-link": { + "title": "✈️ Telegram", + "href": "https://t.me/tactlang", + "newWindow": true + }, + "xtwitter-link": { + "title": "🐦 X/Twitter", + "href": "https://twitter.com/tact_language", + "newWindow": true + } +} diff --git a/pages/book/_meta.js b/pages/book/_meta.js deleted file mode 100644 index a4a55312..00000000 --- a/pages/book/_meta.js +++ /dev/null @@ -1,64 +0,0 @@ -export default { - index: 'Overview', - guides: 'Guides', - cs: 'Cheatsheets', - '-- 1': { - type: 'separator', - title: 'Fundamentals of Tact', - }, - types: 'Type system overview', - integers: 'Integers', - cells: 'Cells, Builders and Slices', - maps: 'Maps', - 'structs-and-messages': 'Structs and Messages', - optionals: 'Optionals', - contracts: 'Contracts', - 'exit-codes': 'Exit codes', - '-- 2': { - type: 'separator', - title: 'Expressiveness', - }, - operators: 'Operators', - expressions: 'Expressions', - statements: 'Statements', - constants: 'Constants', - functions: 'Functions', - '-- 3': { - type: 'separator', - title: 'Communication', - }, - // <- NOTE - // potential place for a rather short overview page describing asynchronous & actor-model nature - // of TON Blockchain with respect to Tact and exchanging messages with it - receive: 'Receive messages', - bounced: 'Bounced messages', - external: 'External messages', - lifecycle: 'Message lifecycle', - send: 'Sending messages', - 'message-mode': 'Message mode', - '-- 4': { - type: 'separator', - title: 'Going places', - }, - deploy: 'Deployment', - debug: 'Debugging', - upgrades: 'Contract upgrades', - import: 'Importing code', - config: 'Configuration', - masterchain: 'Masterchain', - func: 'Compatibility with FunC', - programmatic: 'Programmatic API', - '-- Community': { - type: 'separator', - }, - 'telegram-link': { - title: '✈️ Telegram', - href: 'https://t.me/tactlang', - newWindow: true - }, - 'xtwitter-link': { - title: '🐦 X/Twitter', - href: 'https://twitter.com/tact_language', - newWindow: true - }, -} diff --git a/pages/book/bounced.mdx b/pages/book/bounced.en.mdx similarity index 100% rename from pages/book/bounced.mdx rename to pages/book/bounced.en.mdx diff --git a/pages/book/cells.mdx b/pages/book/cells.en.mdx similarity index 100% rename from pages/book/cells.mdx rename to pages/book/cells.en.mdx diff --git a/pages/book/config.mdx b/pages/book/config.en.mdx similarity index 100% rename from pages/book/config.mdx rename to pages/book/config.en.mdx diff --git a/pages/book/constants.mdx b/pages/book/constants.en.mdx similarity index 100% rename from pages/book/constants.mdx rename to pages/book/constants.en.mdx diff --git a/pages/book/contracts.mdx b/pages/book/contracts.en.mdx similarity index 100% rename from pages/book/contracts.mdx rename to pages/book/contracts.en.mdx diff --git a/pages/book/cs/_meta.en.json b/pages/book/cs/_meta.en.json new file mode 100644 index 00000000..e48d8845 --- /dev/null +++ b/pages/book/cs/_meta.en.json @@ -0,0 +1,4 @@ +{ + "from-func": "Coming from FunC", + "from-solidity": "Coming from Solidity" +} diff --git a/pages/book/cs/_meta.js b/pages/book/cs/_meta.js deleted file mode 100644 index 39b10a76..00000000 --- a/pages/book/cs/_meta.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - 'from-func': 'Coming from FunC', - 'from-solidity': 'Coming from Solidity', -} diff --git a/pages/book/cs/from-func.mdx b/pages/book/cs/from-func.en.mdx similarity index 100% rename from pages/book/cs/from-func.mdx rename to pages/book/cs/from-func.en.mdx diff --git a/pages/book/cs/from-solidity.mdx b/pages/book/cs/from-solidity.en.mdx similarity index 100% rename from pages/book/cs/from-solidity.mdx rename to pages/book/cs/from-solidity.en.mdx diff --git a/pages/book/debug.mdx b/pages/book/debug.en.mdx similarity index 100% rename from pages/book/debug.mdx rename to pages/book/debug.en.mdx diff --git a/pages/book/deploy.mdx b/pages/book/deploy.en.mdx similarity index 100% rename from pages/book/deploy.mdx rename to pages/book/deploy.en.mdx diff --git a/pages/book/exit-codes.mdx b/pages/book/exit-codes.en.mdx similarity index 100% rename from pages/book/exit-codes.mdx rename to pages/book/exit-codes.en.mdx diff --git a/pages/book/expressions.mdx b/pages/book/expressions.en.mdx similarity index 100% rename from pages/book/expressions.mdx rename to pages/book/expressions.en.mdx diff --git a/pages/book/external.mdx b/pages/book/external.en.mdx similarity index 100% rename from pages/book/external.mdx rename to pages/book/external.en.mdx diff --git a/pages/book/func.mdx b/pages/book/func.en.mdx similarity index 100% rename from pages/book/func.mdx rename to pages/book/func.en.mdx diff --git a/pages/book/functions.mdx b/pages/book/functions.en.mdx similarity index 100% rename from pages/book/functions.mdx rename to pages/book/functions.en.mdx diff --git a/pages/book/guides/_meta.en.json b/pages/book/guides/_meta.en.json new file mode 100644 index 00000000..c76b5c9f --- /dev/null +++ b/pages/book/guides/_meta.en.json @@ -0,0 +1,3 @@ +{ + "getting-started": "Getting started" +} diff --git a/pages/book/guides/_meta.js b/pages/book/guides/_meta.js deleted file mode 100644 index 1929dd95..00000000 --- a/pages/book/guides/_meta.js +++ /dev/null @@ -1,3 +0,0 @@ -export default { - 'getting-started': 'Getting started', -} diff --git a/pages/book/guides/getting-started.mdx b/pages/book/guides/getting-started.en.mdx similarity index 100% rename from pages/book/guides/getting-started.mdx rename to pages/book/guides/getting-started.en.mdx diff --git a/pages/book/guides/getting-started/_meta.en.json b/pages/book/guides/getting-started/_meta.en.json new file mode 100644 index 00000000..f7b89f34 --- /dev/null +++ b/pages/book/guides/getting-started/_meta.en.json @@ -0,0 +1,5 @@ +{ + "first": "Writing your first contract", + "deploy": "Deploying your contract", + "test": "Writing Tests" +} diff --git a/pages/book/guides/getting-started/_meta.js b/pages/book/guides/getting-started/_meta.js deleted file mode 100644 index 59579ab8..00000000 --- a/pages/book/guides/getting-started/_meta.js +++ /dev/null @@ -1,5 +0,0 @@ -export default { - first: 'Writing your first contract', - deploy: 'Deploying your contract', - test: 'Writing Tests' -} \ No newline at end of file diff --git a/pages/book/guides/getting-started/deploy.mdx b/pages/book/guides/getting-started/deploy.en.mdx similarity index 100% rename from pages/book/guides/getting-started/deploy.mdx rename to pages/book/guides/getting-started/deploy.en.mdx diff --git a/pages/book/guides/getting-started/first.mdx b/pages/book/guides/getting-started/first.en.mdx similarity index 100% rename from pages/book/guides/getting-started/first.mdx rename to pages/book/guides/getting-started/first.en.mdx diff --git a/pages/book/guides/getting-started/test.mdx b/pages/book/guides/getting-started/test.en.mdx similarity index 100% rename from pages/book/guides/getting-started/test.mdx rename to pages/book/guides/getting-started/test.en.mdx diff --git a/pages/book/import.mdx b/pages/book/import.en.mdx similarity index 100% rename from pages/book/import.mdx rename to pages/book/import.en.mdx diff --git a/pages/book/index.mdx b/pages/book/index.en.mdx similarity index 100% rename from pages/book/index.mdx rename to pages/book/index.en.mdx diff --git a/pages/book/integers.mdx b/pages/book/integers.en.mdx similarity index 100% rename from pages/book/integers.mdx rename to pages/book/integers.en.mdx diff --git a/pages/book/lifecycle.mdx b/pages/book/lifecycle.en.mdx similarity index 100% rename from pages/book/lifecycle.mdx rename to pages/book/lifecycle.en.mdx diff --git a/pages/book/maps.mdx b/pages/book/maps.en.mdx similarity index 100% rename from pages/book/maps.mdx rename to pages/book/maps.en.mdx diff --git a/pages/book/masterchain.mdx b/pages/book/masterchain.en.mdx similarity index 100% rename from pages/book/masterchain.mdx rename to pages/book/masterchain.en.mdx diff --git a/pages/book/message-mode.mdx b/pages/book/message-mode.en.mdx similarity index 100% rename from pages/book/message-mode.mdx rename to pages/book/message-mode.en.mdx diff --git a/pages/book/operators.mdx b/pages/book/operators.en.mdx similarity index 100% rename from pages/book/operators.mdx rename to pages/book/operators.en.mdx diff --git a/pages/book/optionals.mdx b/pages/book/optionals.en.mdx similarity index 100% rename from pages/book/optionals.mdx rename to pages/book/optionals.en.mdx diff --git a/pages/book/programmatic.mdx b/pages/book/programmatic.en.mdx similarity index 100% rename from pages/book/programmatic.mdx rename to pages/book/programmatic.en.mdx diff --git a/pages/book/receive.mdx b/pages/book/receive.en.mdx similarity index 100% rename from pages/book/receive.mdx rename to pages/book/receive.en.mdx diff --git a/pages/book/send.mdx b/pages/book/send.en.mdx similarity index 100% rename from pages/book/send.mdx rename to pages/book/send.en.mdx diff --git a/pages/book/statements.mdx b/pages/book/statements.en.mdx similarity index 100% rename from pages/book/statements.mdx rename to pages/book/statements.en.mdx diff --git a/pages/book/structs-and-messages.mdx b/pages/book/structs-and-messages.en.mdx similarity index 100% rename from pages/book/structs-and-messages.mdx rename to pages/book/structs-and-messages.en.mdx diff --git a/pages/book/types.mdx b/pages/book/types.en.mdx similarity index 100% rename from pages/book/types.mdx rename to pages/book/types.en.mdx diff --git a/pages/book/upgrades.mdx b/pages/book/upgrades.en.mdx similarity index 100% rename from pages/book/upgrades.mdx rename to pages/book/upgrades.en.mdx diff --git a/pages/cookbook/_meta.en.json b/pages/cookbook/_meta.en.json new file mode 100644 index 00000000..154b9f60 --- /dev/null +++ b/pages/cookbook/_meta.en.json @@ -0,0 +1,36 @@ +{ + "index": "Overview", + "-- 1": { + "type": "separator", + "title": "Single contract" + }, + "single-communication": "Single-contract communication", + "type-conversion": "Type conversion", + "data-structures": "Data structures", + "algo": "Algorithms", + "time": "Time and date", + "access": "Access control", + "random": "Randomness", + "misc": "Miscellaneous", + "-- 2+": { + "type": "separator", + "title": "Multiple contracts" + }, + "multi-communication": "Multi-contract communication", + "jettons": "Fungible Tokens (Jettons)", + "nfts": "Non-Fungible Tokens (NFTs)", + "dexes": "Decentralized EXchanges (DEXes)", + "-- Community": { + "type": "separator" + }, + "telegram-link": { + "title": "✈️ Telegram", + "href": "https://t.me/tactlang", + "newWindow": true + }, + "xtwitter-link": { + "title": "🐦 X/Twitter", + "href": "https://twitter.com/tact_language", + "newWindow": true + } +} diff --git a/pages/cookbook/_meta.js b/pages/cookbook/_meta.js deleted file mode 100644 index 1141019f..00000000 --- a/pages/cookbook/_meta.js +++ /dev/null @@ -1,36 +0,0 @@ -export default { - index: 'Overview', - '-- 1': { - type: 'separator', - title: 'Single contract', - }, - 'single-communication': 'Single-contract communication', - 'type-conversion': 'Type conversion', - 'data-structures': 'Data structures', - algo: 'Algorithms', - time: 'Time and date', - access: 'Access control', - random: 'Randomness', - misc: 'Miscellaneous', - '-- 2+': { - type: 'separator', - title: 'Multiple contracts', - }, - 'multi-communication': 'Multi-contract communication', - jettons: 'Fungible Tokens (Jettons)', - nfts: 'Non-Fungible Tokens (NFTs)', - dexes: 'Decentralized EXchanges (DEXes)', - '-- Community': { - type: 'separator', - }, - 'telegram-link': { - title: '✈️ Telegram', - href: 'https://t.me/tactlang', - newWindow: true - }, - 'xtwitter-link': { - title: '🐦 X/Twitter', - href: 'https://twitter.com/tact_language', - newWindow: true - }, -} diff --git a/pages/cookbook/access.mdx b/pages/cookbook/access.en.mdx similarity index 100% rename from pages/cookbook/access.mdx rename to pages/cookbook/access.en.mdx diff --git a/pages/cookbook/algo.mdx b/pages/cookbook/algo.en.mdx similarity index 100% rename from pages/cookbook/algo.mdx rename to pages/cookbook/algo.en.mdx diff --git a/pages/cookbook/data-structures.mdx b/pages/cookbook/data-structures.en.mdx similarity index 100% rename from pages/cookbook/data-structures.mdx rename to pages/cookbook/data-structures.en.mdx diff --git a/pages/cookbook/dexes/_meta.en.json b/pages/cookbook/dexes/_meta.en.json new file mode 100644 index 00000000..c4da0851 --- /dev/null +++ b/pages/cookbook/dexes/_meta.en.json @@ -0,0 +1,4 @@ +{ + "dedust": "DeDust.io", + "stonfi": "STON.fi" +} diff --git a/pages/cookbook/dexes/_meta.js b/pages/cookbook/dexes/_meta.js deleted file mode 100644 index 66dba219..00000000 --- a/pages/cookbook/dexes/_meta.js +++ /dev/null @@ -1,4 +0,0 @@ -export default { - dedust: 'DeDust.io', - stonfi: 'STON.fi', -} diff --git a/pages/cookbook/dexes/dedust.mdx b/pages/cookbook/dexes/dedust.en.mdx similarity index 100% rename from pages/cookbook/dexes/dedust.mdx rename to pages/cookbook/dexes/dedust.en.mdx diff --git a/pages/cookbook/dexes/stonfi.mdx b/pages/cookbook/dexes/stonfi.en.mdx similarity index 100% rename from pages/cookbook/dexes/stonfi.mdx rename to pages/cookbook/dexes/stonfi.en.mdx diff --git a/pages/cookbook/index.mdx b/pages/cookbook/index.en.mdx similarity index 100% rename from pages/cookbook/index.mdx rename to pages/cookbook/index.en.mdx diff --git a/pages/cookbook/jettons.mdx b/pages/cookbook/jettons.en.mdx similarity index 100% rename from pages/cookbook/jettons.mdx rename to pages/cookbook/jettons.en.mdx diff --git a/pages/cookbook/misc.mdx b/pages/cookbook/misc.en.mdx similarity index 100% rename from pages/cookbook/misc.mdx rename to pages/cookbook/misc.en.mdx diff --git a/pages/cookbook/multi-communication.mdx b/pages/cookbook/multi-communication.en.mdx similarity index 100% rename from pages/cookbook/multi-communication.mdx rename to pages/cookbook/multi-communication.en.mdx diff --git a/pages/cookbook/nfts.mdx b/pages/cookbook/nfts.en.mdx similarity index 100% rename from pages/cookbook/nfts.mdx rename to pages/cookbook/nfts.en.mdx diff --git a/pages/cookbook/random.mdx b/pages/cookbook/random.en.mdx similarity index 100% rename from pages/cookbook/random.mdx rename to pages/cookbook/random.en.mdx diff --git a/pages/cookbook/single-communication.mdx b/pages/cookbook/single-communication.en.mdx similarity index 100% rename from pages/cookbook/single-communication.mdx rename to pages/cookbook/single-communication.en.mdx diff --git a/pages/cookbook/time.mdx b/pages/cookbook/time.en.mdx similarity index 100% rename from pages/cookbook/time.mdx rename to pages/cookbook/time.en.mdx diff --git a/pages/cookbook/type-conversion.mdx b/pages/cookbook/type-conversion.en.mdx similarity index 100% rename from pages/cookbook/type-conversion.mdx rename to pages/cookbook/type-conversion.en.mdx diff --git a/pages/ecosystem/_meta.en.json b/pages/ecosystem/_meta.en.json new file mode 100644 index 00000000..dd8e7708 --- /dev/null +++ b/pages/ecosystem/_meta.en.json @@ -0,0 +1,17 @@ +{ + "index": "Overview", + "tools": "Tools", + "-- Community": { + "type": "separator" + }, + "telegram-link": { + "title": "✈️ Telegram", + "href": "https://t.me/tactlang", + "newWindow": true + }, + "xtwitter-link": { + "title": "🐦 X/Twitter", + "href": "https://twitter.com/tact_language", + "newWindow": true + } +} diff --git a/pages/ecosystem/_meta.js b/pages/ecosystem/_meta.js deleted file mode 100644 index 54ac1e02..00000000 --- a/pages/ecosystem/_meta.js +++ /dev/null @@ -1,17 +0,0 @@ -export default { - index: 'Overview', - tools: 'Tools', - '-- Community': { - type: 'separator', - }, - 'telegram-link': { - title: '✈️ Telegram', - href: 'https://t.me/tactlang', - newWindow: true - }, - 'xtwitter-link': { - title: '🐦 X/Twitter', - href: 'https://twitter.com/tact_language', - newWindow: true - }, -} diff --git a/pages/ecosystem/index.mdx b/pages/ecosystem/index.en.mdx similarity index 100% rename from pages/ecosystem/index.mdx rename to pages/ecosystem/index.en.mdx diff --git a/pages/ecosystem/tools/_meta.en.json b/pages/ecosystem/tools/_meta.en.json new file mode 100644 index 00000000..450e718b --- /dev/null +++ b/pages/ecosystem/tools/_meta.en.json @@ -0,0 +1,6 @@ +{ + "overview": "Overview", + "typescript": "Typescript", + "vscode": "VS Code Extension", + "jetbrains": "JetBrains IDEs Plugin" +} diff --git a/pages/ecosystem/tools/_meta.js b/pages/ecosystem/tools/_meta.js deleted file mode 100644 index 66d35b74..00000000 --- a/pages/ecosystem/tools/_meta.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - overview: 'Overview', - typescript: 'Typescript', - vscode: 'VS Code Extension', - jetbrains: 'JetBrains IDEs Plugin' -} \ No newline at end of file diff --git a/pages/ecosystem/tools/jetbrains.mdx b/pages/ecosystem/tools/jetbrains.en.mdx similarity index 100% rename from pages/ecosystem/tools/jetbrains.mdx rename to pages/ecosystem/tools/jetbrains.en.mdx diff --git a/pages/ecosystem/tools/overview.mdx b/pages/ecosystem/tools/overview.en.mdx similarity index 100% rename from pages/ecosystem/tools/overview.mdx rename to pages/ecosystem/tools/overview.en.mdx diff --git a/pages/ecosystem/tools/typescript.mdx b/pages/ecosystem/tools/typescript.en.mdx similarity index 100% rename from pages/ecosystem/tools/typescript.mdx rename to pages/ecosystem/tools/typescript.en.mdx diff --git a/pages/ecosystem/tools/vscode.mdx b/pages/ecosystem/tools/vscode.en.mdx similarity index 100% rename from pages/ecosystem/tools/vscode.mdx rename to pages/ecosystem/tools/vscode.en.mdx diff --git a/pages/index.mdx b/pages/index.en.mdx similarity index 100% rename from pages/index.mdx rename to pages/index.en.mdx diff --git a/pages/index.zh-CN.mdx b/pages/index.zh-CN.mdx new file mode 100644 index 00000000..8cbe3754 --- /dev/null +++ b/pages/index.zh-CN.mdx @@ -0,0 +1,164 @@ +# 学习⚡ Tact 中的所有编程知识 + +import { Cards, Steps, Tabs } from 'nextra/components' + +![Tact横幅](public/banner.jpeg) + +Tact 是 TON 区块链的一种新编程语言,注重效率和简便性。 它设计得易于学习和使用,并且非常适合智能合约。 Tact 是一种静态类型语言,具有简单的语法和强大的类型系统。 + +## 让我们开始吧! [#开始] + + + +### 确保 Node.js LTS 已安装并可用 [#start-1] + +要检查它,请运行 `node --version{:shell}` --它应该会显示 18.0.0 或更高版本。 + +### 运行以下命令 [#start-2] + +它将创建一个带有简单计数器合同的新项目: + + + + ```shell + npm create ton -- simple-counter --type tact-counter --contractName SimpleCounter + ``` + + + ```shell + # recommended + yarn create ton simple-counter --type tact-counter --contractName SimpleCounter + ``` + + + ```shell + pnpm create ton simple-counter --type tact-counter --contractName SimpleCounter + ``` + + + +### 就是这样! [#开始-3] + +您的第一个合同项目已经编写完成! 进入相关目录查看 - `cd simple-counter/contracts{:shell}`。 + +这就是它的样子: + +```tact +import "@stdlib/deploy"; + +message Add { + queryId: Int as uint64; + amount: Int as uint32; +} + +contract SimpleCounter with Deployable { + id: Int as uint32; + counter: Int as uint32; + + init(id: Int) { + self.id = id; + self.counter = 0; + } + + receive(msg: Add) { + self.counter += msg.amount; + + // Notify the caller that the receiver was executed and forward remaining value back + self.notify("Cashback".asComment()); + } + + get fun counter(): Int { + return self.counter; + } + + get fun id(): Int { + return self.id; + } +} +``` + +要重新编译或部署,请参考新创建项目根目录中 `package.json` 脚本部分的命令,以及 [Blueprint](https://github.com/ton-org/blueprint) 的文档 - 这是我们用来在 Tact 中创建和编译第一个简单计数器合约的工具。 事实上,Blueprint 的功能远不止这些:包括测试、定制等。 + + + +## 下一步去哪里? [#下一页] + + + +### 已经掌握了一些区块链知识? [#下一页-1] + +请参阅 [Tact Cookbook](/cookbook),它是每个 Tact 开发人员在智能合约开发过程中面临的日常任务(和解决方案)的便捷集合。 使用它可以避免重复发明轮子。 + +此外,您也可以查看以下小抄,快速入门: + + + + + + +### 想了解更多吗? [#下一页-2] + +有关编译、测试和部署的进一步指导,请参阅 [开始](/book/guides/getting-started) 指南。 + +有关您最喜欢的编辑器的自定义插件和其他工具,请参阅[工具](/ecosystem/tools/overview) 页面。 + +或者,也可以查看以下更广泛的章节: + +- [书籍](/书籍) 帮助您逐步学习语言 +- [Cookbook](/cookbook)为您提供现成的 Tact 代码食谱 +- [参考资料](/ref) 提供了标准库、语法和演变过程的完整词汇表 +- 最后,[生态系统](/ecosystem) 描述了 Tacts 和 TONs 生态系统中的 "外面有什么"。 + + + + + + + + +### 感觉有点不舒服? [#下一页-3] + +如果您遇到困难,请随时联系 Tact 这个蓬勃发展的社区: + + + + + + +祝你在⚡ Tact 的编码冒险中好运! + + diff --git a/pages/ref/_meta.en.json b/pages/ref/_meta.en.json new file mode 100644 index 00000000..18bd8207 --- /dev/null +++ b/pages/ref/_meta.en.json @@ -0,0 +1,42 @@ +{ + "index": "Overview", + "spec": "Specification", + "evolution": "Evolution", + "-- Core library": { + "type": "separator", + "title": "Core library" + }, + "core-base": "Base trait", + "core-common": "Common", + "core-comptime": "Compile-time", + "core-debug": "Debug", + "core-random": "Random", + "core-math": "Math", + "core-strings": "Strings and StringBuilders", + "core-cells": "Cells, Builders and Slices", + "core-advanced": "Advanced", + "-- Stdlib": { + "type": "separator", + "title": "Standard libraries" + }, + "standard-libraries": "Overview", + "stdlib-config": "@stdlib/config", + "stdlib-content": "@stdlib/content", + "stdlib-deploy": "@stdlib/deploy", + "stdlib-dns": "@stdlib/dns", + "stdlib-ownable": "@stdlib/ownable", + "stdlib-stoppable": "@stdlib/stoppable", + "-- Community": { + "type": "separator" + }, + "telegram-link": { + "title": "✈️ Telegram", + "href": "https://t.me/tactlang", + "newWindow": true + }, + "xtwitter-link": { + "title": "🐦 X/Twitter", + "href": "https://twitter.com/tact_language", + "newWindow": true + } +} diff --git a/pages/ref/_meta.js b/pages/ref/_meta.js deleted file mode 100644 index 9ba1719e..00000000 --- a/pages/ref/_meta.js +++ /dev/null @@ -1,42 +0,0 @@ -export default { - index: 'Overview', - spec: 'Specification', - evolution: 'Evolution', - '-- Core library': { - type: 'separator', - title: 'Core library', - }, - 'core-base': 'Base trait', - 'core-common': 'Common', - 'core-comptime': 'Compile-time', - 'core-debug': 'Debug', - 'core-random': 'Random', - 'core-math': 'Math', - 'core-strings': 'Strings and StringBuilders', - 'core-cells': 'Cells, Builders and Slices', - 'core-advanced': 'Advanced', - '-- Stdlib': { - type: 'separator', - title: 'Standard libraries', - }, - 'standard-libraries': 'Overview', - 'stdlib-config': '@stdlib/config', - 'stdlib-content': '@stdlib/content', - 'stdlib-deploy': '@stdlib/deploy', - 'stdlib-dns': '@stdlib/dns', - 'stdlib-ownable': '@stdlib/ownable', - 'stdlib-stoppable': '@stdlib/stoppable', - '-- Community': { - type: 'separator', - }, - 'telegram-link': { - title: '✈️ Telegram', - href: 'https://t.me/tactlang', - newWindow: true - }, - 'xtwitter-link': { - title: '🐦 X/Twitter', - href: 'https://twitter.com/tact_language', - newWindow: true - }, -} diff --git a/pages/ref/core-advanced.mdx b/pages/ref/core-advanced.en.mdx similarity index 100% rename from pages/ref/core-advanced.mdx rename to pages/ref/core-advanced.en.mdx diff --git a/pages/ref/core-base.mdx b/pages/ref/core-base.en.mdx similarity index 100% rename from pages/ref/core-base.mdx rename to pages/ref/core-base.en.mdx diff --git a/pages/ref/core-cells.mdx b/pages/ref/core-cells.en.mdx similarity index 100% rename from pages/ref/core-cells.mdx rename to pages/ref/core-cells.en.mdx diff --git a/pages/ref/core-common.mdx b/pages/ref/core-common.en.mdx similarity index 100% rename from pages/ref/core-common.mdx rename to pages/ref/core-common.en.mdx diff --git a/pages/ref/core-comptime.mdx b/pages/ref/core-comptime.en.mdx similarity index 100% rename from pages/ref/core-comptime.mdx rename to pages/ref/core-comptime.en.mdx diff --git a/pages/ref/core-debug.mdx b/pages/ref/core-debug.en.mdx similarity index 100% rename from pages/ref/core-debug.mdx rename to pages/ref/core-debug.en.mdx diff --git a/pages/ref/core-math.mdx b/pages/ref/core-math.en.mdx similarity index 100% rename from pages/ref/core-math.mdx rename to pages/ref/core-math.en.mdx diff --git a/pages/ref/core-random.mdx b/pages/ref/core-random.en.mdx similarity index 100% rename from pages/ref/core-random.mdx rename to pages/ref/core-random.en.mdx diff --git a/pages/ref/core-strings.mdx b/pages/ref/core-strings.en.mdx similarity index 100% rename from pages/ref/core-strings.mdx rename to pages/ref/core-strings.en.mdx diff --git a/pages/ref/evolution/OTP-001.mdx b/pages/ref/evolution/OTP-001.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-001.mdx rename to pages/ref/evolution/OTP-001.en.mdx diff --git a/pages/ref/evolution/OTP-002.mdx b/pages/ref/evolution/OTP-002.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-002.mdx rename to pages/ref/evolution/OTP-002.en.mdx diff --git a/pages/ref/evolution/OTP-003.mdx b/pages/ref/evolution/OTP-003.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-003.mdx rename to pages/ref/evolution/OTP-003.en.mdx diff --git a/pages/ref/evolution/OTP-004.mdx b/pages/ref/evolution/OTP-004.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-004.mdx rename to pages/ref/evolution/OTP-004.en.mdx diff --git a/pages/ref/evolution/OTP-005.mdx b/pages/ref/evolution/OTP-005.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-005.mdx rename to pages/ref/evolution/OTP-005.en.mdx diff --git a/pages/ref/evolution/OTP-006.mdx b/pages/ref/evolution/OTP-006.en.mdx similarity index 100% rename from pages/ref/evolution/OTP-006.mdx rename to pages/ref/evolution/OTP-006.en.mdx diff --git a/pages/ref/evolution/_meta.en.json b/pages/ref/evolution/_meta.en.json new file mode 100644 index 00000000..dfe429ff --- /dev/null +++ b/pages/ref/evolution/_meta.en.json @@ -0,0 +1,9 @@ +{ + "overview": "Overview", + "OTP-001": "OTP-001: Supported Interfaces", + "OTP-002": "OTP-002: Contract ABI", + "OTP-003": "OTP-003: Self-ABI reporting", + "OTP-004": "OTP-004: Auto Encoder", + "OTP-005": "OTP-005: Argument-addressable contracts", + "OTP-006": "OTP-006: Contract Package" +} diff --git a/pages/ref/evolution/_meta.js b/pages/ref/evolution/_meta.js deleted file mode 100644 index 3e5a9ef4..00000000 --- a/pages/ref/evolution/_meta.js +++ /dev/null @@ -1,9 +0,0 @@ -export default { - overview: 'Overview', - 'OTP-001': 'OTP-001: Supported Interfaces', - 'OTP-002': 'OTP-002: Contract ABI', - 'OTP-003': 'OTP-003: Self-ABI reporting', - 'OTP-004': 'OTP-004: Auto Encoder', - 'OTP-005': 'OTP-005: Argument-addressable contracts', - 'OTP-006': 'OTP-006: Contract Package' -} diff --git a/pages/ref/evolution/overview.mdx b/pages/ref/evolution/overview.en.mdx similarity index 100% rename from pages/ref/evolution/overview.mdx rename to pages/ref/evolution/overview.en.mdx diff --git a/pages/ref/index.mdx b/pages/ref/index.en.mdx similarity index 100% rename from pages/ref/index.mdx rename to pages/ref/index.en.mdx diff --git a/pages/ref/spec.mdx b/pages/ref/spec.en.mdx similarity index 100% rename from pages/ref/spec.mdx rename to pages/ref/spec.en.mdx diff --git a/pages/ref/standard-libraries.mdx b/pages/ref/standard-libraries.en.mdx similarity index 100% rename from pages/ref/standard-libraries.mdx rename to pages/ref/standard-libraries.en.mdx diff --git a/pages/ref/stdlib-config.mdx b/pages/ref/stdlib-config.en.mdx similarity index 100% rename from pages/ref/stdlib-config.mdx rename to pages/ref/stdlib-config.en.mdx diff --git a/pages/ref/stdlib-content.mdx b/pages/ref/stdlib-content.en.mdx similarity index 100% rename from pages/ref/stdlib-content.mdx rename to pages/ref/stdlib-content.en.mdx diff --git a/pages/ref/stdlib-deploy.mdx b/pages/ref/stdlib-deploy.en.mdx similarity index 100% rename from pages/ref/stdlib-deploy.mdx rename to pages/ref/stdlib-deploy.en.mdx diff --git a/pages/ref/stdlib-dns.mdx b/pages/ref/stdlib-dns.en.mdx similarity index 100% rename from pages/ref/stdlib-dns.mdx rename to pages/ref/stdlib-dns.en.mdx diff --git a/pages/ref/stdlib-ownable.mdx b/pages/ref/stdlib-ownable.en.mdx similarity index 100% rename from pages/ref/stdlib-ownable.mdx rename to pages/ref/stdlib-ownable.en.mdx diff --git a/pages/ref/stdlib-stoppable.mdx b/pages/ref/stdlib-stoppable.en.mdx similarity index 100% rename from pages/ref/stdlib-stoppable.mdx rename to pages/ref/stdlib-stoppable.en.mdx diff --git a/scripts/I18nPathJson-generate.js b/scripts/I18nPathJson-generate.js new file mode 100644 index 00000000..726ee8f0 --- /dev/null +++ b/scripts/I18nPathJson-generate.js @@ -0,0 +1,39 @@ +import { dirname, join } from "path"; + +import * as fs from "fs"; + +const transferLangs = ["en", "zh-CN"]; + +const getDirFiles = (path = "") => { + const basePath = "/pages"; + const readDirFiles = fs.readdirSync(process.cwd() + basePath + path); + return { + dirs: readDirFiles.filter((dirName) => !dirName.includes(".")), + files: readDirFiles + .filter((dirName) => dirName.includes(".")) + .map((dirName) => path + "/" + dirName), + }; +}; + +const allPathFilter = (path = "", langPathObj) => { + let { dirs, files } = getDirFiles(path); + + dirs?.length > 0 && + dirs.forEach((dirName) => allPathFilter(`${path}/${dirName}`, langPathObj)); + + files?.length > 0 && + transferLangs.forEach((lang) => { + if (!Array.isArray(langPathObj[lang])) langPathObj[lang] = []; + const filterFiles = files.filter((fileName) => + fileName.includes(`.${lang}.`) + ); + if (filterFiles?.length > 0) langPathObj[lang].push(...filterFiles); + }); +}; + +(async () => { + const langPathObj = {}; + allPathFilter("", langPathObj); + + fs.writeFileSync("LangPath.json", JSON.stringify(langPathObj)); +})(); diff --git a/theme.config.jsx b/theme.config.jsx index 93435121..61bf34e8 100644 --- a/theme.config.jsx +++ b/theme.config.jsx @@ -1,5 +1,10 @@ import { useRouter } from 'next/router' -import { DocsThemeConfig } from 'nextra-theme-docs' +import { DocsThemeConfig, LocaleSwitch } from 'nextra-theme-docs' + +const PLACEHOLDER_LOCALES = { + en: "Search documentation", + "zh-CN": "搜索文档", +}; /** * @type {DocsThemeConfig} @@ -56,6 +61,44 @@ const config = { // }[locale ?? "en"] // } // }, + i18n: [ + { locale: "en", text: "English" }, + { locale: "zh-CN", text: "中文" }, + // { locale: "ru", text: "Русский" }, + // { locale: "ko", text: "한국어" }, + // { locale: "pl", text: "Polski" }, + // { locale: "uk", text: "Українська" }, + { + locale: "HelpTranslate", + text: ( + { + e.stopPropagation(); + }} + target='_blank' + style={{ + borderTop: "1px solid #4c4c4c", + display: "inline-block", + borderRadius: "0", + paddingTop: "6px", + }} + > + Help Us Translate + + ), + }, + ], + search: { + placeholder: function usePlaceholder() { + const { locale, defaultLocale = DEFAULT_LOCALE } = useRouter(); + const text = + (locale && PLACEHOLDER_LOCALES[locale]) || + PLACEHOLDER_LOCALES[defaultLocale] || + "Search documentation"; + return `${text}…`; + }, + }, footer: { text: CC BY 4.0, Tact Software Foundation @@ -92,7 +135,16 @@ const config = { }} />; - ) + ), + navbar: { + extraContent: (props) => { + return ( +
+ +
+ ); + }, + }, } export default config