feat: migrate language code
All checks were successful
Deploy / Deploy (push) Successful in 1m15s

This commit is contained in:
CDN 2025-02-03 22:52:56 +08:00
parent 47cf6171c5
commit 9ac43ef4f9
Signed by: CDN
GPG key ID: 0C656827F9F80080
86 changed files with 119 additions and 80 deletions

View file

@ -46,7 +46,7 @@ homepage
## Contribution Guide
See [CONTRIBUTING.md](docs/en-US/CONTRIBUTING.md) in the `docs` directory to learn how to participate in the project.
See [CONTRIBUTING.md](docs/en/CONTRIBUTING.md) in the `docs` directory to learn how to participate in the project.
## License

View file

@ -46,7 +46,7 @@ homepage
## 贡献指南
参见 `docs` 目录中的 [CONTRIBUTING.md](docs/zh-CN/CONTRIBUTING.md) 了解如何参与项目。
参见 `docs` 目录中的 [CONTRIBUTING.md](docs/zh-Hans/CONTRIBUTING.md) 了解如何参与项目。
## 许可

View file

@ -46,7 +46,7 @@ homepage
## 貢獻指南
參見 `docs` 目錄中的 [CONTRIBUTING.md](docs/zh-TW/CONTRIBUTING.md) 了解如何參與專案。
參見 `docs` 目錄中的 [CONTRIBUTING.md](docs/zh-Hant/CONTRIBUTING.md) 了解如何參與專案。
## 許可

View file

@ -1,3 +1,3 @@
- [简体中文](./zh-CN/)
- [繁體中文](./zh-TW/)
- [English](./en-US/)
- [简体中文](./zh-Hans/)
- [繁體中文](./zh-Hant/)
- [English](./en/)

View file

