@genrwork/laravel-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.
Potentially problematic release.
This version of @genrwork/laravel-i18next might be problematic. Click here for more details.
- package/README.md +367 -0
- package/client.d.ts +12 -0
- package/dist/index.cjs +12 -0
- package/dist/index.mjs +2 -0
- package/dist/react.cjs +40 -0
- package/dist/react.mjs +38 -0
- package/dist/shared/create-i18n-BSEwKsCX.mjs +641 -0
- package/dist/shared/create-i18n-WsDK4Z8L.cjs +647 -0
- package/dist/svelte.cjs +77 -0
- package/dist/svelte.mjs +74 -0
- package/dist/types/backend.d.ts +29 -0
- package/dist/types/contrib/get-plural-index.d.ts +15 -0
- package/dist/types/format.d.ts +43 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/interfaces/locale-file.d.ts +8 -0
- package/dist/types/interfaces/options.d.ts +19 -0
- package/dist/types/interfaces/replacements.d.ts +6 -0
- package/dist/types/plugin/helper.d.ts +6 -0
- package/dist/types/plugin/locale.d.ts +11 -0
- package/dist/types/plugin/parser.d.ts +22 -0
- package/dist/types/plugin/sources.d.ts +53 -0
- package/dist/types/react/i18n-provider-props.d.ts +23 -0
- package/dist/types/react/index.d.ts +3 -0
- package/dist/types/react/provider.d.ts +10 -0
- package/dist/types/shared/create-i18n.d.ts +26 -0
- package/dist/types/svelte/index.d.ts +48 -0
- package/dist/types/utils/pluralization.d.ts +9 -0
- package/dist/types/utils/recognizer.d.ts +29 -0
- package/dist/types/utils/replacer.d.ts +9 -0
- package/dist/types/utils/resolver.d.ts +23 -0
- package/dist/types/utils/sources.d.ts +14 -0
- package/dist/types/vite.d.ts +25 -0
- package/dist/types/vue/index.d.ts +21 -0
- package/dist/vite.cjs +315 -0
- package/dist/vite.mjs +310 -0
- package/dist/vue.cjs +39 -0
- package/dist/vue.mjs +34 -0
- package/package.json +155 -0
package/dist/svelte.cjs
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var svelte = require('svelte');
|
|
4
|
+
var store = require('svelte/store');
|
|
5
|
+
var createI18n = require('./shared/create-i18n-WsDK4Z8L.cjs');
|
|
6
|
+
require('i18next');
|
|
7
|
+
|
|
8
|
+
const CONTEXT_KEY = 'laravel-i18next';
|
|
9
|
+
/**
|
|
10
|
+
* Create an i18next instance translating Laravel language files, and make it
|
|
11
|
+
* available to `useTranslation()` in child components.
|
|
12
|
+
*
|
|
13
|
+
* Every call creates its own instance, so concurrent SSR requests do not share
|
|
14
|
+
* a language. Must be called during component initialization (top level of `<script>`).
|
|
15
|
+
*
|
|
16
|
+
* ```svelte
|
|
17
|
+
* <script>
|
|
18
|
+
* import { setLaravelI18nContext } from '@genrwork/laravel-i18next/svelte';
|
|
19
|
+
*
|
|
20
|
+
* setLaravelI18nContext({
|
|
21
|
+
* locale: data.locale,
|
|
22
|
+
* fallbackLocale: 'en',
|
|
23
|
+
* files: import.meta.glob('/lang/**\/*.json'),
|
|
24
|
+
* });
|
|
25
|
+
* </script>
|
|
26
|
+
* ```
|
|
27
|
+
*/ function setLaravelI18nContext(options) {
|
|
28
|
+
const i18next = createI18n.createI18n(options);
|
|
29
|
+
const unsubscribe = createI18n.syncDocumentLang(i18next);
|
|
30
|
+
svelte.onDestroy(unsubscribe);
|
|
31
|
+
svelte.setContext(CONTEXT_KEY, i18next);
|
|
32
|
+
return i18next;
|
|
33
|
+
}
|
|
34
|
+
function getI18nextFromContext() {
|
|
35
|
+
if (!svelte.hasContext(CONTEXT_KEY)) {
|
|
36
|
+
throw new Error('No Laravel i18next instance found in context. Call setLaravelI18nContext() in a parent component.');
|
|
37
|
+
}
|
|
38
|
+
return svelte.getContext(CONTEXT_KEY);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* A reactive `t()` function and `ready` flag for the given namespace, bound to
|
|
42
|
+
* the i18next instance set with `setLaravelI18nContext()`.
|
|
43
|
+
*
|
|
44
|
+
* ```svelte
|
|
45
|
+
* <script>
|
|
46
|
+
* import { useTranslation } from '@genrwork/laravel-i18next/svelte';
|
|
47
|
+
*
|
|
48
|
+
* const { t } = useTranslation('auth');
|
|
49
|
+
* </script>
|
|
50
|
+
*
|
|
51
|
+
* <p>{$t('failed')}</p>
|
|
52
|
+
* ```
|
|
53
|
+
*/ function useTranslation(namespace, i18nextInstance) {
|
|
54
|
+
const instance = i18nextInstance !== null && i18nextInstance !== void 0 ? i18nextInstance : getI18nextFromContext();
|
|
55
|
+
const revision = store.writable(0);
|
|
56
|
+
const bump = ()=>revision.update((n)=>n + 1);
|
|
57
|
+
instance.on('languageChanged', bump);
|
|
58
|
+
instance.on('loaded', bump);
|
|
59
|
+
instance.on('added', bump);
|
|
60
|
+
svelte.onDestroy(()=>{
|
|
61
|
+
instance.off('languageChanged', bump);
|
|
62
|
+
instance.off('loaded', bump);
|
|
63
|
+
instance.off('added', bump);
|
|
64
|
+
});
|
|
65
|
+
const t = store.derived(revision, ()=>instance.getFixedT(null, namespace !== null && namespace !== void 0 ? namespace : null));
|
|
66
|
+
const ready = store.derived(revision, ()=>instance.isInitialized && (!namespace || instance.hasLoadedNamespace(namespace)));
|
|
67
|
+
const language = store.derived(revision, ()=>instance.language);
|
|
68
|
+
return {
|
|
69
|
+
t,
|
|
70
|
+
ready,
|
|
71
|
+
language,
|
|
72
|
+
i18next: instance
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
exports.setLaravelI18nContext = setLaravelI18nContext;
|
|
77
|
+
exports.useTranslation = useTranslation;
|
package/dist/svelte.mjs
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { onDestroy, setContext, hasContext, getContext } from 'svelte';
|
|
2
|
+
import { writable, derived } from 'svelte/store';
|
|
3
|
+
import { c as createI18n, s as syncDocumentLang } from './shared/create-i18n-BSEwKsCX.mjs';
|
|
4
|
+
import 'i18next';
|
|
5
|
+
|
|
6
|
+
const CONTEXT_KEY = 'laravel-i18next';
|
|
7
|
+
/**
|
|
8
|
+
* Create an i18next instance translating Laravel language files, and make it
|
|
9
|
+
* available to `useTranslation()` in child components.
|
|
10
|
+
*
|
|
11
|
+
* Every call creates its own instance, so concurrent SSR requests do not share
|
|
12
|
+
* a language. Must be called during component initialization (top level of `<script>`).
|
|
13
|
+
*
|
|
14
|
+
* ```svelte
|
|
15
|
+
* <script>
|
|
16
|
+
* import { setLaravelI18nContext } from '@genrwork/laravel-i18next/svelte';
|
|
17
|
+
*
|
|
18
|
+
* setLaravelI18nContext({
|
|
19
|
+
* locale: data.locale,
|
|
20
|
+
* fallbackLocale: 'en',
|
|
21
|
+
* files: import.meta.glob('/lang/**\/*.json'),
|
|
22
|
+
* });
|
|
23
|
+
* </script>
|
|
24
|
+
* ```
|
|
25
|
+
*/ function setLaravelI18nContext(options) {
|
|
26
|
+
const i18next = createI18n(options);
|
|
27
|
+
const unsubscribe = syncDocumentLang(i18next);
|
|
28
|
+
onDestroy(unsubscribe);
|
|
29
|
+
setContext(CONTEXT_KEY, i18next);
|
|
30
|
+
return i18next;
|
|
31
|
+
}
|
|
32
|
+
function getI18nextFromContext() {
|
|
33
|
+
if (!hasContext(CONTEXT_KEY)) {
|
|
34
|
+
throw new Error('No Laravel i18next instance found in context. Call setLaravelI18nContext() in a parent component.');
|
|
35
|
+
}
|
|
36
|
+
return getContext(CONTEXT_KEY);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A reactive `t()` function and `ready` flag for the given namespace, bound to
|
|
40
|
+
* the i18next instance set with `setLaravelI18nContext()`.
|
|
41
|
+
*
|
|
42
|
+
* ```svelte
|
|
43
|
+
* <script>
|
|
44
|
+
* import { useTranslation } from '@genrwork/laravel-i18next/svelte';
|
|
45
|
+
*
|
|
46
|
+
* const { t } = useTranslation('auth');
|
|
47
|
+
* </script>
|
|
48
|
+
*
|
|
49
|
+
* <p>{$t('failed')}</p>
|
|
50
|
+
* ```
|
|
51
|
+
*/ function useTranslation(namespace, i18nextInstance) {
|
|
52
|
+
const instance = i18nextInstance !== null && i18nextInstance !== void 0 ? i18nextInstance : getI18nextFromContext();
|
|
53
|
+
const revision = writable(0);
|
|
54
|
+
const bump = ()=>revision.update((n)=>n + 1);
|
|
55
|
+
instance.on('languageChanged', bump);
|
|
56
|
+
instance.on('loaded', bump);
|
|
57
|
+
instance.on('added', bump);
|
|
58
|
+
onDestroy(()=>{
|
|
59
|
+
instance.off('languageChanged', bump);
|
|
60
|
+
instance.off('loaded', bump);
|
|
61
|
+
instance.off('added', bump);
|
|
62
|
+
});
|
|
63
|
+
const t = derived(revision, ()=>instance.getFixedT(null, namespace !== null && namespace !== void 0 ? namespace : null));
|
|
64
|
+
const ready = derived(revision, ()=>instance.isInitialized && (!namespace || instance.hasLoadedNamespace(namespace)));
|
|
65
|
+
const language = derived(revision, ()=>instance.language);
|
|
66
|
+
return {
|
|
67
|
+
t,
|
|
68
|
+
ready,
|
|
69
|
+
language,
|
|
70
|
+
i18next: instance
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { setLaravelI18nContext, useTranslation };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { BackendModule, ReadCallback } from 'i18next';
|
|
2
|
+
import type LaravelBackendOptions from './interfaces/options';
|
|
3
|
+
/**
|
|
4
|
+
* i18next backend loading Laravel language files as separate namespaces from
|
|
5
|
+
* one or more sources, highest priority first: the default namespace from
|
|
6
|
+
* `{source}/{locale}.json`, and one namespace per PHP file from
|
|
7
|
+
* `{source}/{locale}/{namespace}.json` (generated by the Vite plugin from
|
|
8
|
+
* `{namespace}.php`, or hand-written) -- a namespace may also nest to any
|
|
9
|
+
* depth, see `src/utils/recognizer.ts`.
|
|
10
|
+
*
|
|
11
|
+
* When several sources have a file for the same `(locale, namespace)`, they
|
|
12
|
+
* are merged per key: a higher-priority source's key overrides a
|
|
13
|
+
* lower-priority one, but a key present only in a lower-priority source still
|
|
14
|
+
* resolves. This is what lets a namespace migrate from PHP to hand-written
|
|
15
|
+
* JSON one string at a time.
|
|
16
|
+
*
|
|
17
|
+
* Eager files are read synchronously, lazy files asynchronously -- and the
|
|
18
|
+
* whole read stays synchronous as long as every candidate for the pair is
|
|
19
|
+
* eager, even when several sources contribute, which SSR depends on
|
|
20
|
+
* (`initAsync: false` + `preload`). A (locale, namespace) pair without a
|
|
21
|
+
* matching file in any source gets no resources.
|
|
22
|
+
*/
|
|
23
|
+
export default class LaravelBackend implements BackendModule<LaravelBackendOptions> {
|
|
24
|
+
static type: "backend";
|
|
25
|
+
type: "backend";
|
|
26
|
+
private files;
|
|
27
|
+
init(_services: unknown, backendOptions?: Partial<LaravelBackendOptions>): void;
|
|
28
|
+
read(language: string, namespace: string, callback: ReadCallback): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Get the index to use for pluralization.
|
|
3
|
+
* The plural rules are derived from code of the Zend Framework.
|
|
4
|
+
*
|
|
5
|
+
* @category Zend
|
|
6
|
+
* @package Zend_Locale
|
|
7
|
+
* @public https://github.com/zendframework/zf1/blob/master/library/Zend/Translate/Plural.php
|
|
8
|
+
* @copyright 2005-2015 Zend Technologies USA Inc. http://www.zend.com
|
|
9
|
+
* @license http://framework.zend.com/license New BSD License
|
|
10
|
+
*
|
|
11
|
+
* @param {String} locale
|
|
12
|
+
* @param {Number} number
|
|
13
|
+
* @return {Number}
|
|
14
|
+
*/
|
|
15
|
+
export declare function getPluralIndex(locale: string, number: number): 1 | 0 | 3 | 2 | 4 | 5;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { i18n, TOptions } from 'i18next';
|
|
2
|
+
/**
|
|
3
|
+
* i18next format plugin translating the way Laravel does:
|
|
4
|
+
*
|
|
5
|
+
* - flat keys (`auth.failed`, `Welcome, :name!`), no key or namespace separators;
|
|
6
|
+
* - `:name`, `:NAME` and `:Name` replacements instead of `{{name}}` interpolation and `$t()` nesting;
|
|
7
|
+
* - a numeric `count` selects the message with Laravel's `trans_choice()` rules
|
|
8
|
+
* (`one|many`, `{0} none|[1,19] some|[20,*] many`) instead of `_one`/`_other` suffixed keys;
|
|
9
|
+
* - empty messages fall back to the next language.
|
|
10
|
+
*/
|
|
11
|
+
export default class LaravelFormat {
|
|
12
|
+
static type: "i18nFormat";
|
|
13
|
+
type: "i18nFormat";
|
|
14
|
+
/**
|
|
15
|
+
* Non-string translations (e.g. an empty PHP array) are returned as is.
|
|
16
|
+
*/
|
|
17
|
+
handleAsObject: boolean;
|
|
18
|
+
private i18next?;
|
|
19
|
+
init(i18next: i18n): void;
|
|
20
|
+
getResource(language: string, namespace: string, key: string): any;
|
|
21
|
+
addLookupKeys(finalKeys: string[]): string[];
|
|
22
|
+
parse(message: unknown, options: TOptions, language: string, namespace: string, _key?: string, info?: {
|
|
23
|
+
resolved?: {
|
|
24
|
+
usedLng?: string;
|
|
25
|
+
};
|
|
26
|
+
}): unknown;
|
|
27
|
+
/**
|
|
28
|
+
* The language the message actually resolved in, so pluralization always
|
|
29
|
+
* matches the text being pluralized -- never merely a language that has
|
|
30
|
+
* translations for the namespace, which is not the same thing once several
|
|
31
|
+
* sources can contribute to one namespace (see LaravelBackend): a single
|
|
32
|
+
* unrelated key from a higher-priority source is enough to make
|
|
33
|
+
* `hasResourceBundle(language, namespace)` true for a namespace whose
|
|
34
|
+
* actual message still came from the fallback language.
|
|
35
|
+
*
|
|
36
|
+
* i18next passes the language the lookup succeeded at as `resolved.usedLng`
|
|
37
|
+
* in `parse()`'s 6th argument (`extendTranslation()` in i18next's own
|
|
38
|
+
* `translator.js`); prefer it. Older i18next in the `>=24` peer range that
|
|
39
|
+
* does not pass it falls back to a heuristic: the current language when it
|
|
40
|
+
* has translations for the namespace, else the fallback language.
|
|
41
|
+
*/
|
|
42
|
+
private getPluralLocale;
|
|
43
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import LaravelBackend from './backend';
|
|
2
|
+
import LaravelFormat from './format';
|
|
3
|
+
export { LaravelBackend, LaravelFormat };
|
|
4
|
+
export { createI18n, documentLocale, syncDocumentLang } from './shared/create-i18n';
|
|
5
|
+
export type { LaravelI18nOptions } from './shared/create-i18n';
|
|
6
|
+
export type { default as LaravelBackendOptions, LocaleFiles, LocaleFileSources } from './interfaces/options';
|
|
7
|
+
export type { default as ReplacementsInterface } from './interfaces/replacements';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language files as returned by `import.meta.glob('/lang/*.json')`,
|
|
3
|
+
* either lazy (loader functions) or eager (modules).
|
|
4
|
+
*/
|
|
5
|
+
export type LocaleFiles = Record<string, unknown> | Record<string, () => Promise<unknown>>;
|
|
6
|
+
/**
|
|
7
|
+
* One or more `LocaleFiles` maps, highest priority first. A single map is a
|
|
8
|
+
* one-source project; an array layers several sources, resolving each key from
|
|
9
|
+
* the highest-priority source that has it (see `toSources()` in
|
|
10
|
+
* `src/utils/sources.ts`). The Vite plugin's `sources` option always produces
|
|
11
|
+
* an array, one entry per configured source, in the same order.
|
|
12
|
+
*/
|
|
13
|
+
export type LocaleFileSources = LocaleFiles | LocaleFiles[];
|
|
14
|
+
/**
|
|
15
|
+
* Options of `LaravelBackend`, given as the `backend` option of i18next.
|
|
16
|
+
*/
|
|
17
|
+
export default interface LaravelBackendOptions {
|
|
18
|
+
files: LocaleFileSources;
|
|
19
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
/**
|
|
3
|
+
* Locales having a `lang/{locale}/` directory containing PHP files,
|
|
4
|
+
* however deep -- a locale whose PHP all lives in a nested namespace
|
|
5
|
+
* subdirectory still counts.
|
|
6
|
+
*
|
|
7
|
+
* @param dirname
|
|
8
|
+
*/
|
|
9
|
+
getPhpLocale: (dirname: string) => string[];
|
|
10
|
+
};
|
|
11
|
+
export default _default;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface GeneratedFile {
|
|
2
|
+
path: string;
|
|
3
|
+
basename: string;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Convert every `lang/{locale}/{namespace}.php` file into a sibling
|
|
7
|
+
* `lang/{locale}/{namespace}.json` file, one per PHP file, so each PHP file
|
|
8
|
+
* becomes its own i18next namespace instead of being merged with the others.
|
|
9
|
+
* A PHP file may sit at any depth under the locale directory --
|
|
10
|
+
* `lang/{locale}/{ns1}/{ns2}.php` and deeper -- and gets its sibling `.json`
|
|
11
|
+
* written at that same nested path; the resulting namespace name itself is
|
|
12
|
+
* inferred later, from the generated file's path, by `src/utils/recognizer.ts`.
|
|
13
|
+
*
|
|
14
|
+
* A source meant to hold hand-written JSON (e.g. a frontend-only catalog)
|
|
15
|
+
* should never also hold PHP files of the same name -- this directory is a
|
|
16
|
+
* PHP source, so its sibling `.json` is always regenerated, overwriting
|
|
17
|
+
* whatever was there before (including a leftover from a previous, uncleaned
|
|
18
|
+
* run). Keep hand-written translations in their own source directory.
|
|
19
|
+
*
|
|
20
|
+
* @param dirname
|
|
21
|
+
*/
|
|
22
|
+
export default function parser(dirname: string): GeneratedFile[];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One configured source, resolved against the project root.
|
|
3
|
+
*/
|
|
4
|
+
export interface ResolvedSource {
|
|
5
|
+
/** As configured, e.g. `'Modules/*\/lang'`. */
|
|
6
|
+
pattern: string;
|
|
7
|
+
/** Root-relative glob for `import.meta.glob()`, e.g. `'/Modules/*\/lang/**\/*.json'`. */
|
|
8
|
+
glob: string;
|
|
9
|
+
/** Every directory the pattern currently expands to, existing ones only, sorted. */
|
|
10
|
+
dirnames: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Every directory an ordered source `pattern` currently expands to, relative
|
|
14
|
+
* to `root`. A pattern with no `*` is a single literal directory (existing or
|
|
15
|
+
* not -- callers decide what an absent literal directory means). A pattern
|
|
16
|
+
* with exactly one `*` segment (e.g. `Modules/*\/lang`) is expanded against
|
|
17
|
+
* the filesystem: every existing subdirectory of the prefix before the `*`,
|
|
18
|
+
* with the suffix after it appended, kept only when the full path also
|
|
19
|
+
* exists, sorted. Two or more `*` segments throw -- unsupported.
|
|
20
|
+
*
|
|
21
|
+
* @param pattern
|
|
22
|
+
* @param root
|
|
23
|
+
*/
|
|
24
|
+
export declare function expandDirs(pattern: string, root: string): string[];
|
|
25
|
+
/**
|
|
26
|
+
* The root-relative glob `import.meta.glob()` needs for a source pattern.
|
|
27
|
+
* Globs inside a virtual module have no containing file to be relative to,
|
|
28
|
+
* so this is always root-relative (a leading `/`). A `*` segment in the
|
|
29
|
+
* pattern passes through `path.resolve`/`path.relative`/`normalizePath`
|
|
30
|
+
* completely untouched -- none of them treat it specially -- which is why
|
|
31
|
+
* the emitted glob needs no separate wildcard handling: `Modules/*\/lang`
|
|
32
|
+
* becomes `/Modules/*\/lang/**\/*.json` and `import.meta.glob()` expands the
|
|
33
|
+
* `*` itself, natively, at both dev and build time.
|
|
34
|
+
*
|
|
35
|
+
* @param pattern
|
|
36
|
+
* @param root
|
|
37
|
+
*/
|
|
38
|
+
export declare function toGlob(pattern: string, root: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve every configured source pattern, in the given order.
|
|
41
|
+
*
|
|
42
|
+
* @param patterns
|
|
43
|
+
* @param root
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveSources(patterns: string[], root: string): ResolvedSource[];
|
|
46
|
+
/**
|
|
47
|
+
* Whether `file` sits under one of `dirnames` (any depth) -- used to decide
|
|
48
|
+
* whether a changed file belongs to any configured source at all.
|
|
49
|
+
*
|
|
50
|
+
* @param file
|
|
51
|
+
* @param dirnames
|
|
52
|
+
*/
|
|
53
|
+
export declare function isUnderAnySource(file: string, dirnames: string[]): boolean;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { LocaleFileSources } from '../interfaces/options';
|
|
3
|
+
/**
|
|
4
|
+
* Props of `<LaravelReactI18nProvider>`.
|
|
5
|
+
*/
|
|
6
|
+
export default interface I18nProviderProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
/**
|
|
9
|
+
* Language files: `virtual:laravel-i18next/files` (an ordered array, one
|
|
10
|
+
* entry per configured `sources`, highest priority first), or a single
|
|
11
|
+
* `import.meta.glob('/lang/**\/*.json')` (lazy) /
|
|
12
|
+
* `import.meta.glob('/lang/**\/*.json', { eager: true })` (eager).
|
|
13
|
+
*/
|
|
14
|
+
files: LocaleFileSources;
|
|
15
|
+
/**
|
|
16
|
+
* Defaults to the `<html lang="">` attribute, or `en`.
|
|
17
|
+
*/
|
|
18
|
+
locale?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Defaults to the `<html lang="">` attribute, or `en`.
|
|
21
|
+
*/
|
|
22
|
+
fallbackLocale?: string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type I18nProviderProps from './i18n-provider-props';
|
|
2
|
+
/**
|
|
3
|
+
* Provides an i18next instance translating Laravel language files to react-i18next.
|
|
4
|
+
*
|
|
5
|
+
* Every provider owns its instance, so concurrent SSR requests do not share a language.
|
|
6
|
+
* With eager files, every namespace is preloaded and translated on the first render.
|
|
7
|
+
* While a lazy namespace loads, the children suspend, which keeps server-rendered
|
|
8
|
+
* HTML in place until it can be hydrated.
|
|
9
|
+
*/
|
|
10
|
+
export default function LaravelReactI18nProvider({ children, files, locale, fallbackLocale }: I18nProviderProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { i18n } from 'i18next';
|
|
2
|
+
import type { LocaleFileSources } from '../interfaces/options';
|
|
3
|
+
export interface LaravelI18nOptions {
|
|
4
|
+
files: LocaleFileSources;
|
|
5
|
+
locale?: string;
|
|
6
|
+
fallbackLocale?: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Locale of the `<html lang="">` attribute (set by Laravel's `app.blade.php`), or `en`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function documentLocale(): string;
|
|
12
|
+
/**
|
|
13
|
+
* Set the `<html lang="">` attribute to the instance's current language, and keep it
|
|
14
|
+
* in sync whenever the language changes afterwards.
|
|
15
|
+
*/
|
|
16
|
+
export declare function syncDocumentLang(instance: i18n): () => void;
|
|
17
|
+
/**
|
|
18
|
+
* Create an i18next instance translating Laravel language files: the default
|
|
19
|
+
* namespace from `lang/{locale}.json`, and one namespace per `lang/{locale}/{namespace}.json`
|
|
20
|
+
* file (generated by the Vite plugin, or hand-written).
|
|
21
|
+
*
|
|
22
|
+
* With eager files, every known namespace of the locale and fallback locale is
|
|
23
|
+
* preloaded synchronously, so a first render never has to wait for translations.
|
|
24
|
+
* With lazy files, namespaces are loaded on demand as components request them.
|
|
25
|
+
*/
|
|
26
|
+
export declare function createI18n({ files, locale, fallbackLocale }: LaravelI18nOptions): i18n;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type Readable } from 'svelte/store';
|
|
2
|
+
import type { i18n, TFunction } from 'i18next';
|
|
3
|
+
import type { LaravelI18nOptions } from '../shared/create-i18n';
|
|
4
|
+
export interface Translation<N extends string = string> {
|
|
5
|
+
/** The current translation function, reactive to language and namespace loading. */
|
|
6
|
+
t: Readable<TFunction<N>>;
|
|
7
|
+
/** Whether the namespace is loaded (always `true` with eager files). */
|
|
8
|
+
ready: Readable<boolean>;
|
|
9
|
+
/** The current language, reactive to `changeLanguage()`. */
|
|
10
|
+
language: Readable<string>;
|
|
11
|
+
/** The underlying i18next instance. Not itself reactive; prefer `t`, `ready` and `language`. */
|
|
12
|
+
i18next: i18n;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create an i18next instance translating Laravel language files, and make it
|
|
16
|
+
* available to `useTranslation()` in child components.
|
|
17
|
+
*
|
|
18
|
+
* Every call creates its own instance, so concurrent SSR requests do not share
|
|
19
|
+
* a language. Must be called during component initialization (top level of `<script>`).
|
|
20
|
+
*
|
|
21
|
+
* ```svelte
|
|
22
|
+
* <script>
|
|
23
|
+
* import { setLaravelI18nContext } from '@genrwork/laravel-i18next/svelte';
|
|
24
|
+
*
|
|
25
|
+
* setLaravelI18nContext({
|
|
26
|
+
* locale: data.locale,
|
|
27
|
+
* fallbackLocale: 'en',
|
|
28
|
+
* files: import.meta.glob('/lang/**\/*.json'),
|
|
29
|
+
* });
|
|
30
|
+
* </script>
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function setLaravelI18nContext(options: LaravelI18nOptions): i18n;
|
|
34
|
+
/**
|
|
35
|
+
* A reactive `t()` function and `ready` flag for the given namespace, bound to
|
|
36
|
+
* the i18next instance set with `setLaravelI18nContext()`.
|
|
37
|
+
*
|
|
38
|
+
* ```svelte
|
|
39
|
+
* <script>
|
|
40
|
+
* import { useTranslation } from '@genrwork/laravel-i18next/svelte';
|
|
41
|
+
*
|
|
42
|
+
* const { t } = useTranslation('auth');
|
|
43
|
+
* </script>
|
|
44
|
+
*
|
|
45
|
+
* <p>{$t('failed')}</p>
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function useTranslation<N extends string = string>(namespace?: N, i18nextInstance?: i18n): Translation<N>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Select a proper translation string based on the given number,
|
|
3
|
+
* using the same rules as Laravel's `trans_choice()`.
|
|
4
|
+
*
|
|
5
|
+
* @param message
|
|
6
|
+
* @param number
|
|
7
|
+
* @param locale
|
|
8
|
+
*/
|
|
9
|
+
export default function pluralization(message: string, number: number, locale: string): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { LocaleFileSources } from '../interfaces/options';
|
|
2
|
+
/**
|
|
3
|
+
* The i18next namespace of a flat `lang/{locale}.json` file (i18next's own default).
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEFAULT_NAMESPACE = "translation";
|
|
6
|
+
/**
|
|
7
|
+
* A candidate file for a `(locale, namespace)` pair: `source` is the index of
|
|
8
|
+
* the `LocaleFileSources` entry it came from (lower = higher priority).
|
|
9
|
+
*/
|
|
10
|
+
export interface Candidate {
|
|
11
|
+
source: number;
|
|
12
|
+
file: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Recognize the locales and namespaces available from one or more
|
|
16
|
+
* `LocaleFileSources`, highest priority first.
|
|
17
|
+
*
|
|
18
|
+
* @param files
|
|
19
|
+
*/
|
|
20
|
+
export default function recognizer(files: LocaleFileSources): {
|
|
21
|
+
isLocale: (locale: string) => boolean;
|
|
22
|
+
getLocales: () => string[];
|
|
23
|
+
getNamespaces: (locale: string) => string[];
|
|
24
|
+
getAllNamespaces: () => string[];
|
|
25
|
+
/** The single highest-priority file, or `undefined` when none exists. */
|
|
26
|
+
getFile: (locale: string, namespace: string) => string | undefined;
|
|
27
|
+
/** Every candidate file for the pair, highest priority first. */
|
|
28
|
+
getCandidates: (locale: string, namespace: string) => Candidate[];
|
|
29
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type ReplacementsInterface from '../interfaces/replacements';
|
|
2
|
+
/**
|
|
3
|
+
* Make the place-holder replacements on a line, the Laravel way:
|
|
4
|
+
* `:name` as is, `:NAME` upper-cased and `:Name` capitalized.
|
|
5
|
+
*
|
|
6
|
+
* @param message
|
|
7
|
+
* @param replacements
|
|
8
|
+
*/
|
|
9
|
+
export default function replacer(message: string, replacements?: ReplacementsInterface): string;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type LocaleFileInterface from '../interfaces/locale-file';
|
|
2
|
+
import type { LocaleFileSources } from '../interfaces/options';
|
|
3
|
+
/**
|
|
4
|
+
* One resolved candidate: a module directly (eager `import.meta.glob`, or a
|
|
5
|
+
* plain object) or a promise of one (lazy `import.meta.glob`, or a loader
|
|
6
|
+
* function already called).
|
|
7
|
+
*/
|
|
8
|
+
export type ResolvedFile = LocaleFileInterface | Promise<LocaleFileInterface>;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve every candidate language file of a locale's namespace, highest
|
|
11
|
+
* priority first (see `recognizer().getCandidates()`). Each candidate keeps
|
|
12
|
+
* its own shape -- eager candidates stay synchronous, lazy ones stay a
|
|
13
|
+
* promise -- so a caller merging them can still take an all-synchronous path
|
|
14
|
+
* when every candidate happens to be eager.
|
|
15
|
+
*
|
|
16
|
+
* An empty array means the locale has no file at all for that namespace, in
|
|
17
|
+
* any source.
|
|
18
|
+
*
|
|
19
|
+
* @param files
|
|
20
|
+
* @param locale
|
|
21
|
+
* @param namespace
|
|
22
|
+
*/
|
|
23
|
+
export default function resolver(files: LocaleFileSources, locale: string, namespace: string): ResolvedFile[];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { LocaleFiles, LocaleFileSources } from '../interfaces/options';
|
|
2
|
+
/**
|
|
3
|
+
* Normalize a `LocaleFileSources` (a single `LocaleFiles` map, or an ordered
|
|
4
|
+
* array of them, highest priority first) into an array, always -- a bare
|
|
5
|
+
* map behaves like a single-entry array. Non-object entries (e.g. `null`,
|
|
6
|
+
* defensively) are dropped rather than throwing, so a hand-built array with a
|
|
7
|
+
* gap does not crash the whole app.
|
|
8
|
+
*
|
|
9
|
+
* `Array.isArray` is the discriminator: `LocaleFiles` is itself a plain
|
|
10
|
+
* `Record<string, unknown>`, which is why every consumer of `files` must call
|
|
11
|
+
* this before treating it as a map of maps -- `Object.keys()` on a raw array
|
|
12
|
+
* would otherwise yield `'0'`, `'1'`, ... instead of file paths.
|
|
13
|
+
*/
|
|
14
|
+
export declare function toSources(files: LocaleFileSources): LocaleFiles[];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
|
+
export interface ConfigInterface {
|
|
3
|
+
/**
|
|
4
|
+
* Ordered, highest priority first. Each entry directory may contain
|
|
5
|
+
* hand-written `.json` files, `.php` files (each converted to a sibling
|
|
6
|
+
* `.json`), or both -- but not both for the same namespace, since a
|
|
7
|
+
* hand-written file is never overwritten (see `src/plugin/parser.ts`).
|
|
8
|
+
* One `*` segment is allowed per entry, e.g. `'Modules/*\/lang'`, expanded
|
|
9
|
+
* against every matching directory that exists when the dev server starts
|
|
10
|
+
* or a build runs. Defaults to `['lang']`.
|
|
11
|
+
*/
|
|
12
|
+
sources?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Language files, one namespace per language file: loaded eagerly by server
|
|
16
|
+
* code (SSR), so it renders translated, and lazily by client code. The
|
|
17
|
+
* default export is an ARRAY, one entry per configured source in `sources`,
|
|
18
|
+
* highest priority first (see `LocaleFileSources` in `src/interfaces/options.ts`).
|
|
19
|
+
*/
|
|
20
|
+
export declare const FILES_MODULE_ID = "virtual:laravel-i18next/files";
|
|
21
|
+
/**
|
|
22
|
+
* Makes Laravel PHP translations available to i18next by generating a sibling
|
|
23
|
+
* `.json` file next to each `.php` file, across one or more ordered sources.
|
|
24
|
+
*/
|
|
25
|
+
export default function i18n(config?: ConfigInterface): PluginOption;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Plugin } from 'vue';
|
|
2
|
+
import { useTranslation } from 'i18next-vue';
|
|
3
|
+
import type { LaravelI18nOptions } from '../shared/create-i18n';
|
|
4
|
+
export { useTranslation };
|
|
5
|
+
/**
|
|
6
|
+
* Vue plugin providing an i18next instance translating Laravel language files
|
|
7
|
+
* to `i18next-vue`'s `useTranslation()` composable and `$t`/`$i18next`.
|
|
8
|
+
*
|
|
9
|
+
* Every app instance owns its own i18next instance, so concurrent SSR requests
|
|
10
|
+
* do not share a language.
|
|
11
|
+
*
|
|
12
|
+
* ```ts
|
|
13
|
+
* import { createApp } from 'vue';
|
|
14
|
+
* import { laravelVueI18n } from '@genrwork/laravel-i18next/vue';
|
|
15
|
+
*
|
|
16
|
+
* createApp(App)
|
|
17
|
+
* .use(laravelVueI18n({ locale: 'uk', fallbackLocale: 'en', files: import.meta.glob('/lang/**\/*.json') }))
|
|
18
|
+
* .mount('#app');
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare function laravelVueI18n(options: LaravelI18nOptions): Plugin;
|