@octanejs/i18next 0.1.0
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/LICENSE +22 -0
- package/README.md +151 -0
- package/package.json +73 -0
- package/src/I18nextProvider.js +9 -0
- package/src/IcuTrans.js +104 -0
- package/src/IcuTransUtils/TranslationParserError.js +24 -0
- package/src/IcuTransUtils/htmlEntityDecoder.js +264 -0
- package/src/IcuTransUtils/index.js +4 -0
- package/src/IcuTransUtils/renderTranslation.js +216 -0
- package/src/IcuTransUtils/tokenizer.js +78 -0
- package/src/IcuTransWithoutContext.js +147 -0
- package/src/Trans.js +46 -0
- package/src/TransWithoutContext.d.ts +160 -0
- package/src/TransWithoutContext.js +687 -0
- package/src/Translation.js +15 -0
- package/src/context.js +61 -0
- package/src/defaults.js +21 -0
- package/src/helpers.d.ts +3 -0
- package/src/i18nInstance.js +7 -0
- package/src/index.d.ts +315 -0
- package/src/index.js +26 -0
- package/src/initReactI18next.d.ts +3 -0
- package/src/initReactI18next.js +11 -0
- package/src/internal.js +42 -0
- package/src/unescape.js +31 -0
- package/src/useSSR.js +55 -0
- package/src/useTranslation.js +285 -0
- package/src/utils.js +93 -0
- package/src/withSSR.js +22 -0
- package/src/withTranslation.js +38 -0
package/src/context.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Ported from react-i18next@17.0.9 (8b4a9ea): React context -> octane context.
|
|
2
|
+
import { createContext } from 'octane';
|
|
3
|
+
import { getDefaults, setDefaults } from './defaults.js';
|
|
4
|
+
import { getI18n, setI18n } from './i18nInstance.js';
|
|
5
|
+
import { initReactI18next } from './initReactI18next.js';
|
|
6
|
+
|
|
7
|
+
export { getDefaults, setDefaults, getI18n, setI18n, initReactI18next };
|
|
8
|
+
|
|
9
|
+
export const I18nContext = createContext();
|
|
10
|
+
|
|
11
|
+
export class ReportNamespaces {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.usedNamespaces = {};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
addUsedNamespaces(namespaces) {
|
|
17
|
+
namespaces.forEach((ns) => {
|
|
18
|
+
if (!this.usedNamespaces[ns]) this.usedNamespaces[ns] = true;
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getUsedNamespaces() {
|
|
23
|
+
return Object.keys(this.usedNamespaces);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const composeInitialProps = (ForComponent) => async (ctx) => {
|
|
28
|
+
const componentsInitialProps = (await ForComponent.getInitialProps?.(ctx)) ?? {};
|
|
29
|
+
|
|
30
|
+
const i18nInitialProps = getInitialProps();
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
...componentsInitialProps,
|
|
34
|
+
...i18nInitialProps,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const getInitialProps = () => {
|
|
39
|
+
const i18n = getI18n();
|
|
40
|
+
if (!i18n) {
|
|
41
|
+
console.warn(
|
|
42
|
+
'react-i18next:: getInitialProps: You will need to pass in an i18next instance by using initReactI18next',
|
|
43
|
+
);
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
const namespaces = i18n.reportNamespaces?.getUsedNamespaces() ?? [];
|
|
47
|
+
|
|
48
|
+
const ret = {};
|
|
49
|
+
const initialI18nStore = {};
|
|
50
|
+
i18n.languages.forEach((l) => {
|
|
51
|
+
initialI18nStore[l] = {};
|
|
52
|
+
namespaces.forEach((ns) => {
|
|
53
|
+
initialI18nStore[l][ns] = i18n.getResourceBundle(l, ns) || {};
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
ret.initialI18nStore = initialI18nStore;
|
|
58
|
+
ret.initialLanguage = i18n.language;
|
|
59
|
+
|
|
60
|
+
return ret;
|
|
61
|
+
};
|
package/src/defaults.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { unescape } from './unescape.js';
|
|
2
|
+
|
|
3
|
+
let defaultOptions = {
|
|
4
|
+
bindI18n: 'languageChanged',
|
|
5
|
+
bindI18nStore: '',
|
|
6
|
+
// nsMode: 'fallback' // loop through all namespaces given to hook, HOC, render prop for key lookup
|
|
7
|
+
transEmptyNodeValue: '',
|
|
8
|
+
transSupportBasicHtmlNodes: true,
|
|
9
|
+
transWrapTextNodes: '',
|
|
10
|
+
transKeepBasicHtmlNodesFor: ['br', 'strong', 'i', 'p'],
|
|
11
|
+
// hashTransKey: key => key // calculate a key for Trans component based on defaultValue
|
|
12
|
+
useSuspense: true,
|
|
13
|
+
unescape,
|
|
14
|
+
transDefaultProps: undefined, // { tOptions: {}, shouldUnescape: false, values: {}, components: [] }
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const setDefaults = (options = {}) => {
|
|
18
|
+
defaultOptions = { ...defaultOptions, ...options };
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export const getDefaults = () => defaultOptions;
|
package/src/helpers.d.ts
ADDED
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import type { $Tuple } from './helpers.js';
|
|
2
|
+
import type {
|
|
3
|
+
ReactOptions,
|
|
4
|
+
i18n,
|
|
5
|
+
Resource,
|
|
6
|
+
FlatNamespace,
|
|
7
|
+
Namespace,
|
|
8
|
+
TypeOptions,
|
|
9
|
+
TFunction,
|
|
10
|
+
KeyPrefix,
|
|
11
|
+
KeyPrefixSelector,
|
|
12
|
+
NsResource,
|
|
13
|
+
ParseKeys,
|
|
14
|
+
TOptions,
|
|
15
|
+
} from 'i18next';
|
|
16
|
+
import type { ComponentBody, Context, ElementDescriptor } from 'octane';
|
|
17
|
+
import {
|
|
18
|
+
Trans,
|
|
19
|
+
TransProps,
|
|
20
|
+
TransSelectorProps,
|
|
21
|
+
ErrorCode,
|
|
22
|
+
ErrorArgs,
|
|
23
|
+
} from './TransWithoutContext.js';
|
|
24
|
+
export { initReactI18next } from './initReactI18next.js';
|
|
25
|
+
|
|
26
|
+
export type OctaneNode = unknown;
|
|
27
|
+
export type OctaneElement<P = any> = ElementDescriptor<P>;
|
|
28
|
+
export type OctaneComponentType<P = any> = ComponentBody<P>;
|
|
29
|
+
type ComponentProps<C> = C extends ComponentBody<infer P> ? P : never;
|
|
30
|
+
|
|
31
|
+
export function nodesToString(
|
|
32
|
+
children: OctaneNode,
|
|
33
|
+
i18nOptions?: ReactOptions,
|
|
34
|
+
i18n?: i18n,
|
|
35
|
+
i18nKey?: string,
|
|
36
|
+
): string;
|
|
37
|
+
|
|
38
|
+
export const TransWithoutContext: typeof Trans;
|
|
39
|
+
export { Trans, TransProps, TransSelectorProps, ErrorArgs, ErrorCode };
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Content declaration for IcuTrans component
|
|
43
|
+
* Describes React components as type + props blueprints that will be rendered
|
|
44
|
+
*/
|
|
45
|
+
export interface IcuTransContentDeclaration {
|
|
46
|
+
type: string | OctaneComponentType<any>;
|
|
47
|
+
props?: {
|
|
48
|
+
children?: IcuTransContentDeclaration | IcuTransContentDeclaration[];
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Props for IcuTransWithoutContext component (no React context)
|
|
55
|
+
*/
|
|
56
|
+
export type IcuTransWithoutContextProps<
|
|
57
|
+
Key extends ParseKeys<Ns, TOpt, KPrefix>,
|
|
58
|
+
Ns extends Namespace = _DefaultNamespace,
|
|
59
|
+
KPrefix = undefined,
|
|
60
|
+
TContext extends string | undefined = undefined,
|
|
61
|
+
TOpt extends TOptions & { context?: TContext } = { context: TContext },
|
|
62
|
+
> = {
|
|
63
|
+
/** The i18n key to look up the translation */
|
|
64
|
+
i18nKey: Key;
|
|
65
|
+
/** The default translation in ICU format with numbered tags (e.g., "<0>Click here</0>") */
|
|
66
|
+
defaultTranslation: string;
|
|
67
|
+
/** Declaration tree describing React components and their props */
|
|
68
|
+
content: IcuTransContentDeclaration[];
|
|
69
|
+
/** Optional namespace(s) for the translation */
|
|
70
|
+
// see TransProps.ns: keep single-namespace values assignable when `t` fixes Ns to an array
|
|
71
|
+
ns?: Ns | (Ns extends readonly (infer S extends string)[] ? S : never);
|
|
72
|
+
/** Optional values for ICU variable interpolation */
|
|
73
|
+
values?: Record<string, any>;
|
|
74
|
+
/** i18next instance. If not provided, uses global instance */
|
|
75
|
+
i18n?: i18n;
|
|
76
|
+
/** Custom translation function */
|
|
77
|
+
t?: TFunction<Ns, KPrefix>;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Props for IcuTrans component (with React context support)
|
|
82
|
+
*/
|
|
83
|
+
export type IcuTransProps<
|
|
84
|
+
Key extends ParseKeys<Ns, TOpt, KPrefix>,
|
|
85
|
+
Ns extends Namespace = _DefaultNamespace,
|
|
86
|
+
KPrefix = undefined,
|
|
87
|
+
TContext extends string | undefined = undefined,
|
|
88
|
+
TOpt extends TOptions & { context?: TContext } = { context: TContext },
|
|
89
|
+
> = IcuTransWithoutContextProps<Key, Ns, KPrefix, TContext, TOpt>;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* IcuTrans component for rendering ICU MessageFormat translations
|
|
93
|
+
* This is the context-aware version that works with I18nextProvider
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```tsx
|
|
97
|
+
* // Type-safe usage with namespace
|
|
98
|
+
* <IcuTrans<'welcome.message', 'common'>
|
|
99
|
+
* i18nKey="welcome.message"
|
|
100
|
+
* defaultTranslation="Welcome <0>friend</0>!"
|
|
101
|
+
* content={[{ type: 'strong', props: {} }]}
|
|
102
|
+
* />
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export interface IcuTransComponent {
|
|
106
|
+
<
|
|
107
|
+
Key extends ParseKeys<Ns, TOpt, KPrefix>,
|
|
108
|
+
Ns extends Namespace = _DefaultNamespace,
|
|
109
|
+
KPrefix = undefined,
|
|
110
|
+
TContext extends string | undefined = undefined,
|
|
111
|
+
TOpt extends TOptions & { context?: TContext } = { context: TContext },
|
|
112
|
+
>(
|
|
113
|
+
props: IcuTransProps<Key, Ns, KPrefix, TContext, TOpt>,
|
|
114
|
+
): OctaneNode;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* IcuTransWithoutContext component for rendering ICU MessageFormat translations
|
|
119
|
+
* This version does not use React context and requires explicit i18n instance
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```tsx
|
|
123
|
+
* // Type-safe usage with namespace
|
|
124
|
+
* <IcuTransWithoutContext<'welcome.message', 'common'>
|
|
125
|
+
* i18nKey="welcome.message"
|
|
126
|
+
* defaultTranslation="Welcome <0>back</0>!"
|
|
127
|
+
* content={[{ type: 'strong', props: {} }]}
|
|
128
|
+
* i18n={i18nInstance}
|
|
129
|
+
* />
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export interface IcuTransWithoutContextComponent {
|
|
133
|
+
<
|
|
134
|
+
Key extends ParseKeys<Ns, TOpt, KPrefix>,
|
|
135
|
+
Ns extends Namespace = _DefaultNamespace,
|
|
136
|
+
KPrefix = undefined,
|
|
137
|
+
TContext extends string | undefined = undefined,
|
|
138
|
+
TOpt extends TOptions & { context?: TContext } = { context: TContext },
|
|
139
|
+
>(
|
|
140
|
+
props: IcuTransWithoutContextProps<Key, Ns, KPrefix, TContext, TOpt>,
|
|
141
|
+
): OctaneNode;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const IcuTrans: IcuTransComponent;
|
|
145
|
+
export const IcuTransWithoutContext: IcuTransWithoutContextComponent;
|
|
146
|
+
|
|
147
|
+
export function setDefaults(options: ReactOptions): void;
|
|
148
|
+
export function getDefaults(): ReactOptions;
|
|
149
|
+
export function setI18n(instance: i18n): void;
|
|
150
|
+
export function getI18n(): i18n;
|
|
151
|
+
export function composeInitialProps(ForComponent: any): (ctx: unknown) => Promise<any>;
|
|
152
|
+
export function getInitialProps(): {
|
|
153
|
+
initialI18nStore: {
|
|
154
|
+
[ns: string]: {};
|
|
155
|
+
};
|
|
156
|
+
initialLanguage: string;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export interface ReportNamespaces {
|
|
160
|
+
addUsedNamespaces(namespaces: Namespace): void;
|
|
161
|
+
getUsedNamespaces(): string[];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
declare module 'i18next' {
|
|
165
|
+
// interface i18n {
|
|
166
|
+
// reportNamespaces?: ReportNamespaces;
|
|
167
|
+
// }
|
|
168
|
+
interface CustomInstanceExtensions {
|
|
169
|
+
reportNamespaces?: ReportNamespaces;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
type _DefaultNamespace = TypeOptions['defaultNS'];
|
|
174
|
+
|
|
175
|
+
export function useSSR(initialI18nStore: Resource, initialLanguage: string): void;
|
|
176
|
+
|
|
177
|
+
// If the version is earlier than i18next v25.4.0, enableSelector does not exist in TypeOptions, so a conditional type is used to maintain type compatibility.
|
|
178
|
+
type _EnableSelector = TypeOptions extends { enableSelector: infer U } ? U : false;
|
|
179
|
+
|
|
180
|
+
export type UseTranslationOptions<KPrefix> = {
|
|
181
|
+
i18n?: i18n;
|
|
182
|
+
useSuspense?: boolean;
|
|
183
|
+
keyPrefix?: KPrefix;
|
|
184
|
+
bindI18n?: string | false;
|
|
185
|
+
nsMode?: 'fallback' | 'default';
|
|
186
|
+
lng?: string;
|
|
187
|
+
// other of these options might also work: https://github.com/i18next/i18next/blob/master/index.d.ts#L127
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
export type UseTranslationResponse<Ns extends Namespace, KPrefix> = [
|
|
191
|
+
t: TFunction<Ns, KPrefix>,
|
|
192
|
+
i18n: i18n,
|
|
193
|
+
ready: boolean,
|
|
194
|
+
] & {
|
|
195
|
+
t: TFunction<Ns, KPrefix>;
|
|
196
|
+
i18n: i18n;
|
|
197
|
+
ready: boolean;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Workaround to make code completion to work when suggesting namespaces.
|
|
201
|
+
// This is a typescript limitation when using generics with default values,
|
|
202
|
+
// it'll be addressed in this issue: https://github.com/microsoft/TypeScript/issues/52516
|
|
203
|
+
export type FallbackNs<Ns> = Ns extends undefined
|
|
204
|
+
? _DefaultNamespace
|
|
205
|
+
: Ns extends Namespace
|
|
206
|
+
? Ns
|
|
207
|
+
: _DefaultNamespace;
|
|
208
|
+
|
|
209
|
+
export const useTranslation: _EnableSelector extends true | 'optimize'
|
|
210
|
+
? UseTranslationSelector
|
|
211
|
+
: UseTranslationLegacy;
|
|
212
|
+
|
|
213
|
+
interface UseTranslationLegacy {
|
|
214
|
+
<
|
|
215
|
+
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
216
|
+
const KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
|
|
217
|
+
>(
|
|
218
|
+
ns?: Ns,
|
|
219
|
+
options?: UseTranslationOptions<KPrefix>,
|
|
220
|
+
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
interface UseTranslationSelector {
|
|
224
|
+
// Overload: selector function as keyPrefix — provides type-safe key narrowing
|
|
225
|
+
<
|
|
226
|
+
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined,
|
|
227
|
+
const KPrefix extends KeyPrefixSelector<FallbackNs<Ns>>,
|
|
228
|
+
>(
|
|
229
|
+
ns: Ns,
|
|
230
|
+
options: UseTranslationOptions<KPrefix> & { keyPrefix: KPrefix },
|
|
231
|
+
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
|
|
232
|
+
|
|
233
|
+
// Overload: string or undefined keyPrefix (original behavior)
|
|
234
|
+
<
|
|
235
|
+
const Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
236
|
+
const KPrefix = undefined,
|
|
237
|
+
>(
|
|
238
|
+
ns?: Ns,
|
|
239
|
+
options?: UseTranslationOptions<KPrefix>,
|
|
240
|
+
): UseTranslationResponse<FallbackNs<Ns>, KPrefix>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Need to see usage to improve this
|
|
244
|
+
export function withSSR(): <Props>(
|
|
245
|
+
WrappedComponent: OctaneComponentType<Props>,
|
|
246
|
+
) => OctaneComponentType<
|
|
247
|
+
Props & {
|
|
248
|
+
initialI18nStore: Resource;
|
|
249
|
+
initialLanguage: string;
|
|
250
|
+
}
|
|
251
|
+
> & {
|
|
252
|
+
getInitialProps: (ctx: unknown) => Promise<any>;
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
export interface WithTranslation<
|
|
256
|
+
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
257
|
+
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
|
|
258
|
+
> {
|
|
259
|
+
t: TFunction<FallbackNs<Ns>, KPrefix>;
|
|
260
|
+
i18n: i18n;
|
|
261
|
+
tReady: boolean;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface WithTranslationProps {
|
|
265
|
+
i18n?: i18n;
|
|
266
|
+
useSuspense?: boolean;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function withTranslation<
|
|
270
|
+
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
271
|
+
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
|
|
272
|
+
>(
|
|
273
|
+
ns?: Ns,
|
|
274
|
+
options?: {
|
|
275
|
+
withRef?: boolean;
|
|
276
|
+
keyPrefix?: KPrefix;
|
|
277
|
+
},
|
|
278
|
+
): <C extends OctaneComponentType<any>, ResolvedProps = ComponentProps<C>>(
|
|
279
|
+
component: C,
|
|
280
|
+
) => OctaneComponentType<
|
|
281
|
+
Omit<ResolvedProps, keyof WithTranslation<Ns>> & WithTranslationProps & { ref?: unknown }
|
|
282
|
+
>;
|
|
283
|
+
|
|
284
|
+
export interface I18nextProviderProps {
|
|
285
|
+
children?: OctaneNode;
|
|
286
|
+
i18n: i18n;
|
|
287
|
+
defaultNS?: string | string[];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export const I18nextProvider: OctaneComponentType<I18nextProviderProps>;
|
|
291
|
+
export const I18nContext: Context<{ i18n: i18n; defaultNS?: string | string[] }>;
|
|
292
|
+
|
|
293
|
+
export interface TranslationProps<
|
|
294
|
+
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
295
|
+
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
|
|
296
|
+
> {
|
|
297
|
+
children: (
|
|
298
|
+
t: TFunction<FallbackNs<Ns>, KPrefix>,
|
|
299
|
+
options: {
|
|
300
|
+
i18n: i18n;
|
|
301
|
+
lng: string;
|
|
302
|
+
},
|
|
303
|
+
ready: boolean,
|
|
304
|
+
) => OctaneNode;
|
|
305
|
+
ns?: Ns;
|
|
306
|
+
i18n?: i18n;
|
|
307
|
+
useSuspense?: boolean;
|
|
308
|
+
keyPrefix?: KPrefix;
|
|
309
|
+
nsMode?: 'fallback' | 'default';
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function Translation<
|
|
313
|
+
Ns extends FlatNamespace | $Tuple<FlatNamespace> | undefined = undefined,
|
|
314
|
+
KPrefix extends KeyPrefix<FallbackNs<Ns>> = undefined,
|
|
315
|
+
>(props: TranslationProps<Ns, KPrefix>): any;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export { Trans } from './Trans.js';
|
|
2
|
+
export { Trans as TransWithoutContext } from './TransWithoutContext.js';
|
|
3
|
+
export { IcuTrans } from './IcuTrans.js';
|
|
4
|
+
export { IcuTransWithoutContext } from './IcuTransWithoutContext.js';
|
|
5
|
+
export { useTranslation } from './useTranslation.js';
|
|
6
|
+
export { withTranslation } from './withTranslation.js';
|
|
7
|
+
export { Translation } from './Translation.js';
|
|
8
|
+
export { I18nextProvider } from './I18nextProvider.js';
|
|
9
|
+
export { withSSR } from './withSSR.js';
|
|
10
|
+
export { useSSR } from './useSSR.js';
|
|
11
|
+
export { initReactI18next } from './initReactI18next.js';
|
|
12
|
+
export { setDefaults, getDefaults } from './defaults.js';
|
|
13
|
+
export { setI18n, getI18n } from './i18nInstance.js';
|
|
14
|
+
|
|
15
|
+
export { nodesToString } from './Trans.js';
|
|
16
|
+
|
|
17
|
+
export { I18nContext, composeInitialProps, getInitialProps } from './context.js';
|
|
18
|
+
|
|
19
|
+
// dummy functions for icu.macro support
|
|
20
|
+
|
|
21
|
+
export const date = () => '';
|
|
22
|
+
export const time = () => '';
|
|
23
|
+
export const number = () => '';
|
|
24
|
+
export const select = () => '';
|
|
25
|
+
export const plural = () => '';
|
|
26
|
+
export const selectOrdinal = () => '';
|
package/src/internal.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// Slot mechanics for @octanejs/i18next's published plain-JS hook layer.
|
|
2
|
+
// Compiled callers pass a call-site Symbol as the trailing argument; each
|
|
3
|
+
// composed base hook gets a deterministic sub-slot. Tag-only symbols cover
|
|
4
|
+
// renderHook and other slotless callers while remaining distinct inside the
|
|
5
|
+
// runtime's ambient withSlot path.
|
|
6
|
+
const subSlotCache = new Map();
|
|
7
|
+
const bareTagCache = new Map();
|
|
8
|
+
|
|
9
|
+
export function subSlot(slot, tag) {
|
|
10
|
+
if (slot === undefined) {
|
|
11
|
+
let bare = bareTagCache.get(tag);
|
|
12
|
+
if (bare === undefined) bareTagCache.set(tag, (bare = Symbol.for(`:${tag}`)));
|
|
13
|
+
return bare;
|
|
14
|
+
}
|
|
15
|
+
let byTag = subSlotCache.get(slot);
|
|
16
|
+
if (byTag === undefined) subSlotCache.set(slot, (byTag = new Map()));
|
|
17
|
+
let symbol = byTag.get(tag);
|
|
18
|
+
if (symbol === undefined) {
|
|
19
|
+
symbol = Symbol.for(`${slot.description ?? ''}:${tag}`);
|
|
20
|
+
byTag.set(tag, symbol);
|
|
21
|
+
}
|
|
22
|
+
return symbol;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function splitSlot(args) {
|
|
26
|
+
const tail = args[args.length - 1];
|
|
27
|
+
const slot = typeof tail === 'symbol' ? tail : undefined;
|
|
28
|
+
return [slot === undefined ? args : args.slice(0, -1), slot];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const componentSlots = new Map();
|
|
32
|
+
|
|
33
|
+
// A fixed symbol is safe for a plain-JS component because every component
|
|
34
|
+
// instance owns an independent hook scope.
|
|
35
|
+
export function S(tag) {
|
|
36
|
+
let symbol = componentSlots.get(tag);
|
|
37
|
+
if (symbol === undefined) {
|
|
38
|
+
symbol = Symbol.for(`@octanejs/i18next:${tag}`);
|
|
39
|
+
componentSlots.set(tag, symbol);
|
|
40
|
+
}
|
|
41
|
+
return symbol;
|
|
42
|
+
}
|
package/src/unescape.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// unescape common html entities
|
|
2
|
+
|
|
3
|
+
const matchHtmlEntity =
|
|
4
|
+
/&(?:amp|#38|lt|#60|gt|#62|apos|#39|quot|#34|nbsp|#160|copy|#169|reg|#174|hellip|#8230|#x2F|#47);/g;
|
|
5
|
+
|
|
6
|
+
const htmlEntities = {
|
|
7
|
+
'&': '&',
|
|
8
|
+
'&': '&',
|
|
9
|
+
'<': '<',
|
|
10
|
+
'<': '<',
|
|
11
|
+
'>': '>',
|
|
12
|
+
'>': '>',
|
|
13
|
+
''': "'",
|
|
14
|
+
''': "'",
|
|
15
|
+
'"': '"',
|
|
16
|
+
'"': '"',
|
|
17
|
+
' ': ' ',
|
|
18
|
+
' ': ' ',
|
|
19
|
+
'©': '©',
|
|
20
|
+
'©': '©',
|
|
21
|
+
'®': '®',
|
|
22
|
+
'®': '®',
|
|
23
|
+
'…': '…',
|
|
24
|
+
'…': '…',
|
|
25
|
+
'/': '/',
|
|
26
|
+
'/': '/',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const unescapeHtmlEntity = (m) => htmlEntities[m];
|
|
30
|
+
|
|
31
|
+
export const unescape = (text) => text.replace(matchHtmlEntity, unescapeHtmlEntity);
|
package/src/useSSR.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Ported from react-i18next@17.0.9 (8b4a9ea): context lookup -> octane.
|
|
2
|
+
import { useContext } from 'octane';
|
|
3
|
+
import { getI18n, I18nContext } from './context.js';
|
|
4
|
+
import { warnOnce } from './utils.js';
|
|
5
|
+
import { splitSlot } from './internal.js';
|
|
6
|
+
|
|
7
|
+
export const useSSR = (initialI18nStore, initialLanguage, ...rest) => {
|
|
8
|
+
const [user] = splitSlot(rest);
|
|
9
|
+
const props = user[0] || {};
|
|
10
|
+
const { i18n: i18nFromProps } = props;
|
|
11
|
+
const { i18n: i18nFromContext } = useContext(I18nContext) || {};
|
|
12
|
+
const i18n = i18nFromProps || i18nFromContext || getI18n();
|
|
13
|
+
|
|
14
|
+
if (!i18n) {
|
|
15
|
+
warnOnce(
|
|
16
|
+
i18n,
|
|
17
|
+
'NO_I18NEXT_INSTANCE',
|
|
18
|
+
'useSSR: You will need to pass in an i18next instance by using initReactI18next or by passing it via props or context. In monorepo setups, make sure there is only one instance of react-i18next.',
|
|
19
|
+
);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// opt out if is a cloned instance, eg. created by i18next-http-middleware on request
|
|
24
|
+
// -> do not set initial stuff on server side
|
|
25
|
+
if (i18n.options?.isClone) return;
|
|
26
|
+
|
|
27
|
+
// nextjs / SSR: getting data from next.js or other ssr stack
|
|
28
|
+
if (initialI18nStore && !i18n.initializedStoreOnce) {
|
|
29
|
+
if (!i18n.services?.resourceStore) {
|
|
30
|
+
warnOnce(
|
|
31
|
+
i18n,
|
|
32
|
+
'I18N_NOT_INITIALIZED',
|
|
33
|
+
'useSSR: i18n instance was found but not initialized (services.resourceStore is missing). Make sure you call i18next.init() before using useSSR — e.g. at module level, not only in getStaticProps/getServerSideProps.',
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
i18n.services.resourceStore.data = initialI18nStore;
|
|
38
|
+
|
|
39
|
+
// add namespaces to the config - so a languageChange call loads all namespaces needed
|
|
40
|
+
i18n.options.ns = Object.values(initialI18nStore).reduce((mem, lngResources) => {
|
|
41
|
+
Object.keys(lngResources).forEach((ns) => {
|
|
42
|
+
if (mem.indexOf(ns) < 0) mem.push(ns);
|
|
43
|
+
});
|
|
44
|
+
return mem;
|
|
45
|
+
}, i18n.options.ns);
|
|
46
|
+
|
|
47
|
+
i18n.initializedStoreOnce = true;
|
|
48
|
+
i18n.isInitialized = true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (initialLanguage && !i18n.initializedLanguageOnce) {
|
|
52
|
+
i18n.changeLanguage(initialLanguage);
|
|
53
|
+
i18n.initializedLanguageOnce = true;
|
|
54
|
+
}
|
|
55
|
+
};
|