@tomako/tools-runtime 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -4
- package/src/components/tools/workspace/tool-workspace-layout.tsx +1 -1
- package/src/constants/related-tools.ts +4 -0
- package/src/features/tools/app-icon-resizer/app-icon-resizer.container.tsx +15 -2
- package/src/features/tools/app-icon-resizer/app-icon-resizer.spec.ts +1 -1
- package/src/features/tools/app-icon-resizer/widget/app-icon-resizer.tsx +54 -11
- package/src/features/tools/app-icon-resizer/widget/platform-icons.tsx +2 -5
- package/src/features/tools/mrr-calculator/mrr-calculator.container.tsx +23 -0
- package/src/features/tools/mrr-calculator/mrr-calculator.spec.ts +15 -0
- package/src/features/tools/mrr-calculator/widget/calculations.ts +74 -0
- package/src/features/tools/mrr-calculator/widget/index.ts +1 -0
- package/src/features/tools/mrr-calculator/widget/mrr-calculator.tsx +581 -0
- package/src/features/tools/registry.ts +6 -0
- package/src/features/tools/runtime-registry.ts +4 -0
- package/src/features/tools/shared/create-standard-landing-container.tsx +35 -5
- package/src/features/tools/shared/tool-page-shell.tsx +4 -4
- package/src/features/tools/shared/tool-standard-landing-sections.tsx +262 -52
- package/src/features/tools/spec-registry.ts +2 -0
- package/src/features/tools/widget-registry.ts +2 -0
- package/src/i18n/messages/en/tools/app-icon-resizer.ts +90 -62
- package/src/i18n/messages/en/tools/build-in-public-story-generator.ts +1 -1
- package/src/i18n/messages/en/tools/index.ts +2 -0
- package/src/i18n/messages/en/tools/mrr-calculator.ts +202 -0
- package/src/i18n/messages/en/tools-ui.ts +3 -2
- package/src/i18n/messages/zh/page/tools.ts +2 -3
- package/src/i18n/messages/zh/tools/app-icon-resizer.ts +90 -62
- package/src/i18n/messages/zh/tools/build-in-public-story-generator.ts +1 -1
- package/src/i18n/messages/zh/tools/index.ts +2 -0
- package/src/i18n/messages/zh/tools/mrr-calculator.ts +112 -0
- package/src/i18n/messages/zh/tools-ui.ts +3 -2
- package/src/i18n/messages/zh-tw/tools/app-icon-resizer.ts +90 -62
- package/src/i18n/messages/zh-tw/tools/build-in-public-story-generator.ts +1 -1
- package/src/i18n/messages/zh-tw/tools/index.ts +2 -0
- package/src/i18n/messages/zh-tw/tools/mrr-calculator.ts +52 -0
- package/src/i18n/messages/zh-tw/tools-ui.ts +3 -2
- package/src/lib/tools/resolve-tool-locale-copy.ts +7 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const appIconResizer = {
|
|
2
2
|
meta: {
|
|
3
|
-
title: "App Icon
|
|
3
|
+
title: "App Icon 尺寸產生器:iOS 與 Android",
|
|
4
4
|
description:
|
|
5
|
-
"
|
|
6
|
-
intro: "
|
|
5
|
+
"上傳方形 App Icon,在瀏覽器本地產生 Xcode AppIcon、Google Play 512 與 Android launcher 圖示包,預覽後下載 ZIP。",
|
|
6
|
+
intro: "上傳 1024px+ 方形原圖,本地產生 Xcode AppIcon、Google Play 圖示和 Android legacy launcher 檔案。",
|
|
7
7
|
category: "App 上架工具",
|
|
8
8
|
keywords: [
|
|
9
9
|
"app icon 切圖",
|
|
@@ -19,48 +19,61 @@ const appIconResizer = {
|
|
|
19
19
|
guide: {
|
|
20
20
|
heroVisualAlt:
|
|
21
21
|
"App Icon 資源包視覺圖,展示一個源圖標導出為商店提交圖標、Xcode 資源和 Android launcher 文件。",
|
|
22
|
-
workspaceHeading: "App Icon
|
|
22
|
+
workspaceHeading: "App Icon 尺寸包產生器",
|
|
23
23
|
workspaceHint:
|
|
24
|
-
"
|
|
24
|
+
"一張原圖,本地產生商店與工程圖示 ZIP。",
|
|
25
25
|
workspaceBadge: "PNG + ZIP",
|
|
26
26
|
standard: {
|
|
27
27
|
benefits: {
|
|
28
|
-
title: "
|
|
28
|
+
title: "為什麼用這個工具",
|
|
29
29
|
cards: [
|
|
30
30
|
{
|
|
31
|
-
title: "
|
|
32
|
-
body: "
|
|
31
|
+
title: "一次產生交付檔案",
|
|
32
|
+
body: "不用逐張建立畫板和匯出。一次產生 Xcode AppIcon、Google Play 512 與 Android legacy launcher 檔案。",
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
|
-
title: "
|
|
36
|
-
body: "
|
|
35
|
+
title: "目錄和命名已整理",
|
|
36
|
+
body: "商店上傳圖、Xcode 資源與 Android 工程檔案按用途分組,下載後不用自己建目錄或逐張改名。",
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
|
-
title: "
|
|
40
|
-
body: "
|
|
39
|
+
title: "先預覽再下載",
|
|
40
|
+
body: "先檢查透明背景、裁切方式和檔案大小風險。切圖和 ZIP 打包都在目前瀏覽器完成。",
|
|
41
41
|
},
|
|
42
42
|
],
|
|
43
43
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
44
|
+
outputPreview: {
|
|
45
|
+
eyebrow: "範例輸出",
|
|
46
|
+
title: "下載包裡有什麼",
|
|
47
|
+
intro: "選擇雙平台後,檔案會按實際交付位置分組。下面是目前工具會產生的目錄和檔案。",
|
|
48
|
+
inputLabel: "範例輸入",
|
|
49
|
+
input: "一張 1024 × 1024 方形 PNG,選擇 Apple + Google Play / Android",
|
|
50
|
+
outputLabel: "ZIP 主要內容",
|
|
51
|
+
fields: [
|
|
52
|
+
{
|
|
53
|
+
label: "Apple/AppIcon.appiconset",
|
|
54
|
+
body: "iPhone、iPad 與 ios-marketing 相容尺寸,附 Contents.json。",
|
|
55
55
|
},
|
|
56
|
-
|
|
57
|
-
|
|
56
|
+
{
|
|
57
|
+
label: "Apple/AppStoreIcon-1024.png",
|
|
58
|
+
body: "供團隊在上架前檢查主體、留白和品牌一致性的 1024px 副本。",
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
label: "GooglePlay/app-icon-512.png",
|
|
62
|
+
body: "Play Console store listing 使用的 512 × 512 PNG。",
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
label: "Android/res/mipmap-*",
|
|
66
|
+
body: "mdpi 到 xxxhdpi 的 legacy launcher PNG;不含 Adaptive Icon 圖層。",
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
imageTextSections: [
|
|
58
71
|
{
|
|
59
72
|
id: "platform-scope",
|
|
60
|
-
title: "
|
|
73
|
+
title: "下載後怎麼使用",
|
|
61
74
|
body: [
|
|
62
|
-
"
|
|
63
|
-
"
|
|
75
|
+
"把 Apple/AppIcon.appiconset 匯入 Xcode asset catalog,再隨 build 提交;GooglePlay/app-icon-512.png 用於 Play Console store listing。",
|
|
76
|
+
"Android/res/mipmap-* 是 legacy launcher 工程資源。如果專案使用 Adaptive Icon,仍需在 Android Studio 設定前景、背景和可選的 monochrome 圖層。",
|
|
64
77
|
],
|
|
65
78
|
image: {
|
|
66
79
|
src: "/tools/app-icon-resizer-section-variation-8bit-v3.webp",
|
|
@@ -69,10 +82,10 @@ const appIconResizer = {
|
|
|
69
82
|
},
|
|
70
83
|
{
|
|
71
84
|
id: "handoff-review",
|
|
72
|
-
title: "
|
|
85
|
+
title: "使用前需要知道",
|
|
73
86
|
body: [
|
|
74
|
-
"
|
|
75
|
-
"
|
|
87
|
+
"非方形圖片會置中裁成方形,不會拉伸。正式交付應優先使用方形滿版源圖,也不要預先加入圓角或外陰影。",
|
|
88
|
+
"圖片讀取、縮放和 ZIP 打包都在瀏覽器本地完成。正式發布前仍需複核平台最新規則、品牌版權,以及淺色和深色背景下的辨識度。",
|
|
76
89
|
],
|
|
77
90
|
image: {
|
|
78
91
|
src: "/tools/app-icon-resizer-section-support-8bit-v3.webp",
|
|
@@ -82,31 +95,56 @@ const appIconResizer = {
|
|
|
82
95
|
},
|
|
83
96
|
],
|
|
84
97
|
generation: {
|
|
85
|
-
title: "
|
|
98
|
+
title: "三步完成",
|
|
86
99
|
imageAlt: "上傳 App Icon 後選擇平臺、裁切預覽並導出 ZIP 的 8bit 流程圖",
|
|
87
100
|
ctaLabel: "生成圖標資源包",
|
|
88
101
|
steps: [
|
|
89
102
|
{
|
|
90
|
-
title: "
|
|
91
|
-
body: "
|
|
103
|
+
title: "上傳並選擇平台",
|
|
104
|
+
body: "上傳 PNG、JPG 或 WebP,再選擇 Apple、Google Play / Android 或雙平台。Apple 包需要原圖短邊至少 1024px。",
|
|
92
105
|
},
|
|
93
106
|
{
|
|
94
|
-
title: "
|
|
95
|
-
body: "
|
|
107
|
+
title: "本地生成尺寸包",
|
|
108
|
+
body: "瀏覽器完成裁切、縮放、命名和目錄整理,同時生成 PNG、Contents.json 與 README。",
|
|
96
109
|
},
|
|
97
110
|
{
|
|
98
|
-
title: "
|
|
99
|
-
body: "
|
|
111
|
+
title: "預覽並下載 ZIP",
|
|
112
|
+
body: "在結果區篩選平台、檢查透明網格和檔案大小風險,然後下載完整 ZIP 或單個 PNG。",
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
referenceTable: {
|
|
117
|
+
title: "實際輸出與使用位置",
|
|
118
|
+
intro: "現代 Xcode 可以從單張 1024px 圖示產生較小變體;Tomako 仍提供完整相容型 AppIcon.appiconset,方便接入現有專案和團隊交付。",
|
|
119
|
+
mobileHint: "表格可左右滑動,查看完整檔案去向。",
|
|
120
|
+
columns: ["輸出組", "包含內容", "目前用途", "使用位置"],
|
|
121
|
+
rows: [
|
|
122
|
+
{
|
|
123
|
+
label: "Xcode AppIcon",
|
|
124
|
+
cells: ["20–1024px 相容尺寸,附 Contents.json", "iOS / iPadOS 工程圖示", "Xcode asset catalog"],
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
label: "Apple 1024 副本",
|
|
128
|
+
cells: ["Apple/AppStoreIcon-1024.png", "團隊視覺複核,不是另一套固定上傳流程", "上架前檢查主體與留白"],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
label: "Google Play",
|
|
132
|
+
cells: ["GooglePlay/app-icon-512.png", "512 × 512 PNG;超過 1024KB 時提示", "Play Console store listing"],
|
|
100
133
|
},
|
|
101
134
|
{
|
|
102
|
-
|
|
103
|
-
|
|
135
|
+
label: "Android legacy launcher",
|
|
136
|
+
cells: ["48 / 72 / 96 / 144 / 192px", "相容型 launcher PNG", "Android/res/mipmap-*"],
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: "交付說明",
|
|
140
|
+
cells: ["README.txt", "目錄用途與邊界說明", "設計、工程與 ASO 交接"],
|
|
104
141
|
},
|
|
105
142
|
],
|
|
143
|
+
note: "Google Play 圖示還需符合平台的 PNG、檔案大小和內容規範。目前不產生 Android Adaptive Icon 的 foreground、background 或 monochrome 圖層。",
|
|
106
144
|
},
|
|
107
145
|
faqIntro:
|
|
108
|
-
"
|
|
109
|
-
faqCtaLabel: "
|
|
146
|
+
"先確認原圖要求和檔案去向,再判斷這套輸出是否適合你的發布流程。",
|
|
147
|
+
faqCtaLabel: "回到工作台",
|
|
110
148
|
},
|
|
111
149
|
whatYouGetHeading: "生成後會得到什麼",
|
|
112
150
|
valueVisualAlt: "整理好的 App Icon 文件包,包含 Xcode 資源目錄、商店提交圖標、Android 工程目錄和 ZIP 下載包。",
|
|
@@ -192,44 +230,34 @@ const appIconResizer = {
|
|
|
192
230
|
faqHeading: "常見問題",
|
|
193
231
|
faqItems: [
|
|
194
232
|
{
|
|
195
|
-
question: "
|
|
233
|
+
question: "原圖應該準備成什麼格式和尺寸?",
|
|
196
234
|
answer:
|
|
197
|
-
"
|
|
235
|
+
"建議使用 1024 × 1024 或更大的方形 PNG。JPG 和 WebP 也支援;只生成 Google Play / Android 時,原圖短邊至少需要 512px。",
|
|
198
236
|
},
|
|
199
237
|
{
|
|
200
|
-
question: "
|
|
238
|
+
question: "上傳的 App Icon 會儲存到伺服器嗎?",
|
|
201
239
|
answer:
|
|
202
|
-
"
|
|
240
|
+
"不會。圖片讀取、canvas 切圖和 ZIP 打包都在瀏覽器本地完成。重新整理頁面後檔案狀態會消失,原圖不會上傳伺服器。",
|
|
203
241
|
},
|
|
204
242
|
{
|
|
205
|
-
question: "
|
|
243
|
+
question: "生成的圖示能直接用於 App Store 和 Google Play 嗎?",
|
|
206
244
|
answer:
|
|
207
|
-
"
|
|
245
|
+
"可以作為工程接入和商店上傳素材,但正式發布前仍需複核。GooglePlay/app-icon-512.png 用於 Play Console;Apple/AppIcon.appiconset 應放入 Xcode,再隨 build 提交到 App Store Connect。",
|
|
208
246
|
},
|
|
209
247
|
{
|
|
210
248
|
question: "為什麼不自動加圓角或陰影?",
|
|
211
249
|
answer:
|
|
212
|
-
"
|
|
213
|
-
},
|
|
214
|
-
{
|
|
215
|
-
question: "源圖不是方形怎麼辦?",
|
|
216
|
-
answer:
|
|
217
|
-
"當前會按中心裁成方形,不會拉伸圖片。正式交付建議回到設計源文件導出方形滿版圖,避免主體偏移或邊緣被裁掉。",
|
|
250
|
+
"不用提前加入。商店和系統會套用自己的遮罩、圓角或陰影,預先處理可能造成二次裁切或邊緣不齊。",
|
|
218
251
|
},
|
|
219
252
|
{
|
|
220
|
-
question: "
|
|
253
|
+
question: "原圖不是方形或帶透明背景怎麼辦?",
|
|
221
254
|
answer:
|
|
222
|
-
"
|
|
255
|
+
"會置中裁成方形,不會拉伸圖片。透明像素會保留;正式發布前仍要檢查主體位置、裁切邊緣和不同背景下的辨識度。",
|
|
223
256
|
},
|
|
224
257
|
{
|
|
225
258
|
question: "這個工具會生成 Android adaptive icon 嗎?",
|
|
226
259
|
answer:
|
|
227
|
-
"
|
|
228
|
-
},
|
|
229
|
-
{
|
|
230
|
-
question: "圖片太小會怎樣?",
|
|
231
|
-
answer:
|
|
232
|
-
"選擇 Apple 或雙平臺時,源圖短邊低於 1024px 會被攔截;只生成 Google Play / Android 時,短邊低於 512px 會被攔截。Google Play 512 圖標超過 1024KB 時會顯示風險提示。",
|
|
260
|
+
"不會。目前輸出 Google Play 512 和 legacy launcher mipmap PNG,不生成 adaptive icon 的 foreground/background XML。你需要在 Android 工程裡另外設定。",
|
|
233
261
|
},
|
|
234
262
|
],
|
|
235
263
|
ctaPrimary: "繼續做商店截圖",
|
|
@@ -153,7 +153,7 @@ const buildInPublicStoryGenerator = {
|
|
|
153
153
|
stageLabel: "當前階段",
|
|
154
154
|
productContextLabel: "產品背景",
|
|
155
155
|
productContextPlaceholder:
|
|
156
|
-
"Tomako
|
|
156
|
+
"Tomako 是軟體團隊 24 小時在線的 AI CMO,把產品與市場上下文轉成有優先順序、可審核的內容、KOL、SEO、用戶回饋、發佈和競品情報工作。",
|
|
157
157
|
thisWeekLabel: "這周真實進展",
|
|
158
158
|
thisWeekPlaceholder:
|
|
159
159
|
"例如:我們用 Tomako 生成了冷啟動渠道排序,發現 Reddit 適合問具體判斷,Product Hunt 暫時不適合直接衝發佈。",
|
|
@@ -14,6 +14,7 @@ import marketForecastReport from "./market-forecast-report";
|
|
|
14
14
|
import kokoCodexAssistant from "./koko-codex-assistant";
|
|
15
15
|
import logoGenerator from "./logo-generator";
|
|
16
16
|
import millionUserGrowthTest from "./million-user-growth-test";
|
|
17
|
+
import mrrCalculator from "./mrr-calculator";
|
|
17
18
|
import ogImageGenerator from "./og-image-generator";
|
|
18
19
|
import pitchDeckGenerator from "./pitch-deck-generator";
|
|
19
20
|
import productAgreementGenerator from "./product-agreement-generator";
|
|
@@ -59,6 +60,7 @@ const toolsPages = {
|
|
|
59
60
|
kokoCodexAssistant,
|
|
60
61
|
logoGenerator,
|
|
61
62
|
millionUserGrowthTest,
|
|
63
|
+
mrrCalculator,
|
|
62
64
|
ogImageGenerator,
|
|
63
65
|
appStoreScreenshotGenerator,
|
|
64
66
|
twitterGifDownloader,
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const mrrCalculator = {
|
|
2
|
+
meta: {
|
|
3
|
+
title: "SaaS MRR 計算器|每月經常性收入計算",
|
|
4
|
+
description: "用客戶資料計算 MRR 和 ARR,或根據公開網站流量、方案價格和可調整假設估算 SaaS MRR。",
|
|
5
|
+
intro: "把月繳、季繳或年繳訂閱統一換算為每月經常性收入,也可以切換到公開資料估算模式。",
|
|
6
|
+
category: "SaaS 指標工具",
|
|
7
|
+
keywords: ["MRR 計算器", "每月經常性收入計算器", "MRR 公式", "如何計算 MRR", "MRR 轉 ARR", "SaaS MRR 計算器"],
|
|
8
|
+
inputSignal: "客戶與計費資料,或公開流量與方案價格",
|
|
9
|
+
outputSignal: "MRR、ARR、每月 ARPA、成長率或公開資料估算",
|
|
10
|
+
boundarySignal: "瀏覽器本機計算;估算結果僅供方向判斷,並非公司揭露收入",
|
|
11
|
+
},
|
|
12
|
+
guide: {
|
|
13
|
+
workspaceHeading: "SaaS MRR 計算器",
|
|
14
|
+
workspaceHint: "用已知客戶資料計算每月經常性收入,或切換到估算模式,用公開流量和方案價格粗略判斷 SaaS MRR。",
|
|
15
|
+
standard: {
|
|
16
|
+
benefits: { title: "這個 MRR 計算器會回傳什麼", cards: [
|
|
17
|
+
{ title: "同時得到 MRR 和 ARR", body: "無需製作試算表,直接查看每月經常性收入、年度經常性收入和每月 ARPA。" },
|
|
18
|
+
{ title: "自動統一計費週期", body: "輸入月繳、季繳或年繳價格,計算器會先統一換算到每月口徑。" },
|
|
19
|
+
{ title: "也能粗略評估競品", body: "不知道客戶數時,用公開流量、方案價格和可調整假設得到明確標註的估算結果。" },
|
|
20
|
+
] },
|
|
21
|
+
imageTextSections: [
|
|
22
|
+
{ id: "choose-the-right-mrr-method", title: "選擇正確的\nMRR 計算方式", body: [{ text: "想計算自己產品的 MRR 或 ARR 時,使用預設的「計算 MRR」。填寫活躍付費客戶數、每位客戶的經常性金額和計費週期,工具會直接計算結果。", highlights: ["計算 MRR"] }, { text: "想粗略了解競品或其他 SaaS 的收入量級時,切換到「估算 SaaS MRR」。根據公開網站流量和方案價格填寫估算條件;結果只用於方向判斷,不是對方揭露的收入。", highlights: ["估算 SaaS MRR"] }], image: { src: "/tools/mrr-calculator-section-steps-8bit.webp", alt: "像素風工作台比較用客戶資料計算 MRR 與用公開流量和方案價格估算 SaaS MRR。" }, imageFirst: true, hideImageOnMobile: true },
|
|
23
|
+
{ id: "monthly-recurring-revenue-formula", title: "如何計算\n每月經常性收入", body: [{ text: "每月經常性收入公式是:MRR = 活躍付費客戶數 × 每月 ARPA。季繳經常性金額先除以 3,年繳金額先除以 12,再乘以客戶數。", highlights: ["MRR = 活躍付費客戶數 × 每月 ARPA"] }, "例如,12 位客戶每年支付 1,200 美元,每月 ARPA 為 100 美元,MRR 為 1,200 美元,ARR 為 14,400 美元。若上月 MRR 為 1,000 美元,月增率是(1,200 − 1,000)÷ 1,000 = 20%。"], image: { src: "/tools/mrr-calculator-section-primary-8bit.webp", alt: "像素風 SaaS 帳本把月繳、季繳和年繳訂閱統一換算成 MRR。" } },
|
|
24
|
+
{ id: "estimate-saas-mrr", title: "用公開資料\n估算 SaaS MRR", body: ["估算模式先用「造訪量 × 可用於獲客的造訪比例 × 訪客轉付費率」估算每月新增付費客戶,再用平均付費週期估算活躍客戶數,最後乘以換算後的每月方案價格。", "重複造訪、現有客戶、獲客通路、轉換率、方案結構和流失仍然未知。比較多個產品時應保持假設一致,商業模式不同時再調整;結果只適合方向判斷。"], image: { src: "/tools/mrr-calculator-section-variation-8bit.webp", alt: "像素風公開流量圖和方案價格卡片進入明確標註的 SaaS MRR 估算結果。" }, imageFirst: true },
|
|
25
|
+
],
|
|
26
|
+
editorialSections: [
|
|
27
|
+
{ id: "what-counts-as-mrr", title: "哪些收入應計入每月經常性收入", body: ["MRR 是統一到單月口徑的經常性訂閱收入。應計入目前合約下持續發生的有效方案收入,季繳或年繳金額需要先換算到每月。", "不要計入一次性設定費、導入費、專業服務、硬體、稅費和其他非經常性收款。如果涉及折扣、退款、用量計費、擴張、收縮或流失,應使用帳務系統資料和更完整的收入模型。"] },
|
|
28
|
+
{ id: "common-mrr-calculation-mistakes", title: "常見的 MRR 計算錯誤", body: ["不要把整筆年繳收入全部放進一個月,也不要計入一次性收入,或把新增 MRR 當成公司總 MRR。涉及多個方案時,應分別計算後相加,或使用加權平均月價,不要假設客戶平均分布。", "在估算模式中,不要把所有網站造訪量或直接流量都當成新客戶。流量可能包含回訪者、現有客戶、員工和重複工作階段,因此可用於獲客的造訪比例必須保持為明確假設。", "最重要的是,不要把 Estimated MRR 情境寫成競品真實收入。公開流量和價格無法揭示真實客戶數、折扣、留存和獲客通路。"] },
|
|
29
|
+
{ id: "use-mrr-and-arr-results", title: "如何使用 MRR 和 ARR 結果", body: ["核對客戶數、經常性金額和計費週期後,可以把計算 MRR 用於營運檢查、每月報告或方案情境測試。填寫上月 MRR 可以查看月增率,把 MRR 轉成 ARR 可以得到年化經常性收入視角。", "估算 MRR 只適合競品方向性比較。比較不同產品時保持同一組假設,記錄結果依據,並在流量或價格變化後重新計算。", "這些結果可以繼續用於流失率、留存和 LTV:CAC 分析,但不屬於會計報表、財務建議或估值意見。"] },
|
|
30
|
+
],
|
|
31
|
+
faqIntro: "補充回答多方案、零分母、直接流量、估算邊界和瀏覽器本機資料處理問題。", faqCtaLabel: "使用計算器",
|
|
32
|
+
},
|
|
33
|
+
faqHeading: "MRR 計算器常見問題",
|
|
34
|
+
faqItems: [
|
|
35
|
+
{ question: "這個 MRR 計算器如何使用?", answer: "想計算自己產品的 MRR 或 ARR 時,保留預設的「計算 MRR」,填寫活躍付費客戶數、每位客戶的經常性金額和計費週期。想粗略了解競品或其他 SaaS 的收入量級時,切換到「估算 SaaS MRR」,根據公開網站流量和方案價格填寫估算條件。前者是依你的輸入直接計算;後者只是方向性估算。" },
|
|
36
|
+
{ question: "不同方案可以一起計算嗎?", answer: "V1 接收一個混合平均經常性金額。你可以分別計算各方案後相加,或在知道方案比例時先算出加權平均月價。" },
|
|
37
|
+
{ question: "上月 MRR 為零時怎麼辦?", answer: "工具仍會回傳本月 MRR 和 ARR,但不會顯示月增率,因為分母為零時百分比成長沒有可靠含義。" },
|
|
38
|
+
{ question: "可以把直接流量當成新客戶嗎?", answer: "不可以。直接流量可能包含回訪者、現有客戶、員工、書籤造訪和無法歸因的工作階段。估算時只應計入一部分可用於獲客的造訪量。" },
|
|
39
|
+
{ question: "SaaS MRR 估算有多準確?", answer: "它只適合方向判斷,不是精確結果。是否有參考價值取決於流量、價格、轉換率和付費週期假設是否接近被評估產品。" },
|
|
40
|
+
{ question: "這個估算屬於財務建議或估值意見嗎?", answer: "不屬於。它只是基於公開輸入和你的假設得到的情境,不應作為會計證據、公司收入事實、財務建議或估值結論。" },
|
|
41
|
+
{ question: "輸入的資料會離開瀏覽器嗎?", answer: "不會。目前版本在瀏覽器內完成計算,不上傳輸入值,也不需要註冊帳號。" },
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
widget: {
|
|
45
|
+
calculateMode: "計算 MRR", estimateMode: "估算 SaaS MRR", modeSwitchLabel: "切換計算與估算 MRR 模式", calculateHint: "使用已知付費客戶和訂閱資料,得到計算結果。", estimateHint: "使用公開流量和價格進行粗略估算,並非公司揭露收入。",
|
|
46
|
+
activeCustomers: "活躍付費客戶數", recurringAmount: "每位客戶的經常性金額", billingPeriod: "計費週期", previousMrr: "上月 MRR", optional: "選填", addPreviousMrr: "加入上月 MRR(選填)", currency: "幣別", monthlyVisits: "估算的網站每月造訪量", planPrice: "有代表性的公開方案價格", showAssumptions: "調整估算假設", hideAssumptions: "收起估算假設", assumptionNote: "這些只是起始假設,不是產業基準。請根據被評估產品進行調整。", eligibleVisitShare: "可用於獲客的造訪比例(%)", visitorToPaidRate: "訪客轉付費率(%)", averagePaidLifetime: "平均付費週期(月)",
|
|
47
|
+
periods: { month: "月繳", quarter: "季繳", year: "年繳" }, calculatedLabel: "計算結果", estimatedLabel: "估算結果", calculatedResultTitle: "每月經常性收入", estimatedResultTitle: "SaaS 收入方向性估算", calculatedMrr: "計算 MRR", estimatedMrr: "估算 MRR", arr: "ARR", estimatedArr: "估算 ARR", monthlyArpa: "每月 ARPA", monthlyPlanPrice: "每月方案價格", momGrowth: "MRR 月增率", newPaidCustomers: "每月新增付費客戶", activePaidCustomers: "估算活躍付費客戶", formula: "算式檢查", perMonth: "每月", assumptionsUsed: "本次使用的假設", notAvailable: "無法計算",
|
|
48
|
+
calculatedGuidance: "只計入經常性訂閱收入,排除一次性費用、稅費和非經常性服務。", estimateDisclaimer: "僅為公開資料估算。實際流量品質、轉換率、方案結構、流失和收入可能不同。", copyResult: "複製結果", copied: "已複製", reset: "重設", fixInputsTitle: "檢查標紅的輸入項", fixInputsBody: "輸入有效數值後,結果會自動更新。", errors: { nonNegative: "請輸入零或正數。", positive: "請輸入大於零的數值。", percent: "請輸入 0 到 100 之間的百分比。" },
|
|
49
|
+
},
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
export default mrrCalculator;
|
|
@@ -492,8 +492,9 @@ const toolsUi = {
|
|
|
492
492
|
downloadZip: "下載 ZIP",
|
|
493
493
|
selectedTitle: "選中圖標",
|
|
494
494
|
downloadPng: "下載 PNG",
|
|
495
|
-
outputEmptyTitle: "
|
|
496
|
-
outputEmptyHint: "
|
|
495
|
+
outputEmptyTitle: "上傳後在這裡檢查檔案包",
|
|
496
|
+
outputEmptyHint: "目前的平台選擇會生成下列主要目錄。上傳源圖並生成後,這裡會切換為實際 PNG 預覽和 ZIP 下載。",
|
|
497
|
+
outputExampleLabel: "app-icon-assets.zip 範例結構",
|
|
497
498
|
platforms: {
|
|
498
499
|
all: "App Store + Google Play",
|
|
499
500
|
apple: "Apple App Store / iOS",
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
} from "../../i18n/locale-fallback";
|
|
6
6
|
import type { AppLocale } from "../../i18n/locale";
|
|
7
7
|
import enToolsPages from "../../i18n/messages/en/tools";
|
|
8
|
+
import zhTwToolsPages from "../../i18n/messages/zh-tw/tools";
|
|
8
9
|
import zhToolsPages from "../../i18n/messages/zh/tools";
|
|
9
10
|
import type { ToolLocaleCopy, ToolSpec } from "../../features/tools/types";
|
|
10
11
|
|
|
@@ -13,6 +14,10 @@ import { resolveToolsPagesKey } from "./tool-message-keys";
|
|
|
13
14
|
type ToolPageModule = { meta?: ToolLocaleCopy };
|
|
14
15
|
|
|
15
16
|
function toolsPagesForPrimary(locale: AppLocale): Record<string, ToolPageModule> {
|
|
17
|
+
if (locale === "zh-tw") {
|
|
18
|
+
return zhTwToolsPages as unknown as Record<string, ToolPageModule>;
|
|
19
|
+
}
|
|
20
|
+
|
|
16
21
|
return (resolvePrimaryLocale(locale) === "zh"
|
|
17
22
|
? zhToolsPages
|
|
18
23
|
: enToolsPages) as unknown as Record<string, ToolPageModule>;
|
|
@@ -62,12 +67,13 @@ export function resolveToolSpecLocales(spec: ToolSpec): PartialLocaleRecord<Tool
|
|
|
62
67
|
|
|
63
68
|
const zh = getToolI18nMeta(spec.slug, "zh") ?? spec.locales?.zh;
|
|
64
69
|
const en = getToolI18nMeta(spec.slug, "en") ?? spec.locales?.en;
|
|
70
|
+
const zhTw = getToolI18nMeta(spec.slug, "zh-tw") ?? spec.locales?.["zh-tw"];
|
|
65
71
|
|
|
66
72
|
if (!zh || !en) {
|
|
67
73
|
throw new Error(`Missing zh/en locale copy for tool "${spec.slug}"`);
|
|
68
74
|
}
|
|
69
75
|
|
|
70
|
-
return { zh, en };
|
|
76
|
+
return zhTw ? { zh, en, "zh-tw": zhTw } : { zh, en };
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
export function withResolvedLocales(spec: ToolSpec): ToolSpec {
|