@cavuno/board 3.2.0 → 4.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.
- package/CHANGELOG.md +433 -0
- package/README.md +6 -2
- package/dist/{_spec-nkWAraS0.d.mts → _spec-BZDv2dc5.d.mts} +100 -68
- package/dist/{_spec-nkWAraS0.d.ts → _spec-BZDv2dc5.d.ts} +100 -68
- package/dist/bin.mjs +7 -2
- package/dist/{blog-iV9XEIGf.d.ts → blog-BHx6iH3Z.d.ts} +1 -1
- package/dist/{blog-DGBHmExV.d.mts → blog-DyDvyQrI.d.mts} +1 -1
- package/dist/{board-JhdUWIoq.d.ts → board-BF3Qao6J.d.mts} +7 -6
- package/dist/{board-BJbXd_9_.d.mts → board-BTEZ0uiI.d.ts} +7 -6
- package/dist/breadcrumbs-SGaWth6C.d.ts +372 -0
- package/dist/breadcrumbs-icxRVydh.d.mts +372 -0
- package/dist/chunk-BGKNXG4H.mjs +146 -0
- package/dist/{chunk-NXY3OWGF.mjs → chunk-T2KC6XGV.mjs} +1 -1
- package/dist/{chunk-STE77MAT.mjs → chunk-YEAQ3JEI.mjs} +143 -134
- package/dist/doctor.js +1 -1
- package/dist/doctor.mjs +1 -1
- package/dist/filters.d.mts +25 -20
- package/dist/filters.d.ts +25 -20
- package/dist/filters.js +3 -692
- package/dist/filters.mjs +3 -29
- package/dist/format.d.mts +57 -231
- package/dist/format.d.ts +57 -231
- package/dist/format.js +327 -968
- package/dist/format.mjs +207 -264
- package/dist/go.js +11 -1
- package/dist/go.mjs +1 -1
- package/dist/index.d.mts +67 -76
- package/dist/index.d.ts +67 -76
- package/dist/index.js +45 -36
- package/dist/index.mjs +45 -36
- package/dist/{jobs-BiEvL9d7.d.ts → jobs-BX6d_sdd.d.mts} +4 -4
- package/dist/{jobs-lVvKCBCU.d.mts → jobs-BbLJfz1z.d.ts} +4 -4
- package/dist/paths.d.mts +89 -1
- package/dist/paths.d.ts +89 -1
- package/dist/paths.js +62 -14
- package/dist/paths.mjs +11 -3
- package/dist/route-contract.js +23 -13
- package/dist/route-contract.mjs +2 -2
- package/dist/{search-CCyFWPi3.d.mts → search-7B-mVRNA.d.ts} +14 -3
- package/dist/{search-CyRvVMUp.d.ts → search-OKrdAHv8.d.mts} +14 -3
- package/dist/seo.d.mts +30 -29
- package/dist/seo.d.ts +30 -29
- package/dist/seo.js +197 -718
- package/dist/seo.mjs +26 -24
- package/dist/server.d.mts +5 -5
- package/dist/server.d.ts +5 -5
- package/dist/sitemap.d.mts +5 -5
- package/dist/sitemap.d.ts +5 -5
- package/dist/sitemap.js +25 -14
- package/dist/sitemap.mjs +3 -2
- package/dist/skills.js +1 -1
- package/dist/skills.mjs +1 -1
- package/dist/suggest.d.mts +2 -2
- package/dist/suggest.d.ts +2 -2
- package/dist/suggest.js +17 -3
- package/dist/suggest.mjs +17 -3
- package/dist/well-known.js +23 -13
- package/dist/well-known.mjs +2 -2
- package/package.json +2 -4
- package/skills/cavuno-board-api-client/SKILL.md +42 -0
- package/skills/cavuno-board-applications/SKILL.md +4 -1
- package/skills/cavuno-board-filters/SKILL.md +11 -12
- package/skills/cavuno-board-format/SKILL.md +68 -56
- package/skills/cavuno-board-i18n/SKILL.md +102 -21
- package/skills/cavuno-board-search-suggestions/SKILL.md +8 -3
- package/skills/cavuno-board-seo/BLOG.md +4 -1
- package/skills/cavuno-board-seo/JOB_AND_BREADCRUMBS.md +5 -2
- package/skills/cavuno-board-seo/LISTING.md +35 -12
- package/skills/cavuno-board-seo/SALARY.md +83 -18
- package/skills/cavuno-board-seo/SKILL.md +3 -2
- package/skills/manifest.json +3 -3
- package/dist/breadcrumbs-1hMjcsqA.d.mts +0 -171
- package/dist/breadcrumbs-DexU07TT.d.ts +0 -171
- package/dist/chunk-IJCJ26SQ.mjs +0 -596
- package/dist/chunk-L7ZOYWHW.mjs +0 -98
- package/dist/chunk-LUGJWOT7.mjs +0 -96
- package/dist/ui-copy-CCkz6CVF.d.mts +0 -230
- package/dist/ui-copy-CCkz6CVF.d.ts +0 -230
package/dist/format.js
CHANGED
|
@@ -21,76 +21,99 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var format_exports = {};
|
|
22
22
|
__export(format_exports, {
|
|
23
23
|
COUNTRY_CODES: () => COUNTRY_CODES,
|
|
24
|
-
PUBLIC_LABEL_GROUPS: () => PUBLIC_LABEL_GROUPS,
|
|
25
24
|
buildJobBreadcrumbs: () => buildJobBreadcrumbs,
|
|
26
|
-
cardLocationLabel: () => cardLocationLabel,
|
|
27
|
-
companyIntro: () => companyIntro,
|
|
28
25
|
countryOptions: () => countryOptions,
|
|
29
|
-
fieldLabel: () => fieldLabel,
|
|
30
26
|
formatDate: () => formatDate,
|
|
31
27
|
formatMonthYear: () => formatMonthYear,
|
|
32
28
|
formatPublishedRelativeDate: () => formatPublishedRelativeDate,
|
|
33
29
|
formatSalaryRange: () => formatSalaryRange,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
fullJobToCard: () => fullJobToCard,
|
|
37
|
-
getSalaryLexicon: () => getSalaryLexicon,
|
|
38
|
-
locationLabel: () => locationLabel,
|
|
30
|
+
formatSalaryStat: () => formatSalaryStat,
|
|
31
|
+
formatSalaryStatRange: () => formatSalaryStatRange,
|
|
39
32
|
normalizeWebsiteUrl: () => normalizeWebsiteUrl,
|
|
40
|
-
resolveCustomFieldDisplay: () => resolveCustomFieldDisplay
|
|
41
|
-
uiCopy: () => uiCopy
|
|
33
|
+
resolveCustomFieldDisplay: () => resolveCustomFieldDisplay
|
|
42
34
|
});
|
|
43
35
|
module.exports = __toCommonJS(format_exports);
|
|
44
36
|
|
|
37
|
+
// src/format/locale.ts
|
|
38
|
+
function normalizeLocale(language) {
|
|
39
|
+
const trimmed = language.trim();
|
|
40
|
+
if (!trimmed) return null;
|
|
41
|
+
try {
|
|
42
|
+
const candidate = trimmed.replace(/_/g, "-");
|
|
43
|
+
const [canonical] = Intl.getCanonicalLocales(candidate);
|
|
44
|
+
if (!canonical) return null;
|
|
45
|
+
const [supported] = Intl.NumberFormat.supportedLocalesOf([canonical]);
|
|
46
|
+
return supported ?? null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
45
52
|
// src/format/dates.ts
|
|
46
|
-
var
|
|
53
|
+
var rtfShortCache = /* @__PURE__ */ new Map();
|
|
54
|
+
var rtfNowCache = /* @__PURE__ */ new Map();
|
|
47
55
|
var dtfCache = /* @__PURE__ */ new Map();
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
let rtf =
|
|
56
|
+
var monthYearCache = /* @__PURE__ */ new Map();
|
|
57
|
+
function getShortRelativeTimeFormat(language) {
|
|
58
|
+
let rtf = rtfShortCache.get(language);
|
|
51
59
|
if (!rtf) {
|
|
52
|
-
rtf = new Intl.RelativeTimeFormat(
|
|
60
|
+
rtf = new Intl.RelativeTimeFormat(language, {
|
|
53
61
|
numeric: "always",
|
|
54
|
-
style: "
|
|
62
|
+
style: "short"
|
|
63
|
+
});
|
|
64
|
+
rtfShortCache.set(language, rtf);
|
|
65
|
+
}
|
|
66
|
+
return rtf;
|
|
67
|
+
}
|
|
68
|
+
function getNowRelativeTimeFormat(language) {
|
|
69
|
+
let rtf = rtfNowCache.get(language);
|
|
70
|
+
if (!rtf) {
|
|
71
|
+
rtf = new Intl.RelativeTimeFormat(language, {
|
|
72
|
+
numeric: "auto",
|
|
73
|
+
style: "short"
|
|
55
74
|
});
|
|
56
|
-
|
|
75
|
+
rtfNowCache.set(language, rtf);
|
|
57
76
|
}
|
|
58
77
|
return rtf;
|
|
59
78
|
}
|
|
60
79
|
function getDateTimeFormat(language) {
|
|
61
|
-
|
|
62
|
-
let dtf = dtfCache.get(key);
|
|
80
|
+
let dtf = dtfCache.get(language);
|
|
63
81
|
if (!dtf) {
|
|
64
|
-
dtf = new Intl.DateTimeFormat(
|
|
82
|
+
dtf = new Intl.DateTimeFormat(language, {
|
|
65
83
|
dateStyle: "medium",
|
|
66
84
|
timeZone: "UTC"
|
|
67
85
|
});
|
|
68
|
-
dtfCache.set(
|
|
86
|
+
dtfCache.set(language, dtf);
|
|
87
|
+
}
|
|
88
|
+
return dtf;
|
|
89
|
+
}
|
|
90
|
+
function getMonthYearFormat(language) {
|
|
91
|
+
let dtf = monthYearCache.get(language);
|
|
92
|
+
if (!dtf) {
|
|
93
|
+
dtf = new Intl.DateTimeFormat(language, {
|
|
94
|
+
month: "short",
|
|
95
|
+
year: "numeric",
|
|
96
|
+
timeZone: "UTC"
|
|
97
|
+
});
|
|
98
|
+
monthYearCache.set(language, dtf);
|
|
69
99
|
}
|
|
70
100
|
return dtf;
|
|
71
101
|
}
|
|
72
|
-
function
|
|
102
|
+
function formatShort(value, unit, language) {
|
|
73
103
|
try {
|
|
74
|
-
return
|
|
104
|
+
return getShortRelativeTimeFormat(language).format(value, unit);
|
|
75
105
|
} catch {
|
|
76
|
-
|
|
77
|
-
year: "y",
|
|
78
|
-
month: "mo",
|
|
79
|
-
week: "w",
|
|
80
|
-
day: "d",
|
|
81
|
-
hour: "h",
|
|
82
|
-
minute: "m",
|
|
83
|
-
second: "s"
|
|
84
|
-
};
|
|
85
|
-
return `${Math.abs(value)}${shortMap[unit] ?? unit}`;
|
|
106
|
+
return null;
|
|
86
107
|
}
|
|
87
108
|
}
|
|
88
109
|
function formatDate(locale, value) {
|
|
89
110
|
if (!value) return null;
|
|
90
111
|
const parsed = new Date(value);
|
|
91
112
|
if (Number.isNaN(parsed.getTime())) return null;
|
|
113
|
+
const normalized = normalizeLocale(locale);
|
|
114
|
+
if (!normalized) return null;
|
|
92
115
|
try {
|
|
93
|
-
return getDateTimeFormat(
|
|
116
|
+
return getDateTimeFormat(normalized).format(parsed);
|
|
94
117
|
} catch {
|
|
95
118
|
return parsed.toISOString().split("T")[0];
|
|
96
119
|
}
|
|
@@ -99,6 +122,8 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
|
|
|
99
122
|
if (!value) return null;
|
|
100
123
|
const parsed = new Date(value);
|
|
101
124
|
if (Number.isNaN(parsed.getTime())) return null;
|
|
125
|
+
const normalized = normalizeLocale(locale);
|
|
126
|
+
if (!normalized) return null;
|
|
102
127
|
const diff = referenceNowMs - parsed.getTime();
|
|
103
128
|
const absDiff = Math.abs(diff);
|
|
104
129
|
const minute = 60 * 1e3;
|
|
@@ -108,7 +133,11 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
|
|
|
108
133
|
const month = 30 * day;
|
|
109
134
|
const year = 365 * day;
|
|
110
135
|
if (absDiff < minute) {
|
|
111
|
-
|
|
136
|
+
try {
|
|
137
|
+
return getNowRelativeTimeFormat(normalized).format(0, "second");
|
|
138
|
+
} catch {
|
|
139
|
+
return formatShort(0, "second", normalized);
|
|
140
|
+
}
|
|
112
141
|
}
|
|
113
142
|
const units = [
|
|
114
143
|
{ unit: "year", ms: year },
|
|
@@ -122,435 +151,212 @@ function formatPublishedRelativeDate(locale, value, referenceNowMs = Date.now())
|
|
|
122
151
|
if (absDiff >= candidate.ms) {
|
|
123
152
|
const valueInUnit = Math.round(absDiff / candidate.ms);
|
|
124
153
|
const direction2 = diff >= 0 ? -valueInUnit : valueInUnit;
|
|
125
|
-
return
|
|
154
|
+
return formatShort(direction2, candidate.unit, normalized);
|
|
126
155
|
}
|
|
127
156
|
}
|
|
128
157
|
const seconds = Math.round(absDiff / 1e3);
|
|
129
158
|
const direction = diff >= 0 ? -seconds : seconds;
|
|
130
|
-
return
|
|
159
|
+
return formatShort(direction, "second", normalized);
|
|
131
160
|
}
|
|
132
161
|
function formatMonthYear(locale, dateStr) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
per_week: "Weekly",
|
|
146
|
-
per_day: "Daily",
|
|
147
|
-
per_hour: "Hourly"
|
|
148
|
-
},
|
|
149
|
-
rangePrefix: { from: "From", upTo: "Up to" },
|
|
150
|
-
seniority: {
|
|
151
|
-
entry_level: "Entry level",
|
|
152
|
-
associate: "Associate",
|
|
153
|
-
mid_level: "Mid-level",
|
|
154
|
-
senior: "Senior",
|
|
155
|
-
lead: "Lead",
|
|
156
|
-
principal: "Principal",
|
|
157
|
-
director: "Director",
|
|
158
|
-
executive: "Executive"
|
|
159
|
-
},
|
|
160
|
-
frames: {
|
|
161
|
-
entitySalariesTitle: ({ entity, range }) => `${entity} Salaries (${range}/yr)`,
|
|
162
|
-
salariesInPlaceTitle: ({ place, range }) => `Salaries in ${place} (${range}/yr)`,
|
|
163
|
-
salariesInPlaceTitleNoRange: ({ place }) => `Salaries in ${place}`,
|
|
164
|
-
entitySalariesInPlaceTitle: ({ entity, place, range }) => `${entity} Salaries in ${place} (${range}/yr)`,
|
|
165
|
-
companySalariesTitle: ({ company, range }) => `${company} Salaries${range ? ` (${range}/yr)` : ""}`,
|
|
166
|
-
companyCategorySalariesTitle: ({ company, category, range }) => `${company} ${category} Salaries${range ? ` (${range}/yr)` : ""}`,
|
|
167
|
-
professionalsEarn: ({ entity, range, count }) => `${entity} professionals earn ${range}/yr on average across ${count} job postings.`,
|
|
168
|
-
rolesPay: ({ entity, range, count }) => `${entity} roles pay ${range}/yr on average across ${count} job postings.`,
|
|
169
|
-
professionalsInLocationEarn: ({ entity, place, range, count }) => `${entity} professionals in ${place} earn ${range}/yr on average across ${count} job postings.`,
|
|
170
|
-
seniorityRange: ({ lowLabel, lowAmount, highLabel, highAmount }) => `${lowLabel} roles start at ${lowAmount}, ${highLabel} roles reach ${highAmount}.`,
|
|
171
|
-
seniorityRangeWhile: ({ lowLabel, lowAmount, highLabel, highAmount }) => `${lowLabel} salaries start at ${lowAmount}, while ${highLabel} roles reach ${highAmount}.`,
|
|
172
|
-
compareAcrossTopPaying: ({ count }) => `Compare across ${count} top-paying ${count === 1 ? "company" : "companies"}.`,
|
|
173
|
-
compareAcrossTop: ({ count }) => `Compare across ${count} top ${count === 1 ? "company" : "companies"}.`,
|
|
174
|
-
boardWideAverage: ({ entity, range }) => `Board-wide ${entity} average: ${range}/yr.`,
|
|
175
|
-
averageSalaryInCity: ({ place, range, count }) => `Average salary in ${place} is ${range}/yr across ${count} jobs. Browse salary data by title and skill.`,
|
|
176
|
-
browseLocationsInRegion: ({ count, place }) => `Browse salary data across ${count} locations in ${place}. Compare salaries by title, skill, and company.`,
|
|
177
|
-
companyPays: ({ company, range, count }) => `${company} pays ${range}/yr on average across ${count} job postings.`,
|
|
178
|
-
breakdownAcrossCategories: ({ count }) => `Breakdown across ${count} job ${count === 1 ? "category" : "categories"}.`,
|
|
179
|
-
compareWithSimilar: ({ count }) => `Compare with ${count} similar ${count === 1 ? "company" : "companies"}.`,
|
|
180
|
-
salaryInfoFor: ({ company, board }) => `Salary information for ${company} on ${board}.`,
|
|
181
|
-
categoryRolesAtCompanyPay: ({ category, company, range, count }) => `${category} roles at ${company} pay ${range}/yr based on ${count} job postings.`,
|
|
182
|
-
categorySalaryDataFor: ({ category, company, board }) => `${category} salary data for ${company} on ${board}.`
|
|
183
|
-
}
|
|
184
|
-
};
|
|
185
|
-
var DE = {
|
|
186
|
-
timeframe: {
|
|
187
|
-
per_year: "pro Jahr",
|
|
188
|
-
per_month: "pro Monat",
|
|
189
|
-
per_week: "pro Woche",
|
|
190
|
-
per_day: "pro Tag",
|
|
191
|
-
per_hour: "pro Stunde"
|
|
192
|
-
},
|
|
193
|
-
rangePrefix: { from: "ab", upTo: "bis zu" },
|
|
194
|
-
seniority: {
|
|
195
|
-
entry_level: "Entry-Level",
|
|
196
|
-
associate: "Associate",
|
|
197
|
-
mid_level: "Mid-Level",
|
|
198
|
-
senior: "Senior",
|
|
199
|
-
lead: "Lead",
|
|
200
|
-
principal: "Principal",
|
|
201
|
-
director: "Director",
|
|
202
|
-
executive: "F\xFChrungskraft"
|
|
203
|
-
},
|
|
204
|
-
frames: {
|
|
205
|
-
entitySalariesTitle: ({ entity, range }) => `${entity} Geh\xE4lter (${range}/Jahr)`,
|
|
206
|
-
salariesInPlaceTitle: ({ place, range }) => `Geh\xE4lter in ${place} (${range}/Jahr)`,
|
|
207
|
-
salariesInPlaceTitleNoRange: ({ place }) => `Geh\xE4lter in ${place}`,
|
|
208
|
-
entitySalariesInPlaceTitle: ({ entity, place, range }) => `${entity} Geh\xE4lter in ${place} (${range}/Jahr)`,
|
|
209
|
-
companySalariesTitle: ({ company, range }) => `${company} Geh\xE4lter${range ? ` (${range}/Jahr)` : ""}`,
|
|
210
|
-
companyCategorySalariesTitle: ({ company, category, range }) => `${company} ${category} Geh\xE4lter${range ? ` (${range}/Jahr)` : ""}`,
|
|
211
|
-
professionalsEarn: ({ entity, range, count }) => `${entity}-Fachkr\xE4fte verdienen durchschnittlich ${range}/Jahr, basierend auf ${count} Stellenanzeigen.`,
|
|
212
|
-
rolesPay: ({ entity, range, count }) => `${entity}-Positionen werden mit durchschnittlich ${range}/Jahr verg\xFCtet, basierend auf ${count} Stellenanzeigen.`,
|
|
213
|
-
professionalsInLocationEarn: ({ entity, place, range, count }) => `${entity}-Fachkr\xE4fte in ${place} verdienen durchschnittlich ${range}/Jahr, basierend auf ${count} Stellenanzeigen.`,
|
|
214
|
-
seniorityRange: ({ lowLabel, lowAmount, highLabel, highAmount }) => `${lowLabel}-Positionen beginnen bei ${lowAmount}, ${highLabel}-Positionen erreichen ${highAmount}.`,
|
|
215
|
-
seniorityRangeWhile: ({ lowLabel, lowAmount, highLabel, highAmount }) => `${lowLabel}-Geh\xE4lter beginnen bei ${lowAmount}, w\xE4hrend ${highLabel}-Positionen ${highAmount} erreichen.`,
|
|
216
|
-
compareAcrossTopPaying: ({ count }) => `Vergleichen Sie ${count} ${count === 1 ? "bestzahlendes" : "bestzahlende"} Unternehmen.`,
|
|
217
|
-
compareAcrossTop: ({ count }) => `Vergleichen Sie ${count} Top-Unternehmen.`,
|
|
218
|
-
boardWideAverage: ({ entity, range }) => `Gesamtdurchschnitt ${entity}: ${range}/Jahr.`,
|
|
219
|
-
averageSalaryInCity: ({ place, range, count }) => `Das Durchschnittsgehalt in ${place} betr\xE4gt ${range}/Jahr, basierend auf ${count} Stellen. Gehaltsdaten nach Titel und F\xE4higkeiten durchsuchen.`,
|
|
220
|
-
browseLocationsInRegion: ({ count, place }) => `Gehaltsdaten f\xFCr ${count} Standorte in ${place} durchsuchen. Geh\xE4lter nach Titel, F\xE4higkeiten und Unternehmen vergleichen.`,
|
|
221
|
-
companyPays: ({ company, range, count }) => `${company} zahlt durchschnittlich ${range}/Jahr, basierend auf ${count} Stellenanzeigen.`,
|
|
222
|
-
breakdownAcrossCategories: ({ count }) => `Aufschl\xFCsselung \xFCber ${count} Job-${count === 1 ? "Kategorie" : "Kategorien"}.`,
|
|
223
|
-
compareWithSimilar: ({ count }) => `Vergleichen Sie mit ${count} \xE4hnlichen Unternehmen.`,
|
|
224
|
-
salaryInfoFor: ({ company, board }) => `Gehaltsinformationen f\xFCr ${company} auf ${board}.`,
|
|
225
|
-
categoryRolesAtCompanyPay: ({ category, company, range, count }) => `${category}-Positionen bei ${company} werden mit ${range}/Jahr verg\xFCtet, basierend auf ${count} Stellenanzeigen.`,
|
|
226
|
-
categorySalaryDataFor: ({ category, company, board }) => `${category}-Gehaltsdaten f\xFCr ${company} auf ${board}.`
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
var LEXICONS = { en: EN, de: DE };
|
|
230
|
-
function getSalaryLexicon(locale) {
|
|
231
|
-
return locale && LEXICONS[locale] || EN;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// src/format/labels.ts
|
|
235
|
-
var EN_LABELS = {
|
|
236
|
-
// remoteOption
|
|
237
|
-
on_site: "On-site",
|
|
238
|
-
hybrid: "Hybrid",
|
|
239
|
-
remote: "Remote",
|
|
240
|
-
// employmentType
|
|
241
|
-
full_time: "Full-time",
|
|
242
|
-
part_time: "Part-time",
|
|
243
|
-
contract: "Contract",
|
|
244
|
-
internship: "Internship",
|
|
245
|
-
temporary: "Temporary",
|
|
246
|
-
volunteer: "Volunteer",
|
|
247
|
-
other: "Other"
|
|
248
|
-
};
|
|
249
|
-
var DE_LABELS = {
|
|
250
|
-
on_site: "Vor Ort",
|
|
251
|
-
hybrid: "Hybrid",
|
|
252
|
-
remote: "Remote",
|
|
253
|
-
full_time: "Vollzeit",
|
|
254
|
-
part_time: "Teilzeit",
|
|
255
|
-
contract: "Freiberuflich",
|
|
256
|
-
internship: "Praktikum",
|
|
257
|
-
temporary: "Befristet",
|
|
258
|
-
volunteer: "Ehrenamtlich",
|
|
259
|
-
other: "Sonstiges"
|
|
260
|
-
};
|
|
261
|
-
var FR_LABELS = {
|
|
262
|
-
on_site: "Sur site",
|
|
263
|
-
hybrid: "Hybride",
|
|
264
|
-
remote: "T\xE9l\xE9travail",
|
|
265
|
-
full_time: "Temps plein",
|
|
266
|
-
part_time: "Temps partiel",
|
|
267
|
-
contract: "Contrat",
|
|
268
|
-
internship: "Stage",
|
|
269
|
-
temporary: "Temporaire",
|
|
270
|
-
volunteer: "B\xE9n\xE9volat",
|
|
271
|
-
other: "Autre"
|
|
272
|
-
};
|
|
273
|
-
var VOCABULARIES = {
|
|
274
|
-
en: EN_LABELS,
|
|
275
|
-
de: DE_LABELS,
|
|
276
|
-
fr: FR_LABELS
|
|
277
|
-
};
|
|
278
|
-
var OVERRIDE_KEYS = {
|
|
279
|
-
on_site: "onSiteLabel",
|
|
280
|
-
hybrid: "hybridLabel",
|
|
281
|
-
remote: "remoteLabel",
|
|
282
|
-
full_time: "fullTimeLabel",
|
|
283
|
-
part_time: "partTimeLabel",
|
|
284
|
-
contract: "contractLabel",
|
|
285
|
-
internship: "internshipLabel",
|
|
286
|
-
temporary: "temporaryLabel",
|
|
287
|
-
volunteer: "volunteerLabel",
|
|
288
|
-
entry_level: "seniorityEntryLevel",
|
|
289
|
-
associate: "seniorityAssociate",
|
|
290
|
-
mid_level: "seniorityMidLevel",
|
|
291
|
-
senior: "senioritySenior",
|
|
292
|
-
lead: "seniorityLead",
|
|
293
|
-
principal: "seniorityPrincipal",
|
|
294
|
-
director: "seniorityDirector",
|
|
295
|
-
executive: "seniorityExecutive"
|
|
296
|
-
};
|
|
297
|
-
var SENIORITY_KEYS = /* @__PURE__ */ new Set([
|
|
298
|
-
"entry_level",
|
|
299
|
-
"associate",
|
|
300
|
-
"mid_level",
|
|
301
|
-
"senior",
|
|
302
|
-
"lead",
|
|
303
|
-
"principal",
|
|
304
|
-
"director",
|
|
305
|
-
"executive"
|
|
306
|
-
]);
|
|
307
|
-
function storedOverride(value, labels) {
|
|
308
|
-
const key = OVERRIDE_KEYS[value];
|
|
309
|
-
const override = key ? labels?.jobCardLabels?.[key] : void 0;
|
|
310
|
-
return typeof override === "string" && override.trim() !== "" ? override : null;
|
|
311
|
-
}
|
|
312
|
-
function fieldLabel(locale, value, labels) {
|
|
313
|
-
if (!value) return null;
|
|
314
|
-
const override = storedOverride(value, labels);
|
|
315
|
-
if (override) return override;
|
|
316
|
-
if (SENIORITY_KEYS.has(value)) {
|
|
317
|
-
return getSalaryLexicon(locale).seniority[value];
|
|
318
|
-
}
|
|
319
|
-
const vocabulary = VOCABULARIES[locale] ?? EN_LABELS;
|
|
320
|
-
return vocabulary[value] ?? EN_LABELS[value] ?? value;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// src/format/location.ts
|
|
324
|
-
function locationLabel(locale, job) {
|
|
325
|
-
const office = job.officeLocations[0];
|
|
326
|
-
const place = office ? office.displayName ?? [office.city ?? office.locality, office.country].filter(Boolean).join(", ") : null;
|
|
327
|
-
if (job.remoteOption === "remote") {
|
|
328
|
-
return job.remoteWorldwide ? "Remote (worldwide)" : "Remote";
|
|
329
|
-
}
|
|
330
|
-
if (!place) return fieldLabel(locale, job.remoteOption) ?? "";
|
|
331
|
-
return job.remoteOption === "hybrid" ? `${place} (hybrid)` : place;
|
|
332
|
-
}
|
|
333
|
-
function cardLocationLabel(locale, job) {
|
|
334
|
-
if (job.remoteOption === "remote") {
|
|
335
|
-
return job.remoteLocationLabel ? `Remote \xB7 ${job.remoteLocationLabel}` : "Remote";
|
|
162
|
+
if (!dateStr) return null;
|
|
163
|
+
const isDateOnly = /^\d{4}-\d{2}(-\d{2})?$/.test(dateStr);
|
|
164
|
+
const parsed = isDateOnly ? /* @__PURE__ */ new Date(
|
|
165
|
+
dateStr.length === 7 ? `${dateStr}-01T00:00:00.000Z` : `${dateStr}T00:00:00.000Z`
|
|
166
|
+
) : new Date(dateStr);
|
|
167
|
+
if (Number.isNaN(parsed.getTime())) return null;
|
|
168
|
+
const normalized = normalizeLocale(locale);
|
|
169
|
+
if (!normalized) return null;
|
|
170
|
+
try {
|
|
171
|
+
return getMonthYearFormat(normalized).format(parsed);
|
|
172
|
+
} catch {
|
|
173
|
+
return null;
|
|
336
174
|
}
|
|
337
|
-
return job.locationLabel ?? fieldLabel(locale, job.remoteOption) ?? "";
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// src/format/job-card.ts
|
|
341
|
-
function fullJobToCard(locale, job) {
|
|
342
|
-
return {
|
|
343
|
-
id: job.id,
|
|
344
|
-
object: "job_card",
|
|
345
|
-
slug: job.slug ?? "",
|
|
346
|
-
title: job.title,
|
|
347
|
-
publishedAt: job.publishedAt,
|
|
348
|
-
employmentType: job.employmentType,
|
|
349
|
-
remoteOption: job.remoteOption,
|
|
350
|
-
remoteLocationLabel: job.remoteWorldwide ? "Worldwide" : null,
|
|
351
|
-
salaryMin: job.salaryMin,
|
|
352
|
-
salaryMax: job.salaryMax,
|
|
353
|
-
salaryCurrency: job.salaryCurrency,
|
|
354
|
-
salaryTimeframe: job.salaryTimeframe,
|
|
355
|
-
isFeatured: job.isFeatured,
|
|
356
|
-
locationLabel: locationLabel(locale, job),
|
|
357
|
-
company: job.company?.slug ? {
|
|
358
|
-
slug: job.company.slug,
|
|
359
|
-
name: job.company.name ?? "",
|
|
360
|
-
logoUrl: job.company.logoUrl
|
|
361
|
-
} : null,
|
|
362
|
-
categories: job.categories,
|
|
363
|
-
skills: job.skills,
|
|
364
|
-
links: job.links
|
|
365
|
-
};
|
|
366
175
|
}
|
|
367
176
|
|
|
368
177
|
// src/format/salary-range.ts
|
|
369
|
-
var
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
NZD: "NZ$",
|
|
390
|
-
ZAR: "R ",
|
|
391
|
-
THB: "\u0E3F",
|
|
392
|
-
PHP: "\u20B1",
|
|
393
|
-
IDR: "Rp ",
|
|
394
|
-
MYR: "RM ",
|
|
395
|
-
VND: "\u20AB",
|
|
396
|
-
AED: "AED ",
|
|
397
|
-
SAR: "SAR ",
|
|
398
|
-
ILS: "\u20AA",
|
|
399
|
-
TRY: "\u20BA",
|
|
400
|
-
CZK: "K\u010D ",
|
|
401
|
-
HUF: "Ft ",
|
|
402
|
-
RON: "lei ",
|
|
403
|
-
BGN: "\u043B\u0432 ",
|
|
404
|
-
HRK: "kn ",
|
|
405
|
-
ISK: "kr ",
|
|
406
|
-
CLP: "CLP$",
|
|
407
|
-
COP: "COP$",
|
|
408
|
-
PEN: "S/",
|
|
409
|
-
ARS: "ARS$",
|
|
410
|
-
TWD: "NT$",
|
|
411
|
-
NGN: "\u20A6",
|
|
412
|
-
EGP: "E\xA3",
|
|
413
|
-
PKR: "\u20A8",
|
|
414
|
-
BDT: "\u09F3",
|
|
415
|
-
UAH: "\u20B4",
|
|
416
|
-
KZT: "\u20B8"
|
|
417
|
-
};
|
|
418
|
-
function getCurrencySymbol(currency) {
|
|
419
|
-
if (!currency) {
|
|
420
|
-
return "$";
|
|
421
|
-
}
|
|
422
|
-
const normalized = currency.trim().toUpperCase();
|
|
423
|
-
return CURRENCY_SYMBOLS[normalized] ?? `${normalized} `;
|
|
424
|
-
}
|
|
425
|
-
var compactFormatCache = /* @__PURE__ */ new Map();
|
|
426
|
-
function getCompactFormatter(language) {
|
|
427
|
-
const cached = compactFormatCache.get(language);
|
|
428
|
-
if (cached) return cached;
|
|
429
|
-
const formatter = new Intl.NumberFormat(language, {
|
|
430
|
-
notation: "compact",
|
|
431
|
-
compactDisplay: "short",
|
|
432
|
-
maximumFractionDigits: 1
|
|
433
|
-
});
|
|
434
|
-
compactFormatCache.set(language, formatter);
|
|
435
|
-
return formatter;
|
|
436
|
-
}
|
|
437
|
-
function formatCompactNumber(value, language) {
|
|
438
|
-
try {
|
|
439
|
-
return getCompactFormatter(language ?? "en").format(value);
|
|
440
|
-
} catch {
|
|
441
|
-
if (value >= 1e6) {
|
|
442
|
-
const millions = value / 1e6;
|
|
443
|
-
return millions % 1 === 0 ? `${millions}M` : `${millions.toFixed(1)}M`;
|
|
444
|
-
}
|
|
445
|
-
if (value >= 1e3) {
|
|
446
|
-
const thousands = value / 1e3;
|
|
447
|
-
return thousands % 1 === 0 ? `${thousands}k` : `${thousands.toFixed(1)}k`;
|
|
178
|
+
var currencyCache = /* @__PURE__ */ new Map();
|
|
179
|
+
function formatAmount(value, language, currencyCode, currencyDisplay = "symbol", notation = "compact") {
|
|
180
|
+
const cacheKey = `${language}:${currencyCode}:${currencyDisplay}:${notation}`;
|
|
181
|
+
let formatter = currencyCache.get(cacheKey);
|
|
182
|
+
if (formatter === void 0) {
|
|
183
|
+
try {
|
|
184
|
+
const options = {
|
|
185
|
+
style: "currency",
|
|
186
|
+
currency: currencyCode,
|
|
187
|
+
currencyDisplay,
|
|
188
|
+
notation
|
|
189
|
+
};
|
|
190
|
+
if (notation === "compact") {
|
|
191
|
+
options.compactDisplay = "short";
|
|
192
|
+
options.maximumFractionDigits = 1;
|
|
193
|
+
options.minimumFractionDigits = 0;
|
|
194
|
+
}
|
|
195
|
+
formatter = new Intl.NumberFormat(language, options);
|
|
196
|
+
} catch {
|
|
197
|
+
formatter = null;
|
|
448
198
|
}
|
|
449
|
-
|
|
199
|
+
currencyCache.set(cacheKey, formatter);
|
|
450
200
|
}
|
|
201
|
+
return formatter ? formatter.format(value) : null;
|
|
451
202
|
}
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
const cacheKey = `${language}:${currencyCode}`;
|
|
456
|
-
let formatter = compactCurrencyCache.get(cacheKey);
|
|
203
|
+
function formatRange(min, max, language, currencyCode, currencyDisplay = "symbol", notation = "compact") {
|
|
204
|
+
const cacheKey = `${language}:${currencyCode}:${currencyDisplay}:${notation}:range`;
|
|
205
|
+
let formatter = currencyCache.get(cacheKey);
|
|
457
206
|
if (formatter === void 0) {
|
|
458
207
|
try {
|
|
459
|
-
|
|
208
|
+
const options = {
|
|
460
209
|
style: "currency",
|
|
461
210
|
currency: currencyCode,
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
211
|
+
currencyDisplay,
|
|
212
|
+
notation
|
|
213
|
+
};
|
|
214
|
+
if (notation === "compact") {
|
|
215
|
+
options.compactDisplay = "short";
|
|
216
|
+
options.maximumFractionDigits = 1;
|
|
217
|
+
options.minimumFractionDigits = 0;
|
|
218
|
+
}
|
|
219
|
+
formatter = new Intl.NumberFormat(language, options);
|
|
466
220
|
} catch {
|
|
467
221
|
formatter = null;
|
|
468
222
|
}
|
|
469
|
-
|
|
470
|
-
}
|
|
471
|
-
if (!formatter) {
|
|
472
|
-
return `${getCurrencySymbol(currency)}${formatCompactNumber(value, language)}`;
|
|
223
|
+
currencyCache.set(cacheKey, formatter);
|
|
473
224
|
}
|
|
474
|
-
|
|
225
|
+
if (!formatter) return null;
|
|
226
|
+
return formatter.formatRange(min, max);
|
|
475
227
|
}
|
|
476
|
-
|
|
477
|
-
|
|
228
|
+
var SALARY_TIMEFRAMES = /* @__PURE__ */ new Set([
|
|
229
|
+
"per_year",
|
|
230
|
+
"per_month",
|
|
231
|
+
"per_week",
|
|
232
|
+
"per_day",
|
|
233
|
+
"per_hour"
|
|
234
|
+
]);
|
|
235
|
+
function notationForMagnitude(...values) {
|
|
236
|
+
let anyFinite = false;
|
|
237
|
+
for (const v of values) {
|
|
238
|
+
if (v == null || !Number.isFinite(v)) continue;
|
|
239
|
+
anyFinite = true;
|
|
240
|
+
if (Math.abs(v) >= 1e3) return "compact";
|
|
241
|
+
}
|
|
242
|
+
return anyFinite ? "standard" : "compact";
|
|
243
|
+
}
|
|
244
|
+
function formatSalaryRange(language, min, max, timeframe, currency = null, currencyDisplay = "symbol", notation) {
|
|
245
|
+
const minOk = min == null || Number.isFinite(min);
|
|
246
|
+
const maxOk = max == null || Number.isFinite(max);
|
|
247
|
+
if (!minOk || !maxOk) {
|
|
478
248
|
return null;
|
|
479
249
|
}
|
|
480
|
-
if (!timeframeLabel) {
|
|
481
|
-
return value;
|
|
482
|
-
}
|
|
483
|
-
return `${value} ${timeframeLabel}`;
|
|
484
|
-
}
|
|
485
|
-
function formatSalaryRange(language, min, max, timeframe, currency = null, timeframeOverrides) {
|
|
486
250
|
if (min == null && max == null) {
|
|
487
251
|
return null;
|
|
488
252
|
}
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
const enSourceWord = timeframe ? getSalaryLexicon("en").timeframe[timeframe] : void 0;
|
|
500
|
-
const lexiconTimeframe = timeframe ? lexicon.timeframe[timeframe] ?? null : null;
|
|
501
|
-
const timeframeLabel = rawOverride && rawOverride !== enSourceWord ? rawOverride : lexiconTimeframe;
|
|
502
|
-
const formatAmount = locale === "en" ? (value) => `${getCurrencySymbol(currency)}${formatCompactNumber(value, locale)}` : (value) => formatCompactCurrency(value, locale, currency);
|
|
253
|
+
const currencyCode = currency?.trim().toUpperCase() || null;
|
|
254
|
+
if (!currencyCode) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const locale = normalizeLocale(language);
|
|
258
|
+
if (!locale) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
const resolvedNotation = notation ?? notationForMagnitude(min, max);
|
|
262
|
+
const timeframeLabel = typeof timeframe === "string" && SALARY_TIMEFRAMES.has(timeframe) ? timeframe : null;
|
|
503
263
|
if (min != null && max != null) {
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
264
|
+
const low = min <= max ? min : max;
|
|
265
|
+
const high = min <= max ? max : min;
|
|
266
|
+
const text2 = low === high ? formatAmount(
|
|
267
|
+
low,
|
|
268
|
+
locale,
|
|
269
|
+
currencyCode,
|
|
270
|
+
currencyDisplay,
|
|
271
|
+
resolvedNotation
|
|
272
|
+
) : formatRange(
|
|
273
|
+
low,
|
|
274
|
+
high,
|
|
275
|
+
locale,
|
|
276
|
+
currencyCode,
|
|
277
|
+
currencyDisplay,
|
|
278
|
+
resolvedNotation
|
|
507
279
|
);
|
|
280
|
+
return text2 ? { text: text2, timeframe: timeframeLabel, bound: "range" } : null;
|
|
508
281
|
}
|
|
509
282
|
if (min != null) {
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
283
|
+
const text2 = formatAmount(
|
|
284
|
+
min,
|
|
285
|
+
locale,
|
|
286
|
+
currencyCode,
|
|
287
|
+
currencyDisplay,
|
|
288
|
+
resolvedNotation
|
|
513
289
|
);
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
290
|
+
return text2 ? { text: text2, timeframe: timeframeLabel, bound: "from" } : null;
|
|
291
|
+
}
|
|
292
|
+
const text = formatAmount(
|
|
293
|
+
max,
|
|
294
|
+
locale,
|
|
295
|
+
currencyCode,
|
|
296
|
+
currencyDisplay,
|
|
297
|
+
resolvedNotation
|
|
518
298
|
);
|
|
299
|
+
return text ? { text, timeframe: timeframeLabel, bound: "upTo" } : null;
|
|
519
300
|
}
|
|
520
301
|
|
|
521
302
|
// src/seo/salary.ts
|
|
522
|
-
var
|
|
523
|
-
function
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
return
|
|
536
|
-
|
|
537
|
-
function formatUsd(locale, value) {
|
|
303
|
+
var currencyFormatterCache = /* @__PURE__ */ new Map();
|
|
304
|
+
function notationForMagnitude2(...values) {
|
|
305
|
+
let anyFinite = false;
|
|
306
|
+
for (const v of values) {
|
|
307
|
+
if (v == null || !Number.isFinite(v)) continue;
|
|
308
|
+
anyFinite = true;
|
|
309
|
+
if (Math.abs(v) >= 1e3) return "compact";
|
|
310
|
+
}
|
|
311
|
+
return anyFinite ? "standard" : "compact";
|
|
312
|
+
}
|
|
313
|
+
function getCurrencyFormatter(language, currencyCode, notation) {
|
|
314
|
+
const key = `${language}:${currencyCode}:${notation}`;
|
|
315
|
+
const cached = currencyFormatterCache.get(key);
|
|
316
|
+
if (cached !== void 0) return cached;
|
|
317
|
+
let formatter;
|
|
538
318
|
try {
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
319
|
+
const options = {
|
|
320
|
+
style: "currency",
|
|
321
|
+
currency: currencyCode,
|
|
322
|
+
notation
|
|
323
|
+
};
|
|
324
|
+
if (notation === "compact") {
|
|
325
|
+
options.compactDisplay = "short";
|
|
326
|
+
options.minimumFractionDigits = 0;
|
|
327
|
+
options.maximumFractionDigits = 1;
|
|
548
328
|
}
|
|
549
|
-
|
|
329
|
+
formatter = new Intl.NumberFormat(language, options);
|
|
330
|
+
} catch {
|
|
331
|
+
formatter = null;
|
|
550
332
|
}
|
|
333
|
+
currencyFormatterCache.set(key, formatter);
|
|
334
|
+
return formatter;
|
|
551
335
|
}
|
|
552
|
-
function
|
|
553
|
-
|
|
336
|
+
function formatSalaryStat(locale, value, currency, notation) {
|
|
337
|
+
if (!Number.isFinite(value)) return null;
|
|
338
|
+
const currencyCode = currency?.trim().toUpperCase() || null;
|
|
339
|
+
if (!currencyCode) return null;
|
|
340
|
+
const normalized = normalizeLocale(locale);
|
|
341
|
+
if (!normalized) return null;
|
|
342
|
+
const resolved = notation ?? notationForMagnitude2(value);
|
|
343
|
+
const formatter = getCurrencyFormatter(normalized, currencyCode, resolved);
|
|
344
|
+
if (!formatter) return null;
|
|
345
|
+
return formatter.format(value);
|
|
346
|
+
}
|
|
347
|
+
function formatSalaryStatRange(locale, min, max, currency, notation) {
|
|
348
|
+
if (!Number.isFinite(min) || !Number.isFinite(max)) return null;
|
|
349
|
+
const currencyCode = currency?.trim().toUpperCase() || null;
|
|
350
|
+
if (!currencyCode) return null;
|
|
351
|
+
const normalized = normalizeLocale(locale);
|
|
352
|
+
if (!normalized) return null;
|
|
353
|
+
const resolved = notation ?? notationForMagnitude2(min, max);
|
|
354
|
+
const formatter = getCurrencyFormatter(normalized, currencyCode, resolved);
|
|
355
|
+
if (!formatter) return null;
|
|
356
|
+
const low = Math.min(min, max);
|
|
357
|
+
const high = Math.max(min, max);
|
|
358
|
+
if (low === high) return formatter.format(low);
|
|
359
|
+
return formatter.formatRange(low, high);
|
|
554
360
|
}
|
|
555
361
|
|
|
556
362
|
// src/seo/job-posting.ts
|
|
@@ -560,558 +366,79 @@ function normalizeWebsiteUrl(website) {
|
|
|
560
366
|
return /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
|
|
561
367
|
}
|
|
562
368
|
|
|
563
|
-
// src/
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
worldwideLabel: "Worldwide",
|
|
608
|
-
noExperienceRequiredLabel: "No experience required",
|
|
609
|
-
experienceYears: (years) => `${years}+ years`,
|
|
610
|
-
posted: (published) => `Posted ${published}`,
|
|
611
|
-
noDescriptionText: "No description provided.",
|
|
612
|
-
viewCompanyProfileLabel: "View company profile",
|
|
613
|
-
similarJobsHeading: "Similar jobs",
|
|
614
|
-
breadcrumbAriaLabel: "Breadcrumb"
|
|
615
|
-
},
|
|
616
|
-
apply: {
|
|
617
|
-
applyButtonText: "Apply for this job",
|
|
618
|
-
applyingLabel: "Applying\u2026",
|
|
619
|
-
applyOnEmployerSiteLabel: "Apply on employer site",
|
|
620
|
-
signInToApplyLabel: "Sign in to apply",
|
|
621
|
-
verifyEmailToApplyLabel: "Verify email to apply",
|
|
622
|
-
appliedViewApplicationsLabel: "Applied \u2014 view applications",
|
|
623
|
-
applicationSubmitError: "Something went wrong. Please try again."
|
|
624
|
-
},
|
|
625
|
-
alerts: {
|
|
626
|
-
jobAlertTitle: "Get job alerts",
|
|
627
|
-
jobAlertEmailPlaceholder: "you@example.com",
|
|
628
|
-
jobAlertButtonText: "Get alerts",
|
|
629
|
-
subscribingLabel: "Subscribing\u2026",
|
|
630
|
-
jobAlertSuccessToast: "If this email isn't already subscribed, we've sent a confirmation link \u2014 check your inbox.",
|
|
631
|
-
jobAlertErrorToast: "Something went wrong. Please try again.",
|
|
632
|
-
sectionAriaLabel: "Job alerts",
|
|
633
|
-
emailAriaLabel: "email",
|
|
634
|
-
submitAriaLabel: "get job alerts"
|
|
635
|
-
},
|
|
636
|
-
copyLink: {
|
|
637
|
-
copyLinkLabel: "Copy link",
|
|
638
|
-
copiedLabel: "Copied",
|
|
639
|
-
ariaLabel: "copy link"
|
|
640
|
-
},
|
|
641
|
-
salary: {
|
|
642
|
-
comparisonHeadlineAverage: "Average salary",
|
|
643
|
-
perYearSuffix: "/ year",
|
|
644
|
-
comparisonPercentile25Label: "25th percentile",
|
|
645
|
-
medianLabel: "Median",
|
|
646
|
-
comparisonPercentile75Label: "75th percentile",
|
|
647
|
-
basedOnLabel: "Based on",
|
|
648
|
-
seniorityTableHeaderLevel: "Level",
|
|
649
|
-
seniorityTableHeaderAvg: "Average",
|
|
650
|
-
boardBaselineLabel: "Board baseline",
|
|
651
|
-
seniorityTableHeaderDiff: "vs. board",
|
|
652
|
-
faqHeading: "Frequently asked questions"
|
|
653
|
-
},
|
|
654
|
-
nav: {
|
|
655
|
-
home: "Jobs",
|
|
656
|
-
companies: "Companies",
|
|
657
|
-
pricing: "Pricing",
|
|
658
|
-
talent: "Talent",
|
|
659
|
-
post: "Post a job",
|
|
660
|
-
blog: "Blog"
|
|
661
|
-
},
|
|
662
|
-
footer: {
|
|
663
|
-
forCandidatesHeading: "For Candidates",
|
|
664
|
-
forCompaniesHeading: "For Companies",
|
|
665
|
-
resourcesHeading: "Resources",
|
|
666
|
-
aboutHeading: "About",
|
|
667
|
-
websiteLabel: "Website",
|
|
668
|
-
aboutLabel: "About",
|
|
669
|
-
contactLabel: "Contact us",
|
|
670
|
-
locationsLabel: "Locations",
|
|
671
|
-
salariesLabel: "Salaries",
|
|
672
|
-
sitemapLabel: "Sitemap",
|
|
673
|
-
termsOfServiceLabel: "Terms of Service",
|
|
674
|
-
privacyPolicyLabel: "Privacy Policy",
|
|
675
|
-
cookiePolicyLabel: "Cookie Policy",
|
|
676
|
-
impressumLabel: "Impressum",
|
|
677
|
-
allRightsReservedText: "All rights reserved.",
|
|
678
|
-
copyrightPrefix: "\xA9 {{year}} {{board_name}}.",
|
|
679
|
-
poweredByText: "Powered by",
|
|
680
|
-
defaultDescription: "Discover the latest roles from {{board_name}}."
|
|
681
|
-
},
|
|
682
|
-
breadcrumbs: {
|
|
683
|
-
home: "Home",
|
|
684
|
-
jobs: "Jobs",
|
|
685
|
-
locations: "Locations",
|
|
686
|
-
salaries: "Salaries",
|
|
687
|
-
companies: "Companies",
|
|
688
|
-
skills: "Skills",
|
|
689
|
-
titles: "Titles",
|
|
690
|
-
blog: "Blog",
|
|
691
|
-
post: "Post a Job",
|
|
692
|
-
pricing: "Pricing",
|
|
693
|
-
about: "About",
|
|
694
|
-
impressum: "Impressum",
|
|
695
|
-
termsOfService: "Terms of Service",
|
|
696
|
-
privacyPolicy: "Privacy Policy",
|
|
697
|
-
cookiePolicy: "Cookie Policy",
|
|
698
|
-
talent: "Talent"
|
|
699
|
-
},
|
|
700
|
-
pagination: {
|
|
701
|
-
ariaLabel: "Pagination",
|
|
702
|
-
previousLabel: "Previous",
|
|
703
|
-
nextLabel: "Next"
|
|
704
|
-
},
|
|
705
|
-
blog: {
|
|
706
|
-
bylineLabel: "By",
|
|
707
|
-
readingTimeLabel: "min read",
|
|
708
|
-
searchLabel: "Search",
|
|
709
|
-
searchPlaceholder: "Search posts...",
|
|
710
|
-
clearButtonLabel: "Clear search",
|
|
711
|
-
tagFilterLabel: "Topics",
|
|
712
|
-
tagFilterAllLabel: "All",
|
|
713
|
-
emptyTitle: "No matching posts",
|
|
714
|
-
emptyDescription: "Try adjusting your search or clearing your filters.",
|
|
715
|
-
emptyResetLabel: "Clear filters"
|
|
716
|
-
},
|
|
717
|
-
entity: {
|
|
718
|
-
jobSingular: "job",
|
|
719
|
-
jobPlural: "jobs",
|
|
720
|
-
companySingular: "company",
|
|
721
|
-
companyPlural: "companies"
|
|
722
|
-
}
|
|
723
|
-
};
|
|
724
|
-
var DE2 = {
|
|
725
|
-
jobCard: {
|
|
726
|
-
featuredLabel: "Hervorgehoben",
|
|
727
|
-
aiRankedLabel: "KI-sortiert",
|
|
728
|
-
sortNewestLabel: "Neueste zuerst",
|
|
729
|
-
sortSalaryHighLabel: "Gehalt: absteigend"
|
|
730
|
-
},
|
|
731
|
-
jobSearch: {
|
|
732
|
-
headingJobs: "Jobs",
|
|
733
|
-
keywordLabel: "Stichwort",
|
|
734
|
-
keywordPlaceholder: "Jobtitel oder -beschreibungen suchen",
|
|
735
|
-
locationLabel: "Standort",
|
|
736
|
-
locationPlaceholder: "Stadt, Land oder Region",
|
|
737
|
-
workplacePlaceholder: "Arbeitsmodell",
|
|
738
|
-
anyWorkplaceLabel: "Alle Arbeitsmodelle",
|
|
739
|
-
typePlaceholder: "Anstellungsart",
|
|
740
|
-
anyTypeLabel: "Alle Anstellungsarten",
|
|
741
|
-
sortPlaceholder: "Sortieren",
|
|
742
|
-
loadMoreLabel: "Mehr laden",
|
|
743
|
-
noJobsMatchText: "Keine passenden Jobs \u2014 setze einen Filter zur\xFCck."
|
|
744
|
-
},
|
|
745
|
-
jobDetail: {
|
|
746
|
-
categoriesHeading: "Kategorien",
|
|
747
|
-
skillsHeading: "F\xE4higkeiten",
|
|
748
|
-
additionalDetailsHeading: "Weitere Details",
|
|
749
|
-
customFieldYesLabel: "Ja",
|
|
750
|
-
customFieldNoLabel: "Nein",
|
|
751
|
-
locationsLabel: "Standorte",
|
|
752
|
-
workPermitsLabel: "Arbeitserlaubnis",
|
|
753
|
-
timezonesLabel: "Zeitzonen",
|
|
754
|
-
educationLabel: "Ausbildung",
|
|
755
|
-
experienceLabel: "Berufserfahrung",
|
|
756
|
-
worldwideLabel: "Weltweit",
|
|
757
|
-
noExperienceRequiredLabel: "Keine Berufserfahrung erforderlich",
|
|
758
|
-
experienceYears: (years) => `${years}+ Jahre`,
|
|
759
|
-
posted: (published) => `Ver\xF6ffentlicht ${published}`,
|
|
760
|
-
noDescriptionText: "Keine Beschreibung vorhanden.",
|
|
761
|
-
viewCompanyProfileLabel: "Unternehmensprofil ansehen",
|
|
762
|
-
similarJobsHeading: "\xC4hnliche Jobs",
|
|
763
|
-
breadcrumbAriaLabel: "Seitenpfad"
|
|
764
|
-
},
|
|
765
|
-
apply: {
|
|
766
|
-
applyButtonText: "Jetzt bewerben",
|
|
767
|
-
applyingLabel: "Wird gesendet \u2026",
|
|
768
|
-
applyOnEmployerSiteLabel: "Beim Unternehmen bewerben",
|
|
769
|
-
signInToApplyLabel: "Zum Bewerben anmelden",
|
|
770
|
-
verifyEmailToApplyLabel: "E-Mail best\xE4tigen, um dich zu bewerben",
|
|
771
|
-
appliedViewApplicationsLabel: "Beworben \u2014 Bewerbungen ansehen",
|
|
772
|
-
applicationSubmitError: "Etwas ist schiefgelaufen. Bitte versuche es erneut."
|
|
773
|
-
},
|
|
774
|
-
alerts: {
|
|
775
|
-
jobAlertTitle: "Job-Benachrichtigungen erhalten",
|
|
776
|
-
jobAlertEmailPlaceholder: "name@beispiel.de",
|
|
777
|
-
jobAlertButtonText: "Abonnieren",
|
|
778
|
-
subscribingLabel: "Wird abonniert \u2026",
|
|
779
|
-
jobAlertSuccessToast: "Falls diese E-Mail noch nicht abonniert ist, haben wir einen Best\xE4tigungslink gesendet \u2014 pr\xFCfe deinen Posteingang.",
|
|
780
|
-
jobAlertErrorToast: "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
|
|
781
|
-
sectionAriaLabel: "Job-Benachrichtigungen",
|
|
782
|
-
emailAriaLabel: "E-Mail",
|
|
783
|
-
submitAriaLabel: "Job-Benachrichtigungen erhalten"
|
|
784
|
-
},
|
|
785
|
-
copyLink: {
|
|
786
|
-
copyLinkLabel: "Link kopieren",
|
|
787
|
-
copiedLabel: "Kopiert",
|
|
788
|
-
ariaLabel: "Link kopieren"
|
|
789
|
-
},
|
|
790
|
-
salary: {
|
|
791
|
-
comparisonHeadlineAverage: "Durchschnittsgehalt",
|
|
792
|
-
perYearSuffix: "/ Jahr",
|
|
793
|
-
comparisonPercentile25Label: "25. Perzentil",
|
|
794
|
-
medianLabel: "Median",
|
|
795
|
-
comparisonPercentile75Label: "75. Perzentil",
|
|
796
|
-
basedOnLabel: "Basierend auf",
|
|
797
|
-
seniorityTableHeaderLevel: "Erfahrungslevel",
|
|
798
|
-
seniorityTableHeaderAvg: "Durchschnitt",
|
|
799
|
-
boardBaselineLabel: "Board-Durchschnitt",
|
|
800
|
-
seniorityTableHeaderDiff: "vs. Board",
|
|
801
|
-
faqHeading: "H\xE4ufig gestellte Fragen"
|
|
802
|
-
},
|
|
803
|
-
nav: {
|
|
804
|
-
home: "Jobs",
|
|
805
|
-
companies: "Unternehmen",
|
|
806
|
-
pricing: "Preise",
|
|
807
|
-
talent: "Talente",
|
|
808
|
-
post: "Job ver\xF6ffentlichen",
|
|
809
|
-
blog: "Blog"
|
|
810
|
-
},
|
|
811
|
-
footer: {
|
|
812
|
-
forCandidatesHeading: "F\xFCr Kandidaten",
|
|
813
|
-
forCompaniesHeading: "F\xFCr Unternehmen",
|
|
814
|
-
resourcesHeading: "Ressourcen",
|
|
815
|
-
aboutHeading: "\xDCber uns",
|
|
816
|
-
websiteLabel: "Website",
|
|
817
|
-
aboutLabel: "\xDCber uns",
|
|
818
|
-
contactLabel: "Kontakt",
|
|
819
|
-
locationsLabel: "Standorte",
|
|
820
|
-
salariesLabel: "Geh\xE4lter",
|
|
821
|
-
sitemapLabel: "Sitemap",
|
|
822
|
-
termsOfServiceLabel: "Nutzungsbedingungen",
|
|
823
|
-
privacyPolicyLabel: "Datenschutzerkl\xE4rung",
|
|
824
|
-
cookiePolicyLabel: "Cookie-Richtlinie",
|
|
825
|
-
impressumLabel: "Impressum",
|
|
826
|
-
allRightsReservedText: "Alle Rechte vorbehalten.",
|
|
827
|
-
copyrightPrefix: "\xA9 {{year}} {{board_name}}.",
|
|
828
|
-
poweredByText: "Bereitgestellt von",
|
|
829
|
-
defaultDescription: "Entdecke die neuesten Stellen von {{board_name}}."
|
|
830
|
-
},
|
|
831
|
-
breadcrumbs: {
|
|
832
|
-
home: "Startseite",
|
|
833
|
-
jobs: "Jobs",
|
|
834
|
-
locations: "Standorte",
|
|
835
|
-
salaries: "Geh\xE4lter",
|
|
836
|
-
companies: "Unternehmen",
|
|
837
|
-
skills: "F\xE4higkeiten",
|
|
838
|
-
titles: "Berufsbezeichnungen",
|
|
839
|
-
blog: "Blog",
|
|
840
|
-
post: "Job ver\xF6ffentlichen",
|
|
841
|
-
pricing: "Preise",
|
|
842
|
-
about: "\xDCber uns",
|
|
843
|
-
impressum: "Impressum",
|
|
844
|
-
termsOfService: "Nutzungsbedingungen",
|
|
845
|
-
privacyPolicy: "Datenschutzerkl\xE4rung",
|
|
846
|
-
cookiePolicy: "Cookie-Richtlinie",
|
|
847
|
-
talent: "Talente"
|
|
848
|
-
},
|
|
849
|
-
pagination: {
|
|
850
|
-
ariaLabel: "Seitennummerierung",
|
|
851
|
-
previousLabel: "Zur\xFCck",
|
|
852
|
-
nextLabel: "Weiter"
|
|
853
|
-
},
|
|
854
|
-
blog: {
|
|
855
|
-
bylineLabel: "Von",
|
|
856
|
-
readingTimeLabel: "Minuten Lesezeit",
|
|
857
|
-
searchLabel: "Suche",
|
|
858
|
-
searchPlaceholder: "Artikel suchen...",
|
|
859
|
-
clearButtonLabel: "Suche leeren",
|
|
860
|
-
tagFilterLabel: "Themen",
|
|
861
|
-
tagFilterAllLabel: "Alle",
|
|
862
|
-
emptyTitle: "Keine passenden Artikel gefunden",
|
|
863
|
-
emptyDescription: "Versuche, deine Suche anzupassen oder die Filter zur\xFCckzusetzen.",
|
|
864
|
-
emptyResetLabel: "Filter zur\xFCcksetzen"
|
|
865
|
-
},
|
|
866
|
-
entity: {
|
|
867
|
-
jobSingular: "Job",
|
|
868
|
-
jobPlural: "Jobs",
|
|
869
|
-
companySingular: "Unternehmen",
|
|
870
|
-
companyPlural: "Unternehmen"
|
|
871
|
-
}
|
|
872
|
-
};
|
|
873
|
-
var FR = {
|
|
874
|
-
jobCard: {
|
|
875
|
-
featuredLabel: "\xC0 la une",
|
|
876
|
-
aiRankedLabel: "Classement IA",
|
|
877
|
-
sortNewestLabel: "Plus r\xE9centes",
|
|
878
|
-
sortSalaryHighLabel: "Salaire : d\xE9croissant"
|
|
879
|
-
},
|
|
880
|
-
jobSearch: {
|
|
881
|
-
headingJobs: "Offres d'emploi",
|
|
882
|
-
keywordLabel: "Mot-cl\xE9",
|
|
883
|
-
keywordPlaceholder: "Rechercher une offre\u2026",
|
|
884
|
-
locationLabel: "Lieu",
|
|
885
|
-
locationPlaceholder: "Ville ou r\xE9gion",
|
|
886
|
-
workplacePlaceholder: "Mode de travail",
|
|
887
|
-
anyWorkplaceLabel: "Tous les modes de travail",
|
|
888
|
-
typePlaceholder: "Type de contrat",
|
|
889
|
-
anyTypeLabel: "Tous les types",
|
|
890
|
-
sortPlaceholder: "Trier",
|
|
891
|
-
loadMoreLabel: "Voir plus",
|
|
892
|
-
noJobsMatchText: "Aucune offre ne correspond \u2014 essayez de retirer un filtre."
|
|
893
|
-
},
|
|
894
|
-
jobDetail: {
|
|
895
|
-
categoriesHeading: "Cat\xE9gories",
|
|
896
|
-
skillsHeading: "Comp\xE9tences",
|
|
897
|
-
additionalDetailsHeading: "Informations compl\xE9mentaires",
|
|
898
|
-
customFieldYesLabel: "Oui",
|
|
899
|
-
customFieldNoLabel: "Non",
|
|
900
|
-
locationsLabel: "Lieux",
|
|
901
|
-
workPermitsLabel: "Permis de travail",
|
|
902
|
-
timezonesLabel: "Fuseaux horaires",
|
|
903
|
-
educationLabel: "Formation",
|
|
904
|
-
experienceLabel: "Exp\xE9rience",
|
|
905
|
-
worldwideLabel: "Monde entier",
|
|
906
|
-
noExperienceRequiredLabel: "Aucune exp\xE9rience requise",
|
|
907
|
-
experienceYears: (years) => `${years}+ ans`,
|
|
908
|
-
posted: (published) => `Publi\xE9e ${published}`,
|
|
909
|
-
noDescriptionText: "Aucune description fournie.",
|
|
910
|
-
viewCompanyProfileLabel: "Voir le profil de l'entreprise",
|
|
911
|
-
similarJobsHeading: "Offres similaires",
|
|
912
|
-
breadcrumbAriaLabel: "Fil d'Ariane"
|
|
913
|
-
},
|
|
914
|
-
apply: {
|
|
915
|
-
applyButtonText: "Postuler \xE0 cette offre",
|
|
916
|
-
applyingLabel: "Envoi en cours\u2026",
|
|
917
|
-
applyOnEmployerSiteLabel: "Postuler sur le site de l'employeur",
|
|
918
|
-
signInToApplyLabel: "Connectez-vous pour postuler",
|
|
919
|
-
verifyEmailToApplyLabel: "V\xE9rifiez votre e-mail pour postuler",
|
|
920
|
-
appliedViewApplicationsLabel: "Candidature envoy\xE9e \u2014 voir mes candidatures",
|
|
921
|
-
applicationSubmitError: "Une erreur est survenue. Veuillez r\xE9essayer."
|
|
922
|
-
},
|
|
923
|
-
alerts: {
|
|
924
|
-
jobAlertTitle: "Recevoir des alertes emploi",
|
|
925
|
-
jobAlertEmailPlaceholder: "vous@exemple.fr",
|
|
926
|
-
jobAlertButtonText: "Activer les alertes",
|
|
927
|
-
subscribingLabel: "Abonnement en cours\u2026",
|
|
928
|
-
jobAlertSuccessToast: "Si cet e-mail n'est pas d\xE9j\xE0 abonn\xE9, nous avons envoy\xE9 un lien de confirmation \u2014 v\xE9rifiez votre bo\xEEte de r\xE9ception.",
|
|
929
|
-
jobAlertErrorToast: "Une erreur est survenue. Veuillez r\xE9essayer.",
|
|
930
|
-
sectionAriaLabel: "Alertes emploi",
|
|
931
|
-
emailAriaLabel: "e-mail",
|
|
932
|
-
submitAriaLabel: "recevoir des alertes emploi"
|
|
933
|
-
},
|
|
934
|
-
copyLink: {
|
|
935
|
-
copyLinkLabel: "Copier le lien",
|
|
936
|
-
copiedLabel: "Copi\xE9",
|
|
937
|
-
ariaLabel: "copier le lien"
|
|
938
|
-
},
|
|
939
|
-
salary: {
|
|
940
|
-
comparisonHeadlineAverage: "Salaire moyen",
|
|
941
|
-
perYearSuffix: "/ an",
|
|
942
|
-
comparisonPercentile25Label: "25e percentile",
|
|
943
|
-
medianLabel: "M\xE9diane",
|
|
944
|
-
comparisonPercentile75Label: "75e percentile",
|
|
945
|
-
basedOnLabel: "Bas\xE9 sur",
|
|
946
|
-
seniorityTableHeaderLevel: "Niveau",
|
|
947
|
-
seniorityTableHeaderAvg: "Moyenne",
|
|
948
|
-
boardBaselineLabel: "R\xE9f\xE9rence du site",
|
|
949
|
-
seniorityTableHeaderDiff: "vs site",
|
|
950
|
-
faqHeading: "Questions fr\xE9quentes"
|
|
951
|
-
},
|
|
952
|
-
nav: {
|
|
953
|
-
home: "Offres",
|
|
954
|
-
companies: "Entreprises",
|
|
955
|
-
pricing: "Tarifs",
|
|
956
|
-
talent: "Talents",
|
|
957
|
-
post: "Publier une offre",
|
|
958
|
-
blog: "Blog"
|
|
959
|
-
},
|
|
960
|
-
footer: {
|
|
961
|
-
forCandidatesHeading: "Pour les candidats",
|
|
962
|
-
forCompaniesHeading: "Pour les entreprises",
|
|
963
|
-
resourcesHeading: "Ressources",
|
|
964
|
-
aboutHeading: "\xC0 propos",
|
|
965
|
-
websiteLabel: "Site web",
|
|
966
|
-
aboutLabel: "\xC0 propos",
|
|
967
|
-
contactLabel: "Nous contacter",
|
|
968
|
-
locationsLabel: "Lieux",
|
|
969
|
-
salariesLabel: "Salaires",
|
|
970
|
-
sitemapLabel: "Plan du site",
|
|
971
|
-
termsOfServiceLabel: "Conditions d'utilisation",
|
|
972
|
-
privacyPolicyLabel: "Politique de confidentialit\xE9",
|
|
973
|
-
cookiePolicyLabel: "Politique de cookies",
|
|
974
|
-
impressumLabel: "Mentions l\xE9gales",
|
|
975
|
-
allRightsReservedText: "Tous droits r\xE9serv\xE9s.",
|
|
976
|
-
copyrightPrefix: "\xA9 {{year}} {{board_name}}.",
|
|
977
|
-
poweredByText: "Propuls\xE9 par",
|
|
978
|
-
defaultDescription: "D\xE9couvrez les derni\xE8res offres de {{board_name}}."
|
|
979
|
-
},
|
|
980
|
-
breadcrumbs: {
|
|
981
|
-
home: "Accueil",
|
|
982
|
-
jobs: "Offres",
|
|
983
|
-
locations: "Lieux",
|
|
984
|
-
salaries: "Salaires",
|
|
985
|
-
companies: "Entreprises",
|
|
986
|
-
skills: "Comp\xE9tences",
|
|
987
|
-
titles: "Intitul\xE9s de poste",
|
|
988
|
-
blog: "Blog",
|
|
989
|
-
post: "Publier une offre",
|
|
990
|
-
pricing: "Tarifs",
|
|
991
|
-
about: "\xC0 propos",
|
|
992
|
-
impressum: "Mentions l\xE9gales",
|
|
993
|
-
termsOfService: "Conditions d'utilisation",
|
|
994
|
-
privacyPolicy: "Politique de confidentialit\xE9",
|
|
995
|
-
cookiePolicy: "Politique de cookies",
|
|
996
|
-
talent: "Talents"
|
|
997
|
-
},
|
|
998
|
-
pagination: {
|
|
999
|
-
ariaLabel: "Pagination",
|
|
1000
|
-
previousLabel: "Pr\xE9c\xE9dent",
|
|
1001
|
-
nextLabel: "Suivant"
|
|
1002
|
-
},
|
|
1003
|
-
blog: {
|
|
1004
|
-
bylineLabel: "Par",
|
|
1005
|
-
readingTimeLabel: "min de lecture",
|
|
1006
|
-
searchLabel: "Recherche",
|
|
1007
|
-
searchPlaceholder: "Rechercher un article...",
|
|
1008
|
-
clearButtonLabel: "Effacer la recherche",
|
|
1009
|
-
tagFilterLabel: "Th\xE8mes",
|
|
1010
|
-
tagFilterAllLabel: "Tous",
|
|
1011
|
-
emptyTitle: "Aucun article correspondant",
|
|
1012
|
-
emptyDescription: "Essayez de modifier votre recherche ou de r\xE9initialiser les filtres.",
|
|
1013
|
-
emptyResetLabel: "R\xE9initialiser les filtres"
|
|
1014
|
-
},
|
|
1015
|
-
entity: {
|
|
1016
|
-
jobSingular: "offre",
|
|
1017
|
-
jobPlural: "offres",
|
|
1018
|
-
companySingular: "entreprise",
|
|
1019
|
-
companyPlural: "entreprises"
|
|
1020
|
-
}
|
|
1021
|
-
};
|
|
1022
|
-
var CATALOGS = { en: EN2, de: DE2, fr: FR };
|
|
1023
|
-
function mergeGroup(group, overrides) {
|
|
1024
|
-
if (!overrides) return group;
|
|
1025
|
-
const out = { ...group };
|
|
1026
|
-
for (const [key, value] of Object.entries(group)) {
|
|
1027
|
-
if (typeof value !== "string") continue;
|
|
1028
|
-
const override = overrides[key];
|
|
1029
|
-
if (typeof override === "string" && override.trim() !== "") {
|
|
1030
|
-
out[key] = override;
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1033
|
-
return out;
|
|
1034
|
-
}
|
|
1035
|
-
var GROUP_OVERRIDE_SOURCE = {
|
|
1036
|
-
jobCard: "jobCardLabels",
|
|
1037
|
-
jobSearch: "jobSearchLabels",
|
|
1038
|
-
jobDetail: "jobCardLabels",
|
|
1039
|
-
apply: "jobCardLabels",
|
|
1040
|
-
alerts: "jobCardLabels",
|
|
1041
|
-
copyLink: "jobCardLabels",
|
|
1042
|
-
salary: "salaryLabels",
|
|
1043
|
-
nav: "navLabels",
|
|
1044
|
-
footer: "footerLabels",
|
|
1045
|
-
breadcrumbs: "breadcrumbsLabels",
|
|
1046
|
-
pagination: "globalPaginationLabels",
|
|
1047
|
-
blog: "blogSharedLabels",
|
|
1048
|
-
entity: "entityLabels"
|
|
369
|
+
// src/paths/index.ts
|
|
370
|
+
function encodePathSegment(segment) {
|
|
371
|
+
try {
|
|
372
|
+
return encodeURIComponent(decodeURIComponent(segment)).replace(
|
|
373
|
+
/%3A/gi,
|
|
374
|
+
":"
|
|
375
|
+
);
|
|
376
|
+
} catch {
|
|
377
|
+
return encodeURIComponent(segment).replace(/%3A/gi, ":");
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function jobsCategoryPath(categorySlug) {
|
|
381
|
+
return `/jobs/${encodePathSegment(categorySlug)}`;
|
|
382
|
+
}
|
|
383
|
+
function jobsLocationPath(placeSlug) {
|
|
384
|
+
return `/jobs/locations/${encodePathSegment(placeSlug)}`;
|
|
385
|
+
}
|
|
386
|
+
function jobsLocationCategoryPath(placeSlug, categorySlug) {
|
|
387
|
+
return `${jobsLocationPath(placeSlug)}/${encodePathSegment(categorySlug)}`;
|
|
388
|
+
}
|
|
389
|
+
var BOARD_PATHS = {
|
|
390
|
+
home: "/",
|
|
391
|
+
jobs: "/jobs",
|
|
392
|
+
companies: "/companies",
|
|
393
|
+
salaries: "/salaries",
|
|
394
|
+
salaryCompanies: "/salaries/companies",
|
|
395
|
+
salaryTitles: "/salaries/titles",
|
|
396
|
+
salarySkills: "/salaries/skills",
|
|
397
|
+
salaryLocations: "/salaries/locations",
|
|
398
|
+
blog: "/blog",
|
|
399
|
+
about: "/about",
|
|
400
|
+
privacyPolicy: "/privacy-policy",
|
|
401
|
+
termsOfService: "/terms-of-service",
|
|
402
|
+
cookiePolicy: "/cookie-policy",
|
|
403
|
+
impressum: "/impressum",
|
|
404
|
+
talent: "/talent",
|
|
405
|
+
employers: "/employers",
|
|
406
|
+
/**
|
|
407
|
+
* Alert-email surfaces. Promoted out of raw literals in
|
|
408
|
+
* the job-alerts composer so /go indirection and email composition share
|
|
409
|
+
* one definition.
|
|
410
|
+
*/
|
|
411
|
+
alertsManage: "/alerts/manage",
|
|
412
|
+
alertsConfirm: "/alerts/confirm"
|
|
1049
413
|
};
|
|
1050
|
-
function uiCopy(language, labels) {
|
|
1051
|
-
const catalog = language && CATALOGS[language] || EN2;
|
|
1052
|
-
if (!labels) return catalog;
|
|
1053
|
-
const resolved = {};
|
|
1054
|
-
for (const [group, source] of Object.entries(GROUP_OVERRIDE_SOURCE)) {
|
|
1055
|
-
resolved[group] = mergeGroup(catalog[group], labels[source]);
|
|
1056
|
-
}
|
|
1057
|
-
return resolved;
|
|
1058
|
-
}
|
|
1059
414
|
|
|
1060
415
|
// src/seo/breadcrumbs.ts
|
|
1061
|
-
function buildJobBreadcrumbs(job
|
|
1062
|
-
const copy = uiCopy(language, labels).breadcrumbs;
|
|
416
|
+
function buildJobBreadcrumbs(job) {
|
|
1063
417
|
const crumbs = [
|
|
1064
|
-
{
|
|
1065
|
-
{
|
|
418
|
+
{ kind: "home", path: BOARD_PATHS.home },
|
|
419
|
+
{ kind: "jobs", path: BOARD_PATHS.jobs }
|
|
1066
420
|
];
|
|
1067
421
|
let lastPlaceSlug = null;
|
|
1068
422
|
for (const place of job.placeHierarchy) {
|
|
1069
423
|
lastPlaceSlug = place.slug;
|
|
1070
|
-
crumbs.push({
|
|
424
|
+
crumbs.push({
|
|
425
|
+
kind: "place",
|
|
426
|
+
name: place.name,
|
|
427
|
+
path: jobsLocationPath(place.slug)
|
|
428
|
+
});
|
|
1071
429
|
}
|
|
1072
430
|
const primaryCategory = job.categories[0];
|
|
1073
431
|
if (primaryCategory) {
|
|
1074
432
|
crumbs.push({
|
|
433
|
+
kind: "category",
|
|
1075
434
|
name: primaryCategory.name,
|
|
1076
|
-
path: lastPlaceSlug ?
|
|
435
|
+
path: lastPlaceSlug ? jobsLocationCategoryPath(lastPlaceSlug, primaryCategory.slug) : jobsCategoryPath(primaryCategory.slug)
|
|
1077
436
|
});
|
|
1078
437
|
}
|
|
1079
|
-
crumbs.push({ name: job.title });
|
|
438
|
+
crumbs.push({ kind: "job", name: job.title });
|
|
1080
439
|
return crumbs;
|
|
1081
440
|
}
|
|
1082
441
|
|
|
1083
|
-
// src/format/company-intro.ts
|
|
1084
|
-
var NAMED_ENTITIES = {
|
|
1085
|
-
amp: "&",
|
|
1086
|
-
lt: "<",
|
|
1087
|
-
gt: ">",
|
|
1088
|
-
quot: '"',
|
|
1089
|
-
apos: "'",
|
|
1090
|
-
nbsp: " "
|
|
1091
|
-
};
|
|
1092
|
-
function decodeEntities(input) {
|
|
1093
|
-
return input.replace(/&(#\d+|#x[0-9a-f]+|[a-z]+);/gi, (match, body) => {
|
|
1094
|
-
const token = String(body);
|
|
1095
|
-
if (token[0] === "#") {
|
|
1096
|
-
const code = token[1] === "x" || token[1] === "X" ? parseInt(token.slice(2), 16) : parseInt(token.slice(1), 10);
|
|
1097
|
-
if (!Number.isInteger(code) || code < 0 || code > 1114111) return match;
|
|
1098
|
-
return String.fromCodePoint(code);
|
|
1099
|
-
}
|
|
1100
|
-
return NAMED_ENTITIES[token.toLowerCase()] ?? match;
|
|
1101
|
-
});
|
|
1102
|
-
}
|
|
1103
|
-
function companyIntro(summary, description) {
|
|
1104
|
-
const trimmedSummary = summary?.trim();
|
|
1105
|
-
if (trimmedSummary) return trimmedSummary;
|
|
1106
|
-
if (!description) return null;
|
|
1107
|
-
const text = decodeEntities(
|
|
1108
|
-
description.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<br\s*\/?>/gi, " ").replace(/<\/(p|div|li|h[1-6])>/gi, " ").replace(/<[^>]*>/g, "")
|
|
1109
|
-
).replace(/\s+/g, " ").trim();
|
|
1110
|
-
if (!text) return null;
|
|
1111
|
-
const [first] = text.split(/(?<=[.!?])\s+/);
|
|
1112
|
-
return first ?? text;
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
442
|
// src/format/countries.ts
|
|
1116
443
|
var COUNTRY_CODES = [
|
|
1117
444
|
"AD",
|
|
@@ -1379,8 +706,14 @@ function collator(language) {
|
|
|
1379
706
|
}
|
|
1380
707
|
}
|
|
1381
708
|
function countryOptions(language) {
|
|
1382
|
-
const
|
|
1383
|
-
|
|
709
|
+
const locale = normalizeLocale(language);
|
|
710
|
+
if (!locale) {
|
|
711
|
+
return COUNTRY_CODES.map((code) => ({ code, name: code })).sort(
|
|
712
|
+
(a, b) => a.code < b.code ? -1 : a.code > b.code ? 1 : 0
|
|
713
|
+
);
|
|
714
|
+
}
|
|
715
|
+
const names = regionNames(locale);
|
|
716
|
+
const compare = collator(locale).compare;
|
|
1384
717
|
return COUNTRY_CODES.map((code) => ({
|
|
1385
718
|
code,
|
|
1386
719
|
name: names?.of(code) ?? code
|
|
@@ -1388,24 +721,27 @@ function countryOptions(language) {
|
|
|
1388
721
|
}
|
|
1389
722
|
|
|
1390
723
|
// src/format/custom-fields.ts
|
|
724
|
+
function resolveMultiSelectLabels(def, raw) {
|
|
725
|
+
if (!Array.isArray(raw) || raw.length === 0) return null;
|
|
726
|
+
const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
|
|
727
|
+
return labels.length > 0 ? labels : null;
|
|
728
|
+
}
|
|
1391
729
|
function renderTextValue(def, raw) {
|
|
1392
730
|
switch (def.type) {
|
|
1393
731
|
case "number":
|
|
1394
|
-
return
|
|
732
|
+
return null;
|
|
733
|
+
case "multi_select":
|
|
734
|
+
return null;
|
|
1395
735
|
case "single_select": {
|
|
1396
736
|
if (typeof raw !== "string") return null;
|
|
1397
737
|
return def.options?.find((o) => o.key === raw)?.label ?? null;
|
|
1398
738
|
}
|
|
1399
|
-
case "multi_select": {
|
|
1400
|
-
if (!Array.isArray(raw) || raw.length === 0) return null;
|
|
1401
|
-
const labels = raw.map((key) => def.options?.find((o) => o.key === key)?.label).filter((label) => Boolean(label));
|
|
1402
|
-
return labels.length > 0 ? labels.join(", ") : null;
|
|
1403
|
-
}
|
|
1404
739
|
default:
|
|
1405
740
|
return typeof raw === "string" && raw.trim() !== "" ? raw : null;
|
|
1406
741
|
}
|
|
1407
742
|
}
|
|
1408
|
-
function resolveCustomFieldDisplay(definitions, values) {
|
|
743
|
+
function resolveCustomFieldDisplay(language, definitions, values) {
|
|
744
|
+
void language;
|
|
1409
745
|
const entries = [];
|
|
1410
746
|
for (const def of definitions ?? []) {
|
|
1411
747
|
const raw = values?.[def.key];
|
|
@@ -1420,6 +756,29 @@ function resolveCustomFieldDisplay(definitions, values) {
|
|
|
1420
756
|
}
|
|
1421
757
|
continue;
|
|
1422
758
|
}
|
|
759
|
+
if (def.type === "number") {
|
|
760
|
+
if (typeof raw === "number" && Number.isFinite(raw)) {
|
|
761
|
+
entries.push({
|
|
762
|
+
key: def.key,
|
|
763
|
+
label: def.label,
|
|
764
|
+
kind: "number",
|
|
765
|
+
value: raw
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
continue;
|
|
769
|
+
}
|
|
770
|
+
if (def.type === "multi_select") {
|
|
771
|
+
const labels = resolveMultiSelectLabels(def, raw);
|
|
772
|
+
if (labels) {
|
|
773
|
+
entries.push({
|
|
774
|
+
key: def.key,
|
|
775
|
+
label: def.label,
|
|
776
|
+
kind: "multi_select",
|
|
777
|
+
values: labels
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
1423
782
|
const value = renderTextValue(def, raw);
|
|
1424
783
|
if (value !== null) {
|
|
1425
784
|
entries.push({ key: def.key, label: def.label, kind: "text", value });
|