From a44c3e2261383f5a381b13cd6ac33e32f0535c58 Mon Sep 17 00:00:00 2001
From: Sjur Sutterud Sagen
Date: Tue, 5 Mar 2024 12:06:40 +0100
Subject: [PATCH] Add typesafety/intellisense to translation keys
Since we are going to be translating the app with i18next, it will be
really helpful to get feedback during dev-time for mistakes made with
the translation keys. This will give us errors when we try to use keys
that do not exist, and also add auto-complete for the translation keys.
The auto-complete is a bit fiddly, and in "t('')" it seems to be
activated by adding the ''. So if you don't get it, remove and re-add
them.
Also added an example of using the Trans component for i18n.
Lastly, to build the 'resources.d.ts' file from our translation files,
I installed the package i18next-resources-for-ts. It is fairly new,
but has a score of 75 in the snyk-advisor. One thing to consider here
is that, this is only needed since we use JSON files for storing the
translations, if instead we used .ts file, this might not be needed.
However, our current users are used to JSON files, so did not change
this.
---
.../pxweb2/public/locales/ar/translation.json | 3 ++-
.../pxweb2/public/locales/en/translation.json | 3 ++-
.../pxweb2/public/locales/no/translation.json | 3 ++-
.../pxweb2/public/locales/sv/translation.json | 3 ++-
apps/pxweb2/src/@types/i18next.d.ts | 9 +++++++
apps/pxweb2/src/@types/resources.d.ts | 15 +++++++++++
apps/pxweb2/src/app/app.tsx | 7 ++++-
apps/pxweb2/src/i18n/config.ts | 3 +++
apps/pxweb2/tsconfig.json | 3 ++-
package-lock.json | 26 +++++++++++++++++++
package.json | 4 ++-
11 files changed, 72 insertions(+), 7 deletions(-)
create mode 100644 apps/pxweb2/src/@types/i18next.d.ts
create mode 100644 apps/pxweb2/src/@types/resources.d.ts
diff --git a/apps/pxweb2/public/locales/ar/translation.json b/apps/pxweb2/public/locales/ar/translation.json
index 7b0f92a0..97713e8c 100644
--- a/apps/pxweb2/public/locales/ar/translation.json
+++ b/apps/pxweb2/public/locales/ar/translation.json
@@ -1,6 +1,7 @@
{
"app_title": "مرحبًا بك في PxWeb",
"main": {
- "header": "مرحبا بكم في التطبيق!"
+ "header": "مرحبا بكم في التطبيق!",
+ "welcome_trans_test": "مرحبًا بك في <1>التطبيق1> لـ PxWeb 2.0!"
}
}
diff --git a/apps/pxweb2/public/locales/en/translation.json b/apps/pxweb2/public/locales/en/translation.json
index fc17ce89..53fbe24a 100644
--- a/apps/pxweb2/public/locales/en/translation.json
+++ b/apps/pxweb2/public/locales/en/translation.json
@@ -1,7 +1,8 @@
{
"app_title": "Welcome to PxWeb",
"main": {
- "header": "Welcome to the app!"
+ "header": "Welcome to the app!",
+ "welcome_trans_test": "Welcome to the <1>app1> for PxWeb 2.0!"
},
"date": {
"simple_date": "{{value, datetime}}",
diff --git a/apps/pxweb2/public/locales/no/translation.json b/apps/pxweb2/public/locales/no/translation.json
index ffed364d..3124ec0f 100644
--- a/apps/pxweb2/public/locales/no/translation.json
+++ b/apps/pxweb2/public/locales/no/translation.json
@@ -1,6 +1,7 @@
{
"app_title": "Velkommen til PxWeb",
"main": {
- "header": "Velkommen til appen!"
+ "header": "Velkommen til appen!",
+ "welcome_trans_test": "Velkommen til <1>appen1> for PxWeb 2.0!"
}
}
diff --git a/apps/pxweb2/public/locales/sv/translation.json b/apps/pxweb2/public/locales/sv/translation.json
index 71fe8365..2f5fcc06 100644
--- a/apps/pxweb2/public/locales/sv/translation.json
+++ b/apps/pxweb2/public/locales/sv/translation.json
@@ -1,6 +1,7 @@
{
"app_title": "Välkommen till PxWeb",
"main": {
- "header": "Välkommen till appen!"
+ "header": "Välkommen till appen!",
+ "welcome_trans_test": "Välkommen till <1>appen1> för PxWeb 2.0!"
}
}
diff --git a/apps/pxweb2/src/@types/i18next.d.ts b/apps/pxweb2/src/@types/i18next.d.ts
new file mode 100644
index 00000000..5e2924da
--- /dev/null
+++ b/apps/pxweb2/src/@types/i18next.d.ts
@@ -0,0 +1,9 @@
+import Resources from './resources';
+import { defaultNS } from '../i18n/config';
+
+declare module 'i18next' {
+ interface CustomTypeOptions {
+ defaultNS: typeof defaultNS;
+ resources: Resources;
+ }
+}
diff --git a/apps/pxweb2/src/@types/resources.d.ts b/apps/pxweb2/src/@types/resources.d.ts
new file mode 100644
index 00000000..e82d89e7
--- /dev/null
+++ b/apps/pxweb2/src/@types/resources.d.ts
@@ -0,0 +1,15 @@
+interface Resources {
+ "translation": {
+ "app_title": "Welcome to PxWeb",
+ "main": {
+ "header": "Welcome to the app!",
+ "welcome_trans_test": "Welcome to the <1>app1> for PxWeb 2.0!"
+ },
+ "date": {
+ "simple_date": "{{value, datetime}}",
+ "simple_date_with_time": "{{value, datetime(year: 'numeric'; month: 'numeric'; day: 'numeric'; hour: 'numeric'; minute: 'numeric')}}"
+ }
+ }
+}
+
+export default Resources;
diff --git a/apps/pxweb2/src/app/app.tsx b/apps/pxweb2/src/app/app.tsx
index 6b0c7a27..86544aa6 100644
--- a/apps/pxweb2/src/app/app.tsx
+++ b/apps/pxweb2/src/app/app.tsx
@@ -1,4 +1,4 @@
-import { useTranslation } from 'react-i18next';
+import { useTranslation, Trans } from 'react-i18next';
import {
Button,
@@ -113,6 +113,11 @@ export function App() {
value: new Date('2024-01-25 12:34:56'),
})}
+
+
+ "Welcome to the app for PxWeb 2.0!"
+
+
>
);
}
diff --git a/apps/pxweb2/src/i18n/config.ts b/apps/pxweb2/src/i18n/config.ts
index fe3a16cf..7a47c991 100644
--- a/apps/pxweb2/src/i18n/config.ts
+++ b/apps/pxweb2/src/i18n/config.ts
@@ -2,6 +2,8 @@ import i18n from 'i18next';
import HttpApi from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
+export const defaultNS = 'translation';
+
i18n
.use(HttpApi)
.use(initReactI18next)
@@ -15,6 +17,7 @@ i18n
},
lng: 'en',
fallbackLng: 'en',
+ defaultNS,
// Explicitly tell i18next our
// supported locales.
supportedLngs: ['en', 'no', 'sv', 'ar'],
diff --git a/apps/pxweb2/tsconfig.json b/apps/pxweb2/tsconfig.json
index fdfab6c3..816c7a16 100644
--- a/apps/pxweb2/tsconfig.json
+++ b/apps/pxweb2/tsconfig.json
@@ -5,7 +5,8 @@
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
- "types": ["vite/client", "vitest"]
+ "types": ["vite/client", "vitest"],
+ "resolveJsonModule": true,
},
"files": [],
"include": [],
diff --git a/package-lock.json b/package-lock.json
index 5feab8ec..b9e874d2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,7 @@
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
+ "i18next-resources-for-ts": "^1.5.0",
"jsdom": "~22.1.0",
"nx": "18.0.5",
"prettier": "^2.8.8",
@@ -16203,6 +16204,31 @@
"cross-fetch": "4.0.0"
}
},
+ "node_modules/i18next-resources-for-ts": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/i18next-resources-for-ts/-/i18next-resources-for-ts-1.5.0.tgz",
+ "integrity": "sha512-gzVYct/sMeeOzomK0+8nl7YSr61xiDzf4Kg51g7StBaXAaKE/Vb1CLNHZ8dY56sYz6qmbwHRYHqMPanGi0go4w==",
+ "dev": true,
+ "dependencies": {
+ "@babel/runtime": "^7.23.7",
+ "yaml": "^2.3.4"
+ },
+ "bin": {
+ "i18next-resources-for-ts": "bin/i18next-resources-for-ts.js"
+ }
+ },
+ "node_modules/i18next-resources-for-ts/node_modules/yaml": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.0.tgz",
+ "integrity": "sha512-j9iR8g+/t0lArF4V6NE/QCfT+CO7iLqrXAHZbJdo+LfjqP1vR8Fg5bSiaq6Q2lOD1AUEVrEVIgABvBFYojJVYQ==",
+ "dev": true,
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
diff --git a/package.json b/package.json
index ff1440ec..0c517cd1 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,8 @@
"build": "nx run pxweb2:build && nx run pxweb2-ui:build-storybook",
"start-storybook": "npx nx run pxweb2-ui:storybook",
"build-storybook": "npx nx run pxweb2-ui:build-storybook",
- "build-style-dictionary": "node libs/pxweb2-ui/style-dictionary/build.js"
+ "build-style-dictionary": "node libs/pxweb2-ui/style-dictionary/build.js",
+ "build-i18n-interface": "npx i18next-resources-for-ts interface -i ./apps/pxweb2/public/locales/en -o ./apps/pxweb2/src/@types/resources.d.ts"
},
"private": true,
"dependencies": {
@@ -62,6 +63,7 @@
"eslint-plugin-jsx-a11y": "6.7.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
+ "i18next-resources-for-ts": "^1.5.0",
"jsdom": "~22.1.0",
"nx": "18.0.5",
"prettier": "^2.8.8",