first commit
This commit is contained in:
45
src/i18n/I18nProvider.tsx
Normal file
45
src/i18n/I18nProvider.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { DEFAULT_LOCALE, TRANSLATIONS, type Locale } from './translations'
|
||||
import { I18nContext, type I18nContextValue } from './context'
|
||||
|
||||
const STORAGE_KEY = 'locale'
|
||||
|
||||
export const I18nProvider: React.FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const [locale, setLocaleState] = useState<Locale>(() => {
|
||||
const fromStorage = window.localStorage.getItem(STORAGE_KEY)
|
||||
if (fromStorage === 'th' || fromStorage === 'zh') return fromStorage
|
||||
return DEFAULT_LOCALE
|
||||
})
|
||||
|
||||
const setLocale = useCallback((next: Locale) => {
|
||||
setLocaleState(next)
|
||||
window.localStorage.setItem(STORAGE_KEY, next)
|
||||
}, [])
|
||||
|
||||
const t = useCallback(
|
||||
(key: string) => {
|
||||
return TRANSLATIONS[locale][key] ?? TRANSLATIONS[DEFAULT_LOCALE][key] ?? key
|
||||
},
|
||||
[locale],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.lang = locale
|
||||
document.title =
|
||||
locale === 'zh'
|
||||
? '湘味小馆 | 正宗湘菜在泰国'
|
||||
: 'Xiang Hunan Kitchen | อาหารหูหนานแท้ในไทย'
|
||||
}, [locale])
|
||||
|
||||
const value = useMemo<I18nContextValue>(
|
||||
() => ({
|
||||
locale,
|
||||
setLocale,
|
||||
t,
|
||||
}),
|
||||
[locale, setLocale, t],
|
||||
)
|
||||
|
||||
return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>
|
||||
}
|
||||
|
||||
10
src/i18n/context.ts
Normal file
10
src/i18n/context.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { createContext } from 'react'
|
||||
import type { Locale } from './translations'
|
||||
|
||||
export type I18nContextValue = {
|
||||
locale: Locale
|
||||
setLocale: (locale: Locale) => void
|
||||
t: (key: string) => string
|
||||
}
|
||||
|
||||
export const I18nContext = createContext<I18nContextValue | null>(null)
|
||||
174
src/i18n/translations.ts
Normal file
174
src/i18n/translations.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
export type Locale = 'th' | 'zh'
|
||||
|
||||
export const DEFAULT_LOCALE: Locale = 'th'
|
||||
|
||||
export type Translations = Record<string, string>
|
||||
|
||||
export const TRANSLATIONS: Record<Locale, Translations> = {
|
||||
th: {
|
||||
'nav.home': 'หน้าแรก',
|
||||
'nav.menu': 'เมนู',
|
||||
'nav.about': 'เกี่ยวกับ',
|
||||
'nav.contact': 'ติดต่อ',
|
||||
'nav.news': 'ข่าวสาร',
|
||||
'lang.th': 'ไทย',
|
||||
'lang.zh': '中文',
|
||||
|
||||
'home.hero.title': 'อาหารหูหนาน (湘菜) รสจัด เผ็ดหอม กลมกล่อม',
|
||||
'home.hero.desc':
|
||||
'ร้านอาหารหูหนาน (湘菜) ที่เน้นรสเผ็ดหอม กลิ่นเครื่องเทศชัดเจน เหมาะกับทั้งมื้อครอบครัวและสังสรรค์กับเพื่อน',
|
||||
'home.hero.cta.menu': 'ดูเมนู',
|
||||
'home.hero.cta.contact': 'จองโต๊ะ / ติดต่อ',
|
||||
'home.section.latestNews': 'ข่าวสารล่าสุด',
|
||||
'home.section.latestNews.desc': 'โปรโมชั่น เมนูใหม่ และข่าวกิจกรรมของร้าน',
|
||||
'home.section.latestNews.more': 'ดูทั้งหมด',
|
||||
'home.emptyNews': 'ยังไม่มีข่าวสารในตอนนี้',
|
||||
'home.cta.title': 'พร้อมชิมรสหูหนานแท้แล้วหรือยัง?',
|
||||
'home.cta.desc':
|
||||
'จองโต๊ะง่าย ๆ ผ่านโทรศัพท์หรือไลน์ แล้วมาสัมผัสความเผ็ดหอมแบบหูหนานได้เลย',
|
||||
'home.cta.call': 'โทรจองโต๊ะ',
|
||||
'home.cta.map': 'ดูแผนที่และเวลาเปิดร้าน',
|
||||
|
||||
'home.feature.1.title': 'เผ็ดหอมแบบหูหนาน',
|
||||
'home.feature.1.desc': 'พริกแห้งและเครื่องเทศคั่วสด กลิ่นชัด รสมีมิติ',
|
||||
'home.feature.2.title': 'เหมาะกับการแชร์',
|
||||
'home.feature.2.desc': 'เมนูหลายจาน ทานร่วมกันได้ทั้งครอบครัวและเพื่อนฝูง',
|
||||
'home.feature.3.title': 'จองโต๊ะง่าย',
|
||||
'home.feature.3.desc': 'โทรหรือทักไลน์เพื่อจองโต๊ะและขอแนะนำเมนูได้ทันที',
|
||||
|
||||
'menu.title': 'เมนู',
|
||||
'menu.subtitle':
|
||||
'รสชาติหูหนานแท้ กลิ่นพริกแห้งและเครื่องเทศชัดเจน ปรับระดับความเผ็ดได้ตามชอบ',
|
||||
'menu.allergy.title': 'มีอาการแพ้อาหาร?',
|
||||
'menu.allergy.desc':
|
||||
'แจ้งพนักงานก่อนสั่งอาหาร เราช่วยปรับสูตรและแนะนำเมนูที่เหมาะกับคุณได้',
|
||||
'menu.allergy.call': 'โทรสอบถาม',
|
||||
'menu.allergy.directions': 'ดูวิธีเดินทาง',
|
||||
|
||||
'about.title': 'เรื่องราวของเรา',
|
||||
'about.subtitle':
|
||||
'เราอยากให้ทุกคนได้สัมผัสเสน่ห์ของอาหารหูหนาน (湘菜) ที่โดดเด่นด้วยกลิ่นพริกแห้งและเครื่องเทศ รสเผ็ดหอมแต่กลมกล่อม พร้อมบริการเป็นกันเอง',
|
||||
'about.occasions': 'เหมาะกับโอกาสไหน?',
|
||||
'about.cta.title': 'อยากให้ช่วยแนะนำเมนู?',
|
||||
'about.cta.desc':
|
||||
'โทรหรือทักไลน์ได้เลย เราแนะนำเมนูตามจำนวนคนและระดับความเผ็ดที่ชอบ',
|
||||
'about.cta.call': 'โทรจองโต๊ะ',
|
||||
'about.cta.contact': 'ดูข้อมูลติดต่อ',
|
||||
|
||||
'contact.title': 'ติดต่อและการเดินทาง',
|
||||
'contact.subtitle':
|
||||
'จองโต๊ะ สอบถามเมนู หรือขอคำแนะนำระดับความเผ็ด ติดต่อเราได้ทุกช่องทาง',
|
||||
'contact.shopInfo': 'ข้อมูลร้าน',
|
||||
'contact.phone': 'โทร',
|
||||
'contact.line': 'ไลน์',
|
||||
'contact.address': 'ที่อยู่',
|
||||
'contact.hours': 'เวลาเปิด-ปิด',
|
||||
'contact.mapTitle': 'แผนที่ร้าน',
|
||||
'contact.takeaway.title': 'อยากสั่งกลับบ้าน?',
|
||||
'contact.takeaway.desc':
|
||||
'โทรสั่งล่วงหน้าเพื่อความรวดเร็ว หรือทักไลน์แจ้งเวลารับได้เลย',
|
||||
'contact.takeaway.call': 'โทรสั่งอาหาร',
|
||||
'contact.takeaway.line': 'ทักไลน์',
|
||||
|
||||
'news.title': 'ข่าวสารและโปรโมชั่น',
|
||||
'news.subtitle': 'อัปเดตเมนูใหม่ โปรโมชั่น และกิจกรรมของทางร้าน',
|
||||
'news.empty': 'ยังไม่มีข่าวสารในตอนนี้',
|
||||
|
||||
'post.back': 'กลับหน้าข่าวสาร',
|
||||
'notFound.title': 'ไม่พบหน้านี้',
|
||||
'notFound.desc': 'ลิงก์อาจไม่ถูกต้อง หรือหน้านี้ถูกย้ายไปแล้ว',
|
||||
'notFound.back': 'กลับหน้าแรก',
|
||||
|
||||
'card.more': 'ดูรายละเอียด',
|
||||
'card.open': 'เปิด',
|
||||
|
||||
'common.phone': 'โทร',
|
||||
'common.hours': 'เวลาเปิด-ปิด',
|
||||
'common.call': 'โทร',
|
||||
'common.contact': 'ติดต่อ',
|
||||
'common.loadFailed': 'โหลดข้อมูลไม่สำเร็จ',
|
||||
'footer.contact': 'ติดต่อ',
|
||||
'footer.hours': 'เวลาเปิด-ปิด',
|
||||
'home.hoursPreview': '11:00–22:00',
|
||||
},
|
||||
zh: {
|
||||
'nav.home': '首页',
|
||||
'nav.menu': '菜单',
|
||||
'nav.about': '关于我们',
|
||||
'nav.contact': '联系/地址',
|
||||
'nav.news': '新闻/活动',
|
||||
'lang.th': 'ไทย',
|
||||
'lang.zh': '中文',
|
||||
|
||||
'home.hero.title': '正宗湘菜(湘菜) 香辣鲜香,层次分明',
|
||||
'home.hero.desc':
|
||||
'面向泰国食客的湘菜餐厅:干辣椒与香辛料的香气突出,适合家庭聚餐与朋友小聚,辣度可按需调整。',
|
||||
'home.hero.cta.menu': '查看菜单',
|
||||
'home.hero.cta.contact': '订位/联系',
|
||||
'home.section.latestNews': '最新新闻',
|
||||
'home.section.latestNews.desc': '优惠活动、新菜上线与店内动态',
|
||||
'home.section.latestNews.more': '查看全部',
|
||||
'home.emptyNews': '暂无新闻',
|
||||
'home.cta.title': '准备好来一口地道湘味了吗?',
|
||||
'home.cta.desc': '电话或 Line 轻松订位,来体验香辣过瘾的湘菜。',
|
||||
'home.cta.call': '电话订位',
|
||||
'home.cta.map': '查看地图与营业时间',
|
||||
|
||||
'home.feature.1.title': '湘味重香重辣',
|
||||
'home.feature.1.desc': '干辣椒与香辛料的复合香气突出,层次更丰富。',
|
||||
'home.feature.2.title': '更适合分享',
|
||||
'home.feature.2.desc': '多道菜拼桌更过瘾,适合家庭与朋友聚餐。',
|
||||
'home.feature.3.title': '订位更方便',
|
||||
'home.feature.3.desc': '电话或 Line 一键订位,也可按口味推荐菜品。',
|
||||
|
||||
'menu.title': '菜单',
|
||||
'menu.subtitle': '湘菜重香重辣,干辣椒香气明显;可按口味调整辣度。',
|
||||
'menu.allergy.title': '有过敏/忌口?',
|
||||
'menu.allergy.desc': '下单前告知店员,我们可协助调整做法并推荐合适菜品。',
|
||||
'menu.allergy.call': '电话咨询',
|
||||
'menu.allergy.directions': '查看路线',
|
||||
|
||||
'about.title': '我们的故事',
|
||||
'about.subtitle':
|
||||
'我们希望让更多人体验湘菜的魅力:干辣椒与香辛料的复合香气,香辣但不失平衡,并以轻松友好的服务呈现。',
|
||||
'about.occasions': '适合哪些场景?',
|
||||
'about.cta.title': '不知道怎么点?',
|
||||
'about.cta.desc': '电话或 Line 联系我们,按人数与辣度偏好帮你配菜。',
|
||||
'about.cta.call': '电话订位',
|
||||
'about.cta.contact': '查看联系方式',
|
||||
|
||||
'contact.title': '联系与到店',
|
||||
'contact.subtitle': '订位、咨询菜单、辣度建议,都可以随时联系我们。',
|
||||
'contact.shopInfo': '门店信息',
|
||||
'contact.phone': '电话',
|
||||
'contact.line': 'Line',
|
||||
'contact.address': '地址',
|
||||
'contact.hours': '营业时间',
|
||||
'contact.mapTitle': '门店地图',
|
||||
'contact.takeaway.title': '想打包带走?',
|
||||
'contact.takeaway.desc': '建议提前电话下单,或 Line 告知取餐时间更省心。',
|
||||
'contact.takeaway.call': '电话下单',
|
||||
'contact.takeaway.line': 'Line 联系',
|
||||
|
||||
'news.title': '新闻与活动',
|
||||
'news.subtitle': '更新优惠、活动与新品信息',
|
||||
'news.empty': '暂无新闻',
|
||||
|
||||
'post.back': '返回新闻列表',
|
||||
'notFound.title': '页面不存在',
|
||||
'notFound.desc': '链接可能不正确,或页面已被移动。',
|
||||
'notFound.back': '返回首页',
|
||||
|
||||
'card.more': '查看详情',
|
||||
'card.open': '打开',
|
||||
|
||||
'common.phone': '电话',
|
||||
'common.hours': '营业时间',
|
||||
'common.call': '拨打电话',
|
||||
'common.contact': '联系',
|
||||
'common.loadFailed': '加载失败',
|
||||
'footer.contact': '联系',
|
||||
'footer.hours': '营业时间',
|
||||
'home.hoursPreview': '11:00–22:00',
|
||||
},
|
||||
}
|
||||
10
src/i18n/useI18n.ts
Normal file
10
src/i18n/useI18n.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { useContext } from 'react'
|
||||
import { I18nContext } from './context'
|
||||
|
||||
export const useI18n = () => {
|
||||
const ctx = useContext(I18nContext)
|
||||
if (!ctx) {
|
||||
throw new Error('useI18n must be used within I18nProvider')
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
Reference in New Issue
Block a user