@hexar/biometric-identity-sdk-react-native 1.0.26 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"BiometricIdentityFlow.d.ts","sourceRoot":"","sources":["../../src/components/BiometricIdentityFlow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,EAOL,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,cAAc,EAKd,iBAAiB,EAElB,MAAM,oCAAoC,CAAC;AAuB5C,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,OAAO,CAAC,EAAE,SAAS,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAyXtE,CAAC;AA8NF,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"BiometricIdentityFlow.d.ts","sourceRoot":"","sources":["../../src/components/BiometricIdentityFlow.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AACxE,OAAO,EAOL,SAAS,EACV,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,gBAAgB,EAChB,WAAW,EACX,cAAc,EAKd,iBAAiB,EAElB,MAAM,oCAAoC,CAAC;AA8B5C,MAAM,WAAW,0BAA0B;IACzC,oBAAoB,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACzD,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,SAAS,CAAC;QACtB,OAAO,CAAC,EAAE,SAAS,CAAC;KACrB,CAAC;CACH;AAED,eAAO,MAAM,qBAAqB,EAAE,KAAK,CAAC,EAAE,CAAC,0BAA0B,CAuatE,CAAC;AA8NF,eAAe,qBAAqB,CAAC"}
@@ -44,18 +44,25 @@ const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-cor
44
44
  const useBiometricSDK_1 = require("../hooks/useBiometricSDK");
45
45
  const CameraCapture_1 = require("./CameraCapture");
46
46
  const VideoRecorder_1 = require("./VideoRecorder");
47
- const getInstructionMap = (strings) => ({
48
- look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
49
- look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
50
- look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
51
- look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
52
- turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
53
- turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
54
- smile: { text: strings.liveness.instructions.smile || 'Smile' },
55
- blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
56
- open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
57
- stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
58
- });
47
+ const getInstructionMap = (strings) => {
48
+ if (!strings || !strings.liveness || !strings.liveness.instructions) {
49
+ return {};
50
+ }
51
+ return {
52
+ look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
53
+ look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
54
+ look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
55
+ look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
56
+ turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
57
+ turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
58
+ turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
59
+ turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
60
+ smile: { text: strings.liveness.instructions.smile || 'Smile' },
61
+ blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
62
+ open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
63
+ stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
64
+ };
65
+ };
59
66
  const ValidationProgress_1 = require("./ValidationProgress");
60
67
  const ResultScreen_1 = require("./ResultScreen");
61
68
  const ErrorScreen_1 = require("./ErrorScreen");
@@ -77,6 +84,12 @@ const BiometricIdentityFlow = ({ onValidationComplete, onError, theme, language,
77
84
  (0, react_1.useEffect)(() => {
78
85
  if (language) {
79
86
  (0, biometric_identity_sdk_core_1.setLanguage)(language);
87
+ const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
88
+ biometric_identity_sdk_core_1.logger.info('Language set in BiometricIdentityFlow', {
89
+ language,
90
+ lookLeft: currentStrings.liveness?.instructions?.lookLeft,
91
+ lookRight: currentStrings.liveness?.instructions?.lookRight
92
+ });
80
93
  }
81
94
  }, [language]);
82
95
  const strings = (0, biometric_identity_sdk_core_1.getStrings)();
@@ -112,25 +125,65 @@ const BiometricIdentityFlow = ({ onValidationComplete, onError, theme, language,
112
125
  }
113
126
  catch (error) {
114
127
  biometric_identity_sdk_core_1.logger.warn('Failed to fetch challenges, using defaults');
128
+ if (language) {
129
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
130
+ }
131
+ const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
132
+ const instructionMap = getInstructionMap(currentStrings);
115
133
  const defaultChallenges = sdk.getDefaultChallenges();
116
- const instructionMap = getInstructionMap(strings);
117
- const translatedChallenges = defaultChallenges.map(challenge => ({
118
- ...challenge,
119
- instruction: instructionMap[challenge.action]?.text || challenge.instruction,
120
- icon: instructionMap[challenge.action]?.icon
121
- }));
134
+ biometric_identity_sdk_core_1.logger.info('Translating default challenges', {
135
+ language,
136
+ actionCount: defaultChallenges.length,
137
+ instructionMapKeys: Object.keys(instructionMap),
138
+ lookLeftTranslation: instructionMap['look_left']?.text,
139
+ lookRightTranslation: instructionMap['look_right']?.text
140
+ });
141
+ const translatedChallenges = defaultChallenges.map(challenge => {
142
+ const translated = instructionMap[challenge.action];
143
+ const finalInstruction = translated?.text || challenge.instruction;
144
+ biometric_identity_sdk_core_1.logger.info('Translating challenge', {
145
+ action: challenge.action,
146
+ original: challenge.instruction,
147
+ translated: finalInstruction
148
+ });
149
+ return {
150
+ ...challenge,
151
+ instruction: finalInstruction,
152
+ icon: translated?.icon
153
+ };
154
+ });
122
155
  setCurrentChallenges(translatedChallenges);
123
156
  }
124
157
  setIsLoadingChallenges(false);
125
158
  }
126
159
  else if (mode === 'video' && smartLivenessMode) {
160
+ if (language) {
161
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
162
+ }
163
+ const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
164
+ const instructionMap = getInstructionMap(currentStrings);
127
165
  const defaultChallenges = sdk.getDefaultChallenges();
128
- const instructionMap = getInstructionMap(strings);
129
- const translatedChallenges = defaultChallenges.map(challenge => ({
130
- ...challenge,
131
- instruction: instructionMap[challenge.action]?.text || challenge.instruction,
132
- icon: instructionMap[challenge.action]?.icon
133
- }));
166
+ biometric_identity_sdk_core_1.logger.info('Translating default challenges (offline mode)', {
167
+ language,
168
+ actionCount: defaultChallenges.length,
169
+ instructionMapKeys: Object.keys(instructionMap),
170
+ lookLeftTranslation: instructionMap['look_left']?.text,
171
+ lookRightTranslation: instructionMap['look_right']?.text
172
+ });
173
+ const translatedChallenges = defaultChallenges.map(challenge => {
174
+ const translated = instructionMap[challenge.action];
175
+ const finalInstruction = translated?.text || challenge.instruction;
176
+ biometric_identity_sdk_core_1.logger.info('Translating challenge (offline)', {
177
+ action: challenge.action,
178
+ original: challenge.instruction,
179
+ translated: finalInstruction
180
+ });
181
+ return {
182
+ ...challenge,
183
+ instruction: finalInstruction,
184
+ icon: translated?.icon
185
+ };
186
+ });
134
187
  setCurrentChallenges(translatedChallenges);
135
188
  }
136
189
  setShowCamera(true);
@@ -1 +1 @@
1
- {"version":3,"file":"ResultScreen.d.ts","sourceRoot":"","sources":["../../src/components/ResultScreen.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAEtG,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAoHpD,CAAC;AAgKF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ResultScreen.d.ts","sourceRoot":"","sources":["../../src/components/ResultScreen.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAoB,MAAM,OAAO,CAAC;AAQzC,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,iBAAiB,EAA2B,MAAM,oCAAoC,CAAC;AAE/H,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqHpD,CAAC;AAmKF,eAAe,YAAY,CAAC"}
@@ -3,14 +3,51 @@
3
3
  * Result Screen Component
4
4
  * Shows validation result (pass/fail)
5
5
  */
6
- var __importDefault = (this && this.__importDefault) || function (mod) {
7
- return (mod && mod.__esModule) ? mod : { "default": mod };
8
- };
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
9
39
  Object.defineProperty(exports, "__esModule", { value: true });
10
40
  exports.ResultScreen = void 0;
11
- const react_1 = __importDefault(require("react"));
41
+ const react_1 = __importStar(require("react"));
12
42
  const react_native_1 = require("react-native");
43
+ const biometric_identity_sdk_core_1 = require("@hexar/biometric-identity-sdk-core");
13
44
  const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
45
+ (0, react_1.useEffect)(() => {
46
+ if (language) {
47
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
48
+ }
49
+ }, [language]);
50
+ const strings = (0, biometric_identity_sdk_core_1.getStrings)();
14
51
  const isSuccess = result.isValidFaceMatch && result.isDocumentAuthentic;
15
52
  const successColor = theme?.successColor || '#10B981';
16
53
  const errorColor = theme?.errorColor || '#EF4444';
@@ -21,27 +58,19 @@ const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
21
58
  { backgroundColor: isSuccess ? successColor : errorColor },
22
59
  ] },
23
60
  react_1.default.createElement(react_native_1.Text, { style: styles.icon }, isSuccess ? '✓' : '✗')),
24
- react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, isSuccess
25
- ? (language === 'es' ? '¡Verificación Exitosa!' : 'Verification Successful!')
26
- : (language === 'es' ? 'Verificación Fallida' : 'Verification Failed')),
27
- react_1.default.createElement(react_native_1.Text, { style: [styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }] }, isSuccess
28
- ? (language === 'es'
29
- ? 'Tu identidad ha sido verificada correctamente'
30
- : 'Your identity has been verified successfully')
31
- : (language === 'es'
32
- ? 'No pudimos verificar tu identidad'
33
- : 'We were unable to verify your identity')),
61
+ react_1.default.createElement(react_native_1.Text, { style: [styles.title, { color: theme?.textColor || '#000000' }] }, isSuccess ? strings.result.success.title : strings.result.failure.title),
62
+ react_1.default.createElement(react_native_1.Text, { style: [styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }] }, isSuccess ? strings.result.success.message : strings.result.failure.message),
34
63
  react_1.default.createElement(react_native_1.View, { style: styles.scoresContainer },
35
- react_1.default.createElement(ScoreCard, { label: language === 'es' ? 'Coincidencia Facial' : 'Face Match', score: result.matchScore, passed: result.isValidFaceMatch, theme: theme }),
36
- react_1.default.createElement(ScoreCard, { label: language === 'es' ? 'Presencia Vital' : 'Liveness', score: result.livenessScore, passed: result.livenessScore >= 80, theme: theme })),
64
+ react_1.default.createElement(ScoreCard, { label: strings.validation.matchingFaces || 'Face Match', score: result.matchScore, passed: result.isValidFaceMatch, theme: theme, strings: strings }),
65
+ react_1.default.createElement(ScoreCard, { label: strings.liveness.title || 'Liveness', score: result.livenessScore, passed: result.livenessScore >= 80, theme: theme, strings: strings })),
37
66
  result.extractedDocumentData && (react_1.default.createElement(react_native_1.View, { style: styles.dataContainer },
38
- react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.textColor || '#000000' }] }, language === 'es' ? 'Datos Extraídos' : 'Extracted Data'),
39
- react_1.default.createElement(DataRow, { label: language === 'es' ? 'Nombre' : 'Name', value: `${result.extractedDocumentData.firstName} ${result.extractedDocumentData.lastName}`, theme: theme }),
40
- react_1.default.createElement(DataRow, { label: language === 'es' ? 'Documento' : 'Document Number', value: result.extractedDocumentData.documentNumber, theme: theme }),
41
- react_1.default.createElement(DataRow, { label: language === 'es' ? 'Fecha de Nacimiento' : 'Date of Birth', value: result.extractedDocumentData.dateOfBirth, theme: theme }),
42
- react_1.default.createElement(DataRow, { label: language === 'es' ? 'Nacionalidad' : 'Nationality', value: result.extractedDocumentData.nationality, theme: theme }))),
67
+ react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.textColor || '#000000' }] }, strings.result.success.verificationComplete || 'Extracted Data'),
68
+ react_1.default.createElement(DataRow, { label: "Nombre", value: `${result.extractedDocumentData.firstName} ${result.extractedDocumentData.lastName}`, theme: theme }),
69
+ react_1.default.createElement(DataRow, { label: "Documento", value: result.extractedDocumentData.documentNumber, theme: theme }),
70
+ react_1.default.createElement(DataRow, { label: "Fecha de Nacimiento", value: result.extractedDocumentData.dateOfBirth, theme: theme }),
71
+ react_1.default.createElement(DataRow, { label: "Nacionalidad", value: result.extractedDocumentData.nationality, theme: theme }))),
43
72
  result.warnings.length > 0 && (react_1.default.createElement(react_native_1.View, { style: styles.warningsContainer },
44
- react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }] }, language === 'es' ? 'Advertencias' : 'Warnings'),
73
+ react_1.default.createElement(react_native_1.Text, { style: [styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }] }, "Advertencias"),
45
74
  result.warnings.map((warning, index) => (react_1.default.createElement(react_native_1.Text, { key: index, style: styles.warningText },
46
75
  "\u2022 ",
47
76
  warning.replace(/_/g, ' '))))))),
