Skip to content

Commit

Permalink
Merge pull request #54 from Shopify/hl-internationalization
Browse files Browse the repository at this point in the history
Internationalization
  • Loading branch information
wizardlyhel committed Oct 4, 2022
2 parents b5bcb98 + de2de6c commit f104d25
Show file tree
Hide file tree
Showing 40 changed files with 331 additions and 235 deletions.
8 changes: 4 additions & 4 deletions app/components/AccountDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Link, Outlet, useOutlet } from "@remix-run/react";
import { Outlet, useOutlet } from "@remix-run/react";
import type { Customer } from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import { Modal } from "~/components";
import { Modal, LinkI18n } from "~/components";
import type { AccountDetailsOutletContext } from "~/routes/account/edit";

export function AccountDetails({ customer }: { customer: Customer }) {
Expand All @@ -20,9 +20,9 @@ export function AccountDetails({ customer }: { customer: Customer }) {
<div className="lg:p-8 p-6 border border-gray-200 rounded">
<div className="flex">
<h3 className="font-bold text-base flex-1">Profile & Security</h3>
<Link className="underline text-sm font-normal" to="edit">
<LinkI18n className="underline text-sm font-normal" to="edit">
Edit
</Link>
</LinkI18n>
</div>
<div className="mt-4 text-sm text-primary/50">Name</div>
<p className="mt-1">
Expand Down
7 changes: 4 additions & 3 deletions app/components/CartDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { useScroll } from "react-use";
import { flattenConnection, Money } from "@shopify/hydrogen-ui-alpha";
import {
type FetcherWithComponents,
Link,
useFetcher,
useLocation,
Link,
} from "@remix-run/react";

import {
Expand All @@ -15,6 +15,7 @@ import {
ProductCard,
Skeleton,
Text,
LinkI18n,
} from "~/components";
import type {
Cart,
Expand Down Expand Up @@ -185,9 +186,9 @@ function CartLineItem({
<div className="flex justify-between flex-grow">
<div className="grid gap-2">
<Heading as="h3" size="copy">
<Link to={`/products/${merchandise.product.handle}`}>
<LinkI18n to={`/products/${merchandise.product.handle}`}>
{merchandise.product.title}
</Link>
</LinkI18n>
</Heading>

<div className="grid pb-2">
Expand Down
39 changes: 18 additions & 21 deletions app/components/CountrySelector.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,20 @@
import { Link, useAsyncValue, useLocation, Await } from "@remix-run/react";
import { Link, useAsyncValue, useLocation, Await, useParams } from "@remix-run/react";
import { Country } from "@shopify/hydrogen-ui-alpha/storefront-api-types";

import {Listbox} from '@headlessui/react';
import { useState, Suspense } from "react";
import { IconCaret, IconCheck } from "./Icon";
import { getLocalizationFromLang } from "~/lib/utils";

export function CountrySelector({
defaultCountry,
countries,
}: {
defaultCountry: Country;
countries: Array <Country>;
}) {
const selectedCountry = defaultCountry.isoCode;
return (
<Suspense fallback={<CountrySelectorFallback />}>
<Await resolve={countries}>
<CountrySelectorElement
defaultCountry={defaultCountry}
selectedCountry={selectedCountry}
/>
<CountrySelectorElement />
</Await>
</Suspense>
)
Expand All @@ -32,25 +27,23 @@ function CountrySelectorFallback() {
<Listbox.Button
className={'flex items-center justify-between w-full py-3 px-4 border rounded border-contrast/30 dark:border-white'}
>
<span className="">--</span>
<span>--</span>
<IconCaret direction={'down'} />
</Listbox.Button>
</Listbox>
</div>
)
}

function CountrySelectorElement({
defaultCountry,
selectedCountry
}: {
defaultCountry: Country;
selectedCountry: string;
}) {
function CountrySelectorElement() {
const [listboxOpen, setListboxOpen] = useState(false);
const countries = useAsyncValue<Array <Country>>();
const { pathname } = useLocation();
const currentCountry = countries.find(country => country.isoCode === selectedCountry) || defaultCountry;
const { lang } = useParams();
const { language, country } = getLocalizationFromLang(lang);
const languageIsoCode = language.toLowerCase();
const strippedPathname = pathname.replace(new RegExp(`^\/${lang}\/`), '/')
const currentCountry = countries.find(c => c.isoCode === country);

return (
<div className="relative">
Expand All @@ -64,7 +57,11 @@ function CountrySelectorElement({
open ? 'rounded-b md:rounded-t md:rounded-b-none' : 'rounded'
} border-contrast/30 dark:border-white`}
>
<span className="">{currentCountry.name} ({currentCountry.currency.isoCode} {currentCountry.currency.symbol})</span>
<span>{
currentCountry ?
`${currentCountry.name} (${currentCountry.currency.isoCode} ${currentCountry.currency.symbol})` :
'--'
}</span>
<IconCaret direction={open ? 'up' : 'down'} />
</Listbox.Button>

Expand All @@ -77,13 +74,13 @@ function CountrySelectorElement({
}`}
>
{listboxOpen && Object.values(countries).map(country => {
const isSelected = country.isoCode === currentCountry.isoCode;
const countryIsoCode = country.isoCode.toLocaleLowerCase();
const isSelected = country.isoCode === currentCountry?.isoCode;
const countryIsoCode = country.isoCode.toLowerCase();
return (
<Listbox.Option key={country.isoCode} value={country}>
{({active}) => (
<Link
to={countryIsoCode !== 'us' ? `/${countryIsoCode}${pathname}` : '/'}
to={countryIsoCode !== 'us' ? `/${languageIsoCode}-${countryIsoCode}${strippedPathname}` : strippedPathname}
className={`text-contrast dark:text-primary text-contrast dark:text-primary bg-primary
dark:bg-contrast w-full p-2 transition rounded
flex justify-start items-center text-left cursor-pointer ${
Expand Down
70 changes: 26 additions & 44 deletions app/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useLocation } from "react-router";
import type { EnhancedMenu, EnhancedMenuItem } from "~/lib/utils";
import { EnhancedMenu, EnhancedMenuItem, getLocalizationFromLang, isHomePath } from "~/lib/utils";
import {
Drawer,
useDrawer,
Expand All @@ -15,8 +15,9 @@ import {
CountrySelector,
CartDetails,
CartEmpty,
LinkI18n
} from "~/components";
import { Await, Link, useFetcher } from "@remix-run/react";
import { Await, useFetcher, useParams } from "@remix-run/react";
import { useWindowScroll } from "react-use";
import { Disclosure } from "@headlessui/react";
import type { LayoutData } from "~/data";
Expand All @@ -25,6 +26,7 @@ import type {
Country,
} from "@shopify/hydrogen-ui-alpha/storefront-api-types";
import { Suspense, useEffect } from "react";
import { getI18nPath } from "./LinkI18n";

export function Layout({
children,
Expand All @@ -34,11 +36,10 @@ export function Layout({
data?: {
layout: LayoutData;
countries: Array<Country>;
defaultCountry: Country;
cart: Promise<Cart>;
};
}) {
const { layout, countries, defaultCountry, cart } = data || {};
const { layout, countries, cart } = data || {};

return (
<>
Expand All @@ -60,7 +61,6 @@ export function Layout({
<Footer
menu={layout?.footerMenu}
countries={countries}
defaultCountry={defaultCountry}
/>
</>
);
Expand All @@ -75,12 +75,9 @@ function Header({
menu?: EnhancedMenu;
cart?: Promise<Cart>;
}) {
const { pathname } = useLocation();

// TODO: Ensure locale support like in Hydrogen
const isHome = pathname === "/";
const localeMatch = /^\/([a-z]{2})(\/|$)/i.exec(pathname);
const countryCode = localeMatch ? localeMatch[1] : null;
const { lang } = useParams();
const { country } = getLocalizationFromLang(lang);
const isHome = isHomePath();

const {
isOpen: isCartOpen,
Expand All @@ -101,15 +98,13 @@ function Header({
<MenuDrawer isOpen={isMenuOpen} onClose={closeMenu} menu={menu} />
)}
<DesktopHeader
countryCode={countryCode}
isHome={isHome}
title={title}
menu={menu}
openCart={openCart}
cart={cart}
/>
<MobileHeader
countryCode={countryCode}
isHome={isHome}
title={title}
openCart={openCart}
Expand All @@ -123,19 +118,11 @@ function Header({
function Footer({
menu,
countries,
defaultCountry,
}: {
menu?: EnhancedMenu;
countries?: Array<Country>;
defaultCountry?: Country;
}) {
const { pathname } = useLocation();

// TODO: Ensure locale support like in Hydrogen
const localeMatch = /^\/([a-z]{2})(\/|$)/i.exec(pathname);
const countryCode = localeMatch ? localeMatch[1] : null;

const isHome = pathname === `/${countryCode ? countryCode + "/" : ""}`;
const isHome = isHomePath();
const itemsCount = menu
? menu?.items?.length + 1 > 4
? 4
Expand All @@ -152,14 +139,13 @@ function Footer({
bg-primary dark:bg-contrast dark:text-primary text-contrast overflow-hidden`}
>
<FooterMenu menu={menu} />
{countries && defaultCountry && (
{countries && (
<section className="grid gap-4 w-full md:max-w-[335px] md:ml-auto">
<Heading size="lead" className="cursor-default" as="h3">
Country
</Heading>
<CountrySelector
countries={countries}
defaultCountry={defaultCountry}
/>
</section>
)}
Expand Down Expand Up @@ -256,25 +242,23 @@ function MenuMobileNav({
<nav className="grid gap-4 p-6 sm:gap-6 sm:px-12 sm:py-8">
{/* Top level menu items */}
{(menu?.items || []).map((item) => (
<Link key={item.id} to={item.to} target={item.target} onClick={onClose}>
<LinkI18n key={item.id} to={item.to} target={item.target} onClick={onClose}>
<Text as="span" size="copy">
{item.title}
</Text>
</Link>
</LinkI18n>
))}
</nav>
);
}

function MobileHeader({
countryCode,
title,
isHome,
openCart,
openMenu,
cart,
}: {
countryCode?: string | null;
title: string;
isHome: boolean;
openCart: () => void;
Expand All @@ -301,7 +285,7 @@ function MobileHeader({
<IconMenu />
</button>
<form
action={`/${countryCode ? countryCode + "/" : ""}search`}
action={getI18nPath('/search')}
className="items-center gap-2 sm:flex"
>
<button type="submit" className={styles.button}>
Expand All @@ -321,19 +305,19 @@ function MobileHeader({
</form>
</div>

<Link
<LinkI18n
className="flex items-center self-stretch leading-[3rem] md:leading-[4rem] justify-center flex-grow w-full h-full"
to="/"
>
<Heading className="font-bold text-center" as={isHome ? "h1" : "h2"}>
{title}
</Heading>
</Link>
</LinkI18n>

<div className="flex items-center justify-end w-full gap-4">
<Link to={"/account"} className={styles.button}>
<LinkI18n to={"/account"} className={styles.button}>
<IconAccount />
</Link>
</LinkI18n>
<button onClick={openCart} className={styles.button}>
<IconBag />
{cart && (
Expand All @@ -350,14 +334,12 @@ function MobileHeader({
}

function DesktopHeader({
countryCode,
isHome,
menu,
openCart,
title,
cart,
}: {
countryCode?: string | null;
isHome: boolean;
openCart: () => void;
menu?: EnhancedMenu;
Expand All @@ -381,26 +363,26 @@ function DesktopHeader({
return (
<header role="banner" className={styles.container}>
<div className="flex gap-12">
<Link className={`font-bold`} to="/" prefetch="intent">
<LinkI18n className={`font-bold`} to="/" prefetch="intent">
{title}
</Link>
</LinkI18n>
<nav className="flex gap-8">
{/* Top level menu items */}
{(menu?.items || []).map((item) => (
<Link
<LinkI18n
key={item.id}
to={item.to}
target={item.target}
prefetch="intent"
>
{item.title}
</Link>
</LinkI18n>
))}
</nav>
</div>
<div className="flex items-center gap-1">
<form
action={`/${countryCode ? countryCode + "/" : ""}search`}
action={getI18nPath('/search')}
className="flex items-center gap-2"
>
<Input
Expand All @@ -418,9 +400,9 @@ function DesktopHeader({
<IconSearch />
</button>
</form>
<Link to={"/account"} className={styles.button}>
<LinkI18n to={"/account"} className={styles.button}>
<IconAccount />
</Link>
</LinkI18n>
<button onClick={openCart} className={styles.button}>
<IconBag />
{cart && (
Expand Down Expand Up @@ -471,9 +453,9 @@ function FooterMenu({ menu }: { menu?: EnhancedMenu }) {
}

return (
<Link to={item.to} target={item.target} prefetch="intent">
<LinkI18n to={item.to} target={item.target} prefetch="intent">
{item.title}
</Link>
</LinkI18n>
);
};

Expand Down
Loading

0 comments on commit f104d25

Please sign in to comment.