javascript 多语言+ Astro和本地存储

62lalag4  于 2023-05-21  发布在  Java
关注(0)|答案(1)|浏览(163)

我想创建一个astro组件,考虑到localstorage中的一个变量来获取每个组件的文本,但Astro显示为null。

let locale = "en"; //default
const userLanguage = localStorage.getItem("language");
if (userLanguage ) {
locale = userLanguage;
} let menu;
import(`../locale/${locale}/menu.json`).then((lang) =\> {
menu  = lang.default;
});

我需要找到一种方法来拥有基于语言的json或标记文件,并加载每个用户的语言。我正在考虑使用svelte/react,但我将不得不创建很多调用,或者可能有其他方法来实现它?

41zrol4v

41zrol4v1#

为什么会这样

正如我所理解的,您希望创建*.astro组件并在其中使用localStorage API。但是,浏览器相关的API(如documentwindow在服务器上无法访问,即在AstroMDN中,您可以看到localStoragewindow对象一部分。
window接口的**localStorage**只读属性允许访问Document的原点的Storage对象;所存储的数据在浏览器会话之间被保存。
记住,正确使用localStorage将是window.localStorage,这将导致以下Astro错误:
未定义document(或window
从Astro docs中,你可以看到这实际上意味着什么:
Astro组件在服务器上运行,所以你不能在frontmatter中访问这些特定于浏览器的对象。

可能的解决方案

因此,潜在的解决方案将是使用带有生命周期钩子的框架组件(例如React的useEffect,Vue的onMounted等)或Astro文档中提到的<script>
如果代码在Astro组件中,则将其移动到frontmatter外部的<script>标记中。这告诉Astro在客户端上运行此代码,其中documentwindow可用。
如果代码在框架组件中,请尝试在使用生命周期方法呈现后访问这些对象。。通过使用client:指令(如client:load)来运行这些生命周期方法,告诉框架组件对客户端进行水合。

怎么解决

然而,根据我的经验,我会通过加载所有翻译,即每种语言,将json翻译的异步加载从客户端移动到服务器。
假设您有以下用于翻译的文件夹结构:

- locales
--- menu
----- en.json
----- ru.json
----- es.json
--- other_feature
----- en.json
----- ru.json
----- es.json

然后我们可以使用glob import一次导入所有内容:

const translations = import.meta.glob('./locales/menu/*.json', { eager: true, import: 'default' })

然后,您只需将这个translations对象(该对象的键表示文件路径,值表示json字符串)传递给您的Framework组件。你可以在这里了解更多关于glob import的信息。
框架组件本身应该使用生命周期方法来访问localStorage,以读取用户区域设置,并有条件地从输入 prop 中获取正确的翻译。下面是Vue示例:

<script setup>
import { onMounted, ref } from 'vue'

const props = defineProps(['translations'])
const translation = ref({})

onMounted(() => {
  const userLocale = window.localeStorage.getItem("language")
  // take the correct translation from all translations
  translation.value = JSON.parse(
    translations[Object.keys(translations).find(key => key.includes(userLocale))]
  )
})
</script>

<template>
  <p>This message displayed in your mother tongue: {{ translation.message }}</p>
</template>

因此,最终的Astro文件看起来像这样:

---
const translations = import.meta.glob('./locales/menu/*.json', { eager: true, import: 'default' })
---

<div>
  <!-- Keep in mind that using `client:load` you might face hydration issues. They can be resolved by explicitly rendering the component on the client using `client:only` -->
  <VueMessageComponent translations={ translations } client:load />
</div>

我希望它能有所帮助,但请记住,我是用JavaScript(而不是TypeScript)编写的,这可能会导致null/undefined值出现一些问题。另外,我没有测试这段代码,所以它可能无法开箱即用:)

相关问题