@@ -50,16 +79,16 @@ const ResultScreen = ({ result, theme, language = 'en', onClose, }) => {
50
79
  styles.button,
51
80
  { backgroundColor: theme?.primaryColor || '#6366F1' },
52
81
  ], onPress: onClose },
53
- react_1.default.createElement(react_native_1.Text, { style: styles.buttonText }, language === 'es' ? 'Continuar' : 'Continue')))));
82
+ react_1.default.createElement(react_native_1.Text, { style: styles.buttonText }, strings.common.continue)))));
54
83
  };
55
84
  exports.ResultScreen = ResultScreen;
56
- const ScoreCard = ({ label, score, passed, theme }) => (react_1.default.createElement(react_native_1.View, { style: scoreStyles.container },
85
+ const ScoreCard = ({ label, score, passed, theme, strings }) => (react_1.default.createElement(react_native_1.View, { style: scoreStyles.container },
57
86
  react_1.default.createElement(react_native_1.Text, { style: scoreStyles.label }, label),
58
87
  react_1.default.createElement(react_native_1.Text, { style: [
59
88
  scoreStyles.score,
60
89
  { color: passed ? (theme?.successColor || '#10B981') : (theme?.errorColor || '#EF4444') },
61
90
  ] }, Math.round(score)),
62
- react_1.default.createElement(react_native_1.Text, { style: scoreStyles.status }, passed ? 'Passed' : 'Failed')));
91
+ react_1.default.createElement(react_native_1.Text, { style: scoreStyles.status }, passed ? `✓ ${strings.common.done || 'Passed'}` : `✗ ${strings.common.error || 'Failed'}`)));
63
92
  const scoreStyles = react_native_1.StyleSheet.create({
64
93
  container: {
65
94
  flex: 1,
@@ -1 +1 @@
1
- {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA0CD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAs1BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiDD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAi2BtD,CAAC;AA4OF,eAAe,aAAa,CAAC"}
@@ -69,18 +69,25 @@ const getDefaultChallenges = (strings) => [
69
69
  order: 4,
70
70
  },
71
71
  ];
72
- const getInstructionMap = (strings) => ({
73
- look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
74
- look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
75
- look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
76
- look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
77
- turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
78
- turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
79
- smile: { text: strings.liveness.instructions.smile || 'Smile' },
80
- blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
81
- open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
82
- stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
83
- });
72
+ const getInstructionMap = (strings) => {
73
+ if (!strings || !strings.liveness || !strings.liveness.instructions) {
74
+ return {};
75
+ }
76
+ return {
77
+ look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
78
+ look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
79
+ look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
80
+ look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
81
+ turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
82
+ turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
83
+ turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
84
+ turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
85
+ smile: { text: strings.liveness.instructions.smile || 'Smile' },
86
+ blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
87
+ open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
88
+ stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
89
+ };
90
+ };
84
91
  const VideoRecorder = ({ theme, language, duration, instructions, challenges: propChallenges, sessionId, smartMode = true, onComplete, onCancel, onFetchChallenges, }) => {
85
92
  (0, react_1.useEffect)(() => {
86
93
  if (language) {
@@ -145,9 +152,20 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
145
152
  (0, react_1.useEffect)(() => {
146
153
  const initChallenges = async () => {
147
154
  try {
155
+ if (language) {
156
+ (0, biometric_identity_sdk_core_1.setLanguage)(language);
157
+ }
148
158
  let challengeList;
149
159
  const currentStrings = (0, biometric_identity_sdk_core_1.getStrings)();
150
160
  const instructionMap = getInstructionMap(currentStrings);
161
+ biometric_identity_sdk_core_1.logger.info('Initializing challenges', {
162
+ language,
163
+ hasStrings: !!currentStrings,
164
+ hasInstructions: !!currentStrings?.liveness?.instructions,
165
+ instructionMapKeys: Object.keys(instructionMap),
166
+ turnLeftTranslation: instructionMap['turn_left']?.text,
167
+ turnRightTranslation: instructionMap['turn_right']?.text
168
+ });
151
169
  if (propChallenges && propChallenges.length > 0) {
152
170
  challengeList = propChallenges.map(challenge => ({
153
171
  ...challenge,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "clean": "rm -rf dist"
12
12
  },
13
13
  "peerDependencies": {
14
- "@hexar/biometric-identity-sdk-core": ">=1.0.16",
14
+ "@hexar/biometric-identity-sdk-core": ">=1.0.18",
15
15
  "react": ">=18.0.0",
16
16
  "react-native": ">=0.70.0",
17
17
  "react-native-permissions": ">=4.0.0",
@@ -29,18 +29,25 @@ import { useBiometricSDK } from '../hooks/useBiometricSDK';
29
29
  import { CameraCapture } from './CameraCapture';
30
30
  import { VideoRecorder, VideoRecordingResult } from './VideoRecorder';
31
31
 
32
- const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => ({
33
- look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
34
- look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
35
- look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
36
- look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
37
- turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
38
- turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
39
- smile: { text: strings.liveness.instructions.smile || 'Smile' },
40
- blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
41
- open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
42
- stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
43
- });
32
+ const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => {
33
+ if (!strings || !strings.liveness || !strings.liveness.instructions) {
34
+ return {};
35
+ }
36
+ return {
37
+ look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
38
+ look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
39
+ look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
40
+ look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
41
+ turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
42
+ turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
43
+ turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
44
+ turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
45
+ smile: { text: strings.liveness.instructions.smile || 'Smile' },
46
+ blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
47
+ open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
48
+ stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
49
+ };
50
+ };
44
51
  import { ValidationProgress } from './ValidationProgress';
45
52
  import { ResultScreen } from './ResultScreen';
46
53
  import { ErrorScreen } from './ErrorScreen';
@@ -102,6 +109,12 @@ export const BiometricIdentityFlow: React.FC<BiometricIdentityFlowProps> = ({
102
109
  useEffect(() => {
103
110
  if (language) {
104
111
  setLanguage(language);
112
+ const currentStrings = getStrings();
113
+ logger.info('Language set in BiometricIdentityFlow', {
114
+ language,
115
+ lookLeft: currentStrings.liveness?.instructions?.lookLeft,
116
+ lookRight: currentStrings.liveness?.instructions?.lookRight
117
+ });
105
118
  }
106
119
  }, [language]);
107
120
 
@@ -142,24 +155,64 @@ export const BiometricIdentityFlow: React.FC<BiometricIdentityFlowProps> = ({
142
155
  setCurrentChallenges(challenges);
143
156
  } catch (error) {
144
157
  logger.warn('Failed to fetch challenges, using defaults');
158
+ if (language) {
159
+ setLanguage(language);
160
+ }
161
+ const currentStrings = getStrings();
162
+ const instructionMap = getInstructionMap(currentStrings);
145
163
  const defaultChallenges = sdk.getDefaultChallenges();
146
- const instructionMap = getInstructionMap(strings);
147
- const translatedChallenges = defaultChallenges.map(challenge => ({
148
- ...challenge,
149
- instruction: instructionMap[challenge.action]?.text || challenge.instruction,
150
- icon: instructionMap[challenge.action]?.icon
151
- }));
164
+ logger.info('Translating default challenges', {
165
+ language,
166
+ actionCount: defaultChallenges.length,
167
+ instructionMapKeys: Object.keys(instructionMap),
168
+ lookLeftTranslation: instructionMap['look_left']?.text,
169
+ lookRightTranslation: instructionMap['look_right']?.text
170
+ });
171
+ const translatedChallenges = defaultChallenges.map(challenge => {
172
+ const translated = instructionMap[challenge.action];
173
+ const finalInstruction = translated?.text || challenge.instruction;
174
+ logger.info('Translating challenge', {
175
+ action: challenge.action,
176
+ original: challenge.instruction,
177
+ translated: finalInstruction
178
+ });
179
+ return {
180
+ ...challenge,
181
+ instruction: finalInstruction,
182
+ icon: translated?.icon
183
+ };
184
+ });
152
185
  setCurrentChallenges(translatedChallenges);
153
186
  }
154
187
  setIsLoadingChallenges(false);
155
188
  } else if (mode === 'video' && smartLivenessMode) {
189
+ if (language) {
190
+ setLanguage(language);
191
+ }
192
+ const currentStrings = getStrings();
193
+ const instructionMap = getInstructionMap(currentStrings);
156
194
  const defaultChallenges = sdk.getDefaultChallenges();
157
- const instructionMap = getInstructionMap(strings);
158
- const translatedChallenges = defaultChallenges.map(challenge => ({
159
- ...challenge,
160
- instruction: instructionMap[challenge.action]?.text || challenge.instruction,
161
- icon: instructionMap[challenge.action]?.icon
162
- }));
195
+ logger.info('Translating default challenges (offline mode)', {
196
+ language,
197
+ actionCount: defaultChallenges.length,
198
+ instructionMapKeys: Object.keys(instructionMap),
199
+ lookLeftTranslation: instructionMap['look_left']?.text,
200
+ lookRightTranslation: instructionMap['look_right']?.text
201
+ });
202
+ const translatedChallenges = defaultChallenges.map(challenge => {
203
+ const translated = instructionMap[challenge.action];
204
+ const finalInstruction = translated?.text || challenge.instruction;
205
+ logger.info('Translating challenge (offline)', {
206
+ action: challenge.action,
207
+ original: challenge.instruction,
208
+ translated: finalInstruction
209
+ });
210
+ return {
211
+ ...challenge,
212
+ instruction: finalInstruction,
213
+ icon: translated?.icon
214
+ };
215
+ });
163
216
  setCurrentChallenges(translatedChallenges);
164
217
  }
165
218
 
@@ -3,7 +3,7 @@
3
3
  * Shows validation result (pass/fail)
4
4
  */
5
5
 
6
- import React from 'react';
6
+ import React, { useEffect } from 'react';
7
7
  import {
8
8
  View,
9
9
  Text,
@@ -11,7 +11,7 @@ import {
11
11
  TouchableOpacity,
12
12
  ScrollView,
13
13
  } from 'react-native';
14
- import { ValidationResult, ThemeConfig, SupportedLanguage } from '@hexar/biometric-identity-sdk-core';
14
+ import { ValidationResult, ThemeConfig, SupportedLanguage, getStrings, setLanguage } from '@hexar/biometric-identity-sdk-core';
15
15
 
16
16
  export interface ResultScreenProps {
17
17
  result: ValidationResult;
@@ -26,6 +26,13 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
26
26
  language = 'en',
27
27
  onClose,
28
28
  }) => {
29
+ useEffect(() => {
30
+ if (language) {
31
+ setLanguage(language);
32
+ }
33
+ }, [language]);
34
+
35
+ const strings = getStrings();
29
36
  const isSuccess = result.isValidFaceMatch && result.isDocumentAuthentic;
30
37
  const successColor = theme?.successColor || '#10B981';
31
38
  const errorColor = theme?.errorColor || '#EF4444';
@@ -45,34 +52,28 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
45
52
 
46
53
  {/* Result Title */}
47
54
  <Text style={[styles.title, { color: theme?.textColor || '#000000' }]}>
48
- {isSuccess
49
- ? (language === 'es' ? '¡Verificación Exitosa!' : 'Verification Successful!')
50
- : (language === 'es' ? 'Verificación Fallida' : 'Verification Failed')}
55
+ {isSuccess ? strings.result.success.title : strings.result.failure.title}
51
56
  </Text>
52
57
 
53
58
  <Text style={[styles.subtitle, { color: theme?.secondaryTextColor || '#6B7280' }]}>
54
- {isSuccess
55
- ? (language === 'es'
56
- ? 'Tu identidad ha sido verificada correctamente'
57
- : 'Your identity has been verified successfully')
58
- : (language === 'es'
59
- ? 'No pudimos verificar tu identidad'
60
- : 'We were unable to verify your identity')}
59
+ {isSuccess ? strings.result.success.message : strings.result.failure.message}
61
60
  </Text>
62
61
 
63
62
  {/* Scores */}
64
63
  <View style={styles.scoresContainer}>
65
64
  <ScoreCard
66
- label={language === 'es' ? 'Coincidencia Facial' : 'Face Match'}
65
+ label={strings.validation.matchingFaces || 'Face Match'}
67
66
  score={result.matchScore}
68
67
  passed={result.isValidFaceMatch}
69
68
  theme={theme}
69
+ strings={strings}
70
70
  />
71
71
  <ScoreCard
72
- label={language === 'es' ? 'Presencia Vital' : 'Liveness'}
72
+ label={strings.liveness.title || 'Liveness'}
73
73
  score={result.livenessScore}
74
74
  passed={result.livenessScore >= 80}
75
75
  theme={theme}
76
+ strings={strings}
76
77
  />
77
78
  </View>
78
79
 
@@ -80,25 +81,25 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
80
81
  {result.extractedDocumentData && (
81
82
  <View style={styles.dataContainer}>
82
83
  <Text style={[styles.sectionTitle, { color: theme?.textColor || '#000000' }]}>
83
- {language === 'es' ? 'Datos Extraídos' : 'Extracted Data'}
84
+ {strings.result.success.verificationComplete || 'Extracted Data'}
84
85
  </Text>
85
86
  <DataRow
86
- label={language === 'es' ? 'Nombre' : 'Name'}
87
+ label="Nombre"
87
88
  value={`${result.extractedDocumentData.firstName} ${result.extractedDocumentData.lastName}`}
88
89
  theme={theme}
89
90
  />
90
91
  <DataRow
91
- label={language === 'es' ? 'Documento' : 'Document Number'}
92
+ label="Documento"
92
93
  value={result.extractedDocumentData.documentNumber}
93
94
  theme={theme}
94
95
  />
95
96
  <DataRow
96
- label={language === 'es' ? 'Fecha de Nacimiento' : 'Date of Birth'}
97
+ label="Fecha de Nacimiento"
97
98
  value={result.extractedDocumentData.dateOfBirth}
98
99
  theme={theme}
99
100
  />
100
101
  <DataRow
101
- label={language === 'es' ? 'Nacionalidad' : 'Nationality'}
102
+ label="Nacionalidad"
102
103
  value={result.extractedDocumentData.nationality}
103
104
  theme={theme}
104
105
  />
@@ -109,7 +110,7 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
109
110
  {result.warnings.length > 0 && (
110
111
  <View style={styles.warningsContainer}>
111
112
  <Text style={[styles.sectionTitle, { color: theme?.warningColor || '#F59E0B' }]}>
112
- {language === 'es' ? 'Advertencias' : 'Warnings'}
113
+ Advertencias
113
114
  </Text>
114
115
  {result.warnings.map((warning, index) => (
115
116
  <Text key={index} style={styles.warningText}>
@@ -130,7 +131,7 @@ export const ResultScreen: React.FC<ResultScreenProps> = ({
130
131
  onPress={onClose}
131
132
  >
132
133
  <Text style={styles.buttonText}>
133
- {language === 'es' ? 'Continuar' : 'Continue'}
134
+ {strings.common.continue}
134
135
  </Text>
135
136
  </TouchableOpacity>
136
137
  </View>
@@ -143,9 +144,10 @@ interface ScoreCardProps {
143
144
  score: number;
144
145
  passed: boolean;
145
146
  theme?: ThemeConfig;
147
+ strings: any;
146
148
  }
147
149
 
148
- const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme }) => (
150
+ const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme, strings }) => (
149
151
  <View style={scoreStyles.container}>
150
152
  <Text style={scoreStyles.label}>{label}</Text>
151
153
  <Text
@@ -156,7 +158,9 @@ const ScoreCard: React.FC<ScoreCardProps> = ({ label, score, passed, theme }) =>
156
158
  >
157
159
  {Math.round(score)}
158
160
  </Text>
159
- <Text style={scoreStyles.status}>{passed ? '✓ Passed' : '✗ Failed'}</Text>
161
+ <Text style={scoreStyles.status}>
162
+ {passed ? `✓ ${strings.common.done || 'Passed'}` : `✗ ${strings.common.error || 'Failed'}`}
163
+ </Text>
160
164
  </View>
161
165
  );
162
166
 
@@ -83,18 +83,25 @@ const getDefaultChallenges = (strings: any): ChallengeAction[] => [
83
83
  },
84
84
  ];
85
85
 
86
- const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => ({
87
- look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
88
- look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
89
- look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
90
- look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
91
- turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
92
- turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
93
- smile: { text: strings.liveness.instructions.smile || 'Smile' },
94
- blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
95
- open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
96
- stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
97
- });
86
+ const getInstructionMap = (strings: any): Record<string, { text: string; icon?: string }> => {
87
+ if (!strings || !strings.liveness || !strings.liveness.instructions) {
88
+ return {};
89
+ }
90
+ return {
91
+ look_left: { text: strings.liveness.instructions.lookLeft || 'Slowly turn your head LEFT' },
92
+ look_right: { text: strings.liveness.instructions.lookRight || 'Slowly turn your head RIGHT' },
93
+ look_up: { text: strings.liveness.instructions.lookUp || 'Look UP' },
94
+ look_down: { text: strings.liveness.instructions.lookDown || 'Look DOWN' },
95
+ turn_left: { text: strings.liveness.instructions.turnHeadLeft || strings.liveness.instructions.lookLeft || 'Turn your head LEFT' },
96
+ turn_right: { text: strings.liveness.instructions.turnHeadRight || strings.liveness.instructions.lookRight || 'Turn your head RIGHT' },
97
+ turn_head_left: { text: strings.liveness.instructions.turnHeadLeft || 'Turn your head LEFT' },
98
+ turn_head_right: { text: strings.liveness.instructions.turnHeadRight || 'Turn your head RIGHT' },
99
+ smile: { text: strings.liveness.instructions.smile || 'Smile' },
100
+ blink: { text: strings.liveness.instructions.blink || 'Blink your eyes naturally' },
101
+ open_mouth: { text: strings.liveness.instructions.openMouth || 'Open your mouth slightly' },
102
+ stay_still: { text: strings.liveness.instructions.stayStill || 'Look at the camera and follow the instructions' },
103
+ };
104
+ };
98
105
 
99
106
  export const VideoRecorder: React.FC<VideoRecorderProps> = ({
100
107
  theme,
@@ -184,9 +191,20 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
184
191
  useEffect(() => {
185
192
  const initChallenges = async () => {
186
193
  try {
194
+ if (language) {
195
+ setLanguage(language);
196
+ }
187
197
  let challengeList: ChallengeAction[];
188
198
  const currentStrings = getStrings();
189
199
  const instructionMap = getInstructionMap(currentStrings);
200
+ logger.info('Initializing challenges', {
201
+ language,
202
+ hasStrings: !!currentStrings,
203
+ hasInstructions: !!currentStrings?.liveness?.instructions,
204
+ instructionMapKeys: Object.keys(instructionMap),
205
+ turnLeftTranslation: instructionMap['turn_left']?.text,
206
+ turnRightTranslation: instructionMap['turn_right']?.text
207
+ });
190
208
 
191
209
  if (propChallenges && propChallenges.length > 0) {
192
210
  challengeList = propChallenges.map(challenge => ({