Compare commits

...
Author SHA1 Message Date
github-actions[bot] cdc373986c Version Packages 2023-11-14 03:12:17 +00:00
Alexander Sorokin 9d939c1b1b Switch to using module ID markers
Previously VIntl for Nuxt kept track of files that were used as locale
messages and matched module IDs based on them. This, however, was very
unreliable in certain environments.

With this change VIntl for Nuxt now uses 'markers', URL parameters added
to the end of imported module IDs. This allows it to very quickly test
whether a specific module is imported through a message file and should
be processed by the VIntl unplugin.
2023-11-14 04:03:39 +01:00
github-actions[bot] 2007377bcf Version Packages 2023-10-30 06:01:58 +00:00
Alexander Sorokin 6479891b2d Update VIntl to v4.4.1 2023-10-30 06:57:18 +01:00
renovate[bot]andSasha Sorokin ec6744774d Update all non-major dependencies 2023-10-30 04:47:10 +01:00
renovate[bot]andSasha Sorokin 70a4cc57b0 Update actions/setup-node action to v4 2023-10-30 04:44:13 +01:00
github-actions[bot] f8f4494527 Version Packages 2023-10-10 21:46:16 +00:00
Sasha Sorokin fe75e7ca94 Explicitly pass build subcommand to nuxt-module-build 2023-10-10 23:41:44 +02:00
Sasha Sorokin a9fdc595f5 Update VIntl Unplugin to 1.5.0 2023-10-10 23:39:42 +02:00
github-actions[bot] eee372b21e Version Packages 2023-10-10 04:21:08 +00:00
renovate[bot]andGitHub 220a9cbf00 Update all non-major dependencies 2023-10-10 04:08:51 +00:00
Sasha Sorokin 81c23b0b8d Add support for parsing error handling 2023-10-10 06:02:11 +02:00
Sasha Sorokin 2b5a59b33d Unpin FormatJS dependencies 2023-10-09 02:29:16 +02:00
renovate[bot]andGitHub d8dabcf62e Update all non-major dependencies 2023-10-08 21:07:47 +00:00
renovate[bot]andGitHub 02281aa6c7 Update actions/checkout action to v4 2023-10-08 20:18:27 +00:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
ef16a887d2 Update devDependency prettier-plugin-jsdoc to v1 (#51)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-07 19:49:33 +02:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
880cb1972d Version Packages (#60)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-09-11 20:05:29 +02:00
Sasha Sorokin 979f2bdedd Add more SEO options 2023-09-11 19:59:11 +02:00
Sasha Sorokin 7cb833df1e Switch to bundler module resolution 2023-09-11 19:35:14 +02:00
Sasha SorokinandGitHub 682133f893 Add Bing verification file (#53) 2023-08-21 05:00:27 +02:00
renovate[bot]GitHubrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
9bff4db4c1 Update typescript-eslint monorepo to v6 (#52)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-08-11 03:47:58 +02:00
github-actions[bot]GitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>
70c4dfab73 Version Packages (#50)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-07-12 03:27:10 +02:00
Sasha SorokinandGitHub e1c501caad Improve alternate links generation (#49) 2023-07-12 03:25:31 +02:00
github-actions[bot] b883a37aeb Version Packages 2023-06-19 12:50:57 +00:00
Sasha Sorokin 951f36381f Add graceful fallback for unknown locales in storage 2023-06-19 14:47:00 +02:00
Sasha Sorokin ae71089e74 Use automatic locale if none returned by the storage 2023-06-19 13:58:25 +02:00
github-actions[bot]andSasha Sorokin e45e0fdde9 Version Packages 2023-06-14 12:23:31 +01:00
Sasha Sorokin db449d439a Fix missing options in the newer Nuxt versions 2023-06-14 13:15:51 +02:00
github-actions[bot]andSasha Sorokin 333d55741c Version Packages 2023-06-09 23:32:15 +01:00
Sasha Sorokin 2590b2f942 Downgrade and pin versions of FormatJS packages 2023-06-09 23:29:24 +01:00
github-actions[bot]andSasha Sorokin f59c0af86e Version Packages 2023-06-09 19:25:11 +01:00
Sasha Sorokin 47f1739873 Fix consola not being a dep or used 2023-06-09 19:18:49 +01:00
Sasha Sorokin f62ae92791 Fix importing of no-parser alias 2023-06-09 19:05:18 +01:00
renovate[bot]andGitHub d5b7e30494 Update all non-major dependencies 2023-06-09 16:15:23 +00:00
github-actions[bot]andSasha Sorokin 1d21fc02fe Version Packages 2023-05-24 00:44:09 +02:00
Sasha Sorokin 9756d4acb9 Migrate resolving code to sync implementation 2023-05-23 23:35:42 +01:00
Sasha Sorokin 3d41dd1757 Add missing changeset for VIntl upgrade 2023-05-23 23:30:08 +01:00
renovate[bot]andGitHub 355a7d0726 Update dependency import-meta-resolve to v3 2023-05-23 21:05:49 +00:00
renovate[bot]andGitHub 1a470e9ee2 Update all non-major dependencies 2023-05-23 20:38:06 +00:00
Sasha Sorokin d36cd55167 Update astring dependency and remove patch 2023-05-23 22:32:34 +02:00
Sasha SorokinandGitHub b872f41ea1 Update dependencies (#32) 2023-05-23 22:30:13 +02:00
Sasha Sorokin 256598c87f Update @vintl/vintl to 4.2.0 2023-05-22 19:39:20 +02:00
Sasha Sorokin 19908fb197 Trigger test workflow on root pJson changes 2023-05-22 16:23:12 +02:00
Sasha Sorokin bfbcfd7c06 Add test workflow for pull requests 2023-05-22 16:03:21 +02:00
github-actions[bot]andSasha Sorokin d46362362d Version Packages 2023-05-12 20:55:47 +02:00
Sasha Sorokin 8f4ca76780 Move @nuxt/schema to devdeps 2023-05-12 17:14:52 +02:00
github-actions[bot]andSasha Sorokin 016c6938b8 Version Packages 2023-05-07 05:38:43 +02:00
Sasha Sorokin 25b5acfdd4 Don't require additionalImports to be a non-empty array 2023-05-07 05:36:36 +02:00
github-actions[bot]andSasha Sorokin 67168d2d2e Version Packages 2023-05-03 13:24:11 +02:00
Sasha Sorokin 71b900075f Fix object import sources not resolving by default 2023-05-03 13:06:40 +02:00
Sasha Sorokin 26354bacc9 Improve writing of warning about the descriptions in descriptors 2023-04-29 15:08:04 +02:00
Sasha Sorokin 566bf1561a Fix docs links after moving to Netlify 2023-04-29 14:56:09 +02:00
34 changed files with 6206 additions and 2489 deletions
+2 -2
View File
@@ -22,10 +22,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: 18.x
+79
View File
@@ -0,0 +1,79 @@
name: Test pull request
on:
pull_request:
branches: [main, next, alpha, beta, rc]
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: 18.x
- name: Check changed files
uses: dorny/paths-filter@v2
id: filter
with:
filters: |
shared: &shared
- .npmrc
- package.json
- pnpm-lock.yaml
nvintl:
- *shared
- 'packages/vintl-nuxt/**'
playground:
- *shared
- 'apps/playground/**'
- 'packages/translations/**'
docs:
- *shared
- 'apps/docs/**'
- name: Install pnpm via corepack
shell: bash
run: |
corepack enable
corepack prepare --activate
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install
- name: Prepare everything
run: pnpm all:prepare
- name: Lint everything
run: pnpm all:lint
- name: Build module
if: steps.filter.outputs.nvintl == 'true'
run: pnpm mod:build
- name: Build playground
if: steps.filter.outputs.playground == 'true' || steps.filter.outputs.nvintl == 'true'
run: pnpm pg:build
- name: Build docs
if: steps.filter.outputs.docs == 'true' || steps.filter.outputs.nvintl == 'true'
run: pnpm docs:build
+1 -1
View File
@@ -9,7 +9,7 @@ This monorepo hosts the VIntl for Nuxt module as well as other projects that hel
### Apps
- [Playground](https://github.com/vintl-dev/nuxt/tree/main/apps/playground) 🔒
- [Docs](https://github.com/vintl-dev/nuxt/tree/main/apps/docs) 🔒 [🌐](https://vintl-nuxt.vercel.app/)
- [Docs](https://github.com/vintl-dev/nuxt/tree/main/apps/docs) 🔒 [🌐](https://vintl-nuxt.netlify.app/)
### Packages
+1 -1
View File
@@ -2,7 +2,7 @@ export default defineAppConfig({
docus: {
title: 'VIntl for Nuxt',
description: 'Internationalization for your Nuxt apps',
url: 'http://vintl-nuxt.vercel.app',
url: 'https://vintl-nuxt.netlify.app',
// image: '/social-card-preview.png',
socials: {
github: 'vintl-dev/nuxt',
@@ -12,15 +12,17 @@ Represents the VIntl module options.
**Properties**
| Property | Default | Description |
| :---------------------- | :------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultLocale` | | A string that represents the BCP 47 code of the locale to use by default. |
| `resolveDir` | `'.'` | An optional string that represents a directory where all the imports associated with the locales are being resolved. If it's relative, then it will be resolved against the Nuxt `srcDir` directory. If this property is not set, modules will be resolved from the Nuxt `srcDir`. |
| `locales` | | An array of [`LocaleDescriptor`] objects that represent all the configured locales. |
| `storage` | | An optional string that represents either a built-in storage name (`localStorage` / `cookie`), or a node import specifier that exports a custom storage adapter as the default export. |
| `broadcastLocaleChange` | `true` | An optional boolean value that determines whether to use [`BroadcastChannel`] (if available) to synchronize the locale changes between the tabs on the same origin. |
| `hostLanguageParam` | `'hl'` | An optional name of the query parameter used to override the user language (including automatically detected one). It is used to generate SEO meta tags. Using `null` will disable them. |
| `parserless` | `{ enabled: false }` | An optional [`ParserlessOptions`] object or [`ParserlessMode`] which configures the parserless mode. |
| Property | Default | Description |
| :---------------------- | :-----: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaultLocale` | | A string that represents the BCP 47 code of the locale to use by default. |
| `resolveDir` | `'.'` | An optional string that represents a directory where all the imports associated with the locales are being resolved. If it's relative, then it will be resolved against the Nuxt `srcDir` directory. If this property is not set, modules will be resolved from the Nuxt `srcDir`. |
| `locales` | | An array of [`LocaleDescriptor`] objects that represent all the configured locales. |
| `storage` | | An optional string that represents either a built-in storage name (`localStorage` / `cookie`), or a node import specifier that exports a custom storage adapter as the default export. |
| `broadcastLocaleChange` | `true` | An optional boolean value that determines whether to use [`BroadcastChannel`] (if available) to synchronize the locale changes between the tabs on the same origin. |
| `hostLanguageParam` | `'hl'` | **Deprecated.** An optional name of the query parameter used to override the user language (including automatically detected one). It is used to generate SEO meta tags. Using `null` will disable them. |
| `parserless` | `<...>` | An optional [`ParserlessOptions`] object or [`ParserlessMode`] which configures the parserless mode. |
| `seo` | `<...>` | An optional [`SEOOptions`] object which configures automatic head tags to improve discoverability of other languages by search engines. |
| `onParseError` | | An optional [`ParsingErrorHandlingOption`] value that defines how to handle errors that occur when transforming message files. If not defined, the errors are not handled and thrown during the building, preventing it from succeeding. |
[`BroadcastChannel`]: https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel
[`ParserlessOptions`]: #parserlessoptions
@@ -131,9 +133,9 @@ Represents an import source for the messages file. It is similar to the [`Import
Represents options for the parserless mode.
| Property | Description |
| :-------- | :----------------------------------------------------------------------- |
| `enabled` | A `ParserlessMode` value representing the status of the parserless mode. |
| Property | Default | Description |
| :-------- | :-----: | :----------------------------------------------------------------------- |
| `enabled` | `false` | A `ParserlessMode` value representing the status of the parserless mode. |
## `ParserlessMode`
@@ -144,3 +146,39 @@ A string or boolean value representing the status of the parserless mode.
| `always` | `true` | Parserless mode is always enabled, regardless of the mode in which Nuxt is running. |
| `only-prod` | | Parserless mode is only enabled when building Nuxt app for production. |
| `never` | `false` | Parserless mode is never enabled, the parser is always present in the runtime. |
## `SEOOptions`
[`SEOOptions`]: #seooptions
Represents options for the automatic head tags.
By default VIntl will add `<link>` tags for every language to make search engines aware of other languages, you can disable this by setting `enabled` to `false`.
| Property | Default | Description |
| :-------------------------- | :-----: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `enabled` | `true` | A boolean value representing whether VIntl should automatically add `<link>` tags to `<head>` of the site. These tags make search engines aware that your site is provided in different languages. |
| `hostLanguageParameter`: | `hl` | A string value representing the query parameter that VIntl would use to configure the language of the site. |
| `baseURL` | | **Recommended.** An optional string value representing the base URL of the site. If not set, VIntl will try to guess the URL based on inital request URL or the current `window.location`. |
| `defaultLocaleHasParameter` | `true` | A boolean value representing whether the default locale should have a host language parameter. Disabling this may make URLs of visitors cleaner, however it is not correct, because lack of host language instructs VIntl to use user's language (either the one they've used before or browser's one). |
| `xDefaultHreflang` | `true` | A boolean value representing whether the `x-default` hreflang is present. |
## `ParsingErrorHandlingOption`
[`ParsingErrorHandlingOption`]: #parsingerrorhandlingoption
Represents a value for the message pasing error handling option.
It can be one of the following:
- A string containing a name of the strategy for handling the errors. The following strategies are available:
| Name | Strategy |
| :----------------------- | :-------------------------------------------------------------------------------- |
| `use-message-as-literal` | Use the raw message contents as a literal. |
| `use-id-as-literal` | Use the message ID as a literal. |
| `use-empty-literal` | Use empty string as a literal. |
| `log-and-skip` | Log the error to console and don't include the message into the transformed file. |
| `skip` | Skip including the message into the transformed file. |
- An function that takes in a context for the error (that includes error itself) and optionally returns a fallback AST (not returning anything skips including the message into the transformed file).
@@ -45,7 +45,8 @@ Message descriptors are objects that describe a message and its properties. The
- `description` (optional): A description of the message for translators.
::alert{type="warning"}
VIntl for Nuxt currently does not transpile the descriptors during the build, leaving `description` property as is, meaning they will pointlessly increase bundle size. If you really want to use them, look into wiring up [Babel plugin by FormatJS][babel_plugin].
Note that VIntl for Nuxt does not currently transpile code during the build process. This means that any `description` properties of message descriptors will remain unchanged, needlessly increasing the bundle size. If you must use `description` properties, we recommend using the [Babel plugin by FormatJS][babel_plugin] to remove them.
[babel_plugin]: https://formatjs.io/docs/tooling/babel-plugin
+8 -6
View File
@@ -13,13 +13,15 @@
"start": "node .output/server/index.mjs"
},
"devDependencies": {
"@formatjs/cli": "^6.0.4",
"@nuxt-themes/docus": "^1.10.1",
"@nuxt/eslint-config": "^0.1.1",
"@formatjs/cli": "^6.2.1",
"@nuxt-themes/docus": "^1.15.0",
"@nuxt/eslint-config": "^0.2.0",
"@vintl-dev/eslint-config-peony": "workspace:^",
"@vintl/nuxt": "workspace:^",
"eslint": "^8.39.0",
"nuxt": "^3.4.2",
"theme-colors": "^0.0.5"
"eslint": "^8.52.0",
"nuxi": "^3.9.1",
"nuxt": "^3.8.0",
"theme-colors": "^0.0.5",
"vue": "^3.3.7"
}
}
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0"?>
<users>
<user>9491DAF6E17F64A987F923EC70F0BAEE</user>
</users>
+9
View File
@@ -1,4 +1,7 @@
{
"broken-message": {
"message": "This is {broken, }"
},
"example": {
"message": "Example"
},
@@ -8,6 +11,9 @@
"index.other-page": {
"message": "To the other page"
},
"index.use-automatic": {
"message": "Use automatic"
},
"language": {
"message": "Language"
},
@@ -17,6 +23,9 @@
"other-page.back": {
"message": "Go back"
},
"other-page.description": {
"message": "You must've clicked me a thousand times"
},
"other-page.title": {
"message": "Hello from the other page!"
}
+10
View File
@@ -16,6 +16,11 @@ export default defineNuxtConfig({
format: 'crowdin',
},
],
meta: {
static: {
iso: 'en',
},
},
},
{
tag: 'uk',
@@ -30,5 +35,10 @@ export default defineNuxtConfig({
storage: 'cookie',
broadcastLocaleChange: true,
parserless: 'always',
seo: {
xDefaultHreflang: false,
defaultLocaleHasParameter: false,
},
onParseError: 'log-and-skip',
},
})
+4 -4
View File
@@ -10,15 +10,15 @@
"generate": "nuxi generate",
"preview": "nuxi preview",
"lint": "eslint --cache .",
"intl:extract": "formatjs extract \"{,components,composables,layouts,middleware,modules,pages,plugins,utils}/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore '**/*.d.ts' --ignore 'node_modules' --out-file locales/en-US.json --format simple",
"intl:extract": "formatjs extract \"{,components,composables,layouts,middleware,modules,pages,plugins,utils}/**/*.{vue,ts,tsx,js,jsx,mts,cts,mjs,cjs}\" --ignore '**/*.d.ts' --ignore 'node_modules' --out-file locales/en-US.json --format crowdin",
"start": "node .output/server/index.mjs"
},
"devDependencies": {
"@vintl-dev/eslint-config-peony": "workspace:^",
"@vintl-dev/pg-messages": "workspace:^",
"@vintl/nuxt": "workspace:^",
"eslint": "^8.39.0",
"nuxi": "^3.4.2",
"nuxt": "^3.4.2"
"eslint": "^8.52.0",
"nuxi": "^3.9.1",
"nuxt": "^3.8.0"
}
}
+59 -23
View File
@@ -23,47 +23,83 @@ const messages = defineMessages({
id: 'index.other-page',
defaultMessage: 'To the other page',
},
useAutomatic: {
id: 'index.use-automatic',
defaultMessage: 'Use automatic',
},
brokenMessage: {
id: 'broken-message',
defaultMessage: 'This is {broken, }',
},
} as const)
const vintl = useVIntl()
const { formatMessage } = vintl
const changing = ref(false)
async function changeLocale(locale: string) {
if (changing.value) return
changing.value = true
try {
await vintl.changeLocale(locale)
} catch (err) {
console.error('Error changing locale', err)
} finally {
changing.value = false
}
}
const noop = () => {}
const currentLocale = computed({
get() {
return vintl.locale
},
set() {
// ignore
set(value) {
changeLocale(value).then(noop, noop)
},
})
async function onLocaleChange(e: Event) {
await vintl.changeLocale(
(e as Event & { target: HTMLSelectElement }).target.value,
)
}
const useAutomatic = computed({
get() {
return vintl.automatic
},
set(value) {
changeLocale(value ? 'auto' : vintl.locale).then(noop, noop)
},
})
</script>
<template>
<div>
<div>
<h3>{{ formatMessage(messages.options) }}</h3>
<label for="language-select">
{{ formatMessage(messages.language) }}
</label>
<select
id="language-select"
v-model="currentLocale"
@change="onLocaleChange"
>
<option
v-for="locale in [...$nuxt.$i18n.$locales.value.keys()]"
:key="locale.tag"
:value="locale.tag"
>
{{ locale.tag }}
</option>
</select>
<div>
<label>
<input v-model="useAutomatic" type="checkbox" />
{{ formatMessage(messages.useAutomatic) }}
</label>
</div>
<div>
<label>
{{ formatMessage(messages.language) }}
<select
id="language-select"
v-model="currentLocale"
:disabled="vintl.automatic"
>
<option
v-for="locale in [...$i18n.$locales.value.keys()]"
:key="locale.tag"
:value="locale.tag"
>
{{ locale.tag }}
</option>
</select>
</label>
</div>
</div>
<div>
+3 -6
View File
@@ -7,8 +7,8 @@
"packages/*"
],
"devDependencies": {
"@changesets/cli": "^2.26.1",
"turbo": "^1.8.7"
"@changesets/cli": "^2.26.2",
"turbo": "^1.10.16"
},
"scripts": {
"pg:prepare": "turbo run --no-daemon @vintl-dev/pg#dev:prepare",
@@ -28,9 +28,6 @@
"_ci:publish": "changeset publish"
},
"pnpm": {
"patchedDependencies": {
"astring@1.8.4": "patches/astring@1.8.4.patch"
},
"packageExtensions": {
"@nuxt/kit": {
"dependencies": {
@@ -39,5 +36,5 @@
}
}
},
"packageManager": "pnpm@8.3.1"
"packageManager": "pnpm@8.9.2"
}
+3 -1
View File
@@ -28,7 +28,9 @@
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "warn",
"brace-style": "off",
"vue/singleline-html-element-content-newline": "off"
"vue/singleline-html-element-content-newline": "off",
"vue/max-attributes-per-line": "off",
"vue/html-self-closing": "off"
},
"parserOptions": {
"extraFileExtensions": [".vue"]
+7 -7
View File
@@ -26,14 +26,14 @@
"config.json"
],
"dependencies": {
"@rushstack/eslint-patch": "^1.2.0",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"eslint-plugin-vue": "^9.11.0",
"typescript": "^5.0.4"
"@rushstack/eslint-patch": "^1.5.1",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"eslint-plugin-vue": "^9.18.0",
"typescript": "^5.2.2"
},
"devDependencies": {
"@types/eslint": "^8.37.0",
"eslint": "^8.38.0"
"@types/eslint": "^8.44.6",
"eslint": "^8.52.0"
}
}
+158
View File
@@ -1,5 +1,163 @@
# @vintl/nuxt
## 1.8.0
### Minor Changes
- 9d939c1: Switched to using module ID markers
Previously VIntl for Nuxt kept track of files that were used as locale messages and matched module IDs based on them. This, however, was very unreliable in certain environments.
With this change VIntl for Nuxt now uses markers, URL parameters added to the end of imported module IDs. This allows it to very quickly test whether a specific module is imported through a message file and should be processed by the VIntl unplugin.
## 1.7.1
### Patch Changes
- 6479891: VIntl updated to the latest version
Latest version of VIntl fixes missing type declarations for global properties, like `$t`, `$fmt` and `$i18n`.
We might add automatic handling for disabling of `controllerOpts.globalMixin` in the future releases. You can follow the status update in [#74](https://github.com/vintl-dev/nuxt/issues/74).
For now, if you disable global mixin, use the below augmentation to disable these unusable properties:
```ts
declare global {
namespace VueIntlController {
interface Options {
globalMixin: false
}
}
}
```
## 1.7.0
### Minor Changes
- a9fdc59: Add support for parsing the messages in the error handlers
With [update to `@vintl/unplugin`](https://github.com/vintl-dev/unplugin/releases/tag/v1.5.0), it's now possible to use `parseMessage` from the error handler's context to parse custom messages or re-parse the current message with any modified options.
## 1.6.0
### Minor Changes
- 81c23b0: Support for parsing error handling
`@vintl/nuxt` can now handle errors when parsing message files containing broken messages in a configurable manner.
You can configure how errors are handled using `onParseError` module option, which can either be a name of the built-in strategy, or a function that takes in context and optionally provides fallback message AST.
Check out documentation for more information.
### Patch Changes
- 2b5a59b: Unpin FormatJS dependencies
We proveviously had `@formatjs/intl` pinned because of the broken ESM migration, however this has been fixed for quite some time already, so the pinned version has been to changed to a range, which ensures that `@formatjs/intl` can be updated with latest bug fixes and improvements until the next major version.
## 1.5.0
### Minor Changes
- 979f2bd: Add more SEO options
Two options have been added to the SEO configuration:
- `defaultLocaleHasParameter` allows to disable the host language parameter for the default locale.
This is not recommended and not correct, because lack of the host language parameter instructs VIntl to use user's language (either the one they've used before or browser's one). However, it may be useful in some cases.
- `xDefaultHreflang` allows to disable the `x-default` hreflang in case it's not useful to have (e.g. you disable the automatic language detection), although this is quite rare.
## 1.4.0
### Minor Changes
- e1c501c: Improve alternate links generation
Generation of `alternate` `hreflang` links has been improved to properly use absolute URLs.
Since there's no good way to know the URL of the site, there's now also `seo.baseURL` option which allows to set site's proper domain.
`hostLanguageParam` option has been deperecated in favour of `seo.hostLanguageParameter`, which functions almost the same way, except it does not accept `null`. If you want to disable generation of such tags or handle them yourself, you can set `seo.enabled` to `false`.
See [Configuration](https://vintl-nuxt.netlify.app/introduction/configuration#seooptions) documentation page for more details.
## 1.3.0
### Minor Changes
- 951f363: Add graceful fallback for unknown locales in storage
### Patch Changes
- ae71089: Use automatic locale if none returned by the storage
Fixed a bug where automatic locale would not be restored from the storage properly on the next page reload, despite it being saved properly.
## 1.2.3
### Patch Changes
- db449d4: Fix missing options in the newer Nuxt versions
## 1.2.2
### Patch Changes
- 2590b2f: Downgrade and pin versions of FormatJS packages
Newest versions of FormatJS packages contain exports map that result in illegal ESM as CJS imports. This downgrades them
For more details see the issue at https://github.com/formatjs/formatjs/issues/4126.
## 1.2.1
### Patch Changes
- f62ae92: Adapt to new export conditions of the parser
`@formatjs/icu-messageformat-parser` has been updated and now has export conditions, this change adapts and fixes the error caused by the previous direct import of the file for `no-parser` alias.
- 47f1739: Fix consola not being actually listed as a dependency or used
## 1.2.0
### Minor Changes
- 3d41dd1: Update VIntl to 4.2.0
Previously VIntl was depending on Vue directly, which caused issues, and wasn't correct, as Vue is not its implementation detail. 4.2.0 made Vue a peer dependency, fixing this issue. It also brought updates to other dependencies like `@formatjs/intl`.
### Patch Changes
- 9756d4a: Upgrade `import-meta-resolve` to v3
## 1.1.4
### Patch Changes
- 8f4ca76: Fix compatibility with newer versions of Nuxt
It seems that TypeScript has a trouble identifying similar types from different packages, which caused it to disregard any types from VIntl.
Now that the `@nuxt/schema` is moved to `devDependencies` (since it's used for types only anyway), and doesn't have to strictly match the version, this should be fixed.
## 1.1.3
### Patch Changes
- 25b5acf: Don't require additionalImports to be a non-empty array
## 1.1.2
### Patch Changes
- 71b9000: Fix object import sources not resolving by default
## 1.1.1
### Patch Changes
+1 -1
View File
@@ -36,7 +36,7 @@ Store your users preferences with cookies, localStorage, or with the custom stor
## Documentation
Check out the [VIntl for Nuxt documentation site](https://vintl-nuxt.vercel.app/) for detailed usage instructions and examples.
Check out the [VIntl for Nuxt documentation site](https://vintl-nuxt.netlify.app/) for detailed usage instructions and examples.
## Credits
+21 -17
View File
@@ -1,6 +1,6 @@
{
"name": "@vintl/nuxt",
"version": "1.1.1",
"version": "1.8.0",
"description": "Nuxt Module for VIntl integration",
"keywords": [
"i18n",
@@ -34,32 +34,36 @@
"dist"
],
"scripts": {
"build": "nuxt-module-build",
"build": "nuxt-module-build build",
"lint": "eslint --cache .",
"prepack": "pnpm run -s lint && pnpm run -s build"
},
"devDependencies": {
"@nuxt/module-builder": "^0.3.0",
"@types/hash-sum": "^1.0.0",
"@types/node": "^18.15.12",
"@unhead/vue": "^1.1.26",
"@nuxt/module-builder": "^0.5.2",
"@nuxt/schema": "^3.8.0",
"@types/hash-sum": "^1.0.1",
"@types/node": "^18.18.7",
"@unhead/vue": "^1.7.4",
"@vintl-dev/eslint-config-peony": "workspace:^",
"hookable": "^5.5.3",
"prettier-plugin-jsdoc": "^0.4.2",
"typescript": "^5.0.4"
"prettier-plugin-jsdoc": "^1.1.1",
"typescript": "^5.2.2",
"vue-router": "^4.2.5"
},
"dependencies": {
"@formatjs/intl": "^2.7.1",
"@nuxt/kit": "^3.4.2",
"@nuxt/schema": "^3.4.2",
"@vintl/unplugin": "^1.2.1",
"@vintl/vintl": "^4.1.2",
"astring": "^1.8.4",
"@formatjs/intl": "^2.9.5",
"@formatjs/intl-localematcher": "^0.4.2",
"@nuxt/kit": "^3.8.0",
"@vintl/unplugin": "^1.5.1",
"@vintl/vintl": "^4.4.1",
"astring": "^1.8.6",
"consola": "^3.2.3",
"hash-sum": "^2.0.0",
"import-meta-resolve": "^2.2.2",
"pathe": "^1.1.0",
"import-meta-resolve": "^3.0.0",
"pathe": "^1.1.1",
"picocolors": "^1.0.0",
"slash": "^5.0.0",
"slash": "^5.1.0",
"ufo": "^1.3.1",
"zod": "^3.21.4"
}
}
+32 -15
View File
@@ -21,16 +21,16 @@ import { moduleOptionsSchema } from './schemas/index.js'
import { PluginOptionsBank } from './parser-bank.js'
import { formatZodError } from './utils/zod-error.js'
import { OptionsError } from './errors.js'
import * as consola from 'console'
import { consola } from 'consola'
/** Path to the options file relative to `buildDir`. */
const optionsFilePath = 'i18n/options.mjs'
type _InputModuleOptions = typeof moduleOptionsSchema
export interface InputModuleOptions extends t.input<_InputModuleOptions> {}
export interface ModuleOptions extends t.input<_InputModuleOptions> {}
export default defineNuxtModule<InputModuleOptions>({
export default defineNuxtModule<ModuleOptions>({
meta: {
name: '@vintl/nuxt',
configKey: 'vintl',
@@ -48,7 +48,7 @@ export default defineNuxtModule<InputModuleOptions>({
const resolveInRuntime = createDirResolver(runtimeDir)
addPlugin((await resolveInRuntime('./plugin.js')).path)
addPlugin(resolveInRuntime('./plugin.js').path)
if (!nuxt.options._prepare) {
let options: t.output<typeof moduleOptionsSchema>
@@ -86,27 +86,23 @@ export default defineNuxtModule<InputModuleOptions>({
const optionsFile = addTemplate({
filename: optionsFilePath,
write: true,
async getContents() {
getContents() {
const resolveInResDir = createDirResolver(
resolvePath(nuxt.options.srcDir, options.resolveDir ?? '.'),
)
return await generateOptions(options, {
async resolve(specifier) {
return (await resolveInResDir(specifier)).relativeTo(
optionsFile.dst,
)
return generateOptions(options, {
resolve(specifier) {
return resolveInResDir(specifier).relativeTo(optionsFile.dst)
},
registerMessagesFile(file, importPath) {
pluginOptionsBank.registerFile(
return pluginOptionsBank.registerFile(
file,
resolvePath(dirname(optionsFile.dst), importPath),
)
},
async resolveRuntimeModule(specifier) {
return (await resolveInRuntime(specifier)).relativeTo(
optionsFile.dst,
)
resolveRuntimeModule(specifier) {
return resolveInRuntime(specifier).relativeTo(optionsFile.dst)
},
state: {
parserlessModeEnabled,
@@ -128,11 +124,31 @@ export default defineNuxtModule<InputModuleOptions>({
const { icuMessages } = await import('@vintl/unplugin')
const onParseError = (() => {
let { onParseError } = options
if (onParseError === 'log-and-skip') {
onParseError = (ctx) => {
consola.warn(
`[vintl] Cannot parse the message "${
ctx.messageId
}" in "${relativizePath(
nuxt.options.srcDir,
ctx.moduleId,
)}". It will me skipped.`,
)
}
}
return onParseError
})()
extendWebpackConfig((cfg) => {
const plugins = (cfg.plugins ??= [])
plugins.push(
...pluginOptionsBank
.createOptions({
onParseError,
output: {
type: parserlessModeEnabled ? 'ast' : 'raw',
},
@@ -147,6 +163,7 @@ export default defineNuxtModule<InputModuleOptions>({
...pluginOptionsBank
.createOptions({
pluginsWrapping: true,
onParseError,
output: {
type: parserlessModeEnabled ? 'ast' : 'raw',
},
+17 -16
View File
@@ -616,18 +616,19 @@ interface GeneratorContext {
* @param specifier Module import specifier.
* @returns Path relative to the options file.
*/
resolve(specifier: string): Promise<string>
resolve(specifier: string): string
/**
* Analytical method that is called for every generated messages import.
*
* @param file Input message file options.
* @param importPath Resolved path.
* @returns Marked import path.
*/
registerMessagesFile(
file: t.output<typeof messagesImportSourceSchema>,
importPath: string,
): void
): string
/**
* Resolves a module from the runtime directory.
@@ -635,7 +636,7 @@ interface GeneratorContext {
* @param specifier Module import specifier.
* @returns Path relative to the optons file.
*/
resolveRuntimeModule(specifier: string): Promise<string>
resolveRuntimeModule(specifier: string): string
/** Additional state to encode which is not included in the options. */
state: {
@@ -649,7 +650,7 @@ interface GeneratorContext {
* then renders it to actual JavaScript code. The resulting code can be written
* to a special file and read at runtime to retrieve the options.
*/
export async function generate(
export function generate(
opts: t.output<typeof moduleOptionsSchema>,
{
resolve,
@@ -697,7 +698,7 @@ export async function generate(
imports.push(
new ImportDeclaration(
new Literal(await resolveRuntimeModule('./utils/locale-loader.js')),
new Literal(resolveRuntimeModule('./utils/locale-loader.js')),
)
.addSpecifier(id$defineLocale)
.addSpecifier(id$rawValue),
@@ -780,8 +781,10 @@ export async function generate(
for (const messageFile of files) {
const { from: importPath, name: importKey } = messageFile
const resolvedPath = await resolve(messageFile.from)
registerMessagesFile(messageFile, resolvedPath)
const resolvedPath = registerMessagesFile(
messageFile,
resolve(messageFile.from),
)
if (isDefaultLocale) {
// if import key is 'default' then:
@@ -830,7 +833,7 @@ export async function generate(
for (const import_ of locale.additionalImports ?? []) {
const resolvedPath = import_.resolve
? await resolve(import_.from)
? resolve(import_.from)
: import_.from
if (isDefaultLocale) {
@@ -857,9 +860,7 @@ export async function generate(
resolve: shouldResolve,
} = dataImport
const resolvedPath = shouldResolve
? await resolve(importPath)
: importPath
const resolvedPath = shouldResolve ? resolve(importPath) : importPath
if (isDefaultLocale) {
// if import key is 'default' then:
@@ -984,11 +985,11 @@ export async function generate(
let storage = opts.storage
if (storage === 'localStorage') {
storage = await resolveRuntimeModule('./storage/local-storage.js')
storage = resolveRuntimeModule('./storage/local-storage.js')
} else if (storage === 'cookie') {
storage = await resolveRuntimeModule('./storage/cookie.js')
storage = resolveRuntimeModule('./storage/cookie.js')
} else {
storage = await resolve(storage)
storage = resolve(storage)
}
exports.push(
@@ -1022,8 +1023,8 @@ export async function generate(
new ExportNamedDeclaration().setDeclaration(
new VariableDeclaration('const', [
new VariableDeclarator(
new Identifier('hostLanguageParam'),
new Literal(opts.hostLanguageParam),
new Identifier('seo'),
new Literal(null).setRaw(JSON.stringify(opts.seo)),
),
]),
),
+34 -4
View File
@@ -12,9 +12,36 @@ import type {
type UniqueOptions = t.output<typeof messagesImportOptionsSchema>
class Marker {
private readonly regExp: RegExp
public constructor(public readonly marker: string) {
this.regExp = new RegExp(`(\\?|&)${marker}(&|$)`)
}
public apply(id: string) {
return `${id}${id.includes('?') ? '&' : '?'}${this.marker}`
}
public matches(id: string) {
return this.regExp.test(id)
}
}
class MarkedFileSet extends Set<string> {
public readonly marker: Marker
constructor(marker: Marker, iterable?: Iterable<string> | null | undefined) {
super(iterable)
this.marker = marker
}
}
export class PluginOptionsBank {
// let the uniqueOptions be a map of unique options to a list of files that have the same options
private readonly filesByOptions: Map<UniqueOptions, Set<string>> = new Map()
private readonly filesByOptions = new Map<UniqueOptions, MarkedFileSet>()
private incrementingMarker = 0
private findOrCreateFileSet(optionsVariation: UniqueOptions) {
for (const [knownOptionsVariation, files] of this.filesByOptions) {
@@ -31,7 +58,9 @@ export class PluginOptionsBank {
parser: optionsVariation.parser,
}
const fileSet = new Set<string>()
const marker = new Marker(`icu-messages-${this.incrementingMarker++}`)
const fileSet = new MarkedFileSet(marker)
this.filesByOptions.set(filteredOptions, fileSet)
return fileSet
}
@@ -40,7 +69,8 @@ export class PluginOptionsBank {
file: t.output<typeof messagesImportSourceSchema>,
resolvedPath: string,
) {
this.findOrCreateFileSet(file).add(resolvedPath)
const { marker } = this.findOrCreateFileSet(file).add(resolvedPath)
return marker.apply(resolvedPath)
}
public createOptions<T extends { name: string }>(
@@ -49,7 +79,7 @@ export class PluginOptionsBank {
const options: PluginOptions<T>[] = []
for (const [{ format, parser }, files] of this.filesByOptions) {
const filter = (id: string) => files.has(id)
const filter = (id: string) => files.marker.matches(id)
options.push({
...baseOptions,
+40 -15
View File
@@ -1,11 +1,12 @@
import { useHead } from '#imports'
import type { SEOOptions } from '@vintl/nuxt-runtime/options'
import type { IntlController } from '@vintl/vintl/controller'
import { useRouter as _useRouter, useRequestURL } from 'nuxt/app'
import { joinURL } from 'ufo'
import { computed } from 'vue'
import { useRouter } from 'nuxt/app'
import type { LocationQuery, useRouter as _useRouter$type } from 'vue-router'
type LocationQuery = ReturnType<
typeof useRouter
>['currentRoute']['value']['query']
const useRouter = _useRouter as typeof _useRouter$type
// I wish there was a better way to do this T_T
function getSeachParams(query: LocationQuery) {
@@ -35,11 +36,29 @@ type LangHrefLink = {
href: string
}
export function initHead(controller: IntlController<any>, hlParam: string) {
type HeadOptions = Omit<SEOOptions, 'enabled'>
export function initHead(
controller: IntlController<any>,
options: HeadOptions,
) {
const router = useRouter()
const currentRoute = computed(() => router.currentRoute.value)
const requestURL = useRequestURL()
const normalizeURL = (url: string) => {
try {
return options.baseURL == null
? new URL(url, requestURL).toString()
: joinURL(options.baseURL, url)
} catch (err) {
console.error(`[vintl] cannot normalize url: ${String(err)}`)
return url
}
}
useHead({
htmlAttrs: {
lang: () => controller.$config.locale,
@@ -51,37 +70,43 @@ export function initHead(controller: IntlController<any>, hlParam: string) {
const query = String(getSeachParams(route.query))
const { path } = route
let defaultEntry: LangHrefLink
const hrefLangs: Map<string, LangHrefLink> = new Map()
{
if (options.xDefaultHreflang) {
const sp = new URLSearchParams(query)
sp.delete(hlParam)
defaultEntry = {
sp.delete(options.hostLanguageParameter)
hrefLangs.set('x-default', {
// key: `hreflang-default`,
rel: 'alternate',
hreflang: 'x-default',
href: withQueryParams(path, sp.toString()),
}
href: normalizeURL(withQueryParams(path, sp.toString())),
})
}
const hrefLangs: Map<string, LangHrefLink> = new Map()
for (const locale of controller.availableLocales) {
const hrefLang = locale.meta?.static?.iso ?? locale.tag
if (hrefLangs.has(hrefLang)) continue
const sp = new URLSearchParams(query)
sp.set(hlParam, locale.tag)
if (
controller.defaultLocale === locale.tag &&
!options.defaultLocaleHasParameter
) {
sp.delete(options.hostLanguageParameter)
} else {
sp.set(options.hostLanguageParameter, locale.tag)
}
hrefLangs.set(hrefLang, {
// key: `hreflang-${locale.tag}`,
rel: 'alternate',
hreflang: hrefLang,
href: withQueryParams(path, sp.toString()),
href: normalizeURL(withQueryParams(path, sp.toString())),
})
}
return [defaultEntry, ...hrefLangs.values()]
return Array.from(hrefLangs.values())
},
})
}
+17 -5
View File
@@ -3,8 +3,8 @@ import {
localeDefinitions,
storageAdapterFactory,
broadcastLocaleChange,
hostLanguageParam,
parserless,
seo as seoOptions,
} from '@vintl/nuxt-runtime/options'
import type { LocaleDescriptor, MessageValueType } from '@vintl/vintl'
import type { IntlController } from '@vintl/vintl/controller'
@@ -21,6 +21,7 @@ import { useAcceptLanguageHeader } from '@vintl/vintl/sources/header'
import { defineNuxtPlugin } from 'nuxt/app'
import { syncCaller } from './utils/hookable.js'
import { initHead } from './head.js'
import { match as matchLocales } from '@formatjs/intl-localematcher'
export default defineNuxtPlugin(async (nuxtApp) => {
const locales: LocaleDescriptor[] = Object.entries(localeDefinitions).map(
@@ -41,8 +42,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
}
}
if (hostLanguageParam != null) {
let hlLocale = nuxtApp._route.query[hostLanguageParam]
if (seoOptions.enabled) {
let hlLocale = nuxtApp._route.query[seoOptions.hostLanguageParameter]
if (Array.isArray(hlLocale)) {
hlLocale = hlLocale[0]
@@ -53,12 +54,23 @@ export default defineNuxtPlugin(async (nuxtApp) => {
}
}
if (locale != null) {
const match = matchLocales(
[locale],
locales.map(({ tag }) => tag),
'en-x-placeholder',
)
locale = match == 'en-x-placeholder' ? undefined : match
}
const plugin = createPlugin<MessageValueType>({
injectInto: [nuxtApp],
controllerOpts: {
defaultLocale,
locales,
locale,
usePreferredLocale: locale == null,
listen: {
error(event) {
nuxtApp.hooks.callHookWith(syncCaller, 'i18n:error', {
@@ -139,8 +151,8 @@ export default defineNuxtPlugin(async (nuxtApp) => {
setupBroadcasting(controller)
}
if (hostLanguageParam != null) {
nuxtApp.hook('vue:setup', () => initHead(controller, hostLanguageParam!))
if (seoOptions.enabled) {
nuxtApp.hook('vue:setup', () => initHead(controller, seoOptions))
}
await nuxtApp.callHook('i18n:ready', controller)
+1 -1
View File
@@ -16,7 +16,7 @@ export const importSourceObjectSchema = t.object({
.default('default'),
/** Whether to resolve the import to a path or import as is. */
resolve: t.boolean().optional(),
resolve: t.boolean().default(true),
})
/** A schema for the tuple consisting of an import path and a custom export name. */
@@ -68,7 +68,6 @@ export const localeDescriptorSchema = t
.describe(
'List of additional side-effect only imports (like polyfill data)',
)
.nonempty()
.optional(),
/**
@@ -2,6 +2,28 @@ import { z as t } from 'zod'
import { tSwitch } from '../utils/zod-utils.js'
import { languageTagSchema } from './language-tag.js'
import { localeDescriptorSchema } from './locales.js'
import { seoOptions } from './seo.js'
import type { ParseErrorHandler } from '@vintl/unplugin'
const parseErrorHandingStrategy = t.union([
t.literal('use-message-as-literal'),
t.literal('use-id-as-literal'),
t.literal('use-empty-literal'),
t.literal('log-and-skip'),
t.literal('skip'),
])
const parseErrorHandler = t.custom<ParseErrorHandler>(
(value) => typeof value === 'function',
)
export const parseErrorHandling = tSwitch((input) => {
if (typeof input === 'function') {
return parseErrorHandler
} else if (typeof input === 'string') {
return parseErrorHandingStrategy
}
})
const parselessModeEnumSchema = t.enum(['always', 'only-prod', 'never'])
@@ -167,6 +189,7 @@ export const moduleOptionsSchema = t
* user language. `null` to disable. If this value is `null`, the SEO tags
* generation will be disabled.
*
* @deprecated Use `seo` options instead.
* @default 'hl' // For example, ?hl=en to use English locale (if defined).
*/
hostLanguageParam: t
@@ -174,8 +197,10 @@ export const moduleOptionsSchema = t
.describe(
'Name of the host language URL parameter, used to override the default or user language.',
)
.nullable()
.default('hl'),
.optional()
.nullable(),
seo: seoOptions.optional(),
/**
* Configuration options for the parserless mode, a mode in which the parser
@@ -196,6 +221,29 @@ export const moduleOptionsSchema = t
return parselessOptionsSchema
}
}).default('only-prod'),
/**
* Either a literal name of the strategy or a function that handles the
* errors that occur during the parsing of one of the imported files.
*
* @see https://github.com/vintl-dev/unplugin/blob/df9b3be9c77f78eaf5fe7f8d6fde1655f2bc79e1/README.md#onparseerror
*/
onParseError: parseErrorHandling.optional(),
})
.transform((from) => {
if (from.seo === undefined) {
let hostLanguageParam = from.hostLanguageParam
if (hostLanguageParam === undefined) {
hostLanguageParam = 'hl'
}
from.seo = seoOptions.parse({
enabled: hostLanguageParam != null,
hostLanguageParameter: hostLanguageParam ?? undefined,
})
}
return from as typeof from & { seo: t.output<typeof seoOptions> }
})
.refine((value) => {
return value.locales.some((locale) => locale.tag === value.defaultLocale)
+45
View File
@@ -0,0 +1,45 @@
import { z as t } from 'zod'
export const seoOptions = t.object({
/** Whether CEO features are enabled. */
enabled: t.boolean().default(true),
/**
* Name of the host language URL parameter, used to override the default or
* user language.
*
* @default 'hl' // For example, ?hl=en to use English locale (if defined).
*/
hostLanguageParameter: t
.string()
.describe(
'Name of the host language URL parameter, used to override the default or user language.',
)
.optional()
.default('hl'),
/**
* Base URL at which the site will be available. This is required to generate
* absolute hreflang links. Without this, VIntl will try to guess the URL
* based on the server request URL / current `window.location`.
*/
baseURL: t
.string()
.describe(
'Base URL at which the site will be available. This is required to generate absolute hreflang links. ',
)
.optional(),
/**
* Whether to use the host language parameter for the default locale.
*
* This options helps keep URLs for your default language clean, however it is
* not correct, and is not recommended, because lack of the host language
* parameter tells VIntl to use the user locale (whether it's the one they've
* choosen before or their browser's one).
*/
defaultLocaleHasParameter: t.boolean().default(true),
/** Whether to enable `x-default` hreflang. */
xDefaultHreflang: t.boolean().default(true),
})
+2 -2
View File
@@ -6,8 +6,8 @@ import slash from 'slash'
export function createDirResolver(dir: string) {
const pseudoParent = pathToFileURL(joinPaths(dir, '.resolve-root')).toString()
return async function resolve(specifier: string) {
const resolved = await rawResolve(specifier, pseudoParent)
return function resolve(specifier: string) {
const resolved = rawResolve(specifier, pseudoParent)
return {
/** A getter that returns resolved URL as is. */
+4 -2
View File
@@ -1,8 +1,10 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "nodenext",
"module": "esnext",
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"types": ["./types/nuxt"],
+1 -1
View File
@@ -3,7 +3,7 @@
"compilerOptions": {
"composite": true,
"module": "ESNext",
"module": "nodenext",
"moduleResolution": "nodenext",
"types": ["./types/nuxt"],
+33
View File
@@ -64,4 +64,37 @@ declare module '@vintl/nuxt-runtime/options' {
* descriptor.
*/
export const parserless: boolean
export interface SEOOptions {
/**
* Whether SEO features are enabled.
*
* @default true
*/
enabled: boolean
/**
* Name of the host language URL parameter, used to override the default or
* user language.
*
* @default 'hl' // For example, ?hl=en to use English locale (if defined).
*/
hostLanguageParameter: string
/**
* Base URL at which the site will be available. This is required to
* generate absolute hreflang links. Without this, VIntl must try to guess
* the URL based on the server request URL / current `window.location`.
*/
baseURL?: string
/** Whether to use the host language parameter for the default locale. */
defaultLocaleHasParameter: boolean
/** Whether to enable `x-default` hreflang. */
xDefaultHreflang: boolean
}
/** SEO options. */
export const seo: SEOOptions
}
-12
View File
@@ -1,12 +0,0 @@
diff --git a/package.json b/package.json
index 55da8ad891741382b793f105b328b77997fab1de..d714282fd3742d377fe05db58bc00460ae44cdb6 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"module": "./dist/astring.mjs",
"types": "./astring.d.ts",
"exports": {
+ "types": "./astring.d.ts",
"import": "./dist/astring.mjs",
"require": "./dist/astring.js",
"browser": "./dist/astring.min.js"
+5504 -2330
View File
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -7,8 +7,11 @@
"@vintl/nuxt#lint": {
"outputs": [".eslintcache"]
},
"@vintl-dev/pg#intl:extract": {
"outputs": ["./locales/en-US.json"]
},
"@vintl-dev/pg#dev:prepare": {
"dependsOn": ["@vintl/nuxt#build"],
"dependsOn": ["@vintl/nuxt#build", "@vintl-dev/pg#intl:extract"],
"inputs": ["./package.json", "./nuxt.config.ts"],
"outputs": [".nuxt"]
},