@liujitcn/kratos-taro-app-core 0.0.19 → 0.0.21

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.
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Taro 国际化运行时:合并 core 和业务模块语言包,并向 React 页面、请求工具暴露统一翻译入口。
3
+ * 各语言 JSON 由模块定义文件导入,不能在 JSON 文件内添加注释。
4
+ */
5
+ import Taro from '@tarojs/taro'
6
+ import { create } from 'zustand'
7
+ import type { KratosTaroModule } from '../module'
8
+ import type { GetLanguageResponse } from '../rpc/base/v1/language'
9
+
10
+ /** Taro 端支持的语言区域。 */
11
+ export const SUPPORTED_LOCALES = ['zh-CN', 'zh-TW', 'en-US', 'ja-JP', 'ko-KR', 'fr-FR', 'es-ES'] as const
12
+ /** Taro 端支持的语言区域类型。 */
13
+ export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number]
14
+ /** 单个模块的扁平语言包。 */
15
+ export type LocaleMessages = Record<string, string>
16
+ /** 翻译插值参数。 */
17
+ export type LocaleParams = Record<string, string | number>
18
+
19
+ /** 运行时语言选项。 */
20
+ export interface LocaleOption {
21
+ /** 标准语言代码。 */
22
+ language_code: SupportedLocale
23
+ /** 语言名称。 */
24
+ language_name: string
25
+ /** 本地语言名称。 */
26
+ native_name: string
27
+ /** 排序值。 */
28
+ sort: number
29
+ }
30
+
31
+ /** Taro 语言状态。 */
32
+ export interface LocaleStoreState {
33
+ locale: SupportedLocale
34
+ languageOptions: LocaleOption[]
35
+ supportedLocales: SupportedLocale[]
36
+ }
37
+
38
+ const DEFAULT_LOCALE: SupportedLocale = 'zh-CN'
39
+ const LOCALE_STORAGE_KEY = 'kratos-app:locale'
40
+ const localeMessages = new Map<SupportedLocale, LocaleMessages>()
41
+ const localeChangeHandlers = new Set<() => void | Promise<void>>()
42
+
43
+ /** 响应式语言 Zustand Store。 */
44
+ export const useLocaleStore = create<LocaleStoreState>(() => ({
45
+ locale: DEFAULT_LOCALE,
46
+ languageOptions: [],
47
+ supportedLocales: [...SUPPORTED_LOCALES],
48
+ }))
49
+
50
+ /** 规范化语言区域到应用白名单。 */
51
+ export function normalizeLocale(value?: string): SupportedLocale {
52
+ const normalized = String(value || '').replace('_', '-').toLowerCase()
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
60
+ }
61
+
62
+ /** 将接口或系统语言代码解析为已打包的语言区域。 */
63
+ function parseSupportedLocale(value?: string): SupportedLocale | undefined {
64
+ const normalized = String(value || '').replace('_', '-').toLowerCase()
65
+ if (normalized.startsWith('zh-tw') || normalized.startsWith('zh-hk') || normalized.startsWith('zh-mo')) return 'zh-TW'
66
+ if (normalized.startsWith('zh')) return 'zh-CN'
67
+ if (normalized.startsWith('ja')) return 'ja-JP'
68
+ if (normalized.startsWith('en')) return 'en-US'
69
+ if (normalized.startsWith('ko')) return 'ko-KR'
70
+ if (normalized.startsWith('fr')) return 'fr-FR'
71
+ if (normalized.startsWith('es')) return 'es-ES'
72
+ return undefined
73
+ }
74
+
75
+ /** 初始化持久化语言偏好。 */
76
+ export function initializeLocale(): SupportedLocale {
77
+ const stored = Taro.getStorageSync<string>(LOCALE_STORAGE_KEY)
78
+ const systemLanguage = Taro.getSystemInfoSync().language
79
+ const locale = normalizeLocale(stored || systemLanguage)
80
+ useLocaleStore.setState({ locale, languageOptions: getFallbackLanguageOptions(), supportedLocales: [...SUPPORTED_LOCALES] })
81
+ return locale
82
+ }
83
+
84
+ /** 应用后端语言配置,并在当前语言不可用时回退到接口主语言。 */
85
+ export function applyLanguageConfig(response: GetLanguageResponse): void {
86
+ const options = response.languages.reduce<LocaleOption[]>((items, item) => {
87
+ const languageCode = parseSupportedLocale(item.language_code)
88
+ if (!languageCode || items.some((option) => option.language_code === languageCode)) return items
89
+ items.push({
90
+ language_code: languageCode,
91
+ language_name: item.language_name || t(`common.language.${languageCode}`),
92
+ native_name: item.native_name || item.language_name || languageCode,
93
+ sort: item.sort,
94
+ })
95
+ return items
96
+ }, [])
97
+ const languageOptions = options.length ? options.sort((left, right) => left.sort - right.sort) : getFallbackLanguageOptions()
98
+ const supportedLocales = languageOptions.map((item) => item.language_code)
99
+ const primaryLocale = parseSupportedLocale(response.primary_language_code)
100
+ const currentLocale = getCurrentLocale()
101
+ const locale = supportedLocales.includes(currentLocale)
102
+ ? currentLocale
103
+ : primaryLocale && supportedLocales.includes(primaryLocale)
104
+ ? primaryLocale
105
+ : supportedLocales[0] ?? DEFAULT_LOCALE
106
+ useLocaleStore.setState({ locale, languageOptions, supportedLocales })
107
+ if (locale !== currentLocale) Taro.setStorageSync(LOCALE_STORAGE_KEY, locale)
108
+ }
109
+
110
+ /** 获取当前接口配置的语言选项。 */
111
+ export function getLanguageOptions(): LocaleOption[] {
112
+ return useLocaleStore.getState().languageOptions.length ? useLocaleStore.getState().languageOptions : getFallbackLanguageOptions()
113
+ }
114
+
115
+ /** 获取当前可切换的语言区域。 */
116
+ export function getSupportedLocales(): SupportedLocale[] {
117
+ return useLocaleStore.getState().supportedLocales
118
+ }
119
+
120
+ /** 注册所有模块贡献的语言包并校验七语键集合。 */
121
+ export function registerLocaleMessages(modules: KratosTaroModule[]): void {
122
+ localeMessages.clear()
123
+ SUPPORTED_LOCALES.forEach((locale) => localeMessages.set(locale, {}))
124
+ modules.forEach((module) => {
125
+ const expectedKeys = Object.keys(module.messages?.[DEFAULT_LOCALE] || {}).sort()
126
+ SUPPORTED_LOCALES.forEach((locale) => {
127
+ const messages = module.messages?.[locale]
128
+ if (!messages) throw new Error(`${module.name} 缺少 ${locale} 语言包`)
129
+ const keys = Object.keys(messages).sort()
130
+ if (keys.join('\u0000') !== expectedKeys.join('\u0000')) {
131
+ throw new Error(`${module.name} 的 ${locale} 语言包键集合不一致`)
132
+ }
133
+ const target = localeMessages.get(locale) as LocaleMessages
134
+ keys.forEach((key) => {
135
+ if (!key.startsWith('common.') && !key.startsWith('core.') && !key.startsWith('system.')) {
136
+ throw new Error(`${module.name} 的语言键命名空间无效: ${key}`)
137
+ }
138
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
139
+ throw new Error(`${locale} 语言键重复: ${key}`)
140
+ }
141
+ assertLocalePlaceholders(module.name, key, messages[key], module.messages?.[DEFAULT_LOCALE]?.[key] || '')
142
+ target[key] = messages[key]
143
+ })
144
+ })
145
+ })
146
+ }
147
+
148
+ /** 获取当前语言区域。 */
149
+ export function getCurrentLocale(): SupportedLocale {
150
+ return useLocaleStore.getState().locale
151
+ }
152
+
153
+ /** 获取请求需要携带的语言头。 */
154
+ export function getLocaleRequestHeaders(): Record<'Accept-Language', SupportedLocale> {
155
+ return { 'Accept-Language': getCurrentLocale() }
156
+ }
157
+
158
+ /** 切换当前语言并通知需要刷新动态本地化数据的模块。 */
159
+ export async function setCurrentLocale(value: SupportedLocale): Promise<void> {
160
+ const locale = normalizeLocale(value)
161
+ if (!getSupportedLocales().includes(locale)) return
162
+ if (locale === getCurrentLocale()) return
163
+ useLocaleStore.setState({ locale })
164
+ Taro.setStorageSync(LOCALE_STORAGE_KEY, locale)
165
+ for (const handler of localeChangeHandlers) await handler()
166
+ }
167
+
168
+ /** 注册语言切换后的动态数据刷新处理器。 */
169
+ export function registerLocaleChangeHandler(handler: () => void | Promise<void>): () => void {
170
+ localeChangeHandlers.add(handler)
171
+ return () => localeChangeHandlers.delete(handler)
172
+ }
173
+
174
+ /** 翻译稳定语言键,缺失时回退中文且不展示裸键。 */
175
+ export function t(key: string, params: LocaleParams = {}, locale = getCurrentLocale()): string {
176
+ const message = localeMessages.get(locale)?.[key] || localeMessages.get(DEFAULT_LOCALE)?.[key]
177
+ if (!message) return localeMessages.get(DEFAULT_LOCALE)?.['common.message.unknown'] || ''
178
+ return message.replace(/\{([A-Za-z0-9_]+)\}/g, (_, name: string) => String(params[name] ?? `{${name}}`))
179
+ }
180
+
181
+ /** 在 React 页面中使用响应式国际化能力。 */
182
+ export function useI18n() {
183
+ const locale = useLocaleStore((state) => state.locale)
184
+ return {
185
+ locale,
186
+ setLocale: setCurrentLocale,
187
+ t: (key: string, params: LocaleParams = {}) => t(key, params, locale),
188
+ }
189
+ }
190
+
191
+ function assertLocalePlaceholders(
192
+ moduleName: string,
193
+ key: string,
194
+ message: string,
195
+ sourceMessage: string,
196
+ ): void {
197
+ const placeholders = (value: string) =>
198
+ [...value.matchAll(/\{([A-Za-z0-9_]+)\}/g)].map((match) => match[1]).sort()
199
+ if (placeholders(message).join('\u0000') !== placeholders(sourceMessage).join('\u0000')) {
200
+ throw new Error(`${moduleName} 的 ${key} 占位符集合不一致`)
201
+ }
202
+ }
203
+
204
+ function getFallbackLanguageOptions(): LocaleOption[] {
205
+ return SUPPORTED_LOCALES.map((languageCode, sort) => ({
206
+ language_code: languageCode,
207
+ language_name: t(`common.language.${languageCode}`),
208
+ native_name: languageCode,
209
+ sort,
210
+ }))
211
+ }
@@ -0,0 +1,113 @@
1
+ {
2
+ "common.action.back": "戻る",
3
+ "common.action.cancel": "キャンセル",
4
+ "common.action.confirm": "確認",
5
+ "common.action.delete": "削除",
6
+ "common.action.login": "ログイン",
7
+ "common.action.logout": "ログアウト",
8
+ "common.action.new": "新規作成",
9
+ "common.action.openInNewWindow": "新しいウィンドウで開く",
10
+ "common.action.retry": "再試行",
11
+ "common.action.save": "保存",
12
+ "common.action.search": "検索",
13
+ "common.field.language": "言語",
14
+ "common.language.en-US": "English",
15
+ "common.language.ja-JP": "日本語",
16
+ "common.language.zh-CN": "简体中文",
17
+ "common.language.zh-TW": "繁体字中国語",
18
+ "common.language.ko-KR": "韓国語",
19
+ "common.language.fr-FR": "フランス語",
20
+ "common.language.es-ES": "スペイン語",
21
+ "common.message.loading": "読み込み中",
22
+ "common.message.networkError": "ネットワークエラーです。接続を確認して再試行してください。",
23
+ "common.message.requestError": "リクエストに失敗しました",
24
+ "common.message.unknown": "現在処理できません。しばらくしてから再試行してください。",
25
+ "common.title.notice": "お知らせ",
26
+ "core.auth.loginAgain": "再ログイン",
27
+ "core.auth.sessionExpired": "セッションの有効期限が切れました。再度ログインしてください。",
28
+ "core.config.missing": "アプリ設定が不足しています:{names}",
29
+ "core.crypto.base64DecodeUnsupported": "この環境では Base64 デコードを利用できません",
30
+ "core.crypto.base64EncodeUnsupported": "この環境では Base64 エンコードを利用できません",
31
+ "core.crypto.encryptFailed": "パスワードを暗号化できませんでした",
32
+ "core.crypto.passwordRequired": "パスワードを入力してください",
33
+ "core.crypto.passwordTooLong": "パスワードが長すぎます",
34
+ "core.crypto.publicKeyInvalid": "公開鍵の形式が無効です",
35
+ "core.crypto.randomFailed": "乱数を生成できませんでした",
36
+ "core.crypto.unsupported": "この環境ではパスワード暗号化を利用できません",
37
+ "core.file.downloadFailed": "ダウンロードに失敗しました",
38
+ "core.file.temporaryPathRequired": "ミニプログラムでは一時ファイルパスを使用してください",
39
+ "core.file.uploadFailed": "アップロードに失敗しました",
40
+ "core.home.accountCapability": "アカウント機能",
41
+ "core.home.accountCapabilityDescription": "プロフィール、アプリ設定、AI アシスタントを利用できます",
42
+ "core.home.crossPlatform": "クロスプラットフォーム",
43
+ "core.home.crossPlatformDescription": "同じコードで H5 と WeChat ミニプログラムに対応",
44
+ "core.home.demoScope": "デモ範囲",
45
+ "core.home.framework": "アプリフレームワーク",
46
+ "core.home.language": "開発言語",
47
+ "core.home.mainTitle": "アプリケーションフレームワーク",
48
+ "core.home.stateManagement": "状態管理",
49
+ "core.home.staticHome": "静的ホーム",
50
+ "core.home.staticHomeDescription": "業務 API に依存せずフレームワーク情報を表示します",
51
+ "core.home.styleSolution": "スタイル",
52
+ "core.home.subTitle": "共通ナビゲーションとアカウント体験",
53
+ "core.home.techStack": "技術スタック",
54
+ "core.login.agreePrefix": "以下を読み、同意します",
55
+ "core.login.agreeSeparator": "および",
56
+ "core.login.behaviorClick": "文字を順番にクリックしてください",
57
+ "core.login.behaviorLoading": "読み込み中...",
58
+ "core.login.behaviorPuzzle": "スライダーを動かしてパズルを完成してください",
59
+ "core.login.behaviorRotate": "スライダーを動かして内側の画像を正位置にしてください",
60
+ "core.login.bindWechat": "ログインして WeChat と連携",
61
+ "core.login.captcha": "認証コードを入力してください",
62
+ "core.login.captchaLoadFailed": "認証コードを読み込めませんでした",
63
+ "core.login.captchaTypeMissing": "認証方式が設定されていません",
64
+ "core.login.captchaTypeName": "ログイン認証方式",
65
+ "core.login.captchaTypeResponseMissing": "認証レスポンスにタイプがありません",
66
+ "core.login.captchaWriteFailed": "認証画像を保存できませんでした",
67
+ "core.login.defaultSubTitle": "ようこそ",
68
+ "core.login.loginFailed": "ログインに失敗しました。再試行してください。",
69
+ "core.login.loginSuccess": "ログインしました",
70
+ "core.login.password": "パスワードを入力してください",
71
+ "core.login.privacy": "プライバシーポリシー",
72
+ "core.login.protocolMissing": "利用規約とプライバシーポリシーが設定されていません",
73
+ "core.login.protocolPrompt": "先に規約を確認してください。確認すると同意してログインを続行します。",
74
+ "core.login.service": "利用規約",
75
+ "core.login.tenant": "テナントコードを入力してください",
76
+ "core.login.userName": "ユーザー名または携帯番号を入力してください",
77
+ "core.login.userNameMobile": "ユーザー名または携帯番号を入力してください",
78
+ "core.login.userNamePassword": "ユーザー名とパスワードを入力してください",
79
+ "core.login.wechat": "WeChat でログイン",
80
+ "core.login.wechatBindTip": "この WeChat は未連携です。既存アカウントでログインして連携してください。",
81
+ "core.login.wechatFailed": "WeChat ログインに失敗しました。しばらくしてから再試行してください。",
82
+ "core.navigation.home": "ホーム",
83
+ "core.navigation.error.access": "メニューのアクセスモードが無効です:{access}",
84
+ "core.navigation.error.childTab": "第2階層のメニューのみ tab に設定できます:{name}",
85
+ "core.navigation.error.cycle": "メニューの親階層が循環しています:{name}",
86
+ "core.navigation.error.identity": "メニューの id、name、または app/ パスが無効です",
87
+ "core.navigation.error.itemObject": "メニュー項目がオブジェクトではありません",
88
+ "core.navigation.error.listMissing": "メニューレスポンスにフラットリストがありません",
89
+ "core.navigation.error.namePrefix": "メニュー名は App で始める必要があります:{name}",
90
+ "core.navigation.error.parentInvalid": "メニューの親 ID が無効です:{name}",
91
+ "core.navigation.error.parentMissing": "メニューの親 ID がありません:{name}",
92
+ "core.navigation.error.parentNotFound": "親メニューが見つかりません:{name}",
93
+ "core.navigation.error.responseObject": "メニューレスポンスがオブジェクトではありません",
94
+ "core.navigation.error.rootTab": "第2階層のメニューは tab に設定する必要があります:{name}",
95
+ "core.navigation.error.tabCount": "tabBar は 0 件または 2-5 件にしてください",
96
+ "core.navigation.error.unique": "メニューの id、name、path は一意である必要があります",
97
+ "core.navigation.error.viewKey": "未登録の viewKey:{viewKey}",
98
+ "core.navigation.my": "マイページ",
99
+ "core.navigation.protocol": "規約",
100
+ "core.protocol.loadFailed": "アプリ設定を読み込めませんでした",
101
+ "core.protocol.notConfigured": "{title}が設定されていないため表示できません",
102
+ "core.protocol.privacy": "プライバシーポリシー",
103
+ "core.protocol.service": "利用規約",
104
+ "core.status.CONFIG_ERROR": "ナビゲーション設定が無効です",
105
+ "core.status.FORBIDDEN": "アクセス権限がありません",
106
+ "core.status.NOT_FOUND": "ページが見つかりません",
107
+ "core.status.OFFLINE": "ネットワークを利用できません",
108
+ "core.status.PAGE_UNAVAILABLE": "ページは一時的に利用できません",
109
+ "core.status.loading": "読み込み中",
110
+ "core.webview.embedBlocked": "H5 ではリンク先の埋め込みが制限されている可能性があります",
111
+ "core.webview.invalidUrl": "有効なリンクがありません",
112
+ "core.webview.openFailed": "リンクを開けません"
113
+ }
@@ -0,0 +1,113 @@
1
+ {
2
+ "common.action.back": "반환",
3
+ "common.action.cancel": "취소",
4
+ "common.action.confirm": "확인",
5
+ "common.action.delete": "삭제",
6
+ "common.action.login": "로그인",
7
+ "common.action.logout": "로그아웃",
8
+ "common.action.new": "신규",
9
+ "common.action.openInNewWindow": "새 창이 열립니다",
10
+ "common.action.retry": "다시 시도하세요",
11
+ "common.action.save": "저장",
12
+ "common.action.search": "검색",
13
+ "common.field.language": "언어",
14
+ "common.language.en-US": "영어",
15
+ "common.language.ja-JP": "일본어",
16
+ "common.language.zh-CN": "중국어 간체",
17
+ "common.language.zh-TW": "중국어 번체",
18
+ "common.language.ko-KR": "한국어",
19
+ "common.language.fr-FR": "프랑스어",
20
+ "common.language.es-ES": "스페인어",
21
+ "common.message.loading": "로드 중",
22
+ "common.message.networkError": "네트워크 오류입니다. 다른 네트워크를 사용해 보세요.",
23
+ "common.message.requestError": "요청 오류",
24
+ "common.message.unknown": "지금은 처리할 수 없습니다. 나중에 다시 시도해 주세요.",
25
+ "common.title.notice": "팁",
26
+ "core.auth.loginAgain": "다시 로그인하세요",
27
+ "core.auth.sessionExpired": "현재 페이지가 잘못되었습니다. 다시 로그인해주세요.",
28
+ "core.config.missing": "모바일 단말기 구성이 누락되었습니다: {names}",
29
+ "core.crypto.base64DecodeUnsupported": "현재 환경은 Base64 디코딩을 지원하지 않습니다.",
30
+ "core.crypto.base64EncodeUnsupported": "현재 환경은 Base64 인코딩을 지원하지 않습니다.",
31
+ "core.crypto.encryptFailed": "비밀번호 암호화에 실패했습니다.",
32
+ "core.crypto.passwordRequired": "비밀번호는 비워둘 수 없습니다.",
33
+ "core.crypto.passwordTooLong": "비밀번호 길이가 제한을 초과했습니다.",
34
+ "core.crypto.publicKeyInvalid": "공개 키 형식이 잘못되었습니다.",
35
+ "core.crypto.randomFailed": "난수 생성 실패",
36
+ "core.crypto.unsupported": "현재 환경은 비밀번호 암호화를 지원하지 않습니다.",
37
+ "core.file.downloadFailed": "다운로드 실패",
38
+ "core.file.temporaryPathRequired": "미니프로그램 업로드를 위해 임시 파일 경로를 이용해 주세요.",
39
+ "core.file.uploadFailed": "업로드 실패",
40
+ "core.home.accountCapability": "계정 기능",
41
+ "core.home.accountCapabilityDescription": "개인정보, 애플리케이션 설정, 스마트 어시스턴트 입장이 예약되었습니다.",
42
+ "core.home.crossPlatform": "크로스엔드 페이지",
43
+ "core.home.crossPlatformDescription": "동일한 페이지 코드 세트가 H5 및 WeChat 미니 프로그램에 적용됩니다.",
44
+ "core.home.demoScope": "데모 범위",
45
+ "core.home.framework": "애플리케이션 프레임워크",
46
+ "core.home.language": "개발 언어",
47
+ "core.home.mainTitle": "애플리케이션 프레임워크 예",
48
+ "core.home.stateManagement": "상태 관리",
49
+ "core.home.staticHome": "정적 홈페이지",
50
+ "core.home.staticHomeDescription": "현재 홈페이지는 프레임 정보를 표시하는 데 사용되며 비즈니스 인터페이스에 의존하지 않습니다.",
51
+ "core.home.styleSolution": "스타일 구성표",
52
+ "core.home.subTitle": "범용 탐색 및 개인 센터 경험 보존",
53
+ "core.home.techStack": "현재 기술 스택",
54
+ "core.login.agreePrefix": "읽었으며 이에 동의합니다.",
55
+ "core.login.agreeSeparator": "및",
56
+ "core.login.behaviorClick": "순서대로 텍스트를 클릭해주세요",
57
+ "core.login.behaviorLoading": "로드 중...",
58
+ "core.login.behaviorPuzzle": "퍼즐을 완성하려면 슬라이더를 드래그하세요.",
59
+ "core.login.behaviorRotate": "슬라이더를 드래그하여 내부 원 그림을 수정하세요.",
60
+ "core.login.bindWechat": "로그인 및 WeChat 연결",
61
+ "core.login.captcha": "인증코드를 입력해주세요",
62
+ "core.login.captchaLoadFailed": "인증 코드 로드에 실패했습니다.",
63
+ "core.login.captchaTypeMissing": "로그인 확인 코드 유형이 구성되지 않았습니다.",
64
+ "core.login.captchaTypeName": "로그인 인증 코드 유형",
65
+ "core.login.captchaTypeResponseMissing": "확인 코드 인터페이스가 유형을 반환하지 않습니다.",
66
+ "core.login.captchaWriteFailed": "인증코드 이미지 작성 실패",
67
+ "core.login.defaultSubTitle": "이 애플리케이션에 오신 것을 환영합니다.",
68
+ "core.login.loginFailed": "로그인에 실패했습니다. 다시 시도해 주세요.",
69
+ "core.login.loginSuccess": "로그인 성공",
70
+ "core.login.password": "비밀번호를 입력해주세요",
71
+ "core.login.privacy": "\"개인정보 보호 계약\"",
72
+ "core.login.protocolMissing": "서비스 약관 및 개인정보 보호 계약이 구성되지 않았습니다.",
73
+ "core.login.protocolPrompt": "먼저 약관 내용을 읽고 확인하시기 바랍니다. 확인을 클릭하시면 자동으로 확인 후 로그인이 진행됩니다.",
74
+ "core.login.service": "\"서비스 약관\"",
75
+ "core.login.tenant": "테넌트 번호를 입력하세요.",
76
+ "core.login.userName": "사용자 이름이나 휴대폰 번호를 입력하세요",
77
+ "core.login.userNameMobile": "사용자 이름/휴대폰번호를 입력하세요.",
78
+ "core.login.userNamePassword": "사용자 이름과 비밀번호를 입력하세요",
79
+ "core.login.wechat": "WeChat 원클릭 로그인",
80
+ "core.login.wechatBindTip": "이 WeChat 계정은 아직 연결되지 않았습니다. 바인딩을 완료하려면 기존 계정에 로그인하세요.",
81
+ "core.login.wechatFailed": "WeChat 로그인에 실패했습니다. 나중에 다시 시도해 주세요.",
82
+ "core.navigation.home": "홈",
83
+ "core.navigation.error.access": "잘못된 메뉴 액세스 모드: {access}",
84
+ "core.navigation.error.childTab": "모바일 보조 메뉴만 탭으로 사용할 수 있습니다: {name}",
85
+ "core.navigation.error.cycle": "모바일 메뉴에 루프 상위가 있습니다: {name}",
86
+ "core.navigation.error.identity": "메뉴 ID, 이름 또는 앱/경로가 잘못되었습니다.",
87
+ "core.navigation.error.itemObject": "메뉴 항목이 개체가 아닙니다.",
88
+ "core.navigation.error.listMissing": "메뉴 응답에 일반 목록이 누락되었습니다.",
89
+ "core.navigation.error.namePrefix": "메뉴 이름은 앱 접두어: {name}을 사용해야 합니다.",
90
+ "core.navigation.error.parentInvalid": "모바일 메뉴 상위 번호가 잘못되었습니다: {name}",
91
+ "core.navigation.error.parentMissing": "메뉴에 상위 번호가 없습니다: {name}",
92
+ "core.navigation.error.parentNotFound": "모바일 메뉴 상위가 존재하지 않습니다: {name}",
93
+ "core.navigation.error.responseObject": "메뉴 응답이 개체가 아닙니다.",
94
+ "core.navigation.error.rootTab": "모바일 보조 메뉴는 탭으로 사용해야 합니다: {name}",
95
+ "core.navigation.error.tabCount": "tabBar는 0 또는 2-5개의 항목만 구성할 수 있습니다.",
96
+ "core.navigation.error.unique": "메뉴 ID, 이름, 경로는 고유해야 합니다.",
97
+ "core.navigation.error.viewKey": "등록되지 않은 viewKey: {viewKey}",
98
+ "core.navigation.my": "내 것",
99
+ "core.navigation.protocol": "계약 세부정보",
100
+ "core.protocol.loadFailed": "모바일 구성 로드에 실패했습니다.",
101
+ "core.protocol.notConfigured": "{title}이 구성되지 않아 일시적으로 볼 수 없습니다.",
102
+ "core.protocol.privacy": "개인정보 보호 계약",
103
+ "core.protocol.service": "서비스 약관",
104
+ "core.status.CONFIG_ERROR": "잘못된 탐색 구성",
105
+ "core.status.FORBIDDEN": "아직 액세스할 수 없습니다.",
106
+ "core.status.NOT_FOUND": "페이지가 존재하지 않습니다",
107
+ "core.status.OFFLINE": "네트워크를 사용할 수 없습니다.",
108
+ "core.status.PAGE_UNAVAILABLE": "페이지를 일시적으로 사용할 수 없습니다.",
109
+ "core.status.loading": "로드 중",
110
+ "core.webview.embedBlocked": "현재 H5 페이지는 대상 사이트에 의해 삽입이 제한될 수 있습니다.",
111
+ "core.webview.invalidUrl": "유효한 링크 주소가 누락되었습니다.",
112
+ "core.webview.openFailed": "링크를 열 수 없습니다"
113
+ }
@@ -0,0 +1,113 @@
1
+ {
2
+ "common.action.back": "返回",
3
+ "common.action.cancel": "取消",
4
+ "common.action.confirm": "确定",
5
+ "common.action.delete": "删除",
6
+ "common.action.login": "登录",
7
+ "common.action.logout": "退出登录",
8
+ "common.action.new": "新建",
9
+ "common.action.openInNewWindow": "新窗口打开",
10
+ "common.action.retry": "重试",
11
+ "common.action.save": "保存",
12
+ "common.action.search": "搜索",
13
+ "common.field.language": "语言",
14
+ "common.language.en-US": "English",
15
+ "common.language.ja-JP": "日本語",
16
+ "common.language.zh-CN": "简体中文",
17
+ "common.language.zh-TW": "繁体中文",
18
+ "common.language.ko-KR": "韩语",
19
+ "common.language.fr-FR": "法语",
20
+ "common.language.es-ES": "西班牙语",
21
+ "common.message.loading": "正在加载",
22
+ "common.message.networkError": "网络错误,换个网络试试",
23
+ "common.message.requestError": "请求错误",
24
+ "common.message.unknown": "暂时无法处理,请稍后重试",
25
+ "common.title.notice": "提示",
26
+ "core.auth.loginAgain": "重新登录",
27
+ "core.auth.sessionExpired": "当前页面已失效,请重新登录",
28
+ "core.config.missing": "移动端配置缺失:{names}",
29
+ "core.crypto.base64DecodeUnsupported": "当前环境不支持 Base64 解码",
30
+ "core.crypto.base64EncodeUnsupported": "当前环境不支持 Base64 编码",
31
+ "core.crypto.encryptFailed": "密码加密失败",
32
+ "core.crypto.passwordRequired": "密码不能为空",
33
+ "core.crypto.passwordTooLong": "密码长度超出限制",
34
+ "core.crypto.publicKeyInvalid": "公钥格式无效",
35
+ "core.crypto.randomFailed": "随机数生成失败",
36
+ "core.crypto.unsupported": "当前环境不支持密码加密",
37
+ "core.file.downloadFailed": "下载失败",
38
+ "core.file.temporaryPathRequired": "小程序端请使用文件临时路径上传",
39
+ "core.file.uploadFailed": "上传失败",
40
+ "core.home.accountCapability": "账户能力",
41
+ "core.home.accountCapabilityDescription": "个人资料、应用设置与智能助手入口已保留",
42
+ "core.home.crossPlatform": "跨端页面",
43
+ "core.home.crossPlatformDescription": "同一套页面代码适配 H5 和微信小程序",
44
+ "core.home.demoScope": "演示范围",
45
+ "core.home.framework": "应用框架",
46
+ "core.home.language": "开发语言",
47
+ "core.home.mainTitle": "应用框架示例",
48
+ "core.home.stateManagement": "状态管理",
49
+ "core.home.staticHome": "静态首页",
50
+ "core.home.staticHomeDescription": "当前首页用于展示框架信息,不依赖业务接口",
51
+ "core.home.styleSolution": "样式方案",
52
+ "core.home.subTitle": "保留通用导航与个人中心体验",
53
+ "core.home.techStack": "当前技术栈",
54
+ "core.login.agreePrefix": "我已阅读并同意",
55
+ "core.login.agreeSeparator": "和",
56
+ "core.login.behaviorClick": "请按顺序点击文字",
57
+ "core.login.behaviorLoading": "加载中...",
58
+ "core.login.behaviorPuzzle": "请拖动滑块完成拼图",
59
+ "core.login.behaviorRotate": "拖动滑块,将内圈图片转正",
60
+ "core.login.bindWechat": "登录并绑定微信",
61
+ "core.login.captcha": "请输入验证码",
62
+ "core.login.captchaLoadFailed": "验证码加载失败",
63
+ "core.login.captchaTypeMissing": "登录验证码类型未配置",
64
+ "core.login.captchaTypeName": "登录验证码类型",
65
+ "core.login.captchaTypeResponseMissing": "验证码接口未返回类型",
66
+ "core.login.captchaWriteFailed": "验证码图片写入失败",
67
+ "core.login.defaultSubTitle": "欢迎使用本应用",
68
+ "core.login.loginFailed": "登录失败,请重试",
69
+ "core.login.loginSuccess": "登录成功",
70
+ "core.login.password": "请输入密码",
71
+ "core.login.privacy": "《隐私协议》",
72
+ "core.login.protocolMissing": "服务条款和隐私协议未配置",
73
+ "core.login.protocolPrompt": "请先阅读并勾选协议内容,点击确定后将自动勾选并继续登录",
74
+ "core.login.service": "《服务条款》",
75
+ "core.login.tenant": "请输入租户编号",
76
+ "core.login.userName": "请输入用户名或手机号",
77
+ "core.login.userNameMobile": "请输入用户名/手机号码",
78
+ "core.login.userNamePassword": "请输入用户名和密码",
79
+ "core.login.wechat": "微信一键登录",
80
+ "core.login.wechatBindTip": "该微信尚未绑定,请登录已有账号完成绑定",
81
+ "core.login.wechatFailed": "微信登录失败,请稍后重试",
82
+ "core.navigation.home": "首页",
83
+ "core.navigation.error.access": "菜单访问模式无效:{access}",
84
+ "core.navigation.error.childTab": "只有移动端二级菜单可以作为 tab:{name}",
85
+ "core.navigation.error.cycle": "移动端菜单存在循环父级:{name}",
86
+ "core.navigation.error.identity": "菜单 id、name 或 app/ 路径无效",
87
+ "core.navigation.error.itemObject": "菜单项不是对象",
88
+ "core.navigation.error.listMissing": "菜单响应缺少扁平列表",
89
+ "core.navigation.error.namePrefix": "菜单名称必须使用 App 前缀:{name}",
90
+ "core.navigation.error.parentInvalid": "移动端菜单父级编号无效:{name}",
91
+ "core.navigation.error.parentMissing": "菜单缺少父级编号:{name}",
92
+ "core.navigation.error.parentNotFound": "移动端菜单父级不存在:{name}",
93
+ "core.navigation.error.responseObject": "菜单响应不是对象",
94
+ "core.navigation.error.rootTab": "移动端二级菜单必须作为 tab:{name}",
95
+ "core.navigation.error.tabCount": "tabBar 只能配置 0 或 2-5 项",
96
+ "core.navigation.error.unique": "菜单 id、name、path 必须唯一",
97
+ "core.navigation.error.viewKey": "未注册 viewKey:{viewKey}",
98
+ "core.navigation.my": "我的",
99
+ "core.navigation.protocol": "协议详情",
100
+ "core.protocol.loadFailed": "移动端配置加载失败",
101
+ "core.protocol.notConfigured": "{title}未配置,暂时无法查看",
102
+ "core.protocol.privacy": "隐私协议",
103
+ "core.protocol.service": "服务条款",
104
+ "core.status.CONFIG_ERROR": "导航配置无效",
105
+ "core.status.FORBIDDEN": "暂无访问权限",
106
+ "core.status.NOT_FOUND": "页面不存在",
107
+ "core.status.OFFLINE": "网络不可用",
108
+ "core.status.PAGE_UNAVAILABLE": "页面暂不可用",
109
+ "core.status.loading": "正在加载",
110
+ "core.webview.embedBlocked": "当前 H5 页面可能被目标站点限制嵌入",
111
+ "core.webview.invalidUrl": "缺少有效链接地址",
112
+ "core.webview.openFailed": "链接无法打开"
113
+ }