@juit/vue-i18n 0.4.0 → 1.0.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/AGENTS.md +187 -0
- package/NOTICE.md +13 -0
- package/README.md +232 -93
- package/dist/index.d.ts +235 -174
- package/dist/index.js +147 -90
- package/dist/index.js.map +1 -1
- package/lib/index.ts +386 -0
- package/lib/iso-3166.ts +272 -0
- package/lib/iso-4217.ts +176 -0
- package/lib/iso-639.ts +205 -0
- package/lib/translator.ts +539 -0
- package/package.json +14 -12
package/AGENTS.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Using and maintaining @juit/vue-i18n
|
|
2
|
+
|
|
3
|
+
This is a small Vue 3 internationalization library built around native `Intl`
|
|
4
|
+
APIs. Keep it small: full locale negotiation, CLDR plural rules, and a richer
|
|
5
|
+
message syntax are outside its intended scope. Read `README.md` for public API
|
|
6
|
+
examples and `package.json` for current dependencies and commands.
|
|
7
|
+
|
|
8
|
+
## Guidance for consumer applications
|
|
9
|
+
|
|
10
|
+
Use this section when helping an application adopt the package. The source map
|
|
11
|
+
and verification commands below apply to maintaining this repository; use the
|
|
12
|
+
consumer application's own build and test commands when working there.
|
|
13
|
+
|
|
14
|
+
### Install and configure
|
|
15
|
+
|
|
16
|
+
- Install with `npm install @juit/vue-i18n` and use a compatible Vue 3 version
|
|
17
|
+
(see `peerDependencies` in `package.json`). The package exports ESM and relies
|
|
18
|
+
on native `Intl` APIs, including `Intl.supportedValuesOf`; it provides no
|
|
19
|
+
polyfills.
|
|
20
|
+
- Import runtime APIs and types from `@juit/vue-i18n`. Do not import internal
|
|
21
|
+
`lib/` files or use `makeTranslator` in consumer code.
|
|
22
|
+
- Register `app.use(i18n, options)` before mounting the app. `defaultLanguage`
|
|
23
|
+
is required; provide a translation in its base language for each message.
|
|
24
|
+
- Language selection is explicit. For browser preferences, use
|
|
25
|
+
`new LanguageMatcher(['en', 'de']).match(navigator.languages)` and pass the
|
|
26
|
+
result as `defaultLanguage`. Access `navigator` only in browser code; on the
|
|
27
|
+
server, pass preferences obtained from the request or a configured default.
|
|
28
|
+
|
|
29
|
+
Prefer a configuration module that exports the actual runtime options and
|
|
30
|
+
derives key types from its message dictionary:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import type { I18nOptions, Translations } from '@juit/vue-i18n'
|
|
34
|
+
|
|
35
|
+
const translations = {
|
|
36
|
+
hello: { en: 'Hello, {name}!', de: 'Hallo, {name}!' },
|
|
37
|
+
cats: { en: ['one cat', '{n} cats'], de: ['eine Katze', '{n} Katzen'] },
|
|
38
|
+
} as const satisfies Translations
|
|
39
|
+
|
|
40
|
+
declare module '@juit/vue-i18n' {
|
|
41
|
+
interface I18nConfiguration {
|
|
42
|
+
languages: 'en' | 'de',
|
|
43
|
+
translationKeys: keyof typeof translations,
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const i18nOptions = {
|
|
48
|
+
defaultLanguage: 'en',
|
|
49
|
+
translations,
|
|
50
|
+
} satisfies I18nOptions
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Import `i18nOptions` in the app entry point and pass it to `app.use(i18n,
|
|
54
|
+
i18nOptions)`. Keep the augmentation in a module included by the application's
|
|
55
|
+
TypeScript configuration. Declaration merging supplies compile-time checks;
|
|
56
|
+
it does not register translations or formats at runtime. For custom formats,
|
|
57
|
+
use `satisfies DateTimeFormats` / `satisfies NumberFormats`, derive the
|
|
58
|
+
corresponding configuration unions with `keyof typeof`, and pass those same
|
|
59
|
+
dictionaries to the plugin. See [Configuring Types](README.md#configuring-types).
|
|
60
|
+
|
|
61
|
+
### Use the translator
|
|
62
|
+
|
|
63
|
+
- Call `useTranslator()` inside component setup or another valid Vue injection
|
|
64
|
+
context. For ordinary helper functions, pass the translator as an argument
|
|
65
|
+
instead of calling the composable at module scope.
|
|
66
|
+
- Use `translator.t('hello', { name: 'Alice' })`, `translator.tc('cats', count)`,
|
|
67
|
+
`translator.n(amount, 'EUR')`, and `translator.d(date, 'shortDate')`.
|
|
68
|
+
Templates expose the same methods as `$t`, `$tc`, `$n`, and `$d`.
|
|
69
|
+
- Keep translated text in templates or a `computed(() => translator.t(...))`
|
|
70
|
+
when it must follow locale changes. A string translated once during setup
|
|
71
|
+
does not update itself. Read `translator.language` / `region` / `locale`
|
|
72
|
+
through the reactive object rather than destructuring their current values.
|
|
73
|
+
- Change locale through those properties. Only language and region are
|
|
74
|
+
supported; the message syntax and plural rules below are intentionally basic.
|
|
75
|
+
- Prefer tuples when messages contain literal pipes. Use `as const satisfies
|
|
76
|
+
Translations` to retain tuple and key inference. Parameters accept strings
|
|
77
|
+
and numbers; translations produce plain strings, not Vue components.
|
|
78
|
+
- For asynchronously loaded messages, use `utils.updateTranslations(...)`
|
|
79
|
+
rather than mutating the original dictionary. Updates are not reactive:
|
|
80
|
+
coordinate loading with application state so consumers evaluate translations
|
|
81
|
+
again after loading completes.
|
|
82
|
+
- For server rendering, initialize a translator per app/request. Choose
|
|
83
|
+
matching initial locales and explicit time zones on server and client when
|
|
84
|
+
formatted output must agree.
|
|
85
|
+
|
|
86
|
+
## Source map
|
|
87
|
+
|
|
88
|
+
- `lib/index.ts`: public exports, configuration types, Vue plugin installation,
|
|
89
|
+
`useTranslator()`, component global types, and `LanguageMatcher`.
|
|
90
|
+
- `lib/translator.ts`: reactive locale state, translation parsing and caching,
|
|
91
|
+
number/date formatting, and translation updates. `makeTranslator` is an
|
|
92
|
+
internal factory; it is not a runtime export of the package entry point.
|
|
93
|
+
- `lib/iso-639.ts`, `lib/iso-3166.ts`, `lib/iso-4217.ts`: reference types,
|
|
94
|
+
frozen code arrays, and type guards.
|
|
95
|
+
- `test/01-i18n.test.ts`: translator and reference-data runtime tests.
|
|
96
|
+
- `test/02-app.test.ts` and `test/test.vue`: Vue integration tests.
|
|
97
|
+
- `test/03-matcher.test.ts`: matcher runtime and type assertions.
|
|
98
|
+
- `test/types/`: isolated compiler-only tests for default and augmented types.
|
|
99
|
+
- `test/data/`: authoritative fixtures for this repository's country and
|
|
100
|
+
language tables. See its README for provenance.
|
|
101
|
+
|
|
102
|
+
## Intentional behavior
|
|
103
|
+
|
|
104
|
+
### Locales and matching
|
|
105
|
+
|
|
106
|
+
- Translators retain only language and region, including on construction and
|
|
107
|
+
assignment to `locale`. Scripts and Unicode extensions are discarded.
|
|
108
|
+
- Setting `language` preserves the region; setting `region` preserves the
|
|
109
|
+
language. Setting the region to `undefined` removes it.
|
|
110
|
+
- Translation lookup tries the current regional variant, current base language,
|
|
111
|
+
configured default regional variant, and default base language, where present.
|
|
112
|
+
- `Intl.Locale` canonicalizes some ISO language codes to other codes. The
|
|
113
|
+
resulting mismatch for a few languages is an accepted limitation; do not add
|
|
114
|
+
an alias table merely to eliminate it.
|
|
115
|
+
- `LanguageMatcher` is independent of Vue. It normalizes case, strips suffixes
|
|
116
|
+
after `-` or `_`, and chooses the first supported input preference. Its fallback
|
|
117
|
+
is the first configured language, not the first ISO language alphabetically.
|
|
118
|
+
- Matcher inputs accept readonly tuples/arrays. Its non-empty generic tuple
|
|
119
|
+
`T` is preserved as `Readonly<T>` in `availableLanguages`; `defaultLanguage`
|
|
120
|
+
is `T[0]`, and `match()` returns `T[number]`. Construction copies the input.
|
|
121
|
+
|
|
122
|
+
### Messages and updates
|
|
123
|
+
|
|
124
|
+
- A message is a string or a readonly tuple of one to three strings. One variant
|
|
125
|
+
serves all counts; two mean singular/plural (zero uses plural); three mean
|
|
126
|
+
zero/singular/plural. Only counts zero and one receive special treatment.
|
|
127
|
+
- `t()` delegates to `tc()` with count one. `params.n` overrides both the value
|
|
128
|
+
inserted for `{n}` and the count used for variant selection.
|
|
129
|
+
- Strings split at unescaped pipes. Tuples skip pipe splitting and pipe
|
|
130
|
+
unescaping entirely. Tuple inputs are copied, including during updates.
|
|
131
|
+
- Trailing undefined tuple slots are omitted; gaps and invalid tuple lengths
|
|
132
|
+
are rejected. Empty strings fall through during lookup and are ignored by
|
|
133
|
+
updates. Use `['']` for an intentionally blank translation.
|
|
134
|
+
- Placeholders are case-sensitive, trim their names, and support balanced nested
|
|
135
|
+
braces. Missing parameters render as `{name}`. Only own parameter properties
|
|
136
|
+
are substituted; inserted values are never parsed again.
|
|
137
|
+
- Immediately before a pipe or placeholder, each backslash pair produces one
|
|
138
|
+
literal backslash and an odd remainder escapes the pipe or placeholder.
|
|
139
|
+
Tuples apply only placeholder escaping. Final rendered output is trimmed.
|
|
140
|
+
- Parsed templates contain literal strings and `{ param: string }` tokens.
|
|
141
|
+
Cache them independently of parameter values. Caches are scoped to the
|
|
142
|
+
translator's translation map, current language/region, and message key.
|
|
143
|
+
- Updates are validated and copied before stored messages are changed. They
|
|
144
|
+
invalidate parsed caches but intentionally do not trigger Vue reactivity.
|
|
145
|
+
Locale changes are reactive.
|
|
146
|
+
- Preserve `Map` storage for messages and null-prototype format dictionaries:
|
|
147
|
+
keys such as `constructor` and `__proto__` must not resolve inherited entries.
|
|
148
|
+
|
|
149
|
+
### Formatting and reference data
|
|
150
|
+
|
|
151
|
+
- Date time-zone precedence is explicit argument, format option, configured
|
|
152
|
+
default, then runtime default. Do not mutate the caller's options.
|
|
153
|
+
- The date formatter uses `Object.create` with an own `timeZone` descriptor to
|
|
154
|
+
preserve inherited/non-enumerable options and override readonly time zones.
|
|
155
|
+
The changed getter receiver is an accepted edge case for private-field getters.
|
|
156
|
+
- Numeric interpolation uses the current locale and default number format.
|
|
157
|
+
- Country codes include CLDR's `XK` for Kosovo. Keep language/country reference
|
|
158
|
+
names aligned with `test/data/`, rather than independently modernizing them.
|
|
159
|
+
- Currency codes and built-in currency aliases come from the runtime's
|
|
160
|
+
`Intl.supportedValuesOf('currency')`. The static currency union can differ;
|
|
161
|
+
this is intentional. Do not replace runtime discovery with the static table.
|
|
162
|
+
|
|
163
|
+
## Public types and verification
|
|
164
|
+
|
|
165
|
+
`I18nConfiguration` supports declaration merging for language, translation-key,
|
|
166
|
+
date-format, and number-format unions. With a configured subset of languages,
|
|
167
|
+
all base languages are required in each translation; regional entries remain
|
|
168
|
+
optional. Without configuration, base languages are optional and keys/aliases
|
|
169
|
+
accept arbitrary strings. Built-in format aliases remain available when custom
|
|
170
|
+
aliases are configured. Translation updates accept partial messages.
|
|
171
|
+
|
|
172
|
+
Keep configured and unconfigured type tests in separate TypeScript projects:
|
|
173
|
+
declaration merging affects the entire project. Use `expectTypeOf` for inferred
|
|
174
|
+
types and descriptive `@ts-expect-error` assertions for rejected inputs. Vitest
|
|
175
|
+
alone does not enforce those compiler assertions.
|
|
176
|
+
|
|
177
|
+
- `npm run check`: type-check source, runtime tests, Vue templates, and isolated
|
|
178
|
+
type fixtures; run Vitest with coverage; run ESLint.
|
|
179
|
+
- `npm test`: runtime tests and coverage only.
|
|
180
|
+
- `npm run build`: run all checks, then build ESM and bundled declarations into
|
|
181
|
+
`dist/`, with Vue externalized.
|
|
182
|
+
|
|
183
|
+
Add focused regressions for behavioral fixes and public type changes. Prefer
|
|
184
|
+
the full check before completing code changes; for comment-only edits, lint
|
|
185
|
+
and verification that code/types are unchanged are sufficient. Do not edit
|
|
186
|
+
generated `dist/` or `coverage/` files. Match the existing TypeScript style and
|
|
187
|
+
keep README examples and source comments consistent with changed behavior.
|
package/NOTICE.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Copyright 2021-2024 Juit GmbH
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
[http://www.apache.org/licenses/](http://www.apache.org/licenses/)
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|