Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Lingui to use linguiLocale with country identifiers like en-us, it would always load en in this case. Introced a new useLocale hook to use the correct locale string to use in Intl methods. #2196

Merged
merged 5 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/slow-kiwis-exercise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@graphcommerce/magento-product-configurable': patch
'@graphcommerce/magento-product': patch
'@graphcommerce/next-config': patch
'@graphcommerce/lingui-next': patch
'@graphcommerce/next-ui': patch
'@graphcommerce/docs': patch
---

Allow Lingui to use linguiLocale with country identifiers like `en-us`, it would always load `en` in this case. Introced a new `useLocale` hook to use the correct locale string to use in Intl methods.
10 changes: 7 additions & 3 deletions docs/framework/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Examples:

You can export configuration by running `yarn graphcommerce export-config`

## Extending the configuration in your project
## Extending the configuration in your project

Create a graphql/Config.graphqls file in your project and extend the GraphCommerceConfig, GraphCommerceStorefrontConfig inputs to add configuration.

Expand Down Expand Up @@ -344,7 +344,9 @@ All storefront configuration for the project

#### locale: string (required)

Must be a locale string https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers
Must be a [locale string](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers) for automatic redirects to work.

This value can be used as a sub-path identifier only, make sure linguiLocale is configured for each URL.

#### magentoStoreCode: string (required)

Expand Down Expand Up @@ -400,7 +402,9 @@ Add a gcms-locales header to make sure queries return in a certain language, can

#### linguiLocale: string

Specify a custom locale for to load translations.
Specify a custom locale for to load translations. Must be lowercase valid locale.

This value is also used for the Intl.

### MagentoConfigurableVariantValues

Expand Down
7 changes: 6 additions & 1 deletion examples/magento-graphcms/lingui.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ const linguiNextConfig = require('@graphcommerce/lingui-next/config')
const { loadConfig } = require('@graphcommerce/next-config')
require('dotenv').config()

const locales = loadConfig(process.cwd()).storefront.map(({ locale }) => locale)
const locales = loadConfig(process.cwd()).storefront.map(({ locale, linguiLocale }) => {
if (linguiLocale) return linguiLocale
const matches = locale?.match(/([a-z]{2})-([a-z]{2})-([a-z]+)/i)
if (matches) return `${matches[1]}-${matches[2]}`
return locale
})

module.exports = linguiNextConfig({ locales })
4 changes: 3 additions & 1 deletion packages/lingui-next/Config.graphqls
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
extend input GraphCommerceStorefrontConfig {
"""
Specify a custom locale for to load translations.
Specify a custom locale for to load translations. Must be lowercase valid locale.

This value is also used for the Intl.
"""
linguiLocale: String
}
Expand Down
16 changes: 11 additions & 5 deletions packages/lingui-next/components/LinguiProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import { storefrontConfig, storefrontConfigDefault } from '@graphcommerce/next-ui'
import { useLocale } from '@graphcommerce/next-ui'
import { i18n, Messages } from '@lingui/core'
import { I18nProvider, I18nProviderProps } from '@lingui/react'
import React, { useMemo } from 'react'
import { MessageLoader, SyncMessageLoader } from '../types'
import { normalizeLocale } from '../lib/normalizeLocale'

export type LinguiProviderProps = Omit<I18nProviderProps, 'i18n'> & {
children: React.ReactNode
loader: MessageLoader
ssrLoader: SyncMessageLoader
locale: string
/**
* @deprecated not necessary anumore
*/
locale?: string
}

export const localeConfig = (locale: string = storefrontConfigDefault().locale) =>
storefrontConfig(locale)?.linguiLocale ?? locale?.split('-')[0]
/**
* @deprecated use normalizeLocale
*/
export const localeConfig = normalizeLocale

