Shiny.i18n实时翻译带HTML格式文本异常问题求助
问题根因
shiny.i18n的update_lang前端自动翻译逻辑,默认只会扫描DOM中的纯文本节点做key匹配。如果直接在翻译key里嵌入<strong>这类HTML标签,浏览器渲染时会把整段文本拆成标签节点、文本节点等多个独立子元素,前端无法找到和整句key完全匹配的纯文本节点,就会出现带标签内容完全不触发翻译的问题。
两种错误写法的具体原因:
- 写法
h1(i18n$t(HTML("<strong>Hallo</strong> Welt"))):传入i18n$t()的是shiny的HTML类对象,不是纯字符向量,翻译函数无法识别非字符串输入,直接返回原内容,自然不会触发多语言替换 - 写法
h1(HTML(i18n$t("<strong>Hallo</strong> Welt"))):初始渲染时虽然能拿到翻译结果,但节点被标记为原生HTML内容后,前端自动翻译逻辑会直接跳过该节点,部分旧版本shiny.i18n还会因为类型校验不通过,抛出argument is not a character vector的错误。
无拆分实现方案(保留长文本上下文)
采用「纯文本翻译+后置样式替换」的思路,不在翻译文件中嵌入原生HTML标签,用轻量标记标注需要加粗的词汇,翻译完成后统一替换为HTML样式标签,完全避免DOM节点拆分导致的匹配失败问题。
步骤1:修改翻译配置文件
用markdown通用的**词汇**格式标记需要加粗的内容,去掉原生HTML标签,修改后的translation.json如下:
{ "languages":["de", "en"], "translation":[ { "de":"**Hallo** Welt", "en":"**Hello** World" }, { "de":"Sprache ändern:", "en":"Change language:" }, { "de":"Welt", "en":"World" } ] }
步骤2:编写通用翻译辅助函数
自定义带样式处理的翻译函数,先完成纯文本翻译,再将加粗标记替换为HTML的<strong>标签,最终返回可直接渲染的HTML对象:
# 带加粗样式的翻译函数 t_rich <- function(text_key) { # 先执行纯文本翻译 raw_trans <- i18n$t(text_key) # 替换**标记为加粗HTML标签 rich_trans <- gsub("\\*\\*(.*?)\\*\\*", "<strong>\\1</strong>", raw_trans) # 返回可渲染的HTML内容 HTML(rich_trans) }
步骤3:适配实时语言切换
因为带样式的富文本内容无法被前端自动翻译扫描识别,需要将这类内容放到renderUI中做响应式渲染,确保切换语言时能实时更新,完整可运行代码如下:
library(shiny) library(shiny.i18n) i18n <- Translator$new(translation_json_path='translation.json') i18n$set_translation_language('de') t_rich <- function(text_key) { raw_trans <- i18n$t(text_key) rich_trans <- gsub("\\*\\*(.*?)\\*\\*", "<strong>\\1</strong>", raw_trans) HTML(rich_trans) } ui <- fluidPage( shiny.i18n::usei18n(i18n), h1(i18n$t("Welt")), tags$div( style='float: right;', selectInput( inputId='selected_language', label=i18n$t('Change language'), choices = i18n$get_languages(), selected = i18n$get_key_translation() ) ), # 富文本翻译内容用uiOutput承接 uiOutput("welcome_title") ) server <- function(input, output, session) { # 切换语言时同步更新i18n实例的语言配置 observeEvent(input$selected_language, { i18n$set_translation_language(input$selected_language) update_lang(session, input$selected_language) }) # 响应式渲染带加粗的富文本标题 output$welcome_title <- renderUI({ h1(t_rich("**Hallo** Welt"), windowTitle=NULL) }) } shinyApp(ui = ui, server = server)
方案优势
- 不需要将长文本拆分为独立词汇逐个翻译,完整保留上下文语义,翻译文件维护成本低
- 翻译配置中不嵌入原生HTML代码,不会触发DOM节点拆分导致的翻译匹配失败问题
- 样式规则统一收敛在辅助函数中,后续需要新增斜体、高亮等其他样式时,只需要修改替换规则即可,不需要调整翻译内容
内容的提问来源于stack exchange,提问作者LauraFeh
相关产品推荐
相关产品推荐

