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>التطبيق لـ 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>app 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>appen 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>appen 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>app 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",