@terrafirmagreg-team/translate 0.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +19 -12
- package/src/{provider.tsx → components/provider.tsx} +3 -2
- package/src/components/runtime.tsx +9 -0
- package/src/config.ts +1 -0
- package/src/create.ts +1 -1
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-locale-registry.ts +1 -0
- package/src/hooks/use-resolved-lang.ts +1 -0
- package/src/hooks/use-translations.ts +16 -4
- package/src/lang.ts +1 -0
- package/src/lib/code.ts +1 -0
- package/src/lib/content.ts +14 -11
- package/src/lib/coverage.ts +1 -0
- package/src/lib/flatten.ts +1 -0
- package/src/lib/key-index.ts +21 -0
- package/src/lib/locales.ts +14 -0
- package/src/lib/registry.server.ts +4 -3
- package/src/lib/slug.ts +1 -0
- package/src/lib/tree.ts +3 -2
- package/src/lib/url.ts +1 -0
- package/src/server.ts +1 -0
- package/src/types/content.ts +2 -1
- package/src/types/locale.ts +1 -0
- package/src/types/tree.ts +1 -0
- package/src/runtime.tsx +0 -247
package/package.json
CHANGED
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrafirmagreg-team/translate",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"description": "Localization runtime and catalogs",
|
|
6
|
+
"license": "LGPL-3.0-only",
|
|
5
7
|
"exports": {
|
|
6
|
-
".": "./src/runtime.tsx",
|
|
7
|
-
"./provider": "./src/provider.tsx",
|
|
8
|
+
".": "./src/components/runtime.tsx",
|
|
9
|
+
"./provider": "./src/components/provider.tsx",
|
|
8
10
|
"./lang": "./src/lang.ts",
|
|
9
11
|
"./create": "./src/create.ts",
|
|
10
12
|
"./config": "./src/config.ts",
|
|
11
13
|
"./server": "./src/server.ts",
|
|
12
14
|
"./hooks": "./src/hooks/index.ts",
|
|
13
|
-
"./hooks/*":
|
|
14
|
-
|
|
15
|
+
"./hooks/*": [
|
|
16
|
+
"./src/hooks/*.ts",
|
|
17
|
+
"./src/hooks/*/index.ts"
|
|
18
|
+
],
|
|
19
|
+
"./lib/*": [
|
|
20
|
+
"./src/lib/*.ts",
|
|
21
|
+
"./src/lib/*/index.ts"
|
|
22
|
+
],
|
|
15
23
|
"./types/*": "./src/types/*.ts",
|
|
16
|
-
"./
|
|
17
|
-
"./runtime": "./src/runtime.tsx"
|
|
24
|
+
"./runtime": "./src/components/runtime.tsx"
|
|
18
25
|
},
|
|
19
26
|
"files": [
|
|
20
|
-
"src"
|
|
21
|
-
"styles",
|
|
22
|
-
"styles.css"
|
|
27
|
+
"src"
|
|
23
28
|
],
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@fuma-translate/react": "1.0.2"
|
|
31
|
+
},
|
|
24
32
|
"peerDependencies": {
|
|
25
33
|
"react": ">=19"
|
|
26
34
|
},
|
|
@@ -36,6 +44,5 @@
|
|
|
36
44
|
},
|
|
37
45
|
"publishConfig": {
|
|
38
46
|
"access": "public"
|
|
39
|
-
}
|
|
40
|
-
"scripts": {}
|
|
47
|
+
}
|
|
41
48
|
}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
type ReactNode,
|
|
8
8
|
} from 'react';
|
|
9
9
|
import { TranslationProvider } from './runtime';
|
|
10
|
-
import { LangScopeContext } from '
|
|
11
|
-
import type { LangProviderProps, LocaleItem } from '
|
|
10
|
+
import { LangScopeContext } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
|
|
11
|
+
import type { LangProviderProps, LocaleItem } from '@terrafirmagreg-team/translate/lang';
|
|
12
12
|
|
|
13
13
|
export interface LangContextValue {
|
|
14
14
|
locale?: string;
|
|
@@ -63,3 +63,4 @@ export function LangLocalesScope({
|
|
|
63
63
|
);
|
|
64
64
|
return <LangContext value={value}>{children}</LangContext>;
|
|
65
65
|
}
|
|
66
|
+
|
package/src/config.ts
CHANGED
package/src/create.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type {
|
|
|
6
6
|
import type { LangConfig, LangProviderProps, LocaleItem } from './lang';
|
|
7
7
|
import { nativeLanguageName } from './lib/code';
|
|
8
8
|
|
|
9
|
-
|
|
10
9
|
export function localeItems(codes: readonly string[]): LocaleItem[] {
|
|
11
10
|
return codes.map((code) => ({
|
|
12
11
|
locale: code,
|
|
@@ -62,3 +61,4 @@ export function createLocaleSet(registry: LocaleRegistry) {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
export type LocaleSet = ReturnType<typeof createLocaleSet>;
|
|
64
|
+
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import { useMemo } from 'react';
|
|
2
3
|
import { useTranslations as useFumaTranslations } from '@terrafirmagreg-team/translate/runtime';
|
|
3
4
|
|
|
4
5
|
export function useTranslations<K extends string = string>(options?: {
|
|
5
6
|
note?: string;
|
|
6
7
|
}) {
|
|
7
8
|
const base = useFumaTranslations(options);
|
|
8
|
-
return
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
return useMemo(
|
|
10
|
+
() =>
|
|
11
|
+
((key: K, opts?: {
|
|
12
|
+
note?: string;
|
|
13
|
+
variables?: Record<string, string>;
|
|
14
|
+
}) => {
|
|
15
|
+
const value = base(key, opts);
|
|
16
|
+
return opts?.note && value === key ? base(key) : value;
|
|
17
|
+
}) as (key: K, opts?: {
|
|
18
|
+
note?: string;
|
|
19
|
+
variables?: Record<string, string>;
|
|
20
|
+
}) => string,
|
|
21
|
+
[base],
|
|
22
|
+
);
|
|
12
23
|
}
|
|
24
|
+
|
package/src/lang.ts
CHANGED
package/src/lib/code.ts
CHANGED
package/src/lib/content.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
2
|
import type { ReactNode } from 'react';
|
|
3
3
|
import { loadLocaleRegistry } from './registry.server';
|
|
4
|
-
import type { Lang } from '
|
|
5
|
-
import type { LangContent } from '
|
|
4
|
+
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
5
|
+
import type { LangContent } from '@terrafirmagreg-team/translate/types/content';
|
|
6
6
|
|
|
7
7
|
type Resolve<P> = (
|
|
8
8
|
slug: string[] | undefined,
|
|
@@ -17,19 +17,21 @@ export function langContent<P>(
|
|
|
17
17
|
render: (page: P, lang: Lang) => ReactNode,
|
|
18
18
|
): LangContent | null {
|
|
19
19
|
const { messages, defaultLanguage } = loadLocaleRegistry();
|
|
20
|
-
const
|
|
20
|
+
const primary = resolve(slug, defaultLanguage);
|
|
21
|
+
if (primary && !primary.isFallback) {
|
|
22
|
+
return {
|
|
23
|
+
lang: defaultLanguage,
|
|
24
|
+
node: render(primary.page, defaultLanguage),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
21
27
|
for (const locale of Object.keys(messages)) {
|
|
22
28
|
const resolved = resolve(slug, locale as Lang);
|
|
23
29
|
if (!resolved || resolved.isFallback) continue;
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
if (!Object.keys(content).length) {
|
|
27
|
-
const resolved = resolve(slug, defaultLanguage);
|
|
28
|
-
if (resolved) {
|
|
29
|
-
content[defaultLanguage] = { node: render(resolved.page, defaultLanguage) };
|
|
30
|
-
}
|
|
30
|
+
return { lang: locale as Lang, node: render(resolved.page, locale as Lang) };
|
|
31
31
|
}
|
|
32
|
-
return
|
|
32
|
+
return primary
|
|
33
|
+
? { lang: defaultLanguage, node: render(primary.page, defaultLanguage) }
|
|
34
|
+
: null;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export function firstLocalizedPage<P>(
|
|
@@ -48,3 +50,4 @@ export function firstLocalizedPage<P>(
|
|
|
48
50
|
}
|
|
49
51
|
return undefined;
|
|
50
52
|
}
|
|
53
|
+
|
package/src/lib/coverage.ts
CHANGED
package/src/lib/flatten.ts
CHANGED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
const NOTE_KEY = /^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+$/;
|
|
2
|
+
|
|
3
|
+
export function buildKeyIndex(
|
|
4
|
+
messages: Partial<Record<string, string>>,
|
|
5
|
+
): Map<string, string> {
|
|
6
|
+
const index = new Map<string, string>();
|
|
7
|
+
for (const key of Object.keys(messages)) {
|
|
8
|
+
const end = key.lastIndexOf('(');
|
|
9
|
+
if (!key.endsWith(')') || end < 0) continue;
|
|
10
|
+
const note = key.slice(end + 1, -1);
|
|
11
|
+
if (NOTE_KEY.test(note)) index.set(note, key);
|
|
12
|
+
}
|
|
13
|
+
return index;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function stripKeyNote(full: string, key: string): string {
|
|
17
|
+
return full !== key && full.endsWith(`(${key})`)
|
|
18
|
+
? full.slice(0, -(key.length + 2))
|
|
19
|
+
: full;
|
|
20
|
+
}
|
|
21
|
+
|
package/src/lib/locales.ts
CHANGED
|
@@ -8,3 +8,17 @@ export function createLocaleCatalog(
|
|
|
8
8
|
]),
|
|
9
9
|
);
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
export function createLocaleCatalogs(
|
|
13
|
+
modules: Record<string, unknown>,
|
|
14
|
+
): Record<string, Record<string, unknown>> {
|
|
15
|
+
const catalogs: Record<string, Record<string, unknown>> = {};
|
|
16
|
+
for (const [path, messages] of Object.entries(modules)) {
|
|
17
|
+
const dir = path.slice(0, path.lastIndexOf('/'));
|
|
18
|
+
const catalog = dir.slice(dir.lastIndexOf('/') + 1);
|
|
19
|
+
const lang = path.slice(path.lastIndexOf('/') + 1, -'.json'.length);
|
|
20
|
+
(catalogs[catalog] ??= {})[lang] = messages;
|
|
21
|
+
}
|
|
22
|
+
return catalogs;
|
|
23
|
+
}
|
|
24
|
+
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import 'server-only';
|
|
2
|
-
import { createLocaleSet, type LocaleSet } from '
|
|
3
|
-
import { translateConfig } from '
|
|
2
|
+
import { createLocaleSet, type LocaleSet } from '@terrafirmagreg-team/translate/create';
|
|
3
|
+
import { translateConfig } from '@terrafirmagreg-team/translate/config';
|
|
4
4
|
import { flattenMessages } from './flatten';
|
|
5
5
|
import { sortLangCodes } from './code';
|
|
6
|
-
import type { LocaleMessages, LocaleRegistry } from '
|
|
6
|
+
import type { LocaleMessages, LocaleRegistry } from '@terrafirmagreg-team/translate/types/locale';
|
|
7
7
|
|
|
8
8
|
export { sortLangCodes };
|
|
9
9
|
|
|
@@ -56,3 +56,4 @@ export function loadLocaleSet(): LocaleSet {
|
|
|
56
56
|
cachedSet ??= createLocaleSet(loadLocaleRegistry());
|
|
57
57
|
return cachedSet;
|
|
58
58
|
}
|
|
59
|
+
|
package/src/lib/slug.ts
CHANGED
package/src/lib/tree.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cloneElement, isValidElement } from 'react';
|
|
2
|
-
import type * as PageTree from '
|
|
3
|
-
import type { Lang } from '
|
|
2
|
+
import type * as PageTree from '@terrafirmagreg-team/translate/types/tree';
|
|
3
|
+
import type { Lang } from '@terrafirmagreg-team/translate/types/locale';
|
|
4
4
|
import { isLang } from './code';
|
|
5
5
|
import { loadLocaleRegistry } from './registry.server';
|
|
6
6
|
import { stripLangFromUrl } from './slug';
|
|
@@ -246,3 +246,4 @@ export function localizePageTree(
|
|
|
246
246
|
children: merged.map(keyTreeNode),
|
|
247
247
|
};
|
|
248
248
|
}
|
|
249
|
+
|
package/src/lib/url.ts
CHANGED
package/src/server.ts
CHANGED
package/src/types/content.ts
CHANGED
package/src/types/locale.ts
CHANGED
package/src/types/tree.ts
CHANGED
package/src/runtime.tsx
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import {
|
|
3
|
-
cloneElement,
|
|
4
|
-
createContext,
|
|
5
|
-
isValidElement,
|
|
6
|
-
ReactElement,
|
|
7
|
-
use,
|
|
8
|
-
useMemo,
|
|
9
|
-
type ReactNode,
|
|
10
|
-
} from "react";
|
|
11
|
-
|
|
12
|
-
const Context = createContext<Partial<Record<string, string>>>({});
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export function TranslationProvider({
|
|
16
|
-
translations,
|
|
17
|
-
children,
|
|
18
|
-
}: {
|
|
19
|
-
translations: Partial<Record<string, string>>;
|
|
20
|
-
children: ReactNode;
|
|
21
|
-
}) {
|
|
22
|
-
const parent = use(Context);
|
|
23
|
-
return (
|
|
24
|
-
<Context value={useMemo(() => ({ ...parent, ...translations }), [parent, translations])}>
|
|
25
|
-
{children}
|
|
26
|
-
</Context>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type GetVariables<T extends string> = T extends `${infer Before}{${infer K}}${infer After}`
|
|
31
|
-
? (Before extends `${string}\\` ? never : K) | GetVariables<After>
|
|
32
|
-
: never;
|
|
33
|
-
|
|
34
|
-
type GetTags<T extends string> = T extends `${infer Before}<${infer K}>${infer After}`
|
|
35
|
-
?
|
|
36
|
-
| (Before extends `${string}\\`
|
|
37
|
-
? never
|
|
38
|
-
: K extends `/${string}`
|
|
39
|
-
? never
|
|
40
|
-
: K extends `${infer Tag}/`
|
|
41
|
-
? Tag
|
|
42
|
-
: K)
|
|
43
|
-
| GetTags<After>
|
|
44
|
-
: never;
|
|
45
|
-
|
|
46
|
-
interface HookOptions {
|
|
47
|
-
|
|
48
|
-
note?: string;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export interface TranslationsHook {
|
|
52
|
-
translations: Partial<Record<string, string>>;
|
|
53
|
-
|
|
54
|
-
<Text extends string>(
|
|
55
|
-
text: Text,
|
|
56
|
-
opts?: {
|
|
57
|
-
|
|
58
|
-
note?: string;
|
|
59
|
-
variables?: Record<GetVariables<Text>, string>;
|
|
60
|
-
},
|
|
61
|
-
): string;
|
|
62
|
-
|
|
63
|
-
jsx<Text extends string>(
|
|
64
|
-
text: Text,
|
|
65
|
-
opts?: {
|
|
66
|
-
|
|
67
|
-
note?: string;
|
|
68
|
-
variables?: Record<GetVariables<Text>, ReactNode>;
|
|
69
|
-
tags?: Record<GetTags<Text>, TagRenderer>;
|
|
70
|
-
},
|
|
71
|
-
): ReactNode;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const REGEX_VAR = /\\?\{([^}]+)\}/g;
|
|
75
|
-
|
|
76
|
-
export function useTranslations({ note }: HookOptions = {}): TranslationsHook {
|
|
77
|
-
const translations = use(Context);
|
|
78
|
-
return useMemo(() => fromTranslations(translations, { note }), [translations, note]);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
export function fromTranslations(
|
|
83
|
-
translations: Partial<Record<string, string>>,
|
|
84
|
-
{ note: hookNote }: HookOptions = {},
|
|
85
|
-
): TranslationsHook {
|
|
86
|
-
const res: TranslationsHook = (rawText, opts = {}) => {
|
|
87
|
-
const { note, variables } = opts;
|
|
88
|
-
const notes: string[] = [];
|
|
89
|
-
if (hookNote) notes.push(hookNote);
|
|
90
|
-
if (note) notes.push(note);
|
|
91
|
-
const k = encodeKey(rawText, notes);
|
|
92
|
-
let text = translations[k] ?? rawText;
|
|
93
|
-
|
|
94
|
-
if (variables) {
|
|
95
|
-
text = text.replaceAll(REGEX_VAR, (m, name: string) => {
|
|
96
|
-
if (m[0] === "\\") return m.slice(1);
|
|
97
|
-
if (name in variables) return variables[name as never];
|
|
98
|
-
return m;
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return text;
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
res.jsx = (rawText, opts = {}) => {
|
|
106
|
-
const { note, variables, tags } = opts;
|
|
107
|
-
const notes: string[] = [];
|
|
108
|
-
if (hookNote) notes.push(hookNote);
|
|
109
|
-
if (note) notes.push(note);
|
|
110
|
-
const k = encodeKey(rawText, notes);
|
|
111
|
-
const text = translations[k] ?? rawText;
|
|
112
|
-
if (tags) {
|
|
113
|
-
return onJsx(text, variables, tags);
|
|
114
|
-
}
|
|
115
|
-
if (variables) {
|
|
116
|
-
return onJsxVariables(text, variables);
|
|
117
|
-
}
|
|
118
|
-
return text;
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
res.translations = translations;
|
|
122
|
-
return res;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function encodeKey(text: string, notes: string[]): string {
|
|
126
|
-
return text + notes.map((n) => `(${n})`).join("");
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function onJsxVariables(text: string, variables: Record<string, ReactNode>): ReactNode {
|
|
130
|
-
let idx = 0;
|
|
131
|
-
const out: ReactNode[] = [];
|
|
132
|
-
for (const match of text.matchAll(REGEX_VAR)) {
|
|
133
|
-
const [s, name] = match as unknown as [string, string];
|
|
134
|
-
if (idx < match.index) {
|
|
135
|
-
out.push(text.slice(idx, match.index));
|
|
136
|
-
}
|
|
137
|
-
idx = match.index + s.length;
|
|
138
|
-
|
|
139
|
-
if (s[0] === "\\") {
|
|
140
|
-
out.push(s.slice(1));
|
|
141
|
-
} else if (name in variables) {
|
|
142
|
-
out.push(enforceElementKey(variables[name], idx));
|
|
143
|
-
} else {
|
|
144
|
-
out.push(s);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
out.push(text.slice(idx));
|
|
148
|
-
return out;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
const REGEX_TAG = /\\?<([^>]+)>/g;
|
|
152
|
-
|
|
153
|
-
function onJsx(
|
|
154
|
-
text: string,
|
|
155
|
-
variables: Record<string, ReactNode> | undefined,
|
|
156
|
-
tags: Record<string, TagRenderer>,
|
|
157
|
-
) {
|
|
158
|
-
const stack: { children: ReactNode[]; tag?: string }[] = [{ children: [] }];
|
|
159
|
-
let idx = 0;
|
|
160
|
-
const closeTag = () => {
|
|
161
|
-
const current = stack.pop()!;
|
|
162
|
-
|
|
163
|
-
stack[stack.length - 1]!.children.push(
|
|
164
|
-
enforceElementKey(
|
|
165
|
-
current.tag && tags[current.tag]
|
|
166
|
-
? renderTag(tags[current.tag]!, current.children)
|
|
167
|
-
: current.children,
|
|
168
|
-
idx,
|
|
169
|
-
),
|
|
170
|
-
);
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
for (const match of text.matchAll(REGEX_TAG)) {
|
|
174
|
-
const current = stack[stack.length - 1]!;
|
|
175
|
-
const [s, content] = match as unknown as [string, string];
|
|
176
|
-
|
|
177
|
-
if (idx < match.index) {
|
|
178
|
-
const str = text.slice(idx, match.index);
|
|
179
|
-
current.children.push(variables ? onJsxVariables(str, variables) : str);
|
|
180
|
-
}
|
|
181
|
-
idx = match.index + s.length;
|
|
182
|
-
|
|
183
|
-
if (s[0] === "\\") {
|
|
184
|
-
const str = s.slice(1);
|
|
185
|
-
current.children.push(variables ? onJsxVariables(str, variables) : str);
|
|
186
|
-
} else if (content[0] === "/") {
|
|
187
|
-
const name = content.slice(1);
|
|
188
|
-
|
|
189
|
-
if (current.tag !== name) {
|
|
190
|
-
current.children.push(variables ? onJsxVariables(s, variables) : s);
|
|
191
|
-
} else {
|
|
192
|
-
closeTag();
|
|
193
|
-
}
|
|
194
|
-
} else if (content.at(-1) === "/") {
|
|
195
|
-
const name = content.slice(0, -1);
|
|
196
|
-
|
|
197
|
-
if (tags[name]) {
|
|
198
|
-
current.children.push(enforceElementKey(renderTag(tags[name]), idx));
|
|
199
|
-
}
|
|
200
|
-
} else {
|
|
201
|
-
stack.push({ children: [], tag: content });
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (idx < text.length) {
|
|
206
|
-
const str = text.slice(idx);
|
|
207
|
-
stack[stack.length - 1]!.children.push(variables ? onJsxVariables(str, variables) : str);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
while (stack.length > 1) closeTag();
|
|
211
|
-
return stack[0]!.children;
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
interface TProps<Text extends string> {
|
|
215
|
-
text: Text;
|
|
216
|
-
|
|
217
|
-
note?: string;
|
|
218
|
-
variables?: Record<GetVariables<Text>, ReactNode>;
|
|
219
|
-
tags?: Record<GetTags<Text>, TagRenderer>;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export function T<const Text extends string>({
|
|
223
|
-
text,
|
|
224
|
-
note,
|
|
225
|
-
variables,
|
|
226
|
-
tags,
|
|
227
|
-
}: TProps<Text>): ReactNode {
|
|
228
|
-
const translations = use(Context)!;
|
|
229
|
-
return useMemo(
|
|
230
|
-
() => fromTranslations(translations).jsx(text, { note, variables, tags }),
|
|
231
|
-
[translations, text, note, variables, tags],
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
type TagRenderer = ReactElement | ((children: ReactNode) => ReactNode);
|
|
237
|
-
|
|
238
|
-
function renderTag(renderer: TagRenderer, child?: ReactNode) {
|
|
239
|
-
if (isValidElement(renderer))
|
|
240
|
-
return child === undefined ? renderer : cloneElement(renderer, undefined, child);
|
|
241
|
-
return renderer(child);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function enforceElementKey(value: ReactNode, key: number | string): ReactNode {
|
|
245
|
-
if (isValidElement(value)) return cloneElement(value, { key });
|
|
246
|
-
return value;
|
|
247
|
-
}
|