@asafarim/shared-i18n 0.8.1 → 0.9.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.
Files changed (94) hide show
  1. package/README.md +266 -350
  2. package/demo/README.md +119 -148
  3. package/demo/index.html +12 -1
  4. package/demo/node_modules/.bin/kill-port +17 -0
  5. package/demo/node_modules/.bin/tsc +5 -9
  6. package/demo/node_modules/.bin/tsserver +5 -9
  7. package/demo/node_modules/.bin/vite +5 -9
  8. package/demo/package.json +7 -4
  9. package/demo/public/404.html +24 -0
  10. package/demo/public/favicon.svg +4 -4
  11. package/demo/public/logo.svg +24 -24
  12. package/demo/src/App.tsx +178 -129
  13. package/demo/src/components/CountryLanguageSelectorsPage.tsx +240 -0
  14. package/demo/src/components/GetStartedSection.tsx +56 -56
  15. package/demo/src/components/KeyTable.tsx +29 -29
  16. package/demo/src/components/LanguageBar.tsx +145 -103
  17. package/demo/src/components/LanguageSwitcherDemo.module.css +114 -114
  18. package/demo/src/components/LanguageSwitchersPage.tsx +245 -0
  19. package/demo/src/components/Logo.tsx +6 -6
  20. package/demo/src/components/OverviewSection.tsx +58 -43
  21. package/demo/src/components/Panel.tsx +15 -15
  22. package/demo/src/components/RoutingLabPage.tsx +147 -0
  23. package/demo/src/components/StatusCard.tsx +109 -109
  24. package/demo/src/data/countries.ts +48 -0
  25. package/demo/src/i18n/localeAdapter.ts +91 -0
  26. package/demo/src/i18n/localeRouting.ts +77 -0
  27. package/demo/src/index.css +1075 -644
  28. package/demo/src/locales/de/demo.json +202 -84
  29. package/demo/src/locales/en/demo.json +201 -85
  30. package/demo/src/locales/fr/demo.json +203 -85
  31. package/demo/src/locales/it/demo.json +202 -84
  32. package/demo/src/locales/lb/demo.json +201 -0
  33. package/demo/src/locales/nl/demo.json +203 -85
  34. package/demo/src/main.tsx +32 -29
  35. package/demo/tsconfig.json +18 -18
  36. package/demo/tsconfig.node.json +10 -10
  37. package/demo/tsconfig.tsbuildinfo +1 -1
  38. package/demo/vite-env.d.ts +7 -7
  39. package/demo/vite.config.d.ts +2 -2
  40. package/demo/vite.config.js +10 -10
  41. package/dist/components/LanguageSwitcher.module.css +303 -303
  42. package/dist/country-language-selector.css +431 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +2 -0
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/package.json +8 -5
  48. package/demo/dist/Icon Dropdown_Limited Languages.png +0 -0
  49. package/demo/dist/Select Dropdown_Text Only.png +0 -0
  50. package/demo/dist/assets/favicon-BZYZvBLo.svg +0 -4
  51. package/demo/dist/assets/index-BdjqKw_N.css +0 -1
  52. package/demo/dist/assets/index-C1Tq1uEr.js +0 -191
  53. package/demo/dist/favicon.svg +0 -4
  54. package/demo/dist/index.html +0 -27
  55. package/demo/dist/logo.svg +0 -24
  56. package/demo/node_modules/.bin/browserslist +0 -21
  57. package/demo/node_modules/.bin/browserslist.CMD +0 -12
  58. package/demo/node_modules/.bin/browserslist.ps1 +0 -41
  59. package/demo/node_modules/.bin/tsc.CMD +0 -12
  60. package/demo/node_modules/.bin/tsc.ps1 +0 -41
  61. package/demo/node_modules/.bin/tsserver.CMD +0 -12
  62. package/demo/node_modules/.bin/tsserver.ps1 +0 -41
  63. package/demo/node_modules/.bin/vite.CMD +0 -12
  64. package/demo/node_modules/.bin/vite.ps1 +0 -41
  65. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js +0 -848
  66. package/demo/node_modules/.vite/deps/@asafarim_country-language-selector.js.map +0 -7
  67. package/demo/node_modules/.vite/deps/_metadata.json +0 -76
  68. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js +0 -30
  69. package/demo/node_modules/.vite/deps/chunk-5WRI5ZAA.js.map +0 -7
  70. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js +0 -1004
  71. package/demo/node_modules/.vite/deps/chunk-B3AHR5EX.js.map +0 -7
  72. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js +0 -292
  73. package/demo/node_modules/.vite/deps/chunk-E6BG6WAU.js.map +0 -7
  74. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js +0 -280
  75. package/demo/node_modules/.vite/deps/chunk-MVARZQEG.js.map +0 -7
  76. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js +0 -400
  77. package/demo/node_modules/.vite/deps/i18next-browser-languagedetector.js.map +0 -7
  78. package/demo/node_modules/.vite/deps/i18next.js +0 -2392
  79. package/demo/node_modules/.vite/deps/i18next.js.map +0 -7
  80. package/demo/node_modules/.vite/deps/package.json +0 -3
  81. package/demo/node_modules/.vite/deps/react-dom.js +0 -6
  82. package/demo/node_modules/.vite/deps/react-dom.js.map +0 -7
  83. package/demo/node_modules/.vite/deps/react-dom_client.js +0 -20217
  84. package/demo/node_modules/.vite/deps/react-dom_client.js.map +0 -7
  85. package/demo/node_modules/.vite/deps/react-i18next.js +0 -869
  86. package/demo/node_modules/.vite/deps/react-i18next.js.map +0 -7
  87. package/demo/node_modules/.vite/deps/react.js +0 -5
  88. package/demo/node_modules/.vite/deps/react.js.map +0 -7
  89. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js +0 -278
  90. package/demo/node_modules/.vite/deps/react_jsx-dev-runtime.js.map +0 -7
  91. package/demo/node_modules/.vite/deps/react_jsx-runtime.js +0 -6
  92. package/demo/node_modules/.vite/deps/react_jsx-runtime.js.map +0 -7
  93. package/demo/src/components/CountryLanguageDemo.tsx +0 -140
  94. package/demo/src/components/LanguageSwitcherDemo.tsx +0 -256
