Explorar el Código

Merge pull request #2453 from visuddhinanda/development

Development
visuddhinanda hace 2 semanas
padre
commit
9146e6bcec
Se han modificado 42 ficheros con 2088 adiciones y 51 borrados
  1. 231 0
      api-v13/app/Console/Commands/ExportMobileHeading.php
  2. 12 12
      api-v13/app/Http/Controllers/PageIndexController.php
  3. 72 22
      api-v13/app/Http/Middleware/SetLocale.php
  4. 2 0
      api-v13/config/mint.php
  5. 49 0
      api-v13/resources/css/home.css
  6. 39 0
      api-v13/resources/lang/en/home.php
  7. 20 0
      api-v13/resources/lang/lo/auth.php
  8. 6 0
      api-v13/resources/lang/lo/buttons.php
  9. 199 0
      api-v13/resources/lang/lo/grammar.php
  10. 39 0
      api-v13/resources/lang/lo/home.php
  11. 32 0
      api-v13/resources/lang/lo/labels.php
  12. 10 0
      api-v13/resources/lang/lo/language.php
  13. 160 0
      api-v13/resources/lang/lo/library.php
  14. 19 0
      api-v13/resources/lang/lo/pagination.php
  15. 22 0
      api-v13/resources/lang/lo/passwords.php
  16. 33 0
      api-v13/resources/lang/lo/site.php
  17. 162 0
      api-v13/resources/lang/lo/validation.php
  18. 39 0
      api-v13/resources/lang/my/home.php
  19. 39 0
      api-v13/resources/lang/si/home.php
  20. 39 0
      api-v13/resources/lang/th/home.php
  21. 20 0
      api-v13/resources/lang/vi/auth.php
  22. 6 0
      api-v13/resources/lang/vi/buttons.php
  23. 199 0
      api-v13/resources/lang/vi/grammar.php
  24. 39 0
      api-v13/resources/lang/vi/home.php
  25. 32 0
      api-v13/resources/lang/vi/labels.php
  26. 10 0
      api-v13/resources/lang/vi/language.php
  27. 160 0
      api-v13/resources/lang/vi/library.php
  28. 19 0
      api-v13/resources/lang/vi/pagination.php
  29. 22 0
      api-v13/resources/lang/vi/passwords.php
  30. 33 0
      api-v13/resources/lang/vi/site.php
  31. 162 0
      api-v13/resources/lang/vi/validation.php
  32. 39 0
      api-v13/resources/lang/zh-Hans/home.php
  33. 1 1
      api-v13/resources/lang/zh-Hans/library.php
  34. 39 0
      api-v13/resources/lang/zh-Hant/home.php
  35. 1 1
      api-v13/resources/lang/zh-Hant/library.php
  36. 11 1
      api-v13/resources/views/components/language-switcher.blade.php
  37. 1 1
      api-v13/resources/views/layouts/base.blade.php
  38. 8 3
      api-v13/resources/views/pages/home.blade.php
  39. 2 2
      api-v13/resources/views/typhoon.blade.php
  40. 1 1
      dashboard-v6/src/components/article/TypeCourse.tsx
  41. 10 5
      dashboard-v6/src/features/editor/TextBook.tsx
  42. 49 2
      dashboard-v6/src/pages/workspace/course/textbook.tsx

+ 231 - 0
api-v13/app/Console/Commands/ExportMobileHeading.php

@@ -0,0 +1,231 @@
+<?php
+
+namespace App\Console\Commands;
+
+use App\Models\PaliText;
+use Illuminate\Console\Command;
+use Illuminate\Support\Facades\DB;
+
+/**
+ * 导出移动端离线目录库(SQLite)。
+ *
+ * 替代 wikipali-mobile 仓库里的 src/data/tipitaka_heading.json(5.5MB,
+ * 全量载入内存)。导出 pali_texts 全部段落(不只是章节行,下载功能需要
+ * 非章节段落的字符数),除原有目录字段外额外带上:
+
+ *  - length:段落字符数(源表列名 `lenght` 为拼写错误,导出时纠正)
+ *
+ *  - tags:pali_texts.uid 经 tag_maps / tags 取到的标签名,逗号分隔
+ *  - cs_para + book_name:来自 related_paragraphs,用于由段落定位对应的
+ *    义注 / 复注章节起始位置(已合并进同一张表,输出只有 pali_text 一张表)
+ */
+class ExportMobileHeading extends Command
+{
+    protected $signature = 'export:mobile.heading
+        {--out= : 输出文件路径,默认 storage/app/public/export/mobile/tipitaka-heading-<date>.db3}
+        {--copy-to= : 导出后额外复制一份到该路径(例如移动端仓库的 assets/db/tipitaka.db3)}';
+
+    protected $description = '导出移动端离线目录 SQLite(含 tags 与义注复注关联段落)';
+
+    public function handle(): int
+    {
+        $out = $this->option('out');
+        if (! $out) {
+            $dir = storage_path('app/public/export/mobile');
+            if (! is_dir($dir)) {
+                mkdir($dir, 0775, true);
+            }
+            $out = $dir.'/tipitaka-heading-'.date('Y-m-d').'.db3';
+        }
+        if (file_exists($out)) {
+            unlink($out);
+        }
+
+        $dbh = new \PDO('sqlite:'.$out, '', '', [\PDO::ATTR_PERSISTENT => true]);
+        $dbh->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+        $this->createSchema($dbh);
+
+        $texts = $this->exportPaliTexts($dbh);
+
+        $this->writeMeta($dbh, [
+            'generated_at' => date('c'),
+            'source' => config('app.url', ''),
+            'pali_text_rows' => (string) $texts,
+        ]);
+
+        // 建索引放在插入之后,避免边插边维护索引
+        $this->createIndexes($dbh);
+        $dbh->exec('VACUUM');
+        $dbh = null;
+
+        $this->newLine();
+        $this->info(sprintf(
+            '导出完成:%s(%s,pali_text %d 行)',
+            $out,
+            $this->humanSize(filesize($out)),
+            $texts
+        ));
+
+        $copyTo = $this->option('copy-to');
+        if ($copyTo) {
+            $dir = dirname($copyTo);
+            if (! is_dir($dir)) {
+                mkdir($dir, 0775, true);
+            }
+            copy($out, $copyTo);
+            $this->info('已复制到:'.$copyTo);
+        }
+
+        return 0;
+    }
+
+    private function createSchema(\PDO $dbh): void
+    {
+        // 全量段落(不只是章节标题行):下载功能需要非章节段落的字符数。
+        // 关联注释书(cs_para / book_name)已合并进本表 —— 每个段落至多对应
+        // 一部注释书,无对应时为 NULL。
+        $dbh->exec('CREATE TABLE pali_text (
+            book INTEGER NOT NULL,
+            paragraph INTEGER NOT NULL,
+            level INTEGER NOT NULL,
+            toc TEXT,
+            length INTEGER,
+            chapter_len INTEGER,
+            chapter_strlen INTEGER,
+            parent INTEGER,
+            tags TEXT,
+            cs_para INTEGER,
+            book_name TEXT,
+            PRIMARY KEY (book, paragraph)
+        )');
+
+        $dbh->exec('CREATE TABLE meta (key TEXT PRIMARY KEY, value TEXT)');
+    }
+
+    private function createIndexes(\PDO $dbh): void
+    {
+        $dbh->exec('CREATE INDEX idx_pali_text_book_level ON pali_text (book, level)');
+        $dbh->exec('CREATE INDEX idx_pali_text_parent ON pali_text (book, parent)');
+        $dbh->exec('CREATE INDEX idx_pali_text_related ON pali_text (book_name, cs_para)');
+    }
+
+    private function exportPaliTexts(\PDO $dbh): int
+    {
+        $total = PaliText::count();
+        $this->line("导出 pali_text(全量段落):{$total} 行");
+        $bar = $this->output->createProgressBar($total);
+        $bar->setRedrawFrequency(5000);
+
+        // uid 只用于 join 标签,不写入结果表
+        $tagsByUid = $this->loadTags();
+        $related = $this->loadRelated();
+
+        $stmt = $dbh->prepare('INSERT INTO pali_text
+            (book, paragraph, level, toc, length, chapter_len, chapter_strlen,
+             parent, tags, cs_para, book_name)
+            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)');
+
+        $dbh->beginTransaction();
+        $n = 0;
+        foreach (
+            PaliText::select(['uid', 'book', 'paragraph', 'level', 'toc',
+                'lenght', 'chapter_len', 'chapter_strlen', 'parent'])
+                ->orderBy('book')
+                ->orderBy('paragraph')
+                ->cursor() as $row
+        ) {
+            $rel = $related[$row->book.'-'.$row->paragraph] ?? null;
+            $stmt->execute([
+                $row->book,
+                $row->paragraph,
+                $row->level,
+                $row->toc !== '' ? $row->toc : null,
+                // 源表列名 `lenght` 是拼写错误,导出时纠正为 `length`
+                $row->lenght,
+                $row->chapter_len,
+                $row->chapter_strlen,
+                $row->parent,
+                isset($tagsByUid[$row->uid]) ? implode(',', $tagsByUid[$row->uid]) : null,
+                $rel[0] ?? null,
+                $rel[1] ?? null,
+            ]);
+            $n++;
+            $bar->advance();
+        }
+        $dbh->commit();
+        $bar->finish();
+        $this->newLine();
+
+        return $n;
+    }
+
+    /**
+     * 关联注释书:由段落 (book, para) 找到对应义注 / 复注的起始位置。
+     *
+     * related_paragraphs 中同一段落会有多行,映射到一段连续的 cs_para 区间,
+     * 但 book_name 唯一,因此这里取 min(cs_para) 作为起始位置合并成一行。
+     * book_name 为空表示该段落没有对应的注释书,直接跳过(结果列留 NULL)。
+     *
+     * @return array<string, array{0:int,1:string}> "book-para" => [cs_para, book_name]
+     */
+    private function loadRelated(): array
+    {
+        $this->line('载入 related_paragraphs …');
+        $out = [];
+        DB::table('related_paragraphs')
+            ->select(['book', 'para', 'book_name', DB::raw('min(cs_para) as cs_para')])
+            ->whereNotNull('book_name')
+            ->where('book_name', '<>', '')
+            ->groupBy('book', 'para', 'book_name')
+            ->orderBy('book')
+            ->orderBy('para')
+            ->chunk(50000, function ($rows) use (&$out) {
+                foreach ($rows as $r) {
+                    $out[$r->book.'-'.$r->para] = [(int) $r->cs_para, $r->book_name];
+                }
+            });
+        $this->line('  有注释书的段落:'.count($out));
+
+        return $out;
+    }
+
+    /** @return array<string, string[]> uid => tag names */
+    private function loadTags(): array
+    {
+        $this->line('载入 tag_maps / tags …');
+        $out = [];
+        DB::table('tag_maps')
+            ->join('tags', 'tags.id', '=', 'tag_maps.tag_id')
+            ->where('tag_maps.table_name', 'pali_texts')
+            ->select(['tag_maps.anchor_id', 'tags.name'])
+            ->orderBy('tag_maps.anchor_id')
+            ->chunk(20000, function ($rows) use (&$out) {
+                foreach ($rows as $r) {
+                    $out[$r->anchor_id][] = $r->name;
+                }
+            });
+        $this->line('  标签锚点:'.count($out));
+
+        return $out;
+    }
+
+    private function writeMeta(\PDO $dbh, array $meta): void
+    {
+        $stmt = $dbh->prepare('INSERT INTO meta (key, value) VALUES (?, ?)');
+        foreach ($meta as $k => $v) {
+            $stmt->execute([$k, $v]);
+        }
+    }
+
+    private function humanSize(int $bytes): string
+    {
+        $units = ['B', 'KB', 'MB', 'GB'];
+        $i = 0;
+        while ($bytes >= 1024 && $i < count($units) - 1) {
+            $bytes /= 1024;
+            $i++;
+        }
+
+        return round($bytes, 1).' '.$units[$i];
+    }
+}

+ 12 - 12
api-v13/app/Http/Controllers/PageIndexController.php

@@ -11,49 +11,49 @@ class PageIndexController extends Controller
             [
             [
                 'slug' => 'library',
                 'slug' => 'library',
                 'eyebrow' => 'Library',
                 'eyebrow' => 'Library',
-                'title' => '圣典文库',
-                'lead' => '三藏 · 义注 · 复注 · 字典',
+                'title' => __('home.cards.library.title'),
+                'lead' => __('home.cards.library.lead'),
                 'image' => 'library',
                 'image' => 'library',
                 'tint' => '#2E4A63',
                 'tint' => '#2E4A63',
                 'align' => 'start',
                 'align' => 'start',
                 'href' => '/library',
                 'href' => '/library',
-                'cta' => '进入文库',
+                'cta' => __('home.cards.library.cta'),
                 'available' => true,
                 'available' => true,
             ],
             ],
             [
             [
                 'slug' => 'course',
                 'slug' => 'course',
                 'eyebrow' => 'Course',
                 'eyebrow' => 'Course',
-                'title' => '次第课程',
-                'lead' => '从零开始,逐部研读',
+                'title' => __('home.cards.course.title'),
+                'lead' => __('home.cards.course.lead'),
                 'image' => 'course',
                 'image' => 'course',
                 'tint' => '#2A5257',
                 'tint' => '#2A5257',
                 'align' => 'end',
                 'align' => 'end',
                 'href' => '/library/course',
                 'href' => '/library/course',
-                'cta' => '开始学习',
+                'cta' => __('home.cards.course.cta'),
                 'available' => true,
                 'available' => true,
             ],
             ],
             [
             [
                 'slug' => 'workspace',
                 'slug' => 'workspace',
                 'eyebrow' => 'Workspace',
                 'eyebrow' => 'Workspace',
-                'title' => '翻译工作台',
-                'lead' => '术语、协作、校对、发布',
+                'title' => __('home.cards.workspace.title'),
+                'lead' => __('home.cards.workspace.lead'),
                 'image' => 'workspace',
                 'image' => 'workspace',
                 'tint' => '#2C4A3C',
                 'tint' => '#2C4A3C',
                 'align' => 'start',
                 'align' => 'start',
                 'href' => '/pcd-v2026/workspace',
                 'href' => '/pcd-v2026/workspace',
-                'cta' => '进入工作台',
+                'cta' => __('home.cards.workspace.cta'),
                 'available' => true,
                 'available' => true,
             ],
             ],
             [
             [
                 'slug' => 'development',
                 'slug' => 'development',
                 'eyebrow' => 'Development',
                 'eyebrow' => 'Development',
-                'title' => '二次开发',
-                'lead' => 'API · MCP · GitHub',
+                'title' => __('home.cards.development.title'),
+                'lead' => __('home.cards.development.lead'),
                 'image' => 'development',
                 'image' => 'development',
                 'tint' => '#4A4A48',
                 'tint' => '#4A4A48',
                 'align' => 'end',
                 'align' => 'end',
                 'href' => null,
                 'href' => null,
-                'cta' => '即将上线',
+                'cta' => __('home.cards.development.cta'),
                 'available' => false,
                 'available' => false,
             ],
             ],
         ];
         ];

