@coffic/cosy-ui 0.4.9 → 0.5.4

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.
@@ -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
- * @param lang 要检查的语言代码
31
- * @returns 如果语言被支持返回true,否则返回false
32
- */
33
- export function isLanguageSupported(lang?: string): lang is SupportedLanguage {
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
- * 从URL中提取语言代码
52
- * @param url 当前URL(可选)
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
- if (currentUrl === undefined) {
59
- if (typeof window === 'undefined') return undefined;
60
- currentUrl = window.location.href;
30
+ return result;
61
31
  }
62
32
 
63
- // 尝试从路径中提取语言代码
64
- // 例如: /zh-cn/components/button
65
- const pathMatch = currentUrl.match(/^\/([\w-]+)\//);
66
- if (pathMatch && isLanguageSupported(pathMatch[1])) {
67
- return pathMatch[1];
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
- // 例如: /docs/zh-cn/components/button
72
- const pathMatch2 = currentUrl.match(/^\/([^\/]+)\/([\w-]+)\//);
73
- if (pathMatch2 && isLanguageSupported(pathMatch2[2])) {
74
- return pathMatch2[2];
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
- // 例如: ?lang=zh-cn
79
- const urlParams = new URLSearchParams(currentUrl.split('?')[1]);
80
- const langParam = urlParams.get('lang');
81
- if (langParam && isLanguageSupported(langParam)) {
82
- return langParam;
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
- return undefined;
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
- * @returns 从浏览器设置中获取的语言代码,如果无法获取则返回undefined
91
- */
92
- function getLanguageFromBrowser(): string | undefined {
93
- if (typeof navigator === 'undefined') return undefined;
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
- const browserLang = navigator.language.toLowerCase();
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
- // 例如: zh-TW -> zh-cn
105
- const langPrefix = browserLang.split('-')[0];
106
- for (const lang of SUPPORTED_LANGUAGES) {
107
- if (lang.startsWith(langPrefix)) {
108
- return lang;
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
- return undefined;
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
- const browserLang = getLanguageFromBrowser();
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
- return {
141
- code: DEFAULT_LANGUAGE,
142
- source: LanguageSource.DEFAULT
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
- * @param userLang 用户指定的语言(可选)
149
- * @param url 当前URL(可选)
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
- return detectLanguage();
171
- }
172
+ // 如果无法检测,返回默认语言
173
+ return DEFAULT_LANGUAGE;
174
+ }
175
+ }
@@ -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 `/${lang}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffic/cosy-ui",
3
- "version": "0.4.9",
3
+ "version": "0.5.4",
4
4
  "description": "An astro component library",
5
5
  "author": {
6
6
  "name": "nookery",
@@ -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;
@@ -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
- }