diff --git a/.oxfmtrc.json b/.oxfmtrc.json
index ce081e8..4fde987 100644
--- a/.oxfmtrc.json
+++ b/.oxfmtrc.json
@@ -1,4 +1,4 @@
{
"$schema": "./node_modules/oxfmt/configuration_schema.json",
- "ignorePatterns": ["**/routeTree.gen.ts"]
+ "ignorePatterns": ["**/*.gen.ts", "src/i18n/resources.d.ts", "public"]
}
diff --git a/.oxlintrc.json b/.oxlintrc.json
index 10c812a..b113495 100644
--- a/.oxlintrc.json
+++ b/.oxlintrc.json
@@ -1,6 +1,6 @@
{
"$schema": "./node_modules/oxlint/configuration_schema.json",
- "ignorePatterns": ["**/routeTree.gen.ts"],
+ "ignorePatterns": ["**/*.gen.ts", "src/i18n/resources.d.ts"],
"plugins": ["react", "react-perf", "import", "jsx-a11y", "promise"],
"rules": {
"eqeqeq": ["error", "smart"],
diff --git a/index.html b/index.html
index c1ed1b0..b929bcf 100644
--- a/index.html
+++ b/index.html
@@ -2,9 +2,9 @@
-
+
- honey-fe
+ Honey
diff --git a/package.json b/package.json
index 1fda5f3..14c701d 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"@tanstack/react-router": "^1.166.3",
"@tanstack/react-router-devtools": "^1.166.3",
"@tma.js/sdk-react": "^3.0.16",
+ "@xstate/react": "^6.1.0",
"arktype": "^2.2.0",
"axios": "^1.13.6",
"clsx": "^2.1.1",
@@ -32,10 +33,10 @@
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-i18next": "^16.5.6",
- "tailwindcss": "^4.2.1"
+ "tailwindcss": "^4.2.1",
+ "xstate": "^5.28.0"
},
"devDependencies": {
- "@i18next-selector/vite-plugin": "^0.0.18",
"@tanstack/router-plugin": "^1.166.3",
"@types/node": "^24.10.1",
"@types/react": "^19.2.7",
diff --git a/plugins/i18nextSortPlugin.ts b/plugins/i18nextSortPlugin.ts
new file mode 100644
index 0000000..a9b5e12
--- /dev/null
+++ b/plugins/i18nextSortPlugin.ts
@@ -0,0 +1,68 @@
+import { readFileSync, readdirSync, writeFileSync } from "node:fs";
+import { join, resolve } from "node:path";
+import { normalizePath } from "vite";
+import type { Plugin, ResolvedConfig } from "vite";
+
+interface Options {
+ sourceDir: string;
+}
+
+type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
+type JsonObject = { [key: string]: JsonValue };
+
+function deepSortKeys(obj: JsonObject): JsonObject {
+ const sorted: JsonObject = {};
+ for (const key of Object.keys(obj).sort()) {
+ const value = obj[key];
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
+ sorted[key] = deepSortKeys(value as JsonObject);
+ } else {
+ sorted[key] = value;
+ }
+ }
+ return sorted;
+}
+
+function sortFile(filePath: string): void {
+ const raw = readFileSync(filePath, "utf-8");
+ const parsed = JSON.parse(raw) as JsonObject;
+ const sorted = deepSortKeys(parsed);
+ const output = JSON.stringify(sorted, null, "\t") + "\n";
+
+ if (raw !== output) {
+ writeFileSync(filePath, output, "utf-8");
+ }
+}
+
+function sortAllFiles(sourceDir: string): void {
+ const files = readdirSync(sourceDir).filter((f) => f.endsWith(".json"));
+ for (const file of files) {
+ sortFile(join(sourceDir, file));
+ }
+}
+
+export function i18nextSortPlugin(options: Options): Plugin {
+ let resolvedSourceDir: string;
+
+ return {
+ name: "i18next-sort",
+
+ configResolved(config: ResolvedConfig) {
+ resolvedSourceDir = normalizePath(resolve(config.root, options.sourceDir));
+ },
+
+ buildStart() {
+ sortAllFiles(resolvedSourceDir);
+ },
+
+ configureServer(server) {
+ server.watcher.add(resolvedSourceDir);
+
+ server.watcher.on("change", (file: string) => {
+ if (file.startsWith(resolvedSourceDir) && file.endsWith(".json")) {
+ sortFile(file);
+ }
+ });
+ },
+ };
+}
diff --git a/plugins/i18nextTypesPlugin.ts b/plugins/i18nextTypesPlugin.ts
new file mode 100644
index 0000000..6d646bd
--- /dev/null
+++ b/plugins/i18nextTypesPlugin.ts
@@ -0,0 +1,97 @@
+import { readFileSync, readdirSync, writeFileSync } from "node:fs";
+import { join, resolve } from "node:path";
+import { normalizePath } from "vite";
+import type { Plugin, ResolvedConfig } from "vite";
+
+interface Options {
+ sourceDir: string;
+ destination: string;
+}
+
+type JsonValue = string | number | boolean | null | JsonObject | JsonValue[];
+type JsonObject = { [key: string]: JsonValue };
+
+function flattenKeys(obj: JsonObject, prefix: string, result: Map>): void {
+ for (const [key, value] of Object.entries(obj)) {
+ const fullKey = prefix ? `${prefix}.${key}` : key;
+ if (value !== null && typeof value === "object" && !Array.isArray(value)) {
+ flattenKeys(value as JsonObject, fullKey, result);
+ } else if (typeof value === "string") {
+ if (!result.has(fullKey)) {
+ result.set(fullKey, new Set());
+ }
+ result.get(fullKey)!.add(value);
+ }
+ }
+}
+
+function generateTypes(sourceDir: string, destination: string): void {
+ const files = readdirSync(sourceDir).filter((f) => f.endsWith(".json"));
+
+ const keyValues = new Map>();
+
+ for (const file of files) {
+ const content = JSON.parse(readFileSync(join(sourceDir, file), "utf-8")) as JsonObject;
+
+ flattenKeys(content, "", keyValues);
+ }
+
+ const sortedKeys = Array.from(keyValues.keys()).sort();
+
+ const lines: string[] = [
+ "// Auto-generated by i18nextTypesPlugin — do not edit manually",
+ "declare const resources: {",
+ ];
+
+ for (const key of sortedKeys) {
+ const values = Array.from(keyValues.get(key)!);
+ const union = values.map((v) => JSON.stringify(v)).join(" | ");
+ lines.push(` ${JSON.stringify(key)}: ${union};`);
+ }
+
+ lines.push("};");
+ lines.push("export default resources;");
+ lines.push("");
+
+ writeFileSync(destination, lines.join("\n"), "utf-8");
+}
+
+export function i18nextTypesPlugin(options: Options): Plugin {
+ let resolvedSourceDir: string;
+ let resolvedDestination: string;
+
+ return {
+ name: "i18next-types",
+
+ configResolved(config: ResolvedConfig) {
+ resolvedSourceDir = normalizePath(resolve(config.root, options.sourceDir));
+ resolvedDestination = normalizePath(resolve(config.root, options.destination));
+ },
+
+ buildStart() {
+ generateTypes(resolvedSourceDir, resolvedDestination);
+ },
+
+ configureServer(server) {
+ server.watcher.add(resolvedSourceDir);
+
+ server.watcher.on("change", (file: string) => {
+ if (file.startsWith(resolvedSourceDir) && file.endsWith(".json")) {
+ generateTypes(resolvedSourceDir, resolvedDestination);
+ }
+ });
+
+ server.watcher.on("add", (file: string) => {
+ if (file.startsWith(resolvedSourceDir) && file.endsWith(".json")) {
+ generateTypes(resolvedSourceDir, resolvedDestination);
+ }
+ });
+
+ server.watcher.on("unlink", (file: string) => {
+ if (file.startsWith(resolvedSourceDir) && file.endsWith(".json")) {
+ generateTypes(resolvedSourceDir, resolvedDestination);
+ }
+ });
+ },
+ };
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3f3dc35..fbc749c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -32,6 +32,9 @@ importers:
'@tma.js/sdk-react':
specifier: ^3.0.16
version: 3.0.16(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)
+ '@xstate/react':
+ specifier: ^6.1.0
+ version: 6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.28.0)
arktype:
specifier: ^2.2.0
version: 2.2.0
@@ -65,10 +68,10 @@ importers:
tailwindcss:
specifier: ^4.2.1
version: 4.2.1
+ xstate:
+ specifier: ^5.28.0
+ version: 5.28.0
devDependencies:
- '@i18next-selector/vite-plugin':
- specifier: ^0.0.18
- version: 0.0.18(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))
'@tanstack/router-plugin':
specifier: ^1.166.3
version: 1.166.3(@tanstack/react-router@1.166.3(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))
@@ -363,11 +366,6 @@ packages:
cpu: [x64]
os: [win32]
- '@i18next-selector/vite-plugin@0.0.18':
- resolution: {integrity: sha512-jCdVJdaYDqa3dE8LCscCa7OzCN7UvUP+FDgSuc3L3mkPRlmhiXwNpCSWNUpSgGfubGBcpIE9vexHaLcS2KU0Bg==}
- peerDependencies:
- vite: 6 - 7
-
'@jridgewell/gen-mapping@0.3.13':
resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
@@ -1222,18 +1220,6 @@ packages:
'@tma.js/types@1.0.2':
resolution: {integrity: sha512-qs4mi+U1xZmMQBdMhWAo1X4YqUJ/ae0y28s+GNCpQq58bsJo0h8rvyVOB1RwPvXogIY9+yribbZe6z3AIJmsAQ==}
- '@traversable/json@0.0.26':
- resolution: {integrity: sha512-oXKX0eNxbbHGLjLV27nTuV0uyR6uSoWi0BF+FYJu4jXRcSsWqCHOqNjIb2x/0usKd70rnKLGyHxurlTBTpQVOw==}
- peerDependencies:
- '@traversable/registry': ^0.0.25
- fast-check: ^3
- peerDependenciesMeta:
- fast-check:
- optional: true
-
- '@traversable/registry@0.0.25':
- resolution: {integrity: sha512-idu2DhzoHOeqO+FZSpnDTgrFQWZL+poyxO9KozHeW7KdVqecrtYwR10vCVB/dItVdMBVZbavbNWO6PgUYN1KLg==}
-
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
@@ -1254,6 +1240,15 @@ packages:
peerDependencies:
vite: ^4 || ^5 || ^6 || ^7
+ '@xstate/react@6.1.0':
+ resolution: {integrity: sha512-ep9F0jGTI63B/jE8GHdMpUqtuz7yRebNaKv8EMUaiSi29NOglywc2X2YSOV/ygbIK+LtmgZ0q9anoEA2iBSEOw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ xstate: ^5.28.0
+ peerDependenciesMeta:
+ xstate:
+ optional: true
+
acorn@8.16.0:
resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==}
engines: {node: '>=0.4.0'}
@@ -1279,9 +1274,6 @@ packages:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
engines: {node: '>= 8'}
- argparse@2.0.1:
- resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
-
arkregex@0.0.5:
resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==}
@@ -1619,10 +1611,6 @@ packages:
js-tokens@4.0.0:
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
- js-yaml@4.1.1:
- resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==}
- hasBin: true
-
jsesc@3.1.0:
resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
engines: {node: '>=6'}
@@ -2155,6 +2143,15 @@ packages:
peerDependencies:
browserslist: '>= 4.21.0'
+ use-isomorphic-layout-effect@1.2.1:
+ resolution: {integrity: sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+
use-sync-external-store@1.6.0:
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
peerDependencies:
@@ -2240,6 +2237,9 @@ packages:
utf-8-validate:
optional: true
+ xstate@5.28.0:
+ resolution: {integrity: sha512-Iaqq6ZrUzqeUtA3hC5LQKZfR8ZLzEFTImMHJM3jWEdVvXWdKvvVLXZEiNQWm3SCA9ZbEou/n5rcsna1wb9t28A==}
+
yallist@3.1.1:
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
@@ -2454,15 +2454,6 @@ snapshots:
'@esbuild/win32-x64@0.27.3':
optional: true
- '@i18next-selector/vite-plugin@0.0.18(vite@7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2))':
- dependencies:
- '@traversable/json': 0.0.26(@traversable/registry@0.0.25)
- '@traversable/registry': 0.0.25
- js-yaml: 4.1.1
- vite: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(sass-embedded@1.97.3)(sass@1.97.3)(tsx@4.21.0)(yaml@2.8.2)
- transitivePeerDependencies:
- - fast-check
-
'@jridgewell/gen-mapping@0.3.13':
dependencies:
'@jridgewell/sourcemap-codec': 1.5.5
@@ -3129,12 +3120,6 @@ snapshots:
'@tma.js/types@1.0.2': {}
- '@traversable/json@0.0.26(@traversable/registry@0.0.25)':
- dependencies:
- '@traversable/registry': 0.0.25
-
- '@traversable/registry@0.0.25': {}
-
'@types/estree@1.0.8': {}
'@types/node@24.12.0':
@@ -3157,6 +3142,16 @@ snapshots:
transitivePeerDependencies:
- '@swc/helpers'
+ '@xstate/react@6.1.0(@types/react@19.2.14)(react@19.2.4)(xstate@5.28.0)':
+ dependencies:
+ react: 19.2.4
+ use-isomorphic-layout-effect: 1.2.1(@types/react@19.2.14)(react@19.2.4)
+ use-sync-external-store: 1.6.0(react@19.2.4)
+ optionalDependencies:
+ xstate: 5.28.0
+ transitivePeerDependencies:
+ - '@types/react'
+
acorn@8.16.0: {}
ansi-escapes@7.3.0:
@@ -3174,8 +3169,6 @@ snapshots:
normalize-path: 3.0.0
picomatch: 2.3.1
- argparse@2.0.1: {}
-
arkregex@0.0.5:
dependencies:
'@ark/util': 0.56.0
@@ -3502,10 +3495,6 @@ snapshots:
js-tokens@4.0.0: {}
- js-yaml@4.1.1:
- dependencies:
- argparse: 2.0.1
-
jsesc@3.1.0: {}
json5@2.2.3: {}
@@ -4002,6 +3991,12 @@ snapshots:
escalade: 3.2.0
picocolors: 1.1.1
+ use-isomorphic-layout-effect@1.2.1(@types/react@19.2.14)(react@19.2.4):
+ dependencies:
+ react: 19.2.4
+ optionalDependencies:
+ '@types/react': 19.2.14
+
use-sync-external-store@1.6.0(react@19.2.4):
dependencies:
react: 19.2.4
@@ -4050,6 +4045,8 @@ snapshots:
ws@8.19.0: {}
+ xstate@5.28.0: {}
+
yallist@3.1.1: {}
yaml@2.8.2: {}
diff --git a/public/honey.svg b/public/honey.svg
new file mode 100644
index 0000000..abc6a6c
--- /dev/null
+++ b/public/honey.svg
@@ -0,0 +1,551 @@
+
diff --git a/public/locales/de.json b/public/locales/de.json
new file mode 100644
index 0000000..959e03b
--- /dev/null
+++ b/public/locales/de.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Einzahlungsguthaben",
+ "registrationDate": "Registrierungsdatum",
+ "withdrawalBalance": "Auszahlungsguthaben",
+ "yourId": "Ihre ID"
+ },
+ "actionModal": {
+ "close": "Schließen"
+ },
+ "common": {
+ "off": "aus",
+ "on": "an"
+ },
+ "nav": {
+ "apiary": "Bienenhaus",
+ "cashdesk": "Kasse",
+ "earnings": "Einnahmen",
+ "game": "Spiel",
+ "menu": "Menü",
+ "roulette": "Roulette",
+ "shop": "Shop",
+ "tasks": "Aufgaben"
+ },
+ "operationType": {
+ "deposit": "Einzahlung",
+ "greeting": "Willkommensbonus",
+ "referral": "Empfehlungsbonus",
+ "withdrawal": "Auszahlung"
+ },
+ "pagination": {
+ "of": "von"
+ },
+ "support": {
+ "action": "Support kontaktieren",
+ "text": "Wenn Sie Fragen zum Spiel haben, wenden Sie sich bitte an unseren Support."
+ },
+ "settings": {
+ "accountInfo": "Kontoinformationen",
+ "back": "Zurück",
+ "faq": "FAQ",
+ "language": "Sprache",
+ "sound": "Ton",
+ "support": "Support",
+ "transactionHistory": "Transaktionsverlauf"
+ },
+ "transactionHistory": {
+ "date": "Datum",
+ "operationType": "Vorgangsart",
+ "sum": "Summe",
+ "title": "Transaktionsverlauf",
+ "yourTransactions": "Ihre Transaktionen"
+ }
+}
diff --git a/public/locales/en.d.ts b/public/locales/en.d.ts
deleted file mode 100644
index f736aed..0000000
--- a/public/locales/en.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export declare const resources: {
- hello: "Hello World!";
- "actionModal.close": "Close";
- "nav.shop": "Shop";
- "nav.apiary": "Apiary";
- "nav.game": "Game";
- "nav.cashdesk": "Cashdesk";
- "nav.menu": "Menu";
- "nav.roulette": "Roulette";
- "nav.tasks": "Tasks";
- "nav.earnings": "Earnings";
-};
diff --git a/public/locales/en.json b/public/locales/en.json
index e16142f..1cc61cd 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -1,12 +1,54 @@
{
- "hello": "Hello World!",
- "actionModal.close": "Close",
- "nav.shop": "Shop",
- "nav.apiary": "Apiary",
- "nav.game": "Game",
- "nav.cashdesk": "Cashdesk",
- "nav.menu": "Menu",
- "nav.roulette": "Roulette",
- "nav.tasks": "Tasks",
- "nav.earnings": "Earnings"
+ "accountInfo": {
+ "paymentBalance": "Payment balance",
+ "registrationDate": "Registration date",
+ "withdrawalBalance": "Withdrawal balance",
+ "yourId": "Your ID"
+ },
+ "actionModal": {
+ "close": "Close"
+ },
+ "common": {
+ "off": "off",
+ "on": "on"
+ },
+ "nav": {
+ "apiary": "Apiary",
+ "cashdesk": "Cashdesk",
+ "earnings": "Earnings",
+ "game": "Game",
+ "menu": "Menu",
+ "roulette": "Roulette",
+ "shop": "Shop",
+ "tasks": "Tasks"
+ },
+ "operationType": {
+ "deposit": "Deposit",
+ "greeting": "Greeting bonus",
+ "referral": "Referral bonus",
+ "withdrawal": "Withdrawal"
+ },
+ "pagination": {
+ "of": "of"
+ },
+ "support": {
+ "action": "Contact support",
+ "text": "If you have any questions related to the game, please contact our support team."
+ },
+ "settings": {
+ "accountInfo": "Account information",
+ "back": "Back",
+ "faq": "FAQ",
+ "language": "Language",
+ "sound": "Sound",
+ "support": "Support",
+ "transactionHistory": "Transaction History"
+ },
+ "transactionHistory": {
+ "date": "Date",
+ "operationType": "Operation type",
+ "sum": "Sum",
+ "title": "Transaction History",
+ "yourTransactions": "Your Transactions"
+ }
}
diff --git a/public/locales/es.json b/public/locales/es.json
new file mode 100644
index 0000000..f091a87
--- /dev/null
+++ b/public/locales/es.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Saldo de pagos",
+ "registrationDate": "Fecha de registro",
+ "withdrawalBalance": "Saldo de retiros",
+ "yourId": "Tu ID"
+ },
+ "actionModal": {
+ "close": "Cerrar"
+ },
+ "common": {
+ "off": "no",
+ "on": "sí"
+ },
+ "nav": {
+ "apiary": "Apiario",
+ "cashdesk": "Caja",
+ "earnings": "Ganancias",
+ "game": "Juego",
+ "menu": "Menú",
+ "roulette": "Ruleta",
+ "shop": "Tienda",
+ "tasks": "Tareas"
+ },
+ "operationType": {
+ "deposit": "Depósito",
+ "greeting": "Bono de bienvenida",
+ "referral": "Bono de referido",
+ "withdrawal": "Retiro"
+ },
+ "pagination": {
+ "of": "de"
+ },
+ "support": {
+ "action": "Contactar soporte",
+ "text": "Si tienes alguna pregunta relacionada con el juego, contacta con nuestro equipo de soporte."
+ },
+ "settings": {
+ "accountInfo": "Información de la cuenta",
+ "back": "Volver",
+ "faq": "FAQ",
+ "language": "Idioma",
+ "sound": "Sonido",
+ "support": "Soporte",
+ "transactionHistory": "Historial de transacciones"
+ },
+ "transactionHistory": {
+ "date": "Fecha",
+ "operationType": "Tipo de operación",
+ "sum": "Suma",
+ "title": "Historial de transacciones",
+ "yourTransactions": "Tus transacciones"
+ }
+}
diff --git a/public/locales/fr.json b/public/locales/fr.json
new file mode 100644
index 0000000..4f758ca
--- /dev/null
+++ b/public/locales/fr.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Solde des paiements",
+ "registrationDate": "Date d'inscription",
+ "withdrawalBalance": "Solde des retraits",
+ "yourId": "Votre ID"
+ },
+ "actionModal": {
+ "close": "Fermer"
+ },
+ "common": {
+ "off": "non",
+ "on": "oui"
+ },
+ "nav": {
+ "apiary": "Rucher",
+ "cashdesk": "Caisse",
+ "earnings": "Gains",
+ "game": "Jeu",
+ "menu": "Menu",
+ "roulette": "Roulette",
+ "shop": "Boutique",
+ "tasks": "Tâches"
+ },
+ "operationType": {
+ "deposit": "Dépôt",
+ "greeting": "Bonus de bienvenue",
+ "referral": "Bonus de parrainage",
+ "withdrawal": "Retrait"
+ },
+ "pagination": {
+ "of": "sur"
+ },
+ "support": {
+ "action": "Contacter le support",
+ "text": "Si vous avez des questions liées au jeu, veuillez contacter notre équipe de support."
+ },
+ "settings": {
+ "accountInfo": "Informations du compte",
+ "back": "Retour",
+ "faq": "FAQ",
+ "language": "Langue",
+ "sound": "Son",
+ "support": "Support",
+ "transactionHistory": "Historique des transactions"
+ },
+ "transactionHistory": {
+ "date": "Date",
+ "operationType": "Type d'opération",
+ "sum": "Somme",
+ "title": "Historique des transactions",
+ "yourTransactions": "Vos transactions"
+ }
+}
diff --git a/public/locales/id.json b/public/locales/id.json
new file mode 100644
index 0000000..456df1e
--- /dev/null
+++ b/public/locales/id.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Saldo pembayaran",
+ "registrationDate": "Tanggal pendaftaran",
+ "withdrawalBalance": "Saldo penarikan",
+ "yourId": "ID Anda"
+ },
+ "actionModal": {
+ "close": "Tutup"
+ },
+ "common": {
+ "off": "mati",
+ "on": "nyala"
+ },
+ "nav": {
+ "apiary": "Peternakan Lebah",
+ "cashdesk": "Kasir",
+ "earnings": "Penghasilan",
+ "game": "Permainan",
+ "menu": "Menu",
+ "roulette": "Roulette",
+ "shop": "Toko",
+ "tasks": "Tugas"
+ },
+ "operationType": {
+ "deposit": "Setoran",
+ "greeting": "Bonus sambutan",
+ "referral": "Bonus referral",
+ "withdrawal": "Penarikan"
+ },
+ "pagination": {
+ "of": "dari"
+ },
+ "support": {
+ "action": "Hubungi dukungan",
+ "text": "Jika Anda memiliki pertanyaan terkait permainan, silakan hubungi tim dukungan kami."
+ },
+ "settings": {
+ "accountInfo": "Informasi akun",
+ "back": "Kembali",
+ "faq": "FAQ",
+ "language": "Bahasa",
+ "sound": "Suara",
+ "support": "Dukungan",
+ "transactionHistory": "Riwayat transaksi"
+ },
+ "transactionHistory": {
+ "date": "Tanggal",
+ "operationType": "Jenis operasi",
+ "sum": "Jumlah",
+ "title": "Riwayat transaksi",
+ "yourTransactions": "Transaksi Anda"
+ }
+}
diff --git a/public/locales/it.json b/public/locales/it.json
new file mode 100644
index 0000000..780d35c
--- /dev/null
+++ b/public/locales/it.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Saldo pagamenti",
+ "registrationDate": "Data di registrazione",
+ "withdrawalBalance": "Saldo prelievi",
+ "yourId": "Il tuo ID"
+ },
+ "actionModal": {
+ "close": "Chiudi"
+ },
+ "common": {
+ "off": "no",
+ "on": "sì"
+ },
+ "nav": {
+ "apiary": "Apiario",
+ "cashdesk": "Cassa",
+ "earnings": "Guadagni",
+ "game": "Gioco",
+ "menu": "Menu",
+ "roulette": "Roulette",
+ "shop": "Negozio",
+ "tasks": "Compiti"
+ },
+ "operationType": {
+ "deposit": "Deposito",
+ "greeting": "Bonus di benvenuto",
+ "referral": "Bonus referral",
+ "withdrawal": "Prelievo"
+ },
+ "pagination": {
+ "of": "di"
+ },
+ "support": {
+ "action": "Contatta il supporto",
+ "text": "Se hai domande relative al gioco, contatta il nostro team di supporto."
+ },
+ "settings": {
+ "accountInfo": "Informazioni account",
+ "back": "Indietro",
+ "faq": "FAQ",
+ "language": "Lingua",
+ "sound": "Suono",
+ "support": "Supporto",
+ "transactionHistory": "Cronologia transazioni"
+ },
+ "transactionHistory": {
+ "date": "Data",
+ "operationType": "Tipo di operazione",
+ "sum": "Somma",
+ "title": "Cronologia transazioni",
+ "yourTransactions": "Le tue transazioni"
+ }
+}
diff --git a/public/locales/nl.json b/public/locales/nl.json
new file mode 100644
index 0000000..68b881c
--- /dev/null
+++ b/public/locales/nl.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Stortingssaldo",
+ "registrationDate": "Registratiedatum",
+ "withdrawalBalance": "Opnamesaldo",
+ "yourId": "Uw ID"
+ },
+ "actionModal": {
+ "close": "Sluiten"
+ },
+ "common": {
+ "off": "uit",
+ "on": "aan"
+ },
+ "nav": {
+ "apiary": "Bijenstal",
+ "cashdesk": "Kassa",
+ "earnings": "Inkomsten",
+ "game": "Spel",
+ "menu": "Menu",
+ "roulette": "Roulette",
+ "shop": "Winkel",
+ "tasks": "Taken"
+ },
+ "operationType": {
+ "deposit": "Storting",
+ "greeting": "Welkomstbonus",
+ "referral": "Verwijzingsbonus",
+ "withdrawal": "Opname"
+ },
+ "pagination": {
+ "of": "van"
+ },
+ "support": {
+ "action": "Contact opnemen",
+ "text": "Als u vragen heeft over het spel, neem dan contact op met ons ondersteuningsteam."
+ },
+ "settings": {
+ "accountInfo": "Accountinformatie",
+ "back": "Terug",
+ "faq": "FAQ",
+ "language": "Taal",
+ "sound": "Geluid",
+ "support": "Ondersteuning",
+ "transactionHistory": "Transactiegeschiedenis"
+ },
+ "transactionHistory": {
+ "date": "Datum",
+ "operationType": "Bewerkingstype",
+ "sum": "Som",
+ "title": "Transactiegeschiedenis",
+ "yourTransactions": "Uw transacties"
+ }
+}
diff --git a/public/locales/pl.json b/public/locales/pl.json
new file mode 100644
index 0000000..525e7d2
--- /dev/null
+++ b/public/locales/pl.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Saldo wpłat",
+ "registrationDate": "Data rejestracji",
+ "withdrawalBalance": "Saldo wypłat",
+ "yourId": "Twoje ID"
+ },
+ "actionModal": {
+ "close": "Zamknij"
+ },
+ "common": {
+ "off": "wył",
+ "on": "wł"
+ },
+ "nav": {
+ "apiary": "Pasieka",
+ "cashdesk": "Kasa",
+ "earnings": "Zarobki",
+ "game": "Gra",
+ "menu": "Menu",
+ "roulette": "Ruletka",
+ "shop": "Sklep",
+ "tasks": "Zadania"
+ },
+ "operationType": {
+ "deposit": "Wpłata",
+ "greeting": "Bonus powitalny",
+ "referral": "Bonus polecający",
+ "withdrawal": "Wypłata"
+ },
+ "pagination": {
+ "of": "z"
+ },
+ "support": {
+ "action": "Skontaktuj się z pomocą",
+ "text": "Jeśli masz pytania dotyczące gry, skontaktuj się z naszym zespołem wsparcia."
+ },
+ "settings": {
+ "accountInfo": "Informacje o koncie",
+ "back": "Wstecz",
+ "faq": "FAQ",
+ "language": "Język",
+ "sound": "Dźwięk",
+ "support": "Wsparcie",
+ "transactionHistory": "Historia transakcji"
+ },
+ "transactionHistory": {
+ "date": "Data",
+ "operationType": "Typ operacji",
+ "sum": "Suma",
+ "title": "Historia transakcji",
+ "yourTransactions": "Twoje transakcje"
+ }
+}
diff --git a/public/locales/pt.json b/public/locales/pt.json
new file mode 100644
index 0000000..f104a12
--- /dev/null
+++ b/public/locales/pt.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Saldo de pagamentos",
+ "registrationDate": "Data de registro",
+ "withdrawalBalance": "Saldo de saques",
+ "yourId": "Seu ID"
+ },
+ "actionModal": {
+ "close": "Fechar"
+ },
+ "common": {
+ "off": "não",
+ "on": "sim"
+ },
+ "nav": {
+ "apiary": "Apiário",
+ "cashdesk": "Caixa",
+ "earnings": "Ganhos",
+ "game": "Jogo",
+ "menu": "Menu",
+ "roulette": "Roleta",
+ "shop": "Loja",
+ "tasks": "Tarefas"
+ },
+ "operationType": {
+ "deposit": "Depósito",
+ "greeting": "Bônus de boas-vindas",
+ "referral": "Bônus de indicação",
+ "withdrawal": "Saque"
+ },
+ "pagination": {
+ "of": "de"
+ },
+ "support": {
+ "action": "Contactar suporte",
+ "text": "Se tiver dúvidas relacionadas ao jogo, entre em contato com nossa equipe de suporte."
+ },
+ "settings": {
+ "accountInfo": "Informações da conta",
+ "back": "Voltar",
+ "faq": "FAQ",
+ "language": "Idioma",
+ "sound": "Som",
+ "support": "Suporte",
+ "transactionHistory": "Histórico de transações"
+ },
+ "transactionHistory": {
+ "date": "Data",
+ "operationType": "Tipo de operação",
+ "sum": "Soma",
+ "title": "Histórico de transações",
+ "yourTransactions": "Suas transações"
+ }
+}
diff --git a/public/locales/ru.d.ts b/public/locales/ru.d.ts
deleted file mode 100644
index f24b1fa..0000000
--- a/public/locales/ru.d.ts
+++ /dev/null
@@ -1,12 +0,0 @@
-export declare const resources: {
- hello: "Привет мир";
- "actionModal.close": "Закрыть";
- "nav.shop": "Магазин";
- "nav.apiary": "Пасека";
- "nav.game": "Игра";
- "nav.cashdesk": "Касса";
- "nav.menu": "Меню";
- "nav.roulette": "Рулетка";
- "nav.tasks": "Задания";
- "nav.earnings": "Заработок";
-};
diff --git a/public/locales/ru.json b/public/locales/ru.json
index 11d0099..02eeeda 100644
--- a/public/locales/ru.json
+++ b/public/locales/ru.json
@@ -1,12 +1,54 @@
{
- "hello": "Привет мир",
- "actionModal.close": "Закрыть",
- "nav.shop": "Магазин",
- "nav.apiary": "Пасека",
- "nav.game": "Игра",
- "nav.cashdesk": "Касса",
- "nav.menu": "Меню",
- "nav.roulette": "Рулетка",
- "nav.tasks": "Задания",
- "nav.earnings": "Заработок"
+ "accountInfo": {
+ "paymentBalance": "Баланс пополнений",
+ "registrationDate": "Дата регистрации",
+ "withdrawalBalance": "Баланс выводов",
+ "yourId": "Ваш ID"
+ },
+ "actionModal": {
+ "close": "Закрыть"
+ },
+ "common": {
+ "off": "выкл",
+ "on": "вкл"
+ },
+ "nav": {
+ "apiary": "Пасека",
+ "cashdesk": "Касса",
+ "earnings": "Заработок",
+ "game": "Игра",
+ "menu": "Меню",
+ "roulette": "Рулетка",
+ "shop": "Магазин",
+ "tasks": "Задания"
+ },
+ "operationType": {
+ "deposit": "Пополнение",
+ "greeting": "Приветственный бонус",
+ "referral": "Реферальный бонус",
+ "withdrawal": "Вывод"
+ },
+ "pagination": {
+ "of": "из"
+ },
+ "support": {
+ "action": "Связаться с поддержкой",
+ "text": "Если у вас возникли вопросы, связанные с игрой — обратитесь в нашу службу поддержки."
+ },
+ "settings": {
+ "accountInfo": "Информация об аккаунте",
+ "back": "Назад",
+ "faq": "ЧаВо",
+ "language": "Язык",
+ "sound": "Звук",
+ "support": "Поддержка",
+ "transactionHistory": "История транзакций"
+ },
+ "transactionHistory": {
+ "date": "Дата",
+ "operationType": "Тип операции",
+ "sum": "Сумма",
+ "title": "История транзакций",
+ "yourTransactions": "Ваши транзакции"
+ }
}
diff --git a/public/locales/tr.json b/public/locales/tr.json
new file mode 100644
index 0000000..65ac6d0
--- /dev/null
+++ b/public/locales/tr.json
@@ -0,0 +1,54 @@
+{
+ "accountInfo": {
+ "paymentBalance": "Ödeme bakiyesi",
+ "registrationDate": "Kayıt tarihi",
+ "withdrawalBalance": "Çekim bakiyesi",
+ "yourId": "Kimliğiniz"
+ },
+ "actionModal": {
+ "close": "Kapat"
+ },
+ "common": {
+ "off": "kapalı",
+ "on": "açık"
+ },
+ "nav": {
+ "apiary": "Arılık",
+ "cashdesk": "Kasa",
+ "earnings": "Kazançlar",
+ "game": "Oyun",
+ "menu": "Menü",
+ "roulette": "Rulet",
+ "shop": "Mağaza",
+ "tasks": "Görevler"
+ },
+ "operationType": {
+ "deposit": "Yatırım",
+ "greeting": "Hoş geldin bonusu",
+ "referral": "Referans bonusu",
+ "withdrawal": "Çekim"
+ },
+ "pagination": {
+ "of": "/"
+ },
+ "support": {
+ "action": "Destek ile iletişime geç",
+ "text": "Oyunla ilgili sorularınız varsa lütfen destek ekibimizle iletişime geçin."
+ },
+ "settings": {
+ "accountInfo": "Hesap bilgileri",
+ "back": "Geri",
+ "faq": "SSS",
+ "language": "Dil",
+ "sound": "Ses",
+ "support": "Destek",
+ "transactionHistory": "İşlem Geçmişi"
+ },
+ "transactionHistory": {
+ "date": "Tarih",
+ "operationType": "İşlem türü",
+ "sum": "Toplam",
+ "title": "İşlem Geçmişi",
+ "yourTransactions": "İşlemleriniz"
+ }
+}
diff --git a/public/vite.svg b/public/vite.svg
deleted file mode 100644
index ee9fada..0000000
--- a/public/vite.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/src/audio/AudioContext.tsx b/src/audio/AudioContext.tsx
new file mode 100644
index 0000000..4ff667c
--- /dev/null
+++ b/src/audio/AudioContext.tsx
@@ -0,0 +1,110 @@
+import { type ReactNode, type RefObject, useMemo } from "react";
+import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
+import tg, { STORAGE_KEYS } from "@/tg";
+import type { SoundKey } from "./sounds";
+import { SOUNDS, preloadSounds } from "./sounds";
+
+type AudioCtxValue = {
+ isEnabled: boolean;
+ setIsEnabled: (enabled: boolean) => void;
+ loopingRef: RefObject