export function LinguiProvider(props: LinguiProviderProps) {
const { loader, ssrLoader, locale, ...i18nProviderProps } = props

const localeOnly = localeConfig(locale)
const localeOnly = useLocale()

useMemo(() => {
const data = globalThis.document?.getElementById('lingui')
Expand Down
4 changes: 1 addition & 3 deletions packages/lingui-next/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ function linguiNextConfig(config) {
const { locales, ...otherConfig } = config
return {
orderBy: 'messageId',
locales: isMonorepo()
? ['en', 'nl', 'fr', 'de', 'es', 'it']
: config.locales.map((l) => l?.split('-')[0]),
locales: isMonorepo() ? ['en', 'nl', 'fr', 'de', 'es', 'it'] : config.locales,
// formatOptions: { lineNumbers: false, origins: false, explicitIdAsDefault: true },
format: formatter({ explicitIdAsDefault: true, lineNumbers: false, origins: false }),
catalogs: [
Expand Down
3 changes: 2 additions & 1 deletion packages/lingui-next/document/withLingui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DocumentContext, DocumentInitialProps } from 'next/document'
// eslint-disable-next-line @next/next/no-document-import-in-page
import type NextDocument from 'next/document'
import React from 'react'
import { normalizeLocale } from '../lib/normalizeLocale'
import { MessageLoader } from '../types'

export type LinguiDocumentProps = { linguiScriptTag: React.ReactNode }
Expand All @@ -16,7 +17,7 @@ export function withLingui(
static async getInitialProps(ctx: DocumentContext) {
const initial = await Document.getInitialProps(ctx)

const locale = ctx.locale?.split('-')?.[0]
const locale = normalizeLocale(ctx.locale)

if (!locale) return initial
try {
Expand Down
12 changes: 7 additions & 5 deletions packages/lingui-next/lib/normalizeLocale.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { storefrontConfig, storefrontConfigDefault } from '@graphcommerce/next-ui'

/**
* To support using multiple storefronts using the same language locale (which
* next.js does not support), we use an additional 'tag' in the locale code in
Expand All @@ -9,16 +11,16 @@
* Use this method to get a 'normalized' locale that can safely be used in such
* places.
*/
export function normalizeLocale(locale: string | undefined) {
if (!locale) return locale
export function normalizeLocale(locale: string | undefined = storefrontConfigDefault().locale) {
const linguiLocale = storefrontConfig(locale)?.linguiLocale
if (linguiLocale) return linguiLocale

// Specifically match the xx-yy-storecode format, so we don't accidently 'fix'
// valid locales such as he-Hebr-IL or zh-Hans-CN. This this isn't perfect and
// we should consider a more formalized way to use such pseudo-locales, which
// can be matched more precisely.

const matches = locale?.match(/([a-z]{2})-([a-z]{2})-([a-z]+)/i)
if (matches) {
return `${matches[1]}-${matches[2]}`
}
if (matches) return `${matches[1]}-${matches[2]}`
return locale
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { AddToCartItemSelector, useFormAddProductsToCart } from '@graphcommerce/magento-product'
import { filterNonNullableKeys, ActionCardListProps } from '@graphcommerce/next-ui'
import {
filterNonNullableKeys,
ActionCardListProps,
useStorefrontConfig,
useLocale,
} from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
import { Box, SxProps, Theme } from '@mui/material'
import { useRouter } from 'next/router'
Expand All @@ -27,7 +32,6 @@ export function ConfigurableProductOptions(props: ConfigurableProductOptionsProp
...other
} = props
const { setError, clearErrors } = useFormAddProductsToCart()
const { locale } = useRouter()

const options = filterNonNullableKeys(product.configurable_options, [
'attribute_code',
Expand All @@ -41,8 +45,9 @@ export function ConfigurableProductOptions(props: ConfigurableProductOptionsProp
(configured?.configurable_product_options_selection?.options_available_for_selection ?? [])
.length === 0

const locale = useLocale()
const allLabels = useMemo(() => {
const formatter = new Intl.ListFormat(normalizeLocale(locale), {
const formatter = new Intl.ListFormat(locale, {
style: 'long',
type: 'conjunction',
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
MessageSnackbar,
MessageSnackbarProps,
nonNullable,
useLocale,
} from '@graphcommerce/next-ui'
import { Trans } from '@lingui/react'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { findAddedItems } from './findAddedItems'
import { toUserErrors } from './toUserErrors'
Expand All @@ -26,9 +26,8 @@ export function AddProductsToCartSnackbar(props: AddProductsToCartSnackbarProps)
const { errorSnackbar, successSnackbar } = props
const { error, data, redirect, control, submittedVariables } = useFormAddProductsToCart()
const formState = useFormState({ control })
const { locale } = useRouter()

const formatter = new Intl.ListFormat(locale, { style: 'long', type: 'conjunction' })
const formatter = new Intl.ListFormat(useLocale(), { style: 'long', type: 'conjunction' })

const userErrors = toUserErrors(data)

Expand Down
10 changes: 8 additions & 2 deletions packages/next-ui/TimeAgo/TimeAgo.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { useLocale } from '../hooks/useLocale'

export type TimeAgoProps = {
date: Date
/**
* @deprecated No longer used
*/
locale?: string
}

export function TimeAgo(props: TimeAgoProps) {
const { date, locale = 'en' } = props
const { date } = props
const msPerMinute = 60 * 1000
const msPerHour = msPerMinute * 60
const msPerDay = msPerHour * 24

const timestamp = date.getTime()
const elapsed = Date.now() - timestamp
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })

const rtf = new Intl.RelativeTimeFormat(useLocale(), { numeric: 'auto' })

if (elapsed < msPerMinute) {
return <span>{rtf.format(-Math.floor(elapsed / 1000), 'seconds')}</span>
Expand Down
1 change: 1 addition & 0 deletions packages/next-ui/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export * from './useNumberFormat'
export * from './useMemoObject'
export * from './useStorefrontConfig'
export * from './useUrlQuery'
export * from './useLocale'
10 changes: 4 additions & 6 deletions packages/next-ui/hooks/useDateTimeFormat.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { useStorefrontConfig } from './useStorefrontConfig'
import { useLocale } from '@graphcommerce/next-ui'

export type DateTimeFormatProps = Intl.DateTimeFormatOptions

export function useDateTimeFormat(props?: DateTimeFormatProps) {
const { locale } = useRouter()
const locale = useLocale()

const formatter = useMemo(
() => new Intl.DateTimeFormat(normalizeLocale(locale), props),
[locale, props],
)
const formatter = useMemo(() => new Intl.DateTimeFormat(locale, props), [locale, props])
return formatter
}
7 changes: 7 additions & 0 deletions packages/next-ui/hooks/useLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { useStorefrontConfig } from './useStorefrontConfig'

export function useLocale() {
const { locale } = useStorefrontConfig()
return normalizeLocale(locale)
}
11 changes: 3 additions & 8 deletions packages/next-ui/hooks/useNumberFormat.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { useRouter } from 'next/router'
import { useMemo } from 'react'
import { useLocale } from './useLocale'

export type NumberFormatProps = Intl.NumberFormatOptions

export function useNumberFormat(props?: NumberFormatProps) {
const { locale } = useRouter()

const formatter = useMemo(
() => new Intl.NumberFormat(normalizeLocale(locale), props),
[locale, props],
)
const locale = useLocale()
const formatter = useMemo(() => new Intl.NumberFormat(locale, props), [locale, props])
return formatter
}
6 changes: 4 additions & 2 deletions packagesDev/next-config/Config.graphqls
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Examples:

You can export configuration by running `yarn graphcommerce export-config`

## Extending the configuration in your project
## Extending the configuration in your project

Create a graphql/Config.graphqls file in your project and extend the GraphCommerceConfig, GraphCommerceStorefrontConfig inputs to add configuration.

Expand Down Expand Up @@ -132,7 +132,9 @@ input GraphCommerceStorefrontConfig {
domain: String

"""
Must be a locale string https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers
Must be a [locale string](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers) for automatic redirects to work.

This value can be used as a sub-path identifier only, make sure linguiLocale is configured for each URL.
"""
locale: String!

Expand Down
23 changes: 19 additions & 4 deletions packagesDev/next-config/dist/config/demoConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,25 @@ exports.demoConfig = {
hygraphLocales: ['nl', 'en_us'],
cartDisplayPricesInclTax: true,
},
{ locale: 'fr-be', magentoStoreCode: 'fr_BE', cartDisplayPricesInclTax: true },
{ locale: 'nl-be', magentoStoreCode: 'nl_BE', cartDisplayPricesInclTax: true },
{ locale: 'en-gb', magentoStoreCode: 'en_GB', cartDisplayPricesInclTax: true },
{ locale: 'en-ca', magentoStoreCode: 'en_CA' },
{
locale: 'fr-be',
magentoStoreCode: 'fr_BE',
cartDisplayPricesInclTax: true,
linguiLocale: 'fr',
},
{
locale: 'nl-be',
magentoStoreCode: 'nl_BE',
cartDisplayPricesInclTax: true,
linguiLocale: 'nl',
},
{
locale: 'en-gb',
magentoStoreCode: 'en_GB',
cartDisplayPricesInclTax: true,
linguiLocale: 'en',
},
{ locale: 'en-ca', magentoStoreCode: 'en_CA', linguiLocale: 'en' },
],
productFiltersPro: true,
productFiltersLayout: 'DEFAULT',
Expand Down
23 changes: 19 additions & 4 deletions packagesDev/next-config/src/config/demoConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,25 @@ export const demoConfig: PartialDeep<GraphCommerceConfig, { recurseIntoArrays: t
hygraphLocales: ['nl', 'en_us'],
cartDisplayPricesInclTax: true,
},
{ locale: 'fr-be', magentoStoreCode: 'fr_BE', cartDisplayPricesInclTax: true },
{ locale: 'nl-be', magentoStoreCode: 'nl_BE', cartDisplayPricesInclTax: true },
{ locale: 'en-gb', magentoStoreCode: 'en_GB', cartDisplayPricesInclTax: true },
{ locale: 'en-ca', magentoStoreCode: 'en_CA' },
{
locale: 'fr-be',
magentoStoreCode: 'fr_BE',
cartDisplayPricesInclTax: true,
linguiLocale: 'fr',
},
{
locale: 'nl-be',
magentoStoreCode: 'nl_BE',
cartDisplayPricesInclTax: true,
linguiLocale: 'nl',
},
{
locale: 'en-gb',
magentoStoreCode: 'en_GB',
cartDisplayPricesInclTax: true,
linguiLocale: 'en',
},
{ locale: 'en-ca', magentoStoreCode: 'en_CA', linguiLocale: 'en' },
],
productFiltersPro: true,
productFiltersLayout: 'DEFAULT',
Expand Down
14 changes: 11 additions & 3 deletions packagesDev/next-config/src/generated/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export type DatalayerConfig = {
*
* You can export configuration by running `yarn graphcommerce export-config`
*
* ## Extending the configuration in your project
* ## Extending the configuration in your project
*
* Create a graphql/Config.graphqls file in your project and extend the GraphCommerceConfig, GraphCommerceStorefrontConfig inputs to add configuration.
*
Expand Down Expand Up @@ -365,9 +365,17 @@ export type GraphCommerceStorefrontConfig = {
googleTagmanagerId?: InputMaybe<Scalars['String']['input']>;
/** Add a gcms-locales header to make sure queries return in a certain language, can be an array to define fallbacks. */
hygraphLocales?: InputMaybe<Array<Scalars['String']['input']>>;
/** Specify a custom locale for to load translations. */
/**
* Specify a custom locale for to load translations. Must be lowercase valid locale.
*
* This value is also used for the Intl.
*/
linguiLocale?: InputMaybe<Scalars['String']['input']>;
/** Must be a locale string https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers */
/**
* Must be a [locale string](https://www.unicode.org/reports/tr35/tr35-59/tr35.html#Identifiers) for automatic redirects to work.
*
* This value can be used as a sub-path identifier only, make sure linguiLocale is configured for each URL.
*/
locale: Scalars['String']['input'];
/**
* Magento store code.
Expand Down
Loading