@unbrained/pm-web 2026.7.17-1 → 2026.7.17
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/app.d.ts +1 -15
- package/dist/app.js +1 -15
- package/dist/app.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +1 -1
- package/public/index.html +28 -32
- package/public/src/app.js +0 -14
- package/public/src/app.js.map +1 -1
- package/public/src/app.ts +0 -15
- package/public/src/components/modals.js +3 -5
- package/public/src/components/modals.js.map +1 -1
- package/public/src/components/modals.ts +3 -6
- package/public/src/sw.ts +0 -3
- package/public/src/views/auth.js +7 -8
- package/public/src/views/auth.js.map +1 -1
- package/public/src/views/auth.ts +7 -8
- package/public/src/views/settings.js +48 -73
- package/public/src/views/settings.js.map +1 -1
- package/public/src/views/settings.ts +48 -77
- package/public/sw.js +0 -3
- package/public/src/i18n/de.json +0 -117
- package/public/src/i18n/en.json +0 -117
- package/public/src/i18n/es.json +0 -117
- package/public/src/i18n/zh.json +0 -117
- package/public/src/i18n.js +0 -287
- package/public/src/i18n.js.map +0 -1
- package/public/src/i18n.ts +0 -306
package/public/src/i18n/zh.json
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"auth.brand.tagline": "基于 Git 的项目管理",
|
|
3
|
-
"auth.feature.tasks": "管理任务、功能、缺陷、史诗等",
|
|
4
|
-
"auth.feature.storage": "基于 Git 的存储,位于 .agents/pm/",
|
|
5
|
-
"auth.feature.history": "完整历史记录、评论和活动动态",
|
|
6
|
-
"auth.feature.team": "适合团队的项目隔离",
|
|
7
|
-
"auth.title.login": "欢迎回来",
|
|
8
|
-
"auth.title.register": "创建账户",
|
|
9
|
-
"auth.sub.login": "登录您的账户以继续",
|
|
10
|
-
"auth.sub.register": "加入 pm-web 并开始管理项目",
|
|
11
|
-
"auth.tab.login": "登录",
|
|
12
|
-
"auth.tab.register": "注册",
|
|
13
|
-
"auth.field.displayName": "显示名称",
|
|
14
|
-
"auth.field.email": "电子邮件",
|
|
15
|
-
"auth.field.password": "密码",
|
|
16
|
-
"auth.placeholder.name": "您的名称",
|
|
17
|
-
"auth.placeholder.email": "you@example.com",
|
|
18
|
-
"auth.placeholder.password": "••••••••",
|
|
19
|
-
"auth.divider": "或",
|
|
20
|
-
"auth.oidc.default": "使用 OpenID Connect 继续",
|
|
21
|
-
"auth.oidc.template": "使用 {label} 继续",
|
|
22
|
-
"auth.button.login": "登录",
|
|
23
|
-
"auth.button.register": "创建账户",
|
|
24
|
-
"auth.loading": "请稍候…",
|
|
25
|
-
"nav.skipLink": "跳转到主要内容",
|
|
26
|
-
"nav.settings": "设置",
|
|
27
|
-
"nav.signOut": "退出登录",
|
|
28
|
-
"nav.search": "⌘K 搜索",
|
|
29
|
-
"nav.search.title": "打开搜索 (Ctrl+K)",
|
|
30
|
-
"nav.selectProject": "— 选择项目 —",
|
|
31
|
-
"nav.sse.disconnected": "实时同步已断开",
|
|
32
|
-
"nav.sse.connected": "实时同步已连接",
|
|
33
|
-
"nav.sse.reconnecting": "正在重新连接实时同步…",
|
|
34
|
-
"nav.theme.title": "切换主题(深色 / 浅色 / 自动)",
|
|
35
|
-
"banner.offline": "您处于离线状态 — 重新连接后将同步更改",
|
|
36
|
-
"banner.install.text": "安装 <strong>pm-web</strong> 以获得原生应用体验",
|
|
37
|
-
"banner.install.dismiss": "暂不",
|
|
38
|
-
"banner.install.button": "安装",
|
|
39
|
-
"cookie.banner.title": "Cookie 设置",
|
|
40
|
-
"cookie.banner.text": "我们仅使用登录、安全和偏好设置所必需的技术性存储。不使用任何可选的跟踪 Cookie。",
|
|
41
|
-
"cookie.banner.privacy": "隐私政策",
|
|
42
|
-
"cookie.banner.decline": "仅保留必需项",
|
|
43
|
-
"cookie.banner.accept": "知道了",
|
|
44
|
-
"settings.title": "设置",
|
|
45
|
-
"settings.subtitle": "管理您的个人资料和账户",
|
|
46
|
-
"settings.language": "语言",
|
|
47
|
-
"settings.languageEn": "English",
|
|
48
|
-
"settings.languageDe": "Deutsch",
|
|
49
|
-
"settings.languageEs": "Español",
|
|
50
|
-
"settings.languageZh": "中文",
|
|
51
|
-
"settings.languageHint": "界面语言。法律页面由运营方提供,可能不予翻译。",
|
|
52
|
-
"settings.profile": "个人资料",
|
|
53
|
-
"settings.displayName": "显示名称",
|
|
54
|
-
"settings.displayNamePlaceholder": "您的显示名称",
|
|
55
|
-
"settings.email": "电子邮件",
|
|
56
|
-
"settings.emailReadonly": "电子邮件地址(只读)",
|
|
57
|
-
"settings.accountCreated": "账户创建于 {date}",
|
|
58
|
-
"settings.saveProfile": "保存个人资料",
|
|
59
|
-
"settings.changePassword": "更改密码",
|
|
60
|
-
"settings.currentPassword": "当前密码",
|
|
61
|
-
"settings.newPassword": "新密码",
|
|
62
|
-
"settings.confirmPassword": "确认新密码",
|
|
63
|
-
"settings.currentPasswordPlaceholder": "当前密码",
|
|
64
|
-
"settings.newPasswordPlaceholder": "新密码",
|
|
65
|
-
"settings.confirmPasswordPlaceholder": "确认新密码",
|
|
66
|
-
"settings.githubToken": "GitHub 令牌",
|
|
67
|
-
"settings.tokenConfigured": "✓ 令牌已配置",
|
|
68
|
-
"settings.tokenNotSet": "未设置令牌",
|
|
69
|
-
"settings.tokenPlaceholder.keep": "留空以保留当前令牌",
|
|
70
|
-
"settings.tokenPlaceholder.set": "ghp_…",
|
|
71
|
-
"settings.tokenLabel": "Personal Access Token (PAT)",
|
|
72
|
-
"settings.tokenHint": "用于 GitHub 集成。需要 <code>repo</code> 权限范围。",
|
|
73
|
-
"settings.saveToken": "保存令牌",
|
|
74
|
-
"settings.clearToken": "清除令牌",
|
|
75
|
-
"settings.howToGet": "如何获取令牌:",
|
|
76
|
-
"settings.tokenStep1": "前往 GitHub → Settings → Developer settings → Personal access tokens",
|
|
77
|
-
"settings.tokenStep2": "生成一个具有 <code>repo</code> 权限范围的新令牌",
|
|
78
|
-
"settings.tokenStep3": "将令牌粘贴到下方并点击保存",
|
|
79
|
-
"settings.saving": "保存中…",
|
|
80
|
-
"settings.changing": "更改中…",
|
|
81
|
-
"settings.displayNameEmpty": "显示名称不能为空",
|
|
82
|
-
"settings.profileSaved": "个人资料已保存",
|
|
83
|
-
"settings.allFieldsRequired": "所有字段均为必填",
|
|
84
|
-
"settings.passwordsDoNotMatch": "新密码不匹配",
|
|
85
|
-
"settings.passwordTooShort": "新密码至少需要 6 个字符",
|
|
86
|
-
"settings.passwordChanged": "密码更改成功",
|
|
87
|
-
"settings.tokenEmpty": "请输入令牌",
|
|
88
|
-
"settings.tokenSaved": "GitHub 令牌已保存",
|
|
89
|
-
"settings.tokenCleared": "GitHub 令牌已清除",
|
|
90
|
-
"settings.clearTokenTitle": "清除 GitHub 令牌?",
|
|
91
|
-
"settings.clearTokenBody": "您的 GitHub 集成将停止工作。",
|
|
92
|
-
"dialog.cancel": "取消",
|
|
93
|
-
"dialog.confirm": "确认",
|
|
94
|
-
"dialog.delete": "删除",
|
|
95
|
-
"error.emailPasswordRequired": "电子邮件和密码为必填项",
|
|
96
|
-
"error.passwordTooShort": "密码至少需要 8 个字符",
|
|
97
|
-
"error.invalidEmail": "电子邮件地址无效",
|
|
98
|
-
"error.accountExists": "该电子邮件地址已存在账户",
|
|
99
|
-
"error.registrationFailed": "注册失败",
|
|
100
|
-
"error.invalidCredentials": "电子邮件或密码无效",
|
|
101
|
-
"error.loginFailed": "登录失败",
|
|
102
|
-
"error.userNotFound": "未找到用户",
|
|
103
|
-
"error.fetchUserFailed": "获取用户失败",
|
|
104
|
-
"error.updateProfileFailed": "更新个人资料失败",
|
|
105
|
-
"error.passwordFieldsRequired": "当前密码和新密码为必填项",
|
|
106
|
-
"error.newPasswordTooShort": "新密码至少需要 8 个字符",
|
|
107
|
-
"error.currentPasswordIncorrect": "当前密码不正确",
|
|
108
|
-
"error.changePasswordFailed": "更改密码失败",
|
|
109
|
-
"error.saveTokenFailed": "保存 GitHub 令牌失败",
|
|
110
|
-
"error.oidcDisabled": "未启用 OpenID Connect 登录。",
|
|
111
|
-
"error.oidcDiscoveryFailed": "OpenID Connect 提供者发现失败。",
|
|
112
|
-
"error.oidcProviderError": "OpenID Connect 提供者无法完成登录。",
|
|
113
|
-
"error.oidcDenied": "OpenID Connect 登录已被取消或拒绝。",
|
|
114
|
-
"error.oidcMissingIdToken": "OIDC 提供者未返回 ID 令牌。",
|
|
115
|
-
"error.oidcLoginFailed": "OpenID Connect 登录失败。",
|
|
116
|
-
"legal.disclaimer": "本翻译仅供理解之用;以英文版本为准。"
|
|
117
|
-
}
|
package/public/src/i18n.js
DELETED
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
// ═══════════════════════════════════════════════════════════════
|
|
2
|
-
// i18n — locale resolution, catalog lookup, DOM binding
|
|
3
|
-
// ═══════════════════════════════════════════════════════════════
|
|
4
|
-
//
|
|
5
|
-
// Flat JSON catalogs (one file per locale) live alongside this module in
|
|
6
|
-
// `public/src/i18n/{en,de}.json`. They are fetched at runtime so the browser
|
|
7
|
-
// bundle stays free of new build-time dependencies and the same JSON files
|
|
8
|
-
// can be audited by tests directly. The English catalog is the source of
|
|
9
|
-
// truth: every key must exist in `en.json`; other locales fall back to it.
|
|
10
|
-
//
|
|
11
|
-
// Scope: this module covers *package-owned* SPA UI strings (auth screen, nav,
|
|
12
|
-
// banners, settings incl. registration acknowledgement, consent UI). Server
|
|
13
|
-
// API error strings stay English on the wire and are translated at the
|
|
14
|
-
// display layer via `translateError()` (fallback: raw message).
|
|
15
|
-
//
|
|
16
|
-
// Legal pages (`public/*.html`) are operator-overlay templates
|
|
17
|
-
// (`PM_WEB_LEGAL_DIR`); their localization is an operator concern and is NOT
|
|
18
|
-
// handled here. The language selector deliberately does not promise
|
|
19
|
-
// translated legal pages — see `settings.languageHint` and the German
|
|
20
|
-
// disclaimer string `legal.disclaimer`.
|
|
21
|
-
/** localStorage key persisting the user's locale choice. */
|
|
22
|
-
export const LOCALE_STORAGE_KEY = 'pmLocale';
|
|
23
|
-
/** Locales shipped by this package. The first entry is the default/fallback. */
|
|
24
|
-
export const SUPPORTED_LOCALES = ['en', 'de', 'es', 'zh'];
|
|
25
|
-
const DEFAULT_LOCALE = 'en';
|
|
26
|
-
/**
|
|
27
|
-
* Resolve the active locale without touching the DOM, so it is unit-testable
|
|
28
|
-
* in Node. Resolution order:
|
|
29
|
-
* 1. `opts.storage[LOCALE_STORAGE_KEY]` (explicit user choice)
|
|
30
|
-
* 2. `opts.navLang` prefix match against SUPPORTED_LOCALES (e.g. `de-DE` → `de`)
|
|
31
|
-
* 3. default locale (`en`)
|
|
32
|
-
*
|
|
33
|
-
* `opts.storage` may be `null` to skip the localStorage step; `opts.navLang`
|
|
34
|
-
* may be omitted to skip navigator negotiation. When called with no
|
|
35
|
-
* arguments the real browser globals (`localStorage`, `navigator.language`)
|
|
36
|
-
* are used — but only inside this function, never at module top level, so
|
|
37
|
-
* importing this module in Node has no side effects.
|
|
38
|
-
*/
|
|
39
|
-
export function resolveLocale(opts) {
|
|
40
|
-
// A property that is present (even if `null`) means the caller explicitly
|
|
41
|
-
// wants that value: `null` skips the step. A property that is absent falls
|
|
42
|
-
// back to the real browser global, so the no-argument browser path still
|
|
43
|
-
// works. This keeps the function deterministic and unit-testable in Node.
|
|
44
|
-
const hasStorage = opts != null && Object.prototype.hasOwnProperty.call(opts, 'storage');
|
|
45
|
-
const storage = hasStorage ? opts.storage : safeLocalStorage();
|
|
46
|
-
if (storage) {
|
|
47
|
-
let stored = null;
|
|
48
|
-
try {
|
|
49
|
-
stored = storage.getItem(LOCALE_STORAGE_KEY);
|
|
50
|
-
}
|
|
51
|
-
catch { /* privacy mode */ }
|
|
52
|
-
if (stored && isSupported(stored))
|
|
53
|
-
return stored;
|
|
54
|
-
}
|
|
55
|
-
const hasNav = opts != null && Object.prototype.hasOwnProperty.call(opts, 'navLang');
|
|
56
|
-
const navLang = hasNav ? opts.navLang : safeNavLang();
|
|
57
|
-
if (navLang) {
|
|
58
|
-
const prefix = navLang.toLowerCase().split('-')[0];
|
|
59
|
-
if (prefix && isSupported(prefix))
|
|
60
|
-
return prefix;
|
|
61
|
-
}
|
|
62
|
-
return DEFAULT_LOCALE;
|
|
63
|
-
}
|
|
64
|
-
function isSupported(value) {
|
|
65
|
-
return SUPPORTED_LOCALES.includes(value);
|
|
66
|
-
}
|
|
67
|
-
/** localStorage access guarded so Node imports/tests never crash.
|
|
68
|
-
* Resolving the storage reference itself can throw a `SecurityError` in
|
|
69
|
-
* privacy/incognito modes that disable storage, so the lookup is wrapped too. */
|
|
70
|
-
function safeLocalStorage() {
|
|
71
|
-
try {
|
|
72
|
-
const g = globalThis;
|
|
73
|
-
return g.localStorage ?? null;
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
/** navigator.language access guarded so Node imports/tests never crash. */
|
|
80
|
-
function safeNavLang() {
|
|
81
|
-
const n = globalThis.navigator;
|
|
82
|
-
return n?.language;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Pure catalog lookup with fallback. Used by `t()` and directly by tests.
|
|
86
|
-
*
|
|
87
|
-
* @param catalog the active locale catalog (may be partial)
|
|
88
|
-
* @param fallback the fallback catalog (English — must contain every key)
|
|
89
|
-
* @param key dotted string id
|
|
90
|
-
* @param params optional `{name: value}` substitutions for `{name}` tokens
|
|
91
|
-
* @returns the translated string, or the key itself if missing from both
|
|
92
|
-
*/
|
|
93
|
-
export function translate(catalog, fallback, key, params) {
|
|
94
|
-
let raw = catalog[key] ?? fallback[key] ?? key;
|
|
95
|
-
if (params) {
|
|
96
|
-
for (const [name, value] of Object.entries(params)) {
|
|
97
|
-
raw = raw.replaceAll(`{${name}}`, String(value));
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
return raw;
|
|
101
|
-
}
|
|
102
|
-
// ── Runtime state (populated by initI18n / setLocale) ───────────────
|
|
103
|
-
let currentLocale = DEFAULT_LOCALE;
|
|
104
|
-
let activeCatalog = {};
|
|
105
|
-
let enCatalog = {};
|
|
106
|
-
let initialized = false;
|
|
107
|
-
/**
|
|
108
|
-
* Monotonic request id for setLocale. Each invocation increments this; an
|
|
109
|
-
* in-flight request records its id and, after its awaited catalog fetch,
|
|
110
|
-
* commits state only if it is still the latest. This discards stale catalogs
|
|
111
|
-
* when language changes overlap (e.g. rapid de → en) so a slower earlier
|
|
112
|
-
* fetch can never overwrite a newer selection.
|
|
113
|
-
*/
|
|
114
|
-
let localeReqId = 0;
|
|
115
|
-
/** Fetch a locale catalog JSON. Best-effort: returns {} on any failure. */
|
|
116
|
-
async function fetchCatalog(locale) {
|
|
117
|
-
try {
|
|
118
|
-
const res = await fetch(`/src/i18n/${locale}.json`, { credentials: 'same-origin' });
|
|
119
|
-
if (!res.ok)
|
|
120
|
-
return {};
|
|
121
|
-
const data = (await res.json());
|
|
122
|
-
return data && typeof data === 'object' ? data : {};
|
|
123
|
-
}
|
|
124
|
-
catch {
|
|
125
|
-
return {};
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
/** Apply the resolved locale to `<html lang>` so AT/browsers report it. */
|
|
129
|
-
function syncHtmlLang(locale) {
|
|
130
|
-
const el = globalThis.document?.documentElement;
|
|
131
|
-
if (el)
|
|
132
|
-
el.lang = locale;
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Initialize i18n: resolve the locale, load the English fallback + the active
|
|
136
|
-
* catalog, set `<html lang>`, and apply `data-i18n` bindings in the document.
|
|
137
|
-
* Safe to call once at boot; subsequent calls re-apply translations.
|
|
138
|
-
*/
|
|
139
|
-
export async function initI18n() {
|
|
140
|
-
if (!initialized) {
|
|
141
|
-
currentLocale = resolveLocale();
|
|
142
|
-
}
|
|
143
|
-
if (!Object.keys(enCatalog).length) {
|
|
144
|
-
enCatalog = await fetchCatalog('en');
|
|
145
|
-
}
|
|
146
|
-
if (currentLocale === 'en') {
|
|
147
|
-
activeCatalog = enCatalog;
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
activeCatalog = await fetchCatalog(currentLocale);
|
|
151
|
-
}
|
|
152
|
-
initialized = true;
|
|
153
|
-
syncHtmlLang(currentLocale);
|
|
154
|
-
applyTranslations();
|
|
155
|
-
}
|
|
156
|
-
/** Current active locale. */
|
|
157
|
-
export function getLocale() {
|
|
158
|
-
return currentLocale;
|
|
159
|
-
}
|
|
160
|
-
/**
|
|
161
|
-
* Translate a key using the active locale with English fallback. Requires
|
|
162
|
-
* `initI18n()` to have completed; before that, returns the English value (or
|
|
163
|
-
* the key if English is also missing).
|
|
164
|
-
*/
|
|
165
|
-
export function t(key, params) {
|
|
166
|
-
return translate(activeCatalog, enCatalog, key, params);
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Persist a new locale choice, load its catalog, update `<html lang>`, and
|
|
170
|
-
* re-apply `data-i18n` bindings across the document. Falls back to `en` for
|
|
171
|
-
* unsupported values.
|
|
172
|
-
*/
|
|
173
|
-
export async function setLocale(locale) {
|
|
174
|
-
const next = isSupported(locale) ? locale : DEFAULT_LOCALE;
|
|
175
|
-
const storage = safeLocalStorage();
|
|
176
|
-
if (storage) {
|
|
177
|
-
try {
|
|
178
|
-
storage.setItem(LOCALE_STORAGE_KEY, next);
|
|
179
|
-
}
|
|
180
|
-
catch { /* privacy mode */ }
|
|
181
|
-
}
|
|
182
|
-
currentLocale = next;
|
|
183
|
-
const myReqId = ++localeReqId;
|
|
184
|
-
if (!Object.keys(enCatalog).length) {
|
|
185
|
-
const en = await fetchCatalog('en');
|
|
186
|
-
// A newer setLocale superseded this one: do not assign enCatalog.
|
|
187
|
-
if (myReqId !== localeReqId)
|
|
188
|
-
return;
|
|
189
|
-
enCatalog = en;
|
|
190
|
-
}
|
|
191
|
-
const fetched = next === 'en' ? enCatalog : await fetchCatalog(next);
|
|
192
|
-
// Stale request: discard the fetched catalog so it can never overwrite a
|
|
193
|
-
// newer selection (e.g. a slow German fetch finishing after en was chosen).
|
|
194
|
-
if (myReqId !== localeReqId)
|
|
195
|
-
return;
|
|
196
|
-
activeCatalog = fetched;
|
|
197
|
-
initialized = true;
|
|
198
|
-
syncHtmlLang(next);
|
|
199
|
-
applyTranslations();
|
|
200
|
-
}
|
|
201
|
-
/**
|
|
202
|
-
* Walk the document for `data-i18n` attributes and bind translations. Three
|
|
203
|
-
* attribute flavors are supported:
|
|
204
|
-
* - `data-i18n="key"` → sets `textContent`
|
|
205
|
-
* - `data-i18n-html="key"` → sets `innerHTML` (for strings with markup)
|
|
206
|
-
* - `data-i18n-title="key"` → sets the `title` attribute
|
|
207
|
-
* - `data-i18n-placeholder="key"` → sets the `placeholder` attribute
|
|
208
|
-
* - `data-i18n-aria="key"` → sets the `aria-label` attribute
|
|
209
|
-
*
|
|
210
|
-
* `data-i18n-params` may carry a JSON object literal for parameterized keys.
|
|
211
|
-
*/
|
|
212
|
-
export function applyTranslations(root) {
|
|
213
|
-
const doc = globalThis.document;
|
|
214
|
-
if (!doc)
|
|
215
|
-
return;
|
|
216
|
-
const scope = root ?? doc;
|
|
217
|
-
bindAttr(scope, '[data-i18n]', 'data-i18n', (el, v) => { el.textContent = v; });
|
|
218
|
-
bindAttr(scope, '[data-i18n-html]', 'data-i18n-html', (el, v) => { el.innerHTML = v; });
|
|
219
|
-
bindAttr(scope, '[data-i18n-title]', 'data-i18n-title', (el, v) => { el.setAttribute('title', v); });
|
|
220
|
-
bindAttr(scope, '[data-i18n-placeholder]', 'data-i18n-placeholder', (el, v) => { el.setAttribute('placeholder', v); });
|
|
221
|
-
bindAttr(scope, '[data-i18n-aria]', 'data-i18n-aria', (el, v) => { el.setAttribute('aria-label', v); });
|
|
222
|
-
}
|
|
223
|
-
function bindAttr(scope, selector, attr, setter) {
|
|
224
|
-
scope.querySelectorAll(selector).forEach((el) => {
|
|
225
|
-
const key = el.getAttribute(attr);
|
|
226
|
-
if (!key)
|
|
227
|
-
return;
|
|
228
|
-
const paramsJson = el.getAttribute('data-i18n-params');
|
|
229
|
-
const params = paramsJson ? safeParseParams(paramsJson) : undefined;
|
|
230
|
-
setter(el, t(key, params));
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
function safeParseParams(json) {
|
|
234
|
-
try {
|
|
235
|
-
const parsed = JSON.parse(json);
|
|
236
|
-
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
|
237
|
-
? parsed
|
|
238
|
-
: undefined;
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
return undefined;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
/**
|
|
245
|
-
* Map a known server error message (English, as sent on the wire) to the
|
|
246
|
-
* active locale's catalog. Unknown messages are returned unchanged so the
|
|
247
|
-
* user always sees something meaningful. This is the display-layer
|
|
248
|
-
* translation boundary for API/OIDC errors.
|
|
249
|
-
*/
|
|
250
|
-
export function translateError(message) {
|
|
251
|
-
const reverse = errorReverseMap();
|
|
252
|
-
const key = reverse.get(message);
|
|
253
|
-
if (key)
|
|
254
|
-
return t(key);
|
|
255
|
-
return message;
|
|
256
|
-
}
|
|
257
|
-
// Lazy reverse map: English error text → catalog key. Built from enCatalog
|
|
258
|
-
// so it tracks the catalog without a hand-maintained second table.
|
|
259
|
-
let cachedErrorReverse = null;
|
|
260
|
-
function errorReverseMap() {
|
|
261
|
-
if (cachedErrorReverse)
|
|
262
|
-
return cachedErrorReverse;
|
|
263
|
-
const map = new Map();
|
|
264
|
-
for (const [key, value] of Object.entries(enCatalog)) {
|
|
265
|
-
if (key.startsWith('error.'))
|
|
266
|
-
map.set(value, key);
|
|
267
|
-
}
|
|
268
|
-
cachedErrorReverse = map;
|
|
269
|
-
return map;
|
|
270
|
-
}
|
|
271
|
-
/** Compile-time-complete mapping from supported locale → BCP 47 tag. */
|
|
272
|
-
const LOCALE_TAGS = {
|
|
273
|
-
en: 'en-US',
|
|
274
|
-
de: 'de-DE',
|
|
275
|
-
es: 'es-ES',
|
|
276
|
-
zh: 'zh-CN',
|
|
277
|
-
};
|
|
278
|
-
/**
|
|
279
|
-
* Format a date in the active locale (`de-DE` for de, `en-US` for en) using
|
|
280
|
-
* Intl.DateTimeFormat. Replaces hard-coded `toLocaleDateString('en-US', …)`.
|
|
281
|
-
*/
|
|
282
|
-
export function localeDate(date, options) {
|
|
283
|
-
const localeTag = LOCALE_TAGS[currentLocale];
|
|
284
|
-
const d = date instanceof Date ? date : new Date(date);
|
|
285
|
-
return new Intl.DateTimeFormat(localeTag, options).format(d);
|
|
286
|
-
}
|
|
287
|
-
//# sourceMappingURL=i18n.js.map
|
package/public/src/i18n.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["i18n.ts"],"names":[],"mappings":"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"}
|