com.xmobitea.changx.mini-localization 1.4.10 → 1.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/AGENTS.md CHANGED
@@ -16,13 +16,16 @@ Applies to everything inside this package folder.
16
16
  - UI text updates happen through `LocalizationManager.OnUpdateText`.
17
17
  - online localization success clears and rebuilds the active dictionary from the downloaded XML.
18
18
  - generated key constants come from editor tooling, not from runtime discovery.
19
+ - `FetchType` controls the editor fetch source: `None`, `FromGoogleSheets`, or `FromCsv`.
20
+ - CSV fetch reads a `TextAsset` assigned in settings, parses it with `CsvParser`, and writes XML files to disk. It does not alter the runtime dictionary directly.
19
21
 
20
22
  ## Required Mental Model
21
23
 
22
24
  Treat this package as:
23
25
 
24
26
  - a scene-managed localization system,
25
- - XML-driven translation storage,
27
+ - XML-driven translation storage at runtime,
28
+ - CSV-driven or Google-Sheets-driven fetch workflow in the editor (CSV is converted to XML before being used at runtime),
26
29
  - key-based lookup plus UI refresh events,
27
30
  - editor-assisted setup and code generation.
28
31
 
@@ -59,4 +62,6 @@ If an agent writes code against this package:
59
62
  - it should mention the need for scene setup and settings asset,
60
63
  - it should not assume constants are available unless generation is part of the workflow,
61
64
  - it should account for repeated `OnUpdateText` when async sources are enabled,
62
- - it should document any changed XML or load-order semantics in `README.md`.
65
+ - it should document any changed XML or load-order semantics in `README.md`,
66
+ - when describing the fetch workflow, it should distinguish between `FromGoogleSheets` (runs an external executable) and `FromCsv` (reads a `TextAsset` inside the project and converts it to XML),
67
+ - it should never imply that CSV is read at runtime — CSV is an editor-only input format.
package/AI_USAGE.md CHANGED
@@ -13,6 +13,8 @@ Use this file when you want to generate code without reading the runtime impleme
13
13
  - Addressables optional source: `Yes`
14
14
  - Online optional source: `Yes`
15
15
  - Generated key constants automatic at runtime: `No`
16
+ - Editor fetch source options: `None`, `FromGoogleSheets`, `FromCsv`
17
+ - CSV is editor-only: parsed to XML by editor tooling, never read at runtime
16
18
 
17
19
  ## Exact Rules
18
20
 
@@ -44,6 +46,14 @@ Treat `GetText(key)` fallback-to-key as normal behavior, not as an exception cas
44
46
 
45
47
  If online localization is enabled and succeeds, assume it replaces the active dictionary content.
46
48
 
49
+ ### Rule 8
50
+
51
+ `FetchType` is an enum with three values: `None`, `FromGoogleSheets`, `FromCsv`. It is set separately for local and online fetch.
52
+
53
+ ### Rule 9
54
+
55
+ When `FetchType.FromCsv` is active, the editor reads a `TextAsset` CSV assigned in `LocalizationSettings`, converts it to XML, and writes the result to disk. The runtime never reads CSV directly.
56
+
47
57
  ## Safe Code Template
48
58
 
