lingui / js-lingui

🌍 📖 A readable, automated, and optimized (3 kb) internationalization for JavaScript
https://lingui.dev
MIT License
4.47k stars 378 forks source link

Built react app `n.t` is not a function #204

Closed rssfrncs closed 6 years ago

rssfrncs commented 6 years ago

Source code causing the error is simply ${i18n.tover} ${moment(disconnectedTime).fromNow()}. This works fine in development but throws n.t is not a function when the app is built for production.

This is a stateless functional component 'enhanced' with withI18n()(Component). Logging props.i18n results in: image

rssfrncs commented 6 years ago

Just tried converting the component to be class based (why not?) and still getting the same error thrown in production.

rssfrncs commented 6 years ago

This is with React 16.3.1, is this version supported?

rssfrncs commented 6 years ago

okay downgraded to 16.2 so not the the new context API.

tricoder42 commented 6 years ago

Could you please post the whole line which causes this error? Thanks a lot!

rssfrncs commented 6 years ago

@tricoder42 just playing around with my .babelrc

changing from,

"presets": [
    "@lingui/babel-preset-react",
    "@babel/preset-env",
    "@babel/preset-react"
  ],

to

"presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@lingui/babel-preset-react"
  ],

and it's now working?

Full line is `${i18n.t`over`} ${moment(disconnectedTime).fromNow()}`

tricoder42 commented 6 years ago

Yeah, order of plugins/presets is important. Presets are applied from bottom to top, and @lingui/babel-preset-react should be applied first.

i18n.t is definitely undefined in production, because plugins transforms all i18n calls to low-level i18n._ method.

I'm going to add it to docs!

MartinCerny-awin commented 6 years ago

I am having the same issue and my .babelrc looks to have @lingui/babel-preset-react in correct order "presets": ["env", "stage-0", "react", "@lingui/babel-preset-react"],

tricoder42 commented 6 years ago

@MartinCerny-awin What about plugins, do you use any? The probem is, that plugins are applied before presets, so if you have e.g. transform-runtime, it will mess up with template strings.

MartinCerny-awin commented 6 years ago

@tricoder42 I have two plugins "plugins": ["universal-import", "react-hot-loader/babel"]

MartinCerny-awin commented 6 years ago

I have tried to build it without any plugin and it still doesn't work. I am getting e.i18n.t is not a function error. Is there a way how to do translation without template literals?

tricoder42 commented 6 years ago

Closing this one, because with upcoming jsLingui 3.0 this problem will be solved using babel macros.

@MartinCerny-awin Yes, you can use core method: i18n._(messageId, values), e.g. i18n._("Hello {name}", { name }). This is basically what i18n.t'Hello ${name}' is transformed into. Feel free to open new issue if you still have problems with i18n.t

ackvf commented 5 years ago

So, what is the official solution when using lingui 2.7?

tricoder42 commented 5 years ago

@ackvf If you're having this error, you most probably misconfigured babel. See this comment https://github.com/lingui/js-lingui/issues/204#issuecomment-381973698

zimmicz commented 4 years ago

Following https://lingui.js.org/tutorials/react-patterns.html I get t is not defined when translating element attribute.

