@asafarim/shared-i18n 0.5.0
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.
- package/LICENSE +23 -0
- package/README.md +168 -0
- package/config/i18n.ts +89 -0
- package/hooks/useLanguage.ts +80 -0
- package/index.ts +21 -0
- package/locales/en/common.json +66 -0
- package/locales/en/identity-portal.json +69 -0
- package/locales/en/web.json +343 -0
- package/locales/nl/common.json +66 -0
- package/locales/nl/identity-portal.json +69 -0
- package/locales/nl/web.json +343 -0
- package/package.json +74 -0
- package/tsconfig.json +32 -0
- package/utils/languageUtils.ts +141 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# License
|
|
2
|
+
|
|
3
|
+
This project is licensed under the **Creative Commons Attribution 4.0 International (CC BY 4.0)** license.
|
|
4
|
+
|
|
5
|
+
You are free to:
|
|
6
|
+
|
|
7
|
+
- **Share** — copy and redistribute the material in any medium or format
|
|
8
|
+
- **Adapt** — remix, transform, and build upon the material for any purpose, even commercially
|
|
9
|
+
|
|
10
|
+
Under the following terms:
|
|
11
|
+
|
|
12
|
+
- **Attribution** — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
|
|
13
|
+
You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
|
|
14
|
+
|
|
15
|
+
## 📌 Attribution Requirement
|
|
16
|
+
|
|
17
|
+
If you use or modify this project, please include the following attribution:
|
|
18
|
+
|
|
19
|
+
> Based on work by [Ali Safari](https://github.com/AliSafari-IT/asafarim-dot-be)
|
|
20
|
+
|
|
21
|
+
## 🔗 Full License Text
|
|
22
|
+
|
|
23
|
+
For full details, see the [Creative Commons Attribution 4.0 License](https://creativecommons.org/licenses/by/4.0/).
|
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @asafarim/shared-i18n
|
|
2
|
+
|
|
3
|
+
Lightweight, simple translation module for any React + TypeScript app, built on top of i18next and react-i18next. It ships with sensible defaults (English and Dutch) but can support any language by adding JSON files to your locales folder.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🌍 Works in any React + TypeScript project (monorepo or standalone)
|
|
8
|
+
- 🗂️ JSON-based translations per language and namespace
|
|
9
|
+
- 🔄 Cookie-based language persistence (browser) with automatic detection
|
|
10
|
+
- ⚙️ Optional backend sync for user language preferences
|
|
11
|
+
- ⚡ Lazy loading support for app-specific translations
|
|
12
|
+
- 🪝 React hooks for language management (useLanguage) and translations (useTranslation)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @asafarim/shared-i18n
|
|
18
|
+
# or
|
|
19
|
+
npm i @asafarim/shared-i18n
|
|
20
|
+
# or
|
|
21
|
+
yarn add @asafarim/shared-i18n
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### 1) Initialize i18n in your app
|
|
27
|
+
|
|
28
|
+
In your app's main entry point (e.g., `main.tsx`):
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { StrictMode } from 'react';
|
|
32
|
+
import { createRoot } from 'react-dom/client';
|
|
33
|
+
import { initI18n } from '@asafarim/shared-i18n';
|
|
34
|
+
import App from './App';
|
|
35
|
+
|
|
36
|
+
// Optional: import your app-specific translations
|
|
37
|
+
import enApp from './locales/en/app.json';
|
|
38
|
+
import nlApp from './locales/nl/app.json';
|
|
39
|
+
|
|
40
|
+
initI18n({
|
|
41
|
+
defaultNS: 'common',
|
|
42
|
+
ns: ['common', 'app'],
|
|
43
|
+
resources: {
|
|
44
|
+
en: { app: enApp },
|
|
45
|
+
nl: { app: nlApp }
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
createRoot(document.getElementById('root')!).render(
|
|
50
|
+
<StrictMode>
|
|
51
|
+
<App />
|
|
52
|
+
</StrictMode>
|
|
53
|
+
);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2) Use translations in components
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
import { useTranslation } from '@asafarim/shared-i18n';
|
|
60
|
+
|
|
61
|
+
function MyComponent() {
|
|
62
|
+
const { t } = useTranslation();
|
|
63
|
+
return (
|
|
64
|
+
<div>
|
|
65
|
+
<h1>{t('welcome')}</h1>
|
|
66
|
+
<p>{t('app:customKey')}</p>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 3) Add a language switcher
|
|
73
|
+
|
|
74
|
+
If you use @asafarim/shared-ui-react you can leverage its LanguageSwitcher component. Otherwise, call useLanguage directly.
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { useLanguage } from '@asafarim/shared-i18n';
|
|
78
|
+
|
|
79
|
+
export function LanguageToggle() {
|
|
80
|
+
const { language, changeLanguage, isChanging } = useLanguage();
|
|
81
|
+
return (
|
|
82
|
+
<button onClick={() => changeLanguage(language === 'en' ? 'nl' : 'en')} disabled={isChanging}>
|
|
83
|
+
Switch language (current: {language})
|
|
84
|
+
</button>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Add more languages
|
|
90
|
+
|
|
91
|
+
Yes — you can support any language by adding the required JSON files to your locales folder. For example:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
your-app/
|
|
95
|
+
src/
|
|
96
|
+
locales/
|
|
97
|
+
en/
|
|
98
|
+
app.json
|
|
99
|
+
nl/
|
|
100
|
+
app.json
|
|
101
|
+
anotherLang/
|
|
102
|
+
new.json
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Then include them when initializing:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
import anotherLang from './locales/anotherLang/new.json';
|
|
109
|
+
|
|
110
|
+
initI18n({
|
|
111
|
+
ns: ['common', 'app', 'new'],
|
|
112
|
+
resources: {
|
|
113
|
+
anotherLang: { new: anotherLang }
|
|
114
|
+
},
|
|
115
|
+
supportedLngs: ['en', 'nl', 'anotherLang'],
|
|
116
|
+
defaultLanguage: 'en'
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Notes:
|
|
121
|
+
- If you pass supportedLngs, it will override the default supported languages.
|
|
122
|
+
- defaultLanguage overrides the default fallback (which is English).
|
|
123
|
+
|
|
124
|
+
## API Reference
|
|
125
|
+
|
|
126
|
+
### initI18n(config?: I18nConfig)
|
|
127
|
+
|
|
128
|
+
Initialize i18next with the shared configuration.
|
|
129
|
+
|
|
130
|
+
Parameters:
|
|
131
|
+
- config.defaultNS — Default namespace (default: 'common')
|
|
132
|
+
- config.ns — Namespaces to load (default: ['common'])
|
|
133
|
+
- config.resources — App-specific translation resources
|
|
134
|
+
- config.supportedLngs — Optional list of supported language codes to enable
|
|
135
|
+
- config.defaultLanguage — Optional fallback language code
|
|
136
|
+
|
|
137
|
+
### useLanguage()
|
|
138
|
+
|
|
139
|
+
Hook for managing language preferences.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
- language — Current language code
|
|
143
|
+
- changeLanguage(lang) — Function to change language
|
|
144
|
+
- isChanging — Boolean indicating if language change is in progress
|
|
145
|
+
|
|
146
|
+
### useTranslation()
|
|
147
|
+
|
|
148
|
+
Re-exported from react-i18next. See official docs.
|
|
149
|
+
|
|
150
|
+
## Cookie and backend integration
|
|
151
|
+
|
|
152
|
+
- A preferredLanguage cookie is used to persist the selected language in the browser.
|
|
153
|
+
- If your environment provides an Identity API, updateUserLanguagePreference can sync the preference server-side. If not, the library still works fully client-side.
|
|
154
|
+
|
|
155
|
+
To point to a backend, optionally set:
|
|
156
|
+
|
|
157
|
+
```env
|
|
158
|
+
VITE_IDENTITY_API_URL=https://your-identity.example.com
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Built-in translations
|
|
162
|
+
|
|
163
|
+
This package ships with default English and Dutch common translations. You can ignore them and supply your own resources if preferred.
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
MIT © ASafariM
|
|
168
|
+
|
package/config/i18n.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
4
|
+
|
|
5
|
+
// Import common translations
|
|
6
|
+
import enCommon from '../locales/en/common.json';
|
|
7
|
+
import nlCommon from '../locales/nl/common.json';
|
|
8
|
+
|
|
9
|
+
// Import web app translations
|
|
10
|
+
import enWeb from '../locales/en/web.json';
|
|
11
|
+
import nlWeb from '../locales/nl/web.json';
|
|
12
|
+
|
|
13
|
+
// Import identity-portal translations
|
|
14
|
+
import enIdentityPortal from '../locales/en/identity-portal.json';
|
|
15
|
+
import nlIdentityPortal from '../locales/nl/identity-portal.json';
|
|
16
|
+
|
|
17
|
+
export const SUPPORTED_LANGUAGES = ['en', 'nl'] as const;
|
|
18
|
+
export type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number];
|
|
19
|
+
|
|
20
|
+
export const LANGUAGE_NAMES: Record<SupportedLanguage, string> = {
|
|
21
|
+
en: 'English',
|
|
22
|
+
nl: 'Nederlands'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const DEFAULT_LANGUAGE: SupportedLanguage = 'en';
|
|
26
|
+
export const LANGUAGE_COOKIE_NAME = 'preferredLanguage';
|
|
27
|
+
|
|
28
|
+
export interface I18nConfig {
|
|
29
|
+
defaultNS?: string;
|
|
30
|
+
ns?: string[];
|
|
31
|
+
resources?: Record<string, Record<string, any>>;
|
|
32
|
+
supportedLngs?: string[];
|
|
33
|
+
defaultLanguage?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const initI18n = (config?: I18nConfig) => {
|
|
37
|
+
const {
|
|
38
|
+
defaultNS = 'common',
|
|
39
|
+
ns = ['common', 'web', 'identityPortal'],
|
|
40
|
+
resources = {},
|
|
41
|
+
supportedLngs,
|
|
42
|
+
defaultLanguage,
|
|
43
|
+
} = config || {};
|
|
44
|
+
|
|
45
|
+
// Merge common translations with app-specific resources
|
|
46
|
+
const mergedResources: Record<string, Record<string, any>> = { ...resources };
|
|
47
|
+
|
|
48
|
+
mergedResources.en = {
|
|
49
|
+
common: enCommon,
|
|
50
|
+
web: enWeb,
|
|
51
|
+
identityPortal: enIdentityPortal,
|
|
52
|
+
...(resources.en || {}),
|
|
53
|
+
};
|
|
54
|
+
mergedResources.nl = {
|
|
55
|
+
common: nlCommon,
|
|
56
|
+
web: nlWeb,
|
|
57
|
+
identityPortal: nlIdentityPortal,
|
|
58
|
+
...(resources.nl || {}),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const finalSupportedLngs = supportedLngs ?? Object.keys(mergedResources);
|
|
62
|
+
const fallbackLng = defaultLanguage ?? DEFAULT_LANGUAGE;
|
|
63
|
+
|
|
64
|
+
i18n
|
|
65
|
+
.use(initReactI18next)
|
|
66
|
+
.use(LanguageDetector)
|
|
67
|
+
.init({
|
|
68
|
+
resources: mergedResources,
|
|
69
|
+
fallbackLng,
|
|
70
|
+
defaultNS,
|
|
71
|
+
ns,
|
|
72
|
+
supportedLngs: finalSupportedLngs,
|
|
73
|
+
detection: {
|
|
74
|
+
order: ['cookie', 'navigator'],
|
|
75
|
+
caches: ['cookie'],
|
|
76
|
+
lookupCookie: LANGUAGE_COOKIE_NAME
|
|
77
|
+
},
|
|
78
|
+
interpolation: {
|
|
79
|
+
escapeValue: false // React already escapes
|
|
80
|
+
},
|
|
81
|
+
react: {
|
|
82
|
+
useSuspense: false
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return i18n;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export default i18n;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import {
|
|
4
|
+
setLanguageCookie,
|
|
5
|
+
updateUserLanguagePreference,
|
|
6
|
+
fetchUserLanguagePreference,
|
|
7
|
+
isSupportedLanguage
|
|
8
|
+
} from '../utils/languageUtils';
|
|
9
|
+
import type { SupportedLanguage } from '../config/i18n';
|
|
10
|
+
|
|
11
|
+
export interface UseLanguageReturn {
|
|
12
|
+
language: SupportedLanguage;
|
|
13
|
+
changeLanguage: (lang: SupportedLanguage) => Promise<void>;
|
|
14
|
+
isChanging: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Hook for managing language preferences
|
|
19
|
+
* Handles both frontend (cookie) and backend (API) synchronization
|
|
20
|
+
*/
|
|
21
|
+
export const useLanguage = (): UseLanguageReturn => {
|
|
22
|
+
const { i18n } = useTranslation();
|
|
23
|
+
const [isChanging, setIsChanging] = useState(false);
|
|
24
|
+
|
|
25
|
+
// Safely get current language with fallback
|
|
26
|
+
const currentLanguage = (i18n?.language && isSupportedLanguage(i18n.language)
|
|
27
|
+
? i18n.language
|
|
28
|
+
: 'en') as SupportedLanguage;
|
|
29
|
+
|
|
30
|
+
// Sync with backend on mount (for authenticated users)
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const syncLanguageWithBackend = async () => {
|
|
33
|
+
if (!i18n?.changeLanguage) return;
|
|
34
|
+
|
|
35
|
+
const backendLang = await fetchUserLanguagePreference();
|
|
36
|
+
if (backendLang && backendLang !== currentLanguage) {
|
|
37
|
+
await i18n.changeLanguage(backendLang);
|
|
38
|
+
setLanguageCookie(backendLang);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
syncLanguageWithBackend();
|
|
43
|
+
}, []); // Only run once on mount
|
|
44
|
+
|
|
45
|
+
const changeLanguage = useCallback(async (lang: SupportedLanguage) => {
|
|
46
|
+
if (lang === currentLanguage) return;
|
|
47
|
+
|
|
48
|
+
setIsChanging(true);
|
|
49
|
+
try {
|
|
50
|
+
// Check if i18n is properly initialized
|
|
51
|
+
if (!i18n || typeof i18n.changeLanguage !== 'function') {
|
|
52
|
+
console.error('i18n is not properly initialized');
|
|
53
|
+
// Still update cookie even if i18n fails
|
|
54
|
+
setLanguageCookie(lang);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Update i18next
|
|
59
|
+
await i18n.changeLanguage(lang);
|
|
60
|
+
|
|
61
|
+
// Update cookie immediately
|
|
62
|
+
setLanguageCookie(lang);
|
|
63
|
+
|
|
64
|
+
// Update backend (fire and forget for better UX)
|
|
65
|
+
updateUserLanguagePreference(lang).catch(err => {
|
|
66
|
+
console.warn('Failed to sync language preference with backend:', err);
|
|
67
|
+
});
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error('Failed to change language:', error);
|
|
70
|
+
} finally {
|
|
71
|
+
setIsChanging(false);
|
|
72
|
+
}
|
|
73
|
+
}, [currentLanguage]);
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
language: currentLanguage,
|
|
77
|
+
changeLanguage,
|
|
78
|
+
isChanging
|
|
79
|
+
};
|
|
80
|
+
};
|
package/index.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Main exports
|
|
2
|
+
export { initI18n, SUPPORTED_LANGUAGES, LANGUAGE_NAMES, DEFAULT_LANGUAGE, LANGUAGE_COOKIE_NAME } from './config/i18n';
|
|
3
|
+
export type { SupportedLanguage, I18nConfig } from './config/i18n';
|
|
4
|
+
|
|
5
|
+
// Hooks
|
|
6
|
+
export { useLanguage } from './hooks/useLanguage';
|
|
7
|
+
export type { UseLanguageReturn } from './hooks/useLanguage';
|
|
8
|
+
|
|
9
|
+
// Utils
|
|
10
|
+
export {
|
|
11
|
+
getLanguageFromCookie,
|
|
12
|
+
setLanguageCookie,
|
|
13
|
+
isSupportedLanguage,
|
|
14
|
+
getBrowserLanguage,
|
|
15
|
+
getInitialLanguage,
|
|
16
|
+
updateUserLanguagePreference,
|
|
17
|
+
fetchUserLanguagePreference
|
|
18
|
+
} from './utils/languageUtils';
|
|
19
|
+
|
|
20
|
+
// Re-export react-i18next for convenience
|
|
21
|
+
export { useTranslation, Trans, Translation } from 'react-i18next';
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"welcome": "Welcome",
|
|
3
|
+
"language": "Language",
|
|
4
|
+
"settings": "Settings",
|
|
5
|
+
"profile": "Profile",
|
|
6
|
+
"logout": "Logout",
|
|
7
|
+
"login": "Login",
|
|
8
|
+
"register": "Register",
|
|
9
|
+
"email": "Email",
|
|
10
|
+
"password": "Password",
|
|
11
|
+
"confirmPassword": "Confirm Password",
|
|
12
|
+
"forgotPassword": "Forgot Password?",
|
|
13
|
+
"rememberMe": "Remember Me",
|
|
14
|
+
"submit": "Submit",
|
|
15
|
+
"cancel": "Cancel",
|
|
16
|
+
"save": "Save",
|
|
17
|
+
"delete": "Delete",
|
|
18
|
+
"edit": "Edit",
|
|
19
|
+
"close": "Close",
|
|
20
|
+
"search": "Search",
|
|
21
|
+
"loading": "Loading...",
|
|
22
|
+
"error": "Error",
|
|
23
|
+
"success": "Success",
|
|
24
|
+
"warning": "Warning",
|
|
25
|
+
"info": "Information",
|
|
26
|
+
"yes": "Yes",
|
|
27
|
+
"no": "No",
|
|
28
|
+
"back": "Back",
|
|
29
|
+
"next": "Next",
|
|
30
|
+
"previous": "Previous",
|
|
31
|
+
"home": "Home",
|
|
32
|
+
"about": "About",
|
|
33
|
+
"contact": "Contact",
|
|
34
|
+
"services": "Services",
|
|
35
|
+
"blog": "Blog",
|
|
36
|
+
"careers": "Careers",
|
|
37
|
+
"privacy": "Privacy Policy",
|
|
38
|
+
"terms": "Terms of Service",
|
|
39
|
+
"copyright": " ASafariM. All rights reserved.",
|
|
40
|
+
"languageChanged": "Language changed successfully",
|
|
41
|
+
"preferencesSaved": "Preferences saved successfully",
|
|
42
|
+
"apps": {
|
|
43
|
+
"appName": {
|
|
44
|
+
"web": "ASafariM web",
|
|
45
|
+
"blog": "Blog & documentation",
|
|
46
|
+
"ai": "AI Tools",
|
|
47
|
+
"core": "Core App",
|
|
48
|
+
"jobs": "Job Applications",
|
|
49
|
+
"identity": "Identity Portal",
|
|
50
|
+
"testora": "Testora",
|
|
51
|
+
"taskmanagement": "Task Management",
|
|
52
|
+
"smartops": "SmartOps"
|
|
53
|
+
},
|
|
54
|
+
"description": {
|
|
55
|
+
"web": "ASafariM web portal",
|
|
56
|
+
"blog": "Documentation and blog",
|
|
57
|
+
"ai": "AI-powered tools and services",
|
|
58
|
+
"core": "Core application features",
|
|
59
|
+
"jobs": "Job application tracking",
|
|
60
|
+
"identity": "User management and authentication",
|
|
61
|
+
"testora": "Test Automation System - Testora",
|
|
62
|
+
"taskmanagement": "Task Management",
|
|
63
|
+
"smartops": "SmartOps"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"toast": {
|
|
3
|
+
"notAuthorized": "You do not have permission to access this page",
|
|
4
|
+
"notAdmin": "You do not have permission to access this page",
|
|
5
|
+
"loading": "Loading..."
|
|
6
|
+
},
|
|
7
|
+
"admin-area": {
|
|
8
|
+
"dashboard": {
|
|
9
|
+
"title": "Admin Dashboard",
|
|
10
|
+
"description": "Manage Identity Portal entities",
|
|
11
|
+
"actions": {
|
|
12
|
+
"open": "Open"
|
|
13
|
+
},
|
|
14
|
+
"user-management": {
|
|
15
|
+
"title": "User Management",
|
|
16
|
+
"description": "Manage platform users and their permissions.",
|
|
17
|
+
"icon": "👥",
|
|
18
|
+
"link": "/admin-area/users"
|
|
19
|
+
},
|
|
20
|
+
"role-management": {
|
|
21
|
+
"title": "Role Management",
|
|
22
|
+
"description": "Define and update system roles and privileges.",
|
|
23
|
+
"icon": "🛡️",
|
|
24
|
+
"link": "/admin-area/roles"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"navbar": {
|
|
29
|
+
"admin-area": "Admin Area",
|
|
30
|
+
"me": "My Profile",
|
|
31
|
+
"contact": "Contact",
|
|
32
|
+
"about": "About",
|
|
33
|
+
"auth": {
|
|
34
|
+
"notSignedIn": "Not signed in!",
|
|
35
|
+
"signIn": "Sign In",
|
|
36
|
+
"signOut": "Sign Out",
|
|
37
|
+
"welcome": "Welcome, {{userName}}!"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"dashboard": {
|
|
41
|
+
"title": "Dashboard",
|
|
42
|
+
"description": "Manage Identity Portal entities",
|
|
43
|
+
"actions": {
|
|
44
|
+
"title": "Actions",
|
|
45
|
+
"open": "Open",
|
|
46
|
+
"editProfile": "Edit profile",
|
|
47
|
+
"changePassword": "Change password",
|
|
48
|
+
"manageUsers": "Manage users"
|
|
49
|
+
},
|
|
50
|
+
"user-management": {
|
|
51
|
+
"title": "User Management",
|
|
52
|
+
"description": "Manage platform users and their permissions.",
|
|
53
|
+
"icon": "👥",
|
|
54
|
+
"link": "/admin-area/users",
|
|
55
|
+
"email": "Email",
|
|
56
|
+
"username": "Username"
|
|
57
|
+
},
|
|
58
|
+
"role-management": {
|
|
59
|
+
"title": "Role Management",
|
|
60
|
+
"description": "Define and update system roles and privileges.",
|
|
61
|
+
"icon": "🛡️",
|
|
62
|
+
"link": "/admin-area/roles"
|
|
63
|
+
},
|
|
64
|
+
"access": {
|
|
65
|
+
"title": "Access",
|
|
66
|
+
"roles": "Roles"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|