+ 72 - 22
api-v13/app/Http/Middleware/SetLocale.php

@@ -17,27 +17,25 @@ class SetLocale
      */
      */
     public function handle(Request $request, Closure $next): Response
     public function handle(Request $request, Closure $next): Response
     {
     {
-        // 支持的语言(Laravel 推荐小写 + 标准化
+        // 支持的语言(config 里是 en / zh-Hans 这类带大小写的写法
         $supportedLocales = array_keys(config('mint.languages'));
         $supportedLocales = array_keys(config('mint.languages'));
         $defaultLocale = config('app.locale', 'en');
         $defaultLocale = config('app.locale', 'en');
 
 
         // 1️⃣ URL 参数 ?lang=
         // 1️⃣ URL 参数 ?lang=
-        $locale = $request->query('lang');
+        $locale = $this->matchLocale($request->query('lang'), $supportedLocales);
 
 
         // 2️⃣ Cookie
         // 2️⃣ Cookie
         if (! $locale) {
         if (! $locale) {
-            $locale = Cookie::get('language');
+            $locale = $this->matchLocale(Cookie::get('language'), $supportedLocales);
         }
         }
 
 
-        // 3️⃣ 浏览器语言
+        // 3️⃣ 浏览器 / 操作系统语言(Accept-Language 由浏览器按系统界面语言生成)
         if (! $locale) {
         if (! $locale) {
             $locale = $this->getBrowserLocale($request, $supportedLocales);
             $locale = $this->getBrowserLocale($request, $supportedLocales);
         }
         }
 
 
-        // 4️⃣ 校验
-        if (! in_array($locale, $supportedLocales, true)) {
-            $locale = $defaultLocale;
-        }
+        // 4️⃣ 兜底
+        $locale = $locale ?? $defaultLocale;
 
 
         // 5️⃣ 应用语言
         // 5️⃣ 应用语言
         App::setLocale($locale);
         App::setLocale($locale);
@@ -51,32 +49,84 @@ class SetLocale
 
 
     /**
     /**
      * 从 Accept-Language 头中解析浏览器语言
      * 从 Accept-Language 头中解析浏览器语言
+     *
+     * @param  string[]  $supportedLocales
      */
      */
-    protected function getBrowserLocale(Request $request, array $supportedLocales): string
+    protected function getBrowserLocale(Request $request, array $supportedLocales): ?string
     {
     {
         $acceptLanguage = $request->header('Accept-Language');
         $acceptLanguage = $request->header('Accept-Language');
 
 
         if (! $acceptLanguage) {
         if (! $acceptLanguage) {
-            return config('app.locale', 'en');
+            return null;
         }
         }
 
 
-        // zh-CN,zh;q=0.9,en;q=0.8
-        $languages = array_map(
-            fn ($lang) => strtolower(trim(explode(';', $lang)[0])),
-            explode(',', $acceptLanguage)
-        );
+        // zh-CN,zh;q=0.9,en;q=0.8 → 按 q 值从高到低排序
+        $languages = [];
 
 
-        foreach ($languages as $lang) {
-            // zh-cn → zh-hans / zh-hant
-            if (str_starts_with($lang, 'zh')) {
-                return str_contains($lang, 'hant') ? 'zh-hant' : 'zh-hans';
+        foreach (explode(',', $acceptLanguage) as $part) {
+            $pieces = explode(';', trim($part));
+            $tag = strtolower(trim($pieces[0]));
+
+            if ($tag === '' || $tag === '*') {
+                continue;
             }
             }
 
 
-            if (in_array($lang, $supportedLocales, true)) {
-                return $lang;
+            $quality = 1.0;
+
+            if (isset($pieces[1]) && preg_match('/q=([0-9.]+)/', $pieces[1], $matches)) {
+                $quality = (float) $matches[1];
             }
             }
+
+            $languages[] = ['tag' => $tag, 'quality' => $quality];
+        }
+
+        usort($languages, fn ($a, $b) => $b['quality'] <=> $a['quality']);
+
+        foreach ($languages as $language) {
+            if ($locale = $this->matchLocale($language['tag'], $supportedLocales)) {
+                return $locale;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * 把一个语言标签匹配到受支持的语言,匹配不上返回 null。
+     * 大小写不敏感;zh-CN / zh-SG 归为简体,zh-TW / zh-HK / zh-MO 归为繁体;
+     * 其余按主语言回退,如 th-TH → th。
+     *
+     * @param  string[]  $supportedLocales
+     */
+    protected function matchLocale(?string $tag, array $supportedLocales): ?string
+    {
+        if (! $tag) {
+            return null;
         }
         }
 
 
-        return config('app.locale', 'en');
+        $tag = strtolower(str_replace('_', '-', trim($tag)));
+
+        // lowercase => 原始写法,便于大小写不敏感匹配
+        $lookup = array_combine(array_map('strtolower', $supportedLocales), $supportedLocales);
+
+        // 精确匹配:en、zh-hans……
+        if (isset($lookup[$tag])) {
+            return $lookup[$tag];
+        }
+
+        // 中文按地区码判断简繁
+        if (str_starts_with($tag, 'zh')) {
+            $traditional = str_contains($tag, 'hant')
+                || (bool) preg_match('/\-(tw|hk|mo)\b/', $tag);
+
+            $chinese = $traditional ? 'zh-hant' : 'zh-hans';
+
+            return $lookup[$chinese] ?? null;
+        }
+
+        // 主语言回退:th-th → th
+        $primary = explode('-', $tag)[0];
+
+        return $lookup[$primary] ?? null;
     }
     }
 }
 }

+ 2 - 0
api-v13/config/mint.php

@@ -20,6 +20,8 @@ return [
         'my' => 'မြန်မာဘာသာ',
         'my' => 'မြန်မာဘာသာ',
         'th' => 'ภาษาไทย',
         'th' => 'ภาษาไทย',
         'si' => 'සිංහල',
         'si' => 'සිංහල',
+        'vi' => 'Tiếng Việt',
+        'lo' => 'ພາສາລາວ',
     ],
     ],
     'default_language' => 'en',
     'default_language' => 'en',
     'library' => [
     'library' => [

+ 49 - 0
api-v13/resources/css/home.css

@@ -30,6 +30,55 @@
     background: var(--home-bg);
     background: var(--home-bg);
 }
 }
 
 
+/* 3.5 右上角语言选择(复用 x-language-switcher,改为浅底深字) */
+.home {
+    position: relative;
+}
+
+.home-lang {
+    position: absolute;
+    top: 1rem;
+    right: 1.5rem;
+    z-index: 10;
+}
+
+.home-lang .language-link,
+.home-lang .language-link:hover {
+    color: var(--home-text);
+}
+
+@media (max-width: 768px) {
+    .home-lang {
+        top: .75rem;
+        right: 1rem;
+    }
+
+    /* 组件移动端默认展开为静态列表,首页仍保持下拉 */
+    .home-lang .language-switcher {
+        width: auto;
+    }
+
+    .home-lang .language-dropdown {
+        position: absolute;
+        right: 0;
+        opacity: 0;
+        visibility: hidden;
+        transform: translateY(-10px);
+        background: rgba(0, 0, 0, .85);
+        -webkit-backdrop-filter: blur(10px);
+        backdrop-filter: blur(10px);
+        border: 1px solid rgba(255, 255, 255, .1);
+        margin-top: .5rem;
+        padding: .5rem 0;
+    }
+
+    .home-lang .language-switcher:hover .language-dropdown {
+        opacity: 1;
+        visibility: visible;
+        transform: translateY(0);
+    }
+}
+
 /* 4. 品牌 Hero */
 /* 4. 品牌 Hero */
 .home-hero {
 .home-hero {
     display: flex;
     display: flex;

+ 39 - 0
api-v13/resources/lang/en/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · Pali Buddhist Encyclopedia',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'Pali Buddhist Encyclopedia — step by step into the world of the Pali canon',
+    'cards' => [
+        'library' => [
+            'title' => 'Tipiṭaka Library',
+            'lead' => 'Tipiṭaka · Commentary · Subcommentary · Dictionary',
+            'cta' => 'Enter the library',
+        ],
+        'course' => [
+            'title' => 'Graded Courses',
+            'lead' => 'Start from zero, study text by text',
+            'cta' => 'Start learning',
+        ],
+        'workspace' => [
+            'title' => 'Translation Workspace',
+            'lead' => 'Terms, collaboration, proofreading, publishing',
+            'cta' => 'Enter the workspace',
+        ],
+        'development' => [
+            'title' => 'Development',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'Coming soon',
+        ],
+    ],
+];

+ 20 - 0
api-v13/resources/lang/lo/auth.php

@@ -0,0 +1,20 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+
+    'failed' => 'ຂໍ້ມູນເຂົ້າສູ່ລະບົບບໍ່ກົງກັບຂໍ້ມູນຂອງພວກເຮົາ.',
+    'password' => 'ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ.',
+    'throttle' => 'ພະຍາຍາມເຂົ້າສູ່ລະບົບຫຼາຍເກີນໄປ. ກະລຸນາລອງໃໝ່ໃນອີກ :seconds ວິນາທີ.',
+
+];

+ 6 - 0
api-v13/resources/lang/lo/buttons.php

@@ -0,0 +1,6 @@
+<?php
+
+return [
+    'more' => 'ເພີ່ມເຕີມ',
+    'online-read' => 'ອ່ານອອນລາຍ',
+];

+ 199 - 0
api-v13/resources/lang/lo/grammar.php

@@ -0,0 +1,199 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | site Language Lines
+    |--------------------------------------------------------------------------
+    |
+    |
+    */
+
+    'n' => 'ຄຳນາມ',
+    'n.short' => 'ນ',
+    'ti' => 'ສາມລິງ',
+    'ti.short' => '3ລ',
+    'v' => 'ຄຳກິລິຍາ',
+    'v.short' => 'ກ',
+    'v:ind' => 'ກິລິຍາບໍ່ປ່ຽນຮູບ',
+    'v:ind.short' => 'ກ.ຄງ',
+    'ind' => 'ຄຳບໍ່ປ່ຽນຮູບ',
+    'ind.short' => 'ຄງ',
+    'm' => 'ປຸງລິງ',
+    'm.short' => 'ປ',
+    'nt' => 'ນະປຸງສະກະລິງ',
+    'nt.short' => 'ນປ',
+    'f' => 'ອິດຖີລິງ',
+    'f.short' => 'ອ',
+    'sg' => 'ເອກະພົດ',
+    'sg.short' => 'ອກ',
+    'pl' => 'ພະຫູພົດ',
+    'pl.short' => 'ພຫ',
+    'nom' => 'ປະຖະມາວິພັດ (ຜູ້ກະທຳ)',
+    'nom.short' => 'ປຖ',
+    'acc' => 'ທຸຕິຍາວິພັດ (ກຳ)',
+    'acc.short' => 'ທຕ',
+    'gen' => 'ສາມີສຳພັນ (ຂອງ)',
+    'gen.short' => 'ສາມີ',
+    'dat' => 'ຈະຕຸດຖີວິພັດ (ແກ່)',
+    'dat.short' => 'ຈຕ',
+    'inst' => 'ຕະຕິຍາວິພັດ (ດ້ວຍ)',
+    'inst.short' => 'ຕຕ',
+    'voc' => 'ອາລະປະນະ (ຮ້ອງເອີ້ນ)',
+    'voc.short' => 'ອາລ',
+    'abl' => 'ປັນຈະມີວິພັດ (ຈາກ)',
+    'abl.short' => 'ປຈ',
+    'loc' => 'ສັດຕະມີວິພັດ (ໃນ)',
+    'loc.short' => 'ສຕ',
+    'base' => 'ຮາກສັບ',
+    'base.short' => 'ຮາກ',
+    'imp' => 'ຄຳສັ່ງ',
+    'imp.short' => 'ສັ່ງ',
+    'cond' => 'ເງື່ອນໄຂ',
+    'cond.short' => 'ເງື່ອນ',
+    'opt' => 'ຄວາມເປັນໄປໄດ້',
+    'opt.short' => 'ອາດ',
+    'pres' => 'ປັດຈຸບັນ',
+    'pres.short' => 'ປັດຈຸ',
+    'aor' => 'ອະດີດ',
+    'aor.short' => 'ອະດີດ',
+    'pf' => 'ສົມບູນ',
+    'pf.short' => 'ສມບ',
+    'fut' => 'ອະນາຄົດ',
+    'fut.short' => 'ອະນາ',
+    'act' => 'ກັດຕຸວາຈົກ (ປະທານ)',
+    'act.short' => 'ກັດ',
+    'refl' => 'ສະທ້ອນຕົນ',
+    'refl.short' => 'ສະທ້ອນ',
+    '1p' => 'ບຸລຸດທີ 1',
+    '1p.short' => 'ບ1',
+    '2p' => 'ບຸລຸດທີ 2',
+    '2p.short' => 'ບ2',
+    '3p' => 'ບຸລຸດທີ 3',
+    '3p.short' => 'ບ3',
+    'prp' => 'ກິລິຍາວິເສດປັດຈຸບັນ',
+    'prp.short' => 'ກວປ',
+    'prpp' => 'ກິລິຍາວິເສດປັດຈຸບັນ ກຳມະວາຈົກ',
+    'prpp.short' => 'ກວປກ',
+    'pp' => 'ກິລິຍາວິເສດອະດີດ',
+    'pp.short' => 'ກວອ',
+    'ppa' => 'ກິລິຍາວິເສດອະດີດ ກັດຕຸວາຈົກ',
+    'ppa.short' => 'ກວອກ',
+    'ppp' => 'ກິລິຍາວິເສດອະດີດ ກຳມະວາຈົກ',
+    'ppp.short' => 'ກວອກມ',
+    'futp' => 'ກິລິຍາວິເສດອະນາຄົດ',
+    'futp.short' => 'ກວອນ',
+    'fpa.short' => 'fpa',
+    'grd' => 'ກິລິຍາທີ່ຄວນເຮັດ',
+    'grd.short' => 'ຄວນ',
+    'pass' => 'ກຳມະວາຈົກ (ຖືກກະທຳ)',
+    'pass.short' => 'ກຳ',
+    'caus' => 'ເຫດຸກັດຕາ (ໃຫ້ເຮັດ)',
+    'caus.short' => 'ເຫດ',
+    'desid' => 'ຄວາມປາຖະໜາ',
+    'desid.short' => 'ປາຖ',
+    'intens' => 'ເນັ້ນຄວາມ',
+    'intens.short' => 'ເນັ້ນ',
+    'denom' => 'ແປງຈາກຄຳນາມ',
+    'denom.short' => 'ແປງນາມ',
+    'ger' => 'ນາມກິລິຍາ',
+    'ger.short' => 'ນກ',
+    'abs' => 'ກິລິຍາຕໍ່ເນື່ອງ',
+    'abs.short' => 'ຕໍ່',
+    'inf' => 'ຮູບເດີມ (ເພື່ອ)',
+    'inf.short' => 'ເດີມ',
+    'adj' => 'ຄຳຄຸນນາມ',
+    'adj.short' => 'ຄຸນ',
+    'pron' => 'ຄຳສັບພະນາມ',
+    'pron.short' => 'ສັບພ',
+    'num' => 'ຄຳຈຳນວນ',
+    'num.short' => 'ຈຳນວນ',
+    'adv' => 'ຄຳວິເສດ',
+    'adv.short' => 'ວິເສດ',
+    'conj' => 'ຄຳສັນທານ',
+    'conj.short' => 'ສັນ',
+    'prep' => 'ຄຳບຸບພະບົດ',
+    'prep.short' => 'ບຸບ',
+    'interj' => 'ຄຳອຸທານ',
+    'interj.short' => 'ອຸທ',
+    'pre' => 'ອຸປະສັກ (ໜ້າ)',
+    'pre.short' => 'ໜ້າ',
+    'suf' => 'ປັດໄຈ (ຫຼັງ)',
+    'suf.short' => 'ຫຼັງ',
+    'end' => 'ວິພັດ (ທ້າຍ)',
+    'end.short' => 'ທ້າຍ',
+    'part' => 'ອົງປະກອບ',
+    'part.short' => 'ອົງ',
+    'un' => 'ສົນທິ',
+    'un.short' => 'ສົນທິ',
+    'none' => 'ບໍ່ມີ',
+    'none.short' => '_',
+    'null' => 'ຫວ່າງເປົ່າ',
+    'null.short' => '_',
+    '?' => '?',
+    '?.short' => '?',
+    'ti:base' => 'ຮາກສັບສາມລິງ',
+    'ti:base.short' => '3ລ.ຮາກ',
+    'n:base' => 'ຮາກຄຳນາມ',
+    'n:base.short' => 'ນ.ຮາກ',
+    'v:base' => 'ຮາກຄຳກິລິຍາ',
+    'v:base.short' => 'ກ.ຮາກ',
+    'adj:base' => 'ຮາກຄຳຄຸນນາມ',
+    'adj:base.short' => 'ຄຸນ.ຮາກ',
+    'fpp' => 'ກິລິຍາວິເສດອະນາຄົດ ກຳມະວາຈົກ',
+    'fpp.short' => 'ກວອນກ',
+    'cp.short' => 'ສະມາດ',
+    'cp' => 'ອົງປະກອບຂອງສະມາດ',
+    'indconj.short' => 'ສັນ',
+    'indconj' => 'ຄຳສັນທານບໍ່ປ່ຽນຮູບ',
+    'pron:base.short' => 'ສັບພ.ຮາກ',
+    'pron:base' => 'ຮາກຄຳສັບພະນາມ',
+    'note.short' => 'ໝາຍເຫດ',
+    'note' => 'ໝາຍເຫດ',
+    'vind.short' => 'ກ.ຄງ',
+    'vind' => 'ກິລິຍາບໍ່ປ່ຽນຮູບ',
+    'vdn.short' => 'ນ.ກ',
+    'vdn' => 'ນາມທີ່ມາຈາກກິລິຍາ',
+    'relations.iad.label' => 'ຄຳຂະຫຍາຍປະກອບ',
+    'relations.asv.label' => 'ຜູ້ກະທຳ → ກິລິຍາ',
+    'relations.aov.label' => 'ກຳ → ກິລິຍາ',
+    'relations.daso-p.label' => 'ປະທານ → ກິລິຍາເຊື່ອມ',
+    'relations.daso-s.label' => 'ພາກສະແດງ → ກິລິຍາເຊື່ອມ',
+    'relations.nio.label' => 'ສິ່ງທີ່ຖືກອະທິບາຍ (ປະທານ) → ຄຳຂະຫຍາຍ (ພາກສະແດງ)',
+    'relations.nid.label' => 'ສິ່ງທີ່ຖືກຕັ້ງຊື່ → ຊື່ທີ່ຕັ້ງ',
+    'relations.dasd-p.label' => '<ການຕັ້ງຊື່> ປະທານ → ກິລິຍາເຊື່ອມ',
+    'relations.dasd-s.label' => '<ການຕັ້ງຊື່> ພາກສະແດງ → ກິລິຍາເຊື່ອມ',
+    'relations.dao-p.label' => 'ກຳສອງຊັ້ນ ກຳມະວາຈົກ - ຫຼັກ',
+    'relations.dao-s.label' => 'ກຳສອງຊັ້ນ ກຳມະວາຈົກ - ຮອງ',
+    'relations.iov.label' => 'ກຳ → ກິລິຍາ',
+    'relations.dio-p.label' => 'ກຳສອງຊັ້ນ <ຫຼັກ> → ກິລິຍາ',
+    'relations.dio-s.label' => 'ກຳສອງຊັ້ນ <ຮອງ> → ກິລິຍາ',
+    'relations.dis-p.label' => 'ປະທານ → ກິລິຍາເຊື່ອມ (ກຳມະວາຈົກ)',
+    'relations.dis-s.label' => 'ພາກສະແດງ → ກິລິຍາເຊື່ອມ (ກຳມະວາຈົກ)',
+    'relations.stc.label' => 'ບໍລິບົດເວລາ-ສະຖານທີ່ → ການກະທຳທີ່ດຳເນີນຢູ່',
+    'relations.adv.label' => 'ຄຳຂະຫຍາຍກິລິຍາ → ກິລິຍາ',
+    'relations.imp.label' => 'ລັກສະນະການກະທຳ → ກິລິຍາ',
+    'relations.soe.label' => 'ການໄປນຳກັນ <ມີຄຳສັນທານ>',
+    'relations.soi.label' => 'ການໄປນຳກັນ <ບໍ່ມີຄຳສັນທານ>',
+    'relations.isv.label' => 'ຜູ້ກະທຳ <ບໍ່ແມ່ນປະຖະມາວິພັດ> → ກິລິຍາ',
+    'relations.cau.label' => 'ເຫດ → ຜົນ / ການອ້າງອີງ',
+    'relations.iov-c.label' => 'ກຳຂອງເຫດຸກັດຕາ → ກິລິຍາ',
+    'relations.adj.label' => 'ຄຳຂະຫຍາຍນາມ → ຄຳທີ່ຖືກຂະຫຍາຍ',
+    'relations.rec.label' => 'ຜູ້ຮັບ → ການໃຫ້',
+    'relations.pur.label' => 'ຈຸດປະສົງ → ກິລິຍາ',
+    'relations.det.label' => 'ຈຸດເລີ່ມຕົ້ນ → ການຈາກໄປ',
+    'relations.coc.label' => 'ການແຍກຄວາມແຕກຕ່າງ / ການປຽບທຽບ',
+    'relations.pos.label' => 'ເຈົ້າຂອງ → ສິ່ງທີ່ຖືກຄອບຄອງ',
+    'relations.coi.label' => 'ສ່ວນລວມ [ກຸ່ມ] → ສ່ວນຍ່ອຍ / ກຸ່ມ → ສະມາຊິກ',
+    'relations.lov.label' => 'ພາຊະນະ → ກິລິຍາ / ພາຊະນະ → ສິ່ງທີ່ບັນຈຸ',
+    'relations.mot.label' => 'ການສະແດງອອກ → ຜູ້ທີ່ມີການສະແດງອອກ',
+    'relations.whp.label' => 'ສ່ວນລວມ → ສ່ວນຍ່ອຍ',
+    'relations.def.label' => 'ລັກສະນະທີ່ນິຍາມ',
+    'relations.ac.label' => 'ອະນຸປະໂຫຍກອິດສະລະ',
+    'relations.avc.label' => 'ອະນຸປະໂຫຍກອິດສະລະຕາມວາຈົກ',
+    'relations.qus.label' => 'ພາຍໃນເຄື່ອງໝາຍອ້າງອີງ → ເຄື່ອງໝາຍອ້າງອີງ',
+    'relations.qum.label' => 'ເຄື່ອງໝາຍອ້າງອີງ → ພາຍນອກເຄື່ອງໝາຍອ້າງອີງ',
+    'relations.enu.label' => 'ການລຳດັບ → ຂີດກາງ',
+    'relations.enm.label' => 'ຂີດກາງ → ສິ່ງທີ່ຖືກລຳດັບ',
+];

+ 39 - 0
api-v13/resources/lang/lo/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · ສາລານຸກົມຄຳພີປາລີ',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'ສາລານຸກົມຄຳພີປາລີ — ຈາກງ່າຍໄປຫາຍາກ ເຂົ້າສູ່ໂລກແຫ່ງຄຳພີປາລີ',
+    'cards' => [
+        'library' => [
+            'title' => 'ຫໍສະໝຸດພຣະໄຕຣປິດົກ',
+            'lead' => 'ພຣະໄຕຣປິດົກ · ອັດຖະກະຖາ · ຕີກາ · ວັດຈະນານຸກົມ',
+            'cta' => 'ເຂົ້າສູ່ຫໍສະໝຸດ',
+        ],
+        'course' => [
+            'title' => 'ຫຼັກສູດຕາມລຳດັບ',
+            'lead' => 'ເລີ່ມຈາກສູນ ສຶກສາເທື່ອລະຄຳພີ',
+            'cta' => 'ເລີ່ມຮຽນ',
+        ],
+        'workspace' => [
+            'title' => 'ບ່ອນເຮັດວຽກແປ',
+            'lead' => 'ຄຳສັບ, ການຮ່ວມມື, ການກວດແກ້, ການເຜີຍແຜ່',
+            'cta' => 'ເຂົ້າສູ່ບ່ອນເຮັດວຽກ',
+        ],
+        'development' => [
+            'title' => 'ການພັດທະນາຕໍ່ຍອດ',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'ຈະມາໃນໄວໆນີ້',
+        ],
+    ],
+];

