@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 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}