feat(): Translation statistics (#14299)

* chore(): Translation statistics

* chore(): improve statistics performances

* add end point to get statistics

* add tests

* Create spicy-games-unite.md

* feat(): add material and fix tests

* feat(): add translatable api

* feat(): add translatable api

* fix tests

* fix tests

* fix tests

* feedback
This commit is contained in:
Adrien de Peretti
2025-12-15 14:11:49 +01:00
committed by GitHub
parent 0f1566c644
commit ba6ed8d9dd
20 changed files with 1196 additions and 2 deletions

View File

@@ -21,3 +21,18 @@ export interface AdminTranslationsListParams
*/
locale_code?: string | string[]
}
/**
* Request body for translation statistics endpoint.
*/
export interface AdminTranslationStatisticsParams {
/**
* The locales to check translations for (e.g., ["en-US", "fr-FR"]).
*/
locales: string[]
/**
* The entity types to get statistics for (e.g., ["product", "product_variant"]).
*/
entity_types: string[]
}

View File

@@ -33,3 +33,58 @@ export interface AdminTranslationsBatchResponse {
deleted: boolean
}
}
/**
* Statistics for a specific locale.
*/
export interface AdminTranslationLocaleStatistics {
/**
* Expected number of translated fields.
*/
expected: number
/**
* Actual number of translated fields.
*/
translated: number
/**
* Number of missing translations.
*/
missing: number
}
/**
* Statistics for an entity type.
*/
export interface AdminTranslationEntityStatistics
extends AdminTranslationLocaleStatistics {
/**
* Breakdown of statistics by locale.
*/
by_locale: Record<string, AdminTranslationLocaleStatistics>
}
/**
* Response for translation statistics endpoint.
*/
export interface AdminTranslationStatisticsResponse {
/**
* Statistics by entity type.
*/
statistics: Record<string, AdminTranslationEntityStatistics>
}
/**
* Response for translation settings endpoint.
*/
export interface AdminTranslationSettingsResponse {
/**
* A mapping of entity types to their translatable field names.
*
* @example
* {
* "product": ["title", "description", "subtitle", "status"],
* "product_variant": ["title", "material"]
* }
*/
translatable_fields: Record<string, string[]>
}

View File

@@ -132,3 +132,55 @@ export interface FilterableTranslationProps
*/
locale_code?: string | string[] | OperatorMap<string>
}
/**
* Input for getStatistics method.
*/
export interface TranslationStatisticsInput {
/**
* Locales to check translations for (e.g., ["en-US", "fr-FR"]).
*/
locales: string[]
/**
* Entity types with their total counts.
* Key is the entity type (e.g., "product"), value contains the count of entities.
*/
entities: Record<string, { count: number }>
}
/**
* Statistics for a specific locale.
*/
export interface LocaleStatistics {
/**
* Expected number of translated fields.
*/
expected: number
/**
* Actual number of translated fields (non-null, non-empty).
*/
translated: number
/**
* Number of missing translations (expected - translated).
*/
missing: number
}
/**
* Statistics for an entity type.
*/
export interface EntityTypeStatistics extends LocaleStatistics {
/**
* Breakdown of statistics by locale.
*/
by_locale: Record<string, LocaleStatistics>
}
/**
* Output of getStatistics method.
* Maps entity types to their translation statistics.
*/
export type TranslationStatisticsOutput = Record<string, EntityTypeStatistics>

View File

@@ -7,6 +7,8 @@ import {
FilterableTranslationProps,
LocaleDTO,
TranslationDTO,
TranslationStatisticsInput,
TranslationStatisticsOutput,
} from "./common"
import {
CreateLocaleDTO,
@@ -291,4 +293,57 @@ export interface ITranslationModuleService extends IModuleService {
config?: RestoreReturn<TReturnableLinkableKeys>,
sharedContext?: Context
): Promise<Record<string, string[]> | void>
/**
* This method retrieves translation statistics for the given entities and locales.
* It counts translated fields at a granular level, providing both aggregated
* and per-locale breakdowns.
*
* @param {TranslationStatisticsInput} input - The entities and locales to check.
* @param {Context} sharedContext
* @returns {Promise<TranslationStatisticsOutput>} Statistics by entity type.
*
* @example
* const stats = await translationService.getStatistics({
* locales: ["en-US", "fr-FR"],
* entities: {
* product: { count: 2 },
* product_variant: { count: 5 }
* }
* })
* // Returns:
* // {
* // product: {
* // expected: 20, // 2 products × 5 fields × 2 locales
* // translated: 15,
* // missing: 5,
* // by_locale: {
* // "en-US": { expected: 10, translated: 8, missing: 2 },
* // "fr-FR": { expected: 10, translated: 7, missing: 3 }
* // }
* // }
* // }
*/
getStatistics(
input: TranslationStatisticsInput,
sharedContext?: Context
): Promise<TranslationStatisticsOutput>
/**
* This method retrieves the translatable fields configuration.
* Returns a mapping of entity types to their translatable field names.
*
* @param {string} entityType - Optional entity type to filter by. If not provided, returns all.
* @returns {Record<string, string[]>} A mapping of entity types to their translatable fields.
*
* @example
* // Get all translatable fields
* const allFields = translationService.getTranslatableFields()
* // Returns: { product: ["title", "description", ...], product_variant: ["title", ...] }
*
* // Get fields for a specific entity type
* const productFields = translationService.getTranslatableFields("product")
* // Returns: { product: ["title", "description", "subtitle", "status"] }
*/
getTranslatableFields(entityType?: string): Record<string, string[]>
}