mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
feat: i18n middleware
feat: i18n configuration. feat: i18n with tenancy.
This commit is contained in:
@@ -76,4 +76,8 @@ module.exports = {
|
|||||||
jwtSecret: 'b0JDZW56RnV6aEthb0RGPXVEcUI',
|
jwtSecret: 'b0JDZW56RnV6aEthb0RGPXVEcUI',
|
||||||
contactUsMail: 'support@bigcapital.ly',
|
contactUsMail: 'support@bigcapital.ly',
|
||||||
baseURL: 'https://bigcapital.ly',
|
baseURL: 'https://bigcapital.ly',
|
||||||
|
|
||||||
|
api: {
|
||||||
|
prefix: '/api'
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hapi/boom": "^7.4.3",
|
"@hapi/boom": "^7.4.3",
|
||||||
|
"@types/i18n": "^0.8.7",
|
||||||
"agenda": "^3.1.0",
|
"agenda": "^3.1.0",
|
||||||
"agendash": "^1.0.0",
|
"agendash": "^1.0.0",
|
||||||
"app-root-path": "^3.0.0",
|
"app-root-path": "^3.0.0",
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'reflect-metadata';
|
import 'reflect-metadata';
|
||||||
import { Router, Request, Response } from 'express';
|
import { Router, Request, Response } from 'express';
|
||||||
|
import i18n from 'i18n';
|
||||||
|
|
||||||
export default class Ping {
|
export default class Ping {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import express from 'express';
|
import { Router } from 'express';
|
||||||
import { Container } from 'typedi';
|
import { Container } from 'typedi';
|
||||||
|
|
||||||
// Middlewares
|
// Middlewares
|
||||||
@@ -8,6 +8,7 @@ import SubscriptionMiddleware from '@/http/middleware/SubscriptionMiddleware';
|
|||||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||||
import EnsureTenantIsInitialized from '@/http/middleware/EnsureTenantIsInitialized';
|
import EnsureTenantIsInitialized from '@/http/middleware/EnsureTenantIsInitialized';
|
||||||
import SettingsMiddleware from '@/http/middleware/SettingsMiddleware';
|
import SettingsMiddleware from '@/http/middleware/SettingsMiddleware';
|
||||||
|
import I18nMiddleware from '@/http/middleware/I18nMiddleware';
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
import Authentication from '@/http/controllers/Authentication';
|
import Authentication from '@/http/controllers/Authentication';
|
||||||
@@ -36,43 +37,49 @@ import Agendash from '@/http/controllers/Agendash';
|
|||||||
import Subscription from '@/http/controllers/Subscription';
|
import Subscription from '@/http/controllers/Subscription';
|
||||||
import VouchersController from '@/http/controllers/Subscription/Vouchers';
|
import VouchersController from '@/http/controllers/Subscription/Vouchers';
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const app = Router();
|
||||||
|
|
||||||
export default (app) => {
|
app.use(I18nMiddleware);
|
||||||
app.use('/api/auth', Container.get(Authentication).router());
|
|
||||||
app.use('/api/invite', Container.get(InviteUsers).router());
|
|
||||||
app.use('/api/organization', Container.get(Organization).router());
|
|
||||||
app.use('/api/vouchers', Container.get(VouchersController).router());
|
|
||||||
app.use('/api/subscription', Container.get(Subscription).router());
|
|
||||||
app.use('/api/ping', Container.get(Ping).router());
|
|
||||||
|
|
||||||
const dashboard = express.Router();
|
app.use('/auth', Container.get(Authentication).router());
|
||||||
|
app.use('/invite', Container.get(InviteUsers).router());
|
||||||
|
app.use('/organization', Container.get(Organization).router());
|
||||||
|
app.use('/vouchers', Container.get(VouchersController).router());
|
||||||
|
app.use('/subscription', Container.get(Subscription).router());
|
||||||
|
app.use('/ping', Container.get(Ping).router());
|
||||||
|
|
||||||
|
const dashboard = Router();
|
||||||
|
|
||||||
dashboard.use(JWTAuth);
|
dashboard.use(JWTAuth);
|
||||||
dashboard.use(AttachCurrentTenantUser)
|
dashboard.use(AttachCurrentTenantUser)
|
||||||
dashboard.use(TenancyMiddleware);
|
dashboard.use(TenancyMiddleware);
|
||||||
|
dashboard.use(I18nMiddleware);
|
||||||
dashboard.use(SubscriptionMiddleware('main'));
|
dashboard.use(SubscriptionMiddleware('main'));
|
||||||
dashboard.use(EnsureTenantIsInitialized);
|
dashboard.use(EnsureTenantIsInitialized);
|
||||||
dashboard.use(SettingsMiddleware);
|
dashboard.use(SettingsMiddleware);
|
||||||
|
|
||||||
dashboard.use('/api/users', Users.router());
|
dashboard.use('/users', Users.router());
|
||||||
dashboard.use('/api/currencies', Currencies.router());
|
dashboard.use('/currencies', Currencies.router());
|
||||||
dashboard.use('/api/accounts', Accounts.router());
|
dashboard.use('/accounts', Accounts.router());
|
||||||
dashboard.use('/api/account_types', AccountTypes.router());
|
dashboard.use('/account_types', AccountTypes.router());
|
||||||
dashboard.use('/api/accounting', Accounting.router());
|
dashboard.use('/accounting', Accounting.router());
|
||||||
dashboard.use('/api/views', Views.router());
|
dashboard.use('/views', Views.router());
|
||||||
dashboard.use('/api/items', Container.get(Items).router());
|
dashboard.use('/items', Container.get(Items).router());
|
||||||
dashboard.use('/api/item_categories', Container.get(ItemCategories).router());
|
dashboard.use('/item_categories', Container.get(ItemCategories).router());
|
||||||
dashboard.use('/api/expenses', Expenses.router());
|
dashboard.use('/expenses', Expenses.router());
|
||||||
dashboard.use('/api/financial_statements', FinancialStatements.router());
|
dashboard.use('/financial_statements', FinancialStatements.router());
|
||||||
dashboard.use('/api/settings', Container.get(Settings).router());
|
dashboard.use('/settings', Container.get(Settings).router());
|
||||||
dashboard.use('/api/sales', Sales.router());
|
dashboard.use('/sales', Sales.router());
|
||||||
dashboard.use('/api/customers', Customers.router());
|
dashboard.use('/customers', Customers.router());
|
||||||
dashboard.use('/api/vendors', Vendors.router());
|
dashboard.use('/vendors', Vendors.router());
|
||||||
dashboard.use('/api/purchases', Purchases.router());
|
dashboard.use('/purchases', Purchases.router());
|
||||||
dashboard.use('/api/resources', Resources.router());
|
dashboard.use('/resources', Resources.router());
|
||||||
dashboard.use('/api/exchange_rates', ExchangeRates.router());
|
dashboard.use('/exchange_rates', ExchangeRates.router());
|
||||||
dashboard.use('/api/media', Media.router());
|
dashboard.use('/media', Media.router());
|
||||||
|
|
||||||
app.use('/agendash', Agendash.router());
|
app.use('/agendash', Agendash.router());
|
||||||
app.use('/', dashboard);
|
app.use('/', dashboard);
|
||||||
|
|
||||||
|
return app;
|
||||||
};
|
};
|
||||||
|
|||||||
15
server/src/http/middleware/I18nMiddleware.ts
Normal file
15
server/src/http/middleware/I18nMiddleware.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { Container } from 'typedi';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import i18n from 'i18n';
|
||||||
|
|
||||||
|
export default (req: Request, res: Response, next: NextFunction) => {
|
||||||
|
const Logger = Container.get('logger');
|
||||||
|
let language = req.headers['accept-language'] || 'en';
|
||||||
|
|
||||||
|
if (req.user && req.user.language) {
|
||||||
|
language = req.user.language;
|
||||||
|
}
|
||||||
|
Logger.info('[i18n_middleware] set locale language to i18n.', { language, user: req.user });
|
||||||
|
i18n.setLocale(req, language);
|
||||||
|
next();
|
||||||
|
};
|
||||||
@@ -48,5 +48,9 @@ export default async (req, res, next) => {
|
|||||||
tenantContainer.set('tenant', tenant);
|
tenantContainer.set('tenant', tenant);
|
||||||
Logger.info('[tenancy_middleware] tenant dependencies injected to container.');
|
Logger.info('[tenancy_middleware] tenant dependencies injected to container.');
|
||||||
|
|
||||||
|
if (res.locals) {
|
||||||
|
tenantContainer.set('i18n', res.locals);
|
||||||
|
Logger.info('[tenancy_middleware] i18n locals injected.');
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,18 +20,19 @@ export default class WelcomeEmailJob {
|
|||||||
* @param {Function} done
|
* @param {Function} done
|
||||||
*/
|
*/
|
||||||
public async handler(job, done: Function): Promise<void> {
|
public async handler(job, done: Function): Promise<void> {
|
||||||
const { user, token } = job.attrs.data;
|
const { data } = job.attrs;
|
||||||
|
const { user, token } = data;
|
||||||
const Logger = Container.get('logger');
|
const Logger = Container.get('logger');
|
||||||
const authService = Container.get(AuthenticationService);
|
const authService = Container.get(AuthenticationService);
|
||||||
|
|
||||||
Logger.info(`[send_reset_password] started: ${job.attrs.data}`);
|
Logger.info(`[send_reset_password] started.`, { data });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await authService.mailMessages.sendResetPasswordMessage(user, token);
|
await authService.mailMessages.sendResetPasswordMessage(user, token);
|
||||||
Logger.info(`[send_reset_password] finished: ${job.attrs.data}`);
|
Logger.info(`[send_reset_password] finished.`, { data });
|
||||||
done()
|
done()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Logger.info(`[send_reset_password] error: ${job.attrs.data}, error: ${error}`);
|
Logger.error(`[send_reset_password] error.`, { data, error });
|
||||||
done(error);
|
done(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import agendaFactory from '@/loaders/agenda';
|
|||||||
import SmsClientLoader from '@/loaders/smsClient';
|
import SmsClientLoader from '@/loaders/smsClient';
|
||||||
import mailInstance from '@/loaders/mail';
|
import mailInstance from '@/loaders/mail';
|
||||||
import dbManagerFactory from '@/loaders/dbManager';
|
import dbManagerFactory from '@/loaders/dbManager';
|
||||||
|
import i18n from '@/loaders/i18n';
|
||||||
|
|
||||||
export default ({ mongoConnection, knex }) => {
|
export default ({ mongoConnection, knex }) => {
|
||||||
try {
|
try {
|
||||||
@@ -29,6 +30,9 @@ export default ({ mongoConnection, knex }) => {
|
|||||||
Container.set('agenda', agendaInstance);
|
Container.set('agenda', agendaInstance);
|
||||||
LoggerInstance.info('Agenda has been injected into container');
|
LoggerInstance.info('Agenda has been injected into container');
|
||||||
|
|
||||||
|
Container.set('i18n', i18n);
|
||||||
|
LoggerInstance.info('i18n has been injected into container');
|
||||||
|
|
||||||
return { agenda: agendaInstance };
|
return { agenda: agendaInstance };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
LoggerInstance.error('Error on dependency injector loader: %o', e);
|
LoggerInstance.error('Error on dependency injector loader: %o', e);
|
||||||
|
|||||||
@@ -2,20 +2,42 @@ import express from 'express';
|
|||||||
import helmet from 'helmet';
|
import helmet from 'helmet';
|
||||||
import boom from 'express-boom';
|
import boom from 'express-boom';
|
||||||
import errorHandler from 'errorhandler';
|
import errorHandler from 'errorhandler';
|
||||||
import i18n from 'i18n';
|
|
||||||
import fileUpload from 'express-fileupload';
|
import fileUpload from 'express-fileupload';
|
||||||
|
import i18n from 'i18n';
|
||||||
import routes from '@/http';
|
import routes from '@/http';
|
||||||
|
import config from '@/../config/config';
|
||||||
|
|
||||||
export default ({ app }) => {
|
export default ({ app }) => {
|
||||||
// Express configuration.
|
// Express configuration.
|
||||||
app.set('port', 3000);
|
app.set('port', 3000);
|
||||||
|
|
||||||
|
// Helmet helps you secure your Express apps by setting various HTTP headers.
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
|
|
||||||
|
// Allow to full error stack traces and internal details
|
||||||
app.use(errorHandler());
|
app.use(errorHandler());
|
||||||
|
|
||||||
|
// Boom response objects.
|
||||||
app.use(boom());
|
app.use(boom());
|
||||||
|
|
||||||
|
// Parses both json and urlencoded.
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
|
|
||||||
|
// Handle multi-media requests.
|
||||||
app.use(fileUpload({
|
app.use(fileUpload({
|
||||||
createParentPath: true,
|
createParentPath: true,
|
||||||
}));
|
}));
|
||||||
routes(app);
|
|
||||||
|
// Initialize i18n node.
|
||||||
|
app.use(i18n.init)
|
||||||
|
|
||||||
|
// Prefix all application routes.
|
||||||
|
app.use(config.api.prefix, routes());
|
||||||
|
|
||||||
|
// catch 404 and forward to error handler
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
const err = new Error('Not Found');
|
||||||
|
err['status'] = 404;
|
||||||
|
next(err);
|
||||||
|
})
|
||||||
};
|
};
|
||||||
9
server/src/loaders/i18n.ts
Normal file
9
server/src/loaders/i18n.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import i18n from 'i18n';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
export default () => i18n.configure({
|
||||||
|
locales: ['en', 'ar'],
|
||||||
|
register: global,
|
||||||
|
directory: path.join(global.rootPath, 'src/locales'),
|
||||||
|
updateFiles: false
|
||||||
|
})
|
||||||
@@ -5,6 +5,7 @@ import expressLoader from '@/loaders/express';
|
|||||||
import databaseLoader from '@/database/knex';
|
import databaseLoader from '@/database/knex';
|
||||||
import dependencyInjectorLoader from '@/loaders/dependencyInjector';
|
import dependencyInjectorLoader from '@/loaders/dependencyInjector';
|
||||||
import objectionLoader from '@/database/objection';
|
import objectionLoader from '@/database/objection';
|
||||||
|
import i18nConfig from '@/loaders/i18n';
|
||||||
|
|
||||||
// We have to import at least all the events once so they can be triggered
|
// We have to import at least all the events once so they can be triggered
|
||||||
import '@/loaders/events';
|
import '@/loaders/events';
|
||||||
@@ -29,4 +30,7 @@ export default async ({ expressApp }) => {
|
|||||||
|
|
||||||
expressLoader({ app: expressApp });
|
expressLoader({ app: expressApp });
|
||||||
Logger.info('Express loaded');
|
Logger.info('Express loaded');
|
||||||
|
|
||||||
|
i18nConfig();
|
||||||
|
Logger.info('I18n node configured.');
|
||||||
};
|
};
|
||||||
|
|||||||
4
server/src/locales/ar.json
Normal file
4
server/src/locales/ar.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Empty": "",
|
||||||
|
"Hello": "مرحبا"
|
||||||
|
}
|
||||||
4
server/src/locales/en.json
Normal file
4
server/src/locales/en.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"Empty": "",
|
||||||
|
"Hello": "Hello"
|
||||||
|
}
|
||||||
@@ -17,4 +17,12 @@ export default class HasTenancyService {
|
|||||||
models(tenantId: number) {
|
models(tenantId: number) {
|
||||||
return this.tenantContainer(tenantId).get('models');
|
return this.tenantContainer(tenantId).get('models');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve i18n locales methods.
|
||||||
|
* @param {number} tenantId
|
||||||
|
*/
|
||||||
|
i18n(tenantId: number) {
|
||||||
|
this.tenantContainer(tenantId).get('i18n');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user