瀏覽代碼

update lang

tremble 3 年之前
父節點
當前提交
c897e9de47

+ 442 - 6
package-lock.json

@@ -15,9 +15,11 @@
         "socket.io": "^4.5.1",
         "trtc-js-sdk": "^4.13.0",
         "vue": "^3.2.36",
+        "vue-i18n": "9",
         "vuex": "^4.0.2"
       },
       "devDependencies": {
+        "@intlify/vue-i18n-loader": "^4.2.0",
         "@vue/cli-plugin-babel": "~5.0.0",
         "@vue/cli-service": "~5.0.0",
         "@vue/compiler-sfc": "^3.0.0",
@@ -1707,6 +1709,130 @@
         "@hapi/hoek": "^9.0.0"
       }
     },
+    "node_modules/@intlify/bundle-utils": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
+      "integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
+      "dev": true,
+      "dependencies": {
+        "@intlify/message-compiler": "^9.1.0",
+        "@intlify/shared": "^9.1.0",
+        "jsonc-eslint-parser": "^1.0.1",
+        "source-map": "^0.6.1",
+        "yaml-eslint-parser": "^0.3.2"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/core-base": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.2.2.tgz",
+      "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+      "dependencies": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/devtools-if": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+      "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+      "dependencies": {
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/message-compiler": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+      "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+      "dependencies": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/shared": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.2.2.tgz",
+      "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==",
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+      "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      },
+      "engines": {
+        "node": ">= 14"
+      }
+    },
+    "node_modules/@intlify/vue-i18n-loader": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
+      "integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
+      "dev": true,
+      "dependencies": {
+        "@intlify/bundle-utils": "^2.2.2",
+        "@intlify/shared": "^9.1.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.0",
+        "loader-utils": "^2.0.0"
+      },
+      "engines": {
+        "node": ">= 12"
+      },
+      "peerDependencies": {
+        "petite-vue-i18n": "^9.1.0",
+        "vue": "^3.0.0",
+        "vue-i18n": "^9.1.0"
+      },
+      "peerDependenciesMeta": {
+        "petite-vue-i18n": {
+          "optional": true
+        },
+        "vue-i18n": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@intlify/vue-i18n-loader/node_modules/loader-utils": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.2.tgz",
+      "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+      "dev": true,
+      "dependencies": {
+        "big.js": "^5.2.2",
+        "emojis-list": "^3.0.0",
+        "json5": "^2.1.2"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
     "node_modules/@jridgewell/gen-mapping": {
       "version": "0.1.1",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -2719,9 +2845,9 @@
       "dev": true
     },
     "node_modules/@vue/devtools-api": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
-      "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
+      "integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
     },
     "node_modules/@vue/reactivity": {
       "version": "3.2.36",
@@ -3008,6 +3134,15 @@
         "acorn": "^8"
       }
     },
+    "node_modules/acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "peerDependencies": {
+        "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+      }
+    },
     "node_modules/acorn-walk": {
       "version": "8.2.0",
       "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz",
@@ -3149,6 +3284,12 @@
       "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
       "dev": true
     },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "node_modules/array-flatten": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz",
@@ -4852,6 +4993,53 @@
         "node": ">=8.0.0"
       }
     },
+    "node_modules/eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/eslint-visitor-keys": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/espree": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-6.2.1.tgz",
+      "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^7.1.1",
+        "acorn-jsx": "^5.2.0",
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/espree/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -5949,6 +6137,18 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
+    "node_modules/js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "dependencies": {
+        "argparse": "^2.0.1"
+      },
+      "bin": {
+        "js-yaml": "bin/js-yaml.js"
+      }
+    },
     "node_modules/jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -5991,6 +6191,34 @@
         "node": ">=6"
       }
     },
+    "node_modules/jsonc-eslint-parser": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
+      "integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
+      "dev": true,
+      "dependencies": {
+        "acorn": "^7.4.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^1.3.0",
+        "espree": "^6.0.0",
+        "semver": "^6.3.0"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/jsonc-eslint-parser/node_modules/acorn": {
+      "version": "7.4.1",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+      "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -9236,6 +9464,23 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
       "dev": true
     },
+    "node_modules/vue-i18n": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.2.2.tgz",
+      "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+      "dependencies": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      },
+      "engines": {
+        "node": ">= 14"
+      },
+      "peerDependencies": {
+        "vue": "^3.0.0"
+      }
+    },
     "node_modules/vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -9924,6 +10169,17 @@
         "node": ">= 6"
       }
     },
+    "node_modules/yaml-eslint-parser": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
+      "integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
+      "dev": true,
+      "dependencies": {
+        "eslint-visitor-keys": "^1.3.0",
+        "lodash": "^4.17.20",
+        "yaml": "^1.10.0"
+      }
+    },
     "node_modules/yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",
@@ -11130,6 +11386,87 @@
         "@hapi/hoek": "^9.0.0"
       }
     },
+    "@intlify/bundle-utils": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz",
+      "integrity": "sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==",
+      "dev": true,
+      "requires": {
+        "@intlify/message-compiler": "^9.1.0",
+        "@intlify/shared": "^9.1.0",
+        "jsonc-eslint-parser": "^1.0.1",
+        "source-map": "^0.6.1",
+        "yaml-eslint-parser": "^0.3.2"
+      }
+    },
+    "@intlify/core-base": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.2.2.tgz",
+      "integrity": "sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==",
+      "requires": {
+        "@intlify/devtools-if": "9.2.2",
+        "@intlify/message-compiler": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2"
+      }
+    },
+    "@intlify/devtools-if": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/devtools-if/-/devtools-if-9.2.2.tgz",
+      "integrity": "sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==",
+      "requires": {
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@intlify/message-compiler": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.2.2.tgz",
+      "integrity": "sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==",
+      "requires": {
+        "@intlify/shared": "9.2.2",
+        "source-map": "0.6.1"
+      }
+    },
+    "@intlify/shared": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.2.2.tgz",
+      "integrity": "sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q=="
+    },
+    "@intlify/vue-devtools": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-devtools/-/vue-devtools-9.2.2.tgz",
+      "integrity": "sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2"
+      }
+    },
+    "@intlify/vue-i18n-loader": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/@intlify/vue-i18n-loader/-/vue-i18n-loader-4.2.0.tgz",
+      "integrity": "sha512-d7aBmMNWJskcZPT5rJH4h2XHe/PwNoJUaY0PGla9g+NSD4B0UR8LBKrp126nlaUfA74Xt0FEGvzCfG9KdC9KoA==",
+      "dev": true,
+      "requires": {
+        "@intlify/bundle-utils": "^2.2.2",
+        "@intlify/shared": "^9.1.0",
+        "js-yaml": "^4.1.0",
+        "json5": "^2.2.0",
+        "loader-utils": "^2.0.0"
+      },
+      "dependencies": {
+        "loader-utils": {
+          "version": "2.0.2",
+          "resolved": "https://registry.npmmirror.com/loader-utils/-/loader-utils-2.0.2.tgz",
+          "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^3.0.0",
+            "json5": "^2.1.2"
+          }
+        }
+      }
+    },
     "@jridgewell/gen-mapping": {
       "version": "0.1.1",
       "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
@@ -11986,9 +12323,9 @@
       }
     },
     "@vue/devtools-api": {
-      "version": "6.1.4",
-      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz",
-      "integrity": "sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ=="
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.2.1.tgz",
+      "integrity": "sha512-OEgAMeQXvCoJ+1x8WyQuVZzFo0wcyCmUR3baRVLmKBo1LmYZWMlRiXlux5jd0fqVJu6PfDbOrZItVqUEzLobeQ=="
     },
     "@vue/reactivity": {
       "version": "3.2.36",
@@ -12250,6 +12587,13 @@
       "dev": true,
       "requires": {}
     },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true,