+ 32 - 0
api-v13/resources/lang/lo/labels.php

@@ -0,0 +1,32 @@
+<?php
+
+return [
+    'home' => 'ໜ້າຫຼັກ',
+    'translation' => 'ຄຳແປ',
+    'original' => 'ຕົ້ນສະບັບ',
+    'nissaya' => 'ນິສສະຍະ',
+
+    'script_convertor' => 'ຕົວແປງອັກສອນປາລີ',
+    'input_script' => 'ອັກສອນຕົ້ນທາງ',
+    'output_script' => 'ອັກສອນປາຍທາງ',
+    'paste_here' => 'ວາງຂໍ້ຄວາມທີ່ນີ້...',
+    'converted_text' => 'ຂໍ້ຄວາມທີ່ແປງແລ້ວຈະສະແດງທີ່ນີ້...',
+    'niggahita_label' => 'ṃ / ṁ / ŋ ປ່ຽນເປັນ',
+    'script_roman' => 'Pāli Roman Unicode',
+    'script_sangayana' => 'Sangayana',
+    'script_sinhala' => 'ສິງຫົນ',
+    'script_sinhala_traditional' => 'ສິງຫົນ (ດັ້ງເດີມ)',
+    'script_sinhala_modern' => 'ສິງຫົນ (ສະໄໝໃໝ່)',
+    'script_myanmar' => 'ມຽນມາ',
+    'script_tai_tham' => 'ໄຕທັມ (ລ້ານນາ)',
+    'script_tai_old' => 'ໄຕທັມ (ແບບເກົ່າ ບໍ່ແມ່ນ Unicode)',
+    'script_thai' => 'ໄທ',
+    'script_chinese_detail' => 'ສຽງອ່ານຈີນ (ລະອຽດ)',
+    'script_chinese_simple' => 'ສຽງອ່ານຈີນ (ຫຍໍ້)',
+    'script_chinese_pinyin' => 'ພິນອິນຈີນ',
+    'script_telugu' => 'ເຕລູກູ',
+
+    'toolbox' => 'ກ່ອງເຄື່ອງມື',
+    'tool_script_convertor' => 'ແປງອັກສອນ',
+    'tool_script_convertor_desc' => 'ແປງຂໍ້ຄວາມປາລີລະຫວ່າງລະບົບອັກສອນ',
+];

+ 10 - 0
api-v13/resources/lang/lo/language.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+    'en' => 'English',
+    'pali' => 'pali',
+    'zh' => '中文',
+    'zh-Hans' => '简体中文',
+    'zh-Hant' => '繁体中文',
+    'my' => 'မြန်မာဘာသာ',
+];

+ 160 - 0
api-v13/resources/lang/lo/library.php

