@jjlmoya/utils-finance 1.3.0 → 1.6.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 (168) hide show
  1. package/package.json +2 -1
  2. package/src/tests/faq_count.test.ts +1 -1
  3. package/src/tests/i18n_coverage.test.ts +36 -0
  4. package/src/tests/locale_completeness.test.ts +3 -0
  5. package/src/tests/slug_uniqueness.test.ts +81 -0
  6. package/src/tests/title_quality.test.ts +1 -1
  7. package/src/tool/compoundInterest/i18n/de.ts +208 -0
  8. package/src/tool/compoundInterest/i18n/id.ts +208 -0
  9. package/src/tool/compoundInterest/i18n/it.ts +208 -0
  10. package/src/tool/compoundInterest/i18n/ja.ts +207 -0
  11. package/src/tool/compoundInterest/i18n/ko.ts +207 -0
  12. package/src/tool/compoundInterest/i18n/nl.ts +208 -0
  13. package/src/tool/compoundInterest/i18n/pl.ts +208 -0
  14. package/src/tool/compoundInterest/i18n/pt.ts +208 -0
  15. package/src/tool/compoundInterest/i18n/ru.ts +208 -0
  16. package/src/tool/compoundInterest/i18n/sv.ts +208 -0
  17. package/src/tool/compoundInterest/i18n/tr.ts +208 -0
  18. package/src/tool/compoundInterest/i18n/zh.ts +202 -0
  19. package/src/tool/compoundInterest/index.ts +15 -7
  20. package/src/tool/courtFeeCalculator/i18n/de.ts +152 -0
  21. package/src/tool/courtFeeCalculator/i18n/fr.ts +1 -1
  22. package/src/tool/courtFeeCalculator/i18n/id.ts +152 -0
  23. package/src/tool/courtFeeCalculator/i18n/it.ts +152 -0
  24. package/src/tool/courtFeeCalculator/i18n/ja.ts +152 -0
  25. package/src/tool/courtFeeCalculator/i18n/ko.ts +152 -0
  26. package/src/tool/courtFeeCalculator/i18n/nl.ts +152 -0
  27. package/src/tool/courtFeeCalculator/i18n/pl.ts +152 -0
  28. package/src/tool/courtFeeCalculator/i18n/pt.ts +152 -0
  29. package/src/tool/courtFeeCalculator/i18n/ru.ts +152 -0
  30. package/src/tool/courtFeeCalculator/i18n/sv.ts +152 -0
  31. package/src/tool/courtFeeCalculator/i18n/tr.ts +152 -0
  32. package/src/tool/courtFeeCalculator/i18n/zh.ts +152 -0
  33. package/src/tool/courtFeeCalculator/index.ts +16 -8
  34. package/src/tool/debtSnowball/i18n/de.ts +160 -0
  35. package/src/tool/debtSnowball/i18n/id.ts +160 -0
  36. package/src/tool/debtSnowball/i18n/it.ts +160 -0
  37. package/src/tool/debtSnowball/i18n/ja.ts +157 -0
  38. package/src/tool/debtSnowball/i18n/ko.ts +160 -0
  39. package/src/tool/debtSnowball/i18n/nl.ts +160 -0
  40. package/src/tool/debtSnowball/i18n/pl.ts +160 -0
  41. package/src/tool/debtSnowball/i18n/pt.ts +160 -0
  42. package/src/tool/debtSnowball/i18n/ru.ts +160 -0
  43. package/src/tool/debtSnowball/i18n/sv.ts +160 -0
  44. package/src/tool/debtSnowball/i18n/tr.ts +160 -0
  45. package/src/tool/debtSnowball/i18n/zh.ts +160 -0
  46. package/src/tool/debtSnowball/index.ts +17 -9
  47. package/src/tool/fireCalculator/component.astro +12 -3
  48. package/src/tool/fireCalculator/i18n/de.ts +261 -0
  49. package/src/tool/fireCalculator/i18n/en.ts +5 -5
  50. package/src/tool/fireCalculator/i18n/es.ts +2 -2
  51. package/src/tool/fireCalculator/i18n/fr.ts +5 -5
  52. package/src/tool/fireCalculator/i18n/id.ts +261 -0
  53. package/src/tool/fireCalculator/i18n/it.ts +261 -0
  54. package/src/tool/fireCalculator/i18n/ja.ts +261 -0
  55. package/src/tool/fireCalculator/i18n/ko.ts +261 -0
  56. package/src/tool/fireCalculator/i18n/nl.ts +261 -0
  57. package/src/tool/fireCalculator/i18n/pl.ts +261 -0
  58. package/src/tool/fireCalculator/i18n/pt.ts +261 -0
  59. package/src/tool/fireCalculator/i18n/ru.ts +261 -0
  60. package/src/tool/fireCalculator/i18n/sv.ts +261 -0
  61. package/src/tool/fireCalculator/i18n/tr.ts +261 -0
  62. package/src/tool/fireCalculator/i18n/zh.ts +261 -0
  63. package/src/tool/fireCalculator/index.ts +15 -7
  64. package/src/tool/ibanBicSwiftConverter/i18n/de.ts +128 -0
  65. package/src/tool/ibanBicSwiftConverter/i18n/id.ts +128 -0
  66. package/src/tool/ibanBicSwiftConverter/i18n/it.ts +128 -0
  67. package/src/tool/ibanBicSwiftConverter/i18n/ja.ts +128 -0
  68. package/src/tool/ibanBicSwiftConverter/i18n/ko.ts +128 -0
  69. package/src/tool/ibanBicSwiftConverter/i18n/nl.ts +128 -0
  70. package/src/tool/ibanBicSwiftConverter/i18n/pl.ts +128 -0
  71. package/src/tool/ibanBicSwiftConverter/i18n/pt.ts +128 -0
  72. package/src/tool/ibanBicSwiftConverter/i18n/ru.ts +128 -0
  73. package/src/tool/ibanBicSwiftConverter/i18n/sv.ts +128 -0
  74. package/src/tool/ibanBicSwiftConverter/i18n/tr.ts +128 -0
  75. package/src/tool/ibanBicSwiftConverter/i18n/zh.ts +128 -0
  76. package/src/tool/ibanBicSwiftConverter/index.ts +15 -7
  77. package/src/tool/inflation/i18n/de.ts +297 -0
  78. package/src/tool/inflation/i18n/id.ts +297 -0
  79. package/src/tool/inflation/i18n/it.ts +297 -0
  80. package/src/tool/inflation/i18n/ja.ts +297 -0
  81. package/src/tool/inflation/i18n/ko.ts +297 -0
  82. package/src/tool/inflation/i18n/nl.ts +297 -0
  83. package/src/tool/inflation/i18n/pl.ts +297 -0
  84. package/src/tool/inflation/i18n/pt.ts +297 -0
  85. package/src/tool/inflation/i18n/ru.ts +297 -0
  86. package/src/tool/inflation/i18n/sv.ts +297 -0
  87. package/src/tool/inflation/i18n/tr.ts +297 -0
  88. package/src/tool/inflation/i18n/zh.ts +297 -0
  89. package/src/tool/inflation/index.ts +17 -9
  90. package/src/tool/lateInterest/i18n/de.ts +224 -0
  91. package/src/tool/lateInterest/i18n/id.ts +224 -0
  92. package/src/tool/lateInterest/i18n/it.ts +224 -0
  93. package/src/tool/lateInterest/i18n/ja.ts +224 -0
  94. package/src/tool/lateInterest/i18n/ko.ts +224 -0
  95. package/src/tool/lateInterest/i18n/nl.ts +224 -0
  96. package/src/tool/lateInterest/i18n/pl.ts +224 -0
  97. package/src/tool/lateInterest/i18n/pt.ts +224 -0
  98. package/src/tool/lateInterest/i18n/ru.ts +224 -0
  99. package/src/tool/lateInterest/i18n/sv.ts +224 -0
  100. package/src/tool/lateInterest/i18n/tr.ts +224 -0
  101. package/src/tool/lateInterest/i18n/zh.ts +224 -0
  102. package/src/tool/lateInterest/index.ts +17 -9
  103. package/src/tool/legalInterestRate/i18n/de.ts +143 -0
  104. package/src/tool/legalInterestRate/i18n/id.ts +143 -0
  105. package/src/tool/legalInterestRate/i18n/it.ts +143 -0
  106. package/src/tool/legalInterestRate/i18n/ja.ts +143 -0
  107. package/src/tool/legalInterestRate/i18n/ko.ts +143 -0
  108. package/src/tool/legalInterestRate/i18n/nl.ts +143 -0
  109. package/src/tool/legalInterestRate/i18n/pl.ts +143 -0
  110. package/src/tool/legalInterestRate/i18n/pt.ts +143 -0
  111. package/src/tool/legalInterestRate/i18n/ru.ts +143 -0
  112. package/src/tool/legalInterestRate/i18n/sv.ts +143 -0
  113. package/src/tool/legalInterestRate/i18n/tr.ts +143 -0
  114. package/src/tool/legalInterestRate/i18n/zh.ts +143 -0
  115. package/src/tool/legalInterestRate/index.ts +17 -9
  116. package/src/tool/lotteryOptimizer/i18n/de.ts +177 -0
  117. package/src/tool/lotteryOptimizer/i18n/id.ts +177 -0
  118. package/src/tool/lotteryOptimizer/i18n/it.ts +177 -0
  119. package/src/tool/lotteryOptimizer/i18n/ja.ts +177 -0
  120. package/src/tool/lotteryOptimizer/i18n/ko.ts +177 -0
  121. package/src/tool/lotteryOptimizer/i18n/nl.ts +177 -0
  122. package/src/tool/lotteryOptimizer/i18n/pl.ts +177 -0
  123. package/src/tool/lotteryOptimizer/i18n/pt.ts +177 -0
  124. package/src/tool/lotteryOptimizer/i18n/ru.ts +177 -0
  125. package/src/tool/lotteryOptimizer/i18n/sv.ts +177 -0
  126. package/src/tool/lotteryOptimizer/i18n/tr.ts +177 -0
  127. package/src/tool/lotteryOptimizer/i18n/zh.ts +177 -0
  128. package/src/tool/lotteryOptimizer/index.ts +17 -9
  129. package/src/tool/mortgage/i18n/de.ts +223 -0
  130. package/src/tool/mortgage/i18n/id.ts +219 -0
  131. package/src/tool/mortgage/i18n/it.ts +223 -0
  132. package/src/tool/mortgage/i18n/ja.ts +219 -0
  133. package/src/tool/mortgage/i18n/ko.ts +219 -0
  134. package/src/tool/mortgage/i18n/nl.ts +219 -0
  135. package/src/tool/mortgage/i18n/pl.ts +219 -0
  136. package/src/tool/mortgage/i18n/pt.ts +219 -0
  137. package/src/tool/mortgage/i18n/ru.ts +219 -0
  138. package/src/tool/mortgage/i18n/sv.ts +219 -0
  139. package/src/tool/mortgage/i18n/tr.ts +219 -0
  140. package/src/tool/mortgage/i18n/zh.ts +219 -0
  141. package/src/tool/mortgage/index.ts +15 -7
  142. package/src/tool/percentageCalculator/i18n/de.ts +229 -0
  143. package/src/tool/percentageCalculator/i18n/id.ts +229 -0
  144. package/src/tool/percentageCalculator/i18n/it.ts +229 -0
  145. package/src/tool/percentageCalculator/i18n/ja.ts +229 -0
  146. package/src/tool/percentageCalculator/i18n/ko.ts +229 -0
  147. package/src/tool/percentageCalculator/i18n/nl.ts +229 -0
  148. package/src/tool/percentageCalculator/i18n/pl.ts +229 -0
  149. package/src/tool/percentageCalculator/i18n/pt.ts +229 -0
  150. package/src/tool/percentageCalculator/i18n/ru.ts +229 -0
  151. package/src/tool/percentageCalculator/i18n/sv.ts +229 -0
  152. package/src/tool/percentageCalculator/i18n/tr.ts +229 -0
  153. package/src/tool/percentageCalculator/i18n/zh.ts +229 -0
  154. package/src/tool/percentageCalculator/index.ts +15 -7
  155. package/src/tool/rentIncreaseCalculator/i18n/de.ts +136 -0
  156. package/src/tool/rentIncreaseCalculator/i18n/id.ts +136 -0
  157. package/src/tool/rentIncreaseCalculator/i18n/it.ts +136 -0
  158. package/src/tool/rentIncreaseCalculator/i18n/ja.ts +136 -0
  159. package/src/tool/rentIncreaseCalculator/i18n/ko.ts +136 -0
  160. package/src/tool/rentIncreaseCalculator/i18n/nl.ts +136 -0
  161. package/src/tool/rentIncreaseCalculator/i18n/pl.ts +136 -0
  162. package/src/tool/rentIncreaseCalculator/i18n/pt.ts +136 -0
  163. package/src/tool/rentIncreaseCalculator/i18n/ru.ts +136 -0
  164. package/src/tool/rentIncreaseCalculator/i18n/sv.ts +136 -0
  165. package/src/tool/rentIncreaseCalculator/i18n/tr.ts +136 -0
  166. package/src/tool/rentIncreaseCalculator/i18n/zh.ts +136 -0
  167. package/src/tool/rentIncreaseCalculator/index.ts +17 -9
  168. package/src/types.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-finance",