+      "requires": {}
+    },
     "acorn-walk": {
       "version": "8.2.0",
       "resolved": "https://registry.npmmirror.com/acorn-walk/-/acorn-walk-8.2.0.tgz",
@@ -12359,6 +12703,12 @@
       "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==",
       "dev": true
     },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
     "array-flatten": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/array-flatten/-/array-flatten-2.1.2.tgz",
@@ -13723,6 +14073,40 @@
         "estraverse": "^4.1.1"
       }
     },
+    "eslint-utils": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/eslint-utils/-/eslint-utils-2.1.0.tgz",
+      "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.1.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz",
+      "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/espree/-/espree-6.2.1.tgz",
+      "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.1.1",
+        "acorn-jsx": "^5.2.0",
+        "eslint-visitor-keys": "^1.1.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
     "esrecurse": {
       "version": "4.3.0",
       "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz",
@@ -14609,6 +14993,15 @@
       "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
       "dev": true
     },
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
     "jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -14639,6 +15032,27 @@
       "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==",
       "dev": true
     },
+    "jsonc-eslint-parser": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmmirror.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz",
+      "integrity": "sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==",
+      "dev": true,
+      "requires": {
+        "acorn": "^7.4.1",
+        "eslint-utils": "^2.1.0",
+        "eslint-visitor-keys": "^1.3.0",
+        "espree": "^6.0.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.1",
+          "resolved": "https://registry.npmmirror.com/acorn/-/acorn-7.4.1.tgz",
+          "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+          "dev": true
+        }
+      }
+    },
     "jsonfile": {
       "version": "6.1.0",
       "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz",
@@ -17171,6 +17585,17 @@
       "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==",
       "dev": true
     },
+    "vue-i18n": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.2.2.tgz",
+      "integrity": "sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==",
+      "requires": {
+        "@intlify/core-base": "9.2.2",
+        "@intlify/shared": "9.2.2",
+        "@intlify/vue-devtools": "9.2.2",
+        "@vue/devtools-api": "^6.2.1"
+      }
+    },
     "vue-loader": {
       "version": "17.0.0",
       "resolved": "https://registry.npmmirror.com/vue-loader/-/vue-loader-17.0.0.tgz",
@@ -17715,6 +18140,17 @@
       "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
       "dev": true
     },
+    "yaml-eslint-parser": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz",
+      "integrity": "sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^1.3.0",
+        "lodash": "^4.17.20",
+        "yaml": "^1.10.0"
+      }
+    },
     "yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmmirror.com/yargs/-/yargs-16.2.0.tgz",

+ 2 - 0
package.json

@@ -16,9 +16,11 @@
     "socket.io": "^4.5.1",
     "trtc-js-sdk": "^4.13.0",
     "vue": "^3.2.36",
