Skip to content

Commit

Permalink
feat: language switcher (#230)
Browse files Browse the repository at this point in the history
  • Loading branch information
mdanilowicz authored Jun 14, 2023
1 parent 3ffd000 commit d1e07d6
Show file tree
Hide file tree
Showing 80 changed files with 1,012 additions and 138 deletions.
5 changes: 5 additions & 0 deletions .changeset/clean-monkeys-decide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/api-client": minor
---

Add redirectUrl to the contextService
5 changes: 5 additions & 0 deletions .changeset/long-weeks-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/helpers-next": minor
---

Add internatiolization helpers with mocks data
5 changes: 5 additions & 0 deletions .changeset/neat-walls-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vue-demo-store": minor
---

Add language switcher
5 changes: 5 additions & 0 deletions .changeset/two-chefs-fail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/composables-next": minor
---

useInternationalization add methods related to the language switcher
5 changes: 5 additions & 0 deletions .changeset/two-lies-hunt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware-pwa/cms-base": minor
---

Add language prefix to the links built on the CMS components
1 change: 1 addition & 0 deletions apps/docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const sidebar = [
],
},
{ text: "Routing", link: "/getting-started/routing" },
{ text: "Languages", link: "/getting-started/languages" },
{
text: "CMS",
link: "/getting-started/cms/",
Expand Down
125 changes: 125 additions & 0 deletions apps/docs/src/getting-started/languages.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
---
head:
- - meta
- name: og:title
content: "Work with languages"
- - meta
- name: og:description
content: "How to build multi languages site"
- - meta
- name: og:image
content: "https://frontends-og-image.vercel.app/Work%20with%20**languages**.png?fontSize=150px"
---

# Work with languages

:::warning
This is the implementation working with `vue-demo-store` template only. To see the details, please go to the `templates/vue-demo-store` directory in the repository.
:::
Each store has two sources of translations.

Backend source for:

- CMS translations
- Product and categories
- Routing paths

Frontend source for:

- All static content declared on the frontend app

## Configuration

More about backend translations can be found [here](https://docs.shopware.com/en/shopware-6-en/tutorials-and-faq/translations)

For the frontend app we recommend to use `vue-i18n` module.

**_When you are using same domain:_**

:::warning
Backend languages codes and frontend languages codes must be the same!
:::

```
www.example.com // GB site
www.example.com/de-DE // DE site
```

```
{
i18n: {
vueI18n: {
fallbackLocale: "en-GB",
},
strategy: "prefix_except_default",
defaultLocale: "en-GB",
langDir: "i18n/src/",
locales: [
{
code: "en-GB",
iso: "en-GB",
file: "en-GB.ts",
},
{
code: "de-DE",
iso: "de-DE",
file: "de-DE.ts",
},
],
},
}
```

**_When you are using different domains:_**

```
www.example1.com // GB site
www.example2.com // DE site
```

```
{
i18n: {
vueI18n: {
fallbackLocale: "en-GB",
},
langDir: "i18n/src/",
locales: [
{
domain: 'example1.com'
code: "en-GB",
iso: "en-GB",
file: "en-GB.ts",
},
{
domain: 'example2.com'
code: "de-DE",
iso: "de-DE",
file: "de-DE.ts",
},
],
},
}
```

## Routing

When you are using _prefix_ domain languages, you have to use `useLocalePath()` composable for building URLs.
The main task of this composable is to add a prefix to URL if needed.

```vue
<script setup lang="ts">
const localePath = useLocalePath();
</script>
<template>
<NuxtLink :to="localePath('/account')"> Account</NuxtLink>
</template>
```

## Testing

If you want to test languages locally, and your local domain differs from what is declared on the backend, you can use environment variables.

```
NUXT_PUBLIC_SHOPWARE_DEV_STOREFRONT_URL=http://127.0.0.1:3000
```
2 changes: 1 addition & 1 deletion packages/api-client/src/services/contextService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ async function updateContext(
): Promise<ContextTokenResponse> {
const resp = await contextInstance.invoke.patch(getContextEndpoint(), params);
const contextToken = extractContextToken(resp);
return { contextToken };
return { contextToken, redirectUrl: resp.data.redirectUrl };
}

/**
Expand Down
6 changes: 5 additions & 1 deletion packages/cms-base/components/SwCategoryNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
getTranslatedProperty,
} from "@shopware-pwa/helpers-next";
import { Category, StoreNavigationElement } from "@shopware-pwa/types";
import getUrlPrefix from "../helpers/getUrlPrefix";
import buildUrlPrefix from "../helpers/buildUrlPrefix";
const props = withDefaults(
defineProps<{
Expand All @@ -23,6 +25,8 @@ function getHighlightCategory(navigationElement: Category) {
navigationElement.id === props.activeCategory?.id
);
}
const urlPrefix = getUrlPrefix();
</script>
<template>
<ul v-if="props.elements?.length" class="list-none m-0 px-5">
Expand All @@ -34,7 +38,7 @@ function getHighlightCategory(navigationElement: Category) {
}"
>
<RouterLink
:to="getCategoryRoute(navigationElement)"
:to="buildUrlPrefix(getCategoryRoute(navigationElement), urlPrefix)"
class="flex items-center py-2 px-5 text-base rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 my-2"
:class="[
getHighlightCategory(navigationElement) ? 'font-bold' : 'font-normal',
Expand Down
20 changes: 15 additions & 5 deletions packages/cms-base/components/SwProductCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import { Ref } from "vue";
import SwListingProductPrice from "./SwListingProductPrice.vue";
import deepMerge from "../helpers/deepMerge";
import getTranslations from "../helpers/getTranslations";
import getUrlPrefix from "../helpers/getUrlPrefix";
import buildUrlPrefix from "../helpers/buildUrlPrefix";
const { pushSuccess, pushError } = useNotifications();
Expand Down Expand Up @@ -96,6 +98,7 @@ const addToCartProxy = async () => {
};
const fromPrice = getProductFromPrice(props.product);
const urlPrefix = getUrlPrefix();
const ratingAverage: Ref<number> = computed(() =>
props.product.ratingAverage ? Math.round(props.product.ratingAverage) : 0
);
Expand Down Expand Up @@ -126,7 +129,10 @@ const srcPath = computed(() => {
layoutType === 'image' ? 'h-80' : 'h-60',
]"
>
<RouterLink :to="getProductRoute(product)" class="overflow-hidden">
<RouterLink
:to="buildUrlPrefix(getProductRoute(product), urlPrefix)"
class="overflow-hidden"
>
<img
ref="imageElement"
:src="srcPath"
Expand Down Expand Up @@ -181,7 +187,7 @@ const srcPath = computed(() => {
<div class="px-4 pb-4 h-52 md:h-32">
<RouterLink
class="line-clamp-2"
:to="getProductRoute(product)"
:to="buildUrlPrefix(getProductRoute(product), urlPrefix)"
data-testid="product-box-product-name-link"
>
<h5
Expand Down Expand Up @@ -216,12 +222,16 @@ const srcPath = computed(() => {
{{ count }}
</div>
</button>
<RouterLink v-else :to="getProductRoute(product)" class="">
<button
<RouterLink
v-else
:to="buildUrlPrefix(getProductRoute(product), urlPrefix)"
class=""
>
<div
class="justify-center w-full md:w-auto my-8 md-m-0 py-2 px-3 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-black hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transform transition duration-400 hover:scale-120"
>
<span data-testid="product-box-product-show-details">Details</span>
</button>
</div>
</RouterLink>
</div>
</div>
Expand Down
10 changes: 10 additions & 0 deletions packages/cms-base/helpers/buildUrlPrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export default function buildUrlPrefix(url: string | any, prefix: string) {
if (typeof url === "string") {
return prefix ? `/${prefix}${url}` : url;
}
if (url.path && prefix) {
url.path = `/${prefix}${url.path}`;
}

return url;
}
7 changes: 7 additions & 0 deletions packages/cms-base/helpers/getUrlPrefix.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default function getUrlPrefix() {
try {
return inject("urlPrefix", "");
} catch ($error) {
return "";
}
}
Loading

2 comments on commit d1e07d6

@vercel
Copy link

@vercel vercel bot commented on d1e07d6 Jun 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on d1e07d6 Jun 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

frontends-demo – ./templates/vue-demo-store

frontends-demo.vercel.app
frontends-demo-git-main-shopware-frontends.vercel.app
frontends-demo-shopware-frontends.vercel.app

Please sign in to comment.