@jjlmoya/utils-language 1.1.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.
Files changed (109) hide show
  1. package/.github/workflows/npm-publish.yml +40 -0
  2. package/.gitignore +6 -0
  3. package/.stylelintrc.json +98 -0
  4. package/astro.config.mjs +19 -0
  5. package/eslint.config.js +201 -0
  6. package/package.json +79 -0
  7. package/prompts/create_tool.md +98 -0
  8. package/prompts/i18n/de.md +16 -0
  9. package/prompts/i18n/en.md +16 -0
  10. package/prompts/i18n/es.md +16 -0
  11. package/prompts/i18n/fr.md +16 -0
  12. package/prompts/i18n/id.md +16 -0
  13. package/prompts/i18n/it.md +16 -0
  14. package/prompts/i18n/ja.md +16 -0
  15. package/prompts/i18n/ko.md +16 -0
  16. package/prompts/i18n/nl.md +16 -0
  17. package/prompts/i18n/pl.md +16 -0
  18. package/prompts/i18n/pt.md +16 -0
  19. package/prompts/i18n/ru.md +16 -0
  20. package/prompts/i18n/sv.md +16 -0
  21. package/prompts/i18n/tr.md +16 -0
  22. package/prompts/i18n/zh.md +16 -0
  23. package/prompts/seo.md +58 -0
  24. package/prompts/translations/french.md +33 -0
  25. package/scripts/postinstall.mjs +27 -0
  26. package/src/category/LanguageCategorySEO.astro +9 -0
  27. package/src/category/i18n/de.ts +15 -0
  28. package/src/category/i18n/en.ts +23 -0
  29. package/src/category/i18n/es.ts +15 -0
  30. package/src/category/i18n/fr.ts +15 -0
  31. package/src/category/i18n/id.ts +15 -0
  32. package/src/category/i18n/it.ts +15 -0
  33. package/src/category/i18n/ja.ts +15 -0
  34. package/src/category/i18n/ko.ts +15 -0
  35. package/src/category/i18n/nl.ts +15 -0
  36. package/src/category/i18n/pl.ts +15 -0
  37. package/src/category/i18n/pt.ts +15 -0
  38. package/src/category/i18n/ru.ts +15 -0
  39. package/src/category/i18n/shared.ts +11 -0
  40. package/src/category/i18n/sv.ts +15 -0
  41. package/src/category/i18n/tr.ts +15 -0
  42. package/src/category/i18n/zh.ts +15 -0
  43. package/src/category/index.ts +24 -0
  44. package/src/components/PreviewNavSidebar.astro +116 -0
  45. package/src/components/PreviewToolbar.astro +143 -0
  46. package/src/data.ts +10 -0
  47. package/src/entries.ts +3 -0
  48. package/src/env.d.ts +5 -0
  49. package/src/index.ts +23 -0
  50. package/src/layouts/PreviewLayout.astro +118 -0
  51. package/src/pages/[locale]/[slug].astro +163 -0
  52. package/src/pages/[locale].astro +251 -0
  53. package/src/pages/index.astro +3 -0
  54. package/src/tests/bibliography_wellformed_export.test.ts +46 -0
  55. package/src/tests/diacritics_density.test.ts +118 -0
  56. package/src/tests/faq_count.test.ts +18 -0
  57. package/src/tests/i18n_coverage.test.ts +34 -0
  58. package/src/tests/inverted_punctuation.test.ts +84 -0
  59. package/src/tests/locale_completeness.test.ts +23 -0
  60. package/src/tests/mocks/astro_mock.js +2 -0
  61. package/src/tests/no_em_dash.test.ts +47 -0
  62. package/src/tests/no_en_dash.test.ts +70 -0
  63. package/src/tests/no_h1_in_components.test.ts +48 -0
  64. package/src/tests/pagespeed_best_practices.test.ts +198 -0
  65. package/src/tests/schemas_fulfillment.test.ts +23 -0
  66. package/src/tests/script_density.test.ts +94 -0
  67. package/src/tests/seo_length.test.ts +23 -0
  68. package/src/tests/seo_parity.test.ts +60 -0
  69. package/src/tests/seo_translation_completeness.test.ts +69 -0
  70. package/src/tests/seo_wellformed_export.test.ts +65 -0
  71. package/src/tests/shared-test-helpers.ts +56 -0
  72. package/src/tests/slug_language_code_format.test.ts +23 -0
  73. package/src/tests/slug_uniqueness.test.ts +81 -0
  74. package/src/tests/spanish_leakage.test.ts +175 -0
  75. package/src/tests/title_quality.test.ts +55 -0
  76. package/src/tests/tool_exports.test.ts +34 -0
  77. package/src/tests/tool_validation.test.ts +16 -0
  78. package/src/tests/translation_copy.test.ts +122 -0
  79. package/src/tool/language-learning-study-plan-planner/bibliography.astro +6 -0
  80. package/src/tool/language-learning-study-plan-planner/bibliography.ts +6 -0
  81. package/src/tool/language-learning-study-plan-planner/component.astro +274 -0
  82. package/src/tool/language-learning-study-plan-planner/entry.ts +34 -0
  83. package/src/tool/language-learning-study-plan-planner/i18n/de.ts +22 -0
  84. package/src/tool/language-learning-study-plan-planner/i18n/en.ts +103 -0
  85. package/src/tool/language-learning-study-plan-planner/i18n/es.ts +21 -0
  86. package/src/tool/language-learning-study-plan-planner/i18n/fr.ts +21 -0
  87. package/src/tool/language-learning-study-plan-planner/i18n/id.ts +21 -0
  88. package/src/tool/language-learning-study-plan-planner/i18n/it.ts +21 -0
  89. package/src/tool/language-learning-study-plan-planner/i18n/ja.ts +21 -0
  90. package/src/tool/language-learning-study-plan-planner/i18n/ko.ts +21 -0
  91. package/src/tool/language-learning-study-plan-planner/i18n/nl.ts +21 -0
  92. package/src/tool/language-learning-study-plan-planner/i18n/pl.ts +21 -0
  93. package/src/tool/language-learning-study-plan-planner/i18n/pt.ts +21 -0
  94. package/src/tool/language-learning-study-plan-planner/i18n/ru.ts +21 -0
  95. package/src/tool/language-learning-study-plan-planner/i18n/shared.ts +70 -0
  96. package/src/tool/language-learning-study-plan-planner/i18n/sv.ts +21 -0
  97. package/src/tool/language-learning-study-plan-planner/i18n/tr.ts +21 -0
  98. package/src/tool/language-learning-study-plan-planner/i18n/zh.ts +21 -0
  99. package/src/tool/language-learning-study-plan-planner/index.ts +4 -0
  100. package/src/tool/language-learning-study-plan-planner/language-learning-study-plan-planner.css +403 -0
  101. package/src/tool/language-learning-study-plan-planner/logic.test.ts +59 -0
  102. package/src/tool/language-learning-study-plan-planner/logic.ts +157 -0
  103. package/src/tool/language-learning-study-plan-planner/seo.astro +9 -0
  104. package/src/tool/language-learning-study-plan-planner/types.ts +42 -0
  105. package/src/tool/language-learning-study-plan-planner/ui.ts +46 -0
  106. package/src/tools.ts +6 -0
  107. package/src/types.ts +68 -0
  108. package/tsconfig.json +15 -0
  109. package/vitest.config.ts +20 -0
