Skip to content
Open
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
71 changes: 38 additions & 33 deletions src/service/worker/runtime/graphql/utils/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,68 @@ import { MessagesLoaderV2 } from '../schema/messagesLoaderV2'

export const CONTEXT_REGEX = /\(\(\((?<context>(.)*)\)\)\)/
export const FROM_REGEX = /\<\<\<(?<from>(.)*)\>\>\>/
export const CONTENT_REGEX = /\(\(\((?<context>(.)*)\)\)\)|\<\<\<(?<from>(.)*)\>\>\>/g
export const STATE_REGEX = /\[\[\[(?<state>(.)*)\]\]\]/
export const CONTENT_REGEX = /\(\(\((?<context>(.)*)\)\)\)|\<\<\<(?<from>(.)*)\>\>\>|\[\[\[(?<state>(.)*)\]\]\]/g

export interface TranslatableMessageV2 {
from?: string
content: string
context?: string
state?: 'original' | 'translated'
}

export type TranslationDirectiveType = 'translatableV2' | 'translateTo'

export const parseTranslatableStringV2 = (rawMessage: string): TranslatableMessageV2 => {
const context = rawMessage.match(CONTEXT_REGEX)?.groups?.context
const from = rawMessage.match(FROM_REGEX)?.groups?.from
const state = rawMessage.match(STATE_REGEX)?.groups?.state
const content = rawMessage.replace(CONTENT_REGEX, '')

return {
content: content?.trim(),
context: context?.trim(),
from: from?.trim(),
state: (state?.trim() === 'translated' ? 'translated' : 'original') as 'translated' | 'original',
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The type assertion is redundant since the ternary operator already ensures the return type matches the union type. Consider simplifying to: state: state?.trim() === 'translated' ? 'translated' : 'original'

Suggested change
state: (state?.trim() === 'translated' ? 'translated' : 'original') as 'translated' | 'original',
state: state?.trim() === 'translated' ? 'translated' : 'original',

Copilot uses AI. Check for mistakes.

}
}

export const formatTranslatableStringV2 = ({ from, content, context }: TranslatableMessageV2): string =>
`${content} ${context ? `(((${context})))` : ''} ${from ? `<<<${from}>>>` : ''}`
export const formatTranslatableStringV2 = ({ from, content, context, state }: TranslatableMessageV2): string =>
`${content} ${context ? `(((${context})))` : ''} ${from ? `<<<${from}>>>` : ''} ${state ? `[[[${state}]]]` : ''}`
Copy link

Copilot AI Oct 13, 2025

Choose a reason for hiding this comment

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

The formatting function includes the state marker even when state is 'original', which may add unnecessary markers to strings. Consider only including the state marker when it's 'translated' to keep the output cleaner for the default case.

Suggested change
`${content} ${context ? `(((${context})))` : ''} ${from ? `<<<${from}>>>` : ''} ${state ? `[[[${state}]]]` : ''}`
`${content} ${context ? `(((${context})))` : ''} ${from ? `<<<${from}>>>` : ''} ${state === 'translated' ? `[[[${state}]]]` : ''}`

Copilot uses AI. Check for mistakes.


export const handleSingleString = (
ctx: IOContext,
loader: MessagesLoaderV2,
behavior: Behavior,
directiveName: TranslationDirectiveType
) => async (rawMessage: string | null) => {
// Messages only know how to process non empty strings.
if (rawMessage == null) {
return rawMessage
}
export const handleSingleString =
(ctx: IOContext, loader: MessagesLoaderV2, behavior: Behavior, directiveName: TranslationDirectiveType) =>
async (rawMessage: string | null) => {
// Messages only know how to process non empty strings.
if (rawMessage == null) {
return rawMessage
}

const { content, context, from: maybeFrom } = parseTranslatableStringV2(rawMessage)
const { binding, tenant } = ctx
const { content, context, from: maybeFrom, state } = parseTranslatableStringV2(rawMessage)
const { binding, tenant } = ctx

if (content == null) {
throw new Error(
`@${directiveName} directive needs a content to translate, but received ${JSON.stringify(rawMessage)}`
)
}
if (content == null) {
throw new Error(
`@${directiveName} directive needs a content to translate, but received ${JSON.stringify(rawMessage)}`
)
}

const from = maybeFrom || binding?.locale || tenant?.locale
const from = maybeFrom || binding?.locale || tenant?.locale

if (from == null) {
throw new Error(
`@${directiveName} directive needs a source language to translate from. You can do this by either setting ${'`ctx.vtex.tenant`'} variable, call this app with the header ${'`x-vtex-tenant`'} or format the string with the ${'`formatTranslatableStringV2`'} function with the ${'`from`'} option set`
)
}
if (from == null) {
throw new Error(
`@${directiveName} directive needs a source language to translate from. You can do this by either setting ${'`ctx.vtex.tenant`'} variable, call this app with the header ${'`x-vtex-tenant`'} or format the string with the ${'`formatTranslatableStringV2`'} function with the ${'`from`'} option set`
)
}

return loader.load({
behavior,
content,
context,
from,
})
}
if (state === 'translated') {
return content
}

return loader.load({
behavior,
content,
context,
from,
})
}
Loading