Skip to content

Commit

Permalink
feat(i18n): format currency based on Sanity locale fields
Browse files Browse the repository at this point in the history
  • Loading branch information
mathiazom committed Oct 10, 2024
1 parent 45b6824 commit 61a57b9
Show file tree
Hide file tree
Showing 12 changed files with 192 additions and 7 deletions.
6 changes: 6 additions & 0 deletions src/app/(main)/[lang]/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import { fetchSeoData, generateMetadataFromSeo } from "src/utils/seo";
import { CompanyLocation } from "studio/lib/interfaces/companyDetails";
import { CompensationsPage } from "studio/lib/interfaces/compensations";
import { LegalDocument } from "studio/lib/interfaces/legalDocuments";
import { LocaleDocument } from "studio/lib/interfaces/locale";
import { BlogPage, PageBuilder, Post } from "studio/lib/interfaces/pages";
import { CustomerCasePage } from "studio/lib/interfaces/specialPages";
import {
COMPANY_LOCATIONS_QUERY,
LEGAL_DOCUMENTS_BY_SLUG_AND_LANG_QUERY,
} from "studio/lib/queries/admin";
import { LOCALE_QUERY } from "studio/lib/queries/locale";
import {
BLOG_PAGE_QUERY,
POSTS_QUERY,
Expand Down Expand Up @@ -66,6 +68,7 @@ async function Page({ params }: Props) {
initialLocationsData,
initialCustomerCases,
initialLegalDocument,
initialLocale,
] = await Promise.all([
loadStudioQuery<PageBuilder>(SLUG_QUERY, { slug }, { perspective }),
loadStudioQuery<BlogPage>(BLOG_PAGE_QUERY, { slug }, { perspective }),
Expand All @@ -89,6 +92,7 @@ async function Page({ params }: Props) {
{ slug, language: lang },
{ perspective },
),
loadStudioQuery<LocaleDocument>(LOCALE_QUERY, {}, { perspective }),
]);

if (initialPage.data) {
Expand Down Expand Up @@ -139,11 +143,13 @@ async function Page({ params }: Props) {
<CompensationsPreview
initialCompensations={initialCompensationsPage}
initialLocations={initialLocationsData}
initialLocale={initialLocale}
/>
) : (
<Compensations
compensations={initialCompensationsPage.data}
locations={initialLocationsData.data}
locale={initialLocale.data}
/>
);
}
Expand Down
21 changes: 17 additions & 4 deletions src/compensations/Compensations.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import {
RadioButtonGroup,
} from "src/components/forms/radioButtonGroup/RadioButtonGroup";
import Text from "src/components/text/Text";
import { formatAsCurrency } from "src/utils/i18n";
import { CompanyLocation } from "studio/lib/interfaces/companyDetails";
import { CompensationsPage } from "studio/lib/interfaces/compensations";
import { LocaleDocument } from "studio/lib/interfaces/locale";

import styles from "./compensations.module.css";
import BenefitsByLocation from "./components/benefitsByLocation/BenefitsByLocation";
Expand All @@ -26,14 +28,19 @@ import {
interface CompensationsProps {
compensations: CompensationsPage;
locations: CompanyLocation[];
locale: LocaleDocument;
}

interface SalaryCalculatorFormState {
examinationYear: number;
selectedDegree: Degree;
}

const Compensations = ({ compensations, locations }: CompensationsProps) => {
const Compensations = ({
compensations,
locations,
locale,
}: CompensationsProps) => {
const [selectedLocation, setSelectedLocation] = useState<string>(
locations[0]._id,
);
Expand Down Expand Up @@ -121,19 +128,25 @@ const Compensations = ({ compensations, locations }: CompensationsProps) => {
/>
{salary !== null ? (
<div aria-live="polite">
<Text> Du vil få en årlig lønn på {salary}</Text>
<Text>
{`Du vil få en årlig lønn på ${formatAsCurrency(salary, locale.locale, locale.currency)}`}
</Text>
{compensations.pensionPercent && (
<Text>
Du vil få en årlig pensjon på omtrent{" "}
{calculatePension(salary, compensations.pensionPercent)}
{formatAsCurrency(
calculatePension(salary, compensations.pensionPercent),
locale.locale,
locale.currency,
)}
</Text>
)}
</div>
) : null}
</>
)}
{yearlyBonusesForLocation && (
<YearlyBonuses bonuses={yearlyBonusesForLocation} />
<YearlyBonuses bonuses={yearlyBonusesForLocation} locale={locale} />
)}
<BenefitsByLocation benefits={benefitsFilteredByLocation} />
</div>
Expand Down
12 changes: 11 additions & 1 deletion src/compensations/CompensationsPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,23 @@ import { Suspense } from "react";

import { CompanyLocation } from "studio/lib/interfaces/companyDetails";
import { CompensationsPage } from "studio/lib/interfaces/compensations";
import { LocaleDocument } from "studio/lib/interfaces/locale";
import { COMPANY_LOCATIONS_QUERY } from "studio/lib/queries/admin";
import { LOCALE_QUERY } from "studio/lib/queries/locale";
import { COMPENSATIONS_PAGE_QUERY } from "studio/lib/queries/specialPages";

import Compensations from "./Compensations";

interface CompensationsPreviewProps {
initialCompensations: QueryResponseInitial<CompensationsPage>;
initialLocations: QueryResponseInitial<CompanyLocation[]>;
initialLocale: QueryResponseInitial<LocaleDocument>;
}

const CompensationsPreview = ({
initialCompensations,
initialLocations,
initialLocale,
}: CompensationsPreviewProps) => {
const { data: compensationsData } = useQuery<CompensationsPage>(
COMPENSATIONS_PAGE_QUERY,
Expand All @@ -30,16 +34,22 @@ const CompensationsPreview = ({
{ initial: initialLocations },
);

const { data: locale } = useQuery<LocaleDocument>(LOCALE_QUERY, {
initial: initialLocale,
});

compensationsData.salariesByLocation = stegaClean(
compensationsData.salariesByLocation,
);

return (
locationData && (
locationData &&
locale && (
<Suspense>
<Compensations
compensations={compensationsData}
locations={locationData}
locale={locale}
/>
</Suspense>
)
Expand Down
13 changes: 11 additions & 2 deletions src/compensations/components/yearlyBonuses/YearlyBonuses.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import Text from "src/components/text/Text";
import { formatAsCurrency } from "src/utils/i18n";
import { BonusPage } from "studio/lib/interfaces/compensations";
import { LocaleDocument } from "studio/lib/interfaces/locale";

import styles from "./yearlyBonuses.module.css";

interface YearlyBonusesProps {
bonuses: BonusPage[];
locale: LocaleDocument;
}

const YearlyBonuses = ({ bonuses }: YearlyBonusesProps) => {
const YearlyBonuses = ({ bonuses, locale }: YearlyBonusesProps) => {
return (
<div className={styles.wrapper}>
<Text type={"h3"}>Historisk bonus</Text>
Expand All @@ -31,7 +34,13 @@ const YearlyBonuses = ({ bonuses }: YearlyBonusesProps) => {
<Text type={"small"}>{bonus.year}</Text>
</th>
<td className={styles.bonusCell}>
<Text type={"small"}>{bonus.bonus}</Text>
<Text type={"small"}>
{formatAsCurrency(
bonus.bonus,
locale.locale,
locale.currency,
)}
</Text>
</td>
</tr>
))}
Expand Down
9 changes: 9 additions & 0 deletions src/utils/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export function formatAsCurrency(
number: number,
locale: string,
currency: string,
) {
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(
number,
);
}
11 changes: 11 additions & 0 deletions studio/deskStructure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
CaseIcon,
CogIcon,
DoubleChevronRightIcon,
EarthGlobeIcon,
HeartIcon,
ImagesIcon,
InfoOutlineIcon,
Expand All @@ -24,6 +25,7 @@ import { languageSettingsID } from "./schemas/documents/languageSettings";
import { pageBuilderID } from "./schemas/documents/pageBuilder";
import { brandAssetsID } from "./schemas/documents/siteSettings/brandAssets";
import { brokenLinkID } from "./schemas/documents/siteSettings/brokenLink";
import { localeID } from "./schemas/documents/siteSettings/locale";
import { soMeLinksID } from "./schemas/documents/siteSettings/socialMediaProfiles";
import { customerCasesPageID } from "./schemas/documents/specialPages/customerCasesPage";

Expand Down Expand Up @@ -102,6 +104,15 @@ const siteSettingSection = (S: StructureBuilder) =>
.documentId(languageSettingsID)
.title("Languages"),
),
S.listItem()
.title("Region")
.icon(EarthGlobeIcon)
.child(
S.document()
.schemaType(localeID)
.documentId(localeID)
.title("Region"),
),
S.listItem()
.title("Default SEO")
.icon(SearchIcon)
Expand Down
4 changes: 4 additions & 0 deletions studio/lib/interfaces/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface LocaleDocument {
locale: string;
currency: string;
}
5 changes: 5 additions & 0 deletions studio/lib/queries/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { groq } from "next-sanity";

import { localeID } from "studio/schemas/documents/siteSettings/locale";

export const LOCALE_QUERY = groq`*[_type == "${localeID}" && _id == "${localeID}"][0]`;
2 changes: 2 additions & 0 deletions studio/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import pageBuilder from "./schemas/documents/pageBuilder";
import posts from "./schemas/documents/post";
import brandAssets from "./schemas/documents/siteSettings/brandAssets";
import brokenLink from "./schemas/documents/siteSettings/brokenLink";
import locale from "./schemas/documents/siteSettings/locale";
import navigationManager from "./schemas/documents/siteSettings/navigationManager";
import socialMediaLinks from "./schemas/documents/siteSettings/socialMediaProfiles";
import customerCasesPage from "./schemas/documents/specialPages/customerCasesPage";
Expand Down Expand Up @@ -43,5 +44,6 @@ export const schema: { types: SchemaTypeDefinition[] } = {
defaultSeo,
brandAssets,
languageSettings,
locale,
],
};
41 changes: 41 additions & 0 deletions studio/schemas/documents/siteSettings/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineField, defineType } from "sanity";

import { relevantCurrencies } from "studio/utils/currencies";
import { relevantLocales } from "studio/utils/locales";

export const localeID = "locale";

const locale = defineType({
name: localeID,
type: "document",
fields: [
defineField({
name: "locale",
type: "string",
title: "Region",
description: "Select the most relevant region for the website.",
initialValue: relevantLocales[0].code,
options: {
list: relevantLocales.map(({ title, code }) => ({
title: `${title} (${code})`,
value: code,
})),
},
}),
defineField({
name: "currency",
type: "string",
title: "Currency",
description: "Select the most relevant currency for the website.",
initialValue: relevantCurrencies[0].code,
options: {
list: relevantCurrencies.map(({ title, code }) => ({
title: `${title} (${code})`,
value: code,
})),
},
}),
],
});

export default locale;
36 changes: 36 additions & 0 deletions studio/utils/currencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
interface Currency {
title: string;
code: string;
}

// ISO 4217 currency codes (https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes)
export const relevantCurrencies: Currency[] = [
{
title: "🇳🇴 Norwegian Krone",
code: "NOK",
},
{
title: "🇸🇪 Swedish Krona",
code: "SEK",
},
{
title: "🇪🇺 Euro",
code: "EUR",
},
{
title: "🇬🇧 British Pound",
code: "GBP",
},
{
title: "🇺🇸 US Dollar",
code: "USD",
},
{
title: "🇩🇰 Danish Krone",
code: "DKK",
},
{
title: "🇮🇸 Icelandic Króna",
code: "ISK",
},
];
39 changes: 39 additions & 0 deletions studio/utils/locales.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
interface Locale {
title: string;
code: string;
}

export const relevantLocales: Locale[] = [
{
title: "🇳🇴 Norway",
code: "nb-NO",
},
// {
// title: "🇳🇴 Norway",
// code: "nn-NO",
// },
{
title: "🇸🇪 Sweden",
code: "sv-SE",
},
{
title: "🇬🇧 United Kingdom",
code: "en-GB",
},
{
title: "🇺🇸 United States",
code: "en-US",
},
{
title: "🇩🇰 Denmark",
code: "da-DK",
},
{
title: "🇫🇮 Finland",
code: "fi-FI",
},
{
title: "🇮🇸 Iceland",
code: "is-IS",
},
];

0 comments on commit 61a57b9

Please sign in to comment.