Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion app/i18n/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** Script group decides which font pair loads. Keeping this separate from the
* locale list means adding a Latin-script locale costs no extra font weight. */
export type ScriptGroup = "latin" | "cyrillic" | "devanagari" | "arabic" | "sc" | "jp" | "kr";
export type ScriptGroup = "latin" | "vietnamese" | "cyrillic" | "devanagari" | "arabic" | "sc" | "jp" | "kr";

export type LocaleConfig = {
code: string;
Expand All @@ -27,6 +27,7 @@ export const locales: LocaleConfig[] = [
{ code: "ru", nativeName: "Русский", englishName: "Russian", country: "Russia", dir: "ltr", script: "cyrillic", intl: "ru_RU" },
{ code: "id", nativeName: "Indonesia", englishName: "Indonesian", country: "Indonesia", dir: "ltr", script: "latin", intl: "id_ID" },
{ code: "ko", nativeName: "한국어", englishName: "Korean", country: "South Korea", dir: "ltr", script: "kr", intl: "ko_KR" },
{ code: "vi", nativeName: "Tiếng Việt", englishName: "Vietnamese", country: "Vietnam", dir: "ltr", script: "vietnamese", intl: "vi_VN" },
];

export const defaultLocale = "en";
Expand Down
2 changes: 2 additions & 0 deletions app/i18n/dictionaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const uiLoaders: Record<string, () => Promise<UiDictionary>> = {
ru: () => import("./ui/ru").then((m) => m.ui),
id: () => import("./ui/id").then((m) => m.ui),
ko: () => import("./ui/ko").then((m) => m.ui),
vi: () => import("./ui/vi").then((m) => m.ui),
};

const organLoaders: Record<string, () => Promise<OrganContentDictionary>> = {
Expand All @@ -31,6 +32,7 @@ const organLoaders: Record<string, () => Promise<OrganContentDictionary>> = {
ru: () => import("./organs/ru").then((m) => m.organs),
id: () => import("./organs/id").then((m) => m.organs),
ko: () => import("./organs/ko").then((m) => m.organs),
vi: () => import("./organs/vi").then((m) => m.organs),
};

export async function getDictionary(locale: string): Promise<Dictionary> {
Expand Down
11 changes: 11 additions & 0 deletions app/i18n/fonts.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Be_Vietnam_Pro,
Cormorant_Garamond,
DM_Sans,
Noto_Naskh_Arabic,
Expand All @@ -16,6 +17,15 @@ const cormorantCyrillic = Cormorant_Garamond({ variable: "--font-serif", subsets
const dmSans = DM_Sans({ variable: "--font-sans", subsets: ["latin", "latin-ext"] });
const notoSansCyrillic = Noto_Sans({ variable: "--font-sans", subsets: ["cyrillic", "latin"] });

// Vietnamese needs Latin Extended plus the precomposed tone marks Google ships
// in the dedicated `vietnamese` subset. Requesting it only for `vi` keeps the
// other Latin locales from paying for glyphs they never render.
const cormorantVietnamese = Cormorant_Garamond({ variable: "--font-serif", subsets: ["latin", "latin-ext", "vietnamese"], weight: ["400", "500", "600"] });
// DM Sans ships no Vietnamese subset, so `vi` uses Be Vietnam Pro — a
// geometric sans drawn for Vietnamese diacritics, which keeps the tone marks
// from colliding with ascenders the way a retrofitted face would.
const beVietnamPro = Be_Vietnam_Pro({ variable: "--font-sans", subsets: ["latin", "latin-ext", "vietnamese"], weight: ["400", "500", "600", "700"] });

const devanagariSerif = Noto_Serif_Devanagari({ variable: "--font-serif", subsets: ["devanagari", "latin"], weight: ["400", "500", "600"] });
const devanagariSans = Noto_Sans_Devanagari({ variable: "--font-sans", subsets: ["devanagari", "latin"] });

Expand All @@ -24,6 +34,7 @@ const arabicSans = Noto_Sans_Arabic({ variable: "--font-sans", subsets: ["arabic

const webFonts: Partial<Record<ScriptGroup, { serif: { variable: string }; sans: { variable: string } }>> = {
latin: { serif: cormorant, sans: dmSans },
vietnamese: { serif: cormorantVietnamese, sans: beVietnamPro },
cyrillic: { serif: cormorantCyrillic, sans: notoSansCyrillic },
devanagari: { serif: devanagariSerif, sans: devanagariSans },
arabic: { serif: arabicSerif, sans: arabicSans },
Expand Down
210 changes: 210 additions & 0 deletions app/i18n/organs/vi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import type { OrganContentDictionary } from "../types";

export const organs: OrganContentDictionary = {
heart: {
name: "Tim",
system: "Hệ tim mạch",
description: "Một cơ quan cơ bơm máu đi khắp cơ thể, mang oxy và dưỡng chất đến từng tế bào.",
poetic: "Chiếc bơm không mỏi",
size: "Cỡ bằng nắm tay của bạn",
weight: "250–350 g",
location: "Sau xương ức, hơi lệch sang trái",
function: "Lưu thông máu giàu oxy",
dailyFact: "Đập khoảng 100.000 lần",
medical: "Nhịp điện của tim điều phối từng nhịp đập.",
bloodSupply: "Động mạch vành trái và phải",
funFact: "Tim đập khoảng 2,5 tỉ lần trong một đời người và bắt đầu đập từ trước khi bạn chào đời.",
tissue: "Mô cơ tim",
comparison: "Tim so với não",
conditions: ["Bệnh mạch vành", "Rối loạn nhịp tim", "Bệnh van tim", "Suy tim", "Bệnh cơ tim", "Viêm cơ tim", "Rung nhĩ", "Dị tật tim bẩm sinh"],
hotspots: {
"aorta": { label: "Động mạch chủ", detail: "Động mạch chính" },
"left-atrium": { label: "Tâm nhĩ trái", detail: "Nhận máu giàu oxy" },
"right-atrium": { label: "Tâm nhĩ phải", detail: "Nhận máu tĩnh mạch" },
"left-ventricle": { label: "Tâm thất trái", detail: "Bơm máu đi khắp cơ thể" },
"right-ventricle": { label: "Tâm thất phải", detail: "Bơm máu lên phổi" },
"mitral": { label: "Van hai lá", detail: "Ngăn máu trào ngược" },
},
},
brain: {
name: "Não",
system: "Hệ thần kinh",
description: "Trung tâm điều khiển của cơ thể, tích hợp cảm giác, trí nhớ, cảm xúc và vận động tinh vi.",
poetic: "Vũ trụ bên trong",
size: "Xấp xỉ hai nắm tay siết chặt",
weight: "1,3–1,4 kg",
location: "Được bảo vệ trong hộp sọ",
function: "Xử lý và điều phối tín hiệu",
dailyFact: "Dùng khoảng 20% năng lượng cơ thể",
medical: "Hàng tỉ nơ-ron giao tiếp với nhau bằng tín hiệu điện và hóa học.",
bloodSupply: "Động mạch cảnh trong và động mạch đốt sống",
funFact: "Bản thân não không có thụ thể đau — cơn đau đầu được cảm nhận ở các mô xung quanh nó.",
tissue: "Vỏ não",
comparison: "Não so với mắt",
conditions: ["Đau nửa đầu", "Đột quỵ", "Bệnh thoái hóa thần kinh", "Động kinh", "Chấn thương sọ não", "Viêm màng não", "Xơ cứng rải rác", "Phình mạch não"],
hotspots: {
"frontal": { label: "Thùy trán", detail: "Lập kế hoạch và vận động" },
"parietal": { label: "Thùy đỉnh", detail: "Tích hợp cảm giác" },
"temporal": { label: "Thùy thái dương", detail: "Trí nhớ và thính giác" },
"cerebellum": { label: "Tiểu não", detail: "Thăng bằng và phối hợp" },
},
},
lungs: {
name: "Phổi",
system: "Hệ hô hấp",
description: "Cặp cơ quan hút không khí vào và trao đổi oxy lấy khí carbonic qua một bề mặt rộng lớn, mỏng manh.",
poetic: "Hơi thở của sự sống",
size: "Mỗi lá cao khoảng 25 cm",
weight: "Khoảng 1 kg cho cả hai lá",
location: "Hai bên tim, trong lồng ngực",
function: "Trao đổi oxy lấy khí carbonic",
dailyFact: "Luân chuyển khoảng 11.000 L không khí",
medical: "Các phế nang gấp một bề mặt trao đổi rộng bằng sân tennis vào trong lồng ngực.",
bloodSupply: "Động mạch phổi và động mạch phế quản",
funFact: "Phổi phải có ba thùy còn phổi trái chỉ có hai, chừa một khuyết cho tim.",
tissue: "Mô phế nang",
comparison: "Phổi so với tim",
conditions: ["Hen phế quản", "Bệnh phổi tắc nghẽn mạn tính", "Viêm phổi", "Thuyên tắc phổi", "Xơ phổi", "Viêm phế quản", "Xơ nang", "Ung thư phổi"],
hotspots: {
"trachea": { label: "Khí quản", detail: "Dẫn khí vào phổi" },
"right-lung": { label: "Phổi phải", detail: "Ba thùy" },
"left-lung": { label: "Phổi trái", detail: "Hai thùy, chừa chỗ cho tim" },
"bronchus": { label: "Phế quản", detail: "Đường dẫn khí phân nhánh" },
"base": { label: "Đáy phổi", detail: "Tựa trên cơ hoành" },
},
},
liver: {
name: "Gan",
system: "Hệ tiêu hóa",
description: "Một cơ quan chuyển hóa phi thường, lọc máu, xử lý dưỡng chất và sản xuất mật.",
poetic: "Nhà giả kim thầm lặng",
size: "Cỡ bằng một quả bóng bầu dục",
weight: "1,4–1,6 kg",
location: "Vùng bụng trên bên phải",
function: "Chuyển hóa, khử độc và tạo mật",
dailyFact: "Đảm nhiệm hơn 500 chức năng",
medical: "Gan có thể tái tạo một phần lớn mô đã mất.",
bloodSupply: "Động mạch gan và tĩnh mạch cửa",
funFact: "Đây là cơ quan duy nhất ở người có thể mọc lại đủ kích thước từ một phần nhỏ của chính nó.",
tissue: "Tiểu thùy gan",
comparison: "Gan so với ruột",
conditions: ["Gan nhiễm mỡ", "Viêm gan", "Xơ gan", "Sỏi mật", "Nhiễm sắt mô", "Ung thư gan", "Viêm gan tự miễn", "Tăng áp lực tĩnh mạch cửa"],
hotspots: {
"right-lobe": { label: "Thùy phải", detail: "Thùy gan lớn nhất" },
"left-lobe": { label: "Thùy trái", detail: "Vắt qua đường giữa" },
"portal": { label: "Tĩnh mạch cửa", detail: "Dòng máu giàu dưỡng chất đổ vào" },
},
},
kidneys: {
name: "Thận",
system: "Hệ tiết niệu",
description: "Cặp cơ quan lọc, cân bằng dịch, điện giải, huyết áp và đào thải chất cặn bã.",
poetic: "Bộ lọc tài tình",
size: "Mỗi quả cỡ một con chuột máy tính",
weight: "120–170 g mỗi quả",
location: "Hai bên cột sống, dưới xương sườn",
function: "Lọc máu và tạo nước tiểu",
dailyFact: "Lọc khoảng 180 L dịch",
medical: "Các nephron tinh chỉnh thành phần hóa học của dòng máu.",
bloodSupply: "Động mạch thận",
funFact: "Thận thu hồi lại gần như toàn bộ những gì đã lọc — chỉ khoảng 1–2 L rời khỏi cơ thể dưới dạng nước tiểu.",
tissue: "Vỏ thận",
comparison: "Thận so với gan",
conditions: ["Sỏi thận", "Bệnh thận mạn", "Nhiễm khuẩn tiết niệu", "Viêm cầu thận", "Thận đa nang", "Tăng huyết áp do thận", "Tổn thương thận cấp", "Hội chứng thận hư"],
hotspots: {
"cortex": { label: "Vỏ thận", detail: "Lớp lọc phía ngoài" },
"medulla": { label: "Tủy thận", detail: "Cô đặc nước tiểu" },
"ureter": { label: "Niệu quản", detail: "Dẫn nước tiểu" },
},
},
eyeball: {
name: "Mắt",
system: "Hệ giác quan",
description: "Một cơ quan cảm giác chính xác, biến ánh sáng hội tụ thành tín hiệu thần kinh được diễn giải thành thị giác.",
poetic: "Ô cửa dệt bằng ánh sáng",
size: "Đường kính khoảng 24 mm",
weight: "Khoảng 7,5 g",
location: "Nằm trong hốc mắt xương",
function: "Thu nhận và hội tụ ánh sáng",
dailyFact: "Thực hiện hàng nghìn chuyển động nhỏ",
medical: "Võng mạc là phần kéo dài của hệ thần kinh trung ương.",
bloodSupply: "Động mạch mắt",
funFact: "Giác mạc hoàn toàn không có mạch máu; nó lấy oxy trực tiếp từ không khí.",
tissue: "Các lớp võng mạc",
comparison: "Mắt so với não",
conditions: ["Cận thị", "Đục thủy tinh thể", "Tăng nhãn áp", "Thoái hóa điểm vàng", "Bong võng mạc", "Khô mắt", "Loạn thị", "Viêm kết mạc"],
hotspots: {
"cornea": { label: "Giác mạc", detail: "Bề mặt trong suốt hội tụ ánh sáng" },
"iris": { label: "Mống mắt", detail: "Điều tiết lượng ánh sáng vào" },
"optic": { label: "Thần kinh thị giác", detail: "Truyền tín hiệu thị giác" },
},
},
intestine: {
name: "Ruột",
system: "Hệ tiêu hóa",
description: "Một đường tiêu hóa gấp khúc, nơi dưỡng chất được hấp thu và hệ vi sinh vật nâng đỡ sức khỏe toàn thân.",
poetic: "Khu vườn bên trong",
size: "Khoảng 6–7 m khi trải dài",
weight: "Thay đổi theo lượng chứa bên trong",
location: "Vùng bụng giữa và bụng dưới",
function: "Tiêu hóa và hấp thu dưỡng chất",
dailyFact: "Là nơi cư trú của hàng nghìn tỉ vi sinh vật",
medical: "Bề mặt của ruột được nhân lên nhờ các nếp gấp, nhung mao và vi nhung mao.",
bloodSupply: "Động mạch mạc treo tràng trên và dưới",
funFact: "Lớp niêm mạc ruột tự đổi mới sau vài ngày — tốc độ thay thế nhanh nhất trong mọi mô của cơ thể.",
tissue: "Nhung mao ruột",
comparison: "Ruột so với gan",
conditions: ["Hội chứng ruột kích thích", "Bệnh viêm ruột", "Bệnh celiac", "Viêm túi thừa", "Tắc ruột", "Polyp đại trực tràng", "Bệnh Crohn", "Bất dung nạp lactose"],
hotspots: {
"duodenum": { label: "Tá tràng", detail: "Đoạn đầu của ruột non" },
"jejunum": { label: "Hỗng tràng", detail: "Vùng hấp thu chủ yếu" },
"colon": { label: "Đại tràng", detail: "Thu hồi nước" },
},
},
pancreas: {
name: "Tụy",
system: "Hệ nội tiết",
description: "Một tuyến kiêm hai vai trò: tiết enzyme tiêu hóa vào ruột và tiết hormone giữ ổn định đường huyết.",
poetic: "Người điều tiết thầm lặng",
size: "Dài khoảng 15 cm",
weight: "70–100 g",
location: "Sau dạ dày, vắt ngang vùng bụng trên",
function: "Enzyme tiêu hóa và insulin",
dailyFact: "Tạo khoảng 1,5 L dịch giàu enzyme",
medical: "Các tiểu đảo Langerhans tiết insulin và glucagon để cân bằng đường huyết.",
bloodSupply: "Động mạch lách và động mạch tá tụy",
funFact: "Chỉ chưa tới 2% khối lượng tụy làm nhiệm vụ tiết hormone; phần còn lại dành cho enzyme tiêu hóa.",
tissue: "Nang tuyến tụy",
comparison: "Tụy so với gan",
conditions: ["Viêm tụy", "Đái tháo đường type 1", "Ung thư tụy", "Đái tháo đường type 2", "Suy tụy ngoại tiết", "Nang tụy", "Viêm tụy do sỏi mật", "U tiết insulin"],
hotspots: {
"head": { label: "Đầu tụy", detail: "Được tá tràng ôm lấy" },
"body": { label: "Thân tụy", detail: "Vắt ngang cột sống" },
"tail": { label: "Đuôi tụy", detail: "Vươn tới lách" },
"duct": { label: "Ống tụy", detail: "Dẫn enzyme xuống ruột" },
},
},
skin: {
name: "Da",
system: "Hệ da và mô bao phủ",
description: "Cơ quan lớn nhất cơ thể — một hàng rào sống biết cảm nhận xúc giác, giữ nước và điều hòa thân nhiệt.",
poetic: "Đường biên sống",
size: "Khoảng 2 m² khi trải phẳng",
weight: "3,5–5 kg",
location: "Bao phủ toàn bộ cơ thể",
function: "Bảo vệ, cảm nhận và làm mát",
dailyFact: "Bong khoảng 500 triệu tế bào",
medical: "Ba lớp — biểu bì, trung bì và hạ bì — mỗi lớp đảm nhận một vai trò riêng.",
bloodSupply: "Đám rối mạch máu ở trung bì",
funFact: "Chỉ một xăng-ti-mét vuông da có thể chứa hàng trăm tuyến mồ hôi và hàng mét mạch máu.",
tissue: "Các lớp biểu bì",
comparison: "Da so với ruột",
conditions: ["Chàm", "Vảy nến", "Ung thư hắc tố", "Mụn trứng cá", "Viêm mô tế bào", "Viêm da tiếp xúc", "Trứng cá đỏ", "Bạch biến"],
hotspots: {
"epidermis": { label: "Biểu bì", detail: "Lớp bảo vệ ngoài cùng" },
"dermis": { label: "Trung bì", detail: "Thần kinh, mạch máu và tuyến" },
"hypodermis": { label: "Hạ bì", detail: "Mỡ và lớp cách nhiệt" },
"follicle": { label: "Nang lông", detail: "Neo giữ từng sợi lông" },
},
},
};
Loading