mirror of
https://github.com/ArthurDanjou/artdanj-shortener.git
synced 2026-01-14 13:54:03 +01:00
Add I18n
This commit is contained in:
@@ -23,9 +23,13 @@
|
|||||||
"@adonisjs/auth",
|
"@adonisjs/auth",
|
||||||
"@adonisjs/redis",
|
"@adonisjs/redis",
|
||||||
"@adonisjs/lucid",
|
"@adonisjs/lucid",
|
||||||
"@adonisjs/session"
|
"@adonisjs/session",
|
||||||
|
"@adonisjs/i18n"
|
||||||
],
|
],
|
||||||
"aceProviders": [
|
"aceProviders": [
|
||||||
"@adonisjs/repl"
|
"@adonisjs/repl"
|
||||||
|
],
|
||||||
|
"metaFiles": [
|
||||||
|
"resources/lang/**/*.(json|yaml)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
"name": "connection",
|
"name": "connection",
|
||||||
"propertyName": "connection",
|
"propertyName": "connection",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Define a custom database connection for the migration"
|
"description": "The connection flag is used to lookup the directory for the migration file"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "folder",
|
"name": "folder",
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import Link from "App/Models/Link";
|
|||||||
|
|
||||||
export default class LinksController {
|
export default class LinksController {
|
||||||
|
|
||||||
public async getLink ({params, response}: HttpContextContract) {
|
public async getLink ({params, response, i18n}: HttpContextContract) {
|
||||||
const code = params.id
|
const code = params.id
|
||||||
const link = await Link.findByOrFail('code', code)
|
const link = await Link.findByOrFail('code', code)
|
||||||
|
|
||||||
@@ -14,40 +14,44 @@ export default class LinksController {
|
|||||||
await link.merge({
|
await link.merge({
|
||||||
visitCount: visitCount
|
visitCount: visitCount
|
||||||
}).save()
|
}).save()
|
||||||
return response.redirect(link.target)
|
return response.status(200).redirect(link.target)
|
||||||
}
|
}
|
||||||
return response.badRequest(`Code does not exist ! (/${code})`)
|
return response.status(404).send({
|
||||||
|
message: i18n.formatMessage('messages.no_exists', { code: code })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getAllLinks ({response}: HttpContextContract) {
|
public async getAllLinks ({response}: HttpContextContract) {
|
||||||
const links = await Link.query().orderBy('id', 'asc')
|
const links = await Link.query().orderBy('id', 'asc')
|
||||||
return response.ok(links);
|
return response.status(200).send({
|
||||||
|
links
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async getVisitCount ({params}: HttpContextContract) {
|
public async getVisitCount ({params, response, i18n}: HttpContextContract) {
|
||||||
const code = params.id
|
const code = params.id
|
||||||
const link = await Link.findByOrFail('code', code)
|
const link = await Link.findByOrFail('code', code)
|
||||||
|
|
||||||
//Check if code exists
|
//Check if code exists
|
||||||
if (link.code === code) {
|
if (link.code === code) {
|
||||||
return {
|
return response.status(200).send({
|
||||||
count: link.visitCount
|
count: link.visitCount
|
||||||
}
|
})
|
||||||
}
|
|
||||||
return {
|
|
||||||
message: `Code does not exist ! (${code}`
|
|
||||||
}
|
}
|
||||||
|
return response.status(404).send({
|
||||||
|
message: i18n.formatMessage('messages.no_exists', { code: code })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createLink ({request, auth}: HttpContextContract) {
|
public async createLink ({response, request, auth, i18n}: HttpContextContract) {
|
||||||
await auth.authenticate()
|
await auth.authenticate()
|
||||||
const link = await Link.create(await request.validate(StoreValidator))
|
const link = await Link.create(await request.validate(StoreValidator))
|
||||||
return {
|
return response.status(200).send({
|
||||||
message: `Link successfully created : ${link.code} + ${link.target}`
|
message: i18n.formatMessage('messages.created', { link: link.code, target: link.target })
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateLink ({request, auth}: HttpContextContract) {
|
public async updateLink ({response, request, auth}: HttpContextContract) {
|
||||||
await auth.authenticate()
|
await auth.authenticate()
|
||||||
const link = await Link.findByOrFail('code', request.input('code'))
|
const link = await Link.findByOrFail('code', request.input('code'))
|
||||||
const data = await request.validate(UpdateValidator)
|
const data = await request.validate(UpdateValidator)
|
||||||
@@ -55,14 +59,19 @@ export default class LinksController {
|
|||||||
target: data.target,
|
target: data.target,
|
||||||
visitCount: 0
|
visitCount: 0
|
||||||
}).save()
|
}).save()
|
||||||
return link
|
return response.status(200).send({
|
||||||
|
link
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async deleteLink ({request, auth}: HttpContextContract) {
|
public async deleteLink ({i18n, response, request, auth}: HttpContextContract) {
|
||||||
await auth.authenticate()
|
await auth.authenticate()
|
||||||
const code = request.input('code')
|
const code = request.input('code')
|
||||||
const link = await Link.findByOrFail('code', code)
|
const link = await Link.findByOrFail('code', code)
|
||||||
await link.delete()
|
await link.delete()
|
||||||
|
return response.status(200).send({
|
||||||
|
message: i18n.formatMessage('messages.deleted', { link: link.code })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,39 @@ import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
|||||||
|
|
||||||
export default class UsersController {
|
export default class UsersController {
|
||||||
|
|
||||||
public async login ({request, auth}: HttpContextContract) {
|
public async login ({request, auth, response}: HttpContextContract) {
|
||||||
const email = request.input('email')
|
const email = request.input('email')
|
||||||
const password = request.input('password')
|
const password = request.input('password')
|
||||||
|
|
||||||
const token = await auth.use('api').attempt(email, password, {
|
const token = await auth.use('api').attempt(email, password, {
|
||||||
expiresIn: '2 days'
|
expiresIn: '2 days'
|
||||||
})
|
})
|
||||||
return token.toJSON()
|
return response.status(200).send({
|
||||||
|
token: token.toJSON()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async createInfiniteToken ({request, auth}: HttpContextContract) {
|
public async createInfiniteToken ({response, request, auth}: HttpContextContract) {
|
||||||
const email = request.input('email')
|
const email = request.input('email')
|
||||||
const password = request.input('password')
|
const password = request.input('password')
|
||||||
const token = await auth.use('api').attempt(email, password)
|
const token = await auth.use('api').attempt(email, password)
|
||||||
return token.toJSON()
|
return response.status(200).send({
|
||||||
|
token: token.toJSON()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async logout ({auth}: HttpContextContract) {
|
public async logout ({response, auth, i18n}: HttpContextContract) {
|
||||||
await auth.use('api').revoke()
|
await auth.use('api').revoke()
|
||||||
return { message: 'Vous avez été déconnecté' }
|
return response.status(200).send({
|
||||||
|
message: i18n.formatMessage('messages.logout')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async me ({auth}: HttpContextContract) {
|
public async me ({response, auth}: HttpContextContract) {
|
||||||
await auth.authenticate()
|
await auth.authenticate()
|
||||||
return auth.user
|
return response.status(200).send({
|
||||||
|
user: auth.user
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
45
app/Middleware/DetectUserLocale.ts
Normal file
45
app/Middleware/DetectUserLocale.ts
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import I18n from '@ioc:Adonis/Addons/I18n'
|
||||||
|
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The middleware detects the user language using the "Accept-language" HTTP header
|
||||||
|
* or the "lang" query string parameter.
|
||||||
|
*
|
||||||
|
* Feel free to change the middleware implementation to what suits your needs. Just
|
||||||
|
* make sure
|
||||||
|
*
|
||||||
|
* - You always ensure the user selected language is supported by your app.
|
||||||
|
* - Only call "switchLocale" when the detected language is valid string value and
|
||||||
|
* not "null" or "undefined"
|
||||||
|
*/
|
||||||
|
export default class DetectUserLocale {
|
||||||
|
/**
|
||||||
|
* Detect user language using "Accept-language" header or
|
||||||
|
* the "lang" query string parameter.
|
||||||
|
*
|
||||||
|
* The user language must be part of the "supportedLocales", otherwise
|
||||||
|
* this method should return null.
|
||||||
|
*/
|
||||||
|
protected getUserLanguage(ctx: HttpContextContract) {
|
||||||
|
const availableLocales = I18n.supportedLocales()
|
||||||
|
return ctx.request.language(availableLocales) || ctx.request.input('lang')
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle method is called by AdonisJS automatically on every middleware
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
|
public async handle(ctx: HttpContextContract, next: () => Promise<void>) {
|
||||||
|
const language = this.getUserLanguage(ctx)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Switch locale when we are able to detect the user language and it
|
||||||
|
* is supported by the application
|
||||||
|
*/
|
||||||
|
if (language) {
|
||||||
|
ctx.i18n.switchLocale(language)
|
||||||
|
}
|
||||||
|
|
||||||
|
await next()
|
||||||
|
}
|
||||||
|
}
|
||||||
104
config/i18n.ts
Normal file
104
config/i18n.ts
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**
|
||||||
|
* Config source: https://git.io/Jw53K
|
||||||
|
*
|
||||||
|
* Feel free to let us know via PR, if you find something broken in this config
|
||||||
|
* file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Application from '@ioc:Adonis/Core/Application'
|
||||||
|
import { I18nConfig } from '@ioc:Adonis/Addons/I18n'
|
||||||
|
|
||||||
|
const i18nConfig: I18nConfig = {
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Translations format
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The format in which the translation are written. By default only the
|
||||||
|
| ICU message syntax is supported. However, you can register custom
|
||||||
|
| formatters too and please reference the documentation for that.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
translationsFormat: 'icu',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Default locale
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| The default locale represents the language for which all the translations
|
||||||
|
| are always available.
|
||||||
|
|
|
||||||
|
| Having a default locale allows you to incrementally add translations for
|
||||||
|
| other languages. If a specific language does not have a translation,
|
||||||
|
| then the default locale translation will be used.
|
||||||
|
|
|
||||||
|
| Also, we switch to default locale for HTTP requests where the user language
|
||||||
|
| is not supported by the your app
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
defaultLocale: 'en',
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Supported locales
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Optionally define an array of locales that your application supports. If
|
||||||
|
| not defined, we will derive this value from the translations stored
|
||||||
|
| inside the `resources/lang` directory.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
supportedLocales: [
|
||||||
|
'fr',
|
||||||
|
'en'
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Fallback locales
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Here you can configure per language fallbacks. For example, you can set
|
||||||
|
| "es" as the fallback locale for the Catalan language.
|
||||||
|
|
|
||||||
|
| If not configured, all languages will fallback to the defaultLocale
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
// fallbackLocales: {},
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Provide validator messages
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Set the following option to "true" if you want to use "i18n" for defining
|
||||||
|
| the validation messages.
|
||||||
|
|
|
||||||
|
| The validation messages will be loaded from the "validator.shared" prefix.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
provideValidatorMessages: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Loaders
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Loaders from which to load the translations. You can configure multiple
|
||||||
|
| loaders as well and AdonisJS will merge the translations from all the
|
||||||
|
| loaders to have a unified collection of messages.
|
||||||
|
|
|
||||||
|
| By default, only the "fs" loader is supported. However, you can add custom
|
||||||
|
| loaders too and please reference the documentation for that.
|
||||||
|
|
|
||||||
|
*/
|
||||||
|
loaders: {
|
||||||
|
fs: {
|
||||||
|
enabled: true,
|
||||||
|
location: Application.resourcesPath('lang'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export default i18nConfig
|
||||||
25
package.json
25
package.json
@@ -21,24 +21,25 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@adonisjs/assembler": "^5.3.2",
|
"@adonisjs/assembler": "^5.3.2",
|
||||||
"adonis-preset-ts": "^2.1.0",
|
"adonis-preset-ts": "^2.1.0",
|
||||||
"pino-pretty": "^5.0.2",
|
"pino-pretty": "^7.1.0",
|
||||||
"typescript": "^4.3.4",
|
"typescript": "^4.4.4",
|
||||||
"youch": "^2.2.2",
|
"youch": "^2.2.2",
|
||||||
"youch-terminal": "^1.1.1"
|
"youch-terminal": "^1.1.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adonisjs/auth": "^8.0.6",
|
"@adonisjs/auth": "^8.0.10",
|
||||||
"@adonisjs/core": "^5.1.8",
|
"@adonisjs/core": "^5.4.0",
|
||||||
"@adonisjs/lucid": "^15.0.1",
|
"@adonisjs/i18n": "^1.5.2",
|
||||||
"@adonisjs/redis": "^7.0.6",
|
"@adonisjs/lucid": "^16.2.1",
|
||||||
"@adonisjs/repl": "^3.1.4",
|
"@adonisjs/redis": "^7.0.9",
|
||||||
"@adonisjs/session": "^6.0.6",
|
"@adonisjs/repl": "^3.1.7",
|
||||||
"luxon": "^1.27.0",
|
"@adonisjs/session": "^6.1.2",
|
||||||
|
"luxon": "^2.0.2",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"phc-argon2": "^1.1.1",
|
"phc-argon2": "^1.1.2",
|
||||||
"proxy-addr": "^2.0.7",
|
"proxy-addr": "^2.0.7",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"source-map-support": "^0.5.19",
|
"source-map-support": "^0.5.20",
|
||||||
"tslib": "^2.3.0"
|
"tslib": "^2.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
resources/lang/en/messages.json
Normal file
6
resources/lang/en/messages.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"no_exists": "The code '{code}' does not exist!",
|
||||||
|
"created": "The link '{link}' with target '{target}' was successfully created!",
|
||||||
|
"deleted": "The link '{link}' was successfully deleted!",
|
||||||
|
"logout": "You have been disconnected!"
|
||||||
|
}
|
||||||
6
resources/lang/fr/messages.json
Normal file
6
resources/lang/fr/messages.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"no_exists": "Le code '{code}' n'existe pas!",
|
||||||
|
"created": "Le lien '{link}' avec pour direction '{target}' a bien été créé!",
|
||||||
|
"deleted": "Le lien '{link}' a bien été supprimé!",
|
||||||
|
"logout": "Vous avez été déconnecté !"
|
||||||
|
}
|
||||||
@@ -22,7 +22,8 @@ import Server from '@ioc:Adonis/Core/Server'
|
|||||||
*/
|
*/
|
||||||
Server.middleware.register([
|
Server.middleware.register([
|
||||||
() => import('@ioc:Adonis/Core/BodyParser'),
|
() => import('@ioc:Adonis/Core/BodyParser'),
|
||||||
() => import('App/Middleware/SilentAuth')
|
() => import('App/Middleware/SilentAuth'),
|
||||||
|
() => import('App/Middleware/DetectUserLocale')
|
||||||
])
|
])
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ Route.get('/source', async ({response}: HttpContextContract) => {
|
|||||||
Route.get('health', async ({response}: HttpContextContract) => {
|
Route.get('health', async ({response}: HttpContextContract) => {
|
||||||
const report = await HealthCheck.getReport()
|
const report = await HealthCheck.getReport()
|
||||||
const isLive = await HealthCheck.isLive()
|
const isLive = await HealthCheck.isLive()
|
||||||
const isReady = await HealthCheck.isReady()
|
const isReady = HealthCheck.isReady()
|
||||||
return report.healthy ? response.ok({ isLive, isReady, report: report.report }) : response.badRequest({ isLive, isReady, report: report.report })
|
return report.healthy ? response.ok({ isLive, isReady, report: report.report }) : response.badRequest({ isLive, isReady, report: report.report })
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,8 @@
|
|||||||
"@adonisjs/auth",
|
"@adonisjs/auth",
|
||||||
"@adonisjs/redis",
|
"@adonisjs/redis",
|
||||||
"@adonisjs/lucid",
|
"@adonisjs/lucid",
|
||||||
"@adonisjs/session"
|
"@adonisjs/session",
|
||||||
|
"@adonisjs/i18n"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user