@coffic/cosy-ui 0.4.9 → 0.5.2
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.css +1 -1
- package/dist/components/base/Image.astro +0 -3
- package/dist/components/errors/404.astro +10 -0
- package/dist/components/icons/ChevronDownIcon.astro +34 -0
- package/dist/components/layouts/Footer.astro +2 -2
- package/dist/components/layouts/Header.astro +35 -110
- package/dist/components/layouts/Sidebar.astro +2 -2
- package/dist/components/layouts/SidebarNav.astro +5 -5
- package/dist/components/navigation/LanguageSwitcher.astro +22 -68
- package/dist/components/navigation/TableOfContents.astro +2 -2
- package/dist/entities/SidebarItem.ts +20 -23
- package/dist/index.ts +3 -0
- package/dist/models/BaseDoc.ts +10 -10
- package/dist/types/header.ts +4 -6
- package/dist/types/sidebar.ts +4 -6
- package/dist/utils/language.ts +137 -133
- package/dist/utils/link.ts +28 -2
- package/package.json +1 -1
- package/dist/i18n/Language.ts +0 -25
- package/dist/i18n/ui.ts +0 -18
- package/dist/i18n/utils.ts +0 -54
package/dist/utils/language.ts
CHANGED
@@ -4,168 +4,172 @@
|
|
4
4
|
* 提供语言相关的工具函数,用于多语言支持
|
5
5
|
*/
|
6
6
|
|
7
|
+
import { getRelativeLocaleUrl } from 'astro:i18n';
|
8
|
+
import { logger } from './logger';
|
9
|
+
import { LinkUtil } from './link';
|
10
|
+
import type { AstroGlobal } from 'astro';
|
11
|
+
|
7
12
|
// 支持的语言列表
|
8
13
|
export const SUPPORTED_LANGUAGES = ['en', 'zh-cn', 'zh'] as const;
|
9
14
|
export type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number];
|
10
15
|
|
11
|
-
// 语言来源枚举
|
12
|
-
export enum LanguageSource {
|
13
|
-
USER = 'user', // 用户指定
|
14
|
-
URL = 'url', // 从URL获取
|
15
|
-
BROWSER = 'browser', // 从浏览器设置获取
|
16
|
-
DEFAULT = 'default' // 默认语言
|
17
|
-
}
|
18
|
-
|
19
|
-
// 语言信息接口
|
20
|
-
export interface LanguageInfo {
|
21
|
-
code: SupportedLanguage; // 语言代码
|
22
|
-
source: LanguageSource; // 语言来源
|
23
|
-
}
|
24
|
-
|
25
16
|
// 默认语言
|
26
17
|
export const DEFAULT_LANGUAGE: SupportedLanguage = 'en';
|
27
18
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
if (!lang) return false;
|
35
|
-
return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);
|
36
|
-
}
|
37
|
-
|
38
|
-
/**
|
39
|
-
* 获取有效的语言代码
|
40
|
-
* @param lang 用户提供的语言代码
|
41
|
-
* @returns 有效的语言代码,如果提供的语言不支持则返回默认语言
|
42
|
-
*/
|
43
|
-
export function getValidLanguage(lang?: string): SupportedLanguage {
|
44
|
-
if (lang && isLanguageSupported(lang)) {
|
45
|
-
return lang;
|
46
|
-
}
|
47
|
-
return DEFAULT_LANGUAGE;
|
48
|
-
}
|
19
|
+
export class LanguageUtil {
|
20
|
+
static getRelativeLink(locale: string, astro: AstroGlobal): string {
|
21
|
+
const debug = false;
|
22
|
+
const currentLocale = astro.currentLocale;
|
23
|
+
const originalPath = astro.originPathname;
|
24
|
+
const result = getRelativeLocaleUrl(locale, originalPath.replaceAll("/" + currentLocale, ""));
|
49
25
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
* @returns 从URL中提取的语言代码,如果无法提取则返回undefined
|
54
|
-
*/
|
55
|
-
function getLanguageFromURL(url?: string): string | undefined {
|
56
|
-
let currentUrl = url;
|
26
|
+
if (debug) {
|
27
|
+
logger.debug(`getRelativeLink: locale=${locale}, currentPath=${originalPath}, currentLocale=${currentLocale}, result=${result}`);
|
28
|
+
}
|
57
29
|
|
58
|
-
|
59
|
-
if (typeof window === 'undefined') return undefined;
|
60
|
-
currentUrl = window.location.href;
|
30
|
+
return result;
|
61
31
|
}
|
62
32
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
33
|
+
static getLanguageName(code: string | undefined): string {
|
34
|
+
switch (code) {
|
35
|
+
case 'en':
|
36
|
+
return 'English';
|
37
|
+
case 'zh-cn':
|
38
|
+
return '简体中文';
|
39
|
+
case 'zh':
|
40
|
+
return '中文';
|
41
|
+
case undefined:
|
42
|
+
default:
|
43
|
+
return 'not known';
|
44
|
+
}
|
68
45
|
}
|
69
46
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
47
|
+
/**
|
48
|
+
* 检查语言是否被支持
|
49
|
+
* @param lang 要检查的语言代码
|
50
|
+
* @returns 如果语言被支持返回true,否则返回false
|
51
|
+
*/
|
52
|
+
static isLanguageSupported(lang?: string): lang is SupportedLanguage {
|
53
|
+
if (!lang) return false;
|
54
|
+
return SUPPORTED_LANGUAGES.includes(lang as SupportedLanguage);
|
75
55
|
}
|
76
56
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
57
|
+
/**
|
58
|
+
* 获取有效的语言代码
|
59
|
+
* @param lang 用户提供的语言代码
|
60
|
+
* @returns 有效的语言代码,如果提供的语言不支持则返回默认语言
|
61
|
+
*/
|
62
|
+
static getValidLanguage(lang?: string): SupportedLanguage {
|
63
|
+
if (lang && this.isLanguageSupported(lang)) {
|
64
|
+
return lang;
|
65
|
+
}
|
66
|
+
return DEFAULT_LANGUAGE;
|
83
67
|
}
|
84
68
|
|
85
|
-
|
86
|
-
|
69
|
+
/**
|
70
|
+
* 从URL中提取语言代码
|
71
|
+
* @param url 当前URL(可选)
|
72
|
+
* @returns 从URL中提取的语言代码,如果无法提取则返回undefined
|
73
|
+
*/
|
74
|
+
static getLanguageFromURL(url?: string): string | undefined {
|
75
|
+
let currentUrl = url;
|
76
|
+
|
77
|
+
if (currentUrl === undefined) {
|
78
|
+
if (typeof window === 'undefined') return undefined;
|
79
|
+
currentUrl = window.location.href;
|
80
|
+
}
|
87
81
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
82
|
+
// 尝试从路径中提取语言代码
|
83
|
+
// 例如: /zh-cn/components/button
|
84
|
+
const pathMatch = currentUrl.match(/^\/([\w-]+)\//);
|
85
|
+
if (pathMatch && this.isLanguageSupported(pathMatch[1])) {
|
86
|
+
return pathMatch[1];
|
87
|
+
}
|
88
|
+
|
89
|
+
// 如果网站运行在二级目录,则从路径中提取语言代码
|
90
|
+
// 例如: /docs/zh-cn/components/button
|
91
|
+
const pathMatch2 = currentUrl.match(/^\/([^\/]+)\/([\w-]+)\//);
|
92
|
+
if (pathMatch2 && this.isLanguageSupported(pathMatch2[2])) {
|
93
|
+
return pathMatch2[2];
|
94
|
+
}
|
94
95
|
|
95
|
-
|
96
|
-
|
96
|
+
// 尝试从查询参数中提取语言代码
|
97
|
+
// 例如: ?lang=zh-cn
|
98
|
+
const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
|
99
|
+
const langParam = urlParams.get('lang');
|
100
|
+
if (langParam && this.isLanguageSupported(langParam)) {
|
101
|
+
return langParam;
|
102
|
+
}
|
97
103
|
|
98
|
-
|
99
|
-
if (isLanguageSupported(browserLang)) {
|
100
|
-
return browserLang;
|
104
|
+
return undefined;
|
101
105
|
}
|
102
106
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
107
|
+
/**
|
108
|
+
* 从浏览器设置中获取首选语言
|
109
|
+
* @returns 从浏览器设置中获取的语言代码,如果无法获取则返回undefined
|
110
|
+
*/
|
111
|
+
static getLanguageFromBrowser(): string | undefined {
|
112
|
+
if (typeof navigator === 'undefined') return undefined;
|
113
|
+
|
114
|
+
// 获取浏览器语言
|
115
|
+
const browserLang = navigator.language.toLowerCase();
|
116
|
+
|
117
|
+
// 检查完整匹配
|
118
|
+
if (this.isLanguageSupported(browserLang)) {
|
119
|
+
return browserLang;
|
109
120
|
}
|
110
|
-
}
|
111
121
|
|
112
|
-
|
113
|
-
|
122
|
+
// 检查语言前缀匹配
|
123
|
+
// 例如: zh-TW -> zh-cn
|
124
|
+
const langPrefix = browserLang.split('-')[0];
|
125
|
+
for (const lang of SUPPORTED_LANGUAGES) {
|
126
|
+
if (lang.startsWith(langPrefix)) {
|
127
|
+
return lang;
|
128
|
+
}
|
129
|
+
}
|
114
130
|
|
115
|
-
|
116
|
-
* 自动检测当前语言
|
117
|
-
* @param url 当前URL(可选)
|
118
|
-
* @returns 检测到的语言信息,包括语言代码和来源
|
119
|
-
*/
|
120
|
-
function detectLanguage(url?: string): LanguageInfo {
|
121
|
-
// 尝试从URL中获取语言
|
122
|
-
const urlLang = getLanguageFromURL(url);
|
123
|
-
if (urlLang) {
|
124
|
-
return {
|
125
|
-
code: urlLang as SupportedLanguage,
|
126
|
-
source: LanguageSource.URL
|
127
|
-
};
|
131
|
+
return undefined;
|
128
132
|
}
|
133
|
+
/**
|
134
|
+
* 获取当前语言
|
135
|
+
* @param userLang 用户指定的语言(可选)
|
136
|
+
* @param url 当前URL(可选)
|
137
|
+
* @returns 当前应使用的语言信息,包括语言代码和来源
|
138
|
+
*/
|
139
|
+
static getCurrentLanguage(userLang?: string, url?: string): string {
|
140
|
+
// 如果用户指定了语言,优先使用用户指定的语言
|
141
|
+
if (userLang) {
|
142
|
+
if (this.isLanguageSupported(userLang)) {
|
143
|
+
return userLang as SupportedLanguage;
|
144
|
+
} else {
|
145
|
+
// 用户指定的语言不支持,使用默认语言
|
146
|
+
return DEFAULT_LANGUAGE;
|
147
|
+
}
|
148
|
+
}
|
129
149
|
|
130
|
-
|
131
|
-
|
132
|
-
if (browserLang) {
|
133
|
-
return {
|
134
|
-
code: browserLang as SupportedLanguage,
|
135
|
-
source: LanguageSource.BROWSER
|
136
|
-
};
|
150
|
+
// 否则自动检测语言
|
151
|
+
return this.detectLanguage();
|
137
152
|
}
|
138
153
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
154
|
+
/**
|
155
|
+
* 自动检测当前语言
|
156
|
+
* @param url 当前URL(可选)
|
157
|
+
* @returns 检测到的语言信息,包括语言代码和来源
|
158
|
+
*/
|
159
|
+
static detectLanguage(url?: string): string {
|
160
|
+
// 尝试从URL中获取语言
|
161
|
+
const urlLang = this.getLanguageFromURL(url);
|
162
|
+
if (urlLang) {
|
163
|
+
return urlLang as SupportedLanguage;
|
164
|
+
}
|
145
165
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
* @returns 当前应使用的语言信息,包括语言代码和来源
|
151
|
-
*/
|
152
|
-
export function getCurrentLanguage(userLang?: string, url?: string): LanguageInfo {
|
153
|
-
// 如果用户指定了语言,优先使用用户指定的语言
|
154
|
-
if (userLang) {
|
155
|
-
if (isLanguageSupported(userLang)) {
|
156
|
-
return {
|
157
|
-
code: userLang as SupportedLanguage,
|
158
|
-
source: LanguageSource.USER
|
159
|
-
};
|
160
|
-
} else {
|
161
|
-
// 用户指定的语言不支持,使用默认语言
|
162
|
-
return {
|
163
|
-
code: DEFAULT_LANGUAGE,
|
164
|
-
source: LanguageSource.DEFAULT
|
165
|
-
};
|
166
|
+
// 尝试从浏览器设置中获取语言
|
167
|
+
const browserLang = this.getLanguageFromBrowser();
|
168
|
+
if (browserLang) {
|
169
|
+
return browserLang as SupportedLanguage;
|
166
170
|
}
|
167
|
-
}
|
168
171
|
|
169
|
-
|
170
|
-
|
171
|
-
}
|
172
|
+
// 如果无法检测,返回默认语言
|
173
|
+
return DEFAULT_LANGUAGE;
|
174
|
+
}
|
175
|
+
}
|
package/dist/utils/link.ts
CHANGED
@@ -1,6 +1,22 @@
|
|
1
1
|
import { logger } from "./logger";
|
2
2
|
|
3
3
|
export class LinkUtil {
|
4
|
+
// 从 astro.config.ts 中获取基础路径
|
5
|
+
static getBaseUrl = (): string => {
|
6
|
+
return import.meta.env.BASE_URL
|
7
|
+
};
|
8
|
+
|
9
|
+
// 生成带基础路径的完整 URL
|
10
|
+
static createUrl = (path: string): string => {
|
11
|
+
const baseUrl = LinkUtil.getBaseUrl();
|
12
|
+
// 如果路径以 '/' 开头,去除开头的 '/'
|
13
|
+
if (path.startsWith('/')) {
|
14
|
+
path = path.substring(1);
|
15
|
+
}
|
16
|
+
|
17
|
+
return `${baseUrl}${path}`;
|
18
|
+
};
|
19
|
+
|
4
20
|
/**
|
5
21
|
* 规范化语言代码
|
6
22
|
* @param lang - 语言代码
|
@@ -16,7 +32,7 @@ export class LinkUtil {
|
|
16
32
|
}
|
17
33
|
|
18
34
|
static getHomeLink(lang: string): string {
|
19
|
-
return
|
35
|
+
return `${this.getBaseUrl}/${lang}`;
|
20
36
|
}
|
21
37
|
|
22
38
|
static getLessonsLink(lang: string): string {
|
@@ -213,4 +229,14 @@ export class LinkUtil {
|
|
213
229
|
static getLoginLink(currentOrigin: string): string {
|
214
230
|
return `${currentOrigin}/api/login`;
|
215
231
|
}
|
216
|
-
|
232
|
+
|
233
|
+
static getActiveLink(currentLink: string, links: string[]): string {
|
234
|
+
let activeLink = '';
|
235
|
+
for (const link of links) {
|
236
|
+
if (currentLink.startsWith(link) && link.length > activeLink.length) {
|
237
|
+
activeLink = link;
|
238
|
+
}
|
239
|
+
}
|
240
|
+
return activeLink;
|
241
|
+
}
|
242
|
+
}
|
package/package.json
CHANGED
package/dist/i18n/Language.ts
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
export type LangCode = 'zh-cn' | 'en';
|
2
|
-
|
3
|
-
export default class Language {
|
4
|
-
code: LangCode;
|
5
|
-
|
6
|
-
constructor(code: LangCode) {
|
7
|
-
this.code = code;
|
8
|
-
}
|
9
|
-
|
10
|
-
static fromString(lang: string): Language {
|
11
|
-
return new Language(lang as LangCode);
|
12
|
-
}
|
13
|
-
|
14
|
-
isChinese(): boolean {
|
15
|
-
return this.code === 'zh-cn';
|
16
|
-
}
|
17
|
-
|
18
|
-
isEnglish(): boolean {
|
19
|
-
return this.code === 'en';
|
20
|
-
}
|
21
|
-
|
22
|
-
static isCodeValid(code: string): boolean {
|
23
|
-
return code === 'zh-cn' || code === 'en';
|
24
|
-
}
|
25
|
-
}
|
package/dist/i18n/ui.ts
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
export const languages = {
|
2
|
-
en: 'English',
|
3
|
-
'zh-cn': '简体中文',
|
4
|
-
};
|
5
|
-
|
6
|
-
export const defaultLang = 'zh-cn';
|
7
|
-
|
8
|
-
export const ui = {
|
9
|
-
en: {
|
10
|
-
'nav.home': 'Home',
|
11
|
-
'nav.about': 'About',
|
12
|
-
'nav.twitter': 'Twitter',
|
13
|
-
},
|
14
|
-
'zh-cn': {
|
15
|
-
'nav.home': '首页',
|
16
|
-
'nav.about': '关于',
|
17
|
-
},
|
18
|
-
} as const;
|
package/dist/i18n/utils.ts
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
import { ui, defaultLang } from './ui';
|
2
|
-
|
3
|
-
export function getLangFromUrl(url: URL) {
|
4
|
-
const [, lang] = url.pathname.split('/');
|
5
|
-
if (lang in ui) return lang as keyof typeof ui;
|
6
|
-
return defaultLang;
|
7
|
-
}
|
8
|
-
|
9
|
-
export function t(lang: string, key: keyof typeof ui[typeof defaultLang]) {
|
10
|
-
return ui[lang in ui ? lang as keyof typeof ui : defaultLang][key];
|
11
|
-
}
|
12
|
-
|
13
|
-
export const normalizeLang = (lang: string) => {
|
14
|
-
if (lang === 'zh-CN') {
|
15
|
-
return 'zh-cn';
|
16
|
-
}
|
17
|
-
return lang;
|
18
|
-
}
|
19
|
-
|
20
|
-
export const getLangFromSlug = (slug: string) => {
|
21
|
-
let lang = slug.split('/')[0];
|
22
|
-
|
23
|
-
return normalizeLang(lang);
|
24
|
-
}
|
25
|
-
|
26
|
-
export const getLangFromPathname = (pathname: string) => {
|
27
|
-
// 去除开头的 /
|
28
|
-
pathname = pathname.slice(1);
|
29
|
-
|
30
|
-
let lang = pathname.split('/')[0];
|
31
|
-
|
32
|
-
return normalizeLang(lang);
|
33
|
-
}
|
34
|
-
|
35
|
-
export const isValidLang = (lang: string) => {
|
36
|
-
return ['zh-cn', 'en'].includes(lang);
|
37
|
-
}
|
38
|
-
|
39
|
-
export const getLang = (...args: string[]) => {
|
40
|
-
const debug = false
|
41
|
-
|
42
|
-
if (debug) {
|
43
|
-
// eslint-disable-next-line no-console
|
44
|
-
console.log('getLang', args);
|
45
|
-
}
|
46
|
-
|
47
|
-
for (const arg of args) {
|
48
|
-
let normalizedLang = normalizeLang(arg);
|
49
|
-
if (isValidLang(normalizedLang)) {
|
50
|
-
return normalizedLang;
|
51
|
-
}
|
52
|
-
}
|
53
|
-
return 'zh-cn';
|
54
|
-
}
|