Skip to content

Commit d4ebee5

Browse files
committed
feat: add i18next for internationalization and implement translation functionality
1 parent 119f39a commit d4ebee5

10 files changed

Lines changed: 87 additions & 13 deletions

File tree

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"axios": "^1.10.0",
1616
"dotenv": "^17.2.0",
1717
"fastify": "^5.4.0",
18+
"i18next": "^25.3.2",
1819
"lodash": "^4.17.21",
1920
"moment": "^2.30.1",
2021
"pg": "^8.16.3",

src/controllers/telegram.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { getAddressFromCoordinates, getGoogleMapsRoute } from '../services/googl
55
import { getTelegramUsers } from '../models/telegram-user'
66
import { MolinellaCoordinates } from '../utilites/constants'
77
import { InlineKeyboardButton } from 'telegraf/typings/core/types/typegram'
8-
import { getActualWorkShift } from '../utilites/common'
8+
import { getActualWorkShift, translateKey } from '../utilites/common'
99
import { ParsedMeteoAlert } from '../utilites/meteo-alerts'
1010

1111
const molinellaWord = 'MOLINELLA'
@@ -154,7 +154,7 @@ export const sendNewTomorrowAlertMessage = async (alert: ParsedMeteoAlert) => {
154154
const telegramUsers = await getTelegramUsers()
155155

156156
const criticDataMessage = Object.keys(alert.criticZoneData)
157-
.map((key) => `${key}: ${alert.criticZoneData[key]}`)
157+
.map((key) => `${key.toUpperCase()}: ${translateKey(`alert.colors.${alert.criticZoneData[key]}`, 'it')}`)
158158
.join('\n')
159159

160160
const textMessage = `⚠️ Nuova allerta meteo per domani!

src/models/last-alert-report.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { database } from '..'
2+
3+
const tableName = 'django_models_latestreport'
4+
5+
export interface LastAlertReport {
6+
id: number
7+
report_id: string
8+
}
9+
10+
export const getLastAlertReport = async (): Promise<LastAlertReport> => {
11+
const query = `SELECT * FROM ${tableName} `
12+
13+
const reports = await database.query<LastAlertReport>(query)
14+
15+
if (reports.length === 0) {
16+
throw new Error('No last alert report found')
17+
}
18+
19+
return reports[0]
20+
}
21+
22+
export const updateLastAlertReport = async (reportId: string): Promise<void> => {
23+
const query = `UPDATE ${tableName} SET report_id = $1 WHERE id = 1`
24+
25+
await database.query(query, [reportId])
26+
}

src/models/telegram-user.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,5 @@ export const getTelegramUsers = async (allowTest: boolean = true): Promise<Teleg
2222

2323
const query = `SELECT * FROM ${tableName} ${filterStatements.length > 0 ? 'WHERE ' + filterStatements.join(' AND ') : ''}`
2424

25-
const result = await database.query<TelegramUser>(query)
26-
27-
return result
25+
return database.query<TelegramUser>(query)
2826
}

src/resources/locales/it.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"alert": {
3+
"colors": {
4+
"red": "Rossa",
5+
"orange": "Arancione",
6+
"yellow": "Gialla",
7+
"green": "Verde"
8+
}
9+
}
10+
}

src/routes/meteo-alerts.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
import { sendNewTomorrowAlertMessage } from '../controllers/telegram'
2-
import { getMeteoAlert } from '../services/meteo-alerts'
2+
import { getLastAlertReport, updateLastAlertReport } from '../models/last-alert-report'
3+
import { getTomorrowMeteoAlert } from '../services/meteo-alerts'
34
import { parseMeteoAlert } from '../utilites/meteo-alerts'
45

56
export const registerMeteoAlertsRoutes = (fastify) => {
67
fastify.route({
78
method: 'POST',
89
url: '/meteo-alerts',
910
handler: async (_, reply) => {
10-
const tomorrowAlert = await getMeteoAlert('2025-07-07 13:00')
11+
const tomorrowAlert = await getTomorrowMeteoAlert()
1112

1213
if (!tomorrowAlert) {
1314
return reply.status(204).send(undefined)
1415
}
1516

1617
const parsedAlert = parseMeteoAlert(tomorrowAlert, 'D1')
1718

18-
if(parsedAlert.isCritic){
19-
sendNewTomorrowAlertMessage(parsedAlert)
19+
const lastAlertReport = await getLastAlertReport()
20+
21+
if (lastAlertReport.report_id !== parsedAlert.id) {
22+
if (parsedAlert.isCritic) {
23+
sendNewTomorrowAlertMessage(parsedAlert)
24+
}
25+
26+
await updateLastAlertReport(parsedAlert.id)
2027
}
2128

2229
reply.status(200).send(parsedAlert)

src/server.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,29 @@
11
import Fastify from 'fastify'
22
import { registerNotifireRoutes } from './routes/notifire'
33
import { registerMeteoAlertsRoutes } from './routes/meteo-alerts'
4+
import i18next from 'i18next'
5+
import italian from './resources/locales/it.json'
6+
7+
const translations = {
8+
it: {
9+
translation: italian,
10+
},
11+
}
412

513
export const startServer = async () => {
614
const fastify = Fastify({
715
logger: true,
816
disableRequestLogging: true,
917
})
1018

19+
void i18next.init({
20+
resources: translations,
21+
fallbackLng: 'it',
22+
interpolation: {
23+
escapeValue: false,
24+
},
25+
})
26+
1127
fastify.get('/health', async () => {
1228
return { status: 'ok' }
1329
})

src/utilites/common.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import customMoment from '../custom-components/custom-moment'
22
import { WORK_SHIFTS } from './constants'
3+
import i18next, { TOptions } from 'i18next'
34

45
export interface Workshift {
56
shift: string
@@ -54,3 +55,7 @@ export const getActualWorkShift = (isVolunteer: boolean): Workshift => {
5455
night: nightShift,
5556
}
5657
}
58+
59+
export const translateKey = (key: string, language: string, options: TOptions = {}) => {
60+
return i18next.t(key, { lng: language, ...options })
61+
}

src/utilites/meteo-alerts.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface ParsedMeteoAlert extends BaseMeteoAlert {
1010
}
1111

1212
const correctColors = ['green', null]
13+
const wordToRemove = /allerta|bollettino/i
1314

1415
export const parseMeteoAlert = (alert: MeteoAlert, zone: (typeof alertZones)[number]): ParsedMeteoAlert => {
1516
const zoneData = alert[zone]
@@ -24,13 +25,11 @@ export const parseMeteoAlert = (alert: MeteoAlert, zone: (typeof alertZones)[num
2425
throw new Error(`Invalid link format: ${alert.link}`)
2526
}
2627

27-
const id = fileName.replace('.pdf', '').replace('bollettino', '').replace('_', '/')
28+
const id = fileName.replace('.pdf', '').replace('_', '/').replace(wordToRemove, '').trim()
2829

2930
alert.link = `https://allertameteo.regione.emilia-romagna.it${alert.link}`
3031

31-
const criticData = Object.keys(zoneData).filter(
32-
(key) => !correctColors.includes(zoneData[key])
33-
)
32+
const criticData = Object.keys(zoneData).filter((key) => !correctColors.includes(zoneData[key]))
3433

3534
return {
3635
...omit(alert, alertZones),

yarn.lock

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
# yarn lockfile v1
33

44

5+
"@babel/runtime@^7.27.6":
6+
version "7.28.2"
7+
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.28.2.tgz#2ae5a9d51cc583bd1f5673b3bb70d6d819682473"
8+
integrity sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==
9+
510
"@cspotcode/source-map-support@^0.8.0":
611
version "0.8.1"
712
resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1"
@@ -1094,6 +1099,13 @@ humanize-ms@^1.2.1:
10941099
dependencies:
10951100
ms "^2.0.0"
10961101

1102+
i18next@^25.3.2:
1103+
version "25.3.2"
1104+
resolved "https://registry.yarnpkg.com/i18next/-/i18next-25.3.2.tgz#3d6a7d1dc058caa1b9bdca47fd585483e2e7a637"
1105+
integrity sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==
1106+
dependencies:
1107+
"@babel/runtime" "^7.27.6"
1108+
10971109
ignore-by-default@^1.0.1:
10981110
version "1.0.1"
10991111
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"

0 commit comments

Comments
 (0)