@@ -0,0 +1,160 @@
+<?php
+
+return [
+    // index.blade.php
+    'portal_title' => 'WikiPāli · ຫໍສະໝຸດປາລີ',
+    'portal_hero_title' => 'ຫໍສະໝຸດປາລີ WikiPāli',
+    'portal_hero_subtitle' => 'ສຳຫຼວດພຣະໄຕຣປິດົກ · ປະຕູສູ່ປັນຍາ',
+    'search_placeholder_home' => 'ຄົ້ນຫາພຣະສູດ, ຄຳສັບ, ຊຸດບົດຄວາມ…',
+    'section_tipitaka' => 'ພຣະໄຕຣປິດົກ',
+    'enter_tipitaka' => 'ເຂົ້າສູ່ພຣະໄຕຣປິດົກ',
+    'more' => 'ເພີ່ມເຕີມ',
+    'section_recent' => 'ຄຳແປໃໝ່',
+    'updating_badge' => 'ກຳລັງອັບເດດ',
+    'view_all' => 'ເບິ່ງທັງໝົດ',
+    'badge_new' => 'ໃໝ່',
+    'badge_updated' => 'ອັບເດດແລ້ວ',
+
+    // search.blade.php
+    'search_results_title' => 'ຜົນການຄົ້ນຫາ',
+    'search_title' => 'ຄົ້ນຫາ',
+    'search_placeholder' => 'ຄົ້ນຫາຄຳສັບ, ປາລີ, ສັນສະກຣິດ…',
+    'searching' => 'ຄົ້ນຫາ',
+    'results_found_prefix' => ', ພົບ',
+    'results_found_suffix' => 'ລາຍການ',
+    'page_prefix' => '(ໜ້າ',
+    'page_suffix' => ')',
+    'no_results' => ', ບໍ່ພົບຜົນການຄົ້ນຫາ',
+    'try_other_keywords' => 'ກະລຸນາລອງຄຳຄົ້ນອື່ນ',
+    'filter_by' => 'ກັ່ນຕອງຕາມ ',
+    'filter' => '',
+    'all' => 'ທັງໝົດ',
+    'did_you_mean' => 'ທ່ານໝາຍເຖິງ',
+    'browse_categories' => 'ເບິ່ງຕາມໝວດໝູ່',
+
+    // download.blade.php
+    'download' => 'ດາວໂຫຼດ',
+    'offline_packets' => 'ຊຸດຂໍ້ມູນອອບໄລນ໌ຂອງແອັບ',
+    'packet_count_suffix' => 'ຊຸດ',
+    'no_packets' => 'ບໍ່ມີຊຸດຂໍ້ມູນ',
+    'no_packets_desc' => 'ຍັງບໍ່ມີຊຸດຂໍ້ມູນອອບໄລນ໌ໃຫ້ດາວໂຫຼດ.',
+    'col_name' => 'ຊື່ຊຸດຂໍ້ມູນ',
+    'col_size' => 'ຂະໜາດໄຟລ໌',
+    'col_min_version' => 'ເວີຊັນຕ່ຳສຸດ',
+    'created_at' => 'ວັນທີສ້າງ',
+    'col_links' => 'ລິ້ງດາວໂຫຼດ',
+    'no_links' => 'ບໍ່ມີລິ້ງ',
+    'min_version_prefix' => 'ເວີຊັນຕ່ຳສຸດ ',
+
+    // wiki blade files
+    'wiki_encyclopedia' => 'ສາລານຸກົມພຸດທະສາສະໜາ',
+    'wiki_subtitle' => 'ສຳຫຼວດພຣະທຳ · ປະຕູສູ່ການຕື່ນຮູ້',
+    'wiki_search_placeholder' => 'ຄົ້ນຫາຄຳສັບ, ຄຳພີ, ບຸກຄົນ...',
+    'hot' => 'ຍອດນິຍົມ: ',
+    'wiki_language_divider' => 'ອ່ານສາລານຸກົມພຸດທະສາສະໜາໃນພາສາຂອງທ່ານ',
+    'articles' => 'ບົດ',
+    'today_updates' => 'ອັບເດດມື້ນີ້',
+    'contributors_suffix' => 'ຜູ້ຮ່ວມສ້າງ',
+    'today_entry' => 'ຄຳສັບປະຈຳມື້',
+    'read_full_entry' => 'ອ່ານທັງໝົດ →',
+    'featured_entries' => 'ຄຳສັບແນະນຳ',
+    'statistics' => 'ສະຖິຕິ',
+    'total_entries' => 'ຈຳນວນຄຳສັບທັງໝົດ',
+    'this_month' => 'ເດືອນນີ້',
+    'contributors' => 'ຜູ້ຮ່ວມສ້າງ',
+    'quality_level' => 'ລະດັບຄຸນນະພາບ',
+    'other_versions' => 'ເວີຊັນອື່ນ',
+    'toc' => 'ສາລະບານ',
+    'expand_all' => 'ຂະຫຍາຍທັງໝົດ',
+    'collapse_toc' => 'ຫຍໍ້ລົງ',
+    'entry_info' => 'ຂໍ້ມູນຄຳສັບ',
+    'category' => 'ໝວດໝູ່',
+    'quality' => 'ຄຸນນະພາບ',
+    'recent_updates' => 'ອັບເດດຫຼ້າສຸດ',
+
+    // anthology
+    'site_name' => 'ຫໍສະໝຸດປາລີ',
+    'home' => 'ໜ້າຫຼັກ',
+    'anthology' => 'ຊຸດບົດຄວາມ',
+    'anthology_subtitle' => 'ອັດຖະກະຖາ · ຄູ່ມືກຳມະຖານ · ສົນທະນາທຳ',
+    'no_anthology' => 'ຍັງບໍ່ມີຊຸດບົດຄວາມ',
+    'search_anthology' => 'ຄົ້ນຫາຊຸດບົດຄວາມ…',
+    'author' => 'ຜູ້ຂຽນ',
+    'anthology_count_suffix' => 'ຊຸດ',
+    'last_updated' => 'ອັບເດດຄັ້ງລ່າສຸດ',
+    'chapter_count' => 'ຈຳນວນບົດ',
+    'chapters' => 'ບົດ',
+    'read_online' => 'ອ່ານອອນລາຍ',
+    'open_in_editor' => 'ເປີດໃນຕົວແກ້ໄຂ',
+    'about_anthology' => 'ກ່ຽວກັບຊຸດບົດຄວາມນີ້',
+    'anthology_info' => 'ຂໍ້ມູນຊຸດບົດຄວາມ',
+    'language' => 'ພາສາ',
+    'current_channel' => 'ຊ່ອງປັດຈຸບັນ',
+    'created' => 'ວັນທີສ້າງ',
+    'updated' => 'ວັນທີອັບເດດ',
+    'related_anthology' => 'ຊຸດບົດຄວາມທີ່ກ່ຽວຂ້ອງ',
+    'articles_suffix' => 'ບົດ',
+
+    // tipitaka & reader
+    'tipitaka' => 'ພຣະໄຕຣປິດົກ',
+    'categories' => 'ໝວດໝູ່',
+    'search_tipitaka' => 'ຄົ້ນຫາຄຳພີແລະຄຳແປພຣະໄຕຣປິດົກ',
+    'type' => 'ປະເພດ',
+    'clear_filters' => 'ລ້າງຕົວກັ່ນຕອງ',
+    'total_prefix' => 'ລວມທັງໝົດ',
+    'book_unit' => 'ຫົວ',
+    'sort' => 'ຈັດລຽງ',
+    'weekly_picks' => 'ຄັດສັນປະຈຳອາທິດ',
+    'active_authors' => 'ຜູ້ຂຽນທີ່ຫ້າວຫັນ',
+    'publisher' => 'ຜູ້ຈັດພິມ',
+    'pali' => 'ປາລີ',
+    'book_info' => 'ຂໍ້ມູນໜັງສື',
+    'editor' => 'ຕົວແກ້ໄຂ',
+    'settings' => 'ຕັ້ງຄ່າ',
+    'versions' => 'ເວີຊັນ',
+    'logout' => 'ອອກຈາກລະບົບ',
+    'select_version' => 'ເລືອກເວີຊັນ',
+    'no_content' => 'ບໍ່ມີເນື້ອຫາ',
+    'prev_article' => 'ກ່ອນໜ້າ',
+    'next_article' => 'ຕໍ່ໄປ',
+    'tags' => 'ປ້າຍກຳກັບ',
+    'reading_settings' => 'ຕັ້ງຄ່າການອ່ານ',
+    'show_original' => 'ສະແດງຕົ້ນສະບັບ',
+    'toggle_original' => 'ເປີດ/ປິດ ການສະແດງຕົ້ນສະບັບ',
+    'ui_language' => 'ພາສາໜ້າຈໍ',
+    'auto' => 'ອັດຕະໂນມັດ',
+    'lang_zh_hans' => 'ຈີນຕົວຫຍໍ້',
+    'lang_en' => 'ອັງກິດ',
+    'pali_script' => 'ອັກສອນປາລີ',
+    'script_roman' => 'ໂຣມັນ',
+    'script_myanmar' => 'ມຽນມາ',
+    'script_thai' => 'ໄທ',
+    'commentary_version' => 'ເວີຊັນອັດຖະກະຖາ',
+    'none' => 'ບໍ່ມີ',
+    'cancel' => 'ຍົກເລີກ',
+    'confirm' => 'ຕົກລົງ',
+    'no_toc' => 'ໜັງສືເຫຼັ້ມນີ້ບໍ່ມີສາລະບານ',
+
+    // course (course/index.blade.php + course/history.blade.php)
+    'course' => 'ຫຼັກສູດ',
+    'course_hero_title' => 'ຫຼັກສູດ',
+    'course_hero_subtitle' => 'ສຶກສາຄຳພີກັບຜູ້ຊ່ຽວຊານປາລີຈາກທົ່ວໂລກ',
+    'course_search_placeholder' => 'ຄົ້ນຫາຫຼັກສູດ, ຄູສອນ…',
+    'course_stat_total' => 'ຫຼັກສູດສາທາລະນະ',
+    'course_stat_open' => 'ກຳລັງເປີດຮັບສະໝັກ',
+    'course_stat_closed' => 'ຈົບແລ້ວ',
+    'course_section_latest' => 'ຫຼັກສູດຫຼ້າສຸດ',
+    'course_section_open' => 'ກຳລັງເປີດຮັບສະໝັກ',
+    'course_section_history' => 'ຫຼັກສູດທີ່ຜ່ານມາ',
+    'course_view_all' => 'ເບິ່ງທັງໝົດ',
+    'course_status_open' => 'ຮັບສະໝັກ',
+    'course_status_closed' => 'ສິ້ນສຸດແລ້ວ',
+    'course_period' => 'ຮຸ່ນທີ :n',
+    'course_members' => 'ຜູ້ຮຽນ :n ຄົນ',
+    'course_signup' => 'ສະໝັກຮຽນ',
+    'course_no_latest' => 'ຍັງບໍ່ມີຫຼັກສູດ',
+    'course_no_open' => 'ບໍ່ມີຫຼັກສູດທີ່ເປີດຮັບສະໝັກ',
+    'course_no_history' => 'ຍັງບໍ່ມີຫຼັກສູດທີ່ຜ່ານມາ',
+    'course_history_title' => 'ຫຼັກສູດທີ່ຜ່ານມາ',
+    'course_history_subtitle' => 'ຫຼັກສູດສາທາລະນະທີ່ຈົບແລ້ວ — ທົບທວນກຸ່ມສຶກສາທີ່ຜ່ານມາ',
+];

+ 19 - 0
api-v13/resources/lang/lo/pagination.php

@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; ກ່ອນໜ້າ',
+    'next' => 'ຕໍ່ໄປ &raquo;',
+
+];

+ 22 - 0
api-v13/resources/lang/lo/passwords.php

@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reset Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'reset' => 'ລະຫັດຜ່ານຂອງທ່ານຖືກຕັ້ງໃໝ່ແລ້ວ!',
+    'sent' => 'ພວກເຮົາໄດ້ສົ່ງລິ້ງຕັ້ງລະຫັດຜ່ານໃໝ່ໄປທາງອີເມວຂອງທ່ານແລ້ວ!',
+    'throttled' => 'ກະລຸນາລໍຖ້າກ່ອນລອງໃໝ່.',
+    'token' => 'ລະຫັດຕັ້ງລະຫັດຜ່ານໃໝ່ນີ້ບໍ່ຖືກຕ້ອງ.',
+    'user' => 'ບໍ່ພົບຜູ້ໃຊ້ທີ່ມີອີເມວດັ່ງກ່າວ.',
+
+];

+ 33 - 0
api-v13/resources/lang/lo/site.php

@@ -0,0 +1,33 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | site Language Lines
+    |--------------------------------------------------------------------------
+    |
+    |
+    */
+
+    'logo' => '/logo.png',
+    'title' => 'wikipali',
+    'subhead' => 'wikipali',
+    'keywords' => ['pali', 'buddhistm'],
+    'description' => 'wikipali',
+    'copyright' => 'iapt 2022',
+    'author.name' => 'iapt',
+    'author.email' => 'visuddhindand@gmail.com',
+    'nav' => [
+        'home' => 'ໜ້າຫຼັກ',
+        'tipitaka' => 'ພຣະໄຕຣປິດົກ',
+        'wiki' => 'ສາລານຸກົມ',
+        'anthology' => 'ຊຸດບົດຄວາມ',
+        'course' => 'ຫຼັກສູດ',
+        'download' => 'ດາວໂຫຼດ',
+
+        'menu' => 'ເມນູ',
+        'open_menu' => 'ເປີດເມນູ',
+        'close_menu' => 'ປິດເມນູ',
+    ],
+];

+ 162 - 0
api-v13/resources/lang/lo/validation.php

