@konce-pt/i18n 0.15.1 → 0.16.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/dist/messages.js +2 -2
- package/dist/translate.d.ts +4 -1
- package/dist/translate.js +74 -2
- package/package.json +1 -1
package/dist/messages.js
CHANGED
|
@@ -40,7 +40,7 @@ export const KPT_MESSAGES_EN = {
|
|
|
40
40
|
grid: 'Grid',
|
|
41
41
|
columns: 'Columns',
|
|
42
42
|
empty: 'No items',
|
|
43
|
-
count: '{count} items',
|
|
43
|
+
count: '{count, plural, one {# item} other {# items}}',
|
|
44
44
|
},
|
|
45
45
|
roadmap: {
|
|
46
46
|
empty: 'No items on the roadmap',
|
|
@@ -526,7 +526,7 @@ export const KPT_MESSAGES_PL = {
|
|
|
526
526
|
grid: 'Siatka',
|
|
527
527
|
columns: 'Kolumny',
|
|
528
528
|
empty: 'Brak elementów',
|
|
529
|
-
count: '{count} elementów',
|
|
529
|
+
count: '{count, plural, one {# element} few {# elementy} many {# elementów} other {# elementu}}',
|
|
530
530
|
},
|
|
531
531
|
roadmap: {
|
|
532
532
|
empty: 'Brak elementów na roadmapie',
|
package/dist/translate.d.ts
CHANGED
|
@@ -7,5 +7,8 @@ export declare function lookup(dict: KptMessages, key: string): string | undefin
|
|
|
7
7
|
/**
|
|
8
8
|
* Tłumaczy klucz w notacji kropkowej (np. `'paginator.range'`), z opcjonalną
|
|
9
9
|
* interpolacją `{name}`. Brakujący klucz spada na EN, a w ostateczności zwraca sam klucz.
|
|
10
|
+
*
|
|
11
|
+
* Liczbę mnogą zapisuje się blokiem ICU `{count, plural, one {# item} other {# items}}`;
|
|
12
|
+
* `locale` wybiera reguły odmiany (domyślnie `'en'`). Porty przekazują tu bieżący język.
|
|
10
13
|
*/
|
|
11
|
-
export declare function translate(dict: KptMessages, key: string, params?: Record<string, string | number
|
|
14
|
+
export declare function translate(dict: KptMessages, key: string, params?: Record<string, string | number>, locale?: string): string;
|
package/dist/translate.js
CHANGED
|
@@ -33,13 +33,85 @@ export function lookup(dict, key) {
|
|
|
33
33
|
}
|
|
34
34
|
return typeof node === 'string' ? node : undefined;
|
|
35
35
|
}
|
|
36
|
+
/** Pozycja zamykającego `}` dla `{` stojącego na `open`, z liczeniem zagnieżdżeń; -1 bez pary. */
|
|
37
|
+
function closingBrace(text, open) {
|
|
38
|
+
let depth = 0;
|
|
39
|
+
for (let i = open; i < text.length; i++) {
|
|
40
|
+
if (text[i] === '{')
|
|
41
|
+
depth++;
|
|
42
|
+
else if (text[i] === '}' && --depth === 0)
|
|
43
|
+
return i;
|
|
44
|
+
}
|
|
45
|
+
return -1;
|
|
46
|
+
}
|
|
47
|
+
/** Gałęzie bloku liczby mnogiej: `one {...} few {...} =0 {...}` -> mapa selektor -> tekst. */
|
|
48
|
+
function pluralBranches(body) {
|
|
49
|
+
const branches = new Map();
|
|
50
|
+
let i = 0;
|
|
51
|
+
while (i < body.length) {
|
|
52
|
+
const match = /^\s*(=\d+|zero|one|two|few|many|other)\s*\{/.exec(body.slice(i));
|
|
53
|
+
if (!match)
|
|
54
|
+
return body.slice(i).trim() === '' ? branches : null;
|
|
55
|
+
const open = i + match[0].length - 1;
|
|
56
|
+
const close = closingBrace(body, open);
|
|
57
|
+
if (close < 0)
|
|
58
|
+
return null;
|
|
59
|
+
branches.set(match[1], body.slice(open + 1, close));
|
|
60
|
+
i = close + 1;
|
|
61
|
+
}
|
|
62
|
+
return branches;
|
|
63
|
+
}
|
|
64
|
+
/** Reguły liczby mnogiej per język - `Intl.PluralRules` jest drogi w tworzeniu, więc raz na język. */
|
|
65
|
+
const pluralRules = new Map();
|
|
66
|
+
function pluralCategory(locale, value) {
|
|
67
|
+
let rules = pluralRules.get(locale);
|
|
68
|
+
if (!rules) {
|
|
69
|
+
try {
|
|
70
|
+
rules = new Intl.PluralRules(locale);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Nieznany tag języka (własny słownik pod umowną nazwą) - reguły angielskie zamiast wyjątku.
|
|
74
|
+
rules = new Intl.PluralRules('en');
|
|
75
|
+
}
|
|
76
|
+
pluralRules.set(locale, rules);
|
|
77
|
+
}
|
|
78
|
+
return rules.select(value);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Rozwija bloki `{name, plural, one {...} few {...} many {...} other {...}}` (podzbiór składni ICU).
|
|
82
|
+
*
|
|
83
|
+
* Formę wybiera `Intl.PluralRules` dla `locale`, więc słownik podaje tylko formy, a reguły
|
|
84
|
+
* (`2 elementy`, ale `5 elementów` i `22 elementy`) zna przeglądarka. Dokładne dopasowanie `=N`
|
|
85
|
+
* wygrywa z kategorią, `other` jest obowiązkowym zapasem. `#` w gałęzi to sama liczba.
|
|
86
|
+
* Blok z nieznanym parametrem albo bez pary nawiasów zostaje w tekście bez zmian.
|
|
87
|
+
*/
|
|
88
|
+
function expandPlurals(text, params, locale) {
|
|
89
|
+
const head = /\{(\w+),\s*plural,/g;
|
|
90
|
+
let out = '';
|
|
91
|
+
let from = 0;
|
|
92
|
+
for (let match = head.exec(text); match; match = head.exec(text)) {
|
|
93
|
+
const close = closingBrace(text, match.index);
|
|
94
|
+
const value = Number(params[match[1]]);
|
|
95
|
+
const branches = close < 0 ? null : pluralBranches(text.slice(match.index + match[0].length, close));
|
|
96
|
+
if (close < 0 || !branches || params[match[1]] === undefined || Number.isNaN(value))
|
|
97
|
+
continue;
|
|
98
|
+
const chosen = branches.get(`=${value}`) ?? branches.get(pluralCategory(locale, value)) ?? branches.get('other') ?? '';
|
|
99
|
+
out += text.slice(from, match.index) + chosen.replace(/#/g, String(params[match[1]]));
|
|
100
|
+
from = close + 1;
|
|
101
|
+
head.lastIndex = from;
|
|
102
|
+
}
|
|
103
|
+
return out + text.slice(from);
|
|
104
|
+
}
|
|
36
105
|
/**
|
|
37
106
|
* Tłumaczy klucz w notacji kropkowej (np. `'paginator.range'`), z opcjonalną
|
|
38
107
|
* interpolacją `{name}`. Brakujący klucz spada na EN, a w ostateczności zwraca sam klucz.
|
|
108
|
+
*
|
|
109
|
+
* Liczbę mnogą zapisuje się blokiem ICU `{count, plural, one {# item} other {# items}}`;
|
|
110
|
+
* `locale` wybiera reguły odmiany (domyślnie `'en'`). Porty przekazują tu bieżący język.
|
|
39
111
|
*/
|
|
40
|
-
export function translate(dict, key, params) {
|
|
112
|
+
export function translate(dict, key, params, locale = 'en') {
|
|
41
113
|
const raw = lookup(dict, key) ?? lookup(KPT_MESSAGES_EN, key) ?? key;
|
|
42
114
|
if (!params)
|
|
43
115
|
return raw;
|
|
44
|
-
return raw.replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? `{${name}}`));
|
|
116
|
+
return expandPlurals(raw, params, locale).replace(/\{(\w+)\}/g, (_, name) => String(params[name] ?? `{${name}}`));
|
|
45
117
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konce-pt/i18n",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Translation contract and base dictionaries (EN, PL) for Koncept UI - shared by the Angular and React ports. Pure TypeScript, zero runtime dependencies.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "konce.pt",
|