i18next / i18next-scanner

Scan your code, extract translation keys/values, and merge them into i18n resource files.
http://i18next.github.io/i18next-scanner
MIT License
570 stars 128 forks source link

Extraction from tsx file #179

Open bsdis opened 4 years ago

bsdis commented 4 years ago

Can I extract language from a tsx file (react component) as shown below? If yes, what config file do I need to make this happen?

import React, { useState } from "react";
import { useForm, FormContext, ErrorMessage } from "react-hook-form";
import axios from "axios";
// See https://github.com/i18next/react-i18next/blob/master/test/typescript/examples.test.tsx
import {
  useTranslation,
  Trans,
  withTranslation,
  WithTranslation
} from "react-i18next";
import * as Yup from "yup";

import {
  Button,
  FormGroup,
  FormControl,
  FormLabel,
  Form,
  Modal,
  Navbar,
  Nav
} from "react-bootstrap";
import PropTypes, { InferProps } from "prop-types";
const styles = require("./Register.scss");

type FormData = {
  username: string;
  email: string;
  password: string;
  repeatpassword: string;
};

Register.propTypes = {
  open: PropTypes.bool.isRequired,
  closeAction: PropTypes.func.isRequired
};

export default function Register({
  open,
  closeAction
}: InferProps<typeof Register.propTypes>) {
  const { t, i18n } = useTranslation();

  const methods = useForm<FormData>({
    validationSchema: Yup.object().shape({
      username: Yup.string().required(t("Insert name")),
      email: Yup.string()
        .email(t("Invalid email"))
        .required(t("Insert email")),
      password: Yup.string()
        .required(t("Password is required"))
        .min(8, t("Password is too short - should be 8 chars minimum."))
        .matches(/[a-zA-Z]/, t("Password can only contain Latin letters.")),
      repeatpassword: Yup.string().oneOf(
        [Yup.ref("password"), null],
        t("Passwords must match")
      )
    })
  });
  const { register, handleSubmit, watch, errors } = methods;

  const onSubmit = handleSubmit(
    ({ username, email, password, repeatpassword }) => {
      console.log({ username, email, password, repeatpassword });
    }
  );

  return (
    <>
      <Modal size="sm" show={open} onHide={closeAction}>
        <Modal.Header closeButton>
          <Modal.Title id="signup-title">{t("Sign up")}</Modal.Title>
        </Modal.Header>
        <Modal.Body id="signup-modal-body">
          <Navbar bg="light" expand="lg">
            <Navbar.Brand href="/">{t("Already a member?")}</Navbar.Brand>

            <Navbar.Toggle aria-controls="basic-navbar-nav" />
            <Navbar.Collapse id="basic-navbar-nav">
              <Nav className="mr-auto"></Nav>
              <Form inline>
                <Nav.Link className="text-weight-bold" href="#">
                  {t("Login")}
                </Nav.Link>
              </Form>
            </Navbar.Collapse>
          </Navbar>

          <FormContext {...methods}>
            <Form onSubmit={handleSubmit(data => console.log(data))} noValidate>
              <FormGroup controlId="username">
                <FormLabel>{t("Username")}</FormLabel>
                <FormControl
                  name="username"
                  type="text"
                  ref={register}
                  isInvalid={!!errors.username}
                ></FormControl>
                <ErrorMessage
                  type="invalid"
                  name="username"
                  errors={errors}
                  as={Form.Control.Feedback}
                />
              </FormGroup>
              <FormGroup controlId="email">
                <FormLabel>{t("E-mail")}</FormLabel>
                <FormControl
                  name="email"
                  type="email"
                  ref={register}
                  isInvalid={!!errors.email}
                ></FormControl>
                <ErrorMessage
                  type="invalid"
                  name="email"
                  errors={errors}
                  as={Form.Control.Feedback}
                />
              </FormGroup>
              <FormGroup controlId="password">
                <FormLabel>{t("Password")}</FormLabel>
                <FormControl
                  name="password"
                  type="password"
                  ref={register}
                  isInvalid={!!errors.password}
                ></FormControl>
                <ErrorMessage
                  type="invalid"
                  name="password"
                  errors={errors}
                  as={Form.Control.Feedback}
                />
              </FormGroup>
              <FormGroup controlId="repeatpassword">
                <FormLabel>{t("Retype password")}</FormLabel>
                <FormControl
                  name="repeatpassword"
                  type="password"
                  ref={register}
                  isInvalid={!!errors.repeatpassword}
                ></FormControl>
                <ErrorMessage
                  type="invalid"
                  name="repeatpassword"
                  errors={errors}
                  as={Form.Control.Feedback}
                />
              </FormGroup>
              <Button
                name="registersubmit"
                block
                size="lg"
                type="submit"
                ref={register}
              >
                {t("Login")}
              </Button>
            </Form>
          </FormContext>
        </Modal.Body>
      </Modal>
    </>
  );
}
MynockSpit commented 4 years ago

Easiest way to do this (so far as I know) is to run tsc first, then run your scanner on its output. I have this in my package.json.

scripts: {
  "i18n": "rm -rf tmp/i18n; npx tsc --jsx preserve --outDir tmp/i18n && npx i18next-scanner",
  ...
}
younes200 commented 3 years ago

why compiling ts/tsx is required to extract translation ?

MynockSpit commented 3 years ago

Because i18next-scanner doesn't parse typescript files, so it'll choke when it hits syntax it doesn't recognize. Compiling removes all that syntax.

stepankuzmin commented 3 years ago

Is there any change that i18next-scanner will get TS support?

ImADrafter commented 3 years ago

I haven't really went to far with this, but seems like Parser actually run over Ts files:

const fs = require('fs');
const { Parser } = require('i18next-scanner');

const parser = new Parser();

let content = '';

content = fs.readFileSync('src/components/Home/Home.tsx', 'utf-8');
parser
    .parseFuncFromString(content, {
        list: ['t', 'i18n.t'],
        extensions: ['.ts', '.tsx'],
    });

console.log(parser.get());
SanjanaTailor commented 3 years ago

@ImADrafter : where I have to put above code inside where and how to run it ? `const gulp = require('gulp'); const sort = require('gulp-sort'); const scanner = require('i18next-scanner');

gulp.task('extract', function () { return gulp //.src(['src/*/.{js,ts,tsx,jsx}']) // add path of files to get scanned for extraction .src(['../../.{tsx}']) .pipe(sort()) // Sort files in stream by path .pipe( scanner({ quiet: true, lngs: ['en'], // add supported languages interpolation: { prefix: '{{', suffix: '}}', }, resource: { loadPath: '../.json', // add your folder path to save the extracted files savePath: 'i18n/{{lng}}/{{ns}}.json', }, }), ) .pipe(gulp.dest('assets')); });

running above script `

SanjanaTailor commented 3 years ago

Is there any support or solution or workaround on tsx file...

SanjanaTailor commented 3 years ago

I haven't really went to far with this, but seems like Parser actually run over Ts files:

const fs = require('fs');
const { Parser } = require('i18next-scanner');

const parser = new Parser();

let content = '';

content = fs.readFileSync('src/components/Home/Home.tsx', 'utf-8');
parser
    .parseFuncFromString(content, {
        list: ['t', 'i18n.t'],
        extensions: ['.ts', '.tsx'],
    });

console.log(parser.get());

how and where I have to run this file

SanjanaTailor commented 3 years ago

how to run this scanner over tsx file any solution for same, will helpful