Skip to content

Commit

Permalink
feat: handle lists
Browse files Browse the repository at this point in the history
  • Loading branch information
DoneDeal0 committed Sep 12, 2024
1 parent 490d456 commit b48ada0
Show file tree
Hide file tree
Showing 6 changed files with 105 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
types:
- completed
branches:
- main
- master

jobs:
publish:
Expand Down
35 changes: 33 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@
- Dynamic translations with multiple keys
- Access deeply nested keys in json translations files
- Adapts syntax to gender
- Supports lists
- Supports React Native
- Provides typescript autocompletion for your keys (🤘)
- Provides typescript autocompletion for your keys


<hr/>

Expand Down Expand Up @@ -72,7 +74,8 @@ yarn add talkr
"zero": "you don't have new messages",
"one": "you have 1 message",
"many": "you have __count__ messages"
}
},
"days": ["Monday", "Tuesday", "Wednesday"],
}
```

Expand Down Expand Up @@ -217,6 +220,34 @@ export default function MyComponent() {
}
```

# LIST

- You can render a list of string
- The last junction can be styled thanks to the following parameters:

```ts
listType?: "conjunction" | "disjunction"; // ""conjunction" by default
listStyle?: "long" | "narrow"; // "narrow" by default
```

```json
"days": ["Monday", "Tuesday", "Wednesday"],
```

```javascript
import React from "react";
import { useT } from "talkr";

export default function MyComponent() {
const { T } = useT();
return (
<>
<h1>{T("days", { listType: "conjunction" })}</h1>
</>
);
}
```

<hr/>

# LOCALE
Expand Down
12 changes: 9 additions & 3 deletions src/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PropsWithChildren } from "react";

/* eslint-disable @typescript-eslint/no-explicit-any */
export interface TProps {
children: React.ReactNode;
export interface TProps extends PropsWithChildren {
languages: Record<string, any>;
defaultLanguage: string;
detectBrowserLanguage?: boolean;
Expand Down Expand Up @@ -32,7 +33,12 @@ export type KeyPath<T> = (
? Extract<D, string>
: never;

export type TParams = { count?: number; [key: string]: any };
export type TParams = Record<string, any> & {
count?: number;
gender?: "m" | "f";
listType?: "conjunction" | "disjunction";
listStyle?: "long" | "narrow";
};

export type Autocomplete<schema> = KeyPath<schema>;

Expand Down
36 changes: 26 additions & 10 deletions src/tr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,17 @@ const getPlural = (count: number, locale: string) => {
return count === 0 ? "zero" : count === 1 ? "one" : "other";
};

const getList = (list: Array<string>, locale: string, options: TParams) => {
if (typeof Intl == "object" && typeof Intl.ListFormat == "function") {
const formatter = new Intl.ListFormat(locale, {
style: options?.listStyle || "narrow",
type: options?.listType || "conjunction",
});
return formatter.format(list);
}
return list.join(", ");
};

export function tr<Key extends string, Params extends TParams>(
{ locale, languages, defaultLanguage }: TrContext,
key: Key,
Expand All @@ -15,6 +26,7 @@ export function tr<Key extends string, Params extends TParams>(
const currentLocale = !languages[locale] ? defaultLanguage : locale;
let result = languages[currentLocale];
let currentKey: string = key;

if (params && Object.keys(params).includes("count")) {
const plural = getPlural(params.count as number, currentLocale);
currentKey +=
Expand All @@ -24,21 +36,25 @@ export function tr<Key extends string, Params extends TParams>(
? ".many"
: `.${plural}`;
}
if (params && params["gender"]) {
if (params?.gender) {
currentKey += params.gender === "m" ? ".male" : ".female";
}
currentKey.split(".").forEach((k: string) => {
if (!result[k]) return;
return (result = result[k]);
});
if (typeof result !== "string") {
console.warn(`Talkr: Missing translation for ${key}`);
return "";
if (Array.isArray(result)) {
console.log("IS ARRAY", result);
return getList(result, locale, params as Params);
}
if (typeof result === "string") {
return params
? result
.split("__")
.map((word: string) => params[word] || word)
.join("")
: result;
}
return params
? result
.split("__")
.map((word: string) => params[word] || word)
.join("")
: result;
//console.warn(`Talkr: Missing translation for ${key}`);
return "";
}
4 changes: 4 additions & 0 deletions test/dummy-app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const en = {
one: "You have 1 message",
many: "You have __count__ messages",
},
days: ["Monday", "Tuesday", "Wednesday"],
emptyList: [],
};

const fr = {
Expand All @@ -50,6 +52,8 @@ const fr = {
one: "Vous avez 1 message",
many: "Vous avez __count__ messages",
},
days: ["Lundi", "Mardi", "Mercredi"],
emptyList: [],
};

const DummyApp: FC<{ children: ReactNode }> = ({ children }) => {
Expand Down
32 changes: 32 additions & 0 deletions test/index.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ const Component = () => {
<div data-testid="talkr-queen">
{T("idiom.sovereign", { gender: "f" })}
</div>
<div data-testid="talkr-days-conjunction">{T("days")}</div>
<div data-testid="talkr-days-conjunction-long">
{T("days", { listStyle: "long" })}
</div>
<div data-testid="talkr-days-disjunction">
{T("days", { listType: "disjunction" })}
</div>
<div data-testid=" talkr-empty-list">
{T("empty-list", { listType: "disjunction" })}
</div>
<div data-testid="talkr-unknown">{T("unknown.key")}</div>
<button role="button" onClick={() => setLocale("fr")}>
speak french
Expand Down Expand Up @@ -107,6 +117,28 @@ describe("gender", () => {
});
});

describe("list", () => {
it("return a conjuction list", () => {
render(<Component />);
expect(screen.getByTestId("talkr-days-conjunction").textContent).toEqual(
"Monday, Tuesday, Wednesday",
);
expect(
screen.getByTestId("talkr-days-conjunction-long").textContent,
).toEqual("Monday, Tuesday, and Wednesday");
});
it("return a disjunction list", () => {
render(<Component />);
expect(screen.getByTestId("talkr-days-disjunction").textContent).toEqual(
"Monday, Tuesday, or Wednesday",
);
});
it("return a empty string if the list is empty", () => {
render(<Component />);
expect(screen.getByTestId("talkr-empty-list").textContent).toEqual("");
});
});

describe("live translation update", () => {
it("update a translation based on a dynamic counter", () => {
render(<Counter />);
Expand Down

0 comments on commit b48ada0

Please sign in to comment.