diff --git a/packages/cli/src/api/catalog.test.ts b/packages/cli/src/api/catalog.test.ts index 23de94357..5e498db49 100644 --- a/packages/cli/src/api/catalog.test.ts +++ b/packages/cli/src/api/catalog.test.ts @@ -132,7 +132,7 @@ describe("Catalog", function () { }) describe("POT Flow", function () { - it('Should merge source messages from template if provided', () => { + it("Should merge source messages from template if provided", () => { const catalog = new Catalog( { name: "messages", @@ -144,21 +144,21 @@ describe("Catalog", function () { exclude: [], }, mockConfig({ - locales: ['en', 'pl'], + locales: ["en", "pl"], }) ) - const translations = catalog.getTranslations('pl', { - sourceLocale: 'en', + const translations = catalog.getTranslations("pl", { + sourceLocale: "en", fallbackLocales: { - default: 'en' - } - }); + default: "en", + }, + }) expect(translations).toMatchSnapshot() }) - it('Should get translations from template if locale file not presented', () => { + it("Should get translations from template if locale file not presented", () => { const catalog = new Catalog( { name: "messages", @@ -170,18 +170,18 @@ describe("Catalog", function () { exclude: [], }, mockConfig({ - locales: ['en', 'pl'], + locales: ["en", "pl"], }) ) - const translations = catalog.getTranslations('en', { - sourceLocale: 'en', + const translations = catalog.getTranslations("en", { + sourceLocale: "en", fallbackLocales: { - default: 'en' - } - }); + default: "en", + }, + }) - console.log(translations); + console.log(translations) expect(translations).toMatchSnapshot() }) }) @@ -207,7 +207,10 @@ describe("Catalog", function () { { name: "messages", path: "locales/{locale}", - include: [fixture("collect/componentA"), fixture("collect/componentB.js")], + include: [ + fixture("collect/componentA"), + fixture("collect/componentB.js"), + ], exclude: [], }, mockConfig() @@ -215,7 +218,7 @@ describe("Catalog", function () { const messages = await catalog.collect({ ...defaultMakeOptions, - files: [fixture("collect/componentA")] + files: [fixture("collect/componentA")], }) expect(messages).toMatchSnapshot() }) @@ -718,6 +721,34 @@ describe("getCatalogs", function () { ]) }) + it("should expand {name} multiple times in path", function () { + mockFs({ + componentA: { + "index.js": mockFs.file(), + }, + }) + + const config = mockConfig({ + catalogs: [ + { + path: "{name}/locales/{locale}/{name}_messages_{locale}", + include: ["./{name}/"], + }, + ], + }) + expect(getCatalogs(config)).toEqual([ + new Catalog( + { + name: "componentA", + path: "componentA/locales/{locale}/componentA_messages_{locale}", + include: ["componentA/"], + exclude: [], + }, + config + ), + ]) + }) + it("shouldn't expand {name} for ignored directories", function () { mockFs({ componentA: { @@ -813,6 +844,23 @@ describe("getCatalogForFile", function () { expect(getCatalogForFile("./xyz/en.po", catalogs)).toBeNull() }) + it("should return matching catalog and locale if {locale} is present multiple times in path", function () { + const catalog = new Catalog( + { + name: null, + path: "./src/locales/{locale}/messages_{locale}", + include: ["./src/"], + }, + mockConfig({ format: "po" }) + ) + const catalogs = [catalog] + + expect(getCatalogForFile("./src/locales/en/messages_en.po", catalogs)).toEqual({ + locale: "en", + catalog, + }) + }) + it("should return matching catalog and locale", function () { const catalog = new Catalog( { @@ -934,12 +982,12 @@ describe("normalizeRelativePath", function () { ) }) - it("directories without ending slash are correctly treaten as dirs", function() { + it("directories without ending slash are correctly treaten as dirs", function () { mockFs({ componentA: { "index.js": mockFs.file(), }, - "componentB": mockFs.file(), + componentB: mockFs.file(), }) // checked correctly that is a dir, cuz added that ending slash expect(normalizeRelativePath("./componentA")).toEqual("componentA/") @@ -1029,23 +1077,26 @@ describe("writeCompiled", function () { name: "messages", path: path.join(localeDir, "{locale}", "messages"), include: [], - exclude: [] + exclude: [], }, mockConfig() ) it.each([ - {namespace: "es", extension: /\.mjs$/}, - {namespace: "ts", extension: /\.ts$/}, - {namespace: undefined, extension: /\.js$/}, - {namespace: 'cjs', extension: /\.js$/}, - {namespace: 'window.test', extension: /\.js$/}, - {namespace: 'global.test', extension: /\.js$/} - ])('Should save namespace $namespace in $extension extension', ({namespace, extension}) => { - const compiledCatalog = createCompiledCatalog("en", {}, {namespace}) - // Test that the file extension of the compiled catalog is `.mjs` - expect(catalog.writeCompiled("en", compiledCatalog, namespace)).toMatch( - extension - ) - }) + { namespace: "es", extension: /\.mjs$/ }, + { namespace: "ts", extension: /\.ts$/ }, + { namespace: undefined, extension: /\.js$/ }, + { namespace: "cjs", extension: /\.js$/ }, + { namespace: "window.test", extension: /\.js$/ }, + { namespace: "global.test", extension: /\.js$/ }, + ])( + "Should save namespace $namespace in $extension extension", + ({ namespace, extension }) => { + const compiledCatalog = createCompiledCatalog("en", {}, { namespace }) + // Test that the file extension of the compiled catalog is `.mjs` + expect(catalog.writeCompiled("en", compiledCatalog, namespace)).toMatch( + extension + ) + } + ) }) diff --git a/packages/cli/src/api/catalog.ts b/packages/cli/src/api/catalog.ts index 6a5b6f5f9..fa4f98366 100644 --- a/packages/cli/src/api/catalog.ts +++ b/packages/cli/src/api/catalog.ts @@ -17,7 +17,9 @@ import { CliExtractTemplateOptions } from "../lingui-extract-template" import { CompiledCatalogNamespace } from "./compile" const NAME = "{name}" +const NAME_REPLACE_RE = /{name}/g const LOCALE = "{locale}" +const LOCALE_REPLACE_RE = /{locale}/g const LOCALE_SUFFIX_RE = /\{locale\}.*$/ const PATHSEP = "/" // force posix everywhere @@ -264,10 +266,9 @@ export class Catalog { const catalogs = this.readAll() const template = this.readTemplate() || {} - return R.mapObjIndexed( (_value, key) => this.getTranslation(catalogs, locale, key, options), - { ...template, ...catalogs[locale] }, + { ...template, ...catalogs[locale] } ) } @@ -332,7 +333,8 @@ export class Catalog { // We search in fallbackLocales as dependent of each locale getMultipleFallbacks(locale) || // Get translation in fallbackLocales.default (if any) - (fallbackLocales?.default && getTranslation(fallbackLocales.default as string)) || + (fallbackLocales?.default && + getTranslation(fallbackLocales.default as string)) || // Get message default catalog[key]?.defaults || // If sourceLocale is either target locale of fallback one, use key @@ -348,7 +350,8 @@ export class Catalog { write(locale: string, messages: CatalogType) { const filename = - this.path.replace(LOCALE, locale) + this.format.catalogExtension + this.path.replace(LOCALE_REPLACE_RE, locale) + + this.format.catalogExtension const created = !fs.existsSync(filename) const basedir = path.dirname(filename) @@ -390,7 +393,7 @@ export class Catalog { ext = "js" } - const filename = `${this.path.replace(LOCALE, locale)}.${ext}` + const filename = `${this.path.replace(LOCALE_REPLACE_RE, locale)}.${ext}` const basedir = path.dirname(filename) if (!fs.existsSync(basedir)) { @@ -402,7 +405,8 @@ export class Catalog { read(locale: string) { const filename = - this.path.replace(LOCALE, locale) + this.format.catalogExtension + this.path.replace(LOCALE_REPLACE_RE, locale) + + this.format.catalogExtension if (!fs.existsSync(filename)) return null return this.format.read(filename) @@ -477,7 +481,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] { const correctPath = catalog.path.slice(0, -1) const examplePath = correctPath.replace( - LOCALE, + LOCALE_REPLACE_RE, // Show example using one of configured locales (if any) (config.locales || [])[0] || "en" ) + extension @@ -526,7 +530,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] { return } - const patterns = include.map((path) => path.replace(NAME, "*")) + const patterns = include.map((path) => path.replace(NAME_REPLACE_RE, "*")) const candidates = glob.sync( patterns.length > 1 ? `{${patterns.join(",")}}` : patterns[0], { @@ -541,9 +545,11 @@ export function getCatalogs(config: LinguiConfig): Catalog[] { new Catalog( { name, - path: normalizeRelativePath(catalog.path.replace(NAME, name)), - include: include.map((path) => path.replace(NAME, name)), - exclude: exclude.map((path) => path.replace(NAME, name)), + path: normalizeRelativePath( + catalog.path.replace(NAME_REPLACE_RE, name) + ), + include: include.map((path) => path.replace(NAME_REPLACE_RE, name)), + exclude: exclude.map((path) => path.replace(NAME_REPLACE_RE, name)), }, config ) @@ -557,7 +563,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] { export function getCatalogForFile(file: string, catalogs: Array) { for (const catalog of catalogs) { const catalogFile = `${catalog.path}${catalog.format.catalogExtension}` - const catalogGlob = catalogFile.replace(LOCALE, "*") + const catalogGlob = catalogFile.replace(LOCALE_REPLACE_RE, "*") const match = micromatch.capture( normalizeRelativePath(path.relative(catalog.config.rootDir, catalogGlob)), normalizeRelativePath(file) @@ -584,7 +590,7 @@ export function getCatalogForMerge(config: LinguiConfig) { const correctPath = catalogConfig.catalogsMergePath.slice(0, -1) const examplePath = correctPath.replace( - LOCALE, + LOCALE_REPLACE_RE, // Show example using one of configured locales (if any) (config.locales || [])[0] || "en" ) + extension diff --git a/packages/cli/src/lingui-compile.ts b/packages/cli/src/lingui-compile.ts index 4c4febe78..a826876a8 100644 --- a/packages/cli/src/lingui-compile.ts +++ b/packages/cli/src/lingui-compile.ts @@ -181,9 +181,6 @@ if (require.main === module) { // Check if Watch Mode is enabled if (program.watch) { - const NAME = "{name}" - const LOCALE = "{locale}" - console.info(chalk.bold("Initializing Watch Mode...")) const catalogs = getCatalogs(config) @@ -194,8 +191,8 @@ if (require.main === module) { catalogs.forEach((catalog) => { paths.push( `${catalog.path - .replace(LOCALE, locale) - .replace(NAME, "*")}${catalogExtension}` + .replace(/{locale}/g, locale) + .replace(/{name}/g, "*")}${catalogExtension}` ) }) }) diff --git a/packages/cli/src/services/translationIO.ts b/packages/cli/src/services/translationIO.ts index 3fca19c25..40d7137e6 100644 --- a/packages/cli/src/services/translationIO.ts +++ b/packages/cli/src/services/translationIO.ts @@ -185,9 +185,6 @@ function createPoItemFromSegment(segment) { } function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) { - const NAME = "{name}" - const LOCALE = "{locale}" - Object.keys(segmentsPerLocale).forEach((targetLocale) => { // Remove existing target POs and JS for this target locale paths[targetLocale].forEach((path) => { @@ -201,7 +198,12 @@ function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) { }) // Find target path (ignoring {name}) - const localePath = "".concat(config.catalogs[0].path.replace(LOCALE, targetLocale).replace(NAME, ''), ".po") + const localePath = "".concat( + config.catalogs[0].path + .replace(/{locale}/g, targetLocale) + .replace(/{name}/g, ""), + ".po" + ) const segments = segmentsPerLocale[targetLocale] let po = new PO() @@ -235,15 +237,16 @@ function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) { } function poPathsPerLocale(config) { - const NAME = "{name}" - const LOCALE = "{locale}" const paths = [] config.locales.forEach((locale) => { paths[locale] = [] config.catalogs.forEach((catalog) => { - const path = "".concat(catalog.path.replace(LOCALE, locale).replace(NAME, "*"), ".po") + const path = "".concat( + catalog.path.replace(/{locale}/g, locale).replace(/{name}/g, "*"), + ".po" + ) // If {name} is present (replaced by *), list all the existing POs if (path.includes('*')) {