Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/current-locale-from-context.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@shopware/composables": minor
---

`useSessionContext`: expose `currentLocaleCode` — the active language's locale code (e.g. `"en-GB"`), read directly from the context's `languageInfo`. The current locale can now be derived from the session context alone, without loading the full language list via `useInternationalization().getAvailableLanguages()` just to map the current `languageId` to a locale.
3 changes: 2 additions & 1 deletion examples/adyen-dropin-component/api-gen.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"store-api": {
"patches": [
"storeApiSchema.overrides.json",
"storeApiSchemaCommercial.overrides.json"
"storeApiSchemaCommercial.overrides.json",
"adyenSchema.overrides.json"
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"components": {
"SalesChannelContext": [
{
"properties": {
"extensions": {
"type": "object",
"properties": {
"adyenData": {
"type": "object",
"additionalProperties": true
}
}
}
}
}
]
}
}
81 changes: 41 additions & 40 deletions examples/adyen-dropin-component/api-types/storeApiTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7526,73 +7526,65 @@ export type Schemas = {
readonly updatedAt?: string;
};
SalesChannelContext: {
/** @enum {string} */
apiAlias: "sales_channel_context";
/** Core context with general configuration values and state */
context?: {
currencyFactor?: number;
currencyId?: string;
/** Format: int32 */
currencyPrecision?: number;
languageIdChain?: string[]; // TODO: [OpenAPI][SalesChannelContext] languageIdChain field should be defined properly in context
languageIdChain?: string[];
scope?: string;
source?: string;
source?: {
salesChannelId: string;
/** @enum {string} */
type: "sales-channel" | "shop-api";
};
taxState?: string;
useCache?: boolean;
versionId?: string;
};
/** Currency associated with the current user */
currency?: components["schemas"]["Currency"]; // TODO: [OpenAPI][SalesChannelContext] currency field should be defined reusing Currency schema
currency?: components["schemas"]["Currency"];
/** Customer group of the current user */
currentCustomerGroup?: {
displayGross?: boolean;
name?: string;
};
customer?: components["schemas"]["Customer"]; // TODO: [OpenAPI][SalesChannelContext] customer field should be defined reusing Customer schema
extensions: {
adyenData: object;
customer?: null | components["schemas"]["Customer"];
extensions?: {
adyenData?: {
[key: string]: unknown;
};
};
/** Fallback group if the default customer group is not applicable */
fallbackCustomerGroup?: {
displayGross?: boolean;
name?: string;
};
/** Selected payment method */
paymentMethod?: components["schemas"]["PaymentMethod"] & {
fundingSource: components["schemas"]["AdyenPaymentMethod"]["fundingSource"];
}; // TODO: [OpenAPI][SalesChannelContext] paymentMethod field should be defined properly reusing PaymentMethod schema
/** Information about the current sales channel */
salesChannel?: {
accessKey?: string;
active?: boolean;
analyticsId?: string;
countryId?: string;
currencyId?: string;
customerGroupId?: string;
footerCategoryId?: string;
hreflangActive?: boolean;
hreflangDefaultDomainId?: string;
languageId?: string;
mailHeaderFooterId?: string;
maintenance?: boolean;
maintenanceIpWhitelist?: string;
name?: string;
itemRounding: {
/** @enum {string} */
apiAlias: "shopware_core_framework_data_abstraction_layer_pricing_cash_rounding_config";
/** Format: int32 */
navigationCategoryDepth?: number;
navigationCategoryId?: string;
paymentMethodId?: string;
serviceCategoryId?: string;
shippingMethodId?: string;
shortName?: string;
typeId?: string;
decimals: number;
/** Format: float */
interval: number;
roundForNet: boolean;
};
languageInfo: {
localeCode: string;
name: string;
};
measurementSystem?: components["schemas"]["ContextMeasurementSystemInfo"];
paymentMethod?: components["schemas"]["PaymentMethod"];
salesChannel: components["schemas"]["SalesChannel"];
shippingLocation?: {
// TODO: [OpenAPI][SalesChannelContext] shippingLocation field should be defined properly
apiAlias: "cart_delivery_shipping_location";
country: components["schemas"]["Country"];
address: components["schemas"]["CustomerAddress"];
address?: components["schemas"]["CustomerAddress"];
/** @enum {string} */
apiAlias?: "cart_delivery_shipping_location";
country?: components["schemas"]["Country"];
};
/** Selected shipping method */
shippingMethod?: components["schemas"]["ShippingMethod"]; // TODO: [OpenAPI][SalesChannelContext] shippingMethod field should be defined properly reusing ShippingMethod schema
shippingMethod?: components["schemas"]["ShippingMethod"];
/** Currently active tax rules and/or rates */
taxRules?: {
name?: string;
Expand All @@ -7601,6 +7593,15 @@ export type Schemas = {
}[];
/** Context the user session */
token?: string;
totalRounding: {
/** @enum {string} */
apiAlias: "shopware_core_framework_data_abstraction_layer_pricing_cash_rounding_config";
/** Format: int32 */
decimals: number;
/** Format: float */
interval: number;
roundForNet: boolean;
};
};
SalesChannelDomain: {
/** Format: date-time */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,83 +64,6 @@ export type Schemas = {
storedPaymentMethodId?: string;
id: string;
};
SalesChannelContext: {
apiAlias: "sales_channel_context";
/** Core context with general configuration values and state */
context?: {
currencyFactor?: number;
currencyId?: string;
/** Format: int32 */
currencyPrecision?: number;
languageIdChain?: string[]; // TODO: [OpenAPI][SalesChannelContext] languageIdChain field should be defined properly in context
scope?: string;
source?: string;
taxState?: string;
useCache?: boolean;
versionId?: string;
};
/** Currency associated with the current user */
currency?: components["schemas"]["Currency"]; // TODO: [OpenAPI][SalesChannelContext] currency field should be defined reusing Currency schema
/** Customer group of the current user */
currentCustomerGroup?: {
displayGross?: boolean;
name?: string;
};
customer?: components["schemas"]["Customer"]; // TODO: [OpenAPI][SalesChannelContext] customer field should be defined reusing Customer schema
extensions: {
adyenData: object;
};
/** Fallback group if the default customer group is not applicable */
fallbackCustomerGroup?: {
displayGross?: boolean;
name?: string;
};
/** Selected payment method */
paymentMethod?: components["schemas"]["PaymentMethod"] & {
fundingSource: components["schemas"]["AdyenPaymentMethod"]["fundingSource"];
}; // TODO: [OpenAPI][SalesChannelContext] paymentMethod field should be defined properly reusing PaymentMethod schema
/** Information about the current sales channel */
salesChannel?: {
accessKey?: string;
active?: boolean;
analyticsId?: string;
countryId?: string;
currencyId?: string;
customerGroupId?: string;
footerCategoryId?: string;
hreflangActive?: boolean;
hreflangDefaultDomainId?: string;
languageId?: string;
mailHeaderFooterId?: string;
maintenance?: boolean;
maintenanceIpWhitelist?: string;
name?: string;
/** Format: int32 */
navigationCategoryDepth?: number;
navigationCategoryId?: string;
paymentMethodId?: string;
serviceCategoryId?: string;
shippingMethodId?: string;
shortName?: string;
typeId?: string;
};
shippingLocation?: {
// TODO: [OpenAPI][SalesChannelContext] shippingLocation field should be defined properly
apiAlias: "cart_delivery_shipping_location";
country: components["schemas"]["Country"];
address: components["schemas"]["CustomerAddress"];
};
/** Selected shipping method */
shippingMethod?: components["schemas"]["ShippingMethod"]; // TODO: [OpenAPI][SalesChannelContext] shippingMethod field should be defined properly reusing ShippingMethod schema
/** Currently active tax rules and/or rates */
taxRules?: {
name?: string;
/** Format: float */
taxRate?: number;
}[];
/** Context the user session */
token?: string;
};
};

export type operations = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ describe("useSessionContext", () => {
expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
});

it("currentLocaleCode reads languageInfo.localeCode from the context", async () => {
const { vm, injections } = useSetup(() => useSessionContext());
injections.apiClient.invoke.mockResolvedValue({
data: {
languageInfo: { name: "English", localeCode: "en-GB" },
},
});
await vm.refreshSessionContext();
expect(vm.currentLocaleCode).toBe("en-GB");
});

it("currentLocaleCode is undefined when the context has no languageInfo", () => {
const { vm } = useSetup(() => useSessionContext());
expect(vm.currentLocaleCode).toBeUndefined();
});
Comment on lines +81 to +84

it("setShippingMethod", async () => {
const { vm, injections } = useSetup(() => useSessionContext());
injections.apiClient.invoke.mockResolvedValue({ data: {} });
Expand Down
10 changes: 10 additions & 0 deletions packages/composables/src/useSessionContext/useSessionContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ export type UseSessionContextReturn = {
* current language code
*/
currentLanguageId: ComputedRef<string | undefined>;
/**
* Locale code of the active language (e.g. `"en-GB"`), read from the
* context's `languageInfo`. Available from the session context alone — no
* separate language list request is required to detect the current locale.
*/
currentLocaleCode: ComputedRef<string | undefined>;
/**
* current context's customer object
*/
Expand Down Expand Up @@ -262,6 +268,9 @@ export function useSessionContext(
const languageIdChain = computed(
() => sessionContext.value?.context?.languageIdChain?.[0] || "",
);
const currentLocaleCode = computed(
() => sessionContext.value?.languageInfo?.localeCode,
);
const taxState = computed(() => sessionContext.value?.context?.taxState);
const userFromContext = computed(() => sessionContext.value?.customer);

Expand All @@ -287,6 +296,7 @@ export function useSessionContext(
languageIdChain,
salesChannelLanguageId: languageId,
currentLanguageId: languageIdChain,
currentLocaleCode,
setCountry,
setContext,
};
Expand Down
6 changes: 4 additions & 2 deletions templates/vue-demo-store/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ const {
changeLanguage,
languages: storeLanguages,
} = useInternationalization();
const { languageIdChain, refreshSessionContext } = useSessionContext();
const { languageIdChain, currentLocaleCode, refreshSessionContext } =
useSessionContext();

const { data: languages } = await useAsyncData("languages", async () => {
return await getAvailableLanguages();
Expand Down Expand Up @@ -86,7 +87,8 @@ if (languages.value?.elements.length && router.currentRoute.value.name) {
languageToChangeId = localeProperties.value.localeId as string;
}
} else {
const sessionLanguage = getLanguageCodeFromId(languageIdChain.value);
const sessionLanguage =
currentLocaleCode.value ?? getLanguageCodeFromId(languageIdChain.value);

// If languages are not the same, set one from prefix
if (sessionLanguage !== prefix) {
Expand Down
6 changes: 4 additions & 2 deletions templates/vue-starter-template/app/app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ const {
const router = useRouter();
const route = useRoute();

const { languageIdChain, refreshSessionContext } = useSessionContext();
const { languageIdChain, currentLocaleCode, refreshSessionContext } =
useSessionContext();

let languageToChangeId: string | null = null;

Expand All @@ -100,7 +101,8 @@ if (languages && router.currentRoute.value.name) {
languageToChangeId = localeProperties.value.localeId as string;
}
} else {
const sessionLanguage = getLanguageCodeFromId(languageIdChain.value);
const sessionLanguage =
currentLocaleCode.value ?? getLanguageCodeFromId(languageIdChain.value);

// If languages are not the same, set one from prefix
if (sessionLanguage !== prefix) {
Expand Down