Skip to content

Commit

Permalink
💦 feat: i18n deep complex types keys (#30)
Browse files Browse the repository at this point in the history
  • Loading branch information
pdsuwwz authored Aug 21, 2024
1 parent ecba8c3 commit c0c6b97
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 35 deletions.
30 changes: 4 additions & 26 deletions src/components/HomepageHero/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { HoverEffect } from '@/components/ui/card-hover-effect'
import { cn } from '@/lib/utils'

import { PanelParticles } from '@/components/PanelParticles'
import { useLocale } from '@/hooks'

export const StackItem = ({
className,
Expand All @@ -25,32 +26,9 @@ export const StackItem = ({
}

export default function HomepageHero() {
const featureList = [
{
title: '先进的技术栈',
description: '高效的 React 框架和类型安全支持,使用 Next.js、TypeScript、TypeScript、和 Shadcn UI 打造现代化应用',
},
{
title: 'Tailwind CSS & Iconify 图标集',
description: '原子化 CSS, 集成 Tailwind CSS 和 Iconify 图标集,轻松实现高效设计、响应式界面 UI',
},
{
title: '暗黑模式',
description: '支持暗黑模式,提供更好的夜间使用体验',
},
{
title: '代码规范',
description: '遵循最佳实践的代码规范,结合 ESLint 进行代码质量检查与一致性维护',
},
{
title: '丰富组件 & 支持自由扩展',
description: '提供丰富的预置组件,并支持灵活的自定义扩展',
},
{
title: '轻量化设计',
description: '采用轻量化设计,精简项目设置,专注于内容编写',
},
]
const { t } = useLocale()

const featureList = t('featureList')

const { resolvedTheme } = useTheme()

Expand Down
12 changes: 8 additions & 4 deletions src/hooks/useLocale.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { useCallback } from 'react'
import { useRouter } from 'nextra/hooks'

import type { I18nLangKeys, LocaleKeys } from '@/i18n'
import type { AllLocales, I18nLangKeys, LocaleKeys, PathValue } from '@/i18n'
import { getNestedValue, i18nConfig, interpolateString } from '@/i18n'

// 类型获取给定键的本地化值的类型
type LocalizedValue<T, K extends LocaleKeys> = PathValue<T, K> extends string
? string
: PathValue<T, K>

export const useLocale = () => {
const { locale, defaultLocale } = useRouter()
const currentLocale = (locale || defaultLocale) as I18nLangKeys

const t = useCallback(
<K extends LocaleKeys>(key: K, withData: Record<string, any> = {}): string => {
<K extends LocaleKeys>(key: K, withData: Record<string, any> = {}): LocalizedValue<AllLocales, K> => {
const template = getNestedValue(i18nConfig[currentLocale], key)
if (typeof template === 'string') {
return interpolateString(template, withData)
return interpolateString(template, withData) as LocalizedValue<AllLocales, K>
}
return template || key
return template as LocalizedValue<AllLocales, K>
},
[currentLocale],
)
Expand Down
28 changes: 28 additions & 0 deletions src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,32 @@ export default {
},

badgeTitle: 'Lightweight & Easy 🎉',

featureList: [
{
title: 'Advanced Tech Stack',
description: 'Leveraging efficient React frameworks and type-safe support with Next.js, TypeScript, and Shadcn UI to build modern applications.',
},
{
title: 'Tailwind CSS & Iconify Icons',
description: 'Atomic CSS integrated with Tailwind CSS and Iconify icons, enabling efficient design and responsive UI.',
},
{
title: 'Dark Mode',
description: 'Supports dark mode for an enhanced nighttime experience.',
},
{
title: 'Code Standards',
description: 'Adheres to best practices with code standards and uses ESLint for quality checks and consistency.',
},
{
title: 'Rich Components & Extensible Support',
description: 'Offers a range of built-in components and supports flexible custom extensions.',
},
{
title: 'Lightweight Design',
description: 'Employs a lightweight design approach, streamlining project setup to focus on content creation.',
},
],

}
23 changes: 18 additions & 5 deletions src/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,28 @@ export type NestedKeyOf<ObjectType extends object> = {
export type LocaleKeys = NestedKeyOf<AllLocales>


type DeepObject = Record<string, any>

// 类型提取给定路径上值的类型
export type PathValue<T, P extends string> =
P extends `${infer Key}.${infer Rest}`
? Key extends keyof T
? PathValue<T[Key], Rest>
: never
: P extends keyof T
? T[P]
: never

// 获取嵌套值
export function getNestedValue(obj: Record<string, any>, path: string): any {
return path.split('.').reduce((acc, key) => acc && acc[key], obj)
export function getNestedValue<T extends DeepObject, K extends string>(obj: T, path: K): PathValue<T, K> {
return path.split('.').reduce((acc, key) => acc && acc[key], obj) as PathValue<T, K>
}


// 插入值表达式
export function interpolateString(template: string, context: Record<string, any>): string {
return template.replace(/\{\{(\w+(\.\w+)*)\}\}/g, (_, path) => {
const value = getNestedValue(context, path)
return value !== undefined ? value : `{{${path}}}`
return template.replace(/\{\{\s*(\w+(\.\w+)*)\s*\}\}/g, (_, path) => {
const value = getNestedValue(context, path.trim())
return value !== undefined ? value : `{{${path.trim()}}}`
})
}
27 changes: 27 additions & 0 deletions src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,31 @@ export default {
},

badgeTitle: '轻量级、开箱即用 🎉',

featureList: [
{
title: '先进的技术栈',
description: '高效的 React 框架和类型安全支持,使用 Next.js、TypeScript、TypeScript、和 Shadcn UI 打造现代化应用',
},
{
title: 'Tailwind CSS & Iconify 图标集',
description: '原子化 CSS, 集成 Tailwind CSS 和 Iconify 图标集,轻松实现高效设计、响应式界面 UI',
},
{
title: '暗黑模式',
description: '支持暗黑模式,提供更好的夜间使用体验',
},
{
title: '代码规范',
description: '遵循最佳实践的代码规范,结合 ESLint 进行代码质量检查与一致性维护',
},
{
title: '丰富组件 & 支持自由扩展',
description: '提供丰富的预置组件,并支持灵活的自定义扩展',
},
{
title: '轻量化设计',
description: '采用轻量化设计,精简项目设置,专注于内容编写',
},
],
}

0 comments on commit c0c6b97

Please sign in to comment.