Skip to main content
The TranslationService (App\Services\TranslationService) powers Site Store Pro’s automated multilingual content system. It connects to the OpenAI API to translate content across your entire store — products, CMS pages, email templates, navigation items, and more — while protecting platform-specific shortcode markup from being corrupted or mistranslated during the process.
You must set OPENAI_API_KEY in your .env file before any AI-powered translation feature will function. Without a valid key, all bulk translation jobs will fail silently or throw an authentication error from the OpenAI client. Obtain your key from platform.openai.com and add it as:

OpenAI Client Requirement

The service uses the openai-php/client package at ^0.20.0. Confirm this dependency is present in your composer.json and installed before running translations.

Shortcode Protection Engine

Shortcodes are the bracketed plugin tags embedded in CMS content, such as:
If you send these strings to OpenAI as-is, the model may rephrase, reorder, or omit parts of the tag — breaking the plugin entirely on translated pages. The Shortcode Protection Engine prevents this with a two-step wrap-and-restore process.
1

Strip — Before sending to OpenAI

The service scans the content string for any bracketed shortcode tags and replaces each one with a neutral, indexed placeholder that OpenAI will treat as an opaque token:Input content:
Sent to OpenAI:
The original shortcodes are stored in an internal map keyed by their placeholder index (PLUGIN_0, PLUGIN_1, etc.).
2

Restore — After translation returns

Once OpenAI returns the translated string, the service replaces every {{PLUGIN_N}} placeholder with the corresponding original shortcode from the map:Returned from OpenAI (French):
Final stored translation:
Monitor active and failed translation jobs in real time by navigating to Admin → CMS → Queue Monitor. Each bulk translation run dispatches multiple queued jobs — one per entity batch. The Queue Monitor shows job status, failure reasons, and retry controls without requiring direct server access.

Bulk Translation Pipeline

When an admin clicks Bulk Translate All on a language card, the service dispatches queued translation jobs for all 11 core content entity groups. Each group translates the parent record fields and writes results into the corresponding *_translations child table for the target language.
Each job applies the Shortcode Protection Engine before sending content to OpenAI and restores shortcodes before writing the translated result to the database.

EmailTemplateService

EmailTemplateService::sendEmail() is the standard method for dispatching transactional emails throughout Site Store Pro. It supports full translation lookup, {{variable}} substitution, and dynamic template rendering.

Signature

Parameters

string
required
The unique identifier for the email template record in the email_templates table. Examples: order_confirmation, password_reset, download_reminder.
string
required
The recipient’s email address.
string
required
The recipient’s display name, used in the email greeting and headers.
array
required
An associative array of variable replacements. Keys correspond to {{variable}} tokens present in the template body and subject. For example:
int
required
The ID of the target language. The service eager-loads the matching email_template_translations record for this language ID and falls back to the default template content if no translation exists.

Dispatch Example

The method fetches the active profile for the given $slug, applies translations for $languageId, substitutes all {{variable}} tokens, and dispatches an App\Mail\DynamicTemplateMail mailable to the queue.

Child-Table Translation Pattern

All translatable entities in Site Store Pro follow a consistent parent/child schema pattern. The parent table holds canonical (default language) content, and each language’s translated version of that content lives in a dedicated *_translations child table.
Every child table contains at minimum: When querying translated content, eager-load the appropriate *_translations relationship filtered by language_id to avoid N+1 queries: