# 宣传一下自己写的基于 web 技术的词典软件

**URL:** <https://forum.freemdict.com/t/topic/23724>\
**Category:** 软件经验交流展望\
**Created:** [2023 年9 月 30 日 04:39 UTC](https://forum.freemdict.com/t/topic/23724 "2023-09-30T04:39:00Z")\
**Posts on this page:** 1\
**Showing post:** 73

<div class="post-metadata">

作者： ![Vim](https://forumcdn.freemdict.com/user_avatar/forum.freemdict.com/vim/32/8408_2.png) [@Vim](https://forum.freemdict.com/u/Vim)\
发布日期： [2026 年6 月 1 日 15:41 UTC](https://forum.freemdict.com/t/topic/23724/73 "2026-06-01T15:41:55Z")

</div>

在 docker 中部署了 SilverDict，并成功添加了词典。

但是要想使用好，手头上的词典几乎都要调整适配，因为它的要求比较高：

1. 词典 mdx mdd 和 css 等的文件名称必须完全相同才可以。但我看很多词典的 css 等文件名都是不同的。仅这一条估计就得改一大批词典。很多词典采用多个 mdd，那么文件名也就不同，不知道其是否支持？
2. 虽然软件可以自动扫描添加词典，但对于多层嵌套的目录结构好像不支持，而我习惯将每个词典单独放在一个文件夹中，并放在某个类别目录下，这样方便维护。如果需要手工逐个添加词典，或手工维护词典分类，就很麻烦。
3. CSS，除非是那种简单的可以直接使用，但凡有点特别的，好像效果都消失了。不知道应该遵循什么规则来调试。

此外，总结了这个过程中遇到的 bug（结合 AI 分析来推断的）

> <https://github.com/Crissium/SilverDict/issues/37>
>
> \## 💻 Environment
> 
> \* \*\*OS:\*\* Linux (Synology NAS DSM 7.2)
> \* \*\*Deployment:\*\* Docke…r / Docker Compose
> \* \*\*Dictionary Library Size:\*\* Large-scale library (~428 dictionaries, ~100GB of .mdx/.mdd files)
> 
> \---
> 
> \## 🛑 Bug 1: Unhandled \`AttributeError\` during file-existence check failure (Cascading Failure)
> 
> \### 📝 Description 
> 
> When the application starts up, it reads \`dictionaries.yaml\` and verifies if each file physically exists using \`os.path.isfile()\`. If a dictionary file is missing, or returns \`False\` due to permission/encoding mismatches on Linux, the system attempts to remove it from the list by calling \`self.remove\_dictionary(dictionary\_info)\`.
> 
> However, during this early initialization phase, \`self.dictionary\_metadata\` \*\*has not been loaded or defined yet\*\*. This causes a fatal \`AttributeError\` which crashes the entire server container, trapping it in an infinite boot-loop.
> 
> \### 🔍 Root Cause in Source Code 
> 
> In \`settings.py\` around line 213 (inside \`\_\_init\_\_\`):
> 
> \`\`\`python
> if not os.path.isfile(dictionary\_info\['dictionary\_filename'\]):
> logger.warning(f'Dictionary {dictionary\_info\["dictionary\_name"\]} not found, removing from the list.')
> self.remove\_dictionary(dictionary\_info) # 🚨 Throws AttributeError because metadata hasn't initialized yet!
> 
> \`\`\`
> 
> The \`remove\_dictionary\` method explicitly iterates over \`self.dictionary\_metadata\`, which is defined \*\*later\*\* in the \`\_\_init\_\_\` sequence:
> 
> \`\`\`python
> def remove\_dictionary(self, dictionary\_info: str) -\> None:
> self.dictionaries\_list.remove(dictionary\_info)
> self.\_save\_dictionary\_list()
> for m in self.dictionary\_metadata: # ❌ AttributeError: 'Settings' object has no attribute 'dictionary\_metadata'
> ...
> 
> \`\`\`
> 
> \### 💡 Suggested Fix 
> 
> Move the block that loads \`self.dictionary\_metadata\` \*\*before\*\* the file-existence check loop, or wrap the early-stage \`remove\_dictionary\` call in a safe mechanism that doesn't expect metadata to exist yet.
> 
> \---
> 
> \## 🛑 Bug 2: \`KeyError\` in Logger Warning and Save Logic due to Refactored Dictionary Keys
> 
> \### 📝 Description 
> 
> During the recent refactoring where dictionary metadata keys changed between versions (e.g., introduction of \`dictionary\_filename\` and format enums), the error handling logs and file-saving sequences still rely on old key constraints.
> 
> When a dictionary triggers the "Not Found" warning mentioned in Bug 1, it crashes with \`KeyError: 'dictionary\_name'\` or \`KeyError: 'dictionary\_format'\` because the fallback code paths were not completely updated.
> 
> \### 🔍 Root Cause in Source Code 
> 
> 1. \*\*Logger KeyError:\*\* In \`settings.py\` line 212:
> \`\`\`python
> logger.warning(f'Dictionary {dictionary\_info\["dictionary\_name"\]} not found...')
> 
> \`\`\`
> 
> 
> If the YAML configuration relies purely on newer unique fields, \`dictionary\_name\` might raise a KeyError or crash the warning.
> 2. \*\*Format KeyError:\*\* In \`\_save\_dictionary\_list()\` around line 183:
> \`\`\`python
> for dictionary\_info in self.dictionaries\_list:
> if dictionary\_info\['dictionary\_format'\] == 'DSL (.dsl/.dsl.dz)'... # ❌ Throws KeyError if 'dictionary\_format' is absent during early removals
> 
> \`\`\`
> 
> 
> 
> \### 💡 Suggested Fix 
> 
> Use safe dictionary get methods (e.g., \`dictionary\_info.get('dictionary\_name', 'Unknown')\`) within the logging and serialization layers to prevent a simple warning from becoming a hard, breaking crash.
> 
> \---
> 
> \## 🛑 Bug 3: Automatic Scanning (\`scan\_sources\`) Is Unusable for Large/Complex Local Libraries
> 
> \### 📝 Description 
> 
> The built-in automatic source scanner (\`scan\_sources()\`) works well for simple English filenames, but completely breaks when encountering large-scale local dictionary folders that contain spaces, brackets, or non-ASCII (Chinese) characters.
> 
> The scanner directly uses the raw filename (minus extension) as both \`dictionary\_name\` and \`dictionary\_display\_name\`. When the server reboots and reads this newly generated list, the unescaped spaces and special characters in \`dictionary\_name\` fail inner path validation or database lookups, leading back to the catastrophic crash loops described in Bug 1 and 2.
> 
> \### 🔍 Root Cause in Source Code 
> 
> In \`scan\_source()\` around line 374:
> 
> \`\`\`python
> if filename.endswith('.dsl.dz'):
> name = filename\[:-len('.dsl.dz')\]
> else:
> name = os.path.splitext(filename)\[0\] # 🚨 Directly uses raw filename string with spaces/special characters
> yield {
> 'dictionary\_display\_name': name,
> 'dictionary\_name': name, # ❌ Becomes illegal database slug if it contains spaces/brackets/non-ASCII
> 'dictionary\_format': dictionary\_format,
> 'dictionary\_filename': full\_filename
> }
> 
> \`\`\`
> 
> \### 💡 Suggested Fix 
> 
> Implement a proper slugification algorithm or a regular expression filter (e.g., \`re.sub(r'\[^a-zA-Z0-9\_\]', '', name)\`) for generating the \`dictionary\_name\` field during scanning, keeping it completely alphanumeric and distinct from the user-facing \`dictionary\_display\_name\`.
> 
> \---
> 
> \## 🎨 Architectural Limitation Notice (Optional, for developer's awareness)
> 
> \### Strict Case-Sensitive Sandboxing for Asset Routing (.css/.mdd)
> 
> The web server's routing system rigidly maps a dictionary's asset folder (styles, images) using the exact string value of \`dictionary\_name\` (e.g., \`CACHE\_ROOT + "/" + dictionary\_name\`).
> 
> While desktop clients like GoldenDict gracefully read any arbitrary CSS filename specified inside the \`.mdx\` HTML layer, SilverDict will throw silent \`Entry not found\` or 404 styling errors if the physical \`.css\` file on a Linux filesystem does not precisely match the case-sensitive casing of the registered \`dictionary\_name\` id. It would be helpful to document this strict同名 naming convention or lowercase the asset routing path automatically.

---

_[View the full topic](https://forum.freemdict.com/t/topic/23724)._