@@ -0,0 +1,162 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines contain the default error messages used by
+    | the validator class. Some of these rules have multiple versions such
+    | as the size rules. Feel free to tweak each of these messages here.
+    |
+    */
+
+    'accepted' => 'ຕ້ອງຍອມຮັບ :attribute.',
+    'accepted_if' => 'ຕ້ອງຍອມຮັບ :attribute ເມື່ອ :other ເປັນ :value.',
+    'active_url' => ':attribute ບໍ່ແມ່ນ URL ທີ່ຖືກຕ້ອງ.',
+    'after' => ':attribute ຕ້ອງເປັນວັນທີຫຼັງ :date.',
+    'after_or_equal' => ':attribute ຕ້ອງເປັນວັນທີຫຼັງ ຫຼື ເທົ່າກັບ :date.',
+    'alpha' => ':attribute ຕ້ອງມີແຕ່ຕົວອັກສອນເທົ່ານັ້ນ.',
+    'alpha_dash' => ':attribute ຕ້ອງມີແຕ່ຕົວອັກສອນ, ຕົວເລກ, ຂີດກາງ ແລະ ຂີດລຸ່ມເທົ່ານັ້ນ.',
+    'alpha_num' => ':attribute ຕ້ອງມີແຕ່ຕົວອັກສອນ ແລະ ຕົວເລກເທົ່ານັ້ນ.',
+    'array' => ':attribute ຕ້ອງເປັນອາເຣ.',
+    'before' => ':attribute ຕ້ອງເປັນວັນທີກ່ອນ :date.',
+    'before_or_equal' => ':attribute ຕ້ອງເປັນວັນທີກ່ອນ ຫຼື ເທົ່າກັບ :date.',
+    'between' => [
+        'numeric' => ':attribute ຕ້ອງຢູ່ລະຫວ່າງ :min ຫາ :max.',
+        'file' => ':attribute ຕ້ອງມີຂະໜາດລະຫວ່າງ :min ຫາ :max ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງມີຄວາມຍາວລະຫວ່າງ :min ຫາ :max ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີລະຫວ່າງ :min ຫາ :max ລາຍການ.',
+    ],
+    'boolean' => 'ຊ່ອງ :attribute ຕ້ອງເປັນ true ຫຼື false.',
+    'confirmed' => 'ການຢືນຢັນ :attribute ບໍ່ກົງກັນ.',
+    'current_password' => 'ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ.',
+    'date' => ':attribute ບໍ່ແມ່ນວັນທີທີ່ຖືກຕ້ອງ.',
+    'date_equals' => ':attribute ຕ້ອງເປັນວັນທີເທົ່າກັບ :date.',
+    'date_format' => ':attribute ບໍ່ກົງກັບຮູບແບບ :format.',
+    'declined' => 'ຕ້ອງປະຕິເສດ :attribute.',
+    'declined_if' => 'ຕ້ອງປະຕິເສດ :attribute ເມື່ອ :other ເປັນ :value.',
+    'different' => ':attribute ແລະ :other ຕ້ອງແຕກຕ່າງກັນ.',
+    'digits' => ':attribute ຕ້ອງມີ :digits ຫຼັກ.',
+    'digits_between' => ':attribute ຕ້ອງມີລະຫວ່າງ :min ຫາ :max ຫຼັກ.',
+    'dimensions' => ':attribute ມີຂະໜາດຮູບພາບບໍ່ຖືກຕ້ອງ.',
+    'distinct' => 'ຊ່ອງ :attribute ມີຄ່າຊ້ຳກັນ.',
+    'email' => ':attribute ຕ້ອງເປັນທີ່ຢູ່ອີເມວທີ່ຖືກຕ້ອງ.',
+    'ends_with' => ':attribute ຕ້ອງລົງທ້າຍດ້ວຍໜຶ່ງໃນ: :values.',
+    'enum' => ':attribute ທີ່ເລືອກບໍ່ຖືກຕ້ອງ.',
+    'exists' => ':attribute ທີ່ເລືອກມີຢູ່ແລ້ວ.',
+    'file' => ':attribute ຕ້ອງເປັນໄຟລ໌.',
+    'filled' => 'ຊ່ອງ :attribute ຕ້ອງມີຄ່າ.',
+    'gt' => [
+        'numeric' => ':attribute ຕ້ອງຫຼາຍກວ່າ :value.',
+        'file' => ':attribute ຕ້ອງໃຫຍ່ກວ່າ :value ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງຍາວກວ່າ :value ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີຫຼາຍກວ່າ :value ລາຍການ.',
+    ],
+    'gte' => [
+        'numeric' => ':attribute ຕ້ອງຫຼາຍກວ່າ ຫຼື ເທົ່າກັບ :value.',
+        'file' => ':attribute ຕ້ອງໃຫຍ່ກວ່າ ຫຼື ເທົ່າກັບ :value ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງຍາວກວ່າ ຫຼື ເທົ່າກັບ :value ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີ :value ລາຍການຂຶ້ນໄປ.',
+    ],
+    'image' => ':attribute ຕ້ອງເປັນຮູບພາບ.',
+    'in' => ':attribute ທີ່ເລືອກບໍ່ຖືກຕ້ອງ.',
+    'in_array' => 'ຊ່ອງ :attribute ບໍ່ມີຢູ່ໃນ :other.',
+    'integer' => ':attribute ຕ້ອງເປັນຈຳນວນເຕັມ.',
+    'ip' => ':attribute ຕ້ອງເປັນທີ່ຢູ່ IP ທີ່ຖືກຕ້ອງ.',
+    'ipv4' => ':attribute ຕ້ອງເປັນທີ່ຢູ່ IPv4 ທີ່ຖືກຕ້ອງ.',
+    'ipv6' => ':attribute ຕ້ອງເປັນທີ່ຢູ່ IPv6 ທີ່ຖືກຕ້ອງ.',
+    'mac_address' => ':attribute ຕ້ອງເປັນທີ່ຢູ່ MAC ທີ່ຖືກຕ້ອງ.',
+    'json' => ':attribute ຕ້ອງເປັນຂໍ້ຄວາມ JSON ທີ່ຖືກຕ້ອງ.',
+    'lt' => [
+        'numeric' => ':attribute ຕ້ອງນ້ອຍກວ່າ :value.',
+        'file' => ':attribute ຕ້ອງນ້ອຍກວ່າ :value ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງສັ້ນກວ່າ :value ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີໜ້ອຍກວ່າ :value ລາຍການ.',
+    ],
+    'lte' => [
+        'numeric' => ':attribute ຕ້ອງນ້ອຍກວ່າ ຫຼື ເທົ່າກັບ :value.',
+        'file' => ':attribute ຕ້ອງນ້ອຍກວ່າ ຫຼື ເທົ່າກັບ :value ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງສັ້ນກວ່າ ຫຼື ເທົ່າກັບ :value ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີບໍ່ເກີນ :value ລາຍການ.',
+    ],
+    'max' => [
+        'numeric' => ':attribute ຕ້ອງບໍ່ຫຼາຍກວ່າ :max.',
+        'file' => ':attribute ຕ້ອງບໍ່ໃຫຍ່ກວ່າ :max ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງບໍ່ຍາວກວ່າ :max ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີບໍ່ເກີນ :max ລາຍການ.',
+    ],
+    'mimes' => ':attribute ຕ້ອງເປັນໄຟລ໌ປະເພດ: :values.',
+    'mimetypes' => ':attribute ຕ້ອງເປັນໄຟລ໌ປະເພດ: :values.',
+    'min' => [
+        'numeric' => ':attribute ຕ້ອງມີຄ່າຢ່າງໜ້ອຍ :min.',
+        'file' => ':attribute ຕ້ອງມີຂະໜາດຢ່າງໜ້ອຍ :min ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງມີຢ່າງໜ້ອຍ :min ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີຢ່າງໜ້ອຍ :min ລາຍການ.',
+    ],
+    'multiple_of' => ':attribute ຕ້ອງເປັນຜົນຄູນຂອງ :value.',
+    'not_in' => ':attribute ທີ່ເລືອກບໍ່ຖືກຕ້ອງ.',
+    'not_regex' => 'ຮູບແບບຂອງ :attribute ບໍ່ຖືກຕ້ອງ.',
+    'numeric' => ':attribute ຕ້ອງເປັນຕົວເລກ.',
+    'password' => 'ລະຫັດຜ່ານບໍ່ຖືກຕ້ອງ.',
+    'present' => 'ຕ້ອງມີຊ່ອງ :attribute.',
+    'prohibited' => 'ຊ່ອງ :attribute ຖືກຫ້າມ.',
+    'prohibited_if' => 'ຊ່ອງ :attribute ຖືກຫ້າມເມື່ອ :other ເປັນ :value.',
+    'prohibited_unless' => 'ຊ່ອງ :attribute ຖືກຫ້າມ ເວັ້ນເສຍແຕ່ :other ຢູ່ໃນ :values.',
+    'prohibits' => 'ຊ່ອງ :attribute ຫ້າມບໍ່ໃຫ້ມີ :other.',
+    'regex' => 'ຮູບແບບຂອງ :attribute ບໍ່ຖືກຕ້ອງ.',
+    'required' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute.',
+    'required_if' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເມື່ອ :other ເປັນ :value.',
+    'required_unless' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເວັ້ນເສຍແຕ່ :other ຢູ່ໃນ :values.',
+    'required_with' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເມື່ອມີ :values.',
+    'required_with_all' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເມື່ອມີ :values ທັງໝົດ.',
+    'required_without' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເມື່ອບໍ່ມີ :values.',
+    'required_without_all' => 'ຕ້ອງຕື່ມຊ່ອງ :attribute ເມື່ອບໍ່ມີຄ່າໃດໆໃນ :values.',
+    'same' => ':attribute ແລະ :other ຕ້ອງກົງກັນ.',
+    'size' => [
+        'numeric' => ':attribute ຕ້ອງເທົ່າກັບ :size.',
+        'file' => ':attribute ຕ້ອງມີຂະໜາດ :size ກິໂລໄບຕ໌.',
+        'string' => ':attribute ຕ້ອງມີ :size ຕົວອັກສອນ.',
+        'array' => ':attribute ຕ້ອງມີ :size ລາຍການ.',
+    ],
+    'starts_with' => ':attribute ຕ້ອງເລີ່ມຕົ້ນດ້ວຍໜຶ່ງໃນ: :values.',
+    'string' => ':attribute ຕ້ອງເປັນຂໍ້ຄວາມ.',
+    'timezone' => ':attribute ຕ້ອງເປັນເຂດເວລາທີ່ຖືກຕ້ອງ.',
+    'unique' => ':attribute ຖືກໃຊ້ໄປແລ້ວ.',
+    'uploaded' => 'ອັບໂຫຼດ :attribute ບໍ່ສຳເລັດ.',
+    'url' => ':attribute ຕ້ອງເປັນ URL ທີ່ຖືກຕ້ອງ.',
+    'uuid' => ':attribute ຕ້ອງເປັນ UUID ທີ່ຖືກຕ້ອງ.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify custom validation messages for attributes using the
+    | convention "attribute.rule" to name the lines. This makes it quick to
+    | specify a specific custom language line for a given attribute rule.
+    |
+    */
+
+    'custom' => [
+        'attribute-name' => [
+            'rule-name' => 'custom-message',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Attributes
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used to swap our attribute placeholder
+    | with something more reader friendly such as "E-Mail Address" instead
+    | of "email". This simply helps us make our message more expressive.
+    |
+    */
+
+    'attributes' => [],
+
+];

+ 39 - 0
api-v13/resources/lang/my/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · ပါဠိဗုဒ္ဓစာပေ စွယ်စုံကျမ်း',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'ပါဠိဗုဒ္ဓစာပေ စွယ်စုံကျမ်း — အလွယ်မှ အခက်သို့ ပါဠိစာပေလောကထဲ ဝင်ရောက်ပါ',
+    'cards' => [
+        'library' => [
+            'title' => 'ပိဋကတ် စာကြည့်တိုက်',
+            'lead' => 'တိပိဋက · အဋ္ဌကထာ · ဋီကာ · အဘိဓာန်',
+            'cta' => 'စာကြည့်တိုက်သို့ ဝင်ရန်',
+        ],
+        'course' => [
+            'title' => 'အဆင့်ဆင့် သင်တန်း',
+            'lead' => 'အစမှစ၍ ကျမ်းတစ်စောင်ချင်း လေ့လာပါ',
+            'cta' => 'စတင်လေ့လာရန်',
+        ],
+        'workspace' => [
+            'title' => 'ဘာသာပြန် လုပ်ငန်းခွင်',
+            'lead' => 'ဝေါဟာရ၊ ပူးပေါင်းလုပ်ဆောင်မှု၊ စစ်ဆေးမှု၊ ထုတ်ဝေမှု',
+            'cta' => 'လုပ်ငန်းခွင်သို့ ဝင်ရန်',
+        ],
+        'development' => [
+            'title' => 'ဒုတိယအဆင့် ဖွံ့ဖြိုးရေး',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'မကြာမီ လာမည်',
+        ],
+    ],
+];

+ 39 - 0
api-v13/resources/lang/si/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · පාලි බෞද්ධ විශ්වකෝෂය',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'පාලි බෞද්ධ විශ්වකෝෂය — පියවරෙන් පියවර පාලි ත්‍රිපිටක ලෝකයට',
+    'cards' => [
+        'library' => [
+            'title' => 'ග්‍රන්ථ පුස්තකාලය',
+            'lead' => 'ත්‍රිපිටකය · අට්ඨකථා · ටීකා · ශබ්දකෝෂය',
+            'cta' => 'පුස්තකාලයට පිවිසෙන්න',
+        ],
+        'course' => [
+            'title' => 'පියවරෙන් පියවර පාඨමාලා',
+            'lead' => 'මුල සිට, ග්‍රන්ථයෙන් ග්‍රන්ථය හදාරන්න',
+            'cta' => 'ඉගෙනීම අරඹන්න',
+        ],
+        'workspace' => [
+            'title' => 'පරිවර්තන වැඩපොළ',
+            'lead' => 'පාරිභාෂික වචන, සහයෝගය, පිරික්සුම, ප්‍රකාශනය',
+            'cta' => 'වැඩපොළට පිවිසෙන්න',
+        ],
+        'development' => [
+            'title' => 'ද්විතීයික සංවර්ධනය',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'ඉක්මනින්',
+        ],
+    ],
+];

+ 39 - 0
api-v13/resources/lang/th/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · สารานุกรมพระไตรปิฎกบาลี',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'สารานุกรมพระไตรปิฎกบาลี — จากง่ายไปยาก เข้าสู่โลกแห่งพระบาลี',
+    'cards' => [
+        'library' => [
+            'title' => 'คลังพระคัมภีร์',
+            'lead' => 'พระไตรปิฎก · อรรถกถา · ฎีกา · พจนานุกรม',
+            'cta' => 'เข้าสู่คลังคัมภีร์',
+        ],
+        'course' => [
+            'title' => 'หลักสูตรตามลำดับ',
+            'lead' => 'เริ่มจากศูนย์ ศึกษาทีละคัมภีร์',
+            'cta' => 'เริ่มเรียน',
+        ],
+        'workspace' => [
+            'title' => 'พื้นที่ทำงานแปล',
+            'lead' => 'ศัพท์ การทำงานร่วมกัน การตรวจทาน การเผยแพร่',
+            'cta' => 'เข้าสู่พื้นที่ทำงาน',
+        ],
+        'development' => [
+            'title' => 'การพัฒนาต่อยอด',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'เร็ว ๆ นี้',
+        ],
+    ],
+];

+ 20 - 0
api-v13/resources/lang/vi/auth.php

@@ -0,0 +1,20 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Authentication Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used during authentication for various
+    | messages that we need to display to the user. You are free to modify
+    | these language lines according to your application's requirements.
+    |
+    */
+
+    'failed' => 'Thông tin đăng nhập không khớp với dữ liệu của chúng tôi.',
+    'password' => 'Mật khẩu không đúng.',
+    'throttle' => 'Bạn đã đăng nhập sai quá nhiều lần. Vui lòng thử lại sau :seconds giây.',
+
+];

+ 6 - 0
api-v13/resources/lang/vi/buttons.php

@@ -0,0 +1,6 @@
+<?php
+
+return [
+    'more' => 'Xem thêm',
+    'online-read' => 'Đọc trực tuyến',
+];

+ 199 - 0
api-v13/resources/lang/vi/grammar.php

