Browse Source

feat: implement phone number formatting, dynamic placeholders, and validation for QuestionPhone component with multi-language support

master
mortezaei 5 days ago
parent
commit
879f864575
  1. 101
      src/components/Componentes/question-phone.test.tsx
  2. 174
      src/components/Componentes/question-phone.tsx
  3. 40
      src/components/Componentes/question-snap-list.tsx
  4. 10
      src/components/Componentes/question-viewport-coordinator.ts
  5. 3
      src/translations/locales/ar.json
  6. 3
      src/translations/locales/az.json
  7. 3
      src/translations/locales/bn.json
  8. 3
      src/translations/locales/da.json
  9. 3
      src/translations/locales/de.json
  10. 3
      src/translations/locales/en.json
  11. 3
      src/translations/locales/es.json
  12. 3
      src/translations/locales/fa.json
  13. 3
      src/translations/locales/fr.json
  14. 3
      src/translations/locales/gu.json
  15. 3
      src/translations/locales/ha.json
  16. 3
      src/translations/locales/he.json
  17. 3
      src/translations/locales/hi.json
  18. 3
      src/translations/locales/id.json
  19. 3
      src/translations/locales/ks.json
  20. 3
      src/translations/locales/pt.json
  21. 3
      src/translations/locales/ru.json
  22. 3
      src/translations/locales/sw.json
  23. 3
      src/translations/locales/tg.json
  24. 3
      src/translations/locales/tr.json
  25. 3
      src/translations/locales/ul.json
  26. 3
      src/translations/locales/ur.json
  27. 3
      src/translations/locales/uz.json
  28. 3
      src/translations/locales/zh.json

101
src/components/Componentes/question-phone.test.tsx

@ -30,6 +30,8 @@ vi.mock("@/translations/provider", () => ({
"Select country": "Select country",
Close: "Close",
Confirm: "Confirm",
"Enter a valid phone number with country code.":
"Enter a valid phone number with country code.",
},
}),
}));
@ -207,7 +209,7 @@ describe("QuestionPhone IP country detection and shimmer", () => {
expect(container.querySelectorAll(".shimmer-bg").length).toBe(0);
expect(screen.getByText("+1")).toBeDefined();
expect(screen.getByDisplayValue("2025550143")).toBeDefined();
expect(screen.getByDisplayValue("202-555-0143")).toBeDefined();
expect(httpMocks.get).not.toHaveBeenCalled();
});
@ -219,10 +221,10 @@ describe("QuestionPhone IP country detection and shimmer", () => {
httpMocks.get.mockReturnValue(regionPromise);
render(<QuestionPhone question={phoneQuestion1} />);
render(<QuestionPhone question={phoneQuestion1} countryCode="+44" />);
const input = screen.getByRole("textbox");
fireEvent.change(input, { target: { value: "123456" } });
fireEvent.change(input, { target: { value: "7400123456" } });
await act(async () => {
resolveRegion({
@ -233,6 +235,97 @@ describe("QuestionPhone IP country detection and shimmer", () => {
});
});
expect(screen.getByDisplayValue("123456")).toBeDefined();
expect(screen.getByDisplayValue("7400 123456")).toBeDefined();
});
it("formats Iranian phone numbers as 3-3-4 Telegram style as digits are typed", async () => {
httpMocks.get.mockResolvedValue({
data: { country: "Iran", country_code: "IR" },
});
render(<QuestionPhone question={phoneQuestion1} countryCode="+98" />);
const input = screen.getByRole("textbox") as HTMLInputElement;
// Type 3 digits
fireEvent.change(input, { target: { value: "912" } });
expect(input.value).toBe("912");
// Type 6 digits
fireEvent.change(input, { target: { value: "912345" } });
expect(input.value).toBe("912 345");
// Type 10 digits
fireEvent.change(input, { target: { value: "9123456789" } });
expect(input.value).toBe("912 345 6789");
});
it("shows dynamic placeholder based on resolved country code", async () => {
let resolveRegion!: (value: unknown) => void;
const regionPromise = new Promise((resolve) => {
resolveRegion = resolve;
});
httpMocks.get.mockReturnValue(regionPromise);
render(<QuestionPhone question={phoneQuestion1} countryCode="+44" />);
const input = screen.getByRole("textbox") as HTMLInputElement;
expect(input.placeholder).toBe("7400 123456");
await act(async () => {
resolveRegion({
data: {
country: "Iran",
country_code: "IR",
},
});
});
await waitFor(() => {
expect(input.placeholder).toBe("912 345 6789");
});
});
it("does not show validation error on active typing, only shows error on blur if incomplete", async () => {
httpMocks.get.mockResolvedValue({
data: { country: "Iran", country_code: "IR" },
});
const { container } = render(
<QuestionPhone question={phoneQuestion1} countryCode="+98" />,
);
const input = screen.getByRole("textbox") as HTMLInputElement;
// Focus and start typing incomplete number
fireEvent.focus(input);
fireEvent.change(input, { target: { value: "912" } });
// No error while focused and typing
expect(
screen.queryByText("Enter a valid phone number with country code."),
).toBeNull();
expect(container.querySelector(".border-\\[\\#F2465F\\]")).toBeNull();
// Blur input without completing
fireEvent.blur(input);
// Error appears after blur
expect(
screen.getByText("Enter a valid phone number with country code."),
).toBeDefined();
expect(container.querySelector(".border-\\[\\#F2465F\\]")).not.toBeNull();
// Focus back and complete valid number
fireEvent.focus(input);
fireEvent.change(input, { target: { value: "9123456789" } });
fireEvent.blur(input);
// Error is gone
expect(
screen.queryByText("Enter a valid phone number with country code."),
).toBeNull();
expect(container.querySelector(".border-\\[\\#F2465F\\]")).toBeNull();
});
});

