diff --git a/package-lock.json b/package-lock.json index 5fea326e..d3a372ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-intl": "^6.6.2", "styled-components": "^6.1.8", "webextension-polyfill": "^0.10.0" }, @@ -759,6 +760,132 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@formatjs/ecma402-abstract": { + "version": "1.18.2", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz", + "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==", + "dependencies": { + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/ecma402-abstract/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/fast-memoize": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz", + "integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/fast-memoize/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.7.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.7.6.tgz", + "integrity": "sha512-etVau26po9+eewJKYoiBKP6743I1br0/Ie00Pb/S/PtmYfmjTcOn2YCh2yNkSZI12h6Rg+BOgQYborXk46BvkA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-skeleton-parser": "1.8.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.0.tgz", + "integrity": "sha512-QWLAYvM0n8hv7Nq5BEs4LKIjevpVpbGLAJgOaYzg9wABEoX1j0JO1q2/jVkO6CVlq0dbsxZCngS5aXbysYueqA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/intl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.0.tgz", + "integrity": "sha512-X3xT9guVkKDS86EKV80lS0KxoazUglkJTGZO66sKY7otgl0VeStPA8B3u8UkKT47PexVV98fUzjpkchYmbe9nw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "@formatjs/intl-displaynames": "6.6.6", + "@formatjs/intl-listformat": "7.5.5", + "intl-messageformat": "10.5.11", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.6.tgz", + "integrity": "sha512-Dg5URSjx0uzF8VZXtHb6KYZ6LFEEhCbAbKoYChYHEOnMFTw/ZU3jIo/NrujzQD2EfKPgQzIq73LOUvW6Z/LpFA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-displaynames/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/intl-listformat": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.5.tgz", + "integrity": "sha512-XoI52qrU6aBGJC9KJddqnacuBbPlb/bXFN+lIFVFhQ1RnFHpzuFrlFdjD9am2O7ZSYsyqzYRpkVcXeT1GHkwDQ==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/intl-localematcher": "0.5.4", + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-listformat/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz", + "integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==", + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@formatjs/intl-localematcher/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, + "node_modules/@formatjs/intl/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "dev": true, @@ -1007,6 +1134,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", + "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "dev": true, @@ -1032,12 +1168,10 @@ }, "node_modules/@types/prop-types": { "version": "15.7.11", - "dev": true, "license": "MIT" }, "node_modules/@types/react": { "version": "18.2.45", - "dev": true, "license": "MIT", "dependencies": { "@types/prop-types": "*", @@ -1055,7 +1189,6 @@ }, "node_modules/@types/scheduler": { "version": "0.16.8", - "dev": true, "license": "MIT" }, "node_modules/@types/semver": { @@ -2222,7 +2355,6 @@ }, "node_modules/csstype": { "version": "3.1.3", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -3470,6 +3602,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/htmlparser2": { "version": "8.0.2", "dev": true, @@ -3575,6 +3715,22 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "10.5.11", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.11.tgz", + "integrity": "sha512-eYq5fkFBVxc7GIFDzpFQkDOZgNayNTQn4Oufe8jw6YY6OHVw70/4pA3FyCsQ0Gb2DnvEJEMmN2tOaXUGByM+kg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/fast-memoize": "2.2.0", + "@formatjs/icu-messageformat-parser": "2.7.6", + "tslib": "^2.4.0" + } + }, + "node_modules/intl-messageformat/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/is-arguments": { "version": "1.1.1", "dev": true, @@ -5070,9 +5226,39 @@ "react": "^18.2.0" } }, + "node_modules/react-intl": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.6.2.tgz", + "integrity": "sha512-IpW2IkLtGENSFlX3vfH11rjuCIsW0VyjT0Q1pPKMZPtT2z1FxLt4weFT5Ezti2TScT1xiyb3aQBFth9EB7jzAg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.18.2", + "@formatjs/icu-messageformat-parser": "2.7.6", + "@formatjs/intl": "2.10.0", + "@formatjs/intl-displaynames": "6.6.6", + "@formatjs/intl-listformat": "7.5.5", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "10.5.11", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "react": "^16.6.0 || 17 || 18", + "typescript": "^4.7 || 5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-intl/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + }, "node_modules/react-is": { "version": "16.13.1", - "dev": true, "license": "MIT" }, "node_modules/react-refresh": { diff --git a/package.json b/package.json index 4a0a0b81..8176a682 100755 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "lodash": "^4.17.21", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-intl": "^6.6.2", "styled-components": "^6.1.8", "webextension-polyfill": "^0.10.0" }, diff --git a/src/_locales/en.json b/src/_locales/en.json new file mode 100644 index 00000000..a617aadf --- /dev/null +++ b/src/_locales/en.json @@ -0,0 +1,46 @@ +{ + "account.docs": "docs", + "account.enterPasscode": "Enter your passcode", + "account.onboard.cta": "Don't have a KERIA agent?", + "account.settings": "Settings", + "account.support": "support", + "action.clickToRemove": "click to remove", + "action.connect": "Connect", + "action.create": "Create", + "action.createNew": "Create New", + "action.delete": "Delete", + "action.disconnect": "Disconnect", + "action.open": "Open", + "action.save": "Save", + "action.select": "Select", + "action.toProceed": "to proceed", + "action.toSelectOther": "to select other", + "config.error.enterUrl": "Enter a valid url", + "config.error.invalidUrl": "Invalid url, Vendor configuration not found", + "config.error.setUrl": "Vendor url is not set", + "config.vendorUrl.label": "Vendor Url:", + "config.vendorUrl.placeholder": "Enter vendor url", + "credential.issue.label": "Isuee:", + "credential.lastUsed.label": "Last Used:", + "credential.revoked": "Revoked", + "credential.title": "Credential", + "credential.valid": "Valid", + "credentials.title": "Credentials", + "identifier.aid.label": "AID:", + "identifier.alias.label": "Alias:", + "identifier.create.title": "Create Identifier", + "identifier.error.emptyName": "Name can not be empty", + "identifier.error.noWhiteSpace": "No white spaces allowed.", + "identifier.title": "AID", + "identifier.uniqueName": "Enter unique name for identifier", + "identifiers.title": "Identifiers", + "message.noItems": "No items to show", + "signin.aidOrCredential": "AID or Credential", + "signin.autoSignin": "Auto Sign in", + "signin.identifierAlias": "Identifier Alias", + "signin.lastUsed.label": "Last Used", + "signin.requestAuth": "requests authentication with", + "signin.website": "Website", + "signin.with": "Sign in with", + "signins.title": "Sign Ins" +} diff --git a/src/_locales/es-419.json b/src/_locales/es-419.json new file mode 100644 index 00000000..b83709ae --- /dev/null +++ b/src/_locales/es-419.json @@ -0,0 +1,46 @@ +{ + "account.docs": "documentos", + "account.enterPasscode": "Introduzca su código de acceso", + "account.onboard.cta": "¿No tienes un agente de KERIA?", + "account.settings": "Ajustes", + "account.support": "apoyo", + "action.clickToRemove": "haga clic para eliminar", + "action.connect": "Conectar", + "action.create": "Crear", + "action.createNew": "Crear nuevo", + "action.delete": "Borrar", + "action.disconnect": "Desconectar", + "action.open": "Abierto", + "action.save": "Ahorrar", + "action.select": "Seleccionar", + "action.toProceed": "para proceder", + "action.toSelectOther": "seleccionar otro", + "config.error.enterUrl": "Introduce una URL válida", + "config.error.invalidUrl": "URL no válida, configuración del proveedor no encontrada", + "config.error.setUrl": "La URL del proveedor no está configurada", + "config.vendorUrl.label": "URL del proveedor:", + "config.vendorUrl.placeholder": "Ingrese la URL del proveedor", + "credential.issue.label": "Asunto:", + "credential.lastUsed.label": "Último utilizado:", + "credential.revoked": "Revocado", + "credential.title": "Credencial", + "credential.valid": "Válido", + "credentials.title": "Credenciales", + "identifier.aid.label": "AID:", + "identifier.alias.label": "Alias:", + "identifier.create.title": "Crear identificador", + "identifier.error.emptyName": "El nombre no puede estar vacío", + "identifier.error.noWhiteSpace": "No se permiten espacios en blanco.", + "identifier.title": "AID", + "identifier.uniqueName": "Introduzca un nombre único para el identificador", + "identifiers.title": "Identificadores", + "message.noItems": "No hay elementos para mostrar", + "signin.aidOrCredential": "AID or Credencial", + "signin.autoSignin": "Acceso auto", + "signin.identifierAlias": "Alias ​​del identificador", + "signin.lastUsed.label": "Último utilizado", + "signin.requestAuth": "solicita autenticación con", + "signin.website": "Sitio web", + "signin.with": "Inicia sesión con", + "signins.title": "Iniciar sesión" +} diff --git a/src/_locales/es.json b/src/_locales/es.json new file mode 100644 index 00000000..b83709ae --- /dev/null +++ b/src/_locales/es.json @@ -0,0 +1,46 @@ +{ + "account.docs": "documentos", + "account.enterPasscode": "Introduzca su código de acceso", + "account.onboard.cta": "¿No tienes un agente de KERIA?", + "account.settings": "Ajustes", + "account.support": "apoyo", + "action.clickToRemove": "haga clic para eliminar", + "action.connect": "Conectar", + "action.create": "Crear", + "action.createNew": "Crear nuevo", + "action.delete": "Borrar", + "action.disconnect": "Desconectar", + "action.open": "Abierto", + "action.save": "Ahorrar", + "action.select": "Seleccionar", + "action.toProceed": "para proceder", + "action.toSelectOther": "seleccionar otro", + "config.error.enterUrl": "Introduce una URL válida", + "config.error.invalidUrl": "URL no válida, configuración del proveedor no encontrada", + "config.error.setUrl": "La URL del proveedor no está configurada", + "config.vendorUrl.label": "URL del proveedor:", + "config.vendorUrl.placeholder": "Ingrese la URL del proveedor", + "credential.issue.label": "Asunto:", + "credential.lastUsed.label": "Último utilizado:", + "credential.revoked": "Revocado", + "credential.title": "Credencial", + "credential.valid": "Válido", + "credentials.title": "Credenciales", + "identifier.aid.label": "AID:", + "identifier.alias.label": "Alias:", + "identifier.create.title": "Crear identificador", + "identifier.error.emptyName": "El nombre no puede estar vacío", + "identifier.error.noWhiteSpace": "No se permiten espacios en blanco.", + "identifier.title": "AID", + "identifier.uniqueName": "Introduzca un nombre único para el identificador", + "identifiers.title": "Identificadores", + "message.noItems": "No hay elementos para mostrar", + "signin.aidOrCredential": "AID or Credencial", + "signin.autoSignin": "Acceso auto", + "signin.identifierAlias": "Alias ​​del identificador", + "signin.lastUsed.label": "Último utilizado", + "signin.requestAuth": "solicita autenticación con", + "signin.website": "Sitio web", + "signin.with": "Inicia sesión con", + "signins.title": "Iniciar sesión" +} diff --git a/src/_locales/index.ts b/src/_locales/index.ts new file mode 100644 index 00000000..860aba8f --- /dev/null +++ b/src/_locales/index.ts @@ -0,0 +1,13 @@ +import en from '@src/_locales/en.json'; +import es from '@src/_locales/es.json'; +import es_419 from '@src/_locales/es-419.json'; + +const existingLanguageCodes = ["en", "es", "es-419"]; + +export * from "./localeContext"; +export const defaultLocale = existingLanguageCodes.includes(navigator.language) ? navigator.language : 'en'; +export const messages = { + en, + es, + "es-419": es_419 +} diff --git a/src/_locales/localeContext.tsx b/src/_locales/localeContext.tsx new file mode 100644 index 00000000..da12d34f --- /dev/null +++ b/src/_locales/localeContext.tsx @@ -0,0 +1,45 @@ +import React, { createContext, useContext, useState, useEffect } from "react"; +import { IntlProvider } from "react-intl"; +import { configService } from "@pages/background/services/config"; +import { defaultLocale, messages } from "@src/_locales"; + +interface ILocaleContext { + currentLocale: string; + changeLocale: (newLocale: string) => void; +} + +const LocaleContext = createContext(null); + +export const LocaleProvider = ({ children }) => { + const [currentLocale, setCurrentLocale] = useState(defaultLocale); + + const handleChangeLocale = async () => { + const lang = await configService.getLanguage(); + setCurrentLocale(lang ?? defaultLocale); + }; + + useEffect(() => { + handleChangeLocale(); + }, []); + + const changeLocale = async (newLocale: string) => { + await configService.setLanguage(newLocale); + setCurrentLocale(newLocale); + }; + + return ( + + + {children} + + + ); +}; + +export const useLocale = () => { + const context = useContext(LocaleContext); + if (!context) { + throw new Error("useLocale must be used within a LocaleProvider"); + } + return context; +}; diff --git a/src/components/createIdentifierCard.tsx b/src/components/createIdentifierCard.tsx index a5b2534b..ce41d971 100644 --- a/src/components/createIdentifierCard.tsx +++ b/src/components/createIdentifierCard.tsx @@ -1,17 +1,20 @@ import { isValidElement, useState, useEffect } from "react"; +import { useIntl } from "react-intl"; import { Button } from "@components/ui"; import { hasWhiteSpace, removeWhiteSpace } from "@pages/background/utils"; export function CreateIdentifierCard(props): JSX.Element { const [name, setName] = useState(""); - const [nameError, setNameError] = useState(""); + const [nameError, setNameError] = useState(""); + const { formatMessage } = useIntl(); + const emptyNameError = formatMessage({ id: "identifier.error.emptyName" }); useEffect(() => { setNameError(props.error); }, [props.error]); const onBlurName = () => { if (!name) { - setNameError("Name can not be empty"); + setNameError(emptyNameError); } else { setNameError(""); } @@ -24,17 +27,17 @@ export function CreateIdentifierCard(props): JSX.Element { const onCreateIdentifier = async () => { let hasError = false; if (!name) { - setNameError("Name can not be empty"); + setNameError(emptyNameError); hasError = true; } else if (hasWhiteSpace(name)) { setNameError(
- No white spaces allowed.{" "} + {formatMessage({ id: "identifier.error.noWhiteSpace" })}{" "}
); @@ -54,7 +57,7 @@ export function CreateIdentifierCard(props): JSX.Element { className={`border text-black text-sm rounded-lg block w-full p-2.5 ${ nameError ? " text-red border-red" : "" } `} - placeholder="Enter unique name for identifier" + placeholder={formatMessage({ id: "identifier.uniqueName" })} required value={name} onChange={(e) => setName(e.target.value)} @@ -73,7 +76,9 @@ export function CreateIdentifierCard(props): JSX.Element { handleClick={onCreateIdentifier} className="text-white flex flex-row font-medium rounded-full text-sm px-5 py-2" > -

Create

+

+ {formatMessage({ id: "action.create" })} +

diff --git a/src/components/credentialCard.tsx b/src/components/credentialCard.tsx index cb9e9360..2cd0b3df 100644 --- a/src/components/credentialCard.tsx +++ b/src/components/credentialCard.tsx @@ -1,96 +1,101 @@ -import { Text, Subtext } from "@components/ui"; +import { useIntl } from "react-intl"; +import { Text, Subtext, Card } from "@components/ui"; interface ICredential { credential: any; } export function CredentialCard({ credential }: ICredential): JSX.Element { + const { formatMessage } = useIntl(); + return ( -
-
-
- - {credential.schema.title} - - - {credential.schema.credentialType} - + + <> +
+
+ + {credential.schema.title} + + + {credential.schema.credentialType} + +
+ + +
- - - -
-
- - {credential.schema.description} - - - <> - Isuee:{" "} - - {credential.issueeName} - - - -
-
- - Last Used:{" "} + + {credential.schema.description} - - November 08, 2023 + + <> + {formatMessage({ id: "credential.issue.label" })}{" "} + + {credential.issueeName} + +
- {credential.status?.et === "iss" ? ( -
- - - -

Valid

+
+
+ + {formatMessage({ id: "credential.lastUsed.label" })}{" "} + + + November 08, 2023 +
- ) : ( -
- - - -

Revoked

-
- )} -
-
+ {credential.status?.et === "iss" ? ( +
+ + + +

{formatMessage({ id: "credential.valid" })}

+
+ ) : ( +
+ + + +

{formatMessage({ id: "credential.revoked" })}

+
+ )} +
+ + ); } diff --git a/src/components/credentialList.tsx b/src/components/credentialList.tsx index 76bf1002..39271d25 100644 --- a/src/components/credentialList.tsx +++ b/src/components/credentialList.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import { useIntl } from "react-intl"; import { CredentialCard } from "@components/credentialCard"; import { Loader } from "@components/loader"; import { IMessage } from "@pages/background/types"; @@ -6,13 +7,13 @@ import { IMessage } from "@pages/background/types"; export function CredentialList(): JSX.Element { const [credentials, setCredentials] = useState([]); const [isLoading, setIsLoading] = useState(false); + const { formatMessage } = useIntl(); const fetchCredentials = async () => { setIsLoading(true); const { data } = await chrome.runtime.sendMessage>({ type: "fetch-resource", subtype: "credentials", }); - console.log("credentials", data); setCredentials(data.credentials); setIsLoading(false); }; @@ -34,7 +35,7 @@ export function CredentialList(): JSX.Element {
))} {!isLoading && !credentials?.length ? ( -

No items to show

+

{formatMessage({ id: "message.noItems" })}

) : ( <> )} diff --git a/src/components/identifierCard.tsx b/src/components/identifierCard.tsx index d09ac294..917f3004 100644 --- a/src/components/identifierCard.tsx +++ b/src/components/identifierCard.tsx @@ -1,48 +1,52 @@ import { obfuscateString } from "@pages/background/utils"; -import { Text, Subtext } from "@components/ui"; +import { useIntl } from "react-intl"; +import { Card, Text, Subtext } from "@components/ui"; interface IIdentifier {} export function IdentifierCard({ aid }): JSX.Element { + const { formatMessage } = useIntl(); + return ( -
-
-
+ + <> +
+
+ + {formatMessage({ id: "identifier.alias.label" })}{" "} + + {aid.name} + + +
+ + + +
+
- Alias:{" "} - - {aid.name} + {formatMessage({ id: "identifier.aid.label" })}{" "} + + {obfuscateString(aid.prefix)}
- - - -
-
- - AID:{" "} - - {obfuscateString(aid.prefix)} - - -
- {/* COMMENTED OUT FOR THE DEMO + {/* COMMENTED OUT FOR THE DEMO
Credentials Received: @@ -53,6 +57,7 @@ export function IdentifierCard({ aid }): JSX.Element { November 08, 2023
*/} -
+ + ); } diff --git a/src/components/identifierList.tsx b/src/components/identifierList.tsx index 44253b65..07426086 100644 --- a/src/components/identifierList.tsx +++ b/src/components/identifierList.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import { useIntl } from "react-intl"; import { IdentifierCard } from "@components/identifierCard"; import { Button, Drawer } from "@components/ui"; import { Loader } from "@components/loader"; @@ -11,6 +12,7 @@ export function IdentifierList(): JSX.Element { const [isCreating, setIsCreating] = useState(false); const [showDrawer, setShowDrawer] = useState(false); const [errCreate, setErrCreate] = useState(""); + const { formatMessage } = useIntl(); const fetchIdentifiers = async () => { const { data } = await chrome.runtime.sendMessage>({ @@ -63,13 +65,13 @@ export function IdentifierList(): JSX.Element { handleClick={() => setShowDrawer(true)} className="text-white font-medium rounded-full text-xs px-2 py-1" > - <>{"+ Create New"} + <>{`+ ${formatMessage({ id: "action.createNew" })}`}
setShowDrawer(false)} - header="Create Identifier" + header={formatMessage({ id: "identifier.create.title" })} > ))} {!isLoading && !aids?.length ? ( -

No items to show

+

{formatMessage({ id: "message.noItems" })}

) : ( <> )} diff --git a/src/components/main.tsx b/src/components/main.tsx index 9ca87958..935327dc 100644 --- a/src/components/main.tsx +++ b/src/components/main.tsx @@ -1,6 +1,7 @@ import { useEffect, useState } from "react"; import { styled } from "styled-components"; -import { Sidebar } from "@components/sidebar"; +import { Text } from "@components/ui"; +import { Sidebar, SIDEBAR, SIDEBAR_KEYS } from "@components/sidebar"; import { SelectIdentifier } from "@components/selectIdentifier"; import { SelectCredential } from "@components/selectCredential"; import { TAB_STATE } from "@pages/popup/constants"; @@ -20,7 +21,7 @@ const StyledMainContainer = styled.div` `; export function Main(props: IMain): JSX.Element { - const [activeSidebar, setActiveSidebar] = useState("Identifiers"); + const [activeSidebar, setActiveSidebar] = useState(SIDEBAR[0]); const [tabState, setTabState] = useState(TAB_STATE.DEFAULT); const fetchTabState = async () => { @@ -43,12 +44,12 @@ export function Main(props: IMain): JSX.Element { setActiveSidebar( data?.appState === TAB_STATE.SELECT_IDENTIFIER || data?.appState === TAB_STATE.SELECT_ID_CRED - ? "Identifiers" - : "Credentials" + ? SIDEBAR[0] + : SIDEBAR[1] ); } if (data?.appState === TAB_STATE.SELECT_AUTO_SIGNIN) { - setActiveSidebar("Sign Ins"); + setActiveSidebar(SIDEBAR[2]); } } } @@ -60,8 +61,8 @@ export function Main(props: IMain): JSX.Element { }, []); const renderItems = () => { - switch (activeSidebar) { - case "Credentials": + switch (activeSidebar?.id) { + case SIDEBAR_KEYS.credentials: if ( tabState === TAB_STATE.SELECT_CREDENTIAL || tabState === TAB_STATE.SELECT_ID_CRED @@ -69,7 +70,7 @@ export function Main(props: IMain): JSX.Element { return ; return ; - case "Sign Ins": + case SIDEBAR_KEYS.signin: return ; default: @@ -102,7 +103,9 @@ export function Main(props: IMain): JSX.Element { />
-

{activeSidebar}

+ + {activeSidebar?.title} +
{renderItems()}
diff --git a/src/components/selectCredential.tsx b/src/components/selectCredential.tsx index 98117215..d917e5cd 100644 --- a/src/components/selectCredential.tsx +++ b/src/components/selectCredential.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import { useIntl } from "react-intl"; import { CredentialCard } from "@components/credentialCard"; import { Button } from "@components/ui"; import { Loader } from "@components/loader"; @@ -7,6 +8,7 @@ import { IMessage } from "@pages/background/types"; export function SelectCredential(): JSX.Element { const [credentials, setCredentials] = useState([]); const [isLoading, setIsLoading] = useState(false); + const { formatMessage } = useIntl(); const fetchCredentials = async () => { setIsLoading(true); const { data } = await chrome.runtime.sendMessage>({ @@ -53,7 +55,7 @@ export function SelectCredential(): JSX.Element { handleClick={() => createSigninWithCredential(credential)} className="absolute right-0 bottom-0 text-white font-medium rounded-full text-xs px-2 py-1 " > - <>{"Select >"} + <>{`${formatMessage({ id: "action.select" })} >`}
diff --git a/src/components/selectIdentifier.tsx b/src/components/selectIdentifier.tsx index fd6b2948..0d875b26 100644 --- a/src/components/selectIdentifier.tsx +++ b/src/components/selectIdentifier.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react"; +import { useIntl } from "react-intl"; import { IdentifierCard } from "@components/identifierCard"; import { Button, Drawer } from "@components/ui"; import { Loader } from "@components/loader"; @@ -11,6 +12,7 @@ export function SelectIdentifier(): JSX.Element { const [isCreating, setIsCreating] = useState(false); const [showDrawer, setShowDrawer] = useState(false); const [errCreate, setErrCreate] = useState(""); + const { formatMessage } = useIntl(); const fetchIdentifiers = async () => { const { data } = await chrome.runtime.sendMessage>({ @@ -74,13 +76,13 @@ export function SelectIdentifier(): JSX.Element { handleClick={() => setShowDrawer(true)} className=" text-white font-medium rounded-full text-xs px-2 py-1" > - <>{"+ Create New"} + <>{`+ ${formatMessage({ id: "action.createNew" })}`} setShowDrawer(false)} - header="Create Identifier" + header={formatMessage({ id: "identifier.create.title" })} > createSigninWithIdentifiers(aid)} className=" absolute right-0 bottom-0 text-white font-medium rounded-full text-xs px-2 py-1" > - <>{"Select >"} + <>{`${formatMessage({ id: "action.select" })} >`} diff --git a/src/components/sidebar.tsx b/src/components/sidebar.tsx index b0141293..7e5b6c32 100644 --- a/src/components/sidebar.tsx +++ b/src/components/sidebar.tsx @@ -1,8 +1,15 @@ +import { useIntl, FormattedMessage } from "react-intl"; import { styled } from "styled-components"; -const SIDEBAR = [ +export const SIDEBAR_KEYS = { + identifiers: "identifiers", + credentials: "credentials", + signin: "sign-ins", +}; + +export const SIDEBAR = [ { - id: "Identifiers", + id: SIDEBAR_KEYS.identifiers, icon: ( ), - title: "Identifiers", + title: , }, { - id: "Credentials", + id: SIDEBAR_KEYS.credentials, icon: ( ), - title: "Credentials", + title: , }, { - id: "Sign Ins", + id: SIDEBAR_KEYS.signin, icon: ( ), - title: "Sign Ins", + title: , }, ]; interface ISidebar { - active: string; - onClickLink: (active: string) => void; + active: any; + onClickLink: (active: any) => void; disabled?: boolean; onSignout: () => void; title?: string; @@ -90,6 +97,7 @@ const StyledBottomMenu = styled.div` `; export function Sidebar(props: ISidebar): JSX.Element { + const { formatMessage } = useIntl(); return (