package.json

  "dependencies": {
    "@apollo/client": "^3.1.3",
    "@lingui/react": "^2.9.1",
    "@sentry/browser": "^5.21.4",
    "axios": "^0.20.0",
    "graphql": "^15.3.0",
    "jest-svg-transformer": "^1.0.0",
    "lodash.debounce": "^4.0.8",
    "moment": "^2.27.0",
    "react": "^16.12.0",
    "react-dom": "^16.12.0",
    "react-router": "^5.1.2",
    "react-router-dom": "^5.1.2",
    "react-svg": "^11.0.35",
    "react-tiny-virtual-list": "^2.2.0",
    "styled-components": "^5.1.1",
    "svg-inline-loader": "^0.8.0",
    "ts-event-bus": "^2.1.2",
    "use-lodash-debounce": "^1.1.0"
  },
  "devDependencies": {
    "@babel/core": "^7.11.6",
    "@babel/preset-env": "^7.11.5",
    "@babel/preset-react": "^7.10.4",
    "@babel/preset-typescript": "^7.10.4",
    "@graphql-codegen/add": "^2.0.1",
    "@graphql-codegen/cli": "1.17.8",
    "@graphql-codegen/fragment-matcher": "1.17.8",
    "@graphql-codegen/introspection": "1.17.8",
    "@graphql-codegen/plugin-helpers": "^1.17.8",
    "@graphql-codegen/typescript": "^1.17.9",
    "@graphql-codegen/typescript-operations": "1.17.8",
    "@graphql-codegen/typescript-react-apollo": "2.0.6",
    "@lingui/babel-preset-react": "^2.9.1",
    "@lingui/cli": "^2.9.1",
    "@lingui/loader": "^2.9.1",
    "@lingui/macro": "^2.9.1",
    "@storybook/addon-info": "^5.3.19",
    "@storybook/addons": "^6.0.18",
    "@storybook/react": "^6.0.18",
    "@storybook/theming": "^6.0.18",
    "@svgr/webpack": "^5.4.0",
    "@types/enzyme": "^3.10.4",
    "@types/enzyme-adapter-react-16": "^1.0.5",
    "@types/html-webpack-plugin": "^3.2.1",
    "@types/jest": "^26.0.10",
    "@types/lingui__core": "^2.7.0",
    "@types/lingui__macro": "^2.7.3",
    "@types/lingui__react": "^2.8.2",
    "@types/react": "^16.9.47",
    "@types/react-dom": "^16.9.4",
    "@types/react-router-dom": "^5.1.3",
    "@types/styled-components": "^5.1.2",
    "@typescript-eslint/eslint-plugin": "^3.10.1",
    "@typescript-eslint/parser": "^3.10.1",
    "babel-core": "^7.0.0-bridge.0",
    "babel-loader": "^8.1.0",
    "babel-plugin-dynamic-import-node": "^2.3.3",
    "babel-plugin-macros": "^2.8.0",
    "babel-plugin-syntax-dynamic-import": "^6.18.0",
    "core-js": "^3.6.5",
    "cross-fetch": "^3.0.5",
    "cypress": "^5.0.0",
    "enzyme": "^3.11.0",
    "enzyme-adapter-react-16": "^1.15.2",
    "eslint": "^7.7.0",
    "eslint-config-airbnb-typescript": "^9.0.0",
    "eslint-import-resolver-typescript": "^2.2.1",
    "eslint-plugin-cypress": "^2.8.1",
    "eslint-plugin-import": "^2.22.0",
    "eslint-plugin-jest": "^23.20.0",
    "eslint-plugin-jsx-a11y": "^6.3.1",
    "eslint-plugin-react": "^7.20.6",
    "eslint-plugin-react-hooks": "^4.1.0",
    "file-loader": "^6.0.0",
    "graphql-let": "^0.15.3",
    "html-webpack-plugin": "^4.3.0",
    "husky": "^4.2.5",
    "jest": "^26.4.1",
    "jest-styled-components": "^7.0.3",
    "lint-staged": "^10.2.13",
    "react-docgen-typescript-loader": "^3.7.2",
    "react-test-renderer": "^16.12.0",
    "start-server-and-test": "^1.11.2",
    "ts-jest": "^26.3.0",
    "ts-loader": "^8.0.3",
    "tsconfig-paths-webpack-plugin": "^3.3.0",
    "typescript": "^4.0.2",
    "webpack": "^4.44.1",
    "webpack-cli": "^3.3.12",
    "webpack-dev-server": "^3.9.0"
  }

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const TsConfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const webpack = require('webpack');

module.exports = (env, argv) => ({
  entry: './src/index.tsx',
  devtool: 'source-map',
  resolve: {
    alias: {
      react: path.resolve('./node_modules/react'), // needs this to properly handle `npm link @lstv/epg`
    },
    plugins: [new TsConfigPathsPlugin()],
    extensions: ['.ts', '.tsx', '.js'],
  },
  output: {
    path: path.join(__dirname, `/dist/${process.env.PLATFORM}`),
    filename: 'bundle.min.js',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'babel-loader',
          },
          {
            loader: 'ts-loader',
            options: { allowTsInNodeModules: true },
          },
        ],
      },
      {
        test: /\.svg$/,
        use: [
          {
            loader: '@svgr/webpack',
            options: {
              svgoConfig: {
                plugins: {
                  removeViewBox: false,
                },
              },
              svgProps: { fill: 'magenta' },
            },
          },
          'url-loader',
        ],
      },
      {
        test: /\.ttf$/,
        use: [
          {
            loader: 'file-loader',
          },
        ],
      },
      {
        test: /\.graphql$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: [
                '@babel/preset-typescript',
                '@babel/preset-react'
              ],
            },
          },
          'graphql-let/loader',
        ],
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './public/index.html',
    }),
    new webpack.EnvironmentPlugin({
      PLATFORM: 'CHROME',
      NODE_ENV: argv.mode,
    }),
  ],
});

babel.config.js

module.exports = api => {
  const isTest = api.env('test');
  api.cache(true);

  return {
    presets: [
      [
        '@babel/preset-env', {
          targets: {
            node: 'current',
          },
          modules: isTest && 'commonjs',
        },
      ],
      '@babel/preset-react',
      '@lingui/babel-preset-react',
    ],
    plugins: [
      'syntax-dynamic-import',
      'macros',
    ],
  };
}

lingui.config.js

module.exports = {
  fallbackLocale: 'en',
  localeDir: 'src/locales/',
  srcPathDirs: ['src/'],
  format: 'po',
};
...
import { Trans, t } from '@lingui/macro';
import { I18n } from '@lingui/react';
...

            <I18n>
              {({ i18n }) => (
                <Styled.InfoBar>
                  { genres && <InfoLine title={i18n._(t`Genre`)} content={genres.join(', ')} /> }
                  { year && <InfoLine title={i18n._(t`Year`)} content={year} /> }
                </Styled.InfoBar>
              )}
            </I18n>

Even console.log(t) right after import block throws an error. Is there anything I'm doing wrong?