-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split compiled message catalogs into multiple files (#1407)
This is an attempt to resolve one of the big pain points present in lingui, which is that lingui only creates a single monolithic message file. Right now this is solved by a script called `localebuilder.js` that parses the `messages.po` created by `lingui extract` and the `messages.js` created by `lingui compile` and attempts to associate AST nodes representing message definitions with the source files they're present in, as documented in the references of `messages.po`. The JS is parsed by Babel, which is already a dependency, while the PO is parsed by pofile, a library that lingui already uses, so in the end we don't bring in any new dependencies. We then split up the `messages.js` by slicing it up into "chunks": right now just a "base" chunk and a "norent" one, putting all the messages that come from `frontend/lib/norent` into one compiled message catalog and everything else into another. This leaves it up to us to make sure that anything in our code which uses components from that directory also loads its corresponding message catalog, which isn't awesome, but it's still way better than having a single monolithic bundle.
- Loading branch information
Showing
16 changed files
with
521 additions
and
84 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import fs from "fs"; | ||
import path from "path"; | ||
import { parseCompiledMessages } from "./parse-compiled-messages"; | ||
import { parseExtractedMessages } from "./parse-extracted-messages"; | ||
import { | ||
MessageCatalogSplitterChunkConfig, | ||
MessageCatalogSplitter, | ||
} from "./message-catalog-splitter"; | ||
import { | ||
MessageCatalogPaths, | ||
getAllMessageCatalogPaths, | ||
} from "./message-catalog-paths"; | ||
|
||
const MY_DIR = __dirname; | ||
|
||
const LOCALES_DIR = path.resolve(path.join(MY_DIR, "..", "..", "locales")); | ||
|
||
/** | ||
* This encapsulates our criteria for splitting up Lingui's | ||
* single message catalog into separate individual "chunks". | ||
*/ | ||
const SPLIT_CHUNK_CONFIGS: MessageCatalogSplitterChunkConfig[] = [ | ||
/** | ||
* Any strings that are *only* present in the norent directory | ||
* will go into their own chunk. | ||
*/ | ||
{ | ||
name: "norent", | ||
test: (s) => s.startsWith("frontend/lib/norent/"), | ||
}, | ||
/** | ||
* Everything else goes into a separate chunk. | ||
*/ | ||
{ | ||
name: "base", | ||
test: (s) => true, | ||
}, | ||
]; | ||
|
||
/** | ||
* Split up the message catalog for a single locale. | ||
*/ | ||
function processLocale(paths: MessageCatalogPaths) { | ||
console.log(`Processing locale '${paths.locale}'.`); | ||
|
||
const messagesJs = fs.readFileSync(paths.js, { | ||
encoding: "utf-8", | ||
}); | ||
const compiled = parseCompiledMessages(messagesJs); | ||
const messagesPo = fs.readFileSync(paths.po, { | ||
encoding: "utf-8", | ||
}); | ||
var extracted = parseExtractedMessages(messagesPo); | ||
const splitter = new MessageCatalogSplitter(extracted, compiled, { | ||
locale: paths.locale, | ||
rootDir: paths.rootDir, | ||
chunks: SPLIT_CHUNK_CONFIGS, | ||
}); | ||
splitter.split(); | ||
} | ||
|
||
/** | ||
* Main function to run the localebuilder CLI. | ||
*/ | ||
export function run() { | ||
const allPaths = getAllMessageCatalogPaths(LOCALES_DIR); | ||
for (let paths of allPaths) { | ||
processLocale(paths); | ||
} | ||
} |
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,53 @@ | ||
import fs from "fs"; | ||
import path from "path"; | ||
|
||
/** | ||
* Represents information about filesystem | ||
* paths to a particular locale's message catalog. | ||
*/ | ||
export type MessageCatalogPaths = { | ||
/** The locale this object represents (e.g. 'en'). */ | ||
locale: string; | ||
|
||
/** | ||
* The root directory where the locale's catalog | ||
* is kept (e.g. '/foo/en`). | ||
*/ | ||
rootDir: string; | ||
|
||
/** | ||
* The absolute path to the compiled message catalog | ||
* (JavaScript) for the locale. | ||
*/ | ||
js: string; | ||
|
||
/** | ||
* The absolute path to the extracted message catalog | ||
* (PO) for the locale. | ||
*/ | ||
po: string; | ||
}; | ||
|
||
/** | ||
* Given a root directory where all locale data lives, | ||
* returns a list of path information about every locale. | ||
*/ | ||
export function getAllMessageCatalogPaths( | ||
rootDir: string | ||
): MessageCatalogPaths[] { | ||
const result: MessageCatalogPaths[] = []; | ||
|
||
fs.readdirSync(rootDir).forEach((filename) => { | ||
if (/^[._]/.test(filename)) return; | ||
const abspath = path.join(rootDir, filename); | ||
const stat = fs.statSync(abspath); | ||
if (!stat.isDirectory()) return; | ||
const js = path.join(abspath, "messages.js"); | ||
const po = path.join(abspath, "messages.po"); | ||
if (fs.existsSync(js) && fs.existsSync(po)) { | ||
result.push({ locale: filename, rootDir: abspath, js, po }); | ||
} | ||
}); | ||
|
||
return result; | ||
} |
Oops, something went wrong.