@@ -0,0 +1,199 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | site Language Lines
+    |--------------------------------------------------------------------------
+    |
+    |
+    */
+
+    'n' => 'danh từ',
+    'n.short' => 'n',
+    'ti' => 'ba giống',
+    'ti.short' => '3g',
+    'v' => 'động từ',
+    'v.short' => 'v',
+    'v:ind' => 'động từ bất biến',
+    'v:ind.short' => 'v.ind',
+    'ind' => 'bất biến từ',
+    'ind.short' => 'ind',
+    'm' => 'giống đực',
+    'm.short' => 'm',
+    'nt' => 'giống trung',
+    'nt.short' => 'nt',
+    'f' => 'giống cái',
+    'f.short' => 'f',
+    'sg' => 'số ít',
+    'sg.short' => 'sg',
+    'pl' => 'số nhiều',
+    'pl.short' => 'pl',
+    'nom' => 'chủ cách',
+    'nom.short' => 'nom',
+    'acc' => 'đối cách',
+    'acc.short' => 'acc',
+    'gen' => 'sở thuộc cách',
+    'gen.short' => 'gen',
+    'dat' => 'tặng cách',
+    'dat.short' => 'dat',
+    'inst' => 'công cụ cách',
+    'inst.short' => 'ins',
+    'voc' => 'hô cách',
+    'voc.short' => 'voc',
+    'abl' => 'xuất xứ cách',
+    'abl.short' => 'abl',
+    'loc' => 'vị trí cách',
+    'loc.short' => 'loc',
+    'base' => 'thân từ',
+    'base.short' => 'thân',
+    'imp' => 'mệnh lệnh cách',
+    'imp.short' => 'imp',
+    'cond' => 'điều kiện cách',
+    'cond.short' => 'cond',
+    'opt' => 'khả năng cách',
+    'opt.short' => 'opt',
+    'pres' => 'hiện tại',
+    'pres.short' => 'pres',
+    'aor' => 'quá khứ đơn',
+    'aor.short' => 'aor',
+    'pf' => 'hoàn thành',
+    'pf.short' => 'pf',
+    'fut' => 'tương lai',
+    'fut.short' => 'fut',
+    'act' => 'chủ động',
+    'act.short' => 'act',
+    'refl' => 'phản thân',
+    'refl.short' => 'refl',
+    '1p' => 'ngôi thứ nhất',
+    '1p.short' => '1p',
+    '2p' => 'ngôi thứ hai',
+    '2p.short' => '2p',
+    '3p' => 'ngôi thứ ba',
+    '3p.short' => '3p',
+    'prp' => 'hiện tại phân từ',
+    'prp.short' => 'prp',
+    'prpp' => 'hiện tại phân từ bị động',
+    'prpp.short' => 'prpp',
+    'pp' => 'quá khứ phân từ',
+    'pp.short' => 'pp',
+    'ppa' => 'quá khứ phân từ chủ động',
+    'ppa.short' => 'ppa',
+    'ppp' => 'quá khứ phân từ bị động',
+    'ppp.short' => 'ppp',
+    'futp' => 'tương lai phân từ',
+    'futp.short' => 'futp',
+    'fpa.short' => 'fpa',
+    'grd' => 'phân từ khả năng',
+    'grd.short' => 'grd',
+    'pass' => 'bị động',
+    'pass.short' => 'pass',
+    'caus' => 'sai khiến',
+    'caus.short' => 'caus',
+    'desid' => 'ý muốn',
+    'desid.short' => 'desid',
+    'intens' => 'tăng cường',
+    'intens.short' => 'intens',
+    'denom' => 'phái sinh từ danh từ',
+    'denom.short' => 'denom',
+    'ger' => 'danh động từ',
+    'ger.short' => 'ger',
+    'abs' => 'bất biến phân từ',
+    'abs.short' => 'abs',
+    'inf' => 'nguyên mẫu',
+    'inf.short' => 'inf',
+    'adj' => 'tính từ',
+    'adj.short' => 'adj',
+    'pron' => 'đại từ',
+    'pron.short' => 'pron',
+    'num' => 'số từ',
+    'num.short' => 'num',
+    'adv' => 'trạng từ',
+    'adv.short' => 'adv',
+    'conj' => 'liên từ',
+    'conj.short' => 'conj',
+    'prep' => 'giới từ',
+    'prep.short' => 'prep',
+    'interj' => 'thán từ',
+    'interj.short' => 'interj',
+    'pre' => 'tiền tố',
+    'pre.short' => 'pre',
+    'suf' => 'hậu tố',
+    'suf.short' => 'suf',
+    'end' => 'vĩ tố',
+    'end.short' => 'end',
+    'part' => 'thành phần',
+    'part.short' => 'tp',
+    'un' => 'liên âm (sandhi)',
+    'un.short' => 'sandhi',
+    'none' => 'không',
+    'none.short' => '_',
+    'null' => 'rỗng',
+    'null.short' => '_',
+    '?' => '?',
+    '?.short' => '?',
+    'ti:base' => 'thân từ ba giống',
+    'ti:base.short' => '3g.thân',
+    'n:base' => 'thân danh từ',
+    'n:base.short' => 'n.thân',
+    'v:base' => 'thân động từ',
+    'v:base.short' => 'v.thân',
+    'adj:base' => 'thân tính từ',
+    'adj:base.short' => 'adj.thân',
+    'fpp' => 'tương lai phân từ bị động',
+    'fpp.short' => 'fpp',
+    'cp.short' => 'tp',
+    'cp' => 'thành phần hợp từ',
+    'indconj.short' => 'conj',
+    'indconj' => 'liên từ bất biến',
+    'pron:base.short' => 'pron.thân',
+    'pron:base' => 'thân đại từ',
+    'note.short' => 'ghi chú',
+    'note' => 'ghi chú',
+    'vind.short' => 'v.ind',
+    'vind' => 'động từ bất biến',
+    'vdn.short' => 'v.n',
+    'vdn' => 'danh từ động từ',
+    'relations.iad.label' => 'bổ ngữ phụ thuộc',
+    'relations.asv.label' => 'chủ thể → động từ',
+    'relations.aov.label' => 'tân ngữ → động từ',
+    'relations.daso-p.label' => 'chủ ngữ → hệ từ',
+    'relations.daso-s.label' => 'vị ngữ → hệ từ',
+    'relations.nio.label' => 'được mô tả (chủ ngữ) → định ngữ (vị ngữ)',
+    'relations.nid.label' => 'được gọi tên → tên gọi',
+    'relations.dasd-p.label' => '<tên gọi> chủ ngữ → hệ từ',
+    'relations.dasd-s.label' => '<tên gọi> vị ngữ → hệ từ',
+    'relations.dao-p.label' => 'tân ngữ kép bị động - chính',
+    'relations.dao-s.label' => 'tân ngữ kép bị động - phụ',
+    'relations.iov.label' => 'tân ngữ → động từ',
+    'relations.dio-p.label' => 'tân ngữ kép <chính> → động từ',
+    'relations.dio-s.label' => 'tân ngữ kép <phụ> → động từ',
+    'relations.dis-p.label' => 'chủ ngữ → hệ từ (bị động)',
+    'relations.dis-s.label' => 'vị ngữ → hệ từ (bị động)',
+    'relations.stc.label' => 'bối cảnh không gian - thời gian → hành động đang diễn ra',
+    'relations.adv.label' => 'bổ ngữ của động từ → động từ',
+    'relations.imp.label' => 'cách thức → động từ',
+    'relations.soe.label' => 'đi kèm <có liên từ>',
+    'relations.soi.label' => 'đi kèm <không liên từ>',
+    'relations.isv.label' => 'chủ thể <không ở chủ cách> → động từ',
+    'relations.cau.label' => 'nguyên nhân → kết quả / quy thuộc',
+    'relations.iov-c.label' => 'tân ngữ của sai khiến → động từ',
+    'relations.adj.label' => 'định ngữ của danh từ → từ được bổ nghĩa',
+    'relations.rec.label' => 'người nhận → sự cho',
+    'relations.pur.label' => 'mục đích → động từ',
+    'relations.det.label' => 'điểm xuất phát → sự rời đi',
+    'relations.coc.label' => 'phân biệt / so sánh',
+    'relations.pos.label' => 'sở hữu chủ → vật sở hữu',
+    'relations.coi.label' => 'toàn thể [tập hợp] → bộ phận được chứa / tập hợp → phần tử',
+    'relations.lov.label' => 'vật chứa → động từ / vật chứa → vật được chứa',
+    'relations.mot.label' => 'biểu hiện → cái có biểu hiện',
+    'relations.whp.label' => 'toàn thể → bộ phận',
+    'relations.def.label' => 'đặc tính định nghĩa',
+    'relations.ac.label' => 'mệnh đề tuyệt đối',
+    'relations.avc.label' => 'mệnh đề tuyệt đối theo thể',
+    'relations.qus.label' => 'bên trong trích dẫn → dấu trích dẫn',
+    'relations.qum.label' => 'dấu trích dẫn → bên ngoài trích dẫn',
+    'relations.enu.label' => 'liệt kê → dấu gạch ngang',
+    'relations.enm.label' => 'dấu gạch ngang → cái được liệt kê',
+];

+ 39 - 0
api-v13/resources/lang/vi/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · Bách khoa Thánh điển Pāli',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => 'Bách khoa Thánh điển Pāli — từ dễ đến khó, bước vào thế giới kinh điển Pāli',
+    'cards' => [
+        'library' => [
+            'title' => 'Thư viện Tam Tạng',
+            'lead' => 'Tam Tạng · Chú giải · Phụ chú giải · Từ điển',
+            'cta' => 'Vào thư viện',
+        ],
+        'course' => [
+            'title' => 'Khóa học theo thứ lớp',
+            'lead' => 'Bắt đầu từ số không, học từng bộ một',
+            'cta' => 'Bắt đầu học',
+        ],
+        'workspace' => [
+            'title' => 'Bàn làm việc dịch thuật',
+            'lead' => 'Thuật ngữ, cộng tác, hiệu đính, xuất bản',
+            'cta' => 'Vào bàn làm việc',
+        ],
+        'development' => [
+            'title' => 'Phát triển mở rộng',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => 'Sắp ra mắt',
+        ],
+    ],
+];

+ 32 - 0
api-v13/resources/lang/vi/labels.php

@@ -0,0 +1,32 @@
+<?php
+
+return [
+    'home' => 'Trang chủ',
+    'translation' => 'Bản dịch',
+    'original' => 'Nguyên bản',
+    'nissaya' => 'Nissaya',
+
+    'script_convertor' => 'Bộ chuyển tự Pāli',
+    'input_script' => 'Chữ viết nguồn',
+    'output_script' => 'Chữ viết đích',
+    'paste_here' => 'Dán văn bản vào đây...',
+    'converted_text' => 'Văn bản sau khi chuyển sẽ hiện ở đây...',
+    'niggahita_label' => 'ṃ / ṁ / ŋ chuyển thành',
+    'script_roman' => 'Pāli Roman Unicode',
+    'script_sangayana' => 'Sangayana',
+    'script_sinhala' => 'Sinhala',
+    'script_sinhala_traditional' => 'Sinhala (truyền thống)',
+    'script_sinhala_modern' => 'Sinhala (hiện đại)',
+    'script_myanmar' => 'Miến Điện',
+    'script_tai_tham' => 'Tai Tham (Lanna)',
+    'script_tai_old' => 'Tai Tham (cổ, không Unicode)',
+    'script_thai' => 'Thái',
+    'script_chinese_detail' => 'Phiên âm Hán ngữ (chi tiết)',
+    'script_chinese_simple' => 'Phiên âm Hán ngữ (giản lược)',
+    'script_chinese_pinyin' => 'Bính âm Hán ngữ',
+    'script_telugu' => 'Telugu',
+
+    'toolbox' => 'Hộp công cụ',
+    'tool_script_convertor' => 'Chuyển tự',
+    'tool_script_convertor_desc' => 'Chuyển văn bản Pāli giữa các hệ chữ viết',
+];

+ 10 - 0
api-v13/resources/lang/vi/language.php

@@ -0,0 +1,10 @@
+<?php
+
+return [
+    'en' => 'English',
+    'pali' => 'pali',
+    'zh' => '中文',
+    'zh-Hans' => '简体中文',
+    'zh-Hant' => '繁体中文',
+    'my' => 'မြန်မာဘာသာ',
+];

+ 160 - 0
api-v13/resources/lang/vi/library.php