+    "vue-i18n": "9",
     "vuex": "^4.0.2"
   },
   "devDependencies": {
+    "@intlify/vue-i18n-loader": "^4.2.0",
     "@vue/cli-plugin-babel": "~5.0.0",
     "@vue/cli-service": "~5.0.0",
     "@vue/compiler-sfc": "^3.0.0",

+ 23 - 45
src/app.vue

@@ -8,46 +8,16 @@
     <template v-if="dataLoaded">
       <Information v-if="!isshoppingguide" />
       <Control />
-      <!-- <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
-        <span :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="button-switch" @click.stop="toggleMap">
-          <ui-icon type="show_map_collect"></ui-icon>
-        </span>
-
-        <p class="change" :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" @click="changeMode('dollhouse', $event, 'focus3d')">
-          <ui-icon type="show_3d_normal"></ui-icon>
-          3D模型
-        </p>
-      </teleport> -->
+    
       <teleport v-if="refMiniMap && player.showWidgets" :to="refMiniMap">
         <span :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="button-switch" @click.stop="toggleMap">
           <ui-icon type="show_map_collect"></ui-icon>
         </span>
         <div v-if="controls.showDollhouse" :class="{ gudieDisabled: isshoppingguide && role != 'leader' }" class="change" @click="changeMode('dollhouse')">
           <ui-icon type="show_3d_normal"></ui-icon>
-          <span> 3D模型</span>
+          <span> {{ $t('mode.dollhouseModel') }}</span>
         </div>
       </teleport>
-      <!-- <template v-if="refMiniMap && player.showWidgets">
-        <div
-          :class="{ disabled: flying, gudieDisabled: isshoppingguide && role != 'leader' }"
-          v-show="mode != 'panorama'"
-          v-if="controls.showFloorplan && controls.showDollhouse"
-          class="tab-layer"
-        >
-          <div class="tabs" v-if="controls.showMap">
-            <span :class="{ active: mode === 'floorplan' }" @click="changeMode('floorplan', $event)">
-              <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
-              二維
-            </span>
-            <span :class="{ active: mode === 'dollhouse' }" @click="changeMode('dollhouse', $event)">
-              <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
-
-              三維
-            </span>
-            <div class="background" ref="background"></div>
-          </div>
-        </div>
-      </template> -->
 
       <template v-if="refMiniMap && player.showWidgets">
         <div
@@ -59,12 +29,12 @@
           <div class="tabs" v-if="controls.showMap">
             <span :class="{ active: mode === 'floorplan' }" ref="floorplan_ref" @click="changeMode('floorplan', $event)">
               <ui-icon :type="mode == 'floorplan' ? 'show_plane_selected' : 'show_plane_normal'"></ui-icon>
-              二維
+              {{ $t('mode.floorplan') }}
             </span>
             <span :class="{ active: mode === 'dollhouse' }" ref="dollhouse_ref" @click="changeMode('dollhouse', $event)">
               <ui-icon :type="mode == 'dollhouse' ? 'show_3d_selected' : 'show_3d_normal'"></ui-icon>
 
-              三維
+              {{ $t('mode.dollhouse') }}
             </span>
             <div class="background" ref="background"></div>
           </div>
@@ -99,10 +69,14 @@ import { useStore } from "vuex";
 import browser from "@/utils/browser";
 import { useApp, getApp } from "@/app";
 import common from "@/utils/common";
+import { useI18n, getLocale } from '@/i18n'
 import { Cache } from "@/utils/index";
 import wxShare from "@/utils/wxshare";
 
 import * as apis from "@/apis/index.js";
+
+const { t } = useI18n({ useScope: 'global' })
+
 const store = useStore();
 
 let jumpNewScene = (sceneFirstView) => {
@@ -350,6 +324,7 @@ onMounted(async () => {
     mobile: true,
     isLoadTags: false,
     sceneKind: "tiles",
+    lang: getLocale(),
     scene: {
       markerOpacity: 1,
       markerURL: "https://eurs3.4dkankan.com/cdf/file/43aa29799bfd472298a47cc6370b10cc.png",
@@ -376,7 +351,7 @@ onMounted(async () => {
           return `<span class="tag-icon animate" style="background-image:url({{icon}})"></span>
                       <div class="tag-body">
                         <div data-id="${data.sid}" class="tag-commodity tag-link_scene">
-                          <p class="tag-title">點擊前往下一個區域</p>
+                          <p class="tag-title">${ t('common.goNext')}</p>
                         </div>
                       </div>
                   `;
@@ -392,7 +367,7 @@ onMounted(async () => {
                           <div  style="background-image:url(${data.products[0] ? data.products[0].pic : ""})" class='tag-avatar'>
                           </div>
                           <p class="tag-title">${data.title}</p>
-                          <p class="tag-info">${range} | 查看 ></p>
+                          <p class="tag-info">${range} | ${t('common.view')} ></p>
                         </div>
                       </div>
                   `;
@@ -477,9 +452,12 @@ onMounted(async () => {
   // }
   app.Scene.on("ready", () => {
     show.value = true;
+    store.commit('SetPlayerOptions', {
+        lang: getLocale(),
+    })
     wxShare({
-      title: `cdf澳門上葡京店~`,
-      desc: "cdf澳門上葡京店~",
+      title: `${t('common.title')}~`,
+      desc:  `${t('common.title')}~`,
       link: window.location.href,
       imgUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
     });
@@ -487,16 +465,16 @@ onMounted(async () => {
   app.Scene.on("error", (data) => {
     switch (data.code) {
       case 5033:
-        Dialog.alert("该场景正在计算中,请稍后再试");
+        Dialog.alert(t('common.calculation'));
         break;
       case 5034:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
       case 5009:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
       case 5005:
-        Dialog.alert("服务端开小差,请稍后再试");
+        Dialog.alert(t('common.title'));
         break;
     }
   });
@@ -511,10 +489,10 @@ onMounted(async () => {
       if (!localStorage.getItem("user_guide")) {
         Dialog.confirm({
           showCloseIcon: false,
-          okText: "我知道了",
+          okText: t('common.know'),
           content:
-            "<span style='font-size: 16px; line-height: 1.5;'>開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。<span/>",
-          title: "隱私條款:",
+            "<span style='font-size: 16px; line-height: 1.5;'>"+t('common.notice')+"<span/>",
+          title: `${t('common.know')}:` ,
           single: true,
           func: (state) => {
             if (state == "ok") {

二進制
src/assets/images/icon/en.png


二進制
src/assets/images/icon/zh_CN.png


二進制
src/assets/images/icon/zh_HK.png


+ 6 - 46
src/components/Controls/Panel/Guide.vue

@@ -33,7 +33,7 @@
         </div>
         <div class="back" @click.stop="playTour">
           <ui-icon type="back"></ui-icon>
-          <div>返回</div>
+          <div>{{$t('common.back')}}</div>
         </div>
       </div>
     </div>
@@ -46,6 +46,10 @@ import common from "@/utils/common";
 import { onMounted, watch, computed, ref, nextTick } from "vue";
 import { useApp, getApp } from "@/app";
 import { useMusicPlayer } from "@/utils/sound";
+
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
 let timer = null
 
 const store = useStore();
@@ -90,51 +94,7 @@ const player = computed(() => store.getters["player"]);
 
 const tours = computed(() => store.getters["tour/tours"]);
 
-const menulist = ref([
-  {
-    icon: "customer_service",
-    name: "客服",
-  },
-  {
-    icon: "guided_shopping",
-    name: "一起逛",
-  },
-  {
-    icon: "shopping",
-    name: "購物",
-  },
-]);
-
-const categorylist = ref([
-  {
-    id: "all",
-    name: "全部",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "香化",
-  },
-  {
-    id: "all",
-    name: "全部",
-  },
-  {
-    id: "all",
-    name: "全部",
-  },
-]);
+
 
 const brandlist = ref([]);
 

+ 16 - 13
src/components/Controls/Panel/Main.vue

@@ -5,7 +5,7 @@
       <div @click="toggleOpen" class="menu color">
         <div class="logo">
           <img :src="require('@/assets/images/icon/logo.png')" alt="" />
-          <p>cdf澳門上葡京店</p>
+          <p>{{ $t('common.title') }}</p>
         </div>
         <div class="vline"></div>
         <ul>
@@ -25,11 +25,11 @@
                 onClickMenu({
                   icon: 'guided_shopping',
                   id: 'guided_shopping',
-                  name: '一起逛',
+                  name: $t('common.mode'),
                 })
               "
             ></ui-icon>
-            <div>一起逛</div>
+            <div>{{ $t('common.mode') }}</div>
           </li>
 
           <li>
@@ -39,11 +39,11 @@
                 onClickMenu({
                   icon: 'shopping',
                   id: 'shopping',
-                  name: '購物',
+                  name: $t('common.shopping'),
                 })
               "
             ></ui-icon>
-            <div>購物</div>
+            <div>{{$t('common.shopping')}}</div>
           </li>
         </ul>
       </div>
@@ -51,10 +51,10 @@
       <div class="toolbar color">
         <div class="navigation">
           <div class="h3">
-            <span>專櫃導航</span>
+            <span>{{$t('common.guide')}}</span>
             <div @click="showShopList = true">
               <img :src="require(`@/assets/images/icon/search.svg`)" alt="" />
-              搜索專櫃
+              {{$t('common.searchguide')}}
             </div>
           </div>
           <div class="swiper-container" id="sw-navigation">
@@ -86,11 +86,11 @@
                 @click.stop="
                   onClickCategory({
                     id: '',
-                    categoryName: '全部',
+                    categoryName: $t('common.all'),
                   })
                 "
               >
-                <div>全部</div>
+                <div>{{$t('common.all')}}</div>
               </li>
               <li
                 @click.stop="onClickCategory(item)"
@@ -109,7 +109,7 @@
     <teleport :to="`#app`">
       <div v-show="showShopList" class="shoplist">
         <div class="l-title">
-          專櫃列表 ({{ searchList.length }})
+          {{$t('common.guidelist')}} ({{ searchList.length }})
           <ui-icon type="close" @click="showShopList = false" />
         </div>
 
@@ -131,7 +131,7 @@
           </li>
         </ul>
 
-        <div class="noresult" v-else>暫無結果</div>
+        <div class="noresult" v-else>{{$t('common.noResult')}}</div>
       </div>
     </teleport>
   </div>
@@ -144,6 +144,9 @@ import Panel from "@/views/Panel.vue";
 import { useApp, getApp } from "@/app";
 import * as apis from "@/apis/index.js";
 import browser from "@/utils/browser";
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
 
 const store = useStore();
 
@@ -202,12 +205,12 @@ const menulist = computed(() => {
     {
       icon: "help",
       id: "help",
-      name: "幫助",
+      name: t('common.help'),
     },
     {
       icon: "customer_service",
       id: "kefu",
-      name: "客服",
+      name: t('common.kefu'),
     },
   ];
 

文件差異過大導致無法顯示
+ 798 - 683
src/components/Information/View.Mobile.vue


+ 30 - 28
src/components/RTC/PageRtcLive.vue

@@ -2,7 +2,7 @@
   <div id="PageRtcLive">
     <div class="member_number">
       <div class="members"></div>
-      <span>{{ user_list.length }}觀看</span>
+      <span>{{ user_list.length }}{{$t('common.viewnum')}}</span>
     </div>
     <chat v-show="chatShow" :chatList="chatList" :user_info="user_info"></chat>
 
@@ -12,12 +12,12 @@
       <div v-if="connectStatus == 1" :class="{ disabled: !user_info.IsWords }" class="saySomething" @click="onFocus">
         <!-- <i class="speakIcon"
            :class="{'dis':!user_info.IsWords}"></i> -->
-        <span v-if="user_info.IsWords">說點什麼</span>
-        <span v-if="!user_info.IsWords">已被禁言</span>
+        <span v-if="user_info.IsWords">{{$t('common.saysomething')}}</span>
+        <span v-if="!user_info.IsWords">{{$t('common.cantsay')}}</span>
 
         <div class="disSpeakBtn" @click.stop="chatShow = !chatShow" :class="{ dis: !chatShow }"></div>
       </div>
-      <div style="text-align: right; width: 100%" v-if="connectStatus == 0">連接中...</div>
+      <div style="text-align: right; width: 100%" v-if="connectStatus == 0">{{$t('common.linking')}}</div>
       <div v-if="connectStatus == 1" class="contorl_btn">
         <div v-if="isBrushes && user_info.Role == 'leader'" @click="onDrawUndo" class="brushesBack" :class="{ disabled: !canUndo }"></div>
         <div v-if="user_info.Role == 'leader'" @click="onDraw(!isBrushes)" :class="{ brushesed: isBrushes }" class="brushes"></div>
@@ -33,8 +33,8 @@
     <div class="layer" v-if="showInput" @click="closeInput">
       <div class="inputBox" @click.stop>
         <div class="msgBox">
-          <input id="input_msg" type="text" maxlength="200" v-model.trim="text" :placeholder="`說點什麼~`" />
-          <span class="iconsend_icon" :class="{ disable: text == '' }" @click.stop="sendText">發送</span>
+          <input id="input_msg" type="text" maxlength="200" v-model.trim="text" :placeholder="`${$t('common.saysomething')}~`" />
+          <span class="iconsend_icon" :class="{ disable: text == '' }" @click.stop="sendText">{{$t('common.send')}}</span>
         </div>
       </div>
     </div>
@@ -45,7 +45,7 @@
         <div class="blurBox"></div>
         <div class="content">
           <div class="memberHeader">
-            <span> 成員管理({{ user_list.length }})</span>
+            <span> {{$t('common.member')}}({{ user_list.length }})</span>
             <i class="iconfont"></i>
           </div>
           <div class="memberList">
@@ -54,8 +54,8 @@
                 <div class="avatar">
                   <img :src="require('@/assets/images/rtcLive/avatar_small@2x.png')" alt="" />
                 </div>
-                <div class="name" v-if="user_info.Role == 'leader'">{{ user_info.Nickname }} (主持人,我)</div>
-                <div class="name" v-else>{{ user_info.Nickname }} ()</div>
+                <div class="name" v-if="user_info.Role == 'leader'">{{ user_info.Nickname }} ({{$t('common.leader')}},{{$t('common.me')}})</div>
+                <div class="name" v-else>{{ user_info.Nickname }} ({{$t('common.me')}})</div>
               </div>
 
               <div class="button" v-if="user_info.Role == 'leader'">
@@ -106,6 +106,9 @@ import Trtccom from "./Trtccom.vue";
 import browser from "@/utils/browser";
 import wxShare from "@/utils/wxshare";
 
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
+
 const emit = defineEmits(["openDialog", "closeSocket"]);
 
 const store = useStore();
@@ -146,7 +149,6 @@ let getUrl = (href, queryArr) => {
     if (!browser.hasURLParam(item.key)) {
       let ttt = href.split("index.html?");
       href = `${ttt[0]}index.html?${item.key}=${item.val}&${ttt[1]}`;
-      console.log(href, "------index.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.htmlindex.html----------");
     } else {
       href = browser.replaceQueryString(href, item.key, item.val);
     }
@@ -233,7 +235,7 @@ const userGetOut = (item, i) => {
 const setUserWords = (res) => {
   if (res.userId == user_info.value.UserId) {
     user_info.value.IsWords = res.words;
-    Dialog.toast({ content: !user_info.value.IsWords ? `主持人設置了禁言` : `主持人已解除禁言` });
+    Dialog.toast({ content: !user_info.value.IsWords ? t('common.setTaboo') : t('common.relieveTaboo') });
   }
 };
 
@@ -252,7 +254,7 @@ const setUserMuted = (res) => {
   if (res.userId) {
     if (res.userId == user_info.value.UserId && role.value == "customer") {
       user_info.value.IsMuted = res.muted;
-      Dialog.toast({ content: !user_info.value.IsMuted ? `主持人設置了開麥` : `主持人設置了靜音` });
+      Dialog.toast({ content: !user_info.value.IsMuted ?  t('common.openMic') : t('common.closeMic') });
       disableMic.value = res.muted;
       audioMuted.value = res.muted;
     }
@@ -282,7 +284,7 @@ const onAllMuted = (res) => {
     user_info.value.IsMuted = res.muted;
     item.IsMuted = res.muted;
     if (role.value == "customer") {
-      Dialog.toast({ content: !user_info.value.IsMuted ? `主持人設置了開麥` : `主持人設置了靜音` });
+      Dialog.toast({ content: !user_info.value.IsMuted ? t('common.openMic') : t('common.closeMic') });
       disableMic.value = res.muted;
       audioMuted.value = res.muted;
     }
@@ -302,8 +304,8 @@ const setUserJoin = async (res) => {
   // self.chekcLeaderInfo();
   let name = res.user.Nickname;
   if (res.user.Role == "leader") {
-    name = "主持人";
-    Dialog.toast({ content: `主持人進入房間` });
+    name = t('common.leader') ;
+    Dialog.toast({ content: t('common.leader')+t('common.inRoom') });
     socket.value.emit("action", { type: "user-init" });
   } 
   let data = {
@@ -311,7 +313,7 @@ const setUserJoin = async (res) => {
     mode: mode.value,
     Nickname: name,
     UserId: res.user.UserId,
-    text: "進入房間",
+    text: t('common.inRoom'),
   };
   chatList.value.push(data);
   await nextTick();
@@ -385,7 +387,7 @@ const sendText = async () => {
 const setReceiveMsg = async (res) => {
   console.log(res);
   if (res.role == "leader") {
-    res.Nickname = "主持人";
+    res.Nickname = t('common.leader');
   }
   chatList.value.push(res);
   await nextTick();
@@ -434,14 +436,14 @@ const onMemberLeave = async (res) => {
   let name = res.user.Nickname;
   if (res.user.Role == "leader") {
     name = "主持人";
-    Dialog.toast({ content: `主持人離開了房間` });
+    Dialog.toast({ content: t('common.leader') + t('common.leaveRoom') });
   }
   let data = {
     role: res.user.Role,
     mode: mode.value,
     Nickname: name,
     UserId: res.user.UserId,
-    text: "離開房間",
+    text: t('common.leaveRoom') ,
   };
   chatList.value.push(data);
   await nextTick();
@@ -458,7 +460,7 @@ const userCanSpeak = (item) => {
 const onGetOuT = (data) => {
   if (data.id == user_info.value.UserId) {
     emit("closeSocket");
-    Dialog.toast({ content: `您已被移除` });
+    Dialog.toast({ content: t('common.removed')});
   }
 };
 
@@ -499,7 +501,7 @@ const startFollow = (app) => {
     }, []);
 
     if (meblist.length > 5 && role.value == "customer") {
-      Dialog.toast({ content: `房間已滿員` });
+      Dialog.toast({ content: t('common.fullStarffed') });
       emit("closeSocket");
       return;
     }
@@ -549,8 +551,8 @@ const startFollow = (app) => {
 
     wxShare({
       donotconfig: true,
-      title: `【好友推薦】一起雲逛店吧~`,
-      desc: "【好友推薦】一起雲逛店吧~",
+      title: t('common.invitation'),
+      desc:  t('common.invitation'),
       link: shareLink.value,
       imgUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
     });
@@ -561,7 +563,7 @@ const startFollow = (app) => {
         if (res.miniprogram) {
           wx.miniProgram.postMessage({
             data: {
-              title: "【好友推薦】一起雲逛店吧~",
+              title: t('common.invitation'),
               imageUrl: "https://glp-vr.cdfmembers.com/cdf/file/91dd5305525f463286f03a31abd1c154.jpg",
               h5Url: shareLink.value,
             },
@@ -602,7 +604,7 @@ const startFollow = (app) => {
   socket.value.on("action", (data) => {
     console.log(data,'=============');
     if (data.type == "error") {
-      Dialog.toast({ content: `房間未找到`, type: "error" });
+      Dialog.toast({ content: t('common.notFoundRoom'), type: "error" });
       emit("closeSocket");
     } else if (data.type == "danmumsg") {
       setReceiveMsg(data.data);
@@ -614,9 +616,9 @@ const startFollow = (app) => {
       onDraw(data.open);
       if (role.value == "customer") {
         if (data.open) {
-          Dialog.toast({ content: `主持人開啟畫筆` });
+          Dialog.toast({ content: t('common.leader')+t('common.openBrush')  });
         } else {
-          Dialog.toast({ content: `主持人關閉畫筆` });
+          Dialog.toast({ content:  t('common.leader')+t('common.closeBrush') });
         }
       }
     } else if (data.type == "user-join") {
@@ -632,7 +634,7 @@ const startFollow = (app) => {
       onMemberLeave(data);
     } else if (data.type == "leader-dismiss") {
       emit("closeSocket");
-      Dialog.toast({ content: `主持人已解散房間` });
+      Dialog.toast({ content: t('common.leader')+t('common.dismissRoom')  });
     } else if (data.type == "tagclick") {
       if (role.value == "customer") {
         let item = tags.value.find((item) => item.sid == data.data.sid);

+ 138 - 133
src/components/Tags/goods-list.vue

@@ -11,57 +11,59 @@
       <div class="swiper-container" id="goodlist">
         <ul class="swiper-wrapper">
           <li class="swiper-slide" v-for="(i, index) in tagclick.data.products" :key="index">
-            <viewimg :list="i.pics" :keyid="`viewimg_${index + 1}`" />
+            <div>
+              <viewimg :list="i.pics" :keyid="`viewimg_${index + 1}`" />
 
-            <div class="info">
-              <p>{{ i.name }}</p>
-              <div v-if="i.skus">
-                <span>{{ i.price[0].symbol }}</span
-                ><span>{{ i.currentSku.price }}</span>
+              <div class="info">
+                <p>{{ i.name }}</p>
+                <div v-if="i.skus">
+                  <span>{{ i.price[0].symbol }}</span
+                  ><span>{{ i.currentSku.price }}</span>
+                </div>
+                <ul>
+                  <li>
+                    <span>規格:</span>
+                    <ul>
+                      <li @click.stop="onClickSku(item)" :class="{ active: item.id == i.currentSku.id }" v-for="(item, idx) in i.skus" :key="idx">
+                        <span v-if="item.properties[0]">
+                          {{ item.properties[0].value }}
+                        </span>
+                      </li>
+                    </ul>
+                  </li>
+                  <li>
+                    <span>數量:</span>
+                    <div class="number">
+                      <ui-icon
+                        @click="
+                          () => {
+                            i.cartnum--;
+                            i.cartnum = Math.max(i.cartnum, 0);
+                          }
+                        "
+                        type="cad-neiqiang"
+                      ></ui-icon>
+                      <input maxlength="5" v-model="i.cartnum" type="number" />
+                      <ui-icon
+                        @click="
+                          () => {
+                            i.cartnum++;
+                            i.cartnum = Math.min(i.cartnum, 9999);
+                          }
+                        "
+                        type="add"
+                      ></ui-icon>
+                    </div>
+                  </li>
+                </ul>
+              </div>
+              <div class="goods-button">
+                <span v-if="i.saleChannel == 1" class="tipss">僅供線下門店購買</span>
+                <template v-else>
+                  <div @click.stop="viewDetail(i)">查看詳情</div>
+                  <div @click.stop="addCart(i)">加入購物車</div>
+                </template>
               </div>
-              <ul>
-                <li>
-                  <span>規格:</span>
-                  <ul>
-                    <li @click.stop="onClickSku(item)" :class="{ active: item.id == i.currentSku.id }" v-for="(item, idx) in i.skus" :key="idx">
-                      <span v-if="item.properties[0]">
-                        {{ item.properties[0].value }}
-                      </span>
-                    </li>
-                  </ul>
-                </li>
-                <li>
-                  <span>數量:</span>
-                  <div class="number">
-                    <ui-icon
-                      @click="
-                        () => {
-                          i.cartnum--;
-                          i.cartnum = Math.max(i.cartnum, 0);
-                        }
-                      "
-                      type="cad-neiqiang"
-                    ></ui-icon>
-                    <input maxlength="5" v-model="i.cartnum" type="number" />
-                    <ui-icon
-                      @click="
-                        () => {
-                          i.cartnum++;
-                          i.cartnum = Math.min(i.cartnum, 9999);
-                        }
-                      "
-                      type="add"
-                    ></ui-icon>
-                  </div>
-                </li>
-              </ul>
-            </div>
-            <div class="goods-button">
-              <span v-if="i.saleChannel == 1" class="tipss">僅供線下門店購買</span>
-              <template v-else>
-                <div @click.stop="viewDetail(i)">查看詳情</div>
-                <div @click.stop="addCart(i)">加入購物車</div>
-              </template>
             </div>
           </li>
         </ul>
@@ -292,108 +294,111 @@ onMounted(() => {
         position: relative;
         font-size: 0;
         overflow: hidden;
+
+        > div {
         background: #fff;
         padding-bottom: 14px;
 
-        .info {
-          font-size: 14px;
-          color: #131d34;
-          padding: 16px 20px;
-          > p {
-            font-size: 16px;
-            width: 100%;
-            line-height: 1.5;
-            word-break: break-all;
-          }
-          > div {
-            color: var(--editor-main-color);
-            padding: 8px 0px 10px;
-            border-bottom: 1px solid #ebebeb;
-            > span {
+          .info {
+            font-size: 14px;
+            color: #131d34;
+            padding: 16px 20px;
+            > p {
               font-size: 16px;
-              &:first-of-type {
-                font-size: 12px;
-                margin-right: 4px;
-              }
+              width: 100%;
+              line-height: 1.5;
+              word-break: break-all;
             }
-          }
-          > ul {
-            padding: 12px 0;
-            border-bottom: 1px solid #ebebeb;
-
-            > li {
-              margin: 10px 0;
-              color: #909090;
-              &:last-of-type {
-                display: flex;
-                justify-content: space-between;
-                align-items: center;
-              }
+            > div {
+              color: var(--editor-main-color);
+              padding: 8px 0px 10px;
+              border-bottom: 1px solid #ebebeb;
               > span {
-                font-size: 12px;
+                font-size: 16px;
+                &:first-of-type {
+                  font-size: 12px;
+                  margin-right: 4px;
+                }
               }
-              > ul {
-                display: block;
-                > li {
-                  display: inline-block;
-                  > span {
+            }
+            > ul {
+              padding: 12px 0;
+              border-bottom: 1px solid #ebebeb;
+
+              > li {
+                margin: 10px 0;
+                color: #909090;
+                &:last-of-type {
+                  display: flex;
+                  justify-content: space-between;
+                  align-items: center;
+                }
+                > span {
+                  font-size: 12px;
+                }
+                > ul {
+                  display: block;
+                  > li {
                     display: inline-block;
-                    border: 1px solid #ebebeb;
-                    padding: 2px 4px;
-                    min-width: 73px;
-                    height: 30px;
-                    line-height: 30px;
-                    margin: 8px 8px 0 0;
-                    text-align: center;
-                    border-radius: 4px;
-                    font-size: 12px;
-                  }
-                  &.active {
                     > span {
-                      border: solid 1px var(--editor-main-color);
-                      color: var(--editor-main-color);
+                      display: inline-block;
+                      border: 1px solid #ebebeb;
+                      padding: 2px 4px;
+                      min-width: 73px;
+                      height: 30px;
+                      line-height: 30px;
+                      margin: 8px 8px 0 0;
+                      text-align: center;
+                      border-radius: 4px;
+                      font-size: 12px;
+                    }
+                    &.active {
+                      > span {
+                        border: solid 1px var(--editor-main-color);
+                        color: var(--editor-main-color);
+                      }
                     }
                   }
                 }
-              }
-              .number {
-                display: flex;
-                i {
-                  font-size: 12px;
-                  border: 1px solid #ebebeb;
-                  padding: 2px 8px;
-                  border-radius: 15px;
-                }
-                input {
-                  max-width: 40px;
-                  text-align: center;
-                  color: #131d34;
+                .number {
+                  display: flex;
+                  i {
+                    font-size: 12px;
+                    border: 1px solid #ebebeb;
+                    padding: 2px 8px;
+                    border-radius: 15px;
+                  }
+                  input {
+                    max-width: 40px;
+                    text-align: center;
+                    color: #131d34;
+                  }
                 }
               }
             }
           }
-        }
-        .goods-button {
-          display: flex;
-          justify-content: space-between;
-          font-size: 16px;
-          padding: 0 20px;
+          .goods-button {
+            display: flex;
+            justify-content: space-between;
+            font-size: 16px;
+            padding: 0 20px;
 
-          > div {
-            height: 42px;
-            background: #ff8e24;
-            border-radius: 4px;
-            width: 48%;
-            text-align: center;
-            line-height: 42px;
-            &:last-of-type {
-              background: var(--editor-main-color);
+            > div {
+              height: 42px;
+              background: #ff8e24;
+              border-radius: 4px;
+              width: 48%;
+              text-align: center;
+              line-height: 42px;
+              &:last-of-type {
+                background: var(--editor-main-color);
+              }
+            }
+            .tipss {
+              color: #909090;
+              display: inline-block;
+              margin: 0 auto;
             }
-          }
-          .tipss {
-            color: #909090;
-            display: inline-block;
-            margin: 0 auto;
           }
         }
       }

+ 3 - 2
src/components/openVideo/index.vue

@@ -3,7 +3,7 @@
     <div class="open-video">
       <!-- <div class="vmask"></div> -->
       <video x5-playsinline="true" playsinline="true" webkit-playsinline="true" class="video" ref="openvideo$" preload autoplay :src="videourl"></video>
-      <div v-show="videourl" @click.stop="countdown==0&&emit('close')" class="jump">{{countdown>0?countdown+'s 後可跳過視頻':'跳過'}}</div>
+      <div v-show="videourl" @click.stop="countdown==0&&emit('close')" class="jump">{{countdown>0?countdown+$t('common.jumpTips'):$t('common.jump')}}</div>
       <img v-show="videourl && bofanging" @click.stop="bofang" class="bofang" :src="require('@/assets/images/icon/bofang.png')" alt="" />
     </div>
   </transition>
@@ -11,7 +11,8 @@
 <script setup>
 import { ref, watch, defineEmits, computed, onMounted, nextTick, defineProps } from "vue";
 import * as apis from "@/apis/index.js";
-
+import { useI18n, getLocale } from '@/i18n'
+const { t } = useI18n({ useScope: 'global' })
 const openvideo$ = ref(null);
 
 const videourl = ref(null);

+ 64 - 0
src/i18n/index.js

@@ -0,0 +1,64 @@
+import { nextTick } from 'vue'
+import { useI18n, createI18n } from 'vue-i18n'
+import browser from '@/utils/browser'
+
+export { useI18n }
+export const SUPPORT_LOCALES = ['zh_CN', 'en', 'zh_HK']
+
+export function getLocale() {
+    let lang = browser.getURLParam('lang')
+    if (!lang) {
+        lang = window.navigator.language || window.navigator.userLanguage || null
+        if (lang && /^en/.test(lang)) {
+            console.log('自动获取浏览器语言:' + lang)
+            lang = 'en'
+        }
+        else if (lang && /^zh_CN/.test(lang)) {
+            lang = 'zh_CN'
+        }
+        else {
+            lang = 'zh_HK'
+        }
+    }
+    return lang
+}
+
+export function setupI18n(options = { locale: 'zh_HK' }) {
+    console.log(options.locale);
+    const i18n = createI18n(options)
+    setI18nLanguage(i18n, options.locale)
+    return i18n
+}
+
+export function setI18nLanguage(i18n, locale) {
+    if (i18n.mode === 'legacy') {
+        i18n.global.locale = locale
+    } else {
+        i18n.global.locale.value = locale
+    }
+    /**
+     * NOTE:
+     * If you need to specify the language setting for headers, such as the `fetch` API, set it here.
+     * The following is an example for axios.
+     *
+     * axios.defaults.headers.common['Accept-Language'] = locale
+     */
+    document.querySelector('html').setAttribute('lang', locale)
+}
+
+export async function loadLocaleMessages(i18n, locale) {
+    // load locale messages with dynamic import
+    // set locale and locale message
+
+    const messages = await import(/* webpackChunkName: "locale-[request]" */ `../locales/${locale}.json`)
+    i18n.global.setLocaleMessage(locale, messages.default)
+
+    return nextTick()
+}
+
+export default setupI18n({
+    globalInjection: true,
+    legacy: false,
+    locale: '',
+    fallbackLocale: 'zh_HK',
+})

+ 85 - 0
src/locales/en.json

@@ -0,0 +1,85 @@
+{
+  "common": {
+    "none": "None",
+    "confirm": "Confirm",
+    "cancel": "Cancel",
+    "tips": "Tips",
+    "hide": "Hide",
+    "show": "Show",
+    "review": "Preview",
+    "open": "Open",
+    "close": "Close",
+    "pauseTour": "Pause",
+    "playTour": "Play",
+    "passwordTips": "Please enter the password",
+    "passwordError": "“Password incorrect” error",
+    "tour": "Tour Planner",
+    "all": "All",
+    "model": "Dollhouse",
+    "title":"cdf Macau Grand Lisboa Palace Shop",
+    "mode":"Livestream",
+    "shopping":"Cart",
+    "guide":"Guide",
+    "guidelist":"List",
+    "noResult":"No results found",
+    "searchguide":"Search",
+    "help":"Help",
+    "kefu":"Agent",
+    "viewnum":"View",
+    "saysomething":"Comment...",
+    "cantsay":"Muted",
+    "back":"Back",
+    "linking":"Connecting...",
+    "send":"Send",
+    "member":"Participants",
+    "me":"Me",
+    "leader":"Host",
+    "setTaboo":"Mute all participants",
+    "relieveTaboo":"Unmute all participants",
+    "openMic":" The host is unmuted you",
+    "closeMic":"The host is muted you",
+    "inRoom":"Enter the breakout room",
+    "leaveRoom":"Exit the breakout room",
+    "removed":"You have been removed",
+    "fullStarffed":"The breakout room is full",
+    "notFoundRoom":"The breakout room has not found",
+    "openBrush":" The host enables a brush",
+    "closeBrush":"The host disable the brush",
+    "dismissRoom":"Leave breakout room",
+    "jumpTips":"Video can be skipped after X s",
+    "jump":"Skip",
+
+    "goNext":"Navigate to the next area by clicking.",
+    "view":"View",
+    "calculation":"The current scene is being calculated, please try again later",
+    "error":"System is busy, please try again later",
+    "know":"Got it",
+    "privacy":"Privacy Policy:",
+    "notice":"To preserve the security of user personal information, the developer has adhered with all applicable laws, policies, and regulations regulating the collection and use of end-user personal information.",
+    "invitation":"【Friends Recommend】 Let's conduct a virtual tour walkthrough of the retail store."
+  },
+  "share": {
+    "wechat": "Wechat",
+    "friends": "Moments",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "Copy the link",
+    "shareLink": "Share the link",
+    "shareLinkTips": "Share with friends",
+    "vrMode": "VR Mode",
+    "fastCopy": "Fast copy",
+    "copySuccess":"Successfully copied"
+  },
+  "mode": {
+    "panorama": "Panorama",
+    "floorplan": "Floor Plan",
+    "dollhouse": "Dollhouse",
+    "dollhouseModel": "3D Model",
+    "vr": "VR",
+    "music": "Music",
+    "fullScene": "Full screen",
+    "exitFullScene": "Exit full screen",
+    "rule": "Measurement Tools"
+  }
+}

+ 85 - 0
src/locales/zh.json

@@ -0,0 +1,85 @@
+{
+  "common": {
+    "none": "無",
+    "confirm": "確定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隱藏",
+    "show": "顯示",
+    "review": "預覽",
+    "open": "開",
+    "close": "關",
+    "pauseTour": "暫停導覽",
+    "playTour": "播放導覽",
+    "passwordTips": "請輸入浏覽密碼",
+    "passwordError": "密碼錯誤",
+    "tour": "導覽",
+    "all": "全部",
+    "model": "三維模式",
+    "title":"cdf澳門上葡京店",
+    "mode":"一起逛",
+    "shopping":"購物",
+    "guide":"專櫃導航",
+    "guidelist":"專櫃列表",
+    "noResult":"暫無結果",
+    "searchguide":"搜索專櫃",
+    "help":"幫助",
+    "kefu":"客服",
+    "viewnum":"觀看",
+    "saysomething":"說點什麼",
+    "cantsay":"已被禁言",
+    "back":"返回",
+    "linking":"連接中...",
+    "send":"發送",
+    "member":"成員管理",
+    "me":"我",
+    "leader":"主持人",
+    "setTaboo":"主持人設置了禁言",
+    "relieveTaboo":"主持人已解除禁言",
+    "openMic":"主持人設置了開麥",
+    "closeMic":"主持人設置了靜音",
+    "inRoom":"進入房間",
+    "leaveRoom":"離開房間",
+    "removed":"您已被移除",
+    "fullStarffed":"房間已滿員",
+    "notFoundRoom":"房間未找到",
+    "openBrush":"開啟畫筆",
+    "closeBrush":"關閉畫筆",
+    "dismissRoom":"已解散房間",
+    "jumpTips":"s 後可跳過視頻",
+    "jump":"跳過",
+
+    "goNext":"點擊前往下一個區域",
+    "view":"查看",
+    "calculation":"该场景正在计算中,请稍后再试",
+    "error":"服务端开小差,请稍后再试",
+    "know":"我知道了",
+    "privacy":"隱私條款:",
+    "notice":"開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。",
+    "invitation":"【好友推薦】一起雲逛店吧~"
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "複製鏈接",
+    "shareLink": "分享鏈接",
+    "shareLinkTips": "分享鏈接給好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一鍵複製",
+    "copySuccess":"場景鏈接複製成功"
+  },
+  "mode": {
+    "panorama": "漫遊",
+    "floorplan": "平面",
+    "dollhouse": "三維",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音樂",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "測量工具"
+  }
+}

+ 85 - 0
src/locales/zh_CN.json

@@ -0,0 +1,85 @@
+{
+  "common": {
+    "none": "无",
+    "confirm": "确定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隐藏",
+    "show": "显示",
+    "review": "预览",
+    "open": "开",
+    "close": "关",
+    "pauseTour": "暂停导览",
+    "playTour": "播放导览",
+    "passwordTips": "请输入浏览密码",
+    "passwordError": "密码错误",
+    "tour": "导览",
+    "all": "全部",
+    "model": "三维模式",
+    "title":"cdf澳门上葡京店",
+    "mode":"一起逛",
+    "shopping":"购物",
+    "guide":"专柜导航",
+    "guidelist":"专柜列表",
+    "noResult":"暂无结果",
+    "searchguide":"搜索专柜",
+    "help":"帮助",
+    "kefu":"客服",
+    "viewnum":"观看",
+    "saysomething":"说点什么",
+    "cantsay":"已被禁言",
+    "back":"返回",
+    "linking":"连接中...",
+    "send":"发送",
+    "member":"成员管理",
+    "me":"我",
+    "leader":"主持人",
+    "setTaboo":"主持人设置了禁言",
+    "relieveTaboo":"主持人已解除禁言",
+    "openMic":"主持人设置了开麦",
+    "closeMic":"主持人设置了静音",
+    "inRoom":"进入房间",
+    "leaveRoom":"离开房间",
+    "removed":"您已被移除",
+    "fullStarffed":"房间已满员",
+    "notFoundRoom":"房间未找到",
+    "openBrush":"开启画笔",
+    "closeBrush":"关闭画笔",
+    "dismissRoom":"已解散房间",
+    "jumpTips":"s 后右跳过视频",
+    "jump":"跳过",
+
+    "goNext":"点击前往下一个区域",
+    "view":"查看",
+    "calculation":"该场景正在计算中,请稍后再试",
+    "error":"服务端开小差,请稍后再试",
+    "know":"我知道了",
+    "privacy":"隐私条款:",
+    "notice":"开发者已遵守收集、使用最终用户个人信息有关的所有可适用法律、政策和法规,保护用户个人信息安全。",
+    "invitation":"【好友推荐】一起云逛店吧~"
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "复制链接",
+    "shareLink": "分享链接",
+    "shareLinkTips": "分享链接给好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一键复制",
+    "copySuccess":"场景链接复制成功"
+  },
+  "mode": {
+    "panorama": "漫游",
+    "floorplan": "平面",
+    "dollhouse": "三维",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音乐",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "测量工具"
+  }
+}

+ 85 - 0
src/locales/zh_HK.json

@@ -0,0 +1,85 @@
+{
+  "common": {
+    "none": "無",
+    "confirm": "確定",
+    "cancel": "取消",
+    "tips": "提示",
+    "hide": "隱藏",
+    "show": "顯示",
+    "review": "預覽",
+    "open": "開",
+    "close": "關",
+    "pauseTour": "暫停導覽",
+    "playTour": "播放導覽",
+    "passwordTips": "請輸入浏覽密碼",
+    "passwordError": "密碼錯誤",
+    "tour": "導覽",
+    "all": "全部",
+    "model": "三維模式",
+    "title":"cdf澳門上葡京店",
+    "mode":"一起逛",
+    "shopping":"購物",
+    "guide":"專櫃導航",
+    "guidelist":"專櫃列表",
+    "noResult":"暫無結果",
+    "searchguide":"搜索專櫃",
+    "help":"幫助",
+    "kefu":"客服",
+    "viewnum":"觀看",
+    "saysomething":"說點什麼",
+    "cantsay":"已被禁言",
+    "back":"返回",
+    "linking":"連接中...",
+    "send":"發送",
+    "member":"成員管理",
+    "me":"我",
+    "leader":"主持人",
+    "setTaboo":"主持人設置了禁言",
+    "relieveTaboo":"主持人已解除禁言",
+    "openMic":"主持人設置了開麥",
+    "closeMic":"主持人設置了靜音",
+    "inRoom":"進入房間",
+    "leaveRoom":"離開房間",
+    "removed":"您已被移除",
+    "fullStarffed":"房間已滿員",
+    "notFoundRoom":"房間未找到",
+    "openBrush":"開啟畫筆",
+    "closeBrush":"關閉畫筆",
+    "dismissRoom":"已解散房間",
+    "jumpTips":"s 後可跳過視頻",
+    "jump":"跳過",
+
+    "goNext":"點擊前往下一個區域",
+    "view":"查看",
+    "calculation":"该场景正在计算中,请稍后再试",
+    "error":"服务端开小差,请稍后再试",
+    "know":"我知道了",
+    "privacy":"隱私條款:",
+    "notice":"開發者已遵守收集、使用最終用戶個人信息有關的所有可適用法律、政策和法規,保護用戶個人信息安全。",
+    "invitation":"【好友推薦】一起雲逛店吧~"
+  },
+  "share": {
+    "wechat": "微信",
+    "friends": "朋友圈",
+    "qq": "QQ",
+    "facebook": "Facebook",
+    "whatsApp": "WhatsApp",
+    "copyLink": "複製鏈接",
+    "shareLink": "分享鏈接",
+    "shareLinkTips": "分享鏈接給好友",
+    "vrMode": "VR模式",
+    "fastCopy": "一鍵複製",
+    "copySuccess":"場景鏈接複製成功"
+  },
+  "mode": {
+    "panorama": "漫遊",
+    "floorplan": "平面",
+    "dollhouse": "三維",
+    "dollhouseModel": "3D模型",
+    "vr": "VR功能",
+    "music": "音樂",
+    "fullScene": "全屏",
+    "exitFullScene": "退出全屏",
+    "rule": "測量工具"
+  }
+}

+ 14 - 5
src/main.js

@@ -5,6 +5,7 @@ import { createApp } from "vue";
 import store from "./store";
 import App from "./app.vue";
 import browser from "@/utils/browser";
+import i18n, { getLocale, setI18nLanguage, loadLocaleMessages } from './i18n'
 
 if (!window.location.search) {
   window.location.href = "/index.html?m=eur-KJ-z5ZEV22AeU&pose=pano:408,qua:-0.006,0.6299,0.0049,0.7766";
@@ -12,10 +13,18 @@ if (!window.location.search) {
   let url = window.location.href + `&pose=pano:408,qua:-0.006,0.6299,0.0049,0.7766`;
   window.location.href = url;
 } else {
-  const app = createApp(App);
 
-  app.use(store);
-  app.use(Components);
-  app.directive("click-outside", ClickOutSide);
-  app.mount("#app");
+
+  const local = getLocale()
+  loadLocaleMessages(i18n, local).then(() => {
+    setI18nLanguage(i18n, local)
+    const app = createApp(App);
+    app.use(i18n);
+    app.use(store);
+    app.use(Components);
+    app.directive("click-outside", ClickOutSide);
+    app.mount("#app");
+  })
+
+
 }

+ 1 - 1
src/store/index.js

@@ -8,7 +8,7 @@ const store = createStore({
             flying: false,
             floorId: null,
             player: {
-                lang: 'zh',
+                lang: 'zh_HK',
                 showVR: false,
                 showMore: false,
                 showDescription: false,