Skip to content

Commit 0686055

Browse files
KEN0421claude
andcommitted
feat: +6 tools (68 total, 230 pages, 18 zh-CN translations)
New tools across 5 categories: - finance: simple-interest-calculator, car-loan-calculator - health: body-fat-calculator (US Navy circumference method) - math: statistics-calculator (mean/median/stdev/quartiles) - datetime: workdays-calculator (skip weekends + custom holidays) - color: gradient-generator (linear/radial/conic, presets) All 6 tools include full en/ja/zh-CN translations with 8+ FAQ items and 3+ article sections per locale (HCU compliance maintained). Strategic affiliate fit: - car-loan: high-payout auto/insurance referrals - body-fat: fitness products (MyProtein, fitness trackers) - gradient: design SaaS (Adobe CC, Canva) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c112054 commit 0686055

31 files changed

Lines changed: 1952 additions & 0 deletions

File tree

‎site/src/lib/tools/registry.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,12 @@ import leapYear from "@/tools/leap-year-checker";
6262
import bmr from "@/tools/bmr-calculator";
6363
import ovulation from "@/tools/ovulation-calculator";
6464
import retirement from "@/tools/retirement-calculator";
65+
import simpleInterest from "@/tools/simple-interest-calculator";
66+
import carLoan from "@/tools/car-loan-calculator";
67+
import bodyFat from "@/tools/body-fat-calculator";
68+
import statistics from "@/tools/statistics-calculator";
69+
import workdays from "@/tools/workdays-calculator";
70+
import gradient from "@/tools/gradient-generator";
6571