@@ -0,0 +1,160 @@
+<?php
+
+return [
+    // index.blade.php
+    'portal_title' => 'WikiPāli · Thư viện Pāli',
+    'portal_hero_title' => 'Thư viện Pāli WikiPāli',
+    'portal_hero_subtitle' => 'Khám phá Tam Tạng · Cửa vào trí tuệ',
+    'search_placeholder_home' => 'Tìm kinh, thuật ngữ, tuyển tập…',
+    'section_tipitaka' => 'Tam Tạng',
+    'enter_tipitaka' => 'Vào Tam Tạng',
+    'more' => 'Xem thêm',
+    'section_recent' => 'Bản dịch mới',
+    'updating_badge' => 'Đang cập nhật',
+    'view_all' => 'Xem tất cả',
+    'badge_new' => 'Mới',
+    'badge_updated' => 'Đã cập nhật',
+
+    // search.blade.php
+    'search_results_title' => 'Kết quả tìm kiếm',
+    'search_title' => 'Tìm kiếm',
+    'search_placeholder' => 'Tìm mục từ, Pāli, Sanskrit…',
+    'searching' => 'Tìm kiếm',
+    'results_found_prefix' => ', tìm thấy',
+    'results_found_suffix' => 'kết quả',
+    'page_prefix' => '(trang',
+    'page_suffix' => ')',
+    'no_results' => ', không tìm thấy kết quả nào',
+    'try_other_keywords' => 'Vui lòng thử từ khóa khác',
+    'filter_by' => 'Lọc theo ',
+    'filter' => '',
+    'all' => 'Tất cả',
+    'did_you_mean' => 'Có phải bạn muốn tìm',
+    'browse_categories' => 'Duyệt theo phân loại',
+
+    // download.blade.php
+    'download' => 'Tải về',
+    'offline_packets' => 'Gói dữ liệu ngoại tuyến cho ứng dụng',
+    'packet_count_suffix' => 'gói',
+    'no_packets' => 'Không có gói nào',
+    'no_packets_desc' => 'Hiện chưa có gói dữ liệu ngoại tuyến nào.',
+    'col_name' => 'Tên gói',
+    'col_size' => 'Kích thước',
+    'col_min_version' => 'Phiên bản tối thiểu',
+    'created_at' => 'Ngày tạo',
+    'col_links' => 'Liên kết tải về',
+    'no_links' => 'Không có liên kết',
+    'min_version_prefix' => 'Phiên bản tối thiểu ',
+
+    // wiki blade files
+    'wiki_encyclopedia' => 'Bách khoa Phật học',
+    'wiki_subtitle' => 'Khám phá Chánh pháp · Cửa vào giác ngộ',
+    'wiki_search_placeholder' => 'Tìm thuật ngữ, kinh điển, nhân vật...',
+    'hot' => 'Nổi bật: ',
+    'wiki_language_divider' => 'Đọc Bách khoa Phật học bằng ngôn ngữ của bạn',
+    'articles' => 'bài',
+    'today_updates' => 'Cập nhật hôm nay',
+    'contributors_suffix' => 'người đóng góp',
+    'today_entry' => 'Mục từ hôm nay',
+    'read_full_entry' => 'Đọc toàn bộ mục từ →',
+    'featured_entries' => 'Mục từ tiêu biểu',
+    'statistics' => 'Thống kê',
+    'total_entries' => 'Tổng số mục từ',
+    'this_month' => 'Tháng này',
+    'contributors' => 'Người đóng góp',
+    'quality_level' => 'Mức chất lượng',
+    'other_versions' => 'Phiên bản khác',
+    'toc' => 'Mục lục',
+    'expand_all' => 'Mở rộng tất cả',
+    'collapse_toc' => 'Thu gọn',
+    'entry_info' => 'Thông tin mục từ',
+    'category' => 'Phân loại',
+    'quality' => 'Chất lượng',
+    'recent_updates' => 'Cập nhật gần đây',
+
+    // anthology
+    'site_name' => 'Thư viện Pāli',
+    'home' => 'Trang chủ',
+    'anthology' => 'Tuyển tập',
+    'anthology_subtitle' => 'Chú giải · Hướng dẫn thiền · Đàm luận Phật pháp',
+    'no_anthology' => 'Chưa có tuyển tập',
+    'search_anthology' => 'Tìm tuyển tập…',
+    'author' => 'Tác giả',
+    'anthology_count_suffix' => 'tuyển tập',
+    'last_updated' => 'Cập nhật lần cuối',
+    'chapter_count' => 'Số chương',
+    'chapters' => 'chương',
+    'read_online' => 'Đọc trực tuyến',
+    'open_in_editor' => 'Mở trong trình biên tập',
+    'about_anthology' => 'Giới thiệu tuyển tập',
+    'anthology_info' => 'Thông tin tuyển tập',
+    'language' => 'Ngôn ngữ',
+    'current_channel' => 'Kênh hiện tại',
+    'created' => 'Ngày tạo',
+    'updated' => 'Ngày cập nhật',
+    'related_anthology' => 'Tuyển tập liên quan',
+    'articles_suffix' => 'bài',
+
+    // tipitaka & reader
+    'tipitaka' => 'Tam Tạng',
+    'categories' => 'Phân loại',
+    'search_tipitaka' => 'Tìm kinh văn và bản dịch Tam Tạng',
+    'type' => 'Loại',
+    'clear_filters' => 'Xóa bộ lọc',
+    'total_prefix' => 'Tổng cộng',
+    'book_unit' => 'quyển',
+    'sort' => 'Sắp xếp',
+    'weekly_picks' => 'Tuyển chọn trong tuần',
+    'active_authors' => 'Tác giả tích cực',
+    'publisher' => 'Nhà xuất bản',
+    'pali' => 'Pāli',
+    'book_info' => 'Thông tin sách',
+    'editor' => 'Trình biên tập',
+    'settings' => 'Cài đặt',
+    'versions' => 'Phiên bản',
+    'logout' => 'Đăng xuất',
+    'select_version' => 'Chọn phiên bản',
+    'no_content' => 'Không có nội dung',
+    'prev_article' => 'Trước',
+    'next_article' => 'Sau',
+    'tags' => 'Thẻ',
+    'reading_settings' => 'Cài đặt đọc',
+    'show_original' => 'Hiện nguyên bản',
+    'toggle_original' => 'Bật/tắt hiển thị nguyên bản',
+    'ui_language' => 'Ngôn ngữ giao diện',
+    'auto' => 'Tự động',
+    'lang_zh_hans' => 'Tiếng Trung giản thể',
+    'lang_en' => 'Tiếng Anh',
+    'pali_script' => 'Chữ viết Pāli',
+    'script_roman' => 'La-tinh',
+    'script_myanmar' => 'Miến Điện',
+    'script_thai' => 'Thái',
+    'commentary_version' => 'Phiên bản chú giải',
+    'none' => 'Không',
+    'cancel' => 'Hủy',
+    'confirm' => 'Đồng ý',
+    'no_toc' => 'Sách này không có mục lục',
+
+    // course (course/index.blade.php + course/history.blade.php)
+    'course' => 'Khóa học',
+    'course_hero_title' => 'Khóa học',
+    'course_hero_subtitle' => 'Nghiên cứu kinh điển cùng các chuyên gia Pāli khắp thế giới',
+    'course_search_placeholder' => 'Tìm khóa học, giảng viên…',
+    'course_stat_total' => 'Khóa học công khai',
+    'course_stat_open' => 'Đang mở ghi danh',
+    'course_stat_closed' => 'Đã kết thúc',
+    'course_section_latest' => 'Khóa học mới nhất',
+    'course_section_open' => 'Đang mở ghi danh',
+    'course_section_history' => 'Khóa học đã qua',
+    'course_view_all' => 'Xem tất cả',
+    'course_status_open' => 'Đang ghi danh',
+    'course_status_closed' => 'Đã kết thúc',
+    'course_period' => 'Khóa :n',
+    'course_members' => ':n học viên',
+    'course_signup' => 'Ghi danh',
+    'course_no_latest' => 'Chưa có khóa học nào',
+    'course_no_open' => 'Không có khóa học nào đang mở ghi danh',
+    'course_no_history' => 'Chưa có khóa học đã qua',
+    'course_history_title' => 'Khóa học đã qua',
+    'course_history_subtitle' => 'Các khóa học công khai đã kết thúc — xem lại những nhóm học trước đây',
+];

+ 19 - 0
api-v13/resources/lang/vi/pagination.php

@@ -0,0 +1,19 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Pagination Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used by the paginator library to build
+    | the simple pagination links. You are free to change them to anything
+    | you want to customize your views to better match your application.
+    |
+    */
+
+    'previous' => '&laquo; Trước',
+    'next' => 'Sau &raquo;',
+
+];

+ 22 - 0
api-v13/resources/lang/vi/passwords.php

@@ -0,0 +1,22 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Password Reset Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are the default lines which match reasons
+    | that are given by the password broker for a password update attempt
+    | has failed, such as for an invalid token or invalid new password.
+    |
+    */
+
+    'reset' => 'Mật khẩu của bạn đã được đặt lại!',
+    'sent' => 'Chúng tôi đã gửi liên kết đặt lại mật khẩu qua email của bạn!',
+    'throttled' => 'Vui lòng đợi một lát trước khi thử lại.',
+    'token' => 'Mã đặt lại mật khẩu này không hợp lệ.',
+    'user' => 'Không tìm thấy người dùng nào với địa chỉ email này.',
+
+];

+ 33 - 0
api-v13/resources/lang/vi/site.php

@@ -0,0 +1,33 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | site Language Lines
+    |--------------------------------------------------------------------------
+    |
+    |
+    */
+
+    'logo' => '/logo.png',
+    'title' => 'wikipali',
+    'subhead' => 'wikipali',
+    'keywords' => ['pali', 'buddhistm'],
+    'description' => 'wikipali',
+    'copyright' => 'iapt 2022',
+    'author.name' => 'iapt',
+    'author.email' => 'visuddhindand@gmail.com',
+    'nav' => [
+        'home' => 'Trang chủ',
+        'tipitaka' => 'Tam Tạng',
+        'wiki' => 'Bách khoa',
+        'anthology' => 'Tuyển tập',
+        'course' => 'Khóa học',
+        'download' => 'Tải về',
+
+        'menu' => 'Menu',
+        'open_menu' => 'Mở menu',
+        'close_menu' => 'Đóng menu',
+    ],
+];

+ 162 - 0
api-v13/resources/lang/vi/validation.php

@@ -0,0 +1,162 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines contain the default error messages used by
+    | the validator class. Some of these rules have multiple versions such
+    | as the size rules. Feel free to tweak each of these messages here.
+    |
+    */
+
+    'accepted' => 'Bạn phải chấp nhận :attribute.',
+    'accepted_if' => 'Bạn phải chấp nhận :attribute khi :other là :value.',
+    'active_url' => ':attribute không phải là một URL hợp lệ.',
+    'after' => ':attribute phải là ngày sau :date.',
+    'after_or_equal' => ':attribute phải là ngày sau hoặc bằng :date.',
+    'alpha' => ':attribute chỉ được chứa chữ cái.',
+    'alpha_dash' => ':attribute chỉ được chứa chữ cái, chữ số, dấu gạch ngang và gạch dưới.',
+    'alpha_num' => ':attribute chỉ được chứa chữ cái và chữ số.',
+    'array' => ':attribute phải là một mảng.',
+    'before' => ':attribute phải là ngày trước :date.',
+    'before_or_equal' => ':attribute phải là ngày trước hoặc bằng :date.',
+    'between' => [
+        'numeric' => ':attribute phải nằm trong khoảng :min đến :max.',
+        'file' => ':attribute phải nằm trong khoảng :min đến :max kilobyte.',
+        'string' => ':attribute phải có độ dài từ :min đến :max ký tự.',
+        'array' => ':attribute phải có từ :min đến :max phần tử.',
+    ],
+    'boolean' => 'Trường :attribute phải là true hoặc false.',
+    'confirmed' => 'Xác nhận :attribute không khớp.',
+    'current_password' => 'Mật khẩu không đúng.',
+    'date' => ':attribute không phải là ngày hợp lệ.',
+    'date_equals' => ':attribute phải là ngày bằng :date.',
+    'date_format' => ':attribute không đúng định dạng :format.',
+    'declined' => ':attribute phải bị từ chối.',
+    'declined_if' => ':attribute phải bị từ chối khi :other là :value.',
+    'different' => ':attribute và :other phải khác nhau.',
+    'digits' => ':attribute phải có :digits chữ số.',
+    'digits_between' => ':attribute phải có từ :min đến :max chữ số.',
+    'dimensions' => ':attribute có kích thước hình ảnh không hợp lệ.',
+    'distinct' => 'Trường :attribute có giá trị trùng lặp.',
+    'email' => ':attribute phải là một địa chỉ email hợp lệ.',
+    'ends_with' => ':attribute phải kết thúc bằng một trong các giá trị: :values.',
+    'enum' => ':attribute đã chọn không hợp lệ.',
+    'exists' => ':attribute đã chọn đã tồn tại.',
+    'file' => ':attribute phải là một tập tin.',
+    'filled' => 'Trường :attribute phải có giá trị.',
+    'gt' => [
+        'numeric' => ':attribute phải lớn hơn :value.',
+        'file' => ':attribute phải lớn hơn :value kilobyte.',
+        'string' => ':attribute phải dài hơn :value ký tự.',
+        'array' => ':attribute phải có nhiều hơn :value phần tử.',
+    ],
+    'gte' => [
+        'numeric' => ':attribute phải lớn hơn hoặc bằng :value.',
+        'file' => ':attribute phải lớn hơn hoặc bằng :value kilobyte.',
+        'string' => ':attribute phải dài hơn hoặc bằng :value ký tự.',
+        'array' => ':attribute phải có :value phần tử trở lên.',
+    ],
+    'image' => ':attribute phải là một hình ảnh.',
+    'in' => ':attribute đã chọn không hợp lệ.',
+    'in_array' => 'Trường :attribute không tồn tại trong :other.',
+    'integer' => ':attribute phải là một số nguyên.',
+    'ip' => ':attribute phải là một địa chỉ IP hợp lệ.',
+    'ipv4' => ':attribute phải là một địa chỉ IPv4 hợp lệ.',
+    'ipv6' => ':attribute phải là một địa chỉ IPv6 hợp lệ.',
+    'mac_address' => ':attribute phải là một địa chỉ MAC hợp lệ.',
+    'json' => ':attribute phải là một chuỗi JSON hợp lệ.',
+    'lt' => [
+        'numeric' => ':attribute phải nhỏ hơn :value.',
+        'file' => ':attribute phải nhỏ hơn :value kilobyte.',
+        'string' => ':attribute phải ngắn hơn :value ký tự.',
+        'array' => ':attribute phải có ít hơn :value phần tử.',
+    ],
+    'lte' => [
+        'numeric' => ':attribute phải nhỏ hơn hoặc bằng :value.',
+        'file' => ':attribute phải nhỏ hơn hoặc bằng :value kilobyte.',
+        'string' => ':attribute phải ngắn hơn hoặc bằng :value ký tự.',
+        'array' => ':attribute không được có nhiều hơn :value phần tử.',
+    ],
+    'max' => [
+        'numeric' => ':attribute không được lớn hơn :max.',
+        'file' => ':attribute không được lớn hơn :max kilobyte.',
+        'string' => ':attribute không được dài hơn :max ký tự.',
+        'array' => ':attribute không được có nhiều hơn :max phần tử.',
+    ],
+    'mimes' => ':attribute phải là tập tin thuộc loại: :values.',
+    'mimetypes' => ':attribute phải là tập tin thuộc loại: :values.',
+    'min' => [
+        'numeric' => ':attribute phải ít nhất là :min.',
+        'file' => ':attribute phải ít nhất :min kilobyte.',
+        'string' => ':attribute phải có ít nhất :min ký tự.',
+        'array' => ':attribute phải có ít nhất :min phần tử.',
+    ],
+    'multiple_of' => ':attribute phải là bội số của :value.',
+    'not_in' => ':attribute đã chọn không hợp lệ.',
+    'not_regex' => 'Định dạng :attribute không hợp lệ.',
+    'numeric' => ':attribute phải là một số.',
+    'password' => 'Mật khẩu không đúng.',
+    'present' => 'Trường :attribute phải tồn tại.',
+    'prohibited' => 'Trường :attribute bị cấm.',
+    'prohibited_if' => 'Trường :attribute bị cấm khi :other là :value.',
+    'prohibited_unless' => 'Trường :attribute bị cấm trừ khi :other nằm trong :values.',
+    'prohibits' => 'Trường :attribute không cho phép :other xuất hiện.',
+    'regex' => 'Định dạng :attribute không hợp lệ.',
+    'required' => 'Trường :attribute là bắt buộc.',
+    'required_if' => 'Trường :attribute là bắt buộc khi :other là :value.',
+    'required_unless' => 'Trường :attribute là bắt buộc trừ khi :other nằm trong :values.',
+    'required_with' => 'Trường :attribute là bắt buộc khi có :values.',
+    'required_with_all' => 'Trường :attribute là bắt buộc khi có tất cả :values.',
+    'required_without' => 'Trường :attribute là bắt buộc khi không có :values.',
+    'required_without_all' => 'Trường :attribute là bắt buộc khi không có bất kỳ giá trị nào trong :values.',
+    'same' => ':attribute và :other phải giống nhau.',
+    'size' => [
+        'numeric' => ':attribute phải bằng :size.',
+        'file' => ':attribute phải bằng :size kilobyte.',
+        'string' => ':attribute phải có :size ký tự.',
+        'array' => ':attribute phải chứa :size phần tử.',
+    ],
+    'starts_with' => ':attribute phải bắt đầu bằng một trong các giá trị: :values.',
+    'string' => ':attribute phải là một chuỗi ký tự.',
+    'timezone' => ':attribute phải là một múi giờ hợp lệ.',
+    'unique' => ':attribute đã được sử dụng.',
+    'uploaded' => 'Tải lên :attribute thất bại.',
+    'url' => ':attribute phải là một URL hợp lệ.',
+    'uuid' => ':attribute phải là một UUID hợp lệ.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | Here you may specify custom validation messages for attributes using the
+    | convention "attribute.rule" to name the lines. This makes it quick to
+    | specify a specific custom language line for a given attribute rule.
+    |
+    */
+
+    'custom' => [
+        'attribute-name' => [
+            'rule-name' => 'custom-message',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | Custom Validation Attributes
+    |--------------------------------------------------------------------------
+    |
+    | The following language lines are used to swap our attribute placeholder
+    | with something more reader friendly such as "E-Mail Address" instead
+    | of "email". This simply helps us make our message more expressive.
+    |
+    */
+
+    'attributes' => [],
+
+];

+ 39 - 0
api-v13/resources/lang/zh-Hans/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · 巴利典籍百科',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => '巴利典籍百科 —— 由浅入深,进入巴利典籍的世界',
+    'cards' => [
+        'library' => [
+            'title' => '典籍文库',
+            'lead' => '三藏 · 义注 · 复注 · 字典',
+            'cta' => '进入文库',
+        ],
+        'course' => [
+            'title' => '次第课程',
+            'lead' => '从零开始,逐部研读',
+            'cta' => '开始学习',
+        ],
+        'workspace' => [
+            'title' => '翻译工作台',
+            'lead' => '术语、协作、校对、发布',
+            'cta' => '进入工作台',
+        ],
+        'development' => [
+            'title' => '二次开发',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => '即将上线',
+        ],
+    ],
+];