174
src/components/Componentes/question-phone.tsx

@ -1,6 +1,10 @@
"use client";
import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber";
import {
AsYouTypeFormatter,
PhoneNumberFormat,
PhoneNumberUtil,
} from "google-libphonenumber";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import type { MarriagePhoneFieldValue } from "@/hooks/marriage/types";
@ -264,10 +268,11 @@ function readPhoneValue(value: unknown, fallbackCode: string): PhoneValueParts {
function getMaxLengthForCountry(codeValue: string): number {
try {
const cleanCode = codeValue.replace(/[^\d]/g, "");
if (!cleanCode) return 15;
if (!cleanCode) return 18;
if (cleanCode === "98") return 12; // 3 + 1 + 3 + 1 + 4 = 12 chars
const countryCode = Number(cleanCode);
const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
if (!regionCode || regionCode === "ZZ") return 15;
if (!regionCode || regionCode === "ZZ") return 18;
const exampleMobile = phoneUtil.getExampleNumberForType(
regionCode,
@ -283,9 +288,93 @@ function getMaxLengthForCountry(codeValue: string): number {
: 0;
const baseLen = Math.max(lenMobile, lenGeneral, 8);
return baseLen + 1;
return baseLen + 6;
} catch {
return 15;
return 18;
}
}
function getCountryPlaceholder(
codeValue: string,
defaultPlaceholder?: string,
): string {
try {
const cleanCode = codeValue.replace(/[^\d]/g, "");
if (!cleanCode) {
return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
}
if (cleanCode === "98") {
return "912 345 6789";
}
const countryCode = Number(cleanCode);
const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
if (!regionCode || regionCode === "ZZ") {
return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
}
const example =
phoneUtil.getExampleNumberForType(
regionCode,
1, // MOBILE
) || phoneUtil.getExampleNumber(regionCode);
if (!example) {
return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
}
const intl = phoneUtil.format(example, PhoneNumberFormat.INTERNATIONAL);
const prefix = `+${cleanCode} `;
if (intl.startsWith(prefix)) {
return intl.slice(prefix.length);
}
return String(example.getNationalNumber());
} catch {
return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
}
}
function formatPhoneNumberAsYouType(
rawInput: string,
codeValue: string,
): string {
const cleanCode = codeValue.replace(/[^\d]/g, "");
const rawDigits = rawInput.replace(/\D/g, "");
if (!rawDigits) return "";
// Special telegram-style handling for Iran (+98): 3 - 3 - 4 (e.g. 912 345 6789)
if (cleanCode === "98") {
const digits = rawDigits.startsWith("0") ? rawDigits.slice(1) : rawDigits;
const max10 = digits.slice(0, 10);
if (max10.length <= 3) return max10;
if (max10.length <= 6) return `${max10.slice(0, 3)} ${max10.slice(3)}`;
return `${max10.slice(0, 3)} ${max10.slice(3, 6)} ${max10.slice(6)}`;
}
const countryCode = Number(cleanCode);
const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
if (!regionCode || regionCode === "ZZ") {
return rawDigits;
}
try {
const formatter = new AsYouTypeFormatter(regionCode);
let formatted = "";
const fullNumber = `+${cleanCode}${rawDigits}`;
for (const char of fullNumber) {
formatted = formatter.inputDigit(char);
}
const codePrefix = `+${cleanCode} `;
if (formatted.startsWith(codePrefix)) {
return formatted.slice(codePrefix.length);
}
if (formatted.startsWith(`+${cleanCode}`)) {
return formatted.slice(`+${cleanCode}`.length).trim();
}
return rawDigits;
} catch {
return rawDigits;
}
}
@ -433,13 +522,23 @@ export function QuestionPhone({
}, [value, defaultCodeValue]);
const initialPhone = useMemo(() => {
return readPhoneValue(value, defaultCodeValue).phoneValue;
}, [value, defaultCodeValue]);
const rawPhone = readPhoneValue(value, defaultCodeValue).phoneValue;
return formatPhoneNumberAsYouType(rawPhone, initialCode);
}, [value, defaultCodeValue, initialCode]);
const [codeValue, setCodeValue] = useState(initialCode);
const [phoneValue, setPhoneValue] = useState(initialPhone);
const [isFocused, setIsFocused] = useState(false);
const [isTouched, setIsTouched] = useState(false);
const lastCommittedValueRef = useRef(value);
const dynamicPlaceholder = useMemo(() => {
return getCountryPlaceholder(
codeValue || defaultCodeValue,
question.extras?.placeHolder,
);
}, [codeValue, defaultCodeValue, question.extras?.placeHolder]);
const [isOpen, setIsOpen] = useState(false);
const [isClosing, setIsClosing] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
@ -471,6 +570,11 @@ export function QuestionPhone({
const onGeoCodeResolved = (resolvedCode: string) => {
if (!isMounted || userInteractedRef.current) return;
setCodeValue(resolvedCode);
if (phoneValue) {
setPhoneValue((prev) =>
formatPhoneNumberAsYouType(prev, resolvedCode),
);
}
setIsResolvingCountry(false);
};
@ -480,7 +584,13 @@ export function QuestionPhone({
.then((resolvedCode) => {
if (!isMounted) return;
if (!userInteractedRef.current) {
setCodeValue(resolvedCode || defaultCodeValue);
const finalCode = resolvedCode || defaultCodeValue;
setCodeValue(finalCode);
if (phoneValue) {
setPhoneValue((prev) =>
formatPhoneNumberAsYouType(prev, finalCode),
);
}
}
setIsResolvingCountry(false);
})
@ -496,10 +606,12 @@ export function QuestionPhone({
isMounted = false;
geoListeners.delete(onGeoCodeResolved);
};
}, [hasExplicitValue, isResolvingCountry, defaultCodeValue]);
}, [hasExplicitValue, isResolvingCountry, defaultCodeValue, phoneValue]);
const normalizedPhoneState = getNormalizedPhoneValue(codeValue, phoneValue);
const showInvalidState =
isTouched &&
!isFocused &&
codeValue.trim().length > 0 &&
phoneValue.trim().length > 0 &&
!normalizedPhoneState.isValid;
@ -631,13 +743,19 @@ export function QuestionPhone({
if (explicit) {
setIsResolvingCountry(false);
setCodeValue(nextValue.codeValue);
const maxLen = getMaxLengthForCountry(nextValue.codeValue);
setPhoneValue(nextValue.phoneValue.slice(0, maxLen));
const formatted = formatPhoneNumberAsYouType(
nextValue.phoneValue,
nextValue.codeValue,
);
setPhoneValue(formatted);
} else if (value !== null && value !== undefined) {
const resolvedCode = cachedCode || defaultCodeValue;
setCodeValue(resolvedCode);
const maxLen = getMaxLengthForCountry(resolvedCode);
setPhoneValue(nextValue.phoneValue.slice(0, maxLen));
const formatted = formatPhoneNumberAsYouType(
nextValue.phoneValue,
resolvedCode,
);
setPhoneValue(formatted);
}
lastCommittedValueRef.current = value;
@ -664,12 +782,11 @@ export function QuestionPhone({
userInteractedRef.current = true;
setIsResolvingCountry(false);
setManuallySelectedGeoCode(selectedCode);
const maxLen = getMaxLengthForCountry(selectedCode);
const truncatedPhone = phoneValue.slice(0, maxLen);
const reformatted = formatPhoneNumberAsYouType(phoneValue, selectedCode);
setCodeValue(selectedCode);
setPhoneValue(truncatedPhone);
updateStoredValue(selectedCode, truncatedPhone);
setPhoneValue(reformatted);
updateStoredValue(selectedCode, reformatted);
closeSheet();
};
@ -768,19 +885,27 @@ export function QuestionPhone({
data-bwignore="true"
data-form-type="other"
disabled={disabled}
placeholder={question.extras.placeHolder?.replace(/^\+\d+\s*/, "")}
placeholder={dynamicPlaceholder}
value={phoneValue}
maxLength={getMaxLengthForCountry(codeValue)}
onFocus={() => {
setIsFocused(true);
}}
onBlur={() => {
setIsFocused(false);
setIsTouched(true);
}}
onChange={(event) => {
userInteractedRef.current = true;
setIsResolvingCountry(false);
setManuallySelectedGeoCode(codeValue);
const nextPhoneValue = sanitizePhoneNumber(event.target.value);
const maxLen = getMaxLengthForCountry(codeValue);
const truncatedPhone = nextPhoneValue.slice(0, maxLen);
const formatted = formatPhoneNumberAsYouType(
event.target.value,
codeValue,
);
setPhoneValue(truncatedPhone);
updateStoredValue(codeValue, truncatedPhone);
setPhoneValue(formatted);
updateStoredValue(codeValue, formatted);
}}
dir="ltr"
className="h-full w-full border-0 bg-transparent p-0 text-left text-[15px] font-medium leading-none text-[#181818] tabular-nums outline-none placeholder:text-[#98A2B3]"
@ -789,7 +914,8 @@ export function QuestionPhone({
</div>
{showInvalidState ? (
<span className="block group-10 font-semibold text-[#F2465F]">
Enter a valid phone number with country code.
{t["Enter a valid phone number with country code."] ||
"Enter a valid phone number with country code."}
</span>
) : null}

40
src/components/Componentes/question-snap-list.tsx

@ -14,20 +14,20 @@ import {
useQuestionViewportCoordinator,
} from "./question-viewport-coordinator";
const WHEEL_GESTURE_IDLE_MS = 320;
const BACKGROUND_DRAG_SLOP = 10;
const OPTION_CARD_DRAG_SLOP = 16;
const FAST_FLICK_MIN_DISTANCE = 36;
const DRAG_COMMIT_RATIO = 0.3;
const DRAG_FLICK_VELOCITY = 0.4;
const SNAP_ANIMATION_MS = 340;
const SNAP_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
const RUBBER_BAND_RESISTANCE = 0.4;
const WHEEL_GESTURE_IDLE_MS = 280;
const BACKGROUND_DRAG_SLOP = 8;
const OPTION_CARD_DRAG_SLOP = 14;
const TEXT_INPUT_DRAG_SLOP = 16;
const FAST_FLICK_MIN_DISTANCE = 24;
const DRAG_COMMIT_RATIO = 0.15;
const DRAG_FLICK_VELOCITY = 0.22;
const SNAP_ANIMATION_MS = 240;
const SNAP_EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
const RUBBER_BAND_RESISTANCE = 0.35;
/**
* Text inputs and direct embedded controls that MUST be completely isolated
* from the snap/drag gesture recognizer so first-tap focus, virtual keyboard,
* and caret manipulation work 100% reliably.
* Text inputs and direct embedded controls that MUST allow clean 1st-tap focus
* while still permitting intentional vertical swipe when dragged beyond slop.
*/
const TEXT_INPUT_SELECTOR = [
'input:not([type="radio"]):not([type="checkbox"])',
@ -58,6 +58,7 @@ const OPTION_CARD_SELECTOR = [
type SnapDragState = {
pointerDown: boolean;
hardIgnored: boolean;
isTextInput: boolean;
isOptionCard: boolean;
engaged: boolean;
didDrag: boolean;
@ -113,6 +114,7 @@ export function QuestionSnapList({
const dragRef = useRef<SnapDragState>({
pointerDown: false,
hardIgnored: false,
isTextInput: false,
isOptionCard: false,
engaged: false,
didDrag: false,
@ -397,14 +399,19 @@ export function QuestionSnapList({
const movedPastFlick = Math.abs(drag.offset) >= FAST_FLICK_MIN_DISTANCE;
let direction: 0 | 1 | -1 = 0;
let reason = "none";
if (!cancelled && (draggedFar || flicked || movedPastFlick)) {
if ((drag.offset > 0 || drag.velocity > 0) && canNext) {
direction = 1;
reason = flicked ? "flick_next" : (draggedFar ? "drag_far_next" : "moved_past_flick_next");
} else if ((drag.offset < 0 || drag.velocity < 0) && canPrev) {
direction = -1;
reason = flicked ? "flick_prev" : (draggedFar ? "drag_far_prev" : "moved_past_flick_prev");
}
}
console.log(`[Snap] FinishDrag -> dir: ${direction} (${reason}) | offset: ${drag.offset.toFixed(0)}px | vel: ${drag.velocity.toFixed(2)} | threshold: ${(drag.height * DRAG_COMMIT_RATIO).toFixed(0)}px`);
if (direction === 0) {
snapPanelsTo(0);
return;
@ -491,6 +498,8 @@ export function QuestionSnapList({
drag.startTime = performance.now();
drag.lastMoveTime = drag.startTime;
touchStartYRef.current = drag.startY;
console.log(`[Snap] TouchStart at (${drag.startX.toFixed(0)}, ${drag.startY.toFixed(0)}) on <${target?.tagName?.toLowerCase()}> (isOption: ${isOptionCard})`);
};
const onTouchMove = (event: TouchEvent) => {
@ -526,7 +535,7 @@ export function QuestionSnapList({
}
// Must be primarily vertical gesture (avoid hijacking horizontal swipe / scroll)
if (absY < absX * 1.1) {
if (absY < absX * 0.9) {
return;
}
@ -535,8 +544,13 @@ export function QuestionSnapList({
drag.didDrag = true;
suppressNextClickRef.current = true;
if (document.activeElement instanceof HTMLElement) {
document.activeElement.blur();
}
const elapsed = Math.max(1, now - drag.startTime);
drag.velocity = (drag.startY - currentY) / elapsed;
console.log(`[Snap] Drag Engaged: deltaY=${deltaY.toFixed(0)}px, slop=${slop}px`);
}
// Non-passive preventDefault stops native pan and guarantees gesture ownership

10
src/components/Componentes/question-viewport-coordinator.ts

@ -94,7 +94,15 @@ function setLift(nextLift: number) {
function getKeyboardTop() {
if (!keyboardVisible || !activeQuestionInput) return null;
const flutterTop = window.innerHeight - keyboardHeight;
// When keyboard is opening/visible, use the projected full keyboard height
// (either current incoming height or last known full keyboard height)
// so the CSS transition animates in ONE single smooth, continuous curve concurrent with OS keyboard.
const targetHeight = Math.max(
keyboardHeight,
lastKeyboardHeight > 200 ? lastKeyboardHeight : 320,
);
const flutterTop = window.innerHeight - targetHeight;
const viewport = window.visualViewport;
const viewportBottom = viewport
? viewport.offsetTop + viewport.height

3
src/translations/locales/ar.json

@ -785,5 +785,6 @@
"Voice Call": "مكالمة صوتية",
"Video Call": "مكالمة فيديو",
"No public information is available to display.": "لا توجد معلومات عامة متاحة للعرض.",
"General Information & Personal Details": "المعلومات العامة والتفاصيل الشخصية"
"General Information & Personal Details": "المعلومات العامة والتفاصيل الشخصية",
"Enter a valid phone number with country code.": "يرجى إدخال رقم هاتف صالح مع رمز الدولة."
}

3
src/translations/locales/az.json

@ -785,5 +785,6 @@
"Voice Call": "Səsli Zəng",
"Video Call": "Video Zəng",
"No public information is available to display.": "Göstəriləcək heç bir ictimai məlumat qeyd edilməyib.",
"General Information & Personal Details": "Ümumi məlumat və fərdi xüsusiyyətlər"
"General Information & Personal Details": "Ümumi məlumat və fərdi xüsusiyyətlər",
"Enter a valid phone number with country code.": "Zəhmət olmasa ölkə kodu ilə birlikdə etibarlı bir telefon nömrəsi daxil edin."
}

3
src/translations/locales/bn.json

@ -785,5 +785,6 @@
"Voice Call": "ভয়েস কল",
"Video Call": "ভিডিও কল",
"No public information is available to display.": "প্রদর্শনের জন্য কোনও সর্বজনীন তথ্য পাওয়া যায়নি।",
"General Information & Personal Details": "সাধারণ তথ্য এবং ব্যক্তিগত বিবরণ"
"General Information & Personal Details": "সাধারণ তথ্য এবং ব্যক্তিগত বিবরণ",
"Enter a valid phone number with country code.": "দেশের কোড সহ একটি বৈধ ফোন নম্বর লিখুন।"
}

3
src/translations/locales/da.json

@ -785,5 +785,6 @@
"Voice Call": "Taleopkald",
"Video Call": "Videoopkald",
"No public information is available to display.": "Ingen offentlige oplysninger er tilgængelige.",
"General Information & Personal Details": "Generelle oplysninger og personlige detaljer"
"General Information & Personal Details": "Generelle oplysninger og personlige detaljer",
"Enter a valid phone number with country code.": "Indtast et gyldigt telefonnummer med landekode."
}

3
src/translations/locales/de.json

@ -785,5 +785,6 @@
"Voice Call": "Sprachanruf",
"Video Call": "Videoanruf",
"No public information is available to display.": "Keine öffentlichen Informationen zur Anzeige verfügbar.",
"General Information & Personal Details": "Allgemeine Informationen und persönliche Details"
"General Information & Personal Details": "Allgemeine Informationen und persönliche Details",
"Enter a valid phone number with country code.": "Geben Sie eine gültige Telefonnummer mit Ländercode ein."
}

3
src/translations/locales/en.json

@ -833,5 +833,6 @@
"Text Message": "Text Message",
"Video Call": "Video Call",
"No public information is available to display.": "No public information is available to display.",
"General Information & Personal Details": "General Information & Personal Details"
"General Information & Personal Details": "General Information & Personal Details",
"Enter a valid phone number with country code.": "Enter a valid phone number with country code."
}

3
src/translations/locales/es.json

@ -785,5 +785,6 @@
"Voice Call": "Llamada de Voz",
"Video Call": "Llamada de Video",
"No public information is available to display.": "No hay información pública disponible para mostrar.",
"General Information & Personal Details": "Información general y detalles personales"
"General Information & Personal Details": "Información general y detalles personales",
"Enter a valid phone number with country code.": "Introduce un número de teléfono válido con código de país."
}

3
src/translations/locales/fa.json

@ -833,5 +833,6 @@
"Text Message": "پیام متنی",
"Video Call": "تماس تصویری",
"No public information is available to display.": "اطلاعات عمومی قابل نمایشی ثبت نشده است.",
"General Information & Personal Details": "اطلاعات عمومی و مشخصات فردی"
"General Information & Personal Details": "اطلاعات عمومی و مشخصات فردی",
"Enter a valid phone number with country code.": "لطفاً یک شماره تماس معتبر به همراه کد کشور وارد کنید."
}

3
src/translations/locales/fr.json

@ -785,5 +785,6 @@
"Voice Call": "Appel vocal",
"Video Call": "Appel vidéo",
"No public information is available to display.": "Aucune information publique disponible à afficher.",
"General Information & Personal Details": "Informations générales et détails personnels"
"General Information & Personal Details": "Informations générales et détails personnels",
"Enter a valid phone number with country code.": "Entrez un numéro de téléphone valide avec l'indicatif du pays."
}

3
src/translations/locales/gu.json

@ -785,5 +785,6 @@
"Voice Call": "વૉઇસ કૉલ",
"Video Call": "વિડિયો કૉલ",
"No public information is available to display.": "પ્રદર્શિત કરવા માટે કોઈ જાહેર માહિતી ઉપલબ્ધ નથી.",
"General Information & Personal Details": "સામાન્ય માહિતી અને વ્યક્તિગત વિગતો"
"General Information & Personal Details": "સામાન્ય માહિતી અને વ્યક્તિગત વિગતો",
"Enter a valid phone number with country code.": "દેશના કોડ સાથે માન્ય ફોન નંબર દાખલ કરો."
}

3
src/translations/locales/ha.json

@ -785,5 +785,6 @@
"Voice Call": "Kiran Murya",
"Video Call": "Kiran Bidiyo",
"No public information is available to display.": "Babu bayanan jama'a da za a iya nunawa.",
"General Information & Personal Details": "Bayanai na gama-gari da cikakkun bayanan sirri"
"General Information & Personal Details": "Bayanai na gama-gari da cikakkun bayanan sirri",
"Enter a valid phone number with country code.": "Shigar da lambar waya mai aiki tare da lambar ƙasa."
}

3
src/translations/locales/he.json

@ -322,5 +322,6 @@
"Voice Call": "Voice Call",
"Video Call": "Video Call",
"No public information is available to display.": "אין מידע ציבורי זמין להצגה.",
"General Information & Personal Details": "מידע כללי ופרטים אישיים"
"General Information & Personal Details": "מידע כללי ופרטים אישיים",
"Enter a valid phone number with country code.": "הזן מספר טלפון חוקי עם קידומת מדינה."
}

3
src/translations/locales/hi.json

@ -785,5 +785,6 @@
"Voice Call": "वॉइस कॉल",
"Video Call": "वीडियो कॉल",
"No public information is available to display.": "प्रदर्शित करने के लिए कोई सार्वजनिक जानकारी उपलब्ध नहीं है।",
"General Information & Personal Details": "सामान्य जानकारी और व्यक्तिगत विवरण"
"General Information & Personal Details": "सामान्य जानकारी और व्यक्तिगत विवरण",
"Enter a valid phone number with country code.": "देश कोड के साथ एक मान्य फ़ोन नंबर दर्ज करें।"
}

3
src/translations/locales/id.json

@ -322,5 +322,6 @@
"Voice Call": "Panggilan Suara",
"Video Call": "Panggilan Video",
"No public information is available to display.": "Tidak ada informasi publik yang tersedia untuk ditampilkan.",
"General Information & Personal Details": "Informasi Umum & Rincian Pribadi"
"General Information & Personal Details": "Informasi Umum & Rincian Pribadi",
"Enter a valid phone number with country code.": "Masukkan nomor telepon yang valid dengan kode negara."
}

3
src/translations/locales/ks.json

@ -322,5 +322,6 @@
"Voice Call": "آوازی کال",
"Video Call": "ویڈیو کال",
"No public information is available to display.": "ڈسپلے کرنہٕ خٲطرٕ کانہہ عام معلومات دٔستیاب چُھنہٕ۔",
"General Information & Personal Details": "عام معلومات تہٕ ذٲتی تفصیٖل"
"General Information & Personal Details": "عام معلومات تہٕ ذٲتی تفصیٖل",
"Enter a valid phone number with country code.": "ملک کے کوڈ کے ساتھ ایک درست فون نمبر درج کریں۔"
}

3
src/translations/locales/pt.json

@ -322,5 +322,6 @@
"Voice Call": "Chamada de Voz",
"Video Call": "Chamada de Vídeo",
"No public information is available to display.": "Nenhuma informação pública disponível para exibição.",
"General Information & Personal Details": "Informações Gerais e Detalhes Pessoais"
"General Information & Personal Details": "Informações Gerais e Detalhes Pessoais",
"Enter a valid phone number with country code.": "Introduza um número de telefone válido com o código do país."
}

3
src/translations/locales/ru.json

@ -789,5 +789,6 @@
"Voice Call": "Голосовой звонок",
"Video Call": "Видеозвонок",
"No public information is available to display.": "Нет общедоступной информации для отображения.",
"General Information & Personal Details": "Общая информация и личные данные"
"General Information & Personal Details": "Общая информация и личные данные",
"Enter a valid phone number with country code.": "Введите действительный номер телефона с кодом страны."
}

3
src/translations/locales/sw.json

@ -322,5 +322,6 @@
"Voice Call": "Simu ya Sauti",
"Video Call": "Simu ya Video",
"No public information is available to display.": "Hakuna taarifa za umma zinazoweza kuonyeshwa.",
"General Information & Personal Details": "Taarifa za Jumla na Maelezo Binafsi"
"General Information & Personal Details": "Taarifa za Jumla na Maelezo Binafsi",
"Enter a valid phone number with country code.": "Weka nambari halali ya simu yenye msimbo wa nchi."
}

3
src/translations/locales/tg.json

@ -322,5 +322,6 @@
"Voice Call": "Зангҳои овозӣ",
"Video Call": "Зангҳои видеоӣ",
"No public information is available to display.": "Маълумоти умумии дастрас барои намоиш нест.",
"General Information & Personal Details": "Маълумоти умумӣ ва мушаххасоти инфиродӣ"
"General Information & Personal Details": "Маълумоти умумӣ ва мушаххасоти инфиродӣ",
"Enter a valid phone number with country code.": "Рақами телефони дурустро бо рамзи кишвар ворид кунед."
}

3
src/translations/locales/tr.json

@ -322,5 +322,6 @@
"Voice Call": "Sesli Arama",
"Video Call": "Görüntülü Arama",
"No public information is available to display.": "Görüntülenecek genel bilgi bulunmamaktadır.",
"General Information & Personal Details": "Genel Bilgiler ve Kişisel Detaylar"
"General Information & Personal Details": "Genel Bilgiler ve Kişisel Detaylar",
"Enter a valid phone number with country code.": "Ülke kodu ile geçerli bir telefon numarası girin."
}

3
src/translations/locales/ul.json

@ -322,5 +322,6 @@
"Voice Call": "Voice Call",
"Video Call": "Video Call",
"No public information is available to display.": "Görüntülenecek genel bilgi bulunmamaktadır.",
"General Information & Personal Details": "Genel Bilgiler ve Kişisel Detaylar"
"General Information & Personal Details": "Genel Bilgiler ve Kişisel Detaylar",
"Enter a valid phone number with country code.": "ملک کے کوڈ کے ساتھ ایک درست فون نمبر درج کریں۔"
}

3
src/translations/locales/ur.json

@ -322,5 +322,6 @@
"Voice Call": "وائس کال",
"Video Call": "ویڈیو کال",
"No public information is available to display.": "دکھانے کے لیے کوئی عوامی معلومات دستیاب نہیں ہے۔",
"General Information & Personal Details": "عام معلومات اور ذاتی تفصیلات"
"General Information & Personal Details": "عام معلومات اور ذاتی تفصیلات",
"Enter a valid phone number with country code.": "ملک کے کوڈ کے ساتھ ایک درست فون نمبر درج کریں۔"
}

3
src/translations/locales/uz.json

@ -322,5 +322,6 @@
"Voice Call": "Овозли қўнғироқ",
"Video Call": "Видео қўнғироқ",
"No public information is available to display.": "Ko'rsatish uchun umumiy ma'lumot mavjud emas.",
"General Information & Personal Details": "Umumiy ma'lumotlar va shaxsiy tafsilotlar"
"General Information & Personal Details": "Umumiy ma'lumotlar va shaxsiy tafsilotlar",
"Enter a valid phone number with country code.": "Mamlakat kodi bilan to'g'ri telefon raqamini kiriting."
}

3
src/translations/locales/zh.json

@ -785,5 +785,6 @@
"Voice Call": "语音通话",
"Video Call": "视频通话",
"No public information is available to display.": "没有可显示的公开信息。",
"General Information & Personal Details": "一般信息和个人资料"
"General Information & Personal Details": "一般信息和个人资料",
"Enter a valid phone number with country code.": "请输入包含国家代码的有效电话号码。"
}
Loading…
Cancel
Save