Browse Source

fix(core): Use correct sequence of language fallbacks (#1730) (#1737)

Artem Danilov 3 years ago
parent
commit
897c21c04f
22 changed files with 194 additions and 125 deletions
  1. 8 5
      packages/core/src/api/common/custom-field-relation-resolver.service.ts
  2. 7 4
      packages/core/src/common/configurable-operation.ts
  3. 5 3
      packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts
  4. 5 4
      packages/core/src/service/helpers/locale-string-hydrator/locale-string-hydrator.ts
  5. 4 3
      packages/core/src/service/helpers/order-modifier/order-modifier.ts
  6. 26 0
      packages/core/src/service/helpers/translator/translator.service.ts
  7. 23 19
      packages/core/src/service/helpers/utils/translate-entity.spec.ts
  8. 23 15
      packages/core/src/service/helpers/utils/translate-entity.ts
  9. 3 0
      packages/core/src/service/service.module.ts
  10. 13 12
      packages/core/src/service/services/collection.service.ts
  11. 8 6
      packages/core/src/service/services/country.service.ts
  12. 5 4
      packages/core/src/service/services/customer.service.ts
  13. 9 6
      packages/core/src/service/services/facet-value.service.ts
  14. 10 8
      packages/core/src/service/services/facet.service.ts
  15. 3 2
      packages/core/src/service/services/order-testing.service.ts
  16. 5 4
      packages/core/src/service/services/order.service.ts
  17. 7 5
      packages/core/src/service/services/product-option-group.service.ts
  18. 4 3
      packages/core/src/service/services/product-option.service.ts
  19. 9 8
      packages/core/src/service/services/product-variant.service.ts
  20. 7 6
      packages/core/src/service/services/product.service.ts
  21. 5 4
      packages/core/src/service/services/shipping-method.service.ts
  22. 5 4
      packages/core/src/service/services/zone.service.ts

+ 8 - 5
packages/core/src/api/common/custom-field-relation-resolver.service.ts

@@ -9,7 +9,7 @@ import { TransactionalConnection } from '../../connection/transactional-connecti
 import { VendureEntity } from '../../entity/base/base.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
 import { ProductPriceApplicator } from '../../service/helpers/product-price-applicator/product-price-applicator';
-import { translateDeep } from '../../service/helpers/utils/translate-entity';
+import { TranslatorService } from '../../service/helpers/translator/translator.service';
 
 import { RequestContext } from './request-context';
 
@@ -26,7 +26,10 @@ export class CustomFieldRelationResolverService {
         private connection: TransactionalConnection,
         private configService: ConfigService,
         private productPriceApplicator: ProductPriceApplicator,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
+
     /**
      * @description
      * Used to dynamically resolve related entities in custom fields. Based on the field
@@ -62,10 +65,10 @@ export class CustomFieldRelationResolverService {
         }
 
         const translated: any = Array.isArray(result)
-            ? result.map(r => (this.isTranslatable(r) ? translateDeep(r, ctx.languageCode) : r))
+            ? result.map(r => (this.isTranslatable(r) ? this.translator.translate(r,ctx) : r))
             : this.isTranslatable(result)
-            ? translateDeep(result, ctx.languageCode)
-            : result;
+                ? this.translator.translate(result,ctx)
+                : result;
 
         return translated;
     }

+ 7 - 4
packages/core/src/common/configurable-operation.ts

@@ -363,7 +363,7 @@ export class ConfigurableOperationDef<T extends ConfigArgs = ConfigArgs> {
     toGraphQlType(ctx: RequestContext): ConfigurableOperationDefinition {
         return {
             code: this.code,
-            description: localizeString(this.description, ctx.languageCode),
+            description: localizeString(this.description, ctx.languageCode, ctx.channel.defaultLanguageCode),
             args: Object.entries(this.args).map(
                 ([name, arg]) =>
                     ({
@@ -373,8 +373,8 @@ export class ConfigurableOperationDef<T extends ConfigArgs = ConfigArgs> {
                         required: arg.required ?? true,
                         defaultValue: arg.defaultValue,
                         ui: arg.ui,
-                        label: arg.label && localizeString(arg.label, ctx.languageCode),
-                        description: arg.description && localizeString(arg.description, ctx.languageCode),
+                        label: arg.label && localizeString(arg.label, ctx.languageCode, ctx.channel.defaultLanguageCode),
+                        description: arg.description && localizeString(arg.description, ctx.languageCode, ctx.channel.defaultLanguageCode),
                     } as Required<ConfigArgDefinition>),
             ),
         };
@@ -405,8 +405,11 @@ export class ConfigurableOperationDef<T extends ConfigArgs = ConfigArgs> {
     }
 }
 
-function localizeString(stringArray: LocalizedStringArray, languageCode: LanguageCode): string {
+function localizeString(stringArray: LocalizedStringArray, languageCode: LanguageCode, channelLanguageCode: LanguageCode): string {
     let match = stringArray.find(x => x.languageCode === languageCode);
+    if (!match) {
+        match = stringArray.find(x => x.languageCode === channelLanguageCode);
+    }
     if (!match) {
         match = stringArray.find(x => x.languageCode === DEFAULT_LANGUAGE_CODE);
     }

+ 5 - 3
packages/core/src/service/helpers/entity-hydrator/entity-hydrator.service.ts

@@ -9,7 +9,7 @@ import { TransactionalConnection } from '../../../connection/transactional-conne
 import { VendureEntity } from '../../../entity/base/base.entity';
 import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
 import { ProductPriceApplicator } from '../product-price-applicator/product-price-applicator';
-import { translateDeep } from '../utils/translate-entity';
+import { TranslatorService } from '../translator/translator.service';
 
 import { HydrateOptions } from './entity-hydrator-types';
 
@@ -55,7 +55,9 @@ export class EntityHydrator {
     constructor(
         private connection: TransactionalConnection,
         private productPriceApplicator: ProductPriceApplicator,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     /**
      * @description
@@ -135,7 +137,7 @@ export class EntityHydrator {
 
                 this.assignSettableProperties(
                     target,
-                    translateDeep(target as any, ctx.languageCode, translateDeepRelations as any),
+                    this.translator.translate(target as any, ctx, translateDeepRelations as any),
                 );
             }
         }

+ 5 - 4
packages/core/src/service/helpers/locale-string-hydrator/locale-string-hydrator.ts

@@ -6,8 +6,7 @@ import { RequestContextCacheService } from '../../../cache/request-context-cache
 import { Translatable, TranslatableKeys, Translated } from '../../../common/types/locale-types';
 import { TransactionalConnection } from '../../../connection/transactional-connection';
 import { VendureEntity } from '../../../entity/base/base.entity';
-import { ProductVariant } from '../../../entity/product-variant/product-variant.entity';
-import { translateDeep } from '../utils/translate-entity';
+import { TranslatorService } from '../translator/translator.service';
 
 /**
  * This helper class is to be used in GraphQL entity resolvers, to resolve fields which depend on being
@@ -18,7 +17,9 @@ export class LocaleStringHydrator {
     constructor(
         private connection: TransactionalConnection,
         private requestCache: RequestContextCacheService,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     async hydrateLocaleStringField<T extends VendureEntity & Translatable & { languageCode?: LanguageCode }>(
         ctx: RequestContext,
@@ -60,7 +61,7 @@ export class LocaleStringHydrator {
             });
         }
         if (entity.translations.length) {
-            const translated = translateDeep(entity, ctx.languageCode);
+            const translated = this.translator.translate(entity, ctx);
             for (const localeStringProp of Object.keys(entity.translations[0])) {
                 if (
                     localeStringProp === 'base' ||

+ 4 - 3
packages/core/src/service/helpers/order-modifier/order-modifier.ts

@@ -50,8 +50,8 @@ import { StockMovementService } from '../../services/stock-movement.service';
 import { CustomFieldRelationService } from '../custom-field-relation/custom-field-relation.service';
 import { EntityHydrator } from '../entity-hydrator/entity-hydrator.service';
 import { OrderCalculator } from '../order-calculator/order-calculator';
+import { TranslatorService } from '../translator/translator.service';
 import { patchEntity } from '../utils/patch-entity';
-import { translateDeep } from '../utils/translate-entity';
 
 /**
  * @description
@@ -81,6 +81,7 @@ export class OrderModifier {
         private eventBus: EventBus,
         private entityHydrator: EntityHydrator,
         private historyService: HistoryService,
+        private translator: TranslatorService,
     ) {}
 
     /**
@@ -158,13 +159,13 @@ export class OrderModifier {
                 'productVariant.taxCategory',
             ],
         });
-        lineWithRelations.productVariant = translateDeep(
+        lineWithRelations.productVariant = this.translator.translate(
             await this.productVariantService.applyChannelPriceAndTax(
                 lineWithRelations.productVariant,
                 ctx,
                 order,
             ),
-            ctx.languageCode,
+            ctx
         );
         order.lines.push(lineWithRelations);
         await this.connection.getRepository(ctx, Order).save(order, { reload: false });

+ 26 - 0
packages/core/src/service/helpers/translator/translator.service.ts

@@ -0,0 +1,26 @@
+import { RequestContext } from '../../../api/common/request-context';
+import { Translatable } from '../../../common/types/locale-types';
+import { ConfigService } from '../../../config';
+import { VendureEntity } from '../../../entity';
+import { DeepTranslatableRelations, translateDeep } from '../utils/translate-entity';
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class TranslatorService {
+    constructor(
+        private configService: ConfigService,
+    ) {
+    }
+
+    translate<T extends Translatable & VendureEntity>(
+        translatable: T,
+        ctx: RequestContext,
+        translatableRelations: DeepTranslatableRelations<T> = [],
+    ) {
+        return translateDeep(translatable, [
+            ctx.languageCode,
+            ctx.channel.defaultLanguageCode,
+            this.configService.defaultLanguageCode,
+        ], translatableRelations);
+    }
+}

+ 23 - 19
packages/core/src/service/helpers/utils/translate-entity.spec.ts

@@ -54,25 +54,25 @@ describe('translateEntity()', () => {
     });
 
     it('should unwrap the matching translation', () => {
-        const result = translateEntity(product, LanguageCode.en);
+        const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
         expect(result).toHaveProperty('name', PRODUCT_NAME_EN);
     });
 
     it('should not overwrite translatable id with translation id', () => {
-        const result = translateEntity(product, LanguageCode.en);
+        const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
         expect(result).toHaveProperty('id', '1');
     });
 
     it('should note transfer the base from the selected translation', () => {
-        const result = translateEntity(product, LanguageCode.en);
+        const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
         expect(result).not.toHaveProperty('base');
     });
 
     it('should transfer the languageCode from the selected translation', () => {
-        const result = translateEntity(product, LanguageCode.en);
+        const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
         expect(result).toHaveProperty('languageCode', 'en');
     });
@@ -83,7 +83,7 @@ describe('translateEntity()', () => {
                 aBooleanField: true,
             };
             product.customFields = customFields;
-            const result = translateEntity(product, LanguageCode.en);
+            const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
             expect(result.customFields).toEqual(customFields);
         });
@@ -94,7 +94,7 @@ describe('translateEntity()', () => {
                 aLocaleString2: 'translated2',
             };
             product.translations[0].customFields = translatedCustomFields;
-            const result = translateEntity(product, LanguageCode.en);
+            const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
             expect(result.customFields).toEqual(translatedCustomFields);
         });
@@ -110,7 +110,7 @@ describe('translateEntity()', () => {
             };
             product.customFields = productCustomFields;
             product.translations[0].customFields = translatedCustomFields;
-            const result = translateEntity(product, LanguageCode.en);
+            const result = translateEntity(product, [LanguageCode.en, LanguageCode.en]);
 
             expect(result.customFields).toEqual({ ...productCustomFields, ...translatedCustomFields });
         });
@@ -119,13 +119,17 @@ describe('translateEntity()', () => {
     it('throw if there are no translations available', () => {
         product.translations = [];
 
-        expect(() => translateEntity(product, LanguageCode.en)).toThrow(
+        expect(() => translateEntity(product, [LanguageCode.en, LanguageCode.en])).toThrow(
             'error.entity-has-no-translation-in-language',
         );
     });
 
     it('falls back to default language', () => {
-        expect(translateEntity(product, LanguageCode.zu).name).toEqual(PRODUCT_NAME_EN);
+        expect(translateEntity(product, [LanguageCode.zu, LanguageCode.en]).name).toEqual(PRODUCT_NAME_EN);
+    });
+
+    it('falls back to default language', () => {
+        expect(translateEntity(product, [LanguageCode.zu, LanguageCode.de]).name).toEqual(PRODUCT_NAME_DE);
     });
 });
 
@@ -206,54 +210,54 @@ describe('translateDeep()', () => {
     });
 
     it('should translate the root entity', () => {
-        const result = translateDeep(product, LanguageCode.en);
+        const result = translateDeep(product, [LanguageCode.en, LanguageCode.en]);
 
         expect(result).toHaveProperty('name', PRODUCT_NAME_EN);
     });
 
     it('should not throw if root entity has no translations', () => {
-        expect(() => translateDeep(testProduct, LanguageCode.en)).not.toThrow();
+        expect(() => translateDeep(testProduct, [LanguageCode.en, LanguageCode.en])).not.toThrow();
     });
 
     it('should not throw if first-level nested entity is not defined', () => {
         testProduct.singleRealVariant = undefined as any;
-        expect(() => translateDeep(testProduct, LanguageCode.en, ['singleRealVariant'])).not.toThrow();
+        expect(() => translateDeep(testProduct, [LanguageCode.en, LanguageCode.en], ['singleRealVariant'])).not.toThrow();
     });
 
     it('should not throw if second-level nested entity is not defined', () => {
         testProduct.singleRealVariant.options = undefined as any;
         expect(() =>
-            translateDeep(testProduct, LanguageCode.en, [['singleRealVariant', 'options']]),
+            translateDeep(testProduct, [LanguageCode.en, LanguageCode.en], [['singleRealVariant', 'options']]),
         ).not.toThrow();
     });
 
     it('should translate a first-level nested non-array entity', () => {
-        const result = translateDeep(testProduct, LanguageCode.en, ['singleRealVariant']);
+        const result = translateDeep(testProduct, [LanguageCode.en, LanguageCode.en], ['singleRealVariant']);
 
         expect(result.singleRealVariant).toHaveProperty('name', VARIANT_NAME_EN);
     });
 
     it('should translate a first-level nested entity array', () => {
-        const result = translateDeep(product, LanguageCode.en, ['variants']);
+        const result = translateDeep(product, [LanguageCode.en, LanguageCode.en], ['variants']);
 
         expect(result).toHaveProperty('name', PRODUCT_NAME_EN);
         expect(result.variants[0]).toHaveProperty('name', VARIANT_NAME_EN);
     });
 
     it('should translate a second-level nested non-array entity', () => {
-        const result = translateDeep(testProduct, LanguageCode.en, [['singleTestVariant', 'singleOption']]);
+        const result = translateDeep(testProduct, [LanguageCode.en, LanguageCode.en], [['singleTestVariant', 'singleOption']]);
 
         expect(result.singleTestVariant.singleOption).toHaveProperty('name', OPTION_NAME_EN);
     });
 
     it('should translate a second-level nested entity array (first-level is not array)', () => {
-        const result = translateDeep(testProduct, LanguageCode.en, [['singleRealVariant', 'options']]);
+        const result = translateDeep(testProduct, [LanguageCode.en, LanguageCode.en], [['singleRealVariant', 'options']]);
 
         expect(result.singleRealVariant.options[0]).toHaveProperty('name', OPTION_NAME_EN);
     });
 
     it('should translate a second-level nested entity array', () => {
-        const result = translateDeep(product, LanguageCode.en, ['variants', ['variants', 'options']]);
+        const result = translateDeep(product, [LanguageCode.en, LanguageCode.en], ['variants', ['variants', 'options']]);
 
         expect(result).toHaveProperty('name', PRODUCT_NAME_EN);
         expect(result.variants[0]).toHaveProperty('name', VARIANT_NAME_EN);
@@ -306,7 +310,7 @@ describe('translateTree()', () => {
     });
 
     it('translates all entities in the tree', () => {
-        const result = translateTree(cat1, LanguageCode.en, []);
+        const result = translateTree(cat1, [LanguageCode.en, LanguageCode.en], []);
 
         expect(result.languageCode).toBe(LanguageCode.en);
         expect(result.name).toBe('cat1 en');

+ 23 - 15
packages/core/src/service/helpers/utils/translate-entity.ts

@@ -9,20 +9,20 @@ import { VendureEntity } from '../../../entity/base/base.entity';
 // prettier-ignore
 export type TranslatableRelationsKeys<T> = {
     [K in keyof T]: T[K] extends string ? never :
-    T[K] extends number ? never :
-    T[K] extends boolean ? never :
-    T[K] extends undefined ? never :
-    T[K] extends string[] ? never :
-    T[K] extends number[] ? never :
-    T[K] extends boolean[] ? never :
-    K extends 'translations' ? never :
-    K extends 'customFields' ? never : K
+        T[K] extends number ? never :
+            T[K] extends boolean ? never :
+                T[K] extends undefined ? never :
+                    T[K] extends string[] ? never :
+                        T[K] extends number[] ? never :
+                            T[K] extends boolean[] ? never :
+                                K extends 'translations' ? never :
+                                    K extends 'customFields' ? never : K
 }[keyof T];
 
 // prettier-ignore
 export type NestedTranslatableRelations<T> = {
     [K in TranslatableRelationsKeys<T>]: T[K] extends any[] ?
-        [K, TranslatableRelationsKeys<UnwrappedArray<T[K]>>]:
+        [K, TranslatableRelationsKeys<UnwrappedArray<T[K]>>] :
         [K, TranslatableRelationsKeys<T[K]>]
 };
 
@@ -38,11 +38,19 @@ export type DeepTranslatableRelations<T> = Array<TranslatableRelationsKeys<T> |
  */
 export function translateEntity<T extends Translatable & VendureEntity>(
     translatable: T,
-    languageCode: LanguageCode,
+    languageCode: LanguageCode | [LanguageCode, ...LanguageCode[]],
 ): Translated<T> {
     let translation: Translation<VendureEntity> | undefined;
     if (translatable.translations) {
-        translation = translatable.translations.find(t => t.languageCode === languageCode);
+        if (Array.isArray(languageCode)) {
+            for (const lc of languageCode) {
+                translation = translatable.translations.find(t => t.languageCode === lc);
+                if (translation) break;
+            }
+        } else {
+            translation = translatable.translations.find(t => t.languageCode === languageCode);
+        }
+
         if (!translation && languageCode !== DEFAULT_LANGUAGE_CODE) {
             translation = translatable.translations.find(t => t.languageCode === DEFAULT_LANGUAGE_CODE);
         }
@@ -56,7 +64,7 @@ export function translateEntity<T extends Translatable & VendureEntity>(
     if (!translation) {
         throw new InternalServerError(`error.entity-has-no-translation-in-language`, {
             entityName: translatable.constructor.name,
-            languageCode,
+            languageCode: (Array.isArray(languageCode)) ? languageCode.join() : languageCode,
         });
     }
 
@@ -83,7 +91,7 @@ export function translateEntity<T extends Translatable & VendureEntity>(
  */
 export function translateDeep<T extends Translatable & VendureEntity>(
     translatable: T,
-    languageCode: LanguageCode,
+    languageCode: LanguageCode | [LanguageCode, ...LanguageCode[]],
     translatableRelations: DeepTranslatableRelations<T> = [],
 ): Translated<T> {
     let translatedEntity: Translated<T>;
@@ -132,7 +140,7 @@ export function translateDeep<T extends Translatable & VendureEntity>(
 function translateLeaf(
     object: { [key: string]: any } | undefined,
     property: string,
-    languageCode: LanguageCode,
+    languageCode: LanguageCode | [LanguageCode, ...LanguageCode[]],
 ): any {
     if (object && object[property]) {
         if (Array.isArray(object[property])) {
@@ -150,7 +158,7 @@ export type TreeNode = { children: TreeNode[] } & Translatable & VendureEntity;
  */
 export function translateTree<T extends TreeNode>(
     node: T,
-    languageCode: LanguageCode,
+    languageCode: LanguageCode | [LanguageCode, ...LanguageCode[]],
     translatableRelations: DeepTranslatableRelations<T> = [],
 ): Translated<T> {
     const output = translateDeep(node, languageCode, translatableRelations);

+ 3 - 0
packages/core/src/service/service.module.ts

@@ -26,6 +26,7 @@ import { RequestContextService } from './helpers/request-context/request-context
 import { ShippingCalculator } from './helpers/shipping-calculator/shipping-calculator';
 import { SlugValidator } from './helpers/slug-validator/slug-validator';
 import { TranslatableSaver } from './helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from './helpers/translator/translator.service';
 import { VerificationTokenGenerator } from './helpers/verification-token-generator/verification-token-generator';
 import { InitializerService } from './initializer.service';
 import { AdministratorService } from './services/administrator.service';
@@ -61,6 +62,7 @@ import { TaxRateService } from './services/tax-rate.service';
 import { UserService } from './services/user.service';
 import { ZoneService } from './services/zone.service';
 
+
 const services = [
     AdministratorService,
     AssetService,
@@ -118,6 +120,7 @@ const helpers = [
     ProductPriceApplicator,
     EntityHydrator,
     RequestContextService,
+    TranslatorService
 ];
 
 /**

+ 13 - 12
packages/core/src/service/services/collection.service.ts

@@ -24,7 +24,6 @@ import { assertFound, idsAreEqual } from '../../common/utils';
 import { ConfigService } from '../../config/config.service';
 import { Logger } from '../../config/logger/vendure-logger';
 import { TransactionalConnection } from '../../connection/transactional-connection';
-import { Asset, FacetValue } from '../../entity';
 import { CollectionTranslation } from '../../entity/collection/collection-translation.entity';
 import { Collection } from '../../entity/collection/collection.entity';
 import { ProductVariant } from '../../entity/product-variant/product-variant.entity';
@@ -40,8 +39,8 @@ import { CustomFieldRelationService } from '../helpers/custom-field-relation/cus
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { SlugValidator } from '../helpers/slug-validator/slug-validator';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { moveToIndex } from '../helpers/utils/move-to-index';
-import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { AssetService } from './asset.service';
 import { ChannelService } from './channel.service';
@@ -77,7 +76,9 @@ export class CollectionService implements OnModuleInit {
         private slugValidator: SlugValidator,
         private configArgService: ConfigArgService,
         private customFieldRelationService: CustomFieldRelationService,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     /**
      * @internal
@@ -150,7 +151,7 @@ export class CollectionService implements OnModuleInit {
             .getManyAndCount()
             .then(async ([collections, totalItems]) => {
                 const items = collections.map(collection =>
-                    translateDeep(collection, ctx.languageCode, ['parent']),
+                    this.translator.translate(collection, ctx, ['parent']),
                 );
                 return {
                     items,
@@ -177,7 +178,7 @@ export class CollectionService implements OnModuleInit {
         if (!collection) {
             return;
         }
-        return translateDeep(collection, ctx.languageCode, ['parent']);
+        return this.translator.translate(collection, ctx, ['parent']);
     }
 
     async findByIds(
@@ -190,7 +191,7 @@ export class CollectionService implements OnModuleInit {
             loadEagerRelations: true,
         });
         return collections.then(values =>
-            values.map(collection => translateDeep(collection, ctx.languageCode, ['parent'])),
+            values.map(collection => this.translator.translate(collection, ctx, ['parent'])),
         );
     }
 
@@ -242,7 +243,7 @@ export class CollectionService implements OnModuleInit {
             )
             .getOne();
 
-        return parent && translateDeep(parent, ctx.languageCode);
+        return parent && this.translator.translate(parent, ctx);
     }
 
     /**
@@ -294,7 +295,7 @@ export class CollectionService implements OnModuleInit {
         }
         const result = await qb.getMany();
 
-        return result.map(collection => translateDeep(collection, ctx.languageCode));
+        return result.map(collection => this.translator.translate(collection, ctx));
     }
 
     /**
@@ -321,7 +322,7 @@ export class CollectionService implements OnModuleInit {
         };
 
         const descendants = await getChildren(rootId);
-        return descendants.map(c => translateDeep(c, ctx.languageCode));
+        return descendants.map(c => this.translator.translate(c, ctx));
     }
 
     /**
@@ -360,7 +361,7 @@ export class CollectionService implements OnModuleInit {
                 ancestors.forEach(a => {
                     const category = categories.find(c => c.id === a.id);
                     if (category) {
-                        resultCategories.push(ctx ? translateDeep(category, ctx.languageCode) : category);
+                        resultCategories.push(ctx ? this.translator.translate(category, ctx) : category);
                     }
                 });
                 return resultCategories;
@@ -677,7 +678,7 @@ export class CollectionService implements OnModuleInit {
             .getOne();
 
         if (existingRoot) {
-            this.rootCollection = translateDeep(existingRoot, ctx.languageCode);
+            this.rootCollection = this.translator.translate(existingRoot, ctx);
             return this.rootCollection;
         }
 
@@ -702,7 +703,7 @@ export class CollectionService implements OnModuleInit {
                 filters: [],
             }),
         );
-        this.rootCollection = translateDeep(newRoot, ctx.languageCode);
+        this.rootCollection = this.translator.translate(newRoot, ctx);
         return this.rootCollection;
     }
 }

+ 8 - 6
packages/core/src/service/services/country.service.ts

@@ -21,7 +21,7 @@ import { EventBus } from '../../event-bus';
 import { CountryEvent } from '../../event-bus/events/country-event';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 /**
  * @description
@@ -36,7 +36,9 @@ export class CountryService {
         private listQueryBuilder: ListQueryBuilder,
         private translatableSaver: TranslatableSaver,
         private eventBus: EventBus,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     findAll(
         ctx: RequestContext,
@@ -47,7 +49,7 @@ export class CountryService {
             .build(Country, options, { ctx, relations })
             .getManyAndCount()
             .then(([countries, totalItems]) => {
-                const items = countries.map(country => translateDeep(country, ctx.languageCode));
+                const items = countries.map(country => this.translator.translate(country, ctx));
                 return {
                     items,
                     totalItems,
@@ -63,7 +65,7 @@ export class CountryService {
         return this.connection
             .getRepository(ctx, Country)
             .findOne(countryId, { relations })
-            .then(country => country && translateDeep(country, ctx.languageCode));
+            .then(country => country && this.translator.translate(country, ctx));
     }
 
     /**
@@ -74,7 +76,7 @@ export class CountryService {
         return this.connection
             .getRepository(ctx, Country)
             .find({ where: { enabled: true } })
-            .then(items => items.map(country => translateDeep(country, ctx.languageCode)));
+            .then(items => items.map(country => this.translator.translate(country, ctx)));
     }
 
     /**
@@ -90,7 +92,7 @@ export class CountryService {
         if (!country) {
             throw new UserInputError('error.country-code-not-valid', { countryCode });
         }
-        return translateDeep(country, ctx.languageCode);
+        return this.translator.translate(country, ctx);
     }
 
     async create(ctx: RequestContext, input: CreateCountryInput): Promise<Translated<Country>> {

+ 5 - 4
packages/core/src/service/services/customer.service.ts

@@ -58,9 +58,9 @@ import { PasswordResetEvent } from '../../event-bus/events/password-reset-event'
 import { PasswordResetVerifiedEvent } from '../../event-bus/events/password-reset-verified-event';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { addressToLine } from '../helpers/utils/address-to-line';
 import { patchEntity } from '../helpers/utils/patch-entity';
-import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { ChannelService } from './channel.service';
 import { CountryService } from './country.service';
@@ -85,6 +85,7 @@ export class CustomerService {
         private historyService: HistoryService,
         private channelService: ChannelService,
         private customFieldRelationService: CustomFieldRelationService,
+        private translator: TranslatorService,
     ) {}
 
     findAll(
@@ -155,7 +156,7 @@ export class CustomerService {
             .getMany()
             .then(addresses => {
                 addresses.forEach(address => {
-                    address.country = translateDeep(address.country, ctx.languageCode);
+                    address.country = this.translator.translate(address.country, ctx);
                 });
                 return addresses;
             });
@@ -724,7 +725,7 @@ export class CustomerService {
         if (input.countryCode && input.countryCode !== address.country.code) {
             address.country = await this.countryService.findOneByCode(ctx, input.countryCode);
         } else {
-            address.country = translateDeep(address.country, ctx.languageCode);
+            address.country = this.translator.translate(address.country, ctx);
         }
         let updatedAddress = patchEntity(address, input);
         updatedAddress = await this.connection.getRepository(ctx, Address).save(updatedAddress);
@@ -758,7 +759,7 @@ export class CustomerService {
         if (!customer) {
             throw new EntityNotFoundError('Address', id);
         }
-        address.country = translateDeep(address.country, ctx.languageCode);
+        address.country = this.translator.translate(address.country, ctx);
         await this.reassignDefaultsForDeletedAddress(ctx, address);
         await this.historyService.createHistoryEntryForCustomer({
             customerId: address.customer.id,

+ 9 - 6
packages/core/src/service/services/facet-value.service.ts

@@ -22,6 +22,7 @@ import { EventBus } from '../../event-bus';
 import { FacetValueEvent } from '../../event-bus/events/facet-value-event';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { ChannelService } from './channel.service';
@@ -41,7 +42,9 @@ export class FacetValueService {
         private customFieldRelationService: CustomFieldRelationService,
         private channelService: ChannelService,
         private eventBus: EventBus,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     /**
      * @deprecated Use {@link FacetValueService.findAll findAll(ctx, lang)} instead
@@ -49,10 +52,10 @@ export class FacetValueService {
     findAll(lang: LanguageCode): Promise<Array<Translated<FacetValue>>>;
     findAll(ctx: RequestContext, lang: LanguageCode): Promise<Array<Translated<FacetValue>>>;
     findAll(ctxOrLang: RequestContext | LanguageCode, lang?: LanguageCode): Promise<Array<Translated<FacetValue>>> {
-        const [repository, languageCode] = ctxOrLang instanceof RequestContext 
+        const [repository, languageCode] = ctxOrLang instanceof RequestContext
             ? [this.connection.getRepository(ctxOrLang, FacetValue), lang!]
             : [this.connection.rawConnection.getRepository(FacetValue), ctxOrLang];
-
+        // ToDo Implement usage of channelLanguageCode
         return repository
             .find({
                 relations: ['facet'],
@@ -66,7 +69,7 @@ export class FacetValueService {
             .findOne(id, {
                 relations: ['facet'],
             })
-            .then(facetValue => facetValue && translateDeep(facetValue, ctx.languageCode, ['facet']));
+            .then(facetValue => facetValue && this.translator.translate(facetValue, ctx, ['facet']));
     }
 
     findByIds(ctx: RequestContext, ids: ID[]): Promise<Array<Translated<FacetValue>>> {
@@ -74,7 +77,7 @@ export class FacetValueService {
             relations: ['facet'],
         });
         return facetValues.then(values =>
-            values.map(facetValue => translateDeep(facetValue, ctx.languageCode, ['facet'])),
+            values.map(facetValue => this.translator.translate(facetValue, ctx, ['facet'])),
         );
     }
 
@@ -90,7 +93,7 @@ export class FacetValueService {
                     facet: { id },
                 },
             })
-            .then(values => values.map(facetValue => translateDeep(facetValue, ctx.languageCode)));
+            .then(values => values.map(facetValue => this.translator.translate(facetValue, ctx)));
     }
 
     async create(

+ 10 - 8
packages/core/src/service/services/facet.service.ts

@@ -22,6 +22,7 @@ import { FacetEvent } from '../../event-bus/events/facet-event';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { ChannelService } from './channel.service';
@@ -44,6 +45,7 @@ export class FacetService {
         private channelService: ChannelService,
         private customFieldRelationService: CustomFieldRelationService,
         private eventBus: EventBus,
+        private translator: TranslatorService,
     ) {}
 
     findAll(
@@ -60,7 +62,7 @@ export class FacetService {
             .getManyAndCount()
             .then(([facets, totalItems]) => {
                 const items = facets.map(facet =>
-                    translateDeep(facet, ctx.languageCode, ['values', ['values', 'facet']]),
+                    this.translator.translate(facet, ctx, ['values', ['values', 'facet']]),
                 );
                 return {
                     items,
@@ -78,7 +80,7 @@ export class FacetService {
             .findOneInChannel(ctx, Facet, facetId, ctx.channelId, {
                 relations: relations ?? ['values', 'values.facet', 'channels'],
             })
-            .then(facet => facet && translateDeep(facet, ctx.languageCode, ['values', ['values', 'facet']]));
+            .then(facet => facet && this.translator.translate(facet, ctx, ['values', ['values', 'facet']]));
     }
 
     /**
@@ -87,23 +89,23 @@ export class FacetService {
     findByCode(facetCode: string, lang: LanguageCode): Promise<Translated<Facet> | undefined>;
     findByCode(ctx: RequestContext, facetCode: string, lang: LanguageCode): Promise<Translated<Facet> | undefined>;
     findByCode(
-        ctxOrFacetCode: RequestContext | string, 
-        facetCodeOrLang: string | LanguageCode, 
+        ctxOrFacetCode: RequestContext | string,
+        facetCodeOrLang: string | LanguageCode,
         lang?: LanguageCode
     ): Promise<Translated<Facet> | undefined> {
         const relations = ['values', 'values.facet'];
-        const [repository, facetCode, languageCode] = ctxOrFacetCode instanceof RequestContext 
+        const [repository, facetCode, languageCode] = ctxOrFacetCode instanceof RequestContext
             ? [this.connection.getRepository(ctxOrFacetCode, Facet), facetCodeOrLang, lang!]
             : [this.connection.rawConnection.getRepository(Facet), ctxOrFacetCode, facetCodeOrLang as LanguageCode];
 
-
+        // ToDo Implement usage of channelLanguageCode
         return repository.findOne({
             where: {
                 code: facetCode,
             },
             relations,
         })
-        .then(facet => facet && translateDeep(facet, languageCode, ['values', ['values', 'facet']]));
+        .then(facet => facet && translateDeep(facet, languageCode,['values', ['values', 'facet']]));
     }
 
     /**
@@ -119,7 +121,7 @@ export class FacetService {
             .where('facetValue.id = :id', { id })
             .getOne();
         if (facet) {
-            return translateDeep(facet, ctx.languageCode);
+            return this.translator.translate(facet, ctx);
         }
     }
 

+ 3 - 2
packages/core/src/service/services/order-testing.service.ts

@@ -23,7 +23,7 @@ import { ConfigArgService } from '../helpers/config-arg/config-arg.service';
 import { OrderCalculator } from '../helpers/order-calculator/order-calculator';
 import { ProductPriceApplicator } from '../helpers/product-price-applicator/product-price-applicator';
 import { ShippingCalculator } from '../helpers/shipping-calculator/shipping-calculator';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 /**
  * @description
@@ -41,6 +41,7 @@ export class OrderTestingService {
         private configArgService: ConfigArgService,
         private configService: ConfigService,
         private productPriceApplicator: ProductPriceApplicator,
+        private translator: TranslatorService,
     ) {}
 
     /**
@@ -87,7 +88,7 @@ export class OrderTestingService {
         const eligibleMethods = await this.shippingCalculator.getEligibleShippingMethods(ctx, mockOrder);
         return eligibleMethods
             .map(result => {
-                translateDeep(result.method, ctx.languageCode);
+                this.translator.translate(result.method, ctx);
                 return result;
             })
             .map(result => {

+ 5 - 4
packages/core/src/service/services/order.service.ts

@@ -110,6 +110,7 @@ import { PaymentState } from '../helpers/payment-state-machine/payment-state';
 import { PaymentStateMachine } from '../helpers/payment-state-machine/payment-state-machine';
 import { RefundStateMachine } from '../helpers/refund-state-machine/refund-state-machine';
 import { ShippingCalculator } from '../helpers/shipping-calculator/shipping-calculator';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import {
     orderItemsAreAllCancelled,
     orderItemsAreDelivered,
@@ -118,7 +119,6 @@ import {
     totalCoveredByPayments,
 } from '../helpers/utils/order-utils';
 import { patchEntity } from '../helpers/utils/patch-entity';
-import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { ChannelService } from './channel.service';
 import { CountryService } from './country.service';
@@ -163,6 +163,7 @@ export class OrderService {
         private orderModifier: OrderModifier,
         private customFieldRelationService: CustomFieldRelationService,
         private requestCache: RequestContextCacheService,
+        private translator: TranslatorService,
     ) {}
 
     /**
@@ -254,13 +255,13 @@ export class OrderService {
         if (order) {
             if (effectiveRelations.includes('lines.productVariant')) {
                 for (const line of order.lines) {
-                    line.productVariant = translateDeep(
+                    line.productVariant = this.translator.translate(
                         await this.productVariantService.applyChannelPriceAndTax(
                             line.productVariant,
                             ctx,
                             order,
                         ),
-                        ctx.languageCode,
+                        ctx
                     );
                 }
             }
@@ -1206,7 +1207,7 @@ export class OrderService {
                 line.productVariant,
             );
             if (fulfillableStockLevel < lineInput.quantity) {
-                const productVariant = translateDeep(line.productVariant, ctx.languageCode);
+                const productVariant = this.translator.translate(line.productVariant, ctx);
                 return new InsufficientStockOnHandError(
                     productVariant.id as string,
                     productVariant.name,

+ 7 - 5
packages/core/src/service/services/product-option-group.service.ts

@@ -17,7 +17,7 @@ import { EventBus } from '../../event-bus';
 import { ProductOptionGroupEvent } from '../../event-bus/events/product-option-group-event';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 /**
  * @description
@@ -32,7 +32,9 @@ export class ProductOptionGroupService {
         private translatableSaver: TranslatableSaver,
         private customFieldRelationService: CustomFieldRelationService,
         private eventBus: EventBus,
-    ) {}
+        private translator: TranslatorService,
+    ) {
+    }
 
     findAll(
         ctx: RequestContext,
@@ -50,7 +52,7 @@ export class ProductOptionGroupService {
         return this.connection
             .getRepository(ctx, ProductOptionGroup)
             .find(findOptions)
-            .then(groups => groups.map(group => translateDeep(group, ctx.languageCode, ['options'])));
+            .then(groups => groups.map(group => this.translator.translate(group, ctx, ['options'])));
     }
 
     findOne(
@@ -63,7 +65,7 @@ export class ProductOptionGroupService {
             .findOne(id, {
                 relations: relations ?? ['options'],
             })
-            .then(group => group && translateDeep(group, ctx.languageCode, ['options']));
+            .then(group => group && this.translator.translate(group, ctx, ['options']));
     }
 
     getOptionGroupsByProductId(ctx: RequestContext, id: ID): Promise<Array<Translated<ProductOptionGroup>>> {
@@ -78,7 +80,7 @@ export class ProductOptionGroupService {
                     id: 'ASC',
                 },
             })
-            .then(groups => groups.map(group => translateDeep(group, ctx.languageCode, ['options'])));
+            .then(groups => groups.map(group => this.translator.translate(group, ctx, ['options'])));
     }
 
     async create(

+ 4 - 3
packages/core/src/service/services/product-option.service.ts

@@ -17,7 +17,7 @@ import { EventBus } from '../../event-bus';
 import { ProductOptionEvent } from '../../event-bus/events/product-option-event';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 /**
  * @description
@@ -32,6 +32,7 @@ export class ProductOptionService {
         private translatableSaver: TranslatableSaver,
         private customFieldRelationService: CustomFieldRelationService,
         private eventBus: EventBus,
+        private translator: TranslatorService,
     ) {}
 
     findAll(ctx: RequestContext): Promise<Array<Translated<ProductOption>>> {
@@ -40,7 +41,7 @@ export class ProductOptionService {
             .find({
                 relations: ['group'],
             })
-            .then(options => options.map(option => translateDeep(option, ctx.languageCode)));
+            .then(options => options.map(option => this.translator.translate(option, ctx)));
     }
 
     findOne(ctx: RequestContext, id: ID): Promise<Translated<ProductOption> | undefined> {
@@ -49,7 +50,7 @@ export class ProductOptionService {
             .findOne(id, {
                 relations: ['group'],
             })
-            .then(option => option && translateDeep(option, ctx.languageCode));
+            .then(option => option && this.translator.translate(option, ctx));
     }
 
     async create(

+ 9 - 8
packages/core/src/service/services/product-variant.service.ts

@@ -41,8 +41,8 @@ import { CustomFieldRelationService } from '../helpers/custom-field-relation/cus
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { ProductPriceApplicator } from '../helpers/product-price-applicator/product-price-applicator';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { samplesEach } from '../helpers/utils/samples-each';
-import { translateDeep } from '../helpers/utils/translate-entity';
 
 import { AssetService } from './asset.service';
 import { ChannelService } from './channel.service';
@@ -76,6 +76,7 @@ export class ProductVariantService {
         private customFieldRelationService: CustomFieldRelationService,
         private requestCache: RequestContextCacheService,
         private productPriceApplicator: ProductPriceApplicator,
+        private translator: TranslatorService,
     ) {}
 
     async findAll(
@@ -115,7 +116,7 @@ export class ProductVariantService {
             })
             .then(async result => {
                 if (result) {
-                    return translateDeep(await this.applyChannelPriceAndTax(result, ctx), ctx.languageCode, [
+                    return this.translator.translate(await this.applyChannelPriceAndTax(result, ctx), ctx, [
                         'product',
                     ]);
                 }
@@ -235,7 +236,7 @@ export class ProductVariantService {
             relations: ['productVariant', 'productVariant.taxCategory'],
             includeSoftDeleted: true,
         });
-        return translateDeep(await this.applyChannelPriceAndTax(productVariant, ctx), ctx.languageCode);
+        return this.translator.translate(await this.applyChannelPriceAndTax(productVariant, ctx), ctx);
     }
 
     /**
@@ -247,7 +248,7 @@ export class ProductVariantService {
             .findOneInChannel(ctx, ProductVariant, variantId, ctx.channelId, {
                 relations: ['options'],
             })
-            .then(variant => (!variant ? [] : variant.options.map(o => translateDeep(o, ctx.languageCode))));
+            .then(variant => (!variant ? [] : variant.options.map(o => this.translator.translate(o, ctx))));
     }
 
     getFacetValuesForVariant(ctx: RequestContext, variantId: ID): Promise<Array<Translated<FacetValue>>> {
@@ -256,7 +257,7 @@ export class ProductVariantService {
                 relations: ['facetValues', 'facetValues.facet', 'facetValues.channels'],
             })
             .then(variant =>
-                !variant ? [] : variant.facetValues.map(o => translateDeep(o, ctx.languageCode, ['facet'])),
+                !variant ? [] : variant.facetValues.map(o => this.translator.translate(o, ctx, ['facet'])),
             );
     }
 
@@ -270,7 +271,7 @@ export class ProductVariantService {
         const product = await this.connection.getEntityOrThrow(ctx, Product, variant.productId, {
             includeSoftDeleted: true,
         });
-        return translateDeep(product, ctx.languageCode);
+        return this.translator.translate(product, ctx);
     }
 
     /**
@@ -607,7 +608,7 @@ export class ProductVariantService {
         return await Promise.all(
             variants.map(async variant => {
                 const variantWithPrices = await this.applyChannelPriceAndTax(variant, ctx);
-                return translateDeep(variantWithPrices, ctx.languageCode, [
+                return this.translator.translate(variantWithPrices, ctx, [
                     'options',
                     'facetValues',
                     ['facetValues', 'facet'],
@@ -770,7 +771,7 @@ export class ProductVariantService {
                 const variantOptionIds = this.sortJoin(variant.options, ',', 'id');
                 if (variantOptionIds === inputOptionIds) {
                     throw new UserInputError('error.product-variant-options-combination-already-exists', {
-                        variantName: translateDeep(variant, ctx.languageCode).name,
+                        variantName: this.translator.translate(variant, ctx).name,
                     });
                 }
             });

+ 7 - 6
packages/core/src/service/services/product.service.ts

@@ -35,7 +35,7 @@ import { CustomFieldRelationService } from '../helpers/custom-field-relation/cus
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { SlugValidator } from '../helpers/slug-validator/slug-validator';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 import { AssetService } from './asset.service';
 import { ChannelService } from './channel.service';
@@ -69,6 +69,7 @@ export class ProductService {
         private eventBus: EventBus,
         private slugValidator: SlugValidator,
         private customFieldRelationService: CustomFieldRelationService,
+        private translator: TranslatorService,
     ) {}
 
     async findAll(
@@ -86,7 +87,7 @@ export class ProductService {
             .getManyAndCount()
             .then(async ([products, totalItems]) => {
                 const items = products.map(product =>
-                    translateDeep(product, ctx.languageCode, ['facetValues', ['facetValues', 'facet']]),
+                    this.translator.translate(product, ctx, ['facetValues', ['facetValues', 'facet']]),
                 );
                 return {
                     items,
@@ -115,7 +116,7 @@ export class ProductService {
         if (!product) {
             return;
         }
-        return translateDeep(product, ctx.languageCode, ['facetValues', ['facetValues', 'facet']]);
+        return this.translator.translate(product, ctx, ['facetValues', ['facetValues', 'facet']]);
     }
 
     async findByIds(
@@ -137,7 +138,7 @@ export class ProductService {
             .getMany()
             .then(products =>
                 products.map(product =>
-                    translateDeep(product, ctx.languageCode, ['facetValues', ['facetValues', 'facet']]),
+                    this.translator.translate(product, ctx, ['facetValues', ['facetValues', 'facet']]),
                 ),
             );
     }
@@ -161,7 +162,7 @@ export class ProductService {
                 relations: ['facetValues', 'facetValues.facet', 'facetValues.channels'],
             })
             .then(variant =>
-                !variant ? [] : variant.facetValues.map(o => translateDeep(o, ctx.languageCode, ['facet'])),
+                !variant ? [] : variant.facetValues.map(o => this.translator.translate(o, ctx, ['facet'])),
             );
     }
 
@@ -200,7 +201,7 @@ export class ProductService {
             .getOne()
             .then(product =>
                 product
-                    ? translateDeep(product, ctx.languageCode, ['facetValues', ['facetValues', 'facet']])
+                    ? this.translator.translate(product, ctx, ['facetValues', ['facetValues', 'facet']])
                     : undefined,
             );
     }

+ 5 - 4
packages/core/src/service/services/shipping-method.service.ts

@@ -26,7 +26,7 @@ import { ConfigArgService } from '../helpers/config-arg/config-arg.service';
 import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service';
 import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder';
 import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver';
-import { translateDeep } from '../helpers/utils/translate-entity';
+import { TranslatorService } from '../helpers/translator/translator.service';
 
 import { ChannelService } from './channel.service';
 
@@ -47,6 +47,7 @@ export class ShippingMethodService {
         private translatableSaver: TranslatableSaver,
         private customFieldRelationService: CustomFieldRelationService,
         private eventBus: EventBus,
+        private translator: TranslatorService,
     ) {}
 
     /** @internal */
@@ -73,7 +74,7 @@ export class ShippingMethodService {
             })
             .getManyAndCount()
             .then(([items, totalItems]) => ({
-                items: items.map(i => translateDeep(i, ctx.languageCode)),
+                items: items.map(i => this.translator.translate(i, ctx)),
                 totalItems,
             }));
     }
@@ -94,7 +95,7 @@ export class ShippingMethodService {
                 ...(includeDeleted === false ? { where: { deletedAt: null } } : {}),
             },
         );
-        return shippingMethod && translateDeep(shippingMethod, ctx.languageCode);
+        return shippingMethod && this.translator.translate(shippingMethod, ctx);
     }
 
     async create(ctx: RequestContext, input: CreateShippingMethodInput): Promise<ShippingMethod> {
@@ -205,7 +206,7 @@ export class ShippingMethodService {
         });
         return shippingMethods
             .filter(sm => sm.channels.find(c => idsAreEqual(c.id, ctx.channelId)))
-            .map(m => translateDeep(m, ctx.languageCode));
+            .map(m => this.translator.translate(m, ctx));
     }
 
     /**

+ 5 - 4
packages/core/src/service/services/zone.service.ts

@@ -21,8 +21,8 @@ import { Zone } from '../../entity/zone/zone.entity';
 import { EventBus } from '../../event-bus';
 import { ZoneEvent } from '../../event-bus/events/zone-event';
 import { ZoneMembersEvent } from '../../event-bus/events/zone-members-event';
+import { TranslatorService } from '../helpers/translator/translator.service';
 import { patchEntity } from '../helpers/utils/patch-entity';
-import { translateDeep } from '../helpers/utils/translate-entity';
 
 /**
  * @description
@@ -40,6 +40,7 @@ export class ZoneService {
         private connection: TransactionalConnection,
         private configService: ConfigService,
         private eventBus: EventBus,
+        private translator: TranslatorService,
     ) {}
 
     /** @internal */
@@ -67,10 +68,10 @@ export class ZoneService {
     }
 
     async findAll(ctx: RequestContext): Promise<Zone[]> {
-        return this.zones.memoize([ctx.languageCode], [ctx], (zones, languageCode) => {
+        return this.zones.memoize([], [ctx], (zones) => {
             return zones.map((zone, i) => {
                 const cloneZone = { ...zone };
-                cloneZone.members = zone.members.map(country => translateDeep(country, languageCode));
+                cloneZone.members = zone.members.map(country => this.translator.translate(country, ctx));
                 return cloneZone;
             });
         });
@@ -84,7 +85,7 @@ export class ZoneService {
             })
             .then(zone => {
                 if (zone) {
-                    zone.members = zone.members.map(country => translateDeep(country, ctx.languageCode));
+                    zone.members = zone.members.map(country => this.translator.translate(country, ctx));
                     return zone;
                 }
             });