49
59
  ```csharp
@@ -72,7 +82,9 @@ Do not generate code that assumes:
72
82
  - auto-bootstrap of language selection,
73
83
  - runtime-generated key constants,
74
84
  - null return on missing translation,
75
- - one single synchronous update when online or addressable sources are involved.
85
+ - one single synchronous update when online or addressable sources are involved,
86
+ - CSV files are read at runtime,
87
+ - fetch type is a boolean — it is a `FetchType` enum.
76
88
 
77
89
  ## Recommended AI Output Style
78
90
 
package/CHANGELOG.md CHANGED
@@ -3,6 +3,13 @@ All notable changes to this package will be documented in this file.
3
3
 
4
4
  The format is based on (https://www.npmjs.com/org/xmobitea)
5
5
 
6
+ ## [Unreleased]
7
+ ### Added
8
+ - `FetchType` enum (`None`, `FromGoogleSheets`, `FromCsv`) replacing the previous boolean `usingFetch*` fields in `LocalizationSettings`.
9
+ - `CsvParser` editor utility for parsing RFC-4180 CSV files, including quoted fields and embedded newlines.
10
+ - `csvFetchLocalLocalization` and `csvFetchOnlineLocalization` `TextAsset` fields in `LocalizationSettings` for CSV-based fetch.
11
+ - CSV fetch path in `LocalizationManagerEditor`: reads an assigned CSV asset, converts each language column to XML, and writes output to `Local-LocalizationFile/` or `Online-LocalizationFile/`.
12
+
6
13
  ## [1.0.0] - 2021-04-08
7
14
  ### Added
8
15
  - This is the first release of Chang X plugins, as a Package
package/README.md CHANGED
@@ -123,13 +123,16 @@ Loads `LocalizationSettings` again from `Resources` and returns the configured l
123
123
  Localization XML is parsed using:
124
124
 
125
125
  - `XmlDocument`
126
- - `xmlDoc.ChildNodes.Item(1)`
127
- - nested section nodes
126
+ - `xmlDoc.ChildNodes.Item(1)` — selects the root element regardless of its tag name
127
+ - nested section nodes — the parser iterates all children of the root, then all children of each section
128
128
  - child nodes with attribute `name`
129
129
 
130
- Effective expected shape is:
130
+ The parser does not validate tag names. Both hand-authored XML and editor-generated XML are accepted as long as the nesting is correct.
131
+
132
+ Hand-authored XML example:
131
133
 
132
134
  ```xml
135
+ <?xml version='1.0' encoding='utf-8'?>
133
136
  <root>
134
137
  <section>
135
138
  <item name="SomeKey">Translated text</item>
@@ -137,6 +140,17 @@ Effective expected shape is:
137
140
  </root>
