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
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/models/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ export interface I18nTranslations {

'messages.loading.placeholder': string;

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': string;
'consent.essential_claims': string;
'consent.optional_claims': string;
'consent.authorize_scope': string;
'consent.essential_claims.info': string;
'consent.optional_claims.info': string;
'consent.authorize_scope.info': string;

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'Loading...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'Required',
'consent.essential_claims': 'Essential Attributes',
'consent.optional_claims': 'Optional Attributes',
'consent.authorize_scope': 'Permissions',
'consent.essential_claims.info': 'These attributes are required for the service to function properly.',
'consent.optional_claims.info': 'These attributes are optional and can be provided at your discretion.',
'consent.authorize_scope.info': 'These permissions allow the service to access additional features.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
13 changes: 13 additions & 0 deletions packages/javascript/src/i18n/translations/fr-FR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,19 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'Chargement...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'Obligatoire',
'consent.essential_claims': 'Attributs essentiels',
'consent.optional_claims': 'Attributs facultatifs',
'consent.authorize_scope': 'Autorisations',
'consent.essential_claims.info': 'Ces attributs sont nécessaires au bon fonctionnement du service.',
'consent.optional_claims.info': 'Ces attributs sont facultatifs et peuvent être fournis à votre discrétion.',
'consent.authorize_scope.info':
"Ces autorisations permettent au service d'accéder à des fonctionnalités supplémentaires.",

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/hi-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'लोड हो रहा है...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'आवश्यक',
'consent.essential_claims': 'आवश्यक विशेषताएँ',
'consent.optional_claims': 'वैकल्पिक विशेषताएँ',
'consent.authorize_scope': 'अनुमतियाँ',
'consent.essential_claims.info': 'सेवा के सही ढंग से कार्य करने के लिए ये विशेषताएँ आवश्यक हैं।',
'consent.optional_claims.info': 'ये विशेषताएँ वैकल्पिक हैं और आपके विवेकानुसार प्रदान की जा सकती हैं।',
'consent.authorize_scope.info': 'ये अनुमतियाँ सेवा को अतिरिक्त सुविधाओं तक पहुँचने की अनुमति देती हैं।',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': '読み込み中…',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': '必須',
'consent.essential_claims': '必須属性',
'consent.optional_claims': '任意属性',
'consent.authorize_scope': '権限',
'consent.essential_claims.info': 'これらの属性は、サービスが正常に機能するために必要です。',
'consent.optional_claims.info': 'これらの属性は任意であり、お客様の判断で提供できます。',
'consent.authorize_scope.info': 'これらの権限により、サービスが追加機能にアクセスできるようになります。',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/pt-BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'Carregando...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'Obrigatório',
'consent.essential_claims': 'Atributos essenciais',
'consent.optional_claims': 'Atributos opcionais',
'consent.authorize_scope': 'Permissões',
'consent.essential_claims.info': 'Esses atributos são necessários para o funcionamento adequado do serviço.',
'consent.optional_claims.info': 'Esses atributos são opcionais e podem ser fornecidos a seu critério.',
'consent.authorize_scope.info': 'Essas permissões permitem que o serviço acesse recursos adicionais.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/pt-PT.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'A carregar...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'Obrigatório',
'consent.essential_claims': 'Atributos essenciais',
'consent.optional_claims': 'Atributos opcionais',
'consent.authorize_scope': 'Permissões',
'consent.essential_claims.info': 'Estes atributos são necessários para o correto funcionamento do serviço.',
'consent.optional_claims.info': 'Estes atributos são opcionais e podem ser fornecidos ao seu critério.',
'consent.authorize_scope.info': 'Estas permissões permitem que o serviço aceda a funcionalidades adicionais.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/si-LK.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'ලෝඩ් වෙමින්...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'අවශ්‍යයි',
'consent.essential_claims': 'අත්‍යවශ්‍ය ගුණාංග',
'consent.optional_claims': 'විකල්ප ගුණාංග',
'consent.authorize_scope': 'අවසර',
'consent.essential_claims.info': 'සේවාව නිසි ලෙස ක්‍රියාත්මක වීම සඳහා මෙම ගුණාංග අවශ්‍ය වේ.',
'consent.optional_claims.info': 'මෙම ගුණාංග විකල්ප වන අතර ඔබේ අභිමතය පරිදි සැපයිය හැක.',
'consent.authorize_scope.info': 'මෙම අවසරයන් මගින් සේවාවට අමතර විශේෂාංග වෙත ප්‍රවේශ වීමට ඉඩ සලසයි.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/ta-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'ஏற்றப்படுகிறது...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'தேவையானது',
'consent.essential_claims': 'அத்தியாவசிய பண்புகள்',
'consent.optional_claims': 'விருப்பத் பண்புகள்',
'consent.authorize_scope': 'அனுமதிகள்',
'consent.essential_claims.info': 'சேவை சரியாக செயல்பட இந்த பண்புகள் தேவை.',
'consent.optional_claims.info': 'இந்த பண்புகள் விருப்பத்தேர்வு மற்றும் உங்கள் விருப்பப்படி வழங்கப்படலாம்.',
'consent.authorize_scope.info': 'இந்த அனுமதிகள் கூடுதல் அம்சங்களை அணுக சேவைக்கு உதவுகின்றன.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
12 changes: 12 additions & 0 deletions packages/javascript/src/i18n/translations/te-IN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ const translations: I18nTranslations = {

'messages.loading.placeholder': 'లోడ్ అవుతోంది...',

/* |---------------------------------------------------------------| */
/* | Consent | */
/* |---------------------------------------------------------------| */

'consent.required': 'తప్పనిసరి',
'consent.essential_claims': 'ఆవశ్యక లక్షణాలు',
'consent.optional_claims': 'ఐచ్ఛిక లక్షణాలు',
'consent.authorize_scope': 'అనుమతులు',
'consent.essential_claims.info': 'సేవ సరిగ్గా పనిచేయడానికి ఈ లక్షణాలు అవసరం.',
'consent.optional_claims.info': 'ఈ లక్షణాలు ఐచ్ఛికం మరియు మీ విచక్షణ మేరకు అందించవచ్చు.',
'consent.authorize_scope.info': 'ఈ అనుమతులు సేవను అదనపు ఫీచర్‌లను యాక్సెస్ చేయడానికి అనుమతిస్తాయి.',

/* |---------------------------------------------------------------| */
/* | Errors | */
/* |---------------------------------------------------------------| */
Expand Down
71 changes: 21 additions & 50 deletions packages/react/src/components/adapters/Consent.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
// Copyright 2026 The ThunderID Authors
// SPDX-License-Identifier: Apache-2.0

import {
type ConsentPurposeData,
FlowMetadataResponse,
PromptElement,
resolveFlowTemplateLiterals,
} from '@thunderid/browser';
import {type ConsentPurposeData, PromptElement} from '@thunderid/browser';
import {type ChangeEvent, FC, ReactNode} from 'react';
import ConsentCheckboxList, {getConsentOptionalKey} from './ConsentCheckboxList';
import Typography from '../primitives/Typography/Typography';
Expand Down Expand Up @@ -35,16 +30,6 @@ export interface ConsentRenderProps {
purposes: ConsentPurposeData[];
}

/**
* Interface for consent configuration
*/
export interface ConsentConfig {
essential?: string;
optional?: string;
essentialInfo?: string;
optionalInfo?: string;
}

/**
* Props for the Consent component.
*/
Expand Down Expand Up @@ -78,41 +63,19 @@ export interface ConsentProps {
* Callback invoked when a user toggles an optional attribute.
*/
onInputChange: (name: string, value: string) => void;
/**
* Config to modified detail in consent page
*/
config?: Record<string, unknown>;

/**
* Config of meta response
*/
meta?: FlowMetadataResponse | null;

/**
* translation data
*/
t?: UseTranslation['t'];
}

const defaultConfig: Required<Pick<ConsentConfig, 'essential' | 'optional'>> = {
essential: 'Essential Attributtes',
optional: 'Optional Attributes',
};

/**
* Consent component renders the list of purposes and their associated attributes (essential and optional)
* based on the data provided by the backend. It allows users to toggle optional attributes while essential
* attributes are displayed as read-only.
*/
const Consent: FC<ConsentProps> = ({
consentData,
formValues,
config: suppliedConfig = {},
onInputChange,
children,
meta,
t,
}: ConsentProps) => {
const Consent: FC<ConsentProps> = ({consentData, formValues, onInputChange, children, t}: ConsentProps) => {
// Computed per render (not at module scope): a CSP nonce configured on <ThunderIDProvider>
// isn't known until the provider renders, and Emotion needs it applied before any style
// insertion happens. These are static, so Emotion's own cache dedupes the repeat calls to a
Expand All @@ -135,19 +98,24 @@ const Consent: FC<ConsentProps> = ({
});
const optionalSectionLabelClass: string = css({alignItems: 'center', display: 'flex', gap: '4px'});

/** Resolve any remaining {{t()}} or {{meta()}} template expressions in a string at render time. */
/** Resolve i18n keys */
const resolve = (text: string | undefined): string => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

There's a diff in the two resolve functions. Here we're returning "" (empty) if a key is not found. However in the resolve() function in packages/react/src/components/adapters/ConsentCheckboxList.tsx, we're returning the key as it is.

Shall we modify the resolve() function here also to display the key if the translation is not found?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Also prefer to get rid of the duplicated resolve() functions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ConsentCheckboxList always needs text to display, so its resolve falls back to the original string when no translation exists.

Consent behaves differently — it needs a default value for the label, and the info icon should be hidden when there's no translation for the claim details.

That's why each component has its own resolve implementation.

if (!text || (!t && !meta)) {
if (!text || !t) {
return text || '';
}
return resolveFlowTemplateLiterals(text, {meta, t: t || ((k: string): string => k)});
return t(text);
};

const config: ConsentConfig = {...defaultConfig, ...suppliedConfig};
const essentialInfo = typeof config.essentialInfo === 'string' ? resolve(config.essentialInfo.trim()) : '';
const optionalInfo = typeof config.optionalInfo === 'string' ? resolve(config.optionalInfo.trim()) : '';
const essentialLabel = resolve(config['essential']);
const optionalLabel = resolve(config['optional']);
const essentialInfo = resolve(`consent.essential_claims.info`);
const optionalInfo = resolve(`consent.optional_claims.info`);
const permissionInfo = resolve(`consent.authorize_scope.info`);
/**
* Falls back to default config values if essential/optional
* keys cannot be resolved via translation files .
*/
const essentialLabel = resolve(`consent.essential_claims`) || 'Essential Attributes';
const optionalLabel = resolve(`consent.optional_claims`) || 'Optional Attributes';
const permissionLabel = resolve(`consent.authorize_scope`) || 'Permissions';

/**
* Method to check whether master toggle button is checked or not
Expand Down Expand Up @@ -223,6 +191,7 @@ const Consent: FC<ConsentProps> = ({
purpose={purpose}
formValues={formValues}
onInputChange={onInputChange}
t={t}
/>
</div>
)}
Expand All @@ -232,10 +201,11 @@ const Consent: FC<ConsentProps> = ({
<div className={optionalSectionHeaderClass}>
<div className={optionalSectionLabelClass}>
<Typography variant="subtitle2" fontWeight="bold">
{purpose.type === 'permissions' ? 'Permissions' : optionalLabel}
{purpose.type === 'permissions' ? permissionLabel : optionalLabel}
</Typography>
{optionalInfo !== '' && (
<Tooltip helperText={optionalInfo}>
{/* Show tooltip for optional claims/permissions according to their type */}
{Boolean(purpose.type === 'permissions' ? permissionInfo : optionalInfo) && (
<Tooltip helperText={purpose.type === 'permissions' ? permissionInfo : optionalInfo}>
<Info width="1rem" height="1rem" />
</Tooltip>
)}
Expand All @@ -252,6 +222,7 @@ const Consent: FC<ConsentProps> = ({
purpose={purpose}
formValues={formValues}
onInputChange={onInputChange}
t={t}
/>
</div>
)}
Expand Down
20 changes: 18 additions & 2 deletions packages/react/src/components/adapters/ConsentCheckboxList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import useTheme from '../../contexts/Theme/useTheme';
import {cx} from '../../styles/emotion';
import Toggle from '../primitives/Toggle/Toggle';
import Typography from '../primitives/Typography/Typography';
import {UseTranslation} from '../../hooks/useTranslation';

/**
* Computes the form value key for tracking an optional attribute's consent state.
Expand Down Expand Up @@ -78,6 +79,10 @@ export interface ConsentCheckboxListProps {
purpose: ConsentPurposeData;
/** Whether to render essential (disabled) or optional (toggleable) attributes */
variant: ConsentInputVariant;
/**
* translation data
*/
t?: UseTranslation['t'];
}

/**
Expand All @@ -93,10 +98,21 @@ const ConsentCheckboxList: FC<ConsentCheckboxListProps> = ({
formValues,
onInputChange,
children,
t,
}: ConsentCheckboxListProps) => {
const {theme, colorScheme}: ReturnType<typeof useTheme> = useTheme();
const styles: Record<string, string> = useStyles(theme, colorScheme);

const resolve = (text: string | undefined): string => {
if (!text || !t) {
return text || '';
}

const key: string = `consent.${text}`;
const translated: string = t(key);
return translated === key ? text : translated;
};

const attributes: string[] = (variant === 'ESSENTIAL' ? purpose.essential : purpose.optional).map(
(e): string => e.name,
);
Expand Down Expand Up @@ -153,11 +169,11 @@ const ConsentCheckboxList: FC<ConsentCheckboxListProps> = ({
styles['typography'],
)}
>
{attr}
{resolve(attr)}
</Typography>
</div>
{isEssential ? (
<Typography variant="body2">Required</Typography>
<Typography variant="body2">{resolve('required') || 'Required'}</Typography>
) : (
<Toggle
id={inputId}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -813,8 +813,6 @@ const createAuthComponentFromFlow = (
consentData={consentPromptRawData as any}
formValues={formValues}
onInputChange={onInputChange}
config={component.config}
meta={options.meta}
t={options.t}
/>
);
Expand Down
Loading