@ -14,22 +14,22 @@ interface LanguageConfig {
const LANGUAGES: LanguageConfig[] = [
{
code: 'en-US',
dataDir: 'en-US',
title: 'STARSET Mirror Site Updates',
description: 'Latest updates from STARSET Mirror Site'
code: 'en',
dataDir: 'en',
title: 'Starset Mirror - Updates',
description: 'Latest updates from Starset Mirror'
},
{
code: 'zh-CN',
dataDir: 'zh-CN',
title: 'STARSET Mirror 项目动态',
description: 'STARSET Mirror 最新动态'
code: 'zh-Hans',
dataDir: 'zh-Hans',
title: 'Starset Mirror - 更新',
description: 'Starset Mirror 的最新更新'
},
{
code: 'zh-Hant',
dataDir: 'zh-TW',
title: 'STARSET Mirror 專案動态',
description: 'STARSET Mirror 最新動態'
dataDir: 'zh-Hant',
title: 'Starset Mirror - 更新',
description: 'Starset Mirror 的最新更新'
}
];

View file

@ -6,7 +6,7 @@ import xml2js from 'xml2js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const LANGUAGES = ['en-US', 'zh-CN', 'zh-TW'];
const LANGUAGES = ['en', 'zh-Hans', 'zh-Hant'];
const BASE_URL = 'mirror.starset.fans';
interface Update {

View file

@ -6,7 +6,7 @@ import xml2js from 'xml2js';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const LANGUAGES = ['en-US', 'zh-CN', 'zh-TW'];
const LANGUAGES = ['en', 'zh-Hans', 'zh-Hant'];
const BASE_URL = 'starset.wiki'; // Replace with your actual domain
async function getYearlyIndices(lang) {

View file

@ -4,22 +4,24 @@ import iconMap from '../utils/iconMap';
const About = () => {
const { t } = useTranslation();
const aboutData = t('data.about', { returnObjects: true });
const socialLinks = t('social.links', { returnObjects: true });
const aboutData = t('data.about', { returnObjects: true }) || {};
const socialLinks = t('social.links', { returnObjects: true }) || [];
if (!aboutData) return null;
return (
<div className="container mx-auto px-2 md:px-4">
<div className="max-w-3xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-8 text-gray-900 dark:text-white">{t('about.title')}</h2>
<div className="prose prose-lg mx-auto dark:prose-invert">
{aboutData.content.intro.map((paragraph: string, index: number) => (
{aboutData.content?.intro?.map((paragraph: string, index: number) => (
<p key={index} className="text-gray-600 dark:text-gray-300 mb-6">
{paragraph}
</p>
))}
<div className="grid grid-cols-1 md:grid-cols-3 gap-8 mt-12">
{aboutData.stats.map((stat: { value: string; label: string }, index: number) => (
{aboutData.stats?.map((stat: { value: string; label: string }, index: number) => (
<div key={index} className="text-center">
<h3 className="text-4xl font-bold text-blue-600 dark:text-blue-400 mb-2">{stat.value}</h3>
<p className="text-gray-600 dark:text-gray-300">{stat.label}</p>
@ -27,7 +29,7 @@ const About = () => {
))}
</div>
{aboutData.content.workScope && (
{aboutData.content?.workScope && (
<div className="mt-12">
<h3 className="text-2xl font-semibold mb-6 text-gray-900 dark:text-white"></h3>
<ol className="space-y-4">
@ -36,7 +38,7 @@ const About = () => {
<span className="flex-shrink-0 w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 dark:bg-blue-900 text-blue-600 dark:text-blue-400 font-semibold">
{index + 1}
</span>
<span className="text-lg">{item}</span>
<span>{item}</span>
</li>
))}
</ol>
@ -67,14 +69,14 @@ const About = () => {
<div className="mt-12">
<h3 className="text-2xl font-semibold mb-4 text-gray-900 dark:text-white">{t('about.join.title')}</h3>
<p className="text-gray-600 dark:text-gray-300">
{aboutData.content.contact.description}
{aboutData.content?.contact?.description}
</p>
<div className="mt-4">
<a
href={`mailto:${aboutData.content.contact.email}`}
href={`mailto:${aboutData.content?.contact?.email}`}
className="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300 no-underline"
>
{aboutData.content.contact.email}
{aboutData.content?.contact?.email}
</a>
</div>
</div>

View file

@ -1,5 +1,6 @@
import { useCallback, useRef, useEffect } from 'react'
import { useLocation } from 'react-router-dom'
import { useTranslation } from 'react-i18next'
import 'artalk/dist/Artalk.css'
import Artalk from 'artalk'
@ -9,6 +10,7 @@ interface CommentsProps {
const Comments = ({ title }: CommentsProps) => {
const { pathname } = useLocation()
const { t } = useTranslation()
const artalkRef = useRef<Artalk>()
const containerRef = useRef<HTMLDivElement>(null)

View file

@ -23,7 +23,8 @@ interface Contributor {
const Contributors: React.FC = () => {
const { t } = useTranslation();
const members = t('data.contributors.members', { returnObjects: true }) as Contributor[];
const contributorsData = t('data.contributors', { returnObjects: true }) || {};
const members = Array.isArray(contributorsData.members) ? contributorsData.members : [];
// Fisher-Yates shuffle algorithm
const shuffleArray = <T,>(array: T[]): T[] => {

View file

@ -8,9 +8,9 @@ const LanguageSwitcher = () => {
const menuRef = useRef<HTMLDivElement>(null);
const languages = [
{ code: 'zh-CN', name: '简体中文' },
{ code: 'zh-TW', name: '繁體中文' },
{ code: 'en-US', name: 'English' }
{ code: 'zh-Hans', name: '简体中文' },
{ code: 'zh-Hant', name: '繁體中文' },
{ code: 'en', name: 'English' }
];
useEffect(() => {

View file

@ -13,18 +13,19 @@ interface Project {
const Projects = () => {
const { t } = useTranslation();
const projects = t('data.projects.projects', { returnObjects: true }) as Project[];
const projectsData = t('data.projects', { returnObjects: true }) || {};
const projects = Array.isArray(projectsData.projects) ? projectsData.projects : [];
const [selectedTags, setSelectedTags] = useState<string[]>([]);
// 获取所有可用的标签
const allTags = Array.from(
new Set(projects.flatMap(project => project.tags))
new Set(projects.flatMap(project => project.tags || []))
);
// 根据选中的标签筛选项目
const filteredProjects = selectedTags.length > 0
? projects.filter(project =>
selectedTags.every(tag => project.tags.includes(tag))
selectedTags.every(tag => (project.tags || []).includes(tag))
)
: projects;
@ -119,7 +120,7 @@ const Projects = () => {
{/* 标签区域固定高度 */}
<div className="h-8 mb-4 flex items-center overflow-x-auto">
<div className="flex gap-2">
{project.tags.map((tag) => (
{(project.tags || []).map((tag) => (
<button
key={tag}
onClick={() => handleTagClick(tag)}

View file

@ -1,7 +1,7 @@
import React from 'react';
import { Card, Avatar, Tag } from 'antd';
import { GithubOutlined, TwitterOutlined } from '@ant-design/icons';
import sponsorsData from '../../data/zh-CN/sponsors.json';
import sponsorsData from '../../data/zh-Hans/sponsors.json';
import { useTranslation } from 'react-i18next';
interface SponsorType {
@ -13,26 +13,33 @@ interface SponsorType {
github?: string;
twitter?: string;
};
name: string;
name_zh_TW?: string;
name_en?: string;
}
const Sponsors: React.FC = () => {
const { i18n } = useTranslation();
const getMessage = () => {
const getMessage = (sponsor: SponsorType) => {
switch (i18n.language) {
case 'zh-Hans':
case 'zh-CN':
return '赞助人信息仍在同步,将在晚些时候上线。';
return sponsor.name;
case 'zh-Hant':
case 'zh-TW':
return '贊助人資訊仍在同步,將在晚些時候上線。';
return sponsor.name_zh_TW || sponsor.name;
case 'en':
case 'en-US':
return sponsor.name_en || sponsor.name;
default:
return 'Sponsor information is still being synchronized and will be available later.';
return sponsor.name;
}
};
return (
<div className="flex items-center justify-center min-h-[300px] text-gray-600 dark:text-gray-300 text-lg">
{getMessage()}
{getMessage(sponsorsData[0])}
</div>
);
@ -51,7 +58,7 @@ const Sponsors: React.FC = () => {
/>
</div>
)}
<h3 className="text-lg font-semibold mb-3 text-gray-900 dark:text-white">{sponsor.nickname}</h3>
<h3 className="text-lg font-semibold mb-3 text-gray-900 dark:text-white">{getMessage(sponsor)}</h3>
<div className="space-y-4">
<div className="flex justify-center gap-3">
<Tag color="gold">{sponsor.year}</Tag>

View file

@ -62,6 +62,7 @@ const Pagination: React.FC<PaginationProps> = ({
const Timeline = () => {
const { t, i18n } = useTranslation();
const LANGUAGE_CODE_MAP: Record<string, string> = {
'zh-CN': 'zh-Hans',
'zh-TW': 'zh-Hant'
};
const [searchParams, setSearchParams] = useSearchParams();

View file

@ -36,10 +36,33 @@ const TranslationBackground = () => {
setIsVisible(true);
}, []);
const getLanguageName = (language: string) => {
switch (language) {
case 'zh-Hans':
return '简体中文';
case 'zh-Hant':
return '繁體中文';
default:
return 'English';
}
};
const getTranslationName = (language: string) => {
switch (language) {
case 'zh-Hans':
case 'zh-Hant':
return '翻译';
default:
return 'Translation';
}
};
const getMainText = (translation: typeof translations.translations[0]) => {
switch (i18n.language) {
case 'zh-Hans':
case 'zh-CN':
return translation.zh_CN;
case 'zh-Hant':
case 'zh-TW':
return translation.zh_TW;
default:
@ -49,7 +72,9 @@ const TranslationBackground = () => {
const getSecondaryText = (translation: typeof translations.translations[0]) => {
switch (i18n.language) {
case 'zh-Hans':
case 'zh-CN':
case 'zh-Hant':
case 'zh-TW':
return translation.en;
default:

View file

@ -3,42 +3,42 @@ import { initReactI18next } from 'react-i18next';
import LanguageDetector from 'i18next-browser-languagedetector';
// 导入翻译文件
import zhCNTranslation from '../../data/zh-CN/index.json';
import zhTWTranslation from '../../data/zh-TW/index.json';
import enUSTranslation from '../../data/en-US/index.json';
import zhHansTranslation from '../../data/zh-Hans/index.json';
import zhHantTranslation from '../../data/zh-Hant/index.json';
import enTranslation from '../../data/en/index.json';
// 导入数据文件
import zhCNAbout from '../../data/zh-CN/about.json';
import zhCNProjects from '../../data/zh-CN/projects.json';
import zhCNContributors from '../../data/zh-CN/contributors.json';
import zhCNUpdates from '../../data/zh-CN/updates.json';
import zhHansAbout from '../../data/zh-Hans/about.json';
import zhHansProjects from '../../data/zh-Hans/projects.json';
import zhHansContributors from '../../data/zh-Hans/contributors.json';
import zhHansUpdates from '../../data/zh-Hans/updates.json';
import zhTWAbout from '../../data/zh-TW/about.json';
import zhTWProjects from '../../data/zh-TW/projects.json';
import zhTWContributors from '../../data/zh-TW/contributors.json';
import zhTWUpdates from '../../data/zh-TW/updates.json';
import zhHantAbout from '../../data/zh-Hant/about.json';
import zhHantProjects from '../../data/zh-Hant/projects.json';
import zhHantContributors from '../../data/zh-Hant/contributors.json';
import zhHantUpdates from '../../data/zh-Hant/updates.json';
import enUSAbout from '../../data/en-US/about.json';
import enUSProjects from '../../data/en-US/projects.json';
import enUSContributors from '../../data/en-US/contributors.json';
import enUSUpdates from '../../data/en-US/updates.json';
import enAbout from '../../data/en/about.json';
import enProjects from '../../data/en/projects.json';
import enContributors from '../../data/en/contributors.json';
import enUpdates from '../../data/en/updates.json';
i18n
.use(LanguageDetector)
.use(initReactI18next)
.init({
resources: {
'zh-CN': {
translation: zhCNTranslation.translation
'zh-Hans': {
translation: zhHansTranslation.translation
},
'zh-TW': {
translation: zhTWTranslation.translation
'zh-Hant': {
translation: zhHantTranslation.translation
},
'en-US': {
translation: enUSTranslation.translation
'en': {
translation: enTranslation.translation
}
},
fallbackLng: 'zh-CN',
fallbackLng: 'zh-Hans',
interpolation: {
escapeValue: false,
},
@ -48,30 +48,30 @@ i18n
});
// 添加数据命名空间
i18n.addResourceBundle('zh-CN', 'translation', {
i18n.addResourceBundle('zh-Hans', 'translation', {
data: {
about: zhCNAbout,
projects: zhCNProjects,
contributors: zhCNContributors,
updates: zhCNUpdates
about: zhHansAbout,
projects: zhHansProjects,
contributors: zhHansContributors,
updates: zhHansUpdates
}
}, true, true);
i18n.addResourceBundle('zh-TW', 'translation', {
i18n.addResourceBundle('zh-Hant', 'translation', {
data: {
about: zhTWAbout,
projects: zhTWProjects,
contributors: zhTWContributors,
updates: zhTWUpdates
about: zhHantAbout,
projects: zhHantProjects,
contributors: zhHantContributors,
updates: zhHantUpdates
}
}, true, true);
i18n.addResourceBundle('en-US', 'translation', {
i18n.addResourceBundle('en', 'translation', {
data: {
about: enUSAbout,
projects: enUSProjects,
contributors: enUSContributors,
updates: enUSUpdates
about: enAbout,
projects: enProjects,
contributors: enContributors,
updates: enUpdates
}
}, true, true);