-
Notifications
You must be signed in to change notification settings - Fork 397
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: load remote catalogs with remoteLoader() (#1080)
- Loading branch information
Showing
14 changed files
with
180 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
declare type RemoteLoaderMessages<T> = string | Record<string, any> | T; | ||
export declare function remoteLoader<T>(locale: string, messages: RemoteLoaderMessages<T>, fallbackMessages?: RemoteLoaderMessages<T>): any; | ||
export {}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
export { default } from "./src" | ||
export { remoteLoader } from "./src/remoteLoader" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export { default } from "./webpackLoader" | ||
export { remoteLoader } from "./remoteLoader" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { remoteLoader } from "./remoteLoader" | ||
import fs from "fs" | ||
import path from "path" | ||
|
||
describe("remote-loader", () => { | ||
it("should compile correctly JSON messages coming from the fly", async () => { | ||
const unlink = createConfig("minimal") | ||
const messages = await simulatedJsonResponse() | ||
expect(remoteLoader("en", messages)).toMatchInlineSnapshot( | ||
`/*eslint-disable*/module.exports={messages:{"property.key":"value","{0} Deposited":[["0"]," Deposited"],"{0} Strategy":[["0"]," Strategy"]}};` | ||
) | ||
unlink() | ||
}) | ||
|
||
it("should compile correctly .po messages coming from the fly", async () => { | ||
const unlink = createConfig("po") | ||
const messages = await simulatedPoResponse() | ||
expect(remoteLoader("en", messages)).toMatchInlineSnapshot( | ||
`/*eslint-disable*/module.exports={messages:{"Hello World":"Hello World","My name is {name}":["My name is ",["name"]]}};` | ||
) | ||
unlink() | ||
}) | ||
|
||
describe("fallbacks", () => { | ||
it("should fallback correctly to the fallback collection", async () => { | ||
const unlink = createConfig("minimal") | ||
const messages = await simulatedJsonResponse(true) | ||
const fallbackMessages = await simulatedJsonResponse() | ||
|
||
expect( | ||
remoteLoader("en", messages, fallbackMessages) | ||
).toMatchInlineSnapshot( | ||
`/*eslint-disable*/module.exports={messages:{"property.key":"value","{0} Deposited":[["0"]," Deposited"],"{0} Strategy":[["0"]," Strategy"]}};` | ||
) | ||
unlink() | ||
}) | ||
|
||
it("should fallback to compiled fallback", async () => { | ||
const unlink = createConfig("po") | ||
const messages = await simulatedPoResponse("es") | ||
const fallbackMessages = await simulatedPoCompiledFile() | ||
|
||
expect( | ||
remoteLoader("en", messages, fallbackMessages) | ||
).toMatchInlineSnapshot( | ||
`/*eslint-disable*/module.exports={messages:{"Hello World":"Hello World","My name is {name}":["My name is ",["name"]]}};` | ||
) | ||
unlink() | ||
}) | ||
}) | ||
}) | ||
|
||
function simulatedJsonResponse(nully?: boolean) { | ||
return new Promise((resolve) => { | ||
resolve({ | ||
"property.key": nully ? "" : "value", | ||
"{0} Deposited": "{0} Deposited", | ||
"{0} Strategy": "{0} Strategy", | ||
}) | ||
}) | ||
} | ||
|
||
function simulatedPoResponse(locale = "en") { | ||
return new Promise((resolve) => { | ||
const file = fs.readFileSync( | ||
path.join(__dirname, "..", "test/locale/" + locale + "/messages.po"), | ||
"utf-8" | ||
) | ||
resolve(file) | ||
}) | ||
} | ||
|
||
function simulatedPoCompiledFile() { | ||
return new Promise((resolve) => { | ||
resolve({ | ||
"Hello World": "Hello World", | ||
"My name is {name}": ["My name is ", ["name"]], | ||
}) | ||
}) | ||
} | ||
|
||
function createConfig(format: string) { | ||
const filename = `${process.cwd()}/.linguirc` | ||
const config = ` | ||
{ | ||
'locales': ['en'], | ||
'catalogs': [{ | ||
'path': 'locale/{locale}/messages' | ||
}], | ||
'format': '${format}' | ||
} | ||
` | ||
fs.writeFileSync(filename, config) | ||
return () => fs.unlinkSync(filename) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import R from "ramda" | ||
import { getConfig } from "@lingui/conf" | ||
import { createCompiledCatalog, getFormat } from "@lingui/cli/api" | ||
|
||
type RemoteLoaderMessages<T> = string | Record<string, any> | T | ||
|
||
export function remoteLoader<T>(locale: string, messages: RemoteLoaderMessages<T>, fallbackMessages?: RemoteLoaderMessages<T>) { | ||
const config = getConfig() | ||
|
||
// When format is minimal, everything works fine because are .json files, | ||
// but when is csv, .po, .po-gettext needs to be parsed to something interpretable | ||
let parsedMessages; | ||
let parsedFallbackMessages; | ||
if (config.format) { | ||
const formatter = getFormat(config.format) | ||
if (fallbackMessages) { | ||
// we do this because, people could just import the fallback and import the ./en/messages.js | ||
// generated by lingui and the use case of format .po but fallback as .json module could be perfectly valid | ||
parsedFallbackMessages = typeof fallbackMessages === "object" ? getFormat("minimal").parse(fallbackMessages) : formatter.parse(fallbackMessages) | ||
} | ||
|
||
parsedMessages = formatter.parse(messages) | ||
} else { | ||
throw new Error(` | ||
*format* value in the Lingui configuration is required to make this loader 100% functional | ||
Read more about this here: https://lingui.js.org/ref/conf.html#format | ||
`) | ||
} | ||
|
||
|
||
const mapTranslationsToInterporlatedString = R.mapObjIndexed( | ||
(_, key) => { | ||
// if there's fallback and translation is empty, return the fallback | ||
if (parsedMessages[key].translation === "" && parsedFallbackMessages?.[key]?.translation) { | ||
return parsedFallbackMessages[key].translation | ||
} | ||
|
||
return parsedMessages[key].translation | ||
}, | ||
parsedMessages | ||
) | ||
|
||
// In production we don't want untranslated strings. It's better to use message | ||
// keys as a last resort. | ||
// In development, however, we want to catch missing strings with `missing` parameter | ||
// of I18nProvider (React) or setupI18n (core) and therefore we need to get | ||
// empty translations if missing. | ||
const strict = process.env.NODE_ENV !== "production" | ||
return createCompiledCatalog(locale, mapTranslationsToInterporlatedString, { | ||
strict, | ||
...config, | ||
namespace: config.compileNamespace, | ||
pseudoLocale: config.pseudoLocale, | ||
}) | ||
|
||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
msgid "Hello World" | ||
msgstr "" | ||
|
||
msgid "My name is {name}" | ||
msgstr "My name is {name}" |
e73a4b3
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs: