-
+
{{ $t('order.message.wechatPayHelp') }}
diff --git a/src/components/user/Locale.vue b/src/components/user/Locale.vue
index d4363d50..d5720353 100644
--- a/src/components/user/Locale.vue
+++ b/src/components/user/Locale.vue
@@ -18,12 +18,13 @@
import { defineComponent } from 'vue';
import { ElDropdown, ElDropdownMenu, ElDropdownItem, ElIcon } from 'element-plus';
import { ArrowDown } from '@element-plus/icons-vue';
-import { SUPPORTED_LOCALES, setI18nLanguage } from '@/i18n';
import { setCookie } from 'typescript-cookie';
import { getDomain } from '@/utils/initializer';
+import { I18N_SUPPORTED_LOCALES } from '@/constants/i18n';
+import { setI18nLanguage } from '@/i18n';
export default defineComponent({
- name: 'LocaleSelector',
+ name: 'Locale',
components: {
ElDropdown,
ElDropdownMenu,
@@ -33,7 +34,7 @@ export default defineComponent({
},
data() {
return {
- locales: SUPPORTED_LOCALES
+ locales: I18N_SUPPORTED_LOCALES
};
},
computed: {
diff --git a/src/constants/i18n.ts b/src/constants/i18n.ts
new file mode 100644
index 00000000..6d3f8e35
--- /dev/null
+++ b/src/constants/i18n.ts
@@ -0,0 +1,48 @@
+export const I18N_DEFAULT_LOCALE = 'en';
+
+export const I18N_SUPPORTED_LOCALES = [
+ { value: 'en', label: 'English' },
+ { value: 'de', label: 'Deutsch' },
+ { value: 'pt', label: 'Português' },
+ { value: 'es', label: 'Español' },
+ { value: 'fr', label: 'Français' },
+ { value: 'zh-CN', label: '简体中文' },
+ { value: 'zh-TW', label: '繁體中文' },
+ { value: 'it', label: 'Italiano' },
+ { value: 'ko', label: '한국어' },
+ { value: 'ja', label: '日本語' },
+ { value: 'ru', label: 'Русский' },
+ { value: 'pl', label: 'Polski' },
+ { value: 'fi', label: 'Suomi' },
+ { value: 'sv', label: 'Svenska' },
+ { value: 'el', label: 'Ελληνικά' },
+ { value: 'uk', label: 'Українська' },
+ { value: 'ar', label: 'العربية' },
+ { value: 'sr', label: 'Српски' }
+];
+
+export const I18N_SCOPES = [
+ 'index',
+ 'api',
+ 'application',
+ 'qrart',
+ 'luma',
+ 'pika',
+ 'kling',
+ 'flux',
+ 'hailuo',
+ 'headshots',
+ 'suno',
+ 'common',
+ 'console',
+ 'credential',
+ 'distribution',
+ 'order',
+ 'proxy',
+ 'service',
+ 'usage',
+ 'user',
+ 'site',
+ 'chat',
+ 'midjourney'
+];
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
index ff8090d3..36047ead 100644
--- a/src/i18n/index.ts
+++ b/src/i18n/index.ts
@@ -1,56 +1,16 @@
-import { createI18n, I18n } from 'vue-i18n';
-import { nextTick } from 'vue';
+import { createI18n } from 'vue-i18n';
+import { I18N_DEFAULT_LOCALE, I18N_SCOPES, I18N_SUPPORTED_LOCALES } from '@/constants/i18n';
import axios from 'axios';
+import { nextTick } from 'vue';
-declare namespace Intl {
- // eslint-disable-next-line no-unused-vars
- function getCanonicalLocales(locales: string | string[] | undefined): string[];
-}
-
-export const DEFAULT_LOCALE = 'en';
-
-export const SUPPORTED_LOCALES = [
- { value: 'en', label: 'English' },
- { value: 'de', label: 'Deutsch' },
- { value: 'pt', label: 'Português' },
- { value: 'es', label: 'Español' },
- { value: 'fr', label: 'Français' },
- { value: 'zh-CN', label: '简体中文' },
- { value: 'zh-TW', label: '繁體中文' },
- { value: 'it', label: 'Italiano' },
- { value: 'ko', label: '한국어' },
- { value: 'ja', label: '日本語' },
- { value: 'ru', label: 'Русский' },
- { value: 'pl', label: 'Polski' },
- { value: 'fi', label: 'Suomi' },
- { value: 'sv', label: 'Svenska' },
- { value: 'el', label: 'Ελληνικά' },
- { value: 'uk', label: 'Українська' },
- { value: 'ar', label: 'العربية' },
- { value: 'sr', label: 'Српски' }
-];
-
-export const setupI18n = () => {
- // @ts-ignore
- const i18n: I18n = createI18n({});
- return i18n;
-};
-
-export const i18n = setupI18n();
-
-export const loadLocalResource = async (name: string, locale: string) => {
- try {
- const module = await import(`./${locale}/${name}.json`);
- return module.default;
- } catch (error) {
- const module = await import(`./${DEFAULT_LOCALE}/${name}.json`);
- return module.default;
- }
-};
+export const i18n = createI18n({
+ legacy: true
+});
export const getLocale = (lang?: string): string => {
const canonicalLang = Intl.getCanonicalLocales(lang || navigator.language)?.[0];
- const supportedLocales = SUPPORTED_LOCALES.map((locale) => locale.value);
+ console.debug('canonicalLang', canonicalLang);
+ const supportedLocales = I18N_SUPPORTED_LOCALES.map((locale) => locale.value);
// if the canonical language is supported, use it
if (canonicalLang && supportedLocales.includes(canonicalLang)) {
console.debug('canonicalLang', canonicalLang);
@@ -63,41 +23,35 @@ export const getLocale = (lang?: string): string => {
return canonicalLangPrefix;
}
}
- return DEFAULT_LOCALE;
+ return I18N_DEFAULT_LOCALE;
};
-export const loadLocaleMessages = async (i18n: I18n, locale: string) => {
- const names = [
- 'index',
- 'api',
- 'application',
- 'qrart',
- 'luma',
- 'pika',
- 'kling',
- 'flux',
- 'hailuo',
- 'headshots',
- 'suno',
- 'common',
- 'console',
- 'credential',
- 'distribution',
- 'order',
- 'proxy',
- 'service',
- 'usage',
- 'user',
- 'site',
- 'chat',
- 'midjourney'
- ];
+const messageLoaders = import.meta.glob('./**/*.json');
- const promises = names.map((name) => loadLocalResource(name, locale));
+export const loadLocaleResource = async (name: string, locale: string) => {
+ console.debug('loadLocaleResource', name, locale);
+ const path = `./${locale}/${name}.json`;
+ const loader = messageLoaders[path];
+ if (!loader) {
+ console.warn(`[i18n] Missing translation file: ${path}`);
+ return;
+ }
+
+ const module: any = await loader();
+ console.debug('loaded module', module);
+ if (!module.default) {
+ console.warn(`[i18n] Missing default export in translation file: ${path}`);
+ return;
+ }
+ return module.default;
+};
+
+export const loadLocaleMessages = async (locale: string) => {
+ const promises = I18N_SCOPES.map((name) => loadLocaleResource(name, locale));
const resources = await Promise.all(promises);
const messages: any = {};
- names.forEach((name, index) => {
+ I18N_SCOPES.forEach((name, index) => {
const resource = resources[index];
for (const key in resource) {
if (resource.hasOwnProperty(key)) {
@@ -116,7 +70,7 @@ export const loadLocaleMessages = async (i18n: I18n, locale: string) => {
export const setI18nLanguage = async (locale: string) => {
// load locale messages
if (!i18n.global.availableLocales.includes(locale)) {
- await loadLocaleMessages(i18n, locale);
+ await loadLocaleMessages(locale);
}
// set global locale
diff --git a/src/pages/chat/Conversation.vue b/src/pages/chat/Conversation.vue
index bfa9bc4c..1e2e42fd 100644
--- a/src/pages/chat/Conversation.vue
+++ b/src/pages/chat/Conversation.vue
@@ -305,7 +305,6 @@ export default defineComponent({
},
async onRestoreConversation(id: string) {
console.debug('onRestoreConversation id', id);
- this.$router.push({ ...this.$route, params: { id } });
const conversation = this.conversations?.find((conversation: IChatConversation) => conversation.id === id);
// change the model and model group
const model = conversation?.model;
diff --git a/src/pages/distribution/Index.vue b/src/pages/distribution/Index.vue
index bcd2e504..a45d7927 100644
--- a/src/pages/distribution/Index.vue
+++ b/src/pages/distribution/Index.vue
@@ -152,6 +152,8 @@
:size="180"
:margin="2"
class="block mb-2 ml-auto mr-auto"
+ type="image/png"
+ :color="{ dark: '#000000', light: '#ffffff' }"
/>
{{ $t('distribution.message.distributionQrDescription') }}
diff --git a/src/pages/profile/Index.vue b/src/pages/profile/Index.vue
index 8c55c464..41a9870b 100644
--- a/src/pages/profile/Index.vue
+++ b/src/pages/profile/Index.vue
@@ -41,7 +41,6 @@ import {
} from '@/router';
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import { getBaseUrlAuth, getBaseUrlPlatform } from '@/utils';
-import LocaleSelector from '@/components/common/LocaleSelector.vue';
import HelpDialog from '@/components/common/HelpDialog.vue';
interface ILink {
@@ -58,8 +57,7 @@ export default defineComponent({
components: {
ElImage,
HelpDialog,
- FontAwesomeIcon,
- LocaleSelector
+ FontAwesomeIcon
},
data() {
return {
diff --git a/src/router/index.ts b/src/router/index.ts
index 09442dd6..c10f02e8 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -18,8 +18,9 @@ import site from './site';
import profile from './profile';
import { ROUTE_CHATGPT_CONVERSATION_NEW } from './constants';
-import { DEFAULT_LOCALE, setI18nLanguage } from '@/i18n';
import { getCookie } from 'typescript-cookie';
+import { I18N_DEFAULT_LOCALE } from '@/constants/i18n';
+import { getLocale, setI18nLanguage } from '@/i18n';
const routes = [
{
@@ -51,7 +52,7 @@ const router = createRouter({
});
router.beforeEach(async (_to, _from, next) => {
- const locale = getCookie('LOCALE') || DEFAULT_LOCALE;
+ const locale = getLocale(getCookie('LOCALE') || I18N_DEFAULT_LOCALE);
await setI18nLanguage(locale);
return next();
});
diff --git a/src/shims.d.ts b/src/shims.d.ts
index 92081c14..0a3c025c 100644
--- a/src/shims.d.ts
+++ b/src/shims.d.ts
@@ -6,7 +6,7 @@
import { ComponentCustomProperties } from 'vue';
import { Router } from 'vue-router';
-import { I18n } from 'vue-i18n';
+import { I18n, VueI18n } from 'vue-i18n';
import { Store } from 'vuex';
import { IRootState } from './store/common/models';
import type { Router, RouteLocationNormalizedLoaded } from 'vue-router';
@@ -17,10 +17,7 @@ declare module '*.ico';
declare module '*.css';
declare module '*.scss';
declare module '*.png';
-declare module '*.tpl' {
- const value: string;
- export default value;
-}
+declare module '*.tpl';
declare module '*.vue' {
import { DefineComponent } from 'vue';
@@ -28,9 +25,14 @@ declare module '*.vue' {
export default component;
}
+// declare namespace Intl {
+// function getCanonicalLocales(locales: string | string[] | undefined): string[];
+// }
+
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
- $t: (key: string, ...args: unknown[]) => string;
+ $t: VueI18n['t'];
+ $i18n: VueI18n;
$store: Store;
$router: Router;
$route: RouteLocationNormalizedLoaded;
diff --git a/src/utils/initializer.ts b/src/utils/initializer.ts
index 6c1884d0..aa749891 100644
--- a/src/utils/initializer.ts
+++ b/src/utils/initializer.ts
@@ -1,12 +1,12 @@
import { getCookie, setCookie } from 'typescript-cookie';
import favicon from '@/assets/images/favicon.ico';
-import { getLocale } from '@/i18n';
import { applyTheme } from './theme';
import store from '@/store';
import { IToken } from '@/models';
import { parse } from 'psl';
import { BASE_HOST_HUB, LOCALE_CURRENCY_MAPPING } from '@/constants';
import { isOfficial, isSubOfficial, isWechatBrowser } from './is';
+import { getLocale } from '@/i18n';
export const getDomain = (host: string = window.location.hostname) => {
const parsed = parse(host);
diff --git a/yarn.lock b/yarn.lock
index bf279777..d09c1999 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -503,26 +503,26 @@
resolved "https://registry.yarnpkg.com/@icon-park/vue-next/-/vue-next-1.4.2.tgz#818c048100401620e8112beb2636f10171b195cc"
integrity sha512-+QklF255wkfBOabY+xw6FAI0Bwln/RhdwCunNy/9sKdKuChtaU67QZqU67KGAvZUTeeBgsL+yaHHxqfQeGZXEQ==
-"@intlify/core-base@9.14.5":
- version "9.14.5"
- resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.14.5.tgz#cfed631e98b34f4f6fe1312e6c02dd8ace180197"
- integrity sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==
+"@intlify/core-base@11.1.11":
+ version "11.1.11"
+ resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-11.1.11.tgz#e36893a7d37a3a75fae30977fc58c1d8cf3c853f"
+ integrity sha512-1Z0N8jTfkcD2Luq9HNZt+GmjpFe4/4PpZF3AOzoO1u5PTtSuXZcfhwBatywbfE2ieB/B5QHIoOFmCXY2jqVKEQ==
dependencies:
- "@intlify/message-compiler" "9.14.5"
- "@intlify/shared" "9.14.5"
+ "@intlify/message-compiler" "11.1.11"
+ "@intlify/shared" "11.1.11"
-"@intlify/message-compiler@9.14.5":
- version "9.14.5"
- resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.14.5.tgz#03b0a358c93cb2eed95936531c8a5871fd3841b0"
- integrity sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==
+"@intlify/message-compiler@11.1.11":
+ version "11.1.11"
+ resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-11.1.11.tgz#ba10641f86af0e991ac9def0385bd345c8f150fb"
+ integrity sha512-7PC6neomoc/z7a8JRjPBbu0T2TzR2MQuY5kn2e049MP7+o32Ve7O8husylkA7K9fQRe4iNXZWTPnDJ6vZdtS1Q==
dependencies:
- "@intlify/shared" "9.14.5"
+ "@intlify/shared" "11.1.11"
source-map-js "^1.0.2"
-"@intlify/shared@9.14.5":
- version "9.14.5"
- resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.14.5.tgz#2e42193b8356db20ee28c15d1ee6c8f0fda6c0b9"
- integrity sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==
+"@intlify/shared@11.1.11":
+ version "11.1.11"
+ resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-11.1.11.tgz#6bba3b86617c05767356e4ca939c9e300563a083"
+ integrity sha512-RIBFTIqxZSsxUqlcyoR7iiC632bq7kkOwYvZlvcVObHfrF4NhuKc4FKvu8iPCrEO+e3XsY7/UVpfgzg+M7ETzA==
"@ionic/cli-framework-output@^2.2.5":
version "2.2.8"
@@ -5541,13 +5541,13 @@ vue-eslint-parser@^8.0.1:
lodash "^4.17.21"
semver "^7.3.5"
-vue-i18n@^9.1.7:
- version "9.14.5"
- resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.14.5.tgz#72fbf4384b83a1c59ec9e01ff5d30224bd9150cf"
- integrity sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==
+vue-i18n@^11.1.11:
+ version "11.1.11"
+ resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-11.1.11.tgz#b38ed214896540cf7a68932dfa565d9d4fbbffac"
+ integrity sha512-LvyteQoXeQiuILbzqv13LbyBna/TEv2Ha+4ZWK2AwGHUzZ8+IBaZS0TJkCgn5izSPLcgZwXy9yyTrewCb2u/MA==
dependencies:
- "@intlify/core-base" "9.14.5"
- "@intlify/shared" "9.14.5"
+ "@intlify/core-base" "11.1.11"
+ "@intlify/shared" "11.1.11"
"@vue/devtools-api" "^6.5.0"
vue-qrcode@^2.2.0: