@liujitcn/kratos-uni-app-core 0.0.21 → 0.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/module.mjs +653 -670
- package/dist/vite.mjs +5 -3
- package/package.json +1 -1
- package/src/api/base/language.ts +5 -5
- package/src/bootstrap.ts +1 -1
- package/src/locales/README.md +2 -2
- package/src/locales/en-US.json +65 -68
- package/src/locales/es-ES.json +88 -91
- package/src/locales/fr-FR.json +92 -95
- package/src/locales/generated.ts +22 -0
- package/src/locales/index.ts +48 -67
- package/src/locales/ja-JP.json +78 -81
- package/src/locales/ko-KR.json +87 -90
- package/src/locales/zh-CN.json +65 -68
- package/src/locales/zh-TW.json +65 -68
- package/src/module.ts +3 -17
- package/src/navigation.ts +70 -13
- package/src/rpc/base/v1/config.ts +12 -1
- package/src/rpc/base/v1/language.ts +7 -13
- package/src/rpc/base/v1/login.ts +14 -1
- package/src/stores/modules/setting.ts +3 -3
- package/src/utils/file.ts +2 -2
- package/src/utils/http.ts +5 -5
- package/src/utils/passwordCrypto.ts +14 -14
- package/src/views/pages/index/index.vue +12 -12
- package/src/views/pages/login/login.vue +47 -33
- package/src/views/pages/login/protocal.vue +2 -2
- package/src/views/pages/status/index.vue +9 -8
- package/src/views/pages/webview/webview.vue +4 -4
- package/src/vite.ts +4 -3
- package/src/rpc/base/v1/enum.ts +0 -54
- package/src/rpc/system/common/v1/enum.ts +0 -80
package/src/locales/fr-FR.json
CHANGED
|
@@ -1,110 +1,107 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common.action.cancel": "Annuler",
|
|
3
|
-
"common.action.confirm": "
|
|
3
|
+
"common.action.confirm": "Confirmer",
|
|
4
4
|
"common.action.delete": "Supprimer",
|
|
5
|
-
"common.action.login": "
|
|
5
|
+
"common.action.login": "Se connecter",
|
|
6
6
|
"common.action.logout": "Se déconnecter",
|
|
7
7
|
"common.action.new": "Nouveau",
|
|
8
|
-
"common.action.
|
|
9
|
-
"common.action.retry": "Réessayez",
|
|
8
|
+
"common.action.open_in_new_window": "Ouvrir dans une nouvelle fenêtre",
|
|
10
9
|
"common.action.save": "Enregistrer",
|
|
11
|
-
"common.action.search": "Rechercher",
|
|
12
10
|
"common.field.language": "Langue",
|
|
13
11
|
"common.language.en-US": "Anglais",
|
|
14
12
|
"common.language.ja-JP": "Japonais",
|
|
15
13
|
"common.language.zh-CN": "Chinois simplifié",
|
|
16
14
|
"common.language.zh-TW": "Chinois traditionnel",
|
|
17
|
-
"common.language.ko-KR": "
|
|
15
|
+
"common.language.ko-KR": "Coréen",
|
|
18
16
|
"common.language.fr-FR": "Français",
|
|
19
|
-
"common.language.es-ES": "
|
|
20
|
-
"common.message.loading": "
|
|
21
|
-
"common.message.
|
|
22
|
-
"common.message.
|
|
23
|
-
"common.message.unknown": "
|
|
24
|
-
"common.title.notice": "
|
|
25
|
-
"core.auth.
|
|
26
|
-
"core.auth.
|
|
27
|
-
"core.config.missing": "
|
|
28
|
-
"core.crypto.
|
|
29
|
-
"core.crypto.
|
|
30
|
-
"core.crypto.
|
|
31
|
-
"core.crypto.
|
|
32
|
-
"core.crypto.
|
|
33
|
-
"core.crypto.
|
|
34
|
-
"core.crypto.
|
|
35
|
-
"core.crypto.unsupported": "
|
|
36
|
-
"core.file.
|
|
37
|
-
"core.home.
|
|
38
|
-
"core.home.
|
|
39
|
-
"core.home.
|
|
40
|
-
"core.home.
|
|
41
|
-
"core.home.
|
|
42
|
-
"core.home.framework": "
|
|
43
|
-
"core.home.language": "Langage
|
|
44
|
-
"core.home.
|
|
45
|
-
"core.home.
|
|
46
|
-
"core.home.
|
|
47
|
-
"core.home.
|
|
48
|
-
"core.home.
|
|
49
|
-
"core.home.
|
|
50
|
-
"core.home.
|
|
51
|
-
"core.login.
|
|
52
|
-
"core.login.
|
|
53
|
-
"core.login.
|
|
54
|
-
"core.login.
|
|
55
|
-
"core.login.
|
|
56
|
-
"core.login.
|
|
57
|
-
"core.login.
|
|
58
|
-
"core.login.captcha": "
|
|
59
|
-
"core.login.
|
|
60
|
-
"core.login.
|
|
61
|
-
"core.login.
|
|
62
|
-
"core.login.
|
|
63
|
-
"core.login.
|
|
64
|
-
"core.login.
|
|
65
|
-
"core.login.
|
|
66
|
-
"core.login.
|
|
67
|
-
"core.login.
|
|
68
|
-
"core.login.
|
|
69
|
-
"core.login.
|
|
70
|
-
"core.login.
|
|
71
|
-
"core.login.
|
|
72
|
-
"core.login.
|
|
73
|
-
"core.login.
|
|
74
|
-
"core.login.
|
|
75
|
-
"core.login.
|
|
76
|
-
"core.login.
|
|
77
|
-
"core.login.
|
|
78
|
-
"core.login.wechatFailed": "La connexion à WeChat a échoué, veuillez réessayer plus tard.",
|
|
17
|
+
"common.language.es-ES": "Espagnol",
|
|
18
|
+
"common.message.loading": "Chargement",
|
|
19
|
+
"common.message.network_error": "Erreur réseau. Vérifiez votre connexion et réessayez.",
|
|
20
|
+
"common.message.request_error": "Échec de la requête",
|
|
21
|
+
"common.message.unknown": "Impossible de traiter cette demande. Réessayez plus tard.",
|
|
22
|
+
"common.title.notice": "Avis",
|
|
23
|
+
"core.auth.login_again": "Se reconnecter",
|
|
24
|
+
"core.auth.session_expired": "Votre session a expiré. Veuillez vous reconnecter.",
|
|
25
|
+
"core.config.missing": "Configuration de l’application manquante : {names}",
|
|
26
|
+
"core.crypto.base64_decode_unsupported": "Le décodage Base64 n’est pas pris en charge dans cet environnement",
|
|
27
|
+
"core.crypto.base64_encode_unsupported": "L’encodage Base64 n’est pas pris en charge dans cet environnement",
|
|
28
|
+
"core.crypto.encrypt_failed": "Échec du chiffrement du mot de passe",
|
|
29
|
+
"core.crypto.password_required": "Le mot de passe est obligatoire",
|
|
30
|
+
"core.crypto.password_too_long": "Le mot de passe dépasse la longueur autorisée",
|
|
31
|
+
"core.crypto.public_key_invalid": "Clé publique non valide",
|
|
32
|
+
"core.crypto.random_failed": "Échec de la génération de données aléatoires",
|
|
33
|
+
"core.crypto.unsupported": "Le chiffrement du mot de passe n’est pas pris en charge dans cet environnement",
|
|
34
|
+
"core.file.upload_failed": "Échec de l’envoi",
|
|
35
|
+
"core.home.account_capability": "Fonctionnalités du compte",
|
|
36
|
+
"core.home.account_capability_description": "Le profil, les paramètres de l’application et l’accès à l’assistant IA sont inclus",
|
|
37
|
+
"core.home.cross_platform": "Pages multiplateformes",
|
|
38
|
+
"core.home.cross_platform_description": "Une seule base de code pour H5 et le mini-programme WeChat",
|
|
39
|
+
"core.home.demo_scope": "Périmètre de démonstration",
|
|
40
|
+
"core.home.framework": "Framework de l’application",
|
|
41
|
+
"core.home.language": "Langage",
|
|
42
|
+
"core.home.main_title": "Exemple de framework d’application",
|
|
43
|
+
"core.home.state_management": "Gestion de l’état",
|
|
44
|
+
"core.home.static_home": "Accueil statique",
|
|
45
|
+
"core.home.static_home_description": "Cette page présente le framework sans utiliser d’API métier",
|
|
46
|
+
"core.home.style_solution": "Styles",
|
|
47
|
+
"core.home.sub_title": "Navigation partagée et expérience de compte",
|
|
48
|
+
"core.home.tech_stack": "Stack technique",
|
|
49
|
+
"core.login.agree_prefix": "J’ai lu et j’accepte",
|
|
50
|
+
"core.login.agree_separator": "et",
|
|
51
|
+
"core.login.behavior_click": "Cliquez sur les caractères dans l'ordre",
|
|
52
|
+
"core.login.behavior_loading": "Chargement...",
|
|
53
|
+
"core.login.behavior_puzzle": "Faites glisser le curseur pour terminer le puzzle",
|
|
54
|
+
"core.login.behavior_rotate": "Faites glisser le curseur pour remettre l'image intérieure à l'endroit",
|
|
55
|
+
"core.login.bind_wechat": "Se connecter et lier WeChat",
|
|
56
|
+
"core.login.captcha": "Saisissez le code de vérification",
|
|
57
|
+
"core.login.captcha_load_failed": "Échec du chargement du code de vérification",
|
|
58
|
+
"core.login.captcha_type_missing": "La méthode de vérification n’est pas configurée",
|
|
59
|
+
"core.login.captcha_type_name": "Méthode de vérification de connexion",
|
|
60
|
+
"core.login.captcha_type_response_missing": "La réponse de vérification ne contient aucun type",
|
|
61
|
+
"core.login.captcha_write_failed": "Échec de l’enregistrement de l’image de vérification",
|
|
62
|
+
"core.login.default_sub_title": "Bienvenue",
|
|
63
|
+
"core.login.login_success": "Connexion réussie",
|
|
64
|
+
"core.login.password": "Saisissez le mot de passe",
|
|
65
|
+
"core.login.privacy": "Politique de confidentialité",
|
|
66
|
+
"core.login.protocol_missing": "Les conditions d’utilisation et la politique de confidentialité ne sont pas configurées",
|
|
67
|
+
"core.login.protocol_prompt": "Lisez et acceptez d’abord les accords. Confirmez pour accepter et continuer.",
|
|
68
|
+
"core.login.service": "Conditions d’utilisation",
|
|
69
|
+
"core.login.tenant": "Saisissez le code du locataire",
|
|
70
|
+
"core.login.user_name": "Saisissez le nom d'utilisateur",
|
|
71
|
+
"core.login.user_name_mobile": "Saisissez le nom d’utilisateur ou le numéro de mobile",
|
|
72
|
+
"core.login.user_name_password": "Saisissez le nom d’utilisateur et le mot de passe",
|
|
73
|
+
"core.login.wechat": "Continuer avec WeChat",
|
|
74
|
+
"core.login.wechat_bind_tip": "Ce compte WeChat n’est pas lié. Connectez-vous à un compte existant pour le lier.",
|
|
75
|
+
"core.login.wechat_failed": "Échec de la connexion WeChat. Réessayez plus tard.",
|
|
79
76
|
"core.navigation.home": "Accueil",
|
|
80
|
-
"core.navigation.error.access": "Mode d
|
|
81
|
-
"core.navigation.error.
|
|
82
|
-
"core.navigation.error.cycle": "
|
|
83
|
-
"core.navigation.error.identity": "
|
|
84
|
-
"core.navigation.error.
|
|
85
|
-
"core.navigation.error.
|
|
86
|
-
"core.navigation.error.
|
|
87
|
-
"core.navigation.error.
|
|
88
|
-
"core.navigation.error.
|
|
89
|
-
"core.navigation.error.
|
|
90
|
-
"core.navigation.error.
|
|
91
|
-
"core.navigation.error.
|
|
92
|
-
"core.navigation.error.
|
|
93
|
-
"core.navigation.error.unique": "L
|
|
94
|
-
"core.navigation.error.
|
|
95
|
-
"core.navigation.my": "
|
|
96
|
-
"core.navigation.protocol": "
|
|
97
|
-
"core.protocol.
|
|
98
|
-
"core.protocol.
|
|
99
|
-
"core.protocol.privacy": "
|
|
100
|
-
"core.protocol.service": "Conditions d
|
|
77
|
+
"core.navigation.error.access": "Mode d’accès au menu invalide : {access}",
|
|
78
|
+
"core.navigation.error.child_tab": "Seuls les menus d’application de deuxième niveau peuvent être des onglets : {name}",
|
|
79
|
+
"core.navigation.error.cycle": "Cycle de parenté détecté dans le menu : {name}",
|
|
80
|
+
"core.navigation.error.identity": "L’identifiant, le nom ou le chemin app/ du menu est invalide",
|
|
81
|
+
"core.navigation.error.item_object": "L’élément de menu n’est pas un objet",
|
|
82
|
+
"core.navigation.error.list_missing": "La réponse du menu ne contient pas de liste plate",
|
|
83
|
+
"core.navigation.error.name_prefix": "Le nom du menu doit commencer par App : {name}",
|
|
84
|
+
"core.navigation.error.parent_invalid": "L’identifiant du parent du menu est invalide : {name}",
|
|
85
|
+
"core.navigation.error.parent_missing": "L’identifiant du parent du menu est manquant : {name}",
|
|
86
|
+
"core.navigation.error.parent_not_found": "Le parent du menu est introuvable : {name}",
|
|
87
|
+
"core.navigation.error.response_object": "La réponse du menu n’est pas un objet",
|
|
88
|
+
"core.navigation.error.root_tab": "Le menu d’application de deuxième niveau doit être un onglet : {name}",
|
|
89
|
+
"core.navigation.error.tab_count": "tabBar doit contenir 0 ou 2 à 5 éléments",
|
|
90
|
+
"core.navigation.error.unique": "L’identifiant, le nom et le chemin du menu doivent être uniques",
|
|
91
|
+
"core.navigation.error.view_key": "viewKey non enregistré : {viewKey}",
|
|
92
|
+
"core.navigation.my": "Mon compte",
|
|
93
|
+
"core.navigation.protocol": "Accord",
|
|
94
|
+
"core.protocol.load_failed": "Échec du chargement de la configuration de l’application",
|
|
95
|
+
"core.protocol.not_configured": "{title} n’est pas configuré et ne peut pas être consulté",
|
|
96
|
+
"core.protocol.privacy": "Politique de confidentialité",
|
|
97
|
+
"core.protocol.service": "Conditions d’utilisation",
|
|
101
98
|
"core.status.CONFIG_ERROR": "Configuration de navigation invalide",
|
|
102
|
-
"core.status.
|
|
103
|
-
"core.status.NOT_FOUND": "
|
|
104
|
-
"core.status.
|
|
105
|
-
"core.status.
|
|
106
|
-
"core.status.loading": "
|
|
107
|
-
"core.webview.
|
|
108
|
-
"core.webview.
|
|
109
|
-
"core.webview.
|
|
99
|
+
"core.status.forbidden": "Accès refusé",
|
|
100
|
+
"core.status.NOT_FOUND": "Page introuvable",
|
|
101
|
+
"core.status.offline": "Réseau indisponible",
|
|
102
|
+
"core.status.page_unavailable": "Page temporairement indisponible",
|
|
103
|
+
"core.status.loading": "Chargement",
|
|
104
|
+
"core.webview.embed_blocked": "La destination peut bloquer l’intégration sur H5",
|
|
105
|
+
"core.webview.invalid_url": "Aucun lien valide n’a été fourni",
|
|
106
|
+
"core.webview.open_failed": "Impossible d’ouvrir le lien"
|
|
110
107
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/* 此文件由 scripts/sync_locales.py 生成,请勿手工修改。 */
|
|
2
|
+
import localeZhCn from './zh-CN.json'
|
|
3
|
+
import localeEnUs from './en-US.json'
|
|
4
|
+
import localeEsEs from './es-ES.json'
|
|
5
|
+
import localeFrFr from './fr-FR.json'
|
|
6
|
+
import localeJaJp from './ja-JP.json'
|
|
7
|
+
import localeKoKr from './ko-KR.json'
|
|
8
|
+
import localeZhTw from './zh-TW.json'
|
|
9
|
+
|
|
10
|
+
export const LOCALE_MESSAGES = {
|
|
11
|
+
'zh-CN': localeZhCn,
|
|
12
|
+
'en-US': localeEnUs,
|
|
13
|
+
'es-ES': localeEsEs,
|
|
14
|
+
'fr-FR': localeFrFr,
|
|
15
|
+
'ja-JP': localeJaJp,
|
|
16
|
+
'ko-KR': localeKoKr,
|
|
17
|
+
'zh-TW': localeZhTw,
|
|
18
|
+
} as const satisfies Record<string, Record<string, string>>
|
|
19
|
+
|
|
20
|
+
export type GeneratedLocale = keyof typeof LOCALE_MESSAGES
|
|
21
|
+
export const DEFAULT_LOCALE: GeneratedLocale = 'zh-CN'
|
|
22
|
+
export const SUPPORTED_LOCALES = Object.keys(LOCALE_MESSAGES) as GeneratedLocale[]
|
package/src/locales/index.ts
CHANGED
|
@@ -4,20 +4,17 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { readonly, ref } from 'vue'
|
|
6
6
|
import type { KratosAppModule } from '../module'
|
|
7
|
-
import type {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
'fr-FR',
|
|
17
|
-
'es-ES',
|
|
18
|
-
] as const
|
|
7
|
+
import type { OptionLanguageResponse } from '../rpc/base/v1/language'
|
|
8
|
+
import {
|
|
9
|
+
DEFAULT_LOCALE as GENERATED_DEFAULT_LOCALE,
|
|
10
|
+
SUPPORTED_LOCALES as GENERATED_SUPPORTED_LOCALES,
|
|
11
|
+
type GeneratedLocale,
|
|
12
|
+
} from './generated'
|
|
13
|
+
|
|
14
|
+
/** 移动端已打包的语言区域;运行时可切换列表由 base_language 接口决定。 */
|
|
15
|
+
export const SUPPORTED_LOCALES = GENERATED_SUPPORTED_LOCALES
|
|
19
16
|
/** 移动端支持的语言区域类型。 */
|
|
20
|
-
export type SupportedLocale =
|
|
17
|
+
export type SupportedLocale = GeneratedLocale
|
|
21
18
|
/** 单个模块的扁平语言包。 */
|
|
22
19
|
export type LocaleMessages = Record<string, string>
|
|
23
20
|
/** 翻译插值参数。 */
|
|
@@ -27,15 +24,11 @@ export type LocaleParams = Record<string, string | number>
|
|
|
27
24
|
export interface LocaleOption {
|
|
28
25
|
/** 标准语言代码。 */
|
|
29
26
|
language_code: SupportedLocale
|
|
30
|
-
/** 语言名称。 */
|
|
31
|
-
language_name: string
|
|
32
27
|
/** 本地语言名称。 */
|
|
33
28
|
native_name: string
|
|
34
|
-
/** 排序值。 */
|
|
35
|
-
sort: number
|
|
36
29
|
}
|
|
37
30
|
|
|
38
|
-
const DEFAULT_LOCALE: SupportedLocale =
|
|
31
|
+
const DEFAULT_LOCALE: SupportedLocale = GENERATED_DEFAULT_LOCALE
|
|
39
32
|
const LOCALE_STORAGE_KEY = 'kratos-app:locale'
|
|
40
33
|
const localeMessages = new Map<SupportedLocale, LocaleMessages>()
|
|
41
34
|
const localeChangeHandlers = new Set<() => void | Promise<void>>()
|
|
@@ -47,21 +40,7 @@ export const localeState = readonly(mutableLocaleState)
|
|
|
47
40
|
|
|
48
41
|
/** 规范化语言区域到应用白名单。 */
|
|
49
42
|
export function normalizeLocale(value?: string): SupportedLocale {
|
|
50
|
-
|
|
51
|
-
.replace('_', '-')
|
|
52
|
-
.toLowerCase()
|
|
53
|
-
if (normalized.startsWith('ja')) return 'ja-JP'
|
|
54
|
-
if (normalized.startsWith('en')) return 'en-US'
|
|
55
|
-
if (
|
|
56
|
-
normalized.startsWith('zh-tw') ||
|
|
57
|
-
normalized.startsWith('zh-hk') ||
|
|
58
|
-
normalized.startsWith('zh-mo')
|
|
59
|
-
)
|
|
60
|
-
return 'zh-TW'
|
|
61
|
-
if (normalized.startsWith('ko')) return 'ko-KR'
|
|
62
|
-
if (normalized.startsWith('fr')) return 'fr-FR'
|
|
63
|
-
if (normalized.startsWith('es')) return 'es-ES'
|
|
64
|
-
return DEFAULT_LOCALE
|
|
43
|
+
return parseSupportedLocale(value) ?? DEFAULT_LOCALE
|
|
65
44
|
}
|
|
66
45
|
|
|
67
46
|
/** 将接口或系统语言代码解析为已打包的语言区域。 */
|
|
@@ -69,19 +48,13 @@ function parseSupportedLocale(value?: string): SupportedLocale | undefined {
|
|
|
69
48
|
const normalized = String(value || '')
|
|
70
49
|
.replace('_', '-')
|
|
71
50
|
.toLowerCase()
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
normalized.startsWith('zh-hk') ||
|
|
75
|
-
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (normalized.startsWith('ja')) return 'ja-JP'
|
|
80
|
-
if (normalized.startsWith('en')) return 'en-US'
|
|
81
|
-
if (normalized.startsWith('ko')) return 'ko-KR'
|
|
82
|
-
if (normalized.startsWith('fr')) return 'fr-FR'
|
|
83
|
-
if (normalized.startsWith('es')) return 'es-ES'
|
|
84
|
-
return undefined
|
|
51
|
+
if (!normalized) return undefined
|
|
52
|
+
const alias =
|
|
53
|
+
normalized.startsWith('zh-hk') || normalized.startsWith('zh-mo') ? 'zh-tw' : normalized
|
|
54
|
+
const exact = SUPPORTED_LOCALES.find((locale) => locale.toLowerCase() === alias)
|
|
55
|
+
if (exact) return exact
|
|
56
|
+
const language = alias.split('-', 1)[0]
|
|
57
|
+
return SUPPORTED_LOCALES.find((locale) => locale.toLowerCase().split('-', 1)[0] === language)
|
|
85
58
|
}
|
|
86
59
|
|
|
87
60
|
/** 初始化持久化语言偏好。 */
|
|
@@ -93,29 +66,21 @@ export function initializeLocale(): SupportedLocale {
|
|
|
93
66
|
return mutableLocaleState.value
|
|
94
67
|
}
|
|
95
68
|
|
|
96
|
-
/**
|
|
97
|
-
export function applyLanguageConfig(response:
|
|
69
|
+
/** 应用后端语言配置,并在当前语言不可用时回退到接口第一项。 */
|
|
70
|
+
export function applyLanguageConfig(response: OptionLanguageResponse): void {
|
|
98
71
|
const options = response.languages.reduce<LocaleOption[]>((items, item) => {
|
|
99
72
|
const languageCode = parseSupportedLocale(item.language_code)
|
|
100
73
|
if (!languageCode || items.some((option) => option.language_code === languageCode)) return items
|
|
101
74
|
items.push({
|
|
102
75
|
language_code: languageCode,
|
|
103
|
-
|
|
104
|
-
native_name: item.native_name || item.language_name || languageCode,
|
|
105
|
-
sort: item.sort,
|
|
76
|
+
native_name: item.native_name || languageCode,
|
|
106
77
|
})
|
|
107
78
|
return items
|
|
108
79
|
}, [])
|
|
109
|
-
mutableLanguageOptions.value = options.length
|
|
110
|
-
? options.sort((left, right) => left.sort - right.sort)
|
|
111
|
-
: getFallbackLanguageOptions()
|
|
80
|
+
mutableLanguageOptions.value = options.length ? options : getFallbackLanguageOptions()
|
|
112
81
|
const availableLocales = getSupportedLocales()
|
|
113
|
-
const primaryLocale = parseSupportedLocale(response.primary_language_code)
|
|
114
82
|
if (!availableLocales.includes(mutableLocaleState.value)) {
|
|
115
|
-
const locale =
|
|
116
|
-
primaryLocale && availableLocales.includes(primaryLocale)
|
|
117
|
-
? primaryLocale
|
|
118
|
-
: (availableLocales[0] ?? DEFAULT_LOCALE)
|
|
83
|
+
const locale = availableLocales[0] ?? DEFAULT_LOCALE
|
|
119
84
|
mutableLocaleState.value = locale
|
|
120
85
|
if (typeof uni.setLocale === 'function') uni.setLocale(locale)
|
|
121
86
|
}
|
|
@@ -133,21 +98,21 @@ export function getSupportedLocales(): SupportedLocale[] {
|
|
|
133
98
|
return getLanguageOptions().map((item) => item.language_code)
|
|
134
99
|
}
|
|
135
100
|
|
|
136
|
-
/**
|
|
101
|
+
/** 注册所有模块贡献的语言包并校验语言键集合。 */
|
|
137
102
|
export function registerLocaleMessages(modules: KratosAppModule[]): void {
|
|
138
103
|
localeMessages.clear()
|
|
139
104
|
SUPPORTED_LOCALES.forEach((locale) => localeMessages.set(locale, {}))
|
|
140
105
|
modules.forEach((module) => {
|
|
141
|
-
const expectedKeys =
|
|
106
|
+
const expectedKeys = requiredLocaleKeys(module.messages?.[DEFAULT_LOCALE] || {})
|
|
142
107
|
SUPPORTED_LOCALES.forEach((locale) => {
|
|
143
108
|
const messages = module.messages?.[locale]
|
|
144
109
|
if (!messages) throw new Error(`${module.name} 缺少 ${locale} 语言包`)
|
|
145
|
-
const keys =
|
|
110
|
+
const keys = requiredLocaleKeys(messages)
|
|
146
111
|
if (keys.join('\u0000') !== expectedKeys.join('\u0000')) {
|
|
147
112
|
throw new Error(`${module.name} 的 ${locale} 语言包键集合不一致`)
|
|
148
113
|
}
|
|
149
114
|
const target = localeMessages.get(locale) as LocaleMessages
|
|
150
|
-
keys.forEach((key) => {
|
|
115
|
+
Object.keys(messages).forEach((key) => {
|
|
151
116
|
if (!key.startsWith('common.') && !key.startsWith('core.') && !key.startsWith('system.')) {
|
|
152
117
|
throw new Error(`${module.name} 的语言键命名空间无效: ${key}`)
|
|
153
118
|
}
|
|
@@ -225,11 +190,27 @@ function assertLocalePlaceholders(
|
|
|
225
190
|
}
|
|
226
191
|
}
|
|
227
192
|
|
|
193
|
+
function requiredLocaleKeys(messages: LocaleMessages): string[] {
|
|
194
|
+
return Object.keys(messages)
|
|
195
|
+
.filter((key) => !key.startsWith('common.language.'))
|
|
196
|
+
.sort()
|
|
197
|
+
}
|
|
198
|
+
|
|
228
199
|
function getFallbackLanguageOptions(): LocaleOption[] {
|
|
229
|
-
return SUPPORTED_LOCALES.map((languageCode
|
|
200
|
+
return SUPPORTED_LOCALES.map((languageCode) => ({
|
|
230
201
|
language_code: languageCode,
|
|
231
|
-
|
|
232
|
-
native_name: languageCode,
|
|
233
|
-
sort,
|
|
202
|
+
native_name: fallbackNativeLanguageName(languageCode),
|
|
234
203
|
}))
|
|
235
204
|
}
|
|
205
|
+
|
|
206
|
+
function fallbackNativeLanguageName(languageCode: SupportedLocale): string {
|
|
207
|
+
return {
|
|
208
|
+
'zh-CN': '简体中文',
|
|
209
|
+
'zh-TW': '繁體中文',
|
|
210
|
+
'en-US': 'English',
|
|
211
|
+
'ja-JP': '日本語',
|
|
212
|
+
'ko-KR': '한국어',
|
|
213
|
+
'fr-FR': 'Français',
|
|
214
|
+
'es-ES': 'Español',
|
|
215
|
+
}[languageCode]
|
|
216
|
+
}
|