{"version":3,"sources":["../../src/helpers/i18n.tsx","../../src/helpers/environment.ts","../../src/hooks/useEffectOnce.ts"],"sourcesContent":["import { getCookie, setCookie } from 'cookies-next'\nimport React, { createContext, useState, useCallback, useContext } from 'react'\n\nimport { isSSR } from './environment'\n\nimport { useEffectOnce } from '../hooks/useEffectOnce'\n\nimport type { Optional, AppType } from './common/types'\nimport type { IncomingMessage, ServerResponse } from 'http'\nimport type { Context, PropsWithChildren, FC } from 'react'\n\n/**\n * Based on RFC5646: https://datatracker.ietf.org/doc/html/rfc5646\n */\nexport type LocaleInfo = {\n    /** primary language tag, like \"en\", \"zh\", \"ru\", affects everything in messages */\n    primary: string\n    /** extended language tag, which specify language dialect like \"gan\" in Gan Chinese (\"zh-gan\"), affects almost nothing  */\n    extended: Optional<string>\n    /** region tag, like \"US\", \"CN\", which usually does not affect messages itself, but number / currency formatting */\n    region: Optional<string>\n    /** script tag, like \"Latn\", \"Cyrl\", which refers to used script / alphabet. Affects all messages */\n    script: Optional<string>\n}\n\nexport type AcceptLanguageInfo = LocaleInfo & {\n    /** number in [0.0, 1.0] range to order languages by. The larger the number, the more locale is preferred */\n    quality: number\n}\n\nexport type LocaleSelection<AvailableLocale extends string> = {\n    /** Locale from list of apps available locales, used for language selection and messages loading. Example: \"en\" */\n    selectedLocale: AvailableLocale\n    /** Full locale, which must be passed to tools such react-intl. Can be equal sub-locale of selectedLocale or equal to it. Example: \"en-GB\" */\n    fullLocale: string\n}\n\nexport type PrefetchResult<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n> = LocaleSelection<AvailableLocale> & { messages: MessagesShape }\n\ntype SSRResult<PropsType extends Record<string, unknown>> = {\n    props: PropsType\n}\ntype SSRResultWithI18N<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n    PropsType extends Record<string, unknown>,\n> = {\n    props: PropsType & {\n        [I18N_SELECTED_LOCALE_PROP_NAME]?: AvailableLocale\n        [I18N_FULL_LOCALE_PROP_NAME]?: string\n        [I18N_MESSAGES_PROP_NAME]?: MessagesShape\n    }\n}\n\n\n\ntype TranslationsContextType<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n> = {\n    /**\n     * Selected locale, which is used to determine, what messages set to show to user.\n     * For example: \"en\"\n     * */\n    selectedLocale: AvailableLocale\n    /**\n     * Original locale, from which `selectedLocale` was chosen. Sub-locale of `selectedLocale`.\n     * Should be passed to tools such as `intl`, since it can contain additional info helping with number-formating and so on\n     * For example: \"en-GB\"\n     */\n    fullLocale: string\n    /** Extracted messages to pass into `intl` or any similar tools  */\n    messages: MessagesShape | undefined\n    /** Callback to change current language */\n    switchLocale(newLocale: AvailableLocale): void\n}\n\n/**\n * Translations helper which is used to parse / stringify locales,\n * select most suitable locale based on user preferences, load partial translations with caching and many others\n *\n * @example Init helper inside your app and re-export utils\n * import fetch from 'cross-fetch'\n * import getConfig from 'next/config'\n * import { IntlProvider as DefaultIntlProvider } from 'react-intl'\n *\n * import { TranslationsHelper } from '@open-condo/miniapp-utils/helpers/i18n'\n * import type { TranslationsProviderProps } from '@open-condo/miniapp-utils/helpers/i18n'\n *\n * import { LOCALES, DEFAULT_LOCALE } from '@/domains/common/constants/locales'\n *\n * import type { MessagesKeysType } from '@/global'\n * import type { FC, PropsWithChildren } from 'react'\n *\n * const { publicRuntimeConfig: { serviceUrl } } = getConfig()\n *\n * export type AvailableLocale = typeof LOCALES[number]\n * export type MessagesShape = Record<MessagesKeysType, string>\n *\n * const translationsAPIEndpoint = `${serviceUrl}/api/translations`\n *\n * async function loadDefaultMessages (): Promise<MessagesShape> {\n *     return (await import(`@/lang/${DEFAULT_LOCALE}.json`)).default\n * }\n *\n * async function loadMessages (locale: AvailableLocale): Promise<MessagesShape> {\n *     const response = await fetch(`${translationsAPIEndpoint}/${locale}`)\n *     if (!response.ok) throw new Error(`Could not load translations for ${locale} locale`)\n *     return response.json()\n * }\n *\n * const translationsHelper = new TranslationsHelper({\n *     locales: LOCALES,\n *     defaultLocale: DEFAULT_LOCALE,\n *     loadMessages,\n *     loadDefaultMessages,\n * })\n *\n * export type { PrefetchResult } from '@open-condo/miniapp-utils/helpers/i18n'\n *\n * export const prefetchTranslations = translationsHelper.prefetchTranslations\n * export const extractI18NInfo = translationsHelper.extractI18NInfo\n * export const useTranslationsExtractor = translationsHelper.getUseTranslationsExtractorHook()\n * export const TranslationsProvider: FC<TranslationsProviderProps<AvailableLocale, MessagesShape>> = translationsHelper.getTranslationsProvider()\n * export const useTranslations = translationsHelper.getUseTranslationsHook()\n *\n * export const IntlProvider: FC<PropsWithChildren> = ({ children }) => {\n *     const { messages, fullLocale } = useTranslations()\n *\n *     return (\n *         <DefaultIntlProvider locale={fullLocale} messages={messages}>\n *             {children}\n *         </DefaultIntlProvider>\n *     )\n * }\n *\n * @example use in _app.tsx SSR to prefetch translations\n * const translationsData = await prefetchTranslations(req, res)\n *\n * return extractI18NInfo(translationsData, {\n *     props: {},\n * })\n *\n * @example use in _app.tsx global layout to provide translations\n * const { initialSelectedLocale, initialFullLocale, initialMessages } = useTranslationsExtractor(pageProps)\n *\n * return (\n *       <TranslationsProvider\n *                 initialSelectedLocale={initialSelectedLocale}\n *                 initialFullLocale={initialFullLocale}\n *                 initialMessages={initialMessages}\n *       >\n *          <IntlProvider>\n *              {children}\n *          </IntlProvider>\n *       </TranslationsProvider>\n * )\n * */\nexport type TranslationsProviderProps<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n> = PropsWithChildren<{\n    initialSelectedLocale: AvailableLocale\n    initialFullLocale: string\n    initialMessages: MessagesShape | undefined\n}>\n\ntype TranslationsHelperOptions<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n> = {\n    locales: ReadonlyArray<AvailableLocale>\n    defaultLocale: AvailableLocale\n    loadDefaultMessages: () => Promise<MessagesShape>\n    loadMessages: (locale: AvailableLocale) => Promise<Partial<MessagesShape>>\n    localeCookieName?: string\n    localeQueryParam?: string\n}\n\ntype LocalePartMatcher = {\n    part: 'primary' | 'extended' | 'script' | 'region'\n    matcher: RegExp\n}\n\nconst LOCALE_PARSING_OPTIONS: Array<LocalePartMatcher> = [\n    { part: 'primary', matcher: /^[a-z]+$/ },\n    { part: 'extended', matcher: /^[a-z]{3}$/ },\n    { part: 'script', matcher: /^[A-Z][a-z]{3,}$/ },\n    { part: 'region', matcher: /^([A-Z]{2,3}|[0-9]{3})$/ },\n]\n\nconst LOCALE_RESOLVE_ORDER: Array<Array<keyof LocaleInfo>> = [\n    // Full resolution first\n    ['primary', 'extended', 'script', 'region'],\n    // Extended usually does not affect message a lot, so it can be omitted first\n    ['primary', 'script', 'region'],\n    // Primary + script is more important, since script change alphabet\n    ['primary', 'script'],\n    ['primary', 'region'],\n    ['primary', 'extended'],\n    ['primary'],\n]\n\nconst I18N_SELECTED_LOCALE_PROP_NAME = '__I18N_SELECTED_LOCALE__'\nconst I18N_FULL_LOCALE_PROP_NAME = '__I18N_FULL_LOCALE__'\nconst I18N_MESSAGES_PROP_NAME = '__I18N_MESSAGES__'\n\nexport class TranslationsHelper<\n    AvailableLocale extends string,\n    MessagesShape extends Record<string, string>,\n> {\n    private readonly _locales: Set<string>\n    private readonly _defaultLocale: AvailableLocale\n    private _context: Context<TranslationsContextType<AvailableLocale, MessagesShape>> | undefined\n    private readonly _loadMessages: (locale: AvailableLocale) => Promise<Partial<MessagesShape>>\n    private readonly _loadDefaultMessages: () => Promise<MessagesShape>\n    private readonly _translations: Partial<Record<AvailableLocale, MessagesShape>> = {}\n    private _defaultMessages: MessagesShape | undefined\n    readonly localeCookieName: string = 'NEXT_LOCALE'\n    readonly localeQueryParam: string | undefined = undefined\n\n    constructor (options: TranslationsHelperOptions<AvailableLocale, MessagesShape>) {\n        this._locales = new Set(options.locales)\n        this._defaultLocale = options.defaultLocale\n        this._loadMessages = options.loadMessages\n        this._loadDefaultMessages = options.loadDefaultMessages\n\n        if (options.localeCookieName) {\n            this.localeCookieName = options.localeCookieName\n        }\n        if (options.localeQueryParam) {\n            this.localeQueryParam = options.localeQueryParam\n        }\n\n        this.getTranslationsProvider = this.getTranslationsProvider.bind(this)\n        this.getUseTranslationsHook = this.getUseTranslationsHook.bind(this)\n        this.getUseTranslationsExtractorHook = this.getUseTranslationsExtractorHook.bind(this)\n        this.getPreferredLocale = this.getPreferredLocale.bind(this)\n        this.selectSupportedLocale = this.selectSupportedLocale.bind(this)\n        this.getTranslations = this.getTranslations.bind(this)\n        this.prefetchTranslations = this.prefetchTranslations.bind(this)\n        this.getHOC = this.getHOC.bind(this)\n    }\n\n    /**\n     * This util parses language-defining string according to RFC5646: https://datatracker.ietf.org/doc/html/rfc5646\n     * It also automatically detect and accept-language header format by enhancing result with quality info\n     */\n    static parseLocaleString (localeString: string): AcceptLanguageInfo {\n        const stringParts = localeString.trim().split(';')\n        const localeParts = stringParts[0].split('-')\n\n        const quality = stringParts.length > 1\n            ? parseFloat(stringParts[1].split('=')[1])\n            : 1.0\n\n        const locale: AcceptLanguageInfo = {\n            primary: localeParts[0],\n            extended: undefined,\n            script: undefined,\n            region: undefined,\n            quality,\n        }\n\n        let currentParser = 0\n        localePartsLoop: for (const localePart of localeParts) {\n            for (; currentParser < LOCALE_PARSING_OPTIONS.length; currentParser++) {\n                const { part, matcher } = LOCALE_PARSING_OPTIONS[currentParser]\n                if (matcher.test(localePart)) {\n                    locale[part] = localePart\n                    currentParser++\n                    continue localePartsLoop\n                }\n            }\n            break\n        }\n\n        return locale\n    }\n\n    /**\n     * Parses \"Accept-Language\" header value using \"parseLocaleString\" util and returns array of AcceptLanguageInfo\n     * sorted by descending quality.\n     *\n     * NOTE: Empty header or non-defined header is treated as \"*\"\n     */\n    static parseAcceptLanguageHeader (headerValue: Optional<string>): Array<AcceptLanguageInfo> {\n        return (headerValue || '*')\n            .split(',')\n            .map(TranslationsHelper.parseLocaleString)\n            .sort((a, b) => b.quality - a.quality)\n    }\n\n    /**\n     * Generates locale-string from LocaleInfo or AcceptLanguageInfo\n     */\n    static toLocaleString (locale: LocaleInfo | AcceptLanguageInfo): string {\n        return [locale.primary, locale.extended, locale.script, locale.region].filter(Boolean).join('-')\n    }\n\n    /**\n     * Enrich selected locale by scanning through requested locales\n     * and finding the first one, which is sub-locale of selected one.\n     * For example: selectedLocale = \"en\", requestedLocales: [\"en-GB\", \"fr\", \"en\"] -> fullLocale = \"en-GB\"\n     * If none of requested locales is valid sub-locales, returns selectedLocale as fallback\n     */\n    private _getFullLocale (selectedLocale: AvailableLocale, requestedLocales: Array<AcceptLanguageInfo | LocaleInfo>): string {\n        let fullLocale: string = selectedLocale\n        const selectedLocaleInfo = TranslationsHelper.parseLocaleString(selectedLocale)\n        for (const locale of requestedLocales) {\n            let isSubLocale = true\n            for (const [fieldName, fieldValue] of Object.entries(selectedLocaleInfo)) {\n                if (typeof fieldValue !== 'string') {\n                    continue\n                }\n                if (locale[fieldName as keyof LocaleInfo] !== fieldValue) {\n                    isSubLocale = false\n                    break\n                }\n            }\n            if (isSubLocale) {\n                fullLocale = TranslationsHelper.toLocaleString(locale)\n                break\n            }\n        }\n\n        return fullLocale\n    }\n\n    /**\n     * Takes list of locales and build traverse order according to LOCALE_RESOLVE_ORDER\n     * Then select first available locale from that list defaulting to defaultLocale\n     * After that enhancing it with first locale, matching selected one\n     *\n     * @example\n     * const availableLocales = [\"zh\", \"en\"] // that's what we have\n     * const locales = [\"zh-Hans-CN\", \"en-GB\", \"zh\"] // that's what user want\n     * // During function execution we build order\n     * const helper = new TranslationsHelper({ locales, defaultLocale: \"zh\" })\n     * const { selectedLocale, fullLocale } = helper.selectSupportedLocale(locales.map(TranslationsHelper.parseLocaleString))\n     * // [\"zh-Hans-CN\", \"zh-Hans\", \"en-GB\", \"en\", \"zh\"] - resolved order\n     * // selectedLocale = \"en\" - first match, on which we can load messages\n     * // fullLocale = \"en-GB\" - sub-locale, providing additional info\n     */\n    selectSupportedLocale (locales: Array<AcceptLanguageInfo | LocaleInfo>): LocaleSelection<AvailableLocale> {\n        const reversedResolveOrder: Array<string> = []\n\n        // NOTE: For each locale passed build resolve order starting from end\n        // Order is important, since direct pass on \"en-GB,fr,en-US\" will produce [\"en-GB\", \"en\", \"fr\", \"en-US\"]\n        // While reverse logic will produce [\"en-GB\", \"fr\", \"en-US\", \"en\"]\n        for (let i = locales.length - 1; i >= 0; i--) {\n            const localeToProcess = locales[i]\n            for (let j = LOCALE_RESOLVE_ORDER.length - 1; j >= 0; j--) {\n                const fields = LOCALE_RESOLVE_ORDER[j]\n                const localeCandidate: LocaleInfo = {\n                    primary: localeToProcess.primary,\n                    extended: undefined,\n                    script: undefined,\n                    region: undefined,\n                }\n                let isValidCandidate = true\n                for (const fieldName of fields) {\n                    if (typeof localeToProcess[fieldName] === 'undefined') {\n                        isValidCandidate = false\n                        break\n                    }\n                    localeCandidate[fieldName] = localeToProcess[fieldName]\n                }\n                if (isValidCandidate) {\n                    const stringCandidate = TranslationsHelper.toLocaleString(localeCandidate)\n                    if (!reversedResolveOrder.includes(stringCandidate)) {\n                        reversedResolveOrder.push(stringCandidate)\n                    }\n                }\n            }\n        }\n\n        // NOTE: now convert it back to direct order\n        reversedResolveOrder.reverse()\n\n        let selectedLocale: AvailableLocale = this._defaultLocale\n\n        for (const localeString of reversedResolveOrder) {\n            if (this._locales.has(localeString)) {\n                selectedLocale = localeString as AvailableLocale\n                break\n            }\n        }\n\n        // NOTE: We select the language from available ones,\n        // but we need to enrich return value with first matching locale, which might affect currency display\n        // and others non-related to message staff\n        return {\n            selectedLocale,\n            fullLocale: this._getFullLocale(selectedLocale, locales),\n        }\n    }\n\n    /**\n     * Obtains locale preference from query parameter (if specified), cookie, request.headers['accept-language'] or window.navigator.languages\n     * and then selects supported locale using selectSupportedLocale method\n     */\n    getPreferredLocale (req?: Optional<IncomingMessage>, res?: Optional<ServerResponse>): LocaleSelection<AvailableLocale> {\n        // Step 1: Query must be resolved before any cookies, since it's more explicit\n        if (this.localeQueryParam) {\n            let paramValue: string | null = null\n            if (req && req.url) {\n                paramValue = new URL(req.url, 'https://_').searchParams.get(this.localeQueryParam)\n            } else if (!isSSR()) {\n                paramValue = new URLSearchParams(window.location.search).get(this.localeQueryParam)\n            }\n            if (paramValue) {\n                const localeSelection = this.selectSupportedLocale([TranslationsHelper.parseLocaleString(paramValue)])\n                if (localeSelection.fullLocale === paramValue) {\n                    return localeSelection\n                }\n            }\n        }\n\n        // Step 2: Cookie must be parsed after query and before other preferences\n        const cookieValue = getCookie(this.localeCookieName, { req, res })\n        if (cookieValue) {\n            const localeSelection = this.selectSupportedLocale([TranslationsHelper.parseLocaleString(cookieValue)])\n            if (localeSelection.fullLocale === cookieValue) {\n                return localeSelection\n            }\n        }\n\n        // NOTE: on server extracts locale from accept-language\n        if (req) {\n            return this.selectSupportedLocale(TranslationsHelper.parseAcceptLanguageHeader(req.headers['accept-language']))\n        } else if (!isSSR()) {\n            return this.selectSupportedLocale(window.navigator.languages.map(TranslationsHelper.parseLocaleString))\n        }\n\n        return {\n            selectedLocale: this._defaultLocale,\n            fullLocale: this._defaultLocale,\n        }\n    }\n\n    /**\n     * Extracts prefetched translations to pageProps, so it can be available during SSR\n     */\n    extractI18NInfo<PropsType extends Record<string, unknown>> (\n        translationsData: PrefetchResult<AvailableLocale, MessagesShape>,\n        pageParams: SSRResult<PropsType>\n    ): SSRResultWithI18N<AvailableLocale, MessagesShape, PropsType> {\n        return {\n            ...pageParams,\n            props: {\n                ...pageParams.props,\n                [I18N_SELECTED_LOCALE_PROP_NAME]: translationsData.selectedLocale,\n                [I18N_FULL_LOCALE_PROP_NAME]: translationsData.fullLocale,\n                [I18N_MESSAGES_PROP_NAME]: translationsData.messages,\n            },\n        }\n    }\n\n    async getTranslations (locale: AvailableLocale): Promise<MessagesShape> {\n        // Step 1. Load default messages once to have full set of messages\n        if (!this._defaultMessages) {\n            const existingMessages = this._translations[this._defaultLocale]\n            // NOTE: translations can be prepopulated during SSR or other prefetches\n            if (existingMessages) {\n                this._defaultMessages = existingMessages\n            } else {\n                this._defaultMessages = await this._loadDefaultMessages()\n            }\n\n            this._translations[this._defaultLocale] = this._defaultMessages\n        }\n\n        // Step 2. If messages were already fetched - return existing one\n        const existingMessages = this._translations[locale]\n        if (existingMessages) {\n            return existingMessages\n        }\n\n        // Step 3. Fetch language messages, which might be partially translated\n        // and combined with default messages to build full message set\n        const partialTranslatedMessages = await this._loadMessages(locale)\n        const messages: MessagesShape = {\n            ...this._defaultMessages,\n            ...partialTranslatedMessages,\n        }\n        this._translations[locale] = messages\n\n        return messages\n    }\n\n    async prefetchTranslations (req: Optional<IncomingMessage>, res: Optional<ServerResponse>): Promise<PrefetchResult<AvailableLocale, MessagesShape>> {\n        const localeSelection = this.getPreferredLocale(req, res)\n        const messages = await this.getTranslations(localeSelection.selectedLocale)\n\n        return {\n            selectedLocale: localeSelection.selectedLocale,\n            fullLocale: localeSelection.fullLocale,\n            messages,\n        }\n    }\n\n    getTranslationsProvider (): FC<TranslationsProviderProps<AvailableLocale, MessagesShape>> {\n        if (!this._context) {\n            this._context = createContext<TranslationsContextType<AvailableLocale, MessagesShape>>({\n                selectedLocale: this._defaultLocale,\n                fullLocale: this._defaultLocale,\n                messages: undefined,\n                switchLocale: () => ({}),\n            })\n        }\n\n        const Context = this._context\n        const getFullLocale = this._getFullLocale\n        const localeCookieName = this.localeCookieName\n        const getTranslations = this.getTranslations\n        const translationsObj = this._translations\n        const getPreferredLocale = this.getPreferredLocale\n\n        return function TranslationsProvider ({\n            initialSelectedLocale,\n            initialFullLocale,\n            initialMessages,\n            children,\n        }) {\n            const [selectedLocale, setSelectedLocale] = useState(initialSelectedLocale)\n            const [fullLocale, setFullLocale] = useState(initialFullLocale)\n            const [messages, setMessages] = useState(initialMessages)\n\n            useEffectOnce(() => {\n                if (!isSSR() && initialSelectedLocale && initialMessages) {\n                    translationsObj[initialSelectedLocale] = initialMessages\n                    setCookie(localeCookieName, initialFullLocale, { sameSite: 'none', secure: true })\n                } else if (!isSSR() && (!initialSelectedLocale || !initialFullLocale || !initialMessages)) {\n                    const localeSelection = getPreferredLocale()\n                    setSelectedLocale(localeSelection.selectedLocale)\n                    setFullLocale(localeSelection.fullLocale)\n                    getTranslations(localeSelection.selectedLocale).then(setMessages)\n                    setCookie(localeCookieName, localeSelection.fullLocale, { sameSite: 'none', secure: true })\n                }\n            })\n\n            const switchLocale = useCallback(async (newLocale: AvailableLocale) => {\n                const fullLocale = getFullLocale(newLocale, window.navigator.languages.map(TranslationsHelper.parseLocaleString))\n                const messages = await getTranslations(newLocale)\n                setSelectedLocale(newLocale)\n                setFullLocale(fullLocale)\n                setMessages(messages)\n                setCookie(localeCookieName, fullLocale, { sameSite: 'none', secure: true })\n            }, [])\n\n            return (\n                <Context.Provider value={{ selectedLocale, fullLocale, messages, switchLocale }}>\n                    {children}\n                </Context.Provider>\n            )\n        }\n    }\n\n    getUseTranslationsExtractorHook () {\n        const defaultLocale = this._defaultLocale\n\n        return function useTranslationsExtractor<PropsType extends Record<string, unknown>> (\n            pageProps: SSRResultWithI18N<AvailableLocale, MessagesShape, PropsType>['props']\n        ) {\n            return {\n                initialSelectedLocale: pageProps[I18N_SELECTED_LOCALE_PROP_NAME] || defaultLocale,\n                initialFullLocale: pageProps[I18N_FULL_LOCALE_PROP_NAME] || defaultLocale,\n                initialMessages: pageProps[I18N_MESSAGES_PROP_NAME] || undefined,\n            }\n        }\n    }\n\n    getUseTranslationsHook () {\n        if (!this._context) {\n            this._context = createContext<TranslationsContextType<AvailableLocale, MessagesShape>>({\n                selectedLocale: this._defaultLocale,\n                fullLocale: this._defaultLocale,\n                messages: undefined,\n                switchLocale: () => ({}),\n            })\n        }\n        const context = this._context\n\n        return function useTranslations () {\n            return useContext(context)\n        }\n    }\n\n    getHOC () {\n        const useTranslationsExtractor = this.getUseTranslationsExtractorHook()\n        const TranslationsProvider = this.getTranslationsProvider()\n        const prefetchTranslations = this.prefetchTranslations\n        const extractI18NInfo = this.extractI18NInfo\n\n        return function withTranslations<\n            PropsType extends Record<string, unknown>,\n            ComponentType,\n            RouterType,\n        > (App: AppType<PropsType, ComponentType, RouterType>): AppType<PropsType, ComponentType, RouterType> {\n            const WithTranslations: AppType<PropsType, ComponentType, RouterType> = (props) => {\n                const { pageProps } = props\n                const { initialSelectedLocale, initialFullLocale, initialMessages } = useTranslationsExtractor(pageProps)\n\n                return (\n                    <TranslationsProvider\n                        initialSelectedLocale={initialSelectedLocale}\n                        initialFullLocale={initialFullLocale}\n                        initialMessages={initialMessages}\n                    >\n                        <App {...props} />\n                    </TranslationsProvider>\n                )\n            }\n\n            const appGetInitialProps = App.getInitialProps\n            if (appGetInitialProps) {\n                WithTranslations.getInitialProps = async function (context) {\n                    const appProps = await appGetInitialProps(context)\n                    const { ctx } = context\n                    const translationsData = await prefetchTranslations(ctx.req, ctx.res)\n                    const { props } = extractI18NInfo(translationsData, { props: appProps.pageProps })\n\n                    return { ...appProps, pageProps: props }\n                }\n            }\n\n            return WithTranslations\n        }\n    }\n}\n","/**\n * Check whether it's a server or client environment\n * @example\n * if (!isSSR()) {\n *     console.log(window.location.href)\n * }\n */\nexport function isSSR (): boolean {\n    return typeof window === 'undefined'\n}\n\n/**\n * Check whether it's development environment or not\n * @example\n * const IS_DEBUG_LOGS_ENABLED = isDebug()\n */\nexport function isDebug (): boolean {\n    return process.env.NODE_ENV === 'development'\n}\n","// SRC: https://github.com/streamich/react-use/blob/master/src/useEffectOnce.ts\n\nimport { EffectCallback, useEffect } from 'react'\n\n/**\n * useEffect wrapper, that runs side effect only once on initial component render\n * @example\n * useEffectOnce(() => {\n *     initAnalytics()\n * })\n */\nexport function useEffectOnce (cb: EffectCallback): void {\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n    useEffect(cb, [])\n}\n"],"mappings":";AAAA,SAAS,WAAW,iBAAiB;AACrC,OAAO,SAAS,eAAe,UAAU,aAAa,kBAAkB;;;ACMjE,SAAS,QAAkB;AAC9B,SAAO,OAAO,WAAW;AAC7B;;;ACPA,SAAyB,iBAAiB;AASnC,SAAS,cAAe,IAA0B;AAErD,YAAU,IAAI,CAAC,CAAC;AACpB;;;AF6KA,IAAM,yBAAmD;AAAA,EACrD,EAAE,MAAM,WAAW,SAAS,WAAW;AAAA,EACvC,EAAE,MAAM,YAAY,SAAS,aAAa;AAAA,EAC1C,EAAE,MAAM,UAAU,SAAS,mBAAmB;AAAA,EAC9C,EAAE,MAAM,UAAU,SAAS,0BAA0B;AACzD;AAEA,IAAM,uBAAuD;AAAA;AAAA,EAEzD,CAAC,WAAW,YAAY,UAAU,QAAQ;AAAA;AAAA,EAE1C,CAAC,WAAW,UAAU,QAAQ;AAAA;AAAA,EAE9B,CAAC,WAAW,QAAQ;AAAA,EACpB,CAAC,WAAW,QAAQ;AAAA,EACpB,CAAC,WAAW,UAAU;AAAA,EACtB,CAAC,SAAS;AACd;AAEA,IAAM,iCAAiC;AACvC,IAAM,6BAA6B;AACnC,IAAM,0BAA0B;AAEzB,IAAM,qBAAN,MAAM,oBAGX;AAAA,EAWE,YAAa,SAAoE;AALjF,SAAiB,gBAAiE,CAAC;AAEnF,SAAS,mBAA2B;AACpC,SAAS,mBAAuC;AAG5C,SAAK,WAAW,IAAI,IAAI,QAAQ,OAAO;AACvC,SAAK,iBAAiB,QAAQ;AAC9B,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,uBAAuB,QAAQ;AAEpC,QAAI,QAAQ,kBAAkB;AAC1B,WAAK,mBAAmB,QAAQ;AAAA,IACpC;AACA,QAAI,QAAQ,kBAAkB;AAC1B,WAAK,mBAAmB,QAAQ;AAAA,IACpC;AAEA,SAAK,0BAA0B,KAAK,wBAAwB,KAAK,IAAI;AACrE,SAAK,yBAAyB,KAAK,uBAAuB,KAAK,IAAI;AACnE,SAAK,kCAAkC,KAAK,gCAAgC,KAAK,IAAI;AACrF,SAAK,qBAAqB,KAAK,mBAAmB,KAAK,IAAI;AAC3D,SAAK,wBAAwB,KAAK,sBAAsB,KAAK,IAAI;AACjE,SAAK,kBAAkB,KAAK,gBAAgB,KAAK,IAAI;AACrD,SAAK,uBAAuB,KAAK,qBAAqB,KAAK,IAAI;AAC/D,SAAK,SAAS,KAAK,OAAO,KAAK,IAAI;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,OAAO,kBAAmB,cAA0C;AAChE,UAAM,cAAc,aAAa,KAAK,EAAE,MAAM,GAAG;AACjD,UAAM,cAAc,YAAY,CAAC,EAAE,MAAM,GAAG;AAE5C,UAAM,UAAU,YAAY,SAAS,IAC/B,WAAW,YAAY,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,CAAC,IACvC;AAEN,UAAM,SAA6B;AAAA,MAC/B,SAAS,YAAY,CAAC;AAAA,MACtB,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR;AAAA,IACJ;AAEA,QAAI,gBAAgB;AACpB,oBAAiB,YAAW,cAAc,aAAa;AACnD,aAAO,gBAAgB,uBAAuB,QAAQ,iBAAiB;AACnE,cAAM,EAAE,MAAM,QAAQ,IAAI,uBAAuB,aAAa;AAC9D,YAAI,QAAQ,KAAK,UAAU,GAAG;AAC1B,iBAAO,IAAI,IAAI;AACf;AACA,mBAAS;AAAA,QACb;AAAA,MACJ;AACA;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAO,0BAA2B,aAA0D;AACxF,YAAQ,eAAe,KAClB,MAAM,GAAG,EACT,IAAI,oBAAmB,iBAAiB,EACxC,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,eAAgB,QAAiD;AACpE,WAAO,CAAC,OAAO,SAAS,OAAO,UAAU,OAAO,QAAQ,OAAO,MAAM,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AAAA,EACnG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,eAAgB,gBAAiC,kBAAkE;AACvH,QAAI,aAAqB;AACzB,UAAM,qBAAqB,oBAAmB,kBAAkB,cAAc;AAC9E,eAAW,UAAU,kBAAkB;AACnC,UAAI,cAAc;AAClB,iBAAW,CAAC,WAAW,UAAU,KAAK,OAAO,QAAQ,kBAAkB,GAAG;AACtE,YAAI,OAAO,eAAe,UAAU;AAChC;AAAA,QACJ;AACA,YAAI,OAAO,SAA6B,MAAM,YAAY;AACtD,wBAAc;AACd;AAAA,QACJ;AAAA,MACJ;AACA,UAAI,aAAa;AACb,qBAAa,oBAAmB,eAAe,MAAM;AACrD;AAAA,MACJ;AAAA,IACJ;AAEA,WAAO;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBA,sBAAuB,SAAmF;AACtG,UAAM,uBAAsC,CAAC;AAK7C,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,YAAM,kBAAkB,QAAQ,CAAC;AACjC,eAAS,IAAI,qBAAqB,SAAS,GAAG,KAAK,GAAG,KAAK;AACvD,cAAM,SAAS,qBAAqB,CAAC;AACrC,cAAM,kBAA8B;AAAA,UAChC,SAAS,gBAAgB;AAAA,UACzB,UAAU;AAAA,UACV,QAAQ;AAAA,UACR,QAAQ;AAAA,QACZ;AACA,YAAI,mBAAmB;AACvB,mBAAW,aAAa,QAAQ;AAC5B,cAAI,OAAO,gBAAgB,SAAS,MAAM,aAAa;AACnD,+BAAmB;AACnB;AAAA,UACJ;AACA,0BAAgB,SAAS,IAAI,gBAAgB,SAAS;AAAA,QAC1D;AACA,YAAI,kBAAkB;AAClB,gBAAM,kBAAkB,oBAAmB,eAAe,eAAe;AACzE,cAAI,CAAC,qBAAqB,SAAS,eAAe,GAAG;AACjD,iCAAqB,KAAK,eAAe;AAAA,UAC7C;AAAA,QACJ;AAAA,MACJ;AAAA,IACJ;AAGA,yBAAqB,QAAQ;AAE7B,QAAI,iBAAkC,KAAK;AAE3C,eAAW,gBAAgB,sBAAsB;AAC7C,UAAI,KAAK,SAAS,IAAI,YAAY,GAAG;AACjC,yBAAiB;AACjB;AAAA,MACJ;AAAA,IACJ;AAKA,WAAO;AAAA,MACH;AAAA,MACA,YAAY,KAAK,eAAe,gBAAgB,OAAO;AAAA,IAC3D;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAoB,KAAiC,KAAkE;AAEnH,QAAI,KAAK,kBAAkB;AACvB,UAAI,aAA4B;AAChC,UAAI,OAAO,IAAI,KAAK;AAChB,qBAAa,IAAI,IAAI,IAAI,KAAK,WAAW,EAAE,aAAa,IAAI,KAAK,gBAAgB;AAAA,MACrF,WAAW,CAAC,MAAM,GAAG;AACjB,qBAAa,IAAI,gBAAgB,OAAO,SAAS,MAAM,EAAE,IAAI,KAAK,gBAAgB;AAAA,MACtF;AACA,UAAI,YAAY;AACZ,cAAM,kBAAkB,KAAK,sBAAsB,CAAC,oBAAmB,kBAAkB,UAAU,CAAC,CAAC;AACrG,YAAI,gBAAgB,eAAe,YAAY;AAC3C,iBAAO;AAAA,QACX;AAAA,MACJ;AAAA,IACJ;AAGA,UAAM,cAAc,UAAU,KAAK,kBAAkB,EAAE,KAAK,IAAI,CAAC;AACjE,QAAI,aAAa;AACb,YAAM,kBAAkB,KAAK,sBAAsB,CAAC,oBAAmB,kBAAkB,WAAW,CAAC,CAAC;AACtG,UAAI,gBAAgB,eAAe,aAAa;AAC5C,eAAO;AAAA,MACX;AAAA,IACJ;AAGA,QAAI,KAAK;AACL,aAAO,KAAK,sBAAsB,oBAAmB,0BAA0B,IAAI,QAAQ,iBAAiB,CAAC,CAAC;AAAA,IAClH,WAAW,CAAC,MAAM,GAAG;AACjB,aAAO,KAAK,sBAAsB,OAAO,UAAU,UAAU,IAAI,oBAAmB,iBAAiB,CAAC;AAAA,IAC1G;AAEA,WAAO;AAAA,MACH,gBAAgB,KAAK;AAAA,MACrB,YAAY,KAAK;AAAA,IACrB;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKA,gBACI,kBACA,YAC4D;AAC5D,WAAO;AAAA,MACH,GAAG;AAAA,MACH,OAAO;AAAA,QACH,GAAG,WAAW;AAAA,QACd,CAAC,8BAA8B,GAAG,iBAAiB;AAAA,QACnD,CAAC,0BAA0B,GAAG,iBAAiB;AAAA,QAC/C,CAAC,uBAAuB,GAAG,iBAAiB;AAAA,MAChD;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,gBAAiB,QAAiD;AAEpE,QAAI,CAAC,KAAK,kBAAkB;AACxB,YAAMA,oBAAmB,KAAK,cAAc,KAAK,cAAc;AAE/D,UAAIA,mBAAkB;AAClB,aAAK,mBAAmBA;AAAA,MAC5B,OAAO;AACH,aAAK,mBAAmB,MAAM,KAAK,qBAAqB;AAAA,MAC5D;AAEA,WAAK,cAAc,KAAK,cAAc,IAAI,KAAK;AAAA,IACnD;AAGA,UAAM,mBAAmB,KAAK,cAAc,MAAM;AAClD,QAAI,kBAAkB;AAClB,aAAO;AAAA,IACX;AAIA,UAAM,4BAA4B,MAAM,KAAK,cAAc,MAAM;AACjE,UAAM,WAA0B;AAAA,MAC5B,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACP;AACA,SAAK,cAAc,MAAM,IAAI;AAE7B,WAAO;AAAA,EACX;AAAA,EAEA,MAAM,qBAAsB,KAAgC,KAAwF;AAChJ,UAAM,kBAAkB,KAAK,mBAAmB,KAAK,GAAG;AACxD,UAAM,WAAW,MAAM,KAAK,gBAAgB,gBAAgB,cAAc;AAE1E,WAAO;AAAA,MACH,gBAAgB,gBAAgB;AAAA,MAChC,YAAY,gBAAgB;AAAA,MAC5B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,0BAA0F;AACtF,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,cAAuE;AAAA,QACnF,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,QACjB,UAAU;AAAA,QACV,cAAc,OAAO,CAAC;AAAA,MAC1B,CAAC;AAAA,IACL;AAEA,UAAM,UAAU,KAAK;AACrB,UAAM,gBAAgB,KAAK;AAC3B,UAAM,mBAAmB,KAAK;AAC9B,UAAM,kBAAkB,KAAK;AAC7B,UAAM,kBAAkB,KAAK;AAC7B,UAAM,qBAAqB,KAAK;AAEhC,WAAO,SAAS,qBAAsB;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACJ,GAAG;AACC,YAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAS,qBAAqB;AAC1E,YAAM,CAAC,YAAY,aAAa,IAAI,SAAS,iBAAiB;AAC9D,YAAM,CAAC,UAAU,WAAW,IAAI,SAAS,eAAe;AAExD,oBAAc,MAAM;AAChB,YAAI,CAAC,MAAM,KAAK,yBAAyB,iBAAiB;AACtD,0BAAgB,qBAAqB,IAAI;AACzC,oBAAU,kBAAkB,mBAAmB,EAAE,UAAU,QAAQ,QAAQ,KAAK,CAAC;AAAA,QACrF,WAAW,CAAC,MAAM,MAAM,CAAC,yBAAyB,CAAC,qBAAqB,CAAC,kBAAkB;AACvF,gBAAM,kBAAkB,mBAAmB;AAC3C,4BAAkB,gBAAgB,cAAc;AAChD,wBAAc,gBAAgB,UAAU;AACxC,0BAAgB,gBAAgB,cAAc,EAAE,KAAK,WAAW;AAChE,oBAAU,kBAAkB,gBAAgB,YAAY,EAAE,UAAU,QAAQ,QAAQ,KAAK,CAAC;AAAA,QAC9F;AAAA,MACJ,CAAC;AAED,YAAM,eAAe,YAAY,OAAO,cAA+B;AACnE,cAAMC,cAAa,cAAc,WAAW,OAAO,UAAU,UAAU,IAAI,oBAAmB,iBAAiB,CAAC;AAChH,cAAMC,YAAW,MAAM,gBAAgB,SAAS;AAChD,0BAAkB,SAAS;AAC3B,sBAAcD,WAAU;AACxB,oBAAYC,SAAQ;AACpB,kBAAU,kBAAkBD,aAAY,EAAE,UAAU,QAAQ,QAAQ,KAAK,CAAC;AAAA,MAC9E,GAAG,CAAC,CAAC;AAEL,aACI,oCAAC,QAAQ,UAAR,EAAiB,OAAO,EAAE,gBAAgB,YAAY,UAAU,aAAa,KACzE,QACL;AAAA,IAER;AAAA,EACJ;AAAA,EAEA,kCAAmC;AAC/B,UAAM,gBAAgB,KAAK;AAE3B,WAAO,SAAS,yBACZ,WACF;AACE,aAAO;AAAA,QACH,uBAAuB,UAAU,8BAA8B,KAAK;AAAA,QACpE,mBAAmB,UAAU,0BAA0B,KAAK;AAAA,QAC5D,iBAAiB,UAAU,uBAAuB,KAAK;AAAA,MAC3D;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,yBAA0B;AACtB,QAAI,CAAC,KAAK,UAAU;AAChB,WAAK,WAAW,cAAuE;AAAA,QACnF,gBAAgB,KAAK;AAAA,QACrB,YAAY,KAAK;AAAA,QACjB,UAAU;AAAA,QACV,cAAc,OAAO,CAAC;AAAA,MAC1B,CAAC;AAAA,IACL;AACA,UAAM,UAAU,KAAK;AAErB,WAAO,SAAS,kBAAmB;AAC/B,aAAO,WAAW,OAAO;AAAA,IAC7B;AAAA,EACJ;AAAA,EAEA,SAAU;AACN,UAAM,2BAA2B,KAAK,gCAAgC;AACtE,UAAM,uBAAuB,KAAK,wBAAwB;AAC1D,UAAM,uBAAuB,KAAK;AAClC,UAAM,kBAAkB,KAAK;AAE7B,WAAO,SAAS,iBAIb,KAAmG;AAClG,YAAM,mBAAkE,CAAC,UAAU;AAC/E,cAAM,EAAE,UAAU,IAAI;AACtB,cAAM,EAAE,uBAAuB,mBAAmB,gBAAgB,IAAI,yBAAyB,SAAS;AAExG,eACI;AAAA,UAAC;AAAA;AAAA,YACG;AAAA,YACA;AAAA,YACA;AAAA;AAAA,UAEA,oCAAC,OAAK,GAAG,OAAO;AAAA,QACpB;AAAA,MAER;AAEA,YAAM,qBAAqB,IAAI;AAC/B,UAAI,oBAAoB;AACpB,yBAAiB,kBAAkB,eAAgB,SAAS;AACxD,gBAAM,WAAW,MAAM,mBAAmB,OAAO;AACjD,gBAAM,EAAE,IAAI,IAAI;AAChB,gBAAM,mBAAmB,MAAM,qBAAqB,IAAI,KAAK,IAAI,GAAG;AACpE,gBAAM,EAAE,MAAM,IAAI,gBAAgB,kBAAkB,EAAE,OAAO,SAAS,UAAU,CAAC;AAEjF,iBAAO,EAAE,GAAG,UAAU,WAAW,MAAM;AAAA,QAC3C;AAAA,MACJ;AAEA,aAAO;AAAA,IACX;AAAA,EACJ;AACJ;","names":["existingMessages","fullLocale","messages"]}