Skip to content

Feat [Shopify]: Internationalization Support for Shopify Loaders and Queries #1100

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

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
22 changes: 18 additions & 4 deletions shopify/loaders/ProductDetailsPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ import {
GetProductQuery,
GetProductQueryVariables,
HasMetafieldsMetafieldsArgs,
LanguageCode,
CountryCode
} from "../utils/storefront/storefront.graphql.gen.ts";
import { GetProduct } from "../utils/storefront/queries.ts";
import { Metafield } from "../utils/types.ts";
import { LanguageContextArgs, Metafield } from "../utils/types.ts";

export interface Props {
slug: RequestURLParam;
Expand All @@ -17,6 +19,18 @@ export interface Props {
* @description search for metafields
*/
metafields?: Metafield[];
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
}

/**
Expand All @@ -29,7 +43,7 @@ const loader = async (
ctx: AppContext,
): Promise<ProductDetailsPage | null> => {
const { storefront } = ctx;
const { slug } = props;
const { slug, languageCode = "PT", countryCode = "BR" } = props;
const metafields = props.metafields || [];

const splitted = slug?.split("-");
Expand All @@ -39,9 +53,9 @@ const loader = async (

const data = await storefront.query<
GetProductQuery,
GetProductQueryVariables & HasMetafieldsMetafieldsArgs
GetProductQueryVariables & HasMetafieldsMetafieldsArgs & LanguageContextArgs
>({
variables: { handle, identifiers: metafields },
variables: { handle, identifiers: metafields, languageCode, countryCode },
...GetProduct,
});

Expand Down
26 changes: 24 additions & 2 deletions shopify/loaders/ProductList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
QueryRootCollectionArgs,
QueryRootSearchArgs,
SearchResultItemConnection,
LanguageCode,
CountryCode
} from "../utils/storefront/storefront.graphql.gen.ts";
import { toProduct } from "../utils/transform.ts";
import {
Expand All @@ -21,7 +23,7 @@ import {
searchSortShopify,
sortShopify,
} from "../utils/utils.ts";
import { Metafield } from "../utils/types.ts";
import { LanguageContextArgs, Metafield } from "../utils/types.ts";

export interface QueryProps {
/** @description search term to use on search */
Expand Down Expand Up @@ -70,6 +72,18 @@ export type Props = {
* @description search for metafields
*/
metafields?: Metafield[];
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
};

// deno-lint-ignore no-explicit-any
Expand All @@ -92,6 +106,8 @@ const loader = async (

const count = props.count ?? 12;
const metafields = expandedProps.metafields || [];
const languageCode = expandedProps?.languageCode ?? "PT";
const countryCode = expandedProps?.countryCode ?? "BR";

let shopifyProducts:
| SearchResultItemConnection
Expand Down Expand Up @@ -119,15 +135,18 @@ const loader = async (
});

if (isQueryList(props)) {

const data = await storefront.query<
QueryRoot,
QueryRootSearchArgs & HasMetafieldsMetafieldsArgs
QueryRootSearchArgs & HasMetafieldsMetafieldsArgs & LanguageContextArgs
>({
variables: {
first: count,
query: props.query,
productFilters: filters,
identifiers: metafields,
languageCode,
countryCode,
...searchSortShopify[sort],
},
...SearchProducts,
Expand All @@ -139,12 +158,15 @@ const loader = async (
& QueryRootCollectionArgs
& CollectionProductsArgs
& HasMetafieldsMetafieldsArgs
& LanguageContextArgs
>({
variables: {
first: count,
handle: props.collection,
filters,
identifiers: metafields,
languageCode,
countryCode,
...sortShopify[sort],
},
...ProductsByCollection,
Expand Down
25 changes: 23 additions & 2 deletions shopify/loaders/ProductListingPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import {
QueryRootCollectionArgs,
QueryRootSearchArgs,
SearchResultItemConnection,
LanguageCode,
CountryCode
} from "../utils/storefront/storefront.graphql.gen.ts";
import { toFilter, toProduct } from "../utils/transform.ts";
import { Metafield } from "../utils/types.ts";
import { LanguageContextArgs, Metafield } from "../utils/types.ts";
import {
getFiltersByUrl,
searchSortOptions,
Expand Down Expand Up @@ -69,6 +71,18 @@ export interface Props {
* @description The URL of the page, used to override URL from request
*/
pageHref?: string;
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
}

/**
Expand All @@ -94,6 +108,8 @@ const loader = async (
const startCursor = props.startCursor ||
url.searchParams.get("startCursor") || "";
const metafields = props.metafields || [];
const languageCode = props?.languageCode || "PT";
const countryCode = props?.countryCode || "BR";

const isSearch = Boolean(query);
let hasNextPage = false;
Expand All @@ -113,7 +129,7 @@ const loader = async (
if (isSearch) {
const data = await storefront.query<
QueryRoot,
QueryRootSearchArgs & HasMetafieldsMetafieldsArgs
QueryRootSearchArgs & HasMetafieldsMetafieldsArgs & LanguageContextArgs
>({
variables: {
...(!endCursor && { first: count }),
Expand All @@ -123,6 +139,8 @@ const loader = async (
query: query,
productFilters: getFiltersByUrl(url),
identifiers: metafields,
languageCode,
countryCode,
...searchSortShopify[sort],
},
...SearchProducts,
Expand All @@ -145,6 +163,7 @@ const loader = async (
& QueryRootCollectionArgs
& CollectionProductsArgs
& HasMetafieldsMetafieldsArgs
& LanguageContextArgs
>({
variables: {
...(!endCursor && { first: count }),
Expand All @@ -154,6 +173,8 @@ const loader = async (
identifiers: metafields,
handle: pathname,
filters: getFiltersByUrl(url),
languageCode,
countryCode,
...sortShopify[sort],
},
...ProductsByCollection,
Expand Down
26 changes: 21 additions & 5 deletions shopify/loaders/RelatedProducts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ import {
HasMetafieldsMetafieldsArgs,
ProductRecommendationsQuery,
ProductRecommendationsQueryVariables,
LanguageCode,
CountryCode
} from "../utils/storefront/storefront.graphql.gen.ts";
import { toProduct } from "../utils/transform.ts";
import { Metafield } from "../utils/types.ts";
import { LanguageContextArgs, Metafield } from "../utils/types.ts";

export interface Props {
slug: RequestURLParam;
Expand All @@ -27,6 +29,18 @@ export interface Props {
* @description search for metafields
*/
metafields?: Metafield[];
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
}

/**
Expand All @@ -39,7 +53,7 @@ const loader = async (
ctx: AppContext,
): Promise<Product[] | null> => {
const { storefront } = ctx;
const { slug, count } = props;
const { slug, count, languageCode = "PT", countryCode = "BR" } = props;

const splitted = slug?.split("-");
const maybeSkuId = Number(splitted[splitted.length - 1]);
Expand All @@ -48,9 +62,9 @@ const loader = async (

const query = await storefront.query<
GetProductQuery,
GetProductQueryVariables & HasMetafieldsMetafieldsArgs
GetProductQueryVariables & HasMetafieldsMetafieldsArgs & LanguageContextArgs
>({
variables: { handle, identifiers: metafields },
variables: { handle, identifiers: metafields, languageCode, countryCode },
...GetProduct,
});

Expand All @@ -60,11 +74,13 @@ const loader = async (

const data = await storefront.query<
ProductRecommendationsQuery,
ProductRecommendationsQueryVariables & HasMetafieldsMetafieldsArgs
ProductRecommendationsQueryVariables & HasMetafieldsMetafieldsArgs & LanguageContextArgs
>({
variables: {
productId: query.product.id,
identifiers: metafields,
languageCode,
countryCode
},
...ProductRecommendations,
});
Expand Down
35 changes: 29 additions & 6 deletions shopify/loaders/cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,54 @@ import { AppContext } from "../mod.ts";
import { getCartCookie, setCartCookie } from "../utils/cart.ts";
import { CreateCart, GetCart } from "../utils/storefront/queries.ts";
import {
CountryCode,
CreateCartMutation,
CreateCartMutationVariables,
GetCartQuery,
GetCartQueryVariables,
LanguageCode,
} from "../utils/storefront/storefront.graphql.gen.ts";
import { LanguageContextArgs } from "../utils/types.ts";

export interface Props {
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
}

const loader = async (
_props: unknown,
props: Props,
req: Request,
ctx: AppContext,
): Promise<GetCartQuery["cart"]> => {
const { languageCode = "PT", countryCode = "BR" } = props;
const { storefront } = ctx;
const maybeCartId = getCartCookie(req.headers);

const cartId = maybeCartId ||
await storefront.query<CreateCartMutation, CreateCartMutationVariables>(
CreateCart,
).then((data) => data.payload?.cart?.id);
await storefront.query<CreateCartMutation, CreateCartMutationVariables>({
variables: { countryCode },
...CreateCart,
}).then((data) => data.payload?.cart?.id);

if (!cartId) {
throw new Error("Missing cart id");
}

const cart = await storefront.query<GetCartQuery, GetCartQueryVariables>({
variables: { id: decodeURIComponent(cartId) },
const cart = await storefront.query<
GetCartQuery,
GetCartQueryVariables & LanguageContextArgs
>({
variables: { id: decodeURIComponent(cartId), languageCode, countryCode },
...GetCart,
}).then((data) => data.cart);

Expand Down
25 changes: 21 additions & 4 deletions shopify/loaders/shop.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
import { AppContext } from "../mod.ts";
import { GetShopInfo } from "../utils/storefront/queries.ts";
import {
CountryCode,
LanguageCode,
Shop,
ShopMetafieldsArgs,
} from "../utils/storefront/storefront.graphql.gen.ts";
import { Metafield } from "../utils/types.ts";
import { LanguageContextArgs, Metafield } from "../utils/types.ts";

export interface Props {
/**
* @title Metafields
* @description search for metafields
*/
metafields?: Metafield[];
/**
* @title Language Code
* @description Language code for the storefront API
* @example "EN" for English, "FR" for French, etc.
*/
languageCode?: LanguageCode;
/**
* @title Country Code
* @description Country code for the storefront API
* @example "US" for United States, "FR" for France, etc.
*/
countryCode?: CountryCode;
}

export const defaultVisibility = "private";
Expand All @@ -22,10 +36,13 @@ const loader = async (
ctx: AppContext,
): Promise<Shop> => {
const { storefront } = ctx;
const { metafields = [] } = props;
const { metafields = [], languageCode = "PT", countryCode = "BR" } = props;

const shop = await storefront.query<{ shop: Shop }, ShopMetafieldsArgs>({
variables: { identifiers: metafields },
const shop = await storefront.query<
{ shop: Shop },
ShopMetafieldsArgs & LanguageContextArgs
>({
variables: { identifiers: metafields, languageCode, countryCode },
...GetShopInfo,
}).then((data) => data.shop);

Expand Down
Loading
Loading