3
- "version": "1.3.0",
3
+ "version": "1.6.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -37,6 +37,7 @@
37
37
  },
38
38
  "dependencies": {
39
39
  "@iconify-json/mdi": "^1.2.3",
40
+ "@jjlmoya/prompagate": "^1.1.0",
40
41
  "@jjlmoya/utils-shared": "1.2.0",
41
42
  "astro": "^6.1.2",
42
43
  "astro-icon": "^1.1.0",
@@ -1,7 +1,7 @@
1
1
  import { describe, it, expect } from 'vitest';
2
2
  import type * as DATA from '../data';
3
3
 
4
- const TOOLS: typeof DATA.financeCategory[] = [];
4
+ const TOOLS: typeof DATA.audiovisualCategory[] = [];
5
5
 
6
6
  describe('FAQ Content Validation', () => {
7
7
  TOOLS.forEach((entry) => {
@@ -0,0 +1,36 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+
4
+ const EXPECTED_LOCALES = [
5
+ 'de', 'en', 'es', 'fr', 'id', 'it', 'ja', 'ko', 'nl', 'pl', 'pt', 'ru', 'sv', 'tr', 'zh'
6
+ ];
7
+
8
+ describe('I18n Coverage Validation', () => {
9
+ it('all tools should be registered', () => {
10
+ expect(ALL_TOOLS.length).toBeGreaterThan(0);
11
+ });
12
+
13
+ ALL_TOOLS.forEach(({ entry }: { entry: any }) => {
14
+ describe(`Tool: ${entry.id}`, () => {
15
+ it('should have all 15 required locales', () => {
16
+ const registeredLocales = Object.keys(entry.i18n);
17
+ EXPECTED_LOCALES.forEach((locale) => {
18
+ expect(
19
+ registeredLocales,
20
+ `Tool "${entry.id}" is missing locale "${locale}"`,
21
+ ).toContain(locale);
22
+ });
23
+ });
24
+
25
+ it('all locale loaders should be functions', () => {
26
+ EXPECTED_LOCALES.forEach((locale) => {
27
+ const loader = entry.i18n[locale as keyof typeof entry.i18n];
28
+ expect(
29
+ typeof loader,
30
+ `Tool "${entry.id}" locale "${locale}" loader is not a function`,
31
+ ).toBe('function');
32
+ });
33
+ });
34
+ });
35
+ });
36
+ });
@@ -35,5 +35,8 @@ describe('Locale Completeness Validation', () => {
35
35
  });
36
36
  });
37
37
 
38
+ it('all 12 tools registered', () => {
39
+ expect(ALL_TOOLS.length).toBe(12);
40
+ });
38
41
  });
39
42
 
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_TOOLS } from '../tools';
3
+ import type { ToolLocaleContent } from '../types';
4
+
5
+ const sharingLocales = ['ja', 'ko', 'zh'];
6
+
7
+ interface ValidateParams {
8
+ toolId: string;
9
+ locale: string;
10
+ content: ToolLocaleContent;
11
+ enSlug: string;
12
+ slugs: Map<string, string>;
13
+ }
14
+
15
+ const validateLocaleSlug = ({
16
+ toolId,
17
+ locale,
18
+ content,
19
+ enSlug,
20
+ slugs,
21
+ }: ValidateParams) => {
22
+ expect(
23
+ content.slug,
24
+ `Tool "${toolId}" locale "${locale}" has an invalid slug ("${content.slug}"). Slugs must be transliterated (only a-z, 0-9, and -).`,
25
+ ).toMatch(/^[a-z0-9-]+$/);
26
+
27
+ if (locale === 'en') {
28
+ return;
29
+ }
30
+
31
+ if (sharingLocales.includes(locale)) {
32
+ expect(
33
+ content.slug,
34
+ `Tool "${toolId}" locale "${locale}" must use the same slug as "en" ("${enSlug}").`,
35
+ ).toBe(enSlug);
36
+ } else {
37
+ expect(
38
+ content.slug,
39
+ `Tool "${toolId}" locale "${locale}" has the same slug as "en" ("${enSlug}"). Cada slug tiene que estar en su propia idioma`,
40
+ ).not.toBe(enSlug);
41
+
42
+ if (slugs.has(content.slug)) {
43
+ const previousLocale = slugs.get(content.slug);
44
+ expect(
45
+ false,
46
+ `Tool "${toolId}" locales "${locale}" and "${previousLocale}" share the same slug ("${content.slug}"). Cada slug tiene que estar en su propia idioma`,
47
+ ).toBe(true);
48
+ }
49
+ slugs.set(content.slug, locale);
50
+ }
51
+ };
52
+
53
+ describe('Slug Localization and Uniqueness Validation', () => {
54
+ ALL_TOOLS.forEach((tool) => {
55
+ describe(`Tool: ${tool.entry.id}`, () => {
56
+ it('every locale should have a unique, translated slug', async () => {
57
+ const slugs = new Map<string, string>();
58
+ const locales = Object.keys(tool.entry.i18n);
59
+
60
+ let enSlug = '';
61
+ if (locales.includes('en')) {
62
+ const enLoader = tool.entry.i18n['en' as keyof typeof tool.entry.i18n];
63
+ const enContent = (await enLoader?.()) as ToolLocaleContent;
64
+ enSlug = enContent.slug;
65
+ }
66
+
67
+ for (const locale of locales) {
68
+ const loader = tool.entry.i18n[locale as keyof typeof tool.entry.i18n];
69
+ const content = (await loader?.()) as ToolLocaleContent;
70
+ validateLocaleSlug({
71
+ toolId: tool.entry.id,
72
+ locale,
73
+ content,
74
+ enSlug,
75
+ slugs,
76
+ });
77
+ }
78
+ });
79
+ });
80
+ });
81
+ });
@@ -41,7 +41,7 @@ describe('Project Titles - Separator Validation', () => {
41
41
  let match;
42
42
  while ((match = pattern.exec(content)) !== null) {
43
43
  const title = match[1];
44
- if (title && (title.includes('|') || title.includes('-'))) {
44
+ if (title.includes('|') || title.includes('-')) {
45
45
  findings.push(title);
46
46
  }
47
47
  }
@@ -0,0 +1,208 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { CompoundInterestUI } from '../ui';
4
+
5
+ const slug = 'zinseszins-rechner';
6
+ const title = 'Zinseszinsrechner: Visualisieren Sie Ihren zukünftigen Reichtum';
7
+ const description =
8
+ 'Entdecken Sie, wie aus kleinen Investitionen große Vermögen werden. Das ultimative Werkzeug zur Planung Ihrer finanziellen Freiheit und Altersvorsorge.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Was ist Zinseszins?',
13
+ answer:
14
+ 'Es sind Zinsen, die auf das ursprüngliche Kapital und auch auf die aufgelaufenen Zinsen früherer Perioden berechnet werden. Dies führt im Laufe der Zeit zu einem exponentiellen Wachstum der Investition.',
15
+ },
16
+ {
17
+ question: 'Was ist der Unterschied zum einfachen Zins?',
18
+ answer:
19
+ 'Beim einfachen Zins werden die Zinsen nur auf das ursprüngliche Kapital berechnet. Beim Zinseszins werden die Zinsen reinvestiert, was bedeutet, dass „Ihre Zinsen weitere Zinsen generieren“.',
20
+ },
21
+ {
22
+ question: 'Was ist die „72er-Regel“?',
23
+ answer:
24
+ 'Es ist eine Faustformel zur Schätzung, wie lange es dauert, bis sich eine Investition verdoppelt: Teilen Sie 72 durch die jährliche Rendite. Beispiel: Bei 8 % pro Jahr verdoppeln Sie Ihr Geld in 9 Jahren.',
25
+ },
26
+ {
27
+ question: 'Warum ist es wichtig, jung mit dem Investieren zu beginnen?',
28
+ answer:
29
+ 'Aufgrund des exponentiellen Effekts ist der entscheidendste Faktor nicht die Menge des gesparten Geldes, sondern die Zeit. Ein paar Jahre früher anzufangen, kann aufgrund der Kapitalisierung zu einem viel größeren Endvermögen führen.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Startkapital eingeben',
36
+ text: 'Geben Sie den Geldbetrag ein, mit dem Sie Ihren Investitionsplan beginnen möchten.',
37
+ },
38
+ {
39
+ name: 'Regelmäßige Beiträge festlegen',
40
+ text: 'Geben Sie an, wie viel Geld Sie zusätzlich jeden Monat oder jedes Jahr sparen und investieren werden.',
41
+ },
42
+ {
43
+ name: 'Jährliche Rendite schätzen',
44
+ text: 'Geben Sie den Prozentsatz an jährlichem Gewinn ein, den Sie erwarten (z. B. 7 % für den historischen S&P 500).',
45
+ },
46
+ {
47
+ name: 'Zeithorizont anpassen',
48
+ text: 'Wählen Sie aus, wie viele Jahre Sie die Investition halten möchten, um das exponentielle Wachstum Ihres Kapitals zu visualisieren.',
49
+ },
50
+ ];
51
+
52
+ const faqSchema: WithContext<FAQPage> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'FAQPage',
55
+ mainEntity: faqData.map((item) => ({
56
+ '@type': 'Question',
57
+ name: item.question,
58
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
59
+ })),
60
+ };
61
+
62
+ const howToSchema: WithContext<HowTo> = {
63
+ '@context': 'https://schema.org',
64
+ '@type': 'HowTo',
65
+ name: title,
66
+ description,
67
+ step: howToData.map((step, i) => ({
68
+ '@type': 'HowToStep',
69
+ position: i + 1,
70
+ name: step.name,
71
+ text: step.text,
72
+ })),
73
+ };
74
+
75
+ const appSchema: WithContext<SoftwareApplication> = {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'SoftwareApplication',
78
+ name: title,
79
+ description,
80
+ applicationCategory: 'FinanceApplication',
81
+ operatingSystem: 'All',
82
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'EUR' },
83
+ inLanguage: 'de',
84
+ };
85
+
86
+ export const content: ToolLocaleContent<CompoundInterestUI> = {
87
+ slug,
88
+ title,
89
+ description,
90
+ faqTitle: 'Häufig gestellte Fragen',
91
+ faq: faqData,
92
+ bibliographyTitle: 'Quellen und Referenzen',
93
+ bibliography: [
94
+ {
95
+ name: 'Investopedia: Compound Interest Definition',
96
+ url: 'https://www.investopedia.com/terms/c/compoundinterest.asp',
97
+ },
98
+ {
99
+ name: 'Investopedia: Rule of 72',
100
+ url: 'https://www.investopedia.com/terms/r/ruleof72.asp',
101
+ },
102
+ {
103
+ name: 'Khan Academy: Interest and Debt',
104
+ url: 'https://www.khanacademy.org/economics-finance-domain/core-finance/interest-tutorial',
105
+ },
106
+ ],
107
+ howTo: howToData,
108
+ schemas: [faqSchema, howToSchema, appSchema],
109
+ seo: [
110
+ {
111
+ type: 'title',
112
+ text: 'Die Magie des Zinseszinses: Bauen Sie Ihren Reichtum exponentiell auf',
113
+ level: 2,
114
+ },
115
+ {
116
+ type: 'paragraph',
117
+ html: 'Albert Einstein nannte ihn das „achte Weltwunder“. Der <strong>Zinseszins</strong> ist der mächtigste Mechanismus, um langfristig Reichtum zu generieren. Man muss kein Finanzexperte sein, um ihn zu nutzen: Man braucht nur Zeit, Geduld und investiertes Geld.',
118
+ },
119
+ {
120
+ type: 'title',
121
+ text: 'Einfacher Zins vs. Zinseszins: Die Metapher des Schneeballs',
122
+ level: 3,
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'Stellen Sie sich vor, Sie haben einen Schneeball oben auf einem Hügel. Der <strong>einfache Zins</strong> ist so, als würde man diesen Ball hinunterrollen und manuell jeden Meter Schnee hinzufügen müssen, damit er wächst. Der <strong>Zinseszins</strong> ist so, als würde man den Ball von selbst rollen lassen: Er sammelt von alleine Schnee auf, und je größer er wird, desto mehr Oberfläche hat er, um bei jeder Drehung <em>noch mehr</em> Schnee aufzusammeln.',
127
+ },
128
+ {
129
+ type: 'comparative',
130
+ columns: 2,
131
+ items: [
132
+ {
133
+ title: 'Einfacher Zins',
134
+ description: 'Die Zinsen werden immer nur auf das ursprüngliche Kapital berechnet.',
135
+ points: [
136
+ 'Formel: Kapital × Zinssatz × Zeit',
137
+ 'Lineares und vorhersehbares Wachstum',
138
+ 'Verwendet bei kurzfristigen Krediten',
139
+ 'Keine Reinvestition von Gewinnen',
140
+ ],
141
+ },
142
+ {
143
+ title: 'Zinseszins',
144
+ description: 'Zinsen werden zum Kapital addiert und generieren neue Zinsen.',
145
+ highlight: true,
146
+ points: [
147
+ 'Formel: Kapital × (1 + Zinssatz)^Zeit',
148
+ 'Beschleunigtes exponentielles Wachstum',
149
+ 'Grundlage jeder langfristigen Investition',
150
+ 'Ihre Gewinne generieren weitere Gewinne',
151
+ ],
152
+ },
153
+ ],
154
+ },
155
+ {
156
+ type: 'paragraph',
157
+ html: 'Mathematisch gesehen generieren Ihre Gewinne neue Gewinne. In den ersten Jahren scheint es langsam zu sein, aber nach dem „Wendepunkt“ schießt die Kurve vertikal nach oben. Hier entstehen die wahren Vermögen.',
158
+ },
159
+ {
160
+ type: 'title',
161
+ text: 'Warum ist Zeit Ihr größter Verbündeter?',
162
+ level: 3,
163
+ },
164
+ {
165
+ type: 'paragraph',
166
+ html: 'Der entscheidendste Faktor ist nicht die Geldmenge, die Sie investieren, sondern <strong>wie viele Jahre Sie es wachsen lassen</strong>. 10 Jahre früher anzufangen kann zu einem 2- oder 3-mal größeren Endvermögen führen, selbst wenn Sie insgesamt weniger Geld investieren. Dieser exponentielle Effekt ist der Grund, warum junge Investoren einen unvergleichlichen Vorteil haben.',
167
+ },
168
+ {
169
+ type: 'tip',
170
+ title: 'Die 72er Regel',
171
+ html: '<p>Teilen Sie 72 durch Ihre jährliche Rendite, um zu wissen, wie viele Jahre es dauert, bis sich <strong>Ihr Geld verdoppelt</strong>.</p><p><em>Beispiel: Bei 8% verdoppeln Sie alle 9 Jahre (72/8 = 9).</em></p><p>Diese magische Formel funktioniert für jede Rendite und hilft Ihnen, das Wachstum Ihrer Investitionen schnell abzuschätzen.</p>',
172
+ },
173
+ {
174
+ type: 'tip',
175
+ title: 'Tipp 2026',
176
+ html: '<p>Die Inflation bleibt ein Faktor. Stellen Sie sicher, dass Ihre Nettorendite mindestens 2-3 % pro Jahr übersteigt, um keine Kaufkraft zu verlieren. Investieren Sie in Vermögenswerte, die schneller wachsen als die Inflation.</p>',
177
+ },
178
+ {
179
+ type: 'title',
180
+ text: 'Zinskapitalisierung: Spielt sie eine Rolle?',
181
+ level: 3,
182
+ },
183
+ {
184
+ type: 'paragraph',
185
+ html: 'Zinsen können jährlich, halbjährlich, vierteljährlich, monatlich oder sogar täglich kapitalisiert werden. Je <strong>häufiger die Kapitalisierung</strong> erfolgt, desto größer ist der Zinseszinseffekt. Ein gleiches Kapital mit demselben Zinssatz wächst stärker, wenn die Zinsverrechnung monatlich erfolgt als wenn sie nur jährlich durchgeführt wird.',
186
+ },
187
+ ],
188
+ ui: {
189
+ labelTitle: 'Investitionssimulator',
190
+ labelRealtime: 'Echtzeit',
191
+ labelInitial: 'Startkapital',
192
+ labelMonthly: 'Monatlicher Beitrag',
193
+ labelRate: 'Rendite p.a.',
194
+ labelYears: 'Jahre',
195
+ labelMyMoney: 'Ihr Geld',
196
+ labelProfit: 'Gewinn (Zinsen)',
197
+ labelTotal: 'Gesamtguthaben',
198
+ labelYear: 'Jahr',
199
+ labelPrincipal: 'Ihr Geld (Stammkapital)',
200
+ labelInterest: 'Generierte Zinsen',
201
+ insightPrefix: '[TIPP] Im Jahr ',
202
+ insightSuffix:
203
+ ', werden Ihre jährlich generierten Zinsen Ihre Beiträge übersteigen. Das Geld arbeitet bereits mehr als Sie!',
204
+ currencySymbol: '€',
205
+ currencyCode: 'EUR',
206
+ currencyLocale: 'de-DE',
207
+ },
208
+ };
@@ -0,0 +1,208 @@
1
+ import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
2
+ import type { ToolLocaleContent } from '../../../types';
3
+ import type { CompoundInterestUI } from '../ui';
4
+
5
+ const slug = 'bunga-majemuk';
6
+ const title = 'Kalkulator Bunga Majemuk: Visualisasikan Kekayaan Masa Depan Anda';
7
+ const description =
8
+ 'Temukan bagaimana investasi kecil dapat berubah menjadi kekayaan besar. Alat utama untuk merencanakan kebebasan finansial dan masa pensiun Anda.';
9
+
10
+ const faqData = [
11
+ {
12
+ question: 'Apa itu bunga majemuk?',
13
+ answer:
14
+ 'Ini adalah bunga yang dihitung berdasarkan modal awal dan juga akumulasi bunga dari periode sebelumnya. Hal ini menghasilkan pertumbuhan investasi yang eksponensial seiring berjalannya waktu.',
15
+ },
16
+ {
17
+ question: 'Apa perbedaan dengan bunga tunggal?',
18
+ answer:
19
+ 'Pada bunga tunggal, bunga hanya dihitung dari modal awal. Pada bunga majemuk, bunga diinvestasikan kembali, yang berarti "bunga Anda menghasilkan lebih banyak bunga".',
20
+ },
21
+ {
22
+ question: 'Apa itu "Aturan 72"?',
23
+ answer:
24
+ 'Ini adalah rumus cepat untuk memperkirakan berapa lama waktu yang dibutuhkan agar investasi menjadi dua kali lipat: bagi 72 dengan tingkat pengembalian tahunan. Misalnya, pada bunga 8% per tahun, Anda akan melipatgandakan uang Anda dalam 9 tahun.',
25
+ },
26
+ {
27
+ question: 'Mengapa penting untuk mulai berinvestasi sejak muda?',
28
+ answer:
29
+ 'Karena efek eksponensial, faktor yang paling menentukan bukanlah jumlah uang yang ditabung, melainkan waktu. Memulai beberapa tahun lebih awal dapat menghasilkan kekayaan akhir yang jauh lebih besar karena kapitalisasi.',
30
+ },
31
+ ];
32
+
33
+ const howToData = [
34
+ {
35
+ name: 'Masukkan modal awal',
36
+ text: 'Tuliskan jumlah uang yang akan Anda gunakan untuk memulai rencana investasi Anda.',
37
+ },
38
+ {
39
+ name: 'Tentukan kontribusi berkala',
40
+ text: 'Tunjukkan berapa banyak uang yang akan Anda tabung dan investasikan setiap bulan atau setiap tahun sebagai tambahan.',
41
+ },
42
+ {
43
+ name: 'Perkirakan imbal hasil tahunan',
44
+ text: 'Masukkan persentase keuntungan tahunan yang Anda harapkan (misal: 7% untuk historis S&P 500).',
45
+ },
46
+ {
47
+ name: 'Sesuaikan cakrawala waktu',
48
+ text: 'Pilih berapa tahun Anda akan mempertahankan investasi untuk memvisualisasikan pertumbuhan eksponensial modal Anda.',
49
+ },
50
+ ];
51
+
52
+ const faqSchema: WithContext<FAQPage> = {
53
+ '@context': 'https://schema.org',
54
+ '@type': 'FAQPage',
55
+ mainEntity: faqData.map((item) => ({
56
+ '@type': 'Question',
57
+ name: item.question,
58
+ acceptedAnswer: { '@type': 'Answer', text: item.answer },
59
+ })),
60
+ };
61
+
62
+ const howToSchema: WithContext<HowTo> = {
63
+ '@context': 'https://schema.org',
64
+ '@type': 'HowTo',
65
+ name: title,
66
+ description,
67
+ step: howToData.map((step, i) => ({
68
+ '@type': 'HowToStep',
69
+ position: i + 1,
70
+ name: step.name,
71
+ text: step.text,
72
+ })),
73
+ };
74
+
75
+ const appSchema: WithContext<SoftwareApplication> = {
76
+ '@context': 'https://schema.org',
77
+ '@type': 'SoftwareApplication',
78
+ name: title,
79
+ description,
80
+ applicationCategory: 'FinanceApplication',
81
+ operatingSystem: 'All',
82
+ offers: { '@type': 'Offer', price: '0', priceCurrency: 'IDR' },
83
+ inLanguage: 'id',
84
+ };
85
+
86
+ export const content: ToolLocaleContent<CompoundInterestUI> = {
87
+ slug,
88
+ title,
89
+ description,
90
+ faqTitle: 'Pertanyaan Umum',
91
+ faq: faqData,
92
+ bibliographyTitle: 'Sumber dan Referensi',
93
+ bibliography: [
94
+ {
95
+ name: 'Investopedia: Compound Interest Definition',
96
+ url: 'https://www.investopedia.com/terms/c/compoundinterest.asp',
97
+ },
98
+ {
99
+ name: 'Investopedia: Rule of 72',
100
+ url: 'https://www.investopedia.com/terms/r/ruleof72.asp',
101
+ },
102
+ {
103
+ name: 'Khan Academy: Bunga dan Hutang',
104
+ url: 'https://id.khanacademy.org/economics-finance-domain/core-finance/interest-tutorial',
105
+ },
106
+ ],
107
+ howTo: howToData,
108
+ schemas: [faqSchema, howToSchema, appSchema],
109
+ seo: [
110
+ {
111
+ type: 'title',
112
+ text: 'Keajaiban Bunga Majemuk: Bangun Kekayaan Anda Secara Eksponensial',
113
+ level: 2,
114
+ },
115
+ {
116
+ type: 'paragraph',
117
+ html: 'Albert Einstein menyebutnya sebagai "keajaiban dunia kedelapan". <strong>Bunga majemuk</strong> adalah mekanisme paling kuat untuk menghasilkan kekayaan jangka panjang. Anda tidak perlu menjadi ahli keuangan untuk memanfaatkannya: Anda hanya butuh waktu, kesabaran, dan uang yang diinvestasikan.',
118
+ },
119
+ {
120
+ type: 'title',
121
+ text: 'Bunga Tunggal vs Majemuk: Metafora Bola Salju',
122
+ level: 3,
123
+ },
124
+ {
125
+ type: 'paragraph',
126
+ html: 'Bayangkan Anda memiliki bola salju di atas bukit. <strong>Bunga tunggal</strong> seperti mendorong bola itu ke bawah dan harus menambahkan salju secara manual setiap meternya agar besar. <strong>Bunga majemuk</strong> seperti membiarkan bola itu menggelinding sendiri: ia mengumpulkan salju dengan sendirinya, dan semakin besar ia tumbuh, semakin banyak permukaan yang dimilikinya untuk mengumpulkan <em>bahkan lebih banyak</em> salju di setiap putaran.',
127
+ },
128
+ {
129
+ type: 'comparative',
130
+ columns: 2,
131
+ items: [
132
+ {
133
+ title: 'Bunga Tunggal',
134
+ description: 'Bunga selalu dihitung berdasarkan modal awal saja.',
135
+ points: [
136
+ 'Rumus: Modal × Tingkat × Waktu',
137
+ 'Pertumbuhan linier dan terprediksi',
138
+ 'Digunakan dalam pinjaman jangka pendek',
139
+ 'Tanpa reinvestasi keuntungan',
140
+ ],
141
+ },
142
+ {
143
+ title: 'Bunga Majemuk',
144
+ description: 'Bunga ditambahkan ke modal dan menghasilkan bunga baru.',
145
+ highlight: true,
146
+ points: [
147
+ 'Rumus: Modal × (1 + Tingkat)^Waktu',
148
+ 'Pertumbuhan eksponensial yang dipercepat',
149
+ 'Dasar dari semua investasi jangka panjang',
150
+ 'Keuntungan Anda menghasilkan lebih banyak keuntungan',
151
+ ],
152
+ },
153
+ ],
154
+ },
155
+ {
156
+ type: 'paragraph',
157
+ html: 'Secara matematis, keuntungan Anda menghasilkan keuntungan baru. Di tahun-tahun awal tampaknya lambat, tetapi setelah melewati "titik balik", kurva akan melonjak secara vertikal. Di sinilah kekayaan sejati tercipta.',
158
+ },
159
+ {
160
+ type: 'title',
161
+ text: 'Mengapa waktu adalah sekutu terbesar Anda?',
162
+ level: 3,
163
+ },
164
+ {
165
+ type: 'paragraph',
166
+ html: 'Faktor yang paling menentukan bukanlah jumlah uang yang Anda investasikan, melainkan <strong>berapa tahun Anda membiarkannya tumbuh</strong>. Memulai 10 tahun lebih awal dapat menghasilkan kekayaan akhir 2 atau 3 kali lipat lebih besar, bahkan jika Anda menginvestasikan total uang yang lebih sedikit. Efek eksponensial ini adalah alasan mengapa investor muda memiliki keuntungan yang tidak tertandingi.',
167
+ },
168
+ {
169
+ type: 'tip',
170
+ title: 'Aturan 72',
171
+ html: '<p>Bagi 72 dengan imbal hasil tahunan Anda untuk mengetahui berapa tahun yang dibutuhkan untuk <strong>melipatgandakan uang Anda</strong>.</p><p><em>Contoh: Pada bunga 8%, Anda melipatgandakannya setiap 9 tahun (72/8 = 9).</em></p><p>Rumus ajaib ini berlaku untuk tingkat pengembalian berapa pun dan membantu Anda memperkirakan pertumbuhan investasi Anda dengan cepat.</p>',
172
+ },
173
+ {
174
+ type: 'tip',
175
+ title: 'Saran 2026',
176
+ html: '<p>Inflasi tetap menjadi faktor. Pastikan imbal hasil bersih Anda melebihi setidaknya 2-3% per tahun agar tidak kehilangan daya beli. Investasikan pada aset yang tumbuh lebih cepat daripada inflasi.</p>',
177
+ },
178
+ {
179
+ type: 'title',
180
+ text: 'Frekuensi Kapitalisasi: Apakah Berpengaruh?',
181
+ level: 3,
182
+ },
183
+ {
184
+ type: 'paragraph',
185
+ html: 'Bunga dapat dikapitalisasi secara tahunan, semesteran, triwulanan, bulanan, atau bahkan harian. Semakin <strong>sering kapitalisasi dilakukan</strong>, semakin besar efek majemuknya. Modal yang sama dengan tingkat yang sama akan tumbuh lebih besar jika bunga dimajemukkan secara bulanan dibandingkan secara tahunan.',
186
+ },
187
+ ],
188
+ ui: {
189
+ labelTitle: 'Simulator Investasi',
190
+ labelRealtime: 'Waktu nyata',
191
+ labelInitial: 'Kontribusi Awal',
192
+ labelMonthly: 'Kontribusi Bulanan',
193
+ labelRate: 'Hasil Tahunan',
194
+ labelYears: 'Tahun',
195
+ labelMyMoney: 'Uang Anda',
196
+ labelProfit: 'Keuntungan (Bunga)',
197
+ labelTotal: 'Total Terakumulasi',
198
+ labelYear: 'Tahun',
199
+ labelPrincipal: 'Uang Anda (Pokok)',
200
+ labelInterest: 'Bunga yang Dihasilkan',
201
+ insightPrefix: '[TIPS] Di tahun ke-',
202
+ insightSuffix:
203
+ ', bunga tahunan yang Anda hasilkan akan melampaui kontribusi Anda. Uang sudah bekerja lebih keras daripada Anda!',
204
+ currencySymbol: 'Rp',
205
+ currencyCode: 'IDR',
206
+ currencyLocale: 'id-ID',
207
+ },
208
+ };