From 797fe4eea007454adeda86dfb162205d8e373e76 Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Tue, 5 Mar 2024 09:59:57 +0100 Subject: [PATCH] refactor(macro): add tests for #1797 issue --- .../expected/locales/_app.page/en.po | 8 ++ .../expected/locales/_app.page/pl.po | 8 ++ .../expected/locales/index.page/en.po | 67 ++++++++++++++ .../expected/locales/index.page/pl.po | 67 ++++++++++++++ .../fixtures/components/AboutText.tsx | 18 ++++ .../fixtures/components/Developers.tsx | 25 +++++ .../fixtures/components/Switcher.tsx | 46 ++++++++++ .../fixtures/pages/_app.page.tsx | 19 ++++ .../fixtures/pages/index.page.tsx | 92 +++++++++++++++++++ .../fixtures/styles/Index.module.css | 47 ++++++++++ .../fixtures/styles/globals.css | 16 ++++ .../fixtures/utils.ts | 30 ++++++ .../extractor-experimental-1797/package.json | 30 ++++++ .../extractor-experimental-1797/tsconfig.json | 31 +++++++ packages/cli/test/index.test.ts | 53 +++++++++++ packages/macro/package.json | 1 - 16 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/en.po create mode 100644 packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/pl.po create mode 100644 packages/cli/test/extractor-experimental-1797/expected/locales/index.page/en.po create mode 100644 packages/cli/test/extractor-experimental-1797/expected/locales/index.page/pl.po create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/components/AboutText.tsx create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/components/Developers.tsx create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/components/Switcher.tsx create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/pages/_app.page.tsx create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/pages/index.page.tsx create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/styles/Index.module.css create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/styles/globals.css create mode 100644 packages/cli/test/extractor-experimental-1797/fixtures/utils.ts create mode 100644 packages/cli/test/extractor-experimental-1797/package.json create mode 100644 packages/cli/test/extractor-experimental-1797/tsconfig.json diff --git a/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/en.po b/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/en.po new file mode 100644 index 000000000..e69a4d65f --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/en.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-03-15 10:00+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" diff --git a/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/pl.po b/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/pl.po new file mode 100644 index 000000000..4062525fc --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/expected/locales/_app.page/pl.po @@ -0,0 +1,8 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-03-15 10:00+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: pl\n" diff --git a/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/en.po b/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/en.po new file mode 100644 index 000000000..cd75d8dce --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/en.po @@ -0,0 +1,67 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-03-15 10:00+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: en\n" + +#: fixtures/pages/index.page.tsx:83 +#: fixtures/pages/index.page.tsx:85 +msgid "{0, plural, one {# Person} other {# Persons}}" +msgstr "{0, plural, one {# Person} other {# Persons}}" + +#: fixtures/components/Developers.tsx:20 +msgid "{selected, plural, one {Developer} other {Developers}}" +msgstr "{selected, plural, one {Developer} other {Developers}}" + +#: fixtures/pages/index.page.tsx:62 +msgid "<0>Next.jssay hi." +msgstr "<0>Next.jssay hi." + +#: fixtures/components/Switcher.tsx:10 +msgid "English" +msgstr "English" + +#: fixtures/components/AboutText.tsx:6 +msgid "Hello, world" +msgstr "Hello, world" + +#. js-lingui-explicit-id +#: fixtures/components/AboutText.tsx:8 +msgid "message.next-explanation" +msgstr "Next.js is an open-source React front-end development web framework that enables functionality such as server-side rendering and generating static websites for React based web applications. It is a production-ready framework that allows developers to quickly create static and dynamic JAMstack websites and is used widely by many large companies." + +#: fixtures/pages/index.page.tsx:58 +#: fixtures/pages/index.page.tsx:60 +msgid "Plain text" +msgstr "Plain text" + +#: fixtures/components/Developers.tsx:9 +msgid "Plural Test: How many developers?" +msgstr "Plural Test: How many developers?" + +#: fixtures/components/Switcher.tsx:11 +msgid "Serbian" +msgstr "Serbian" + +#: fixtures/components/Switcher.tsx:12 +msgid "Spanish" +msgstr "Spanish" + +#: fixtures/pages/index.page.tsx:46 +msgid "Translation Demo" +msgstr "Translation Demo" + +#: fixtures/pages/index.page.tsx:53 +msgid "Welcome to <0>Next.js!" +msgstr "Welcome to <0>Next.js!" + +#: fixtures/pages/index.page.tsx:67 +msgid "Wonderful framework <0>Next.jssay hi." +msgstr "Wonderful framework <0>Next.jssay hi." + +#: fixtures/pages/index.page.tsx:72 +msgid "Wonderful framework <0>Next.jssay hi. And <1>Next.jssay hi." +msgstr "Wonderful framework <0>Next.jssay hi. And <1>Next.jssay hi." diff --git a/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/pl.po b/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/pl.po new file mode 100644 index 000000000..a64a5158e --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/expected/locales/index.page/pl.po @@ -0,0 +1,67 @@ +msgid "" +msgstr "" +"POT-Creation-Date: 2023-03-15 10:00+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=utf-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: @lingui/cli\n" +"Language: pl\n" + +#: fixtures/pages/index.page.tsx:83 +#: fixtures/pages/index.page.tsx:85 +msgid "{0, plural, one {# Person} other {# Persons}}" +msgstr "" + +#: fixtures/components/Developers.tsx:20 +msgid "{selected, plural, one {Developer} other {Developers}}" +msgstr "" + +#: fixtures/pages/index.page.tsx:62 +msgid "<0>Next.jssay hi." +msgstr "" + +#: fixtures/components/Switcher.tsx:10 +msgid "English" +msgstr "" + +#: fixtures/components/AboutText.tsx:6 +msgid "Hello, world" +msgstr "" + +#. js-lingui-explicit-id +#: fixtures/components/AboutText.tsx:8 +msgid "message.next-explanation" +msgstr "" + +#: fixtures/pages/index.page.tsx:58 +#: fixtures/pages/index.page.tsx:60 +msgid "Plain text" +msgstr "" + +#: fixtures/components/Developers.tsx:9 +msgid "Plural Test: How many developers?" +msgstr "" + +#: fixtures/components/Switcher.tsx:11 +msgid "Serbian" +msgstr "" + +#: fixtures/components/Switcher.tsx:12 +msgid "Spanish" +msgstr "" + +#: fixtures/pages/index.page.tsx:46 +msgid "Translation Demo" +msgstr "" + +#: fixtures/pages/index.page.tsx:53 +msgid "Welcome to <0>Next.js!" +msgstr "" + +#: fixtures/pages/index.page.tsx:67 +msgid "Wonderful framework <0>Next.jssay hi." +msgstr "" + +#: fixtures/pages/index.page.tsx:72 +msgid "Wonderful framework <0>Next.jssay hi. And <1>Next.jssay hi." +msgstr "" diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/components/AboutText.tsx b/packages/cli/test/extractor-experimental-1797/fixtures/components/AboutText.tsx new file mode 100644 index 000000000..5cf066a99 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/components/AboutText.tsx @@ -0,0 +1,18 @@ +import { Trans } from '@lingui/macro' + +export function AboutText() { + return ( +