+ 1 - 1
api-v13/resources/lang/zh-Hans/library.php

@@ -141,7 +141,7 @@ return [
     // course (course/index.blade.php + course/history.blade.php)
     // course (course/index.blade.php + course/history.blade.php)
     'course' => '课程',
     'course' => '课程',
     'course_hero_title' => '课程',
     'course_hero_title' => '课程',
-    'course_hero_subtitle' => '跟随世界各地的巴利专家研读典',
+    'course_hero_subtitle' => '跟随世界各地的巴利专家研读典',
     'course_search_placeholder' => '搜索课程、讲师…',
     'course_search_placeholder' => '搜索课程、讲师…',
     'course_stat_total' => '公开课程',
     'course_stat_total' => '公开课程',
     'course_stat_open' => '开放报名',
     'course_stat_open' => '开放报名',

+ 39 - 0
api-v13/resources/lang/zh-Hant/home.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+
+    /*
+    |--------------------------------------------------------------------------
+    | Home Page Language Lines
+    |--------------------------------------------------------------------------
+    |
+    | 首页(/)文案:品牌 hero 与四张分流卡片。
+    |
+    */
+
+    'title' => 'WikiPāli · 巴利典籍百科',
+    'hero_title' => 'WikiPāli',
+    'hero_lead' => '巴利典籍百科 —— 由淺入深,進入巴利典籍的世界',
+    'cards' => [
+        'library' => [
+            'title' => '典籍文庫',
+            'lead' => '三藏 · 義註 · 複註 · 字典',
+            'cta' => '進入文庫',
+        ],
+        'course' => [
+            'title' => '次第課程',
+            'lead' => '從零開始,逐部研讀',
+            'cta' => '開始學習',
+        ],
+        'workspace' => [
+            'title' => '翻譯工作台',
+            'lead' => '術語、協作、校對、發布',
+            'cta' => '進入工作台',
+        ],
+        'development' => [
+            'title' => '二次開發',
+            'lead' => 'API · MCP · GitHub',
+            'cta' => '即將上線',
+        ],
+    ],
+];

+ 1 - 1
api-v13/resources/lang/zh-Hant/library.php

@@ -138,7 +138,7 @@ return [
     // course (course/index.blade.php + course/history.blade.php)
     // course (course/index.blade.php + course/history.blade.php)
     'course' => '課程',
     'course' => '課程',
     'course_hero_title' => '課程',
     'course_hero_title' => '課程',
-    'course_hero_subtitle' => '跟隨世界各地的巴利專家研讀典',
+    'course_hero_subtitle' => '跟隨世界各地的巴利專家研讀典',
     'course_search_placeholder' => '搜尋課程、講師…',
     'course_search_placeholder' => '搜尋課程、講師…',
     'course_stat_total' => '公開課程',
     'course_stat_total' => '公開課程',
     'course_stat_open' => '開放報名',
     'course_stat_open' => '開放報名',

+ 11 - 1
api-v13/resources/views/components/language-switcher.blade.php

@@ -2,6 +2,16 @@
 $currentLocale = app()->getLocale();
 $currentLocale = app()->getLocale();
 $languages = config('mint.languages');
 $languages = config('mint.languages');
 $currentLanguage = $languages[$currentLocale] ?? 'en';
 $currentLanguage = $languages[$currentLocale] ?? 'en';
+// 首页等无名路由用当前 URL 追加 ?lang= ,具名路由仍按路由参数生成,保持原有链接形式
+$localeUrl = function (string $locale) {
+    $routeName = Route::currentRouteName();
+
+    if ($routeName) {
+        return route($routeName, array_merge(request()->route()->parameters(), ['lang' => $locale]));
+    }
+
+    return request()->fullUrlWithQuery(['lang' => $locale]);
+};
 @endphp
 @endphp
 
 
 <div class="language-switcher">
 <div class="language-switcher">
@@ -20,7 +30,7 @@ $currentLanguage = $languages[$currentLocale] ?? 'en';
     <ul class="language-dropdown">
     <ul class="language-dropdown">
         @foreach ($languages as $locale => $language)
         @foreach ($languages as $locale => $language)
         <li>
         <li>
-            <a href="{{ route(Route::currentRouteName(), array_merge(request()->route()->parameters(), ['lang' => $locale])) }}"
+            <a href="{{ $localeUrl($locale) }}"
                 class="language-dropdown-item {{ $locale === $currentLocale ? 'active' : '' }}">
                 class="language-dropdown-item {{ $locale === $currentLocale ? 'active' : '' }}">
                 {{ $language }}
                 {{ $language }}
             </a>
             </a>

+ 1 - 1
api-v13/resources/views/layouts/base.blade.php

@@ -9,7 +9,7 @@
     <meta charset="utf-8" />
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
     <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
     <meta http-equiv="X-UA-Compatible" content="ie=edge" />
     <meta http-equiv="X-UA-Compatible" content="ie=edge" />
-    <title>@yield('title', 'WikiPāli · 巴利典百科')</title>
+    <title>@yield('title', 'WikiPāli · 巴利典百科')</title>
 
 
     {{-- 基础样式由子布局通过 @vite 注入 --}}
     {{-- 基础样式由子布局通过 @vite 注入 --}}
     @stack('styles')
     @stack('styles')

+ 8 - 3
api-v13/resources/views/pages/home.blade.php

@@ -4,7 +4,7 @@
 --}}
 --}}
 @extends('layouts.base')
 @extends('layouts.base')
 
 
-@section('title', 'WikiPāli · 巴利佛典百科')
+@section('title', __('home.title'))
 
 
 @push('styles')
 @push('styles')
 @vite(['resources/css/home.css'])
 @vite(['resources/css/home.css'])
@@ -15,10 +15,15 @@
 @section('page')
 @section('page')
 <main class="home">
 <main class="home">
 
 
+    {{-- 右上角语言选择,复用 library 的切换组件 --}}
+    <div class="home-lang">
+        <x-language-switcher />
+    </div>
+
     {{-- 品牌 Hero:站名 + 一行说明,居中 --}}
     {{-- 品牌 Hero:站名 + 一行说明,居中 --}}
     <header class="home-hero">
     <header class="home-hero">
-        <h1 class="home-hero__title">WikiPāli</h1>
-        <p class="home-hero__lead">巴利佛典百科 —— 由浅入深,进入巴利圣典的世界</p>
+        <h1 class="home-hero__title">{{ __('home.hero_title') }}</h1>
+        <p class="home-hero__lead">{{ __('home.hero_lead') }}</p>
     </header>
     </header>
 
 
     {{-- 四张通栏卡片,纵向排列 --}}
     {{-- 四张通栏卡片,纵向排列 --}}

+ 2 - 2
api-v13/resources/views/typhoon.blade.php

@@ -159,13 +159,13 @@ Read the documentation
 <div class="xl:container xl:mx-auto md:px-6 px-4">
 <div class="xl:container xl:mx-auto md:px-6 px-4">
 <div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
 <div class="w-5/6 md:w-3/4 lg:w-2/3 xl:w-1/2 text-center mx-auto mb-16">
 <div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
 <div class="text-xs md:text-sm opacity-75 font-semibold uppercase tracking-wide text-gray-700 dark:text-gray-300">
-让学习典变得更容易
+让学习典变得更容易
 </div>
 </div>
 <h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
 <h2 class="mt-1 tracking-tight leading-tighter text-2xl sm:text-3xl md:text-4xl lg:text-5xl font-extrabold text-gray-900 dark:text-gray-100">
 我们的愿景
 我们的愿景
 </h2>
 </h2>
 <div class="mt-3 text-base md:text-lg lg:text-xl prose">
 <div class="mt-3 text-base md:text-lg lg:text-xl prose">
-<p>打造一个公共的巴利典学习平台。</p>
+<p>打造一个公共的巴利典学习平台。</p>
 </div>
 </div>
 </div>
 </div>
 <div class="grid md:grid-cols-2 xl:grid-cols-3 gap-y-8 gap-x-8 ">
 <div class="grid md:grid-cols-2 xl:grid-cols-3 gap-y-8 gap-x-8 ">

+ 1 - 1
dashboard-v6/src/components/article/TypeCourse.tsx

@@ -54,7 +54,7 @@ interface IWidget {
   onArticleChange?: (
   onArticleChange?: (
     type: ArticleType,
     type: ArticleType,
     id: string,
     id: string,
-    target: string,
+    target?: TTarget,
     param?: ISearchParams[]
     param?: ISearchParams[]
   ) => void;
   ) => void;
 }
 }

+ 10 - 5
dashboard-v6/src/features/editor/TextBook.tsx

@@ -2,7 +2,9 @@
 // Props
 // Props
 // ─────────────────────────────────────────────
 // ─────────────────────────────────────────────
 
 
-import type { ArticleMode } from "../../api/article";
+import type { ArticleMode, ArticleType } from "../../api/article";
+import type { ISearchParams } from "../../components/article/TypePali";
+import type { TTarget } from "../../types";
 import AnthologyTocTree from "../../components/anthology/AnthologyTocTree";
 import AnthologyTocTree from "../../components/anthology/AnthologyTocTree";
 import TypeCourse from "../../components/article/TypeCourse";
 import TypeCourse from "../../components/article/TypeCourse";
 import { useCourse } from "../../components/course/hooks/useCourse";
 import { useCourse } from "../../components/course/hooks/useCourse";
@@ -24,7 +26,12 @@ export interface ArticleEditorProps {
   /** 选择了新的 anthology 时触发 */
   /** 选择了新的 anthology 时触发 */
   onAnthologySelect?: (anthologyId: string) => void;
   onAnthologySelect?: (anthologyId: string) => void;
   /** 文章内部触发跳转(type: 'article' | 'anthology' 等) */
   /** 文章内部触发跳转(type: 'article' | 'anthology' 等) */
-  onArticleChange?: (type: string, id: string) => void;
+  onArticleChange?: (
+    type: ArticleType,
+    id: string,
+    target?: TTarget,
+    param?: ISearchParams[]
+  ) => void;
 }
 }
 
 
 // ─────────────────────────────────────────────
 // ─────────────────────────────────────────────
@@ -40,9 +47,7 @@ export default function TextBookEditor({
   onArticleChange,
   onArticleChange,
 }: ArticleEditorProps) {
 }: ArticleEditorProps) {
   const channels = channelId ? channelId.split("_") : undefined;
   const channels = channelId ? channelId.split("_") : undefined;
-  const { data, loading, errorCode } = useCourse(courseId);
-
-  console.error(errorCode);
+  const { data, loading } = useCourse(courseId);
 
 
   return (
   return (
     <Editor
     <Editor

+ 49 - 2
dashboard-v6/src/pages/workspace/course/textbook.tsx

@@ -1,11 +1,21 @@
 //课程详情页面
 //课程详情页面
-import { useMatches, useParams } from "react-router";
+import {
+  useLocation,
+  useMatches,
+  useNavigate,
+  useParams,
+  useSearchParams,
+} from "react-router";
 import { useIntl } from "react-intl";
 import { useIntl } from "react-intl";
 
 
+import type { ArticleMode } from "../../../api/article";
 import TextBookEditor from "../../../features/editor/TextBook";
 import TextBookEditor from "../../../features/editor/TextBook";
 
 
 const Widget = () => {
 const Widget = () => {
   const { courseId, articleId } = useParams(); //url 参数
   const { courseId, articleId } = useParams(); //url 参数
+  const [searchParams] = useSearchParams();
+  const navigate = useNavigate();
+  const { search } = useLocation();
   const intl = useIntl();
   const intl = useIntl();
   const matches = useMatches() as {
   const matches = useMatches() as {
     data?: { title?: string; name?: string; word?: string };
     data?: { title?: string; name?: string; word?: string };
@@ -14,10 +24,47 @@ const Widget = () => {
   const name = data?.title ?? data?.name ?? data?.word;
   const name = data?.title ?? data?.name ?? data?.word;
   const prefix = intl.formatMessage({ id: "columns.studio.course.title" });
   const prefix = intl.formatMessage({ id: "columns.studio.course.title" });
 
 
+  const mode = (searchParams.get("mode") ?? "read") as ArticleMode;
+  const channelId = searchParams.get("channel");
+
   return (
   return (
     <>
     <>
       <title>{name ? `${prefix}-${name}` : prefix}</title>
       <title>{name ? `${prefix}-${name}` : prefix}</title>
-      <TextBookEditor courseId={courseId} articleId={articleId} />
+      <TextBookEditor
+        courseId={courseId}
+        articleId={articleId}
+        mode={mode}
+        channelId={channelId}
+        onArticleClick={(_anthologyId, id, target) => {
+          const url = `workspace/course/${courseId}/textbook/${id}`;
+          if (target === "_blank") {
+            window.open(
+              `${window.location.origin}${import.meta.env.BASE_URL}${url}${search}`,
+              "_blank"
+            );
+          } else {
+            navigate(`/${url}${search}`);
+          }
+        }}
+        onArticleChange={(type, id, target, param) => {
+          const url =
+            type === "textbook"
+              ? `workspace/course/${courseId}/textbook/${id}`
+              : `workspace/tipitaka/${type}/${id}`;
+          const urlSearch =
+            param && param.length > 0
+              ? "?" + param.map((item) => `${item.key}=${item.value}`).join("&")
+              : search;
+          if (target === "_blank") {
+            window.open(
+              `${window.location.origin}${import.meta.env.BASE_URL}${url}${urlSearch}`,
+              "_blank"
+            );
+          } else {
+            navigate(`/${url}${urlSearch}`);
+          }
+        }}
+      />
     </>
     </>
   );
   );
 };
 };