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 +280 -0
- package/dist/index.js +25 -10
- package/dist/index.mjs +5 -10
- package/package.json +11 -2
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
|
-
|
|
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/
|
|
30
|
+
// src/locon/index.tsx
|
|
12
31
|
function getSystemLanguage(availableLocales) {
|
|
32
|
+
if (!availableLocales?.length) return null;
|
|
13
33
|
try {
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
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
|
|
11
|
-
|
|
12
|
-
|
|
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.
|
|
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",
|