imba-localization 0.4.12 → 0.5.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/README.md +76 -6
- package/index.d.ts +21 -1
- package/localization.imba +2 -91
- package/package.json +10 -1
- package/state.imba +195 -0
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ A lightweight Imba module for loading and handling JSON-based localization files
|
|
|
8
8
|
- 🔍 **Automatic language detection** - Uses the user's browser language settings
|
|
9
9
|
- 💾 **Persistence**: Stores user choice in local storage across sessions
|
|
10
10
|
- 🔄 **Smart fallback system** - Falls back to a default language when needed
|
|
11
|
+
- 📦 **Optional split loading** - Loads only the selected language file and caches languages on demand
|
|
11
12
|
- 🧠 **Intuitive access** - Proxy-based access to translation strings
|
|
12
13
|
- 📡 **Event handling** - Support for `onready`, `onchange`, and `onerror` events
|
|
13
14
|
- 🧾 **Type declarations** - Includes package-level TypeScript declarations for editor and language-server imports
|
|
@@ -30,6 +31,13 @@ npm install imba-localization
|
|
|
30
31
|
bun add imba-localization
|
|
31
32
|
```
|
|
32
33
|
|
|
34
|
+
For local module development:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
bun install
|
|
38
|
+
bun run test
|
|
39
|
+
```
|
|
40
|
+
|
|
33
41
|
## 🚀 Quick Start
|
|
34
42
|
|
|
35
43
|
### 1️⃣ Preload the localization file
|
|
@@ -71,9 +79,9 @@ loc.onready = do
|
|
|
71
79
|
|
|
72
80
|
loc.onerror = do(error, details)
|
|
73
81
|
# The Localization object can return following types of errors:
|
|
74
|
-
# '
|
|
75
|
-
# '
|
|
76
|
-
# '
|
|
82
|
+
# 'localization-no-file' - if there was a problem downloading a JSON file
|
|
83
|
+
# 'localization-no-default' - if there is no default localization
|
|
84
|
+
# 'localization-no-key' - if a requested interface key is missing
|
|
77
85
|
console.error "Localization error:", error, details
|
|
78
86
|
|
|
79
87
|
loc.onchange = do(lang_key)
|
|
@@ -94,7 +102,9 @@ console.log loc.languages[loc.active]
|
|
|
94
102
|
|
|
95
103
|
```
|
|
96
104
|
|
|
97
|
-
## 📄 JSON Structure
|
|
105
|
+
## 📄 Bundle JSON Structure
|
|
106
|
+
|
|
107
|
+
The original single-file format remains supported and is the default mode:
|
|
98
108
|
|
|
99
109
|
Your localization file should follow this format:
|
|
100
110
|
|
|
@@ -119,22 +129,80 @@ Your localization file should follow this format:
|
|
|
119
129
|
}
|
|
120
130
|
```
|
|
121
131
|
|
|
132
|
+
## 📦 Split Language Files
|
|
133
|
+
|
|
134
|
+
Split mode loads a small manifest first and then downloads only the selected language. Previously loaded languages are kept in memory and are not requested again.
|
|
135
|
+
|
|
136
|
+
```imba
|
|
137
|
+
const loc = new Localization('/localization/languages.json', 'en', {split: true})
|
|
138
|
+
|
|
139
|
+
# Optional when application startup needs to wait explicitly.
|
|
140
|
+
await loc.pending
|
|
141
|
+
|
|
142
|
+
# Assignment still works and starts an asynchronous language switch.
|
|
143
|
+
loc.active = 'fr'
|
|
144
|
+
|
|
145
|
+
# Use `use` when the caller needs to wait for the new dictionary.
|
|
146
|
+
await loc.use('fr')
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Recommended file structure:
|
|
150
|
+
|
|
151
|
+
```text
|
|
152
|
+
public/localization/
|
|
153
|
+
├── languages.json
|
|
154
|
+
├── en.json
|
|
155
|
+
├── fr.json
|
|
156
|
+
└── ru.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`languages.json` contains only language metadata and file locations:
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"default": "en",
|
|
164
|
+
"languages": {
|
|
165
|
+
"en": { "name": "English", "flag": "us", "src": "en.json" },
|
|
166
|
+
"fr": { "name": "Français", "flag": "fr", "src": "fr.json" },
|
|
167
|
+
"ru": { "name": "Русский", "flag": "ru", "src": "ru.json" }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Each language file contains the dictionary directly:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"welcome": "Welcome",
|
|
177
|
+
"goodbye": "Goodbye",
|
|
178
|
+
"user": {
|
|
179
|
+
"profile": "Profile",
|
|
180
|
+
"settings": "Settings"
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
If `src` is omitted, the language code is used as the filename (`ru` → `ru.json`). The manifest `default` overrides the constructor fallback when present. A failed switch keeps the current language active and reports `localization-no-file` through `onerror`.
|
|
186
|
+
|
|
122
187
|
## 🛠️ API Reference
|
|
123
188
|
|
|
124
189
|
### Constructor
|
|
125
190
|
|
|
126
191
|
```imba
|
|
127
|
-
new Localization(url, default = 'en')
|
|
192
|
+
new Localization(url, default = 'en', options = {})
|
|
128
193
|
```
|
|
129
194
|
|
|
130
195
|
- `url`: Path to your JSON localization file
|
|
131
196
|
- `default`: Fallback language code (defaults to 'en')
|
|
197
|
+
- `options.split`: Treat `url` as a language manifest and load dictionaries on demand
|
|
132
198
|
|
|
133
199
|
### Properties
|
|
134
200
|
|
|
135
201
|
- `active`: Get or set the code of the active language
|
|
136
|
-
- `languages`:
|
|
202
|
+
- `languages`: Available languages; unloaded split entries contain only `$` metadata
|
|
137
203
|
- `preferred`: Detected browser language (first 2 characters of `navigator.language`)
|
|
204
|
+
- `loaded`: Map of language codes whose dictionaries are already in memory
|
|
205
|
+
- `pending`: Promise settled after initial localization loading finishes
|
|
138
206
|
|
|
139
207
|
### Methods
|
|
140
208
|
|
|
@@ -142,6 +210,8 @@ new Localization(url, default = 'en')
|
|
|
142
210
|
- `text(path, fallback = '', data = null)`: Reads a localized string and replaces `{key}` placeholders from `data`
|
|
143
211
|
- `table(path)`: Reads a nested object table, returning `{}` when missing
|
|
144
212
|
- `render(value, data = null)`: Replaces `{key}` placeholders in any string-like value
|
|
213
|
+
- `use(language)`: Loads and activates a language, returning a Promise
|
|
214
|
+
- `load(language)`: Loads a split dictionary without activating it
|
|
145
215
|
|
|
146
216
|
### Events
|
|
147
217
|
|
package/index.d.ts
CHANGED
|
@@ -1,13 +1,26 @@
|
|
|
1
1
|
export type LocalizationLanguage = Record<string, any>;
|
|
2
2
|
export type LocalizationLanguages = Record<string, LocalizationLanguage>;
|
|
3
3
|
|
|
4
|
+
export interface LocalizationOptions {
|
|
5
|
+
split?: boolean;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface LocalizationManifestLanguage {
|
|
9
|
+
name?: string;
|
|
10
|
+
flag?: string;
|
|
11
|
+
src?: string;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export type LocalizationManifest = Record<string, string | LocalizationManifestLanguage>;
|
|
16
|
+
|
|
4
17
|
export interface LocalizationErrorState {
|
|
5
18
|
cache: Record<string, boolean>;
|
|
6
19
|
throw(code: string, details?: any): void;
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
export class Localization {
|
|
10
|
-
constructor(url: string, fallback?: string);
|
|
23
|
+
constructor(url: string, fallback?: string, options?: LocalizationOptions);
|
|
11
24
|
|
|
12
25
|
onready?: () => void;
|
|
13
26
|
onerror?: (error: string, details?: any) => void;
|
|
@@ -17,6 +30,11 @@ export class Localization {
|
|
|
17
30
|
preferred: string;
|
|
18
31
|
default: string;
|
|
19
32
|
ready: boolean;
|
|
33
|
+
mode: "bundle" | "split";
|
|
34
|
+
source: string;
|
|
35
|
+
manifest: LocalizationManifest;
|
|
36
|
+
loaded: Record<string, boolean>;
|
|
37
|
+
pending: Promise<void>;
|
|
20
38
|
err: LocalizationErrorState;
|
|
21
39
|
|
|
22
40
|
active: string;
|
|
@@ -25,6 +43,8 @@ export class Localization {
|
|
|
25
43
|
lookup(path: string | string[], fallback?: unknown): unknown;
|
|
26
44
|
text(path: string | string[], fallback?: string, data?: Record<string, unknown> | null): string;
|
|
27
45
|
table(path: string | string[]): Record<string, any>;
|
|
46
|
+
use(language: string): Promise<string>;
|
|
47
|
+
load(language: string): Promise<boolean>;
|
|
28
48
|
|
|
29
49
|
[key: string]: any;
|
|
30
50
|
}
|
package/localization.imba
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# Emoji flags mapping - comprehensive list by region
|
|
2
|
+
export {Localization} from './state.imba'
|
|
3
|
+
|
|
2
4
|
export const flags = {
|
|
3
5
|
# Europe
|
|
4
6
|
'gb': '🇬🇧', 'us': '🇺🇸', 'ru': '🇷🇺', 'de': '🇩🇪', 'fr': '🇫🇷',
|
|
@@ -62,97 +64,6 @@ export const flags = {
|
|
|
62
64
|
'dg': '🇩🇬', 'ea': '🇪🇦', 'ic': '🇮🇨', 'xk': '🇽🇰'
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
export class Localization
|
|
66
|
-
onready
|
|
67
|
-
onerror
|
|
68
|
-
onchange
|
|
69
|
-
languages = {}
|
|
70
|
-
preferred = (window..navigator..language || 'en-US').slice(0, 2)
|
|
71
|
-
default
|
|
72
|
-
ready = false
|
|
73
|
-
err = {
|
|
74
|
-
cache: {}
|
|
75
|
-
throw: do(code, details)
|
|
76
|
-
return if err.cache[code]
|
|
77
|
-
if onerror isa Function and ready
|
|
78
|
-
onerror(code, details)
|
|
79
|
-
else
|
|
80
|
-
console.log "Localization error:", code, details
|
|
81
|
-
err.cache[code] = true
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
def constructor url, fallback = 'en'
|
|
85
|
-
default = fallback
|
|
86
|
-
window.fetch(url)
|
|
87
|
-
.then(do(response) response.json!)
|
|
88
|
-
.then(do(data) _finalize(data, undefined))
|
|
89
|
-
.catch(do(error) _finalize(undefined, error))
|
|
90
|
-
|
|
91
|
-
return new Proxy self, {
|
|
92
|
-
get: do(target, p, receiver)
|
|
93
|
-
if Reflect.has(target, p)
|
|
94
|
-
return Reflect.get(target, p, receiver)
|
|
95
|
-
if !target.ready
|
|
96
|
-
target.err.throw("Request before localization is ready:", p)
|
|
97
|
-
return
|
|
98
|
-
return if target.err.cache[p]
|
|
99
|
-
return target.languages[p] if target.languages[p]
|
|
100
|
-
return target.languages[target.active][p] if target.languages[target.active] and target.languages[target.active][p]
|
|
101
|
-
target.err.throw('localization-no-key', p)
|
|
102
|
-
return ''
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
def render value, data = null
|
|
106
|
-
let text = String(value or '')
|
|
107
|
-
return text unless data and typeof data == 'object'
|
|
108
|
-
text.replace /\{([^}]+)\}/g, do(_match, key)
|
|
109
|
-
const value = data[key]
|
|
110
|
-
if value == undefined or value == null then '' else String(value)
|
|
111
|
-
|
|
112
|
-
def lookup path, fallback = ''
|
|
113
|
-
const keys = if Array.isArray(path) then path else String(path or '').split('.').filter(do(part) part)
|
|
114
|
-
let value = languages..[active] or languages..[default] or {}
|
|
115
|
-
for key in keys
|
|
116
|
-
if value and typeof value == 'object' and value[key] != undefined
|
|
117
|
-
value = value[key]
|
|
118
|
-
else
|
|
119
|
-
return fallback
|
|
120
|
-
value
|
|
121
|
-
|
|
122
|
-
def text path, fallback = '', data = null
|
|
123
|
-
const value = lookup(path, fallback)
|
|
124
|
-
return render(value, data) if typeof value == 'string' or typeof value == 'number'
|
|
125
|
-
render(fallback, data)
|
|
126
|
-
|
|
127
|
-
def table path
|
|
128
|
-
const value = lookup(path, {})
|
|
129
|
-
if value and typeof value == 'object' then value else {}
|
|
130
|
-
|
|
131
|
-
def _finalize data, error
|
|
132
|
-
if error or !data
|
|
133
|
-
err.throw('localization-no-file',error)
|
|
134
|
-
elif !data[default]
|
|
135
|
-
err.throw('localization-no-default', default)
|
|
136
|
-
else
|
|
137
|
-
languages = data
|
|
138
|
-
ready = true
|
|
139
|
-
err.cache = {}
|
|
140
|
-
onready! if onready isa Function
|
|
141
|
-
onchange(active) if onchange isa Function
|
|
142
|
-
|
|
143
|
-
get active
|
|
144
|
-
const saved = window.localStorage.getItem('imba-localization')
|
|
145
|
-
return saved if saved and languages[saved]
|
|
146
|
-
return preferred if languages[preferred]
|
|
147
|
-
return default
|
|
148
|
-
|
|
149
|
-
set active name
|
|
150
|
-
name = name and languages[name] ? name : active
|
|
151
|
-
if window.localStorage.getItem('imba-localization') != name
|
|
152
|
-
window.localStorage.setItem('imba-localization', name)
|
|
153
|
-
onchange(name) if onchange isa Function
|
|
154
|
-
err.cache = {}
|
|
155
|
-
|
|
156
67
|
export const path-arrow-down = <path d="M213.66,165.66a8,8,0,0,1-11.32,0L128,91.31,53.66,165.66a8,8,0,0,1-11.32-11.32l80-80a8,8,0,0,1,11.32,0l80,80A8,8,0,0,1,213.66,165.66Z">
|
|
157
68
|
|
|
158
69
|
tag language-selector
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imba-localization",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/HeapVoid/imba-localization.git"
|
|
@@ -25,8 +25,17 @@
|
|
|
25
25
|
"keywords": ["imba", "theme", "localization"],
|
|
26
26
|
"license": "MIT",
|
|
27
27
|
"type": "module",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"test": "bimba state.imba --outdir .cache/test --external imba --external imba/runtime && bun test"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"bimba-cli": "latest",
|
|
33
|
+
"imba": "latest",
|
|
34
|
+
"typescript": "^5.9.3"
|
|
35
|
+
},
|
|
28
36
|
"files": [
|
|
29
37
|
"localization.imba",
|
|
38
|
+
"state.imba",
|
|
30
39
|
"index.d.ts"
|
|
31
40
|
]
|
|
32
41
|
}
|
package/state.imba
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export class Localization
|
|
2
|
+
onready
|
|
3
|
+
onerror
|
|
4
|
+
onchange
|
|
5
|
+
languages = {}
|
|
6
|
+
preferred = (window..navigator..language || 'en-US').slice(0, 2)
|
|
7
|
+
default
|
|
8
|
+
ready = false
|
|
9
|
+
mode = 'bundle'
|
|
10
|
+
source = ''
|
|
11
|
+
manifest = {}
|
|
12
|
+
loaded = {}
|
|
13
|
+
loading = {}
|
|
14
|
+
pending
|
|
15
|
+
#active = null
|
|
16
|
+
#request = 0
|
|
17
|
+
err = {
|
|
18
|
+
cache: {}
|
|
19
|
+
throw: do(code, details)
|
|
20
|
+
return if err.cache[code]
|
|
21
|
+
if onerror isa Function
|
|
22
|
+
onerror(code, details)
|
|
23
|
+
else
|
|
24
|
+
console.log "Localization error:", code, details
|
|
25
|
+
err.cache[code] = true
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def constructor url, fallback = 'en', options = {}
|
|
29
|
+
default = fallback
|
|
30
|
+
source = url
|
|
31
|
+
mode = options..split ? 'split' : 'bundle'
|
|
32
|
+
pending = if mode == 'split' then _manifest! else _bundle!
|
|
33
|
+
|
|
34
|
+
return new Proxy self, {
|
|
35
|
+
get: do(target, p, receiver)
|
|
36
|
+
if Reflect.has(target, p)
|
|
37
|
+
return Reflect.get(target, p, receiver)
|
|
38
|
+
if !target.ready
|
|
39
|
+
target.err.throw("Request before localization is ready:", p)
|
|
40
|
+
return
|
|
41
|
+
return if target.err.cache[p]
|
|
42
|
+
return target.languages[p] if target.languages[p]
|
|
43
|
+
return target.languages[target.active][p] if target.languages[target.active] and target.languages[target.active][p]
|
|
44
|
+
target.err.throw('localization-no-key', p)
|
|
45
|
+
return ''
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def render value, data = null
|
|
49
|
+
let text = String(value or '')
|
|
50
|
+
return text unless data and typeof data == 'object'
|
|
51
|
+
text.replace /\{([^}]+)\}/g, do(_match, key)
|
|
52
|
+
const value = data[key]
|
|
53
|
+
if value == undefined or value == null then '' else String(value)
|
|
54
|
+
|
|
55
|
+
def lookup path, fallback = ''
|
|
56
|
+
const keys = if Array.isArray(path) then path else String(path or '').split('.').filter(do(part) part)
|
|
57
|
+
let value = languages..[active] or languages..[default] or {}
|
|
58
|
+
for key in keys
|
|
59
|
+
if value and typeof value == 'object' and value[key] != undefined
|
|
60
|
+
value = value[key]
|
|
61
|
+
else
|
|
62
|
+
return fallback
|
|
63
|
+
value
|
|
64
|
+
|
|
65
|
+
def text path, fallback = '', data = null
|
|
66
|
+
const value = lookup(path, fallback)
|
|
67
|
+
return render(value, data) if typeof value == 'string' or typeof value == 'number'
|
|
68
|
+
render(fallback, data)
|
|
69
|
+
|
|
70
|
+
def table path
|
|
71
|
+
const value = lookup(path, {})
|
|
72
|
+
if value and typeof value == 'object' then value else {}
|
|
73
|
+
|
|
74
|
+
def use name
|
|
75
|
+
name = name and languages[name] ? name : active
|
|
76
|
+
return active unless name
|
|
77
|
+
const request = ++#request
|
|
78
|
+
if mode == 'split' and !loaded[name]
|
|
79
|
+
unless await load(name)
|
|
80
|
+
imba.commit!
|
|
81
|
+
return active
|
|
82
|
+
return active if request != #request
|
|
83
|
+
_activate(name)
|
|
84
|
+
|
|
85
|
+
def load name
|
|
86
|
+
return false unless languages[name]
|
|
87
|
+
return true if loaded[name]
|
|
88
|
+
return await loading[name] if loading[name]
|
|
89
|
+
loading[name] = _load(name)
|
|
90
|
+
const success = await loading[name]
|
|
91
|
+
delete loading[name]
|
|
92
|
+
success
|
|
93
|
+
|
|
94
|
+
def _load name
|
|
95
|
+
try
|
|
96
|
+
const data = await _read(_resolve(name))
|
|
97
|
+
const content = if data..[name] and data[name]..$ then data[name] else data
|
|
98
|
+
unless content and typeof content == 'object' and !Array.isArray(content)
|
|
99
|
+
throw new Error("Localization file for {name} is not an object")
|
|
100
|
+
const settings = _settings(name)
|
|
101
|
+
const language = Object.assign({}, content)
|
|
102
|
+
language.$ = Object.assign({}, content..$, settings)
|
|
103
|
+
languages[name] = language
|
|
104
|
+
loaded[name] = true
|
|
105
|
+
return true
|
|
106
|
+
catch error
|
|
107
|
+
err.throw('localization-no-file', {language: name, url: _resolve(name), error})
|
|
108
|
+
return false
|
|
109
|
+
|
|
110
|
+
def _bundle
|
|
111
|
+
try
|
|
112
|
+
_finalize(await _read(source), undefined)
|
|
113
|
+
catch error
|
|
114
|
+
_finalize(undefined, error)
|
|
115
|
+
|
|
116
|
+
def _manifest
|
|
117
|
+
try
|
|
118
|
+
const data = await _read(source)
|
|
119
|
+
const catalog = data..languages
|
|
120
|
+
default = data.default if data..default isa 'string'
|
|
121
|
+
unless catalog and typeof catalog == 'object' and !Array.isArray(catalog) and Object.keys(catalog).length
|
|
122
|
+
throw new Error('Localization manifest has no languages')
|
|
123
|
+
manifest = catalog
|
|
124
|
+
for own name of manifest
|
|
125
|
+
languages[name] = {$: _settings(name)}
|
|
126
|
+
unless languages[default]
|
|
127
|
+
err.throw('localization-no-default', default)
|
|
128
|
+
return
|
|
129
|
+
let name = _select!
|
|
130
|
+
let success = await load(name)
|
|
131
|
+
if !success and name != default
|
|
132
|
+
name = default
|
|
133
|
+
success = await load(name)
|
|
134
|
+
return unless success
|
|
135
|
+
_finish(name)
|
|
136
|
+
catch error
|
|
137
|
+
err.throw('localization-no-file', {url: source, error})
|
|
138
|
+
|
|
139
|
+
def _read url
|
|
140
|
+
const response = await window.fetch(url)
|
|
141
|
+
unless response.ok
|
|
142
|
+
throw new Error("Localization request failed with status {response.status}")
|
|
143
|
+
await response.json!
|
|
144
|
+
|
|
145
|
+
def _resolve name
|
|
146
|
+
const settings = _settings(name)
|
|
147
|
+
const file = settings.src or "{name}.json"
|
|
148
|
+
new window.URL(file, new window.URL(source, window.location.href)).toString!
|
|
149
|
+
|
|
150
|
+
def _settings name
|
|
151
|
+
const settings = manifest[name]
|
|
152
|
+
return {src: settings} if settings isa 'string'
|
|
153
|
+
settings or {}
|
|
154
|
+
|
|
155
|
+
def _select
|
|
156
|
+
const saved = window.localStorage.getItem('imba-localization')
|
|
157
|
+
return saved if saved and languages[saved]
|
|
158
|
+
return preferred if languages[preferred]
|
|
159
|
+
default
|
|
160
|
+
|
|
161
|
+
def _activate name
|
|
162
|
+
return active unless languages[name] and loaded[name]
|
|
163
|
+
const changed = #active != name
|
|
164
|
+
#active = name
|
|
165
|
+
if window.localStorage.getItem('imba-localization') != name
|
|
166
|
+
window.localStorage.setItem('imba-localization', name)
|
|
167
|
+
err.cache = {}
|
|
168
|
+
onchange(name) if changed and onchange isa Function
|
|
169
|
+
imba.commit! if ready
|
|
170
|
+
#active
|
|
171
|
+
|
|
172
|
+
def _finish name
|
|
173
|
+
#active = name
|
|
174
|
+
ready = true
|
|
175
|
+
err.cache = {}
|
|
176
|
+
onready! if onready isa Function
|
|
177
|
+
onchange(name) if onchange isa Function
|
|
178
|
+
imba.commit!
|
|
179
|
+
|
|
180
|
+
def _finalize data, error
|
|
181
|
+
if error or !data
|
|
182
|
+
err.throw('localization-no-file', error)
|
|
183
|
+
elif !data[default]
|
|
184
|
+
err.throw('localization-no-default', default)
|
|
185
|
+
else
|
|
186
|
+
languages = data
|
|
187
|
+
loaded = {}
|
|
188
|
+
loaded[name] = true for own name of languages
|
|
189
|
+
_finish(_select!)
|
|
190
|
+
|
|
191
|
+
get active
|
|
192
|
+
#active or _select!
|
|
193
|
+
|
|
194
|
+
set active name
|
|
195
|
+
use(name)
|