This is a follow-up issue after #947.
Feature(s) Requested
gen_oopsy_timeline_replace.ts automatically generates Oopsyraidsy's timelineReplace.
Draft: https://github.com/Jaehyuk-Lee/cactbot/blob/oopsy-i18n-data/util/gen_oopsy_timeline_replace.ts
See auto-generated examples
See: Jaehyuk-Lee@fd7ffad
CLI Output
$ node --loader=ts-node/esm util/gen_oopsy_timeline_replace.ts
(node:34236) ExperimentalWarning: `--experimental-loader` may be removed in the future; instead use `register()`:
--import 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));'
(Use `node --trace-warnings ...` to show where the warning was created)
(node:34236) [DEP0180] DeprecationWarning: fs.Stats constructor is deprecated.
(Use `node --trace-deprecation ...` to show where the warning was created)
Alert: ui/oopsyraidsy/data/05-shb/raid/e12s.ts: Multiple candidates for 'Ice Pillar' in cn: 冰柱, 氷柱
Alert: Using existing translation: '冰柱'
Alert: ui/oopsyraidsy/data/05-shb/raid/e12s.ts: Multiple candidates for 'Ice Pillar' in tc: 冰柱, 氷柱
Alert: Using existing translation: '冰柱'
Success: Updated: 13, Skipped: 242
How this work
- Search and process all data files from
ui/oopsyraidsy/data (process repeatedly file by file)
- Find all
source and target. Capture with regex /(?:source|target):\s*(?:['"]([^'"]+)['"]|\[((?:['"][^'"]+['"],?\s*)+)\])/g
- If there are no
source and target, skip the file.
- Fetch BNpcName data from xivapi, and ffxiv-datamining-ko / cn / tc
- Map "English BNpcName" → "BNpcName ID" (works case-insensitive)
- Map "BNpcName ID" → "localized BNpcName"
Handling edge cases
| New Data |
Multiple Candidates |
Existing Translation |
Action |
| ✓ Found |
✗ Single |
- |
Use new data |
| ✓ Found |
✓ Multiple |
✓ Exists |
Keep existing + console.warn() |
| ✓ Found |
✓ Multiple |
✗ None |
Output 'candidate1 / candidate2' (human review) |
| ✗ Not found |
- |
- |
Keep existing |
Example: Multiple Candidates Found
The English name "Lindwurm" has multiple Korean translations in the datamining:
- ID 130: "린드부름" (Lindwurm variant 1)
- ID 14378: "린드블룸" (Lindwurm variant 2)
If existing translation exists:
// Existing in file:
'Lindwurm': '린드블룸',
// Script finds: ['린드부름', '린드블룸']
// Action: Keep existing '린드블룸', output console.warn()
If no existing translation:
// No existing translation in file
// Script finds: ['린드부름', '린드블룸']
// Action: Output '린드부름 / 린드블룸' (requires human review)
Example: Multiple Candidates + No Existing Translation
When the script finds multiple translation candidates for a source name, but no existing translation exists in the file:
-
Output all candidates joined by " / "
'Lindwurm': '린드부름 / 린드블룸',
-
Output console.warn() with details
[WARNING] example.ts: Multiple candidates for 'Lindwurm' in ko: 린드부름, 린드블룸
No existing translation found. Manual review required.
-
Human review required
- The generated output is syntactically valid TypeScript
- However, the regex pattern
'린드부름 / 린드블룸' won't match actual game logs
- Developer must manually choose the correct translation for that specific encounter
- After review, update to single value:
'Lindwurm': '린드블룸',
When the script couldn't translate all source and target
missingTranslation: true, will be automatically added.
...(allTranslated ? [] : [` 'missingTranslations': true,`]),
Question for Deutsch
This question is resolved. Fixed by Jaehyuk-Lee@bae9985
#,Singular,Plural,Adjective,PossessivePronoun,StartsWithVowel,Unknown0,Pronoun,Article
9876,Alchemist[p] des 2. Ordens,Alchemisten[p] des 2. Ordens,6,1,0,1,0,0
9877,Ghomoro-Golem,Ghomoro-Golems,4,1,0,1,0,0
9878,Konstrukt[p] 2,Konstrukte[p] 2,4,2,0,1,2,0
9879,Granate,Granaten,3,1,0,1,1,0
9880,Patriarch[p] des 2. Ordens Za Da,Patriarchen[p] des 2. Ordens Za Da,6,1,0,1,0,0
9881,Felsspalter[p] des 2. Ordens,Felsspalter[p] des 2. Ordens,4,1,0,1,0,0
9882,Stimme[p] von Xeven,Stimme[p] von Xeven,3,2,0,1,1,0
9883,Stimme[p] von Stanik,Stimme[p] von Stanik,3,2,0,1,1,0
9884,Stimme[p] von Isolde,Stimme[p] von Isolde,3,2,0,1,1,0
Some BNpcName have [p], [a], or [t].
Candidate 1:
{
'locale': 'de',
'replaceSync': {
'Chiseled Sculpture': 'Abbild eines Mannes',
'Ice Pillar': 'Eissäule',
'Beastly Sculpture': 'Abbild eines Löwen',
'Regal Sculpture': 'Abbild eines großen Löwen',
},
},
Candidate 2:
{
'locale': 'de',
'replaceSync': {
'Chiseled Sculpture': 'Abbild[p] eines Mannes',
'Ice Pillar': 'Eissäule',
'Beastly Sculpture': 'Abbild[p] eines Löwen',
'Regal Sculpture': 'Abbild[p] eines großen Löwen',
},
},
Which one is the right one? Should all [x] text be removed? The gen_oopsy_timeline_replace is using Abbild eines Löwen (Candidate 1) for now.
This is a follow-up issue after #947.
Feature(s) Requested
gen_oopsy_timeline_replace.tsautomatically generates Oopsyraidsy's timelineReplace.Draft: https://github.com/Jaehyuk-Lee/cactbot/blob/oopsy-i18n-data/util/gen_oopsy_timeline_replace.ts
See auto-generated examples
See: Jaehyuk-Lee@fd7ffad
CLI Output
How this work
ui/oopsyraidsy/data(process repeatedly file by file)sourceandtarget. Capture with regex/(?:source|target):\s*(?:['"]([^'"]+)['"]|\[((?:['"][^'"]+['"],?\s*)+)\])/gsourceandtarget, skip the file.Handling edge cases
console.warn()'candidate1 / candidate2'(human review)Example: Multiple Candidates Found
The English name "Lindwurm" has multiple Korean translations in the datamining:
If existing translation exists:
If no existing translation:
Example: Multiple Candidates + No Existing Translation
When the script finds multiple translation candidates for a source name, but no existing translation exists in the file:
Output all candidates joined by " / "
Output
console.warn()with detailsHuman review required
'린드부름 / 린드블룸'won't match actual game logs'Lindwurm': '린드블룸',When the script couldn't translate all
sourceandtargetmissingTranslation: true,will be automatically added.Question for Deutsch
This question is resolved. Fixed by Jaehyuk-Lee@bae9985
Some BNpcName have
[p],[a], or[t].Candidate 1:
Candidate 2:
Which one is the right one? Should all
[x]text be removed? Thegen_oopsy_timeline_replaceis usingAbbild eines Löwen(Candidate 1) for now.