@@ -0,0 +1,274 @@
1
+ ---
2
+ import './language-learning-study-plan-planner.css';
3
+ import { LEVEL_LABELS, STUDY_LEVELS } from './logic';
4
+ import type { LanguagePlannerUI } from './ui';
5
+
6
+ interface Props {
7
+ ui: LanguagePlannerUI;
8
+ }
9
+
10
+ const { ui } = Astro.props as Props;
11
+ const languages = ui.languageOptions.split('|');
12
+ ---
13
+
14
+ <div class="language-planner" data-study-planner data-ui={JSON.stringify(ui)}>
15
+ <div class="planner-editor">
16
+ <div class="form-intro">
17
+ <div>
18
+ <p class="eyebrow">{ui.plannerKicker}</p>
19
+ <h2>{ui.plannerHeading}</h2>
20
+ </div>
21
+ <p class="form-note">{ui.plannerNote}</p>
22
+ </div>
23
+
24
+ <form class="planner-form" data-planner-form>
25
+ <div class="form-grid">
26
+ <label>
27
+ <span>{ui.sourceLanguage}</span>
28
+ <select name="sourceLanguage" required>
29
+ {languages.map((language) => <option value={language}>{language}</option>)}
30
+ </select>
31
+ </label>
32
+ <label>
33
+ <span>{ui.targetLanguage}</span>
34
+ <select name="targetLanguage" required>
35
+ {languages.map((language, index) => <option value={language} selected={index === 1}>{language}</option>)}
36
+ </select>
37
+ </label>
38
+ <label>
39
+ <span>{ui.currentLevel}</span>
40
+ <select name="currentLevel" required>
41
+ {STUDY_LEVELS.map((level, index) => <option value={level} selected={index === 0}>{LEVEL_LABELS[level]} ({level})</option>)}
42
+ </select>
43
+ </label>
44
+ <label>
45
+ <span>{ui.targetLevel}</span>
46
+ <select name="targetLevel" required>
47
+ {STUDY_LEVELS.slice(1).map((level, index) => <option value={level} selected={index === 0}>{LEVEL_LABELS[level]} ({level})</option>)}
48
+ </select>
49
+ </label>
50
+ <label>
51
+ <span>{ui.targetDate}</span>
52
+ <input type="date" name="targetDate" required />
53
+ </label>
54
+ <label>
55
+ <span>{ui.weeklyHours}</span>
56
+ <input type="number" name="weeklyHours" min="0.5" max="40" step="0.5" value="5" required />
57
+ </label>
58
+ <label class="wide-field">
59
+ <span>{ui.priority}</span>
60
+ <select name="priority" required>
61
+ <option value="balanced">{ui.balanced}</option>
62
+ <option value="comprehension">{ui.comprehension}</option>
63
+ <option value="production">{ui.production}</option>
64
+ </select>
65
+ </label>
66
+ </div>
67
+
68
+ <div class="preset-row" aria-label={ui.presetsLabel}>
69
+ <span>{ui.presetsLabel}</span>
70
+ <button type="button" data-preset="casual">{ui.casualPreset}</button>
71
+ <button type="button" data-preset="steady">{ui.steadyPreset}</button>
72
+ <button type="button" data-preset="sprint">{ui.sprintPreset}</button>
73
+ </div>
74
+
75
+ <div class="form-actions">
76
+ <button class="primary-button" type="submit">{ui.planButton}</button>
77
+ <button class="text-button" type="reset">{ui.resetButton}</button>
78
+ </div>
79
+ </form>
80
+ </div>
81
+
82
+ <section class="planner-output">
83
+ <div class="output-heading">
84
+ <div>
85
+ <p class="eyebrow">{ui.resultTitle}</p>
86
+ <h2>{ui.resultTitle}</h2>
87
+ </div>
88
+ <span class="live-badge">{ui.liveLabel}</span>
89
+ </div>
90
+ <div class="results-panel" data-results aria-live="polite" aria-atomic="true">
91
+ <div class="empty-result">
92
+ <span class="route-mark" aria-hidden="true">→</span>
93
+ <p>{ui.emptyResultDescription}</p>
94
+ </div>
95
+ </div>
96
+ </section>
97
+ </div>
98
+
99
+ <script>
100
+ import { calculateStudyPlan, LEVEL_LABELS } from './logic';
101
+ import type { LanguagePlannerInputs, PlanStatus, StudyPlan, StudyLevel } from './types';
102
+ import type { LanguagePlannerUI } from './ui';
103
+
104
+ const root = document.querySelector<HTMLElement>('[data-study-planner]');
105
+ const ui = JSON.parse(root?.dataset.ui ?? '{}') as LanguagePlannerUI;
106
+ const form = root?.querySelector<HTMLFormElement>('[data-planner-form]');
107
+ const results = root?.querySelector<HTMLElement>('[data-results]');
108
+ const targetDateInput = form?.querySelector<HTMLInputElement>('[name="targetDate"]');
109
+ const storageKey = 'language-learning-study-plan';
110
+
111
+ if (root && form && results && targetDateInput) {
112
+ const input = (name: string) => form.elements.namedItem(name) as HTMLInputElement | HTMLSelectElement;
113
+ const today = new Date();
114
+ const defaultDate = new Date(today);
115
+ defaultDate.setDate(defaultDate.getDate() + 180);
116
+ targetDateInput.value = defaultDate.toISOString().slice(0, 10);
117
+ targetDateInput.min = today.toISOString().slice(0, 10);
118
+
119
+ try {
120
+ const saved = JSON.parse(localStorage.getItem(storageKey) || 'null');
121
+ if (saved) {
122
+ Object.entries(saved).forEach(([name, value]) => {
123
+ const field = input(name);
124
+ if (field && typeof value === 'string') field.value = value;
125
+ });
126
+ }
127
+ } catch {
128
+ localStorage.removeItem(storageKey);
129
+ }
130
+
131
+ root.querySelectorAll<HTMLButtonElement>('[data-preset]').forEach((button) => {
132
+ button.addEventListener('click', () => {
133
+ const preset = button.dataset.preset;
134
+ let hours = '5';
135
+ if (preset === 'casual') hours = '3.5';
136
+ if (preset === 'sprint') hours = '10';
137
+ input('weeklyHours').value = hours;
138
+ input('priority').value = preset === 'sprint' ? 'production' : 'balanced';
139
+ form.requestSubmit();
140
+ });
141
+ });
142
+
143
+ form.addEventListener('reset', () => {
144
+ window.setTimeout(() => {
145
+ input('weeklyHours').value = '5';
146
+ input('priority').value = 'balanced';
147
+ targetDateInput.value = defaultDate.toISOString().slice(0, 10);
148
+ results.replaceChildren(createEmptyResult());
149
+ }, 0);
150
+ });
151
+
152
+ form.addEventListener('submit', (event) => {
153
+ event.preventDefault();
154
+ const formData = new FormData(form);
155
+ const values = Object.fromEntries(formData.entries());
156
+ const plannerInputs: LanguagePlannerInputs = {
157
+ sourceLanguage: String(values.sourceLanguage),
158
+ targetLanguage: String(values.targetLanguage),
159
+ currentLevel: String(values.currentLevel) as StudyLevel,
160
+ targetLevel: String(values.targetLevel) as StudyLevel,
161
+ targetDate: String(values.targetDate),
162
+ weeklyHours: Number(values.weeklyHours),
163
+ priority: String(values.priority) as LanguagePlannerInputs['priority'],
164
+ };
165
+ const result = calculateStudyPlan(plannerInputs);
166
+ if (!result.ok) {
167
+ results.replaceChildren(createErrorResult(result.error));
168
+ return;
169
+ }
170
+ localStorage.setItem(storageKey, JSON.stringify(values));
171
+ results.replaceChildren(createPlanResult(result.plan));
172
+ });
173
+ }
174
+
175
+ function createEmptyResult() {
176
+ const wrapper = document.createElement('div');
177
+ wrapper.className = 'empty-result';
178
+ wrapper.innerHTML = '<span class="route-mark" aria-hidden="true">→</span><p></p>';
179
+ const description = wrapper.querySelector('p');
180
+ if (description) description.textContent = ui.emptyResultDescription;
181
+ return wrapper;
182
+ }
183
+
184
+ function createErrorResult(message: string) {
185
+ const wrapper = document.createElement('div');
186
+ wrapper.className = 'error-result';
187
+ const title = document.createElement('h2');
188
+ title.textContent = ui.errorPrefix;
189
+ const detail = document.createElement('p');
190
+ detail.textContent = message;
191
+ wrapper.append(title, detail);
192
+ return wrapper;
193
+ }
194
+
195
+ function createPlanResult(plan: StudyPlan) {
196
+ const wrapper = document.createElement('div');
197
+ wrapper.className = 'plan-result';
198
+ const status = document.createElement('div');
199
+ status.className = `status-banner ${plan.status}`;
200
+ status.append(createText('status-label', ui.statusLabel), createText('status-copy', statusMessage(plan.status)));
201
+ wrapper.append(status);
202
+
203
+ const heading = document.createElement('div');
204
+ heading.className = 'result-heading';
205
+ heading.append(createText('eyebrow', `${plan.inputs.sourceLanguage} → ${plan.inputs.targetLanguage}`), createText('result-level', `${LEVEL_LABELS[plan.inputs.currentLevel]} to ${LEVEL_LABELS[plan.inputs.targetLevel]}`));
206
+ wrapper.append(heading);
207
+
208
+ const cards = document.createElement('div');
209
+ cards.className = 'metric-grid';
210
+ cards.append(
211
+ metric(ui.hoursRange, `${plan.hoursLow}-${plan.hoursHigh} h`, ui.guidedLearningDetail),
212
+ metric(ui.weeksAvailable, String(plan.weeksRemaining), ui.targetDateDetail),
213
+ metric(ui.hoursAvailable, `${plan.availableHours} h`, ui.weeklyPaceDetail),
214
+ metric(ui.recommendedTime, `${plan.recommendedWeeklyHoursLow}-${plan.recommendedWeeklyHoursHigh} h`, ui.recommendedRangeDetail),
215
+ );
216
+ wrapper.append(cards);
217
+
218
+ wrapper.append(createAllocation(plan), createMilestones(plan));
219
+ return wrapper;
220
+ }
221
+
222
+ function createAllocation(plan: StudyPlan) {
223
+ const section = document.createElement('section');
224
+ section.className = 'allocation-section';
225
+ section.append(createText('section-title', ui.weeklyAllocation));
226
+ const grid = document.createElement('div');
227
+ grid.className = 'allocation-grid';
228
+ [['listening', ui.skillListening], ['speaking', ui.skillSpeaking], ['reading', ui.skillReading], ['writing', ui.skillWriting]].forEach(([skill, label]) => {
229
+ const item = document.createElement('div');
230
+ item.className = 'allocation-item';
231
+ const allocation = plan.allocation[skill as keyof typeof plan.allocation];
232
+ const minutes = Math.round((plan.inputs.weeklyHours * 60 * allocation) / 100);
233
+ item.append(createText('allocation-label', `${label} ${allocation}%`), createText('allocation-time', `${minutes} min`));
234
+ grid.append(item);
235
+ });
236
+ section.append(grid);
237
+ return section;
238
+ }
239
+
240
+ function createMilestones(plan: StudyPlan) {
241
+ const section = document.createElement('section');
242
+ section.className = 'milestones-section';
243
+ section.append(createText('section-title', ui.milestones));
244
+ const list = document.createElement('ol');
245
+ list.className = 'milestone-list';
246
+ plan.milestones.forEach((milestone) => {
247
+ const item = document.createElement('li');
248
+ item.append(createText('milestone-level', `${ui.milestoneTarget} ${LEVEL_LABELS[milestone.level]}`), createText('milestone-date', `${milestone.date} · ${ui.milestoneWeek} ${milestone.week}`), createText('milestone-hours', `${milestone.hoursLow}-${milestone.hoursHigh} h`));
249
+ list.append(item);
250
+ });
251
+ section.append(list);
252
+ return section;
253
+ }
254
+
255
+ function metric(label: string, value: string, detail: string) {
256
+ const card = document.createElement('div');
257
+ card.className = 'metric-card';
258
+ card.append(createText('metric-label', label), createText('metric-value', value), createText('metric-detail', detail));
259
+ return card;
260
+ }
261
+
262
+ function createText(className: string, text: string) {
263
+ const element = document.createElement('span');
264
+ element.className = className;
265
+ element.textContent = text;
266
+ return element;
267
+ }
268
+
269
+ function statusMessage(status: PlanStatus) {
270
+ if (status === 'on-track') return ui.onTrack;
271
+ if (status === 'tight') return ui.tight;
272
+ return ui.insufficient;
273
+ }
274
+ </script>
@@ -0,0 +1,34 @@
1
+ import type { LanguageToolEntry, ToolDefinition, ToolLocaleContent } from '../../types';
2
+ import type { LanguagePlannerUI } from './ui';
3
+
4
+ export type { LanguagePlannerUI } from './ui';
5
+ export type LanguageLearningStudyPlanPlannerLocaleContent = ToolLocaleContent<LanguagePlannerUI>;
6
+
7
+ export const languageLearningStudyPlanPlanner: LanguageToolEntry<LanguagePlannerUI> = {
8
+ id: 'language-learning-study-plan-planner',
9
+ icons: { bg: 'mdi:map-clock-outline', fg: 'mdi:translate-variant' },
10
+ i18n: {
11
+ de: () => import('./i18n/de').then((module) => module.content),
12
+ en: () => import('./i18n/en').then((module) => module.content),
13
+ es: () => import('./i18n/es').then((module) => module.content),
14
+ fr: () => import('./i18n/fr').then((module) => module.content),
15
+ id: () => import('./i18n/id').then((module) => module.content),
16
+ it: () => import('./i18n/it').then((module) => module.content),
17
+ ja: () => import('./i18n/ja').then((module) => module.content),
18
+ ko: () => import('./i18n/ko').then((module) => module.content),
19
+ nl: () => import('./i18n/nl').then((module) => module.content),
20
+ pl: () => import('./i18n/pl').then((module) => module.content),
21
+ pt: () => import('./i18n/pt').then((module) => module.content),
22
+ ru: () => import('./i18n/ru').then((module) => module.content),
23
+ sv: () => import('./i18n/sv').then((module) => module.content),
24
+ tr: () => import('./i18n/tr').then((module) => module.content),
25
+ zh: () => import('./i18n/zh').then((module) => module.content),
26
+ },
27
+ };
28
+
29
+ export const LANGUAGE_LEARNING_STUDY_PLAN_PLANNER_TOOL: ToolDefinition = {
30
+ entry: languageLearningStudyPlanPlanner,
31
+ Component: () => import('./component.astro'),
32
+ SEOComponent: () => import('./seo.astro'),
33
+ BibliographyComponent: () => import('./bibliography.astro'),
34
+ };
@@ -0,0 +1,22 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Lernplaner für Sprachen', plannerHeading: 'Mache dein nächstes Sprachziel sichtbar.',
6
+ sourceLanguage: 'Ausgangssprache', targetLanguage: 'Zu lernende Sprache', currentLevel: 'Aktuelles GER Niveau', targetLevel: 'Zielniveau nach GER', targetDate: 'Zieldatum', weeklyHours: 'Lernzeit pro Woche', priority: 'Schwerpunkt', balanced: 'Ausgewogen', comprehension: 'Verstehen', production: 'Sprechen und Schreiben', planButton: 'Meinen Lernplan erstellen', resetButton: 'Zurücksetzen', presetsLabel: 'Mit einem Tempo starten', casualPreset: '30 Minuten pro Tag', steadyPreset: '5 Stunden pro Woche', sprintPreset: 'Prüfungssprint', plannerNote: 'Dies ist eine Planungsschätzung auf Basis grober Bereiche für angeleitetes Lernen. Sie prüft dein Niveau nicht und garantiert keine Sprachflüssigkeit.', resultTitle: 'Dein Lernplan', liveLabel: 'Live', hoursRange: 'Geschätzter Stundenbedarf', weeksAvailable: 'Wochen bis zu deinem Datum', hoursAvailable: 'Verfügbare Stunden bei diesem Tempo', recommendedTime: 'Empfohlener Wochenbereich', onTrack: 'Dein Zeitplan deckt die obere Schätzung ab.', tight: 'Dein Zeitplan deckt die untere Schätzung ab. Lass Zeit für Wiederholung.', insufficient: 'Deine Frist ist kürzer als die untere Schätzung. Plane mehr Zeit ein oder verschiebe das Datum.', statusLabel: 'Planstatus', weeklyAllocation: 'Wochenaufteilung nach Fertigkeit', milestones: 'Meilensteine', skillListening: 'Hörverstehen', skillSpeaking: 'Sprechen', skillReading: 'Leseverstehen', skillWriting: 'Schreiben', milestoneTarget: 'Erreichen', milestoneWeek: 'etwa in Woche', errorPrefix: 'Prüfe deine Eingaben', languageOptions: 'Englisch|Spanisch|Französisch|Deutsch|Italienisch|Portugiesisch|Japanisch|Koreanisch|Chinesisch|Arabisch', emptyResultDescription: 'Wähle ein Tempo und ein Datum, um deine Route von einem Niveau zum nächsten zu sehen.', guidedLearningDetail: 'Bereich für angeleitetes Lernen', targetDateDetail: 'bis zu deinem Zieldatum', weeklyPaceDetail: 'bei deinem gewählten Wochentempo', recommendedRangeDetail: 'pro Woche für diesen Bereich',
7
+ };
8
+
9
+ const faq = [
10
+ { question: 'Was berechnet der Sprachlernplaner?', answer: 'Er schaetzt den Stundenbereich zwischen deinem aktuellen und deinem angestrebten GER Niveau, vergleicht ihn mit den verfuegbaren Wochen und Stunden bis zu deinem Datum und schlaegt eine Wochenaufteilung nach Fertigkeiten vor.' },
11
+ { question: 'Sind die Lernstunden garantiert?', answer: 'Nein. Es handelt sich um grobe Planungsbereiche aus Schaetzungen fuer angeleitetes Lernen. Sprachpaar, Vorerfahrung, Kontakt mit der Sprache, Lernqualitaet und Gelegenheiten zum Ueben koennen die tatsaechliche Dauer deutlich veraendern.' },
12
+ { question: 'Was ist der Unterschied zwischen Verstehen und Produktion?', answer: 'Verstehen legt mehr Zeit auf Hoeren und Lesen. Produktion legt mehr Zeit auf Sprechen und Schreiben. Ausgewogen verteilt die Zeit gleichmaessig auf alle vier Fertigkeiten.' },
13
+ { question: 'Kann ich den Planer fuer jedes Sprachpaar verwenden?', answer: 'Du kannst ihn fuer jedes Sprachpaar als Ausgangspunkt verwenden. Die Schaetzung beruecksichtigt jedoch weder Sprachabstand noch Schriftsystem. Behandle sie als Planungsrahmen und passe sie anhand deines Fortschritts an.' },
14
+ { question: 'Speichert der Planer meine Sprachauswahl?', answer: 'Der Planer laeuft in deinem Browser und sendet deine Eingaben nicht an einen Server. Die aktuelle Seite benoetigt weder Konto noch Anmeldung.' },
15
+ ];
16
+
17
+ export const content = createPlannerLocale({
18
+ slug: 'sprachlernplan-rechner', title: 'Rechner fuer deinen Sprachlernplan', description: 'Schaetze GER Lernstunden, pruefe eine Frist und verwandle deine woechentliche Zeit in einen praktischen Sprachlernplan.', ui, faq,
19
+ seo: makePlannerSeo({ titles: ['Einen Sprachlernplan passend zu deiner Frist erstellen', 'So wird die Schaetzung berechnet', 'Den Planstatus vor deiner Zusage lesen', 'Die Zahl als Ausgangspunkt verwenden'], paragraphs: ['Verwende diesen Rechner, wenn du ein klares Sprachziel hast, aber wissen musst, ob deine Frist realistisch ist. Waehle dein aktuelles GER Niveau, ein Zielniveau, ein Datum und die Wochenzeit, die du wirklich schuetzen kannst. Du erhaeltst einen Stundenbereich, ein empfohlenes Tempo und Meilensteine fuer die Kontrolle deines Fortschritts.', 'Der Planer nutzt kumulative Stundenbereiche fuer angeleitetes Lernen auf jeder GER Stufe. Er zieht den Bereich deines aktuellen Niveaus vom Bereich des Zielniveaus ab und vergleicht die Stunden mit den verbleibenden Wochen. Der Schritt von A1 zu B1 entspricht in diesem Modell ungefaehr 260 bis 300 Stunden. Bei 5 Stunden pro Woche dauert das etwa 52 bis 60 Wochen, bevor ausgefallene Einheiten oder zusaetzliche Wiederholung eingerechnet werden.', 'Das GER beschreibt Faehigkeiten, aber keinen garantierten Stundenplan. Sprachabstand, Vorkenntnisse, Lernqualitaet und echte Anwendung koennen das Tempo stark veraendern. Vergleiche die Meilensteine mit echten Ergebnissen: Wenn du langsamer vorankommst, verschiebe das Datum oder senke das Ziel. Wenn du schneller bist, nutze die freie Zeit fuer Gespraeche und Wiederholung.'], tableHeaders: ['Status', 'Bedeutung', 'Naechster Schritt'], tableRows: [['Im Plan', 'Deine verfuegbaren Stunden erreichen die obere Schaetzung.', 'Halte die Routine und nutze die Meilensteine als Kontrollpunkte.'], ['Eng', 'Deine Stunden erreichen die untere, aber nicht die obere Schaetzung.', 'Behandle das Datum als anspruchsvoll und schuetze Zeit fuer Wiederholung.'], ['Nicht ausreichend', 'Deine Stunden bleiben unter der unteren Schaetzung.', 'Erhoehe die Wochenzeit, verschiebe das Datum oder waehle ein naeheres Ziel.']] }),
20
+ bibliography: [{ name: 'Europarat: Begleitband zum GER, spanische Ausgabe', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: Stunden fuer angeleitetes Lernen', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
21
+ howToName: 'Einen Sprachlernplan erstellen', howTo: [{ name: 'Sprachen waehlen', text: 'Waehle die Sprache, die du sicher verwendest, und die Sprache, die du lernen moechtest.' }, { name: 'GER Fortschritt festlegen', text: 'Waehle dein aktuelles Niveau und das Niveau, das du erreichen willst. Das Ziel muss ueber dem aktuellen Niveau liegen.' }, { name: 'Eine Wochenroutine schuetzen', text: 'Setze ein kuenftiges Zieldatum und gib die Stunden ein, die du realistisch jede Woche lernen kannst.' }, { name: 'Pruefen und anpassen', text: 'Nutze Status, Aufteilung und Meilensteine als Startplan und passe Datum oder Wochenzeit mit neuen Erkenntnissen an.' }],
22
+ });
@@ -0,0 +1,103 @@
1
+ import type { ToolLocaleContent } from '../../../types';
2
+ import type { LanguagePlannerUI } from '../ui';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Language learning planner',
6
+ plannerHeading: 'Make your next language goal visible.',
7
+ sourceLanguage: 'Starting language',
8
+ targetLanguage: 'Language you want to learn',
9
+ currentLevel: 'Current CEFR level',
10
+ targetLevel: 'Target CEFR level',
11
+ targetDate: 'Target date',
12
+ weeklyHours: 'Study time per week',
13
+ priority: 'Skill priority',
14
+ balanced: 'Balanced',
15
+ comprehension: 'Comprehension',
16
+ production: 'Speaking and writing',
17
+ planButton: 'Build my study plan',
18
+ resetButton: 'Reset',
19
+ presetsLabel: 'Start with a pace',
20
+ casualPreset: '30 min a day',
21
+ steadyPreset: '5 hours a week',
22
+ sprintPreset: 'Exam sprint',
23
+ plannerNote: 'This is a planning estimate based on broad guided-learning ranges. It does not test your level or guarantee fluency.',
24
+ resultTitle: 'Your study plan',
25
+ liveLabel: 'Live',
26
+ hoursRange: 'Estimated hours needed',
27
+ weeksAvailable: 'Weeks until your date',
28
+ hoursAvailable: 'Hours available at this pace',
29
+ recommendedTime: 'Suggested weekly range',
30
+ onTrack: 'Your schedule covers the upper estimate.',
31
+ tight: 'Your schedule covers the lower estimate; keep room for review.',
32
+ insufficient: 'Your deadline is tighter than the lower estimate. Add time or move the date.',
33
+ statusLabel: 'Plan status',
34
+ weeklyAllocation: 'Weekly skill split',
35
+ milestones: 'Milestones',
36
+ skillListening: 'Listening',
37
+ skillSpeaking: 'Speaking',
38
+ skillReading: 'Reading',
39
+ skillWriting: 'Writing',
40
+ milestoneTarget: 'Reach',
41
+ milestoneWeek: 'around week',
42
+ errorPrefix: 'Check your inputs',
43
+ languageOptions: 'English|Spanish|French|German|Italian|Portuguese|Japanese|Korean|Chinese|Arabic',
44
+ emptyResultDescription: 'Choose a pace and date to see your weekly route from one level to the next.',
45
+ guidedLearningDetail: 'guided-learning range',
46
+ targetDateDetail: 'until your target date',
47
+ weeklyPaceDetail: 'at your chosen weekly pace',
48
+ recommendedRangeDetail: 'per week for the range',
49
+ };
50
+
51
+ const faq = [
52
+ { question: 'What does the language learning planner calculate?', answer: 'It estimates a range of hours between your current and target CEFR levels, compares that range with the weeks and hours available before your date, and suggests a weekly skill split.' },
53
+ { question: 'Are the study hours a guarantee?', answer: 'No. They are broad planning ranges based on guided-learning estimates. Your language pair, previous experience, exposure, study quality, and practice opportunities can move the real number substantially.' },
54
+ { question: 'What is the difference between comprehension and production priorities?', answer: 'Comprehension puts more of the weekly plan into listening and reading. Production puts more into speaking and writing. Balanced divides the time evenly across the four skills.' },
55
+ { question: 'Can I use this for any language pair?', answer: 'You can use it as a starting point for any language pair, but the estimate does not model language distance or script complexity. Treat the range as a planning envelope and revise it with evidence from your own progress.' },
56
+ { question: 'Does the planner store my language choices?', answer: 'The planner works in your browser and does not send your inputs to a server. The current page does not require an account or a sign-in.' },
57
+ ];
58
+
59
+ export const content: ToolLocaleContent<LanguagePlannerUI> = {
60
+ slug: 'language-learning-study-plan-planner',
61
+ title: 'Language Study Plan Calculator',
62
+ description: 'Estimate CEFR study hours, test a deadline, and turn your weekly availability into a practical language learning plan.',
63
+ ui,
64
+ seo: [
65
+ { type: 'title', text: 'Build a Language Study Plan That Fits Your Deadline', level: 2 },
66
+ { type: 'paragraph', html: 'Use this calculator when you have a clear language goal but need to know whether your deadline is realistic. Choose a current CEFR level, a target level, a date, and the weekly time you can protect. You get a workload range, a suggested weekly pace, and milestones you can review as you progress.' },
67
+ { type: 'title', text: 'How the Estimate Is Calculated', level: 2 },
68
+ { type: 'paragraph', html: 'The planner uses cumulative guided learning hour bands for each CEFR level. It subtracts the band for your current level from the band for your target level, then compares those hours with the weeks remaining. For example, moving from A1 to B1 represents roughly 260 to 300 guided hours in this model. At 5 hours a week, that workload needs about 52 to 60 weeks, before allowing for missed sessions or extra review.' },
69
+ { type: 'title', text: 'Read the Plan Status Before You Commit', level: 2 },
70
+ { type: 'table', headers: ['Status', 'What it means', 'What to do'], rows: [
71
+ ['On track', 'Your available hours reach the upper estimate.', 'Keep the routine and use milestones as checkpoints.'],
72
+ ['Tight', 'Your available hours reach the lower estimate but not the upper one.', 'Treat the date as a stretch and protect review time.'],
73
+ ['Insufficient', 'Your available hours fall below the lower estimate.', 'Add weekly time, move the date, or choose a nearer target.'],
74
+ ] },
75
+ { type: 'title', text: 'Use the Number as a Baseline', level: 2 },
76
+ { type: 'paragraph', html: 'CEFR describes ability, not a guaranteed timetable. Language distance, previous exposure, study quality, and real world practice can change the pace substantially. Use the milestones to compare the plan with real evidence: if progress is slower, move the date or reduce the target; if it is faster, keep the extra time for conversation and review.' },
77
+ ],
78
+ faq,
79
+ bibliography: [
80
+ { name: 'Council of Europe: CEFR Companion Volume, Spanish edition', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' },
81
+ { name: 'Cambridge English: Guided learning hours', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' },
82
+ ],
83
+ howTo: [
84
+ { name: 'Choose your languages', text: 'Select the language you already use comfortably and the language you want to learn.' },
85
+ { name: 'Set the CEFR progression', text: 'Choose your current level and the level you want to reach. The target must be above the current level.' },
86
+ { name: 'Protect a weekly routine', text: 'Set a future target date and enter the hours you can realistically study every week.' },
87
+ { name: 'Review and adapt', text: 'Use the status, skill split, and milestones as a starting plan, then move the date or revise the weekly time as you learn.' },
88
+ ],
89
+ schemas: [
90
+ { '@context': 'https://schema.org', '@type': 'SoftwareApplication', name: 'Language Study Plan Calculator', applicationCategory: 'EducationalApplication', operatingSystem: 'Any', isAccessibleForFree: true, url: 'https://gamebob.dev/en/language-learning-study-plan-planner' },
91
+ { '@context': 'https://schema.org', '@type': 'HowTo', name: 'Build a language study plan', step: contentSteps(ui) },
92
+ { '@context': 'https://schema.org', '@type': 'FAQPage', mainEntity: faq.map((item) => ({ '@type': 'Question', name: item.question, acceptedAnswer: { '@type': 'Answer', text: item.answer } })) },
93
+ ],
94
+ };
95
+
96
+ function contentSteps(labels: LanguagePlannerUI) {
97
+ return [
98
+ { '@type': 'HowToStep', name: 'Choose your languages', text: labels.sourceLanguage },
99
+ { '@type': 'HowToStep', name: 'Set the CEFR progression', text: labels.currentLevel },
100
+ { '@type': 'HowToStep', name: 'Protect a weekly routine', text: labels.weeklyHours },
101
+ { '@type': 'HowToStep', name: 'Review and adapt', text: labels.planButton },
102
+ ];
103
+ }
@@ -0,0 +1,21 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Planificador de aprendizaje de idiomas', plannerHeading: 'Haz visible tu próximo objetivo lingüístico.', sourceLanguage: 'Idioma de partida', targetLanguage: 'Idioma que quieres aprender', currentLevel: 'Nivel MCER actual', targetLevel: 'Nivel MCER objetivo', targetDate: 'Fecha objetivo', weeklyHours: 'Tiempo de estudio semanal', priority: 'Prioridad de destrezas', balanced: 'Equilibrado', comprehension: 'Comprensión', production: 'Expresión oral y escrita', planButton: 'Crear mi plan de estudio', resetButton: 'Restablecer', presetsLabel: 'Empieza con un ritmo', casualPreset: '30 min al día', steadyPreset: '5 horas a la semana', sprintPreset: 'Sprint de examen', plannerNote: 'Es una estimación de planificación basada en rangos amplios de aprendizaje guiado. No evalúa tu nivel ni garantiza fluidez.', resultTitle: 'Tu plan de estudio', liveLabel: 'En directo', hoursRange: 'Horas estimadas necesarias', weeksAvailable: 'Semanas hasta tu fecha', hoursAvailable: 'Horas disponibles a este ritmo', recommendedTime: 'Rango semanal sugerido', onTrack: 'Tu calendario cubre la estimación superior.', tight: 'Tu calendario cubre la estimación inferior. Reserva margen para repasar.', insufficient: 'Tu fecha es más ajustada que la estimación inferior. Añade tiempo o mueve la fecha.', statusLabel: 'Estado del plan', weeklyAllocation: 'Reparto semanal por destreza', milestones: 'Hitos', skillListening: 'Comprensión auditiva', skillSpeaking: 'Expresión oral', skillReading: 'Comprensión lectora', skillWriting: 'Expresión escrita', milestoneTarget: 'Alcanzar', milestoneWeek: 'hacia la semana', errorPrefix: 'Revisa tus datos', languageOptions: 'inglés|español|francés|alemán|italiano|portugués|japonés|coreano|chino|árabe', emptyResultDescription: 'Elige un ritmo y una fecha para ver tu recorrido de un nivel al siguiente.', guidedLearningDetail: 'rango de aprendizaje guiado', targetDateDetail: 'hasta tu fecha objetivo', weeklyPaceDetail: 'con tu ritmo semanal', recommendedRangeDetail: 'por semana para este recorrido',
6
+ };
7
+
8
+ const faq = [
9
+ { question: '¿Qué calcula el planificador de aprendizaje de idiomas?', answer: 'Estima el rango de horas entre tus niveles MCER actual y objetivo, lo compara con las semanas y horas disponibles hasta tu fecha y propone un reparto semanal entre destrezas.' },
10
+ { question: '¿Las horas de estudio son una garantía?', answer: 'No. Son rangos amplios para planificar, basados en estimaciones de aprendizaje guiado. La combinación de idiomas, tu experiencia, la exposición, la calidad del estudio y las oportunidades de práctica pueden cambiar mucho el resultado real.' },
11
+ { question: '¿Cuál es la diferencia entre comprensión y producción?', answer: 'Comprensión dedica más tiempo a escuchar y leer. Producción dedica más tiempo a hablar y escribir. Equilibrado reparte el tiempo por igual entre las cuatro destrezas.' },
12
+ { question: '¿Puedo usarlo para cualquier combinación de idiomas?', answer: 'Puedes usarlo como punto de partida para cualquier combinación, pero la estimación no modela la distancia entre idiomas ni la complejidad de su escritura. Toma el rango como una referencia y ajústalo con tu progreso.' },
13
+ { question: '¿El planificador guarda mis idiomas?', answer: 'Funciona en tu navegador y no envía tus datos a un servidor. La página no necesita cuenta ni inicio de sesión.' },
14
+ ];
15
+
16
+ export const content = createPlannerLocale({
17
+ slug: 'calculadora-plan-estudio-idiomas', title: 'Calculadora de plan de estudio de idiomas', description: 'Estima las horas de estudio según el MCER, comprueba una fecha límite y convierte tu tiempo semanal en un plan práctico para aprender idiomas.', ui, faq,
18
+ seo: makePlannerSeo({ titles: ['Crea un plan de idiomas que encaje con tu fecha', 'Cómo se calcula la estimación', 'Lee el estado del plan antes de comprometerte', 'Usa la cifra como punto de partida'], paragraphs: ['Usa esta calculadora cuando tengas un objetivo lingüístico claro pero necesites saber si tu fecha es realista. Elige tu nivel MCER actual, el nivel objetivo, una fecha y el tiempo semanal que puedes proteger de verdad. Obtendrás un rango de carga, un ritmo semanal sugerido y varios hitos para revisar tu avance.', 'El planificador usa rangos acumulados de horas de aprendizaje guiado para cada nivel MCER. Resta el rango de tu nivel actual al del nivel objetivo y compara esas horas con las semanas restantes. Por ejemplo, pasar de A1 a B1 representa aproximadamente entre 260 y 300 horas guiadas en este modelo. A 5 horas por semana, la carga requiere unas 52 a 60 semanas, sin contar sesiones perdidas ni repasos adicionales.', 'El MCER describe capacidades, no un calendario garantizado. La distancia entre idiomas, la exposición previa, la calidad del estudio y la práctica real pueden cambiar mucho el ritmo. Compara los hitos con pruebas reales: si avanzas más despacio, mueve la fecha o reduce el objetivo; si avanzas más rápido, reserva el tiempo extra para conversar y repasar.'], tableHeaders: ['Estado', 'Qué significa', 'Qué hacer'], tableRows: [['En camino', 'Tus horas disponibles alcanzan la estimación superior.', 'Mantén la rutina y usa los hitos como puntos de control.'], ['Ajustado', 'Tus horas llegan a la estimación inferior, pero no a la superior.', 'Trata la fecha como exigente y protege tiempo para repasar.'], ['Insuficiente', 'Tus horas disponibles quedan por debajo de la estimación inferior.', 'Añade tiempo semanal, mueve la fecha o elige un objetivo más cercano.']] }),
19
+ bibliography: [{ name: 'Consejo de Europa: Volumen complementario del MCER, edición en español', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: Horas de aprendizaje guiado', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
20
+ howToName: 'Crear un plan de estudio de idiomas', howTo: [{ name: 'Elige tus idiomas', text: 'Selecciona el idioma que ya usas con soltura y el idioma que quieres aprender.' }, { name: 'Define la progresión MCER', text: 'Elige tu nivel actual y el nivel que quieres alcanzar. El objetivo debe estar por encima del nivel actual.' }, { name: 'Protege una rutina semanal', text: 'Indica una fecha objetivo futura y las horas que puedes estudiar de forma realista cada semana.' }, { name: 'Revisa y adapta', text: 'Usa el estado, el reparto de destrezas y los hitos como punto de partida; después ajusta la fecha o el tiempo semanal.' }],
21
+ });
@@ -0,0 +1,21 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Planificateur d apprentissage des langues', plannerHeading: 'Rendez votre prochain objectif linguistique visible.', sourceLanguage: 'Langue de départ', targetLanguage: 'Langue à apprendre', currentLevel: 'Niveau CECR actuel', targetLevel: 'Niveau CECR visé', targetDate: 'Date cible', weeklyHours: 'Temps d étude par semaine', priority: 'Priorité de compétence', balanced: 'Équilibré', comprehension: 'Compréhension', production: 'Expression orale et écrite', planButton: 'Créer mon plan d étude', resetButton: 'Réinitialiser', presetsLabel: 'Commencer avec un rythme', casualPreset: '30 min par jour', steadyPreset: '5 heures par semaine', sprintPreset: 'Sprint examen', plannerNote: 'Il s agit d une estimation de planification fondée sur de larges fourchettes d apprentissage guidé. Elle ne teste pas votre niveau et ne garantit pas la fluidité.', resultTitle: 'Votre plan d étude', liveLabel: 'En direct', hoursRange: 'Heures estimées nécessaires', weeksAvailable: 'Semaines jusqu à votre date', hoursAvailable: 'Heures disponibles à ce rythme', recommendedTime: 'Fourchette hebdomadaire suggérée', onTrack: 'Votre calendrier couvre l estimation haute.', tight: 'Votre calendrier couvre l estimation basse. Gardez du temps pour réviser.', insufficient: 'Votre délai est plus court que l estimation basse. Ajoutez du temps ou repoussez la date.', statusLabel: 'État du plan', weeklyAllocation: 'Répartition hebdomadaire par compétence', milestones: 'Jalons', skillListening: 'Compréhension orale', skillSpeaking: 'Expression orale', skillReading: 'Compréhension écrite', skillWriting: 'Expression écrite', milestoneTarget: 'Atteindre', milestoneWeek: 'vers la semaine', errorPrefix: 'Vérifiez vos données', languageOptions: 'anglais|espagnol|français|allemand|italien|portugais|japonais|coréen|chinois|arabe', emptyResultDescription: 'Choisissez un rythme et une date pour voir votre parcours entre deux niveaux.', guidedLearningDetail: 'fourchette d apprentissage guidé', targetDateDetail: 'jusqu à votre date cible', weeklyPaceDetail: 'à votre rythme hebdomadaire', recommendedRangeDetail: 'par semaine pour ce parcours',
6
+ };
7
+
8
+ const faq = [
9
+ { question: 'Que calcule le planificateur d apprentissage des langues ?', answer: 'Il estime une fourchette d heures entre vos niveaux CECR actuel et visé, la compare aux semaines et aux heures disponibles avant votre date, puis propose une répartition hebdomadaire par compétence.' },
10
+ { question: 'Les heures d étude sont elles garanties ?', answer: 'Non. Ce sont de larges fourchettes de planification issues d estimations d apprentissage guidé. La combinaison linguistique, votre expérience, votre exposition, la qualité du travail et les occasions de pratiquer peuvent modifier fortement la durée réelle.' },
11
+ { question: 'Quelle différence entre compréhension et production ?', answer: 'La compréhension donne davantage de temps à l écoute et à la lecture. La production en donne davantage à l oral et à l écrit. Le mode équilibré partage le temps entre les quatre compétences.' },
12
+ { question: 'Puis je utiliser cet outil pour toutes les langues ?', answer: 'Vous pouvez l utiliser comme point de départ pour toute combinaison de langues, mais l estimation ne modélise ni la distance linguistique ni la complexité de l écriture. Considérez la fourchette comme un cadre et révisez la avec vos progrès.' },
13
+ { question: 'Le planificateur conserve t il mes choix de langues ?', answer: 'Il fonctionne dans votre navigateur et n envoie pas vos données à un serveur. La page actuelle ne demande ni compte ni connexion.' },
14
+ ];
15
+
16
+ export const content = createPlannerLocale({
17
+ slug: 'calculateur-plan-etude-langue', title: 'Calculateur de plan d étude des langues', description: 'Estimez les heures selon le CECR, testez une échéance et transformez votre temps hebdomadaire en plan d apprentissage pratique.', ui, faq,
18
+ seo: makePlannerSeo({ titles: ['Construire un plan de langue adapté à votre échéance', 'Comment l estimation est calculée', 'Lire l état du plan avant de s engager', 'Utiliser le nombre comme base de travail'], paragraphs: ['Utilisez ce calculateur lorsque vous avez un objectif linguistique précis mais devez vérifier si votre échéance est réaliste. Choisissez un niveau CECR actuel, un niveau visé, une date et le temps hebdomadaire que vous pouvez réellement protéger. Vous obtenez une charge estimée, un rythme conseillé et des jalons à revoir pendant votre progression.', 'Le planificateur utilise des fourchettes cumulées d heures d apprentissage guidé pour chaque niveau CECR. Il soustrait la fourchette du niveau actuel de celle du niveau visé, puis compare ces heures aux semaines restantes. Dans ce modèle, le passage de A1 à B1 représente environ 260 à 300 heures guidées. À raison de 5 heures par semaine, cette charge demande environ 52 à 60 semaines, sans compter les séances manquées ni les révisions supplémentaires.', 'Le CECR décrit des capacités, pas un calendrier garanti. La distance entre les langues, l exposition antérieure, la qualité de l étude et la pratique réelle peuvent modifier sensiblement le rythme. Comparez les jalons à des preuves concrètes: si les progrès ralentissent, repoussez la date ou réduisez l objectif; s ils accélèrent, gardez le temps gagné pour la conversation et la révision.'], tableHeaders: ['État', 'Signification', 'Action'], tableRows: [['Dans les temps', 'Vos heures disponibles atteignent l estimation haute.', 'Gardez la routine et utilisez les jalons comme points de contrôle.'], ['Serré', 'Vos heures atteignent l estimation basse mais pas l estimation haute.', 'Considérez la date comme ambitieuse et protégez du temps de révision.'], ['Insuffisant', 'Vos heures disponibles restent sous l estimation basse.', 'Ajoutez du temps chaque semaine, repoussez la date ou choisissez un objectif plus proche.']] }),
19
+ bibliography: [{ name: 'Conseil de l Europe: Volume complémentaire du CECR, édition espagnole', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: Heures d apprentissage guidé', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
20
+ howToName: 'Construire un plan d apprentissage linguistique', howTo: [{ name: 'Choisir vos langues', text: 'Sélectionnez la langue que vous utilisez déjà avec aisance et celle que vous souhaitez apprendre.' }, { name: 'Définir la progression CECR', text: 'Choisissez votre niveau actuel et le niveau à atteindre. La cible doit être supérieure au niveau actuel.' }, { name: 'Protéger une routine hebdomadaire', text: 'Définissez une date cible future et indiquez les heures que vous pouvez étudier de façon réaliste chaque semaine.' }, { name: 'Revoir et adapter', text: 'Utilisez l état, la répartition et les jalons comme plan de départ, puis ajustez la date ou le temps hebdomadaire.' }],
21
+ });
@@ -0,0 +1,21 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Perencana belajar bahasa', plannerHeading: 'Buat target bahasa berikutnya terlihat.', sourceLanguage: 'Bahasa awal', targetLanguage: 'Bahasa yang ingin dipelajari', currentLevel: 'Level CEFR saat ini', targetLevel: 'Level CEFR tujuan', targetDate: 'Tanggal tujuan', weeklyHours: 'Waktu belajar per minggu', priority: 'Prioritas keterampilan', balanced: 'Seimbang', comprehension: 'Pemahaman', production: 'Berbicara dan menulis', planButton: 'Buat rencana belajar saya', resetButton: 'Atur ulang', presetsLabel: 'Mulai dengan tempo', casualPreset: '30 menit per hari', steadyPreset: '5 jam per minggu', sprintPreset: 'Sprint ujian', plannerNote: 'Ini adalah perkiraan perencanaan berdasarkan rentang umum pembelajaran terpandu. Alat ini tidak menguji level atau menjamin kefasihan.', resultTitle: 'Rencana belajar Anda', liveLabel: 'Langsung', hoursRange: 'Perkiraan jam yang dibutuhkan', weeksAvailable: 'Minggu sampai tanggal Anda', hoursAvailable: 'Jam tersedia dengan tempo ini', recommendedTime: 'Rentang mingguan yang disarankan', onTrack: 'Jadwal Anda mencakup perkiraan tertinggi.', tight: 'Jadwal Anda mencakup perkiraan terendah. Sisakan waktu untuk mengulang.', insufficient: 'Batas waktu Anda lebih singkat dari perkiraan terendah. Tambahkan waktu atau ubah tanggal.', statusLabel: 'Status rencana', weeklyAllocation: 'Pembagian keterampilan mingguan', milestones: 'Tonggak', skillListening: 'Menyimak', skillSpeaking: 'Berbicara', skillReading: 'Membaca', skillWriting: 'Menulis', milestoneTarget: 'Mencapai', milestoneWeek: 'sekitar minggu', errorPrefix: 'Periksa masukan Anda', languageOptions: 'Inggris|Spanyol|Prancis|Jerman|Italia|Portugis|Jepang|Korea|Tionghoa|Arab', emptyResultDescription: 'Pilih tempo dan tanggal untuk melihat jalur dari satu level ke level berikutnya.', guidedLearningDetail: 'rentang pembelajaran terpandu', targetDateDetail: 'sampai tanggal tujuan', weeklyPaceDetail: 'dengan tempo mingguan pilihan Anda', recommendedRangeDetail: 'per minggu untuk rentang ini',
6
+ };
7
+
8
+ const faq = [
9
+ { question: 'Apa yang dihitung oleh perencana belajar bahasa?', answer: 'Alat ini memperkirakan rentang jam antara level CEFR saat ini dan tujuan, membandingkannya dengan minggu serta jam yang tersedia sebelum tanggal Anda, lalu menyarankan pembagian keterampilan mingguan.' },
10
+ { question: 'Apakah jam belajar ini merupakan jaminan?', answer: 'Tidak. Ini adalah rentang perencanaan umum berdasarkan perkiraan pembelajaran terpandu. Pasangan bahasa, pengalaman sebelumnya, paparan, kualitas belajar, dan kesempatan berlatih dapat mengubah jumlah sebenarnya secara besar.' },
11
+ { question: 'Apa perbedaan prioritas pemahaman dan produksi?', answer: 'Pemahaman memberi porsi lebih besar untuk menyimak dan membaca. Produksi memberi porsi lebih besar untuk berbicara dan menulis. Seimbang membagi waktu rata pada empat keterampilan.' },
12
+ { question: 'Bisakah alat ini dipakai untuk pasangan bahasa apa pun?', answer: 'Anda dapat memakainya sebagai titik awal untuk pasangan bahasa apa pun, tetapi perkiraan ini tidak memodelkan jarak bahasa atau kerumitan aksara. Anggap rentang ini sebagai batas perencanaan dan tinjau dengan kemajuan Anda.' },
13
+ { question: 'Apakah perencana menyimpan pilihan bahasa saya?', answer: 'Perencana berjalan di browser dan tidak mengirim masukan Anda ke server. Halaman ini tidak memerlukan akun atau proses masuk.' },
14
+ ];
15
+
16
+ export const content = createPlannerLocale({
17
+ slug: 'kalkulator-rencana-belajar-bahasa', title: 'Kalkulator Rencana Belajar Bahasa', description: 'Perkirakan jam belajar berdasarkan CEFR, uji tenggat waktu, dan ubah waktu mingguan Anda menjadi rencana belajar bahasa yang praktis.', ui, faq,
18
+ seo: makePlannerSeo({ titles: ['Buat rencana belajar bahasa yang sesuai tenggat', 'Cara perkiraan ini dihitung', 'Baca status rencana sebelum berkomitmen', 'Gunakan angka sebagai dasar'], paragraphs: ['Gunakan kalkulator ini saat Anda memiliki tujuan bahasa yang jelas tetapi perlu tahu apakah tenggat Anda realistis. Pilih level CEFR saat ini, level tujuan, tanggal, dan waktu mingguan yang benar benar dapat Anda lindungi. Anda akan mendapat rentang beban belajar, tempo mingguan yang disarankan, dan tonggak untuk meninjau kemajuan.', 'Perencana memakai rentang jam pembelajaran terpandu kumulatif untuk setiap level CEFR. Rentang level saat ini dikurangi dari rentang level tujuan, lalu hasilnya dibandingkan dengan minggu yang tersisa. Dalam model ini, perpindahan dari A1 ke B1 mewakili sekitar 260 sampai 300 jam terpandu. Dengan 5 jam per minggu, beban itu memerlukan sekitar 52 sampai 60 minggu, sebelum memperhitungkan sesi yang terlewat atau pengulangan tambahan.', 'CEFR menjelaskan kemampuan, bukan jadwal yang dijamin. Jarak bahasa, paparan sebelumnya, kualitas belajar, dan praktik nyata dapat mengubah tempo secara signifikan. Bandingkan tonggak dengan bukti nyata: jika kemajuan lebih lambat, ubah tanggal atau turunkan tujuan; jika lebih cepat, gunakan waktu tambahan untuk percakapan dan pengulangan.'], tableHeaders: ['Status', 'Artinya', 'Tindakan'], tableRows: [['Sesuai rencana', 'Jam tersedia mencapai perkiraan tertinggi.', 'Pertahankan rutinitas dan gunakan tonggak sebagai titik pemeriksaan.'], ['Ketat', 'Jam tersedia mencapai perkiraan terendah tetapi belum mencapai yang tertinggi.', 'Anggap tanggal ini menantang dan sisakan waktu untuk mengulang.'], ['Tidak cukup', 'Jam tersedia berada di bawah perkiraan terendah.', 'Tambah waktu mingguan, ubah tanggal, atau pilih tujuan yang lebih dekat.']] }),
19
+ bibliography: [{ name: 'Council of Europe: CEFR Companion Volume, edisi bahasa Spanyol', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: Jam pembelajaran terpandu', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
20
+ howToName: 'Membuat rencana belajar bahasa', howTo: [{ name: 'Pilih bahasa Anda', text: 'Pilih bahasa yang sudah Anda gunakan dengan nyaman dan bahasa yang ingin Anda pelajari.' }, { name: 'Atur kemajuan CEFR', text: 'Pilih level saat ini dan level yang ingin dicapai. Tujuan harus lebih tinggi dari level saat ini.' }, { name: 'Lindungi rutinitas mingguan', text: 'Atur tanggal tujuan di masa depan dan masukkan jam yang realistis untuk belajar setiap minggu.' }, { name: 'Tinjau dan sesuaikan', text: 'Gunakan status, pembagian keterampilan, dan tonggak sebagai rencana awal, lalu sesuaikan tanggal atau waktu mingguan.' }],
21
+ });
@@ -0,0 +1,21 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: 'Pianificatore per lo studio delle lingue', plannerHeading: 'Rendi visibile il tuo prossimo obiettivo linguistico.', sourceLanguage: 'Lingua di partenza', targetLanguage: 'Lingua da imparare', currentLevel: 'Livello QCER attuale', targetLevel: 'Livello QCER obiettivo', targetDate: 'Data obiettivo', weeklyHours: 'Tempo di studio settimanale', priority: 'Priorità delle abilità', balanced: 'Bilanciato', comprehension: 'Comprensione', production: 'Produzione orale e scritta', planButton: 'Crea il mio piano di studio', resetButton: 'Reimposta', presetsLabel: 'Inizia con un ritmo', casualPreset: '30 min al giorno', steadyPreset: '5 ore a settimana', sprintPreset: 'Sprint per l esame', plannerNote: 'È una stima per la pianificazione basata su ampi intervalli di apprendimento guidato. Non verifica il tuo livello e non garantisce la padronanza.', resultTitle: 'Il tuo piano di studio', liveLabel: 'Attivo', hoursRange: 'Ore stimate necessarie', weeksAvailable: 'Settimane fino alla tua data', hoursAvailable: 'Ore disponibili a questo ritmo', recommendedTime: 'Intervallo settimanale suggerito', onTrack: 'Il tuo calendario copre la stima alta.', tight: 'Il tuo calendario copre la stima bassa. Lascia spazio per il ripasso.', insufficient: 'La tua scadenza è più stretta della stima bassa. Aggiungi tempo o sposta la data.', statusLabel: 'Stato del piano', weeklyAllocation: 'Ripartizione settimanale delle abilità', milestones: 'Traguardi', skillListening: 'Ascolto', skillSpeaking: 'Conversazione', skillReading: 'Lettura', skillWriting: 'Scrittura', milestoneTarget: 'Raggiungere', milestoneWeek: 'circa alla settimana', errorPrefix: 'Controlla i dati', languageOptions: 'inglese|spagnolo|francese|tedesco|italiano|portoghese|giapponese|coreano|cinese|arabo', emptyResultDescription: 'Scegli un ritmo e una data per vedere il percorso da un livello al successivo.', guidedLearningDetail: 'intervallo di apprendimento guidato', targetDateDetail: 'fino alla data obiettivo', weeklyPaceDetail: 'al ritmo settimanale scelto', recommendedRangeDetail: 'a settimana per questo intervallo',
6
+ };
7
+
8
+ const faq = [
9
+ { question: 'Che cosa calcola il pianificatore per lo studio delle lingue?', answer: 'Stima un intervallo di ore tra il livello QCER attuale e quello obiettivo, lo confronta con le settimane e le ore disponibili prima della data scelta e suggerisce una ripartizione settimanale delle abilità.' },
10
+ { question: 'Le ore di studio sono una garanzia?', answer: 'No. Sono intervalli ampi per la pianificazione, basati su stime di apprendimento guidato. La coppia di lingue, l esperienza precedente, l esposizione, la qualità dello studio e le occasioni di pratica possono cambiare molto il risultato reale.' },
11
+ { question: 'Qual è la differenza tra comprensione e produzione?', answer: 'La comprensione assegna più tempo all ascolto e alla lettura. La produzione assegna più tempo alla conversazione e alla scrittura. Bilanciato divide il tempo in modo uniforme tra le quattro abilità.' },
12
+ { question: 'Posso usare il pianificatore per qualsiasi coppia di lingue?', answer: 'Puoi usarlo come punto di partenza per qualsiasi coppia, ma la stima non considera la distanza linguistica o la complessità della scrittura. Considera l intervallo una base di pianificazione e correggilo osservando i tuoi progressi.' },
13
+ { question: 'Il pianificatore conserva le mie scelte linguistiche?', answer: 'Funziona nel browser e non invia i tuoi dati a un server. La pagina non richiede un account né un accesso.' },
14
+ ];
15
+
16
+ export const content = createPlannerLocale({
17
+ slug: 'calcolatore-piano-studio-lingua', title: 'Calcolatore del piano di studio linguistico', description: 'Stima le ore QCER, verifica una scadenza e trasforma il tempo settimanale in un piano pratico per imparare una lingua.', ui, faq,
18
+ seo: makePlannerSeo({ titles: ['Crea un piano linguistico adatto alla tua scadenza', 'Come viene calcolata la stima', 'Leggi lo stato del piano prima di impegnarti', 'Usa il numero come punto di partenza'], paragraphs: ['Usa questo calcolatore quando hai un obiettivo linguistico chiaro ma vuoi sapere se la tua scadenza è realistica. Scegli il livello QCER attuale, il livello obiettivo, una data e il tempo settimanale che puoi davvero proteggere. Otterrai un intervallo di carico, un ritmo settimanale consigliato e traguardi da controllare durante il percorso.', 'Il pianificatore usa intervalli cumulativi di ore di apprendimento guidato per ogni livello QCER. Sottrae l intervallo del livello attuale da quello del livello obiettivo e confronta le ore con le settimane rimanenti. In questo modello, passare da A1 a B1 rappresenta circa 260 a 300 ore guidate. Con 5 ore alla settimana, il carico richiede circa 52 a 60 settimane, prima di considerare lezioni perse o ripassi aggiuntivi.', 'Il QCER descrive capacità, non un calendario garantito. La distanza tra le lingue, l esposizione precedente, la qualità dello studio e la pratica reale possono cambiare molto il ritmo. Confronta i traguardi con prove concrete: se i progressi rallentano, sposta la data o riduci l obiettivo; se accelerano, dedica il tempo extra alla conversazione e al ripasso.'], tableHeaders: ['Stato', 'Significato', 'Cosa fare'], tableRows: [['In linea', 'Le ore disponibili raggiungono la stima alta.', 'Mantieni la routine e usa i traguardi come punti di controllo.'], ['Stretto', 'Le ore raggiungono la stima bassa ma non quella alta.', 'Considera la data ambiziosa e proteggi il tempo per il ripasso.'], ['Insufficiente', 'Le ore disponibili restano sotto la stima bassa.', 'Aggiungi tempo settimanale, sposta la data o scegli un obiettivo più vicino.']] }),
19
+ bibliography: [{ name: 'Consiglio d Europa: Volume complementare del QCER, edizione spagnola', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: Ore di apprendimento guidato', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
20
+ howToName: 'Creare un piano di studio linguistico', howTo: [{ name: 'Scegli le lingue', text: 'Seleziona la lingua che già usi con sicurezza e quella che vuoi imparare.' }, { name: 'Imposta il percorso QCER', text: 'Scegli il livello attuale e quello che vuoi raggiungere. L obiettivo deve essere superiore al livello attuale.' }, { name: 'Proteggi una routine settimanale', text: 'Imposta una data obiettivo futura e inserisci le ore che puoi studiare realisticamente ogni settimana.' }, { name: 'Controlla e adatta', text: 'Usa stato, ripartizione e traguardi come piano iniziale, poi modifica la data o il tempo settimanale mentre impari.' }],
21
+ });
@@ -0,0 +1,21 @@
1
+ import type { LanguagePlannerUI } from '../ui';
2
+ import { createPlannerLocale, makePlannerSeo } from './shared';
3
+
4
+ const ui: LanguagePlannerUI = {
5
+ plannerKicker: '語学学習プランナー', plannerHeading: '次の語学目標を見える化しましょう。', sourceLanguage: '開始時の言語', targetLanguage: '学びたい言語', currentLevel: '現在のCEFRレベル', targetLevel: '目標のCEFRレベル', targetDate: '目標日', weeklyHours: '週あたりの学習時間', priority: '技能の優先順位', balanced: 'バランス', comprehension: '理解', production: '会話と作文', planButton: '学習プランを作成', resetButton: 'リセット', presetsLabel: 'ペースを選んで開始', casualPreset: '1日30分', steadyPreset: '週5時間', sprintPreset: '試験対策スプリント', plannerNote: 'これは幅広いガイド付き学習時間に基づく計画用の目安です。レベルを判定したり、流暢さを保証したりするものではありません。', resultTitle: 'あなたの学習プラン', liveLabel: '現在値', hoursRange: '必要な学習時間の目安', weeksAvailable: '目標日までの週数', hoursAvailable: 'このペースで使える時間', recommendedTime: '推奨される週あたりの範囲', onTrack: '利用できる時間が上限の目安を満たしています。', tight: '下限の目安は満たしていますが、復習の時間も確保しましょう。', insufficient: '期限が下限の目安より短くなっています。時間を増やすか目標日を変更してください。', statusLabel: 'プランの状態', weeklyAllocation: '週ごとの技能配分', milestones: '到達目安', skillListening: '聞く', skillSpeaking: '話す', skillReading: '読む', skillWriting: '書く', milestoneTarget: '到達', milestoneWeek: '週目ごろ', errorPrefix: '入力を確認してください', languageOptions: '英語|スペイン語|フランス語|ドイツ語|イタリア語|ポルトガル語|日本語|韓国語|中国語|アラビア語', emptyResultDescription: 'ペースと日付を選ぶと、現在のレベルから次のレベルまでの道筋が表示されます。', guidedLearningDetail: 'ガイド付き学習の範囲', targetDateDetail: '目標日まで', weeklyPaceDetail: '選択した週ペースで', recommendedRangeDetail: 'この範囲に必要な週あたり',
6
+ };
7
+
8
+ const faq = [
9
+ { question: '語学学習プランナーは何を計算しますか?', answer: '現在のCEFRレベルから目標レベルまでに必要な時間の範囲を見積もり、目標日までの週数と学習時間を比較して、技能ごとの週配分を提案します。' },
10
+ { question: '表示される学習時間は保証ですか?', answer: 'いいえ。ガイド付き学習の目安に基づく幅広い計画用の範囲です。言語の組み合わせ、経験、触れる量、学習の質、練習機会によって実際の時間は大きく変わります。' },
11
+ { question: '理解と産出の優先順位はどう違いますか?', answer: '理解は聞くことと読むことに多くの時間を配分します。産出は話すことと書くことに多く配分します。バランスは4技能に均等に時間を配分します。' },
12
+ { question: 'どの言語の組み合わせにも使えますか?', answer: 'どの言語の組み合わせでも出発点として使えますが、言語間の距離や文字体系の複雑さは計算していません。計画の枠として使い、実際の進歩を見ながら修正してください。' },
13
+ { question: '選んだ言語は保存されますか?', answer: 'このプランナーはブラウザー内で動作し、入力内容をサーバーへ送信しません。アカウントやログインも必要ありません。' },
14
+ ];
15
+
16
+ export const content = createPlannerLocale({
17
+ slug: 'language-learning-study-plan-planner', title: '語学学習プラン計算ツール', description: 'CEFRレベル間の学習時間を見積もり、目標日と週の学習時間から実行しやすい語学学習プランを作成します。', ui, faq,
18
+ seo: makePlannerSeo({ titles: ['期限に合う語学学習プランを作る', '見積もりの計算方法', '決める前にプランの状態を確認する', '数値を出発点として使う'], paragraphs: ['明確な語学目標があり、設定した期限が現実的かを知りたいときに使ってください。現在のCEFRレベル、目標レベル、日付、毎週確保できる学習時間を選びます。必要時間の範囲、提案される週ペース、進捗を確認するための到達目安が表示されます。', 'このプランナーは各CEFRレベルについて、ガイド付き学習時間の累積範囲を使います。現在のレベルの範囲を目標レベルの範囲から引き、残りの週数と比較します。このモデルではA1からB1への移行はおよそ260から300時間です。週5時間なら、欠席や追加の復習を含める前に約52から60週かかります。', 'CEFRは能力を表す基準であり、保証された日程ではありません。言語間の距離、これまでの接触、学習の質、実際の使用によってペースは変わります。到達目安を実際の結果と比べ、遅ければ目標日を延ばすか目標を下げ、速ければ会話と復習に余分な時間を使いましょう。'], tableHeaders: ['状態', '意味', '対応'], tableRows: [['順調', '利用できる時間が上限の目安に届いています。', '習慣を維持し、到達目安を確認点として使います。'], ['余裕が少ない', '下限には届きますが、上限には届きません。', '挑戦的な期限と考え、復習時間を確保します。'], ['不足', '利用できる時間が下限の目安を下回っています。', '週の時間を増やす、日付を延ばす、近い目標を選ぶ、のいずれかを行います。']] }),
19
+ bibliography: [{ name: '欧州評議会: CEFR Companion Volume スペイン語版', url: 'https://rm.coe.int/marco-comun-europeo-de-referencia-para-las-lenguas-aprendizaje-ensenan/1680a52d53' }, { name: 'Cambridge English: ガイド付き学習時間', url: 'https://support.cambridgeenglish.org/hc/en-gb/articles/202838506-Guided-learning-hours' }],
20
+ howToName: '語学学習プランを作成する', howTo: [{ name: '言語を選ぶ', text: 'すでに無理なく使える言語と、学びたい言語を選びます。' }, { name: 'CEFRの進歩を設定する', text: '現在のレベルと到達したいレベルを選びます。目標は現在のレベルより上である必要があります。' }, { name: '毎週の習慣を確保する', text: '将来の目標日を設定し、毎週現実的に学べる時間を入力します。' }, { name: '確認して調整する', text: '状態、技能配分、到達目安を最初の計画として使い、学習しながら日付や週の時間を調整します。' }],
21
+ });