@jjlmoya/utils-finance 1.24.0 → 1.26.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 (133) hide show
  1. package/package.json +1 -1
  2. package/src/tests/seo_parity.test.ts +68 -0
  3. package/src/tests/spanish_leakage.test.ts +153 -0
  4. package/src/tests/translation_copy.test.ts +124 -0
  5. package/src/tool/courtFeeCalculator/i18n/de.ts +23 -1
  6. package/src/tool/courtFeeCalculator/i18n/id.ts +23 -1
  7. package/src/tool/courtFeeCalculator/i18n/it.ts +23 -1
  8. package/src/tool/courtFeeCalculator/i18n/ja.ts +23 -1
  9. package/src/tool/courtFeeCalculator/i18n/ko.ts +23 -1
  10. package/src/tool/courtFeeCalculator/i18n/nl.ts +23 -1
  11. package/src/tool/courtFeeCalculator/i18n/pl.ts +23 -1
  12. package/src/tool/courtFeeCalculator/i18n/pt.ts +23 -1
  13. package/src/tool/courtFeeCalculator/i18n/ru.ts +23 -1
  14. package/src/tool/courtFeeCalculator/i18n/sv.ts +23 -1
  15. package/src/tool/courtFeeCalculator/i18n/tr.ts +23 -1
  16. package/src/tool/courtFeeCalculator/i18n/zh.ts +23 -1
  17. package/src/tool/debtSnowball/i18n/de.ts +22 -2
  18. package/src/tool/debtSnowball/i18n/id.ts +22 -2
  19. package/src/tool/debtSnowball/i18n/it.ts +22 -2
  20. package/src/tool/debtSnowball/i18n/ja.ts +22 -2
  21. package/src/tool/debtSnowball/i18n/ko.ts +22 -2
  22. package/src/tool/debtSnowball/i18n/nl.ts +22 -2
  23. package/src/tool/debtSnowball/i18n/pl.ts +22 -2
  24. package/src/tool/debtSnowball/i18n/pt.ts +22 -2
  25. package/src/tool/debtSnowball/i18n/ru.ts +22 -2
  26. package/src/tool/debtSnowball/i18n/sv.ts +22 -2
  27. package/src/tool/debtSnowball/i18n/tr.ts +22 -2
  28. package/src/tool/debtSnowball/i18n/zh.ts +22 -2
  29. package/src/tool/financialFreedom/i18n/de.ts +6 -1
  30. package/src/tool/financialFreedom/i18n/fr.ts +6 -1
  31. package/src/tool/financialFreedom/i18n/id.ts +6 -1
  32. package/src/tool/financialFreedom/i18n/it.ts +6 -1
  33. package/src/tool/financialFreedom/i18n/ja.ts +6 -1
  34. package/src/tool/financialFreedom/i18n/ko.ts +6 -1
  35. package/src/tool/financialFreedom/i18n/nl.ts +6 -1
  36. package/src/tool/financialFreedom/i18n/pl.ts +6 -1
  37. package/src/tool/financialFreedom/i18n/pt.ts +6 -1
  38. package/src/tool/financialFreedom/i18n/ru.ts +6 -1
  39. package/src/tool/financialFreedom/i18n/sv.ts +6 -1
  40. package/src/tool/financialFreedom/i18n/tr.ts +6 -1
  41. package/src/tool/financialFreedom/i18n/zh.ts +6 -1
  42. package/src/tool/ibanBicSwiftConverter/i18n/de.ts +3 -1
  43. package/src/tool/ibanBicSwiftConverter/i18n/id.ts +3 -1
  44. package/src/tool/ibanBicSwiftConverter/i18n/it.ts +3 -1
  45. package/src/tool/ibanBicSwiftConverter/i18n/ja.ts +3 -1
  46. package/src/tool/ibanBicSwiftConverter/i18n/ko.ts +3 -1
  47. package/src/tool/ibanBicSwiftConverter/i18n/nl.ts +3 -1
  48. package/src/tool/ibanBicSwiftConverter/i18n/pl.ts +3 -1
  49. package/src/tool/ibanBicSwiftConverter/i18n/pt.ts +3 -1
  50. package/src/tool/ibanBicSwiftConverter/i18n/ru.ts +3 -1
  51. package/src/tool/ibanBicSwiftConverter/i18n/sv.ts +3 -1
  52. package/src/tool/ibanBicSwiftConverter/i18n/tr.ts +3 -1
  53. package/src/tool/ibanBicSwiftConverter/i18n/zh.ts +3 -1
  54. package/src/tool/lateInterest/i18n/de.ts +4 -2
  55. package/src/tool/lateInterest/i18n/es.ts +4 -2
  56. package/src/tool/lateInterest/i18n/fr.ts +4 -2
  57. package/src/tool/lateInterest/i18n/id.ts +4 -2
  58. package/src/tool/lateInterest/i18n/it.ts +4 -2
  59. package/src/tool/lateInterest/i18n/ja.ts +4 -2
  60. package/src/tool/lateInterest/i18n/ko.ts +4 -2
  61. package/src/tool/lateInterest/i18n/nl.ts +4 -2
  62. package/src/tool/lateInterest/i18n/pl.ts +4 -2
  63. package/src/tool/lateInterest/i18n/pt.ts +4 -2
  64. package/src/tool/lateInterest/i18n/ru.ts +4 -2
  65. package/src/tool/lateInterest/i18n/sv.ts +4 -2
  66. package/src/tool/lateInterest/i18n/tr.ts +4 -2
  67. package/src/tool/lateInterest/i18n/zh.ts +4 -2
  68. package/src/tool/legalInterestRate/i18n/de.ts +3 -1
  69. package/src/tool/legalInterestRate/i18n/id.ts +3 -1
  70. package/src/tool/legalInterestRate/i18n/it.ts +3 -1
  71. package/src/tool/legalInterestRate/i18n/ja.ts +3 -1
  72. package/src/tool/legalInterestRate/i18n/ko.ts +3 -1
  73. package/src/tool/legalInterestRate/i18n/nl.ts +3 -1
  74. package/src/tool/legalInterestRate/i18n/pl.ts +3 -1
  75. package/src/tool/legalInterestRate/i18n/pt.ts +3 -1
  76. package/src/tool/legalInterestRate/i18n/ru.ts +3 -1
  77. package/src/tool/legalInterestRate/i18n/sv.ts +3 -1
  78. package/src/tool/legalInterestRate/i18n/tr.ts +3 -1
  79. package/src/tool/legalInterestRate/i18n/zh.ts +3 -1
  80. package/src/tool/lotteryOptimizer/i18n/de.ts +3 -1
  81. package/src/tool/lotteryOptimizer/i18n/id.ts +3 -1
  82. package/src/tool/lotteryOptimizer/i18n/it.ts +3 -1
  83. package/src/tool/lotteryOptimizer/i18n/ja.ts +3 -1
  84. package/src/tool/lotteryOptimizer/i18n/ko.ts +3 -1
  85. package/src/tool/lotteryOptimizer/i18n/nl.ts +3 -1
  86. package/src/tool/lotteryOptimizer/i18n/pl.ts +3 -1
  87. package/src/tool/lotteryOptimizer/i18n/pt.ts +3 -1
  88. package/src/tool/lotteryOptimizer/i18n/ru.ts +3 -1
  89. package/src/tool/lotteryOptimizer/i18n/sv.ts +3 -1
  90. package/src/tool/lotteryOptimizer/i18n/tr.ts +3 -1
  91. package/src/tool/lotteryOptimizer/i18n/zh.ts +3 -1
  92. package/src/tool/profitability/i18n/de.ts +6 -1
  93. package/src/tool/profitability/i18n/es.ts +3 -1
  94. package/src/tool/profitability/i18n/fr.ts +8 -2
  95. package/src/tool/profitability/i18n/id.ts +8 -2
  96. package/src/tool/profitability/i18n/it.ts +8 -2
  97. package/src/tool/profitability/i18n/ja.ts +8 -2
  98. package/src/tool/profitability/i18n/ko.ts +8 -2
  99. package/src/tool/profitability/i18n/nl.ts +8 -2
  100. package/src/tool/profitability/i18n/pl.ts +8 -2
  101. package/src/tool/profitability/i18n/pt.ts +8 -2
  102. package/src/tool/profitability/i18n/ru.ts +8 -2
  103. package/src/tool/profitability/i18n/sv.ts +8 -2
  104. package/src/tool/profitability/i18n/tr.ts +8 -2
  105. package/src/tool/profitability/i18n/zh.ts +8 -2
  106. package/src/tool/profitability/validation.ts +16 -0
  107. package/src/tool/rentIncreaseCalculator/i18n/de.ts +7 -2
  108. package/src/tool/rentIncreaseCalculator/i18n/id.ts +7 -2
  109. package/src/tool/rentIncreaseCalculator/i18n/it.ts +7 -2
  110. package/src/tool/rentIncreaseCalculator/i18n/ja.ts +7 -2
  111. package/src/tool/rentIncreaseCalculator/i18n/ko.ts +7 -2
  112. package/src/tool/rentIncreaseCalculator/i18n/nl.ts +7 -2
  113. package/src/tool/rentIncreaseCalculator/i18n/pl.ts +7 -2
  114. package/src/tool/rentIncreaseCalculator/i18n/pt.ts +7 -2
  115. package/src/tool/rentIncreaseCalculator/i18n/ru.ts +7 -2
  116. package/src/tool/rentIncreaseCalculator/i18n/sv.ts +7 -2
  117. package/src/tool/rentIncreaseCalculator/i18n/tr.ts +7 -2
  118. package/src/tool/rentIncreaseCalculator/i18n/zh.ts +7 -2
  119. package/src/tool/revolvingCardCalculator/i18n/de.ts +27 -3
  120. package/src/tool/revolvingCardCalculator/i18n/es.ts +24 -3
  121. package/src/tool/revolvingCardCalculator/i18n/fr.ts +27 -3
  122. package/src/tool/revolvingCardCalculator/i18n/id.ts +27 -3
  123. package/src/tool/revolvingCardCalculator/i18n/it.ts +27 -3
  124. package/src/tool/revolvingCardCalculator/i18n/ja.ts +27 -3
  125. package/src/tool/revolvingCardCalculator/i18n/ko.ts +27 -3
  126. package/src/tool/revolvingCardCalculator/i18n/nl.ts +27 -3
  127. package/src/tool/revolvingCardCalculator/i18n/pl.ts +27 -3
  128. package/src/tool/revolvingCardCalculator/i18n/pt.ts +27 -3
  129. package/src/tool/revolvingCardCalculator/i18n/ru.ts +27 -3
  130. package/src/tool/revolvingCardCalculator/i18n/sv.ts +27 -3
  131. package/src/tool/revolvingCardCalculator/i18n/tr.ts +27 -3
  132. package/src/tool/revolvingCardCalculator/i18n/zh.ts +27 -3
  133. package/src/tool/revolvingCardCalculator/validation.ts +16 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jjlmoya/utils-finance",
