diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3d11ce08 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.github/ +.next/ +.vscode/ +db/ +node_modules/ + +docker-compose.yml +.prettier* diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..191c0a5f --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,21 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch via NPM", + "request": "launch", + "runtimeArgs": [ + "run-script", + "dev" + ], + "runtimeExecutable": "npm", + "skipFiles": [ + "/**" + ], + "type": "node" + }, + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..0eed48f0 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "spellright.language": [ + "en_US", + "ru_RU" + ], + "spellright.documentTypes": [ + "markdown", + "latex", + "plaintext", + "json" + ] +} \ No newline at end of file diff --git a/.vscode/spellright.dict b/.vscode/spellright.dict new file mode 100644 index 00000000..032fdd98 --- /dev/null +++ b/.vscode/spellright.dict @@ -0,0 +1,25 @@ +Бонусные +Pearlbrook +АМБАССАДОРЫ +реактивировать +Monastary +Ветко +ВП +Амбассадора +Санблейз +Sunblaze +токена +Everdell +Hopewatch +Mistrise +Омикрон +Снаут +Iluminor +Иллюминор +Crustina +Крустина +Босли +Bosley +Seaglass +realtime +Амбассадоров diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..daff47d5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,29 @@ +# Build stage +FROM node:16-alpine AS builder + +WORKDIR /app + +COPY package*.json ./ +RUN npm install + +COPY . . +RUN npm run build:ci + +# Production stage +FROM node:16-alpine AS production + +WORKDIR /app + +COPY --from=builder /app/.next /app/.next +COPY --from=builder /app/public /app/public +COPY --from=builder /app/package.json /app/package.json +COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder /app/next.config.js ./next.config.js +COPY --from=builder /app/next-i18next.config.js ./next-i18next.config.js + +# Install only production dependencies +RUN npm install --production + +EXPOSE 3000 + +CMD ["npm", "run", "start"] diff --git a/db/.gitignore b/db/.gitignore index 26216325..1f6a5b45 100644 --- a/db/.gitignore +++ b/db/.gitignore @@ -1,2 +1,3 @@ *.sqlite *.db +*.csv diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..433dd45d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,18 @@ +version: "3.8" + +services: + everdell: + build: + context: . + target: production + ports: + - "3010:3000" + volumes: + - everdell-db:/app/db # Mount the volume at /app/db inside the container + environment: + - DB_PATH=/app/db/game.sqlite # Update DB_PATH to use the mounted volume + - PORT=3000 + # entrypoint: ["/bin/sleep", "infinty"] + +volumes: + everdell-db: # Named volume for the database diff --git a/locale_helper.js b/locale_helper.js new file mode 100644 index 00000000..a260e711 --- /dev/null +++ b/locale_helper.js @@ -0,0 +1,62 @@ +// Made by ChatGPT. To copy missing keys from ru-RU to pt-BR run it like this: +// +// node locale_helper.js public/locales/{ru-RU,pt-BR}/common.json + +const fs = require('fs'); +const path = require('path'); + +// Get arguments from the command line +const args = process.argv.slice(2); + +if (args.length < 2) { + console.error('Usage: node script.js '); + process.exit(1); +} + +const [filePath1, filePath2] = args.map(arg => path.resolve(arg)); + +// Helper function to load a JSON file +function loadJSON(filePath) { + try { + const data = fs.readFileSync(filePath, 'utf-8'); + return JSON.parse(data); + } catch (error) { + console.error(`Error loading JSON file at ${filePath}:`, error); + process.exit(1); + } +} + +// Helper function to save a JSON file +function saveJSON(filePath, data) { + try { + fs.writeFileSync(filePath, JSON.stringify(data, null, 2)); + console.log(`File saved successfully at ${filePath}`); + } catch (error) { + console.error(`Error saving JSON file at ${filePath}:`, error); + } +} + +// Main function to add missing keys +function addMissingKeys() { + const json1 = loadJSON(filePath1); + const json2 = loadJSON(filePath2); + + const keys1 = Object.keys(json1); + let modified = false; + + keys1.forEach((key) => { + if (!(key in json2)) { + json2[key] = key; // Set the key's name as its own value + modified = true; + console.log(`Added missing key: ${key}`); + } + }); + + if (modified) { + saveJSON(filePath2, json2); + } else { + console.log('No missing keys were found.'); + } +} + +addMissingKeys(); diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 00000000..5b94caa9 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,8 @@ +/** @type {import('next-i18next').UserConfig} */ +module.exports = { + i18n: { + defaultLocale: "en-US", + locales: ["en-US", "ru-RU", "pt-BR"], + localeDetection: false, + }, +}; diff --git a/next.config.js b/next.config.js new file mode 100644 index 00000000..b901ff55 --- /dev/null +++ b/next.config.js @@ -0,0 +1,5 @@ +const { i18n } = require("./next-i18next.config"); + +module.exports = { + i18n, +}; diff --git a/package-lock.json b/package-lock.json index 0a53f58b..90f4fe1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,15 +12,21 @@ "eslint-config-next": "^13.0.5", "expect.js": "^0.3.1", "formik": "^2.2.6", + "i18next": "^23.16.3", "lodash": "^4.17.20", "mocha": "^10.2.0", "next": "^13.0.5", + "next-i18next": "^15.3.1", + "next-themes": "^0.3.0", "nodemon": "^2.0.7", "pg": "^8.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^15.1.0", + "react-icons": "^5.3.0", "short-uuid": "^4.1.0", - "sqlite3": "^5.1.2" + "sqlite3": "^5.1.2", + "use-local-storage": "^3.0.0" }, "devDependencies": { "@types/expect.js": "^0.3.29", @@ -52,32 +58,33 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.9.tgz", + "integrity": "sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==", "dependencies": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.14.0" }, "engines": { "node": ">=6.9.0" @@ -95,6 +102,11 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/runtime/node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -231,9 +243,9 @@ } }, "node_modules/@next/env": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5.tgz", - "integrity": "sha512-F3KLtiDrUslAZhTYTh8Zk5ZaavbYwLUn3NYPBnOjAXU8hWm0QVGVzKIOuURQ098ofRU4e9oglf3Sj9pFx5nI5w==" + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.7.tgz", + "integrity": "sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA==" }, "node_modules/@next/eslint-plugin-next": { "version": "13.0.5", @@ -262,40 +274,10 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@next/swc-android-arm-eabi": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5.tgz", - "integrity": "sha512-YO691dxHlviy6H0eghgwqn+5kU9J3iQnKERHTDSppqjjGDBl6ab4wz9XfI5AhljjkaTg3TknHoIEWFDoZ4Ve8g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-android-arm64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5.tgz", - "integrity": "sha512-ugbwffkUmp8cd2afehDC8LtQeFUxElRUBBngfB5UYSWBx18HW4OgzkPFIY8jUBH16zifvGZWXbICXJWDHrOLtw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-darwin-arm64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5.tgz", - "integrity": "sha512-mshlh8QOtOalfZbc17uNAftWgqHTKnrv6QUwBe+mpGz04eqsSUzVz1JGZEdIkmuDxOz00cK2NPoc+VHDXh99IQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.7.tgz", + "integrity": "sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g==", "cpu": [ "arm64" ], @@ -308,9 +290,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5.tgz", - "integrity": "sha512-SfigOKW4Z2UB3ruUPyvrlDIkcJq1hiw1wvYApWugD+tQsAkYZKEoz+/8emCmeYZ6Gwgi1WHV+z52Oj8u7bEHPg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.7.tgz", + "integrity": "sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g==", "cpu": [ "x64" ], @@ -322,40 +304,10 @@ "node": ">= 10" } }, - "node_modules/@next/swc-freebsd-x64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5.tgz", - "integrity": "sha512-0NJg8HZr4yG8ynmMGFXQf+Mahvq4ZgBmUwSlLXXymgxEQgH17erH/LoR69uITtW+KTsALgk9axEt5AAabM4ucg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm-gnueabihf": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5.tgz", - "integrity": "sha512-Cye+h3oDT3NDWjACMlRaolL8fokpKie34FlPj9nfoW7bYKmoMBY1d4IO/GgBF+5xEl7HkH0Ny/qex63vQ0pN+A==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5.tgz", - "integrity": "sha512-5BfDS/VoRDR5QUGG9oedOCEZGmV2zxUVFYLUJVPMSMeIgqkjxWQBiG2BUHZI6/LGk9yvHmjx7BTvtBCLtRg6IQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.7.tgz", + "integrity": "sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg==", "cpu": [ "arm64" ], @@ -368,9 +320,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5.tgz", - "integrity": "sha512-xenvqlXz+KxVKAB1YR723gnVNszpsCvKZkiFFaAYqDGJ502YuqU2fwLsaSm/ASRizNcBYeo9HPLTyc3r/9cdMQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.7.tgz", + "integrity": "sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg==", "cpu": [ "arm64" ], @@ -383,9 +335,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5.tgz", - "integrity": "sha512-9Ahi1bbdXwhrWQmOyoTod23/hhK05da/FzodiNqd6drrMl1y7+RujoEcU8Dtw3H1mGWB+yuTlWo8B4Iba8hqiQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.7.tgz", + "integrity": "sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw==", "cpu": [ "x64" ], @@ -398,9 +350,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5.tgz", - "integrity": "sha512-V+1mnh49qmS9fOZxVRbzjhBEz9IUGJ7AQ80JPWAYQM5LI4TxfdiF4APLPvJ52rOmNeTqnVz1bbKtVOso+7EZ4w==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.7.tgz", + "integrity": "sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA==", "cpu": [ "x64" ], @@ -413,9 +365,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5.tgz", - "integrity": "sha512-wRE9rkp7I+/3Jf2T9PFIJOKq3adMWYEFkPOA7XAkUfYbQHlDJm/U5cVCWUsKByyQq5RThwufI91sgd19MfxRxg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.7.tgz", + "integrity": "sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA==", "cpu": [ "arm64" ], @@ -428,9 +380,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5.tgz", - "integrity": "sha512-Q1XQSLEhFuFhkKFdJIGt7cYQ4T3u6P5wrtUNreg5M+7P+fjSiC8+X+Vjcw+oebaacsdl0pWZlK+oACGafush1w==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.7.tgz", + "integrity": "sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA==", "cpu": [ "ia32" ], @@ -443,9 +395,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5.tgz", - "integrity": "sha512-t5gRblrwwiNZP6cT7NkxlgxrFgHWtv9ei5vUraCLgBqzvIsa7X+PnarZUeQCXqz6Jg9JSGGT9j8lvzD97UqeJQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.7.tgz", + "integrity": "sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g==", "cpu": [ "x64" ], @@ -555,9 +507,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "node_modules/@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "dependencies": { "tslib": "^2.4.0" } @@ -582,6 +534,15 @@ "integrity": "sha1-KN01kVW4S47LCUr8P0t0wyItyjs=", "dev": true }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", @@ -623,8 +584,7 @@ "node_modules/@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "dev": true + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "node_modules/@types/puppeteer": { "version": "5.4.2", @@ -639,7 +599,6 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", - "dev": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -1194,9 +1153,9 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, "node_modules/axe-core": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", - "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.1.tgz", + "integrity": "sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==", "engines": { "node": ">=4" } @@ -1326,6 +1285,17 @@ "node": ">=4" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -1407,9 +1377,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==", + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==", "funding": [ { "type": "opencollective", @@ -1418,6 +1388,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ] }, @@ -1738,6 +1712,16 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, + "node_modules/core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==", + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, "node_modules/core-js-pure": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", @@ -1769,8 +1753,7 @@ "node_modules/csstype": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", - "dev": true + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" }, "node_modules/cypress": { "version": "12.7.0", @@ -2174,9 +2157,9 @@ } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "engines": { "node": ">=6" } @@ -2900,14 +2883,6 @@ "node": ">=4" } }, - "node_modules/executable/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/expect.js": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz", @@ -2951,9 +2926,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -3127,9 +3102,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "hasInstallScript": true, "optional": true, "os": [ @@ -3140,9 +3115,12 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/function.prototype.name": { "version": "1.1.5", @@ -3318,6 +3296,11 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, "node_modules/global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -3448,6 +3431,17 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3464,6 +3458,14 @@ "react-is": "^16.7.0" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -3526,6 +3528,33 @@ "ms": "^2.0.0" } }, + "node_modules/i18next": { + "version": "23.16.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.3.tgz", + "integrity": "sha512-e8q9gFyjrou5v/hBXgRwtWVK7Gp5So19Kf42spJlijGDfkin5abYFHlblonmD2GFYcsf9XIT7MpVuveJq/VCcA==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, + "node_modules/i18next-fs-backend": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.3.2.tgz", + "integrity": "sha512-LIwUlkqDZnUI8lnUxBnEj8K/FrHQTT/Sc+1rvDm9E8YvvY5YxzoEAASNx+W5M9DfD5s77lI5vSAFWeTp26B/3Q==" + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -3558,9 +3587,9 @@ ] }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } @@ -3700,11 +3729,14 @@ } }, "node_modules/is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4673,9 +4705,15 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -4698,49 +4736,43 @@ } }, "node_modules/next": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/next/-/next-13.0.5.tgz", - "integrity": "sha512-awpc3DkphyKydwCotcBnuKwh6hMqkT5xdiBK4OatJtOZurDPBYLP62jtM2be/4OunpmwIbsS0Eyv+ZGU97ciEg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/next/-/next-13.5.7.tgz", + "integrity": "sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg==", "dependencies": { - "@next/env": "13.0.5", - "@swc/helpers": "0.4.14", + "@next/env": "13.5.7", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.0" + "postcss": "8.4.31", + "styled-jsx": "5.1.1", + "watchpack": "2.4.0" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=14.6.0" + "node": ">=16.14.0" }, "optionalDependencies": { - "@next/swc-android-arm-eabi": "13.0.5", - "@next/swc-android-arm64": "13.0.5", - "@next/swc-darwin-arm64": "13.0.5", - "@next/swc-darwin-x64": "13.0.5", - "@next/swc-freebsd-x64": "13.0.5", - "@next/swc-linux-arm-gnueabihf": "13.0.5", - "@next/swc-linux-arm64-gnu": "13.0.5", - "@next/swc-linux-arm64-musl": "13.0.5", - "@next/swc-linux-x64-gnu": "13.0.5", - "@next/swc-linux-x64-musl": "13.0.5", - "@next/swc-win32-arm64-msvc": "13.0.5", - "@next/swc-win32-ia32-msvc": "13.0.5", - "@next/swc-win32-x64-msvc": "13.0.5" + "@next/swc-darwin-arm64": "13.5.7", + "@next/swc-darwin-x64": "13.5.7", + "@next/swc-linux-arm64-gnu": "13.5.7", + "@next/swc-linux-arm64-musl": "13.5.7", + "@next/swc-linux-x64-gnu": "13.5.7", + "@next/swc-linux-x64-musl": "13.5.7", + "@next/swc-win32-arm64-msvc": "13.5.7", + "@next/swc-win32-ia32-msvc": "13.5.7", + "@next/swc-win32-x64-msvc": "13.5.7" }, "peerDependencies": { - "fibers": ">= 3.1.0", - "node-sass": "^6.0.0 || ^7.0.0", + "@opentelemetry/api": "^1.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { - "fibers": { - "optional": true - }, - "node-sass": { + "@opentelemetry/api": { "optional": true }, "sass": { @@ -4748,6 +4780,50 @@ } } }, + "node_modules/next-i18next": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/next-i18next/-/next-i18next-15.3.1.tgz", + "integrity": "sha512-+pa2pZJb7B6k5PKW3TLVMmAodqkNaOBWVYlpWX56mgcEJz0UMW+MKSdKM9Z72CHp6Bp48g7OWwDnLqxXNp/84w==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://locize.com" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2", + "@types/hoist-non-react-statics": "^3.3.4", + "core-js": "^3", + "hoist-non-react-statics": "^3.3.2", + "i18next-fs-backend": "^2.3.2" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "i18next": ">= 23.7.13", + "next": ">= 12.0.0", + "react": ">= 17.0.2", + "react-i18next": ">= 13.5.0" + } + }, + "node_modules/next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18", + "react-dom": "^16.8 || ^17 || ^18" + } + }, "node_modules/node-addon-api": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", @@ -5304,9 +5380,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5319,10 +5395,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "funding": [ { "type": "opencollective", @@ -5331,10 +5415,14 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "dependencies": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -5546,6 +5634,35 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "node_modules/react-i18next": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.0.tgz", + "integrity": "sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==", + "dependencies": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "peerDependencies": { + "react": "*" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5632,11 +5749,11 @@ } }, "node_modules/resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dependencies": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -5989,9 +6106,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -6077,6 +6194,14 @@ "node": ">= 8" } }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -6189,9 +6314,9 @@ } }, "node_modules/styled-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", - "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "dependencies": { "client-only": "0.0.1" }, @@ -6605,6 +6730,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-local-storage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/use-local-storage/-/use-local-storage-3.0.0.tgz", + "integrity": "sha512-wlPNnBCG3ULIJMr5A+dvWqLiPWCfsN1Kwijq+sAhT5yV4ex0u6XmZuNwP+RerIOfzBuz1pwSZuzhZMiluGQHfQ==", + "peerDependencies": { + "react": ">=16.8.1" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -6636,6 +6769,26 @@ "extsprintf": "^1.2.0" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -6895,26 +7048,34 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==" }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.25.9.tgz", + "integrity": "sha512-llL88JShoCsth8fF8R4SJnIn+WLvR6ccFxu1H3FlMhDontdcmZWf2HgIZ7AIqV3Xcck1idlohrN4EUBQz6klbw==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "@babel/helper-validator-identifier": "^7.25.9", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" } }, "@babel/runtime": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", - "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.9.tgz", + "integrity": "sha512-4zpTHZ9Cm6L9L+uIqghQX8ZXg8HKFcjYO3qHoO8zTmRm6HQUJ8SSJ+KRvbMBZn0EGVlT4DRYeQ/6hjlyXBh+Kg==", "requires": { - "regenerator-runtime": "^0.13.10" + "regenerator-runtime": "^0.14.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + } } }, "@babel/runtime-corejs3": { @@ -7042,9 +7203,9 @@ } }, "@next/env": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.0.5.tgz", - "integrity": "sha512-F3KLtiDrUslAZhTYTh8Zk5ZaavbYwLUn3NYPBnOjAXU8hWm0QVGVzKIOuURQ098ofRU4e9oglf3Sj9pFx5nI5w==" + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.7.tgz", + "integrity": "sha512-uVuRqoj28Ys/AI/5gVEgRAISd0KWI0HRjOO1CTpNgmX3ZsHb5mdn14Y59yk0IxizXdo7ZjsI2S7qbWnO+GNBcA==" }, "@next/eslint-plugin-next": { "version": "13.0.5", @@ -7069,82 +7230,58 @@ } } }, - "@next/swc-android-arm-eabi": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.5.tgz", - "integrity": "sha512-YO691dxHlviy6H0eghgwqn+5kU9J3iQnKERHTDSppqjjGDBl6ab4wz9XfI5AhljjkaTg3TknHoIEWFDoZ4Ve8g==", - "optional": true - }, - "@next/swc-android-arm64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-android-arm64/-/swc-android-arm64-13.0.5.tgz", - "integrity": "sha512-ugbwffkUmp8cd2afehDC8LtQeFUxElRUBBngfB5UYSWBx18HW4OgzkPFIY8jUBH16zifvGZWXbICXJWDHrOLtw==", - "optional": true - }, "@next/swc-darwin-arm64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.5.tgz", - "integrity": "sha512-mshlh8QOtOalfZbc17uNAftWgqHTKnrv6QUwBe+mpGz04eqsSUzVz1JGZEdIkmuDxOz00cK2NPoc+VHDXh99IQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.7.tgz", + "integrity": "sha512-7SxmxMex45FvKtRoP18eftrDCMyL6WQVYJSEE/s7A1AW/fCkznxjEShKet2iVVzf89gWp8HbXGaL4hCaseux6g==", "optional": true }, "@next/swc-darwin-x64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.5.tgz", - "integrity": "sha512-SfigOKW4Z2UB3ruUPyvrlDIkcJq1hiw1wvYApWugD+tQsAkYZKEoz+/8emCmeYZ6Gwgi1WHV+z52Oj8u7bEHPg==", - "optional": true - }, - "@next/swc-freebsd-x64": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.5.tgz", - "integrity": "sha512-0NJg8HZr4yG8ynmMGFXQf+Mahvq4ZgBmUwSlLXXymgxEQgH17erH/LoR69uITtW+KTsALgk9axEt5AAabM4ucg==", - "optional": true - }, - "@next/swc-linux-arm-gnueabihf": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.5.tgz", - "integrity": "sha512-Cye+h3oDT3NDWjACMlRaolL8fokpKie34FlPj9nfoW7bYKmoMBY1d4IO/GgBF+5xEl7HkH0Ny/qex63vQ0pN+A==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.7.tgz", + "integrity": "sha512-6iENvgyIkGFLFszBL4b1VfEogKC3TDPEB6/P/lgxmgXVXIV09Q4or1MVn+U/tYyYmm7oHMZ3oxGpHAyJ80nA6g==", "optional": true }, "@next/swc-linux-arm64-gnu": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.5.tgz", - "integrity": "sha512-5BfDS/VoRDR5QUGG9oedOCEZGmV2zxUVFYLUJVPMSMeIgqkjxWQBiG2BUHZI6/LGk9yvHmjx7BTvtBCLtRg6IQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.7.tgz", + "integrity": "sha512-P42jDX56wu9zEdVI+Xv4zyTeXB3DpqgE1Gb4bWrc0s2RIiDYr6uKBprnOs1hCGIwfVyByxyTw5Va66QCdFFNUg==", "optional": true }, "@next/swc-linux-arm64-musl": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.5.tgz", - "integrity": "sha512-xenvqlXz+KxVKAB1YR723gnVNszpsCvKZkiFFaAYqDGJ502YuqU2fwLsaSm/ASRizNcBYeo9HPLTyc3r/9cdMQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.7.tgz", + "integrity": "sha512-A06vkj+8X+tLRzSja5REm/nqVOCzR+x5Wkw325Q/BQRyRXWGCoNbQ6A+BR5M86TodigrRfI3lUZEKZKe3QJ9Bg==", "optional": true }, "@next/swc-linux-x64-gnu": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.5.tgz", - "integrity": "sha512-9Ahi1bbdXwhrWQmOyoTod23/hhK05da/FzodiNqd6drrMl1y7+RujoEcU8Dtw3H1mGWB+yuTlWo8B4Iba8hqiQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.7.tgz", + "integrity": "sha512-UdHm7AlxIbdRdMsK32cH0EOX4OmzAZ4Xm+UVlS0YdvwLkI3pb7AoBEoVMG5H0Wj6Wpz6GNkrFguHTRLymTy6kw==", "optional": true }, "@next/swc-linux-x64-musl": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.5.tgz", - "integrity": "sha512-V+1mnh49qmS9fOZxVRbzjhBEz9IUGJ7AQ80JPWAYQM5LI4TxfdiF4APLPvJ52rOmNeTqnVz1bbKtVOso+7EZ4w==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.7.tgz", + "integrity": "sha512-c50Y8xBKU16ZGj038H6C13iedRglxvdQHD/1BOtes56gwUrIRDX2Nkzn3mYtpz3Wzax0gfAF9C0Nqljt93IxvA==", "optional": true }, "@next/swc-win32-arm64-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.5.tgz", - "integrity": "sha512-wRE9rkp7I+/3Jf2T9PFIJOKq3adMWYEFkPOA7XAkUfYbQHlDJm/U5cVCWUsKByyQq5RThwufI91sgd19MfxRxg==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.7.tgz", + "integrity": "sha512-NcUx8cmkA+JEp34WNYcKW6kW2c0JBhzJXIbw+9vKkt9m/zVJ+KfizlqmoKf04uZBtzFN6aqE2Fyv2MOd021WIA==", "optional": true }, "@next/swc-win32-ia32-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.5.tgz", - "integrity": "sha512-Q1XQSLEhFuFhkKFdJIGt7cYQ4T3u6P5wrtUNreg5M+7P+fjSiC8+X+Vjcw+oebaacsdl0pWZlK+oACGafush1w==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.7.tgz", + "integrity": "sha512-wXp+/3NVcuyJDED6gJiLXs5dqHaWO7moAB6aBtjlKZvsxBDxpcyjsfRbtHPeYtaT20zCkmPs69H0K25lrVZmlA==", "optional": true }, "@next/swc-win32-x64-msvc": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.5.tgz", - "integrity": "sha512-t5gRblrwwiNZP6cT7NkxlgxrFgHWtv9ei5vUraCLgBqzvIsa7X+PnarZUeQCXqz6Jg9JSGGT9j8lvzD97UqeJQ==", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.7.tgz", + "integrity": "sha512-PLyD3Dl6jTTkLG8AoqhPGd5pXtSs8wbqIhWPQt3yEMfnYld/dGYuF2YPs3YHaVFrijCIF9pXY3+QOyvP23Zn7g==", "optional": true }, "@nodelib/fs.scandir": { @@ -7224,9 +7361,9 @@ "integrity": "sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==" }, "@swc/helpers": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz", - "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", + "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", "requires": { "tslib": "^2.4.0" }, @@ -7250,6 +7387,15 @@ "integrity": "sha1-KN01kVW4S47LCUr8P0t0wyItyjs=", "dev": true }, + "@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "requires": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", @@ -7291,8 +7437,7 @@ "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", - "dev": true + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/puppeteer": { "version": "5.4.2", @@ -7307,7 +7452,6 @@ "version": "17.0.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", - "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -7690,9 +7834,9 @@ "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, "axe-core": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.5.2.tgz", - "integrity": "sha512-u2MVsXfew5HBvjsczCv+xlwdNnB1oQR9HlAcsejZttNjKKSkeDNVwB1vMThIUIFI9GoT57Vtk8iQLwqOfAkboA==" + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.1.tgz", + "integrity": "sha512-qPC9o+kD8Tir0lzNGLeghbOrWMr3ZJpaRlCIb6Uobt/7N4FiEDvqUMnxzCHRHmg8vOg14kr5gVNyScRmbMaJ9g==" }, "axobject-query": { "version": "2.2.0", @@ -7779,6 +7923,14 @@ "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==" }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "requires": { + "streamsearch": "^1.1.0" + } + }, "cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -7838,9 +7990,9 @@ "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==" }, "caniuse-lite": { - "version": "1.0.30001434", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001434.tgz", - "integrity": "sha512-aOBHrLmTQw//WFa2rcF1If9fa3ypkC1wzqqiKHgfdrXTWcU8C4gKVZT77eQAPWN1APys3+uQ0Df07rKauXGEYA==" + "version": "1.0.30001669", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", + "integrity": "sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w==" }, "caseless": { "version": "0.12.0", @@ -8076,6 +8228,11 @@ "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, + "core-js": { + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.38.1.tgz", + "integrity": "sha512-OP35aUorbU3Zvlx7pjsFdu1rGNnD4pgw/CWoYzRY3t2EzoVT7shKHY1dlAy3f41cGIO7ZDPQimhGFTlEYkG/Hw==" + }, "core-js-pure": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.26.1.tgz", @@ -8099,8 +8256,7 @@ "csstype": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", - "dev": true + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" }, "cypress": { "version": "12.7.0", @@ -8409,9 +8565,9 @@ "dev": true }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" }, "escape-string-regexp": { "version": "1.0.5", @@ -8917,13 +9073,6 @@ "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", "requires": { "pify": "^2.2.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - } } }, "expect.js": { @@ -8958,9 +9107,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -9092,15 +9241,15 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" }, "function.prototype.name": { "version": "1.1.5", @@ -9230,6 +9379,11 @@ "is-glob": "^4.0.1" } }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + }, "global-dirs": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", @@ -9318,6 +9472,14 @@ "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -9331,6 +9493,14 @@ "react-is": "^16.7.0" } }, + "html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "requires": { + "void-elements": "3.1.0" + } + }, "http-cache-semantics": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", @@ -9381,6 +9551,19 @@ "ms": "^2.0.0" } }, + "i18next": { + "version": "23.16.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.16.3.tgz", + "integrity": "sha512-e8q9gFyjrou5v/hBXgRwtWVK7Gp5So19Kf42spJlijGDfkin5abYFHlblonmD2GFYcsf9XIT7MpVuveJq/VCcA==", + "requires": { + "@babel/runtime": "^7.23.2" + } + }, + "i18next-fs-backend": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/i18next-fs-backend/-/i18next-fs-backend-2.3.2.tgz", + "integrity": "sha512-LIwUlkqDZnUI8lnUxBnEj8K/FrHQTT/Sc+1rvDm9E8YvvY5YxzoEAASNx+W5M9DfD5s77lI5vSAFWeTp26B/3Q==" + }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -9396,9 +9579,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" }, "ignore-by-default": { "version": "1.0.1", @@ -9499,11 +9682,11 @@ } }, "is-core-module": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", - "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "requires": { - "has": "^1.0.3" + "hasown": "^2.0.2" } }, "is-date-object": { @@ -10193,9 +10376,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", - "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==" + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==" }, "natural-compare": { "version": "1.4.0", @@ -10209,30 +10392,46 @@ "optional": true }, "next": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/next/-/next-13.0.5.tgz", - "integrity": "sha512-awpc3DkphyKydwCotcBnuKwh6hMqkT5xdiBK4OatJtOZurDPBYLP62jtM2be/4OunpmwIbsS0Eyv+ZGU97ciEg==", - "requires": { - "@next/env": "13.0.5", - "@next/swc-android-arm-eabi": "13.0.5", - "@next/swc-android-arm64": "13.0.5", - "@next/swc-darwin-arm64": "13.0.5", - "@next/swc-darwin-x64": "13.0.5", - "@next/swc-freebsd-x64": "13.0.5", - "@next/swc-linux-arm-gnueabihf": "13.0.5", - "@next/swc-linux-arm64-gnu": "13.0.5", - "@next/swc-linux-arm64-musl": "13.0.5", - "@next/swc-linux-x64-gnu": "13.0.5", - "@next/swc-linux-x64-musl": "13.0.5", - "@next/swc-win32-arm64-msvc": "13.0.5", - "@next/swc-win32-ia32-msvc": "13.0.5", - "@next/swc-win32-x64-msvc": "13.0.5", - "@swc/helpers": "0.4.14", + "version": "13.5.7", + "resolved": "https://registry.npmjs.org/next/-/next-13.5.7.tgz", + "integrity": "sha512-W7KIRTE+hPcgGdq89P3mQLDX3m7pJ6nxSyC+YxYaUExE+cS4UledB+Ntk98tKoyhsv6fjb2TRAnD7VDvoqmeFg==", + "requires": { + "@next/env": "13.5.7", + "@next/swc-darwin-arm64": "13.5.7", + "@next/swc-darwin-x64": "13.5.7", + "@next/swc-linux-arm64-gnu": "13.5.7", + "@next/swc-linux-arm64-musl": "13.5.7", + "@next/swc-linux-x64-gnu": "13.5.7", + "@next/swc-linux-x64-musl": "13.5.7", + "@next/swc-win32-arm64-msvc": "13.5.7", + "@next/swc-win32-ia32-msvc": "13.5.7", + "@next/swc-win32-x64-msvc": "13.5.7", + "@swc/helpers": "0.5.2", + "busboy": "1.6.0", "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.0" + "postcss": "8.4.31", + "styled-jsx": "5.1.1", + "watchpack": "2.4.0" } }, + "next-i18next": { + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/next-i18next/-/next-i18next-15.3.1.tgz", + "integrity": "sha512-+pa2pZJb7B6k5PKW3TLVMmAodqkNaOBWVYlpWX56mgcEJz0UMW+MKSdKM9Z72CHp6Bp48g7OWwDnLqxXNp/84w==", + "requires": { + "@babel/runtime": "^7.23.2", + "@types/hoist-non-react-statics": "^3.3.4", + "core-js": "^3", + "hoist-non-react-statics": "^3.3.2", + "i18next-fs-backend": "^2.3.2" + } + }, + "next-themes": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.3.0.tgz", + "integrity": "sha512-/QHIrsYpd6Kfk7xakK4svpDI5mmXP0gfvCoJdGpZQ2TOrQZmsW0QxjaiLn8wbIKjtm4BTSqLoix4lxYYOnLJ/w==", + "requires": {} + }, "node-addon-api": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", @@ -10636,21 +10835,26 @@ } }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==" + }, "postcss": { - "version": "8.4.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", - "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", "requires": { - "nanoid": "^3.3.4", + "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } @@ -10797,6 +11001,21 @@ "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-2.0.4.tgz", "integrity": "sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw==" }, + "react-i18next": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.1.0.tgz", + "integrity": "sha512-zj3nJynMnZsy2gPZiOTC7XctCY5eQGqT3tcKMmfJWC9FMvgd+960w/adq61j8iPzpwmsXejqID9qC3Mqu1Xu2Q==", + "requires": { + "@babel/runtime": "^7.25.0", + "html-parse-stringify": "^3.0.1" + } + }, + "react-icons": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.3.0.tgz", + "integrity": "sha512-DnUk8aFbTyQPSkCfF8dbX6kQjXA9DktMeJqfjrg6cK9vwQVMxmcA3BfP4QoiztVmEHtwlTgLFsPuH2NskKT6eg==", + "requires": {} + }, "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -10859,11 +11078,11 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, "resolve": { - "version": "1.22.1", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", - "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "requires": { - "is-core-module": "^2.9.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" } @@ -11107,9 +11326,9 @@ "dev": true }, "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==" }, "source-map-support": { "version": "0.5.19", @@ -11170,6 +11389,11 @@ "minipass": "^3.1.1" } }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -11257,9 +11481,9 @@ "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" }, "styled-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.0.tgz", - "integrity": "sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", "requires": { "client-only": "0.0.1" } @@ -11564,6 +11788,12 @@ "punycode": "^2.1.0" } }, + "use-local-storage": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/use-local-storage/-/use-local-storage-3.0.0.tgz", + "integrity": "sha512-wlPNnBCG3ULIJMr5A+dvWqLiPWCfsN1Kwijq+sAhT5yV4ex0u6XmZuNwP+RerIOfzBuz1pwSZuzhZMiluGQHfQ==", + "requires": {} + }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -11589,6 +11819,20 @@ "extsprintf": "^1.2.0" } }, + "void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==" + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", diff --git a/package.json b/package.json index d33a74eb..ec4d67a5 100644 --- a/package.json +++ b/package.json @@ -26,15 +26,21 @@ "eslint-config-next": "^13.0.5", "expect.js": "^0.3.1", "formik": "^2.2.6", + "i18next": "^23.16.3", "lodash": "^4.17.20", "mocha": "^10.2.0", "next": "^13.0.5", + "next-i18next": "^15.3.1", + "next-themes": "^0.3.0", "nodemon": "^2.0.7", "pg": "^8.5.1", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^15.1.0", + "react-icons": "^5.3.0", "short-uuid": "^4.1.0", - "sqlite3": "^5.1.2" + "sqlite3": "^5.1.2", + "use-local-storage": "^3.0.0" }, "devDependencies": { "@types/expect.js": "^0.3.29", diff --git a/public/locales/en-US/common.json b/public/locales/en-US/common.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/public/locales/en-US/common.json @@ -0,0 +1 @@ +{} diff --git a/public/locales/pt-BR/common.json b/public/locales/pt-BR/common.json new file mode 100644 index 00000000..e1440929 --- /dev/null +++ b/public/locales/pt-BR/common.json @@ -0,0 +1,715 @@ +{ + " & gain 1 ": " e ganhe 1 ", + " activated ": " ativou ", + " added ": " adicionou ", + " added 2 ": " adicionou 2 ", + " added the ": " adicionou o ", + " after you play a ": " depois de jogar um ", + " and ": " e ", + " and 1 ": " e 1 ", + " and discard 2 ": " e descarte 2 ", + " and discard 3 ": " e descarte 3 ", + " and draw 1 ": " e compre 1 ", + " and drew ": " e comprou ", + " and drew 1 ": " e comprou 1 ", + " and drew 2 ": " e comprou 2 ", + " and drew 3 ": " e comprou 3 ", + " and drew 4 ": " e comprou 4 ", + " and drew 5 ": " e comprou 5 ", + " and gain 1 ": " e ganhe 1 ", + " and pay 1 ": " e pague 1 ", + " and pay 2 ": " e pague 2 ", + " and play 1 for -1 ": " e jogue 1 por -1 ", + " and play it for free.": " e jogue-a de graça.", + " and receive 1 ": " e receba 1 ", + " and reveal hidden ": " e revele a ", + " and you have at least 1 ": " e você tiver pelo menos 1 ", + " beneath this Event.": " abaixo deste Evento.", + " by adding ": ", enviando ", + " by occupying ": ", usando ", + " chose to play ": " escolheu jogar ", + " claimed ": " reivindicou ", + " claimed the ": " reivindicou o ", + " color in your city.": " cor na sua cidade.", + " copied ": " copiou ", + " cost and draw 2 ": " e compre 2 ", + " decline to spend any ": " decline to spend any ", + " discarded ": " descartou ", + " discarded 0 ": " descartou 0 ", + " discarded 1 ": " descartou 1 ", + " discarded 2 ": " descartou 2 ", + " discarded 3 ": " descartou 3 ", + " discarded 4 ": " descartou 4 ", + " discarded 5 ": " descartou 5 ", + " discarded 6 ": " discarded 6 ", + " discarded 7 ": " discarded 7 ", + " discarded 8 ": " discarded 8 ", + " discarded.": ", que você descartou.", + " drawn.": ", que você comprou.", + " drew ": " comprou ", + " drew 1 ": " comprou 1 ", + " drew 2 ": " comprou 2 ", + " drew 3 ": " comprou 3 ", + " drew 4 ": " comprou 4 ", + " drew 5 ": " comprou 5 ", + " each).": " cada).", + " each.": " cada.", + " event.": " evento.", + " every time you gain a ": " toda vez que você ganhar um ", + " facedown beneath this event.": " com a face para baixo abaixo deste Evento.", + " for ": " por ", + " for 3 fewer ": " por 3 a menos ", + " for each ": " para cada ", + " for each basic event you achieved.": " para cada evento básico que você alcançou.", + " for each buried worker in your Cemetery.": " para cada trabalhador enterrado no seu Cemitério.", + " for each discarded ": " para cada descartado ", + " for each of your unused ": " para cada não utilizado seu ", + " for each Pearlbrook card in your city.": " para cada carta Pearlbrook na sua cidade.", + " for each prisoner in your Dungeon.": " para cada prisioneiro na sua Masmorra.", + " for each resource there, up to 6.": " para cada recurso lá, até 6.", + " for each special event you achieved.": " para cada evento especial que você alcançou", + " for each special Event, including this one": " para cada Evento especial, incluindo este", + " for each unique colored ": " para cada colorido único ", + " for each worker in your Monastery.": " para cada trabalhador no seu Mosteiro.", + " for every ": " para cada ", + " for every 2 ": " para cada 2 ", + " for every 2 Constructions in your city.": " para cada 2 Construções na sua cidade.", + " for every 3 ": " para cada 3 ", + " for free.": " de graça.", + " for free. ": " de graça. ", + " for playing a Construction.": " por jogar uma Construção.", + " for playing a Critter.": " por jogar uma Criatura.", + " from here to activate 1 of the Basic or Forest locations where you have a worker deployed.": " daqui para ativar 1 dos locais Básicos ou da Floresta onde você tem um trabalhador alocado.", + " from the deck or discard pile and play 1 for free. ": " do baralho ou pilha de descarte e jogue 1 de graça. ", + " from the deck, the Meadow, and/or ": " do baralho, da Campina e/ou ", + " from the Discard Pile.": " from the Discard Pile.", + " from the Meadow": " da Campina", + " from the Meadow / Station. Replenish and draw 3 ": " da Campina / Estação. Recomponha e compre 3 ", + " from the Meadow and gain 1 ": " da Campina e ganhe 1 ", + " from the Meadow facedown beneath this Event.": " da Campina com a face para baixo abaixo deste Evento.", + " from the Meadow for 3 fewer ": " da Campina por 3 a menos ", + " from the Meadow of that color. ": " daquela cor da Campina. ", + " from the Meadow worth up to ": " da Campina valendo até ", + " from the Meadow, replenish, then draw 1 ": " da Campina, reabasteça e compre 1 ", + " from the Meadow.": " da Campina.", + " from the Station": " da Estação", + " from the Supply.": " do Suprimento.", + " from their city and gained ": " da cidade deles e ganhou ", + " from their city beneath this event (": " da cidade deles abaixo deste evento (", + " from their city.": " from their city.", + " from their hand": " da mão deles", + " from their hand by occupying ": " da mão deles, usando ", + " from their hand using ": " da mão deles, usando ", + " from their hand.": " da mão deles.", + " from your city beneath this ": " da sua cidade abaixo deste ", + " from your city beneath this Event.": " da sua Cidade abaixo deste Evento.", + " from your city to decrease the cost by 3 ": " da sua cidade para diminuir o custo em 3 ", + " from your city to gain 1 ": " da sua cidade e ganhe 1 ", + " from your city to play that ": " da sua cidade para jogar aquele ", + " from your city.": " da sua cidade.", + " from your city. Gain resources equal to that ": " da sua cidade. Ganhe recursos iguais àquele ", + " from your city. Gain resources equal to that card's cost, ": " da sua cidade. Ganhe recursos iguais ao custo daquela carta, ", + " from your hand beneath this Event.": " da sua mão abaixo deste Evento.", + " from your hand to gain 1 ": " da sua mão e ganhe 1 ", + " from your hand. ": " da sua mão. ", + " gained ": " ganhou ", + " gained 1 ": " ganhou 1 ", + " gained 2 ": " ganhou 2 ", + " gained 3 ": " ganhou 3 ", + " gained 4 ": " ganhou 4 ", + " gained 5 ": " ganhou 5 ", + " gave ": " deu ", + " gave 1 ": " deu 1 ", + " gave 2 ": " deu 2 ", + " gave 3 ": " deu 3 ", + " gave 4 ": " deu 4 ", + " gave 5 ": " deu 5 ", + " here from the supply": " aqui do suprimento", + " here.": " aqui.", + " here. Increase your hand limit size by 1 ": " aqui. Aumente o tamanho limite da sua mão em 1 ", + " if paired with a ": " se emparelhado com um ", + " in an opponent's city": " na cidade de um oponente", + " in an opponent's city. ": " na cidade de um oponente. ", + " in an opponent's city. Apply to your city. ": " na cidade de um oponente. Aplica-se à sua cidade. ", + " in city": " na cidade", + " in your city": " na sua cidade", + " in your city, gain 1 additional ": " na sua cidade, ganhe 1 adicional ", + " in your city.": " na sua cidade.", + " in your city. ": " na sua cidade. ", + " in your hand, up to 5.": " na sua mão, até 5.", + " into an empty space in an opponent's city.": " em um espaço vazio na cidade de um oponente.", + " is at least 7, gain 1 ": " for pelo menos 7, ganhe 1 ", + " is now in ": " agora está em ", + " moved deployed worker on ": " moveu o trabalhador alocado em ", + " on ": " em ", + " on this ": " neste ", + " on this Event.": " neste Evento.", + " on your ": " no seu ", + " or ": " ou ", + " or 1 ": " ou 1 ", + " or 2 ": " ou 2 ", + " or 3 ": " ou 3 ", + " pair in every city.": " par em cada cidade.", + " per ": " por ", + " place a worker on ": " coloque um trabalhador em ", + " placed a worker on ": " colocou um trabalhador em ", + " played ": " jogou ", + " played by an opponent.": ", jogado por um oponente.", + " recalled their workers.": " chamou de volta seus trabalhadores.", + " revealed ": " revelou ", + " ruined ": " arruinou ", + " selected ": " selecionou ", + " spent ": " gastou ", + " spent 1 ": " gastou 1 ", + " spent 2 ": " gastou 2 ", + " spent 3 ": " gastou 3 ", + " spent 4 ": " gastou 4 ", + " spent 5 ": " gastou 5 ", + " spent 6 ": " gastou 6 ", + " spot by playing ": " ponto jogando ", + " that opponent has, up to a maximum of 3.": " que o oponente tem, até um máximo de 3.", + " then draw 2 ": ", então compre 2 ", + " to ": " para ", + " to activate worker on ": ", para ativar o trabalhador em ", + " to add ": " para adicionar ", + " to add 1 ": " para adicionar 1 ", + " to add 2 ": " para adicionar 2 ", + " to add 3 ": " para adicionar 3 ", + " to add 4 ": " para adicionar 4 ", + " to add 5 ": " para adicionar 5 ", + " to add 6 ": " para adicionar 6 ", + " to add 7 ": " para adicionar 7 ", + " to add 8 ": " para adicionar 8 ", + " to an empty space in an opponent's city. ": " para um espaço vazio na cidade de um oponente. ", + " to an opponent and gain 4 ": " para um oponente e ganhe 4 ", + " to an opponent to ": " para um oponente para ", + " to an opponent to gain 2 ": " para um oponente e ganhe 2 ", + " to decrease the cost by 3 ": ", para diminuir o custo em 3 ", + " to draw ": ", para comprar ", + " to draw 2 ": ", para comprar 2 ", + " to draw 3 ": ", para comprar 3 ", + " to draw 4 ": ", para comprar 4 ", + " to draw and reveal an equal amount of ": ", para comprar e revelar uma quantidade igual de ", + " to gain 1 ": " e ganhou 1 ", + " to gain 2 ": " e ganhou 2 ", + " to gain 3 ": " e ganhou 3 ", + " to gain 4 ": " e ganhou 4 ", + " to gain 5 ": " e ganhou 5 ", + " to gain 6 ": " e ganhou 6 ", + " to gain 7 ": " e ganhou 7 ", + " to gain 8 ": " e ganhou 8 ", + " to gain an equal amount of ": ", para ganhar uma quantidade igual de ", + " to opponents. ": "para oponentes. ", + " to play from the Meadow.": ", para jogá-la da Campina.", + " tokens you have.": " fichas que você tem.", + " took the game end action.": " fez a ação de fim de jogo.", + " took the prepare for season action.": " fez a ação de preparar para a estação.", + " Unlock second ": " Desbloqueie a segunda ", + " using ": ", usando ", + " when ": ", quando ", + " when other players visit this card.": ", quando outros jogadores visitam esta carta.", + " with 1 ": " com 1 ", + " worth up to ": " valendo até ", + " worth up to 3 ": ", valendo até 3 ", + " you built.": ", que você construiu.", + " you discard, gain 1 ": ", que você descartar, ganhe 1 ", + " you have.": ".", + "'s City": " Cidade de ", + "'s city.": " cidade.", + ", and 1 ": ", e 1 ", + ", and discard 2 ": ", e descarte 2 ", + ", and discard 3 ": ", e descarte 3 ", + ", gain 1 ": ", ganhe 1 ", + ", gain 2 ": ", ganhe 2 ", + ", keep 1, and give the other to an opponent.": ", fique com 1 e dê a outra para um oponente.", + ", then discard any number of ": ", então descarte qualquer número de ", + ", then gain 1 ": ", então ganhe 1 ", + ", to a maximum of 6.": ", até um máximo de 6.", + ", you may discard this ": " você pode descartar este ", + ", you may draw 2 ": ", você pode comprar 2 ", + ", you may give an opponent 1 ": ", você pode dar a um oponente 1 ", + ", you may place a ": ", você pode colocar um ", + ", you may replace 1 ": ", você pode substituir 1 ", + ". Also draw 1 ": ". Também compre 1 ", + ". Draw 1 ": ". Compre 1 ", + ". Draw 2 ": ". Compre 2 ", + ". Gain 1 ": ". Ganhe 1 ", + ". If you have a ": ". Se você tiver um ", + "1 / PLAYER": "1 / jogador", + "2 Each of ": "2 de cada ", + "About the Game": "Sobre o Jogo", + "Activate 2 ": "Ativa 2 ", + "Activate 2 different ": "Ativa 2 diferentes ", + "Activate it based on your city.": "Você pode ativar esta carta com base na sua cidade.", + "Activate Production": "Ativar Produção", + "active": "ativo", + "Active: ": "Jogador ativo: ", + "Adornment": "Enfeite", + "Adornment Points": "Pontos de Enfeite", + "Adornments": "Enfeites", + "After you play a ": "Depois de jogar um ", + "Air Balloon": "Balão de Ar Quente", + "Allow undo": "Permitir desfazer", + "Also draw 2 ": "Também compre 2 ", + "Also gain 1 ": "Também ganhe 1 ", + "AMBASSADORS": "EMBAIXADORES", + "Ambassadors on card:": "Embaixadores na carta:", + "and draw up to your hand limit.": "e compre até o limite da sua mão.", + "Any": "Qualquer", + "Architect": "Arquiteto", + "Architectural Renaissance": "Renascença Arquitetônica", + "Arts and Music Festival": "Festival de Artes e Música", + "At the beginning of Preparing for Season, you may pay 1 ": "No início da Preparação para a Estação, você pode pagar 1 ", + "autumn": "outono", + "Autumn": "Outono", + "Baker": "Padeiro", + "Ballroom": "Salão de Baile", + "Bank": "Banco", + "Bard": "Bardo", + "Barge Toad": "Sapo-Balsa", + "BASIC": "BÁSICO", + "basic Event you have achieved, and ": "evento básico que você alcançou e ", + "Bed and Breakfast Guild": "Guilda de Cama e Café da Manhã", + "Bell": "Sino", + "Beneath Card:": "Abaixo da Carta:", + "Bosley the Artist": "Bosley, o Artista", + "Bridge": "Ponte", + "Card Owner:": "Dono da Carta:", + "Card Points": "Pontos de Carta", + "Cards": "Cartas", + "Castle": "Castelo", + "Cemetary": "Cemitério", + "Chapel": "Capela", + "Chip Sweep": "Coleta de fichas", + "Chipsmith": "Carpinteiro", + "Choose a color. ": "Escolha uma cor. ", + "CITIZEN": "CIDADÃO", + "City Hall": "Prefeitura", + "City Holiday": "Feriado da Cidade", + "City is empty.": "A cidade está vazia.", + "City Jubilee": "Jubileu da Cidade", + "Claim Event": "Reivindicar Evento", + "Claim Event / Wonders": "Reivindicar Evento / Maravilhas", + "Claimed: ": "Reivindicado: ", + "Clock Tower": "Torre do Relógio", + "Common Construction": "Construção Comum", + "Common Constructions": "Construções Comuns", + "Common Critter": "Criatura Comum", + "Common Critters": "Criaturas Comuns", + "Compass": "Bússola", + "Conductor": "Condutor", + "Construction": "Construção", + "Constructions": "Construções", + "Copy 1 ": "Copia 1 ", + "Copy 1 Basic Location & ": "Copiar 1 Local Básico e ", + "Copy 1 Forest location": "Copie 1 Local da Floresta", + "Copy any ": "Copia qualquer ", + "Copy any Basic location and draw 1 ": "Copie qualquer Local Básico e compre 1 ", + "Copy any revealed ": "Copia qualquer revelado ", + "Copy links to share with other players:": "Copie os links para compartilhar com outros jogadores:", + "Courthouse": "Tribunal", + "Crane": "Guindaste", + "Critter": "Criatura", + "Critters": "Criaturas", + "Crustina the Constable": "Crustina, a Condestável", + "Dealing cards to each player.": "Distribuindo cartas para cada jogador.", + "Dealing cards to the Meadow.": "Distribuindo cartas para a Campina.", + "Deck: ": "Baralho: ", + "Diplomat": "Diplomata", + "Discard 1 Visitor at the station, then gain 1 visitor and 1 train car tile": "Descarte 1 Visitante na estação, depois ganhe 1 visitante e 1 peça de vagão de trem", + "Discard 2 ": "Descarte 2 ", + "Discard 2 Meadow cards, replenish, then draw 2 Meadow cards.": "Descarte 2 cartas da Campina, reabasteça e compre 2 cartas da Campina.", + "Discard 3 ": "Descarte 3 ", + "Discard 4 ": "Descarte 4 ", + "Discard 5 ": "Descarte 5 ", + "Discard a ": "Descarte um ", + "Discard any, then draw 2 for every ": "Descarte qualquer, depois compre 2 para cada ", + "Discard the other.": "Descarte a outra.", + "Discard the others.": "Descarte as outras.", + "Discard up to 3 ": "Descarte até 3 ", + "Discard up to 4 ": "descarte até 4 ", + "Discard: ": "Descarte: ", + "Do not count ": "Não conte ", + "Doctor": "Médico", + "Does not take up a space in your city.": "Não ocupa espaço na sua cidade.", + "Draw 1 ": "Compre 1 ", + "Draw 2 ": "Compre 2 ", + "Draw 2 Meadow ": "Compre 2 da Campina ", + "Draw 3 ": "Compre 3 ", + "Draw all the ": "Compre todos os ", + "Dungeon": "Masmorra", + "Each ": "Cada ", + "End Game": "Fim de Jogo", + "Event Points": "Pontos de Evento", + "Events": "Eventos", + "Events & Wonders": "Eventos e Maravilhas", + "Ever Wall": "Muralha Eterna", + "Ever Wall Tower Constructed": "Torre da Muralha Eterna Construída", + "Evertree": "Árvore Eterna", + "every 5 spaces you have filled in your city.": "a cada 5 espaços que você preencheu na sua cidade.", + "Expansions": "Expansões", + "Fairgrounds": "Parque de Diversões", + "Farm": "Fazenda", + "Ferry": "Balsa", + "Ferry Ferret": "Furão da Balsa", + "Fool": "Bobo da Corte", + "For each donation gain 2 ": "Para cada doação, ganhe 2 ", + "For every 2 ": "Para cada 2 ", + "for every 5 spaces you have filled in your city.": "para cada 5 espaços que você preencheu na sua cidade.", + "FOREST": "FLORESTA", + "Forest Locations": "Locais da Floresta", + "Freight Car": "Vagão de Carga", + "Gain 1 ": "Ganhe 1 ", + "Gain 2 ": "Ganhe 2 ", + "Gain 2 resources from this Freight Car.": "Ganhe 2 recursos deste Vagão de Carga.", + "Gain 3 ": "Ganhe 3 ", + "Gain resources equal to the cost of any ": "Ganhe recursos iguais ao custo de qualquer ", + "Game Cards": "Cartas do Jogo", + "Game Created": "Jogo Criado", + "Game created with 2 players.": "Jogo criado com 2 jogadores.", + "Game created with 3 players.": "Jogo criado com 3 jogadores.", + "Game created with 4 players.": "Jogo criado com 4 jogadores.", + "Game Input": "Entrada do Jogo", + "Game Log": "Registro do Jogo", + "Game Over": " Fim de Jogo", + "Game Over!": "Fim de Jogo!", + "Gardener": "Jardineiro", + "Gardens": "Jardins", + "Gathering of Elders": "Reunião dos Anciãos", + "General Store": "Loja Geral", + "Gilded Book": "Livro Dourado", + "Give an opponent 2 ": "Dê a um oponente 2 ", + "Glow Light Festival": "Festival das Luzes", + "Great Hall": "Grande Salão", + "Greenhouse": "Estufa", + "Gus the Gardener": "Gus, o Jardineiro", + "Harbor": "Porto", + "Historian": "Historiador", + "Hopewatch Gate": "Portão da Vigília da Esperança", + "Hot Air Balloon Race": "Corrida de Balões de Ar Quente", + "Hotel": "Hotel", + "Hourglass": "Ampulheta", + "Husband": "Marido", + "Husband / Wife": "Marido / Esposa", + "If ": "Se ", + "If paying for card with resources, pay resources to an opponent.": "Se pagar pela carta com recursos, pague os recursos a um oponente.", + "If sharing a space with a ": "Se compartilhar um espaço com um ", + "If total point base value of drawn ": "Se o valor base total de pontos das cartas compradas ", + "If you have at least 2 ": "Se você tiver pelo menos 2 ", + "Iluminor the Inventor": "Iluminor, o Inventor", + "in your city. Gain 1 ": " na sua cidade. Ganhe 1 ", + "Increase your hand size by 1 for every ": "Aumente o tamanho da sua mão em 1 para cada ", + "Inn": "Pousada", + "Innkeeper": "Pousadeiro", + "Inventor": "Inventor", + "JOURNEY": "VIAGEM", + "Journey 2": "Journey 2", + "Journey 3": "Journey 3", + "Journey 4": "Journey 4", + "Journey 5": "Journey 5", + "Journey Points": "Pontos de Viagem", + "Judge": "Juiz", + "Juniper Jig Dance Contest": "Concurso de Dança Juniper Jig", + "Key to the City": "Chave da Cidade", + "King": "Rei", + "King's Road Established": "Estrada do Rei Estabelecida", + "KNOLL": "COLINA", + "Lamplighter": "Acendedor de Lampiões", + "Library": "Biblioteca", + "LOCATION": "LOCAL", + "Locations": "Locais", + "Locomotive": "Locomotiva", + "Lookout": "Vigia", + "Magic Snow": "Neve Mágica", + "Magician": "Mágico", + "Main Road": "Estrada Principal", + "Market": "Mercado", + "Masque": "Máscara", + "May discard any ": "Pode descartar qualquer ", + "May not be copied, removed, or reactivated.": "Não pode ser copiada, removida ou reativada.", + "May not be used with any other card-playing ability.": "Não pode ser usada com qualquer outra habilidade de jogar cartas.", + "May not copy Cemetary, Chapel, Monastary, Pirate Ship, or Legendary.": "Não pode copiar Cemitério, Capela, Mosteiro, Navio Pirata ou Lendário.", + "May not copy Fool, Main Road, Ruins, or Legendary.": "Não pode copiar Bobo da Corte, Estrada Principal, Ruínas ou Lendário.", + "Mayor": "Prefeito", + "Meadow": "Campina", + "Messenger": "Mensageiro", + "Miller": "Moleiro", + "Mine": "Mina", + "Miner Mole": "Toupeira Mineira", + "Mirror": "Espelho", + "Mistrise Fountain": "Fonte de Mistrise", + "Monastery": "Mosteiro", + "Monk": "Monge", + "Move 1 of your deployed workers to a new location.": "Mova 1 dos seus trabalhadores alocados para um novo local.", + "Move this ": "Mova este ", + "Museum": "Museu", + "Must share a space with a Construction.": "Deve compartilhar um espaço com uma Construção.", + "New Game": "Novo Jogo", + "Observatory": "Observatório", + "Omicron the Elder": "Ômicron, o Ancião", + "On Card:": "Na Carta:", + "Opens an additional space in your city. ": "Abre um espaço adicional na sua cidade. ", + "Other players may visit this card. Owner gains ": "Outros jogadores podem visitar esta carta. O Dono ganha ", + "Owner gains ": "O Dono ganha ", + "Palace": "Palácio", + "Pay 1 ": "Pague 1 ", + "Pay 2 ": "Pague 2 ", + "Pay 3 ": "Pague 3 ", + "Peddler": "Mascate", + "Photographer": "Fotógrafo", + "Pie Eating Contest": "Concurso de Comer Torta", + "Pirate": "Pirata", + "Pirate Ship": "Navio Pirata", + "Place 1 ": "Coloque 1 ", + "Place Ambassador": "Coloque um Embaixador", + "Place either 3 ": "Coloque 3 ", + "Place worker: Take all resources on this card.": "Coloque um trabalhador: Pegue todos os recursos desta carta.", + "Play a ": "Jogue um ", + "Play a Critter or Construction from your hard for 3 fewer ": "Jogue uma Criatura ou Construção da sua mão por 3 a menos ", + "Play a Station ": " Use uma Estação ", + "Play Adornment": "Jogue um Enfeite", + "Play Card": "Jogue uma Carta", + "Play this ": "Jogue este ", + "Play Train Ticket": "Jogue um Bilhete de Trem", + "Player": "Jogador", + "Players": "Jogadores", + "Poet": "Poeta", + "POINTS": "PONTOS", + "Points Breakdown": "Detalhes dos Pontos", + "Post Office": "Correio", + "Postal Pigeon": "Pombo-Correio", + "Prepare for season": "Prepare for season", + "Prepare for Season": "Preparar para a Estação", + "Prepare for season: ": "Preparar para a estação: ", + "Queen": "Rainha", + "Randomize player order": "Aleatorizar ordem dos jogadores", + "Ranger": "Guarda Florestal", + "Requires 10 ": "Requer 10 ", + "Requires 4 ": "Requer 4 ", + "Requires 5 ": "Requer 5 ", + "Requires 9 ": "Requer 9 ", + "Requires that you have achieved at least 3 ": "Requer que você tenha alcançado pelo menos 3 ", + "Reserve Meadow/Station Card": "Reservar Carta da Campina/Estação", + "Resin Refinery": "Refinaria de Resina", + "Reveal 2 ": "Revele 2 ", + "Reveal 4 ": "Revele 4 ", + "Reveal and discard 1 ": "Revele e descarte 1 ", + "Reveal and discard 2 ": "Revele e descarte 2 ", + "Reveal and discard 3 ": "Revele e descarte 3 ", + "Reveal and discard 3 different colored ": "Revele e descarte 3 de cores diferentes ", + "River Destination": "Destino do Rio", + "River Destination Spots": "Locais de Destino do Rio", + "River Destinations": "Destinos do Rio", + "Royal Tea": "Chá Real", + "Royal Wedding": "Casamento Real", + "Ruins": "Ruínas", + "Scales": "Balança", + "School": "Escola", + "Score based on your city.": "Pontuação baseada na sua cidade.", + "Seaglass Amulet": "Amuleto de Vidro do Mar", + "Select Visitor to discard": "Selecione um Visitante para descartar", + "Select Visitor to keep": "Selecione um Visitante para manter", + "Settings": "Configurações", + "Share Feedback": "Compartilhar Feedback", + "Share space:": "Compartilhar espaço:", + "Shepherd": "Pastor", + "Shipwright": "Construtor Naval", + "Shoal": "Cardume", + "Shopkeeper": "Lojista", + "Show points in realtime": "Mostrar pontos em tempo real", + "Snout the Explorer": "Snout, o Explorador", + "spectator link": "link do espectador", + "Spring": "Primavera", + "spring": "primavera", + "Spyglass": "Luneta", + "Start Game": "Iniciar Jogo", + "STATION": "ESTAÇÃO", + "Statues Commissioned": "Estátuas Encomendadas", + "Stock Market Boom": "Boom do Mercado de Ações", + "Storehouse": "Armazém", + "Submit": "Enviar", + "Summer": "Verão", + "summer": "verão", + "Sunblaze Bridge": "Ponte Sunblaze", + "Sundial": "Relógio de Sol", + "Sunflower Parade": "Desfile de Girassóis", + "Tea House": "Casa de Chá", + "Teacher": "Professor", + "The Everdell Games": "Os Jogos de Everdell", + "the Station.": "", + "Theatre": "Teatro", + "Then gain 1 ": "Então ganhe 1 ", + "then gain 1 ": ", então ganhe 1 ", + "Tiara": "Tiara", + "to a maximum of 3.": ", até um máximo de 3.", + "Total": "Total", + "Twig Barge": "Balsa de Galhos", + "Undertaker": "Agente Funerário", + "Undo last action": "Desfazer última ação", + "Unique Construction": "Construção Única", + "Unique Constructions": "Construções Únicas", + "Unique Critter": "Criatura Única", + "Unique Critters": "Criaturas Únicas", + "University": "Universidade", + "Unlocks second ": "Desbloqueia a segunda ", + "Visit Destination Card": "Visitar Carta de Destino", + "Visit Location": "Visitar Local", + "Visit to gain 1 ": "Visite para ganhar 1 ", + "VP": "PV", + "Waiting for": "Aguardando", + "Wanderer": "Andarilho", + "Watermill": "Moinho de Água", + "What's New": "Novidades", + "When achieved, bring back one of your deployed workers": "Quando alcançado, traga de volta um dos seus trabalhadores alocados", + "When achieved, draw 1 ": "Quando alcançado, compre 1 ", + "When achieved, gain 1 ": "Quando alcançado, ganhe 1 ", + "When achieved, pay 2 ": "Quando alcançado, pague 2 ", + "When achieved, place 1 ": "Quando alcançado, coloque 1 ", + "When achieved, place either your ": "Quando alcançado, coloque ou seu ", + "When achieved, place up to 2 ": "Quando alcançado, coloque até 2 ", + "When achieved, reveal 5 ": "Quando alcançado, revele 5 ", + "When achieved, you may discard 1 ": "Quando alcançado, você pode descartar 1 ", + "When achieved, you may give opponents up to a total of 3 ": "Quando alcançado, você pode dar aos oponentes até um total de 3 ", + "When achieved, you may give up to 6 ": "Quando alcançado, você pode dar até 6 ", + "When achieved, you may place up to 3 ": "Quando alcançado, você pode colocar até 3 ", + "When achieved, you may play 1 ": "Quando alcançado, você pode jogar 1 ", + "When achieved, you may search through the deck for a ": "Quando alcançado, você pode procurar no baralho por um(a) ", + "When played, place 2 ": "Quando jogado, coloque 2 ", + "When played, place 3 ": "Quando jogado, coloque 3 ", + "When playing a ": "Ao jogar um(a) ", + "When visiting a River Destination, this Messenger is considered the same color as the shared Construction.": "Ao visitar um Destino no Rio, este MensEageiro é considerado da mesma cor da Construção compartilhada.", + "When you play a ": "Quando você joga um(a) ", + "Wife": "Esposa", + "Winter": "Inverno", + "winter": "inverno", + "Wonder": "Maravilha", + "Wonder Points": "Pontos de Maravilha", + "Woodcarver": "Entalhador", + "Worker stays here permanently. Unlock second ": "O Trabalhador permanece aqui permanentemente. Desbloqueia o segundo(a) ", + "WORKERS": "TRABALHADORES", + "Workers on card:": "Trabalhadores na carta:", + "Workers: ": "Trabalhadores: ", + "You": "Você", + "You may activate 2 different ": "Você pode ativar 2 diferentes ", + "You may activate Production for up to 3 ": "Você pode ativar a Produção de até 3 ", + "You may copy any ability from an ": "Você pode copiar qualquer habilidade de um(a) ", + "You may discard 1 other ": "Você pode descartar 1 outro(a) ", + "You may discard up to 4 ": "Você pode descartar até 4 ", + "You may discard up to 5 ": "Você pode descartar até 5 ", + "You may draw 2 ": "Você pode comprar 2 ", + "You may draw any or place any beneath this Event.": "Você pode comprar qualquer carta ou colocar qualquer carta embaixo deste Evento.", + "You may give up to 2 ": "Você pode dar até 2 ", + "You may give up to 5 ": "Você pode dar até 5 ", + "You may pay 1 ": "Você pode pagar 1 ", + "You may pay up to 2 ": "Você pode pagar até 2 ", + "You may pay up to 3 ": "Você pode pagar até 3 ", + "You may play 1 ": "Você pode jogar 1 ", + "You may play 1 worth up to ": "Você pode jogar 1 carta com valor de até ", + "You may reactivate 2 ": "Você pode reativar 2 ", + "You may take the action of any 1 Forest location, and gain 1 ": "Você pode realizar a ação de qualquer 1 Localização de Floresta e ganhar 1 ", + "Your City": "Sua Cidade", + "Your hand": "Sua Mão", + "Points: ": "Points: ", + " has 1 points.": " has 1 points.", + " has 10 points.": " has 10 points.", + " has 100 points.": " has 100 points.", + " has 11 points.": " has 11 points.", + " has 12 points.": " has 12 points.", + " has 13 points.": " has 13 points.", + " has 14 points.": " has 14 points.", + " has 15 points.": " has 15 points.", + " has 16 points.": " has 16 points.", + " has 17 points.": " has 17 points.", + " has 18 points.": " has 18 points.", + " has 19 points.": " has 19 points.", + " has 2 points.": " has 2 points.", + " has 20 points.": " has 20 points.", + " has 21 points.": " has 21 points.", + " has 22 points.": " has 22 points.", + " has 23 points.": " has 23 points.", + " has 24 points.": " has 24 points.", + " has 25 points.": " has 25 points.", + " has 26 points.": " has 26 points.", + " has 27 points.": " has 27 points.", + " has 28 points.": " has 28 points.", + " has 29 points.": " has 29 points.", + " has 3 points.": " has 3 points.", + " has 30 points.": " has 30 points.", + " has 31 points.": " has 31 points.", + " has 32 points.": " has 32 points.", + " has 33 points.": " has 33 points.", + " has 34 points.": " has 34 points.", + " has 35 points.": " has 35 points.", + " has 36 points.": " has 36 points.", + " has 37 points.": " has 37 points.", + " has 38 points.": " has 38 points.", + " has 39 points.": " has 39 points.", + " has 4 points.": " has 4 points.", + " has 40 points.": " has 40 points.", + " has 41 points.": " has 41 points.", + " has 42 points.": " has 42 points.", + " has 43 points.": " has 43 points.", + " has 44 points.": " has 44 points.", + " has 45 points.": " has 45 points.", + " has 46 points.": " has 46 points.", + " has 47 points.": " has 47 points.", + " has 48 points.": " has 48 points.", + " has 49 points.": " has 49 points.", + " has 5 points.": " has 5 points.", + " has 50 points.": " has 50 points.", + " has 51 points.": " has 51 points.", + " has 52 points.": " has 52 points.", + " has 53 points.": " has 53 points.", + " has 54 points.": " has 54 points.", + " has 55 points.": " has 55 points.", + " has 56 points.": " has 56 points.", + " has 57 points.": " has 57 points.", + " has 58 points.": " has 58 points.", + " has 59 points.": " has 59 points.", + " has 6 points.": " has 6 points.", + " has 60 points.": " has 60 points.", + " has 61 points.": " has 61 points.", + " has 62 points.": " has 62 points.", + " has 63 points.": " has 63 points.", + " has 64 points.": " has 64 points.", + " has 65 points.": " has 65 points.", + " has 66 points.": " has 66 points.", + " has 67 points.": " has 67 points.", + " has 68 points.": " has 68 points.", + " has 69 points.": " has 69 points.", + " has 7 points.": " has 7 points.", + " has 70 points.": " has 70 points.", + " has 71 points.": " has 71 points.", + " has 72 points.": " has 72 points.", + " has 73 points.": " has 73 points.", + " has 74 points.": " has 74 points.", + " has 75 points.": " has 75 points.", + " has 76 points.": " has 76 points.", + " has 77 points.": " has 77 points.", + " has 78 points.": " has 78 points.", + " has 79 points.": " has 79 points.", + " has 8 points.": " has 8 points.", + " has 80 points.": " has 80 points.", + " has 81 points.": " has 81 points.", + " has 82 points.": " has 82 points.", + " has 83 points.": " has 83 points.", + " has 84 points.": " has 84 points.", + " has 85 points.": " has 85 points.", + " has 86 points.": " has 86 points.", + " has 87 points.": " has 87 points.", + " has 88 points.": " has 88 points.", + " has 89 points.": " has 89 points.", + " has 9 points.": " has 9 points.", + " has 90 points.": " has 90 points.", + " has 91 points.": " has 91 points.", + " has 92 points.": " has 92 points.", + " has 93 points.": " has 93 points.", + " has 94 points.": " has 94 points.", + " has 95 points.": " has 95 points.", + " has 96 points.": " has 96 points.", + " has 97 points.": " has 97 points.", + " has 98 points.": " has 98 points.", + " has 99 points.": " has 99 points.", + " points.": " points.", + "Game over": "Game over" +} \ No newline at end of file diff --git a/public/locales/ru-RU/common.json b/public/locales/ru-RU/common.json new file mode 100644 index 00000000..d7bc30f3 --- /dev/null +++ b/public/locales/ru-RU/common.json @@ -0,0 +1,717 @@ +{ + " & gain 1 ": " и получите 1 ", + " activated ": " активировал ", + " added ": " добавил ", + " added 2 ": " добавил 2 ", + " added the ": " разместил ", + " after you play a ": " при использовании карты ", + " and ": " и ", + " and 1 ": " и 1 ", + " and discard 2 ": " и сбросьте 2 ", + " and discard 3 ": " и сбросьте 3 ", + " and draw 1 ": " и взять 1 ", + " and drew ": " и взял ", + " and drew 1 ": " и взял 1 ", + " and drew 2 ": " и взял 2 ", + " and drew 3 ": " и взял 3 ", + " and drew 4 ": " и взял 4 ", + " and drew 5 ": " и взял 5 ", + " and gain 1 ": " и получите 1 ", + " and pay 1 ": " и заплатите 1 ", + " and pay 2 ": " и заплатите 2 ", + " and play 1 for -1 ": " и используйте 1 за -1 ", + " and play it for free.": " и сыграть эту карту бесплатно.", + " and receive 1 ": " и получите 1 ", + " and reveal hidden ": " и откройте скрытую ", + " and you have at least 1 ": " и у Вас есть хотя бы 1 ", + " beneath this Event.": " под этим Событием.", + " by adding ": ", отправив ", + " by occupying ": ", используя ", + " chose to play ": " выбрал ", + " claimed ": " активировал ", + " claimed the ": " активировал ", + " color in your city.": " цвет в Вашем городе.", + " copied ": " скопировал ", + " cost and draw 2 ": " и возьмите 2 ", + " decline to spend any ": " отказывается тратить ", + " discarded ": " сбросил ", + " discarded 0 ": " сбросил 0 ", + " discarded 1 ": " сбросил 1 ", + " discarded 2 ": " сбросил 2 ", + " discarded 3 ": " сбросил 3 ", + " discarded 4 ": " сбросил 4 ", + " discarded 5 ": " сбросил 5 ", + " discarded 6 ": " сбросил 6 ", + " discarded 7 ": " сбросил 7 ", + " discarded 8 ": " сбросил 8 ", + " discarded.": ", сброшенную Вами.", + " drawn.": ", взятую Вами.", + " drew ": " взял ", + " drew 1 ": " взял 1 ", + " drew 2 ": " взял 2 ", + " drew 3 ": " взял 3 ", + " drew 4 ": " взял 4 ", + " drew 5 ": " взял 5 ", + " each).": " за каждого).", + " each.": " за каждый.", + " event.": " событие.", + " every time you gain a ": " каждый раз при получении ", + " facedown beneath this event.": " крапом вниз под этим Событием.", + " for ": " за ", + " for 3 fewer ": " со скидкой в 3 ", + " for each ": " за каждый ", + " for each basic event you achieved.": " за каждой базовое событие, которые Вы достигли.", + " for each buried worker in your Cemetery.": "за каждого похороненного работника на Вашем Кладбище.", + " for each discarded ": " за каждую сброшенную ", + " for each of your unused ": " за каждый имеющийся ", + " for each Pearlbrook card in your city.": " за каждую карту из дополнения Pearlbrook в Вашем городе.", + " for each prisoner in your Dungeon.": " за каждого узника в Вашей Темнице.", + " for each resource there, up to 6.": " за каждый ресурс здесь, но не более 6.", + " for each special event you achieved.": " за каждое специальное событие, которые Вы достигли", + " for each special Event, including this one": " за каждое специальное Событие, включая это", + " for each unique colored ": " за каждую уникальную цветную ", + " for each worker in your Monastery.": " за каждого работника в Вашем Монастыре.", + " for every ": " за каждую ", + " for every 2 ": " за каждые 2 ", + " for every 2 Constructions in your city.": " за каждые 2 Постройки в Вашем городе.", + " for every 3 ": " за каждые 3 ", + " for free.": " бесплатно.", + " for free. ": " бесплатно.", + " for playing a Construction.": " за использование карты Постройки.", + " for playing a Critter.": " за использование карты Существа.", + " from here to activate 1 of the Basic or Forest locations where you have a worker deployed.": " отсюда, чтобы активировать 1 Базовую или Лесную локацию, где у Вас размещен рабочий.", + " from the deck or discard pile and play 1 for free. ": "из колоды или отбоя и сыграть 1 бесплатно. ", + " from the deck, the Meadow, and/or ": " из колоды, Лужайки, и/или ", + " from the Discard Pile.": " из Отбоя.", + " from the Meadow": " с Лужайки", + " from the Meadow / Station. Replenish and draw 3 ": " с Лужайки / Станции. Пополните и и возьмите 3 ", + " from the Meadow and gain 1 ": " с Лужайки и получите 1 ", + " from the Meadow facedown beneath this Event.": " из Лужайки крапом вверх под этим Событием.", + " from the Meadow for 3 fewer ": " с Лужайки со скидкой в 3 ", + " from the Meadow of that color. ": " такого же цвета из Лужайки. ", + " from the Meadow worth up to ": " из Лужайки ценностью до ", + " from the Meadow, replenish, then draw 1 ": " из Лужайки, добирает из колоды, и затем берет 1 ", + " from the Meadow.": " из Лужайки.", + " from the Station": " со Станции", + " from the Supply.": " из Сокровищницы.", + " from their city and gained ": " их их города и получил ", + " from their city beneath this event (": " из их города под этим событием (", + " from their city.": " из их города.", + " from their hand": " из их руки", + " from their hand by occupying ": " из их руки, используя ", + " from their hand using ": " из их руки, используя ", + " from their hand.": " с их руки.", + " from your city beneath this ": " из Вашего города под этой ", + " from your city beneath this Event.": " из Вашего Города под этим Событием.", + " from your city to decrease the cost by 3 ": " из Вашего города, чтобы уменьшить цену на 3 ", + " from your city to gain 1 ": " из Вашего городе и получить 1 ", + " from your city to play that ": " из Вашего города, чтобы использовать эту ", + " from your city.": " из Вашего города.", + " from your city. Gain resources equal to that ": " из Вашего города. Получите взамен ресурсы с этой ", + " from your city. Gain resources equal to that card's cost, ": " из Вашего города. Получите взамен ресурсы в размере стоимости этой карты, ", + " from your hand beneath this Event.": " из Вашей руки под этим Событием.", + " from your hand to gain 1 ": " из Вашей руки и получите 1 ", + " from your hand. ": " из Вашей руки. ", + " gained ": " получил ", + " gained 1 ": " получил 1 ", + " gained 2 ": " получил 2 ", + " gained 3 ": " получил 3 ", + " gained 4 ": " получил 4 ", + " gained 5 ": " получил 5 ", + " gave ": " отдал ", + " gave 1 ": " отдал 1 ", + " gave 2 ": " отдал 2 ", + " gave 3 ": " отдал 3 ", + " gave 4 ": " отдал 4 ", + " gave 5 ": " отдал 5 ", + " has 1 points.": " имеет 1 очков.", + " has 10 points.": " имеет 10 очков.", + " has 100 points.": " имеет 100 очков.", + " has 11 points.": " имеет 11 очков.", + " has 12 points.": " имеет 12 очков.", + " has 13 points.": " имеет 13 очков.", + " has 14 points.": " имеет 14 очков.", + " has 15 points.": " имеет 15 очков.", + " has 16 points.": " имеет 16 очков.", + " has 17 points.": " имеет 17 очков.", + " has 18 points.": " имеет 18 очков.", + " has 19 points.": " имеет 19 очков.", + " has 2 points.": " имеет 2 очков.", + " has 20 points.": " имеет 20 очков.", + " has 21 points.": " имеет 21 очков.", + " has 22 points.": " имеет 22 очков.", + " has 23 points.": " имеет 23 очков.", + " has 24 points.": " имеет 24 очков.", + " has 25 points.": " имеет 25 очков.", + " has 26 points.": " имеет 26 очков.", + " has 27 points.": " имеет 27 очков.", + " has 28 points.": " имеет 28 очков.", + " has 29 points.": " имеет 29 очков.", + " has 3 points.": " имеет 3 очков.", + " has 30 points.": " имеет 30 очков.", + " has 31 points.": " имеет 31 очков.", + " has 32 points.": " имеет 32 очков.", + " has 33 points.": " имеет 33 очков.", + " has 34 points.": " имеет 34 очков.", + " has 35 points.": " имеет 35 очков.", + " has 36 points.": " имеет 36 очков.", + " has 37 points.": " имеет 37 очков.", + " has 38 points.": " имеет 38 очков.", + " has 39 points.": " имеет 39 очков.", + " has 4 points.": " имеет 4 очков.", + " has 40 points.": " имеет 40 очков.", + " has 41 points.": " имеет 41 очков.", + " has 42 points.": " имеет 42 очков.", + " has 43 points.": " имеет 43 очков.", + " has 44 points.": " имеет 44 очков.", + " has 45 points.": " имеет 45 очков.", + " has 46 points.": " имеет 46 очков.", + " has 47 points.": " имеет 47 очков.", + " has 48 points.": " имеет 48 очков.", + " has 49 points.": " имеет 49 очков.", + " has 5 points.": " имеет 5 очков.", + " has 50 points.": " имеет 50 очков.", + " has 51 points.": " имеет 51 очков.", + " has 52 points.": " имеет 52 очков.", + " has 53 points.": " имеет 53 очков.", + " has 54 points.": " имеет 54 очков.", + " has 55 points.": " имеет 55 очков.", + " has 56 points.": " имеет 56 очков.", + " has 57 points.": " имеет 57 очков.", + " has 58 points.": " имеет 58 очков.", + " has 59 points.": " имеет 59 очков.", + " has 6 points.": " имеет 6 очков.", + " has 60 points.": " имеет 60 очков.", + " has 61 points.": " имеет 61 очков.", + " has 62 points.": " имеет 62 очков.", + " has 63 points.": " имеет 63 очков.", + " has 64 points.": " имеет 64 очков.", + " has 65 points.": " имеет 65 очков.", + " has 66 points.": " имеет 66 очков.", + " has 67 points.": " имеет 67 очков.", + " has 68 points.": " имеет 68 очков.", + " has 69 points.": " имеет 69 очков.", + " has 7 points.": " имеет 7 очков.", + " has 70 points.": " имеет 70 очков.", + " has 71 points.": " имеет 71 очков.", + " has 72 points.": " имеет 72 очков.", + " has 73 points.": " имеет 73 очков.", + " has 74 points.": " имеет 74 очков.", + " has 75 points.": " имеет 75 очков.", + " has 76 points.": " имеет 76 очков.", + " has 77 points.": " имеет 77 очков.", + " has 78 points.": " имеет 78 очков.", + " has 79 points.": " имеет 79 очков.", + " has 8 points.": " имеет 8 очков.", + " has 80 points.": " имеет 80 очков.", + " has 81 points.": " имеет 81 очков.", + " has 82 points.": " имеет 82 очков.", + " has 83 points.": " имеет 83 очков.", + " has 84 points.": " имеет 84 очков.", + " has 85 points.": " имеет 85 очков.", + " has 86 points.": " имеет 86 очков.", + " has 87 points.": " имеет 87 очков.", + " has 88 points.": " имеет 88 очков.", + " has 89 points.": " имеет 89 очков.", + " has 9 points.": " имеет 9 очков.", + " has 90 points.": " имеет 90 очков.", + " has 91 points.": " имеет 91 очков.", + " has 92 points.": " имеет 92 очков.", + " has 93 points.": " имеет 93 очков.", + " has 94 points.": " имеет 94 очков.", + " has 95 points.": " имеет 95 очков.", + " has 96 points.": " имеет 96 очков.", + " has 97 points.": " имеет 97 очков.", + " has 98 points.": " имеет 98 очков.", + " has 99 points.": " имеет 99 очков.", + " here from the supply": " здесь из сокровищницы", + " here.": " здесь.", + " here. Increase your hand limit size by 1 ": " здесь. Увеличьте максимальный размер руки на 1 ", + " if paired with a ": " в команде с ", + " in an opponent's city": " из города оппонента", + " in an opponent's city. ": " из города оппонента. ", + " in an opponent's city. Apply to your city. ": " в городе оппонента. Также применяется к Вашему городу.", + " in city": " в городе", + " in your city": " в Вашем городе", + " in your city, gain 1 additional ": " в Вашем городе, получите дополнительно 1 ", + " in your city.": " в Вашем городе.", + " in your city. ": " в Вашем городе. ", + " in your hand, up to 5.": " в Вашей руке, не более 5.", + " into an empty space in an opponent's city.": " на свободное место в городе оппонента.", + " is at least 7, gain 1 ": " как минимум 7, получите 1 ", + " is now in ": " перешел в ", + " moved deployed worker on ": " переместил рабочего с ", + " on ": " на ", + " on this ": " на эту ", + " on this Event.": " на этом Событии.", + " on your ": " на Вашей ", + " or ": " или ", + " or 1 ": " или 1 ", + " or 2 ": " или 2 ", + " or 3 ": " или 3 ", + " pair in every city.": " пару в каждом городе.", + " per ": " за каждый ", + " place a worker on ": " отправил рабочего в ", + " placed a worker on ": " отправил рабочего в ", + " played ": " сыграл ", + " played by an opponent.": ", сыгранную оппонентом.", + " points.": " очков.", + " recalled their workers.": " отозвал своих рабочих.", + " revealed ": " открыл ", + " ruined ": " разрушил ", + " selected ": " выбрал ", + " spent ": " потратил ", + " spent 1 ": " потратил 1 ", + " spent 2 ": " потратил 2 ", + " spent 3 ": " потратил 3 ", + " spent 4 ": " потратил 4 ", + " spent 5 ": " потратил 5 ", + " spent 6 ": " потратил 6 ", + " spot by playing ": " точку при использовании ", + " that opponent has, up to a maximum of 3.": ", имеющийся у оппонента, но не более 3.", + " then draw 2 ": ", затем взять 2 ", + " to ": " ", + " to activate worker on ": ", чтобы активировать рабочего на ", + " to add ": " для добавления ", + " to add 1 ": " для добавления 1 ", + " to add 2 ": " для добавления 2 ", + " to add 3 ": " для добавления 3 ", + " to add 4 ": " для добавления 4 ", + " to add 5 ": " для добавления 5 ", + " to add 6 ": " для добавления 6 ", + " to add 7 ": " для добавления 7 ", + " to add 8 ": " для добавления 8 ", + " to an empty space in an opponent's city. ": " на свободное место в городе оппонента.", + " to an opponent and gain 4 ": " любому оппоненту и получите 4 ", + " to an opponent to ": " оппоненту, чтобы ", + " to an opponent to gain 2 ": " оппоненту и получить 2 ", + " to decrease the cost by 3 ": ", чтобы уменьшить цену на 3 ", + " to draw ": ", чтобы взять ", + " to draw 2 ": ", чтобы взять 2 ", + " to draw 3 ": ", чтобы взять 3 ", + " to draw 4 ": ", чтобы взять 4 ", + " to draw and reveal an equal amount of ": ", чтобы взять и показать такое же количество ", + " to gain 1 ": " и получил 1 ", + " to gain 1 ": " и получите 1 ", + " to gain 2 ": " и получил 2 ", + " to gain 3 ": " и получил 3 ", + " to gain 3 ": " и получите 3 ", + " to gain 4 ": " и получил 4 ", + " to gain 5 ": " и получил 5 ", + " to gain 6 ": " и получил 6 ", + " to gain 7 ": " и получил 7 ", + " to gain 8 ": " и получил 8 ", + " to gain an equal amount of ": ", чтобы получить такое же количество ", + " to opponents. ": "оппонентам. ", + " to play from the Meadow.": ", чтобы сыграть ее с Лужайки.", + " tokens you have.": " Ваших токена.", + " took the game end action.": " завершил игру.", + " took the prepare for season action.": " готовится к сезону.", + " Unlock second ": " Разблокируйте вторую ", + " using ": ", используя ", + " when ": ", когда ", + " when other players visit this card.": ", когда другие игроки посещают эту карту.", + " with 1 ": " на 1 ", + " worth up to ": " ценностью до ", + " worth up to 3 ": ", стоимостью до 3 ", + " you built.": ", построенный Вами.", + " you discard, gain 1 ": ", Вами сброшенные, получите 1 ", + " you have.": ".", + "'s City": " Город", + "'s city.": " город.", + ", and 1 ": ", и 1 ", + ", and discard 2 ": ", и сбросьте 2 ", + ", and discard 3 ": ", и сбросьте 3 ", + ", gain 1 ": ", получите 1 ", + ", gain 2 ": ", получите 2 ", + ", keep 1, and give the other to an opponent.": ", оставьте 1 себе и отдайте другую оппоненту.", + ", then discard any number of ": ", сбросьте любое количество ", + ", then gain 1 ": ", затем получите 1 ", + ", to a maximum of 6.": ", но не более 6.", + ", you may discard this ": " Вы можете сбросить эту ", + ", you may draw 2 ": ", Вы можете взять 2 ", + ", you may give an opponent 1 ": ", Вы можете отдать оппоненту 1 ", + ", you may place a ": ", Вы можете разместить ", + ", you may replace 1 ": ", Вы можете заменить 1 ", + ". Also draw 1 ": ". Также возьмите 1 ", + ". Draw 1 ": ". Возьмите 1 ", + ". Draw 2 ": ". Возьмите 2 ", + ". Gain 1 ": ". Получите 1 ", + ". If you have a ": ". Если у Вас есть ", + "1 / PLAYER": "1 / игрок", + "2 Each of ": "По 2 каждого ", + "About the Game": "Об Игре", + "Activate 2 ": "Активирует 2 ", + "Activate 2 different ": "Активирует 2 разных ", + "Activate it based on your city.": "Вы можете активировать эту карту на основе своего города.", + "Activate Production": "Активировать Производство", + "active": "ходит", + "Active: ": "Текущий игрок: ", + "Adornment": "Украшение", + "Adornment Points": "Очки за Украшения", + "Adornments": "Украшения", + "After you play a ": "После использования карты типа ", + "Air Balloon": "Воздушный шар", + "Allow undo": "Разрешить отмену последнего действия", + "Also draw 2 ": "Также возьмите 2 ", + "Also gain 1 ": "Также получите 1 ", + "AMBASSADORS": "АМБАССАДОРЫ", + "Ambassadors on card:": "Амбассадоров на карте:", + "and draw up to your hand limit.": "и набирает до лимита карт на руке.", + "Any": "Любой", + "Architect": "Архитектор", + "Architectural Renaissance": "Архитектурный Ренессанс", + "Arts and Music Festival": "Фестиваль искусств и музыки", + "At the beginning of Preparing for Season, you may pay 1 ": "В начале Подготовки к Сезону, Вы можете заплатить 1 ", + "autumn": "осень", + "Autumn": "Осень", + "Baker": "Пекарь", + "Ballroom": "Бальный зал", + "Bank": "Банк", + "Bard": "Бард", + "Barge Toad": "Лягушка-баржа", + "BASIC": "Базовая", + "basic Event you have achieved, and ": "базовое Событие, которые Вы достигли, и ", + "Bed and Breakfast Guild": "Гильдия ночлега и завтрака", + "Bell": "Колокольчик", + "Beneath Card:": "Под Картой:", + "Bosley the Artist": "Босли-художник", + "Bridge": "Мост", + "Card Owner:": "Владелец:", + "Card Points": "Очки за Карты", + "Cards": "Карты", + "Castle": "Замок", + "Cemetary": "Кладбище", + "Chapel": "Часовня", + "Chip Sweep": "Дровосек", + "Chipsmith": "Плотник", + "Choose a color. ": "Выберите цвет. ", + "CITIZEN": "Гражданин", + "City Hall": "Ратуша", + "City Holiday": "Городской праздник", + "City is empty.": "Город пуст.", + "City Jubilee": "Юбилей Города", + "Claim Event": "Активировать Событие", + "Claim Event / Wonders": "Активировать Событие / Чудо", + "Claimed: ": "Активировано: ", + "Clock Tower": "Часовая башня", + "Common Construction": "Обычная постройка", + "Common Constructions": "Обычные Постройки", + "Common Critter": "Обычное существо", + "Common Critters": "Обычные Существа", + "Compass": "Компас", + "Conductor": "Кондуктор", + "Construction": "Постройка", + "Constructions": "Постройки", + "Copy 1 ": "Копирует 1 ", + "Copy 1 Basic Location & ": "Скопировать 1 Базовую Локацию и ", + "Copy 1 Forest location": "Скопируйте 1 Лесную Локацию", + "Copy any ": "Копирует любого ", + "Copy any Basic location and draw 1 ": "Скопируйте любую Базовую Локацию и возьмите 1 ", + "Copy any revealed ": "Копирует любую уже открытую ", + "Copy links to share with other players:": "Используйте ссылки для участников игры:", + "Courthouse": "Суд", + "Crane": "Кран", + "Critter": "Существо", + "Critters": "Существа", + "Crustina the Constable": "Констебль Крустина", + "Dealing cards to each player.": "Раздача карт игрокам.", + "Dealing cards to the Meadow.": "Раздача карт на Лужайку.", + "Deck: ": "В Колоде: ", + "Diplomat": "Дипломат", + "Discard 1 Visitor at the station, then gain 1 visitor and 1 train car tile": "Сбросьте 1 Посетителя на станции, затем получите 1 посетителя и 1 плитку вагона поезда", + "Discard 2 ": "Сбросьте 2 ", + "Discard 2 Meadow cards, replenish, then draw 2 Meadow cards.": "Сбросьте 2 карты с Лужайки, пополните, и возьмите себе 2 карты с Лужайки.", + "Discard 3 ": "Сбросьте 3 ", + "Discard 4 ": "Сбросьте 4 ", + "Discard 5 ": "Сбросьте 5 ", + "Discard a ": "Сбросьте ", + "Discard any, then draw 2 for every ": "Сбросьте любые карты, затем возьмите 2 за каждую ", + "Discard the other.": "Сбросьте другую.", + "Discard the others.": "Сбросить остальные", + "Discard up to 3 ": "Сбросьте до 3 ", + "Discard up to 4 ": "сбросьте до 4 ", + "Discard: ": "Отбой: ", + "Do not count ": "Не учитывает ", + "Doctor": "Доктор", + "Does not take up a space in your city.": "Не занимает места в Вашем городе.", + "Draw 1 ": "Возьмите 1 ", + "Draw 2 ": "Возьмите 2 ", + "Draw 2 Meadow ": "Возьмите 2 Луговых ", + "Draw 3 ": "Возьмите 3 ", + "Draw all the ": "Возьмите все ", + "Dungeon": "Темница", + "Each ": "Каждый ", + "End Game": "Закончить Игру", + "Event Points": "Очки за События", + "Events": "События", + "Events & Wonders": "События и Чудеса", + "Ever Wall": "Вечная стена", + "Ever Wall Tower Constructed": "Построена Башня Ever Wall", + "Evertree": "Вечное древо", + "every 5 spaces you have filled in your city.": "каждые 5 занятых мест в Вашем городе.", + "Expansions": "Дополнения", + "Fairgrounds": "Ярмарка", + "Farm": "Ферма", + "Ferry": "Паром", + "Ferry Ferret": "Хорек-паромщик", + "Fool": "Шут", + "For each donation gain 2 ": "За каждое пожертвование получите 2 ", + "For every 2 ": "За каждые 2", + "for every 5 spaces you have filled in your city.": "за каждые 5 занятых мест в Вашем городе.", + "FOREST": "Лес", + "Forest Locations": "Лесные Локации", + "Freight Car": "Грузовой вагон", + "Gain 1 ": "Дает 1 ", + "Gain 2 ": "Дает 2 ", + "Gain 2 resources from this Freight Car.": "Получите 2 ресурса из этого Грузового Вагона.", + "Gain 3 ": "Дает 3 ", + "Gain resources equal to the cost of any ": "Получите ресурсы в размере цены любого ", + "Game Cards": "Игровые Карты", + "Game Created": "Игра создана", + "Game created with 2 players.": "Игра создана с 2 игроками", + "Game created with 3 players.": "Игра создана с 3 игроками", + "Game created with 4 players.": "Игра создана с 4 игроками", + "Game Input": "Игровые Действия", + "Game Log": "Игровой Журнал", + "Game over": "Игра окончена", + "Game Over": " Игра Окончена", + "Game Over!": "Игра окончена!", + "Gardener": "Садовник", + "Gardens": "Сады", + "Gathering of Elders": "Собрание Старейшин", + "General Store": "Лавка", + "Gilded Book": "Позолоченная книга", + "Give an opponent 2 ": "Отдает оппоненту 2 ", + "Glow Light Festival": "Фестиваль Glow Light", + "Great Hall": "Большой зал", + "Greenhouse": "Теплица", + "Gus the Gardener": "Гас Садовник", + "Harbor": "Гавань", + "Historian": "Историк", + "Hopewatch Gate": "Врата Hopewatch", + "Hot Air Balloon Race": "Гонка на Баллонах с Горячим Воздухом", + "Hotel": "Отель", + "Hourglass": "Песочные часы", + "Husband": "Муж", + "Husband / Wife": "Муж / Жена", + "If ": "Если ", + "If paying for card with resources, pay resources to an opponent.": "Если Вы платите за эту карту своими ресурсами, они отдаются оппонент.", + "If sharing a space with a ": "Если занятое место делится с ", + "If total point base value of drawn ": "Если сумма очков на вытянутых ", + "If you have at least 2 ": "Если у Вас есть хотя бы 2 ", + "Iluminor the Inventor": "Иллюминор Изобретатель", + "in your city. Gain 1 ": " в Вашем городе. Получите 1 ", + "Increase your hand size by 1 for every ": "Увеличьте максимальный размер руки на 1 за каждую Вашу", + "Inn": "Гостиница", + "Innkeeper": "Трактирщик", + "Inventor": "Изобретатель", + "JOURNEY": "Путешествие", + "Journey 2": "Путешествие 2", + "Journey 3": "Путешествие 3", + "Journey 4": "Путешествие 4", + "Journey 5": "Путешествие 5", + "Journey Points": "Очки за Путешествия", + "Judge": "Судья", + "Juniper Jig Dance Contest": "Танцевальный Конкурс Juniper Jig", + "Key to the City": "Ключ от города", + "King": "Король", + "King's Road Established": "Открытие Королевской дороги", + "KNOLL": "Холм", + "Lamplighter": "Фонарщик", + "Library": "Библиотека", + "LOCATION": "Локация", + "Locations": "Локации", + "Locomotive": "Паровоз", + "Lookout": "Дозорный", + "Magic Snow": "Волшебный Снег", + "Magician": "Фокусник", + "Main Road": "Главная дорога", + "Market": "Рынок", + "Masque": "Маска", + "May discard any ": "Вы можете сбросить любую ", + "May not be copied, removed, or reactivated.": "Карту нельзя копировать, убрать или реактивировать.", + "May not be used with any other card-playing ability.": "Не может быть использована со свойствами других карт.", + "May not copy Cemetary, Chapel, Monastary, Pirate Ship, or Legendary.": "Постройки типа Legendary, Кладбище, Часовня, Монастырь, Пиратский Корабль копировать нельзя.", + "May not copy Fool, Main Road, Ruins, or Legendary.": "Нельзя копировать карты Fool, Main Road, Ruins, or Legendary.", + "Mayor": "Мэр", + "Meadow": "Лужайка", + "Messenger": "Гонщик", + "Miller": "Мельник", + "Mine": "Шахта", + "Miner Mole": "Крот-шахтер", + "Mirror": "Зеркало", + "Mistrise Fountain": "Фонтан Mistrise", + "Monastery": "Монастырь", + "Monk": "Монах", + "Move 1 of your deployed workers to a new location.": "Перемещает 1 из Ваших занятых рабочих с одной локации на другую.", + "Move this ": "Помещает эту ", + "Museum": "Музей", + "Must share a space with a Construction.": "Непременно делит занятое место с Постройкой.", + "New Game": "Новая Игра", + "Observatory": "Обсерватория", + "Omicron the Elder": "Омикрон Старший", + "On Card:": "На Карте:", + "Opens an additional space in your city. ": "Добавляет одно дополнительное место в Вашем городе. ", + "Other players may visit this card. Owner gains ": "Другие игроки могут посетить эту карту. Владелец получает ", + "Owner gains ": "Владелец получает ", + "Palace": "Дворец", + "Pay 1 ": "Заплатите 1 ", + "Pay 2 ": "Заплатите 2 ", + "Pay 3 ": "Заплатите 3 ", + "Peddler": "Коробейник", + "Photographer": "Фотограф", + "Pie Eating Contest": "Конкурс по поеданию пирогов", + "Pirate": "Пират", + "Pirate Ship": "Пиратский корабль", + "Place 1 ": "Разместить 1 ", + "Place Ambassador": "Разместить Амбассадора", + "Place either 3 ": "Разместите либо 3 ", + "Place worker: Take all resources on this card.": "Разместите рабочего: Взять все ресурсы с этой карты.", + "Play a ": "Сыграть ", + "Play a Critter or Construction from your hard for 3 fewer ": "Сыграйте Существо или Постройку из Вашей руки со скидкой в 3 ", + "Play a Station ": " Используйте Станцию ", + "Play Adornment": "Сыграть Украшение", + "Play Card": "Сыграть Карту", + "Play this ": "Сыграть эту ", + "Play Train Ticket": "Сыграть Билет на Поезд", + "Player": "Игрок", + "Players": "Игроки", + "Poet": "Поэт", + "POINTS": "ОЧКИ", + "Points Breakdown": "Итоговые Очки", + "Points: ": "Очки: ", + "Post Office": "Почта", + "Postal Pigeon": "Почтовый голубь", + "Prepare for Season": "Подготовка к Сезону", + "Prepare for season": "Подготовка к сезону", + "Prepare for season: ": "Подготовка к сезону: ", + "Queen": "Королева", + "Randomize player order": "Случайный порядок игроков", + "Ranger": "Егерь", + "Requires 10 ": "Требует 10 ", + "Requires 4 ": "Требует 4 ", + "Requires 5 ": "Требует 5 ", + "Requires 9 ": "Требует 9 ", + "Requires that you have achieved at least 3 ": "Требует, чтобы Вами были достигнуты как минимум 3 ", + "Reserve Meadow/Station Card": "Зарезервировать Карту с Лужайки/Станции", + "Resin Refinery": "Завод жвачки", + "Reveal 2 ": "Показать 2 ", + "Reveal 4 ": "Показать 4 ", + "Reveal and discard 1 ": "Откройте и сбросьте 1 ", + "Reveal and discard 2 ": "Откройте и сбросьте 2 ", + "Reveal and discard 3 ": "Откройте и сбросьте 3 ", + "Reveal and discard 3 different colored ": "Откройте и сбросьте 3 разноцветных ", + "River Destination": "Точку Реки", + "River Destination Spots": "Места назначения на реке", + "River Destinations": "Речные направления", + "Royal Tea": "Королевский чай", + "Royal Wedding": "Королевская свадьба", + "Ruins": "Руины", + "Scales": "Весы", + "School": "Школа", + "Score based on your city.": "Бонусные очки будут учитываться на основе Вашего города.", + "Seaglass Amulet": "Амулет из морского стекла", + "Select Visitor to discard": "Выбрать Посетителя для выброса", + "Select Visitor to keep": "Выбрать Посетителя для удержания", + "Settings": "Настройки", + "Share Feedback": "Обратная Связь", + "Share space:": "Делит место с:", + "Shepherd": "Пастух", + "Shipwright": "Судостроитель", + "Shoal": "Мелководье", + "Shopkeeper": "Лавочник", + "Show points in realtime": "Показывать очки в реальном времени", + "Snout the Explorer": "Снаут-исследователь", + "spectator link": "ссылка для зрителей", + "Spring": "Весна", + "spring": "весна", + "Spyglass": "Подзорная труба", + "Start Game": "Начать Игру", + "STATION": "Станция", + "Statues Commissioned": "Построены Статуи", + "Stock Market Boom": "Бум на фондовом рынке", + "Storehouse": "Склад", + "Submit": "Применить", + "Summer": "Лето", + "summer": "лето", + "Sunblaze Bridge": "Мост Санблейз", + "Sundial": "Солнечные часы", + "Sunflower Parade": "Парад Sunflower", + "Tea House": "Чайная", + "Teacher": "Учитель", + "The Everdell Games": "Игры Everdell", + "the Station.": "", + "Theatre": "Театр", + "Then gain 1 ": "После дает 1 ", + "then gain 1 ": ", затем получите 1 ", + "Tiara": "Тиара", + "to a maximum of 3.": ", но не более 3.", + "Total": "Всего", + "Twig Barge": "Ветко-баржа", + "Undertaker": "Гробовщик", + "Undo last action": "Отменить последнее действие", + "Unique Construction": "Уникальная постройка", + "Unique Constructions": "Уникальные Постройки", + "Unique Critter": "Уникальное существо", + "Unique Critters": "Уникальные Существа", + "University": "Университет", + "Unlocks second ": "Разблокирует вторую ", + "Visit Destination Card": "Посетить Карту", + "Visit Location": "Посетить Локацию", + "Visit to gain 1 ": "Посетите и получите 1 ", + "VP": "ВП", + "Waiting for": "Ожидание", + "Wanderer": "Странник", + "Watermill": "Водяная мельница", + "What's New": "Что Нового", + "When achieved, bring back one of your deployed workers": "При достижении позволяет вернуть одного из занятых рабочих.", + "When achieved, draw 1 ": "При достижении этого события, возьмите 1 ", + "When achieved, gain 1 ": "При достижении получите 1 ", + "When achieved, pay 2 ": "При достижении заплатите 2 ", + "When achieved, place 1 ": "При достижении разместите 1 ", + "When achieved, place either your ": "При достижении разместите либо Вашего ", + "When achieved, place up to 2 ": "При достижении позволяет разместить до 2 ", + "When achieved, reveal 5 ": "При достижении этого события, откройте 5 ", + "When achieved, you may discard 1 ": "При достижении Вы можете сбросить 1 ", + "When achieved, you may give opponents up to a total of 3 ": "При достижении Вы можете отдать оппонентам до 3 ", + "When achieved, you may give up to 6 ": "При достижении Вы можете отдать до 6 ", + "When achieved, you may place up to 3 ": "При достижении этого события, Вы можете поместить до 3 ", + "When achieved, you may play 1 ": "При достижении Вы можете сыграть 1 ", + "When achieved, you may search through the deck for a ": "При достижении Вы можете найти в колоде ", + "When played, place 2 ": "При использовании, помещает 2 ", + "When played, place 3 ": "При использовании помещает 3 ", + "When playing a ": "При использовании ", + "When visiting a River Destination, this Messenger is considered the same color as the shared Construction.": "При посещении Реки, этот Посланник считается того же цвета, что и занятая им Постройка.", + "When you play a ": "При использовании ", + "Wife": "Жена", + "Winter": "Зима", + "winter": "зима", + "Wonder": "Чудо", + "Wonder Points": "Очки за Чудеса", + "Woodcarver": "Резчик по дереву", + "Worker stays here permanently. Unlock second ": "Работник остается здесь навсегда. Разблокирует вторую ", + "WORKERS": "РАБОТНИКИ", + "Workers on card:": "Рабочих на карте:", + "Workers: ": "Рабочие: ", + "You": "Вы", + "You may activate 2 different ": "Вы можете активировать 2 разных ", + "You may activate Production for up to 3 ": "Вы можете активировать Производство до 3 ", + "You may copy any ability from an ": "Вы можете скопировать любую способность из ", + "You may discard 1 other ": "Вы можете сбросить 1 другое ", + "You may discard up to 4 ": "Вы можете сбросить до 4 ", + "You may discard up to 5 ": "Вы можете сбросить до 5 ", + "You may draw 2 ": "Вы можете взять 2 ", + "You may draw any or place any beneath this Event.": "Вы можете взять любую карту, либо поместить любую под этим Событием.", + "You may give up to 2 ": "Вы можете отдать до 2 ", + "You may give up to 5 ": "Вы можете отдать до 5 ", + "You may pay 1 ": "Вы можете заплатить 1 ", + "You may pay up to 2 ": "Вы можете заплатить до 2 ", + "You may pay up to 3 ": "Вы можете заплатить до 3 ", + "You may play 1 ": "Вы можете сыграть 1 ", + "You may play 1 worth up to ": "Вы можете сыграть 1 карту ценностью до ", + "You may reactivate 2 ": "Вы можете реактивировать 2 ", + "You may take the action of any 1 Forest location, and gain 1 ": "Вы можете совершить действие любой Лесной Локации и получить 1 ", + "Your City": "Ваш Город", + "Your hand": "Ваша Рука" +} \ No newline at end of file diff --git a/src/accept-header.ts b/src/accept-header.ts new file mode 100644 index 00000000..3c343d17 --- /dev/null +++ b/src/accept-header.ts @@ -0,0 +1,141 @@ +/* eslint-disable */ +// https://raw.githubusercontent.com/vercel/next.js/refs/tags/v13.5.4/packages/next/src/server/accept-header.ts + +interface Selection { + pos: number; + pref?: number; + q: number; + token: string; +} + +interface Options { + prefixMatch?: boolean; + type: "accept-language"; +} + +function parse( + raw: string, + preferences: string[] | undefined, + options: Options +) { + const lowers = new Map(); + const header = raw.replace(/[ \t]/g, ""); + + if (preferences) { + let pos = 0; + for (const preference of preferences) { + const lower = preference.toLowerCase(); + lowers.set(lower, { orig: preference, pos: pos++ }); + if (options.prefixMatch) { + const parts = lower.split("-"); + while ((parts.pop(), parts.length > 0)) { + const joined = parts.join("-"); + if (!lowers.has(joined)) { + lowers.set(joined, { orig: preference, pos: pos++ }); + } + } + } + } + } + + const parts = header.split(","); + const selections: Selection[] = []; + const map = new Set(); + + for (let i = 0; i < parts.length; ++i) { + const part = parts[i]; + if (!part) { + continue; + } + + const params = part.split(";"); + if (params.length > 2) { + throw new Error(`Invalid ${options.type} header`); + } + + let token = params[0].toLowerCase(); + if (!token) { + throw new Error(`Invalid ${options.type} header`); + } + + const selection: Selection = { token, pos: i, q: 1 }; + if (preferences && lowers.has(token)) { + selection.pref = lowers.get(token)!.pos; + } + + map.add(selection.token); + + if (params.length === 2) { + const q = params[1]; + const [key, value] = q.split("="); + + if (!value || (key !== "q" && key !== "Q")) { + throw new Error(`Invalid ${options.type} header`); + } + + const score = parseFloat(value); + if (score === 0) { + continue; + } + + if (Number.isFinite(score) && score <= 1 && score >= 0.001) { + selection.q = score; + } + } + + selections.push(selection); + } + + selections.sort((a, b) => { + if (b.q !== a.q) { + return b.q - a.q; + } + + if (b.pref !== a.pref) { + if (a.pref === undefined) { + return 1; + } + + if (b.pref === undefined) { + return -1; + } + + return a.pref - b.pref; + } + + return a.pos - b.pos; + }); + + const values = selections.map((selection) => selection.token); + if (!preferences || !preferences.length) { + return values; + } + + const preferred: string[] = []; + for (const selection of values) { + if (selection === "*") { + // @ts-ignore + for (const [preference, value] of lowers) { + if (!map.has(preference)) { + preferred.push(value.orig); + } + } + } else { + const lower = selection.toLowerCase(); + if (lowers.has(lower)) { + preferred.push(lowers.get(lower)!.orig); + } + } + } + + return preferred; +} + +export function acceptLanguage(header = "", preferences?: string[]) { + return ( + parse(header, preferences, { + type: "accept-language", + prefixMatch: true, + })[0] || "" + ); +} diff --git a/src/components/Adornment.tsx b/src/components/Adornment.tsx index 8bf62da7..5e2b3386 100644 --- a/src/components/Adornment.tsx +++ b/src/components/Adornment.tsx @@ -6,13 +6,15 @@ import { Adornment as AdornmentModel } from "../model/adornment"; import { AdornmentName, ResourceType } from "../model/types"; import { Description, ItemWrapper } from "./common"; +import { useTranslation } from "next-i18next"; export const AdornmentInner = ({ name }: { name: AdornmentName }) => { const adornment = AdornmentModel.fromName(name); + const { t } = useTranslation("common"); return (
-
{adornment.name}
+
{t(adornment.name)}
{ // handle the farm and evertree const getAssociatedCard = (card: CardModel) => { + const { t } = useTranslation("common"); + const associatedCard = card.associatedCard; if (associatedCard.type === "CARD") { - return associatedCard.cardName; + return t(associatedCard.cardName); } else if (associatedCard.type === "HUSBAND_WIFE") { - return "Husband / Wife"; + return t("Husband / Wife"); } else if (associatedCard.type === "ANY") { - return "Any"; + return t("Any"); } else if (associatedCard.type === "GOLDEN_LEAF") { const textParts = toGameText([ "Any ", @@ -184,9 +187,10 @@ const Card: React.FC<{ name: CardName; usedForCritter?: boolean }> = ({ name, usedForCritter = false, }) => { + const { t } = useTranslation("common"); + const card = CardModel.fromName(name as any); const colorClass = colorClassMap[card.cardType]; - const rarityLabel = getRarityLabel(card); return (
@@ -203,7 +207,7 @@ const Card: React.FC<{ name: CardName; usedForCritter?: boolean }> = ({ {card.baseVP}
- {name} + {t(name)} {card.expansion && ( {card.expansion} )} @@ -243,13 +247,24 @@ const Card: React.FC<{ name: CardName; usedForCritter?: boolean }> = ({
-
-
- {rarityLabel} -  · {romanize(card.numInDeck)} -
- + +
+ ); +}; + +const CardFooter: React.FC<{ + card: CardModel; + usedForCritter: boolean; +}> = ({ card, usedForCritter }) => { + const { t } = useTranslation("common"); + const rarityLabel = getRarityLabel(card); + return ( +
+
+ {t(rarityLabel)} +  · {romanize(card.numInDeck)}
+
); }; @@ -267,6 +282,7 @@ const CardPoints: React.FC<{ gameState: GameState; }> = ({ cardName, cardIdx, cardOwner, gameState }) => { const card = CardModel.fromName(cardName); + const { t } = useTranslation("common"); // Bonus for the WIFE needs to be computed separately because it // relies on the whole city. @@ -278,7 +294,7 @@ const CardPoints: React.FC<{ extraPoints += 3; } return ( -
Points: {card.getPoints(cardOwner, gameState) + extraPoints}
+
{t("Points: ")} {card.getPoints(cardOwner, gameState) + extraPoints}
); }; @@ -289,6 +305,8 @@ export const PlayedCard: React.FC<{ viewerId: string | null; cardIdx?: number; }> = ({ playedCard, cardOwner, gameState, viewerId, cardIdx }) => { + const { t } = useTranslation("common"); + const { cardOwnerId, cardName, @@ -310,7 +328,7 @@ export const PlayedCard: React.FC<{
- Card Owner: {viewerId === cardOwnerId ? "You" : cardOwner.name} + {t("Card Owner:")} {viewerId === cardOwnerId ? t("You") : cardOwner.name}
{cardIdx && card.cardType === CardType.PROSPERITY && ( )} {"workers" in playedCard && ( -
Workers on card: {workers.length}
+
{t("Workers on card:")} {workers.length}
)} {"ambassador" in playedCard && ( -
Ambassadors on card: {ambassador ? "1" : "0"}
+
{t("Ambassadors on card:")} {ambassador ? "1" : "0"}
)} {"shareSpaceWith" in playedCard && shareSpaceWith && ( -
Share space: {shareSpaceWith}
+
{t("Share space:")} {shareSpaceWith}
)} {"pairedCards" in playedCard && (
- Beneath Card:{" "} + {t("Beneath Card:")}{" "}
)} {"resources" in playedCard && (
- On Card:{" "} + {t("On Card:")}{" "}
)} diff --git a/src/components/Event.tsx b/src/components/Event.tsx index 1914fad5..70adab3a 100644 --- a/src/components/Event.tsx +++ b/src/components/Event.tsx @@ -6,11 +6,13 @@ import { Event as EventModel } from "../model/event"; import { EventName, EventType } from "../model/types"; import { Description, ItemWrapper } from "./common"; +import { useTranslation } from "next-i18next"; export const EventInner: React.FC<{ name: EventName; }> = ({ name }) => { const event = EventModel.fromName(name as any); + const { t } = useTranslation("common"); return ( <>
@@ -39,7 +41,7 @@ export const EventInner: React.FC<{ /> ) : ( - name + t(name) )}
@@ -74,6 +76,7 @@ export const Event = ({ name: EventName; claimedBy?: string | null; }) => { + const { t } = useTranslation("common"); return (
- {"Claimed: "} + {t("Claimed: ")} {claimedBy}
) diff --git a/src/components/Game.tsx b/src/components/Game.tsx index e00ea1e0..8cc4ebbd 100644 --- a/src/components/Game.tsx +++ b/src/components/Game.tsx @@ -10,10 +10,11 @@ import GameInputBox from "./GameInputBox"; import GameUpdater from "./GameUpdater"; import { Player } from "../model/player"; import { GameState } from "../model/gameState"; -import { GameInput } from "../model/types"; +import { GameInput, ResourceType } from "../model/types"; import { GameJSON, PlayerJSON } from "../model/jsonTypes"; import styles from "../styles/Game.module.css"; +import StickyBar from "./StickyBar"; const Game: React.FC<{ gameJSON: GameJSON; @@ -99,6 +100,17 @@ const Game: React.FC<{ viewingPlayer={viewingPlayer} /> + p.name)} + playerResources={gameState.players.reduce< + Record> + >((acc, player) => { + acc[player.name] = player.getResources(); + return acc; + }, {})} + players={gameState.players} + gameState={gameState} + />
); }; diff --git a/src/components/GameAdmin.tsx b/src/components/GameAdmin.tsx index 6c743d48..6104a41a 100644 --- a/src/components/GameAdmin.tsx +++ b/src/components/GameAdmin.tsx @@ -7,6 +7,7 @@ import { Meadow } from "./gameBoard"; import GameInputBox from "./GameInputBox"; import styles from "../styles/GameAdmin.module.css"; +import { useTranslation } from "next-i18next"; const GameAdmin = ({ gameJSON, @@ -15,12 +16,14 @@ const GameAdmin = ({ gameJSON: GameJSON; devDebugMode: boolean; }) => { + const { t } = useTranslation("common"); + const gameState = GameState.fromJSON(gameJSON.gameState); return (
- +
- Copy links to share with other players: + {t('Copy links to share with other players:')}
diff --git a/src/components/GameBuilder.tsx b/src/components/GameBuilder.tsx index a559cfde..8984c443 100644 --- a/src/components/GameBuilder.tsx +++ b/src/components/GameBuilder.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { useRouter } from "next/router"; import { Formik, Form, Field, FieldArray } from "formik"; import styles from "../styles/Home.module.css"; +import { useTranslation } from "next-i18next"; let playerIdx = 0; const getDummyPlayer = (name = "") => { @@ -13,6 +14,8 @@ const getDummyPlayer = (name = "") => { const GameBuilder: React.FC = () => { const router = useRouter(); + const { t } = useTranslation("common"); + return (
{ name="players" render={(arrayHelpers) => (
-

New Game

-

Players

+

{t("New Game")}

+

{t("Players")}

{numPlayers > 0 ? ( players.map((player, idx) => (
{ <> )}
-

Expansions

+

{t("Expansions")}

-

Settings

+

{t("Settings")}

@@ -152,7 +155,7 @@ const GameBuilder: React.FC = () => { className={styles.button} type="submit" > - Start Game + {t("Start Game")}
diff --git a/src/components/GameInputBox.tsx b/src/components/GameInputBox.tsx index e5d5466d..0225ebe6 100644 --- a/src/components/GameInputBox.tsx +++ b/src/components/GameInputBox.tsx @@ -29,14 +29,17 @@ import GameInputVisitorSelector from "./GameInputVisitorSelector"; import GameInputPlayCard from "./GameInputPlayCard"; import { assertUnreachable } from "../utils"; +import { useTranslation } from "next-i18next"; const GameInputBoxText: React.FC<{ title?: string; text: string; }> = ({ title = "Game Input", text }) => { + const { t } = useTranslation("common"); + return ( -

{text}

+

{t(text)}

); }; @@ -257,8 +260,10 @@ const GameInputBox: React.FC<{ viewingPlayer, }) => { const activePlayer = gameState.getActivePlayer(); + const { t } = useTranslation("common"); + if (gameState.isGameOver()) { - return ; + return ; } if (!viewingPlayer) { return ( @@ -269,7 +274,7 @@ const GameInputBox: React.FC<{ return ( ); } @@ -300,7 +305,7 @@ const GameInputBox: React.FC<{ // For inputs that require selecting multiple things, update the button // text to say how many have been selected. - let submitLabel = "Submit"; + let submitLabel = t("Submit"); if (selectedGameInput) { if ( selectedGameInput.inputType === GameInputType.SELECT_CARDS || @@ -308,13 +313,13 @@ const GameInputBox: React.FC<{ ) { const numSelected = selectedGameInput.clientOptions.selectedCards.length; - submitLabel = `${numSelected} Selected`; + submitLabel = `${numSelected} ${t("Selected")}`; } else if ( selectedGameInput.inputType === GameInputType.DISCARD_CARDS ) { const numSelected = selectedGameInput.clientOptions.cardsToDiscard.length; - submitLabel = `${numSelected} Selected`; + submitLabel = `${numSelected} ${t("Selected")}`; } } diff --git a/src/components/GameLog.tsx b/src/components/GameLog.tsx index d5039849..440ce427 100644 --- a/src/components/GameLog.tsx +++ b/src/components/GameLog.tsx @@ -9,6 +9,7 @@ import { GameLogEntry } from "../model/types"; import { GameState } from "../model/gameState"; import { GameStateJSON } from "../model/jsonTypes"; import { Description, GameBlock } from "./common"; +import { useTranslation } from "next-i18next"; const GameLog: React.FC<{ logs: GameLogEntry[]; @@ -25,6 +26,7 @@ const GameLog: React.FC<{ logsElRef.current.scrollTop = lastLogElRef.current.offsetTop; } }, [logs.length]); + const { t } = useTranslation("common"); let activePlayerEl = null; if (gameStateJSON) { @@ -34,10 +36,10 @@ const GameLog: React.FC<{ <>
{gameStateImpl.isGameOver() ? ( - Game Over + {t("Game Over")} ) : ( <> - Active: + {t("Active: ")} {activePlayerImpl.name} )} @@ -48,17 +50,17 @@ const GameLog: React.FC<{ } return ( - + {gameStateJSON && (
{activePlayerEl}
- Deck: + {t("Deck: ")} {gameStateJSON.deck.numCards}
{" / "}
- Discard: + {t("Discard: ")} {gameStateJSON.discardPile.numCards}
diff --git a/src/components/GamePointsBreakdown.tsx b/src/components/GamePointsBreakdown.tsx index d7eb4059..3062de20 100644 --- a/src/components/GamePointsBreakdown.tsx +++ b/src/components/GamePointsBreakdown.tsx @@ -5,26 +5,28 @@ import { ResourceType } from "../model/types"; import { GameState } from "../model/gameState"; import { GameBlock, Description } from "./common"; +import { useTranslation } from "next-i18next"; const GamePointsBreakdown: React.FC<{ gameState: GameState; }> = ({ gameState }) => { + const { t } = useTranslation("common"); return (
- - - + + + {gameState.gameOptions.pearlbrook && ( <> - - + + )} - + @@ -37,7 +39,7 @@ const GamePointsBreakdown: React.FC<{ /> )} - + diff --git a/src/components/Location.tsx b/src/components/Location.tsx index 111ee625..2d91e7c3 100644 --- a/src/components/Location.tsx +++ b/src/components/Location.tsx @@ -11,6 +11,7 @@ import { LocationOccupancy, } from "../model/types"; import { Description, ItemWrapper } from "./common"; +import { useTranslation } from "next-i18next"; const colorClassMap = { BASIC: styles.color_basic, @@ -24,6 +25,7 @@ const colorClassMap = { export const LocationInner: React.FC<{ name: LocationName }> = ({ name }) => { const location = LocationModel.fromName(name as any); const colorClass = colorClassMap[location.type]; + const { t } = useTranslation("common"); return ( <>
@@ -33,12 +35,12 @@ export const LocationInner: React.FC<{ name: LocationName }> = ({ name }) => {
-
{location.type}
+
{t(location.type)}
{location.occupancy === LocationOccupancy.UNLIMITED ? "1+" : location.occupancy === LocationOccupancy.UNLIMITED_MAX_ONE - ? "1 / PLAYER" + ? t("1 / PLAYER") : ""}
@@ -54,6 +56,7 @@ const Location: React.FC<{ gameState?: GameState | null; }> = ({ name, playerWorkers = [], viewingPlayer = null, gameState = null }) => { const location = LocationModel.fromName(name); + const { t } = useTranslation("common"); let acceptingWorkers = true; if (location.occupancy === LocationOccupancy.EXCLUSIVE) { @@ -83,7 +86,7 @@ const Location: React.FC<{ footerChildren={ playerWorkers.length !== 0 && (
- Workers: + {t("Workers: ")} {playerWorkers.join(", ")} diff --git a/src/components/Players.tsx b/src/components/Players.tsx index 6b639082..fd85e32d 100644 --- a/src/components/Players.tsx +++ b/src/components/Players.tsx @@ -15,7 +15,6 @@ import { GameBlock } from "./common"; import { PlayerCity } from "./gameBoard"; import { InfoIconSvg, - VPIcon, CardIcon, GoldenLeafIcon, ReservationTokenIcon, @@ -25,6 +24,7 @@ import { EmptyCitySpotIcon, TicketIcon, } from "./common"; +import { useTranslation } from "next-i18next"; export const Players = ({ gameState, @@ -55,6 +55,289 @@ export const Players = ({ ); }; +// --- Components --- + +export const PlayerName: React.FC<{ name: string; onClick: () => void }> = ({ + name, + onClick, +}) => { + return ( +
+
+
{name}
+
+
+ +
+
+
+
+ ); +}; + +const PlayerStatusPill: React.FC<{ + player: Player; + isActivePlayer: boolean; +}> = ({ player, isActivePlayer }) => { + const { t } = useTranslation("common"); + return ( +
+ {player.getStatus() === TPlayerStatus.GAME_ENDED ? ( +
+ {t("passed")} +
+ ) : ( + <> +
+ {t(player.currentSeason.toLowerCase())} +
+ {isActivePlayer && ( +
+ {t("active")} +
+ )} + + )} +
+ ); +}; + +export const ResourceList: React.FC<{ + playerResources: Record; +}> = ({ playerResources }) => { + return ( +
+ {[ + ResourceType.TWIG, + ResourceType.RESIN, + ResourceType.BERRY, + ResourceType.PEBBLE, + ].map((resourceType) => ( + + ))} +
+ ); +}; + +const ResourceItem: React.FC<{ + resourceType: ResourceType; + count: number; +}> = ({ resourceType, count }) => { + return ( +
+
+ +
+
{count}
+
+ ); +}; + +export const CardTypeList: React.FC<{ player: Player }> = ({ player }) => { + return ( +
+ {[ + CardType.TRAVELER, + CardType.PRODUCTION, + CardType.DESTINATION, + CardType.GOVERNANCE, + CardType.PROSPERITY, + ].map((cardType) => ( + + ))} + } + /> +
+ ); +}; + +const CardTypeItem: React.FC<{ + cardType: CardType | null; + count: number; + icon?: React.ReactNode; +}> = ({ cardType, count, icon }) => { + return ( +
+
+ {icon || } +
+
{count}
+
+ ); +}; + +export const OtherResources: React.FC<{ + player: Player; + gameState: GameState; + showRealtimePoints?: boolean; +}> = ({ player, gameState, showRealtimePoints }) => { + return ( +
+ {gameState.gameOptions.pearlbrook && ( + + )} + + } + /> + {gameState.gameOptions.newleaf?.cards && ( + } + /> + )} + {gameState.gameOptions.newleaf?.reserving && ( + + )} + {gameState.gameOptions.newleaf?.ticket && ( + + )} + {gameState.gameOptions.pearlbrook && ( + } + /> + )} + + {gameState.gameOptions.pearlbrook && ( + + )} + {showRealtimePoints && ( + + )} +
+ ); +}; + +const ReservationTokenItem: React.FC<{ player: Player }> = ({ player }) => { + return ( +
+
+ +
+
+ {player.canReserveCard() + ? "Unused" + : player.getReservedCardOrNull() ?? "Used"} +
+
+ ); +}; + +const TrainTicketItem: React.FC<{ player: Player }> = ({ player }) => { + return ( +
+
+
+ +
+
+
+ {player.trainTicketStatus === TrainTicketStatus.VALID_FROM_WINTER + ? "Valid from Winter" + : player.trainTicketStatus === TrainTicketStatus.VALID_FROM_SUMMER + ? "Valid from Summer" + : "Discarded"} +
+
+ ); +}; + +const WorkerCountItem: React.FC<{ player: Player }> = ({ player }) => { + const { t } = useTranslation("common"); + + return ( +
+
+ {t("WORKERS")} +
+
+ {player.numAvailableWorkers} +
+
+ ); +}; + +const AmbassadorCountItem: React.FC<{ player: Player }> = ({ player }) => { + const { t } = useTranslation("common"); + + return ( +
+
+ {t("AMBASSADORS")} +
+
+ {player.hasUnusedAmbassador() ? 1 : 0} +
+
+ ); +}; + +const PointsItem: React.FC<{ player: Player; gameState: GameState }> = ({ + player, + gameState, +}) => { + const { t } = useTranslation("common"); + + return ( +
+
{t("POINTS")}
+
+ {player.getPoints(gameState)} +
+
+ ); +}; + +// --- Main Component --- const PlayerStatus: React.FC<{ player: Player; @@ -72,6 +355,8 @@ const PlayerStatus: React.FC<{ showRealtimePoints, }) => { const [showCity, setShowCity] = useState(false); + const { t } = useTranslation("common"); + return ( <>
-
-
-
{player.name}
-
-
{ - setShowCity(!showCity); - }} - style={{ - cursor: "pointer", - }} - className={styles.status_box_item_resource_icon} - > - -
-
-
- -
- {player.getStatus() === TPlayerStatus.GAME_ENDED ? ( -
- passed -
- ) : ( - <> -
- {player.currentSeason.toLowerCase()} -
- {isActivePlayer && ( -
- active -
- )} - - )} -
-
+ setShowCity(!showCity)} + /> +
-
- {[ - ResourceType.TWIG, - ResourceType.RESIN, - ResourceType.BERRY, - ResourceType.PEBBLE, - ].map((resourceType) => ( -
-
- -
-
- {player.getNumResourcesByType(resourceType)} -
-
- ))} -
+
-
- {[ - CardType.TRAVELER, - CardType.PRODUCTION, - CardType.DESTINATION, - CardType.GOVERNANCE, - CardType.PROSPERITY, - ].map((cardType) => ( -
-
- -
-
- {player.getNumCardType(cardType)} -
-
- ))} -
-
- -
-
- {player.maxCitySize - player.getNumOccupiedSpacesInCity()} -
-
-
+
-
- {gameState.gameOptions.pearlbrook && ( -
-
- -
-
- {player.getNumResourcesByType(ResourceType.PEARL)} -
-
- )} -
-
- -
-
- {player.getNumResourcesByType(ResourceType.VP)} -
-
-
-
- -
-
- {player.numCardsInHand} -
-
- {gameState.gameOptions.newleaf?.cards && ( -
-
- -
-
- {player.numGoldenLeaf} -
-
- )} - {gameState.gameOptions.newleaf?.reserving && ( -
-
- -
-
- {player.canReserveCard() - ? "Unused" - : player.getReservedCardOrNull() ?? "Used"} -
-
- )} - {gameState.gameOptions.newleaf?.ticket && ( -
-
-
- -
-
-
- {player.trainTicketStatus === - TrainTicketStatus.VALID_FROM_WINTER - ? "Valid from Winter" - : player.trainTicketStatus === - TrainTicketStatus.VALID_FROM_SUMMER - ? "Valid from Summer" - : "Discarded"} -
-
- )} - {gameState.gameOptions.pearlbrook && ( -
-
- -
-
- {player.numAdornmentsInHand} -
-
- )} -
-
- {"WORKERS"} -
-
- {player.numAvailableWorkers} -
-
- {gameState.gameOptions.pearlbrook && ( -
-
- {"AMBASSADORS"} -
-
- {player.hasUnusedAmbassador() ? 1 : 0} -
-
- )} - {showRealtimePoints && ( -
-
- {"POINTS"} -
-
- {player.getPoints(gameState)} -
-
- )} -
+
{showCity && (
- + { const riverDestination = RiverDestinationModel.fromName(name); + const { t } = useTranslation("common"); + return (
- {riverDestination.name} + {t(riverDestination.name)} {riverDestination.name !== RiverDestinationName.SHOAL && ( -
{riverDestination.type}
+
{t(riverDestination.type)}
)}
diff --git a/src/components/StickyBar.tsx b/src/components/StickyBar.tsx new file mode 100644 index 00000000..fca02f5b --- /dev/null +++ b/src/components/StickyBar.tsx @@ -0,0 +1,73 @@ +import * as React from "react"; +import { GameState } from "../model/gameState"; +import { Player } from "../model/player"; +import { ResourceType } from "../model/types"; +import styles from "../styles/StickyBar.module.css"; // Import your Game component's styles +import { + CardTypeList, + OtherResources, + PlayerName, + ResourceList, +} from "./Players"; +import { BsChevronDoubleDown } from "react-icons/bs"; + +export const StickyBar: React.FC<{ + playerNames: string[]; + playerResources: Record>; + players: Player[]; + gameState: GameState; +}> = ({ playerNames, playerResources, players, gameState }) => { + const [isCollapsed, setIsCollapsed] = React.useState(false); + + const handleClick = () => { + setIsCollapsed(!isCollapsed); + }; + return ( +
+ +
+
+ {playerNames.map((name) => { + return ( +
+ + + p.name === name)!} /> + p.name === name)!} + gameState={gameState} + showRealtimePoints={true} + /> +
+ ); + })} +
+
+
+ ); +}; + +const CollapseButton: React.FC<{ + isCollapsed: boolean; + handleClick: () => void; +}> = ({ isCollapsed, handleClick }) => { + return ( +
+ +
+ ); +}; + +export default StickyBar; diff --git a/src/components/common.tsx b/src/components/common.tsx index 315673db..a1d364c2 100644 --- a/src/components/common.tsx +++ b/src/components/common.tsx @@ -14,6 +14,7 @@ import { CardType, } from "../model/types"; import { assertUnreachable } from "../utils"; +import { useTranslation } from "next-i18next"; export const GameBlockTitle: React.FC = ({ children }) => { return ( @@ -28,9 +29,11 @@ export const GameBlock: React.FC<{ title: string; id?: string }> = ({ id, children, }) => { + const { t } = useTranslation("common"); + return (
- {title} + {t(title)} {children}
); @@ -174,6 +177,8 @@ export const ResourceTypeIcon = ({ }: { resourceType: ResourceType; }) => { + const { t } = useTranslation("common"); + return ( <> {resourceType === ResourceType.BERRY ? ( @@ -211,8 +216,15 @@ export const ResourceTypeIcon = ({ layout="fill" priority /> + ) : resourceType === ResourceType.VP ? ( + ) : ( - <>{resourceType} + <>{t(resourceType)} )} ); @@ -259,12 +271,14 @@ export const GameIcon = ({ }; export const Description = ({ textParts }: { textParts: GameText }) => { + const { t } = useTranslation("common"); + return textParts ? ( {textParts.map((part: TextPart, idx: number) => { switch (part.type) { case "text": - return part.text; + return t(part.text); case "iblock": return ( @@ -274,13 +288,13 @@ export const Description = ({ textParts }: { textParts: GameText }) => { case "i": return ( - {part.text} + {t(part.text)} ); case "em": return ( - {part.text} + {t(part.text)} ); case "BR": diff --git a/src/components/gameBoard.tsx b/src/components/gameBoard.tsx index 2d3f49d7..53075154 100644 --- a/src/components/gameBoard.tsx +++ b/src/components/gameBoard.tsx @@ -23,12 +23,15 @@ import Adornment from "./Adornment"; import Event from "./Event"; import { VisitorInner as Visitor } from "./Visitor"; import { GameBlock, ItemWrapper } from "./common"; +import { useTranslation } from "next-i18next"; export const Meadow: React.FC<{ meadowCards: CardName[] }> = ({ meadowCards, }) => { + const { t } = useTranslation("common"); + return ( - +
{meadowCards.slice(0, 4).map((cardName, idx) => ( @@ -261,14 +264,15 @@ export const PlayerCity: React.FC<{ const playedAdornments = player.getPlayedAdornments(); const playedCardIdx: { [cardName: string]: number } = {}; const claimedVisitors = player.claimedVisitors; + const { t } = useTranslation("common"); const labelToCount: [string, number][] = [ - ["Critters", player.getNumPlayedCritters()], - ["Constructions", player.getNumPlayedConstructions()], - ["Common Critters", player.getNumPlayedCommonCritters()], - ["Common Constructions", player.getNumPlayedCommonConstructions()], - ["Unique Critters", player.getNumPlayedUniqueCritters()], - ["Unique Constructions", player.getNumPlayedUniqueConstructions()], + [t("Critters"), player.getNumPlayedCritters()], + [t("Constructions"), player.getNumPlayedConstructions()], + [t("Common Critters"), player.getNumPlayedCommonCritters()], + [t("Common Constructions"), player.getNumPlayedCommonConstructions()], + [t("Unique Critters"), player.getNumPlayedUniqueCritters()], + [t("Unique Constructions"), player.getNumPlayedUniqueConstructions()], ]; return playedCards.length !== 0 || @@ -325,7 +329,7 @@ export const PlayerCity: React.FC<{
) : (
- City is empty. + {t("City is empty.")}
); }; @@ -335,6 +339,8 @@ export const GameBoard: React.FC<{ gameStateJSON: GameStateJSON; viewingPlayer: Player | null; }> = ({ gameState, gameStateJSON, viewingPlayer }) => { + const { t } = useTranslation("common"); + return (
@@ -343,7 +349,7 @@ export const GameBoard: React.FC<{ {" "}
@@ -397,7 +403,7 @@ export const GameBoard: React.FC<{ diff --git a/src/components/gameInputCommon.tsx b/src/components/gameInputCommon.tsx index f2ae930c..c0d5775a 100644 --- a/src/components/gameInputCommon.tsx +++ b/src/components/gameInputCommon.tsx @@ -16,6 +16,7 @@ import { inputContextPrefix, toGameText } from "../model/gameText"; import { GameBlock, Description } from "./common"; import { assertUnreachable } from "../utils"; import { GameUpdaterContext } from "./GameUpdater"; +import { useTranslation } from "next-i18next"; type TValues = { gameInput: GameInput | null; @@ -248,41 +249,42 @@ export const renderGameInputLabel = ( gameInput: GameInput, gameOptions: Pick = { pearlbrook: false } ): React.ReactElement => { + const { t } = useTranslation("common"); switch (gameInput.inputType) { case GameInputType.PLAY_CARD: - return {"Play Card"}; + return {t("Play Card")}; case GameInputType.PLACE_WORKER: - return {"Visit Location"}; + return {t("Visit Location")}; case GameInputType.VISIT_DESTINATION_CARD: - return {"Visit Destination Card"}; + return {t("Visit Destination Card")}; case GameInputType.CLAIM_EVENT: return ( - {gameOptions?.pearlbrook ? "Claim Event / Wonders" : "Claim Event"} + {gameOptions?.pearlbrook ? t("Claim Event / Wonders") : t("Claim Event")} ); case GameInputType.PREPARE_FOR_SEASON: - return {"Prepare for Season"}; + return {t("Prepare for Season")}; case GameInputType.GAME_END: - return {"End Game"}; + return {t("End Game")}; case GameInputType.PLAY_ADORNMENT: - return {"Play Adornment"}; + return {t("Play Adornment")}; case GameInputType.PLACE_AMBASSADOR: - return {"Place Ambassador"}; + return {t("Place Ambassador")}; case GameInputType.RESERVE_CARD: - return {"Reserve Meadow/Station Card"}; + return {t("Reserve Meadow/Station Card")}; case GameInputType.PLAY_TRAIN_TICKET: - return {"Play Train Ticket"}; + return {t("Play Train Ticket")}; case GameInputType.SELECT_VISITOR: return ( {gameInput.prevInputType === GameInputType.PLACE_WORKER - ? "Select Visitor to discard" - : "Select Visitor to keep"} + ? t("Select Visitor to discard") + : t("Select Visitor to keep")} ); case GameInputType.UNDO: - return {"Undo last action"}; + return {t("Undo last action")}; default: return renderMultiStepGameInputLabel(gameInput); } diff --git a/src/middleware.ts b/src/middleware.ts new file mode 100644 index 00000000..72f53ee3 --- /dev/null +++ b/src/middleware.ts @@ -0,0 +1,43 @@ +import { NextRequest, NextResponse } from "next/server"; +import { acceptLanguage } from "./accept-header"; + +// I consiously duplicate these from i18n config because middleware sees this config as undefined. Obviously, in order to perform custom locale processing middleware must be able to access this config. I hope it is due to me not being able to understand how to actually achieve that. +export const locales = ["en-US", "ru-RU", "pt-BR"]; +export const defaultLocale = "en-US"; + +const PUBLIC_FILE = /\.(.*)$/; + +export async function middleware(req: NextRequest) { + if ( + req.nextUrl.pathname.startsWith("/_next") || + req.nextUrl.pathname.includes("/api/") || + PUBLIC_FILE.test(req.nextUrl.pathname) + ) { + return; + } + + const localeHeader = req.headers.get("accept-language"); + if (localeHeader === null) return; + const locale = acceptLanguage(localeHeader); + const parts = locale.split("-"); + let localeWithUpperCaseEnding = + parts.length > 1 ? `${parts[0]}-${parts[1].toUpperCase()}` : locale; + + if ( + locales.every((loc) => { + return localeWithUpperCaseEnding !== loc; + }) + ) + localeWithUpperCaseEnding = defaultLocale; + + if (req.nextUrl.toString().includes(`/${localeWithUpperCaseEnding}`)) return; + + if (req.nextUrl.locale !== localeWithUpperCaseEnding) { + return NextResponse.redirect( + new URL( + `/${localeWithUpperCaseEnding}${req.nextUrl.pathname}${req.nextUrl.search}`, + req.url + ) + ); + } +} diff --git a/src/model/card.ts b/src/model/card.ts index 3b9ec8ef..89c01bec 100644 --- a/src/model/card.ts +++ b/src/model/card.ts @@ -4083,7 +4083,7 @@ const CARD_REGISTRY: Record = { cardType: CardType.PROSPERITY, cardDescription: toGameText([ { type: "points", value: 2 }, - "for each of your unused BERRY ", + " for each of your unused BERRY ", { type: "BR" }, "to a maximum of 3.", ]), @@ -5485,7 +5485,7 @@ const CARD_REGISTRY: Record = { associatedCard: { type: "GOLDEN_LEAF", cardType: "COMMON" }, cardType: CardType.TRAVELER, cardDescription: toGameText([ - { type: "i", text: "Does not take up space in your city." }, + { type: "i", text: "Does not take up a space in your city." }, { type: "BR" }, "Opens an additional space in your city. ", { type: "i", text: "May not be copied, removed, or reactivated." }, diff --git a/src/model/event.ts b/src/model/event.ts index 728aadbc..f4ca10cf 100644 --- a/src/model/event.ts +++ b/src/model/event.ts @@ -2052,7 +2052,7 @@ const EVENT_REGISTRY: Record = { requiredCards: [CardName.FERRY_FERRET, CardName.TWIG_BARGE], type: EventType.SPECIAL, eventDescription: toGameText([ - "When achived, place either your ", + "When achieved, place either your ", { type: "entity", entityType: "card", card: CardName.FERRY_FERRET }, " or ", { type: "entity", entityType: "card", card: CardName.TWIG_BARGE }, diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index ee51efa0..d18222b1 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,8 +1,10 @@ -import "../styles/globals.css"; +import { appWithTranslation } from "next-i18next"; +import { ThemeProvider } from "next-themes"; import Head from "next/head"; import { useEffect } from "react"; +import "../styles/globals.css"; -export default function App({ +function App({ Component, pageProps, }: { @@ -25,6 +27,7 @@ export default function App({ } } }, []); + return ( <> @@ -38,7 +41,11 @@ export default function App({ Everdell - + + + ); } + +export default appWithTranslation(App); diff --git a/src/pages/game/[gameId].tsx b/src/pages/game/[gameId].tsx index 38bca4c2..20018432 100644 --- a/src/pages/game/[gameId].tsx +++ b/src/pages/game/[gameId].tsx @@ -1,10 +1,12 @@ import { GetServerSideProps } from "next"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; +import { i18n } from "../../../next-i18next.config"; +import Game from "../../components/Game"; +import GameAdmin from "../../components/GameAdmin"; import { getGameById } from "../../model/game"; import { GameJSON, PlayerJSON } from "../../model/jsonTypes"; import { GameInput } from "../../model/types"; -import GameAdmin from "../../components/GameAdmin"; -import Game from "../../components/Game"; export const getServerSideProps: GetServerSideProps = async (context) => { const { @@ -13,6 +15,8 @@ export const getServerSideProps: GetServerSideProps = async (context) => { playerSecret = null, debug = null, } = context.query; + const locale = context.locale; + const game = await getGameById(gameId as string); if (!game) { return { redirect: { statusCode: 302, destination: "/" } }; @@ -26,6 +30,9 @@ export const getServerSideProps: GetServerSideProps = async (context) => { return { props: { + ...(await serverSideTranslations(locale || i18n.defaultLocale, [ + "common", + ])), isGameAdmin, devDebugMode: process.env.NODE_ENV === "development" && !!debug, gameJSON: diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 5af8429c..9a8999b4 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,12 +1,18 @@ -import * as React from "react"; -import { useState } from "react"; +import { useTranslation } from "next-i18next"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import Head from "next/head"; import Link from "next/link"; +import * as React from "react"; +import { useState } from "react"; +import i18nextConfig from "../..//next-i18next.config"; +import GameBuilder from "../components/GameBuilder"; import styles from "../styles/Home.module.css"; -import GameBuilder from "../components/GameBuilder"; +export const availableLocales = i18nextConfig.i18n.locales; const Main: React.FC = () => { + const { t } = useTranslation("common"); + const [showGameBuilder, setShowGameBuilder] = useState(false); return (
@@ -22,38 +28,38 @@ const Main: React.FC = () => { className={styles.button} onClick={() => setShowGameBuilder(true)} > - New Game + {t("New Game")} - About the Game + {t("About the Game")} - Game Cards + {t("Game Cards")} - Github Source + {t("Github Source")} - What's New + {t("What's New")} - Share Feedback + {t("Share Feedback")}
)} @@ -61,6 +67,15 @@ const Main: React.FC = () => { ); }; +export async function getStaticProps({ locale }: { locale: string }) { + return { + props: { + ...(await serverSideTranslations(locale, ["common"])), + // Will be passed to the page component as props + }, + }; +} + export default function IndexPage() { return (
diff --git a/src/pages/test/ui.tsx b/src/pages/test/ui.tsx index aa755fbd..32f1affa 100644 --- a/src/pages/test/ui.tsx +++ b/src/pages/test/ui.tsx @@ -26,6 +26,7 @@ import { RiverDestinationName, RiverDestinationSpotName, } from "../../model/types"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; const ItemsList: React.FC<{ title: string; visible: boolean }> = ({ title, @@ -43,7 +44,9 @@ const ItemsList: React.FC<{ title: string; visible: boolean }> = ({ export const getServerSideProps: GetServerSideProps = async (context) => { return { - props: {}, + props: { + ...(await serverSideTranslations(context.locale || "en", ["common"])), + }, }; }; diff --git a/src/styles/Game.module.css b/src/styles/Game.module.css index 2632a6a3..4a0e3420 100644 --- a/src/styles/Game.module.css +++ b/src/styles/Game.module.css @@ -5,4 +5,7 @@ flex-direction: column; color: var(--text-color); width: 1420px; + position: relative; + min-height: 100vh; + padding-bottom: 70px; /* Adjust this value to match your sticky bar height */ } diff --git a/src/styles/Players.module.css b/src/styles/Players.module.css index e0a731cc..b10a5dd2 100644 --- a/src/styles/Players.module.css +++ b/src/styles/Players.module.css @@ -9,6 +9,7 @@ .status_box_item { padding: 5px; + position: relative; } .status_box_city { @@ -19,7 +20,7 @@ padding: 3px; padding-right: 0; display: flex; - height: 100%; + height: auto; width: 150px; flex-direction: column; justify-content: space-between; @@ -33,6 +34,10 @@ .status_box_bio_meta { display: flex; + font-weight: var(--font-bold); + margin-top: auto; + bottom: 5px; + position: absolute; } .status_box_bio_pill { @@ -83,10 +88,6 @@ text-transform: uppercase; } -.status_box_bio_meta { - font-weight: var(--font-bold); -} - .status_box_item_resource_list { height: 100%; display: flex; diff --git a/src/styles/StickyBar.module.css b/src/styles/StickyBar.module.css new file mode 100644 index 00000000..bca2d1fa --- /dev/null +++ b/src/styles/StickyBar.module.css @@ -0,0 +1,64 @@ +.stickyBarContainer { + position: relative; /* Allow absolute positioning of the button */ +} +.stickyBar { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + padding: 10px; + background-color: var(--bg-color); + display: flex; + width: auto; + align-items: center; + z-index: 11; +} + +.stickyBarContent { + flex-grow: 1; + overflow: hidden; /* Hide content when stickyBar is collapsed */ + transition: height 0.3s ease; /* Add transition for smooth collapse */ +} + +.stickyBar.hidden .stickyBarContent { + height: 0; /* Collapse the content when stickyBar is hidden */ +} + +.sticky_bar_item { + display: flex; + width: fit-content; +} + +.collapseButton { + position: fixed; /* Position the button absolutely */ + bottom: 20px; /* Adjust vertical position as needed */ + left: 20px; /* Adjust horizontal position as needed */ + margin-inline-end: 24px; + border-radius: 50%; + padding: 16px; + transition: opacity 0.3s ease; + cursor: pointer; + z-index: 12; /* Ensure button stays on top */ +} + +.collapseButton:hover { + background-color: var(--highlight-color); +} + +.collapseButton > svg { + display: block; + margin: 0 auto; +} + +.collapseButton.clicked { + transform: rotate(180deg); +} + +.stickyBarContainer .collapseButton { + opacity: 0; /* Make the button fully transparent when hidden */ +} + +.stickyBarContainer .collapseButton:hover { + opacity: 1; /* Make the button fully opaque on hover */ + transition: opacity 0s ease; /* Remove transition delay on hover */ +} diff --git a/tsconfig.json b/tsconfig.json index ff070a65..599fa20b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -17,6 +17,6 @@ "types": ["cypress", "node"], "incremental": false }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next-i18next.config.ts", "next.config.ts"], "exclude": ["node_modules", "./cypress.config.ts"] }
{"Player"}{"Card Points"}{"Event Points"}{t("Player")}{t("Card Points")}{t("Event Points")}{"Adornment Points"}{"Wonder Points"}{t("Adornment Points")}{t("Wonder Points")}{"Journey Points"}{t("Journey Points")} {"Total"}{t("Total")}