返回 CodeWhale
merch-storefront.ts
根目录 / web / lib / content / merch-storefront.ts
1 import { pickText } from "@/lib/i18n/dictionaries";
2 import { MERCH_TARGET_PRICE } from "@/lib/merch/catalog";
3
4 /** Merch copy uses the site's shared en/zh content bridge; other locales fall back to English. */
5 const text = {
6 title: { en: "Open source, worn out loud.", zh: "把开源精神,穿在身上。" },
7 description: { en: "For the people building with Codewhale. Everyday solids, an ocean-wave mascot hoodie and one ombré option; a soft mascot companion in development; expressive art for your desk and a contributor edition at cost.", zh: "给和 Codewhale 一起创造的人。日常纯色、海浪吉祥物卫衣,还有一款渐变;正在筹备的毛绒吉祥物、热闹的桌面图案,以及按成本提供的贡献者款。" },
8 launchTitle: { en: "Everyday staples. Mascot favorites.", zh: "日常基础款,也有吉祥物。" },
9 launchBody: { en: "Eight apparel styles, led by everyday staples and the blue wave-and-stars mascot hoodie. The seated plush doll joins the planned collection, with its prototype next. Tide stays the single ombré option.", zh: "八款服饰,日常基础款搭配蓝色海浪与星星的吉祥物卫衣。坐姿毛绒娃娃也加入计划中的系列,下一步先做实物样品。海潮款仍是唯一的渐变设计。" },
10 launchAlt: { en: "Six Codewhale apparel concepts in two color choices each: solid Signature and Classic cotton tees, Whale Bro cotton tees, solid Signature polos, solid Everyday fleece hoodies, and Tide ombré jersey tees.", zh: "六款 Codewhale 服饰概念,每款两种配色:纯色日常款与经典款棉 T 恤、鲸鱼兄弟棉 T 恤、纯色 Polo 衫、纯色抓绒卫衣,以及海潮渐变 T 恤。" },
11 launchPriceNote: { en: "Fabric and print samples come first. Each garment will have its own price and destination shipping quote.", zh: "先确认面料与印花样品,再分别确定每款服饰的售价和目的地运费。" },
12 lineupTitle: { en: "The first-drop shortlist.", zh: "首批款式候选。" },
13 lineupBody: { en: "Six solid styles, the Ocean Wave mascot hoodie with a White alternative, and one ombré tee. Two proposed options per style: sixteen combinations before sizes. The doll is a separate planned product, with a prototype before orders.", zh: "六款纯色、海浪吉祥物卫衣(另有白色款),以及一款渐变 T 恤。每款提出两种选择,共十六种组合,再分别选择尺码。娃娃作为单独筹备的产品,先完成实物样品,再开放订购。" },
14 mascotTitle: { en: "Ocean waves. Our blue-haired mascot.", zh: "海浪与星星,蓝发吉祥物。" },
15 mascotBody: { en: "The cobalt hoodie with flowing pale-blue waves and stars leads the mascot edition. Keep the original saluting character, 誓死效忠 Codewhale badge and codewhale.net. A White hoodie and solid mascot tees complete the choices.", zh: "钴蓝衣身、浅蓝海浪与星星,是吉祥物系列的主打卫衣。保留原来的敬礼角色、「誓死效忠 Codewhale」徽章与 codewhale.net。另有白色卫衣与纯色吉祥物 T 恤。" },
16 mascotAlt: { en: "Original Codewhale mascot hoodie concepts: White and cobalt-blue Ocean Wave hoodies, with pale-blue waves and stars on the blue option and the centered saluting character badge on both.", zh: "原来的 Codewhale 吉祥物卫衣概念:白色与钴蓝海浪款。蓝色款印有浅蓝海浪与星星,两款正中都保留敬礼角色徽章。" },
17 mascotLink: { en: "Meet the mascot edition", zh: "看看吉祥物款" },
18 mascotTee: { en: "Mascot Tee", zh: "吉祥物 T 恤" },
19 mascotHoodie: { en: "Ocean Wave Mascot Hoodie", zh: "海浪吉祥物卫衣" },
20 mascotLayout: { en: "One centered character badge, 誓死效忠 Codewhale and website", zh: "一个正中的角色徽章,搭配誓死效忠 Codewhale 与网址" },
21 mascotTeeColors: { en: "White / cobalt", zh: "白色 / 钴蓝" },
22 mascotHoodieColors: { en: "Ocean Wave / White", zh: "蓝色海浪 / 白色" },
23 mascotHoodieLayout: { en: "Flowing waves and stars around the centered mascot badge; plain White alternative", zh: "海浪与星星围绕正中的吉祥物徽章,另有纯白款" },
24 mascotCotton: { en: "Cotton tee; colorful badge print to be sampled", zh: "棉 T 恤,彩色徽章印花待样品确认" },
25 mascotLightweight: { en: "Lightweight stretch-polyester scuba; all-over print", zh: "轻薄聚酯弹力空气层面料,全幅印花" },
26 colorLabel: { en: "Color options · concepts", zh: "候选配色 · 概念" },
27 whaleBroTee: { en: "Whale Bro Tee", zh: "鲸鱼兄弟 T 恤" },
28 tideTee: { en: "Tide Ombré Tee", zh: "海潮渐变 T 恤" },
29 whaleBroLayout: { en: "Compact whale, 鲸鱼兄弟, Codewhale and website", zh: "简洁小鲸、鲸鱼兄弟、Codewhale 与网址" },
30 tideLayout: { en: "Small left-chest signature on the single gradient design", zh: "唯一渐变款,搭配左胸小标志" },
31 ombreDirection: { en: "Stretch-polyester jersey for the budget gradient option", zh: "聚酯弹力针织面料,用作平价渐变款" },
32 signatureColors: { en: "White / navy", zh: "白色 / 深蓝" },
33 classicColors: { en: "Ivory / cobalt", zh: "象牙白 / 钴蓝" },
34 whaleBroColors: { en: "White / ice blue", zh: "白色 / 冰蓝" },
35 poloColors: { en: "Navy / ivory", zh: "深蓝 / 象牙白" },
36 hoodieColors: { en: "Charcoal / navy", zh: "炭灰 / 深蓝" },
37 tideColors: { en: "Lagoon / Daybreak", zh: "浅海 / 晨光" },
38 styleLabel: { en: "Style", zh: "款式" },
39 printLabel: { en: "Print layout", zh: "印花位置" },
40 fabricLabel: { en: "Fabric direction · samples pending", zh: "面料方向 · 待样品确认" },
41 signatureTee: { en: "Signature Tee", zh: "日常款 T 恤" },
42 classicTee: { en: "Classic Tee", zh: "经典款 T 恤" },
43 tidePolo: { en: "Signature Polo", zh: "日常款 Polo 衫" },
44 signatureHoodie: { en: "Everyday Hoodie", zh: "日常抓绒卫衣" },
45 classicHoodie: { en: "Classic Hoodie", zh: "经典款卫衣" },
46 signatureLayout: { en: "Small left-chest whale, name and website", zh: "左胸小鲸、名字与网址" },
47 classicLayout: { en: "Centered whale, name and website", zh: "正中小鲸、名字与网址" },
48 poloLayout: { en: "Small left-chest signature, clear of the buttons", zh: "左胸小标志,避开纽扣" },
49 cottonDirection: { en: "Solid cotton tee with a simple flat print", zh: "纯色棉 T 恤,搭配简洁单色印花" },
50 poloDirection: { en: "Solid stretch-polyester polo with a small flat print", zh: "纯色聚酯弹力 Polo 衫,搭配小印花" },
51 hoodieDirection: { en: "Heavier polyester fleece; solid body and a small flat print", zh: "较厚聚酯抓绒,纯色衣身搭配小印花" },
52 paletteNote: { en: "Everyday solids, one blue wave-and-stars hoodie, and one Tide gradient tee. Only Tide uses Lagoon (aqua → navy) or Daybreak (ivory → cobalt). Colors are concepts; exact fabric, variants and print need samples.", zh: "日常纯色、一款蓝色海浪星星卫衣,以及一款海潮渐变 T 恤。只有海潮款采用浅海配色(浅水蓝 → 深蓝)或晨光配色(象牙白 → 钴蓝)。配色为概念,具体面料、款式和印花需用实物样品确认。" },
53 fitLink: { en: "Compare garment measurements", zh: "对比成衣尺寸" },
54 launchLink: { en: "Explore the full lineup", zh: "看看完整系列" },
55 archiveTitle: { en: "More layouts and earlier studies", zh: "其他版式与之前的设计" },
56 archiveBody: { en: "These print studies remain useful references. The White cotton tee price review below belongs to the earlier configured garment; it does not price every color or the polo, hoodie and ombré tee.", zh: "这些印花版式保留作参考。下方白色棉 T 恤的价格确认属于之前配置的服饰方案,不代表所有配色、Polo 衫、卫衣或渐变 T 恤的价格。" },
57 preparing: { en: "Preparing the first drop. Samples before sales.", zh: "首批周边筹备中,先确认实物样品,再开售。" },
58 merch: { en: "Merch", zh: "周边" },
59 browse: { en: "Explore the tees", zh: "看看 T 恤" },
60 heroTitle: { en: "Codewhale Signature", zh: "Codewhale 日常款" },
61 heroBody: { en: "A little whale beside the name. Small on the chest, easy to wear every day.", zh: "名字旁边,一只小鲸。胸前小小的图案,日常穿着刚刚好。" },
62 heroAlt: { en: "Six everyday Codewhale shirt concepts: Signature, Wordmark, Classic, Field, Ocean line and use codewhale., with different colors and print placements.", zh: "六款日常 Codewhale T 恤概念:日常款、字标款、经典款、前后印款、海平线款和 use codewhale.,展示不同颜色与印花位置。" },
63 collection: { en: "Codewhale, every day.", zh: "日常穿 Codewhale。" },
64 collectionBody: { en: "Small chest prints, clean wordmarks and the classic whale. Start with Signature or Classic; the other layouts give you more ways to wear Codewhale.", zh: "胸前小图案、简洁字标,还有经典的小鲸。先看看日常款和经典款,再选一种你喜欢的 Codewhale 穿法。" },
65 colorNote: { en: "Color studies above; ordering begins with reviewed white-tee prints. Field has two print placements and needs a separate quote.", zh: "上图为配色探索;订购先从确认过的白色 T 恤印花开始。前后印款有两个印花位置,需要单独报价。" },
66 classicTitle: { en: "Codewhale Classic", zh: "Codewhale 经典款" },
67 classicBody: { en: "The whale and the name, centered. A familiar Codewhale tee for a meetup or an ordinary Tuesday.", zh: "小鲸和名字,放在正中间。去聚会,或普通的一天,都可以穿 Codewhale。" },
68 wordmarkTitle: { en: "Codewhale Wordmark", zh: "Codewhale 字标款" },
69 wordmarkBody: { en: "Just Codewhale and the website. Clean type, no extra story.", zh: "只印 Codewhale 和网址。简洁的字,不必多说。" },
70 oceanTitle: { en: "Ocean line", zh: "海平线款" },
71 oceanBody: { en: "A small whale, the name, one quiet line. Plenty of space around it.", zh: "一只小鲸,一个名字,一条安静的线。四周,留一点空白。" },
72 pocketTitle: { en: "use codewhale. · small chest print", zh: "use codewhale. · 胸前小印花" },
73 pocketBody: { en: "The invitation, kept small. Whale, words and website on the left chest.", zh: "把邀请,印得小一点。小鲸、文字和网址,都在左胸。" },
74 community: { en: "A little more personality.", zh: "再添一点个性。" },
75 communityBody: { en: "Whale Bro, a build-time joke, or a larger use codewhale. print. The same simple apparel direction.", zh: "鲸鱼兄弟、编译时的笑话,或更大的 use codewhale. 印花。依然是简洁的日常服饰。" },
76 whaleBroTitle: { en: "Whale Bro", zh: "鲸鱼兄弟" },
77 whaleBroBody: { en: "The community nickname, with a small whale and Codewhale signature.", zh: "社区里的昵称,配一只小鲸和 Codewhale 的署名。" },
78 select: { en: "Review this tee", zh: "查看这款 T 恤" },
79 selected: { en: "Selected design", zh: "已选图案" },
80 targetPrice: { en: "Target tee price: ¥{cny} in mainland China · US${usd} elsewhere", zh: "T 恤目标售价:中国大陆 ¥{cny} · 其他地区 US${usd}" },
81 priceNote: { en: "Shipping is separate. Final pricing depends on the approved print; delivery and tax notes are reviewed before payment.", zh: "运费单独显示。最终价格以确认的印刷方案为准,付款前会说明配送与税费情况。" },
82 designNote: { en: "Artwork previews are concepts. Print details, fit and availability need a production proof.", zh: "图案预览为设计概念,印刷细节、版型与供货需要生产样品确认。" },
83 compilingTitle: { en: "Compiling…", zh: "编译中……" },
84 compilingBody: { en: "Quiet type for the time between builds.", zh: "把编译的等待,留给一行安静的文字。" },
85 useTitle: { en: "use codewhale.", zh: "use codewhale." },
86 useBody: { en: "The whale mark and a straightforward invitation. One color, a few words.", zh: "鲸鱼标记,加上一句直接的邀请。一种颜色,几句简单的话。" },
87 contributorTitle: { en: "Built together. Offered at cost.", zh: "一起构建,按成本提供。" },
88 contributorBody: { en: "Pick a phrase in one of Codewhale's languages. Add a GitHub username, user ID or contribution link; it stays off the print. Production, processing and postage are shown before payment.", zh: "用 Codewhale 的一种语言,选一句想穿在身上的话。填写 GitHub 用户名、用户 ID 或贡献链接,这些信息不会印在衣服上。付款前,制作、支付和运费都会列明。" },
89 contributorLink: { en: "Choose your contributor tee", zh: "选择贡献者 T 恤" },
90 contributorFlowNote: { en: "Choose your print text and review the local draft first. Delivery details are sent only when you request a delivery price.", zh: "先选择印字并查看本地草稿。仅在请求配送价格时发送配送信息。" },
91 contributorArtNote: { en: "Simple print text in Codewhale's languages. The contributor page previews your selected phrase.", zh: "用 Codewhale 的语言,印上简单的文字。贡献者页面会预览你选择的短语。" },
92 seasonal: { en: "A moment on tour.", zh: "巡游中的一刻。" },
93 seasonalBody: { en: "The China Tour design sits beside the everyday collection. Seasonal artwork is being reviewed; ordering is not open for this edition.", zh: "中国巡游图案与日常系列并肩。季节限定设计仍在确认,这一款暂未开放订购。" },
94 deskTitle: { en: "Detailed art for the desk.", zh: "复杂的故事,留给桌面。" },
95 deskBody: { en: "Stage Dive, cache jokes and developer characters, with room for color and detail. Mousepads, deskmats and stickers need print, texture and durability samples before pricing or orders.", zh: "舞台跃动、缓存笑话与开发者角色,让色彩与细节展开。鼠标垫、桌垫与贴纸先确认印刷、手感与耐用度,再定价和开放订购。" },
96 stageTitle: { en: "Open source. Open mic.", zh: "开源,也开麦。" },
97 stageBody: { en: "Concert energy for a deskmat or sticker. The detailed Stage Dive artwork is being adapted for the desk.", zh: "把开源演出的热闹,放到桌垫和贴纸上。舞台跃动的复杂图案,正在改为桌面周边。" },
98 stageAlt: { en: "Codewhale desk gear concepts with Stage Dive concert artwork, ocean currents and developer stickers.", zh: "Codewhale 桌面周边概念:舞台演出图案、海流和开发者贴纸。" },
99 cacheTitle: { en: "Cache me if you can.", zh: "Cache me if you can." },
100 cacheAlt: { en: "Flat mousepad artwork with a whale, ocean currents and Cache me if you can lettering.", zh: "鼠标垫平面图案:鲸鱼、海流与 Cache me if you can 字样。" },
101 deskmat: { en: "Deskmat", zh: "桌垫" },
102 mousepad: { en: "Mousepad", zh: "鼠标垫" },
103 stickers: { en: "Stickers", zh: "贴纸" },
104 deskAlt: { en: "Detailed Codewhale mousepad, deskmat and sticker artwork concepts.", zh: "Codewhale 鼠标垫、桌垫与贴纸的复杂图案设计概念。" },
105 later: { en: "Still in the sketchbook.", zh: "还在画册里。" },
106 laterBody: { en: "Little things to take with you. These accessory concepts are still being explored.", zh: "能随身带走的小物。这些配件图案仍在探索中。" },
107 concept: { en: "Concept · not for sale", zh: "设计概念 · 暂不售卖" },
108 plushTitle: { en: "A Codewhale companion for your desk.", zh: "桌边的 Codewhale 毛绒伙伴。" },
109 plushBody: { en: "A 20cm seated doll with an embroidered face, layered navy-to-aqua hair, soft whale clip and tail, and a navy-and-white outfit. Part of the planned collection; a physical prototype comes before preorders.", zh: "20 厘米坐姿娃娃,刺绣面孔、深蓝到浅水蓝的层叠头发、柔软的鲸鱼发夹与尾巴,搭配蓝白服装。它已加入计划中的系列,先做实物样品,再开放预订。" },
110 plushStatus: { en: "In development · prototype planned", zh: "筹备中 · 实物打样计划" },
111 plushAlt: { en: "Codewhale 20cm seated plush doll concept in front, three-quarter and back views: embroidered blue eyes, layered navy-to-aqua hair, whale clip and tail, and navy-and-white outfit.", zh: "Codewhale 20 厘米坐姿毛绒娃娃概念,展示正面、侧面与背面:蓝色刺绣眼睛、深蓝至浅水蓝的层叠头发、鲸鱼发夹与尾巴,以及蓝白服装。" },
112 plushLink: { en: "Meet the plush doll", zh: "看看毛绒娃娃" },
113 accessoriesTitle: { en: "Little companions", zh: "随身小伙伴" },
114 hoodieTitle: { en: "The same little whale. A simple hoodie.", zh: "还是那只小鲸,换一件简单的卫衣。" },
115 hoodieBody: { en: "A small whale mark and quiet type on a plain hoodie. Fabric, fit and print are sampled before ordering opens.", zh: "纯色卫衣上,印一只小鲸和简单的字。先用实物样品确认面料、版型与印刷,再开放订购。" },
116 deliveryTitle: { en: "Your tee, your destination.", zh: "你的 T 恤,寄到你的目的地。" },
117 deliveryBody: { en: "Choose a country and enter the delivery address. Review the approved destination shipping rate, total and tax note before continuing to payment.", zh: "选择国家或地区并填写配送地址。前往付款前,先查看经确认的目的地运费、合计与税费说明。" },
118 destination: { en: "Delivery country or region", zh: "配送国家或地区" },
119 size: { en: "Size", zh: "尺码" },
120 white: { en: "White cotton tee · one item", zh: "白色纯棉 T 恤 · 一件" },
121 name: { en: "Recipient name", zh: "收件人姓名" },
122 line1: { en: "Street address", zh: "街道地址" },
123 line2: { en: "Apartment, building or unit (optional)", zh: "楼栋、单元、房间号(选填)" },
124 city: { en: "City", zh: "城市" },
125 region: { en: "State, province or region", zh: "州、省或地区" },
126 postalCode: { en: "Postal code", zh: "邮政编码" },
127 phone: { en: "Delivery phone number", zh: "配送联系电话" },
128 addressNote: { en: "This address is used for delivery checks and fulfillment. Changing it requires a new price review.", zh: "此地址用于配送确认与履约。修改地址后需重新确认价格。" },
129 reviewDelivery: { en: "Review delivery price", zh: "确认配送价格" },
130 reviewing: { en: "Reviewing delivery…", zh: "正在确认配送……" },
131 notReady: { en: "Samples before sales. Ordering is not open for this design yet; no payment will be taken.", zh: "先确认样品,再开售。此图案尚未开放订购,不会收取付款。" },
132 checking: { en: "Checking ordering availability…", zh: "正在确认是否可订购……" },
133 statusError: { en: "Ordering availability could not be checked. Please try again later.", zh: "暂时无法确认是否可订购,请稍后重试。" },
134 quoteError: { en: "We couldn't review delivery for this address. Check the details and try again.", zh: "无法确认此地址的配送价格,请检查信息后重试。" },
135 routeUnavailable: { en: "This design and destination still need a print and delivery review.", zh: "此图案与目的地仍需完成印刷及配送确认。" },
136 tryLater: { en: "Please wait a minute, then try again.", zh: "请稍等一分钟后重试。" },
137 checkoutError: { en: "Payment could not be opened. Your order has not been placed. Try again or review delivery again.", zh: "无法打开付款页面,订单尚未提交。请重试或重新确认配送价格。" },
138 expired: { en: "This price review has expired. Please review delivery again before payment.", zh: "此次价格确认已过期,付款前请重新确认配送价格。" },
139 merchandise: { en: "Printed tee", zh: "印花 T 恤" },
140 shipping: { en: "Destination shipping rate", zh: "目的地运费" },
141 processing: { en: "Payment processing", zh: "支付手续费" },
142 total: { en: "Total before any charges noted below", zh: "合计(下方另有说明的费用除外)" },
143 quoteTitle: { en: "Review before you pay.", zh: "付款前,确认一下。" },
144 payment: { en: "Continue to secure payment", zh: "前往安全付款" },
145 opening: { en: "Opening payment…", zh: "正在打开付款……" },
146 returnToMerch: { en: "Back to the collection", zh: "返回周边系列" },
147 } as const;
148
149 export type StorefrontCopy = Record<keyof typeof text, string>;
150 export function getStorefrontCopy(locale: string): StorefrontCopy {
151 const copy = Object.fromEntries(Object.entries(text).map(([key, value]) => [key, pickText(value, locale)])) as StorefrontCopy;
152 const number = new Intl.NumberFormat(locale, { maximumFractionDigits: 2 });
153 copy.targetPrice = copy.targetPrice.replace("{cny}", number.format(MERCH_TARGET_PRICE.cny / 100)).replace("{usd}", number.format(MERCH_TARGET_PRICE.usd / 100));
154 return copy;
155 }
156
157 export function countryLabel(code: string, fallback: string, locale: string): string {
158 try { return new Intl.DisplayNames([locale], { type: "region" }).of(code) ?? fallback; }
159 catch { return fallback; }
160 }
161
161 lines TYPESCRIPT