3
- "version": "1.24.0",
3
+ "version": "1.26.0",
4
4
  "type": "module",
5
5
  "main": "./src/index.ts",
6
6
  "types": "./src/index.ts",
@@ -0,0 +1,68 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { ALL_ENTRIES } from '../entries';
3
+ import type { KnownLocale } from '../types';
4
+
5
+ interface ExpectedCounts {
6
+ seo: number;
7
+ faq: number;
8
+ howTo: number;
9
+ }
10
+
11
+ const SPANISH_RICH_SEO_ENTRIES = new Set([
12
+ 'conversor-iban-bic-swift',
13
+ 'optimizador-loterias',
14
+ 'calculadora-subida-alquiler-ipc',
15
+ ]);
16
+
17
+ function countItems(arr: unknown[] | undefined): number {
18
+ return arr?.length ?? 0;
19
+ }
20
+
21
+ async function verifyLocaleParity(
22
+ entry: typeof ALL_ENTRIES[number],
23
+ loc: KnownLocale,
24
+ expected: ExpectedCounts,
25
+ ): Promise<void> {
26
+ const locContent = await entry.i18n[loc]?.();
27
+ expect(locContent, `Locale ${loc} missing content`).toBeDefined();
28
+
29
+ const locSeoCount = countItems(locContent?.seo);
30
+ const locFaqCount = countItems(locContent?.faq);
31
+ const locHowToCount = countItems(locContent?.howTo);
32
+
33
+ expect(
34
+ locSeoCount,
35
+ `Locale ${loc} SEO sections count (${locSeoCount}) must match EN (${expected.seo})`,
36
+ ).toBe(expected.seo);
37
+ expect(
38
+ locFaqCount,
39
+ `Locale ${loc} FAQ items count (${locFaqCount}) must match EN (${expected.faq})`,
40
+ ).toBe(expected.faq);
41
+ expect(
42
+ locHowToCount,
43
+ `Locale ${loc} HowTo steps count (${locHowToCount}) must match EN (${expected.howTo})`,
44
+ ).toBe(expected.howTo);
45
+ }
46
+
47
+ describe('SEO & i18n Structural Parity Suite', () => {
48
+ ALL_ENTRIES.forEach((entry) => {
49
+ describe(`Tool: ${entry.id}`, () => {
50
+ it('all 15 locales should have identical SEO section counts and types as English', async () => {
51
+ const enContent = await entry.i18n.en?.();
52
+ expect(enContent).toBeDefined();
53
+ const expected: ExpectedCounts = {
54
+ seo: countItems(enContent?.seo),
55
+ faq: countItems(enContent?.faq),
56
+ howTo: countItems(enContent?.howTo),
57
+ };
58
+
59
+ const locales = (Object.keys(entry.i18n) as KnownLocale[]).filter(
60
+ (loc) => !(loc === 'es' && SPANISH_RICH_SEO_ENTRIES.has(entry.id)),
61
+ );
62
+ for (const loc of locales) {
63
+ await verifyLocaleParity(entry, loc, expected);
64
+ }
65
+ });
66
+ });
67
+ });
68
+ });
@@ -0,0 +1,153 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_ENTRIES } from '../entries';
3
+
4
+ const STRUCTURAL_KEYS = new Set([
5
+ 'id',
6
+ 'slug',
7
+ 'url',
8
+ 'icon',
9
+ 'image',
10
+ 'imageUrl',
11
+ 'keywords',
12
+ 'category',
13
+ 'tags',
14
+ 'toolId',
15
+ 'type',
16
+ 'locale',
17
+ 'language',
18
+ 'direction',
19
+ ]);
20
+
21
+ const TRANSLATABLE_KEYS = [
22
+ 'title',
23
+ 'description',
24
+ 'faqTitle',
25
+ 'faq',
26
+ 'howTo',
27
+ 'seo',
28
+ 'schemas',
29
+ ] as const;
30
+
31
+ const SPANISH_MARKERS = [
32
+ ['sangre', /\bsangre\b/gi],
33
+ ['molino', /\bmolino\b/gi],
34
+ ['grano', /\bgrano\b/gi],
35
+ ['paladar', /\bpaladar\b/gi],
36
+ ['cuchillas', /\bcuchillas\b/gi],
37
+ ['trozos', /\btrozos\b/gi],
38
+ ['frescura', /\bfrescura\b/gi],
39
+ ['ingresa', /\bingresa\b/gi],
40
+ ['selecciona', /\bselecciona\b/gi],
41
+ ['herramienta', /\bherramienta\b/gi],
42
+ ['según', /\bsegún\b/gi],
43
+ ['después', /\bdespués\b/gi],
44
+ ['puedes', /\bpuedes\b/gi],
45
+ ['debes', /\bdebes\b/gi],
46
+ ['tus', /\btus\b/gi],
47
+ ['a la vez', /\ba la vez\b/gi],
48
+ ['los datos', /\blos datos\b/gi],
49
+ ['las opciones', /\blas opciones\b/gi],
50
+ ['el resultado', /\bel resultado\b/gi],
51
+ ['método de extracción', /\bmétodo de extracción\b/gi],
52
+ ['uniformidad del molino', /\buniformidad del molino\b/gi],
53
+ ['café recién tostado', /\bcafé recién tostado\b/gi],
54
+ ] as const;
55
+
56
+ type UnknownRecord = Record<string, unknown>;
57
+
58
+ function normalize(value: string): string {
59
+ return value
60
+ .replace(/<[^>]*>/g, ' ')
61
+ .replace(/&nbsp;/gi, ' ')
62
+ .replace(/\s+/g, ' ')
63
+ .trim()
64
+ .toLocaleLowerCase('es');
65
+ }
66
+
67
+ function isTechnicalInvariant(text: string): boolean {
68
+ return [
69
+ 'data:image/svg+xml;base64',
70
+ 'background-image: url',
71
+ '.layout-playground {',
72
+ 'const samplerate =',
73
+ ].some((pattern) => text.includes(pattern)) || (text.includes('presets') && text.includes('hz'));
74
+ }
75
+
76
+ function collectString(value: string, output: string[]): void {
77
+ const text = normalize(value);
78
+ if (!isTechnicalInvariant(text) && text.length >= 20) output.push(text);
79
+ }
80
+
81
+ function collectObject(value: UnknownRecord, output: string[]): void {
82
+ Object.entries(value).forEach(([childKey, childValue]) => {
83
+ collectText(childValue, output, childKey);
84
+ });
85
+ }
86
+
87
+ function collectText(value: unknown, output: string[], key?: string): void {
88
+ if (key && STRUCTURAL_KEYS.has(key)) return;
89
+ if (typeof value === 'string') return collectString(value, output);
90
+ if (Array.isArray(value)) return value.forEach((item) => collectText(item, output));
91
+ if (value && typeof value === 'object') collectObject(value as UnknownRecord, output);
92
+ }
93
+
94
+ async function loadText(loader: unknown): Promise<string[]> {
95
+ if (typeof loader !== 'function') return [];
96
+ const module = await (loader as () => Promise<unknown>)();
97
+ const output: string[] = [];
98
+ if (module && typeof module === 'object') {
99
+ const record = module as UnknownRecord;
100
+ for (const key of TRANSLATABLE_KEYS) {
101
+ collectText(record[key], output, key);
102
+ }
103
+ }
104
+ return output;
105
+ }
106
+
107
+ function findSpanishMarkers(text: string[]): string[] {
108
+ const corpus = text.join(' ');
109
+ return SPANISH_MARKERS.flatMap(([label, pattern]) =>
110
+ pattern.test(corpus) ? [label] : [],
111
+ );
112
+ }
113
+
114
+ function findCopiedFragments(spanish: string[], translated: string[]): string[] {
115
+ const corpus = translated.join(' ');
116
+ return spanish
117
+ .filter((fragment) => fragment.length >= 80 && corpus.includes(fragment))
118
+ .sort((a, b) => b.length - a.length)
119
+ .slice(0, 3);
120
+ }
121
+
122
+ describe('Locales must not contain copied Spanish content', () => {
123
+ for (const entry of ALL_ENTRIES) {
124
+ it(`${entry.id} has no untranslated Spanish blocks`, async () => {
125
+ const spanish = await loadText(entry.i18n.es);
126
+ const failures: string[] = [];
127
+
128
+ for (const [locale, loader] of Object.entries(entry.i18n)) {
129
+ if (locale === 'es') continue;
130
+
131
+ const translated = await loadText(loader);
132
+ const copiedFragments = findCopiedFragments(spanish, translated);
133
+ const markerHits = findSpanishMarkers(translated);
134
+
135
+ if (copiedFragments.length > 0 || markerHits.length >= 2) {
136
+ const details = [
137
+ copiedFragments.length > 0
138
+ ? `copied fragments: ${copiedFragments
139
+ .map((fragment) => JSON.stringify(fragment.slice(0, 120)))
140
+ .join(', ')}`
141
+ : '',
142
+ markerHits.length >= 2 ? `Spanish markers: ${markerHits.join(', ')}` : '',
143
+ ]
144
+ .filter(Boolean)
145
+ .join('; ');
146
+ failures.push(`${locale}: ${details}`);
147
+ }
148
+ }
149
+
150
+ expect(failures, failures.join('\n')).toEqual([]);
151
+ });
152
+ }
153
+ });
@@ -0,0 +1,124 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { ALL_ENTRIES } from '../entries';
3
+
4
+ const COPY_THRESHOLD = 0.9;
5
+
6
+ const STRUCTURAL_KEYS = new Set([
7
+ '@context',
8
+ '@type',
9
+ 'applicationCategory',
10
+ 'columns',
11
+ 'highlight',
12
+ 'icon',
13
+ 'level',
14
+ 'operatingSystem',
15
+ 'position',
16
+ 'positive',
17
+ 'price',
18
+ 'priceCurrency',
19
+ 'slug',
20
+ 'trend',
21
+ 'type',
22
+ 'url',
23
+ 'value',
24
+ 'variant',
25
+ ]);
26
+
27
+ function normalizeText(value: string): string {
28
+ return value
29
+ .replace(/<[^>]*>/g, ' ')
30
+ .replace(/&(?:amp|lt|gt|quot|apos|nbsp);/gi, ' ')
31
+ .replace(/[\u2018\u2019]/g, "'")
32
+ .replace(/[\u201c\u201d]/g, '"')
33
+ .replace(/\s+/g, ' ')
34
+ .trim()
35
+ .toLocaleLowerCase();
36
+ }
37
+
38
+ function collectText(value: unknown, path: string, parts: string[]): void {
39
+ if (typeof value === 'string') {
40
+ const normalized = normalizeText(value);
41
+ if (normalized.length >= 2) parts.push(normalized);
42
+ return;
43
+ }
44
+
45
+ if (Array.isArray(value)) {
46
+ value.forEach((item, index) => collectText(item, `${path}[${index}]`, parts));
47
+ return;
48
+ }
49
+
50
+ if (!value || typeof value !== 'object') return;
51
+
52
+ Object.entries(value).forEach(([key, child]) => {
53
+ if (STRUCTURAL_KEYS.has(key)) return;
54
+ collectText(child, `${path}.${key}`, parts);
55
+ });
56
+ }
57
+
58
+ function localeCorpus(content: unknown): string {
59
+ if (!content || typeof content !== 'object') return '';
60
+
61
+ const record = content as Record<string, unknown>;
62
+ const parts: string[] = [];
63
+ collectText(record.title, 'title', parts);
64
+ collectText(record.description, 'description', parts);
65
+ collectText(record.faqTitle, 'faqTitle', parts);
66
+ collectText(record.faq, 'faq', parts);
67
+ collectText(record.seo, 'seo', parts);
68
+ collectText(record.schemas, 'schemas', parts);
69
+ return parts.join(' ');
70
+ }
71
+
72
+ function tokenCounts(text: string): Map<string, number> {
73
+ const counts = new Map<string, number>();
74
+ for (const token of text.match(/[\p{L}\p{N}]+/gu) ?? []) {
75
+ counts.set(token, (counts.get(token) ?? 0) + 1);
76
+ }
77
+ return counts;
78
+ }
79
+
80
+ function copySimilarity(left: string, right: string): number {
81
+ const leftCounts = tokenCounts(left);
82
+ const rightCounts = tokenCounts(right);
83
+ const leftTotal = [...leftCounts.values()].reduce((sum, count) => sum + count, 0);
84
+ const rightTotal = [...rightCounts.values()].reduce((sum, count) => sum + count, 0);
85
+ if (leftTotal === 0 || rightTotal === 0) return 0;
86
+
87
+ let shared = 0;
88
+ for (const [token, count] of leftCounts) {
89
+ shared += Math.min(count, rightCounts.get(token) ?? 0);
90
+ }
91
+ return (2 * shared) / (leftTotal + rightTotal);
92
+ }
93
+
94
+ describe('Locales must not copy another locale wholesale', () => {
95
+ ALL_ENTRIES.forEach((entry) => {
96
+ it(`${entry.id} is not at least ${COPY_THRESHOLD * 100}% identical to another locale`, async () => {
97
+ const corpora = new Map<string, string>();
98
+
99
+ for (const [locale, loader] of Object.entries(entry.i18n)) {
100
+ if (!loader) continue;
101
+ corpora.set(locale, localeCorpus(await loader()));
102
+ }
103
+
104
+ const locales = [...corpora.keys()];
105
+ const violations: string[] = [];
106
+
107
+ for (let leftIndex = 0; leftIndex < locales.length; leftIndex += 1) {
108
+ for (let rightIndex = leftIndex + 1; rightIndex < locales.length; rightIndex += 1) {
109
+ const left = locales[leftIndex];
110
+ const right = locales[rightIndex];
111
+ const similarity = copySimilarity(corpora.get(left) ?? '', corpora.get(right) ?? '');
112
+
113
+ if (similarity >= COPY_THRESHOLD) {
114
+ violations.push(`${left} ↔ ${right}: ${(similarity * 100).toFixed(1)}%`);
115
+ }
116
+ }
117
+ }
118
+
119
+ expect(violations, `Locale copy threshold exceeded in ${entry.id}`).toEqual([]);
120
+ });
121
+ });
122
+ });
123
+
124
+
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: 'Der <strong>Gerichtskostenrechner</strong> ist ein technisches Hilfsmittel zur präzisen Bestimmung der fiskalischen Kosten der rechtsprechenden Gewalt in Spanien gemäß dem Gesetz 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
64
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
65
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
66
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
67
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
68
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
69
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
70
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
71
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
72
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
73
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
74
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
75
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
76
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
77
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
78
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
79
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
80
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
81
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },
82
+ { type: 'paragraph', html: "Deuten Sie das Ergebnis gemeinsam mit den angezeigten Annahmen, bevor Sie es verwenden." },
83
+ { type: 'paragraph', html: "Prüfen Sie Eingaben, Einheiten, Rundung, Datum und Recht, weil jedes Element die Schätzung verändern kann." },
84
+ { type: 'paragraph', html: "Nutzen Sie das Ergebnis zur Planung und zum Szenarienvergleich; es ersetzt keine offizielle Berechnung oder Fachberatung." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: '<strong>Kalkulator Biaya Pengadilan</strong> adalah alat teknis yang dirancang untuk menentukan secara akurat biaya fiskal dari kekuasaan kehakiman di Spanyol sesuai dengan Undang-Undang 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
64
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
65
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
66
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
67
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
68
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
69
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
70
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
71
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
72
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
73
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
74
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
75
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
76
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
77
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
78
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
79
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
80
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
81
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },
82
+ { type: 'paragraph', html: "Tafsirkan hasil bersama asumsi yang ditampilkan kalkulator sebelum menggunakannya." },
83
+ { type: 'paragraph', html: "Periksa input, satuan, pembulatan, tanggal, dan yurisdiksi karena semuanya dapat mengubah estimasi." },
84
+ { type: 'paragraph', html: "Gunakan hasil untuk perencanaan dan perbandingan skenario; hasil ini bukan pengganti perhitungan resmi atau nasihat ahli." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: 'Il <strong>Calcolatore di Tasse Giudiziarie</strong> è uno strumento tecnico progettato per determinare con precisione il costo fiscale della potestà giurisdizionale in Spagna secondo la Legge 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
64
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
65
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
66
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
67
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
68
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
69
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
70
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
71
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
72
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
73
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
74
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
75
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
76
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
77
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
78
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
79
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
80
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
81
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },
82
+ { type: 'paragraph', html: "Interpreta il risultato insieme alle ipotesi mostrate dal calcolatore prima di utilizzarlo." },
83
+ { type: 'paragraph', html: "Controlla dati, unità, arrotondamento, data e giurisdizione, perché ciascun elemento può cambiare la stima." },
84
+ { type: 'paragraph', html: "Usa il risultato per pianificare e confrontare scenari; non sostituisce un calcolo ufficiale o una consulenza professionale." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: '<strong>裁判手数料計算機</strong>は、スペインの法律10/2012に基づき、裁判権行使に伴う税負担額を正確に算出するために設計された技術ツールです。',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
64
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
65
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
66
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
67
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
68
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
69
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
70
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
71
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
72
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
73
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
74
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
75
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
76
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
77
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
78
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
79
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
80
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
81
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },
82
+ { type: 'paragraph', html: "利用する前に、計算機が示す前提条件と結果を一緒に確認してください。" },
83
+ { type: 'paragraph', html: "入力値、単位、丸め、日付、地域を確認してください。どれも推定結果を変える可能性があります。" },
84
+ { type: 'paragraph', html: "結果は計画や条件比較に使えますが、公式計算や専門家の助言の代わりにはなりません。" },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: '<strong>법원 수수료 계산기</strong>는 법률 10/2012에 따라 스페인 사법권 행사의 조세 비용을 정확하게 결정하기 위해 설계된 기술적 도구입니다.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
64
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
65
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
66
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
67
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
68
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
69
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
70
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
71
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
72
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
73
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
74
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
75
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
76
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
77
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
78
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
79
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
80
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
81
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },
82
+ { type: 'paragraph', html: "사용하기 전에 계산기가 표시한 가정과 결과를 함께 확인하세요." },
83
+ { type: 'paragraph', html: "입력값, 단위, 반올림, 날짜와 관할을 확인하세요. 모두 추정값을 바꿀 수 있습니다." },
84
+ { type: 'paragraph', html: "결과는 계획과 시나리오 비교에 사용할 수 있지만 공식 계산이나 전문가 조언을 대신하지 않습니다." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: 'De <strong>Gerechtskosten Calculator</strong> is een technisch hulpmiddel dat is ontworpen om nauwkeurig de fiscale kosten van de rechtsmacht in Spanje te bepalen volgens Wet 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
64
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
65
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
66
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
67
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
68
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
69
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
70
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
71
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
72
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
73
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
74
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
75
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
76
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
77
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
78
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
79
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
80
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
81
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },
82
+ { type: 'paragraph', html: "Interpreteer het resultaat samen met de aannames van de calculator voordat u het gebruikt." },
83
+ { type: 'paragraph', html: "Controleer invoer, eenheden, afronding, datum en jurisdictie, want elk onderdeel kan de schatting wijzigen." },
84
+ { type: 'paragraph', html: "Gebruik het resultaat voor planning en scenariokeuze; het vervangt geen officiële berekening of professioneel advies." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: '<strong>Kalkulator Opłat Sądowych</strong> to narzędzie techniczne zaprojektowane w celu precyzyjnego określenia kosztów fiskalnych sprawowania władzy sądowniczej w Hiszpanii zgodnie z ustawą 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
64
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
65
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
66
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
67
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
68
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
69
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
70
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
71
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
72
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
73
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
74
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
75
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
76
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
77
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
78
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
79
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
80
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
81
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },
82
+ { type: 'paragraph', html: "Przed użyciem zinterpretuj wynik razem z założeniami pokazanymi przez kalkulator." },
83
+ { type: 'paragraph', html: "Sprawdź dane, jednostki, zaokrąglenie, datę i jurysdykcję, ponieważ każdy element może zmienić szacunek." },
84
+ { type: 'paragraph', html: "Używaj wyniku do planowania i porównywania scenariuszy; nie zastępuje obliczeń urzędowych ani porady specjalisty." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',
@@ -59,7 +59,29 @@ const seoData = [
59
59
  type: 'paragraph',
60
60
  html: 'A <strong>Calculadora de Taxas Judiciais</strong> é uma ferramenta técnica concebida para determinar com precisão o custo fiscal da atividade jurisdicional em Espanha segundo a Lei 10/2012.',
61
61
  },
62
- ];
62
+
63
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
64
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
65
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
66
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
67
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
68
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
69
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
70
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
71
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
72
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
73
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
74
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
75
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
76
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
77
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
78
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
79
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
80
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
81
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },
82
+ { type: 'paragraph', html: "Interprete o resultado juntamente com as premissas apresentadas pela calculadora antes de o utilizar." },
83
+ { type: 'paragraph', html: "Confirme dados, unidades, arredondamento, data e jurisdição, pois cada elemento pode alterar a estimativa." },
84
+ { type: 'paragraph', html: "Use o resultado para planear e comparar cenários; não substitui um cálculo oficial nem aconselhamento profissional." },];
63
85
 
64
86
  const faqSchema: WithContext<FAQPage> = {
65
87
  '@context': 'https://schema.org',