From 3af43b3366ae88f3b9a7e4ab75526ac6e68a6077 Mon Sep 17 00:00:00 2001 From: Hampus Date: Fri, 11 Sep 2026 15:48:56 +0200 Subject: [PATCH] feat(api): add SEK, DKK and NOK as localized currencies (#2684) --- fluxer_api/src/api/Config.ts | 12 + fluxer_api/src/api/config/APIConfig.ts | 12 + fluxer_api/src/api/openapi/openapi.json | 28 +- fluxer_api/src/api/stripe/ProductRegistry.ts | 111 +++++++ .../stripe/services/PremiumStateService.ts | 3 + .../stripe/services/StripeCheckoutService.ts | 54 ++++ .../tests/ProductRegistryLegacyPrices.test.ts | 8 +- .../tests/StripeNordicCurrencies.test.ts | 288 ++++++++++++++++++ .../test/msw/handlers/StripeApiHandlers.ts | 40 ++- fluxer_api/src/api/utils/CurrencyUtils.ts | 20 +- .../src/api/utils/tests/CurrencyUtils.test.ts | 35 ++- fluxer_api/vitest.config.ts | 1 + .../src/content/docs/http-api/premium.mdx | 3 + .../content/docs/operator/configuration.mdx | 2 +- .../src/config_loader/EnvironmentOverrides.ts | 12 + packages/i18n/marketing/ar.po | 12 +- packages/i18n/marketing/bg.po | 12 +- packages/i18n/marketing/cs.po | 12 +- packages/i18n/marketing/da.po | 12 +- packages/i18n/marketing/de.po | 12 +- packages/i18n/marketing/el.po | 12 +- packages/i18n/marketing/en-GB.po | 12 +- packages/i18n/marketing/en-US.po | 12 +- packages/i18n/marketing/es-419.po | 12 +- packages/i18n/marketing/es-ES.po | 12 +- packages/i18n/marketing/fi.po | 12 +- packages/i18n/marketing/fr.po | 12 +- packages/i18n/marketing/he.po | 12 +- packages/i18n/marketing/hi.po | 12 +- packages/i18n/marketing/hr.po | 12 +- packages/i18n/marketing/hu.po | 12 +- packages/i18n/marketing/id.po | 12 +- packages/i18n/marketing/it.po | 12 +- packages/i18n/marketing/ja.po | 11 +- packages/i18n/marketing/ko.po | 11 +- packages/i18n/marketing/lt.po | 12 +- packages/i18n/marketing/nl.po | 12 +- packages/i18n/marketing/no.po | 12 +- packages/i18n/marketing/pl.po | 12 +- packages/i18n/marketing/pt-BR.po | 12 +- packages/i18n/marketing/ro.po | 12 +- packages/i18n/marketing/ru.po | 12 +- packages/i18n/marketing/sv-SE.po | 12 +- packages/i18n/marketing/th.po | 12 +- packages/i18n/marketing/tr.po | 12 +- packages/i18n/marketing/uk.po | 12 +- packages/i18n/marketing/vi.po | 12 +- packages/i18n/marketing/zh-CN.po | 11 +- packages/i18n/marketing/zh-TW.po | 11 +- .../src/domains/premium/PremiumSchemas.ts | 25 +- 50 files changed, 668 insertions(+), 390 deletions(-) create mode 100644 fluxer_api/src/api/stripe/tests/StripeNordicCurrencies.test.ts diff --git a/fluxer_api/src/api/Config.ts b/fluxer_api/src/api/Config.ts index a63a7f1cc..d62dbd30b 100644 --- a/fluxer_api/src/api/Config.ts +++ b/fluxer_api/src/api/Config.ts @@ -372,17 +372,29 @@ export function buildAPIConfigFromMaster(master: MasterConfig): APIConfig { monthlyUsd: master.integrations.stripe.prices.monthly_usd, monthlyEur: master.integrations.stripe.prices.monthly_eur, monthlyBrl: master.integrations.stripe.prices.monthly_brl, + monthlyDkk: master.integrations.stripe.prices.monthly_dkk, monthlyInr: master.integrations.stripe.prices.monthly_inr, + monthlyNok: master.integrations.stripe.prices.monthly_nok, monthlyPln: master.integrations.stripe.prices.monthly_pln, + monthlySek: master.integrations.stripe.prices.monthly_sek, monthlyTry: master.integrations.stripe.prices.monthly_try, yearlyUsd: master.integrations.stripe.prices.yearly_usd, yearlyEur: master.integrations.stripe.prices.yearly_eur, yearlyBrl: master.integrations.stripe.prices.yearly_brl, + yearlyDkk: master.integrations.stripe.prices.yearly_dkk, yearlyInr: master.integrations.stripe.prices.yearly_inr, + yearlyNok: master.integrations.stripe.prices.yearly_nok, yearlyPln: master.integrations.stripe.prices.yearly_pln, + yearlySek: master.integrations.stripe.prices.yearly_sek, yearlyTry: master.integrations.stripe.prices.yearly_try, gift1MonthUsd: master.integrations.stripe.prices.gift_1_month_usd, gift1MonthEur: master.integrations.stripe.prices.gift_1_month_eur, + gift1MonthSek: master.integrations.stripe.prices.gift_1_month_sek, + gift1YearSek: master.integrations.stripe.prices.gift_1_year_sek, + gift1MonthDkk: master.integrations.stripe.prices.gift_1_month_dkk, + gift1YearDkk: master.integrations.stripe.prices.gift_1_year_dkk, + gift1MonthNok: master.integrations.stripe.prices.gift_1_month_nok, + gift1YearNok: master.integrations.stripe.prices.gift_1_year_nok, gift1MonthBrl: master.integrations.stripe.prices.gift_1_month_brl, gift1MonthInr: master.integrations.stripe.prices.gift_1_month_inr, gift1MonthPln: master.integrations.stripe.prices.gift_1_month_pln, diff --git a/fluxer_api/src/api/config/APIConfig.ts b/fluxer_api/src/api/config/APIConfig.ts index d75868e6b..7dc431d89 100644 --- a/fluxer_api/src/api/config/APIConfig.ts +++ b/fluxer_api/src/api/config/APIConfig.ts @@ -226,17 +226,29 @@ export interface APIConfig { monthlyUsd?: string; monthlyEur?: string; monthlyBrl?: string; + monthlyDkk?: string; monthlyInr?: string; + monthlyNok?: string; monthlyPln?: string; + monthlySek?: string; monthlyTry?: string; yearlyUsd?: string; yearlyEur?: string; yearlyBrl?: string; + yearlyDkk?: string; yearlyInr?: string; + yearlyNok?: string; yearlyPln?: string; + yearlySek?: string; yearlyTry?: string; gift1MonthUsd?: string; gift1MonthEur?: string; + gift1MonthSek?: string; + gift1YearSek?: string; + gift1MonthDkk?: string; + gift1YearDkk?: string; + gift1MonthNok?: string; + gift1YearNok?: string; gift1MonthBrl?: string; gift1MonthInr?: string; gift1MonthPln?: string; diff --git a/fluxer_api/src/api/openapi/openapi.json b/fluxer_api/src/api/openapi/openapi.json index f107d10e3..be566f501 100644 --- a/fluxer_api/src/api/openapi/openapi.json +++ b/fluxer_api/src/api/openapi/openapi.json @@ -32064,9 +32064,9 @@ "description": "The amount the user is actually charged, in the currency minor unit" }, "currency": { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "description": "Currency of the charged amount" }, "billing_cycle": { @@ -32345,9 +32345,9 @@ "currency": { "anyOf": [ { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"] + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"] }, {"type": "null"} ], @@ -32418,9 +32418,9 @@ "currency": { "anyOf": [ { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"] + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"] }, {"type": "null"} ], @@ -32662,15 +32662,15 @@ "description": "Gift 1 year price amount in the currency minor unit" }, "currency": { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "description": "Currency for the prices" }, "gift_currency": { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "description": "Currency for gift prices" } }, @@ -32749,9 +32749,9 @@ "description": "Amount billed before the switch, in the currency minor unit" }, "currency": { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "description": "Currency of both amounts" } }, @@ -32788,9 +32788,9 @@ "description": "Amount billed before the switch, in the currency minor unit" }, "currency": { - "enum": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "enum": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "type": "string", - "x-enumNames": ["USD", "EUR", "BRL", "INR", "PLN", "TRY"], + "x-enumNames": ["USD", "EUR", "BRL", "DKK", "INR", "NOK", "PLN", "SEK", "TRY"], "description": "Currency of both amounts" } }, diff --git a/fluxer_api/src/api/stripe/ProductRegistry.ts b/fluxer_api/src/api/stripe/ProductRegistry.ts index ab97d307a..8354a8c07 100644 --- a/fluxer_api/src/api/stripe/ProductRegistry.ts +++ b/fluxer_api/src/api/stripe/ProductRegistry.ts @@ -56,8 +56,11 @@ const LEGACY_SLOT_CURRENCIES: Record = { usd: 'USD', eur: 'EUR', brl: 'BRL', + dkk: 'DKK', inr: 'INR', + nok: 'NOK', pln: 'PLN', + sek: 'SEK', try: 'TRY', }; @@ -109,6 +112,14 @@ export class ProductRegistry { currency: 'BRL', billingCycle: 'monthly', }); + this.registerProduct(prices.monthlyDkk, { + type: ProductType.MONTHLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: false, + currency: 'DKK', + billingCycle: 'monthly', + }); this.registerProduct(prices.monthlyInr, { type: ProductType.MONTHLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -117,6 +128,14 @@ export class ProductRegistry { currency: 'INR', billingCycle: 'monthly', }); + this.registerProduct(prices.monthlyNok, { + type: ProductType.MONTHLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: false, + currency: 'NOK', + billingCycle: 'monthly', + }); this.registerProduct(prices.monthlyPln, { type: ProductType.MONTHLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -125,6 +144,14 @@ export class ProductRegistry { currency: 'PLN', billingCycle: 'monthly', }); + this.registerProduct(prices.monthlySek, { + type: ProductType.MONTHLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: false, + currency: 'SEK', + billingCycle: 'monthly', + }); this.registerProduct(prices.monthlyTry, { type: ProductType.MONTHLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -157,6 +184,14 @@ export class ProductRegistry { currency: 'BRL', billingCycle: 'yearly', }); + this.registerProduct(prices.yearlyDkk, { + type: ProductType.YEARLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: false, + currency: 'DKK', + billingCycle: 'yearly', + }); this.registerProduct(prices.yearlyInr, { type: ProductType.YEARLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -165,6 +200,14 @@ export class ProductRegistry { currency: 'INR', billingCycle: 'yearly', }); + this.registerProduct(prices.yearlyNok, { + type: ProductType.YEARLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: false, + currency: 'NOK', + billingCycle: 'yearly', + }); this.registerProduct(prices.yearlyPln, { type: ProductType.YEARLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -173,6 +216,14 @@ export class ProductRegistry { currency: 'PLN', billingCycle: 'yearly', }); + this.registerProduct(prices.yearlySek, { + type: ProductType.YEARLY_SUBSCRIPTION, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: false, + currency: 'SEK', + billingCycle: 'yearly', + }); this.registerProduct(prices.yearlyTry, { type: ProductType.YEARLY_SUBSCRIPTION, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -209,6 +260,48 @@ export class ProductRegistry { isGift: true, currency: 'INR', }); + this.registerProduct(prices.gift1MonthDkk, { + type: ProductType.GIFT_1_MONTH, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: true, + currency: 'DKK', + }); + this.registerProduct(prices.gift1YearDkk, { + type: ProductType.GIFT_1_YEAR, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: true, + currency: 'DKK', + }); + this.registerProduct(prices.gift1MonthNok, { + type: ProductType.GIFT_1_MONTH, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: true, + currency: 'NOK', + }); + this.registerProduct(prices.gift1YearNok, { + type: ProductType.GIFT_1_YEAR, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: true, + currency: 'NOK', + }); + this.registerProduct(prices.gift1MonthSek, { + type: ProductType.GIFT_1_MONTH, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 1, + isGift: true, + currency: 'SEK', + }); + this.registerProduct(prices.gift1YearSek, { + type: ProductType.GIFT_1_YEAR, + premiumType: UserPremiumTypes.SUBSCRIPTION, + durationMonths: 12, + isGift: true, + currency: 'SEK', + }); this.registerProduct(prices.gift1MonthPln, { type: ProductType.GIFT_1_MONTH, premiumType: UserPremiumTypes.SUBSCRIPTION, @@ -319,12 +412,21 @@ export class ProductRegistry { if (normalizedCurrency === 'brl') { return billingCycle === 'monthly' ? (prices.monthlyBrl ?? null) : (prices.yearlyBrl ?? null); } + if (normalizedCurrency === 'dkk') { + return billingCycle === 'monthly' ? (prices.monthlyDkk ?? null) : (prices.yearlyDkk ?? null); + } if (normalizedCurrency === 'inr') { return billingCycle === 'monthly' ? (prices.monthlyInr ?? null) : (prices.yearlyInr ?? null); } + if (normalizedCurrency === 'nok') { + return billingCycle === 'monthly' ? (prices.monthlyNok ?? null) : (prices.yearlyNok ?? null); + } if (normalizedCurrency === 'pln') { return billingCycle === 'monthly' ? (prices.monthlyPln ?? null) : (prices.yearlyPln ?? null); } + if (normalizedCurrency === 'sek') { + return billingCycle === 'monthly' ? (prices.monthlySek ?? null) : (prices.yearlySek ?? null); + } if (normalizedCurrency === 'try') { return billingCycle === 'monthly' ? (prices.monthlyTry ?? null) : (prices.yearlyTry ?? null); } @@ -349,6 +451,15 @@ export class ProductRegistry { if (normalizedCurrency === 'inr') { return duration === 'gift_1_month' ? (prices.gift1MonthInr ?? null) : (prices.gift1YearInr ?? null); } + if (normalizedCurrency === 'dkk') { + return duration === 'gift_1_month' ? (prices.gift1MonthDkk ?? null) : (prices.gift1YearDkk ?? null); + } + if (normalizedCurrency === 'nok') { + return duration === 'gift_1_month' ? (prices.gift1MonthNok ?? null) : (prices.gift1YearNok ?? null); + } + if (normalizedCurrency === 'sek') { + return duration === 'gift_1_month' ? (prices.gift1MonthSek ?? null) : (prices.gift1YearSek ?? null); + } if (normalizedCurrency === 'pln') { return duration === 'gift_1_month' ? (prices.gift1MonthPln ?? null) : (prices.gift1YearPln ?? null); } diff --git a/fluxer_api/src/api/stripe/services/PremiumStateService.ts b/fluxer_api/src/api/stripe/services/PremiumStateService.ts index f74bc6ae0..3ce51ca09 100644 --- a/fluxer_api/src/api/stripe/services/PremiumStateService.ts +++ b/fluxer_api/src/api/stripe/services/PremiumStateService.ts @@ -104,8 +104,11 @@ function normalizeCurrency(value: string | null | undefined): Currency | null { currency === 'USD' || currency === 'EUR' || currency === 'BRL' || + currency === 'DKK' || currency === 'INR' || + currency === 'NOK' || currency === 'PLN' || + currency === 'SEK' || currency === 'TRY' ) { return currency; diff --git a/fluxer_api/src/api/stripe/services/StripeCheckoutService.ts b/fluxer_api/src/api/stripe/services/StripeCheckoutService.ts index 5b0f06eb7..f44d777ba 100644 --- a/fluxer_api/src/api/stripe/services/StripeCheckoutService.ts +++ b/fluxer_api/src/api/stripe/services/StripeCheckoutService.ts @@ -1108,6 +1108,15 @@ export class StripeCheckoutService { yearly: prices.yearlyBrl, currency, }; + case 'DKK': + if (!prices.monthlyDkk || !prices.yearlyDkk) { + return null; + } + return { + monthly: prices.monthlyDkk, + yearly: prices.yearlyDkk, + currency, + }; case 'INR': if (!prices.monthlyInr || !prices.yearlyInr) { return null; @@ -1117,6 +1126,15 @@ export class StripeCheckoutService { yearly: prices.yearlyInr, currency, }; + case 'NOK': + if (!prices.monthlyNok || !prices.yearlyNok) { + return null; + } + return { + monthly: prices.monthlyNok, + yearly: prices.yearlyNok, + currency, + }; case 'PLN': if (!prices.monthlyPln || !prices.yearlyPln) { return null; @@ -1126,6 +1144,15 @@ export class StripeCheckoutService { yearly: prices.yearlyPln, currency, }; + case 'SEK': + if (!prices.monthlySek || !prices.yearlySek) { + return null; + } + return { + monthly: prices.monthlySek, + yearly: prices.yearlySek, + currency, + }; case 'TRY': if (!prices.monthlyTry || !prices.yearlyTry) { return null; @@ -1175,6 +1202,33 @@ export class StripeCheckoutService { gift_1_year: prices.gift1YearInr, gift_currency: 'INR', }; + case 'DKK': + if (!prices.gift1MonthDkk || !prices.gift1YearDkk) { + return null; + } + return { + gift_1_month: prices.gift1MonthDkk, + gift_1_year: prices.gift1YearDkk, + gift_currency: 'DKK', + }; + case 'NOK': + if (!prices.gift1MonthNok || !prices.gift1YearNok) { + return null; + } + return { + gift_1_month: prices.gift1MonthNok, + gift_1_year: prices.gift1YearNok, + gift_currency: 'NOK', + }; + case 'SEK': + if (!prices.gift1MonthSek || !prices.gift1YearSek) { + return null; + } + return { + gift_1_month: prices.gift1MonthSek, + gift_1_year: prices.gift1YearSek, + gift_currency: 'SEK', + }; case 'PLN': if (!prices.gift1MonthPln || !prices.gift1YearPln) { return null; diff --git a/fluxer_api/src/api/stripe/tests/ProductRegistryLegacyPrices.test.ts b/fluxer_api/src/api/stripe/tests/ProductRegistryLegacyPrices.test.ts index 819109da7..6a391a790 100644 --- a/fluxer_api/src/api/stripe/tests/ProductRegistryLegacyPrices.test.ts +++ b/fluxer_api/src/api/stripe/tests/ProductRegistryLegacyPrices.test.ts @@ -119,16 +119,22 @@ describe('ProductRegistry - legacy prices', () => { monthly_usd: ['legacy_usd'], monthly_eur: ['legacy_eur'], monthly_brl: ['legacy_brl'], + monthly_dkk: ['legacy_dkk'], monthly_inr: ['legacy_inr'], + monthly_nok: ['legacy_nok'], monthly_pln: ['legacy_pln'], + monthly_sek: ['legacy_sek'], monthly_try: ['legacy_try'], }, ); expect(registry.getProduct('legacy_usd')?.currency).toBe('USD'); expect(registry.getProduct('legacy_eur')?.currency).toBe('EUR'); expect(registry.getProduct('legacy_brl')?.currency).toBe('BRL'); + expect(registry.getProduct('legacy_dkk')?.currency).toBe('DKK'); expect(registry.getProduct('legacy_inr')?.currency).toBe('INR'); + expect(registry.getProduct('legacy_nok')?.currency).toBe('NOK'); expect(registry.getProduct('legacy_pln')?.currency).toBe('PLN'); + expect(registry.getProduct('legacy_sek')?.currency).toBe('SEK'); expect(registry.getProduct('legacy_try')?.currency).toBe('TRY'); }); @@ -205,7 +211,7 @@ describe('ProductRegistry - legacy prices', () => { test('no legacy price id is ever returned by either price getter, for any cycle or currency', () => { const registry = buildRegistry(MOCK_PRICES, MOCK_LEGACY_PRICES); const legacyIds = new Set(Object.values(MOCK_LEGACY_PRICES).flatMap((ids) => ids ?? [])); - const currencies = ['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']; + const currencies = ['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']; const offered: Array = []; for (const currency of currencies) { diff --git a/fluxer_api/src/api/stripe/tests/StripeNordicCurrencies.test.ts b/fluxer_api/src/api/stripe/tests/StripeNordicCurrencies.test.ts new file mode 100644 index 000000000..c900ad142 --- /dev/null +++ b/fluxer_api/src/api/stripe/tests/StripeNordicCurrencies.test.ts @@ -0,0 +1,288 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes'; +import type { + CurrentSubscriptionPriceResponse, + PriceIdsResponse, +} from '@fluxer/schema/src/domains/premium/PremiumSchemas'; +import type {GeoipResult} from '@pkgs/geoip/src/GeoipLookup'; +import {afterAll, beforeAll, beforeEach, describe, expect, test, vi} from 'vitest'; +import {createTestAccount} from '../../auth/tests/AuthTestUtils'; +import {Config} from '../../Config'; +import {type ApiTestHarness, createApiTestHarness} from '../../test/ApiTestHarness'; +import {createStripeApiHandlers, type StripeApiHandlers} from '../../test/msw/handlers/StripeApiHandlers'; +import {server} from '../../test/msw/server'; +import {HTTP_STATUS} from '../../test/TestConstants'; +import {createBuilder} from '../../test/TestRequestBuilder'; + +const {lookupGeoipMock} = vi.hoisted(() => ({ + lookupGeoipMock: vi.fn(), +})); + +vi.mock('../../utils/IpUtils', async (importOriginal) => ({ + ...(await importOriginal()), + lookupGeoip: lookupGeoipMock, +})); + +const MOCK_PRICES = { + monthlyUsd: 'price_nordic_monthly_usd', + yearlyUsd: 'price_nordic_yearly_usd', + monthlyEur: 'price_nordic_monthly_eur', + yearlyEur: 'price_nordic_yearly_eur', + monthlyDkk: 'price_nordic_monthly_dkk', + yearlyDkk: 'price_nordic_yearly_dkk', + monthlyNok: 'price_nordic_monthly_nok', + yearlyNok: 'price_nordic_yearly_nok', + monthlyPln: 'price_nordic_monthly_pln', + yearlyPln: 'price_nordic_yearly_pln', + monthlySek: 'price_nordic_monthly_sek', + yearlySek: 'price_nordic_yearly_sek', + gift1MonthUsd: 'price_nordic_gift_1_month_usd', + gift1YearUsd: 'price_nordic_gift_1_year_usd', + gift1MonthEur: 'price_nordic_gift_1_month_eur', + gift1YearEur: 'price_nordic_gift_1_year_eur', +}; + +const MOCK_PRICE_SEEDS = { + [MOCK_PRICES.monthlyUsd]: {unit_amount: 499, currency: 'usd', interval: 'month' as const}, + [MOCK_PRICES.yearlyUsd]: {unit_amount: 4999, currency: 'usd', interval: 'year' as const}, + [MOCK_PRICES.monthlyEur]: {unit_amount: 499, currency: 'eur', interval: 'month' as const}, + [MOCK_PRICES.yearlyEur]: {unit_amount: 4999, currency: 'eur', interval: 'year' as const}, + [MOCK_PRICES.monthlyDkk]: {unit_amount: 3700, currency: 'dkk', interval: 'month' as const}, + [MOCK_PRICES.yearlyDkk]: {unit_amount: 37000, currency: 'dkk', interval: 'year' as const}, + [MOCK_PRICES.monthlyNok]: {unit_amount: 5400, currency: 'nok', interval: 'month' as const}, + [MOCK_PRICES.yearlyNok]: {unit_amount: 54000, currency: 'nok', interval: 'year' as const}, + [MOCK_PRICES.monthlyPln]: {unit_amount: 1900, currency: 'pln', interval: 'month' as const}, + [MOCK_PRICES.yearlyPln]: {unit_amount: 19000, currency: 'pln', interval: 'year' as const}, + [MOCK_PRICES.monthlySek]: {unit_amount: 5400, currency: 'sek', interval: 'month' as const}, + [MOCK_PRICES.yearlySek]: {unit_amount: 54000, currency: 'sek', interval: 'year' as const}, +}; + +const EUR_SUBSCRIPTION_ID = 'sub_nordic_legacy_eur'; + +function geoipCountry(countryCode: string | null): GeoipResult { + return { + countryCode, + normalizedIp: '203.0.113.10', + city: null, + region: null, + countryName: null, + }; +} + +describe('Nordic localized currencies', () => { + let harness: ApiTestHarness; + let stripeHandlers: StripeApiHandlers; + let originalPrices: typeof Config.stripe.prices | undefined; + + async function createPurchaser(): Promise { + const account = await createTestAccount(harness); + await createBuilder(harness, account.token) + .post(`/test/users/${account.userId}/security-flags`) + .body({email_verified: true}) + .execute(); + return account.token; + } + + function getPriceIds(countryCode: string | null): Promise { + lookupGeoipMock.mockResolvedValue(geoipCountry(countryCode)); + return createBuilder(harness, '').get('/premium/price-ids').expect(HTTP_STATUS.OK).execute(); + } + + beforeAll(async () => { + originalPrices = Config.stripe.prices; + Config.stripe.prices = MOCK_PRICES; + harness = await createApiTestHarness(); + }); + afterAll(async () => { + await harness.shutdown(); + Config.stripe.prices = originalPrices; + }); + beforeEach(() => { + Config.stripe.prices = MOCK_PRICES; + lookupGeoipMock.mockReset(); + lookupGeoipMock.mockResolvedValue(geoipCountry(null)); + stripeHandlers = createStripeApiHandlers({ + prices: MOCK_PRICE_SEEDS, + subscriptionsListEmpty: true, + subscriptions: { + [EUR_SUBSCRIPTION_ID]: { + customer: `cus_${EUR_SUBSCRIPTION_ID}`, + price_id: MOCK_PRICES.monthlyEur, + unit_amount: 499, + currency: 'eur', + interval: 'month', + item_id: `si_${EUR_SUBSCRIPTION_ID}`, + }, + }, + }); + server.use(...stripeHandlers.handlers); + }); + + describe('country routing', () => { + test('Sweden resolves to the SEK subscription catalog', async () => { + const priceIds = await getPriceIds('SE'); + expect(priceIds.currency).toBe('SEK'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlySek); + expect(priceIds.yearly).toBe(MOCK_PRICES.yearlySek); + }); + + test('Denmark resolves to the DKK subscription catalog', async () => { + const priceIds = await getPriceIds('DK'); + expect(priceIds.currency).toBe('DKK'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyDkk); + expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyDkk); + }); + + test('Norway resolves to the NOK subscription catalog', async () => { + const priceIds = await getPriceIds('NO'); + expect(priceIds.currency).toBe('NOK'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyNok); + expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyNok); + }); + + test('Poland still resolves to the PLN subscription catalog', async () => { + const priceIds = await getPriceIds('PL'); + expect(priceIds.currency).toBe('PLN'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyPln); + }); + + test.each(['DE', 'NL', 'FI', 'IE', 'IS'])('%s still resolves to the EUR subscription catalog', async (country) => { + const priceIds = await getPriceIds(country); + expect(priceIds.currency).toBe('EUR'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyEur); + expect(priceIds.yearly).toBe(MOCK_PRICES.yearlyEur); + }); + + test.each(['SE', 'DK', 'NO'])('%s keeps gifts on the base currency', async (country) => { + const priceIds = await getPriceIds(country); + expect(priceIds.gift_currency).toBe('EUR'); + expect(priceIds.gift_1_month).toBe(MOCK_PRICES.gift1MonthEur); + expect(priceIds.gift_1_year).toBe(MOCK_PRICES.gift1YearEur); + }); + + test('a Nordic gift falls through to USD when no EUR gift price is configured', async () => { + Config.stripe.prices = { + ...MOCK_PRICES, + gift1MonthEur: undefined, + gift1YearEur: undefined, + }; + const priceIds = await getPriceIds('SE'); + expect(priceIds.currency).toBe('SEK'); + expect(priceIds.gift_currency).toBe('USD'); + expect(priceIds.gift_1_month).toBe(MOCK_PRICES.gift1MonthUsd); + }); + + test('Sweden falls back to EUR while the SEK prices are still unconfigured', async () => { + Config.stripe.prices = { + ...MOCK_PRICES, + monthlySek: undefined, + yearlySek: undefined, + }; + const priceIds = await getPriceIds('SE'); + expect(priceIds.currency).toBe('EUR'); + expect(priceIds.monthly).toBe(MOCK_PRICES.monthlyEur); + expect(priceIds.gift_currency).toBe('EUR'); + }); + }); + + describe('checkout enforcement', () => { + test('rejects the EUR subscription price for a purchase that geolocates to Sweden', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + await createBuilder(harness, token) + .post('/stripe/checkout/subscription') + .body({price_id: MOCK_PRICES.monthlyEur, country_code: 'SE'}) + .expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION) + .execute(); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); + }); + + test('rejects the USD subscription price for a purchase that geolocates to Sweden', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + await createBuilder(harness, token) + .post('/stripe/checkout/subscription') + .body({price_id: MOCK_PRICES.monthlyUsd, country_code: 'US'}) + .expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION) + .execute(); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); + }); + + test('rejects a neighbouring Nordic price for a purchase that geolocates to Sweden', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + await createBuilder(harness, token) + .post('/stripe/checkout/subscription') + .body({price_id: MOCK_PRICES.monthlyDkk, country_code: 'DK'}) + .expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION) + .execute(); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); + }); + + test('accepts the SEK subscription price for a purchase that geolocates to Sweden', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + const response = await createBuilder<{url: string}>(harness, token) + .post('/stripe/checkout/subscription') + .body({price_id: MOCK_PRICES.monthlySek, country_code: 'SE'}) + .expect(HTTP_STATUS.OK) + .execute(); + expect(response.url).toContain('checkout.stripe.com'); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(1); + }); + + test('accepts the base gift price from inside Sweden', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + const response = await createBuilder<{url: string}>(harness, token) + .post('/stripe/checkout/gift') + .body({price_id: MOCK_PRICES.gift1MonthEur, country_code: 'SE'}) + .expect(HTTP_STATUS.OK) + .execute(); + expect(response.url).toContain('checkout.stripe.com'); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(1); + }); + + test('rejects the SEK subscription price on the gift endpoint', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const token = await createPurchaser(); + await createBuilder(harness, token) + .post('/stripe/checkout/gift') + .body({price_id: MOCK_PRICES.monthlySek, country_code: 'SE'}) + .expect(HTTP_STATUS.BAD_REQUEST, APIErrorCodes.STRIPE_INVALID_PRODUCT_CONFIGURATION) + .execute(); + expect(stripeHandlers.spies.createdCheckoutSessions).toHaveLength(0); + }); + }); + + describe('existing subscribers', () => { + test('a Swedish customer billed in EUR keeps the EUR price and is not repriced to SEK', async () => { + lookupGeoipMock.mockResolvedValue(geoipCountry('SE')); + const account = await createTestAccount(harness); + await createBuilder(harness, account.token) + .post(`/test/users/${account.userId}/premium`) + .body({ + stripe_subscription_id: EUR_SUBSCRIPTION_ID, + premium_type: 1, + premium_billing_cycle: 'monthly', + premium_until: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), + premium_will_cancel: false, + }) + .execute(); + const price = await createBuilder(harness, account.token) + .get('/premium/current-subscription-price') + .expect(HTTP_STATUS.OK) + .execute(); + expect(price).toMatchObject({ + price_id: MOCK_PRICES.monthlyEur, + currency: 'EUR', + billing_cycle: 'monthly', + is_grandfathered: false, + list_price_id: MOCK_PRICES.monthlyEur, + }); + expect(stripeHandlers.spies.updatedSubscriptions).toHaveLength(0); + expect(stripeHandlers.spies.createdSubscriptionSchedules).toHaveLength(0); + }); + }); +}); diff --git a/fluxer_api/src/api/test/msw/handlers/StripeApiHandlers.ts b/fluxer_api/src/api/test/msw/handlers/StripeApiHandlers.ts index e98af18c6..fbe486015 100644 --- a/fluxer_api/src/api/test/msw/handlers/StripeApiHandlers.ts +++ b/fluxer_api/src/api/test/msw/handlers/StripeApiHandlers.ts @@ -801,13 +801,19 @@ export function createStripeApiHandlers(config: StripeApiMockConfig = {}): Strip ? 'eur' : normalizedPriceId.includes('brl') ? 'brl' - : normalizedPriceId.includes('inr') - ? 'inr' - : normalizedPriceId.includes('pln') - ? 'pln' - : normalizedPriceId.includes('try') - ? 'try' - : 'usd', + : normalizedPriceId.includes('dkk') + ? 'dkk' + : normalizedPriceId.includes('inr') + ? 'inr' + : normalizedPriceId.includes('nok') + ? 'nok' + : normalizedPriceId.includes('pln') + ? 'pln' + : normalizedPriceId.includes('sek') + ? 'sek' + : normalizedPriceId.includes('try') + ? 'try' + : 'usd', interval: normalizedPriceId.includes('year') ? 'year' : 'month', }; } @@ -1724,13 +1730,19 @@ export function createStripeApiHandlers(config: StripeApiMockConfig = {}): Strip ? 'eur' : normalizedPriceId.includes('brl') ? 'brl' - : normalizedPriceId.includes('inr') - ? 'inr' - : normalizedPriceId.includes('pln') - ? 'pln' - : normalizedPriceId.includes('try') - ? 'try' - : 'usd'), + : normalizedPriceId.includes('dkk') + ? 'dkk' + : normalizedPriceId.includes('inr') + ? 'inr' + : normalizedPriceId.includes('nok') + ? 'nok' + : normalizedPriceId.includes('pln') + ? 'pln' + : normalizedPriceId.includes('sek') + ? 'sek' + : normalizedPriceId.includes('try') + ? 'try' + : 'usd'), unit_amount: overrides?.unit_amount ?? (normalizedPriceId.includes('year') ? 4999 : 499), type: 'recurring', recurring: { diff --git a/fluxer_api/src/api/utils/CurrencyUtils.ts b/fluxer_api/src/api/utils/CurrencyUtils.ts index 3e10748b7..87cb5d2d0 100644 --- a/fluxer_api/src/api/utils/CurrencyUtils.ts +++ b/fluxer_api/src/api/utils/CurrencyUtils.ts @@ -2,7 +2,7 @@ import {isEuEeaCountryCode} from '@fluxer/constants/src/EuropeanEconomicArea'; -export type Currency = 'USD' | 'EUR' | 'BRL' | 'INR' | 'PLN' | 'TRY'; +export type Currency = 'USD' | 'EUR' | 'BRL' | 'DKK' | 'INR' | 'NOK' | 'PLN' | 'SEK' | 'TRY'; export function getCurrency(countryCode: string | null | undefined): Currency { return getCurrencyPreferences(countryCode)[0]; @@ -16,12 +16,21 @@ export function getCurrencyPreferences(countryCode: string | null | undefined): if (upperCode === 'BR') { return ['BRL', 'USD', 'EUR']; } + if (upperCode === 'DK') { + return ['DKK', 'EUR', 'USD']; + } if (upperCode === 'IN') { return ['INR', 'USD', 'EUR']; } + if (upperCode === 'NO') { + return ['NOK', 'EUR', 'USD']; + } if (upperCode === 'PL') { return ['PLN', 'EUR', 'USD']; } + if (upperCode === 'SE') { + return ['SEK', 'EUR', 'USD']; + } if (upperCode === 'TR') { return ['TRY', 'USD', 'EUR']; } @@ -31,9 +40,10 @@ export function getCurrencyPreferences(countryCode: string | null | undefined): return ['USD', 'EUR']; } +const GIFT_ELIGIBLE_LOCALIZED_CURRENCIES = new Set(['DKK', 'NOK', 'SEK']); + export function getGiftCurrencyPreferences(countryCode: string | null | undefined): Array { - if (countryCode && isEuEeaCountryCode(countryCode.toUpperCase())) { - return ['EUR', 'USD']; - } - return ['USD', 'EUR']; + return getCurrencyPreferences(countryCode).filter( + (currency) => currency === 'USD' || currency === 'EUR' || GIFT_ELIGIBLE_LOCALIZED_CURRENCIES.has(currency), + ); } diff --git a/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts b/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts index 682a0edf4..9a980c19e 100644 --- a/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts +++ b/fluxer_api/src/api/utils/tests/CurrencyUtils.test.ts @@ -55,11 +55,11 @@ describe('getCurrency', () => { it('returns EUR for Finland', () => { expect(getCurrency('FI')).toBe('EUR'); }); - it('returns EUR for Sweden', () => { - expect(getCurrency('SE')).toBe('EUR'); + it('returns SEK for Sweden', () => { + expect(getCurrency('SE')).toBe('SEK'); }); - it('returns EUR for Denmark', () => { - expect(getCurrency('DK')).toBe('EUR'); + it('returns DKK for Denmark', () => { + expect(getCurrency('DK')).toBe('DKK'); }); it('returns PLN for Poland', () => { expect(getCurrency('PL')).toBe('PLN'); @@ -76,8 +76,8 @@ describe('getCurrency', () => { it('returns EUR for Romania', () => { expect(getCurrency('RO')).toBe('EUR'); }); - it('returns EUR for Norway (EEA but not EU)', () => { - expect(getCurrency('NO')).toBe('EUR'); + it('returns NOK for Norway (EEA but not EU)', () => { + expect(getCurrency('NO')).toBe('NOK'); }); it('returns EUR for Iceland (EEA but not EU)', () => { expect(getCurrency('IS')).toBe('EUR'); @@ -128,7 +128,6 @@ describe('getCurrency', () => { 'HR', 'CY', 'CZ', - 'DK', 'EE', 'FI', 'FR', @@ -147,10 +146,8 @@ describe('getCurrency', () => { 'SK', 'SI', 'ES', - 'SE', 'IS', 'LI', - 'NO', ]; for (const country of eeaCountries) { it(`returns EUR for ${country}`, () => { @@ -160,16 +157,30 @@ describe('getCurrency', () => { it('uses local currency for Poland', () => { expect(getCurrency('PL')).toBe('PLN'); }); + it('uses local currency for Sweden', () => { + expect(getCurrency('SE')).toBe('SEK'); + }); + it('uses local currency for Denmark', () => { + expect(getCurrency('DK')).toBe('DKK'); + }); + it('uses local currency for Norway', () => { + expect(getCurrency('NO')).toBe('NOK'); + }); }); }); describe('getGiftCurrencyPreferences', () => { - it('never offers a localized currency for a localized market', () => { + it('never offers a localized currency that is cheaper than the base price', () => { for (const country of ['BR', 'IN', 'PL', 'TR']) { expect(getGiftCurrencyPreferences(country)).not.toContain(getCurrencyPreferences(country)[0]); } }); - it('uses EUR for EEA countries', () => { + it('offers the localized currency where it is not cheaper than the base price', () => { + expect(getGiftCurrencyPreferences('SE')).toEqual(['SEK', 'EUR', 'USD']); + expect(getGiftCurrencyPreferences('DK')).toEqual(['DKK', 'EUR', 'USD']); + expect(getGiftCurrencyPreferences('NO')).toEqual(['NOK', 'EUR', 'USD']); + }); + it('uses EUR for other EEA countries', () => { expect(getGiftCurrencyPreferences('DE')).toEqual(['EUR', 'USD']); expect(getGiftCurrencyPreferences('PL')).toEqual(['EUR', 'USD']); }); @@ -184,7 +195,7 @@ describe('getGiftCurrencyPreferences', () => { expect(getGiftCurrencyPreferences(undefined)).toEqual(['USD', 'EUR']); }); it('is case insensitive', () => { - expect(getGiftCurrencyPreferences('de')).toEqual(['EUR', 'USD']); + expect(getGiftCurrencyPreferences('se')).toEqual(['SEK', 'EUR', 'USD']); expect(getGiftCurrencyPreferences('br')).toEqual(['USD', 'EUR']); }); }); diff --git a/fluxer_api/vitest.config.ts b/fluxer_api/vitest.config.ts index 90fcc91b7..de60a70ae 100644 --- a/fluxer_api/vitest.config.ts +++ b/fluxer_api/vitest.config.ts @@ -27,6 +27,7 @@ const MODULE_REGISTRY_TEST_FILES = [ 'src/api/gif/GifRequestCountry.test.ts', 'src/api/risk/__tests__/AccountPolicyService.test.ts', 'src/api/stripe/tests/StripeCheckoutCountryEnforcement.test.ts', + 'src/api/stripe/tests/StripeNordicCurrencies.test.ts', ]; const INSTANCE_POLICY_TEST_FILES = [ diff --git a/fluxer_docs/src/content/docs/http-api/premium.mdx b/fluxer_docs/src/content/docs/http-api/premium.mdx index 39a418ed7..d7271ae10 100644 --- a/fluxer_docs/src/content/docs/http-api/premium.mdx +++ b/fluxer_docs/src/content/docs/http-api/premium.mdx @@ -52,8 +52,11 @@ Every amount field is in the minor unit of the currency field its description na | USD | United States dollar | | EUR | Euro | | BRL | Brazilian real | +| DKK | Danish krone | | INR | Indian rupee | +| NOK | Norwegian krone | | PLN | Polish zloty | +| SEK | Swedish krona | | TRY | Turkish lira | ## Price IDs object diff --git a/fluxer_docs/src/content/docs/operator/configuration.mdx b/fluxer_docs/src/content/docs/operator/configuration.mdx index 5c3bd54f3..38ecd4994 100644 --- a/fluxer_docs/src/content/docs/operator/configuration.mdx +++ b/fluxer_docs/src/content/docs/operator/configuration.mdx @@ -918,7 +918,7 @@ Default empty. The webhook signing secret. Not forwarded by the shipped Compose Default `{}`. Every price ID at once. JSON object. The individual price variables are declared after it and win. -Twenty-eight individual price variables also exist, one per product and currency: `FLUXER_STRIPE_PRICE_MONTHLY_` and `FLUXER_STRIPE_PRICE_YEARLY_` in USD, EUR, BRL, INR, PLN, and TRY, `FLUXER_STRIPE_PRICE_VISIONARY_` and `FLUXER_STRIPE_PRICE_GIFT_VISIONARY_` in USD and EUR, and `FLUXER_STRIPE_PRICE_GIFT_1_MONTH_` and `FLUXER_STRIPE_PRICE_GIFT_1_YEAR_` in the same six currencies. +Thirty-four individual price variables also exist, one per product and currency: `FLUXER_STRIPE_PRICE_MONTHLY_` and `FLUXER_STRIPE_PRICE_YEARLY_` in USD, EUR, BRL, DKK, INR, NOK, PLN, SEK, and TRY, `FLUXER_STRIPE_PRICE_VISIONARY_` and `FLUXER_STRIPE_PRICE_GIFT_VISIONARY_` in USD and EUR, and `FLUXER_STRIPE_PRICE_GIFT_1_MONTH_` and `FLUXER_STRIPE_PRICE_GIFT_1_YEAR_` in USD, EUR, BRL, INR, PLN, and TRY. #### `FLUXER_STRIPE_LEGACY_PRICES` diff --git a/packages/config/src/config_loader/EnvironmentOverrides.ts b/packages/config/src/config_loader/EnvironmentOverrides.ts index 2c1ce09e9..9f388d508 100644 --- a/packages/config/src/config_loader/EnvironmentOverrides.ts +++ b/packages/config/src/config_loader/EnvironmentOverrides.ts @@ -301,14 +301,20 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record = { FLUXER_STRIPE_PRICE_MONTHLY_USD: {path: ['integrations', 'stripe', 'prices', 'monthly_usd']}, FLUXER_STRIPE_PRICE_MONTHLY_EUR: {path: ['integrations', 'stripe', 'prices', 'monthly_eur']}, FLUXER_STRIPE_PRICE_MONTHLY_BRL: {path: ['integrations', 'stripe', 'prices', 'monthly_brl']}, + FLUXER_STRIPE_PRICE_MONTHLY_DKK: {path: ['integrations', 'stripe', 'prices', 'monthly_dkk']}, FLUXER_STRIPE_PRICE_MONTHLY_INR: {path: ['integrations', 'stripe', 'prices', 'monthly_inr']}, + FLUXER_STRIPE_PRICE_MONTHLY_NOK: {path: ['integrations', 'stripe', 'prices', 'monthly_nok']}, FLUXER_STRIPE_PRICE_MONTHLY_PLN: {path: ['integrations', 'stripe', 'prices', 'monthly_pln']}, + FLUXER_STRIPE_PRICE_MONTHLY_SEK: {path: ['integrations', 'stripe', 'prices', 'monthly_sek']}, FLUXER_STRIPE_PRICE_MONTHLY_TRY: {path: ['integrations', 'stripe', 'prices', 'monthly_try']}, FLUXER_STRIPE_PRICE_YEARLY_USD: {path: ['integrations', 'stripe', 'prices', 'yearly_usd']}, FLUXER_STRIPE_PRICE_YEARLY_EUR: {path: ['integrations', 'stripe', 'prices', 'yearly_eur']}, FLUXER_STRIPE_PRICE_YEARLY_BRL: {path: ['integrations', 'stripe', 'prices', 'yearly_brl']}, + FLUXER_STRIPE_PRICE_YEARLY_DKK: {path: ['integrations', 'stripe', 'prices', 'yearly_dkk']}, FLUXER_STRIPE_PRICE_YEARLY_INR: {path: ['integrations', 'stripe', 'prices', 'yearly_inr']}, + FLUXER_STRIPE_PRICE_YEARLY_NOK: {path: ['integrations', 'stripe', 'prices', 'yearly_nok']}, FLUXER_STRIPE_PRICE_YEARLY_PLN: {path: ['integrations', 'stripe', 'prices', 'yearly_pln']}, + FLUXER_STRIPE_PRICE_YEARLY_SEK: {path: ['integrations', 'stripe', 'prices', 'yearly_sek']}, FLUXER_STRIPE_PRICE_YEARLY_TRY: {path: ['integrations', 'stripe', 'prices', 'yearly_try']}, FLUXER_STRIPE_PRICE_VISIONARY_USD: {path: ['integrations', 'stripe', 'prices', 'visionary_usd']}, FLUXER_STRIPE_PRICE_VISIONARY_EUR: {path: ['integrations', 'stripe', 'prices', 'visionary_eur']}, @@ -316,6 +322,12 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record = { FLUXER_STRIPE_PRICE_GIFT_VISIONARY_EUR: {path: ['integrations', 'stripe', 'prices', 'gift_visionary_eur']}, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_USD: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_usd']}, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_EUR: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_eur']}, + FLUXER_STRIPE_PRICE_GIFT_1_MONTH_SEK: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_sek']}, + FLUXER_STRIPE_PRICE_GIFT_1_YEAR_SEK: {path: ['integrations', 'stripe', 'prices', 'gift_1_year_sek']}, + FLUXER_STRIPE_PRICE_GIFT_1_MONTH_DKK: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_dkk']}, + FLUXER_STRIPE_PRICE_GIFT_1_YEAR_DKK: {path: ['integrations', 'stripe', 'prices', 'gift_1_year_dkk']}, + FLUXER_STRIPE_PRICE_GIFT_1_MONTH_NOK: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_nok']}, + FLUXER_STRIPE_PRICE_GIFT_1_YEAR_NOK: {path: ['integrations', 'stripe', 'prices', 'gift_1_year_nok']}, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_BRL: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_brl']}, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_INR: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_inr']}, FLUXER_STRIPE_PRICE_GIFT_1_MONTH_PLN: {path: ['integrations', 'stripe', 'prices', 'gift_1_month_pln']}, diff --git a/packages/i18n/marketing/ar.po b/packages/i18n/marketing/ar.po index 6ee8a6406..2ac4fffbd 100644 --- a/packages/i18n/marketing/ar.po +++ b/packages/i18n/marketing/ar.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2372,14 +2372,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "إذا كنت تكتب عن {product_name}، راسلنا على {email}. سيقرأ رسالتك شخص حقيقي." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"الاشتراك نفسه متاح أيضًا بعملة {base_currency_code}، بسعر {base_monthly_price} شهريًا أو {base_yearly_price} سنويًا." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/bg.po b/packages/i18n/marketing/bg.po index 678b1a0e0..ffc514ec8 100644 --- a/packages/i18n/marketing/bg.po +++ b/packages/i18n/marketing/bg.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: bg\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2388,14 +2388,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Ако пишеш материал за {product_name}, изпрати имейл на {email}. Чете го истински човек." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/cs.po b/packages/i18n/marketing/cs.po index 822543d92..23723c9c3 100644 --- a/packages/i18n/marketing/cs.po +++ b/packages/i18n/marketing/cs.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2381,14 +2381,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Píšeš o {product_name}? Napiš nám na {email}. Čte to skutečný člověk." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Stejné předplatné se prodává i v měně {base_currency_code}, a to za {base_monthly_price} měsíčně nebo {base_yearly_price} na rok." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/da.po b/packages/i18n/marketing/da.po index 21cc3fac8..8e0c087ec 100644 --- a/packages/i18n/marketing/da.po +++ b/packages/i18n/marketing/da.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2388,14 +2388,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Skriver du om {product_name}, så send en mail til {email}. Der sidder et rigtigt menneske og læser den." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Det samme abonnement sælges også i {base_currency_code}, til {base_monthly_price} om måneden eller {base_yearly_price} for et år." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/de.po b/packages/i18n/marketing/de.po index 3a5b6eb4d..d069aac3d 100644 --- a/packages/i18n/marketing/de.po +++ b/packages/i18n/marketing/de.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2396,14 +2396,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Wenn du über {product_name} schreibst, schreib uns an {email}. Ein echter Mensch liest mit." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Dasselbe Abo wird auch in {base_currency_code} verkauft, für {base_monthly_price} im Monat oder {base_yearly_price} im Jahr." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/el.po b/packages/i18n/marketing/el.po index c6de3f8e6..53a9074b9 100644 --- a/packages/i18n/marketing/el.po +++ b/packages/i18n/marketing/el.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: el\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2405,14 +2405,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Αν γράφεις για το {product_name}, στείλε email στο {email}. Το διαβάζει πραγματικός άνθρωπος." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Η ίδια συνδρομή πωλείται και σε {base_currency_code}, με {base_monthly_price} τον μήνα ή {base_yearly_price} τον χρόνο." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/en-GB.po b/packages/i18n/marketing/en-GB.po index 95a5191a9..45d957dc3 100644 --- a/packages/i18n/marketing/en-GB.po +++ b/packages/i18n/marketing/en-GB.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: en-GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2380,14 +2380,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "If you are writing about {product_name}, email {email}. A real person reads it." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/en-US.po b/packages/i18n/marketing/en-US.po index 0b5c20018..5b413f8ec 100644 --- a/packages/i18n/marketing/en-US.po +++ b/packages/i18n/marketing/en-US.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: en-US\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2380,14 +2380,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "If you are writing about {product_name}, email {email}. A real person reads it." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/es-419.po b/packages/i18n/marketing/es-419.po index ad4dd639e..ead893c78 100644 --- a/packages/i18n/marketing/es-419.po +++ b/packages/i18n/marketing/es-419.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: es-419\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2393,14 +2393,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Si estás escribiendo sobre {product_name}, escríbenos a {email}. Una persona real lo lee." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"La misma suscripción también se vende en {base_currency_code}, a {base_monthly_price} al mes o {base_yearly_price} al año." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/es-ES.po b/packages/i18n/marketing/es-ES.po index 110b998c0..0b9f8f4bc 100644 --- a/packages/i18n/marketing/es-ES.po +++ b/packages/i18n/marketing/es-ES.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: es-ES\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2395,14 +2395,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Si estás escribiendo sobre {product_name}, escribe a {email}. Lo lee una persona de verdad." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"La misma suscripción también se vende en {base_currency_code}, por {base_monthly_price} al mes o {base_yearly_price} al año." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/fi.po b/packages/i18n/marketing/fi.po index 4ab0f3c1e..a0729dce7 100644 --- a/packages/i18n/marketing/fi.po +++ b/packages/i18n/marketing/fi.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2394,14 +2394,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Jos kirjoitat {product_name}-palvelusta, lähetä sähköpostia osoitteeseen {email}. Viestin lukee oikea ihminen." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Sama tilaus on myynnissä myös valuutassa {base_currency_code} hintaan {base_monthly_price} kuukaudessa tai {base_yearly_price} vuodessa." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/fr.po b/packages/i18n/marketing/fr.po index 8e2dca339..32ab57f06 100644 --- a/packages/i18n/marketing/fr.po +++ b/packages/i18n/marketing/fr.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2400,14 +2400,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Si tu prépares un sujet sur {product_name}, écris à {email}. Une vraie personne le lit." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Le même abonnement est aussi vendu en {base_currency_code}, à {base_monthly_price} par mois ou {base_yearly_price} par an." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/he.po b/packages/i18n/marketing/he.po index 455e37401..9a32997ee 100644 --- a/packages/i18n/marketing/he.po +++ b/packages/i18n/marketing/he.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2364,14 +2364,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "אם אתם כותבים על {product_name}, שלחו אימייל ל-{email}. אדם אמיתי קורא אותו." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"אותו מנוי נמכר גם ב-{base_currency_code}, במחיר {base_monthly_price} לחודש או {base_yearly_price} לשנה." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/hi.po b/packages/i18n/marketing/hi.po index cce17e233..e1fc811f4 100644 --- a/packages/i18n/marketing/hi.po +++ b/packages/i18n/marketing/hi.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2383,14 +2383,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "अगर आप {product_name} के बारे में लिख रहे हैं, तो {email} पर ईमेल करें। इसे एक असली इंसान पढ़ता है।" -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"यही सब्सक्रिप्शन {base_currency_code} में भी मिलता है — {base_monthly_price} प्रति माह या {base_yearly_price} प्रति वर्ष।" - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/hr.po b/packages/i18n/marketing/hr.po index 0718126bc..474a4fe01 100644 --- a/packages/i18n/marketing/hr.po +++ b/packages/i18n/marketing/hr.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2386,14 +2386,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Ako pišeš o {product_name}u, javi se na {email}. Čita ih prava osoba." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Ista se pretplata prodaje i u valuti {base_currency_code}, po cijeni od {base_monthly_price} mjesečno ili {base_yearly_price} godišnje." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/hu.po b/packages/i18n/marketing/hu.po index cf545b17b..4c743ddbe 100644 --- a/packages/i18n/marketing/hu.po +++ b/packages/i18n/marketing/hu.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2395,14 +2395,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Ha a {product_name}-ről írsz, írj a következő címre: {email}. Valódi ember olvassa." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Ugyanez az előfizetés {base_currency_code} pénznemben is megvásárolható, havi {base_monthly_price} vagy éves {base_yearly_price} áron." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/id.po b/packages/i18n/marketing/id.po index 542194ab0..962f0f4df 100644 --- a/packages/i18n/marketing/id.po +++ b/packages/i18n/marketing/id.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2384,14 +2384,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Kalau kamu sedang menulis tentang {product_name}, kirim email ke {email}. Ada orang sungguhan yang membacanya." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Langganan yang sama juga dijual dalam {base_currency_code}, seharga {base_monthly_price} per bulan atau {base_yearly_price} per tahun." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/it.po b/packages/i18n/marketing/it.po index f97033d0a..8850c1922 100644 --- a/packages/i18n/marketing/it.po +++ b/packages/i18n/marketing/it.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2393,14 +2393,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Se stai scrivendo di {product_name}, scrivi a {email}. A leggere c'è una persona vera." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Lo stesso abbonamento è venduto anche in {base_currency_code}, a {base_monthly_price} al mese o {base_yearly_price} per un anno." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/ja.po b/packages/i18n/marketing/ja.po index d6ef9c883..995310aa9 100644 --- a/packages/i18n/marketing/ja.po +++ b/packages/i18n/marketing/ja.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2346,13 +2346,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "{product_name}について記事を書かれる方は、{email}までメールをお送りください。実際に担当者が目を通します。" -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "同じサブスクリプションは{base_currency_code}でも販売しており、月額{base_monthly_price}または年額{base_yearly_price}です。" - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/ko.po b/packages/i18n/marketing/ko.po index c8eff4243..58da2e194 100644 --- a/packages/i18n/marketing/ko.po +++ b/packages/i18n/marketing/ko.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2344,13 +2344,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "{product_name}에 대한 기사를 준비 중이시라면 {email}로 메일을 보내 주세요. 실제 담당자가 읽습니다." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "동일한 구독을 {base_currency_code}로도 판매하며, 월 {base_monthly_price} 또는 연 {base_yearly_price}입니다." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/lt.po b/packages/i18n/marketing/lt.po index 4b43230ba..534eb3c67 100644 --- a/packages/i18n/marketing/lt.po +++ b/packages/i18n/marketing/lt.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2389,14 +2389,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Jei rašai apie {product_name}, parašyk adresu {email}. Laiškus skaito tikras žmogus." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Ta pati prenumerata parduodama ir {base_currency_code} valiuta: {base_monthly_price} per mėnesį arba {base_yearly_price} per metus." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/nl.po b/packages/i18n/marketing/nl.po index 91961f382..2e2503fb5 100644 --- a/packages/i18n/marketing/nl.po +++ b/packages/i18n/marketing/nl.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2390,14 +2390,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Schrijf je over {product_name}? Mail dan naar {email}. Er leest een echt mens mee." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Hetzelfde abonnement wordt ook verkocht in {base_currency_code}, voor {base_monthly_price} per maand of {base_yearly_price} per jaar." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/no.po b/packages/i18n/marketing/no.po index 3e18e1ca9..eb9e81f08 100644 --- a/packages/i18n/marketing/no.po +++ b/packages/i18n/marketing/no.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: no\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2387,14 +2387,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Skriver du om {product_name}? Send en e-post til {email}. Det er et ekte menneske som leser den." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Det samme abonnementet selges også i {base_currency_code}, til {base_monthly_price} i måneden eller {base_yearly_price} for et år." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/pl.po b/packages/i18n/marketing/pl.po index 2fbf5b15a..55170c68d 100644 --- a/packages/i18n/marketing/pl.po +++ b/packages/i18n/marketing/pl.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2391,14 +2391,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Jeśli piszesz o {product_name}, napisz na {email}. Czyta to prawdziwa osoba." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Ta sama subskrypcja jest też sprzedawana w {base_currency_code}: {base_monthly_price} miesięcznie lub {base_yearly_price} rocznie." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/pt-BR.po b/packages/i18n/marketing/pt-BR.po index 521b4e688..6d3761f52 100644 --- a/packages/i18n/marketing/pt-BR.po +++ b/packages/i18n/marketing/pt-BR.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: pt-BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2392,14 +2392,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Se você está escrevendo sobre o {product_name}, escreva para {email}. Uma pessoa de verdade lê as mensagens." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"A mesma assinatura também é vendida em {base_currency_code}, a {base_monthly_price} por mês ou {base_yearly_price} por ano." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/ro.po b/packages/i18n/marketing/ro.po index 844db4dec..12ee158da 100644 --- a/packages/i18n/marketing/ro.po +++ b/packages/i18n/marketing/ro.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2396,14 +2396,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "Dacă scrii despre {product_name}, trimite-ne un e-mail la {email}. Îl citește un om adevărat." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Același abonament este vândut și în {base_currency_code}, la {base_monthly_price} pe lună sau {base_yearly_price} pe an." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/ru.po b/packages/i18n/marketing/ru.po index c2a985ab9..ffdc46b34 100644 --- a/packages/i18n/marketing/ru.po +++ b/packages/i18n/marketing/ru.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2389,14 +2389,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Пишешь материал о {product_name}? Напиши нам на {email}. Письма читает живой человек." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Эта же подписка продаётся и в {base_currency_code}: {base_monthly_price} в месяц или {base_yearly_price} в год." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/sv-SE.po b/packages/i18n/marketing/sv-SE.po index 2e4028280..7f53faa67 100644 --- a/packages/i18n/marketing/sv-SE.po +++ b/packages/i18n/marketing/sv-SE.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: sv-SE\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2390,14 +2390,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Om du skriver om {product_name}, mejla {email}. En riktig människa läser det." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Samma prenumeration säljs även i {base_currency_code}, för {base_monthly_price} i månaden eller {base_yearly_price} för ett år." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/th.po b/packages/i18n/marketing/th.po index e2cf192f0..4de67a21d 100644 --- a/packages/i18n/marketing/th.po +++ b/packages/i18n/marketing/th.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: th\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2378,14 +2378,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "หากคุณกำลังเขียนถึง {product_name} ส่งอีเมลมาที่ {email} มีคนจริงๆ อ่านทุกฉบับ" -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"การสมัครสมาชิกแบบเดียวกันนี้มีจำหน่ายในสกุลเงิน {base_currency_code} ด้วย ที่ {base_monthly_price} ต่อเดือน หรือ {base_yearly_price} ต่อปี" - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/tr.po b/packages/i18n/marketing/tr.po index 50a942256..98b35f67b 100644 --- a/packages/i18n/marketing/tr.po +++ b/packages/i18n/marketing/tr.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2386,14 +2386,6 @@ msgid "If you are writing about {product_name}, email {email}. A real person rea msgstr "" "{product_name} hakkında yazıyorsan {email} adresine e-posta gönder. Mesajını gerçek bir insan okuyor." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Aynı abonelik {base_currency_code} cinsinden de satılıyor: ayda {base_monthly_price} veya yılda {base_yearly_price}." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/uk.po b/packages/i18n/marketing/uk.po index a518555ff..1ede62a6f 100644 --- a/packages/i18n/marketing/uk.po +++ b/packages/i18n/marketing/uk.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2389,14 +2389,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Якщо ти пишеш про {product_name}, напиши на {email}. Це читає жива людина." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Ця сама передплата також продається у {base_currency_code}: {base_monthly_price} на місяць або {base_yearly_price} на рік." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/vi.po b/packages/i18n/marketing/vi.po index e59aed146..0abf24569 100644 --- a/packages/i18n/marketing/vi.po +++ b/packages/i18n/marketing/vi.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: vi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2390,14 +2390,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "Nếu bạn đang viết về {product_name}, hãy gửi email tới {email}. Một người thật sẽ đọc nó." -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "" -"Cùng gói đăng ký này cũng được bán bằng {base_currency_code}, với giá {base_monthly_price} mỗi tháng hoặc {base_yearly_price} mỗi năm." - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/zh-CN.po b/packages/i18n/marketing/zh-CN.po index 75f993683..68147af6b 100644 --- a/packages/i18n/marketing/zh-CN.po +++ b/packages/i18n/marketing/zh-CN.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: zh-CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2335,13 +2335,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "如果你要撰写关于 {product_name} 的报道,请发送邮件至 {email}。会有真人阅读。" -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "同样的订阅也以 {base_currency_code} 出售,每月 {base_monthly_price} 或每年 {base_yearly_price}。" - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/i18n/marketing/zh-TW.po b/packages/i18n/marketing/zh-TW.po index b51c9c480..dd9a7ae97 100644 --- a/packages/i18n/marketing/zh-TW.po +++ b/packages/i18n/marketing/zh-TW.po @@ -4,8 +4,8 @@ msgid "" msgstr "" "Project-Id-Version: fluxer-marketing\n" -"POT-Creation-Date: 2026-09-10 00:00+0000\n" -"PO-Revision-Date: 2026-09-10 00:00+0000\n" +"POT-Creation-Date: 2026-09-11 00:00+0000\n" +"PO-Revision-Date: 2026-09-11 00:00+0000\n" "Language: zh-TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -2335,13 +2335,6 @@ msgctxt "press_branding.contact.story_prompt_message" msgid "If you are writing about {product_name}, email {email}. A real person reads it." msgstr "如果你正在撰寫關於 {product_name} 的報導,請寄信到 {email},會有真人閱讀。" -#. Body copy in pricing UI explaining the base-currency checkout option. Preserve all currency and price placeholders exactly. -#: fluxer_marketing/generated:pricing_and_tiers.billing.base_currency_also_available -msgctxt "pricing_and_tiers.billing.base_currency_also_available" -msgid "" -"The same subscription is also sold in {base_currency_code}, at {base_monthly_price} a month or {base_yearly_price} for a year." -msgstr "同樣的訂閱方案也以 {base_currency_code} 販售,每月 {base_monthly_price},或每年 {base_yearly_price}。" - #. Compact monthly price label in pricing UI. Preserve the monthly price placeholder exactly. #: fluxer_marketing/generated:pricing_and_tiers.billing.monthly_price msgctxt "pricing_and_tiers.billing.monthly_price" diff --git a/packages/schema/src/domains/premium/PremiumSchemas.ts b/packages/schema/src/domains/premium/PremiumSchemas.ts index ff5fbc0be..394a8dc95 100644 --- a/packages/schema/src/domains/premium/PremiumSchemas.ts +++ b/packages/schema/src/domains/premium/PremiumSchemas.ts @@ -29,8 +29,10 @@ export const PriceIdsResponse = z.object({ .nullish() .describe('Gift 1 month price amount in the currency minor unit'), gift_1_year_amount_minor: z.number().int().nullish().describe('Gift 1 year price amount in the currency minor unit'), - currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).describe('Currency for the prices'), - gift_currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).describe('Currency for gift prices'), + currency: z.enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']).describe('Currency for the prices'), + gift_currency: z + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) + .describe('Currency for gift prices'), }); export type PriceIdsResponse = z.infer; @@ -83,7 +85,9 @@ export const CurrentSubscriptionPriceResponse = z .object({ price_id: z.string().describe('The Stripe price ID the user is currently billed against'), amount_minor: z.number().int().describe('The amount the user is actually charged, in the currency minor unit'), - currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).describe('Currency of the charged amount'), + currency: z + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) + .describe('Currency of the charged amount'), billing_cycle: z.enum(['monthly', 'yearly']).describe('The recurring billing cycle of the active subscription'), is_grandfathered: z .boolean() @@ -119,7 +123,10 @@ export const PendingSubscriptionChangeResponse = z .int() .nullable() .describe('Unit amount of the price that will be used after the change, in the currency minor unit'), - currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).nullable().describe('Currency for the pending change'), + currency: z + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) + .nullable() + .describe('Currency for the pending change'), initial_amount_minor: z .number() .int() @@ -187,7 +194,7 @@ export const ListPriceSwitchState = z.object({ .nullable() .describe('Current list price for the same cycle and currency, in the currency minor unit'), currency: z - .enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']) + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) .nullable() .describe('Currency of both the current and the list amount'), billing_cycle: z.enum(['monthly', 'yearly']).nullable().describe('Recurring billing cycle the switch applies to'), @@ -206,7 +213,9 @@ export const SwitchToListPriceResponse = z.discriminatedUnion('status', [ target_price_id: z.string().describe('Stripe price ID the subscription will be billed against after the switch'), target_amount_minor: z.number().int().describe('Amount billed after the switch, in the currency minor unit'), current_amount_minor: z.number().int().describe('Amount billed before the switch, in the currency minor unit'), - currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).describe('Currency of both amounts'), + currency: z + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) + .describe('Currency of both amounts'), }), z.object({ status: z.literal('already_scheduled').describe('The switch was already scheduled by an earlier request'), @@ -214,7 +223,9 @@ export const SwitchToListPriceResponse = z.discriminatedUnion('status', [ target_price_id: z.string().describe('Stripe price ID the subscription will be billed against after the switch'), target_amount_minor: z.number().int().describe('Amount billed after the switch, in the currency minor unit'), current_amount_minor: z.number().int().describe('Amount billed before the switch, in the currency minor unit'), - currency: z.enum(['USD', 'EUR', 'BRL', 'INR', 'PLN', 'TRY']).describe('Currency of both amounts'), + currency: z + .enum(['USD', 'EUR', 'BRL', 'DKK', 'INR', 'NOK', 'PLN', 'SEK', 'TRY']) + .describe('Currency of both amounts'), }), z.object({ status: z.literal('ineligible').describe('The subscription cannot be moved to the current list price'),