@l10nmonster/server 3.0.0-alpha.1
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/README.md +101 -0
- package/index.js +69 -0
- package/mockData.js +67 -0
- package/package.json +55 -0
- package/ui/dist/assets/index-CIfVvUo9.css +1 -0
- package/ui/dist/assets/index-DexglpRr.js +253 -0
- package/ui/dist/favicon.ico +0 -0
- package/ui/dist/index.html +21 -0
- package/ui/dist/logo192.png +0 -0
- package/ui/dist/logo512.png +0 -0
- package/ui/dist/manifest.json +25 -0
- package/ui/dist/robots.txt +3 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# @l10nmonster/server
|
|
2
|
+
|
|
3
|
+
L10n Monster Manager UI - Web-based interface for managing localization projects and translation workflows.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @l10nmonster/server
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Start the server with API only:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
l10n serve --port 9691
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Start the server with web UI:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
l10n serve --port 9691 --ui
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Features
|
|
26
|
+
|
|
27
|
+
### Web UI
|
|
28
|
+
|
|
29
|
+
Modern React-based interface built with:
|
|
30
|
+
- **Material-UI**: Professional design system
|
|
31
|
+
- **React Router**: Client-side routing
|
|
32
|
+
- **Vite**: Fast development and build tooling
|
|
33
|
+
- **Responsive Design**: Works on desktop and mobile
|
|
34
|
+
|
|
35
|
+
### API Endpoints
|
|
36
|
+
|
|
37
|
+
- `GET /api/status` - Translation status and project overview
|
|
38
|
+
- `GET /api/untranslated/:sourceLang/:targetLang` - Untranslated content for language pairs
|
|
39
|
+
- `GET /api/tm/stats/:sourceLang/:targetLang` - Translation memory statistics
|
|
40
|
+
|
|
41
|
+
### Pages
|
|
42
|
+
|
|
43
|
+
- **Home**: Project overview and status dashboard
|
|
44
|
+
- **Untranslated**: Browse untranslated content by language pair
|
|
45
|
+
- **Translation Memory**: View and manage translation memory statistics
|
|
46
|
+
- **404**: Error handling for unknown routes
|
|
47
|
+
|
|
48
|
+
## Development
|
|
49
|
+
|
|
50
|
+
### Start Development Server
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm run dev
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Build for Production
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm run build
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Preview Production Build
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm run preview
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
The server supports:
|
|
71
|
+
- **Custom Port**: Specify listening port with `--port` option
|
|
72
|
+
- **UI Toggle**: Enable/disable web interface with `--ui` flag
|
|
73
|
+
- **CORS**: Cross-Origin Resource Sharing enabled for API access
|
|
74
|
+
- **Static Serving**: Production-ready static file serving
|
|
75
|
+
|
|
76
|
+
## Architecture
|
|
77
|
+
|
|
78
|
+
- **Express.js**: Backend API server
|
|
79
|
+
- **React 19**: Modern frontend framework
|
|
80
|
+
- **Material-UI v7**: Component library and theming
|
|
81
|
+
- **Client-Side Routing**: Single-page application with React Router
|
|
82
|
+
- **Mock Data**: Development-friendly mock data system
|
|
83
|
+
|
|
84
|
+
## Dependencies
|
|
85
|
+
|
|
86
|
+
### Backend
|
|
87
|
+
- `express`: Web application framework
|
|
88
|
+
- `cors`: Cross-Origin Resource Sharing
|
|
89
|
+
- `open`: Automatic browser launching
|
|
90
|
+
|
|
91
|
+
### Frontend
|
|
92
|
+
- `react` & `react-dom`: Core React libraries
|
|
93
|
+
- `@mui/material` & `@mui/icons-material`: Material Design components
|
|
94
|
+
- `@emotion/react` & `@emotion/styled`: CSS-in-JS styling
|
|
95
|
+
- `react-router-dom`: Client-side routing
|
|
96
|
+
|
|
97
|
+
### Development
|
|
98
|
+
- `vite`: Build tool and development server
|
|
99
|
+
- `@vitejs/plugin-react`: React integration for Vite
|
|
100
|
+
- `vitest`: Testing framework
|
|
101
|
+
- `@testing-library/*`: React testing utilities
|
package/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import open from 'open';
|
|
5
|
+
import * as mockData from './mockData.js'; // Import named exports, add .js extension
|
|
6
|
+
|
|
7
|
+
export default class serve {
|
|
8
|
+
static help = {
|
|
9
|
+
description: 'starts the L10n Monster server.',
|
|
10
|
+
options: [
|
|
11
|
+
[ '--port <number>', 'listen to specified port' ],
|
|
12
|
+
[ '--ui', 'also serve a web frontend' ],
|
|
13
|
+
]
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
static async action(mm, options) {
|
|
17
|
+
const port = options.port ?? 9691;
|
|
18
|
+
const app = express();
|
|
19
|
+
|
|
20
|
+
// === Middleware ===
|
|
21
|
+
app.use(cors());
|
|
22
|
+
app.use(express.json());
|
|
23
|
+
|
|
24
|
+
// === API Routes ===
|
|
25
|
+
const apiRouter = express.Router();
|
|
26
|
+
|
|
27
|
+
// GET /api/status
|
|
28
|
+
apiRouter.get('/status', async (req, res) => {
|
|
29
|
+
try {
|
|
30
|
+
const status = await mm.getTranslationStatus()
|
|
31
|
+
res.json(status);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error('Error fetching status: ', error);
|
|
34
|
+
res.status(500).json({ message: 'Problems fetching status data' });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// GET /api/untranslated/:sourceLang/:targetLang
|
|
39
|
+
apiRouter.get('/untranslated/:sourceLang/:targetLang', (req, res) => {
|
|
40
|
+
const { sourceLang, targetLang } = req.params;
|
|
41
|
+
const pairKey = `${sourceLang}_${targetLang}`;
|
|
42
|
+
const content = mockData.untranslatedContent[pairKey] || [];
|
|
43
|
+
res.json(content);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// GET /api/tm/stats/:sourceLang/:targetLang
|
|
47
|
+
apiRouter.get('/tm/stats/:sourceLang/:targetLang', (req, res) => {
|
|
48
|
+
const { sourceLang, targetLang } = req.params;
|
|
49
|
+
const pairKey = `${sourceLang}_${targetLang}`;
|
|
50
|
+
const tmInfo = mockData.tmData[pairKey] || { summary: { totalUnits: 0, lastUpdated: 'N/A' }, units: [] };
|
|
51
|
+
res.json(tmInfo);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Mount the API router under the /api prefix
|
|
55
|
+
app.use('/api', apiRouter);
|
|
56
|
+
|
|
57
|
+
if (options.ui) {
|
|
58
|
+
const uiDistPath = path.join(import.meta.dirname, 'ui', 'dist');
|
|
59
|
+
app.use(express.static(uiDistPath)); // rest of dist files
|
|
60
|
+
app.get('/*splat', (req, res) => res.sendFile(path.join(uiDistPath, 'index.html'))); // fallback for Client-Side Routing
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// === Start Server ===
|
|
64
|
+
app.listen(port, async () => {
|
|
65
|
+
console.log(`🚀 L10n Monster Server listening on port ${port}`);
|
|
66
|
+
options.ui && await open(`http://localhost:${port}`);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
package/mockData.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// server/data/mockData.js
|
|
2
|
+
|
|
3
|
+
// Use 'export const' for each piece of data
|
|
4
|
+
|
|
5
|
+
export const languagePairs = [
|
|
6
|
+
{ source: 'en-us', target: 'fr-fr', sourceName: 'English (US)', targetName: 'French (FR)', sourceFlag: '🇺🇸', targetFlag: '🇫🇷' },
|
|
7
|
+
{ source: 'en-us', target: 'de-de', sourceName: 'English (US)', targetName: 'German (DE)', sourceFlag: '🇺🇸', targetFlag: '🇩🇪' },
|
|
8
|
+
{ source: 'es-es', target: 'en-gb', sourceName: 'Spanish (ES)', targetName: 'English (UK)', sourceFlag: '🇪🇸', targetFlag: '🇬🇧' },
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export const projectsData = {
|
|
12
|
+
'en-us_fr-fr': {
|
|
13
|
+
'Website': [
|
|
14
|
+
{ id: 'proj1', name: 'Q3 Homepage Update', status: 'In Progress', progress: 80, totalWords: 1500, translatedWords: 1200, untranslatedWords: 300, dueDate: '2023-12-15' },
|
|
15
|
+
{ id: 'proj2', name: 'Blog Feature Launch', status: 'Needs Translation', progress: 0, totalWords: 500, translatedWords: 0, untranslatedWords: 500, dueDate: '2023-12-20' },
|
|
16
|
+
],
|
|
17
|
+
'Mobile App (iOS)': [
|
|
18
|
+
{ id: 'proj3', name: 'v2.5 String Updates', status: 'Completed', progress: 100, totalWords: 850, translatedWords: 850, untranslatedWords: 0, dueDate: '2023-11-30' },
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
'en-us_de-de': {
|
|
22
|
+
'Website': [
|
|
23
|
+
{ id: 'proj4', name: 'Q3 Homepage Update', status: 'Needs Review', progress: 100, totalWords: 1500, translatedWords: 1500, untranslatedWords: 0, dueDate: '2023-12-15' },
|
|
24
|
+
],
|
|
25
|
+
'Marketing Docs': [
|
|
26
|
+
{ id: 'proj5', name: 'Winter Sale Brochure', status: 'Needs Translation', progress: 0, totalWords: 2000, translatedWords: 0, untranslatedWords: 2000, dueDate: '2023-12-10' },
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
'es-es_en-gb': {
|
|
30
|
+
'General': [
|
|
31
|
+
{ id: 'proj6', name: 'Support Articles Q4', status: 'In Progress', progress: 50, totalWords: 5000, translatedWords: 2500, untranslatedWords: 2500, dueDate: '2024-01-10' },
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const untranslatedContent = {
|
|
37
|
+
'en-us_fr-fr': [
|
|
38
|
+
{ id: 'str1', sourceText: 'Welcome to our new feature!', contextKey: 'homepage.welcome_banner', channel: 'Website', project: 'Q3 Homepage Update', dateAdded: '2023-11-15' },
|
|
39
|
+
{ id: 'str2', sourceText: 'Click here to learn more.', contextKey: 'button.learn_more', channel: 'Website', project: 'Q3 Homepage Update', dateAdded: '2023-11-15' },
|
|
40
|
+
{ id: 'str3', sourceText: 'Update your profile settings.', contextKey: 'profile.update_prompt', channel: 'Mobile App (iOS)', project: 'v2.5 String Updates', dateAdded: '2023-11-10' },
|
|
41
|
+
{ id: 'str4', sourceText: 'Are you sure you want to delete?', contextKey: 'dialog.delete_confirm', channel: 'Mobile App (iOS)', project: 'v2.5 String Updates', dateAdded: '2023-11-10' },
|
|
42
|
+
{ id: 'str5', sourceText: 'Read our latest blog post on AI trends.', contextKey: 'blog.latest_post_title', channel: 'Website', project: 'Blog Feature Launch', dateAdded: '2023-11-18' },
|
|
43
|
+
],
|
|
44
|
+
'en-us_de-de': [
|
|
45
|
+
{ id: 'str6', sourceText: 'Discover our winter collection.', contextKey: 'marketing.winter_headline', channel: 'Marketing Docs', project: 'Winter Sale Brochure', dateAdded: '2023-11-20' },
|
|
46
|
+
{ id: 'str7', sourceText: 'Save up to 30%!', contextKey: 'marketing.winter_discount', channel: 'Marketing Docs', project: 'Winter Sale Brochure', dateAdded: '2023-11-20' },
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const tmData = {
|
|
51
|
+
'en-us_fr-fr': {
|
|
52
|
+
summary: { totalUnits: 15830, lastUpdated: '2023-11-20' },
|
|
53
|
+
units: [
|
|
54
|
+
{ id: 'tm1', sourceText: 'Save Changes', targetText: 'Enregistrer les modifications', contextKey: 'button.save', project: 'Q2 Settings Update', dateModified: '2023-06-10' },
|
|
55
|
+
{ id: 'tm2', sourceText: 'Cancel', targetText: 'Annuler', contextKey: 'button.cancel', project: 'Q2 Settings Update', dateModified: '2023-06-10' },
|
|
56
|
+
{ id: 'tm3', sourceText: 'Welcome back, {user}!', targetText: 'Bon retour, {user} !', contextKey: 'header.greeting', project: 'Initial Launch', dateModified: '2023-01-15' },
|
|
57
|
+
{ id: 'tm4', sourceText: 'View Details', targetText: 'Voir les détails', contextKey: 'link.view_details', project: 'Product Page v1', dateModified: '2023-03-22' },
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
'en-us_de-de': {
|
|
61
|
+
summary: { totalUnits: 12105, lastUpdated: '2023-11-18' },
|
|
62
|
+
units: [
|
|
63
|
+
{ id: 'tm5', sourceText: 'Save Changes', targetText: 'Änderungen speichern', contextKey: 'button.save', project: 'Q2 Settings Update', dateModified: '2023-06-10' },
|
|
64
|
+
{ id: 'tm6', sourceText: 'Cancel', targetText: 'Abbrechen', contextKey: 'button.cancel', project: 'Q2 Settings Update', dateModified: '2023-06-10' },
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@l10nmonster/server",
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
|
+
"description": "L10n Monster Manager UI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "index.js",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@emotion/react": "^11.14.0",
|
|
9
|
+
"@emotion/styled": "^11.14.0",
|
|
10
|
+
"@mui/icons-material": "^7.0.2",
|
|
11
|
+
"@mui/material": "^7.0.2",
|
|
12
|
+
"@testing-library/dom": "^10.4.0",
|
|
13
|
+
"@testing-library/user-event": "^14",
|
|
14
|
+
"cors": "^2.8.5",
|
|
15
|
+
"dotenv": "^16.5.0",
|
|
16
|
+
"express": "^5.1.0",
|
|
17
|
+
"open": "^10.1.1",
|
|
18
|
+
"react": "^19.1.0",
|
|
19
|
+
"react-dom": "^19.1.0",
|
|
20
|
+
"react-router-dom": "^7.5.2",
|
|
21
|
+
"web-vitals": "^5"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"dev": "vite",
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"preview": "vite preview",
|
|
27
|
+
"test": "node --test"
|
|
28
|
+
},
|
|
29
|
+
"eslintConfig": {
|
|
30
|
+
"extends": [
|
|
31
|
+
"react-app",
|
|
32
|
+
"react-app/jest"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"browserslist": {
|
|
36
|
+
"production": [
|
|
37
|
+
">0.2%",
|
|
38
|
+
"not dead",
|
|
39
|
+
"not op_mini all"
|
|
40
|
+
],
|
|
41
|
+
"development": [
|
|
42
|
+
"last 1 chrome version",
|
|
43
|
+
"last 1 firefox version",
|
|
44
|
+
"last 1 safari version"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@testing-library/jest-dom": "^6.6.3",
|
|
49
|
+
"@testing-library/react": "^16.3.0",
|
|
50
|
+
"@vitejs/plugin-react": "^4.4.1",
|
|
51
|
+
"jsdom": "^26.1.0",
|
|
52
|
+
"vite": "^6.3.3",
|
|
53
|
+
"vitest": "^3.1.2"
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}
|