138
141
  ```
139
142
 
143
+ Editor-generated XML (produced by CSV fetch or key generator) uses the language name as the root tag and `<key>` as the child tag:
144
+
145
+ ```xml
146
+ <?xml version='1.0' encoding='utf-8'?>
147
+ <English>
148
+ <section name="Local">
149
+ <key name="SomeKey">Translated text</key>
150
+ </section>
151
+ </English>
152
+ ```
153
+
140
154
  Important behavior:
141
155
 
142
156
  - key is read from `node.Attributes.GetNamedItem("name").Value`
@@ -163,10 +177,12 @@ It contains:
163
177
 
164
178
  - `worksheetKey`
165
179
  - `localizationLanguageItems`
166
- - `usingFetchLocalLocalization`
167
- - `sheetNameFetchLocalLocalization`
168
- - `usingFetchOnlineLocalization`
169
- - `sheetNameFetchOnlineLocalization`
180
+ - `fetchTypeLocalLocalization` — `FetchType` enum: `None`, `FromGoogleSheets`, `FromCsv`
181
+ - `sheetNameFetchLocalLocalization` — sheet name used when `FromGoogleSheets`
182
+ - `csvFetchLocalLocalization` — `TextAsset` CSV used when `FromCsv`
183
+ - `fetchTypeOnlineLocalization` — `FetchType` enum: `None`, `FromGoogleSheets`, `FromCsv`
184
+ - `sheetNameFetchOnlineLocalization` — sheet name used when `FromGoogleSheets`
185
+ - `csvFetchOnlineLocalization` — `TextAsset` CSV used when `FromCsv`
170
186
 
171
187
  These fields are mainly used by editor tooling.
172
188
 
@@ -247,25 +263,96 @@ Important behavior:
247
263
 
248
264
  ## Fetch localization
249
265
 
250
- Editor fetch flow:
266
+ `FetchType` controls where the editor reads source data. Each of local and online can independently set `None`, `FromGoogleSheets`, or `FromCsv`.
267
+
268
+ ### FromGoogleSheets
251
269
 
252
270
  1. reads settings,
253
271
  2. locates a packaged external executable in `Editor/Tools/GetLocalizationTools`,
254
272
  3. passes worksheet key, sheet names, and languages as command-line arguments,
255
- 4. expects XML files under `Local-LocalizationFile/<Language>.xml`,
273
+ 4. expects XML files written under `Local-LocalizationFile/<Language>.xml` and/or `Online-LocalizationFile/<Language>.xml`,
256
274
  5. copies those files into the asset paths referenced by configured `LocalizationLanguageItem.XML`,
257
275
  6. refreshes assets,
258
276
  7. regenerates `LocalizationConstantId.cs`.
259
277
 
260
- This fetch path is editor tooling behavior, not runtime behavior.
278
+ ### FromCsv
279
+
280
+ 1. reads the `TextAsset` CSV assigned to `csvFetchLocalLocalization` or `csvFetchOnlineLocalization` in settings,
281
+ 2. parses it with `CsvParser` — first row is a header and is skipped, first column is the key, subsequent columns are language values in the same order as `localizationLanguageItems`,
282
+ 3. converts each language column to XML and writes files under `Local-LocalizationFile/<Language>.xml` or `Online-LocalizationFile/<Language>.xml`,
283
+ 4. copies those XML files into the asset paths referenced by configured `LocalizationLanguageItem.XML`,
284
+ 5. refreshes assets,
285
+ 6. regenerates `LocalizationConstantId.cs`.
286
+
287
+ CSV is always an editor-only input. The runtime never reads CSV files.
288
+
289
+ Expected CSV layout (columns after key must match the order of `localizationLanguageItems`):
290
+
291
+ ```
292
+ key,English,Vietnamese
293
+ Home_Title,Home,Trang Chủ
294
+ Home_Subtitle,Welcome,Chào Mừng
295
+ ```
261
296
 
262
297
  ## Key picker / scene window
263
298
 
264
- Editor tools provide:
299
+ ### Key picker drawer
300
+
301
+ Every field tagged with `[LocalizationKey]` (used on `_key` in all localization components) renders a custom property drawer in the Inspector:
302
+
303
+ - **Text field** — the key value can be typed directly.
304
+ - **`▼` dropdown button** — opens an `AdvancedDropdown` listing all string constants from `LocalizationConstantId`. Selecting an entry writes the key to the field.
305
+ - **Red warning label** — appears directly below the field when the current key value is not found in any `LocalizationConstantId` partial class. This indicates the editor generator has not been run or the key was renamed.
306
+ - The available key list is populated by reflecting over all loaded assemblies and is refreshed at most once every 60 seconds.
307
+
308
+ ### LocalizationComponent window
309
+
310
+ Accessible from:
311
+
312
+ ```text
313
+ XmobiTea Tools/Localization/Show LocalizationComponentWindows
314
+ ```
315
+
316
+ or from the `LocalizationManager` inspector button or the `LocalizationSettings` inspector button.
317
+
318
+ The window scans UI text objects under a selected parent GameObject and provides controls to add or remove `LocalizationComponent` instances in bulk.
319
+
320
+ ## LocalizationSettings inspector
321
+
322
+ The `LocalizationSettings` asset has a custom inspector that shows fields conditionally based on the selected `FetchType`.
323
+
324
+ ### Language list
325
+
326
+ - **Localization Language Length** — integer field that controls the array size of `localizationLanguageItems`. Increase or decrease to add or remove language entries.
327
+ - Each language entry expands to show: `System Language`, `Flag Spr`, local `XML` asset, and (when Addressables is installed) `XMLRef`.
328
+
329
+ ### Local fetch section
330
+
331
+ - **Fetch Type Local Localization** — enum: `None`, `FromGoogleSheets`, `FromCsv`.
332
+ - When `FromGoogleSheets`: shows **Sheet Name Fetch Local Localization** field with a hint that the value must not be empty and must not contain spaces.
333
+ - When `FromCsv`: shows **Csv Fetch Local Localization** (`TextAsset`) field with a hint that the file must not be null and must use `,` as delimiter.
265
334
 
266
- - property drawer dropdown for keys
267
- - validation message when a key is missing from generated constants
268
- - a window to scan UI text objects under a selected parent and add/remove localization components
335
+ ### Online fetch section
336
+
337
+ - **Fetch Type Online Localization** same enum as above with the same conditional fields.
338
+
339
+ ### Shared hints
340
+
341
+ - When either fetch type is `FromCsv`: the inspector displays a live hint showing the expected CSV column order based on the currently configured languages, for example:
342
+ ```
343
+ The column in csv must start at Key, English, Vietnamese
344
+ ```
345
+ - When either fetch type is `FromGoogleSheets`: shows **Worksheet Key** and a hint about the expected column layout in the Google Sheet.
346
+
347
+ ### Action buttons
348
+
349
+ Three buttons are available at the bottom of the `LocalizationSettings` inspector (and also from the `LocalizationManager` inspector):
350
+
351
+ | Button | Action |
352
+ |---|---|
353
+ | `Show LocalizationComponentWindows` | Opens the bulk component assignment window |
354
+ | `Generate LocalizationConstantId.cs` | Parses the first language XML and writes the constants file |
355
+ | `Fetch Localization` | Runs the configured fetch (Google Sheets or CSV) then regenerates constants |
269
356
 
270
357
  ## Required Setup
271
358
 
@@ -280,18 +367,22 @@ Editor tools provide:
280
367
 
281
368
  ## UI setup
282
369
 
283
- For `UnityEngine.UI.Text`:
370
+ ### LocalizationComponent (UnityEngine.UI.Text)
371
+
372
+ 1. Add `LocalizationComponent` to the same GameObject as the `Text` component.
373
+ 2. Inspector fields:
374
+ - **Main Txt** — reference to the `UnityEngine.UI.Text` target. If left empty, it is auto-resolved via `GetComponent<Text>()` at both edit time (`OnValidate`) and at runtime.
375
+ - **Key** — the localization key. Uses the key picker drawer: type directly or use the `▼` dropdown. A red warning appears if the key is missing from generated constants.
376
+ 3. Click **Reload Text** in the inspector to force a text refresh in the editor without entering Play mode.
284
377
 
285
- 1. add `LocalizationComponent`
286
- 2. assign or auto-resolve `Text`
287
- 3. set the localization key
378
+ ### TMP_LocalizationComponent (TextMesh Pro)
288
379
 
289
- For TMP:
380
+ 1. Install the TextMesh Pro package.
381
+ 2. Add `TMP_LocalizationComponent` to the same GameObject as the `TMP_Text` component.
382
+ 3. Inspector fields match `LocalizationComponent`: **TMP Text** reference and **Key** with the same key picker drawer and warning behavior.
383
+ 4. Click **Reload Text** to force a refresh in the editor.
290
384
 
291
- 1. install TextMesh Pro
292
- 2. add `TMP_LocalizationComponent`
293
- 3. assign or auto-resolve `TMP_Text`
294
- 4. set the localization key
385
+ Both components subscribe to `LocalizationManager.OnUpdateText` in `OnEnable` and unsubscribe in `OnDisable`. They also call `OnUpdateText()` immediately after subscribing so the correct text appears when the object becomes active.
295
386
 
296
387
  ## Basic Usage
297
388
 
@@ -328,12 +419,66 @@ var text = LocalizationManager.GetText(LocalizationConstantId.Home_Title);
328
419
 
329
420
  ## React to async updates
330
421
 
422
+ `LocalizationManager.OnUpdateText` is a static `Action` that fires each time the active translation dictionary has been rebuilt or updated.
423
+
424
+ ### When it fires
425
+
426
+ One call to `ChooseLanguage(...)` can cause `OnUpdateText` to fire **more than once**:
427
+
428
+ | Trigger | Fires? |
429
+ |---|---|
430
+ | Local XML loaded synchronously | Yes — always, at the end of `ChooseLanguage(...)` |
431
+ | Addressables XML loaded (async) | Yes — when the async operation completes, if the language has not changed again |
432
+ | Online XML loaded (async) | Yes — when the HTTP request completes, if the language has not changed again |
433
+
434
+ For a project that uses only local XML, `OnUpdateText` fires exactly once per `ChooseLanguage(...)` call. For projects that enable addressable or online sources, expect it to fire two or three times.
435
+
436
+ ### When to use it directly vs via LocalizationComponent
437
+
438
+ Use `LocalizationComponent` / `TMP_LocalizationComponent` for UI text objects — they subscribe and unsubscribe automatically and handle the refresh internally.
439
+
440
+ Subscribe to `OnUpdateText` directly when you need to refresh non-UI state, for example a data model, a mesh, or custom rendering that depends on localized strings.
441
+
442
+ ### Subscription and unsubscription
443
+
444
+ Always unsubscribe when the subscriber is destroyed or disabled to avoid calling a method on a dead object:
445
+
446
+ ```csharp
447
+ using UnityEngine;
448
+ using XmobiTea.MiniLocalization;
449
+
450
+ public sealed class ExampleLocalizationListener : MonoBehaviour
451
+ {
452
+ void OnEnable()
453
+ {
454
+ LocalizationManager.OnUpdateText -= Refresh;
455
+ LocalizationManager.OnUpdateText += Refresh;
456
+ Refresh();
457
+ }
458
+
459
+ void OnDisable()
460
+ {
461
+ LocalizationManager.OnUpdateText -= Refresh;
462
+ }
463
+
464
+ void Refresh()
465
+ {
466
+ var title = LocalizationManager.GetText(LocalizationConstantId.Home_Title);
467
+ // apply title to non-UI state
468
+ }
469
+ }
470
+ ```
471
+
472
+ The `-=` before `+=` in `OnEnable` prevents duplicate subscriptions if the object is enabled more than once.
473
+
474
+ ### Minimal example
475
+
331
476
  ```csharp