+ Hello, world +
+ + Next.js is an open-source React front-end development web framework that + enables functionality such as server-side rendering and generating + static websites for React based web applications. It is a + production-ready framework that allows developers to quickly create + static and dynamic JAMstack websites and is used widely by many large + companies. + +

+ ) +} diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/components/Developers.tsx b/packages/cli/test/extractor-experimental-1797/fixtures/components/Developers.tsx new file mode 100644 index 000000000..027f5bf4d --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/components/Developers.tsx @@ -0,0 +1,25 @@ +import { useState } from 'react' +import { Trans, Plural } from '@lingui/macro' + +export default function Developers() { + const [selected, setSelected] = useState('1') + return ( +
+

+ Plural Test: How many developers? +

+
+ +

+ +

+
+
+ ) +} diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/components/Switcher.tsx b/packages/cli/test/extractor-experimental-1797/fixtures/components/Switcher.tsx new file mode 100644 index 000000000..b6813d068 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/components/Switcher.tsx @@ -0,0 +1,46 @@ +import { useRouter } from 'next/router' +import { useState } from 'react' +import { t, msg } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { MessageDescriptor } from '@lingui/core' + +type LOCALES = 'en' | 'sr' | 'es' | 'pseudo' + +const languages: { [key: string]: MessageDescriptor } = { + en: msg`English`, + sr: msg`Serbian`, + es: msg`Spanish` +} + +export function Switcher() { + const router = useRouter() + const { i18n } = useLingui() + + const [locale, setLocale] = useState( + router.locale!.split('-')[0] as LOCALES + ) + + // disabled for DEMO - so we can demonstrate the 'pseudo' locale functionality + // if (process.env.NEXT_PUBLIC_NODE_ENV !== 'production') { + // languages['pseudo'] = t`Pseudo` + // } + + function handleChange(event: React.ChangeEvent) { + const locale = event.target.value as LOCALES + + setLocale(locale) + router.push(router.pathname, router.pathname, { locale }) + } + + return ( + + ) +} diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/pages/_app.page.tsx b/packages/cli/test/extractor-experimental-1797/fixtures/pages/_app.page.tsx new file mode 100644 index 000000000..0883e2eb7 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/pages/_app.page.tsx @@ -0,0 +1,19 @@ +import { i18n } from '@lingui/core' +import { I18nProvider } from '@lingui/react' +import '../styles/globals.css' +import type { AppProps } from 'next/app' +import { useLinguiInit } from '../utils' + +function MyApp({ Component, pageProps }: AppProps) { + useLinguiInit(pageProps.translation) + + return ( + <> + + + + + ) +} + +export default MyApp diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/pages/index.page.tsx b/packages/cli/test/extractor-experimental-1797/fixtures/pages/index.page.tsx new file mode 100644 index 000000000..a49cf25d2 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/pages/index.page.tsx @@ -0,0 +1,92 @@ +import { Plural, t, Trans } from '@lingui/macro' + +import path from 'path' +import { GetStaticProps, NextPage } from 'next' +import Head from 'next/head' +import { AboutText } from '../components/AboutText' +import Developers from '../components/Developers' +import { Switcher } from '../components/Switcher' +import styles from '../styles/Index.module.css' +import { loadCatalog } from '../utils' +import { useLingui } from '@lingui/react' + +export const getStaticProps: GetStaticProps = async (ctx) => { + const fileName = __filename + const cwd = process.cwd() + const { locale } = ctx + + const pathname = path + .relative(cwd, fileName) + .replace('.next/server/pages/', '') + .replace('.js', '') + + const translation = await loadCatalog(locale || 'en', pathname) + return { + props: { + translation + } + } +} + +const Index: NextPage = () => { + /** + * This hook is needed to subscribe your + * component for changes if you use t`` macro + */ + useLingui() + + return ( +
+ + {/* + The Next Head component is not being rendered in the React + component tree and React Context is not being passed down to the components placed in the . + That means we cannot use the component here and instead have to use `t` macro. + */} + {t`Translation Demo`} + + + +
+ +

+ + Welcome to Next.js! + +

+

+ Plain text +

+

{t`Plain text`}

+

+ + Next.js say hi. + +

+

+ + Wonderful framework Next.js say hi. + +

+

+ + Wonderful framework Next.js say hi. + And Next.js say hi. + +

+
+ +
+ + +
+ +
+ +
+
+
+ ) +} + +export default Index diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/styles/Index.module.css b/packages/cli/test/extractor-experimental-1797/fixtures/styles/Index.module.css new file mode 100644 index 000000000..4abdd3311 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/styles/Index.module.css @@ -0,0 +1,47 @@ +.container { + min-height: 100vh; + padding: 0 0.5rem; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.main { + padding: 5rem 0; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.title a { + color: #0070f3; + text-decoration: none; +} + +.title a:hover, +.title a:focus, +.title a:active { + text-decoration: underline; +} + +.title { + margin-bottom: 0; + margin-top: 2rem; + line-height: 1.15; + font-size: 4rem; +} + +.title, +.description { + text-align: center; +} + +.description { + /* line-height: 1.5; */ + /* font-size: 1.5rem; */ + max-width: 600px; + text-align: left; +} diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/styles/globals.css b/packages/cli/test/extractor-experimental-1797/fixtures/styles/globals.css new file mode 100644 index 000000000..e5e2dcc23 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/styles/globals.css @@ -0,0 +1,16 @@ +html, +body { + padding: 0; + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, + Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +* { + box-sizing: border-box; +} diff --git a/packages/cli/test/extractor-experimental-1797/fixtures/utils.ts b/packages/cli/test/extractor-experimental-1797/fixtures/utils.ts new file mode 100644 index 000000000..d70a97b17 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/fixtures/utils.ts @@ -0,0 +1,30 @@ +import { i18n, Messages } from '@lingui/core' +import { useRouter } from 'next/router' +import { useEffect, useState } from 'react' + +export async function loadCatalog(locale: string, pathname: string) { + if (pathname === '_error') { + return {} + } + const catalog = await import( + `@lingui/loader!./locales/src/pages/${pathname}.page/${locale}.po` + ) + return catalog.messages +} + +export function useLinguiInit(messages: Messages) { + const router = useRouter() + const locale = router.locale || router.defaultLocale! + useState(() => { + i18n.loadAndActivate({ locale, messages }) + }) + + useEffect(() => { + const localeDidChange = locale !== i18n.locale + if (localeDidChange) { + i18n.loadAndActivate({ locale, messages }) + } + }, [locale, messages]) + + return i18n +} diff --git a/packages/cli/test/extractor-experimental-1797/package.json b/packages/cli/test/extractor-experimental-1797/package.json new file mode 100644 index 000000000..985e126a1 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/package.json @@ -0,0 +1,30 @@ +{ + "name": "nextjs-swc-example", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "debug": "NODE_OPTIONS='--inspect' next dev", + "build": "npm run lingui:extract && next build", + "start": "next start", + "lingui:extract": "lingui extract-experimental", + "test": "npm run build" + }, + "dependencies": { + "@lingui/core": "^4.5.0", + "@lingui/react": "^4.5.0", + "next": "13.4.12", + "react": "18.2.0", + "react-dom": "18.2.0" + }, + "devDependencies": { + "@lingui/cli": "^4.5.0", + "@lingui/loader": "^4.5.0", + "@lingui/macro": "^4.5.0", + "@lingui/swc-plugin": "^4.0.4", + "@types/react": "^18.0.14", + "eslint": "8.35.0", + "eslint-config-next": "12.3.4", + "typescript": "^4.7.4" + } +} diff --git a/packages/cli/test/extractor-experimental-1797/tsconfig.json b/packages/cli/test/extractor-experimental-1797/tsconfig.json new file mode 100644 index 000000000..005baed64 --- /dev/null +++ b/packages/cli/test/extractor-experimental-1797/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "es6", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + // "downlevelIteration": true, + "jsx": "preserve", + "incremental": true + }, + "include": [ + "next-env.d.ts", + "src/**/*.ts", + "src/**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/cli/test/index.test.ts b/packages/cli/test/index.test.ts index 1bac33ec5..40c62e507 100644 --- a/packages/cli/test/index.test.ts +++ b/packages/cli/test/index.test.ts @@ -287,5 +287,58 @@ describe("E2E Extractor Test", () => { compareFolders(actualPath, expectedPath) }) + + it("Should extract correctly all messages github-issue: 1797", async () => { + const { rootDir, actualPath, expectedPath } = await prepare( + "extractor-experimental-1797" + ) + + await mockConsole(async (console) => { + const result = await extractExperimentalCommand( + makeConfig({ + rootDir: rootDir, + locales: ["en", "pl"], + sourceLocale: "en", + format: "po", + catalogs: [], + experimental: { + extractor: { + entries: ["/fixtures/pages/**/*.page.tsx"], + output: "/actual/locales/{entryName}/{locale}", + }, + }, + }), + { + clean: true, + } + ) + + expect(getConsoleMockCalls(console.error)).toBeFalsy() + expect(result).toBeTruthy() + expect(getConsoleMockCalls(console.log)).toMatchInlineSnapshot(` + You have using an experimental feature + Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk. + + Catalog statistics for fixtures/pages/_app.page.tsx: + ┌─────────────┬─────────────┬─────────┐ + │ Language │ Total count │ Missing │ + ├─────────────┼─────────────┼─────────┤ + │ en (source) │ 0 │ - │ + │ pl │ 0 │ 0 │ + └─────────────┴─────────────┴─────────┘ + + Catalog statistics for fixtures/pages/index.page.tsx: + ┌─────────────┬─────────────┬─────────┐ + │ Language │ Total count │ Missing │ + ├─────────────┼─────────────┼─────────┤ + │ en (source) │ 14 │ - │ + │ pl │ 14 │ 14 │ + └─────────────┴─────────────┴─────────┘ + + `) + }) + + compareFolders(actualPath, expectedPath) + }) }) }) diff --git a/packages/macro/package.json b/packages/macro/package.json index 520f408f5..3184bc1e0 100644 --- a/packages/macro/package.json +++ b/packages/macro/package.json @@ -63,7 +63,6 @@ "import": { "types": "./dist/plugin.d.ts", "default": "./dist/index.mjs" - } } },