feat(i18n): let translators edit the catalogues in the browser with Fink

Add an inlang project so Fink opens the repository directly:
English as the source, the nine shipped locales, and the
message-format plugin pinned to 4.5.5 reading messages/{locale}.json.
That plugin keeps the nested keys and {placeholder} tokens as they
are; the i18next and next-intl plugins Fink also supports either
rewrite numeric-keyed objects as arrays or misread quotes around a
placeholder as ICU escapes.

Checked against these files with the inlang CLI and SDK: all nine
locales load with no errors and no findings, a load and save without
edits leaves every file byte-identical, and an edit changes exactly
the edited lines. inlang's working files beside settings.json ignore
themselves in git and are kept out of the format check.

CONTRIBUTING gains the browser workflow (fork, push, pull request),
what a reviewer checks, and that machine translation is a draft a
reader of the language goes through before the pull request; the
new-locale steps now name the real locale list. A test keeps the
project's locales equal to the shipped ones and the plugin pinned.
This commit is contained in:
Marc-André Bombeck committed 2026-10-22 15:03:16 +02:00
1 parent dc9beaefb7
commit 2d16183b58
5 files changed
+95 -11

No files matched your search

@@ -0,0 +1,48 @@
/**
* The inlang project that the Fink browser editor opens.
*
* `project.inlang/settings.json` names the locales Fink offers and the files it
* writes. A locale added to the i18n config but not here never shows
* up in the editor, and one listed here but not shipped would invite
* translations of a bundle the app never loads. The plugin is pinned to an
* exact version: it decides how the nested JSON is written back, and the
* round trip was verified against that version only (an unpinned URL would
* pick up a new serialiser on its own).
*/
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { defaultLocale, locales } from "@/lib/i18n/config";
const SETTINGS = join(__dirname, "../../project.inlang/settings.json");
interface InlangSettings {
baseLocale: string;
locales: string[];
modules: string[];
"plugin.inlang.messageFormat"?: { pathPattern?: string };
}
const settings = JSON.parse(readFileSync(SETTINGS, "utf8")) as InlangSettings;
describe("inlang project settings", () => {
it("offers exactly the shipped locales, English as the source", () => {
expect(settings.baseLocale).toBe(defaultLocale);
expect([...settings.locales].sort()).toEqual([...locales].sort());
});
it("points the message-format plugin at the catalogues", () => {
expect(settings["plugin.inlang.messageFormat"]?.pathPattern).toBe(
"./messages/{locale}.json",
);
});
it("pins the one plugin to an exact version", () => {
expect(settings.modules).toHaveLength(1);
expect(settings.modules[0]).toMatch(
/^https:\/\/cdn\.jsdelivr\.net\/npm\/@inlang\/plugin-message-format@\d+\.\d+\.\d+\/dist\/index\.js$/,
);
});
});