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

Normalize <html lang=... locale (GCOM-1304) #2137

Merged
merged 5 commits into from
Jan 15, 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
7 changes: 7 additions & 0 deletions .changeset/sixty-zebras-battle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@graphcommerce/magento-product-configurable': patch
'@graphcommerce/lingui-next': patch
'@graphcommerce/next-ui': patch
---

Don't render pseudo-locale in HTML lang attribute
3 changes: 2 additions & 1 deletion examples/magento-graphcms/pages/_document.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { withLingui } from '@graphcommerce/lingui-next/document/withLingui'
import type { LinguiDocumentProps } from '@graphcommerce/lingui-next/document/withLingui'
import { EmotionCacheProps, withEmotionCache } from '@graphcommerce/next-ui'
Expand All @@ -6,7 +7,7 @@ import NextDocument, { Html, Head, Main, NextScript } from 'next/document'
class Document extends NextDocument<EmotionCacheProps & LinguiDocumentProps> {
render() {
return (
<Html>
<Html lang={normalizeLocale(this.props.locale)}>
<Head>
{/* Inject MUI styles first to match with the prepend: true configuration. */}
<meta name='emotion-insertion-point' content='' />
Expand Down
1 change: 1 addition & 0 deletions packages/lingui-next/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './components/LinguiProvider'
export * from './types'
export * from './lib/normalizeLocale'
24 changes: 24 additions & 0 deletions packages/lingui-next/lib/normalizeLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* 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
* which we specify a unique string (i.e. a Magento store code).
*
* This makes next.js happy, as it still follows the BCP47 spec. However, the
* Intl API and other places may not accept this as a valid locale.
*
* 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

// 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]}`
}
return locale
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { AddToCartItemSelector, useFormAddProductsToCart } from '@graphcommerce/magento-product'
import { filterNonNullableKeys, ActionCardListProps } from '@graphcommerce/next-ui'
import { i18n } from '@lingui/core'
Expand Down Expand Up @@ -41,10 +42,10 @@ export function ConfigurableProductOptions(props: ConfigurableProductOptionsProp
.length === 0

const allLabels = useMemo(() => {
// Remove optional dialect from locale, which Intl.NumberFormat does not support.
const strippedLocale = locale?.split('-', 2).join('-')

const formatter = new Intl.ListFormat(strippedLocale, { style: 'long', type: 'conjunction' })
const formatter = new Intl.ListFormat(normalizeLocale(locale), {
style: 'long',
type: 'conjunction',
})
return formatter.format(options.map((o) => o.label))
}, [locale, options])

Expand Down
1 change: 1 addition & 0 deletions packages/magento-product-configurable/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@graphcommerce/graphql": "^8.0.0-canary.78",
"@graphcommerce/graphql-mesh": "^8.0.0-canary.78",
"@graphcommerce/image": "^8.0.0-canary.78",
"@graphcommerce/lingui-next": "8.0.0-canary.78",
"@graphcommerce/magento-cart": "^8.0.0-canary.78",
"@graphcommerce/magento-cart-items": "^8.0.0-canary.78",
"@graphcommerce/magento-category": "^8.0.0-canary.78",
Expand Down
8 changes: 3 additions & 5 deletions packages/next-ui/hooks/useDateTimeFormat.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { useRouter } from 'next/router'
import { useMemo } from 'react'

Expand All @@ -6,12 +7,9 @@ export type DateTimeFormatProps = Intl.DateTimeFormatOptions
export function useDateTimeFormat(props?: DateTimeFormatProps) {
const { locale } = useRouter()

// Remove optional dialect from locale, which Intl.NumberFormat does not support.
const strippedLocale = locale?.split('-', 2).join('-')

const formatter = useMemo(
() => new Intl.DateTimeFormat(strippedLocale, props),
[strippedLocale, props],
() => new Intl.DateTimeFormat(normalizeLocale(locale), props),
[locale, props],
)
return formatter
}
8 changes: 3 additions & 5 deletions packages/next-ui/hooks/useNumberFormat.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { normalizeLocale } from '@graphcommerce/lingui-next'
import { useRouter } from 'next/router'
import { useMemo } from 'react'

Expand All @@ -6,12 +7,9 @@ export type NumberFormatProps = Intl.NumberFormatOptions
export function useNumberFormat(props?: NumberFormatProps) {
const { locale } = useRouter()

// Remove optional dialect from locale, which Intl.NumberFormat does not support.
const strippedLocale = locale?.split('-', 2).join('-')

const formatter = useMemo(
() => new Intl.NumberFormat(strippedLocale, props),
[strippedLocale, props],
() => new Intl.NumberFormat(normalizeLocale(locale), props),
[locale, props],
)
return formatter
}
1 change: 1 addition & 0 deletions packages/next-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"@graphcommerce/framer-scroller": "^8.0.0-canary.78",
"@graphcommerce/framer-utils": "^8.0.0-canary.78",
"@graphcommerce/image": "^8.0.0-canary.78",
"@graphcommerce/lingui-next": "^8.0.0-canary.78",
"@graphcommerce/prettier-config-pwa": "^8.0.0-canary.78",
"@graphcommerce/typescript-config-pwa": "^8.0.0-canary.78",
"@lingui/core": "^4.2.1",
Expand Down
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3967,6 +3967,7 @@ __metadata:
"@graphcommerce/graphql": ^8.0.0-canary.78
"@graphcommerce/graphql-mesh": ^8.0.0-canary.78
"@graphcommerce/image": ^8.0.0-canary.78
"@graphcommerce/lingui-next": 8.0.0-canary.78
"@graphcommerce/magento-cart": ^8.0.0-canary.78
"@graphcommerce/magento-cart-items": ^8.0.0-canary.78
"@graphcommerce/magento-category": ^8.0.0-canary.78
Expand Down Expand Up @@ -4300,6 +4301,7 @@ __metadata:
"@graphcommerce/framer-scroller": ^8.0.0-canary.78
"@graphcommerce/framer-utils": ^8.0.0-canary.78
"@graphcommerce/image": ^8.0.0-canary.78
"@graphcommerce/lingui-next": ^8.0.0-canary.78
"@graphcommerce/prettier-config-pwa": ^8.0.0-canary.78
"@graphcommerce/typescript-config-pwa": ^8.0.0-canary.78
"@lingui/core": ^4.2.1
Expand Down
Loading