diff --git a/packages/cli/src/api/catalog.ts b/packages/cli/src/api/catalog.ts index bfe317c27..bf290786b 100644 --- a/packages/cli/src/api/catalog.ts +++ b/packages/cli/src/api/catalog.ts @@ -76,6 +76,13 @@ export class Catalog { getTemplatePath(this.format.getTemplateExtension(), this.path) } + getFilename(locale: string): string { + return ( + replacePlaceholders(this.path, { locale }) + + this.format.getCatalogExtension() + ) + } + async make(options: MakeOptions): Promise { const nextCatalog = await this.collect({ files: options.files }) if (!nextCatalog) return false @@ -176,9 +183,7 @@ export class Catalog { locale: string, messages: CatalogType ): Promise<[created: boolean, filename: string]> { - const filename = - replacePlaceholders(this.path, { locale }) + - this.format.getCatalogExtension() + const filename = this.getFilename(locale) const created = !fs.existsSync(filename) @@ -211,11 +216,7 @@ export class Catalog { } async read(locale: string): Promise { - const filename = - replacePlaceholders(this.path, { locale }) + - this.format.getCatalogExtension() - - return await this.format.read(filename, locale) + return await this.format.read(this.getFilename(locale), locale) } async readAll(): Promise { diff --git a/packages/cli/src/api/catalog/getCatalogDependentFiles.test.ts b/packages/cli/src/api/catalog/getCatalogDependentFiles.test.ts new file mode 100644 index 000000000..ded62ee4e --- /dev/null +++ b/packages/cli/src/api/catalog/getCatalogDependentFiles.test.ts @@ -0,0 +1,76 @@ +import { getCatalogDependentFiles, getFormat } from "@lingui/cli/api" +import { makeConfig } from "@lingui/conf" +import { Catalog } from "../catalog" +import { FormatterWrapper } from "../formats" + +describe("getCatalogDependentFiles", () => { + let format: FormatterWrapper + + beforeAll(async () => { + format = await getFormat("po", {}, "en") + }) + + it("Should return list template + fallbacks + sourceLocale", () => { + const config = makeConfig( + { + locales: ["en", "pl", "es", "pt-PT", "pt-BR"], + sourceLocale: "en", + fallbackLocales: { + "pt-PT": "pt-BR", + default: "en", + }, + }, + { skipValidation: true } + ) + + const catalog = new Catalog( + { + name: null, + path: "src/locales/{locale}", + include: ["src/"], + exclude: [], + format, + }, + config + ) + + expect(getCatalogDependentFiles(catalog, "pt-PT")).toMatchInlineSnapshot(` + [ + src/locales/messages.pot, + src/locales/pt-BR.po, + src/locales/en.po, + ] + `) + }) + + it("Should not return itself", () => { + const config = makeConfig( + { + locales: ["en", "pl", "es", "pt-PT", "pt-BR"], + sourceLocale: "en", + fallbackLocales: { + "pt-PT": "pt-BR", + default: "en", + }, + }, + { skipValidation: true } + ) + + const catalog = new Catalog( + { + name: null, + path: "src/locales/{locale}", + include: ["src/"], + exclude: [], + format, + }, + config + ) + + expect(getCatalogDependentFiles(catalog, "en")).toMatchInlineSnapshot(` + [ + src/locales/messages.pot, + ] + `) + }) +}) diff --git a/packages/cli/src/api/catalog/getCatalogDependentFiles.ts b/packages/cli/src/api/catalog/getCatalogDependentFiles.ts new file mode 100644 index 000000000..8b5fbf31b --- /dev/null +++ b/packages/cli/src/api/catalog/getCatalogDependentFiles.ts @@ -0,0 +1,23 @@ +import { Catalog } from "../catalog" +import { getFallbackListForLocale } from "./getFallbackListForLocale" + +/** + * Return all files catalog implicitly depends on. + */ +export function getCatalogDependentFiles( + catalog: Catalog, + locale: string +): string[] { + const files = new Set([catalog.templateFile]) + getFallbackListForLocale(catalog.config.fallbackLocales, locale).forEach( + (locale) => { + files.add(catalog.getFilename(locale)) + } + ) + + if (catalog.config.sourceLocale && locale !== catalog.config.sourceLocale) { + files.add(catalog.getFilename(catalog.config.sourceLocale)) + } + + return Array.from(files.values()) +} diff --git a/packages/cli/src/api/catalog/getFallbackListForLocale.test.ts b/packages/cli/src/api/catalog/getFallbackListForLocale.test.ts new file mode 100644 index 000000000..d9c95605e --- /dev/null +++ b/packages/cli/src/api/catalog/getFallbackListForLocale.test.ts @@ -0,0 +1,64 @@ +import { getFallbackListForLocale } from "./getFallbackListForLocale" + +describe("getFallbackListForLocale", () => { + it("Should return normalized fallback locales for passed locale", () => { + const actual = getFallbackListForLocale( + { + "pt-PT": "pt-BR", + default: "en", + }, + "pt-PT" + ) + + expect(actual).toMatchInlineSnapshot(` + [ + pt-BR, + en, + ] + `) + }) + + it("Should work with list of fallbacks", () => { + const actual = getFallbackListForLocale( + { + "pt-PT": ["pt-BR", "pt"], + default: "en", + }, + "pt-PT" + ) + + expect(actual).toMatchInlineSnapshot(` + [ + pt-BR, + pt, + en, + ] + `) + }) + + it("Should work when no fallback set", () => { + const actual = getFallbackListForLocale( + { + default: "en", + }, + "pt-PT" + ) + + expect(actual).toMatchInlineSnapshot(` + [ + en, + ] + `) + }) + + it("Should not return itself", () => { + const actual = getFallbackListForLocale( + { + default: "en", + }, + "en" + ) + + expect(actual).toMatchInlineSnapshot(`[]`) + }) +}) diff --git a/packages/cli/src/api/catalog/getFallbackListForLocale.ts b/packages/cli/src/api/catalog/getFallbackListForLocale.ts new file mode 100644 index 000000000..1291fd0d5 --- /dev/null +++ b/packages/cli/src/api/catalog/getFallbackListForLocale.ts @@ -0,0 +1,19 @@ +import { FallbackLocales } from "@lingui/conf" + +export function getFallbackListForLocale( + fallbackLocales: FallbackLocales, + locale: string +): string[] { + const fL: string[] = [] + + if (fallbackLocales?.[locale]) { + const mapping = fallbackLocales?.[locale] + Array.isArray(mapping) ? fL.push(...mapping) : fL.push(mapping) + } + + if (fallbackLocales?.default && locale !== fallbackLocales?.default) { + fL.push(fallbackLocales?.default) + } + + return fL +} diff --git a/packages/cli/src/api/catalog/getTranslationsForCatalog.ts b/packages/cli/src/api/catalog/getTranslationsForCatalog.ts index 1a45af900..327de9994 100644 --- a/packages/cli/src/api/catalog/getTranslationsForCatalog.ts +++ b/packages/cli/src/api/catalog/getTranslationsForCatalog.ts @@ -1,6 +1,7 @@ import { Catalog } from "../catalog" import { FallbackLocales } from "@lingui/conf" import type { AllCatalogsType, CatalogType, MessageType } from "../types" +import { getFallbackListForLocale } from "./getFallbackListForLocale" export type TranslationMissingEvent = { source: string @@ -53,19 +54,14 @@ function getTranslation( } const getMultipleFallbacks = (_locale: string) => { - const fL = fallbackLocales && fallbackLocales?.[_locale] + const fL = getFallbackListForLocale(fallbackLocales, _locale) - // some probably the fallback will be undefined, so just search by locale - if (!fL) return null + if (!fL.length) return null - if (Array.isArray(fL)) { - for (const fallbackLocale of fL) { - if (catalogs[fallbackLocale] && getTranslation(fallbackLocale)) { - return getTranslation(fallbackLocale) - } + for (const fallbackLocale of fL) { + if (catalogs[fallbackLocale] && getTranslation(fallbackLocale)) { + return getTranslation(fallbackLocale) } - } else { - return getTranslation(fL) } } @@ -75,14 +71,11 @@ function getTranslation( // -> template message // ** last resort ** // -> id - let translation = // Get translation in target locale getTranslation(locale) || // We search in fallbackLocales as dependent of each locale getMultipleFallbacks(locale) || - // Get translation in fallbackLocales.default (if any) - (fallbackLocales?.default && getTranslation(fallbackLocales.default)) || (sourceLocale && sourceLocale === locale && sourceLocaleFallback(catalogs[sourceLocale], key)) diff --git a/packages/cli/src/api/index.ts b/packages/cli/src/api/index.ts index 5e8d90e81..ff8d8235d 100644 --- a/packages/cli/src/api/index.ts +++ b/packages/cli/src/api/index.ts @@ -4,5 +4,5 @@ export { getCatalogForFile, getCatalogs } from "./catalog/getCatalogs" export { createCompiledCatalog } from "./compile" export { default as extractor } from "./extractors/babel" - +export { getCatalogDependentFiles } from "./catalog/getCatalogDependentFiles" export * from "./types" diff --git a/packages/loader/src/webpackLoader.ts b/packages/loader/src/webpackLoader.ts index c8f34afa1..19a540b33 100644 --- a/packages/loader/src/webpackLoader.ts +++ b/packages/loader/src/webpackLoader.ts @@ -4,6 +4,7 @@ import { createCompiledCatalog, getCatalogs, getCatalogForFile, + getCatalogDependentFiles, } from "@lingui/cli/api" import { LoaderDefinitionFunction } from "webpack" @@ -28,6 +29,10 @@ const loader: LoaderDefinitionFunction = async function ( await getCatalogs(config) ) + getCatalogDependentFiles(catalog, locale).forEach((locale) => { + this.addDependency(catalog.getFilename(locale)) + }) + const messages = await catalog.getTranslations(locale, { fallbackLocales: config.fallbackLocales, sourceLocale: config.sourceLocale, diff --git a/packages/vite-plugin/src/index.ts b/packages/vite-plugin/src/index.ts index 2f39e3c93..d18679ede 100644 --- a/packages/vite-plugin/src/index.ts +++ b/packages/vite-plugin/src/index.ts @@ -3,6 +3,7 @@ import { createCompiledCatalog, getCatalogs, getCatalogForFile, + getCatalogDependentFiles, } from "@lingui/cli/api" import path from "path" import type { Plugin } from "vite" @@ -52,6 +53,10 @@ Please check that catalogs.path is filled properly.\n` const { locale, catalog } = fileCatalog + getCatalogDependentFiles(catalog, locale).forEach((locale) => { + this.addWatchFile(catalog.getFilename(locale)) + }) + const messages = await catalog.getTranslations(locale, { fallbackLocales: config.fallbackLocales, sourceLocale: config.sourceLocale,