6672
/**
6773
* 全ツールの中央レジストリ。新規ツール追加時はここに登録するだけで
@@ -130,6 +136,12 @@ export const TOOLS: ToolDefinition[] = [
130136
bmr,
131137
ovulation,
132138
retirement,
139+
simpleInterest,
140+
carLoan,
141+
bodyFat,
142+
statistics,
143+
workdays,
144+
gradient,
133145
];
134146

135147
const SLUG_INDEX = new Map(TOOLS.map((t) => [t.meta.slug, t]));
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"use client";
2+
3+
import { useMemo, useState } from "react";
4+
import { useLocale, useTranslations } from "next-intl";
5+
6+
type Sex = "male" | "female";
7+
8+
function navyBodyFat(sex: Sex, heightCm: number, neckCm: number, waistCm: number, hipCm: number): number {
9+
// US Navy method (cm version)
10+
if (sex === "male") {
11+
return 86.010 * Math.log10(waistCm - neckCm) - 70.041 * Math.log10(heightCm) + 36.76;
12+
}
13+
// female
14+
return 163.205 * Math.log10(waistCm + hipCm - neckCm) - 97.684 * Math.log10(heightCm) - 78.387;
15+
}
16+
17+
function bfCategory(sex: Sex, bfPct: number): string {
18+
if (sex === "male") {
19+
if (bfPct < 6) return "essential";
20+
if (bfPct < 14) return "athlete";
21+
if (bfPct < 18) return "fit";
22+
if (bfPct < 25) return "average";
23+
return "obese";
24+
}
25+
if (bfPct < 14) return "essential";
26+
if (bfPct < 21) return "athlete";
27+
if (bfPct < 25) return "fit";
28+
if (bfPct < 32) return "average";
29+
return "obese";
30+
}
31+
32+
export default function BodyFatCalculator() {
33+
const t = useTranslations("tools.body-fat-calculator");
34+
const locale = useLocale();
35+
const [sex, setSex] = useState<Sex>("male");
36+
const [height, setHeight] = useState("170");
37+
const [waist, setWaist] = useState("85");
38+
const [neck, setNeck] = useState("38");
39+
const [hip, setHip] = useState("95");
40+
const [weight, setWeight] = useState("70");
41+
42+
const result = useMemo(() => {
43+
const h = parseFloat(height);
44+
const w = parseFloat(waist);
45+
const n = parseFloat(neck);
46+
const hp = parseFloat(hip);
47+
const wt = parseFloat(weight);
48+
if (![h, w, n, wt].every(isFinite)) return null;
49+
if (sex === "female" && !isFinite(hp)) return null;
50+
const bf = navyBodyFat(sex, h, n, w, hp);
51+
if (!isFinite(bf) || bf <= 0 || bf >= 60) return null;
52+
const fatMass = (wt * bf) / 100;
53+
const leanMass = wt - fatMass;
54+
return { bf, fatMass, leanMass, category: bfCategory(sex, bf) };
55+
}, [sex, height, waist, neck, hip, weight]);
56+
57+
const fmt = useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 1 }), [locale]);
58+
59+
return (
60+
<div>
61+
<div className="mb-3 flex gap-2">
62+
<button onClick={() => setSex("male")} className={`rounded px-3 py-1 text-sm ${sex === "male" ? "bg-brand-600 text-white" : "border border-slate-300 dark:border-slate-700"}`}>{t("sex.male")}</button>
63+
<button onClick={() => setSex("female")} className={`rounded px-3 py-1 text-sm ${sex === "female" ? "bg-brand-600 text-white" : "border border-slate-300 dark:border-slate-700"}`}>{t("sex.female")}</button>
64+
</div>
65+
66+
<div className="grid gap-4 sm:grid-cols-2">
67+
<label className="block">
68+
<span className="text-sm font-medium">{t("input.height")} (cm)</span>
69+
<input type="number" value={height} onChange={(e) => setHeight(e.target.value)} className="mt-1 w-full rounded border border-slate-300 px-3 py-2 tabular-nums dark:border-slate-700 dark:bg-slate-900" />
70+
</label>
71+
<label className="block">
72+
<span className="text-sm font-medium">{t("input.weight")} (kg)</span>
73+
<input type="number" value={weight} onChange={(e) => setWeight(e.target.value)} className="mt-1 w-full rounded border border-slate-300 px-3 py-2 tabular-nums dark:border-slate-700 dark:bg-slate-900" />
74+
</label>
75+
<label className="block">
76+
<span className="text-sm font-medium">{t("input.waist")} (cm)</span>
77+
<input type="number" value={waist} onChange={(e) => setWaist(e.target.value)} className="mt-1 w-full rounded border border-slate-300 px-3 py-2 tabular-nums dark:border-slate-700 dark:bg-slate-900" />
78+
</label>
79+
<label className="block">
80+
<span className="text-sm font-medium">{t("input.neck")} (cm)</span>
81+
<input type="number" value={neck} onChange={(e) => setNeck(e.target.value)} className="mt-1 w-full rounded border border-slate-300 px-3 py-2 tabular-nums dark:border-slate-700 dark:bg-slate-900" />
82+
</label>
83+
{sex === "female" && (
84+
<label className="block">
85+
<span className="text-sm font-medium">{t("input.hip")} (cm)</span>
86+
<input type="number" value={hip} onChange={(e) => setHip(e.target.value)} className="mt-1 w-full rounded border border-slate-300 px-3 py-2 tabular-nums dark:border-slate-700 dark:bg-slate-900" />
87+
</label>
88+
)}
89+
</div>
90+
91+
<div aria-live="polite" className="mt-6 rounded-lg border border-slate-200 p-4 dark:border-slate-800">
92+
{result ? (
93+
<div>
94+
<div className="flex items-baseline gap-3">
95+
<div className="text-4xl font-bold tabular-nums">{fmt.format(result.bf)}%</div>
96+
<div className="text-sm text-slate-600 dark:text-slate-400">{t(`category.${result.category}`)}</div>
97+
</div>
98+
<dl className="mt-4 grid gap-2 text-sm">
99+
<div className="flex justify-between border-b border-slate-200 py-1 dark:border-slate-800"><dt>{t("result.fatMass")}</dt><dd className="tabular-nums">{fmt.format(result.fatMass)} kg</dd></div>
100+
<div className="flex justify-between border-b border-slate-200 py-1 dark:border-slate-800"><dt>{t("result.leanMass")}</dt><dd className="tabular-nums">{fmt.format(result.leanMass)} kg</dd></div>
101+
</dl>
102+
</div>
103+
) : (
104+
<div className="text-sm text-slate-500">{t("empty")}</div>
105+
)}
106+
</div>
107+
</div>
108+
);
109+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { ToolDefinition } from "@/lib/tools/types";
2+
import Component from "./Component";
3+
4+
const def: ToolDefinition = {
5+
meta: {
6+
slug: "body-fat-calculator",
7+
category: "health",
8+
applicationCategory: "HealthApplication",
9+
updatedAt: "2026-05-07",
10+
related: ["bmi-calculator", "bmr-calculator", "ideal-weight-calculator", "calorie-calculator"],
11+
primaryKeyword: {
12+
en: "body fat calculator",
13+
ja: "体脂肪率 計算",
14+
"zh-CN": "体脂率 计算器",
15+
},
16+
hasHowTo: true,
17+
hasFaq: true,
18+
},
19+
Component,
20+
};
21+
22+
export default def;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"title": "Body Fat Calculator (US Navy Method)",
3+
"shortDescription": "Estimate body fat percentage using the US Navy method (waist, neck, and hip for women). Returns fat mass, lean mass, and fitness category.",
4+
"description": "Enter your height, weight, neck, waist (and hip for women) circumferences. The US Navy formula gives a body fat percentage that's reasonably accurate without expensive scans, plus your fat-free lean mass.",
5+
"metaDescription": "Free body fat calculator using US Navy circumference method. Just need a tape measure. Shows body fat %, fat mass, lean mass, and fitness category.",
6+
"keywords": ["body fat calculator", "US Navy body fat", "body fat percentage", "fat mass calculator", "lean body mass"],
7+
"empty": "Enter your measurements to estimate body fat.",
8+
"sex": { "male": "Male", "female": "Female" },
9+
"input": {
10+
"height": "Height",
11+
"weight": "Weight",
12+
"waist": "Waist",
13+
"neck": "Neck",
14+
"hip": "Hip"
15+
},
16+
"result": {
17+
"fatMass": "Fat mass",
18+
"leanMass": "Lean mass"
19+
},
20+
"category": {
21+
"essential": "Essential fat",
22+
"athlete": "Athlete",
23+
"fit": "Fitness",
24+
"average": "Average",
25+
"obese": "Obese"
26+
},
27+
"article": {
28+
"sections": [
29+
{
30+
"heading": "How the US Navy method works",
31+
"paragraphs": [
32+
"The US Navy circumference method estimates body fat using the relationship between waist, neck (and hip, for women) measurements relative to height. The formulas were developed by Hodgdon and Beckett in 1984 and remain the standard for military body composition assessment because they require only a tape measure and produce results within ±3-4% of more expensive methods like DEXA scans for the average adult.",
33+
"Men: %BF = 86.010 × log₁₀(waist − neck) − 70.041 × log₁₀(height) + 36.76. Women: %BF = 163.205 × log₁₀(waist + hip − neck) − 97.684 × log₁₀(height) − 78.387. Measurements are in centimeters."
34+
]
35+
},
36+
{
37+
"heading": "Body fat ranges and what they mean",
38+
"paragraphs": [
39+
"Essential fat (men 2-5%, women 10-13%): the absolute minimum your body needs for vital functions. Below this is dangerous and impossible to maintain.",
40+
"Athlete (men 6-13%, women 14-20%): visible muscle definition, low fat. Common in competitive endurance athletes and physique competitors.",
41+
"Fitness (men 14-17%, women 21-24%): healthy and athletic-looking. Most regular exercisers fall here. This is typically the maintenance target for active adults.",
42+
"Average (men 18-24%, women 25-31%): the median for the general adult population. Not unhealthy in itself but raises some risk factors at the upper end.",
43+
"Obese (men 25%+, women 32%+): elevated risk of cardiovascular disease, diabetes, and other conditions. Targeting reduction to fitness range improves long-term health outcomes."
44+
]
45+
},
46+
{
47+
"heading": "Accuracy and limitations",
48+
"paragraphs": [
49+
"The US Navy method is good for tracking changes over time in the same person. Less reliable for absolute accuracy: very muscular individuals tend to be overestimated, and individuals with abnormally large or small frames (very narrow shoulders, very wide hips) may also see error.",
50+
"For the most accurate measurement, DEXA (dual-energy X-ray absorptiometry) is the clinical gold standard. BodPod and underwater weighing are also highly accurate. Bioelectrical impedance scales are convenient but vary by 3-8% from day to day depending on hydration. Skin calipers, when used by a trained person, are nearly as accurate as the Navy method.",
51+
"Measurement consistency matters more than absolute accuracy when tracking progress. Take measurements first thing in the morning, after using the bathroom, before eating or drinking, and use the same tape measure tension. Variation of 1-2 cm at the waist can shift the result by 1%."
52+
]
53+
}
54+
],
55+
"howTo": [
56+
{ "name": "Pick your sex", "text": "Female version uses an additional hip measurement." },
57+
{ "name": "Measure height and weight", "text": "Standard measurements." },
58+
{ "name": "Measure waist", "text": "At the navel, with a relaxed abdomen, tape parallel to the floor." },
59+
{ "name": "Measure neck", "text": "Just below the larynx (Adam's apple), tape sloping slightly downward at the front." },
60+
{ "name": "Measure hip (women)", "text": "At the widest point, tape level with the floor." },
61+
{ "name": "Read your body fat percentage", "text": "Plus fat mass, lean mass, and fitness category." }
62+
]
63+
},
64+
"faq": [
65+
{ "q": "How accurate is the US Navy method?", "a": "Within ±3-4% of clinical methods (DEXA) for typical adults. Less accurate for very muscular or atypically-shaped bodies." },
66+
{ "q": "Is body fat better than BMI?", "a": "For health assessment, generally yes — BMI doesn't distinguish muscle from fat. A bodybuilder may have BMI 30 (obese) and body fat 8% (athlete). Body fat is more meaningful." },
67+
{ "q": "What's a healthy body fat percentage?", "a": "Men: 14-24% (fitness-to-average range). Women: 21-31%. Below 5% (men) or 13% (women) is dangerous; above 25% (men) or 32% (women) raises health risks." },
68+
{ "q": "Why do I need to measure my neck?", "a": "Neck circumference correlates with overall lean mass and acts as a 'reference' to calibrate the formula. Skipping it would lose accuracy." },
69+
{ "q": "How often should I measure?", "a": "Weekly is usually enough for tracking weight loss or muscle gain progress. Daily measurements bounce too much from hydration and meal timing to be useful." },
70+
{ "q": "Should I measure before or after exercise?", "a": "Before, ideally first thing in the morning after using the bathroom. Post-exercise measurements are skewed by sweat loss and tissue swelling." },
71+
{ "q": "Why is the female formula different?", "a": "Women carry more body fat in hip and thigh regions on average, so adding hip circumference improves accuracy. The constants differ to reflect typical female body composition." },
72+
{ "q": "Does the data leave my browser?", "a": "No. Calculation is local; nothing is sent to a server." }
73+
]
74+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"title": "体脂肪率計算機(US Navy法)",
3+
"shortDescription": "US Navy法(メジャーで腹囲・首・女性は腰回りを測定)で体脂肪率を推定。脂肪量・除脂肪量・カテゴリを表示。",
4+
"description": "身長・体重・首回り・腹囲(女性は腰回りも)を入力すると、米海軍式の計算式で体脂肪率を推定し、脂肪量・除脂肪量も表示します。",
5+
"metaDescription": "無料の体脂肪率計算機(米海軍法)。メジャーがあれば家で測定可能。体脂肪率・脂肪量・除脂肪量・カテゴリを即座に算出。",
6+
"keywords": ["体脂肪率 計算", "米海軍式 体脂肪", "体脂肪率 測定", "除脂肪体重", "脂肪量 計算"],
7+
"empty": "サイズを入力してください。",
8+
"sex": { "male": "男性", "female": "女性" },
9+
"input": {
10+
"height": "身長",
11+
"weight": "体重",
12+
"waist": "腹囲",
13+
"neck": "首回り",
14+
"hip": "腰回り"
15+
},
16+
"result": {
17+
"fatMass": "脂肪量",
18+
"leanMass": "除脂肪量"
19+
},
20+
"category": {
21+
"essential": "必須脂肪",
22+
"athlete": "アスリート",
23+
"fit": "フィットネス",
24+
"average": "平均",
25+
"obese": "肥満"
26+
},
27+
"article": {
28+
"sections": [
29+
{
30+
"heading": "US Navy法の仕組み",
31+
"paragraphs": [
32+
"米海軍式の周囲長法は、身長に対する腹囲・首回り(女性は腰回りも)の比率から体脂肪率を推定する手法。1984年にHodgdonとBeckettによって開発され、メジャー1本で測定でき、平均的な成人ではDEXA測定との誤差が±3-4%に収まることから、軍隊の身体組成評価の標準となっています。",
33+
"男性: %BF = 86.010 × log₁₀(腹囲 − 首回り) − 70.041 × log₁₀(身長) + 36.76。女性: %BF = 163.205 × log₁₀(腹囲 + 腰回り − 首回り) − 97.684 × log₁₀(身長) − 78.387。すべてセンチメートル。"
34+
]
35+
},
36+
{
37+
"heading": "体脂肪率の意味",
38+
"paragraphs": [
39+
"必須脂肪(男性2-5%、女性10-13%): 生命維持のための絶対最低値。これ以下は危険で長期維持は不可能。",
40+
"アスリート(男性6-13%、女性14-20%): 筋肉が明確に見える低脂肪状態。競技持久系アスリートやフィジーク選手に多い。",
41+
"フィットネス(男性14-17%、女性21-24%): 健康的で運動習慣のある体格。定期的な運動者の多くがこの範囲。",
42+
"平均(男性18-24%、女性25-31%): 一般成人の中央値。それ自体は不健康ではないが、上限近くではリスク要因が増加。",
43+
"肥満(男性25%以上、女性32%以上): 心血管疾患・糖尿病等のリスク上昇。フィットネス範囲への減量が長期的健康に有益。"
44+
]
45+
},
46+
{
47+
"heading": "精度と限界",
48+
"paragraphs": [
49+
"US Navy法は同一人物の経時変化追跡に有用。絶対精度では、筋肉量が非常に多い人には過大評価、肩幅が非常に狭い・骨盤が非常に広い等の体型異常がある人にも誤差が出やすい。",
50+
"最も正確なのはDEXA(二重エネルギーX線吸収測定法)が臨床的なゴールドスタンダード。BodPodや水中体重測定も高精度。家庭用体組成計(生体電気インピーダンス)は便利だが、水分状態によって日々3-8%の変動があります。皮下脂肪厚計(キャリパー法)は熟練者が測ればNavy法とほぼ同等の精度。",
51+
"進捗追跡では絶対精度より測定の一貫性が重要。朝起床後・トイレ後・飲食前に同じメジャー張力で測ること。腹囲が1-2cm違うと結果が1%変わります。"
52+
]
53+
}
54+
],
55+
"howTo": [
56+
{ "name": "性別を選択", "text": "女性版は腰回りも入力。" },
57+
{ "name": "身長と体重を入力", "text": "標準的な計測。" },
58+
{ "name": "腹囲を測定", "text": "おへそ位置で、腹を緩めて、メジャーを床と平行に。" },
59+
{ "name": "首回りを測定", "text": "喉ぼとけのすぐ下、メジャーは前下がりに。" },
60+
{ "name": "腰回り(女性)", "text": "最も広い部分を、メジャーを床と平行に。" },
61+
{ "name": "結果を確認", "text": "体脂肪率・脂肪量・除脂肪量・カテゴリ。" }
62+
]
63+
},
64+
"faq": [
65+
{ "q": "US Navy法はどのくらい正確?", "a": "一般的成人ならDEXAなど臨床法から±3-4%程度。筋肉量が極端に多い人や骨格が標準と大きく異なる人には誤差が出やすい。" },
66+
{ "q": "BMIより体脂肪率の方が良い?", "a": "健康評価には一般的にはい。BMIは筋肉と脂肪を区別しません。ボディビルダーはBMI30(肥満)でも体脂肪率8%(アスリート)の場合あり。体脂肪率の方が意味のある指標。" },
67+
{ "q": "健康的な体脂肪率は?", "a": "男性: 14-24%(フィットネス〜平均範囲)。女性: 21-31%。男性5%・女性13%以下は危険、男性25%・女性32%以上はリスク上昇。" },
68+
{ "q": "なぜ首回りを測る必要が?", "a": "首回りは全身の除脂肪量と相関し、計算式の「基準点」として機能します。省略すると精度が大幅に低下。" },
69+
{ "q": "どれくらいの頻度で測定すべき?", "a": "減量や筋肥大の進捗追跡なら週1回で十分。毎日測定すると水分や食事タイミングの変動が大きすぎて意味のある変化が見えません。" },
70+
{ "q": "運動前と運動後どちらで測る?", "a": "運動前、できれば朝起床後トイレ後の状態。運動後は発汗による水分減少や組織の浮腫で値がぶれます。" },
71+
{ "q": "なぜ女性は計算式が違う?", "a": "女性は平均的に腰部・大腿部に脂肪が多いため、腰回りを加えることで精度が向上します。定数も典型的な女性の体組成に合わせて調整されています。" },
72+
{ "q": "データは送信されますか?", "a": "送信されません。計算はローカル、サーバーには何も送信しません。" }
73+
]
74+
}

0 commit comments

Comments
 (0)