locon 1.0.2 → 1.0.4

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/README.md ADDED
@@ -0,0 +1,280 @@
1
+ ## locon
2
+
3
+ `locon` is a small, typed i18n helper for React Native.
4
+ It provides a context provider, a hook, and a simple text component to keep
5
+ your translations flat, explicit, and easy to use.
6
+
7
+ ---
8
+
9
+ ### Features
10
+
11
+ - **React Native–first**: tiny API, no global singletons
12
+ - **Context provider** with pluggable assets (per-locale JSON)
13
+ - **Device language auto-detection** (optional)
14
+ - **`useLocon()` hook** for accessing translations and changing locale
15
+ - **`<LText>` component** as a drop-in localized `<Text>`
16
+ - Fully typed TypeScript build (`dist/index.d.ts`)
17
+
18
+ ---
19
+
20
+ ### Installation
21
+
22
+ ```bash
23
+ npm install locon
24
+ # or
25
+ yarn add locon
26
+ ```
27
+
28
+ **Peer dependencies**
29
+
30
+ - `react >= 19.0.0`
31
+ - `react-native >= 0.70.0`
32
+
33
+ ---
34
+
35
+ ### Quick start
36
+
37
+ #### 1. Prepare your translation assets
38
+
39
+ `locon` expects flat key/value objects per locale:
40
+
41
+ ```ts
42
+ // en.json
43
+ export default {
44
+ hello: 'Hello',
45
+ welcome_title: 'Welcome',
46
+ change_language: 'Change language',
47
+ }
48
+
49
+ // de.json
50
+ export default {
51
+ hello: 'Hallo',
52
+ welcome_title: 'Willkommen',
53
+ change_language: 'Sprache ändern',
54
+ }
55
+ ```
56
+
57
+ You can also use JSON files and import them in your app bundle.
58
+
59
+ #### 2. Wrap your app with `Locon`
60
+
61
+ ```tsx
62
+ import React from 'react'
63
+ import { NavigationContainer, DefaultTheme } from '@react-navigation/native'
64
+ import Locon from 'locon'
65
+
66
+ import en from './assets/i18n/en.json'
67
+ import de from './assets/i18n/de.json'
68
+ import RootNavigator from './RootNavigator'
69
+
70
+ function App() {
71
+ const theme = {
72
+ ...DefaultTheme,
73
+ colors: {
74
+ ...DefaultTheme.colors,
75
+ background: '#ffffff',
76
+ },
77
+ }
78
+
79
+ return (
80
+ <Locon
81
+ assets={{ en, de }}
82
+ defaultLocale='en'
83
+ projectLocale='de'
84
+ autodetect
85
+ >
86
+ <NavigationContainer theme={theme}>
87
+ <RootNavigator />
88
+ </NavigationContainer>
89
+ </Locon>
90
+ )
91
+ }
92
+
93
+ export default App
94
+ ```
95
+
96
+ If `currentLocale` is not provided and `autodetect` is `true` (default),
97
+ `locon` will:
98
+
99
+ 1. Read device locale from `Intl.DateTimeFormat().resolvedOptions().locale`
100
+ 2. Extract language code (e.g. `'en'` from `'en-US'`)
101
+ 3. If that language exists in `assets`, use it as initial locale
102
+ 4. Otherwise, fall back to `defaultLocale`
103
+
104
+ #### 3. Use the `useLocon()` hook
105
+
106
+ ```tsx
107
+ import React from 'react'
108
+ import { View, Button, Text } from 'react-native'
109
+ import { useLocon } from 'locon'
110
+
111
+ function LanguageSwitcher() {
112
+ const { l, currentLocale, setLocale } = useLocon()
113
+
114
+ return (
115
+ <View>
116
+ {/* Localised value by key */}
117
+ <Text>
118
+ {l('current_language')}: {currentLocale}
119
+ </Text>
120
+ {/* or by value in project- or default language */}
121
+ <Text>
122
+ {l('Current language')}: {currentLocale}
123
+ </Text>
124
+ <Button
125
+ title='English'
126
+ onPress={() => setLocale('en')}
127
+ />
128
+ <Button
129
+ title='Deutsch'
130
+ onPress={() => setLocale('de')}
131
+ />
132
+ </View>
133
+ )
134
+ }
135
+ ```
136
+
137
+ #### 4. Use `<LText>` for localized text
138
+
139
+ ```tsx
140
+ import React from 'react'
141
+ import { View } from 'react-native'
142
+ import { LText } from 'locon'
143
+
144
+ function Welcome() {
145
+ return (
146
+ <View>
147
+ {/* Uses children as value to find the key, with fallback children */}
148
+ <LText>Hello</LText>
149
+
150
+ {/* Or explicit key with fallback children */}
151
+ <LText assetKey='welcome_title'>Welcome</LText>
152
+ </View>
153
+ )
154
+ }
155
+ ```
156
+
157
+ ---
158
+
159
+ ### API
160
+
161
+ #### `<Locon />`
162
+
163
+ ```ts
164
+ interface LoconProps extends PropsWithChildren {
165
+ assets?: Record<string, Record<string, string>>
166
+ currentLocale?: string
167
+ defaultLocale?: string
168
+ projectLocale?: string
169
+ autodetect?: boolean
170
+ }
171
+ ```
172
+
173
+ - **`assets`**
174
+ Map of locale → flat translations, e.g. `{ en: { hello: 'Hello' }, de: { hello: 'Hallo' } }`
175
+
176
+ - **`currentLocale`**
177
+ Initial locale. If provided, it overrides auto-detection.
178
+
179
+ - **`defaultLocale`** (default `'en'`)
180
+ Used when key is missing in the current locale.
181
+
182
+ - **`projectLocale`** (default `'en'`)
183
+ Used for reverse lookup by value (e.g. when you only know the string in
184
+ project language).
185
+
186
+ - **`autodetect`** (default `true`)
187
+ Enables system language detection as described above.
188
+
189
+ #### `useLocon()`
190
+
191
+ ```ts
192
+ import { useLocon } from 'locon'
193
+
194
+ const {
195
+ l, // (key/value: string) => string
196
+ assets, // all assets map
197
+ currentLocale, // current language code
198
+ defaultLocale, // default language code
199
+ autodetect, // boolean
200
+ setLocale, // (locale: string) => void
201
+ } = useLocon()
202
+ ```
203
+
204
+ - `l(key)`
205
+ Returns the localized string or the key itself if nothing was found.
206
+
207
+ - `setLocale(locale)`
208
+ Switches to `locale` if it exists in `assets`, otherwise logs a warning
209
+ in development.
210
+
211
+ #### `<LText />`
212
+
213
+ ```ts
214
+ interface LTextProps extends ComponentProps<typeof Text> {
215
+ children: string
216
+ assetKey?: string
217
+ }
218
+ ```
219
+
220
+ - If `assetKey` is provided, `LText` uses it directly as the lookup key.
221
+ - Otherwise, it uses `children` as an input to the same resolution logic
222
+ as `l()`:
223
+ 1. Try to find a key in the **project locale** whose **value** equals
224
+ `children`, then use that key in the current locale.
225
+ 2. If nothing is found in the project locale, try **directly** using
226
+ `children` as a key in the current locale.
227
+ 3. If still nothing is found, fall back to the **default locale**.
228
+ 4. If there is no match anywhere, render `children` as-is.
229
+
230
+ ---
231
+
232
+ ### ESLint integration (`react-native/no-raw-text`)
233
+
234
+ If you use `eslint-plugin-react-native` with the `react-native/no-raw-text`
235
+ rule enabled, you may see errors like:
236
+
237
+ > Raw text (Hello) cannot be used outside of a `<Text>` tag
238
+
239
+ because the rule does not know that `LText` ultimately renders a React
240
+ Native `<Text>` under the hood.
241
+
242
+ To fix this, tell the rule to treat `LText` as an allowed wrapper:
243
+
244
+ ```js
245
+ // .eslintrc.js
246
+ module.exports = {
247
+ // ...
248
+ rules: {
249
+ // other rules...
250
+ 'react-native/no-raw-text': [
251
+ 'error',
252
+ {
253
+ skip: ['LText'],
254
+ },
255
+ ],
256
+ },
257
+ }
258
+ ```
259
+
260
+ This keeps the rule active for other components, but lets you freely use
261
+ raw text inside `<LText>`.
262
+
263
+ ---
264
+
265
+ ### TypeScript
266
+
267
+ The published package contains:
268
+
269
+ - `dist/index.js` (CJS)
270
+ - `dist/index.mjs` (ESM)
271
+ - `dist/index.d.ts` (types)
272
+
273
+ So you can import from `locon` in both JS and TS projects and get type
274
+ support for `Locon`, `useLocon`, and `LText`.
275
+
276
+ ---
277
+
278
+ ### License
279
+
280
+ `locon` is released under the **ISC** license.
package/dist/index.js CHANGED
@@ -3,22 +3,37 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var react = require('react');
6
+ var RNLocalize = require('react-native-localize');
6
7
  var reactNative = require('react-native');
