@jjlmoya/utils-sports 1.45.0 → 1.46.0
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 +1 -1
- package/src/tests/seo_parity.test.ts +60 -0
- package/src/tests/translation_copy.test.ts +124 -0
- package/src/tool/runningPacePredictor/i18n/de.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/fr.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/id.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/it.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/ja.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/ko.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/nl.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/pl.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/pt.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/ru.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/sv.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/tr.ts +9 -0
- package/src/tool/runningPacePredictor/i18n/zh.ts +9 -0
- package/src/tool/swimCssCalculator/i18n/de.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/es.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/fr.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/id.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/it.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/ja.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/ko.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/nl.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/pl.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/pt.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/ru.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/sv.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/tr.ts +8 -0
- package/src/tool/swimCssCalculator/i18n/zh.ts +8 -0
package/package.json
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { ALL_ENTRIES } from '../entries';
|
|
3
|
+
import type { KnownLocale } from '../types';
|
|
4
|
+
|
|
5
|
+
interface ExpectedCounts {
|
|
6
|
+
seo: number;
|
|
7
|
+
faq: number;
|
|
8
|
+
howTo: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function countItems(arr: unknown[] | undefined): number {
|
|
12
|
+
return arr?.length ?? 0;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async function verifyLocaleParity(
|
|
16
|
+
entry: typeof ALL_ENTRIES[number],
|
|
17
|
+
loc: KnownLocale,
|
|
18
|
+
expected: ExpectedCounts,
|
|
19
|
+
): Promise<void> {
|
|
20
|
+
const locContent = await entry.i18n[loc]?.();
|
|
21
|
+
expect(locContent, `Locale ${loc} missing content`).toBeDefined();
|
|
22
|
+
|
|
23
|
+
const locSeoCount = countItems(locContent?.seo);
|
|
24
|
+
const locFaqCount = countItems(locContent?.faq);
|
|
25
|
+
const locHowToCount = countItems(locContent?.howTo);
|
|
26
|
+
|
|
27
|
+
expect(
|
|
28
|
+
locSeoCount,
|
|
29
|
+
`Locale ${loc} SEO sections count (${locSeoCount}) must match EN (${expected.seo})`,
|
|
30
|
+
).toBe(expected.seo);
|
|
31
|
+
expect(
|
|
32
|
+
locFaqCount,
|
|
33
|
+
`Locale ${loc} FAQ items count (${locFaqCount}) must match EN (${expected.faq})`,
|
|
34
|
+
).toBe(expected.faq);
|
|
35
|
+
expect(
|
|
36
|
+
locHowToCount,
|
|
37
|
+
`Locale ${loc} HowTo steps count (${locHowToCount}) must match EN (${expected.howTo})`,
|
|
38
|
+
).toBe(expected.howTo);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
describe('SEO & i18n Structural Parity Suite', () => {
|
|
42
|
+
ALL_ENTRIES.forEach((entry) => {
|
|
43
|
+
describe(`Tool: ${entry.id}`, () => {
|
|
44
|
+
it('all 15 locales should have identical SEO section counts and types as English', async () => {
|
|
45
|
+
const enContent = await entry.i18n.en?.();
|
|
46
|
+
expect(enContent).toBeDefined();
|
|
47
|
+
const expected: ExpectedCounts = {
|
|
48
|
+
seo: countItems(enContent?.seo),
|
|
49
|
+
faq: countItems(enContent?.faq),
|
|
50
|
+
howTo: countItems(enContent?.howTo),
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const locales = Object.keys(entry.i18n) as KnownLocale[];
|
|
54
|
+
for (const loc of locales) {
|
|
55
|
+
await verifyLocaleParity(entry, loc, expected);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { ALL_ENTRIES } from '../entries';
|
|
3
|
+
|
|
4
|
+
const COPY_THRESHOLD = 0.9;
|
|
5
|
+
|
|
6
|
+
const STRUCTURAL_KEYS = new Set([
|
|
7
|
+
'@context',
|
|
8
|
+
'@type',
|
|
9
|
+
'applicationCategory',
|
|
10
|
+
'columns',
|
|
11
|
+
'highlight',
|
|
12
|
+
'icon',
|
|
13
|
+
'level',
|
|
14
|
+
'operatingSystem',
|
|
15
|
+
'position',
|
|
16
|
+
'positive',
|
|
17
|
+
'price',
|
|
18
|
+
'priceCurrency',
|
|
19
|
+
'slug',
|
|
20
|
+
'trend',
|
|
21
|
+
'type',
|
|
22
|
+
'url',
|
|
23
|
+
'value',
|
|
24
|
+
'variant',
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
function normalizeText(value: string): string {
|
|
28
|
+
return value
|
|
29
|
+
.replace(/<[^>]*>/g, ' ')
|
|
30
|
+
.replace(/&(?:amp|lt|gt|quot|apos|nbsp);/gi, ' ')
|
|
31
|
+
.replace(/[\u2018\u2019]/g, "'")
|
|
32
|
+
.replace(/[\u201c\u201d]/g, '"')
|
|
33
|
+
.replace(/\s+/g, ' ')
|
|
34
|
+
.trim()
|
|
35
|
+
.toLocaleLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function collectText(value: unknown, path: string, parts: string[]): void {
|
|
39
|
+
if (typeof value === 'string') {
|
|
40
|
+
const normalized = normalizeText(value);
|
|
41
|
+
if (normalized.length >= 2) parts.push(normalized);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
value.forEach((item, index) => collectText(item, `${path}[${index}]`, parts));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!value || typeof value !== 'object') return;
|
|
51
|
+
|
|
52
|
+
Object.entries(value).forEach(([key, child]) => {
|
|
53
|
+
if (STRUCTURAL_KEYS.has(key)) return;
|
|
54
|
+
collectText(child, `${path}.${key}`, parts);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function localeCorpus(content: unknown): string {
|
|
59
|
+
if (!content || typeof content !== 'object') return '';
|
|
60
|
+
|
|
61
|
+
const record = content as Record<string, unknown>;
|
|
62
|
+
const parts: string[] = [];
|
|
63
|
+
collectText(record.title, 'title', parts);
|
|
64
|
+
collectText(record.description, 'description', parts);
|
|
65
|
+
collectText(record.faqTitle, 'faqTitle', parts);
|
|
66
|
+
collectText(record.faq, 'faq', parts);
|
|
67
|
+
collectText(record.seo, 'seo', parts);
|
|
68
|
+
collectText(record.schemas, 'schemas', parts);
|
|
69
|
+
return parts.join(' ');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function tokenCounts(text: string): Map<string, number> {
|
|
73
|
+
const counts = new Map<string, number>();
|
|
74
|
+
for (const token of text.match(/[\p{L}\p{N}]+/gu) ?? []) {
|
|
75
|
+
counts.set(token, (counts.get(token) ?? 0) + 1);
|
|
76
|
+
}
|
|
77
|
+
return counts;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function copySimilarity(left: string, right: string): number {
|
|
81
|
+
const leftCounts = tokenCounts(left);
|
|
82
|
+
const rightCounts = tokenCounts(right);
|
|
83
|
+
const leftTotal = [...leftCounts.values()].reduce((sum, count) => sum + count, 0);
|
|
84
|
+
const rightTotal = [...rightCounts.values()].reduce((sum, count) => sum + count, 0);
|
|
85
|
+
if (leftTotal === 0 || rightTotal === 0) return 0;
|
|
86
|
+
|
|
87
|
+
let shared = 0;
|
|
88
|
+
for (const [token, count] of leftCounts) {
|
|
89
|
+
shared += Math.min(count, rightCounts.get(token) ?? 0);
|
|
90
|
+
}
|
|
91
|
+
return (2 * shared) / (leftTotal + rightTotal);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
describe('Locales must not copy another locale wholesale', () => {
|
|
95
|
+
ALL_ENTRIES.forEach((entry) => {
|
|
96
|
+
it(`${entry.id} is not at least ${COPY_THRESHOLD * 100}% identical to another locale`, async () => {
|
|
97
|
+
const corpora = new Map<string, string>();
|
|
98
|
+
|
|
99
|
+
for (const [locale, loader] of Object.entries(entry.i18n)) {
|
|
100
|
+
if (!loader) continue;
|
|
101
|
+
corpora.set(locale, localeCorpus(await loader()));
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const locales = [...corpora.keys()];
|
|
105
|
+
const violations: string[] = [];
|
|
106
|
+
|
|
107
|
+
for (let leftIndex = 0; leftIndex < locales.length; leftIndex += 1) {
|
|
108
|
+
for (let rightIndex = leftIndex + 1; rightIndex < locales.length; rightIndex += 1) {
|
|
109
|
+
const left = locales[leftIndex];
|
|
110
|
+
const right = locales[rightIndex];
|
|
111
|
+
const similarity = copySimilarity(corpora.get(left) ?? '', corpora.get(right) ?? '');
|
|
112
|
+
|
|
113
|
+
if (similarity >= COPY_THRESHOLD) {
|
|
114
|
+
violations.push(`${left} ↔ ${right}: ${(similarity * 100).toFixed(1)}%`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
expect(violations, `Locale copy threshold exceeded in ${entry.id}`).toEqual([]);
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Erfolgsfaktor Negative Split',
|
|
166
166
|
html: 'Statistische Analysen von Weltrekorden belegen, dass ein Negative Split (zweite Hälfte leicht schneller als die erste) den Stoffwechsel schont. Ein Anlaufen 2% unter dem Riegel-Zieltempo schützt vor vorzeitiger Übersäuerung.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Wissenschaftliche Trainingszonen strukturieren',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Effektives Ausdauertraining verteilt die Intensität bewusst auf verschiedene physiologische Zonen. Lockere Läufe fördern die mitochondriale Anpassung, Tempoläufe verbessern den Laktatabbau und Intervalle steigern die VO2max. Individuelle Tempobereiche helfen, Übertraining zu vermeiden und den Trainingsreiz gezielt zu setzen.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Lauftempo Rechner und Wettzeit Prognose',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Efficacité du Negative Split',
|
|
166
166
|
html: 'Courir la seconde moitié légèrement plus vite que la première préserve le glycogène et évite une acidose musculaire précoce.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Structurer les zones d\'entraînement scientifique',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Un entraînement d\'endurance efficace répartit volontairement l\'intensité entre plusieurs zones physiologiques. Les sorties faciles favorisent les adaptations mitochondriales, les séances au seuil améliorent l\'élimination du lactate et les intervalles développent le VO2 max. Des allures personnalisées limitent le risque de surcharge tout en ciblant l\'adaptation recherchée.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Calculateur d Allure de Course et Prédiction de Temps',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Manfaat Negative Split',
|
|
166
166
|
html: 'Berlari lebih cepat pada paruh kedua lomba menghemat cadangan glikogen dan mencegah penumpukan asam laktat prematur.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Menyusun Zona Latihan Berbasis Sains',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Latihan daya tahan yang efektif membagi intensitas secara terencana ke dalam beberapa zona fisiologis. Lari ringan mendukung adaptasi mitokondria, lari tempo meningkatkan pembersihan laktat, dan interval intensitas tinggi mengembangkan batas VO2 maks. Rentang pace yang dipersonalisasi membantu mencegah latihan berlebihan sekaligus menjaga stimulus adaptasi.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Kalkulator Tempo Lari dan Prediksi Waktu Lomba',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Efficacia del Negative Split',
|
|
166
166
|
html: 'Correre la seconda metà di gara leggermente più velocemente della prima preserva il glicogeno ed evita l acidosi precoce.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Strutturare le zone di allenamento scientifiche',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Un allenamento di resistenza efficace distribuisce intenzionalmente l\'intensità tra diverse zone fisiologiche. Le corse facili favoriscono gli adattamenti mitocondriali, i ritmi sostenuti migliorano lo smaltimento del lattato e gli intervalli aumentano il limite del VO2 max. Ritmi personalizzati aiutano a evitare il sovrallenamento e a massimizzare lo stimolo adattivo.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Calcolatore Passo Corsa e Previsione Tempi di Gara',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'ネガティブスプリットの有効性',
|
|
166
166
|
html: '後半を前半より速く走るネガティブスプリットは代謝効率を高めます。前半を抑えることで筋疲労とグリコーゲン消費を抑制できます。',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: '科学的なトレーニングゾーンの構成',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: '効果的な持久力トレーニングでは、強度を複数の生理学的ゾーンに意図的に分けます。イージーランはミトコンドリアの適応を促し、テンポ走は乳酸の処理能力を高め、高強度インターバルはVO2 maxの上限を伸ばします。個別に計算したペースを使うことで、過負荷を避けながら適切な刺激を与えられます。',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'ランニングペース計算・タイム予測',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: '네거티브 스플릿의 효과',
|
|
166
166
|
html: '후반부를 전반부보다 살짝 빠르게 달리는 네거티브 스플릿은 대사 효율을 극대화합니다. 초반 오버페이스를 방지하여 근피로와 글리코겐 소모를 줄일 수 있습니다.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: '과학적인 훈련 구간 구성',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: '효과적인 지구력 훈련은 강도를 여러 생리학적 구간으로 계획적으로 나눕니다. 편한 달리기는 미토콘드리아 적응을 돕고, 템포 달리기는 젖산 처리 능력을 높이며, 고강도 인터벌은 VO2 max의 한계를 확장합니다. 개인별 페이스 범위를 사용하면 과훈련을 피하면서 필요한 적응 자극을 유지할 수 있습니다.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: '러닝 페이스 계산기 및 기록 예측',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Het Nut van een Negative Split',
|
|
166
166
|
html: 'De tweede helft van een wedstrijd iets sneller lopen dan de eerste helft spaart de glycogeenvoorraad en voorkomt vroege verzuring.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Wetenschappelijke trainingszones structureren',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Effectieve duurtraining verdeelt de intensiteit bewust over verschillende fysiologische zones. Rustige duurlopen stimuleren mitochondriale aanpassing, tempolopen verbeteren de lactaatverwerking en intervallen verhogen de VO2max. Persoonlijke tempobereiken helpen overbelasting te voorkomen en leveren tegelijk de juiste trainingsprikkel.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Hardloop Tempo Calculator en Racetijd Voorspeller',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Zalety Negative Split',
|
|
166
166
|
html: 'Bieg pokonany szybciej w drugiej połowie chroni zapasy glikogenu i zapobiega przedwczesnemu zakwaszeniu.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Naukowe wyznaczanie stref treningowych',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Skuteczny trening wytrzymałościowy świadomie rozdziela intensywność między różne strefy fizjologiczne. Spokojne biegi wspierają adaptację mitochondriów, tempo poprawia usuwanie mleczanu, a interwały zwiększają pułap VO2 max. Indywidualne zakresy tempa pomagają uniknąć przetrenowania i utrzymać właściwy bodziec treningowy.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Kalkulator Tempa Biegu i Prognoza Czasu Rezultatu',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Eficácia do Negative Split',
|
|
166
166
|
html: 'Correr a segunda metade da prova ligeiramente mais rápido preserva as reservas de glicogénio e evita a acidose precoce.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Estruturar zonas de treino com base científica',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Um treino de resistência eficaz distribui intencionalmente a intensidade por diferentes zonas fisiológicas. As corridas fáceis promovem adaptações mitocondriais, os treinos de ritmo melhoram a remoção de lactato e os intervalos aumentam o limite de VO2 máximo. Ritmos personalizados ajudam a evitar o excesso de treino e a manter o estímulo adequado.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Calculadora de Ritmo de Corrida e Previsão de Provas',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Эффективность Negative Split',
|
|
166
166
|
html: 'Преодоление второй половины дистанции быстрее первой экономит гликоген и предотвращает раннее закисление.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Научная структура тренировочных зон',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Эффективная тренировка выносливости осознанно распределяет интенсивность между несколькими физиологическими зонами. Легкие пробежки поддерживают адаптацию митохондрий, темповые отрезки улучшают выведение лактата, а интервалы повышают предел VO2 max. Индивидуальные диапазоны темпа помогают избежать перегрузки и сохранить нужный тренировочный стимул.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Калькулятор Темпа Бега и Прогноз Времени на Соревнованиях',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Fördelen med Negative Split',
|
|
166
166
|
html: 'Att springa andra halvan något snabbare sparar glykogen och förhindrar tidig mjölksyra.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Strukturera vetenskapliga träningszoner',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Effektiv uthållighetsträning fördelar medvetet intensiteten mellan flera fysiologiska zoner. Lugna pass stödjer mitokondriell anpassning, tempolöpning förbättrar laktatnedbrytningen och intervaller höjer VO2 max-taket. Personliga fartintervall minskar risken för överträning och ger rätt träningsstimulans.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Löpartempo Kalkylator och Racetid Prognos',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: 'Negative Split Avantajı',
|
|
166
166
|
html: 'Yarışın ikinci yarısını ilk yarısından biraz daha hızlı koşmak glikojen depolarını korur ve erken yorulmayı önler.',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: 'Bilimsel Antrenman Bölgelerini Yapılandırma',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: 'Etkili dayanıklılık antrenmanı yoğunluğu farklı fizyolojik bölgelere bilinçli olarak dağıtır. Kolay koşular mitokondriyal uyumu destekler, tempo koşuları laktat temizleme kapasitesini geliştirir ve interval çalışmaları VO2 max sınırını yükseltir. Kişiye özel tempo aralıkları aşırı antrenmanı önlerken doğru uyaranı korur.',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: 'Koşu Temposu Hesaplama ve Yarış Süresi Tahmini',
|
|
@@ -165,6 +165,15 @@ export const content: ToolLocaleContent<RunningPacePredictorUI> = {
|
|
|
165
165
|
title: '负分段配速的科学优势',
|
|
166
166
|
html: '马拉松世界纪录统计表明,后半程用时略快于前半程的负分段配速能最大化代谢效率。比赛前半程比目标配速慢2%左右,可防止乳酸过早堆积并节省肝糖原。',
|
|
167
167
|
},
|
|
168
|
+
{
|
|
169
|
+
type: 'title',
|
|
170
|
+
text: '科学安排训练强度区间',
|
|
171
|
+
level: 2,
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: 'paragraph',
|
|
175
|
+
html: '有效的耐力训练需要将强度有计划地分配到不同的生理区间。轻松跑促进线粒体适应,节奏跑提升乳酸清除能力,高强度间歇则提高 VO2 max 上限。根据近期成绩计算个人配速范围,可以避免过度训练并保持恰当的适应刺激。',
|
|
176
|
+
},
|
|
168
177
|
],
|
|
169
178
|
ui: {
|
|
170
179
|
title: '跑步配速计算器与完赛成绩预测',
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Kann CSS auch in Yards statt Metern berechnet werden?',
|
|
17
17
|
answer: 'Ja. Die mathematische CSS Formel gilt auf Kurzbahnen in Yards genau gleich. Schalten Sie den Einheiten Umschalter einfach auf Yards um.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Warum wird für die CSS-Berechnung ein 400m- und ein 200m-Test verwendet?',
|
|
21
|
+
answer: 'Der 400m-Test zeigt die aerobe Ausdauer, während der 200m-Test die maximale anaerobe Geschwindigkeit erfasst. Die Steigung zwischen beiden Leistungen isoliert die funktionelle aerobe Schwimmgeschwindigkeit.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Wie werden CSS-Zielzeiten im Intervalltraining eingesetzt?',
|
|
25
|
+
answer: 'Schwimmen Sie bei Serien wie zehn Wiederholungen über 100m jede Wiederholung möglichst genau im berechneten CSS-Tempo und machen Sie 15 bis 20 Sekunden Pause. Gleichmäßiges Tempo verhindert frühe Laktatansammlung.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: '¿Se puede calcular el CSS en piscinas de yardas?',
|
|
17
17
|
answer: 'Si. La formula matematica de velocidad critica se aplica exactamente igual en piscinas cortas de yardas. Cambia el selector a yardas para obtener ritmos por cada 100 yardas.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: '¿Por que se combinan las pruebas de 400m y 200m para calcular el CSS?',
|
|
21
|
+
answer: 'La prueba de 400m mide la resistencia aerobica continua y la de 200m refleja la velocidad anaerobica maxima. La pendiente entre ambas pruebas aisla la velocidad aerobica funcional.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '¿Como se aplican los ritmos CSS durante las series en piscina?',
|
|
25
|
+
answer: 'En series de umbral, como diez repeticiones de 100 metros, intenta mantener el ritmo CSS calculado en cada repeticion con descansos breves de 15 a 20 segundos. La regularidad evita acumular lactato demasiado pronto.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Peut on calculer le CSS en yards au lieu des metres?',
|
|
17
17
|
answer: 'Oui. La formule mathematique s applique exactement de la meme maniere dans les bassins en yards.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Pourquoi utiliser un test de 400m et un test de 200m pour calculer le CSS?',
|
|
21
|
+
answer: 'Le test de 400m mesure l endurance aerobie continue, tandis que le test de 200m represente la vitesse anaerobie maximale. La pente entre les deux efforts isole la vitesse aerobie fonctionnelle.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Comment appliquer les allures CSS dans une serie en piscine?',
|
|
25
|
+
answer: 'Pour une serie au seuil, par exemple dix repetitions de 100 metres, visez l allure CSS calculee a chaque repetition avec 15 a 20 secondes de repos. Une allure reguliere limite l accumulation precoce de lactate.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Apakah CSS dapat dihitung dalam satuan yard?',
|
|
17
17
|
answer: 'Ya. Rumus matematika kecepatan kritis berlaku sama persis untuk kolam lintasan pendek yard.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Mengapa tes 400m dan 200m digunakan untuk menghitung CSS?',
|
|
21
|
+
answer: 'Tes 400m mengukur daya tahan aerobik berkelanjutan, sedangkan tes 200m menangkap kecepatan anaerobik maksimum. Kemiringan di antara keduanya mengisolasi kecepatan aerobik fungsional.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Bagaimana pace CSS digunakan dalam interval renang?',
|
|
25
|
+
answer: 'Untuk set ambang seperti sepuluh repetisi 100 meter, pertahankan pace CSS yang dihitung pada setiap repetisi dengan istirahat 15 hingga 20 detik. Pace yang konsisten mencegah penumpukan laktat terlalu dini.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Si puo calcolare il CSS in vasche da yarde?',
|
|
17
17
|
answer: 'Sì. La formula matematica si applica esattamente allo stesso modo in vasche da yarde.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Perche si usano test da 400m e 200m per calcolare il CSS?',
|
|
21
|
+
answer: 'Il test da 400m misura la resistenza aerobica continua, mentre quello da 200m rileva la velocita anaerobica massima. La pendenza tra i due sforzi isola la velocita aerobica funzionale.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Come si applicano i ritmi CSS nelle serie in piscina?',
|
|
25
|
+
answer: 'In una serie di soglia, ad esempio dieci ripetute da 100 metri, mantieni il ritmo CSS calcolato a ogni ripetuta con 15-20 secondi di recupero. Un ritmo costante limita l accumulo precoce di lattato.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'ヤード単位のプールでも計算できますか?',
|
|
17
17
|
answer: 'はい。限界水泳速度の計算式はヤード単位の短水路プールでも同様に適用されます。',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'CSSの計算に400mと200mのテストを使うのはなぜですか?',
|
|
21
|
+
answer: '400mテストは連続した有酸素持久力を、200mテストは最大無酸素速度を測定します。2つの記録の傾きから機能的な有酸素水泳速度を求められます。',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'プールのインターバルでCSSペースをどう使いますか?',
|
|
25
|
+
answer: '100mを10本泳ぐような閾値セットでは、各本を計算されたCSSペースにそろえ、15〜20秒の短い休憩を取ります。一定のペースが早期の乳酸蓄積を抑えます。',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: '야드 단위 풀에서도 계산이 가능한가요?',
|
|
17
17
|
answer: '네. 임계 수영 속도 수식은 야드 단위 쇼트 코스 풀에서도 동일하게 적용됩니다.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'CSS 계산에 400m와 200m 테스트를 함께 사용하는 이유는 무엇인가요?',
|
|
21
|
+
answer: '400m 테스트는 지속적인 유산소 지구력을 측정하고 200m 테스트는 최대 무산소 속도를 보여 줍니다. 두 기록 사이의 기울기를 이용하면 기능적 유산소 수영 속도를 분리할 수 있습니다.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '수영 인터벌에서 CSS 목표 페이스를 어떻게 사용하나요?',
|
|
25
|
+
answer: '100m 10회와 같은 역치 세트에서는 각 반복을 계산된 CSS 페이스에 맞추고 15~20초 휴식합니다. 일정한 페이스는 젖산이 너무 일찍 쌓이는 것을 줄여 줍니다.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Kan CSS ook in yards worden berekend?',
|
|
17
17
|
answer: 'Ja. De wiskundige CSS formule geldt exact hetzelfde voor kortere yard banen.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Waarom worden een 400m- en een 200m-test gebruikt voor CSS?',
|
|
21
|
+
answer: 'De 400m-test meet voortdurende aerobe uithouding, terwijl de 200m-test de maximale anaerobe snelheid vastlegt. De helling tussen beide prestaties geeft de functionele aerobe zwemsnelheid weer.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Hoe gebruik je CSS-doeltempo\'s tijdens zwemintervallen?',
|
|
25
|
+
answer: 'Houd bij een drempelset, zoals tien herhalingen van 100 meter, elke herhaling zo dicht mogelijk bij het berekende CSS-tempo en neem 15 tot 20 seconden rust. Gelijkmatig tempo voorkomt vroege lactaatophoping.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Czy mozna obliczyc CSS dla basenow w jardach?',
|
|
17
17
|
answer: 'Tak. Wzor matematyczny krytycznej predkosci stosuje sie identycznie dla basenow w jardach.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Dlaczego do obliczenia CSS wykorzystuje sie testy 400m i 200m?',
|
|
21
|
+
answer: 'Test 400m mierzy ciagla wytrzymalosc tlenowa, a test 200m pokazuje maksymalna predkosc beztlenowa. Nachylenie pomiedzy tymi wysilkami pozwala wyodrebnic funkcjonalna predkosc tlenowa.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Jak stosowac tempo CSS podczas interwalow plywackich?',
|
|
25
|
+
answer: 'W serii progowej, na przyklad dziesieciu powtorzeniach po 100 metrow, utrzymuj obliczone tempo CSS w kazdym powtorzeniu i odpoczywaj 15-20 sekund. Rowny rytm ogranicza wczesne nagromadzenie mleczanu.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'E possivel calcular o CSS em piscinas de yardas?',
|
|
17
17
|
answer: 'Sim. A formula matematica de velocidade critica aplica-se exatamente da mesma forma em piscinas em yardas.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Por que sao usados testes de 400m e 200m para calcular o CSS?',
|
|
21
|
+
answer: 'O teste de 400m mede a resistencia aerobica continua, enquanto o de 200m mostra a velocidade anaerobica maxima. A inclinacao entre os dois esforcos isola a velocidade aerobica funcional.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Como aplicar os ritmos CSS nas series de piscina?',
|
|
25
|
+
answer: 'Numa serie de limiar, como dez repeticoes de 100 metros, mantenha o ritmo CSS calculado em cada repeticao com 15 a 20 segundos de descanso. Um ritmo constante evita a acumulacao precoce de lactato.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Можно ли рассчитать CSS для бассейнов в ярдах?',
|
|
17
17
|
answer: 'Да. Математическая формула критической скорости применяется аналогично для ярдовых бассейнов.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Почему для расчета CSS используют тесты на 400м и 200м?',
|
|
21
|
+
answer: 'Тест на 400м измеряет непрерывную аэробную выносливость, а тест на 200м показывает максимальную анаэробную скорость. Наклон между этими результатами позволяет выделить функциональную аэробную скорость плавания.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Как применять целевой темп CSS в интервальных сериях?',
|
|
25
|
+
answer: 'В пороговой серии, например из десяти повторов по 100 метров, старайтесь держать рассчитанный темп CSS на каждом повторе и отдыхайте 15-20 секунд. Ровный темп ограничивает раннее накопление лактата.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'Kan CSS beraknas i yards istallet for meter?',
|
|
17
17
|
answer: 'Ja. Den matematiska CSS formeln galler pa exakt samma satt for yard bassanger.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'Varfor anvands 400m- och 200m-test for att berakna CSS?',
|
|
21
|
+
answer: '400m-testet mater kontinuerlig aerob uthallighet, medan 200m-testet visar maximal anaerob hastighet. Lutningen mellan resultaten isolerar den funktionella aeroba simhastigheten.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Hur anvands CSS-maltempo under intervaller i bassangen?',
|
|
25
|
+
answer: 'I ett troskelpass, till exempel tio repetitioner pa 100 meter, ska varje repetition ligga nara det beraknade CSS-tempot med 15 till 20 sekunders vila. Jamn fart minskar tidig laktatansamling.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: 'CSS yard cinsinden hesaplanabilir mi?',
|
|
17
17
|
answer: 'Evet. Kritik yuzme hizi matematiksel formulu yardlik kulvarlarda da birebir ayni sekilde gecerlidir.',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: 'CSS hesaplamasinda neden 400m ve 200m testleri kullanilir?',
|
|
21
|
+
answer: '400m testi surekli aerobik dayanıkliligi, 200m testi ise maksimum anaerobik hizi olcer. Iki performans arasindaki egim, islevsel aerobik yuzme hizini ayristirir.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: 'Havuz araliklarinda CSS hedef temposu nasil kullanilir?',
|
|
25
|
+
answer: 'On tekrar 100 metre gibi esik setlerinde her tekrari hesaplanan CSS temposunda yuzun ve 15-20 saniye dinlenin. Sabit tempo, laktatin erken birikmesini azaltir.',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|
|
@@ -16,6 +16,14 @@ const faqData = [
|
|
|
16
16
|
question: '能在以码为单位的泳池中使用 CSS 计算吗?',
|
|
17
17
|
answer: '可以。临界游泳速度公式同样适用于短池码制泳池。',
|
|
18
18
|
},
|
|
19
|
+
{
|
|
20
|
+
question: '为什么要用400米和200米测试来计算 CSS?',
|
|
21
|
+
answer: '400米测试反映持续有氧耐力,200米测试反映最大无氧速度。通过两次成绩之间的斜率,可以分离出功能性有氧游泳速度。',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
question: '在泳池间歇训练中如何使用 CSS 目标配速?',
|
|
25
|
+
answer: '在十组100米这样的阈值训练中,每组都尽量保持计算出的 CSS 配速,并安排15至20秒短暂休息。稳定配速有助于避免乳酸过早积累。',
|
|
26
|
+
},
|
|
19
27
|
];
|
|
20
28
|
|
|
21
29
|
const howToData = [
|