Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): use Intl.PluralRules #1486

Merged
merged 3 commits into from
Mar 9, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 23 additions & 26 deletions examples/create-react-app/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,39 @@
import React from 'react'
import { getByText, render, act } from '@testing-library/react'
import { i18n } from '@lingui/core'
import { I18nProvider } from '@lingui/react'
import { en, cs } from 'make-plural/plurals'
import React from "react"
import { getByText, render, act } from "@testing-library/react"
import { i18n } from "@lingui/core"
import { I18nProvider } from "@lingui/react"

import { messages } from './locales/en/messages'
import { messages as csMessages } from './locales/cs/messages'
import App from './App'
import { messages } from "./locales/en/messages"
import { messages as csMessages } from "./locales/cs/messages"
import App from "./App"

i18n.load({
en: messages,
cs: csMessages
cs: csMessages,
})
i18n.loadLocaleData({
en: { plurals: en },
cs: { plurals: cs }
});

const TestingProvider = ({ children }: any) => (
<I18nProvider i18n={i18n}>
{children}
</I18nProvider>
<I18nProvider i18n={i18n}>{children}</I18nProvider>
)

test('Test that lang is translated correctly in English' , () => {
test("Test that lang is translated correctly in English", () => {
act(() => {
i18n.activate('en')
i18n.activate("en")
})
const { getByTestId, container } = render(<App />, { wrapper: TestingProvider });
expect(getByTestId('h3-title')).toBeInTheDocument()
const { getByTestId, container } = render(<App />, {
wrapper: TestingProvider,
})
expect(getByTestId("h3-title")).toBeInTheDocument()
expect(getByText(container, "Language switcher example:")).toBeDefined()
});
})

test('Test that lang is translated correctly in Czech', () => {
test("Test that lang is translated correctly in Czech", () => {
act(() => {
i18n.activate('cs')
i18n.activate("cs")
})
const { getByTestId, container } = render(<App />, { wrapper: TestingProvider });
expect(getByTestId('h3-title')).toBeInTheDocument()
const { getByTestId, container } = render(<App />, {
wrapper: TestingProvider,
})
expect(getByTestId("h3-title")).toBeInTheDocument()
expect(getByText(container, "Příklad přepínače jazyků:")).toBeDefined()
});
})
16 changes: 6 additions & 10 deletions examples/create-react-app/src/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
import { i18n } from "@lingui/core";
import { en, cs } from 'make-plural/plurals'
import { i18n } from "@lingui/core"

export const locales = {
en: "English",
cs: "Česky",
};
export const defaultLocale = "en";

i18n.loadLocaleData({
en: { plurals: en },
cs: { plurals: cs },
})
}
export const defaultLocale = "en"

/**
* We do a dynamic import of just the catalog that we need
* @param locale any locale string
*/
export async function dynamicActivate(locale: string) {
const { messages } = await import(`@lingui/loader!./locales/${locale}/messages.po`)
const { messages } = await import(
`@lingui/loader!./locales/${locale}/messages.po`
)
i18n.load(locale, messages)
i18n.activate(locale)
}
4 changes: 0 additions & 4 deletions examples/js/src/ids.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { i18n } from "@lingui/core"
import { t, plural, defineMessage } from "@lingui/macro"
import { en, cs } from "make-plural/plurals"

i18n.loadLocaleData("en", { plurals: en })
i18n.loadLocaleData("cs", { plurals: cs })

