Skip to content

Commit 9e2d7d2

Browse files
authored
fix(cli): {locale} and {name} replace only once in catalog path (#1342)
1 parent 12ad0df commit 9e2d7d2

File tree

4 files changed

+115
-58
lines changed

4 files changed

+115
-58
lines changed

packages/cli/src/api/catalog.test.ts

+84-33
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ describe("Catalog", function () {
132132
})
133133

134134
describe("POT Flow", function () {
135-
it('Should merge source messages from template if provided', () => {
135+
it("Should merge source messages from template if provided", () => {
136136
const catalog = new Catalog(
137137
{
138138
name: "messages",
@@ -144,21 +144,21 @@ describe("Catalog", function () {
144144
exclude: [],
145145
},
146146
mockConfig({
147-
locales: ['en', 'pl'],
147+
locales: ["en", "pl"],
148148
})
149149
)
150150

151-
const translations = catalog.getTranslations('pl', {
152-
sourceLocale: 'en',
151+
const translations = catalog.getTranslations("pl", {
152+
sourceLocale: "en",
153153
fallbackLocales: {
154-
default: 'en'
155-
}
156-
});
154+
default: "en",
155+
},
156+
})
157157

158158
expect(translations).toMatchSnapshot()
159159
})
160160

161-
it('Should get translations from template if locale file not presented', () => {
161+
it("Should get translations from template if locale file not presented", () => {
162162
const catalog = new Catalog(
163163
{
164164
name: "messages",
@@ -170,18 +170,18 @@ describe("Catalog", function () {
170170
exclude: [],
171171
},
172172
mockConfig({
173-
locales: ['en', 'pl'],
173+
locales: ["en", "pl"],
174174
})
175175
)
176176

177-
const translations = catalog.getTranslations('en', {
178-
sourceLocale: 'en',
177+
const translations = catalog.getTranslations("en", {
178+
sourceLocale: "en",
179179
fallbackLocales: {
180-
default: 'en'
181-
}
182-
});
180+
default: "en",
181+
},
182+
})
183183

184-
console.log(translations);
184+
console.log(translations)
185185
expect(translations).toMatchSnapshot()
186186
})
187187
})
@@ -207,15 +207,18 @@ describe("Catalog", function () {
207207
{
208208
name: "messages",
209209
path: "locales/{locale}",
210-
include: [fixture("collect/componentA"), fixture("collect/componentB.js")],
210+
include: [
211+
fixture("collect/componentA"),
212+
fixture("collect/componentB.js"),
213+
],
211214
exclude: [],
212215
},
213216
mockConfig()
214217
)
215218

216219
const messages = await catalog.collect({
217220
...defaultMakeOptions,
218-
files: [fixture("collect/componentA")]
221+
files: [fixture("collect/componentA")],
219222
})
220223
expect(messages).toMatchSnapshot()
221224
})
@@ -718,6 +721,34 @@ describe("getCatalogs", function () {
718721
])
719722
})
720723

724+
it("should expand {name} multiple times in path", function () {
725+
mockFs({
726+
componentA: {
727+
"index.js": mockFs.file(),
728+
},
729+
})
730+
731+
const config = mockConfig({
732+
catalogs: [
733+
{
734+
path: "{name}/locales/{locale}/{name}_messages_{locale}",
735+
include: ["./{name}/"],
736+
},
737+
],
738+
})
739+
expect(getCatalogs(config)).toEqual([
740+
new Catalog(
741+
{
742+
name: "componentA",
743+
path: "componentA/locales/{locale}/componentA_messages_{locale}",
744+
include: ["componentA/"],
745+
exclude: [],
746+
},
747+
config
748+
),
749+
])
750+
})
751+
721752
it("shouldn't expand {name} for ignored directories", function () {
722753
mockFs({
723754
componentA: {
@@ -813,6 +844,23 @@ describe("getCatalogForFile", function () {
813844
expect(getCatalogForFile("./xyz/en.po", catalogs)).toBeNull()
814845
})
815846

847+
it("should return matching catalog and locale if {locale} is present multiple times in path", function () {
848+
const catalog = new Catalog(
849+
{
850+
name: null,
851+
path: "./src/locales/{locale}/messages_{locale}",
852+
include: ["./src/"],
853+
},
854+
mockConfig({ format: "po" })
855+
)
856+
const catalogs = [catalog]
857+
858+
expect(getCatalogForFile("./src/locales/en/messages_en.po", catalogs)).toEqual({
859+
locale: "en",
860+
catalog,
861+
})
862+
})
863+
816864
it("should return matching catalog and locale", function () {
817865
const catalog = new Catalog(
818866
{
@@ -934,12 +982,12 @@ describe("normalizeRelativePath", function () {
934982
)
935983
})
936984

937-
it("directories without ending slash are correctly treaten as dirs", function() {
985+
it("directories without ending slash are correctly treaten as dirs", function () {
938986
mockFs({
939987
componentA: {
940988
"index.js": mockFs.file(),
941989
},
942-
"componentB": mockFs.file(),
990+
componentB: mockFs.file(),
943991
})
944992
// checked correctly that is a dir, cuz added that ending slash
945993
expect(normalizeRelativePath("./componentA")).toEqual("componentA/")
@@ -1029,23 +1077,26 @@ describe("writeCompiled", function () {
10291077
name: "messages",
10301078
path: path.join(localeDir, "{locale}", "messages"),
10311079
include: [],
1032-
exclude: []
1080+
exclude: [],
10331081
},
10341082
mockConfig()
10351083
)
10361084

10371085
it.each([
1038-
{namespace: "es", extension: /\.mjs$/},
1039-
{namespace: "ts", extension: /\.ts$/},
1040-
{namespace: undefined, extension: /\.js$/},
1041-
{namespace: 'cjs', extension: /\.js$/},
1042-
{namespace: 'window.test', extension: /\.js$/},
1043-
{namespace: 'global.test', extension: /\.js$/}
1044-
])('Should save namespace $namespace in $extension extension', ({namespace, extension}) => {
1045-
const compiledCatalog = createCompiledCatalog("en", {}, {namespace})
1046-
// Test that the file extension of the compiled catalog is `.mjs`
1047-
expect(catalog.writeCompiled("en", compiledCatalog, namespace)).toMatch(
1048-
extension
1049-
)
1050-
})
1086+
{ namespace: "es", extension: /\.mjs$/ },
1087+
{ namespace: "ts", extension: /\.ts$/ },
1088+
{ namespace: undefined, extension: /\.js$/ },
1089+
{ namespace: "cjs", extension: /\.js$/ },
1090+
{ namespace: "window.test", extension: /\.js$/ },
1091+
{ namespace: "global.test", extension: /\.js$/ },
1092+
])(
1093+
"Should save namespace $namespace in $extension extension",
1094+
({ namespace, extension }) => {
1095+
const compiledCatalog = createCompiledCatalog("en", {}, { namespace })
1096+
// Test that the file extension of the compiled catalog is `.mjs`
1097+
expect(catalog.writeCompiled("en", compiledCatalog, namespace)).toMatch(
1098+
extension
1099+
)
1100+
}
1101+
)
10511102
})

packages/cli/src/api/catalog.ts

+19-13
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import { CliExtractTemplateOptions } from "../lingui-extract-template"
1717
import { CompiledCatalogNamespace } from "./compile"
1818

1919
const NAME = "{name}"
20+
const NAME_REPLACE_RE = /{name}/g
2021
const LOCALE = "{locale}"
22+
const LOCALE_REPLACE_RE = /{locale}/g
2123
const LOCALE_SUFFIX_RE = /\{locale\}.*$/
2224
const PATHSEP = "/" // force posix everywhere
2325

@@ -264,10 +266,9 @@ export class Catalog {
264266
const catalogs = this.readAll()
265267
const template = this.readTemplate() || {}
266268

267-
268269
return R.mapObjIndexed(
269270
(_value, key) => this.getTranslation(catalogs, locale, key, options),
270-
{ ...template, ...catalogs[locale] },
271+
{ ...template, ...catalogs[locale] }
271272
)
272273
}
273274

@@ -332,7 +333,8 @@ export class Catalog {
332333
// We search in fallbackLocales as dependent of each locale
333334
getMultipleFallbacks(locale) ||
334335
// Get translation in fallbackLocales.default (if any)
335-
(fallbackLocales?.default && getTranslation(fallbackLocales.default as string)) ||
336+
(fallbackLocales?.default &&
337+
getTranslation(fallbackLocales.default as string)) ||
336338
// Get message default
337339
catalog[key]?.defaults ||
338340
// If sourceLocale is either target locale of fallback one, use key
@@ -348,7 +350,8 @@ export class Catalog {
348350

349351
write(locale: string, messages: CatalogType) {
350352
const filename =
351-
this.path.replace(LOCALE, locale) + this.format.catalogExtension
353+
this.path.replace(LOCALE_REPLACE_RE, locale) +
354+
this.format.catalogExtension
352355

353356
const created = !fs.existsSync(filename)
354357
const basedir = path.dirname(filename)
@@ -390,7 +393,7 @@ export class Catalog {
390393
ext = "js"
391394
}
392395

393-
const filename = `${this.path.replace(LOCALE, locale)}.${ext}`
396+
const filename = `${this.path.replace(LOCALE_REPLACE_RE, locale)}.${ext}`
394397

395398
const basedir = path.dirname(filename)
396399
if (!fs.existsSync(basedir)) {
@@ -402,7 +405,8 @@ export class Catalog {
402405

403406
read(locale: string) {
404407
const filename =
405-
this.path.replace(LOCALE, locale) + this.format.catalogExtension
408+
this.path.replace(LOCALE_REPLACE_RE, locale) +
409+
this.format.catalogExtension
406410

407411
if (!fs.existsSync(filename)) return null
408412
return this.format.read(filename)
@@ -477,7 +481,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] {
477481
const correctPath = catalog.path.slice(0, -1)
478482
const examplePath =
479483
correctPath.replace(
480-
LOCALE,
484+
LOCALE_REPLACE_RE,
481485
// Show example using one of configured locales (if any)
482486
(config.locales || [])[0] || "en"
483487
) + extension
@@ -526,7 +530,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] {
526530
return
527531
}
528532

529-
const patterns = include.map((path) => path.replace(NAME, "*"))
533+
const patterns = include.map((path) => path.replace(NAME_REPLACE_RE, "*"))
530534
const candidates = glob.sync(
531535
patterns.length > 1 ? `{${patterns.join(",")}}` : patterns[0],
532536
{
@@ -541,9 +545,11 @@ export function getCatalogs(config: LinguiConfig): Catalog[] {
541545
new Catalog(
542546
{
543547
name,
544-
path: normalizeRelativePath(catalog.path.replace(NAME, name)),
545-
include: include.map((path) => path.replace(NAME, name)),
546-
exclude: exclude.map((path) => path.replace(NAME, name)),
548+
path: normalizeRelativePath(
549+
catalog.path.replace(NAME_REPLACE_RE, name)
550+
),
551+
include: include.map((path) => path.replace(NAME_REPLACE_RE, name)),
552+
exclude: exclude.map((path) => path.replace(NAME_REPLACE_RE, name)),
547553
},
548554
config
549555
)
@@ -557,7 +563,7 @@ export function getCatalogs(config: LinguiConfig): Catalog[] {
557563
export function getCatalogForFile(file: string, catalogs: Array<Catalog>) {
558564
for (const catalog of catalogs) {
559565
const catalogFile = `${catalog.path}${catalog.format.catalogExtension}`
560-
const catalogGlob = catalogFile.replace(LOCALE, "*")
566+
const catalogGlob = catalogFile.replace(LOCALE_REPLACE_RE, "*")
561567
const match = micromatch.capture(
562568
normalizeRelativePath(path.relative(catalog.config.rootDir, catalogGlob)),
563569
normalizeRelativePath(file)
@@ -584,7 +590,7 @@ export function getCatalogForMerge(config: LinguiConfig) {
584590
const correctPath = catalogConfig.catalogsMergePath.slice(0, -1)
585591
const examplePath =
586592
correctPath.replace(
587-
LOCALE,
593+
LOCALE_REPLACE_RE,
588594
// Show example using one of configured locales (if any)
589595
(config.locales || [])[0] || "en"
590596
) + extension

packages/cli/src/lingui-compile.ts

+2-5
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,6 @@ if (require.main === module) {
198198

199199
// Check if Watch Mode is enabled
200200
if (program.watch) {
201-
const NAME = "{name}"
202-
const LOCALE = "{locale}"
203-
204201
console.info(chalk.bold("Initializing Watch Mode..."))
205202

206203
const catalogs = getCatalogs(config)
@@ -211,8 +208,8 @@ if (require.main === module) {
211208
catalogs.forEach((catalog) => {
212209
paths.push(
213210
`${catalog.path
214-
.replace(LOCALE, locale)
215-
.replace(NAME, "*")}${catalogExtension}`
211+
.replace(/{locale}/g, locale)
212+
.replace(/{name}/g, "*")}${catalogExtension}`
216213
)
217214
})
218215
})

packages/cli/src/services/translationIO.ts

+10-7
Original file line numberDiff line numberDiff line change
@@ -185,9 +185,6 @@ function createPoItemFromSegment(segment) {
185185
}
186186

187187
function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) {
188-
const NAME = "{name}"
189-
const LOCALE = "{locale}"
190-
191188
Object.keys(segmentsPerLocale).forEach((targetLocale) => {
192189
// Remove existing target POs and JS for this target locale
193190
paths[targetLocale].forEach((path) => {
@@ -201,7 +198,12 @@ function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) {
201198
})
202199

203200
// Find target path (ignoring {name})
204-
const localePath = "".concat(config.catalogs[0].path.replace(LOCALE, targetLocale).replace(NAME, ''), ".po")
201+
const localePath = "".concat(
202+
config.catalogs[0].path
203+
.replace(/{locale}/g, targetLocale)
204+
.replace(/{name}/g, ""),
205+
".po"
206+
)
205207
const segments = segmentsPerLocale[targetLocale]
206208

207209
let po = new PO()
@@ -235,15 +237,16 @@ function saveSegmentsToTargetPos(config, paths, segmentsPerLocale) {
235237
}
236238

237239
function poPathsPerLocale(config) {
238-
const NAME = "{name}"
239-
const LOCALE = "{locale}"
240240
const paths = []
241241

242242
config.locales.forEach((locale) => {
243243
paths[locale] = []
244244

245245
config.catalogs.forEach((catalog) => {
246-
const path = "".concat(catalog.path.replace(LOCALE, locale).replace(NAME, "*"), ".po")
246+
const path = "".concat(
247+
catalog.path.replace(/{locale}/g, locale).replace(/{name}/g, "*"),
248+
".po"
249+
)
247250

248251
// If {name} is present (replaced by *), list all the existing POs
249252
if (path.includes('*')) {

0 commit comments

Comments
 (0)