7
8
  var jsxRuntime = require('react/jsx-runtime');
8
9
 
9
- // src/locon/index.tsx
10
+ function _interopNamespace(e) {
11
+ if (e && e.__esModule) return e;
12
+ var n = Object.create(null);
13
+ if (e) {
14
+ Object.keys(e).forEach(function (k) {
15
+ if (k !== 'default') {
16
+ var d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: function () { return e[k]; }
20
+ });
21
+ }
22
+ });
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ var RNLocalize__namespace = /*#__PURE__*/_interopNamespace(RNLocalize);
10
29
 
11
- // src/locon/utils/getSystemLanguage.ts
30
+ // src/locon/index.tsx
12
31
  function getSystemLanguage(availableLocales) {
32
+ if (!availableLocales?.length) return null;
13
33
  try {
14
- const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
15
- if (!systemLocale) return null;
16
- const languageCode = systemLocale.split("-")[0]?.toLowerCase();
17
- if (!languageCode) return null;
18
- const supportedLanguage = availableLocales.find((locale) => locale === languageCode);
19
- return supportedLanguage ? languageCode : null;
20
- } catch (error) {
21
- console.log("Could not detect system language:", error);
34
+ const best = RNLocalize__namespace.findBestLanguageTag(availableLocales);
35
+ return best?.languageTag ?? null;
36
+ } catch {
22
37
  return null;
23
38
  }
24
39
  }