@@ -1,85 +1,203 @@
1
- {
2
- "title": "Shared i18n Demo",
3
- "subtitle": "Ein leichtgewichtiger React i18next Wrapper mit Cookie-Persistenz.",
4
- "cta": "Sprache wechseln",
5
- "overview": {
6
- "heading": "Übersicht",
7
- "description": "@asafarim/shared-i18n ist ein produktionsreifes Internationalisierungspaket (i18n) für React-Anwendungen.",
8
- "features": {
9
- "title": "Hauptmerkmale",
10
- "items": [
11
- "Mehrsprachiger Support mit Cookie-Persistenz",
12
- "Eingebaute Spracherkennung und Fallback",
13
- "TypeScript-Support mit vollständiger Typsicherheit",
14
- "Vorkonfiguriert mit gemeinsamen und Identity-Portal-Namespaces",
15
- "Nahtlose Integration mit react-i18next",
16
- "Backend-Sprachpräferenz-Synchronisation",
17
- "SSR-sichere Dienstprogramme für serverseitiges Rendering"
18
- ]
19
- },
20
- "useCases": {
21
- "title": "Perfekt für",
22
- "items": [
23
- "Globale Anwendungen, die mehrsprachigen Support benötigen",
24
- "Enterprise-Plattformen mit Benutzer-Sprachpräferenzen",
25
- "Identitäts- und Authentifizierungssysteme",
26
- "Content-Management-Systeme mit Lokalisierung",
27
- "SaaS-Anwendungen, die internationale Benutzer bedienen"
28
- ]
29
- }
30
- },
31
- "getStarted": {
32
- "heading": "Erste Schritte",
33
- "intro": "Erfahren Sie, wie Sie @asafarim/shared-i18n in wenigen Minuten in Ihre React-Anwendung integrieren.",
34
- "steps": [
35
- {
36
- "title": "1. Paket installieren",
37
- "description": "Fügen Sie das Paket zu Ihren Projektabhängigkeiten hinzu.",
38
- "code": "pnpm add @asafarim/shared-i18n"
39
- },
40
- {
41
- "title": "2. i18n in Ihrer App initialisieren",
42
- "description": "Rufen Sie initI18n beim Anwendungsstart auf, um Spracheinstellungen zu konfigurieren.",
43
- "code": "import { initI18n } from '@asafarim/shared-i18n';\n\ninitI18n({\n defaultNS: 'common',\n ns: ['common', 'identityPortal'],\n resources: { /* your translations */ }\n});"
44
- },
45
- {
46
- "title": "3. Den useLanguage Hook verwenden",
47
- "description": "Greifen Sie auf Sprachwechsel und aktuellen Sprachstatus in Ihren Komponenten zu.",
48
- "code": "const { changeLanguage, isChanging } = useLanguage();\n\n<button onClick={() => changeLanguage('nl')}>\n Zu Niederländisch wechseln\n</button>"
49
- },
50
- {
51
- "title": "4. Mit useTranslation übersetzen",
52
- "description": "Verwenden Sie den useTranslation Hook, um auf übersetzte Zeichenfolgen zuzugreifen.",
53
- "code": "const { t } = useTranslation('common');\n\n<h1>{t('welcome')}</h1>"
54
- },
55
- {
56
- "title": "5. Cookie-Persistenz nutzen",
57
- "description": "Benutzer-Sprachpräferenzen werden automatisch gespeichert und wiederhergestellt.",
58
- "code": "// Automatisch über Cookies persistiert\n// Keine zusätzliche Einrichtung erforderlich!"
59
- },
60
- {
61
- "title": "6. Mit Backend synchronisieren (Optional)",
62
- "description": "Aktualisieren Sie Benutzer-Sprachpräferenzen auf Ihrem Backend.",
63
- "code": "import { updateUserLanguagePreference } from '@asafarim/shared-i18n';\n\nawait updateUserLanguagePreference('en');"
64
- }
65
- ],
66
- "tips": {
67
- "title": "Pro-Tipps",
68
- "items": [
69
- "Verwenden Sie Namespaces, um Übersetzungen nach Funktionen zu organisieren",
70
- "Nutzen Sie die Trans-Komponente für komplexe Übersetzungen mit Variablen",
71
- "Setzen Sie VITE_IDENTITY_API_URL für Backend-Synchronisation",
72
- "Überprüfen Sie die Browser-Spracherkennung mit getInitialLanguage()",
73
- "Kombinieren Sie mit Design-Tokens für konsistente Themen über Sprachen hinweg"
74
- ]
75
- }
76
- },
77
- "status": {
78
- "heading": "Live-Status",
79
- "cookie": "Cookie",
80
- "backend": "Backend",
81
- "simulate": "Backend-Synchronisation simulieren",
82
- "resultOk": "Backend-Synchronisation erfolgreich",
83
- "resultFail": "Backend-Synchronisation fehlgeschlagen"
84
- }
1
+ {
2
+ "title": "Shared i18n Demo",
3
+ "subtitle": "Ein leichtgewichtiger React i18next Wrapper mit Cookie-Persistenz.",
4
+ "cta": "Sprache wechseln",
5
+ "overview": {
6
+ "heading": "Übersicht",
7
+ "description": "@asafarim/shared-i18n ist ein produktionsreifes Internationalisierungspaket (i18n) für React-Anwendungen.",
8
+ "features": {
9
+ "title": "Hauptmerkmale",
10
+ "items": [
11
+ "Mehrsprachiger Support mit Cookie-Persistenz",
12
+ "Eingebaute Spracherkennung und Fallback",
13
+ "TypeScript-Support mit vollständiger Typsicherheit",
14
+ "Vorkonfiguriert mit gemeinsamen und Identity-Portal-Namespaces",
15
+ "Nahtlose Integration mit react-i18next",
16
+ "Backend-Sprachpräferenz-Synchronisation",
17
+ "SSR-sichere Dienstprogramme für serverseitiges Rendering"
18
+ ]
19
+ },
20
+ "selectors": {
21
+ "title": "CountryLanguageSelector",
22
+ "items": [
23
+ "Enthält sowohl Land ALS auch Sprache: { country: 'BE', language: 'en' }",
24
+ "Kann be-en von nl-en, lu-en, gb-en unterscheiden",
25
+ "Bildflaggen (flagcdn.com) oder Emoji-Flaggen",
26
+ "Kompakte, vollständige, nur-Flagge und benutzerdefinierte Trigger-Varianten",
27
+ "Beste für lokalisierte URLs wie /be-en/get-started"
28
+ ]
29
+ },
30
+ "useCases": {
31
+ "title": "Perfekt für",
32
+ "items": [
33
+ "Globale Anwendungen, die mehrsprachigen Support benötigen",
34
+ "Enterprise-Plattformen mit Benutzer-Sprachpräferenzen",
35
+ "Identitäts- und Authentifizierungssysteme",
36
+ "Content-Management-Systeme mit Lokalisierung",
37
+ "SaaS-Anwendungen, die internationale Benutzer bedienen"
38
+ ]
39
+ }
40
+ },
41
+ "getStarted": {
42
+ "heading": "Erste Schritte",
43
+ "intro": "Erfahren Sie, wie Sie @asafarim/shared-i18n in wenigen Minuten in Ihre React-Anwendung integrieren.",
44
+ "steps": [
45
+ {
46
+ "title": "1. Paket installieren",
47
+ "description": "Fügen Sie das Paket zu Ihren Projektabhängigkeiten hinzu.",
48
+ "code": "pnpm add @asafarim/shared-i18n"
49
+ },
50
+ {
51
+ "title": "2. i18n in Ihrer App initialisieren",
52
+ "description": "Rufen Sie initI18n beim Anwendungsstart auf, um Spracheinstellungen zu konfigurieren.",
53
+ "code": "import { initI18n } from '@asafarim/shared-i18n';\n\ninitI18n({\n defaultNS: 'common',\n ns: ['common', 'identityPortal'],\n resources: { /* your translations */ }\n});"
54
+ },
55
+ {
56
+ "title": "3. Den useLanguage Hook verwenden",
57
+ "description": "Greifen Sie auf Sprachwechsel und aktuellen Sprachstatus in Ihren Komponenten zu.",
58
+ "code": "const { changeLanguage, isChanging } = useLanguage();\n\n<button onClick={() => changeLanguage('nl')}>\n Zu Niederländisch wechseln\n</button>"
59
+ },
60
+ {
61
+ "title": "4. Mit useTranslation übersetzen",
62
+ "description": "Verwenden Sie den useTranslation Hook, um auf übersetzte Zeichenfolgen zuzugreifen.",
63
+ "code": "const { t } = useTranslation('common');\n\n<h1>{t('welcome')}</h1>"
64
+ },
65
+ {
66
+ "title": "5. Cookie-Persistenz nutzen",
67
+ "description": "Benutzer-Sprachpräferenzen werden automatisch gespeichert und wiederhergestellt.",
68
+ "code": "// Automatisch über Cookies persistiert\n// Keine zusätzliche Einrichtung erforderlich!"
69
+ },
70
+ {
71
+ "title": "6. Mit Backend synchronisieren (Optional)",
72
+ "description": "Aktualisieren Sie Benutzer-Sprachpräferenzen auf Ihrem Backend.",
73
+ "code": "import { updateUserLanguagePreference } from '@asafarim/shared-i18n';\n\nawait updateUserLanguagePreference('en');"
74
+ }
75
+ ],
76
+ "tips": {
77
+ "title": "Pro-Tipps",
78
+ "items": [
79
+ "Verwenden Sie Namespaces, um Übersetzungen nach Funktionen zu organisieren",
80
+ "Nutzen Sie die Trans-Komponente für komplexe Übersetzungen mit Variablen",
81
+ "Setzen Sie VITE_IDENTITY_API_URL für Backend-Synchronisation",
82
+ "Überprüfen Sie die Browser-Spracherkennung mit getInitialLanguage()",
83
+ "Kombinieren Sie mit Design-Tokens für konsistente Themen über Sprachen hinweg"
84
+ ]
85
+ }
86
+ },
87
+ "routingContract": {
88
+ "heading": "Routing-Vertrag",
89
+ "description": "Die URL ist die Quelle der Wahrheit für sowohl Locale als auch aktive Seite.",
90
+ "items": [
91
+ "{base} leitet weiter zu {target}",
92
+ "{pattern} steuert die UI",
93
+ "CountryLanguageSelector Änderung → vollständige Locale-Aktualisierung",
94
+ "LanguageSwitcher Änderung → Sprache aufgelöst via resolveLocaleFromLanguage",
95
+ "Ungültiger Slug → {defaultSlug}; ungültige Seite → {defaultPage}"
96
+ ]
97
+ },
98
+ "languageSwitchers": {
99
+ "heading": "Sprachwechsler",
100
+ "intro": "LanguageSwitcher ist sprache-nur — es weiß nicht, in welchem Land sich der Benutzer befindet. Jede Variante unten ist über resolveLocaleFromLanguage verbunden, sodass die Auswahl einer Sprache immer noch eine gültige Locale-URL erzeugt.",
101
+ "distinction": {
102
+ "title": "Warum LanguageSwitcher CountryLanguageSelector nicht ersetzen kann",
103
+ "p1": "Diese vier Locale-Slugs tragen dieselbe Sprache (Englisch) aber unterschiedliche Länder. LanguageSwitcher kann sie nicht unterscheiden:",
104
+ "p2": "CountryLanguageSelector trägt sowohl Land als auch Sprache, sodass es alle vier darstellen und unterscheiden kann.",
105
+ "link": "Siehe den Country Language Selectors Tab dafür."
106
+ },
107
+ "comparison": {
108
+ "title": "Vergleich",
109
+ "capability": "Fähigkeit",
110
+ "ls": "LanguageSwitcher",
111
+ "cls": "CountryLanguageSelector",
112
+ "yes": "Ja",
113
+ "no": "Nein",
114
+ "needsAdapter": "Nein — braucht Adapter",
115
+ "optional": "Optional",
116
+ "yesVia": "Ja, via locale.language",
117
+ "changesLang": "Ändert i18n Sprache",
118
+ "knowsCountry": "Kennt Land",
119
+ "representsBeEn": "Kann be-en darstellen",
120
+ "distinguishes": "Kann be-en von gb-en unterscheiden",
121
+ "bestUrls": "Beste für lokalisierte URLs",
122
+ "bestTransOnly": "Beste für übersetzung-nur Apps"
123
+ },
124
+ "variants": [
125
+ { "title": "Knopf-Variante", "desc": "Einzelne Knöpfe für jede Sprache. Feuert onChanged mit einem Sprachcode." },
126
+ { "title": "Select dropdown — nur Text", "desc": "Native <select> mit Sprachnamen. Kein Emoji." },
127
+ { "title": "Icon dropdown — alle Sprachen", "desc": "Kompakte Flagge-Emoji Dropdown zeigt alle unterstützten Sprachen." },
128
+ { "title": "Icon dropdown — eingeschränkte Sprachen", "desc": "Eingeschränkt auf nur Englisch und Niederländisch. Nützlich um den Demo-Länderumfang zu matchen." },
129
+ { "title": "Icon dropdown — mit Labels", "desc": "Gleich wie oben aber zeigt Sprachnamen neben dem Flagge-Emoji." },
130
+ { "title": "Toggle-Knopf (2 Sprachen)", "desc": "Wenn genau 2 Sprachen angegeben und isToggler=true, rendert einen einzelnen Toggle-Knopf." }
131
+ ],
132
+ "note": "Diese Steuerung ändert nur die Sprache. Die Demo mapped die ausgewählte Sprache zurück zu einer vollständigen Locale via resolveLocaleFromLanguage, sodass die URL gültig bleibt.",
133
+ "preview": "Vorschau",
134
+ "code": "Code"
135
+ },
136
+ "routingLab": {
137
+ "heading": "Routing-Labor",
138
+ "intro": "Vergleich nebeneinander wie sich LanguageSwitcher und CountryLanguageSelector beim Ändern der Locale verhalten. Die URL wird live aktualisiert.",
139
+ "currentLocale": "Aktuelle Locale",
140
+ "slug": "Slug",
141
+ "urlPath": "URL-Pfad",
142
+ "langOnly": {
143
+ "title": "Sprache-nur Aktion",
144
+ "desc": "LanguageSwitcher feuert einen Sprachcode ab. Die Demo nutzt resolveLocaleFromLanguage, um ihn auf eine gültige vollständige Locale zu mappen. Wenn das aktuelle Land die Sprache nicht unterstützt, wird ein Fallback-Hinweis angezeigt.",
145
+ "resolved": "Aufgelöste Locale:"
146
+ },
147
+ "countryLang": {
148
+ "title": "Land-Sprache Aktion",
149
+ "desc": "CountryLanguageSelector feuert direkt ein vollständiges { country, language } Objekt ab — kein Adapter nötig. Die URL hat immer das richtige Land + Sprache.",
150
+ "resolved": "Aufgelöste Locale:"
151
+ },
152
+ "scenarios": {
153
+ "title": "Skriptierte Szenarien",
154
+ "desc": "Klicken Sie auf eine Zeile, um das Szenario anzuwenden und beobachten Sie die Aktualisierung der URL und des Locale-Chips.",
155
+ "expected": "Erwartet:"
156
+ }
157
+ },
158
+ "status": {
159
+ "heading": "Live-Status",
160
+ "cookie": "Cookie",
161
+ "backend": "Backend",
162
+ "simulate": "Backend-Synchronisation simulieren",
163
+ "resultOk": "Backend-Synchronisation erfolgreich",
164
+ "resultFail": "Backend-Synchronisation fehlgeschlagen"
165
+ },
166
+ "nav": {
167
+ "overview": "Übersicht",
168
+ "getStarted": "Erste Schritte",
169
+ "languageSwitchers": "Sprachwechsler",
170
+ "countrySelectors": "Land Selektoren",
171
+ "routingLab": "Routing-Labor",
172
+ "tagline": "Benelux + UK Locale Routing Demo"
173
+ },
174
+ "footer": {
175
+ "activeRoute": "Aktive Route",
176
+ "builtWith": "Gebaut mit"
177
+ },
178
+ "hero": {
179
+ "getStarted": "Erste Schritte",
180
+ "trySelector": "Selektor ausprobieren",
181
+ "liveRoutePreview": "Live Route Vorschau"
182
+ },
183
+ "countryLanguageSelectors": {
184
+ "heading": "Land Sprach Selektoren",
185
+ "intro": "CountryLanguageSelector trägt sowohl Land als auch Sprache, was es zur richtigen Wahl für lokalisierte URLs macht. Alle Varianten unten teilen dieselbe kontrollierte Locale — das Ändern einer aktualisiert die URL und beide Navbar-Selektoren.",
186
+ "country": "Land",
187
+ "language": "Sprache",
188
+ "slug": "Slug",
189
+ "url": "URL",
190
+ "preview": "Vorschau",
191
+ "code": "Code",
192
+ "variants": [
193
+ { "title": "Bildflaggen · kompakter Trigger", "desc": "Verwendet SVG-Flaggen von flagcdn.com — funktioniert auf Windows, wo Emoji-Flaggen als ISO-Codes angezeigt werden." },
194
+ { "title": "Deaktivierter Zustand", "desc": "Der Selektor kann während des Ladens oder wenn der Benutzer keine Berechtigung zum Ändern der Locale hat, deaktiviert werden." },
195
+ { "title": "Vollständige Namen · Bildflaggen", "desc": "Zeigt die vollständigen Landes- und Sprachnamen an — nützlich, wenn der Platz nicht begrenzt ist." },
196
+ { "title": "Nur-Flagge Trigger", "desc": "Nur die Flagge wird im Trigger angezeigt — ideal für sehr kompakte Navbars." },
197
+ { "title": "Benutzerdefinierter Trigger via renderTrigger", "desc": "Volle Kontrolle über den Trigger-Button via renderTrigger render prop. Verwendet ein Inline-Flaggen-Bild, damit es auf Windows funktioniert." },
198
+ { "title": "Popover am Anfang ausgerichtet", "desc": "Richten Sie die Dropdown-Popover am linken Rand des Triggers via align='start' aus." }
199
+ ],
200
+ "open": "Öffnen",
201
+ "close": "Schließen"
202
+ }
85
203
  }
@@ -1,85 +1,201 @@
1
- {
2
- "title": "Shared i18n demo",
3
- "subtitle": "A lightweight React i18next wrapper with cookie persistence.",
4
- "cta": "Switch language",
5
- "overview": {
6
- "heading": "Overview",
7
- "description": "@asafarim/shared-i18n is a production-grade internationalization (i18n) package for React applications.",
8
- "features": {
9
- "title": "Key Features",
10
- "items": [
11
- "Multi-language support with cookie persistence",
12
- "Built-in language detection and fallback",
13
- "TypeScript support with full type safety",
14
- "Pre-configured with common and identity-portal namespaces",
15
- "Seamless integration with react-i18next",
16
- "Backend language preference synchronization",
17
- "SSR-safe utilities for server-side rendering"
18
- ]
19
- },
20
- "useCases": {
21
- "title": "Perfect For",
22
- "items": [
23
- "Global applications requiring multi-language support",
24
- "Enterprise platforms with user language preferences",
25
- "Identity and authentication systems",
26
- "Content management systems with localization",
27
- "SaaS applications serving international users"
28
- ]
29
- }
30
- },
31
- "getStarted": {
32
- "heading": "Get Started",
33
- "intro": "Learn how to integrate @asafarim/shared-i18n into your React application in minutes.",
34
- "steps": [
35
- {
36
- "title": "1. Install the Package",
37
- "description": "Add the package to your project dependencies.",
38
- "code": "pnpm add @asafarim/shared-i18n"
39
- },
40
- {
41
- "title": "2. Initialize i18n in Your App",
42
- "description": "Call initI18n at application startup to configure language settings.",
43
- "code": "import { initI18n } from '@asafarim/shared-i18n';\n\ninitI18n({\n defaultNS: 'common',\n ns: ['common', 'identityPortal'],\n resources: { /* your translations */ }\n});"
44
- },
45
- {
46
- "title": "3. Use the useLanguage Hook",
47
- "description": "Access language switching and current language state in your components.",
48
- "code": "const { changeLanguage, isChanging } = useLanguage();\n\n<button onClick={() => changeLanguage('nl')}>\n Switch to Dutch\n</button>"
49
- },
50
- {
51
- "title": "4. Translate with useTranslation",
52
- "description": "Use the useTranslation hook to access translated strings.",
53
- "code": "const { t } = useTranslation('common');\n\n<h1>{t('welcome')}</h1>"
54
- },
55
- {
56
- "title": "5. Leverage Cookie Persistence",
57
- "description": "User language preferences are automatically saved and restored.",
58
- "code": "// Automatically persisted via cookies\n// No additional setup required!"
59
- },
60
- {
61
- "title": "6. Sync with Backend (Optional)",
62
- "description": "Update user language preferences on your backend.",
63
- "code": "import { updateUserLanguagePreference } from '@asafarim/shared-i18n';\n\nawait updateUserLanguagePreference('en');"
64
- }
65
- ],
66
- "tips": {
67
- "title": "Pro Tips",
68
- "items": [
69
- "Use namespaces to organize translations by feature",
70
- "Leverage the Trans component for complex translations with variables",
71
- "Set VITE_IDENTITY_API_URL for backend synchronization",
72
- "Check browser language detection with getInitialLanguage()",
73
- "Combine with design tokens for consistent theming across languages"
74
- ]
75
- }
76
- },
77
- "status": {
78
- "heading": "Live status",
79
- "cookie": "Cookie",
80
- "backend": "Backend",
81
- "simulate": "Simulate backend sync",
82
- "resultOk": "Backend sync succeeded",
83
- "resultFail": "Backend sync failed"
84
- }
85
- }
1
+ {
2
+ "title": "Shared i18n demo",
3
+ "subtitle": "A lightweight React i18next wrapper with cookie persistence.",
4
+ "cta": "Switch language",
5
+ "overview": {
6
+ "heading": "Overview",
7
+ "description": "@asafarim/shared-i18n ships both a LanguageSwitcher (language-only) and CountryLanguageSelector (country + language) from a single install.",
8
+ "features": {
9
+ "title": "LanguageSwitcher",
10
+ "items": [
11
+ "Changes i18n translation language (en, nl, fr, de …)",
12
+ "Three variants: buttons, select dropdown, icon-dropdown",
13
+ "Cookie-based persistence and automatic browser detection",
14
+ "Use resolveLocaleFromLanguage adapter for URL-safe routing",
15
+ "Best for apps where country / market does not matter"
16
+ ]
17
+ },
18
+ "selectors": {
19
+ "title": "CountryLanguageSelector",
20
+ "items": [
21
+ "Carries both country AND language: { country: 'BE', language: 'en' }",
22
+ "Can distinguish be-en from nl-en, lu-en, gb-en",
23
+ "Image flags (flagcdn.com) or emoji flags",
24
+ "Compact, full, flag-only, and custom trigger variants",
25
+ "Best for localized URLs like /be-en/get-started"
26
+ ]
27
+ },
28
+ "useCases": {
29
+ "title": "Perfect For",
30
+ "items": [
31
+ "Locale-aware URL routing (Benelux, EU, global)",
32
+ "Enterprise platforms with country + language preferences",
33
+ "Apps that need to distinguish be-en from gb-en",
34
+ "Identity and authentication systems with per-locale content",
35
+ "SaaS applications serving international markets"
36
+ ]
37
+ }
38
+ },
39
+ "getStarted": {
40
+ "heading": "Get Started",
41
+ "intro": "Everything you need LanguageSwitcher and CountryLanguageSelector — comes from one package.",
42
+ "steps": [
43
+ {
44
+ "title": "1. Install",
45
+ "description": "One package gives you both the language-only switcher and the country+language selector.",
46
+ "code": "pnpm add @asafarim/shared-i18n\n# or: npm i @asafarim/shared-i18n"
47
+ },
48
+ {
49
+ "title": "2. Import the selector stylesheet",
50
+ "description": "Add the CountryLanguageSelector CSS once in your entry point.",
51
+ "code": "// main.tsx\nimport '@asafarim/shared-i18n/country-language-selector.css';"
52
+ },
53
+ {
54
+ "title": "3. Initialize i18n",
55
+ "description": "Call initI18n at application startup with your translation resources.",
56
+ "code": "import { initI18n } from '@asafarim/shared-i18n';\n\ninitI18n({\n defaultLanguage: 'en',\n defaultNS: 'common',\n ns: ['common', 'app'],\n resources: {\n en: { app: enApp },\n nl: { app: nlApp }\n }\n});"
57
+ },
58
+ {
59
+ "title": "4. Add CountryLanguageSelector",
60
+ "description": "Import from @asafarim/shared-i18n — no separate package required.",
61
+ "code": "import {\n CountryLanguageSelector,\n type Country,\n type Locale,\n} from '@asafarim/shared-i18n';\n\n<CountryLanguageSelector\n countries={countries}\n value={locale}\n onChange={(locale) => {\n i18n.changeLanguage(locale.language);\n navigate(`/${locale.country.toLowerCase()}-${locale.language}/`);\n }}\n triggerVariant=\"compact\"\n flagMode=\"image\"\n/>"
62
+ },
63
+ {
64
+ "title": "5. Add LanguageSwitcher",
65
+ "description": "For translation-only apps. Wire through resolveLocaleFromLanguage when you also have locale URLs.",
66
+ "code": "import { LanguageSwitcher } from '@asafarim/shared-i18n';\n\n<LanguageSwitcher\n variant=\"buttons\"\n onChanged={(lang) => {\n // map language → full locale for URL-safe routing:\n const { locale } = resolveLocaleFromLanguage(currentLocale, lang, countries);\n navigate(locale);\n }}\n/>"
67
+ },
68
+ {
69
+ "title": "6. GitHub Pages SPA fallback (optional)",
70
+ "description": "For static hosting on GitHub Pages, add a 404.html redirect strategy.",
71
+ "code": "// public/404.html — redirects deep links via query string\n// index.html — decodes them back via history.replaceState\n// See the demo source for the full implementation."
72
+ }
73
+ ],
74
+ "tips": {
75
+ "title": "Pro Tips",
76
+ "items": [
77
+ "Use CountryLanguageSelector when your app has locale-aware URLs",
78
+ "Use LanguageSwitcher + resolveLocaleFromLanguage as a lightweight adapter layer",
79
+ "Both components are controlled — pass value={locale} and onChange to stay in sync",
80
+ "i18next stores language-only; the URL stores country + language",
81
+ "Set VITE_IDENTITY_API_URL for backend language-preference synchronization"
82
+ ]
83
+ }
84
+ },
85
+ "routingContract": {
86
+ "heading": "Routing contract",
87
+ "description": "The URL is the source of truth for both locale and active page.",
88
+ "items": [
89
+ "{base} redirects to {target}",
90
+ "{pattern} drives the UI",
91
+ "CountryLanguageSelector change → full locale update",
92
+ "LanguageSwitcher change → language resolved via resolveLocaleFromLanguage",
93
+ "Invalid slug → {defaultSlug}; invalid page → {defaultPage}"
94
+ ]
95
+ },
96
+ "languageSwitchers": {
97
+ "heading": "Language Switchers",
98
+ "intro": "LanguageSwitcher is language-only — it does not know which country the user is in. Each variant below is wired through resolveLocaleFromLanguage so selecting a language still produces a valid locale URL.",
99
+ "distinction": {
100
+ "title": "Why LanguageSwitcher cannot replace CountryLanguageSelector",
101
+ "p1": "All four of these locale slugs carry the same language (English) but different countries. LanguageSwitcher cannot distinguish them:",
102
+ "p2": "CountryLanguageSelector carries both country and language, so it can represent and distinguish all four.",
103
+ "link": "See the Country Language Selectors tab for that."
104
+ },
105
+ "comparison": {
106
+ "title": "Comparison",
107
+ "capability": "Capability",
108
+ "ls": "LanguageSwitcher",
109
+ "cls": "CountryLanguageSelector",
110
+ "yes": "Yes",
111
+ "no": "No",
112
+ "needsAdapter": "No — needs adapter",
113
+ "optional": "Optional",
114
+ "yesVia": "Yes, via locale.language",
115
+ "changesLang": "Changes i18n language",
116
+ "knowsCountry": "Knows country",
117
+ "representsBeEn": "Can represent be-en",
118
+ "distinguishes": "Can distinguish be-en from gb-en",
119
+ "bestUrls": "Best for localized URLs",
120
+ "bestTransOnly": "Best for translation-only apps"
121
+ },
122
+ "variants": [
123
+ { "title": "Buttons variant", "desc": "Individual buttons for each language. Fires onChanged with a language code." },
124
+ { "title": "Select dropdown — text only", "desc": "Native <select> with language names. No emoji." },
125
+ { "title": "Icon dropdown — all languages", "desc": "Compact flag-emoji dropdown showing all supported languages." },
126
+ { "title": "Icon dropdown — limited languages", "desc": "Restricted to English and Dutch only. Useful to match the demo country scope." },
127
+ { "title": "Icon dropdown — with labels", "desc": "Same as above but shows language names alongside the flag emoji." },
128
+ { "title": "Toggle button (2 languages)", "desc": "When exactly 2 languages are provided and isToggler=true, renders a single toggle button." }
129
+ ],
130
+ "note": "This control changes language only. The demo maps the selected language back to a full locale via resolveLocaleFromLanguage so the URL stays valid.",
131
+ "preview": "Preview",
132
+ "code": "Code"
133
+ },
134
+ "routingLab": {
135
+ "heading": "Routing Lab",
136
+ "intro": "Side-by-side comparison of how LanguageSwitcher and CountryLanguageSelector behave when changing locale. The URL updates live.",
137
+ "currentLocale": "Current locale",
138
+ "slug": "Slug",
139
+ "urlPath": "URL path",
140
+ "langOnly": {
141
+ "title": "Language-only action",
142
+ "desc": "LanguageSwitcher fires a language code. The demo uses resolveLocaleFromLanguage to map it to a valid full locale. If the current country does not support the language, a fallback notice is shown.",
143
+ "resolved": "Resolved locale:"
144
+ },
145
+ "countryLang": {
146
+ "title": "Country-language action",
147
+ "desc": "CountryLanguageSelector fires a full { country, language } object directly — no adapter needed. The URL always has the right country + language.",
148
+ "resolved": "Resolved locale:"
149
+ },
150
+ "scenarios": {
151
+ "title": "Scripted scenarios",
152
+ "desc": "Click a row to apply the scenario and watch the URL and locale chip update.",
153
+ "expected": "Expected:"
154
+ }
155
+ },
156
+ "status": {
157
+ "heading": "Live status",
158
+ "cookie": "Cookie",
159
+ "backend": "Backend",
160
+ "simulate": "Simulate backend sync",
161
+ "resultOk": "Backend sync succeeded",
162
+ "resultFail": "Backend sync failed"
163
+ },
164
+ "nav": {
165
+ "overview": "Overview",
166
+ "getStarted": "Get Started",
167
+ "languageSwitchers": "Language Switchers",
168
+ "countrySelectors": "Country Selectors",
169
+ "routingLab": "Routing Lab",
170
+ "tagline": "Benelux + UK locale routing demo"
171
+ },
172
+ "footer": {
173
+ "activeRoute": "Active route",
174
+ "builtWith": "Built with"
175
+ },
176
+ "hero": {
177
+ "getStarted": "Get started",
178
+ "trySelector": "Try the selector",
179
+ "liveRoutePreview": "Live route preview"
180
+ },
181
+ "countryLanguageSelectors": {
182
+ "heading": "Country Language Selectors",
183
+ "intro": "CountryLanguageSelector carries both country and language, making it the right choice for locale-aware URLs. All variants below share the same controlled locale — changing any one updates the URL and both navbar selectors.",
184
+ "country": "Country",
185
+ "language": "Language",
186
+ "slug": "Slug",
187
+ "url": "URL",
188
+ "preview": "Preview",
189
+ "code": "Code",
190
+ "variants": [
191
+ { "title": "Image flags · compact trigger", "desc": "Uses SVG flags from flagcdn.com — works on Windows where emoji flags render as ISO codes." },
192
+ { "title": "Disabled state", "desc": "The selector can be disabled while loading or when the user lacks permission to change locale." },
193
+ { "title": "Full names · image flags", "desc": "Shows the full country and language names — useful when space is not constrained." },
194
+ { "title": "Flag-only trigger", "desc": "Only the flag is shown in the trigger — ideal for very tight navbars." },
195
+ { "title": "Custom trigger via renderTrigger", "desc": "Full control over the trigger button via the renderTrigger render prop. Uses an inline flag image so it works on Windows." },
196
+ { "title": "Popover aligned to start", "desc": "Align the dropdown popover to the left edge of the trigger via align='start'." }
197
+ ],
198
+ "open": "Open",
199
+ "close": "Close"
200
+ }
201
+ }