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