package/dist/index.mjs CHANGED
@@ -1,20 +1,15 @@
1
1
  import { createContext, useContext, useState, useCallback, useMemo } from 'react';
2
+ import * as RNLocalize from 'react-native-localize';
2
3
  import { Text } from 'react-native';
3
4
  import { jsx } from 'react/jsx-runtime';
4
5
 
5
6
  // src/locon/index.tsx
6
-
7
- // src/locon/utils/getSystemLanguage.ts
8
7
  function getSystemLanguage(availableLocales) {
8
+ if (!availableLocales?.length) return null;
9
9
  try {
10
- const systemLocale = Intl.DateTimeFormat().resolvedOptions().locale;
11
- if (!systemLocale) return null;
12
- const languageCode = systemLocale.split("-")[0]?.toLowerCase();
13
- if (!languageCode) return null;
14
- const supportedLanguage = availableLocales.find((locale) => locale === languageCode);
15
- return supportedLanguage ? languageCode : null;
16
- } catch (error) {
17
- console.log("Could not detect system language:", error);
10
+ const best = RNLocalize.findBestLanguageTag(availableLocales);
11
+ return best?.languageTag ?? null;
12
+ } catch {
18
13
  return null;
19
14
  }
20
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "locon",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "locon is a simple and lightweight library for React Native that provides a simple way to manage translations in your app.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -30,7 +30,16 @@
30
30
  "license": "ISC",
31
31
  "peerDependencies": {
32
32
  "react": ">=19.0.0",
33
- "react-native": ">=0.70.0"
33
+ "react-native": ">=0.70.0",
34
+ "react-native-localize": ">=3.0.0"
35
+ },
36
+ "peerDependenciesMeta": {
37
+ "react-native-localize": {
38
+ "optional": true
39
+ }
40
+ },
41
+ "dependencies": {
42
+ "react-native-localize": "^3.2.1"
34
43
  },
35
44
  "devDependencies": {
36
45
  "@babel/preset-react": "^7.24.7",