refactor: reorganize project structure and improve type definitions
- Split types into separate modules for better organization - Move data files to dedicated directories with proper documentation - Enhance i18n utilities with better type safety and performance - Maintain backward compatibility with legacy imports
This commit is contained in:
143
src/i18n/translations.ts
Normal file
143
src/i18n/translations.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
/**
|
||||
* Translations module
|
||||
* Contains all translation data for the application
|
||||
*/
|
||||
|
||||
/**
|
||||
* Available languages in the application
|
||||
*/
|
||||
export const languages = {
|
||||
en: 'English',
|
||||
zh: '简体中文',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Default language for the application
|
||||
*/
|
||||
export const defaultLang = 'en';
|
||||
|
||||
/**
|
||||
* Structured translations data
|
||||
* Organized by language and feature area
|
||||
*/
|
||||
export const translations = {
|
||||
en: {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
services: 'Services',
|
||||
projects: 'Projects',
|
||||
blog: 'Blog',
|
||||
contact: 'Contact',
|
||||
},
|
||||
site: {
|
||||
title: 'Joy Zhao - Full Stack Developer',
|
||||
description: 'Full Stack Developer specializing in React, Node.js, and modern web technologies',
|
||||
},
|
||||
hero: {
|
||||
githubLink: 'GitHub Profile',
|
||||
linkedinLink: 'LinkedIn Profile',
|
||||
},
|
||||
footer: {
|
||||
rights: 'All rights reserved',
|
||||
},
|
||||
project: {
|
||||
tag: {
|
||||
business: 'Business Project',
|
||||
opensource: 'Open Source',
|
||||
personal: 'Personal Product',
|
||||
portfolio: 'Portfolio',
|
||||
ecommerce: 'E-Commerce',
|
||||
},
|
||||
visit: 'Visit',
|
||||
demo: 'Live Demo',
|
||||
},
|
||||
projects: {
|
||||
title: 'My Projects',
|
||||
description: 'A collection of my recent work, showcasing innovative solutions and clean code. Explore the details of each project below.',
|
||||
slogan: 'Crafting elegant solutions to complex problems with clean code and innovative thinking.',
|
||||
},
|
||||
blog: {
|
||||
slogan: 'This is where innovative thinking meets complex problems.',
|
||||
},
|
||||
},
|
||||
zh: {
|
||||
nav: {
|
||||
home: '首页',
|
||||
about: '关于',
|
||||
services: '服务',
|
||||
projects: '项目',
|
||||
blog: '博客',
|
||||
contact: '联系',
|
||||
},
|
||||
site: {
|
||||
title: 'Joy Zhao - 全栈开发者',
|
||||
description: '专注于 React、Node.js 和现代 Web 技术的全栈开发者',
|
||||
},
|
||||
hero: {
|
||||
githubLink: 'GitHub 主页',
|
||||
linkedinLink: 'LinkedIn 主页',
|
||||
},
|
||||
footer: {
|
||||
rights: '版权所有',
|
||||
},
|
||||
project: {
|
||||
tag: {
|
||||
business: '商业项目',
|
||||
opensource: '开源项目',
|
||||
personal: '个人产品',
|
||||
portfolio: '作品集',
|
||||
ecommerce: '电子商务',
|
||||
},
|
||||
visit: '访问',
|
||||
demo: '在线演示',
|
||||
},
|
||||
projects: {
|
||||
title: '我的项目',
|
||||
description: '这里展示了我最近的作品集,展现了创新解决方案和整洁的代码。请浏览下方了解每个项目的详细信息。',
|
||||
slogan: '用优雅的代码和创新的思维,为复杂问题打造精致的解决方案。',
|
||||
},
|
||||
blog: {
|
||||
slogan: '这里是创新思维与复杂问题相遇的地方。',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Generate flattened translations for better performance
|
||||
* This creates a flat object with dot notation keys
|
||||
*/
|
||||
export function generateFlatTranslations() {
|
||||
const flattenedTranslations: Record<string, Record<string, string>> = {};
|
||||
|
||||
// Initialize for each language
|
||||
Object.keys(translations).forEach(lang => {
|
||||
flattenedTranslations[lang] = {};
|
||||
});
|
||||
|
||||
// Recursive function to flatten nested objects
|
||||
function flatten(obj: any, lang: string, prefix = '') {
|
||||
for (const key in obj) {
|
||||
const value = obj[key];
|
||||
const newKey = prefix ? `${prefix}.${key}` : key;
|
||||
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
flatten(value, lang, newKey);
|
||||
} else {
|
||||
flattenedTranslations[lang][newKey] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process each language
|
||||
Object.keys(translations).forEach(lang => {
|
||||
flatten(translations[lang as keyof typeof translations], lang);
|
||||
});
|
||||
|
||||
return flattenedTranslations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flattened translations for faster lookups
|
||||
*/
|
||||
export const flatTranslations = generateFlatTranslations();
|
||||
134
src/i18n/ui.ts
134
src/i18n/ui.ts
@@ -1,123 +1,30 @@
|
||||
// src/i18n/ui.ts
|
||||
export const languages = {
|
||||
en: 'English',
|
||||
zh: '简体中文',
|
||||
} as const;
|
||||
/**
|
||||
* UI internationalization module
|
||||
* This file is maintained for backward compatibility
|
||||
* New code should import from translations.ts directly
|
||||
*/
|
||||
|
||||
export const defaultLang = 'en';
|
||||
// Re-export from translations.ts
|
||||
export { languages, defaultLang, translations as structuredUI } from './translations';
|
||||
|
||||
// 定义嵌套结构的国际化数据
|
||||
const structuredUi = {
|
||||
en: {
|
||||
nav: {
|
||||
home: 'Home',
|
||||
about: 'About',
|
||||
services: 'Services',
|
||||
projects: 'Projects',
|
||||
blog: 'Blog',
|
||||
contact: 'Contact',
|
||||
},
|
||||
site: {
|
||||
title: 'Joy Zhao - Full Stack Developer',
|
||||
description: 'Full Stack Developer specializing in React, Node.js, and modern web technologies',
|
||||
},
|
||||
hero: {
|
||||
githubLink: 'GitHub Profile',
|
||||
linkedinLink: 'LinkedIn Profile',
|
||||
},
|
||||
footer: {
|
||||
rights: 'All rights reserved',
|
||||
},
|
||||
project: {
|
||||
tag: {
|
||||
business: 'Business Project',
|
||||
opensource: 'Open Source',
|
||||
personal: 'Personal Product',
|
||||
portfolio: 'Portfolio',
|
||||
ecommerce: 'E-Commerce',
|
||||
},
|
||||
visit: 'Visit',
|
||||
demo: 'Live Demo',
|
||||
},
|
||||
projects: {
|
||||
title: 'My Projects',
|
||||
description: 'A collection of my recent work, showcasing innovative solutions and clean code. Explore the details of each project below.',
|
||||
slogan: 'Crafting elegant solutions to complex problems with clean code and innovative thinking.',
|
||||
},
|
||||
blog: {
|
||||
slogan: 'This is where innovative thinking meets complex problems.',
|
||||
},
|
||||
// Projects and services content has been inlined into respective page files
|
||||
// to reduce reliance on the translation system and improve maintainability
|
||||
},
|
||||
zh: {
|
||||
nav: {
|
||||
home: '首页',
|
||||
about: '关于',
|
||||
services: '服务',
|
||||
projects: '项目',
|
||||
blog: '博客',
|
||||
contact: '联系',
|
||||
},
|
||||
site: {
|
||||
title: 'Joy Zhao - 全栈开发者',
|
||||
description: '专注于 React、Node.js 和现代 Web 技术的全栈开发者',
|
||||
},
|
||||
hero: {
|
||||
githubLink: 'GitHub 主页',
|
||||
linkedinLink: 'LinkedIn 主页',
|
||||
},
|
||||
footer: {
|
||||
rights: '版权所有',
|
||||
},
|
||||
project: {
|
||||
tag: {
|
||||
business: '商业项目',
|
||||
opensource: '开源项目',
|
||||
personal: '个人产品',
|
||||
portfolio: '作品集',
|
||||
ecommerce: '电子商务',
|
||||
},
|
||||
visit: '访问',
|
||||
demo: '在线演示',
|
||||
},
|
||||
projects: {
|
||||
title: '我的项目',
|
||||
description: '这里展示了我最近的作品集,展现了创新解决方案和整洁的代码。请浏览下方了解每个项目的详细信息。',
|
||||
slogan: '用优雅的代码和创新的思维,为复杂问题打造精致的解决方案。',
|
||||
},
|
||||
blog: {
|
||||
slogan: '这里是创新思维与复杂问题相遇的地方。',
|
||||
},
|
||||
// Projects and services content has been inlined into respective page files
|
||||
// to reduce reliance on the translation system and improve maintainability
|
||||
},
|
||||
} as const;
|
||||
// Import flattened translations for compatibility layer
|
||||
import { flatTranslations } from './translations';
|
||||
|
||||
// 创建代理对象,保持与现有代码的兼容性
|
||||
/**
|
||||
* Create a backward compatible UI object
|
||||
* This maintains the old API for existing code
|
||||
*/
|
||||
function createCompatibleUi() {
|
||||
// 为每种语言创建代理
|
||||
const compatibleUi: Record<string, any> = {};
|
||||
|
||||
Object.keys(structuredUi).forEach(lang => {
|
||||
// Create a proxy for each language
|
||||
Object.keys(flatTranslations).forEach(lang => {
|
||||
compatibleUi[lang] = new Proxy({}, {
|
||||
get(target, prop) {
|
||||
if (typeof prop !== 'string') return undefined;
|
||||
|
||||
// 处理点符号键 (如 'nav.home')
|
||||
const parts = prop.split('.');
|
||||
let value: any = structuredUi[lang as keyof typeof structuredUi];
|
||||
|
||||
// 遍历嵌套结构
|
||||
for (const part of parts) {
|
||||
if (value && typeof value === 'object' && part in value) {
|
||||
value = value[part as keyof typeof value];
|
||||
} else {
|
||||
return undefined; // 键不存在
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
// Return the translation from flattened translations
|
||||
return flatTranslations[lang][prop];
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -125,8 +32,5 @@ function createCompatibleUi() {
|
||||
return compatibleUi;
|
||||
}
|
||||
|
||||
// 导出兼容的UI对象
|
||||
export const ui = createCompatibleUi() as typeof structuredUi;
|
||||
|
||||
// 导出结构化的UI对象,供将来使用
|
||||
export const structuredUI = structuredUi;
|
||||
// Export the compatible UI object
|
||||
export const ui = createCompatibleUi();
|
||||
@@ -1,25 +1,68 @@
|
||||
// src/i18n/utils.ts
|
||||
import { ui, defaultLang, languages } from './ui';
|
||||
import { type Lang, type UiKeys } from '@/types';
|
||||
|
||||
// 重新导出类型,以保持向后兼容性
|
||||
export type { Lang, UiKeys };
|
||||
/**
|
||||
* Internationalization utilities module
|
||||
* Contains helper functions for i18n functionality
|
||||
*/
|
||||
import { defaultLang, languages, flatTranslations } from './translations';
|
||||
import type { Lang } from '@/types/i18n';
|
||||
|
||||
/**
|
||||
* Translation key type
|
||||
* Represents a dot-notation path to a translation string
|
||||
*/
|
||||
export type TranslationKey = string;
|
||||
|
||||
/**
|
||||
* Get translation function
|
||||
* Returns a function that can be used to get translations for a specific language
|
||||
*
|
||||
* @param lang - The language to get translations for
|
||||
* @returns A translation function
|
||||
*/
|
||||
export function useTranslations(lang: Lang | undefined) {
|
||||
const currentLang = lang || defaultLang;
|
||||
return function t(key: UiKeys, ...args: any[]): string {
|
||||
// 使用类型断言解决索引问题
|
||||
let translation: string = (ui[currentLang] as any)[key] || (ui[defaultLang] as any)[key];
|
||||
|
||||
/**
|
||||
* Translation function
|
||||
* Gets a translation string for the specified key and replaces placeholders
|
||||
*
|
||||
* @param key - The translation key (dot notation)
|
||||
* @param args - Optional arguments to replace placeholders in the translation
|
||||
* @returns The translated string
|
||||
*/
|
||||
return function t(key: TranslationKey, ...args: any[]): string {
|
||||
// Get translation from flattened translations for better performance
|
||||
let translation = flatTranslations[currentLang][key];
|
||||
|
||||
// Fallback to default language if translation not found
|
||||
if (!translation && currentLang !== defaultLang) {
|
||||
translation = flatTranslations[defaultLang][key];
|
||||
}
|
||||
|
||||
// Fallback to key if translation not found in any language
|
||||
if (!translation) {
|
||||
console.warn(`Translation key not found: ${key}`);
|
||||
return key;
|
||||
}
|
||||
|
||||
// Replace placeholders with arguments
|
||||
if (args.length > 0) {
|
||||
args.forEach((arg, index) => {
|
||||
translation = translation.replace(`{${index}}`, arg);
|
||||
translation = translation.replace(`{${index}}`, String(arg));
|
||||
});
|
||||
}
|
||||
|
||||
return translation;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized path
|
||||
* Adds language prefix to path if needed
|
||||
*
|
||||
* @param path - The path to localize
|
||||
* @param lang - The language to localize for
|
||||
* @returns The localized path
|
||||
*/
|
||||
export function getLocalizedPath(path: string, lang: Lang | undefined): string {
|
||||
const currentLang = lang || defaultLang;
|
||||
const basePath = import.meta.env.BASE_URL === '/' ? '' : import.meta.env.BASE_URL;
|
||||
|
||||
Reference in New Issue
Block a user