Skip to content
Open
33 changes: 33 additions & 0 deletions packages/i18n/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@serverlessworkflow/i18n",
"version": "1.0.0",
"files": [
"dist",
"src"
],
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"clean": "rimraf ./dist",
"build": "pnpm clean && tsc -p tsconfig.json",
"build:prod": "pnpm run build"
},
"devDependencies": {
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"rimraf": "catalog:",
"typescript": "catalog:"
},
"peerDependencies": {
"react": "^19.0.0",
"react-dom": "^19.0.0"
}
}
29 changes: 29 additions & 0 deletions packages/i18n/src/core/createI18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export type Dictionary = Record<string, string>;
export type Dictionaries = Record<string, Dictionary>;

export function createI18n(dictionaries: Dictionaries, locale: string) {
function t(key: string): string {
return dictionaries[locale]?.[key] ?? key;
}

return {
t,
locale,
};
}
19 changes: 19 additions & 0 deletions packages/i18n/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export * from "./react/I18nProvider";
export * from "./core/createI18n";
export * from "./utils/detectLocale";
46 changes: 46 additions & 0 deletions packages/i18n/src/react/I18nProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import React, { createContext, useContext, useMemo } from "react";
import { createI18n, Dictionaries } from "../core/createI18n";

type I18nContextType = ReturnType<typeof createI18n>;

const I18nContext = createContext<I18nContextType | null>(null);

export const I18nProvider = ({
children,
locale,
dictionaries,
}: {
children: React.ReactNode;
locale: string;
dictionaries: Dictionaries;
}) => {
const i18n = useMemo(() => {
return createI18n(dictionaries, locale);
}, [locale, dictionaries]);

return <I18nContext.Provider value={i18n}>{children}</I18nContext.Provider>;
};

export const useI18n = () => {
const ctx = useContext(I18nContext);
if (!ctx) {
throw new Error("useI18n must be used inside I18nProvider");
}
return ctx;
};
36 changes: 36 additions & 0 deletions packages/i18n/src/utils/detectLocale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export function detectLocale(supportedLocales: readonly string[], fallback: string = "en"): string {
if (typeof navigator === "undefined") {
return fallback;
}
const languages =
navigator.languages && navigator.languages.length > 0
? navigator.languages
: [navigator.language];

const normalizedSupported = supportedLocales.map((l) => l.toLowerCase().trim());
for (const lang of languages) {
if (!lang) continue;

const short = lang.split("-")[0]?.toLowerCase().trim();
if (short && normalizedSupported.includes(short)) {
return short;
}
}
return fallback;
}
14 changes: 14 additions & 0 deletions packages/i18n/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"declaration": true,
"declarationMap": true,
"sourceMap": true,

"rootDirs": ["./src", "./tests"],
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "dist-storybook"]
}
1 change: 1 addition & 0 deletions packages/serverless-workflow-diagram-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"build:storybook": "pnpm clean:storybook && storybook build --output-dir ./dist-storybook"
},
"dependencies": {
"@serverlessworkflow/i18n": "workspace:*",
"@xyflow/react": "catalog:"
},
"devDependencies": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@

import * as React from "react";
import { Diagram, DiagramRef } from "../react-flow/diagram/Diagram";

import { I18nProvider, useI18n, detectLocale } from "@serverlessworkflow/i18n";
import { dictionaries } from "../i18n/locales";
/**
* DiagramEditor component API
*/
Expand All @@ -26,33 +27,42 @@ export type DiagramEditorRef = {

export type DiagramEditorProps = {
isReadOnly: boolean;
locale: string;
ref?: React.Ref<DiagramEditorRef>;
locale?: string;
};

const Content = () => {
const { t } = useI18n();
return <p>{t("save")}</p>;
};

export const DiagramEditor = ({ ref }: DiagramEditorProps) => {
export const DiagramEditor = (props: DiagramEditorProps) => {
// TODO: i18n
// TODO: store, context
// TODO: ErrorBoundary / fallback

// Refs
const diagramDivRef = React.useRef<HTMLDivElement | null>(null);
const diagramRef = React.useRef<DiagramRef | null>(null);
const supportedLocales = Object.keys(dictionaries);
const locale = detectLocale(supportedLocales);

// Allow imperatively controlling the Editor
React.useImperativeHandle(
ref,
() => ({
doSomething: () => {
// TODO: to be implemented, it is just a placeholder
},
}),
[],
);
// React.useImperativeHandle(
// ref,
// () => ({
// doSomething: () => {
// TODO: to be implemented, it is just a placeholder
// },
// }),
// [],
// );

return (
<>
<Diagram ref={diagramRef} divRef={diagramDivRef} />
<I18nProvider locale={locale} dictionaries={dictionaries}>
<Diagram ref={diagramRef} divRef={diagramDivRef} />
<Content />
</I18nProvider>
</>
);
};
23 changes: 23 additions & 0 deletions packages/serverless-workflow-diagram-editor/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export const en = {
save: "Save",
delete: "Delete",
cancel: "Cancel",
} as const;

export type TranslationKeys = keyof typeof en;
23 changes: 23 additions & 0 deletions packages/serverless-workflow-diagram-editor/src/i18n/locales/fr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import type { TranslationKeys } from "./en";

export const fr: Record<TranslationKeys, string> = {
save: "Sauvegarder",
delete: "Supprimer",
cancel: "Annuler",
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { en } from "./en";
import { fr } from "./fr";

export const dictionaries = {
en,
fr,
};
Loading