i18n.load({
en: require("./locale/en/messages").messages,
4 changes: 0 additions & 4 deletions examples/js/src/messages.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import { i18n } from "@lingui/core"
import { t, plural, defineMessage } from "@lingui/macro"
import { en, cs } from "make-plural/plurals"

i18n.loadLocaleData("en", { plurals: en })
i18n.loadLocaleData("cs", { plurals: cs })

i18n.load({
en: require("./locale/en/messages").messages,
4 changes: 0 additions & 4 deletions examples/next-js/lingui-example/i18n.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import { i18n } from "@lingui/core"
import { en, cs } from "make-plural/plurals"

i18n.loadLocaleData("en", { plurals: en })
i18n.loadLocaleData("cs", { plurals: cs })

/**
* Load messages for requested locale and activate it.
2 changes: 0 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -56,7 +56,6 @@
"@lingui/core": "4.0.0-next.0",
"@messageformat/parser": "^5.0.0",
"babel-plugin-macros": "^3.0.1",
"bcp-47": "^1.0.7",
"chalk": "^4.1.0",
"chokidar": "3.5.1",
"cli-table": "0.3.6",
@@ -65,7 +64,6 @@
"date-fns": "^2.16.1",
"glob": "^7.1.4",
"inquirer": "^7.3.3",
"make-plural": "^6.2.2",
"micromatch": "4.0.2",
"mkdirp": "^1.0.4",
"node-gettext": "^3.0.0",
17 changes: 0 additions & 17 deletions packages/cli/src/api/locales.test.ts

This file was deleted.

38 changes: 0 additions & 38 deletions packages/cli/src/api/locales.ts

This file was deleted.

13 changes: 0 additions & 13 deletions packages/cli/src/lingui-compile.ts
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@ import chalk from "chalk"
import chokidar from "chokidar"
import fs from "fs"
import { program } from "commander"
import * as plurals from "make-plural"

import { getConfig, LinguiConfigNormalized } from "@lingui/conf"

@@ -33,18 +32,6 @@ export function command(
console.log("Compiling message catalogs…")

for (const locale of config.locales) {
const [language] = locale.split(/[_-]/)
// todo: this validation should be in @lingui/conf
// todo: validate locales according bcp47, instead of plurals
if (locale !== config.pseudoLocale && !(plurals as any)[language]) {
console.error(
chalk.red(
`Error: Invalid locale ${chalk.bold(locale)} (missing plural rules)!`
)
)
console.error()
}

andrii-bodnar marked this conversation as resolved.
Show resolved Hide resolved
for (const catalog of catalogs) {
const missingMessages: TranslationMissingEvent[] = []

5 changes: 0 additions & 5 deletions packages/cli/src/test/__snapshots__/compile.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CLI Command: Compile Locales Validation Should throw error for invalid locale 1`] = `
Error: Invalid locale abra (missing plural rules)!

`;

exports[`CLI Command: Compile allowEmpty = false Should show error and stop compilation of catalog if message doesnt have a translation (no template) 1`] = `
Error: Failed to compile catalog for locale pl!
Missing 1 translation(s)
52 changes: 0 additions & 52 deletions packages/cli/src/test/compile.test.ts
Original file line number Diff line number Diff line change
@@ -9,58 +9,6 @@ describe("CLI Command: Compile", () => {
// todo
})

describe("Locales Validation", () => {
// todo: should be moved to @lingui/conf
it("Should throw error for invalid locale", () => {
const config = makeConfig({
locales: ["abra"],
rootDir: "/test",
catalogs: [
{
path: "<rootDir>/{locale}",
include: ["<rootDir>"],
exclude: [],
},
],
})

mockFs()

mockConsole((console) => {
const result = command(config, {})
mockFs.restore()
const log = getConsoleMockCalls(console.error)
expect(log).toMatchSnapshot()

expect(result).toBeTruthy()
})
})

it("Should not throw error for pseudolocale", () => {
const config = makeConfig({
locales: ["abracadabra"],
rootDir: "/test",
pseudoLocale: "abracadabra",
catalogs: [
{
path: "<rootDir>/{locale}",
include: ["<rootDir>"],
exclude: [],
},
],
})

mockFs()

mockConsole((console) => {
const result = command(config, {})
mockFs.restore()
expect(console.error).not.toBeCalled()
expect(result).toBeTruthy()
})
})
})

describe("allowEmpty = false", () => {
const config = makeConfig({
locales: ["en", "pl"],
3 changes: 1 addition & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
@@ -60,8 +60,7 @@
],
"dependencies": {
"@babel/runtime": "^7.20.13",
"@messageformat/parser": "^5.0.0",
"make-plural": "^6.2.2"
"@messageformat/parser": "^5.0.0"
},
"devDependencies": {
"@lingui/jest-mocks": "*"
48 changes: 29 additions & 19 deletions packages/core/src/compile/compileMessage.test.ts
Original file line number Diff line number Diff line change
@@ -2,25 +2,11 @@ import { compileMessage as compile } from "./compileMessage"
import { mockEnv, mockConsole } from "@lingui/jest-mocks"
import { interpolate } from "../context"
import { Locale, Locales } from "../i18n"
import { PluralCategory } from "make-plural"

describe("compile", () => {
const englishPlurals = {
plurals(value: number, ordinal: boolean) {
if (ordinal) {
return (
({ "1": "one", "2": "two", "3": "few" }[value] as PluralCategory) ||
"other"
)
} else {
return value === 1 ? "one" : "other"
}
},
}

const prepare = (translation: string, locale?: Locale, locales?: Locales) => {
const tokens = compile(translation)
return interpolate(tokens, locale || "en", locales, englishPlurals)
return interpolate(tokens, locale || "en", locales)
}

it("should handle an error if message has syntax errors", () => {
@@ -62,9 +48,7 @@ describe("compile", () => {

it("should compile message with variable", () => {
const cache = compile("Hey {name}!")
expect(interpolate(cache, "en", [], {})({ name: "Joe" })).toEqual(
"Hey Joe!"
)
expect(interpolate(cache, "en", [])({ name: "Joe" })).toEqual("Hey Joe!")
})

it("should not interpolate escaped placeholder", () => {
@@ -96,6 +80,32 @@ describe("compile", () => {
expect(cache({ value: 2 })).toEqual("2nd Book")
})

it("should support nested choice components", () => {
const cache = prepare(
`{
gender, select,
male {{numOfGuests, plural, one {He invites one guest} other {He invites # guests}}}
female {{numOfGuests, plural, one {She invites one guest} other {She invites # guests}}}
other {They is {gender}}}`
)

expect(cache({ numOfGuests: 1, gender: "male" })).toEqual(
"He invites one guest"
)
expect(cache({ numOfGuests: 3, gender: "male" })).toEqual(
"He invites 3 guests"
)
expect(cache({ numOfGuests: 1, gender: "female" })).toEqual(
"She invites one guest"
)
expect(cache({ numOfGuests: 3, gender: "female" })).toEqual(
"She invites 3 guests"
)
expect(cache({ numOfGuests: 3, gender: "unknown" })).toEqual(
"They is unknown"
)
})

it("should compile select", () => {
const cache = prepare("{value, select, female {She} other {They}}")
expect(cache({ value: "female" })).toEqual("She")
@@ -137,7 +147,7 @@ describe("compile", () => {
style: "currency",
currency: "EUR",
minimumFractionDigits: 2,
} as Intl.NumberFormatOptions,
} satisfies Intl.NumberFormatOptions,
}
const currency = prepare("{value, number, currency}", locale, locales)
expect(currency({ value: 0.1 }, formats)).toEqual(expectedCurrency1)
Loading