Search code examples
next.jsi18nextreact-i18nextnext-i18next

Hiding locales using next-i18next


Environment

I am making a website with some administration/hidden for regular user functionality. The application has internationalization. Main stack:

  • Next
  • next-i18next
  • NX

Packages

{
  "name": "client",
  "version": "1.0.0",
  "license": "MIT",
  "scripts": {
    "serve": "nx serve",
    "serve:prod": "nx serve --prod",
    "build": "nx build",
    "test": "nx test",
    "eslint": "eslint --cache",
    "prettier": "prettier --check --cache",
    "lint": "nx lint",
    "format": "nx format:check",
    "e2e": "nx e2e",
    "affected:e2e": "nx affected --target=e2e",
    "affected:lint": "nx affected --target=lint",
    "affected:format": "nx affected --target=format",
    "affected:test": "nx affected --target=test",
    "storybook": "nx storybook",
    "storybook:build": "nx build-storybook",
    "deploy": "vercel",
    "deploy:prod": "vercel --prod",
    "prepare": "husky install"
  },
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.9.3",
    "@emotion/styled": "^11.9.3",
    "@fontsource/roboto": "^4.5.7",
    "@mui/base": "^5.0.0-alpha.91",
    "@mui/icons-material": "^5.8.4",
    "@mui/material": "^5.9.2",
    "@reduxjs/toolkit": "^1.8.3",
    "axios": "^0.27.2",
    "formik": "^2.2.9",
    "next": "^12.2.3",
    "next-i18next": "^11.0.0",
    "next-redux-wrapper": "^7.0.5",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-redux": "^8.0.2",
    "vercel": "^27.1.3",
    "yup": "^0.32.11"
  },
  "devDependencies": {
    "@babel/core": "7.12.13",
    "@babel/preset-typescript": "7.12.13",
    "@commitlint/cli": "^17.0.3",
    "@commitlint/config-conventional": "^17.0.3",
    "@next/bundle-analyzer": "^12.2.3",
    "@nrwl/cli": "14.4.2",
    "@nrwl/cypress": "14.5.1",
    "@nrwl/eslint-plugin-nx": "14.4.3",
    "@nrwl/express": "^14.4.3",
    "@nrwl/jest": "14.5.1",
    "@nrwl/linter": "14.4.2",
    "@nrwl/next": "14.4.0",
    "@nrwl/node": "14.4.0",
    "@nrwl/react": "14.4.2",
    "@nrwl/storybook": "14.4.2",
    "@nrwl/web": "14.4.3",
    "@nrwl/workspace": "14.4.2",
    "@storybook/addon-essentials": "~6.5.9",
    "@storybook/builder-webpack5": "~6.5.9",
    "@storybook/core-server": "~6.5.9",
    "@storybook/manager-webpack5": "~6.5.9",
    "@storybook/react": "~6.5.9",
    "@svgr/webpack": "^5.4.0",
    "@testing-library/cypress": "^8.0.3",
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "13.3.0",
    "@types/express": "4.17.13",
    "@types/jest": "27.4.1",
    "@types/node": "18.0.4",
    "@types/react": "18.0.15",
    "@types/react-dom": "18.0.5",
    "@typescript-eslint/eslint-plugin": "^5.30.3",
    "@typescript-eslint/parser": "^5.30.3",
    "babel-jest": "27.5.1",
    "babel-loader": "8.1.0",
    "core-js": "^3.6.5",
    "cypress": "^10.3.0",
    "cypress-real-events": "^1.7.1",
    "cypress-web-vitals": "^2.0.0",
    "eslint": "8.2.0",
    "eslint-config-airbnb": "19.0.4",
    "eslint-config-airbnb-typescript": "^17.0.0",
    "eslint-config-next": "12.1.6",
    "eslint-config-prettier": "8.1.0",
    "eslint-plugin-cypress": "^2.10.3",
    "eslint-plugin-import": "2.25.3",
    "eslint-plugin-jsx-a11y": "6.5.1",
    "eslint-plugin-react": "7.30.1",
    "eslint-plugin-react-hooks": "4.6.0",
    "husky": "^8.0.1",
    "jest": "27.5.1",
    "lint-staged": "^13.0.3",
    "nx": "14.5.1",
    "prettier": "^2.7.1",
    "react-test-renderer": "18.2.0",
    "regenerator-runtime": "0.13.7",
    "ts-jest": "27.1.4",
    "ts-node": "~10.8.0",
    "tslib": "^2.3.0",
    "typescript": "~4.7.2",
    "url-loader": "^3.0.0"
  }
}

Problem

The problem is that raw locales in the .json format are accessible by GET requests. For example, I can visit http://localhost/locales/en/common.json and get:

{
  "email": {
    "length": {
      "short": "Email is too short",
      "long": "Email is too long"
    },
    "format": "Email format is wrong",
    "required": "Email is required"
  },
  "password": {
    "length": {
      "short": "Password is too short",
      "long": "Password is too long"
    },
    "format": "Password format is wrong",
    "required": "Password is required"
  }
}

The locales are not placed in the public directory during development and next-i18next is configured for another locales path by its next-i18next.config.js.

I18n config

const path = require('path');

module.exports = {
  debug: process.env.NODE_ENV === 'development',
  i18n: {
    locales: ['en', 'uk'],
    defaultLocale: 'en',
  },
  localePath: path.resolve('./apps/client/locales'),
};

Question

I think that users can guess these paths and get some sensitive information. Are there any ways to make them accessible only for Next, but not for the public? Maybe I should change my way of localization at all, shouldn't I?


Solution

  • next-translate looks like the library with different internationalization logic, which resolved my problem.