332
477
  using XmobiTea.MiniLocalization;
333
478
 
334
479
  LocalizationManager.OnUpdateText += () =>
335
480
  {
336
- UnityEngine.Debug.Log("Localization updated");
481
+ UnityEngine.Debug.Log("Localization updated");
337
482
  };
338
483
  ```
339
484
 
@@ -454,5 +599,5 @@ com.xmobitea.changx.mini-localization.editor
454
599
  ## Package Metadata
455
600
 
456
601
  - Package name: `com.xmobitea.changx.mini-localization`
457
- - Unity version: `2020.3+`
602
+ - Unity version: `2022.3+`
458
603
  - License: `Apache-2.0`
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "com.xmobitea.changx.mini-localization",
3
- "version": "1.4.10",
3
+ "version": "1.5.0",
4
4
  "displayName": "XmobiTea Localization",
5
5
  "description": "XmobiTea Unity Toolkit packages",
6
- "unity": "2020.3",
6
+ "unity": "2022.3",
7
7
  "dependencies": {
8
- "com.xmobitea.changx.app": "1.4.0",
9
- "com.xmobitea.changx.mini-autogenerate": "1.4.0",
10
- "com.xmobitea.changx.mini-singleton": "1.4.0"
8
+ "com.xmobitea.changx.app": "1.5.0",
9
+ "com.xmobitea.changx.mini-autogenerate": "1.5.0",
10
+ "com.xmobitea.changx.mini-singleton": "1.5.0"
11
11
  },
12
12
  "keywords": [
13
13
  "chang x",